bitfield

package module
v0.0.0-...-a20dcd4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 11, 2025 License: MIT Imports: 2 Imported by: 0

README

go-bitfield: Declarative bit-fields decoder for Go

Description

Go library for simple declarative decoding of bit-fields.

Usage

The following is a simple example:

// Bit-fields definitions
type bitFields struct {
    // List fields in order from least significant bit
    A uint8 `bit:"1"`
    B uint8 `bit:"2"`
    _ uint8 `bit:"1"` // For place holder
    C uint8 `bit:"4"`
}

// Byte slice to parse and decode
input := []byte{0b1010_0_10_1} // 0xA5

// Variable of the bit-fields to store the result of decoding
var out bitFields

_ = bitfield.Unmarshal(input, &out)

fmt.Printf("A=%#b, B=%#b, C=%#b\n", out.A, out.B, out.C)
// Output: A=0b1, B=0b10, C=0b1010

Bit-fields and their bit sizes are specified by a bit struct tag. bit:"N" means that the field will parse N bits from the input byte slice.

For more details, refer to the the documents in pkg.go.dev.

Installation

go get github.com/jmatsuzawa/go-bitfield

TODO

The following is a part of the TODO list:

  • Marshaling (Encoding) bit-fields to a byte slice
  • Streaming Encoders and Decoders

Licensing

MIT License.

Authors

  • Jiro Matsuzawa

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Unmarshal

func Unmarshal(data []byte, out any, opts ...Option) error

Unmarshal parses a byte slice and stores the result in a struct with bit-fields pointed by out.

The following is a simple example:

// Bit-fields definitions
type bitFields struct {
    // List fields from least significant bit
    A uint8 `bit:"1"`
    B uint8 `bit:"2"`
    _ uint8 `bit:"1"` // For place holder
    C uint8 `bit:"4"`
}

// Byte slice to parse and decode
input := []byte{0b1010_0_10_1} // 0xA5

// Variable of the bit-fields to store the result of decoding
var out bitFields

_ = bitfield.Unmarshal(input, &out)

fmt.Printf("A=%#b, B=%#b, C=%#b\n", out.A, out.B, out.C)
// Output: A=0b1, B=0b10, C=0b1010

Bit-fields can be declared with a struct tag "bit" as in the above example. The constant number following `bit:` represents the bit size of the field. The bit size must be within the range of 1 to the size of the underlying integer type. For example, uint8 A `bit:"9"` is not acceptable, which causes FieldError to be returned. Fields must be listed in order, starting from the least significant bit.

This library borrows the idea of bit-fields from the C language. The function Unmarshal is aimed to make it easy to create an instance of a struct with bit-fields from a byte slice in a declarative way, just like type casting of a byte array into a struct pointer in C. However, there are some differences between this package and C language:

  • Bit-fields are not C-like packed bit-fields. Their actual size is the

same as the size of their underlying type. For example, Field uint8 `bit:"4"` occupies 8 bits (not 4 bits) in a struct. The bit tag is used to specify the bit position to parse in the provided byte slice. Bit-fields does not reduce memory usage. Data packing is not the purpose of this package.

Unmarshal parses multi-byte data in LittleEndian by default. You can change the byte order by specifying WithByteOrder option. Example:

var out struct {
	A uint8  `bit:"4"`
	B uint8  `bit:"8"`
	C uint32 `bit:"20"`
}
data := []byte{0x06, 0x90, 0x95, 0xC4}
_ = bitfield.Unmarshal(data, &out, bitfield.WithByteOrder(bitfield.BigEndian))
fmt.Printf("A=%d, B=%d, C=%#x\n", out.A, out.B, out.C)
// Output: "A=6, B=0, C=0x995c4"

The provided struct can also have plain integer fields without a bit tag. If an integer field does not have a bit tag, the bit size of the field will be the size of the type. The difference between bit-fields and plain integer fields is that bit-fields parse from the bit following the last parsed bit, while plain integer fields always parse from the LSB of the next byte. The following example code demonstrates the difference:

type withBitTag struct {
	A uint8  `bit:"4"`
	B uint8  `bit:"8"`
}
var out withBitTag
data := []byte{0x55, 0xaa}
_ = bitfield.Unmarshal(data, &out)
fmt.Printf("A=%#x, B=%#x\n", out.A, out.B)
// Output: "A=0x5, B=0xa5"

type withoutBitTag struct {
	A uint8  `bit:"4"`
	B uint8
}
var out withoutBitTag
data := []byte{0x55, 0xaa}
_ = bitfield.Unmarshal(data, &out)
fmt.Printf("A=%#x, B=%#x\n", out.A, out.B)
// Output: "A=0x5, B=0xaa"

If out is not a non-nil pointer to a struct, Unmarshal returns TypeError.

opts is a variadic parameter to specify how to parse the byte slice. Currently, only WithByteOrder option is available to specify the byte order for multi-byte fields.

Paramters:

  • data: A byte slice to parse
  • out: A non-nil pointer to a struct with bit-fields which stores the result of parsing
  • opts: Options to specify how to parse the byte slice

Returns:

  • nil if the byte slice is successfully parsed and stored in the struct
  • FieldError if the struct pointed by out has an invalid bit-field
  • TypeError if out is not a non-nil pointer to a struct
Example
var out struct {
	A uint8  `bit:"4"`
	B uint8  `bit:"8"`
	C uint32 `bit:"20"`
}
input := []byte{0x12, 0x34, 0x56, 0x78}

_ = bitfield.Unmarshal(input, &out)
fmt.Printf("A=%#x, B=%#x, C=%#x\n", out.A, out.B, out.C)
Output:
A=0x2, B=0x41, C=0x78563

Types

type ByteOrder

type ByteOrder int
const (
	LittleEndian ByteOrder = iota
	BigEndian
)

ByteOrder is an enumeration type that represents the byte order of binary data. LittleEndian and BigEndian are the two possible values, representing little-endian and big-endian byte order respectively.

type FieldError

type FieldError struct {
	Field reflect.StructField
	// contains filtered or unexported fields
}

FieldError describes an invalid bit-field in a struct passed to Unmarshal.

func (*FieldError) Error

func (e *FieldError) Error() string

type Option

type Option func(*options) error

func WithByteOrder

func WithByteOrder(order ByteOrder) Option

WithByteOrder specifies the byte order in which Unmarshal parses multi-byte data in a byte slice

Examples of usage:

// For little-endian:
Unmarshal(data, out, WithByteOrder(LittleEndian))
// Little-endian is the default byte order. It can be omitted. The following is equivalent to the above.
Unmarshal(data, out)

// For big-endian:
Unmarshal(data, out, WithByteOrder(BigEndian))

type TypeError

type TypeError struct {
	Type reflect.Type
	// contains filtered or unexported fields
}

TypeError describes an invalid type passed to Unmarshal. (The argument to Unmarshal must be a non-nil pointer to a struct.)

func (*TypeError) Error

func (e *TypeError) Error() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL