From 08db5c9eb826f7aa0dba36cbef8011d7bc6b55a5 Mon Sep 17 00:00:00 2001 From: Christopher Peplin Date: Sat, 4 Jan 2014 10:51:16 -0500 Subject: [PATCH] Add a get_bitfield function for byte arrays. --- Makefile | 3 ++- README.mkd | 6 +++--- src/bitfield/8byte.c | 10 ++++++---- src/bitfield/8byte.h | 4 ++-- src/bitfield/bitfield.c | 18 ++++++++++++++++++ src/bitfield/bitfield.h | 34 +++++++++++++++++++++++++++++++++- src/canutil/read.c | 18 ++++++++++++++---- src/canutil/read.h | 42 +++++++++++++++++++++++++++++++++++++----- tests/8byte_tests.c | 40 ++++++++++++++++++++-------------------- tests/read_tests.c | 35 +++++++++++++++++++++++++++++------ 10 files changed, 164 insertions(+), 46 deletions(-) diff --git a/Makefile b/Makefile index c3c3e5a4..89fbf25a 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CC = gcc INCLUDES = -Isrc -CFLAGS = $(INCLUDES) -c -w -Wall -Werror -g -ggdb -std=gnu++0x -coverage +CFLAGS = $(INCLUDES) -c -w -Wall -Werror -g -ggdb -std=gnu99 -coverage LDFLAGS = -coverage -lm LDLIBS = -lcheck @@ -36,6 +36,7 @@ coverage: @make clean @make test @lcov --base-directory . --directory $(TEST_OBJDIR) -c -o $(TEST_OBJDIR)/coverage.info + @lcov --remove $(COVERAGE_INFO_PATH) "/usr/*" -o $(COVERAGE_INFO_PATH) @genhtml -o $(TEST_OBJDIR)/coverage -t "isotp-c test coverage" --num-spaces 4 $(COVERAGE_INFO_PATH) @$(BROWSER) $(TEST_OBJDIR)/coverage/index.html @echo "$(GREEN)Coverage information generated in $(TEST_OBJDIR)/coverage/index.html.$(COLOR_RESET)" diff --git a/README.mkd b/README.mkd index d16a8931..0cad3d21 100644 --- a/README.mkd +++ b/README.mkd @@ -30,11 +30,11 @@ useful. ### 8 Byte Decoding uint64_t data = 0x8000000000000000; - uint64_t result = get_bit_field(data, 0, 1, false); + uint64_t result = get_bitfield(data, 0, 1, false); // result == 0x1 data = 0x0402574d555a0401; - result = get_bit_field(data, 16, 32, false); + result = get_bitfield(data, 16, 32, false); // result = 0x574d555a; data = 0x00000000F34DFCFF; @@ -51,7 +51,7 @@ useful. uint64_t data = 0; fail_unless(set_bit_field(&data, 1, 0, 1)); - uint64_t result = get_bit_field(data, 0, 1, false); + uint64_t result = get_bitfield(data, 0, 1, false); ck_assert_int_eq(result, 0x1); ### CAN Signal Encoding diff --git a/src/bitfield/8byte.c b/src/bitfield/8byte.c index 76dccc75..3c555f08 100644 --- a/src/bitfield/8byte.c +++ b/src/bitfield/8byte.c @@ -8,7 +8,7 @@ uint8_t eightbyte_get_nibble(const uint64_t source, const uint8_t nibble_index, const bool data_is_big_endian) { - return eightbyte_get_bit_field(source, NIBBLE_SIZE * nibble_index, NIBBLE_SIZE, + return eightbyte_get_bitfield(source, NIBBLE_SIZE * nibble_index, NIBBLE_SIZE, data_is_big_endian); } @@ -20,7 +20,10 @@ uint8_t eightbyte_get_byte(uint64_t source, const uint8_t byte_index, return (source >> (EIGHTBYTE_BIT - ((byte_index + 1) * CHAR_BIT))) & 0xFF; } -uint64_t eightbyte_get_bit_field(uint64_t source, const uint16_t offset, +// TODO is this funciton necessary anymore? is it any faster for uint64_t than +// get_bitfield(data[], ...)? is the performance better on a 32 bit platform +// like the PIC32? +uint64_t eightbyte_get_bitfield(uint64_t source, const uint16_t offset, const uint16_t bit_count, const bool data_is_big_endian) { int startByte = offset / CHAR_BIT; int endByte = (offset + bit_count - 1) / CHAR_BIT; @@ -33,8 +36,7 @@ uint64_t eightbyte_get_bit_field(uint64_t source, const uint16_t offset, uint64_t ret = bytes[startByte]; if(startByte != endByte) { // The lowest byte address contains the most significant bit. - int i; - for(i = startByte + 1; i <= endByte; i++) { + for(uint8_t i = startByte + 1; i <= endByte; i++) { ret = ret << 8; ret = ret | bytes[i]; } diff --git a/src/bitfield/8byte.h b/src/bitfield/8byte.h index 194007c8..ab775caa 100644 --- a/src/bitfield/8byte.h +++ b/src/bitfield/8byte.h @@ -32,11 +32,11 @@ extern "C" { * * Examples * - * uint64_t value = get_bit_field(data, 2, 4); + * uint64_t value = get_bitfield(data, 2, 4); * * Returns the value of the requested bit field, right aligned in a uint64_t. */ -uint64_t eightbyte_get_bit_field(uint64_t source, const uint16_t offset, +uint64_t eightbyte_get_bitfield(uint64_t source, const uint16_t offset, const uint16_t bit_count, const bool data_is_big_endian); /* Public: Return a single nibble from the payload, with range checking. diff --git a/src/bitfield/bitfield.c b/src/bitfield/bitfield.c index 3b0c5fdd..52f368f5 100644 --- a/src/bitfield/bitfield.c +++ b/src/bitfield/bitfield.c @@ -2,6 +2,7 @@ #include #include #include +#include uint64_t bitmask(const uint8_t bit_count) { return (((uint64_t)0x1) << bit_count) - 1; @@ -26,8 +27,25 @@ uint8_t get_byte(const uint8_t source[], const uint8_t source_length, return 0; } +uint64_t get_bitfield(const uint8_t source[], const uint8_t source_length, + const uint16_t offset, const uint16_t bit_count) { + if(bit_count > 64 || bit_count < 1) { + // TODO error reporting? + return 0; + } + + union { + uint64_t whole; + uint8_t bytes[sizeof(uint64_t)]; + } combined; + copy_bits_right_aligned(source, source_length, offset, bit_count, + combined.bytes, sizeof(combined.bytes)); + return htobe64(combined.whole); +} + bool set_nibble(const uint16_t nibble_index, const uint8_t value, uint8_t* destination, const uint16_t destination_length) { return copy_bits(&value, CHAR_BIT, NIBBLE_SIZE, NIBBLE_SIZE, destination, destination_length, nibble_index * NIBBLE_SIZE); } + diff --git a/src/bitfield/bitfield.h b/src/bitfield/bitfield.h index 5dd976f0..80f31243 100644 --- a/src/bitfield/bitfield.h +++ b/src/bitfield/bitfield.h @@ -10,6 +10,36 @@ extern "C" { #endif +/* Public: Reads a subset of bits into a uint64_t, right aligned so they may be + * interpreted as a number. + * + * source - the bytes in question. + * source_size - the number of bytes in the source. + * offset - the starting index of the bit field (beginning from 0). + * bit_count - the width of the bit field to extract. This must be less than or + * equal to 64. + * + * Bit fields are positioned according to big-endian bit layout and the data is + * swapped automatically as necessary depending on the compiled architecture. + * + * For example, the bit layout of the value "42" (i.e. 00101010 set at position + * 14 with length 6 is: + * + * 000000000000001010100000000000000000000000000000000000000000000 + * + * and the same value and position but with length 8 is: + * + * 000000000000000010101000000000000000000000000000000000000000000 + * + * Examples + * + * uint64_t value = get_bitfield(data, data_size, 2, 4); + * + * Returns the value of the requested bit field, right aligned in a uint64_t. + */ +uint64_t get_bits(const uint8_t source[], const uint8_t source_length, + const uint16_t offset, const uint16_t bit_count); + /* Public: Return a single nibble from the byte array, with range checking. * * source - the source byte array. @@ -142,7 +172,9 @@ bool set_nibble(const uint16_t nibble_index, const uint8_t value, */ uint16_t bits_to_bytes(uint32_t bits); -/* Private: +/* Public: Return a right aligned bitmask for a uint64_t. + * + * bit_count - the number of bits to mask, right aligned. */ uint64_t bitmask(const uint8_t bit_count); diff --git a/src/canutil/read.c b/src/canutil/read.c index 4864a60f..3931721a 100644 --- a/src/canutil/read.c +++ b/src/canutil/read.c @@ -4,7 +4,7 @@ float eightbyte_parse_float(uint64_t data, uint8_t bit_offset, uint8_t bit_size, float factor, float offset) { - uint64_t raw = eightbyte_get_bit_field(data, bit_offset, bit_size, true); + uint64_t raw = eightbyte_get_bitfield(data, bit_offset, bit_size, true); return raw * factor + offset; } @@ -14,9 +14,19 @@ bool eightbyte_parse_bool(uint64_t data, uint8_t bit_offset, uint8_t bit_size, return value == 0.0 ? false : true; } -float bitfield_parse_float(const uint8_t data[], const uint16_t size, +float bitfield_parse_float(const uint8_t source[], const uint16_t source_length, const uint8_t bit_offset, const uint8_t bit_size, const float factor, const float offset) { - //TODO - return 0; + uint64_t raw = get_bitfield(source, source_length, bit_offset, bit_size); + // TODO seems dumb that this is repeated from eightbyte_parse_float - is it + // really worth keeping around these two implementations? + return raw * factor + offset; +} + +float bitfield_parse_bool(const uint8_t source[], const uint16_t source_length, + const uint8_t bit_offset, const uint8_t bit_size, const float factor, + const float offset) { + float value = bitfield_parse_float(source, source_length, bit_offset, + bit_size, factor, offset); + return value == 0.0 ? false : true; } diff --git a/src/canutil/read.h b/src/canutil/read.h index bf6c0ade..733e3509 100644 --- a/src/canutil/read.h +++ b/src/canutil/read.h @@ -10,7 +10,7 @@ extern "C" { /* Public: Parse a CAN signal from a message and apply required transformation. * - * data - the payload containing the signal. + * source - the payload containing the signal. * bit_offset - the starting bit for the signal. * bit_size - the width of the signal. * factor - the transformation factor for the signal value, applied after @@ -20,16 +20,30 @@ extern "C" { * * Returns the decoded and transformed value of the signal. */ -float eightbyte_parse_float(const uint64_t data, const uint8_t bit_offset, +float eightbyte_parse_float(const uint64_t source, const uint8_t bit_offset, const uint8_t bit_size, const float factor, const float offset); -float bitfield_parse_float(const uint8_t data[], const uint16_t size, +/* Public: Parse a CAN signal from a message storage as a byte array and apply + * required transformation. + * + * source - the payload containing the signal. + * source_size - the size of the payload in bytes. + * bit_offset - the starting bit for the signal. + * bit_size - the width of the signal. + * factor - the transformation factor for the signal value, applied after + * pulling out the bit field. Use 1.0 for no factor. + * offset - the transformation offset for the signal value, applied after + * pulling out the bit field. Use 0 for no offset. + * + * Returns the decoded and transformed value of the signal. + */ +float bitfield_parse_float(const uint8_t source[], const uint16_t source_size, const uint8_t bit_offset, const uint8_t bit_size, const float factor, const float offset); /* Public: Parse a CAN signal from a message and interpret it as a boolean. * - * data - the payload containing the signal. + * source - the payload containing the signal. * bit_offset - the starting bit for the signal. * bit_size - the width of the signal. * factor - the transformation factor for the signal value, applied after @@ -39,9 +53,27 @@ float bitfield_parse_float(const uint8_t data[], const uint16_t size, * * Returns false if the value was 0, otherwise true. */ -bool eightbyte_parse_bool(uint64_t data, uint8_t bit_offset, uint8_t bit_size, +bool eightbyte_parse_bool(uint64_t source, uint8_t bit_offset, uint8_t bit_size, float factor, float offset); +/* Public: Parse a CAN signal from a message storage as a byte array and + * interpret it as a boolean. + * + * source - the payload containing the signal. + * source_size - the size of the payload in bytes. + * bit_offset - the starting bit for the signal. + * bit_size - the width of the signal. + * factor - the transformation factor for the signal value, applied after + * pulling out the bit field. Use 1.0 for no factor. + * offset - the transformation offset for the signal value, applied after + * pulling out the bit field. Use 0 for no offset. + * + * Returns false if the value was 0, otherwise true. + */ +float bitfield_parse_float(const uint8_t source[], const uint16_t source_size, + const uint8_t bit_offset, const uint8_t bit_size, const float factor, + const float offset); + #ifdef __cplusplus } #endif diff --git a/tests/8byte_tests.c b/tests/8byte_tests.c index 5ebb0db0..12ff417d 100644 --- a/tests/8byte_tests.c +++ b/tests/8byte_tests.c @@ -12,7 +12,7 @@ END_TEST START_TEST (test_one_bit_not_swapped) { uint64_t data = 0x80; - uint64_t result = eightbyte_get_bit_field(data, 0, 1, false); + uint64_t result = eightbyte_get_bitfield(data, 0, 1, false); fail_if(result == 1); } END_TEST @@ -20,7 +20,7 @@ END_TEST START_TEST (test_one_bit) { uint64_t data = 0x8000000000000000; - uint64_t result = eightbyte_get_bit_field(data, 0, 1, false); + uint64_t result = eightbyte_get_bitfield(data, 0, 1, false); fail_unless(result == 0x1, "First bit in 0x%llx was 0x%llx instead of 0x1", data, result); } @@ -29,7 +29,7 @@ END_TEST START_TEST (test_32_bit_parse) { uint64_t data = 0x0402574d555a0401; - uint64_t result = eightbyte_get_bit_field(data, 16, 32, false); + uint64_t result = eightbyte_get_bitfield(data, 16, 32, false); uint64_t expectedValue = 0x574d555a; fail_unless(result == expectedValue, "Field retrieved in 0x%llx was 0x%llx instead of 0x%llx", data, @@ -40,7 +40,7 @@ END_TEST START_TEST (test_16_bit_parse) { uint64_t data = 0xF34DFCFF00000000; - uint64_t result = eightbyte_get_bit_field(data, 16, 16, false); + uint64_t result = eightbyte_get_bitfield(data, 16, 16, false); uint64_t expectedValue = 0xFCFF; fail_unless(result == expectedValue, "Field retrieved in 0x%llx was 0x%llx instead of 0x%llx", data, @@ -51,13 +51,13 @@ END_TEST START_TEST (test_one_byte) { uint64_t data = 0xFA00000000000000; - uint64_t result = eightbyte_get_bit_field(data, 0, 4, false); + uint64_t result = eightbyte_get_bitfield(data, 0, 4, false); fail_unless(result == 0xF, "First nibble in 0x%llx was 0x%llx instead of 0xF", data, result); - result = eightbyte_get_bit_field(data, 4, 4, false); + result = eightbyte_get_bitfield(data, 4, 4, false); fail_unless(result == 0xA, "Second nibble in 0x%llx was 0x%llx instead of 0xA", data, result); - result = eightbyte_get_bit_field(data, 0, 8, false); + result = eightbyte_get_bitfield(data, 0, 8, false); fail_unless(result == 0xFA, "All bits in 0x%llx were 0x%llx instead of 0x%llx", data, result, data); } @@ -66,19 +66,19 @@ END_TEST START_TEST (test_multi_byte) { uint64_t data = 0x12FA000000000000; - uint64_t result = eightbyte_get_bit_field(data, 0, 4, false); + uint64_t result = eightbyte_get_bitfield(data, 0, 4, false); fail_unless(result == 0x1, "First 4 bits in 0x%llx was 0x%llx instead of 0xF", (data >> 60) & 0xF, result); - result = eightbyte_get_bit_field(data, 4, 4, false); + result = eightbyte_get_bitfield(data, 4, 4, false); fail_unless(result == 0x2, "Second 4 bits in 0x%llx was 0x%llx instead of 0xA", (data >> 56) & 0xF, result); - result = eightbyte_get_bit_field(data, 8, 4, false); + result = eightbyte_get_bitfield(data, 8, 4, false); fail_unless(result == 0xF, "First 4 bits in 0x%llx was 0x%llx instead of 0x1", (data >> 52) & 0xF, result); - result = eightbyte_get_bit_field(data, 12, 4, false); + result = eightbyte_get_bitfield(data, 12, 4, false); fail_unless(result == 0xA, "Second 4 bits in 0x%llx was 0x%llx instead of 0x2", (data >> 48) % 0xF, result); @@ -88,7 +88,7 @@ END_TEST START_TEST (test_get_multi_byte) { uint64_t data = 0x12FA000000000000; - uint64_t result = eightbyte_get_bit_field(data, 0, 9, false); + uint64_t result = eightbyte_get_bitfield(data, 0, 9, false); ck_assert_int_eq(result, 0x25); } END_TEST @@ -96,7 +96,7 @@ END_TEST START_TEST (test_get_off_byte_boundary) { uint64_t data = 0x000012FA00000000; - uint64_t result = eightbyte_get_bit_field(data, 12, 8, false); + uint64_t result = eightbyte_get_bitfield(data, 12, 8, false); ck_assert_int_eq(result, 0x01); } END_TEST @@ -111,16 +111,16 @@ START_TEST (test_set_field) { uint64_t data = 0; fail_unless(set_bit_field(&data, 1, 0, 1)); - uint64_t result = eightbyte_get_bit_field(data, 0, 1, false); + uint64_t result = eightbyte_get_bitfield(data, 0, 1, false); ck_assert_int_eq(result, 0x1); data = 0; fail_unless(set_bit_field(&data, 1, 1, 1)); - result = eightbyte_get_bit_field(data, 1, 1, false); + result = eightbyte_get_bitfield(data, 1, 1, false); ck_assert_int_eq(result, 0x1); data = 0; fail_unless(set_bit_field(&data, 0xf, 3, 4)); - result = eightbyte_get_bit_field(data, 3, 4, false); + result = eightbyte_get_bitfield(data, 3, 4, false); ck_assert_int_eq(result, 0xf); } END_TEST @@ -129,7 +129,7 @@ START_TEST (test_set_doesnt_clobber_existing_data) { uint64_t data = 0xFFFC4DF300000000; fail_unless(set_bit_field(&data, 0x4fc8, 16, 16)); - uint64_t result = eightbyte_get_bit_field(data, 16, 16, false); + uint64_t result = eightbyte_get_bitfield(data, 16, 16, false); fail_unless(result == 0x4fc8, "Field retrieved in 0x%llx was 0x%llx instead of 0x%x", data, result, 0xc84f); @@ -146,7 +146,7 @@ START_TEST (test_set_off_byte_boundary) { uint64_t data = 0xFFFC4DF300000000; fail_unless(set_bit_field(&data, 0x12, 12, 8)); - uint64_t result = eightbyte_get_bit_field(data, 12, 12, false); + uint64_t result = eightbyte_get_bitfield(data, 12, 12, false); ck_assert_int_eq(result,0x12d); } END_TEST @@ -155,14 +155,14 @@ START_TEST (test_set_odd_number_of_bits) { uint64_t data = 0xFFFC4DF300000000LLU; fail_unless(set_bit_field(&data, 0x12, 11, 5)); - uint64_t result = eightbyte_get_bit_field(data, 11, 5, false); + uint64_t result = eightbyte_get_bitfield(data, 11, 5, false); fail_unless(result == 0x12, "Field set in 0x%llx%llx%llx%llx was 0x%llx instead of 0x%llx", data, result, 0x12); data = 0xFFFC4DF300000000LLU; fail_unless(set_bit_field(&data, 0x2, 11, 5)); - result = eightbyte_get_bit_field(data, 11, 5, false); + result = eightbyte_get_bitfield(data, 11, 5, false); fail_unless(result == 0x2, "Field set in 0x%llx%llx%llx%llx was 0x%llx instead of 0x%llx", data, result, 0x2); diff --git a/tests/read_tests.c b/tests/read_tests.c index 1cde4612..512d87e8 100644 --- a/tests/read_tests.c +++ b/tests/read_tests.c @@ -3,8 +3,9 @@ #include const uint64_t BIG_ENDIAN_TEST_DATA = __builtin_bswap64(0xEB00000000000000); +const uint8_t ARRAY_TEST_DATA[] = {0xEB, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; -START_TEST (test_parse_float) +START_TEST (test_eightbyte_parse_float) { float result = eightbyte_parse_float(BIG_ENDIAN_TEST_DATA, 2, 4, 1001.0, -30000.0); @@ -14,10 +15,30 @@ START_TEST (test_parse_float) } END_TEST -START_TEST (test_parse_bool) +START_TEST (test_eightbyte_parse_bool) { - float result = eightbyte_parse_bool(BIG_ENDIAN_TEST_DATA, 0, 1, 1.0, 0); - float correctResult = true; + bool result = eightbyte_parse_bool(BIG_ENDIAN_TEST_DATA, 0, 1, 1.0, 0); + bool correctResult = true; + fail_unless(result == correctResult, + "parse is incorrect: %d but should be %d", result, correctResult); +} +END_TEST + +START_TEST (test_bitfield_parse_float) +{ + float result = bitfield_parse_float(ARRAY_TEST_DATA, + sizeof(ARRAY_TEST_DATA), 2, 4, 1001.0, -30000.0); + float correctResult = 0xA * 1001.0 - 30000.0; + fail_unless(result == correctResult, + "parse is incorrect: %f but should be %f", result, correctResult); +} +END_TEST + +START_TEST (test_bitfield_parse_bool) +{ + bool result = bitfield_parse_bool(ARRAY_TEST_DATA, sizeof(ARRAY_TEST_DATA), + 0, 1, 1.0, 0); + bool correctResult = true; fail_unless(result == correctResult, "parse is incorrect: %d but should be %d", result, correctResult); } @@ -27,8 +48,10 @@ Suite* canreadSuite(void) { Suite* s = suite_create("read"); TCase *tc_core = tcase_create("core"); tcase_add_checked_fixture(tc_core, NULL, NULL); - tcase_add_test(tc_core, test_parse_float); - tcase_add_test(tc_core, test_parse_bool); + tcase_add_test(tc_core, test_eightbyte_parse_float); + tcase_add_test(tc_core, test_eightbyte_parse_bool); + tcase_add_test(tc_core, test_bitfield_parse_float); + tcase_add_test(tc_core, test_bitfield_parse_bool); suite_add_tcase(s, tc_core); return s; -- 2.16.6