kubo39's blog

ただの雑記です。

RLP encode 実装メモ

d-eth/dethのrlpEncodeの実装がだいぶ厳しい。

理由としては主に

  • 引数にbytes(ubyteのtype alias)しかとれない
    • 主要な型をrlpのbytes表現にシリアライズしてからrlpEncodeしないといけない
  • パフォーマンスに気を使っていない実装になっている
    • .dupなどコピーしまくり
    • 数値のコンパクト表現を実装する際も毎回メモリを確保

なのでこのあたりを置き換えるようなものを作ろうとしている。まだencodeしか実装していないがいちおうdecodeも作る予定。

kubo39/rlpでは

  • 型ごとにencodeとencodeLengthを実装
  • 内部的な実装ではバッファを外から渡す方式にしてそこにencodeしたものを追加していく
  • 数値のコンパクト表現の実装の細かい最適化
    • llvm_ctlz intrinsicの利用/ヒープ領域の割り当てを避けるなど

といったあたりを意識しながら作っている。

go-ethereumのrlp実装などもみたが、主に参考にしているのはalloy-rs/rlpAPIなども似せている。

バイト列の扱い

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;
    }
}