Loads an integer from packed memory with provided bit_count, bit_offset, and signedness. Asserts that T is large enough to store the read value.
pub fn readVarPackedInt(
comptime T: type,
bytes: []const u8,
bit_offset: usize,
bit_count: usize,
endian: std.builtin.Endian,
signedness: std.builtin.Signedness,
) T
pub fn readVarPackedInt(
comptime T: type,
bytes: []const u8,
bit_offset: usize,
bit_count: usize,
endian: std.builtin.Endian,
signedness: std.builtin.Signedness,
) T {
const uN = @Int(.unsigned, @bitSizeOf(T));
const iN = @Int(.signed, @bitSizeOf(T));
const Log2N = std.math.Log2Int(T);
const read_size = (bit_count + (bit_offset % 8) + 7) / 8;
const bit_shift = @as(u3, @intCast(bit_offset % 8));
const pad = @as(Log2N, @intCast(@bitSizeOf(T) - bit_count));
const lowest_byte = switch (endian) {
.big => bytes.len - (bit_offset / 8) - read_size,
.little => bit_offset / 8,
};
const read_bytes = bytes[lowest_byte..][0..read_size];
if (@bitSizeOf(T) <= 8) {
// These are the same shifts/masks we perform below, but adds `@truncate`/`@intCast`
// where needed since int is smaller than a byte.
const value: uN = if (read_size == 1) b: {
break :b @truncate(read_bytes[0] >> bit_shift);
} else b: {
const i: u1 = @intFromBool(endian == .big);
const head: uN = @truncate(read_bytes[i] >> bit_shift);
const tail_shift: Log2N = @intCast(@as(u4, 8) - bit_shift);
const tail: uN = @truncate(read_bytes[1 - i]);
break :b (tail << tail_shift) | head;
};
switch (signedness) {
.signed => return @intCast((@as(iN, @bitCast(value)) << pad) >> pad),
.unsigned => return @intCast((value << pad) >> pad),
}
}
// Copy the value out (respecting endianness), accounting for bit_shift
var int: uN = 0;
switch (endian) {
.big => {
for (read_bytes[0 .. read_size - 1]) |elem| {
int = elem | (int << 8);
}
int = (read_bytes[read_size - 1] >> bit_shift) | (int << (@as(u4, 8) - bit_shift));
},
.little => {
int = read_bytes[0] >> bit_shift;
for (read_bytes[1..], 0..) |elem, i| {
int |= (@as(uN, elem) << @as(Log2N, @intCast((8 * (i + 1) - bit_shift))));
}
},
}
switch (signedness) {
.signed => return @intCast((@as(iN, @bitCast(int)) << pad) >> pad),
.unsigned => return @intCast((int << pad) >> pad),
}
}