const std = @import("std"); const assert = std.debug.assert; /// Bit writer for use in deflate (compression). /// /// Has internal bits buffer of 64 bits and internal bytes buffer of 248 bytes. /// When we accumulate 48 bits 6 bytes are moved to the bytes buffer. When we /// accumulate 240 bytes they are flushed to the underlying inner_writer. /// pub fn BitWriter(comptime WriterType: type) type { // buffer_flush_size indicates the buffer size // after which bytes are flushed to the writer. // Should preferably be a multiple of 6, since // we accumulate 6 bytes between writes to the buffer. const buffer_flush_size = 240; // buffer_size is the actual output byte buffer size. // It must have additional headroom for a flush // which can contain up to 8 bytes. const buffer_size = buffer_flush_size + 8; return struct { inner_writer: WriterType, // Data waiting to be written is bytes[0 .. nbytes] // and then the low nbits of bits. Data is always written // sequentially into the bytes array. bits: u64 = 0, nbits: u32 = 0, // number of bits bytes: [buffer_size]u8 = undefined, nbytes: u32 = 0, // number of bytes const Self = @This(); pub const Error = WriterType.Error || error{UnfinishedBits}; pub fn init(writer: WriterType) Self { return .{ .inner_writer = writer }; } pub fn setWriter(self: *Self, new_writer: WriterType) void { //assert(self.bits == 0 and self.nbits == 0 and self.nbytes == 0); self.inner_writer = new_writer; } pub fn flush(self: *Self) Error!void { var n = self.nbytes; while (self.nbits != 0) { self.bytes[n] = @as(u8, @truncate(self.bits)); self.bits >>= 8; if (self.nbits > 8) { // Avoid underflow self.nbits -= 8; } else { self.nbits = 0; } n += 1; } self.bits = 0; _ = try self.inner_writer.write(self.bytes[0..n]); self.nbytes = 0; } pub fn writeBits(self: *Self, b: u32, nb: u32) Error!void { self.bits |= @as(u64, @intCast(b)) << @as(u6, @intCast(self.nbits)); self.nbits += nb; if (self.nbits < 48) return; var n = self.nbytes; std.mem.writeInt(u64, self.bytes[n..][0..8], self.bits, .little); n += 6; if (n >= buffer_flush_size) { _ = try self.inner_writer.write(self.bytes[0..n]); n = 0; } self.nbytes = n; self.bits >>= 48; self.nbits -= 48; } pub fn writeBytes(self: *Self, bytes: []const u8) Error!void { var n = self.nbytes; if (self.nbits & 7 != 0) { return error.UnfinishedBits; } while (self.nbits != 0) { self.bytes[n] = @as(u8, @truncate(self.bits)); self.bits >>= 8; self.nbits -= 8; n += 1; } if (n != 0) { _ = try self.inner_writer.write(self.bytes[0..n]); } self.nbytes = 0; _ = try self.inner_writer.write(bytes); } }; }