プロトコルの実装でよく出てくるやつのまとめです。
Node.jsの`Buffer`を通して得た、オクテットバイトストリームってやつを対象に。
普通に`Buffer`のメソッドを使うこともあるし、特定のバイトからビットを取り出すこともあるはずで、そのバリエーションのメモ。
もうバリエーションはないかもしれないけど、また見つけたら追記する。
バイト単位で読み取る
一番シンプルな読み出しのパターン。
const first1Byte = buf[0];
というようにインデックスで対象の1バイトが取れる。
その他は、APIのリストにある`readXxx()`系のメソッドを使う。
// 1バイトなのでBEもLEも関係ない const byte0_1 = buf.readUInt8(0); const byte0_2 = buf.readUInt16BE(0); const byte2_4 = buf.readUInt16BE(2); const byte0_4 = buf.readUInt32BE(0);
という具合で、読み取り位置をオフセットで指定する。
まだ実装経験が浅いけど、おそらく`readUIntXXBE()`系のメソッドしか使わなさそう。
4byteより大きい読み取り
`readUIntXXBE()`系のメソッドは、32bit = 4byteまでしか用意されてない。
そこで使うのが`readUIntBE()`・・ではあるが、これも最大で6byteまでしか読み取れない。
const byte0_2 = buf.readUIntBE(0, 2); const byte0_6 = buf.readUIntBE(0, 6); const byte0_7 = buf.readUIntBE(0, 7); // RangeError!
それ以上を一気に読み取りたい場合どうするか。
6byteより大きい読み取り
まあほとんどの場合はそれを数値ではなく、その中身をそのまま使いたいとかなはず。
const byte0_8 = buf.slice(0, 8); const last2Byte = buf.slice(-2);
それ用のメソッドが`slice()`です。
その後は`toString()`するか、そのまま使うことが多いはず。
ビット単位で読み取る
ここから先は、バイトではなくビットが欲しい場合。
ビットが欲しい = `1`か`0`が知りたいということになる。
先頭1bitの値を得る
const byte = buf[0]; const first1Bit = (byte & 0x80) >>> 7;
- 16進数の`0x80`は、10進数で`128`で、2進数で`10000000`
- 先頭だけが`1`の8bit
- それを`&`で演算すると、同じ桁が`1`のところだけ`1`が残る
- 先頭は`1`なら`1`が残る、他の桁は`0`になる
- それを`>>> 7`、つまり右から7桁捨てる
- 残るのは2進数で`1`か`0`、10進数でも`1`か`0`
- なので先頭1bitが`0`か`1`が取れる
という仕組み。
うしろ4bitの値を得る
さっきの応用といえば応用。
const byte = buf[0]; const last4Bit = byte & 0x0f;
- 16進数の`0x0f`は、10進数で`15`、2進数で`1111`
- つまり8bitでかくと`00001111`
- それを`&`で演算すると、上4桁は`0000`に固定できる
- 下4桁は`1`なら`1`になるし、`0`なら`0`のまま残る
- 最終的に、2進数で`1111`から`0000`の範囲の値が取れる
- = 10進数の`15`から`0`の範囲の値
つまり欲しいビットの桁だけを`1`にしたやつで`&`演算すれば、その値がわかるということ。
たとえばうしろ7bitが欲しいなら、2進数の`01111111`つまり16進数の`0x7f`で`&`すればいい。
- うしろ1bit: `& 0x01`
- うしろ2bit: `& 0x03`
- うしろ3bit: `& 0x07`
- うしろ4bit: `& 0x0f`
- うしろ5bit: `& 0x1f`
- うしろ6bit: `& 0x3f`
- うしろ7bit: `& 0x7f`
という感じ。