たまたま見かけた記事でmecabのバグと書いてたので調べたメモ。
結論:2,621,440文字が限界
※1文字を2byteで単純計算した場合
これはcommon.hで定義されてる値、MAX_INPUT_BUFFER_SIZEを見ればわかる.
# define NBEST_MAX 512
# define NODE_FREELIST_SIZE 512
# define PATH_FREELIST_SIZE 2048
# define MIN_INPUT_BUFFER_SIZE 8192
# define MAX_INPUT_BUFFER_SIZE (8192*640)
# define BUF_SIZE 8192
8192×640byteのデータを最大で受け取りそうなことが分かる.つまり8192×640÷2で2,621,440文字.
以上.
※ソースはMeCab開発者の工藤拓さんがgithubに公開しているものを引用しています(参考)
おまけ
※これ以降はソースを見た所見のような何かです.
実際にMAX_INPUT_BUFFER_SIZEを使用している処理はtagger.cppのみだった(tagger.cpp:1213-1276行目抜粋).
size_t ibufsize = std::min(MAX_INPUT_BUFFER_SIZE,
std::max(param.get<int>
("input-buffer-size"),
MIN_INPUT_BUFFER_SIZE));
const bool partial = param.get<bool>("partial");
if (partial) {
ibufsize *= 8;
}
MeCab::scoped_array<char> ibuf_data(new char[ibufsize]);
char *ibuf = ibuf_data.get();
MeCab::scoped_ptr<MeCab::Tagger> tagger(model->createTagger());
if (!tagger.get()) {
WHAT_ERROR("cannot create tagger");
}
for (size_t i = 0; i < rest.size(); ++i) {
MeCab::istream_wrapper ifs(rest[i].c_str());
if (!*ifs) {
WHAT_ERROR("no such file or directory: " << rest[i]);
}
while (true) {
if (!partial) {
ifs->getline(ibuf, ibufsize);
} else {
std::string sentence;
MeCab::scoped_fixed_array<char, BUF_SIZE> line;
for (;;) {
if (!ifs->getline(line.get(), line.size())) {
ifs->clear(std::ios::eofbit|std::ios::badbit);
break;
}
sentence += line.get();
sentence += '\n';
if (std::strcmp(line.get(), "EOS") == 0 || line[0] == '\0') {
break;
}
}
std::strncpy(ibuf, sentence.c_str(), ibufsize);
}
if (ifs->eof() && !ibuf[0]) {
return false;
}
if (ifs->fail()) {
std::cerr << "input-buffer overflow. "
<< "The line is split. use -b #SIZE option." << std::endl;
ifs->clear();
}
const char *r = (nbest >= 2) ? tagger->parseNBest(nbest, ibuf) :
tagger->parse(ibuf);
if (!r) {
WHAT_ERROR(tagger->what());
}
*ofs << r << std::flush;
}
}
return EXIT_SUCCESS;
# undef WHAT_ERROR
このコードからMeCab.Tagger.parse()の処理では最大でもMAX_INPUT_BUFFER_SIZEを超えないことが分かる.
次にstring_buffer.hとtagger.cppから分かるラティスの解析について.(string_buffer.h:15-37行目抜粋)
bool StringBuffer::reserve(size_t length) {
if (!is_delete_) {
error_ = (size_ + length >= alloc_size_);
return (!error_);
}
if (size_ + length >= alloc_size_) {
if (alloc_size_ == 0) {
alloc_size_ = DEFAULT_ALLOC_SIZE;
ptr_ = new char[alloc_size_];
}
size_t len = size_ + length;
do {
alloc_size_ *= 2;
} while (len >= alloc_size_);
char *new_ptr = new char[alloc_size_];
std::memcpy(new_ptr, ptr_, size_);
delete [] ptr_;
ptr_ = new_ptr;
}
return true;
}
領域の取得を行っているこのreserveはtagger.cppでのみ呼ばれている.(tagger.cpp:733-741行目抜粋)
LatticeImpl::LatticeImpl(const Writer *writer)
: sentence_(0), size_(0), theta_(kDefaultTheta), Z_(0.0),
request_type_(MECAB_ONE_BEST),
writer_(writer),
ostrs_(0),
allocator_(new Allocator<Node, Path>) {
begin_nodes_.reserve(MIN_INPUT_BUFFER_SIZE);
end_nodes_.reserve(MIN_INPUT_BUFFER_SIZE);
}
そしてLatticeImplはラティスを実体化したときに実行されている(と思う).(tagger.cpp:227-239抜粋)
class LatticeImpl : public Lattice {
public:
explicit LatticeImpl(const Writer *writer = 0);
~LatticeImpl();
// clear internal lattice
void clear();
bool is_available() const {
return (sentence_ &&
!begin_nodes_.empty() &&
!end_nodes_.empty());
}
これらのことから、ラティスの解析ではメモリが足りなくなると領域を2倍してmemcpyで取得してるっぽいのでそっちは限界無さそうなことが分かる(勿論メモリを食い潰したら終わるはずだがその前にMAX_INPUT_BUFFER_SIZEで止まると思われる).
その他の参考
mecab.h:lattice等、構造体の定義 etc
libmecab.cpp:tagger.cppのmutable_lattice()とかを追跡してたらたどり着いたmecab_model_new_lattice()等、modelに紐づいた関数の定義 etc
END.