kuromojiにヌル文字<0x00>を渡すとエラーになる

# 現象

Qiitaの記事を日々textlintでチェックしていると、たま～に以下のようなエラーが出る。

```
TypeError: Cannot read property 'length' of undefined
    at ViterbiBuilder.build (~\node_modules\kuromoji\src\viterbi\ViterbiBuilder.js:53:48)
    at Tokenizer.getLattice (~\node_modules\kuromoji\src\Tokenizer.js:126:33)
    at Tokenizer.tokenizeForSentence (~\node_modules\kuromoji\src\Tokenizer.js:81:24)
    at ~\node_modules\kuromo
jin\lib\kuromojin.js:60:26
    at <anonymous>
```

catchしてスキップすればいいだけなので困ってはいないが、原因調査。

# 結論

タイトルどおり、Qiitaの記事に`0x00`(NUL)が含まれていたから。


# 動作検証環境

* Windows7 64bit
* node.js v9.3.0
* `textlint@10.2.1`
* `kuromoji@0.1.2`
* `text-encoding@0.6.4`

# 原因stringの確認

エラーが出る投稿を確認した。
端末への出力をコピペしたものでは再現しなかったので、
直接ファイルに書き出すことに。
ファイルを読み込んだstringをチェックすることでエラーが再現した
 → stringとtextlint以外の部分がエラーに関与している可能性がなくなった

ファイルを確認すると、通常、あまり目にしない内容が含まれていることが確認できた。

(画像は投稿内容を短く加工後)

![2018-07-20_11h27_52.png](https://qiita-image-store.s3.amazonaws.com/0/62354/dfa644fc-7629-ed5f-c2fe-3ced649f2615.png)

![2018-07-20_11h27_34.png](https://qiita-image-store.s3.amazonaws.com/0/62354/b2b6c63e-88e8-b2ef-ae31-4cd026c56700.png)

# 一応原因の切り分け

textlintに渡してエラーが出たことを確認したコード

```js
const TextLintEngine = require('textlint').TextLintEngine;
const engine = new TextLintEngine();

const fs = require('fs');
searchTarget = fs.readFileSync('out.txt').toString();

;(async () =>{
        const result = await engine.executeOnText(searchTarget).catch(e => { 
            console.log('textlint error', e);
            console.log(searchTarget)
            return [{messages : []}];
        });
})()
```

バックトレースがkuromojiなので、textlintの確認はこれぐらいで。

kuromoji単体で`0x00`が原因で発生するかを別途installして確認する。

```js
const kuromoji = require("kuromoji");

const path = require("path");
const kuromojiDir = path.dirname(require.resolve("kuromoji"));
const option = {dicPath: path.join(kuromojiDir, "..", "dict")}

const TextDecoder = require('text-encoding').TextDecoder;
const nullbyte = (new TextDecoder).decode(Uint8Array.of(0x00))
kuromoji.builder(option).build(function (err, tokenizer) {
    // tokenizer is ready
    var path = tokenizer.tokenize(nullbyte);
    console.log(path);
});
```

このコードでも同じエラーが出たので、

* textlint関係なく
* 前後の文字列関係なく

kuromojiに`0x00`を渡すとエラーになると決めつけた。

(kuromojiだともうすこしエラーが詳細に)

```
~\node_modules\kuromoji\src\viterbi\Viterb
iBuilder.js:53
            for (var i = 0; i < token_info_ids.length; i++) {
                                               ^

TypeError: Cannot read property 'length' of undefined
    at ViterbiBuilder.build (~\node_module
s\kuromoji\src\viterbi\ViterbiBuilder.js:53:48)
(後略)
```


# 参考

* [azu/kuromojin: Provide a high level wrapper for kuromoji.js](https://github.com/azu/kuromojin)  
textlintで使っているラッパーだが、kuromojiの呼び出し方のコードを使わしてもらいました。
* [JavaScript、Node.js で文字列とバイト列の相互変換 - Qiita](https://qiita.com/masakielastic/items/8eb4bf4efc2310ee7baf)  
 `0x00`文字列の作り方  
 Node.jsでは別途パッケージのインストールが必要と知らなかったので助かりました。

***

* [takuyaa/kuromoji.js: JavaScript implementation of Japanese morphological analyzer](https://github.com/takuyaa/kuromoji.js)
* [inexorabletash/text-encoding: Polyfill for the Encoding Living Standard's API](https://github.com/inexorabletash/text-encoding)


# メモ

ワークアラウンドで回避するなら事前に置換？

null文字はlengthを持っていないのかと思ったが、そうでもないようなので、やはりパーサ側の処理が原因のようだ。

```js
const nullbyte = (new TextDecoder).decode(Uint8Array.of(0x00))
console.log('null', nullbyte.length) // 1
```

ただ本当に現状はクリティカルな問題ではないので放置している。
(なので原因究明が出来てなくて、Issueも送れていません)