diff --git a/NOTES b/NOTES index c3a4cb6d7b8021fb12b95260276162e76e071750..84b8c4637aea271c54bb505aae4680208c3610f4 100644 --- a/NOTES +++ b/NOTES @@ -35,4 +35,4 @@ what the comments say. TODO: implement datastructure linearization func TODO: implement free func for parsers -TODO: Reimplement GSequence et al for use with arena allocator. (*&%$#@ Glib doesn't allow per-datastructure allocators) +TODO: Remove glib dependency (i.e., GQueue and GHashtable) \ No newline at end of file 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.c b/src/hammer.c index adcc210b501045b205d3cf42553baf586a5ae17c..82ca235243b90c5c16dda2953e75056d7d382577 100644 --- a/src/hammer.c +++ b/src/hammer.c @@ -44,6 +44,7 @@ static HCachedResult *cached_result(const HParseState *state, HParseResult *resu // Really library-internal tool to perform an uncached parse, and handle any common error-handling. static inline HParseResult* perform_lowlevel_parse(HParseState *state, const HParser *parser) { + // TODO(thequux): these nested conditions are ugly. Factor this appropriately, so that it is clear which codes is executed when. HParseResult *tmp_res; if (parser) { HInputStream bak = state->input_stream; diff --git a/src/hammer.h b/src/hammer.h index e8db7fd8ebdc69c7f7bd0d79886e2b3330b01e92..af0a84a7862e226fb986a0d5ae5b7b08bfab4fe6 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* @@ -454,4 +460,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