Last active
February 24, 2025 07:04
-
-
Save oplanre/de0bba4f1e2f769458ca1adff7f12280 to your computer and use it in GitHub Desktop.
Added NextBitField
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package bitfield | |
import "fmt" | |
type BitField[T ~uint32 | ~uint64, U ~uint32 | ~uint64] struct { | |
Shift int | |
Size int | |
Mask U | |
} | |
// NewBitField creates a new BitField with the given shift and size. | |
func NewBitField[T ~uint32 | ~uint64, U ~uint32 | ~uint64](shift, size int) BitField[T, U] { | |
switch bSize := bitSize[U](); { | |
case shift >= bSize: | |
panic("invalid shift parameter") | |
case size >= bSize: | |
panic("invalid size parameter") | |
case shift+size > bSize: | |
panic("invalid shift/size parameters") | |
case size <= 0: | |
panic("invalid size parameter") | |
} | |
mask := ((U(1) << shift) << size) - (U(1) << shift) | |
return BitField[T, U]{Shift: shift, Size: size, Mask: mask} | |
} | |
// bitSize returns the bit size of a given type. | |
func bitSize[T any]() int { | |
var zero T | |
switch any(zero).(type) { | |
case uint32: | |
return 32 | |
case uint64: | |
return 64 | |
default: | |
panic("unsupported type") | |
} | |
} | |
// IsValid checks if the value fits within the bit field. | |
func (bf BitField[T, U]) IsValid(value T) bool { | |
maxValue := (U(1) << bf.Size) - 1 | |
return U(value)&^maxValue == 0 | |
} | |
// Encode encodes a value into the bit field. | |
func (bf BitField[T, U]) Encode(value T) U { | |
if !bf.IsValid(value) { | |
panic(fmt.Sprintf("value %v out of range", value)) | |
} | |
return U(value) << bf.Shift | |
} | |
// Update updates the bit field within an existing value. | |
func (bf BitField[T, U]) Update(previous U, value T) U { | |
return (previous &^ bf.Mask) | bf.Encode(value) | |
} | |
// Decode extracts the bit field from a value. | |
func (bf BitField[T, U]) Decode(value U) T { | |
return T((value & bf.Mask) >> bf.Shift) | |
} | |
// NextBitField returns a new BitField starting from the previous one. | |
func (bf BitField[T, U]) NextBitField(size int) BitField[T, U] { | |
return NewBitField[T, U](bf.Shift+bf.Size, size) | |
} | |
/* Examples | |
// Example 1: Using BitField with uint32 | |
func Example_uint32() { | |
// Create a bit field for a 4-bit value starting at position 4 | |
bf := NewBitField[uint32, uint32](4, 4) | |
// Encode the value 5 (0101 in binary) into the bit field | |
encoded := bf.Encode(5) // Result: 0000 0000 0000 0000 0000 0000 0101 0000 | |
fmt.Printf("Encoded: %032b\n", encoded) | |
// Decode the value back | |
decoded := bf.Decode(encoded) // Result: 5 | |
fmt.Printf("Decoded: %d\n", decoded) | |
// Update an existing value | |
previous := uint32(0xFF) // 1111 1111 | |
updated := bf.Update(previous, 3) // Will preserve bits outside the field | |
fmt.Printf("Updated: %032b\n", updated) | |
} | |
// Example 2: Using BitField with uint64 for larger values | |
func Example_uint64() { | |
// Create three adjacent bit fields in a uint64 | |
colorR := NewBitField[uint32, uint64](0, 8) // 8 bits for red | |
colorG := colorR.NextBitField(8) // 8 bits for green, equivalent to NewBitField[uint32, uint64](8, 8) | |
colorB := colorG.NextBitField(8) // 8 bits for blue, equivalent to NewBitField[uint32, uint64](16, 8) | |
// Build a color value | |
var color uint64 | |
color = colorR.Update(color, 255) // Set red to max | |
color = colorG.Update(color, 128) // Set green to mid | |
color = colorB.Update(color, 64) // Set blue to quarter | |
// Extract individual components | |
r := colorR.Decode(color) | |
g := colorG.Decode(color) | |
b := colorB.Decode(color) | |
fmt.Printf("Color RGB: (%d, %d, %d)\n", r, g, b) | |
} | |
// Example 3: Demonstrating validation | |
func Example_validation() { | |
bf := NewBitField[uint32, uint32](0, 3) // 3-bit field at position 0 | |
// This is valid (value 5 doesn't fit in 3 bits) | |
fmt.Printf("Is 7 valid? %v\n", bf.IsValid(7)) // true | |
fmt.Printf("Is 8 valid? %v\n", bf.IsValid(8)) // false | |
// This will panic due to value being too large for the field | |
// bf.Encode(8) // Would panic: "value 8 out of range" | |
} | |
type Example4Enum uint32 | |
const ( | |
Example4EnumValue1 Example4Enum = 1 | |
Example4EnumValue2 Example4Enum = 2 | |
Example4EnumValue3 Example4Enum = 3 | |
) | |
func Example4() { | |
bf := NewBitField[Example4Enum, uint32](0, 2) | |
// Encode the value Example4EnumValue2 | |
encoded := bf.Encode(Example4EnumValue2) | |
fmt.Printf("Encoded: %032b\n", encoded) | |
encoded = bf.Encode(1) | |
fmt.Printf("Encoded: %032b\n", encoded) | |
// Decode the value back | |
decoded := bf.Decode(encoded) | |
fmt.Printf("Decoded: %d\n", decoded) | |
//should panic | |
// bf.Encode(10) | |
} | |
func main() { | |
Example_uint32() | |
Example_uint64() | |
Example_validation() | |
Example4() | |
} | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://github.com/lnear-dev/bitfield