diff --git a/.gitignore b/.gitignore index a70237a6305fd373ddb12639d7b2eddd4f90a774..7bd36bf93b53402a42c0988343b536a38f63139e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ *.o *~ +*.a +src/test_suite diff --git a/common.mk b/common.mk new file mode 100644 index 0000000000000000000000000000000000000000..c048330de19404513c3a033889ba4cf329374aa9 --- /dev/null +++ b/common.mk @@ -0,0 +1,14 @@ +CFLAGS := $(shell pkg-config --cflags glib-2.0) +LDFLAGS := $(shell pkg-config --libs glib-2.0) +CC := gcc + +CFLAGS += -DINCLUDE_TESTS + +.SUFFIX: + +%.a: + -rm -f $@ + ar crv $@ $^ + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< \ No newline at end of file diff --git a/config.mk b/config.mk deleted file mode 100644 index 16209be97db2ac81dff0c1f0e58884aa2398817b..0000000000000000000000000000000000000000 --- a/config.mk +++ /dev/null @@ -1,2 +0,0 @@ -CFLAGS := $(shell pkg-config --cflags glib-2.0) -LDFLAGS := $(shell pkg-config --libs glib-2.0) \ No newline at end of file diff --git a/lib/Makefile b/lib/Makefile index de2a9af10396f3205f4bc30a75cf8eeb4278d850..e8c0b5137d1e72d25d0262cb0caf2a831c9b4c68 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -1,6 +1,4 @@ -include ../config.mk +include ../common.mk all: allocator.o -%.o: %.c - gcc $(CFLAGS) -c -o $@ $^ \ No newline at end of file diff --git a/lib/allocator.h b/lib/allocator.h index 3cea5906f1df279f0cfa6592515ac5d4c6d300f3..3bc7cedc44739424dd9ad7a9f9f7db6b5b0d51ad 100644 --- a/lib/allocator.h +++ b/lib/allocator.h @@ -1,11 +1,11 @@ -#ifndef LIB_ALLOCATOR__H__ -#define LIB_ALLOCATOR__H__ +#ifndef HAMMER_ALLOCATOR__H__ +#define HAMMER_ALLOCATOR__H__ #include <sys/types.h> typedef struct arena* arena_t; // hidden implementation arena_t new_arena(size_t block_size); // pass 0 for default... -void* arena_malloc(arena_t arena, size_t count); // TODO: needs the malloc attribute +void* arena_malloc(arena_t arena, size_t count) __attribute__(( malloc, alloc_size(2) )); void delete_arena(arena_t arena); diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..319cc722be1306fae4feae99f44333d9fd60b829 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,10 @@ +-include ../common.mk + +all: libhammer.a test_suite + +test_suite: test_suite.o libhammer.a + $(CC) $(LDFLAGS) -o $@ $^ + +libhammer.a: bitreader.o + +bitreader.o: test_suite.h diff --git a/src/bitreader.c b/src/bitreader.c new file mode 100644 index 0000000000000000000000000000000000000000..b1f91fe81b8fb2c30faab4c6922ab8b64e9f2535 --- /dev/null +++ b/src/bitreader.c @@ -0,0 +1,112 @@ +#include <stdint.h> +#include <stdio.h> +#include "internal.h" +#include "hammer.h" +#include "test_suite.h" + +#define LSB(range) (0:range) +#define MSB(range) (1:range) +#define LDB(range,i) (((i)>>LSB(range))&((1<<(MSB(range)-LSB(range)+1))-1)) + +long long read_bits(input_stream_t* state, int count, char signed_p) { + long long out = 0; + int offset = 0; + long long msb = (!!signed_p) << (count - 1); // 0 if unsigned, else 1 << (nbits - 1) + while (count) { + int segment, segment_len; + // Read a segment... + if (state->endianness & BIT_BIG_ENDIAN) { + if (count >= state->bit_offset) { + segment_len = state->bit_offset; + state->bit_offset = 8; + segment = state->input[state->index] & ((1 << segment_len) - 1); + state->index++; + } else { + segment_len = count; + state->bit_offset -= count; + segment = (state->input[state->index] >> state->bit_offset) & ((1 << segment_len) - 1); + } + } else { // BIT_LITTLE_ENDIAN + if (count + state->bit_offset >= 8) { + segment_len = 8 - state->bit_offset; + segment = (state->input[state->index] >> state->bit_offset); + state->index++; + state->bit_offset = 0; + } else { + segment_len = count; + segment = (state->input[state->index] >> state->bit_offset) & ((1 << segment_len) - 1); + state->bit_offset += segment_len; + } + } + + // have a valid segment; time to assemble the byte + if (state->endianness & BYTE_BIG_ENDIAN) { + out = out << segment_len | segment; + } else { // BYTE_LITTLE_ENDIAN + out |= segment << offset; + offset += segment_len; + } + count -= segment_len; + } + return (out ^ msb) - msb; // perform sign extension +} + +#ifdef INCLUDE_TESTS + +#define MK_INPUT_STREAM(buf,len,endianness_) \ + { \ + .input = buf, \ + .length = len, \ + .index = 0, \ + .bit_offset = (((endianness_) & BIT_BIG_ENDIAN) ? 8 : 0), \ + .endianness = endianness_ \ + } + +static void test_bitreader_be(void) { + input_stream_t is = MK_INPUT_STREAM("\x6A\x5A", 2, BIT_BIG_ENDIAN | BYTE_BIG_ENDIAN); + g_check_cmpint(read_bits(&is, 3, false), ==, 0x03); + g_check_cmpint(read_bits(&is, 8, false), ==, 0x52); + g_check_cmpint(read_bits(&is, 5, false), ==, 0x1A); +} +static void test_bitreader_le(void) { + input_stream_t is = MK_INPUT_STREAM("\x6A\x5A", 2, BIT_LITTLE_ENDIAN | BYTE_LITTLE_ENDIAN); + g_check_cmpint(read_bits(&is, 3, false), ==, 0x02); + g_check_cmpint(read_bits(&is, 8, false), ==, 0x4D); + g_check_cmpint(read_bits(&is, 5, false), ==, 0x0B); +} + +static void test_largebits_be(void) { + input_stream_t is = MK_INPUT_STREAM("\x6A\x5A", 2, BIT_BIG_ENDIAN | BYTE_BIG_ENDIAN); + g_check_cmpint(read_bits(&is, 11, false), ==, 0x352); + g_check_cmpint(read_bits(&is, 5, false), ==, 0x1A); +} + +static void test_largebits_le(void) { + input_stream_t is = MK_INPUT_STREAM("\x6A\x5A", 2, BIT_LITTLE_ENDIAN | BYTE_LITTLE_ENDIAN); + g_check_cmpint(read_bits(&is, 11, false), ==, 0x26A); + g_check_cmpint(read_bits(&is, 5, false), ==, 0x0B); +} + +static void test_offset_largebits_be(void) { + input_stream_t is = MK_INPUT_STREAM("\x6A\x5A", 2, BIT_BIG_ENDIAN | BYTE_BIG_ENDIAN); + g_check_cmpint(read_bits(&is, 5, false), ==, 0xD); + g_check_cmpint(read_bits(&is, 11, false), ==, 0x25A); +} + +static void test_offset_largebits_le(void) { + input_stream_t is = MK_INPUT_STREAM("\x6A\x5A", 2, BIT_LITTLE_ENDIAN | BYTE_LITTLE_ENDIAN); + g_check_cmpint(read_bits(&is, 5, false), ==, 0xA); + g_check_cmpint(read_bits(&is, 11, false), ==, 0x2D3); +} + + +void register_bitreader_tests(void) { + g_test_add_func("/core/bitreader/be", test_bitreader_be); + g_test_add_func("/core/bitreader/le", test_bitreader_le); + g_test_add_func("/core/bitreader/largebits-be", test_largebits_be); + g_test_add_func("/core/bitreader/largebits-le", test_largebits_le); + g_test_add_func("/core/bitreader/offset-largebits-be", test_offset_largebits_be); + g_test_add_func("/core/bitreader/offset-largebits-le", test_offset_largebits_le); +} + +#endif // #ifdef INCLUDE_TESTS diff --git a/src/hammer.h b/src/hammer.h index bbacb13d416b7b3ed85d09ffec5a6a05f1967833..99b4578c11dcd13bba70b0933e0b87e95a8ec8ab 100644 --- a/src/hammer.h +++ b/src/hammer.h @@ -15,6 +15,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#ifndef HAMMER_HAMMER__H +#define HAMMER_HAMMER__H #include <glib.h> #include <stdint.h> @@ -33,11 +35,23 @@ * at which it's been applied are memoized. * */ -typedef struct parse_state { +#define BYTE_BIG_ENDIAN 0x1 +#define BIT_BIG_ENDIAN 0x2 +#define BIT_LITTLE_ENDIAN 0x0 +#define BYTE_LITTLE_ENDIAN 0x0 + +typedef struct input_stream { + // This should be considered to be a really big value type. const uint8_t *input; size_t index; size_t length; + char bit_offset; + char endianness; +} input_stream_t; + +typedef struct parse_state { GHashTable *cache; + input_stream_t input_stream; } parse_state_t; typedef struct parse_result { @@ -81,5 +95,4 @@ parser_t* epsilon_p(); parser_t* and(parser_t* p); parser_t* not(parser_t* p); - - +#endif // #ifndef HAMMER_HAMMER__H diff --git a/src/internal.h b/src/internal.h new file mode 100644 index 0000000000000000000000000000000000000000..b678b563c0f31c4d97870fe2723f930b33004cc0 --- /dev/null +++ b/src/internal.h @@ -0,0 +1,10 @@ +#ifndef HAMMER_INTERNAL__H +#define HAMMER_INTERNAL__H +#include "hammer.h" + +#define false 0 +#define true 1 + +long long read_bits(input_stream_t* state, int count, char signed_p); + +#endif // #ifndef HAMMER_INTERNAL__H diff --git a/src/test_suite.c b/src/test_suite.c new file mode 100644 index 0000000000000000000000000000000000000000..f519245624d7920b0b3e0c8bdfca1ebf4f3138e4 --- /dev/null +++ b/src/test_suite.c @@ -0,0 +1,14 @@ +#include "hammer.h" +#include "test_suite.h" + +extern void register_bitreader_tests(); + +int main(int argc, char** argv) { + g_test_init(&argc, &argv, NULL); + + // register various test suites... + register_bitreader_tests(); + + g_test_run(); +} + diff --git a/src/test_suite.h b/src/test_suite.h new file mode 100644 index 0000000000000000000000000000000000000000..914f4c43b7517a834fba557fd85ec3c0d2b2aeb6 --- /dev/null +++ b/src/test_suite.h @@ -0,0 +1,17 @@ +#ifndef HAMMER_TEST_SUITE__H +#define HAMMER_TEST_SUITE__H + +// Equivalent to g_assert_*, but not using g_assert... +#define g_check_cmpint(n1, op, n2) { \ + typeof (n1) _n1 = (n1); \ + typeof (n2) _n2 = (n2); \ + if (!(_n1 op _n2)) { \ + g_test_message("Check failed: (%s): (%d %s %d)", \ + #n1 " " #op " " #n2, \ + _n1, #op, _n2); \ + g_test_fail(); \ + } \ + } + + +#endif // #ifndef HAMMER_TEST_SUITE__H