From 7adae0da7cca766207ea6f940fc20ef4cd4901c9 Mon Sep 17 00:00:00 2001 From: Dan Hirsch Date: Mon, 23 Apr 2012 19:39:44 +0100 Subject: [PATCH 1/2] Wrote bitwise reader; needs tests --- lib/allocator.h | 4 ++-- src/bitreader.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ src/hammer.h | 12 +++++++++--- src/internal.h | 7 +++++++ 4 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 src/bitreader.c create mode 100644 src/internal.h diff --git a/lib/allocator.h b/lib/allocator.h index 3cea590..130804a 100644 --- a/lib/allocator.h +++ b/lib/allocator.h @@ -1,5 +1,5 @@ -#ifndef LIB_ALLOCATOR__H__ -#define LIB_ALLOCATOR__H__ +#ifndef HAMMER_ALLOCATOR__H__ +#define HAMMER_ALLOCATOR__H__ #include typedef struct arena* arena_t; // hidden implementation diff --git a/src/bitreader.c b/src/bitreader.c new file mode 100644 index 0000000..431c351 --- /dev/null +++ b/src/bitreader.c @@ -0,0 +1,49 @@ +#include +#include "internal.h" +#include "hammer.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 out = 0; + int offset = 0; + 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; +} diff --git a/src/hammer.h b/src/hammer.h index bbacb13..d6c73ee 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 #include @@ -33,11 +35,16 @@ * at which it's been applied are memoized. * */ +#define BYTE_BIG_ENDIAN 0x1 +#define BIT_BIG_ENDIAN 0x2 + typedef struct parse_state { const uint8_t *input; + GHashTable *cache; size_t index; size_t length; - GHashTable *cache; + char bit_offset; + char endianness; } parse_state_t; typedef struct parse_result { @@ -81,5 +88,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 0000000..0657de0 --- /dev/null +++ b/src/internal.h @@ -0,0 +1,7 @@ +#ifndef HAMMER_INTERNAL__H +#define HAMMER_INTERNAL__H +#include "hammer.h" + +long long read_bits(parse_state_t* state, int count); + +#endif // #ifndef HAMMER_INTERNAL__H From faab7b7cf89f48a745e9cd4840b572be8d5b396a Mon Sep 17 00:00:00 2001 From: Dan Hirsch Date: Sun, 29 Apr 2012 01:45:52 +0100 Subject: [PATCH 2/2] Wrote basic makefile, wrote tests, and then fixed them --- .gitignore | 2 ++ common.mk | 14 ++++++++++ config.mk | 2 -- lib/Makefile | 4 +-- lib/allocator.h | 2 +- src/Makefile | 10 ++++++++ src/bitreader.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++-- src/hammer.h | 11 ++++++-- src/internal.h | 5 +++- src/test_suite.c | 14 ++++++++++ src/test_suite.h | 17 ++++++++++++ 11 files changed, 137 insertions(+), 11 deletions(-) create mode 100644 common.mk delete mode 100644 config.mk create mode 100644 src/Makefile create mode 100644 src/test_suite.c create mode 100644 src/test_suite.h diff --git a/.gitignore b/.gitignore index a70237a..7bd36bf 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 0000000..c048330 --- /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 16209be..0000000 --- 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 de2a9af..e8c0b51 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 130804a..3bc7ced 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 0000000..319cc72 --- /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 431c351..b1f91fe 100644 --- a/src/bitreader.c +++ b/src/bitreader.c @@ -1,14 +1,17 @@ #include +#include #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 d6c73ee..99b4578 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 0657de0..b678b56 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 0000000..f519245 --- /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 0000000..914f4c4 --- /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