IT練習ノート

IT関連で調べたこと(実際は嵌ったこと)を書いています。

PutをBitPutにするとflushされてしまう

Binary処理が定義されているとします。

例えば、下記は1Byteの処理です。

 > :t example_ColFlags
example_ColFlags :: ColFlags
 > :t put example_ColFlags
put example_ColFlags :: Put
 >
 > BSL.writeFile "work\\colflags.bin" (runPut $ put example_ColFlags)
 >
  Offset: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F    
00000000: FF                                                 .

このようなPutが定義されているときに、前後にbit単位の処理を追加することを考えます。

joinPutを使うと、直掩までのBit処理がflushされてしまいます。

 > let foo = putBool True >> (joinPut $ put (example_ColFlags ::ColFlags)) >> putBool True
 > BSL.writeFile "work\\colflags.bin" (runPut $ runBitPut foo)
 >

ここでは、1bitを出しているので、ゼロフィルされて80が出力されてしまいます。この挙動はドキュメントに記載されています。

Run a Put inside BitPut. Any partially written bytes will be flushed before Put executes to ensure byte alignment.

  Offset: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F    
00000000: 80 FF 80                                           ...

flushを避けるため、仕方がないので、バイト単位の処理はいったんByteStringにして、それをBit処理しました。

 > let foo = putBool True >> (Bit.putByteString $ BSL.toStrict $ runPut $ put (example_ColFlags ::ColFlags)) >> putBool True
 > BSL.writeFile "work\\colflags.bin" (runPut $ runBitPut foo)
  Offset: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F    
00000000: FF C0                                              .@