d-eth/dethのrlpEncodeの実装がだいぶ厳しい。
理由としては主に
- 引数にbytes(ubyteのtype alias)しかとれない
- 主要な型をrlpのbytes表現にシリアライズしてからrlpEncodeしないといけない
- パフォーマンスに気を使っていない実装になっている
- .dupなどコピーしまくり
- 数値のコンパクト表現を実装する際も毎回メモリを確保
なのでこのあたりを置き換えるようなものを作ろうとしている。まだencodeしか実装していないがいちおうdecodeも作る予定。
- 型ごとにencodeとencodeLengthを実装
- わざわざシリアライズしてからencode処理を呼び出す必要がない
- 内部的な実装ではバッファを外から渡す方式にしてそこにencodeしたものを追加していく
- 数値のコンパクト表現の実装の細かい最適化
- llvm_ctlz intrinsicの利用/ヒープ領域の割り当てを避けるなど
といったあたりを意識しながら作っている。
go-ethereumのrlp実装などもみたが、主に参考にしているのはalloy-rs/rlpでAPIなども似せている。
バイト列の扱い
ubyteを渡す際にRLP表現においてこれがたんなるバイト列なのかubyte型数値のリストなのかを区別する必要がある。 どういう方式でencodeしたいかはユーザの意思しだいであるが、ubyte型のみでは区別ができないので多少工夫がいる。
kubo39/rlpではubyteをencodeする場合のみテンプレート引数としてasListを渡す方式にした。
void encode(bool asList = false, T)(T[] value, ref ubyte[] buffer) nothrow pure @safe if (is(Unqual!T == ubyte)) { static if (asList) { rlpListHeader(value).encodeHeader(buffer); foreach (elem; value) elem.encode(buffer); } else { if (value.length != 1 || value[0] >= rlp.EMPTY_STRING_CODE) { Header h = { isList: false, payloadLength: value.length }; h.encodeHeader(buffer); } buffer ~= value; } }