For reading the reversed bit streams used to encode FSE compressed data.
const ReverseBitReader = struct
const ReverseBitReader = struct {
bytes: []const u8,
remaining: usize,
bits: u8,
count: u4,
fn init(bytes: []const u8) error{MissingStartBit}!ReverseBitReader {
var result: ReverseBitReader = .{
.bytes = bytes,
.remaining = bytes.len,
.bits = 0,
.count = 0,
};
if (bytes.len == 0) return result;
for (0..8) |_| if (0 != (result.readBitsNoEof(u1, 1) catch unreachable)) return result;
return error.MissingStartBit;
}
fn initBits(comptime T: type, out: anytype, num: u16) Bits(T) {
const UT = @Int(.unsigned, @bitSizeOf(T));
return .{
@bitCast(@as(UT, @intCast(out))),
num,
};
}
fn readBitsNoEof(self: *ReverseBitReader, comptime T: type, num: u16) error{EndOfStream}!T {
const b, const c = try self.readBitsTuple(T, num);
if (c < num) return error.EndOfStream;
return b;
}
fn readBits(self: *ReverseBitReader, comptime T: type, num: u16, out_bits: *u16) !T {
const b, const c = try self.readBitsTuple(T, num);
out_bits.* = c;
return b;
}
fn readBitsTuple(self: *ReverseBitReader, comptime T: type, num: u16) !Bits(T) {
const UT = @Int(.unsigned, @bitSizeOf(T));
const U = if (@bitSizeOf(T) < 8) u8 else UT;
if (num <= self.count) return initBits(T, self.removeBits(@intCast(num)), num);
var out_count: u16 = self.count;
var out: U = self.removeBits(self.count);
const full_bytes_left = (num - out_count) / 8;
for (0..full_bytes_left) |_| {
const byte = takeByte(self) catch |err| switch (err) {
error.EndOfStream => return initBits(T, out, out_count),
};
if (U == u8) out = 0 else out <<= 8;
out |= byte;
out_count += 8;
}
const bits_left = num - out_count;
const keep = 8 - bits_left;
if (bits_left == 0) return initBits(T, out, out_count);
const final_byte = takeByte(self) catch |err| switch (err) {
error.EndOfStream => return initBits(T, out, out_count),
};
out <<= @intCast(bits_left);
out |= final_byte >> @intCast(keep);
self.bits = final_byte & low_bit_mask[keep];
self.count = @intCast(keep);
return initBits(T, out, num);
}
fn takeByte(rbr: *ReverseBitReader) error{EndOfStream}!u8 {
if (rbr.remaining == 0) return error.EndOfStream;
rbr.remaining -= 1;
return rbr.bytes[rbr.remaining];
}
fn isEmpty(self: *const ReverseBitReader) bool {
return self.remaining == 0 and self.count == 0;
}
fn removeBits(self: *ReverseBitReader, num: u4) u8 {
if (num == 8) {
self.count = 0;
return self.bits;
}
const keep = self.count - num;
const bits = self.bits >> @intCast(keep);
self.bits &= low_bit_mask[keep];
self.count = keep;
return bits;
}
}