This commit is contained in:
Vicente Ferrari Smith 2026-02-05 12:32:12 +01:00
parent 9a5c15e47e
commit 29c136d34c

100
src/shared/bits.zig Normal file
View File

@ -0,0 +1,100 @@
const std = @import("std");
pub const BitWriter = struct {
// We store a pointer to the ArrayList so we can grow it
bytes: *std.ArrayList(u8),
bit_offset: usize = 0,
pub fn init(bytes: *std.ArrayList(u8)) BitWriter {
return .{ .bytes = bytes };
}
pub fn write(self: *BitWriter, allocator: std.mem.Allocator, value: anytype) !void {
const T = @TypeOf(value);
const info = @typeInfo(T);
const bit_width = switch (info) {
.int => |int| int.bits,
.bool => 1,
else => @compileError("Unsupported type: " ++ @typeName(T)),
};
const UnsignedT = std.meta.Int(.unsigned, bit_width);
const bits_to_write = @as(UnsignedT, @bitCast(value));
var remaining : usize = bit_width;
while (remaining > 0) {
const byte_index = self.bit_offset / 8;
const bit_in_byte = self.bit_offset % 8;
// If we are moving into a new byte, grow the ArrayList
if (byte_index >= self.bytes.items.len) {
try self.bytes.append(allocator, 0);
}
const space_in_byte = 8 - bit_in_byte;
const chunk_size = @min(remaining, space_in_byte);
const mask : u8 = std.math.shr(u8, 0xFF, 8 - chunk_size);
const bits : u8 = @truncate(std.math.shr(UnsignedT, bits_to_write, remaining - chunk_size) & mask);
self.bytes.items[byte_index] |= std.math.shl(u8, bits, space_in_byte - chunk_size);
self.bit_offset += chunk_size;
remaining -= chunk_size;
}
}
pub fn written(self: *BitWriter) []const u8 {
const bytes_used = (self.bit_offset + 7) / 8; // round up bits bytes
return self.bytes.items[0..bytes_used];
}
};
pub const BitReader = struct {
bytes: []const u8,
bit_offset: usize = 0,
pub fn init(bytes: []const u8) BitReader {
return .{ .bytes = bytes };
}
pub fn read(self: *BitReader, comptime T: type) T {
const info = @typeInfo(T);
const bit_width = switch (info) {
.int => |int| int.bits,
.bool => 1,
else => @compileError("Unsupported type: " ++ @typeName(T)),
};
const UnsignedT = std.meta.Int(.unsigned, bit_width);
var remaining: usize = bit_width;
var result: UnsignedT = 0;
while (remaining > 0) {
const byte_index = self.bit_offset / 8;
const bit_in_byte = self.bit_offset % 8;
const space_in_byte = 8 - bit_in_byte;
const chunk_size = @min(remaining, space_in_byte);
const mask: u8 = std.math.shr(u8, 0xFF, 8 - chunk_size);
// Extract chunk MSB-first from current byte
const shift_from_byte = space_in_byte - chunk_size;
const bits: u8 = std.math.shr(u8, self.bytes[byte_index], shift_from_byte) & mask;
// Shift into result (from MSB side)
result |= std.math.shl(UnsignedT, @intCast(bits), remaining - chunk_size);
self.bit_offset += chunk_size;
remaining -= chunk_size;
}
return @bitCast(result);
}
};