<commit_before>//
//  catalog.cpp
//  MiniSQL
//
//  Created by scn3 on 15/10/30.
//  Copyright © 2015年 scn3. All rights reserved.
//

#include "catalog.h"
#include "../buffer/buffer.h"

bool Catalog::loadSchema(string tableName) {
    if (schema.tableName == tableName)
        return true;
    else clearSchema();

    string fileName = tableName + ".catalog";
    //    if (Buffer::exists(fileName))
    //        return false;
    //    if (access(fileName.c_str(), 0) == -1)
    //        return false;
    Block *block = Buffer::access(fileName, 0);
    const char *data = block->constData();
    if (data[0] == 0)
        return false;
    schema.tableName = tableName;
    string str = "";
    infoType curInfoType = ATTRNAME;
    AttrDetail *curAttr;
    int zeroCount = 0;  // 连续的0的个数，一个0表示字符串结束，两个0表示attr结束（包括其index），三个0表示table结束；

    int blockIndex = 0;
    int fromOffset;
    while (true) {
        if (blockIndex == 0)
            fromOffset = 1;
        else fromOffset = 0;
        Block *block = Buffer::access(fileName, blockIndex++);
        const char *data = block->constData();
        for (int i = fromOffset; i < block->size(); i++) {
            char ch = data[i];
            if (ch == 0) {
                if (++zeroCount == 1)
                    switch (curInfoType) {
                        case ATTRNAME: {
                            curAttr = new AttrDetail;
                            curAttr->attrName = str;
                            break;
                        }
                        case ATTRTYPE: {
                            if (str == "0")
                                curAttr->attrType = INTTYPE;
                            else if (str == "1")
                                curAttr->attrType = CHARTYPE;
                            else if (str == "2")
                                curAttr->attrType = FLOATTYPE;
                            else return false;
                            break;
                        }
                        case ATTRLEN: {
                            curAttr->length = str.at(0);
                            break;
                        }
                        case ATTRUNIQUE: {
                            curAttr->unique = str == "1";
                            break;
                        }
                        case ATTRPRIMARY: {
                            curAttr->primary = str == "1";
                            break;
                        }
                        case INDEXNAME: {
                            curAttr->indexList.push_back(str);
                        }
                        default:
                            break;
                    }
                if (zeroCount > 2) {
                    schema.attrDetailList.push_back(*curAttr);
                    return true;
                }
                str.clear();
            }
            else {
                if (zeroCount == 2) {   // 一个Attr结束，到一个新的attr，则第一个信息类型是Attr名
                    curInfoType = ATTRNAME;
                    schema.attrDetailList.push_back(*curAttr);
                }
                else if (zeroCount == 1) {
                    if (curInfoType < INDEXNAME)    // 如果当前信息不是索引名信息，则切换到下一信息类型
                        curInfoType++;
                }
                zeroCount = 0;
                str += ch;
            }
        }
    }

    return true;
}

bool Catalog::storeSchema() {
    if (schema.tableName.empty())
        return false;

    string fileName = schema.tableName + ".catalog";
    int blockIndex = 0;
    int offset = 1;
    Block *block = Buffer::access(fileName, 0);
    char *data = block->data();
    data[0] = 1;

    for (vector<AttrDetail>::iterator itr = schema.attrDetailList.begin(); itr != schema.attrDetailList.end(); itr++) {
        save(itr->attrName, blockIndex, offset);
        save('\0', blockIndex, offset);
        if (itr->attrType == INTTYPE)
            save('0', blockIndex, offset);
        else if (itr->attrType == CHARTYPE)
            save('1', blockIndex, offset);
        else save('2', blockIndex, offset);
        save('\0', blockIndex, offset);
        char ch = itr->length;
        string str = "";
        str += ch;
        save(str, blockIndex, offset);
        save('\0', blockIndex, offset);
        if (itr->unique)
            save('1', blockIndex, offset);
        else save('0', blockIndex, offset);
        save('\0', blockIndex, offset);
        if (itr->primary)
            save('1', blockIndex, offset);
        else save('0', blockIndex, offset);
        save('\0', blockIndex, offset);
        for (strings::iterator itr2 = itr->indexList.begin(); itr2 != itr->indexList.end(); itr2++) {
            save(*itr2, blockIndex, offset);
            save('\0', blockIndex, offset);
        }
        save('\0', blockIndex, offset);
    }
    save('\0', blockIndex, offset);
    return true;
}

void Catalog::save(string str, int &blockIndex, int &offset) {
    for (int i = 0; i < str.size(); i++)
        save(str.at(i), blockIndex, offset);
}

void Catalog::save(char ch, int &blockIndex, int &offset) {
    if (offset == defaultBlockSize) {
        offset = 0;
        blockIndex++;
    }
    Block *block = Buffer::access(schema.tableName + ".catalog", blockIndex);
    char *data = block->data();
    data[offset++] = ch;
}

void Catalog::clearSchema() {
    schema.tableName.clear();
    for (vector<AttrDetail>::iterator itr = schema.attrDetailList.begin(); itr != schema.attrDetailList.end(); itr++) {
        vector<string> ().swap(itr->indexList);
    }
    vector<AttrDetail> ().swap(schema.attrDetailList);
}

bool Catalog::checkTableExist(string tableName) {
    if (schema.tableName == tableName)
        return true;
    return loadSchema(tableName);
}

int Catalog::checkAttributeExist(string tableName, string attributeName) {
    if (!loadSchema(tableName))
        return -1;
    for (vector<AttrDetail>::iterator itr = schema.attrDetailList.begin(); itr != schema.attrDetailList.end(); itr++) {
        if (itr->attrName == attributeName)
            return itr - schema.attrDetailList.begin();
    }
    return -1;
}

bool Catalog::checkIndexExist(string tableName, string indexName) {
    if (!loadSchema(tableName))
        return false;
    for (vector<AttrDetail>::iterator aitr = schema.attrDetailList.begin(); aitr != schema.attrDetailList.end(); aitr++) {
        vector<string> curIndexList = aitr->indexList;
        for (vector<string>::iterator sitr = curIndexList.begin(); sitr != curIndexList.end(); sitr++) {
            if (*sitr == indexName)
                return true;
        }
    }
    return false;
}

bool Catalog::getIndexList(string tableName, string attributeName, Strings indexList) {
    indexList.clear();
    clearSchema();
    if (!loadSchema(tableName))
        return false;
    for (vector<AttrDetail>::iterator aitr = schema.attrDetailList.begin(); aitr != schema.attrDetailList.end(); aitr++)
        if (aitr->attrName == attributeName) {
            indexList = aitr->indexList;
            return true;
        }
    return false;
}

bool Catalog::createTable(string tableName, vector<AttributeDetail> &attributeList) {
    clearSchema();
    if (checkTableExist(tableName))
        return false;
    schema.tableName = tableName;
    for (vector<AttributeDetail>::iterator itr = attributeList.begin(); itr != attributeList.end(); itr++) {
        AttrDetail attrDetail;
        attrDetail.attrName = itr->name;
        attrDetail.attrType = itr->type;
        if (itr->type == INTTYPE || itr->type == FLOATTYPE)
            attrDetail.length = 4;
        else attrDetail.length = itr->length;
        attrDetail.unique = itr->unique;
        attrDetail.primary = itr->primary;
        schema.attrDetailList.push_back(attrDetail);
    }
    storeSchema();
    return true;
}

bool Catalog::createIndex(string tableName, string attributeName, string indexName) {
    if (!loadSchema(tableName))
        return false;
    if (!checkAttributeExist(tableName, attributeName))
        return false;
    if (checkIndexExist(tableName, indexName))
        return false;
    for (vector<AttrDetail>::iterator itr = schema.attrDetailList.begin(); itr != schema.attrDetailList.end(); itr++)
        if (itr->attrName == attributeName)
            itr->indexList.push_back(indexName);
    storeSchema();
    return true;
}

bool Catalog::dropTable(string tableName) {
    if (!checkTableExist(tableName))
        return false;
    Block *block = Buffer::access(tableName + ".catalog", 0);
    char *data = block->data();
    data[0] = 0;
    tableName = "";
    clearSchema();
    return true;
}

bool Catalog::dropIndex(string tableName, string indexName) {
    if (!loadSchema(tableName))
        return false;
    if (!checkIndexExist(tableName, indexName))
        return false;
    for (vector<AttrDetail>::iterator aitr = schema.attrDetailList.begin(); aitr != schema.attrDetailList.end(); aitr++) {
        for (vector<string>::iterator sitr = aitr->indexList.begin(); sitr != aitr->indexList.end();) {
            if (*sitr == indexName) {
                aitr->indexList.erase(sitr++);
                return true;
            }
            else sitr++;
        }
    }
    return storeSchema();
}

int Catalog::getAttributeSize(string tableName, string attributeName) {
    loadSchema(tableName);
    for (vector<AttrDetail>::iterator itr = schema.attrDetailList.begin(); itr != schema.attrDetailList.end(); itr++)
        if (itr->attrName == attributeName)
            return itr->length;
    return 4;
}

int Catalog::getAttributesTotalSize(string tableName) {
    int sum = 0;
    loadSchema(tableName);
    for (vector<AttrDetail>::iterator itr = schema.attrDetailList.begin(); itr != schema.attrDetailList.end(); itr++)
        sum += itr->length;
    return sum;
}

vector<attributeType> Catalog::getAttributeType(string tableName, string queryName) {
    vector<attributeType> res;
    loadSchema(tableName);
    for (vector<AttrDetail>::iterator itr = schema.attrDetailList.begin(); itr != schema.attrDetailList.end(); itr++) {
        if (queryName == "") {
            res.push_back(itr->attrType);
        } else if (itr->attrName == queryName) {
            res.push_back(itr->attrType);
        }
    }
    return res;
}

vector<int> Catalog::getPrimaryOrUniquePosition(string tableName) {
    vector<int> res;
    loadSchema(tableName);
    for (vector<AttrDetail>::iterator itr = schema.attrDetailList.begin(); itr != schema.attrDetailList.end(); itr++) {
      if (itr->unique || itr->primary) {
          res.push_back(itr - schema.attrDetailList.begin());
      }
    }
    return res;
}

vector<string> Catalog::getAttributeName(string tableName) {
    vector<string> res;
    loadSchema(tableName);
    for (vector<AttrDetail>::iterator itr = schema.attrDetailList.begin(); itr != schema.attrDetailList.end(); itr++) {
      res.push_back(itr->attrName);
    }
    return res;
}
<commit_msg>adjust return value<commit_after>//
//  catalog.cpp
//  MiniSQL
//
//  Created by scn3 on 15/10/30.
//  Copyright © 2015年 scn3. All rights reserved.
//

#include "catalog.h"
#include "../buffer/buffer.h"

bool Catalog::loadSchema(string tableName) {
    if (schema.tableName == tableName)
        return true;
    else clearSchema();

    string fileName = tableName + ".catalog";
    //    if (Buffer::exists(fileName))
    //        return false;
    //    if (access(fileName.c_str(), 0) == -1)
    //        return false;
    Block *block = Buffer::access(fileName, 0);
    const char *data = block->constData();
    if (data[0] == 0)
        return false;
    schema.tableName = tableName;
    string str = "";
    infoType curInfoType = ATTRNAME;
    AttrDetail *curAttr;
    int zeroCount = 0;  // 连续的0的个数，一个0表示字符串结束，两个0表示attr结束（包括其index），三个0表示table结束；

    int blockIndex = 0;
    int fromOffset;
    while (true) {
        if (blockIndex == 0)
            fromOffset = 1;
        else fromOffset = 0;
        Block *block = Buffer::access(fileName, blockIndex++);
        const char *data = block->constData();
        for (int i = fromOffset; i < block->size(); i++) {
            char ch = data[i];
            if (ch == 0) {
                if (++zeroCount == 1)
                    switch (curInfoType) {
                        case ATTRNAME: {
                            curAttr = new AttrDetail;
                            curAttr->attrName = str;
                            break;
                        }
                        case ATTRTYPE: {
                            if (str == "0")
                                curAttr->attrType = INTTYPE;
                            else if (str == "1")
                                curAttr->attrType = CHARTYPE;
                            else if (str == "2")
                                curAttr->attrType = FLOATTYPE;
                            else return false;
                            break;
                        }
                        case ATTRLEN: {
                            curAttr->length = str.at(0);
                            break;
                        }
                        case ATTRUNIQUE: {
                            curAttr->unique = str == "1";
                            break;
                        }
                        case ATTRPRIMARY: {
                            curAttr->primary = str == "1";
                            break;
                        }
                        case INDEXNAME: {
                            curAttr->indexList.push_back(str);
                        }
                        default:
                            break;
                    }
                if (zeroCount > 2) {
                    schema.attrDetailList.push_back(*curAttr);
                    return true;
                }
                str.clear();
            }
            else {
                if (zeroCount == 2) {   // 一个Attr结束，到一个新的attr，则第一个信息类型是Attr名
                    curInfoType = ATTRNAME;
                    schema.attrDetailList.push_back(*curAttr);
                }
                else if (zeroCount == 1) {
                    if (curInfoType < INDEXNAME)    // 如果当前信息不是索引名信息，则切换到下一信息类型
                        curInfoType++;
                }
                zeroCount = 0;
                str += ch;
            }
        }
    }

    return true;
}

bool Catalog::storeSchema() {
    if (schema.tableName.empty())
        return false;

    string fileName = schema.tableName + ".catalog";
    int blockIndex = 0;
    int offset = 1;
    Block *block = Buffer::access(fileName, 0);
    char *data = block->data();
    data[0] = 1;

    for (vector<AttrDetail>::iterator itr = schema.attrDetailList.begin(); itr != schema.attrDetailList.end(); itr++) {
        save(itr->attrName, blockIndex, offset);
        save('\0', blockIndex, offset);
        if (itr->attrType == INTTYPE)
            save('0', blockIndex, offset);
        else if (itr->attrType == CHARTYPE)
            save('1', blockIndex, offset);
        else save('2', blockIndex, offset);
        save('\0', blockIndex, offset);
        char ch = itr->length;
        string str = "";
        str += ch;
        save(str, blockIndex, offset);
        save('\0', blockIndex, offset);
        if (itr->unique)
            save('1', blockIndex, offset);
        else save('0', blockIndex, offset);
        save('\0', blockIndex, offset);
        if (itr->primary)
            save('1', blockIndex, offset);
        else save('0', blockIndex, offset);
        save('\0', blockIndex, offset);
        for (strings::iterator itr2 = itr->indexList.begin(); itr2 != itr->indexList.end(); itr2++) {
            save(*itr2, blockIndex, offset);
            save('\0', blockIndex, offset);
        }
        save('\0', blockIndex, offset);
    }
    save('\0', blockIndex, offset);
    return true;
}

void Catalog::save(string str, int &blockIndex, int &offset) {
    for (int i = 0; i < str.size(); i++)
        save(str.at(i), blockIndex, offset);
}

void Catalog::save(char ch, int &blockIndex, int &offset) {
    if (offset == defaultBlockSize) {
        offset = 0;
        blockIndex++;
    }
    Block *block = Buffer::access(schema.tableName + ".catalog", blockIndex);
    char *data = block->data();
    data[offset++] = ch;
}

void Catalog::clearSchema() {
    schema.tableName.clear();
    for (vector<AttrDetail>::iterator itr = schema.attrDetailList.begin(); itr != schema.attrDetailList.end(); itr++) {
        vector<string> ().swap(itr->indexList);
    }
    vector<AttrDetail> ().swap(schema.attrDetailList);
}

bool Catalog::checkTableExist(string tableName) {
    if (schema.tableName == tableName)
        return true;
    return loadSchema(tableName);
}

int Catalog::checkAttributeExist(string tableName, string attributeName) {
    if (!loadSchema(tableName))
        return -1;
    for (vector<AttrDetail>::iterator itr = schema.attrDetailList.begin(); itr != schema.attrDetailList.end(); itr++) {
        if (itr->attrName == attributeName)
            return itr - schema.attrDetailList.begin();
    }
    return -1;
}

bool Catalog::checkIndexExist(string tableName, string indexName) {
    if (!loadSchema(tableName))
        return false;
    for (vector<AttrDetail>::iterator aitr = schema.attrDetailList.begin(); aitr != schema.attrDetailList.end(); aitr++) {
        vector<string> curIndexList = aitr->indexList;
        for (vector<string>::iterator sitr = curIndexList.begin(); sitr != curIndexList.end(); sitr++) {
            if (*sitr == indexName)
                return true;
        }
    }
    return false;
}

bool Catalog::getIndexList(string tableName, string attributeName, Strings indexList) {
    indexList.clear();
    clearSchema();
    if (!loadSchema(tableName))
        return false;
    for (vector<AttrDetail>::iterator aitr = schema.attrDetailList.begin(); aitr != schema.attrDetailList.end(); aitr++)
        if (aitr->attrName == attributeName) {
            indexList = aitr->indexList;
            return true;
        }
    return false;
}

bool Catalog::createTable(string tableName, vector<AttributeDetail> &attributeList) {
    clearSchema();
    if (checkTableExist(tableName))
        return false;
    schema.tableName = tableName;
    for (vector<AttributeDetail>::iterator itr = attributeList.begin(); itr != attributeList.end(); itr++) {
        AttrDetail attrDetail;
        attrDetail.attrName = itr->name;
        attrDetail.attrType = itr->type;
        if (itr->type == INTTYPE || itr->type == FLOATTYPE)
            attrDetail.length = 4;
        else attrDetail.length = itr->length;
        attrDetail.unique = itr->unique;
        attrDetail.primary = itr->primary;
        schema.attrDetailList.push_back(attrDetail);
    }
    storeSchema();
    return true;
}

bool Catalog::createIndex(string tableName, string attributeName, string indexName) {
    if (!loadSchema(tableName))
        return false;
    if (checkAttributeExist(tableName, attributeName) == -1)
        return false;
    if (checkIndexExist(tableName, indexName))
        return false;
    for (vector<AttrDetail>::iterator itr = schema.attrDetailList.begin(); itr != schema.attrDetailList.end(); itr++)
        if (itr->attrName == attributeName)
            itr->indexList.push_back(indexName);
    storeSchema();
    return true;
}

bool Catalog::dropTable(string tableName) {
    if (!checkTableExist(tableName))
        return false;
    Block *block = Buffer::access(tableName + ".catalog", 0);
    char *data = block->data();
    data[0] = 0;
    tableName = "";
    clearSchema();
    return true;
}

bool Catalog::dropIndex(string tableName, string indexName) {
    if (!loadSchema(tableName))
        return false;
    if (!checkIndexExist(tableName, indexName))
        return false;
    for (vector<AttrDetail>::iterator aitr = schema.attrDetailList.begin(); aitr != schema.attrDetailList.end(); aitr++) {
        for (vector<string>::iterator sitr = aitr->indexList.begin(); sitr != aitr->indexList.end();) {
            if (*sitr == indexName) {
                aitr->indexList.erase(sitr++);
                return true;
            }
            else sitr++;
        }
    }
    return storeSchema();
}

int Catalog::getAttributeSize(string tableName, string attributeName) {
    loadSchema(tableName);
    for (vector<AttrDetail>::iterator itr = schema.attrDetailList.begin(); itr != schema.attrDetailList.end(); itr++)
        if (itr->attrName == attributeName)
            return itr->length;
    return 4;
}

int Catalog::getAttributesTotalSize(string tableName) {
    int sum = 0;
    loadSchema(tableName);
    for (vector<AttrDetail>::iterator itr = schema.attrDetailList.begin(); itr != schema.attrDetailList.end(); itr++)
        sum += itr->length;
    return sum;
}

vector<attributeType> Catalog::getAttributeType(string tableName, string queryName) {
    vector<attributeType> res;
    loadSchema(tableName);
    for (vector<AttrDetail>::iterator itr = schema.attrDetailList.begin(); itr != schema.attrDetailList.end(); itr++) {
        if (queryName == "") {
            res.push_back(itr->attrType);
        } else if (itr->attrName == queryName) {
            res.push_back(itr->attrType);
        }
    }
    return res;
}

vector<int> Catalog::getPrimaryOrUniquePosition(string tableName) {
    vector<int> res;
    loadSchema(tableName);
    for (vector<AttrDetail>::iterator itr = schema.attrDetailList.begin(); itr != schema.attrDetailList.end(); itr++) {
      if (itr->unique || itr->primary) {
          res.push_back(itr - schema.attrDetailList.begin());
      }
    }
    return res;
}

vector<string> Catalog::getAttributeName(string tableName) {
    vector<string> res;
    loadSchema(tableName);
    for (vector<AttrDetail>::iterator itr = schema.attrDetailList.begin(); itr != schema.attrDetailList.end(); itr++) {
      res.push_back(itr->attrName);
    }
    return res;
}
<|endoftext|>


<commit_before>///////////////////////////////////////////////////////////////////////
// File:        linerec.cpp
// Description: Top-level line-based recognition module for Tesseract.
// Author:      Ray Smith
//
// (C) Copyright 2013, Google Inc.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///////////////////////////////////////////////////////////////////////

#include "tesseractclass.h"

#include "allheaders.h"
#include "boxread.h"
#include "imagedata.h"
#ifndef ANDROID_BUILD
#include "lstmrecognizer.h"
#include "recodebeam.h"
#endif
#include "pageres.h"
#include "tprintf.h"

#include <algorithm>

namespace tesseract {

// Scale factor to make certainty more comparable to Tesseract.
const float kCertaintyScale = 7.0f;
// Worst acceptable certainty for a dictionary word.
const float kWorstDictCertainty = -25.0f;

// Generates training data for training a line recognizer, eg LSTM.
// Breaks the page into lines, according to the boxes, and writes them to a
// serialized DocumentData based on output_basename.
// Return true if successful, false if an error occurred.
bool Tesseract::TrainLineRecognizer(const STRING& input_imagename,
                                    const STRING& output_basename,
                                    BLOCK_LIST *block_list) {
  STRING lstmf_name = output_basename + ".lstmf";
  DocumentData images(lstmf_name);
  if (applybox_page > 0) {
    // Load existing document for the previous pages.
    if (!images.LoadDocument(lstmf_name.c_str(), 0, 0, nullptr)) {
      tprintf("Failed to read training data from %s!\n", lstmf_name.c_str());
      return false;
    }
  }
  GenericVector<TBOX> boxes;
  GenericVector<STRING> texts;
  // Get the boxes for this page, if there are any.
  if (!ReadAllBoxes(applybox_page, false, input_imagename, &boxes, &texts, nullptr,
                    nullptr) ||
      boxes.empty()) {
    tprintf("Failed to read boxes from %s\n", input_imagename.c_str());
    return false;
  }
  TrainFromBoxes(boxes, texts, block_list, &images);
  if (images.PagesSize() == 0) {
    tprintf("Failed to read pages from %s\n", input_imagename.c_str());
    return false;
  }
  images.Shuffle();
  if (!images.SaveDocument(lstmf_name.c_str(), nullptr)) {
    tprintf("Failed to write training data to %s!\n", lstmf_name.c_str());
    return false;
  }
  return true;
}

// Generates training data for training a line recognizer, eg LSTM.
// Breaks the boxes into lines, normalizes them, converts to ImageData and
// appends them to the given training_data.
void Tesseract::TrainFromBoxes(const GenericVector<TBOX>& boxes,
                               const GenericVector<STRING>& texts,
                               BLOCK_LIST *block_list,
                               DocumentData* training_data) {
  int box_count = boxes.size();
  // Process all the text lines in this page, as defined by the boxes.
  int end_box = 0;
  // Don't let \t, which marks newlines in the box file, get into the line
  // content, as that makes the line unusable in training.
  while (end_box < texts.size() && texts[end_box] == "\t") ++end_box;
  for (int start_box = end_box; start_box < box_count; start_box = end_box) {
    // Find the textline of boxes starting at start and their bounding box.
    TBOX line_box = boxes[start_box];
    STRING line_str = texts[start_box];
    for (end_box = start_box + 1; end_box < box_count && texts[end_box] != "\t";
         ++end_box) {
      line_box += boxes[end_box];
      line_str += texts[end_box];
    }
    // Find the most overlapping block.
    BLOCK* best_block = nullptr;
    int best_overlap = 0;
    BLOCK_IT b_it(block_list);
    for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) {
      BLOCK* block = b_it.data();
      if (block->pdblk.poly_block() != nullptr && !block->pdblk.poly_block()->IsText())
        continue;  // Not a text block.
      TBOX block_box = block->pdblk.bounding_box();
      block_box.rotate(block->re_rotation());
      if (block_box.major_overlap(line_box)) {
        TBOX overlap_box = line_box.intersection(block_box);
        if (overlap_box.area() > best_overlap) {
          best_overlap = overlap_box.area();
          best_block = block;
        }
      }
    }
    ImageData* imagedata = nullptr;
    if (best_block == nullptr) {
      tprintf("No block overlapping textline: %s\n", line_str.c_str());
    } else {
      imagedata = GetLineData(line_box, boxes, texts, start_box, end_box,
                              *best_block);
    }
    if (imagedata != nullptr)
      training_data->AddPageToDocument(imagedata);
    // Don't let \t, which marks newlines in the box file, get into the line
    // content, as that makes the line unusable in training.
    while (end_box < texts.size() && texts[end_box] == "\t") ++end_box;
  }
}

// Returns an Imagedata containing the image of the given box,
// and ground truth boxes/truth text if available in the input.
// The image is not normalized in any way.
ImageData* Tesseract::GetLineData(const TBOX& line_box,
                                  const GenericVector<TBOX>& boxes,
                                  const GenericVector<STRING>& texts,
                                  int start_box, int end_box,
                                  const BLOCK& block) {
  TBOX revised_box;
  ImageData* image_data = GetRectImage(line_box, block, kImagePadding,
                                       &revised_box);
  if (image_data == nullptr) return nullptr;
  image_data->set_page_number(applybox_page);
  // Copy the boxes and shift them so they are relative to the image.
  FCOORD block_rotation(block.re_rotation().x(), -block.re_rotation().y());
  ICOORD shift = -revised_box.botleft();
  GenericVector<TBOX> line_boxes;
  GenericVector<STRING> line_texts;
  for (int b = start_box; b < end_box; ++b) {
    TBOX box = boxes[b];
    box.rotate(block_rotation);
    box.move(shift);
    line_boxes.push_back(box);
    line_texts.push_back(texts[b]);
  }
  GenericVector<int> page_numbers;
  page_numbers.init_to_size(line_boxes.size(), applybox_page);
  image_data->AddBoxes(line_boxes, line_texts, page_numbers);
  return image_data;
}

// Helper gets the image of a rectangle, using the block.re_rotation() if
// needed to get to the image, and rotating the result back to horizontal
// layout. (CJK characters will be on their left sides) The vertical text flag
// is set in the returned ImageData if the text was originally vertical, which
// can be used to invoke a different CJK recognition engine. The revised_box
// is also returned to enable calculation of output bounding boxes.
ImageData* Tesseract::GetRectImage(const TBOX& box, const BLOCK& block,
                                   int padding, TBOX* revised_box) const {
  TBOX wbox = box;
  wbox.pad(padding, padding);
  *revised_box = wbox;
  // Number of clockwise 90 degree rotations needed to get back to tesseract
  // coords from the clipped image.
  int num_rotations = 0;
  if (block.re_rotation().y() > 0.0f)
    num_rotations = 1;
  else if (block.re_rotation().x() < 0.0f)
    num_rotations = 2;
  else if (block.re_rotation().y() < 0.0f)
    num_rotations = 3;
  // Handle two cases automatically: 1 the box came from the block, 2 the box
  // came from a box file, and refers to the image, which the block may not.
  if (block.pdblk.bounding_box().major_overlap(*revised_box))
    revised_box->rotate(block.re_rotation());
  // Now revised_box always refers to the image.
  // BestPix is never colormapped, but may be of any depth.
  Pix* pix = BestPix();
  int width = pixGetWidth(pix);
  int height = pixGetHeight(pix);
  TBOX image_box(0, 0, width, height);
  // Clip to image bounds;
  *revised_box &= image_box;
  if (revised_box->null_box()) return nullptr;
  Box* clip_box = boxCreate(revised_box->left(), height - revised_box->top(),
                            revised_box->width(), revised_box->height());
  Pix* box_pix = pixClipRectangle(pix, clip_box, nullptr);
  if (box_pix == nullptr) return nullptr;
  boxDestroy(&clip_box);
  if (num_rotations > 0) {
    Pix* rot_pix = pixRotateOrth(box_pix, num_rotations);
    pixDestroy(&box_pix);
    box_pix = rot_pix;
  }
  // Convert sub-8-bit images to 8 bit.
  int depth = pixGetDepth(box_pix);
  if (depth < 8) {
    Pix* grey;
    grey = pixConvertTo8(box_pix, false);
    pixDestroy(&box_pix);
    box_pix = grey;
  }
  bool vertical_text = false;
  if (num_rotations > 0) {
    // Rotated the clipped revised box back to internal coordinates.
    FCOORD rotation(block.re_rotation().x(), -block.re_rotation().y());
    revised_box->rotate(rotation);
    if (num_rotations != 2)
      vertical_text = true;
  }
  return new ImageData(vertical_text, box_pix);
}

#ifndef ANDROID_BUILD
// Recognizes a word or group of words, converting to WERD_RES in *words.
// Analogous to classify_word_pass1, but can handle a group of words as well.
void Tesseract::LSTMRecognizeWord(const BLOCK& block, ROW *row, WERD_RES *word,
                                  PointerVector<WERD_RES>* words) {
  TBOX word_box = word->word->bounding_box();
  // Get the word image - no frills.
  if (tessedit_pageseg_mode == PSM_SINGLE_WORD ||
      tessedit_pageseg_mode == PSM_RAW_LINE) {
    // In single word mode, use the whole image without any other row/word
    // interpretation.
    word_box = TBOX(0, 0, ImageWidth(), ImageHeight());
  } else {
    float baseline = row->base_line((word_box.left() + word_box.right()) / 2);
    if (baseline + row->descenders() < word_box.bottom())
      word_box.set_bottom(baseline + row->descenders());
    if (baseline + row->x_height() + row->ascenders() > word_box.top())
      word_box.set_top(baseline + row->x_height() + row->ascenders());
  }
  ImageData* im_data = GetRectImage(word_box, block, kImagePadding, &word_box);
  if (im_data == nullptr) return;

  bool do_invert = tessedit_do_invert;
  lstm_recognizer_->RecognizeLine(*im_data, do_invert, classify_debug_level > 0,
                                  kWorstDictCertainty / kCertaintyScale,
                                  word_box, words, lstm_choice_mode,
                                  lstm_choice_iterations);
  delete im_data;
  SearchWords(words);
}

// Apply segmentation search to the given set of words, within the constraints
// of the existing ratings matrix. If there is already a best_choice on a word
// leaves it untouched and just sets the done/accepted etc flags.
void Tesseract::SearchWords(PointerVector<WERD_RES>* words) {
  // Run the segmentation search on the network outputs and make a BoxWord
  // for each of the output words.
  // If we drop a word as junk, then there is always a space in front of the
  // next.
  const Dict* stopper_dict = lstm_recognizer_->GetDict();
  if (stopper_dict == nullptr) stopper_dict = &getDict();
  bool any_nonspace_delimited = false;
  for (int w = 0; w < words->size(); ++w) {
    WERD_RES* word = (*words)[w];
    if (word->best_choice != nullptr &&
        word->best_choice->ContainsAnyNonSpaceDelimited()) {
      any_nonspace_delimited = true;
      break;
    }
  }
  for (int w = 0; w < words->size(); ++w) {
    WERD_RES* word = (*words)[w];
    if (word->best_choice == nullptr) {
      // It is a dud.
      word->SetupFake(lstm_recognizer_->GetUnicharset());
    } else {
      // Set the best state.
      for (int i = 0; i < word->best_choice->length(); ++i) {
        int length = word->best_choice->state(i);
        word->best_state.push_back(length);
      }
      word->reject_map.initialise(word->best_choice->length());
      word->tess_failed = false;
      word->tess_accepted = true;
      word->tess_would_adapt = false;
      word->done = true;
      word->tesseract = this;
      float word_certainty = std::min(word->space_certainty,
                                 word->best_choice->certainty());
      word_certainty *= kCertaintyScale;
      if (getDict().stopper_debug_level >= 1) {
        tprintf("Best choice certainty=%g, space=%g, scaled=%g, final=%g\n",
                word->best_choice->certainty(), word->space_certainty,
                std::min(word->space_certainty, word->best_choice->certainty()) *
                    kCertaintyScale,
                word_certainty);
        word->best_choice->print();
      }
      word->best_choice->set_certainty(word_certainty);

      word->tess_accepted = stopper_dict->AcceptableResult(word);
    }
  }
}
#endif  // ANDROID_BUILD

}  // namespace tesseract.
<commit_msg>Destroy box before potentially exiting function<commit_after>///////////////////////////////////////////////////////////////////////
// File:        linerec.cpp
// Description: Top-level line-based recognition module for Tesseract.
// Author:      Ray Smith
//
// (C) Copyright 2013, Google Inc.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///////////////////////////////////////////////////////////////////////

#include "tesseractclass.h"

#include "allheaders.h"
#include "boxread.h"
#include "imagedata.h"
#ifndef ANDROID_BUILD
#include "lstmrecognizer.h"
#include "recodebeam.h"
#endif
#include "pageres.h"
#include "tprintf.h"

#include <algorithm>

namespace tesseract {

// Scale factor to make certainty more comparable to Tesseract.
const float kCertaintyScale = 7.0f;
// Worst acceptable certainty for a dictionary word.
const float kWorstDictCertainty = -25.0f;

// Generates training data for training a line recognizer, eg LSTM.
// Breaks the page into lines, according to the boxes, and writes them to a
// serialized DocumentData based on output_basename.
// Return true if successful, false if an error occurred.
bool Tesseract::TrainLineRecognizer(const STRING& input_imagename,
                                    const STRING& output_basename,
                                    BLOCK_LIST *block_list) {
  STRING lstmf_name = output_basename + ".lstmf";
  DocumentData images(lstmf_name);
  if (applybox_page > 0) {
    // Load existing document for the previous pages.
    if (!images.LoadDocument(lstmf_name.c_str(), 0, 0, nullptr)) {
      tprintf("Failed to read training data from %s!\n", lstmf_name.c_str());
      return false;
    }
  }
  GenericVector<TBOX> boxes;
  GenericVector<STRING> texts;
  // Get the boxes for this page, if there are any.
  if (!ReadAllBoxes(applybox_page, false, input_imagename, &boxes, &texts, nullptr,
                    nullptr) ||
      boxes.empty()) {
    tprintf("Failed to read boxes from %s\n", input_imagename.c_str());
    return false;
  }
  TrainFromBoxes(boxes, texts, block_list, &images);
  if (images.PagesSize() == 0) {
    tprintf("Failed to read pages from %s\n", input_imagename.c_str());
    return false;
  }
  images.Shuffle();
  if (!images.SaveDocument(lstmf_name.c_str(), nullptr)) {
    tprintf("Failed to write training data to %s!\n", lstmf_name.c_str());
    return false;
  }
  return true;
}

// Generates training data for training a line recognizer, eg LSTM.
// Breaks the boxes into lines, normalizes them, converts to ImageData and
// appends them to the given training_data.
void Tesseract::TrainFromBoxes(const GenericVector<TBOX>& boxes,
                               const GenericVector<STRING>& texts,
                               BLOCK_LIST *block_list,
                               DocumentData* training_data) {
  int box_count = boxes.size();
  // Process all the text lines in this page, as defined by the boxes.
  int end_box = 0;
  // Don't let \t, which marks newlines in the box file, get into the line
  // content, as that makes the line unusable in training.
  while (end_box < texts.size() && texts[end_box] == "\t") ++end_box;
  for (int start_box = end_box; start_box < box_count; start_box = end_box) {
    // Find the textline of boxes starting at start and their bounding box.
    TBOX line_box = boxes[start_box];
    STRING line_str = texts[start_box];
    for (end_box = start_box + 1; end_box < box_count && texts[end_box] != "\t";
         ++end_box) {
      line_box += boxes[end_box];
      line_str += texts[end_box];
    }
    // Find the most overlapping block.
    BLOCK* best_block = nullptr;
    int best_overlap = 0;
    BLOCK_IT b_it(block_list);
    for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) {
      BLOCK* block = b_it.data();
      if (block->pdblk.poly_block() != nullptr && !block->pdblk.poly_block()->IsText())
        continue;  // Not a text block.
      TBOX block_box = block->pdblk.bounding_box();
      block_box.rotate(block->re_rotation());
      if (block_box.major_overlap(line_box)) {
        TBOX overlap_box = line_box.intersection(block_box);
        if (overlap_box.area() > best_overlap) {
          best_overlap = overlap_box.area();
          best_block = block;
        }
      }
    }
    ImageData* imagedata = nullptr;
    if (best_block == nullptr) {
      tprintf("No block overlapping textline: %s\n", line_str.c_str());
    } else {
      imagedata = GetLineData(line_box, boxes, texts, start_box, end_box,
                              *best_block);
    }
    if (imagedata != nullptr)
      training_data->AddPageToDocument(imagedata);
    // Don't let \t, which marks newlines in the box file, get into the line
    // content, as that makes the line unusable in training.
    while (end_box < texts.size() && texts[end_box] == "\t") ++end_box;
  }
}

// Returns an Imagedata containing the image of the given box,
// and ground truth boxes/truth text if available in the input.
// The image is not normalized in any way.
ImageData* Tesseract::GetLineData(const TBOX& line_box,
                                  const GenericVector<TBOX>& boxes,
                                  const GenericVector<STRING>& texts,
                                  int start_box, int end_box,
                                  const BLOCK& block) {
  TBOX revised_box;
  ImageData* image_data = GetRectImage(line_box, block, kImagePadding,
                                       &revised_box);
  if (image_data == nullptr) return nullptr;
  image_data->set_page_number(applybox_page);
  // Copy the boxes and shift them so they are relative to the image.
  FCOORD block_rotation(block.re_rotation().x(), -block.re_rotation().y());
  ICOORD shift = -revised_box.botleft();
  GenericVector<TBOX> line_boxes;
  GenericVector<STRING> line_texts;
  for (int b = start_box; b < end_box; ++b) {
    TBOX box = boxes[b];
    box.rotate(block_rotation);
    box.move(shift);
    line_boxes.push_back(box);
    line_texts.push_back(texts[b]);
  }
  GenericVector<int> page_numbers;
  page_numbers.init_to_size(line_boxes.size(), applybox_page);
  image_data->AddBoxes(line_boxes, line_texts, page_numbers);
  return image_data;
}

// Helper gets the image of a rectangle, using the block.re_rotation() if
// needed to get to the image, and rotating the result back to horizontal
// layout. (CJK characters will be on their left sides) The vertical text flag
// is set in the returned ImageData if the text was originally vertical, which
// can be used to invoke a different CJK recognition engine. The revised_box
// is also returned to enable calculation of output bounding boxes.
ImageData* Tesseract::GetRectImage(const TBOX& box, const BLOCK& block,
                                   int padding, TBOX* revised_box) const {
  TBOX wbox = box;
  wbox.pad(padding, padding);
  *revised_box = wbox;
  // Number of clockwise 90 degree rotations needed to get back to tesseract
  // coords from the clipped image.
  int num_rotations = 0;
  if (block.re_rotation().y() > 0.0f)
    num_rotations = 1;
  else if (block.re_rotation().x() < 0.0f)
    num_rotations = 2;
  else if (block.re_rotation().y() < 0.0f)
    num_rotations = 3;
  // Handle two cases automatically: 1 the box came from the block, 2 the box
  // came from a box file, and refers to the image, which the block may not.
  if (block.pdblk.bounding_box().major_overlap(*revised_box))
    revised_box->rotate(block.re_rotation());
  // Now revised_box always refers to the image.
  // BestPix is never colormapped, but may be of any depth.
  Pix* pix = BestPix();
  int width = pixGetWidth(pix);
  int height = pixGetHeight(pix);
  TBOX image_box(0, 0, width, height);
  // Clip to image bounds;
  *revised_box &= image_box;
  if (revised_box->null_box()) return nullptr;
  Box* clip_box = boxCreate(revised_box->left(), height - revised_box->top(),
                            revised_box->width(), revised_box->height());
  Pix* box_pix = pixClipRectangle(pix, clip_box, nullptr);
  boxDestroy(&clip_box);
  if (box_pix == nullptr) return nullptr;
  if (num_rotations > 0) {
    Pix* rot_pix = pixRotateOrth(box_pix, num_rotations);
    pixDestroy(&box_pix);
    box_pix = rot_pix;
  }
  // Convert sub-8-bit images to 8 bit.
  int depth = pixGetDepth(box_pix);
  if (depth < 8) {
    Pix* grey;
    grey = pixConvertTo8(box_pix, false);
    pixDestroy(&box_pix);
    box_pix = grey;
  }
  bool vertical_text = false;
  if (num_rotations > 0) {
    // Rotated the clipped revised box back to internal coordinates.
    FCOORD rotation(block.re_rotation().x(), -block.re_rotation().y());
    revised_box->rotate(rotation);
    if (num_rotations != 2)
      vertical_text = true;
  }
  return new ImageData(vertical_text, box_pix);
}

#ifndef ANDROID_BUILD
// Recognizes a word or group of words, converting to WERD_RES in *words.
// Analogous to classify_word_pass1, but can handle a group of words as well.
void Tesseract::LSTMRecognizeWord(const BLOCK& block, ROW *row, WERD_RES *word,
                                  PointerVector<WERD_RES>* words) {
  TBOX word_box = word->word->bounding_box();
  // Get the word image - no frills.
  if (tessedit_pageseg_mode == PSM_SINGLE_WORD ||
      tessedit_pageseg_mode == PSM_RAW_LINE) {
    // In single word mode, use the whole image without any other row/word
    // interpretation.
    word_box = TBOX(0, 0, ImageWidth(), ImageHeight());
  } else {
    float baseline = row->base_line((word_box.left() + word_box.right()) / 2);
    if (baseline + row->descenders() < word_box.bottom())
      word_box.set_bottom(baseline + row->descenders());
    if (baseline + row->x_height() + row->ascenders() > word_box.top())
      word_box.set_top(baseline + row->x_height() + row->ascenders());
  }
  ImageData* im_data = GetRectImage(word_box, block, kImagePadding, &word_box);
  if (im_data == nullptr) return;

  bool do_invert = tessedit_do_invert;
  lstm_recognizer_->RecognizeLine(*im_data, do_invert, classify_debug_level > 0,
                                  kWorstDictCertainty / kCertaintyScale,
                                  word_box, words, lstm_choice_mode,
                                  lstm_choice_iterations);
  delete im_data;
  SearchWords(words);
}

// Apply segmentation search to the given set of words, within the constraints
// of the existing ratings matrix. If there is already a best_choice on a word
// leaves it untouched and just sets the done/accepted etc flags.
void Tesseract::SearchWords(PointerVector<WERD_RES>* words) {
  // Run the segmentation search on the network outputs and make a BoxWord
  // for each of the output words.
  // If we drop a word as junk, then there is always a space in front of the
  // next.
  const Dict* stopper_dict = lstm_recognizer_->GetDict();
  if (stopper_dict == nullptr) stopper_dict = &getDict();
  bool any_nonspace_delimited = false;
  for (int w = 0; w < words->size(); ++w) {
    WERD_RES* word = (*words)[w];
    if (word->best_choice != nullptr &&
        word->best_choice->ContainsAnyNonSpaceDelimited()) {
      any_nonspace_delimited = true;
      break;
    }
  }
  for (int w = 0; w < words->size(); ++w) {
    WERD_RES* word = (*words)[w];
    if (word->best_choice == nullptr) {
      // It is a dud.
      word->SetupFake(lstm_recognizer_->GetUnicharset());
    } else {
      // Set the best state.
      for (int i = 0; i < word->best_choice->length(); ++i) {
        int length = word->best_choice->state(i);
        word->best_state.push_back(length);
      }
      word->reject_map.initialise(word->best_choice->length());
      word->tess_failed = false;
      word->tess_accepted = true;
      word->tess_would_adapt = false;
      word->done = true;
      word->tesseract = this;
      float word_certainty = std::min(word->space_certainty,
                                 word->best_choice->certainty());
      word_certainty *= kCertaintyScale;
      if (getDict().stopper_debug_level >= 1) {
        tprintf("Best choice certainty=%g, space=%g, scaled=%g, final=%g\n",
                word->best_choice->certainty(), word->space_certainty,
                std::min(word->space_certainty, word->best_choice->certainty()) *
                    kCertaintyScale,
                word_certainty);
        word->best_choice->print();
      }
      word->best_choice->set_certainty(word_certainty);

      word->tess_accepted = stopper_dict->AcceptableResult(word);
    }
  }
}
#endif  // ANDROID_BUILD

}  // namespace tesseract.
<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/debug/profiler.h"

#include <string>

#include "base/process_util.h"
#include "base/string_util.h"

#if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC)
#include "third_party/tcmalloc/chromium/src/google/profiler.h"
#endif

namespace base {
namespace debug {

#if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC)

static int profile_count = 0;

void StartProfiling(const std::string& name) {
  ++profile_count;
  std::string full_name(name);
  std::string pid = StringPrintf("%d", GetCurrentProcId());
  std::string count = StringPrintf("%d", profile_count);
  ReplaceSubstringsAfterOffset(&full_name, 0, "{pid}", pid);
  ReplaceSubstringsAfterOffset(&full_name, 0, "{count}", count);
  ProfilerStart(full_name.c_str());
}

void StopProfiling() {
  ProfilerFlush();
  ProfilerStop();
}

void FlushProfiling() {
  ProfilerFlush();
}

bool BeingProfiled() {
  return ProfilingIsEnabledForAllThreads();
}

#else

void StartProfiling(const std::string& name) {
}

void StopProfiling() {
}

void FlushProfiling() {
}

bool BeingProfiled() {
  return false;
}

#endif

}  // namespace debug
}  // namespace base

<commit_msg>Include base/stringprintf.h in base/debug/profiler.cc.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/debug/profiler.h"

#include <string>

#include "base/process_util.h"
#include "base/string_util.h"
#include "base/stringprintf.h"

#if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC)
#include "third_party/tcmalloc/chromium/src/google/profiler.h"
#endif

namespace base {
namespace debug {

#if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC)

static int profile_count = 0;

void StartProfiling(const std::string& name) {
  ++profile_count;
  std::string full_name(name);
  std::string pid = StringPrintf("%d", GetCurrentProcId());
  std::string count = StringPrintf("%d", profile_count);
  ReplaceSubstringsAfterOffset(&full_name, 0, "{pid}", pid);
  ReplaceSubstringsAfterOffset(&full_name, 0, "{count}", count);
  ProfilerStart(full_name.c_str());
}

void StopProfiling() {
  ProfilerFlush();
  ProfilerStop();
}

void FlushProfiling() {
  ProfilerFlush();
}

bool BeingProfiled() {
  return ProfilingIsEnabledForAllThreads();
}

#else

void StartProfiling(const std::string& name) {
}

void StopProfiling() {
}

void FlushProfiling() {
}

bool BeingProfiled() {
  return false;
}

#endif

}  // namespace debug
}  // namespace base
<|endoftext|>


<commit_before>// Copyright 2008, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//    * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//    * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//    * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "base/object_watcher.h"

#include "base/logging.h"

namespace base {

//-----------------------------------------------------------------------------

struct ObjectWatcher::Watch : public Task {
  ObjectWatcher* watcher;    // The associated ObjectWatcher instance
  HANDLE object;             // The object being watched
  HANDLE wait_object;        // Returned by RegisterWaitForSingleObject
  MessageLoop* origin_loop;  // Used to get back to the origin thread
  Delegate* delegate;        // Delegate to notify when signaled
  bool did_signal;           // DoneWaiting was called

  virtual void Run() {
    // The watcher may have already been torn down, in which case we need to
    // just get out of dodge.
    if (!watcher)
      return;

    DCHECK(did_signal);
    watcher->StopWatching();

    delegate->OnObjectSignaled(object);
  }
};

//-----------------------------------------------------------------------------

ObjectWatcher::ObjectWatcher() : watch_(NULL) {
}

ObjectWatcher::~ObjectWatcher() {
  StopWatching();
}

bool ObjectWatcher::StartWatching(HANDLE object, Delegate* delegate) {
  if (watch_) {
    NOTREACHED() << "Already watching an object";
    return false;
  }

  Watch* watch = new Watch;
  watch->watcher = this;
  watch->object = object;
  watch->origin_loop = MessageLoop::current();
  watch->delegate = delegate;
  watch->did_signal = false;

  // Since our job is to just notice when an object is signaled and report the
  // result back to this thread, we can just run on a Windows wait thread.
  DWORD wait_flags = WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE;

  if (!RegisterWaitForSingleObject(&watch->wait_object, object, DoneWaiting,
                                   watch, INFINITE, wait_flags)) {
    NOTREACHED() << "RegisterWaitForSingleObject failed: " << GetLastError();
    delete watch;
    return false;
  }

  watch_ = watch;

  // We need to know if the current message loop is going away so we can
  // prevent the wait thread from trying to access a dead message loop.
  MessageLoop::current()->AddDestructionObserver(this);
  return true;
}

bool ObjectWatcher::StopWatching() {
  if (!watch_)
    return false;

  // Make sure ObjectWatcher is used in a single-threaded fashion.
  DCHECK(watch_->origin_loop == MessageLoop::current());

  // If DoneWaiting is in progress, we wait for it to finish.  We know whether
  // DoneWaiting happened or not by inspecting the did_signal flag.
  if (!UnregisterWaitEx(watch_->wait_object, INVALID_HANDLE_VALUE)) {
    NOTREACHED() << "UnregisterWaitEx failed: " << GetLastError();
    return false;
  }

  // Make sure that we see any mutation to did_signal.  This should be a no-op
  // since we expect that UnregisterWaitEx resulted in a memory barrier, but
  // just to be sure, we're going to be explicit.
  MemoryBarrier();

  // If the watch has been posted, then we need to make sure it knows not to do
  // anything once it is run.
  watch_->watcher = NULL;

  // If DoneWaiting was called, then the watch would have been posted as a
  // task, and will therefore be deleted by the MessageLoop.  Otherwise, we
  // need to take care to delete it here.
  if (!watch_->did_signal)
    delete watch_;

  watch_ = NULL;

  MessageLoop::current()->RemoveDestructionObserver(this);
  return true;
}

// static
void CALLBACK ObjectWatcher::DoneWaiting(void* param, BOOLEAN timed_out) {
  DCHECK(!timed_out);

  Watch* watch = static_cast<Watch*>(param);

  // Record that we ran this function.
  watch->did_signal = true;

  // Make this run very soon on the target thread.  TODO(darin): This could
  // lead to starving other tasks on the origin thread.
  watch->set_priority(1000);

  // We rely on the locking in PostTask() to ensure that a memory barrier is
  // provided, which in turn ensures our change to did_signal can be observed
  // on the target thread.
  watch->origin_loop->PostTask(FROM_HERE, watch);
}

void ObjectWatcher::WillDestroyCurrentMessageLoop() {
  // Need to shutdown the watch so that we don't try to access the MessageLoop
  // after this point.
  StopWatching();
}

}  // namespace base
<commit_msg>rollback my experiment to see the effects<commit_after>// Copyright 2008, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//    * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//    * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//    * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "base/object_watcher.h"

#include "base/logging.h"

namespace base {

//-----------------------------------------------------------------------------

struct ObjectWatcher::Watch : public Task {
  ObjectWatcher* watcher;    // The associated ObjectWatcher instance
  HANDLE object;             // The object being watched
  HANDLE wait_object;        // Returned by RegisterWaitForSingleObject
  MessageLoop* origin_loop;  // Used to get back to the origin thread
  Delegate* delegate;        // Delegate to notify when signaled
  bool did_signal;           // DoneWaiting was called

  virtual void Run() {
    // The watcher may have already been torn down, in which case we need to
    // just get out of dodge.
    if (!watcher)
      return;

    DCHECK(did_signal);
    watcher->StopWatching();

    delegate->OnObjectSignaled(object);
  }
};

//-----------------------------------------------------------------------------

ObjectWatcher::ObjectWatcher() : watch_(NULL) {
}

ObjectWatcher::~ObjectWatcher() {
  StopWatching();
}

bool ObjectWatcher::StartWatching(HANDLE object, Delegate* delegate) {
  if (watch_) {
    NOTREACHED() << "Already watching an object";
    return false;
  }

  Watch* watch = new Watch;
  watch->watcher = this;
  watch->object = object;
  watch->origin_loop = MessageLoop::current();
  watch->delegate = delegate;
  watch->did_signal = false;

  // Since our job is to just notice when an object is signaled and report the
  // result back to this thread, we can just run on a Windows wait thread.
  DWORD wait_flags = WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE;

  if (!RegisterWaitForSingleObject(&watch->wait_object, object, DoneWaiting,
                                   watch, INFINITE, wait_flags)) {
    NOTREACHED() << "RegisterWaitForSingleObject failed: " << GetLastError();
    delete watch;
    return false;
  }

  watch_ = watch;

  // We need to know if the current message loop is going away so we can
  // prevent the wait thread from trying to access a dead message loop.
  MessageLoop::current()->AddDestructionObserver(this);
  return true;
}

bool ObjectWatcher::StopWatching() {
  if (!watch_)
    return false;

  // Make sure ObjectWatcher is used in a single-threaded fashion.
  DCHECK(watch_->origin_loop == MessageLoop::current());

  // If DoneWaiting is in progress, we wait for it to finish.  We know whether
  // DoneWaiting happened or not by inspecting the did_signal flag.
  if (!UnregisterWaitEx(watch_->wait_object, INVALID_HANDLE_VALUE)) {
    NOTREACHED() << "UnregisterWaitEx failed: " << GetLastError();
    return false;
  }

  // Make sure that we see any mutation to did_signal.  This should be a no-op
  // since we expect that UnregisterWaitEx resulted in a memory barrier, but
  // just to be sure, we're going to be explicit.
  MemoryBarrier();

  // If the watch has been posted, then we need to make sure it knows not to do
  // anything once it is run.
  watch_->watcher = NULL;

  // If DoneWaiting was called, then the watch would have been posted as a
  // task, and will therefore be deleted by the MessageLoop.  Otherwise, we
  // need to take care to delete it here.
  if (!watch_->did_signal)
    delete watch_;

  watch_ = NULL;

  MessageLoop::current()->RemoveDestructionObserver(this);
  return true;
}

// static
void CALLBACK ObjectWatcher::DoneWaiting(void* param, BOOLEAN timed_out) {
  DCHECK(!timed_out);

  Watch* watch = static_cast<Watch*>(param);

  // Record that we ran this function.
  watch->did_signal = true;

  // We rely on the locking in PostTask() to ensure that a memory barrier is
  // provided, which in turn ensures our change to did_signal can be observed
  // on the target thread.
  watch->origin_loop->PostTask(FROM_HERE, watch);
}

void ObjectWatcher::WillDestroyCurrentMessageLoop() {
  // Need to shutdown the watch so that we don't try to access the MessageLoop
  // after this point.
  StopWatching();
}

}  // namespace base
<|endoftext|>


<commit_before>#include "../runtime.hpp"
#include "../collector.hpp"
#include "../classes.hpp"

namespace Mirb
{
	void Object::generate_hash()
	{
		hash_value = (size_t)hash_number((size_t)this); // Note: The first two bits are constant
		this->hashed = true;
	}

	value_t Object::allocate(Class *instance_of)
	{
		return Collector::allocate<Object>(instance_of);
	}

	value_t Object::tap(value_t obj, value_t block)
	{
		OnStack<1> os(obj);

		yield_argv(block, 1, &obj);

		return obj;
	}

	value_t Object::rb_inspect(value_t obj)
	{
		return call(obj, "to_s");
	}

	value_t Object::to_s(value_t obj)
	{
		value_t c = class_of(obj);
		value_t name = get_var(c, context->syms.classname);

		if(name != value_nil)
		{
			auto classname = cast<String>(name);

			CharArray result = "#<" + classname->string + ":0x" + CharArray::hex((size_t)obj) + ">";

			return result.to_string();
		}
		else
		{
			CharArray result = "#<0x" + CharArray::hex((size_t)obj) + ">";

			return result.to_string();
		}
	}

	value_t Object::dummy()
	{
		return value_nil;
	}

	value_t Object::variadic_dummy(size_t)
	{
		return value_nil;
	}

	value_t Object::pattern(value_t)
	{
		return value_nil;
	}

	value_t Object::equal(value_t obj, value_t other)
	{
		return Value::from_bool(obj == other);
	}

	value_t Object::not_equal(value_t obj, value_t other)
	{
		value_t result = call_argv(obj, "==", 1, &other);

		return Value::from_bool(!Value::test(result));
	}

	value_t Object::not_match(value_t obj, value_t other)
	{
		value_t result = call_argv(obj, "=~", 1, &other);

		return Value::from_bool(!Value::test(result));
	}

	value_t Object::rb_not(value_t obj)
	{
		return Value::from_bool(!Value::test(obj));
	}

	value_t Object::instance_eval(value_t obj, String *string, value_t block)
	{
		if(!string)
		{
			Proc *proc = get_proc(block);

			return call_code(proc->block, obj, proc->name, context->frame->prev->scope, proc->scopes, block, 0, nullptr);
		}
		else
		{
			CharArray code = string->string.c_str();

			return eval(obj, Symbol::get("in eval"), context->frame->prev->scope, code.str_ref(), code.str_length(), "(eval)");
		}
	}

	value_t Object::klass(value_t obj)
	{
		return class_of(obj);
	}

	value_t Object::extend(value_t obj, size_t argc, value_t argv[])
	{
		OnStack<1> os(obj);

		for(size_t i = 0; i < argc; ++i)
		{
			call_argv(argv[i], "extend_object", 1, &obj);
			call_argv(argv[i], "extended", 1, &obj);
		}

		return obj;
	}

	value_t object_id(value_t obj)
	{
		return Fixnum::from_size_t(Value::hash(obj)); // TODO: Make a proper object_id
	}

	value_t Object::kind_of(value_t obj, Class *klass)
	{
		return Value::from_bool(Mirb::kind_of(klass, obj));
	}

	value_t Object::respond_to(value_t obj, Symbol *name)
	{
		return Value::from_bool(Mirb::respond_to(obj, name) != 0);
	}

	value_t nil()
	{
		return value_false;
	}

	template<Value::Type type> struct DupObject
	{
		typedef value_t Result;
		typedef typename Value::TypeClass<type>::Class Class;

		static value_t func(value_t value)
		{
			return Class::dup(static_cast<Class *>(value));
		}
	};

	value_t Object::rb_dup(value_t obj)
	{
		value_t result = Value::virtual_do<DupObject>(Value::type(obj), obj);

		OnStack<1> os(result);

		call_argv(result, "initialize_copy", 1, &obj);

		return result;
	}

	value_t Object::initialize_copy(value_t)
	{
		return value_nil;
	}

	value_t instance_variable_set(value_t self, Symbol *name, value_t value)
	{
		if(name->string.size() == 0 || name->string[0] != '@')
			raise(context->standard_error, "Invalid instance variable name");

		set_var(self, name, value);

		return value;
	}

	value_t instance_variable_get(value_t self, Symbol *name)
	{
		if(name->string.size() == 0 || name->string[0] != '@')
			raise(context->standard_error, "Invalid instance variable name");

		return get_var(self, name);
	}

	value_t rb_send(value_t self, Symbol *name, size_t argc, value_t argv[])
	{
		return call_argv(self, name, argc, argv);
	}

	void Object::initialize()
	{
		method<Arg::Self<Arg::Value>, Arg::Class<Symbol>, Arg::Value, &instance_variable_set>(context->object_class, "instance_variable_set");
		method<Arg::Self<Arg::Value>, Arg::Class<Symbol>, &instance_variable_get>(context->object_class, "instance_variable_get");

		method<Arg::Self<Arg::Value>, &object_id>(context->object_class, "object_id");
		method<Arg::Self<Arg::Value>, &object_id>(context->object_class, "__id__");
		method<Arg::Count, &variadic_dummy>(context->object_class, "initialize");
		context->inspect_method = method<Arg::Self<Arg::Value>, &rb_inspect>(context->object_class, "inspect");
		method<&nil>(context->object_class, "nil?");
		method<Arg::Self<Arg::Value>, &to_s>(context->object_class, "to_s");
		method<Arg::Self<Arg::Value>, &rb_dup>(context->object_class, "dup");
		method<Arg::Self<Arg::Value>, &klass>(context->object_class, "class");
		method<&dummy>(context->object_class, "freeze");
		method<Arg::Self<Arg::Value>, Arg::Class<Symbol>, &respond_to>(context->object_class, "respond_to?");
		method<Arg::Self<Arg::Value>, Arg::Class<Class>, &kind_of>(context->object_class, "is_a?");
		method<Arg::Self<Arg::Value>, Arg::Class<Class>, &kind_of>(context->object_class, "kind_of?");
		method<&dummy>(context->object_class, "frozen?");
		method<Arg::Value, &pattern>(context->object_class, "=~");
		method<Arg::Value, &initialize_copy>(context->object_class, "initialize_copy");
		method<Arg::Self<Arg::Value>, Arg::Count, Arg::Values, &extend>(context->object_class, "extend");
		method<Arg::Self<Arg::Value>, Arg::Block, &tap>(context->object_class, "tap");
		method<Arg::Self<Arg::Value>, Arg::Value, &equal>(context->object_class, "equal?");
		method<Arg::Self<Arg::Value>, Arg::Value, &equal>(context->object_class, "eql?");
		method<Arg::Self<Arg::Value>, Arg::Value, &equal>(context->object_class, "==");
		method<Arg::Self<Arg::Value>, Arg::Value, &equal>(context->object_class, "===");
		method<Arg::Self<Arg::Value>, Arg::Value, &not_equal>(context->object_class, "!=");
		method<Arg::Self<Arg::Value>, Arg::Value, &not_match>(context->object_class, "!~");
		method<Arg::Self<Arg::Value>, &rb_not>(context->object_class, "!");
		method<Arg::Self<Arg::Value>, Arg::Optional<Arg::Class<String>>, Arg::Block, &instance_eval>(context->object_class, "instance_eval");
		method<Arg::Self<Arg::Value>, Arg::Class<Symbol>, Arg::Count, Arg::Values, &rb_send>(context->object_class, "send");
		method<Arg::Self<Arg::Value>, Arg::Class<Symbol>, Arg::Count, Arg::Values, &rb_send>(context->object_class, "__send__");

		singleton_method<Arg::Self<Arg::Class<Class>>, &allocate>(context->object_class, "allocate");
	}
};

<commit_msg>Added Object#clone.<commit_after>#include "../runtime.hpp"
#include "../collector.hpp"
#include "../classes.hpp"

namespace Mirb
{
	void Object::generate_hash()
	{
		hash_value = (size_t)hash_number((size_t)this); // Note: The first two bits are constant
		this->hashed = true;
	}

	value_t Object::allocate(Class *instance_of)
	{
		return Collector::allocate<Object>(instance_of);
	}

	value_t Object::tap(value_t obj, value_t block)
	{
		OnStack<1> os(obj);

		yield_argv(block, 1, &obj);

		return obj;
	}

	value_t Object::rb_inspect(value_t obj)
	{
		return call(obj, "to_s");
	}

	value_t Object::to_s(value_t obj)
	{
		value_t c = class_of(obj);
		value_t name = get_var(c, context->syms.classname);

		if(name != value_nil)
		{
			auto classname = cast<String>(name);

			CharArray result = "#<" + classname->string + ":0x" + CharArray::hex((size_t)obj) + ">";

			return result.to_string();
		}
		else
		{
			CharArray result = "#<0x" + CharArray::hex((size_t)obj) + ">";

			return result.to_string();
		}
	}

	value_t Object::dummy()
	{
		return value_nil;
	}

	value_t Object::variadic_dummy(size_t)
	{
		return value_nil;
	}

	value_t Object::pattern(value_t)
	{
		return value_nil;
	}

	value_t Object::equal(value_t obj, value_t other)
	{
		return Value::from_bool(obj == other);
	}

	value_t Object::not_equal(value_t obj, value_t other)
	{
		value_t result = call_argv(obj, "==", 1, &other);

		return Value::from_bool(!Value::test(result));
	}

	value_t Object::not_match(value_t obj, value_t other)
	{
		value_t result = call_argv(obj, "=~", 1, &other);

		return Value::from_bool(!Value::test(result));
	}

	value_t Object::rb_not(value_t obj)
	{
		return Value::from_bool(!Value::test(obj));
	}

	value_t Object::instance_eval(value_t obj, String *string, value_t block)
	{
		if(!string)
		{
			Proc *proc = get_proc(block);

			return call_code(proc->block, obj, proc->name, context->frame->prev->scope, proc->scopes, block, 0, nullptr);
		}
		else
		{
			CharArray code = string->string.c_str();

			return eval(obj, Symbol::get("in eval"), context->frame->prev->scope, code.str_ref(), code.str_length(), "(eval)");
		}
	}

	value_t Object::klass(value_t obj)
	{
		return class_of(obj);
	}

	value_t Object::extend(value_t obj, size_t argc, value_t argv[])
	{
		OnStack<1> os(obj);

		for(size_t i = 0; i < argc; ++i)
		{
			call_argv(argv[i], "extend_object", 1, &obj);
			call_argv(argv[i], "extended", 1, &obj);
		}

		return obj;
	}

	value_t object_id(value_t obj)
	{
		return Fixnum::from_size_t(Value::hash(obj)); // TODO: Make a proper object_id
	}

	value_t Object::kind_of(value_t obj, Class *klass)
	{
		return Value::from_bool(Mirb::kind_of(klass, obj));
	}

	value_t Object::respond_to(value_t obj, Symbol *name)
	{
		return Value::from_bool(Mirb::respond_to(obj, name) != 0);
	}

	value_t nil()
	{
		return value_false;
	}

	template<Value::Type type> struct DupObject
	{
		typedef value_t Result;
		typedef typename Value::TypeClass<type>::Class Class;

		static value_t func(value_t value)
		{
			return Class::dup(static_cast<Class *>(value));
		}
	};

	value_t Object::rb_dup(value_t obj)
	{
		value_t result = Value::virtual_do<DupObject>(Value::type(obj), obj);

		OnStack<1> os(result);

		call_argv(result, "initialize_copy", 1, &obj);

		return result;
	}

	value_t Object::initialize_copy(value_t)
	{
		return value_nil;
	}

	value_t instance_variable_set(value_t self, Symbol *name, value_t value)
	{
		if(name->string.size() == 0 || name->string[0] != '@')
			raise(context->standard_error, "Invalid instance variable name");

		set_var(self, name, value);

		return value;
	}

	value_t instance_variable_get(value_t self, Symbol *name)
	{
		if(name->string.size() == 0 || name->string[0] != '@')
			raise(context->standard_error, "Invalid instance variable name");

		return get_var(self, name);
	}

	value_t rb_send(value_t self, Symbol *name, size_t argc, value_t argv[])
	{
		return call_argv(self, name, argc, argv);
	}

	void Object::initialize()
	{
		method<Arg::Self<Arg::Value>, Arg::Class<Symbol>, Arg::Value, &instance_variable_set>(context->object_class, "instance_variable_set");
		method<Arg::Self<Arg::Value>, Arg::Class<Symbol>, &instance_variable_get>(context->object_class, "instance_variable_get");

		method<Arg::Self<Arg::Value>, &object_id>(context->object_class, "object_id");
		method<Arg::Self<Arg::Value>, &object_id>(context->object_class, "__id__");
		method<Arg::Count, &variadic_dummy>(context->object_class, "initialize");
		context->inspect_method = method<Arg::Self<Arg::Value>, &rb_inspect>(context->object_class, "inspect");
		method<&nil>(context->object_class, "nil?");
		method<Arg::Self<Arg::Value>, &to_s>(context->object_class, "to_s");
		method<Arg::Self<Arg::Value>, &rb_dup>(context->object_class, "dup");
		method<Arg::Self<Arg::Value>, &rb_dup>(context->object_class, "clone");
		method<Arg::Self<Arg::Value>, &klass>(context->object_class, "class");
		method<&dummy>(context->object_class, "freeze");
		method<Arg::Self<Arg::Value>, Arg::Class<Symbol>, &respond_to>(context->object_class, "respond_to?");
		method<Arg::Self<Arg::Value>, Arg::Class<Class>, &kind_of>(context->object_class, "is_a?");
		method<Arg::Self<Arg::Value>, Arg::Class<Class>, &kind_of>(context->object_class, "kind_of?");
		method<&dummy>(context->object_class, "frozen?");
		method<Arg::Value, &pattern>(context->object_class, "=~");
		method<Arg::Value, &initialize_copy>(context->object_class, "initialize_copy");
		method<Arg::Self<Arg::Value>, Arg::Count, Arg::Values, &extend>(context->object_class, "extend");
		method<Arg::Self<Arg::Value>, Arg::Block, &tap>(context->object_class, "tap");
		method<Arg::Self<Arg::Value>, Arg::Value, &equal>(context->object_class, "equal?");
		method<Arg::Self<Arg::Value>, Arg::Value, &equal>(context->object_class, "eql?");
		method<Arg::Self<Arg::Value>, Arg::Value, &equal>(context->object_class, "==");
		method<Arg::Self<Arg::Value>, Arg::Value, &equal>(context->object_class, "===");
		method<Arg::Self<Arg::Value>, Arg::Value, &not_equal>(context->object_class, "!=");
		method<Arg::Self<Arg::Value>, Arg::Value, &not_match>(context->object_class, "!~");
		method<Arg::Self<Arg::Value>, &rb_not>(context->object_class, "!");
		method<Arg::Self<Arg::Value>, Arg::Optional<Arg::Class<String>>, Arg::Block, &instance_eval>(context->object_class, "instance_eval");
		method<Arg::Self<Arg::Value>, Arg::Class<Symbol>, Arg::Count, Arg::Values, &rb_send>(context->object_class, "send");
		method<Arg::Self<Arg::Value>, Arg::Class<Symbol>, Arg::Count, Arg::Values, &rb_send>(context->object_class, "__send__");

		singleton_method<Arg::Self<Arg::Class<Class>>, &allocate>(context->object_class, "allocate");
	}
};

<|endoftext|>


<commit_before>// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation.  See file COPYING.
 *
 */

#include "common/errno.h"
#include "include/ceph/libceph.h"
#include <stdlib.h>

#include <errno.h>
#include <dirent.h>
#include <iostream>
#include <fcntl.h>
#include <sys/xattr.h>
#include <string.h>

using std::cout;
using std::cerr;

int main(int argc, const char **argv)
{
  struct ceph_mount_info *cmount;
    cout << "calling ceph_create..." << std::endl;
  int my_fd;
  int ret = ceph_create(&cmount, NULL);
  if (ret) {
    cerr << "ceph_create failed with error: " << ret << std::endl;
    return 1;
  }
    cout << "calling ceph_conf_read_file..." << ret << std::endl;
  ceph_conf_read_file(cmount, NULL);
    cout << "calling ceph_conf_parse_argv..." << ret << std::endl;
  ceph_conf_parse_argv(cmount, argc, argv);

  char buf[128];
    cout << "calling ceph_conf_get..." << ret << std::endl;
  ret = ceph_conf_get(cmount, "log file", buf, sizeof(buf));
  if (ret) {
    cerr << "ceph_conf_get(\"log file\") failed with error " << ret << std::endl;
  }
  else {
    cout << "log_file = \"" << buf << "\"" << std::endl;
  }

  ret = ceph_mount(cmount, NULL);
  if (ret) {
    cerr << "ceph_mount error: " << ret << std::endl;
    return 1;
  }
  cout << "Successfully mounted Ceph!" << std::endl;

  // what if foo is already there???
  struct ceph_dir_result *foo_dir = NULL;
  ret = ceph_opendir(cmount, "foo", &foo_dir);
  if (ret != -ENOENT) {
    cerr << "ceph_opendir error: unexpected result from trying to open foo: "
	 << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_opendir: success, foo does not exist" << std::endl;
  }

  // Can't close a dir that is not open!
  //ret = ceph_closedir(cmount, foo_dir);
  //if (ret == 0) {
  //  cerr << "ceph_closedir success" << std::endl;
  //} else {
  //  cerr << "ceph_closedir error: " << cpp_strerror(ret) << std::endl;
  //}
  //
  ret = ceph_mkdir(cmount, "foo",  0777);
  if (ret) {
    cerr << "ceph_mkdir error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_mkdir: success" << std::endl;
  }

  struct stat stbuf;
  ret = ceph_lstat(cmount, "foo", &stbuf);
  if (ret) {
    cerr << "ceph_lstat error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_lstat: success" << std::endl;
  }

  if (!S_ISDIR(stbuf.st_mode)) {
    cerr << "ceph_lstat(foo): foo is not a directory? st_mode = "
	 << stbuf.st_mode << std::endl;
    return 1;
  } else {
    cout << "ceph_lstat: mode success" << std::endl;
  }

  ret = ceph_rmdir(cmount, "foo");
  if (ret) {
    cerr << "ceph_rmdir error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_rmdir: success" << std::endl;
  }

  ret = ceph_mkdirs(cmount, "foo/bar/baz",  0777);
  if (ret) {
    cerr << "ceph_mkdirs error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_mkdirs: success" << std::endl;
  }

  // Should test .. as well!

  cout << "testing chdir on foo/bar/baz";
  ret = ceph_chdir(cmount, "foo/bar/baz");
  if (ret) {
    cerr << ": failed: " << cpp_strerror(ret) << std::endl;
    return 1;
  }

  cout << " success!" << std::endl;

  // Now, try absolute
  cout << "testing chdir to /foo/bar";
  ret = ceph_chdir(cmount, "/foo/bar");
  if (ret) {
    cerr << ": failed: " << cpp_strerror(ret) << std::endl;
    return 1;
  }

  cout << " success!" << std::endl;

  // Now, back to the top
  cout << "testing chdir to /";
  ret = ceph_chdir(cmount, "/");
  if (ret) {
    cerr << ": failed: " << cpp_strerror(ret) << std::endl;
    return 1;
  }

  cout << "success!" << std::endl;

  ret = ceph_rmdir(cmount, "foo/bar/baz");
  if (ret) {
    cerr << "ceph_rmdir error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_rmdir: success" << std::endl;
  }
  ret = ceph_rmdir(cmount, "foo/bar");
  if (ret) {
    cerr << "ceph_rmdir error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_rmdir: success" << std::endl;
  }
  ret = ceph_rmdir(cmount, "foo");
  if (ret) {
    cerr << "ceph_rmdir error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_rmdir: success" << std::endl;
  }
  my_fd = ret = ceph_open(cmount, "barfile", O_CREAT, 0666);
  if (ret < 0) {
    cerr << "ceph_open O_CREAT error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_open: success" << std::endl;
  }
  ret = ceph_setxattr(cmount, "barfile", "user.testxattr", (void *) "AYBABTU", 7, XATTR_CREATE);
  if (ret < 0) {
    cerr << "ceph_setxattr error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_setxattr: success" << std::endl;
  }
  char *outlist;
  outlist = (char *) malloc(256);
  ret = ceph_listxattr(cmount, "barfile", outlist, 0);
  free(outlist);
  if (ret < 1) {
    cerr << "ceph_listxattr error (a): should have returned > 0" << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_listxattr: success" << std::endl;
  }

  outlist = (char *) malloc(ret);
  ret = ceph_listxattr(cmount, "barfile", outlist, ret);
  if (ret < 1) {
    cerr << "ceph_listxattr error (b): should have returned > 0" << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_listxattr: success" << std::endl;
  }
  void *aybabtu;
  aybabtu = malloc(8);
  ret = ceph_getxattr(cmount, "barfile", "user.testxattr", aybabtu, 7);
  if (ret < 1) {
    cerr << "ceph_getxattr error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_getxattr: success" << std::endl;
  }
  char aybabtu_reference[]="AYBABTU";
  if (strncmp((char *) aybabtu, aybabtu_reference,7)) {
    cerr << "ceph_getxattr error: no match (" << aybabtu << ") should be (" << aybabtu_reference << cpp_strerror(ret) << std::endl;
  }
  ret = ceph_close(cmount,my_fd);
  if (ret < 0) {
    cerr << "ceph_close error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_close: success" << std::endl;
  }

  cout << "Attempting lstat on '/.'" << std::endl;
  ret = ceph_lstat(cmount, "/.", &stbuf);
  if (ret) {
    cerr << "ceph_lstat error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_lstat: success" << std::endl;
  }
  cout << "Attempting lstat on '.'" << std::endl;
  ret = ceph_lstat(cmount, ".", &stbuf);
  if (ret) {
    cerr << "ceph_lstat error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_lstat: success" << std::endl;
  }
  cout << "Setting up readdir test" << std::endl;
  ret = ceph_mkdir(cmount, "readdir_test",  0777);
  if (ret) {
    cerr << "ceph_mkdir error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_mkdir: success" << std::endl;
  }
  my_fd = ret = ceph_open(cmount, "readdir_test/opened_file_1", O_CREAT, 0666);
  if (ret < 0) {
    cerr << "ceph_open O_CREAT error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_open: success" << std::endl;
  }
  ret = ceph_close(cmount, my_fd);
  if (ret < 0) {
    cerr << "ceph_close error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_close: success" << std::endl;
  }

  struct ceph_dir_result *readdir_test_dir;
  ret = ceph_opendir(cmount, "readdir_test", &readdir_test_dir);
  if (ret != 0) {
    cerr << "ceph_opendir error: unexpected result from trying to open readdir_test: "
	 << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_opendir: success" << std::endl;
  }
  cout << "Attempting readdir on opened directory..." << std::endl;
  struct dirent * result;
  //result = (struct dirent *) malloc(sizeof(struct dirent));
  result = ceph_readdir(cmount, readdir_test_dir);
  if (result == (dirent *) NULL) {
    cout << "ceph_readdir: failed to read any entries" << std::endl;
  }
  loff_t telldir_result;
  while ( result != (dirent *) NULL) {
    cout << "ceph_readdir: dirent->d_name: (" << result->d_name << ")" << std::endl;
    cout << "ceph_telldir: starting" << std::endl;
    telldir_result = ceph_telldir(cmount, readdir_test_dir);
    if (telldir_result > -1) {
      cout << "ceph_telldir: offset: from return code:" << telldir_result << std::endl;
    } else {
      cout << "ceph_telldir: failed" << std::endl;
    }
    cout << "ceph_readdir: lookup success: trying for another..." << std::endl;
    result = ceph_readdir(cmount, readdir_test_dir);
  }
  cout << "ceph_readdir: finished" << std::endl;

  // tell us that we're at the end of the directory:
  cout << "ceph_telldir: starting" << std::endl;
  telldir_result = ceph_telldir(cmount, readdir_test_dir);
  if (telldir_result > -1) {
    cout << "ceph_telldir: offset: from return code:" << telldir_result << std::endl;
  } else {
    cout << "ceph_telldir: failed" << std::endl;
  }

  ret = ceph_closedir(cmount,readdir_test_dir);
  if (ret == 0) {
    cerr << "ceph_closedir success" << std::endl;
  } else {
    cerr << "ceph_closedir error: " << cpp_strerror(ret) << std::endl;
  }

  ceph_shutdown(cmount);

  return 0;
}
<commit_msg>testceph: test file opens with extra slashes<commit_after>// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation.  See file COPYING.
 *
 */

#include "common/errno.h"
#include "include/ceph/libceph.h"
#include <stdlib.h>

#include <errno.h>
#include <dirent.h>
#include <iostream>
#include <fcntl.h>
#include <sys/xattr.h>
#include <string.h>

using std::cout;
using std::cerr;

int main(int argc, const char **argv)
{
  struct ceph_mount_info *cmount;
    cout << "calling ceph_create..." << std::endl;
  int my_fd;
  int ret = ceph_create(&cmount, NULL);
  if (ret) {
    cerr << "ceph_create failed with error: " << ret << std::endl;
    return 1;
  }
    cout << "calling ceph_conf_read_file..." << ret << std::endl;
  ceph_conf_read_file(cmount, NULL);
    cout << "calling ceph_conf_parse_argv..." << ret << std::endl;
  ceph_conf_parse_argv(cmount, argc, argv);

  char buf[128];
    cout << "calling ceph_conf_get..." << ret << std::endl;
  ret = ceph_conf_get(cmount, "log file", buf, sizeof(buf));
  if (ret) {
    cerr << "ceph_conf_get(\"log file\") failed with error " << ret << std::endl;
  }
  else {
    cout << "log_file = \"" << buf << "\"" << std::endl;
  }

  ret = ceph_mount(cmount, NULL);
  if (ret) {
    cerr << "ceph_mount error: " << ret << std::endl;
    return 1;
  }
  cout << "Successfully mounted Ceph!" << std::endl;

  // what if foo is already there???
  struct ceph_dir_result *foo_dir = NULL;
  ret = ceph_opendir(cmount, "foo", &foo_dir);
  if (ret != -ENOENT) {
    cerr << "ceph_opendir error: unexpected result from trying to open foo: "
	 << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_opendir: success, foo does not exist" << std::endl;
  }

  // Can't close a dir that is not open!
  //ret = ceph_closedir(cmount, foo_dir);
  //if (ret == 0) {
  //  cerr << "ceph_closedir success" << std::endl;
  //} else {
  //  cerr << "ceph_closedir error: " << cpp_strerror(ret) << std::endl;
  //}
  //
  ret = ceph_mkdir(cmount, "foo",  0777);
  if (ret) {
    cerr << "ceph_mkdir error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_mkdir: success" << std::endl;
  }

  struct stat stbuf;
  ret = ceph_lstat(cmount, "foo", &stbuf);
  if (ret) {
    cerr << "ceph_lstat error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_lstat: success" << std::endl;
  }

  if (!S_ISDIR(stbuf.st_mode)) {
    cerr << "ceph_lstat(foo): foo is not a directory? st_mode = "
	 << stbuf.st_mode << std::endl;
    return 1;
  } else {
    cout << "ceph_lstat: mode success" << std::endl;
  }

  ret = ceph_rmdir(cmount, "foo");
  if (ret) {
    cerr << "ceph_rmdir error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_rmdir: success" << std::endl;
  }

  ret = ceph_mkdirs(cmount, "foo/bar/baz",  0777);
  if (ret) {
    cerr << "ceph_mkdirs error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_mkdirs: success" << std::endl;
  }

  // Should test .. as well!

  cout << "testing chdir on foo/bar/baz";
  ret = ceph_chdir(cmount, "foo/bar/baz");
  if (ret) {
    cerr << ": failed: " << cpp_strerror(ret) << std::endl;
    return 1;
  }

  cout << " success!" << std::endl;

  // Now, try absolute
  cout << "testing chdir to /foo/bar";
  ret = ceph_chdir(cmount, "/foo/bar");
  if (ret) {
    cerr << ": failed: " << cpp_strerror(ret) << std::endl;
    return 1;
  }

  cout << " success!" << std::endl;

  // Now, back to the top
  cout << "testing chdir to /";
  ret = ceph_chdir(cmount, "/");
  if (ret) {
    cerr << ": failed: " << cpp_strerror(ret) << std::endl;
    return 1;
  }

  cout << "success!" << std::endl;

  ret = ceph_rmdir(cmount, "foo/bar/baz");
  if (ret) {
    cerr << "ceph_rmdir error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_rmdir: success" << std::endl;
  }
  ret = ceph_rmdir(cmount, "foo/bar");
  if (ret) {
    cerr << "ceph_rmdir error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_rmdir: success" << std::endl;
  }
  ret = ceph_rmdir(cmount, "foo");
  if (ret) {
    cerr << "ceph_rmdir error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_rmdir: success" << std::endl;
  }
  my_fd = ret = ceph_open(cmount, "barfile", O_CREAT, 0666);
  if (ret < 0) {
    cerr << "ceph_open O_CREAT error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_open: success" << std::endl;
  }
  ret = ceph_setxattr(cmount, "barfile", "user.testxattr", (void *) "AYBABTU", 7, XATTR_CREATE);
  if (ret < 0) {
    cerr << "ceph_setxattr error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_setxattr: success" << std::endl;
  }
  char *outlist;
  outlist = (char *) malloc(256);
  ret = ceph_listxattr(cmount, "barfile", outlist, 0);
  free(outlist);
  if (ret < 1) {
    cerr << "ceph_listxattr error (a): should have returned > 0" << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_listxattr: success" << std::endl;
  }

  outlist = (char *) malloc(ret);
  ret = ceph_listxattr(cmount, "barfile", outlist, ret);
  if (ret < 1) {
    cerr << "ceph_listxattr error (b): should have returned > 0" << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_listxattr: success" << std::endl;
  }
  void *aybabtu;
  aybabtu = malloc(8);
  ret = ceph_getxattr(cmount, "barfile", "user.testxattr", aybabtu, 7);
  if (ret < 1) {
    cerr << "ceph_getxattr error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_getxattr: success" << std::endl;
  }
  char aybabtu_reference[]="AYBABTU";
  if (strncmp((char *) aybabtu, aybabtu_reference,7)) {
    cerr << "ceph_getxattr error: no match (" << aybabtu << ") should be (" << aybabtu_reference << cpp_strerror(ret) << std::endl;
  }
  ret = ceph_close(cmount,my_fd);
  if (ret < 0) {
    cerr << "ceph_close error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_close: success" << std::endl;
  }

  cout << "Attempting lstat on '/.'" << std::endl;
  ret = ceph_lstat(cmount, "/.", &stbuf);
  if (ret) {
    cerr << "ceph_lstat error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_lstat: success" << std::endl;
  }
  cout << "Attempting lstat on '.'" << std::endl;
  ret = ceph_lstat(cmount, ".", &stbuf);
  if (ret) {
    cerr << "ceph_lstat error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_lstat: success" << std::endl;
  }
  cout << "Setting up readdir test" << std::endl;
  ret = ceph_mkdir(cmount, "readdir_test",  0777);
  if (ret) {
    cerr << "ceph_mkdir error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_mkdir: success" << std::endl;
  }
  my_fd = ret = ceph_open(cmount, "readdir_test/opened_file_1", O_CREAT, 0666);
  if (ret < 0) {
    cerr << "ceph_open O_CREAT error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_open: success" << std::endl;
  }
  ret = ceph_close(cmount, my_fd);
  if (ret < 0) {
    cerr << "ceph_close error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_close: success" << std::endl;
  }

  // test empty name components
  my_fd = ret = ceph_open(cmount, "readdir_test//opened_file_1", O_RDONLY, 0666);
  if (ret < 0) {
    cerr << "ceph_open O_RDONLY error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_open: success" << std::endl;
  }
  ret = ceph_close(cmount, my_fd);
  if (ret < 0) {
    cerr << "ceph_close error: " << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_close: success" << std::endl;
  }

  struct ceph_dir_result *readdir_test_dir;
  ret = ceph_opendir(cmount, "readdir_test", &readdir_test_dir);
  if (ret != 0) {
    cerr << "ceph_opendir error: unexpected result from trying to open readdir_test: "
	 << cpp_strerror(ret) << std::endl;
    return 1;
  } else {
    cout << "ceph_opendir: success" << std::endl;
  }
  cout << "Attempting readdir on opened directory..." << std::endl;
  struct dirent * result;
  //result = (struct dirent *) malloc(sizeof(struct dirent));
  result = ceph_readdir(cmount, readdir_test_dir);
  if (result == (dirent *) NULL) {
    cout << "ceph_readdir: failed to read any entries" << std::endl;
  }
  loff_t telldir_result;
  while ( result != (dirent *) NULL) {
    cout << "ceph_readdir: dirent->d_name: (" << result->d_name << ")" << std::endl;
    cout << "ceph_telldir: starting" << std::endl;
    telldir_result = ceph_telldir(cmount, readdir_test_dir);
    if (telldir_result > -1) {
      cout << "ceph_telldir: offset: from return code:" << telldir_result << std::endl;
    } else {
      cout << "ceph_telldir: failed" << std::endl;
    }
    cout << "ceph_readdir: lookup success: trying for another..." << std::endl;
    result = ceph_readdir(cmount, readdir_test_dir);
  }
  cout << "ceph_readdir: finished" << std::endl;

  // tell us that we're at the end of the directory:
  cout << "ceph_telldir: starting" << std::endl;
  telldir_result = ceph_telldir(cmount, readdir_test_dir);
  if (telldir_result > -1) {
    cout << "ceph_telldir: offset: from return code:" << telldir_result << std::endl;
  } else {
    cout << "ceph_telldir: failed" << std::endl;
  }

  ret = ceph_closedir(cmount,readdir_test_dir);
  if (ret == 0) {
    cerr << "ceph_closedir success" << std::endl;
  } else {
    cerr << "ceph_closedir error: " << cpp_strerror(ret) << std::endl;
  }

  ceph_shutdown(cmount);

  return 0;
}
<|endoftext|>