diff --git a/src/Makefile b/src/Makefile
index 9c69124241ca1148c6152c6d49344c5029cfc0b5..24408ccae11ab69b84ee5f1c24275138e6660312 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -25,6 +25,7 @@ PARSERS := \
 
 OUTPUTS := bitreader.o \
 	   hammer.o \
+	   bitwriter.o \
 	   libhammer.a \
 	   pprint.o \
 	   allocator.o \
@@ -42,7 +43,7 @@ all: libhammer.a test_suite
 test_suite: test_suite.o libhammer.a
 	$(call hush, "Linking $@") $(CC) -o $@ $^ $(LDFLAGS)
 
-libhammer.a: bitreader.o hammer.o pprint.o allocator.o datastructures.o \
+libhammer.a: bitreader.o hammer.o pprint.o allocator.o datastructures.o bitwriter.o \
 	$(PARSERS:%=parsers/%.o)
 
 bitreader.o: test_suite.h
diff --git a/src/bitwriter.c b/src/bitwriter.c
new file mode 100644
index 0000000000000000000000000000000000000000..f307caa24585723ae8886093b19ee8ecb59799ac
--- /dev/null
+++ b/src/bitwriter.c
@@ -0,0 +1,93 @@
+#include <string.h>
+#include <assert.h>
+#include "hammer.h"
+#include "internal.h"
+
+// This file provides the logical inverse of bitreader.c
+struct HBitWriter_ {
+  uint8_t* buf;
+  size_t index;
+  size_t capacity;
+  char bit_offset; // unlike in bit_reader, this is always the number
+		   // of used bits in the current byte. i.e., 0 always
+		   // means that 8 bits are available for use.
+  char flags;
+};
+
+// h_bit_writer_
+HBitWriter *h_bit_writer_new() {
+  HBitWriter *writer = g_new0(HBitWriter, 1);
+  writer->buf = g_malloc0(writer->capacity = 8);
+
+  writer->flags = BYTE_BIG_ENDIAN | BIT_BIG_ENDIAN;
+
+  return writer;
+}
+
+/**
+ * Ensure there are at least [nbits] bits available at the end of the
+ * buffer. If the buffer is expanded, the added bits should be zeroed.
+ */
+static void h_bit_writer_reserve(HBitWriter* w, size_t nbits) {
+  // As is, this may overshoot by a byte, e.g., nbits=9, bit_offset=1.
+  // This will assume that the last partial byte is full, and reserve
+  // 2 bytes at the end, whereas only one is necessary.
+  //
+  // That said, this guarantees the postcondition that w->buf[w->index]
+  // is valid.
+
+  // Round up to bytes
+  int nbytes = (nbits + 7) / 8 + ((w->bit_offset != 0) ? 1 : 0);
+  size_t old_capacity = w->capacity;
+  while (w->index + nbytes >= w->capacity) {
+    w->buf = g_realloc(w->buf, w->capacity *= 2);
+  }
+
+  if (old_capacity != w->capacity)
+    memset(w->buf + old_capacity, 0, w->capacity - old_capacity);
+}
+
+
+void h_bit_writer_put(HBitWriter* w, unsigned long long data, size_t nbits) {
+  assert(nbits > 0); // Less than or equal to zero makes complete nonsense
+
+  // expand size...
+  h_bit_writer_reserve(w, nbits);
+
+  while (nbits) {
+    size_t count = MIN((size_t)(8 - w->bit_offset), nbits);
+
+    // select the bits to be written from the source
+    uint16_t bits;
+    if (w->flags & BYTE_BIG_ENDIAN) {
+      // read from the top few bits; masking will be done later.
+      bits = data >> (nbits - count);
+    } else {
+      // just copy the bottom byte over.
+      bits = data & 0xff;
+      data >>= count; // remove the bits that have just been used.
+    }
+    // mask off the unnecessary bits.
+    bits &= (1 << count) - 1;
+
+    // Now, push those bits onto the current byte...
+    if (w->flags & BIT_BIG_ENDIAN)
+      w->buf[w->index] = (w->buf[w->index] << count) | bits;
+    else
+      w->buf[w->index] = (w->buf[w->index] | (bits << 8)) >> count;
+
+    // update index and bit_offset.
+    w->bit_offset += count;
+    if (w->bit_offset == 8) {
+      w->bit_offset = 0;
+      w->index++;
+    }
+    nbits -= count;
+  }
+
+}
+
+
+uint8_t *h_bit_writer_get_buffer(HBitWriter* w, size_t *len);
+
+// TESTS BELOW HERE
diff --git a/src/hammer.h b/src/hammer.h
index 62b5676610415b0a03a9546cea7b829b3477d658..8df48a8cd407c7a9c7c79d9173d974efd7e480b6 100644
--- a/src/hammer.h
+++ b/src/hammer.h
@@ -79,6 +79,12 @@ typedef struct HParseResult_ {
   HArena * arena;
 } HParseResult;
 
+/**
+ * TODO: document me.
+ * Relevant functions: h_bit_writer_new, h_bit_writer_put, h_bit_writer_get_buffer, h_bit_writer_free
+ */
+typedef struct HBitWriter_ HBitWriter;
+
 /**
  * Type of an action to apply to an AST, used in the action() parser. 
  * It can be any (user-defined) function that takes a HParseResult*
@@ -449,4 +455,26 @@ char* h_write_result_unamb(const HParsedToken* tok);
  */
 void h_pprint(FILE* stream, const HParsedToken* tok, int indent, int delta);
 
+/**
+ * TODO: Document me
+ */
+HBitWriter *h_bit_writer_new(void);
+
+/**
+ * TODO: Document me
+ */
+void h_bit_writer_put(HBitWriter* w, unsigned long long data, size_t nbits);
+
+/**
+ * TODO: Document me
+ * Must not free [w] until you're done with the result.
+ * [len] is in bytes.
+ */
+uint8_t *h_bit_writer_get_buffer(HBitWriter* w, size_t *len);
+
+/**
+ * TODO: Document me
+ */
+void h_bit_writer_free(HBitWriter* w);
+
 #endif // #ifndef HAMMER_HAMMER__H