Skip to content

Support bitfields #542

Description

@MeteorsLiu

Bitfields are a useful feature for low-level tasks like OS development in C. However, Go's language specification does not include native support for bitfields.

​​C Example:​​

This C structure uses bitfields to pack data efficiently:

structvector_desc_t {
intflags: 16; // Bitfield: OR of VECDESC_FL_* defines (16 bits)unsigned intcpu: 1; // Bitfield (1 bit)unsigned intintno: 5; // Bitfield (5 bits)intsource: 8; // Bitfield: Interrupt mux flags (used when not shared, 8 bits)shared_vector_desc_t*shared_vec_info; // Pointer (used when VECDESC_FL_SHARED)vector_desc_t*next; // Pointer
};

The numbers (16, 1, 5, 8) specify the ​​exact number of bits​​ each field occupies in memory.

Emulating Bitfields in Go​​

While Go lacks direct language-level support for bitfields, we can achieve similar functionality through ​​code generation​​. This approach manually implements the bit manipulation operations.

The Go team exemplifies this technique in the https://cs.opensource.google/go/x/text/+/refs/tags/v0.28.0:internal/gen/bitfield/bitfield.go. For instance, code like this:

typemyUint8uint8typetest1struct { // Represents 28 bits of datafoouint16`bitfield:",fob"`// Tag hints for field "foo" with getter "fob"Barint8`bitfield:"5,baz"`// Tag: occupies 5 bits, getter named "baz"Foouint64// Regular field (whole uint64)barmyUint8`bitfield:"3"`// Tag: occupies 3 bits (default getter "bar")Boolbool`bitfield:""`// Tag: occupies 1 bit (default getter "Bool")Bazint8`bitfield:"3"`// Tag: occupies 3 bits (default getter "Baz")
}

Can be processed by a generator to produce methods that manipulate the bits within a single underlying integer type (here, uint32 for the bitfields):

typetest1uint32// Underlying type holding the packed bitsfunc (ttest1) fob() uint16 { // Getter for field 'foo' (tag defined name "fob")returnuint16((t>>16) &0xffff) // Shift and mask for bits 16-31
}
func (ttest1) baz() int8 { // Getter for field 'Bar' (tag defined name "baz")returnint8((t>>11) &0x1f) // Shift and mask for 5 bits at pos 11
}
func (ttest1) bar() myUint8 { // Getter for field 'bar' (type myUint8)returnmyUint8((t>>8) &0x7) // Shift and mask for 3 bits at pos 8
}
func (ttest1) Bool() bool { // Getter for field 'Bool' (1 bit)constbit=1<<7// Bit mask for position 7returnt&bit==bit// Check if bit at pos 7 is set
}
func (ttest1) Baz() int8 { // Getter for field 'Baz'returnint8((t>>4) &0x7) // Shift and mask for 3 bits at pos 4
}

Memory alignment

By default, the most of compilers respect that memory alignment:

Size = ceil(bits ÷ unit_bits)+ padding for larger members.
(Unit size = sizeof(unsigned int), typically 32 bits).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions