Store bits using native endianness when value spans multiple bytes.
On big endian architectures:
bit_offset uses MSb 0 bit numbering.
On little endian architectures:bit_offset uses LSb 0 bit numbering.pub fn storeBits(comptime Int: type, buffer: []Int, bit_offset: usize, value: anytype) void
pub fn storeBits(comptime Int: type, buffer: []Int, bit_offset: usize, value: anytype) void {
const Value = @TypeOf(value);
const ValueInt = @Int(.unsigned, @bitSizeOf(Value));
const value_int: ValueInt = @bitCast(value);
const index = bit_offset / @bitSizeOf(Int);
const small_bit_offset = bit_offset % @bitSizeOf(Int);
const available_bits = @bitSizeOf(Int) - small_bit_offset;
if (available_bits >= @bitSizeOf(ValueInt)) {
const shift = switch (native_endian) {
.little => small_bit_offset,
.big => available_bits - @bitSizeOf(ValueInt),
};
buffer[index] &= ~(((@as(Int, 1) << @intCast(@bitSizeOf(Value))) - 1) << @intCast(shift));
buffer[index] |= @as(Int, value_int) << @intCast(shift);
} else {
const DoubleInt = @Int(.unsigned, @bitSizeOf(Int) * 2);
const shift = switch (native_endian) {
.little => small_bit_offset,
.big => @bitSizeOf(DoubleInt) - small_bit_offset - @bitSizeOf(ValueInt),
};
const ptr: *align(@alignOf(Int)) DoubleInt = @ptrCast(buffer[index..][0..2]);
ptr.* &= ~(((@as(DoubleInt, 1) << @intCast(@bitSizeOf(Value))) - 1) << @intCast(shift));
ptr.* |= @as(DoubleInt, value_int) << @intCast(shift);
}
}