ネットワーク その7 ビットフィールドで圧縮

ビットフィールドは古くから知られている素晴らしいデータパッキング手法です。C#プログラマーがビットフィールドを使う機会は非常に少ないですが、ネットワークパケットの圧縮にはもってこいなので、この機会に使ってみましょう。

バイトは8ビット、intは32ビット。でも、送るべきデータが8ビットや32ビットの倍数にならないときはどうします?例えば以下のようなデータを送るとします。

     bool isAlive;       // 生きているか?
    bool isFiring;      // 撃っているか?

    enum Species    // 種類
    {
        Camel,          // ラクダ
        Cat,            // ねこ
        Caterpillar,    // いもむし
        Cheetah,        // チーター
        Chimpanzee,     // チンパンジー
        Cobra,          // コブラ
        Cormorant,      // 鵜(う)
        Cougar,         // クーガー
        Coyote,         // コヨーテ
        Crab,           // カニ
        Crocodile,      // ワニ
    }

    packetWriter.Write(isAlive);
    packetWriter.Write(isFiring);
    packetWriter.Write((byte)species)

 

これで3バイトになりますが、実際にはそんなにいりません。ブーリアン型は1ビットしか必要としませんし、生物の種類も11種しか居ないので4ビットで足ります。

それぞれのフィールドにどれだけのビット数が必要かが判れば、ビットシフトすることによって複数のビット群を1つbyteやintにまとめることができます。使いやすいように、指定されたビット群をbyteやintといった値にまとめるメソッドを作るといいでしょう。

ここでは、先の例では3バイトを送っていたところを1バイトにまとめて送ることができます(まだ2ビット余ってる)

     void AddToBitfield( ref int bitfield, int bitCount, int value )
    {
        bitfield <<= bitCount;
        bitfield |= value;
    }

    int bitfield = 0;
    AddToBitfield( ref bitfield, 1, isAlive? 1: 0 );
    AddToBitfield( ref bitfield, 1, isFiring? 1: 0 );
    AddToBitfield( ref bitfield, 4, (int)species );

    packetWriter.Write((byte)bitfield);

 

AddToBitfieldメソッドは2つのことをします

  • 現在のビットフィールドを左シフト(「<<」演算子を使う)して、新しいフィールドを確保
  • 新しいビット群を設定(「|」演算子を使う)

読み込むためには、逆のプロセスを行います。

 

     int ReadFromBitfield( ref int bitfield, int bitCount )
    {
        int value = bitfield & ((1 << bitCount) - 1 );
        bitfield >>= bitCount;
        return value;
    }

    int bitfield = PacketReader.ReadByte();
    species = (Species)ReadFromBitfield( ref bitfield, 4 );
    isFiring = ReadFromBitfield( ref bitfield, 1 ) != 0;
    isAlive = ReadFromBitfield( ref bitfield, 1 ) != 0;

書き込んだ順番とは逆に読み込んでいることに注意してください。

ReadFromBtfieldメソッドはAddToBitfieldと逆のことをします。

  • ビットフィールドの下位ビットからビット群を取り出す(「&」演算子を使う)
  • 残りのビットフィールドを右にシフトする(「>>」演算子を使う)

ビットフィールドはenumやブーリアンをまとめるのに有効ですが、他の数値データも前回紹介した量子化の手法と組み合わせることで格納することができます。

原文:
http://blogs.msdn.com/shawnhar/archive/2007/12/28/network-compression-bitfields.aspx