Is there a standard lib function that does the majority of this or do I need to handwrite this whole thing?
/// Write bits of length at bit position in the buffer.
/// Write value as little-endian.
/// Bit position is lowest to highest memory address, least to most significant within a byte.
///
/// writeBitsAtPos(out, bool, 0, N) does nothing
/// writeBitsAtPos(out, bool, 1, N) writes 1 bit at position N
/// writeBitsAtPos(out, bool, 2, N) writes 2 bits, bool at position N, 0 at position N + 1
/// writeBitsAtPos(out, void, 3, N) writes 3 zero bits at position N
/// writeBitsAtPos(out, u8, 5, N) writes the 5 least significant bits of u8 at position N
pub fn writeBitsAtPos(out_buffer: []u8, value: anytype, bitlength: u16, bit_pos: u16) void {
// TODO: implement
}
Yeah, the example is not good; using it to write to a field of a packed struct doesn’t make much sense. Here’s a better use case (a tightly packed array):
// pretend `codepoints` is a slice of u21
const packed_bytes_len = try std.math.divCeil(usize, @bitSizeOf(u21) * codepoints.len, 8);
const packed_bytes = try allocator.alloc(u8, packed_bytes_len);
defer allocator.free(packed_bytes);
for (codepoints, 0..) |codepoint, i| {
std.mem.writePackedInt(
u21,
packed_bytes,
i * @bitSizeOf(u21),
codepoint,
.little,
);
}
// Now packed_bytes is a tighly packed array of
// `u21` with no padding bits between the elements.
// We can use `readPackedInt` to retrieve elements from it.
(adapted from this real example [in the real code it’s even more useful since it writes the elements out-of-order], a few more examples here since PackedIntArray was removed in favor of the std.mem.{read,write}PackedInt functions)
I think an example is important because a curious reader may be confused that access to an unaligned array will not cause a crash (on some cpu).
This (AFAIK) works only because the compiler add extra and expensive code to avoid a crash. Not sure if this was done from early compilers days or recently.
I am not sure what you are talking about, the array is packed through math and bitshifting by std.mem.writePackedInt, so the programmer that uses that function adds extra more expensive code, the compiler has nothing to do with it, it also doesn’t do any unaligned access it just maps the packed bit position to a dense array of bytes instead of one that includes padding.
/// Stores an integer to packed memory with provided bit_offset, bit_count, and signedness.
/// If negative, the written value is sign-extended.
pub fn writeVarPackedInt(bytes: []u8, bit_offset: usize, bit_count: usize, value: anytype, endian: std.builtin.Endian)