Every compressor speaks in bits, not bytes, because its codes are rarely a whole number of bytes long. Today you build the substrate everything else needs - a bit writer that packs individual bits into bytes, high bit first, and pads the last byte when you flush.
Build a bit writer that accepts single bits and packs them into bytes, most significant bit first.
A compressor’s whole job is to spend fewer bits on common things, so it must be able to emit codes that are not a whole number of bits: a 3-bit code here, a 7-bit code there. Bytes are the wrong unit. The fix is a bit writer that accepts bits one at a time and packs them into bytes for you.
We pack most significant bit first: the first bit you write lands in the top
position of the byte (the 0x80 place), the next just below it, and so on. So
writing 1, 0, 1 builds the byte 10100000, which is 0xA0. When you
flush, any partially filled byte is emitted as-is, with the unused low bits
left as zero padding. That padding is harmless as long as the reader knows
how many real bits to expect, which is exactly what later lessons arrange. Pick a
bit order and state it plainly; the only rule is that the reader must use the
same one.
// fill each byte from the top bit (0x80) downwardtype BitWriter struct {out []bytecur byte // byte being fillednbit uint // bits used in cur, 0..7}func (w *BitWriter) WriteBit(b uint) {w.cur |= byte(b&1) << (7 - w.nbit) // place at the next high positionw.nbit++// when nbit reaches 8, append cur and reset (left as an exercise)}