From 29c136d34c61eef593f2d36f46e240170067b693 Mon Sep 17 00:00:00 2001 From: Vicente Ferrari Smith Date: Thu, 5 Feb 2026 12:32:12 +0100 Subject: [PATCH] ... --- src/shared/bits.zig | 100 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 src/shared/bits.zig diff --git a/src/shared/bits.zig b/src/shared/bits.zig new file mode 100644 index 0000000..992b7a2 --- /dev/null +++ b/src/shared/bits.zig @@ -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); + } +};