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 130804ad589d36f0f864713b8f9802f0f3eeaf16..3bc7cedc44739424dd9ad7a9f9f7db6b5b0d51ad 100644 --- a/lib/allocator.h +++ b/lib/allocator.h @@ -5,7 +5,7 @@ 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 index 431c351c7b1a7e97c224436367d150bb130e6c0d..b1f91fe81b8fb2c30faab4c6922ab8b64e9f2535 100644 --- a/src/bitreader.c +++ b/src/bitreader.c @@ -1,14 +1,17 @@ #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(parse_state_t* state, int count) { +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... @@ -45,5 +48,65 @@ long long read_bits(parse_state_t* state, int count) { } count -= segment_len; } - return out; + 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 d6c73eeba73a435ce5439e5c0596809da8be6204..99b4578c11dcd13bba70b0933e0b87e95a8ec8ab 100644 --- a/src/hammer.h +++ b/src/hammer.h @@ -37,14 +37,21 @@ */ #define BYTE_BIG_ENDIAN 0x1 #define BIT_BIG_ENDIAN 0x2 +#define BIT_LITTLE_ENDIAN 0x0 +#define BYTE_LITTLE_ENDIAN 0x0 -typedef struct parse_state { +typedef struct input_stream { + // This should be considered to be a really big value type. const uint8_t *input; - GHashTable *cache; 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 { diff --git a/src/internal.h b/src/internal.h index 0657de0a08f269db8f856122f1153604e3dca92b..b678b563c0f31c4d97870fe2723f930b33004cc0 100644 --- a/src/internal.h +++ b/src/internal.h @@ -2,6 +2,9 @@ #define HAMMER_INTERNAL__H #include "hammer.h" -long long read_bits(parse_state_t* state, int count); +#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