| ... | @@ -1,179 +0,0 @@ |
| 1 | const std = @import("../std.zig"); |
| 2 | |
| 3 | //General note on endianess: |
| 4 | //Big endian is packed starting in the most significant part of the byte and subsequent |
| 5 | // bytes contain less significant bits. Thus we write out bits from the high end |
| 6 | // of our input first. |
| 7 | //Little endian is packed starting in the least significant part of the byte and |
| 8 | // subsequent bytes contain more significant bits. Thus we write out bits from |
| 9 | // the low end of our input first. |
| 10 | //Regardless of endianess, within any given byte the bits are always in most |
| 11 | // to least significant order. |
| 12 | //Also regardless of endianess, the buffer always aligns bits to the low end |
| 13 | // of the byte. |
| 14 | |
| 15 | /// Creates a bit writer which allows for writing bits to an underlying standard writer |
| 16 | pub fn BitWriter(comptime endian: std.builtin.Endian, comptime Writer: type) type { |
| 17 | return struct { |
| 18 | writer: Writer, |
| 19 | bits: u8 = 0, |
| 20 | count: u4 = 0, |
| 21 | |
| 22 | const low_bit_mask = [9]u8{ |
| 23 | 0b00000000, |
| 24 | 0b00000001, |
| 25 | 0b00000011, |
| 26 | 0b00000111, |
| 27 | 0b00001111, |
| 28 | 0b00011111, |
| 29 | 0b00111111, |
| 30 | 0b01111111, |
| 31 | 0b11111111, |
| 32 | }; |
| 33 | |
| 34 | /// Write the specified number of bits to the writer from the least significant bits of |
| 35 | /// the specified value. Bits will only be written to the writer when there |
| 36 | /// are enough to fill a byte. |
| 37 | pub fn writeBits(self: *@This(), value: anytype, num: u16) !void { |
| 38 | const T = @TypeOf(value); |
| 39 | const UT = std.meta.Int(.unsigned, @bitSizeOf(T)); |
| 40 | const U = if (@bitSizeOf(T) < 8) u8 else UT; //<u8 is a pain to work with |
| 41 | |
| 42 | var in: U = @as(UT, @bitCast(value)); |
| 43 | var in_count: u16 = num; |
| 44 | |
| 45 | if (self.count > 0) { |
| 46 | //if we can't fill the buffer, add what we have |
| 47 | const bits_free = 8 - self.count; |
| 48 | if (num < bits_free) { |
| 49 | self.addBits(@truncate(in), @intCast(num)); |
| 50 | return; |
| 51 | } |
| 52 | |
| 53 | //finish filling the buffer and flush it |
| 54 | if (num == bits_free) { |
| 55 | self.addBits(@truncate(in), @intCast(num)); |
| 56 | return self.flushBits(); |
| 57 | } |
| 58 | |
| 59 | switch (endian) { |
| 60 | .big => { |
| 61 | const bits = in >> @intCast(in_count - bits_free); |
| 62 | self.addBits(@truncate(bits), bits_free); |
| 63 | }, |
| 64 | .little => { |
| 65 | self.addBits(@truncate(in), bits_free); |
| 66 | in >>= @intCast(bits_free); |
| 67 | }, |
| 68 | } |
| 69 | in_count -= bits_free; |
| 70 | try self.flushBits(); |
| 71 | } |
| 72 | |
| 73 | //write full bytes while we can |
| 74 | const full_bytes_left = in_count / 8; |
| 75 | for (0..full_bytes_left) |_| { |
| 76 | switch (endian) { |
| 77 | .big => { |
| 78 | const bits = in >> @intCast(in_count - 8); |
| 79 | try self.writer.writeByte(@truncate(bits)); |
| 80 | }, |
| 81 | .little => { |
| 82 | try self.writer.writeByte(@truncate(in)); |
| 83 | if (U == u8) in = 0 else in >>= 8; |
| 84 | }, |
| 85 | } |
| 86 | in_count -= 8; |
| 87 | } |
| 88 | |
| 89 | //save the remaining bits in the buffer |
| 90 | self.addBits(@truncate(in), @intCast(in_count)); |
| 91 | } |
| 92 | |
| 93 | //convenience funciton for adding bits to the buffer |
| 94 | //in the appropriate position based on endianess |
| 95 | fn addBits(self: *@This(), bits: u8, num: u4) void { |
| 96 | if (num == 8) self.bits = bits else switch (endian) { |
| 97 | .big => { |
| 98 | self.bits <<= @intCast(num); |
| 99 | self.bits |= bits & low_bit_mask[num]; |
| 100 | }, |
| 101 | .little => { |
| 102 | const pos = bits << @intCast(self.count); |
| 103 | self.bits |= pos; |
| 104 | }, |
| 105 | } |
| 106 | self.count += num; |
| 107 | } |
| 108 | |
| 109 | /// Flush any remaining bits to the writer, filling |
| 110 | /// unused bits with 0s. |
| 111 | pub fn flushBits(self: *@This()) !void { |
| 112 | if (self.count == 0) return; |
| 113 | if (endian == .big) self.bits <<= @intCast(8 - self.count); |
| 114 | try self.writer.writeByte(self.bits); |
| 115 | self.bits = 0; |
| 116 | self.count = 0; |
| 117 | } |
| 118 | }; |
| 119 | } |
| 120 | |
| 121 | pub fn bitWriter(comptime endian: std.builtin.Endian, writer: anytype) BitWriter(endian, @TypeOf(writer)) { |
| 122 | return .{ .writer = writer }; |
| 123 | } |
| 124 | |
| 125 | /////////////////////////////// |
| 126 | |
| 127 | test "api coverage" { |
| 128 | var mem_be = [_]u8{0} ** 2; |
| 129 | var mem_le = [_]u8{0} ** 2; |
| 130 | |
| 131 | var mem_out_be = std.io.fixedBufferStream(&mem_be); |
| 132 | var bit_stream_be = bitWriter(.big, mem_out_be.writer()); |
| 133 | |
| 134 | const testing = std.testing; |
| 135 | |
| 136 | try bit_stream_be.writeBits(@as(u2, 1), 1); |
| 137 | try bit_stream_be.writeBits(@as(u5, 2), 2); |
| 138 | try bit_stream_be.writeBits(@as(u128, 3), 3); |
| 139 | try bit_stream_be.writeBits(@as(u8, 4), 4); |
| 140 | try bit_stream_be.writeBits(@as(u9, 5), 5); |
| 141 | try bit_stream_be.writeBits(@as(u1, 1), 1); |
| 142 | |
| 143 | try testing.expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001011); |
| 144 | |
| 145 | mem_out_be.pos = 0; |
| 146 | |
| 147 | try bit_stream_be.writeBits(@as(u15, 0b110011010000101), 15); |
| 148 | try bit_stream_be.flushBits(); |
| 149 | try testing.expect(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001010); |
| 150 | |
| 151 | mem_out_be.pos = 0; |
| 152 | try bit_stream_be.writeBits(@as(u32, 0b110011010000101), 16); |
| 153 | try testing.expect(mem_be[0] == 0b01100110 and mem_be[1] == 0b10000101); |
| 154 | |
| 155 | try bit_stream_be.writeBits(@as(u0, 0), 0); |
| 156 | |
| 157 | var mem_out_le = std.io.fixedBufferStream(&mem_le); |
| 158 | var bit_stream_le = bitWriter(.little, mem_out_le.writer()); |
| 159 | |
| 160 | try bit_stream_le.writeBits(@as(u2, 1), 1); |
| 161 | try bit_stream_le.writeBits(@as(u5, 2), 2); |
| 162 | try bit_stream_le.writeBits(@as(u128, 3), 3); |
| 163 | try bit_stream_le.writeBits(@as(u8, 4), 4); |
| 164 | try bit_stream_le.writeBits(@as(u9, 5), 5); |
| 165 | try bit_stream_le.writeBits(@as(u1, 1), 1); |
| 166 | |
| 167 | try testing.expect(mem_le[0] == 0b00011101 and mem_le[1] == 0b10010101); |
| 168 | |
| 169 | mem_out_le.pos = 0; |
| 170 | try bit_stream_le.writeBits(@as(u15, 0b110011010000101), 15); |
| 171 | try bit_stream_le.flushBits(); |
| 172 | try testing.expect(mem_le[0] == 0b10000101 and mem_le[1] == 0b01100110); |
| 173 | |
| 174 | mem_out_le.pos = 0; |
| 175 | try bit_stream_le.writeBits(@as(u32, 0b1100110100001011), 16); |
| 176 | try testing.expect(mem_le[0] == 0b00001011 and mem_le[1] == 0b11001101); |
| 177 | |
| 178 | try bit_stream_le.writeBits(@as(u0, 0), 0); |
| 179 | } |