...
This commit is contained in:
parent
9a5c15e47e
commit
29c136d34c
100
src/shared/bits.zig
Normal file
100
src/shared/bits.zig
Normal 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);
|
||||
}
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user