From 8cc267345c011c6cdb0d05f7b99a09d26cdcf83f Mon Sep 17 00:00:00 2001 From: Anders Blomdell <anders.blomdell@control.lth.se> Date: Mon, 22 Apr 2013 17:00:33 +0200 Subject: [PATCH] Changed encoding/decoding of packed32 type to be big endian. Testcase for some of the encoding/decoding functions. --- lib/c/Makefile | 14 +-- lib/c/labcomm_dynamic_buffer_writer.h | 4 - lib/c/labcomm_private.h | 133 ++++++++++++++++-------- lib/c/test/test_labcomm_private.c | 142 ++++++++++++++++++++++++++ 4 files changed, 240 insertions(+), 53 deletions(-) create mode 100644 lib/c/test/test_labcomm_private.c diff --git a/lib/c/Makefile b/lib/c/Makefile index a4868f1..dd08db0 100644 --- a/lib/c/Makefile +++ b/lib/c/Makefile @@ -2,7 +2,7 @@ # Use LLVM clang if it's found. CC = $(shell hash clang 2>/dev/null && echo clang || echo gcc) -CFLAGS = -g -Wall -Werror -I. -Itest +CFLAGS = -g -Wall -Werror -O3 -I. -Itest LDFLAGS = -L. LDLIBS_TEST = -lcunit -llabcomm @@ -10,6 +10,7 @@ OBJS= labcomm.o labcomm_dynamic_buffer_writer.o labcomm_fd_reader_writer.o labco LABCOMMC_PATH=../../compiler LABCOMMC_JAR=$(LABCOMMC_PATH)/labComm.jar +TESTS=test_labcomm_private test_labcomm test_labcomm_errors TEST_DIR=test TESTDATA_DIR=$(TEST_DIR)/testdata TEST_GEN_DIR=$(TESTDATA_DIR)/gen @@ -51,15 +52,15 @@ $(CREATED_DIRS): mkdir -p $@ ## NB! the tests need CUnit to be installed -run-test: $(TEST_DIR)/test_labcomm $(TEST_DIR)/test_labcomm_errors \ - | $(TEST_DIR) - test/test_labcomm - test/test_labcomm_errors +run-test: $(TESTS:%=run-test-%) + +run-test-%: $(TEST_DIR)/% | $(TEST_DIR) + $< $(TEST_DIR)/%.o: $(TEST_DIR)/%.c $(CC) $(CFLAGS) -o $@ -c $< -$(TEST_DIR)/%: $(TEST_DIR)/%.o | liblabcomm.a +$(TEST_DIR)/%: $(TEST_DIR)/%.o liblabcomm.a $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) $(LDLIBS_TEST) $(TEST_GEN_DIR)/%.c $(TEST_GEN_DIR)/%.h: $(TESTDATA_DIR)/%.lc \ @@ -88,3 +89,4 @@ distclean: clean # Extra dependencies $(TEST_DIR)/test_labcomm: $(TEST_GEN_DIR)/test_sample.o +$(TEST_DIR)/test_labcomm_private.o: labcomm_private.h diff --git a/lib/c/labcomm_dynamic_buffer_writer.h b/lib/c/labcomm_dynamic_buffer_writer.h index 1591b11..c743c02 100644 --- a/lib/c/labcomm_dynamic_buffer_writer.h +++ b/lib/c/labcomm_dynamic_buffer_writer.h @@ -3,10 +3,6 @@ #include "labcomm.h" -extern int labcomm_dynamic_buffer_reader( - labcomm_reader_t *reader, - labcomm_reader_action_t action); - extern int labcomm_dynamic_buffer_writer( labcomm_writer_t *writer, labcomm_writer_action_t action, diff --git a/lib/c/labcomm_private.h b/lib/c/labcomm_private.h index 9d66f44..00c9461 100644 --- a/lib/c/labcomm_private.h +++ b/lib/c/labcomm_private.h @@ -8,6 +8,7 @@ #include <stdio.h> #endif +#include <stdint.h> #include <stdlib.h> #include <string.h> #include "labcomm.h" @@ -117,31 +118,52 @@ LABCOMM_DECODE(long, long long) LABCOMM_DECODE(float, float) LABCOMM_DECODE(double, double) +/* + * Unpack a 32 bit unsigned number from a sequence bytes, where the + * first byte is prefixed with a variable length bit pattern that + * indicates the number of bytes used for encoding. The encoding + * is inspired by the UTF-8 encoding. + * + * 0b0 - 1 byte (0x00000000 - 0x0000007f) + * 0b10 - 2 bytes (0x00000080 - 0x00003fff) + * 0b110 - 3 bytes (0x00004000 - 0x001fffff) + * 0b1110 - 4 bytes (0x00200000 - 0x0fffffff) + * 0b11110 - 5 bytes (0x10000000 - 0xffffffff) [4 bits unused] + */ static inline unsigned int labcomm_unpack32(labcomm_reader_t *r) { - unsigned int res=0; - unsigned char i=0; - unsigned char cont=1; - do { + unsigned int result = 0; + int n, i; + unsigned char tag; + + if (r->pos >= r->count) { + r->read(r, labcomm_reader_continue); + } + tag = r->data[r->pos]; + r->pos++; + if (tag < 0x80) { + n = 1; + result = tag; + } else if (tag < 0xc0) { + n = 2; + result = tag & 0x3f; + } else if (tag < 0xe0) { + n = 3; + result = tag & 0x1f; + } else if (tag < 0xf0) { + n = 4; + result = tag & 0x0f; + } else { + n = 5; + } + for (i = 1 ; i < n ; i++) { if (r->pos >= r->count) { r->read(r, labcomm_reader_continue); } -#ifdef IDIOTDEBUG - { - int k; - for(k=0; k<=r->pos; k++) printf("%2x\n", r->data[k]); - } -#endif - unsigned char c = r->data[r->pos]; - res |= (c & 0x7f) << 7*i; - cont = c & 0x80; -#ifdef IDIOTDEBUG - printf("unpack32: %x (%x, %d, %d)\n", res, c, i, cont); -#endif - i++; + result = (result << 8) | r->data[r->pos]; r->pos++; - } while(cont); - return res; + } + return result; } static inline unsigned int labcomm_decode_packed32(labcomm_decoder_t *d) @@ -229,13 +251,14 @@ void labcomm_internal_encode( labcomm_signature_t *signature, void *value); -/* Should these really be visible? */ -void labcomm_encoder_start(struct labcomm_encoder *e, - labcomm_signature_t *s) ; +void labcomm_encoder_start( + labcomm_encoder_t *encoder, + labcomm_signature_t *signature); //HERE BE DRAGONS: is the signature_t* needed here? -void labcomm_encoder_end(struct labcomm_encoder *e, - labcomm_signature_t *s) ; +void labcomm_encoder_end( + labcomm_encoder_t *encoder, + labcomm_signature_t *signature); @@ -284,28 +307,51 @@ LABCOMM_ENCODE(float, float) LABCOMM_ENCODE(double, double) /* - * Pack the 32 bit number data as a sequence of 7 bit chunks, represented in bytes - * with the high bit meaning that more data is to come. + * Pack the 32 bit unsigned number data as a sequence bytes, where the + * first byte is prefixed with a variable length bit pattern that + * indicates the number of bytes used for encoding. The encoding + * is inspired by the UTF-8 encoding. * - * The chunks are sent "little endian": each 7 bit chunk is more significant than - * the previous. - */ + * 0b0 - 1 byte (0x00000000 - 0x0000007f) + * 0b10 - 2 bytes (0x00000080 - 0x00003fff) + * 0b110 - 3 bytes (0x00004000 - 0x001fffff) + * 0b1110 - 4 bytes (0x00200000 - 0x0fffffff) + * 0b11110 - 5 bytes (0x10000000 - 0xffffffff) [4 bits unused] + */ static inline void labcomm_pack32(labcomm_writer_t *w, unsigned int data) { - unsigned int tmp; - - tmp = data; - - while (tmp >= 0x80) { - if (w->pos >= w->count) { - w->write(w, labcomm_writer_continue); - } - w->data[w->pos] = (tmp & 0x7f) | 0x80; - w->pos++; - tmp >>= 7; - } - w->data[w->pos] = tmp; - w->pos++; + int n; + unsigned char tag; + unsigned char tmp[4] = { (data >> 24) & 0xff, + (data >> 16) & 0xff, + (data >> 8) & 0xff, + (data ) & 0xff }; + if (data < 0x80) { + n = 1; + tag = 0x00; + } else if (data < 0x4000) { + n = 2; + tag = 0x80; + } else if (data < 0x200000) { + n = 3; + tag = 0xc0; + } else if (data < 0x10000000) { + n = 4; + tag = 0xe0; + } else { + n = 5; + tag = 0xf0; + } + if (w->pos + n - 1 >= w->count) { + w->write(w, labcomm_writer_continue, n); + } + switch (n) { + case 5: w->data[w->pos++] = tag; tag = 0; + case 4: w->data[w->pos++] = tmp[0] | tag; tag = 0; + case 3: w->data[w->pos++] = tmp[1] | tag; tag = 0; + case 2: w->data[w->pos++] = tmp[2] | tag; tag = 0; + case 1: w->data[w->pos++] = tmp[3] | tag; + } } static inline void labcomm_encode_packed32(labcomm_encoder_t *e, unsigned int data) @@ -313,6 +359,7 @@ static inline void labcomm_encode_packed32(labcomm_encoder_t *e, unsigned int da labcomm_pack32(&e->writer, data); } + static inline void labcomm_write_string(labcomm_writer_t *w, char *s) { int length, i; diff --git a/lib/c/test/test_labcomm_private.c b/lib/c/test/test_labcomm_private.c new file mode 100644 index 0000000..a529d4d --- /dev/null +++ b/lib/c/test/test_labcomm_private.c @@ -0,0 +1,142 @@ +#include <stdint.h> +#include <string.h> +#include "labcomm_private.h" + + +int test_write(struct labcomm_writer *w, labcomm_writer_action_t a, ...) +{ + fprintf(stderr, "test_write should not be called\n"); + exit(1); +} + +int test_read(struct labcomm_reader *r, labcomm_reader_action_t a) +{ + fprintf(stderr, "test_read should not be called\n"); + exit(1); +} + +static unsigned char buffer[128]; +static labcomm_encoder_t encoder = { + .context = NULL, + .writer = { + .context = NULL, + .data = buffer, + .data_size = sizeof(buffer), + .count = sizeof(buffer), + .pos = 0, + .error = 0, + .write = test_write, + .ioctl = NULL, + .on_error = NULL, + }, + .do_register = NULL, + .do_encode = NULL, + .on_error = NULL, +}; + +static labcomm_decoder_t decoder = { + .context = NULL, + .reader = { + .context = NULL, + .data = buffer, + .data_size = sizeof(buffer), + .count = sizeof(buffer), + .pos = 0, +// .error = 0, + .read = test_read, + .ioctl = NULL, + .on_error = NULL, + }, + .do_register = NULL, +// .do_decode = NULL, + .on_error = NULL, +}; + +typedef unsigned int packed32; +typedef unsigned char boolean; + +#define TEST_WRITE_READ(type, format, value, expect_count, expect_bytes) \ + { \ + type decoded; \ + encoder.writer.pos = 0; \ + labcomm_encode_##type(&encoder, value); \ + writer_assert(#type, __LINE__, expect_count, (uint8_t*)expect_bytes); \ + decoder.reader.pos = 0; \ + decoded = labcomm_decode_##type(&decoder); \ + if (decoded != value) { \ + fprintf(stderr, "Decode error" format " != " format " @%s:%d \n", value, decoded, \ + __FILE__, __LINE__); \ + exit(1); \ + } \ + } + +static void writer_assert(char *type, + int line, + int count, + uint8_t *bytes) +{ + if (encoder.writer.pos != count) { + fprintf(stderr, + "Wrong number of bytes written for '%s' (%d != %d) @%s:%d\n", + type, encoder.writer.pos, count, __FILE__, line); + exit(1); + } + if (memcmp(encoder.writer.data, bytes, count) != 0) { + int i; + + fprintf(stderr, "Wrong bytes written for '%s' ( ", type); + for (i = 0 ; i < count ; i++) { + fprintf(stderr, "%2.2x ", encoder.writer.data[i]); + } + fprintf(stderr, "!= "); + for (i = 0 ; i < count ; i++) { + fprintf(stderr, "%2.2x ", bytes[i]); + } + fprintf(stderr, ") @%s:%d\n", __FILE__, line); + exit(1); + } +} + +int main(void) +{ + TEST_WRITE_READ(packed32, "%d", 0x0, 1, "\x00"); + TEST_WRITE_READ(packed32, "%d", 0x7f, 1, "\x7f"); + TEST_WRITE_READ(packed32, "%d", 0x80, 2, "\x80\x80"); + TEST_WRITE_READ(packed32, "%d", 0x3fff, 2, "\xbf\xff"); + TEST_WRITE_READ(packed32, "%d", 0x4000, 3, "\xc0\x40\x00"); + TEST_WRITE_READ(packed32, "%d", 0x1fffff, 3, "\xdf\xff\xff"); + TEST_WRITE_READ(packed32, "%d", 0x200000, 4, "\xe0\x20\x00\x00"); + TEST_WRITE_READ(packed32, "%d", 0xfffffff, 4, "\xef\xff\xff\xff"); + TEST_WRITE_READ(packed32, "%d", 0x10000000, 5, "\xf0\x10\x00\x00\x00"); + TEST_WRITE_READ(packed32, "%d", 0xffffffff, 5, "\xf0\xff\xff\xff\xff"); + TEST_WRITE_READ(boolean, "%d", 0, 1, "\00"); + TEST_WRITE_READ(short, "%d", 0, 2, "\x00\x00"); + TEST_WRITE_READ(short, "%d", 0x7fff, 2, "\x7f\xff"); + TEST_WRITE_READ(short, "%d", -1, 2, "\xff\xff"); + TEST_WRITE_READ(int, "%d", 0, 4, "\x00\x00\x00\x00"); + TEST_WRITE_READ(int, "%d", 0x7fffffff, 4, "\x7f\xff\xff\xff"); + TEST_WRITE_READ(int, "%d", -1, 4, "\xff\xff\xff\xff"); + TEST_WRITE_READ(float, "%f", 0.0, 4, "\x00\x00\x00\x00"); + TEST_WRITE_READ(float, "%f", 1.0, 4, "\x3f\x80\x00\x00"); + TEST_WRITE_READ(float, "%f", 2.0, 4, "\x40\x00\x00\x00"); + TEST_WRITE_READ(float, "%f", 0.5, 4, "\x3f\x00\x00\x00"); + TEST_WRITE_READ(float, "%f", 0.25, 4, "\x3e\x80\x00\x00"); + TEST_WRITE_READ(float, "%f", -0.0, 4, "\x80\x00\x00\x00"); + TEST_WRITE_READ(float, "%f", -1.0, 4, "\xbf\x80\x00\x00"); + TEST_WRITE_READ(float, "%f", -2.0, 4, "\xc0\x00\x00\x00"); + TEST_WRITE_READ(float, "%f", -0.5, 4, "\xbf\x00\x00\x00"); + TEST_WRITE_READ(float, "%f", -0.25, 4, "\xbe\x80\x00\x00"); + TEST_WRITE_READ(double, "%f", 0.0, 8, "\x00\x00\x00\x00\x00\x00\x00\x00"); + TEST_WRITE_READ(double, "%f", 1.0, 8, "\x3f\xf0\x00\x00\x00\x00\x00\x00"); + TEST_WRITE_READ(double, "%f", 2.0, 8, "\x40\x00\x00\x00\x00\x00\x00\x00"); + TEST_WRITE_READ(double, "%f", 0.5, 8, "\x3f\xe0\x00\x00\x00\x00\x00\x00"); + TEST_WRITE_READ(double, "%f", 0.25, 8, "\x3f\xd0\x00\x00\x00\x00\x00\x00"); + TEST_WRITE_READ(double, "%f", -0.0, 8, "\x80\x00\x00\x00\x00\x00\x00\x00"); + TEST_WRITE_READ(double, "%f", -1.0, 8, "\xbf\xf0\x00\x00\x00\x00\x00\x00"); + TEST_WRITE_READ(double, "%f", -2.0, 8, "\xc0\x00\x00\x00\x00\x00\x00\x00"); + TEST_WRITE_READ(double, "%f", -0.5, 8, "\xbf\xe0\x00\x00\x00\x00\x00\x00"); + TEST_WRITE_READ(double, "%f", -0.25, 8, "\xbf\xd0\x00\x00\x00\x00\x00\x00"); + fprintf(stderr, "%s succeded\n", __FILE__); + return 0; +} + -- GitLab