Add a get_bitfield function for byte arrays.
authorChristopher Peplin <chris.peplin@rhubarbtech.com>
Sat, 4 Jan 2014 15:51:16 +0000 (10:51 -0500)
committerChristopher Peplin <chris.peplin@rhubarbtech.com>
Sat, 4 Jan 2014 15:52:42 +0000 (10:52 -0500)
Makefile
README.mkd
src/bitfield/8byte.c
src/bitfield/8byte.h
src/bitfield/bitfield.c
src/bitfield/bitfield.h
src/canutil/read.c
src/canutil/read.h
tests/8byte_tests.c
tests/read_tests.c

index c3c3e5a..89fbf25 100644 (file)
--- 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)"
index d16a893..0cad3d2 100644 (file)
@@ -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
index 76dccc7..3c555f0 100644 (file)
@@ -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];
         }
index 194007c..ab775ca 100644 (file)
@@ -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.
index 3b0c5fd..52f368f 100644 (file)
@@ -2,6 +2,7 @@
 #include <limits.h>
 #include <string.h>
 #include <stddef.h>
+#include <endian.h>
 
 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);
 }
+
index 5dd976f..80f3124 100644 (file)
 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);
 
index 4864a60..3931721 100644 (file)
@@ -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;
 }
index bf6c0ad..733e350 100644 (file)
@@ -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
index 5ebb0db..12ff417 100644 (file)
@@ -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);
index 1cde461..512d87e 100644 (file)
@@ -3,8 +3,9 @@
 #include <canutil/read.h>
 
 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;