いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Goメモ-401 (ビットフィールドを楽に読み取りたい)(nokute78/go-bit)

関連記事

GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ

概要

以下、自分用のメモです。忘れないうちにメモメモ。。。

バイナリファイルや通信メッセージを扱う際、ビット単位で意味を持っているフィールドなどはよく出てきます。

1ビットで意味を持つものや、3ビットで意味を持つものなどなど。

C言語だとビットフィールドがあるので楽なのですが、Goではビットフィールドはありません。

なので、今までは1バイト取得してビット操作していたのですが、以下のライブラリを発見。

github.com

求めていたものドンピシャでした。めっちゃ便利です。作者の方に感謝。

binary.Read と同じようなインターフェースが用意されているので、とても使いやすいです。

上のリポジトリの方にとてもわかり易いサンプルが付いているのですが、ついでに自分用にもサンプルつくってみました。

サンプル

package main

import (
    "bytes"
    "encoding/binary"
    "fmt"
    "log"

    "github.com/nokute78/go-bit/v2"
)

type (
    OneByte struct {
        B1 bit.Bit
        B2 bit.Bit
        B3 bit.Bit
        B4 bit.Bit
        B5 bit.Bit
        B6 bit.Bit
        B7 bit.Bit
        B8 bit.Bit
    }

    CompositeBitField struct {
        Length [4]bit.Bit
        Value  [3]bit.Bit
        Flag   bit.Bit
    }
)

func (me OneByte) String() string {
    return fmt.Sprintf("%v%v%v%v%v%v%v%v", me.B1, me.B2, me.B3, me.B4, me.B5, me.B6, me.B7, me.B8)
}

func init() {
    log.SetFlags(0)
}

func main() {
    if err := run(); err != nil {
        log.Fatal(err)
    }
}

func run() error {
    if err := readOneByte(); err != nil {
        return err
    }

    if err := readComposite(); err != nil {
        return err
    }

    return nil
}

func readOneByte() error {
    var (
        source = []byte{1, 2, 3, 7, 15, 16, 127, 128, 129, 255}
        buf    = bytes.NewBuffer(source)
        data   [10]OneByte
        err    error
    )

    err = bit.Read(buf, binary.BigEndian, &data)
    if err != nil {
        return err
    }

    for i, b := range source {
        log.Printf("%3d : %s", b, data[i])
    }

    return nil
}

func readComposite() error {
    var (
        source = []byte{0x82, 0x47, 0xF1}
        endian = binary.BigEndian
        buf    = bytes.NewBuffer(source)
        data   [3]CompositeBitField
        err    error
    )

    err = bit.Read(buf, endian, &data)
    if err != nil {
        return err
    }

    for _, d := range data {
        var (
            l = bit.BitsToBytes(d.Length[:], endian)[0]
            v = bit.BitsToBytes(d.Value[:], endian)[0]
            r = bit.BitsToBytes([]bit.Bit{d.Flag}, endian)[0]
        )

        log.Printf("Length=%2d(%v), Value=%d(%v), Flag=%d(%v)", l, d.Length, v, d.Value, r, d.Flag)
    }

    return nil
}

try-golang-extlib/examples/gobit/main.go at main · devlights/try-golang-extlib · GitHub

実行すると以下のような感じです。

      $ task
       task: [default] go run main.go
         1 : 00000001
         2 : 00000010
         3 : 00000011
         7 : 00000111
        15 : 00001111
        16 : 00010000
       127 : 01111111
       128 : 10000000
       129 : 10000001
       255 : 11111111
       Length= 8([0 0 0 1]), Value=1([1 0 0]), Flag=0(0)
       Length= 4([0 0 1 0]), Value=3([1 1 0]), Flag=1(1)
       Length=15([1 1 1 1]), Value=0([0 0 0]), Flag=1(1)

ちゃんと読み取ってくれていますね。素晴らしい。

参考情報

作者の方がzennで紹介記事書いてらっしゃいました。

zenn.dev

Goのおすすめ書籍


過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。