ちりもつもればミルキーウェイ

好奇心に可処分時間が奪われる

自作のprotobuf unmarshalerで固定長64bit/32bitフィールドをパースできるようにした

はじめに

前に簡易的なunmarshalerかいたので それに機能追加した話

この時点では、対応するtypeはVarintとLength-delimitedだけだったので、固定長64bit/32bitフィールドをパースできるようにしました。

差分は以下 https://github.com/convto/protowire/compare/6f4b6c1..eb618a7

対応

とくに難しいことはしていない。

単に固定長フィールドをサポートしつつ、適切な型でbindしてるだけですね。

差分をみればわかるんですが、このあたりのbyte読み取りや型変換は標準パッケージに実装されているので、何も考えずに実装できて楽でした。たとえば以下

  • binary.LittleEndian.Uint64() およびその32bit実装
  • math.Float64frombits() およびその32bit実装

ひとつ、最初間違って実装してしまったのは sfixed64/32 のときにzigzagで読み取ろうとしたこと。

固定長byte列の場合は最初から64/32bitなりで全サイズのデータがバイナリに含まれるので、可変長のときのように負数ならzigzagを使ったほうがお得!とならない。
(2の補数表現だろうが、値が 1 だろうが -1 だろうが、どっちみち固定長ぶんバイトが使われるため)

そのためsfixed64/32のときはzigzagを使わないようで、いままでの流れでzigzagつかうunmarshalerを実装したらおかしな値が出力されてerrorになった。

あとはいつもの通り https://github.com/golang/protobuf で生成したバイナリを読み取ってみてテストした。

感想

めちゃめちゃ簡単だった。これであとは下位互換をのぞくとrepeatableの対応とか別メッセージの埋め込み対応とかだけかな?あとちょっとでひとしきり実装できそう。

テストがちょっとめんどくさい。手元でいちいちバイナリ吐いて〜とするのが面倒なので、testdirとかつくってそこにテスト用のproto定義とprotoc-gen-goで生成したコード配置してテストではそれを参照するようにしたほうが楽かなぁ。

かなり素朴に実装していてベタッと書いてるので、ひととおり仕様準拠したらリファクタしてもいいかも