Type Function BitReader [src]

Alias for std.io.bit_reader.BitReader

Creates a bit reader which allows for reading bits from an underlying standard reader

Prototype

pub fn BitReader(comptime endian: std.builtin.Endian, comptime Reader: type) type

Parameters

endian: std.builtin.EndianReader: type

Source

pub fn BitReader(comptime endian: std.builtin.Endian, comptime Reader: type) type { return struct { reader: Reader, bits: u8 = 0, count: u4 = 0, const low_bit_mask = [9]u8{ 0b00000000, 0b00000001, 0b00000011, 0b00000111, 0b00001111, 0b00011111, 0b00111111, 0b01111111, 0b11111111, }; fn Bits(comptime T: type) type { return struct { T, u16, }; } fn initBits(comptime T: type, out: anytype, num: u16) Bits(T) { const UT = std.meta.Int(.unsigned, @bitSizeOf(T)); return .{ @bitCast(@as(UT, @intCast(out))), num, }; } /// Reads `bits` bits from the reader and returns a specified type /// containing them in the least significant end, returning an error if the /// specified number of bits could not be read. pub fn readBitsNoEof(self: *@This(), comptime T: type, num: u16) !T { const b, const c = try self.readBitsTuple(T, num); if (c < num) return error.EndOfStream; return b; } /// Reads `bits` bits from the reader and returns a specified type /// containing them in the least significant end. The number of bits successfully /// read is placed in `out_bits`, as reaching the end of the stream is not an error. pub fn readBits(self: *@This(), comptime T: type, num: u16, out_bits: *u16) !T { const b, const c = try self.readBitsTuple(T, num); out_bits.* = c; return b; } /// Reads `bits` bits from the reader and returns a tuple of the specified type /// containing them in the least significant end, and the number of bits successfully /// read. Reaching the end of the stream is not an error. pub fn readBitsTuple(self: *@This(), comptime T: type, num: u16) !Bits(T) { const UT = std.meta.Int(.unsigned, @bitSizeOf(T)); const U = if (@bitSizeOf(T) < 8) u8 else UT; //it is a pain to work with return initBits(T, out, out_count), else => |e| return e, }; switch (endian) { .big => { if (U == u8) out = 0 else out <<= 8; //shifting u8 by 8 is illegal in Zig out |= byte; }, .little => { const pos = @as(U, byte) << @intCast(out_count); out |= pos; }, } 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 = self.reader.readByte() catch |err| switch (err) { error.EndOfStream => return initBits(T, out, out_count), else => |e| return e, }; switch (endian) { .big => { out <<= @intCast(bits_left); out |= final_byte >> @intCast(keep); self.bits = final_byte & low_bit_mask[keep]; }, .little => { const pos = @as(U, final_byte & low_bit_mask[bits_left]) << @intCast(out_count); out |= pos; self.bits = final_byte >> @intCast(bits_left); }, } self.count = @intCast(keep); return initBits(T, out, num); } //convenience function for removing bits from //the appropriate part of the buffer based on //endianess. fn removeBits(self: *@This(), num: u4) u8 { if (num == 8) { self.count = 0; return self.bits; } const keep = self.count - num; const bits = switch (endian) { .big => self.bits >> @intCast(keep), .little => self.bits & low_bit_mask[num], }; switch (endian) { .big => self.bits &= low_bit_mask[keep], .little => self.bits >>= @intCast(num), } self.count = keep; return bits; } pub fn alignToByte(self: *@This()) void { self.bits = 0; self.count = 0; } }; }