├── .github └── workflows │ └── main.yml ├── .gitignore ├── CHANGELOG.mkd ├── LICENSE ├── Makefile ├── README.mkd ├── runtests.sh ├── src ├── bitfield │ ├── 8byte.c │ ├── 8byte.h │ ├── bitarray.c │ ├── bitfield.c │ └── bitfield.h └── canutil │ ├── read.c │ ├── read.h │ ├── write.c │ └── write.h └── tests ├── 8byte_tests.c ├── bitfield_tests.c ├── read_tests.c └── write_tests.c /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Install Dependencies 13 | run: | 14 | sudo apt-get update -qq 15 | sudo apt-get install check 16 | git submodule update --init --recursive 17 | - name: make test 18 | run: make test 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | .DS_Store 3 | *~ 4 | *.bin 5 | build 6 | -------------------------------------------------------------------------------- /CHANGELOG.mkd: -------------------------------------------------------------------------------- 1 | # Bitfield Utilities in C 2 | 3 | ## v0.1 4 | 5 | * Initial release 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Ford Motor Company 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | INCLUDES = -Isrc 3 | CFLAGS = $(INCLUDES) -c -Wall -Werror -g -ggdb -coverage 4 | LDFLAGS = -coverage -lm 5 | LDLIBS = -lcheck -lm -lrt -lpthread -lsubunit 6 | 7 | TEST_DIR = tests 8 | TEST_OBJDIR = build 9 | 10 | # Guard against \r\n line endings only in Cygwin 11 | OSTYPE := $(shell uname) 12 | ifneq ($(OSTYPE),Darwin) 13 | OSTYPE := $(shell uname -o) 14 | ifeq ($(OSTYPE),Cygwin) 15 | TEST_SET_OPTS = igncr 16 | endif 17 | endif 18 | 19 | SRC = $(wildcard src/**/*.c) 20 | OBJS = $(SRC:.c=.o) 21 | OBJS = $(patsubst %,$(TEST_OBJDIR)/%,$(SRC:.c=.o)) 22 | TEST_SRC = $(wildcard $(TEST_DIR)/*_tests.c) 23 | TESTS=$(patsubst %.c,$(TEST_OBJDIR)/%.bin,$(TEST_SRC)) 24 | 25 | all: $(OBJS) 26 | 27 | test: $(TESTS) 28 | @set -o $(TEST_SET_OPTS) >/dev/null 2>&1 29 | @export SHELLOPTS 30 | @sh runtests.sh $(TEST_OBJDIR)/$(TEST_DIR) 31 | 32 | COVERAGE_INFO_FILENAME = coverage.info 33 | COVERAGE_INFO_PATH = $(TEST_OBJDIR)/$(COVERAGE_INFO_FILENAME) 34 | coverage: 35 | @lcov --base-directory . --directory $(TEST_OBJDIR) --zerocounters -q 36 | @make clean 37 | @make test 38 | @lcov --base-directory . --directory $(TEST_OBJDIR) -c -o $(TEST_OBJDIR)/coverage.info 39 | @lcov --remove $(COVERAGE_INFO_PATH) "/usr/*" -o $(COVERAGE_INFO_PATH) 40 | @genhtml -o $(TEST_OBJDIR)/coverage -t "isotp-c test coverage" --num-spaces 4 $(COVERAGE_INFO_PATH) 41 | @$(BROWSER) $(TEST_OBJDIR)/coverage/index.html 42 | @echo "$(GREEN)Coverage information generated in $(TEST_OBJDIR)/coverage/index.html.$(COLOR_RESET)" 43 | 44 | $(TEST_OBJDIR)/%.o: %.c 45 | @mkdir -p $(dir $@) 46 | $(CC) $(CFLAGS) $(CC_SYMBOLS) $(INCLUDES) -o $@ $< 47 | 48 | $(TEST_OBJDIR)/%.bin: $(TEST_OBJDIR)/%.o $(OBJS) $(TEST_SUPPORT_OBJS) 49 | @mkdir -p $(dir $@) 50 | $(CC) $(LDFLAGS) $(CC_SYMBOLS) $(INCLUDES) -o $@ $^ $(LDLIBS) 51 | 52 | clean: 53 | rm -rf $(TEST_OBJDIR) 54 | -------------------------------------------------------------------------------- /README.mkd: -------------------------------------------------------------------------------- 1 | Bitfield Utilities in C 2 | =========================== 3 | 4 | This is a C library with functions to help encode and decode Controller Area 5 | Network (CAN) message payloads or other bitfields. 6 | 7 | The header files contain complete function documentation, but to get you 8 | started, here are examples using the API: 9 | 10 | ## Bitfield Manipulation 11 | 12 | The bitfields are stored in `uint8_t[]`. 13 | 14 | ```c 15 | uint8_t data[4] = {0x12, 0x34, 0x56, 0x78}; 16 | uint8_t result = get_byte(data, sizeof(data), 0); 17 | // result = 0x12; 18 | result = get_nibble(data, sizeof(data), 0); 19 | // result = 0x1; 20 | bool success = copy_bits_right_aligned(data, 4, 4, 12, result, 4) 21 | // success == true 22 | // result[0] == 0x2 23 | // result[1] == 0x34 24 | ``` 25 | 26 | ## 8 Byte Helpers 27 | 28 | If you are dealing with 8 byte CAN messages as `uint64_t`, there are some 29 | additional functions prefixed with `eightbyte_` that may be faster or more 30 | useful. 31 | 32 | ### 8 Byte Decoding 33 | 34 | ```c 35 | uint64_t data = 0x8000000000000000; 36 | uint64_t result = eightbyte_get_bitfield(data, 0, 1, false); 37 | // result == 0x1 38 | 39 | data = 0x0402574d555a0401; 40 | result = eightbyte_get_bitfield(data, 16, 32, false); 41 | // result = 0x574d555a; 42 | 43 | data = 0x00000000F34DFCFF; 44 | result = eightbyte_get_byte(data, 0, false); 45 | //result = 0x0 46 | 47 | result = eightbyte_get_byte(data, 4, false); 48 | //result = 0xF3 49 | 50 | result = eightbyte_get_nibble(data, 10, false); 51 | //result = 0x4; 52 | ``` 53 | 54 | ### 8 Byte Encoding 55 | 56 | ```c 57 | uint64_t data = 0; 58 | fail_unless(8byte_set_bitfield(1, 0, 1, &data)); 59 | uint64_t result = eightbyte_get_bitfield(data, 0, 1, false); 60 | ck_assert_int_eq(result, 0x1); 61 | ``` 62 | 63 | ### CAN Signal Encoding 64 | 65 | The library supports encoding floating point CAN signals as well as booleans 66 | into a uint64_t payload. 67 | 68 | ```c 69 | uint64_t payload = eightbyte_encode_float(1, 1, 3, 1, 0) 70 | // payload == 0x1000000000000000 71 | 72 | payload = eightbyte_encode_bool(true, 1, 3); 73 | // payload == 0x1000000000000000 74 | ``` 75 | 76 | ### CAN Signal Decoding 77 | 78 | The library supports parsing floating point CAN signals as well as booleans. 79 | 80 | ```c 81 | uint64_t payload = 0xeb00000000000000; 82 | float float_result = eightbyte_parse_float(payload, 83 | 2, // starting bit 84 | 4, // width of the signal's field 85 | 1001.0, // transformation factor for the signal value 86 | -30000.0); // transformation offset for the signal value 87 | // float_result == -19990.0 88 | 89 | bool bool_result = eightbyte_parse_bool(payload, 90 | 0, // starting bit 91 | 1, // width of the signal's field 92 | 1.0, // transformation factor for the signal value 93 | 0); // transformation offset for the signal value 94 | // bool_result == true 95 | ``` 96 | 97 | ## Testing 98 | 99 | The library includes a test suite that uses the `check` C unit test library. It 100 | requires the unit testing library `check`. 101 | 102 | $ make test 103 | 104 | You can also see the test coverage if you have `lcov` installed and the 105 | `BROWSER` environment variable set to your choice of web browsers: 106 | 107 | $ BROWSER=google-chrome-stable make coverage 108 | 109 | ## Authors 110 | 111 | Chris Peplin cpeplin@ford.com 112 | 113 | ## License 114 | 115 | Copyright (c) 2013 Ford Motor Company 116 | 117 | Licensed under the BSD license. 118 | -------------------------------------------------------------------------------- /runtests.sh: -------------------------------------------------------------------------------- 1 | echo "Running unit tests:" 2 | 3 | for i in $1/*.bin 4 | do 5 | if test -f $i 6 | then 7 | if ./$i 8 | then 9 | echo $i PASS 10 | else 11 | echo "ERROR in test $i:" 12 | exit 1 13 | fi 14 | fi 15 | done 16 | 17 | echo "${txtbld}$(tput setaf 2)All unit tests passed.$(tput sgr0)" 18 | -------------------------------------------------------------------------------- /src/bitfield/8byte.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define EIGHTBYTE_BIT (8 * sizeof(uint64_t)) 8 | 9 | uint8_t eightbyte_get_nibble(const uint64_t source, const uint8_t nibble_index, 10 | const bool data_is_big_endian) { 11 | return (uint8_t) eightbyte_get_bitfield(source, NIBBLE_SIZE * nibble_index, 12 | NIBBLE_SIZE, data_is_big_endian); 13 | } 14 | 15 | uint8_t eightbyte_get_byte(uint64_t source, const uint8_t byte_index, 16 | const bool data_is_big_endian) { 17 | if(data_is_big_endian) { 18 | source = __builtin_bswap64(source); 19 | } 20 | return (source >> (EIGHTBYTE_BIT - ((byte_index + 1) * CHAR_BIT))) & 0xFF; 21 | } 22 | 23 | // TODO is this funciton necessary anymore? is it any faster for uint64_t than 24 | // get_bitfield(data[], ...)? is the performance better on a 32 bit platform 25 | // like the PIC32? 26 | uint64_t eightbyte_get_bitfield(uint64_t source, const uint16_t offset, 27 | const uint16_t bit_count, const bool data_is_big_endian) { 28 | int startByte = offset / CHAR_BIT; 29 | int endByte = (offset + bit_count - 1) / CHAR_BIT; 30 | 31 | if(!data_is_big_endian) { 32 | source = __builtin_bswap64(source); 33 | } 34 | 35 | uint8_t* bytes = (uint8_t*)&source; 36 | uint64_t ret = bytes[startByte]; 37 | if(startByte != endByte) { 38 | // The lowest byte address contains the most significant bit. 39 | uint8_t i; 40 | for(i = startByte + 1; i <= endByte; i++) { 41 | ret = ret << 8; 42 | ret = ret | bytes[i]; 43 | } 44 | } 45 | 46 | ret >>= 8 - find_end_bit(offset + bit_count); 47 | return ret & bitmask(bit_count); 48 | } 49 | 50 | bool eightbyte_set_bitfield(uint64_t value, const uint16_t offset, 51 | const uint16_t bit_count, uint64_t* destination) { 52 | if(value > bitmask(bit_count)) { 53 | return false; 54 | } 55 | 56 | int shiftDistance = EIGHTBYTE_BIT - offset - bit_count; 57 | value <<= shiftDistance; 58 | *destination &= ~(bitmask(bit_count) << shiftDistance); 59 | *destination |= value; 60 | return true; 61 | } 62 | -------------------------------------------------------------------------------- /src/bitfield/8byte.h: -------------------------------------------------------------------------------- 1 | #ifndef __8BYTE_H__ 2 | #define __8BYTE_H__ 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | /* Public: Reads a subset of bits into a uint64_t. 12 | * 13 | * source - the bytes in question. 14 | * offset - the starting index of the bit field (beginning from 0). 15 | * bit_count - the width of the bit field to extract. 16 | * data_is_big_endian - if the data passed in is little endian, set this to false and it 17 | * will be flipped before grabbing the bit field. 18 | * 19 | * Bit fields are positioned according to big-endian bit layout. 20 | * 21 | * For example, the bit layout of the value "42" (i.e. 00101010 set at position 22 | * 14 with length 6 is: 23 | * 24 | * 000000000000001010100000000000000000000000000000000000000000000 25 | * 26 | * and the same value and position but with length 8 is: 27 | * 28 | * 000000000000000010101000000000000000000000000000000000000000000 29 | * 30 | * If the architecture where is code is running is little-endian, the input data 31 | * will be swapped before grabbing the bit field. 32 | * 33 | * Examples 34 | * 35 | * uint64_t value = get_bitfield(data, 2, 4); 36 | * 37 | * Returns the value of the requested bit field, right aligned in a uint64_t. 38 | */ 39 | uint64_t eightbyte_get_bitfield(uint64_t source, const uint16_t offset, 40 | const uint16_t bit_count, const bool data_is_big_endian); 41 | 42 | /* Public: Return a single nibble from the payload, with range checking. 43 | * 44 | * source - the source payload. 45 | * nibble_index - the index of the nibble to retreive. The leftmost nibble is 46 | * index 0. 47 | * data_is_big_endian - if the data passed in is little endian, set this to false and it 48 | * will be flipped before grabbing the bit field. 49 | * 50 | * Returns the retreived nibble, right aligned in a uint8_t. 51 | */ 52 | uint8_t eightbyte_get_nibble(const uint64_t source, const uint8_t nibble_index, 53 | const bool data_is_big_endian); 54 | 55 | /* Public: Return a single byte from the payload, with range checking. 56 | * 57 | * source - the source byte array. 58 | * byte_index - the index of the byte to retreive. The leftmost byte is index 0. 59 | * data_is_big_endian - if the data passed in is little endian, set this to false and it 60 | * will be flipped before grabbing the bit field. 61 | * 62 | * Returns the retreived byte. 63 | */ 64 | uint8_t eightbyte_get_byte(const uint64_t source, const uint8_t byte_index, 65 | const bool data_is_big_endian); 66 | 67 | /* Public: Set the bit field in the given data array to the new value. 68 | * 69 | * destination - a byte array with size at least offset + bit_count. 70 | * value - the value to set in the bit field. 71 | * offset - the starting index of the bit field (beginning from 0). 72 | * bit_count - the number of bits to set in the data. 73 | * 74 | * Returns true if the bit_count is enough to fully represent the value, and 75 | * false if it will not fit. 76 | */ 77 | bool eightbyte_set_bitfield(uint64_t value, 78 | const uint16_t offset, const uint16_t bit_count, uint64_t* destination); 79 | 80 | /* Private: Determine the index of the last bit used. 81 | */ 82 | uint8_t find_end_bit(const uint16_t num_bits); 83 | 84 | #ifdef __cplusplus 85 | } 86 | #endif 87 | 88 | #endif // __8BYTE_H__ 89 | -------------------------------------------------------------------------------- /src/bitfield/bitarray.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define PREPARE_FIRST_COPY() \ 7 | do { \ 8 | if (bit_count >= (CHAR_BIT - destination_offset_modulo)) { \ 9 | *destination &= reverse_mask[destination_offset_modulo]; \ 10 | bit_count -= CHAR_BIT - destination_offset_modulo; \ 11 | } else { \ 12 | *destination &= reverse_mask[destination_offset_modulo] \ 13 | | reverse_mask_xor[destination_offset_modulo + bit_count];\ 14 | c &= reverse_mask[destination_offset_modulo + bit_count ];\ 15 | bit_count = 0; \ 16 | } } while (0) 17 | 18 | static const uint8_t reverse_mask[] = 19 | { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; 20 | static const uint8_t reverse_mask_xor[] = 21 | { 0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01, 0x00 }; 22 | 23 | bool copy_bits(const uint8_t* source_origin, const uint16_t source_length, 24 | const uint16_t source_offset, uint16_t bit_count, 25 | uint8_t* destination_origin, const uint16_t destination_length, 26 | const uint16_t destination_offset) { 27 | if(bit_count < 1) { 28 | return false; 29 | } 30 | 31 | if(source_offset + bit_count > source_length * CHAR_BIT || 32 | destination_offset + bit_count > destination_length * CHAR_BIT ) { 33 | return false; 34 | } 35 | 36 | const uint8_t* source = source_origin + (source_offset / CHAR_BIT); 37 | uint8_t* destination = destination_origin + (destination_offset / CHAR_BIT); 38 | int source_offset_modulo = source_offset % CHAR_BIT; 39 | int destination_offset_modulo = destination_offset % CHAR_BIT; 40 | 41 | if(source_offset_modulo == destination_offset_modulo) { 42 | if(source_offset_modulo > 0) { 43 | uint8_t c = reverse_mask_xor[destination_offset_modulo] & *source++; 44 | PREPARE_FIRST_COPY(); 45 | *destination++ |= c; 46 | } 47 | 48 | int byte_len = bit_count / CHAR_BIT; 49 | int bit_count_modulo = bit_count % CHAR_BIT; 50 | 51 | if(byte_len > 0) { 52 | memcpy(destination, source, byte_len); 53 | source += byte_len; 54 | destination += byte_len; 55 | } 56 | 57 | if(bit_count_modulo > 0) { 58 | *destination &= reverse_mask_xor[bit_count_modulo]; 59 | *destination |= reverse_mask[bit_count_modulo] & *source; 60 | } 61 | } else { 62 | int bit_diff_left_shift; 63 | int bit_diff_right_shift; 64 | uint8_t c; 65 | /* 66 | * Begin: Line things up on destination. 67 | */ 68 | if(source_offset_modulo > destination_offset_modulo) { 69 | bit_diff_left_shift = source_offset_modulo - destination_offset_modulo; 70 | bit_diff_right_shift = CHAR_BIT - bit_diff_left_shift; 71 | 72 | c = *source++ << bit_diff_left_shift; 73 | c |= *source >> bit_diff_right_shift; 74 | c &= reverse_mask_xor[destination_offset_modulo]; 75 | } else { 76 | bit_diff_right_shift = destination_offset_modulo - source_offset_modulo; 77 | bit_diff_left_shift = CHAR_BIT - bit_diff_right_shift; 78 | 79 | c = *source >> bit_diff_right_shift & 80 | reverse_mask_xor[destination_offset_modulo]; 81 | } 82 | PREPARE_FIRST_COPY(); 83 | *destination++ |= c; 84 | 85 | /* 86 | * Middle: copy with only shifting the source. 87 | */ 88 | int byte_len = bit_count / CHAR_BIT; 89 | while(--byte_len >= 0) { 90 | c = *source++ << bit_diff_left_shift; 91 | c |= *source >> bit_diff_right_shift; 92 | *destination++ = c; 93 | } 94 | 95 | /* 96 | * End: copy the remaing bits; 97 | */ 98 | int bit_count_modulo = bit_count % CHAR_BIT; 99 | if(bit_count_modulo > 0) { 100 | c = *source++ << bit_diff_left_shift; 101 | c |= *source >> bit_diff_right_shift; 102 | c &= reverse_mask[bit_count_modulo]; 103 | 104 | *destination &= reverse_mask_xor[bit_count_modulo]; 105 | *destination |= c; 106 | } 107 | } 108 | return true; 109 | } 110 | 111 | uint16_t bits_to_bytes(uint32_t bits) { 112 | uint8_t byte_count = bits / CHAR_BIT; 113 | if(bits % CHAR_BIT != 0) { 114 | ++byte_count; 115 | } 116 | return byte_count; 117 | } 118 | 119 | /** 120 | * Find the ending bit of a bitfield within the final byte. 121 | * 122 | * Returns: a bit position from 0 to 7. 123 | */ 124 | uint8_t find_end_bit(const uint16_t numBits) { 125 | int endBit = numBits % CHAR_BIT; 126 | return endBit == 0 ? CHAR_BIT : endBit; 127 | } 128 | 129 | bool copy_bits_right_aligned(const uint8_t source[], const uint16_t source_length, 130 | const uint16_t offset, const uint16_t bit_count, 131 | uint8_t* destination, const uint16_t destination_length) { 132 | return copy_bits(source, source_length, offset, bit_count, destination, 133 | destination_length, 134 | // provide a proper destination offset so the result is right 135 | // aligned 136 | (destination_length - bits_to_bytes(bit_count)) * CHAR_BIT + 137 | CHAR_BIT - find_end_bit(bit_count)); 138 | } 139 | 140 | bool copy_bytes_right_aligned(const uint8_t source[], const uint16_t source_length, 141 | const uint16_t offset, const uint16_t byte_count, 142 | uint8_t* destination, const uint16_t destination_length) { 143 | return copy_bits_right_aligned(source, source_length, offset * CHAR_BIT, 144 | byte_count * CHAR_BIT, destination, destination_length); 145 | } 146 | -------------------------------------------------------------------------------- /src/bitfield/bitfield.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #ifndef _MSC_VER 7 | #include 8 | 9 | static inline uint64_t bswap64(uint64_t v) 10 | { 11 | return __builtin_bswap64(v); 12 | } 13 | 14 | static inline bool is_little_endian(void) 15 | { 16 | return BYTE_ORDER == LITTLE_ENDIAN; 17 | } 18 | 19 | #else 20 | 21 | static inline uint64_t bswap64(uint64_t v) 22 | { 23 | return ((v << 56) & 0xff00000000000000ULL) | 24 | ((v << 40) & 0x00ff000000000000ULL) | 25 | ((v << 24) & 0x0000ff0000000000ULL) | 26 | ((v << 8) & 0x000000ff00000000ULL) | 27 | ((v >> 8) & 0x00000000ff000000ULL) | 28 | ((v >> 24) & 0x0000000000ff0000ULL) | 29 | ((v >> 40) & 0x000000000000ff00ULL) | 30 | ((v >> 56) & 0x00000000000000ffULL); 31 | } 32 | 33 | static inline bool is_little_endian(void) 34 | { 35 | union EndianTest 36 | { 37 | uint32_t word; 38 | uint8_t bytes[4]; 39 | }; 40 | 41 | static const union EndianTest ENDIAN_TEST = 42 | { 43 | .word = 1 44 | }; 45 | 46 | return ENDIAN_TEST.bytes[0] == 1; 47 | } 48 | #endif 49 | 50 | uint64_t bitmask ( const uint8_t bit_count ) 51 | { 52 | if ( bit_count >= 64 ) 53 | { 54 | return 0xFFFFFFFFFFFFFFFF; 55 | } 56 | else 57 | { 58 | return ( ( ( uint64_t ) 0x1 ) << bit_count ) - 1; 59 | } 60 | } 61 | 62 | uint8_t get_nibble(const uint8_t source[], const uint8_t source_length, 63 | const uint8_t nibble_index) { 64 | uint8_t byte_index = nibble_index / 2; 65 | uint8_t result = get_byte(source, source_length, byte_index); 66 | if(nibble_index % 2 == 0) { 67 | result >>= NIBBLE_SIZE; 68 | } 69 | result &= bitmask(NIBBLE_SIZE); 70 | return result; 71 | } 72 | 73 | uint8_t get_byte(const uint8_t source[], const uint8_t source_length, 74 | const uint8_t byte_index) { 75 | if(byte_index < source_length) { 76 | return source[byte_index]; 77 | } 78 | return 0; 79 | } 80 | 81 | uint64_t get_bitfield(const uint8_t source[], const uint16_t source_length, 82 | const uint16_t offset, const uint16_t bit_count) { 83 | if(bit_count > 64 || bit_count < 1) { 84 | // TODO error reporting? 85 | return 0; 86 | } 87 | 88 | ArrayOrBytes combined; 89 | memset(combined.bytes, 0, sizeof(combined.bytes)); 90 | if(copy_bits_right_aligned(source, source_length, offset, bit_count, 91 | combined.bytes, sizeof(combined.bytes))) { 92 | if(is_little_endian()) { 93 | combined.whole = bswap64(combined.whole); 94 | } 95 | } else { 96 | // debug("couldn't copy enough bits from source") 97 | } 98 | return combined.whole; 99 | } 100 | 101 | bool set_nibble(const uint16_t nibble_index, const uint8_t value, 102 | uint8_t* destination, const uint16_t destination_length) { 103 | return copy_bits(&value, CHAR_BIT, NIBBLE_SIZE, NIBBLE_SIZE, destination, 104 | destination_length, nibble_index * NIBBLE_SIZE); 105 | } 106 | 107 | bool set_bitfield(const uint64_t value, const uint16_t offset, 108 | const uint16_t bit_count, uint8_t destination[], 109 | uint16_t destination_length) { 110 | if(value > bitmask(bit_count)) { 111 | return false; 112 | } 113 | 114 | ArrayOrBytes combined = { 115 | .whole=value 116 | }; 117 | 118 | if (is_little_endian()) { 119 | combined.whole = bswap64(combined.whole); 120 | } 121 | 122 | return copy_bits(combined.bytes, sizeof(combined.bytes), 123 | sizeof(combined.bytes) * CHAR_BIT - bit_count, bit_count, 124 | destination, destination_length, offset); 125 | } 126 | -------------------------------------------------------------------------------- /src/bitfield/bitfield.h: -------------------------------------------------------------------------------- 1 | #ifndef __BITFIELD_H__ 2 | #define __BITFIELD_H__ 3 | 4 | #include 5 | #include 6 | 7 | #define NIBBLE_SIZE (CHAR_BIT / 2) 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | /* Public: Reads a subset of bits into a uint64_t, right aligned so they may be 14 | * interpreted as a number. 15 | * 16 | * source - the bytes in question. 17 | * source_size - the number of bytes in the source. 18 | * offset - the starting index of the bit field (beginning from 0). 19 | * bit_count - the width of the bit field to extract. This must be less than or 20 | * equal to 64. 21 | * 22 | * Bit fields are positioned according to big-endian bit layout and the data is 23 | * swapped automatically as necessary depending on the compiled architecture. 24 | * 25 | * For example, the bit layout of the value "42" (i.e. 00101010 set at position 26 | * 14 with length 6 is: 27 | * 28 | * 000000000000001010100000000000000000000000000000000000000000000 29 | * 30 | * and the same value and position but with length 8 is: 31 | * 32 | * 000000000000000010101000000000000000000000000000000000000000000 33 | * 34 | * Examples 35 | * 36 | * uint64_t value = get_bitfield(data, data_size, 2, 4); 37 | * 38 | * Returns the value of the requested bit field, right aligned in a uint64_t. 39 | */ 40 | uint64_t get_bitfield(const uint8_t source[], const uint16_t source_length, 41 | const uint16_t offset, const uint16_t bit_count); 42 | 43 | /* Public: Return a single nibble from the byte array, with range checking. 44 | * 45 | * source - the source byte array. 46 | * source_length - the total length of the source array. 47 | * nibble_index - the index of the nibble to retreive. The leftmost nibble is 48 | * index 0. 49 | * 50 | * Returns the retreived nibble, right aligned in a uint8_t. 51 | */ 52 | uint8_t get_nibble(const uint8_t source[], const uint8_t source_length, 53 | const uint8_t nibble_index); 54 | 55 | /* Public: Return a single byte from the byte array, with range checking. 56 | * 57 | * source - the source byte array. 58 | * source_length - the total length of the source array. 59 | * byte_index - the index of the byte to retreive. The leftmost byte is index 0. 60 | * 61 | * Returns the retreived byte. 62 | */ 63 | uint8_t get_byte(const uint8_t source[], const uint8_t source_length, 64 | const uint8_t byte_index); 65 | 66 | /* Public: Copy a range of bits from one bit array to another. 67 | * 68 | * The range does not need to be byte aligned, and the source and destination do 69 | * not have to be the same size (as long as the desitnation has enough room to 70 | * fit the range). 71 | * 72 | * A bit array with regards to this function always has the leftmost bit in byte 73 | * 0, i.e. bit index is the leftmost bit of byte 0. Endianness does not matter. 74 | * 75 | * For example: 76 | * 77 | * uint8_t source[4] = {0x11, 0x22, 0x33, 0x44}; 78 | * uint8_t destination[4] = {0}; 79 | * copy_bits(source, sizeof(source), 8, 8, destination, 80 | * sizeof(destination), 0); 81 | * // destination[0] == 0x22 82 | * // destination[1] == 0x0 83 | * // destination[2] == 0x0 84 | * // destination[3] == 0x0 85 | * 86 | * Thanks to 87 | * http://stackoverflow.com/questions/3534535/whats-a-time-efficient-algorithm-to-copy-unaligned-bit-arrays 88 | * for the implementation of the algorithm. 89 | * 90 | * source_origin - the source array. 91 | * source_length - the total length of the source array in bytes, 92 | * for range checking. 93 | * source_offset - an offset in bits to start the copy from the source array. 94 | * Specify 0 to start from source_origin. 95 | * bit_count - the number of bits to copy. 96 | * destination_origin - the destination array. 97 | * desitnation_length - the total length of the destination array in bytes, 98 | * for range checking. 99 | * destination_offset - an offset in bits to start placing the copied range into 100 | * the destination array. Specify 0 to start from the beginning of the 101 | * destination. If you are copying a range not aligned on a byte, you 102 | * probably want to set this to a positive offset to right the resulting 103 | * bits in the destination. 104 | * 105 | * Returns true if the copy was successful and false if the range exceeded the 106 | * size of the source or destination, or if the range size negative or 0. 107 | */ 108 | bool copy_bits(const uint8_t* source_origin, const uint16_t source_length, 109 | const uint16_t source_offset, uint16_t bit_count, 110 | uint8_t* destination_origin, const uint16_t destination_length, 111 | const uint16_t destination_offset); 112 | 113 | /* Public: Copy a range of bits from one array to another, right aligning the 114 | * result. 115 | * 116 | * This is mostly useful if you want to cast the result to an integer type 117 | * instead of a byte array. 118 | * 119 | * For example: 120 | * 121 | * uint8_t source[4] = {0x11, 0x22, 0x33, 0x44}; 122 | * uint8_t destination[4] = {0}; 123 | * copy_bits_right_aligned(source, sizeof(source), 8, 8, destination, 124 | * sizeof(destination)); 125 | * // destination[0] == 0x0 126 | * // destination[1] == 0x0 127 | * // destination[2] == 0x0 128 | * // destination[3] == 0x22 129 | * 130 | * int value = (int)destination; 131 | * // value == 0x22 == 32 132 | * 133 | * The arguments are the same as copy_bits, but without the destination_offset 134 | * option - that's set automatically to right align the result. 135 | * 136 | * Returns true if the copy was successful and false if the range exceeded the 137 | * size of the source or destination, or if the range size negative or 0. 138 | */ 139 | bool copy_bits_right_aligned(const uint8_t source[], const uint16_t source_length, 140 | const uint16_t offset, const uint16_t bit_count, 141 | uint8_t* destination, const uint16_t destination_length); 142 | 143 | /* Public: Copy a range of bytes from one byte array to another. 144 | * 145 | * The source and destination do not have to be the same size (as long as the 146 | * desitnation has enough room to fit the range). 147 | * 148 | * source_origin - the source array. 149 | * source_length - the total length of the source array in bytes, 150 | * for range checking. 151 | * source_offset - a byte offset to start the copy from the source array. 152 | * Specify 0 to start from source_origin. 153 | * byte_count - the number of bytes to copy. 154 | * destination_origin - the destination array. 155 | * desitnation_length - the total length of the destination array in bytes, 156 | * for range checking. 157 | * destination_offset - an offset in bytes to start placing the copied range into 158 | * the destination array. Specify 0 to start from the beginning of the 159 | * destination. 160 | * 161 | * Returns true if the copy was successful and false if the range exceeded the 162 | * size of the source or destination, or if the range size negative or 0. 163 | */ 164 | bool copy_bytes_right_aligned(const uint8_t source[], const uint16_t source_length, 165 | const uint16_t offset, const uint16_t byte_count, 166 | uint8_t* destination, const uint16_t destination_length); 167 | 168 | /* Public: Set the a nibble in the given data array to the new value. 169 | * 170 | * nibble_index - the index of the nibble to retreive. The leftmost nibble is 171 | * index 0. 172 | * value - the value to set in the bit field. 173 | * destination - the destination array. 174 | * destination_length - the total length of the destination array in bytes, 175 | * for range checking. 176 | * 177 | * Returns true if the bit_count is enough to fully represent the value, and 178 | * false if it will not fit. 179 | */ 180 | bool set_nibble(const uint16_t nibble_index, const uint8_t value, 181 | uint8_t* destination, const uint16_t destination_length); 182 | 183 | /* Public: Set the bit field in the given data array to the new value. 184 | * 185 | * value - the value to set in the bit field. 186 | * offset - the starting index of the bit field (beginning from 0). 187 | * bit_count - the number of bits to set in the data. 188 | * destination - the destination array. 189 | * destination_length - the total length of the destination array in bytes, 190 | * for range checking. 191 | * 192 | * Returns true if the bit_count is enough to fully represent the value, and 193 | * false if it will not fit. 194 | */ 195 | bool set_bitfield(const uint64_t value, const uint16_t offset, 196 | const uint16_t bit_count, uint8_t destination[], 197 | uint16_t destination_length); 198 | 199 | /* Public: Return a right aligned bitmask for a uint64_t. 200 | * 201 | * bit_count - the number of bits to mask, right aligned. 202 | */ 203 | uint64_t bitmask(const uint8_t bit_count); 204 | 205 | /* Private: 206 | */ 207 | uint16_t bits_to_bytes(uint32_t bits); 208 | 209 | /* Private: A union to assist swapping between uint64_t and a uint8_t array. 210 | */ 211 | typedef union { 212 | uint64_t whole; 213 | uint8_t bytes[sizeof(uint64_t)]; 214 | } ArrayOrBytes; 215 | 216 | #ifdef __cplusplus 217 | } 218 | #endif 219 | 220 | #endif // __BITFIELD_H__ 221 | -------------------------------------------------------------------------------- /src/canutil/read.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static float decode_float(uint64_t raw, float factor, float offset) { 6 | return raw * factor + offset; 7 | } 8 | 9 | float eightbyte_parse_float(uint64_t data, uint8_t bit_offset, uint8_t bit_size, 10 | float factor, float offset) { 11 | return decode_float(eightbyte_get_bitfield(data, bit_offset, bit_size, 12 | true), factor, offset); 13 | } 14 | 15 | bool eightbyte_parse_bool(uint64_t data, uint8_t bit_offset, uint8_t bit_size, 16 | float factor, float offset) { 17 | float value = eightbyte_parse_float(data, bit_offset, bit_size, factor, offset); 18 | return value == 0.0 ? false : true; 19 | } 20 | 21 | float bitfield_parse_float(const uint8_t source[], const uint16_t source_length, 22 | const uint8_t bit_offset, const uint8_t bit_size, const float factor, 23 | const float offset) { 24 | return decode_float(get_bitfield(source, source_length, bit_offset, bit_size), 25 | factor, offset); 26 | } 27 | 28 | bool bitfield_parse_bool(const uint8_t source[], const uint16_t source_length, 29 | const uint8_t bit_offset, const uint8_t bit_size, const float factor, 30 | const float offset) { 31 | float value = bitfield_parse_float(source, source_length, bit_offset, 32 | bit_size, factor, offset); 33 | return value == 0.0 ? false : true; 34 | } 35 | -------------------------------------------------------------------------------- /src/canutil/read.h: -------------------------------------------------------------------------------- 1 | #ifndef __READ_H__ 2 | #define __READ_H__ 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | /* Public: Parse a CAN signal from a message and apply required transformation. 12 | * 13 | * source - the payload containing the signal. 14 | * bit_offset - the starting bit for the signal. 15 | * bit_size - the width of the signal. 16 | * factor - the transformation factor for the signal value, applied after 17 | * pulling out the bit field. Use 1.0 for no factor. 18 | * offset - the transformation offset for the signal value, applied after 19 | * pulling out the bit field. Use 0 for no offset. 20 | * 21 | * Returns the decoded and transformed value of the signal. 22 | */ 23 | float eightbyte_parse_float(const uint64_t source, const uint8_t bit_offset, 24 | const uint8_t bit_size, const float factor, const float offset); 25 | 26 | /* Public: Parse a CAN signal from a message storage as a byte array and apply 27 | * required transformation. 28 | * 29 | * source - the payload containing the signal. 30 | * source_size - the size of the payload in bytes. 31 | * bit_offset - the starting bit for the signal. 32 | * bit_size - the width of the signal. 33 | * factor - the transformation factor for the signal value, applied after 34 | * pulling out the bit field. Use 1.0 for no factor. 35 | * offset - the transformation offset for the signal value, applied after 36 | * pulling out the bit field. Use 0 for no offset. 37 | * 38 | * Returns the decoded and transformed value of the signal. 39 | */ 40 | float bitfield_parse_float(const uint8_t source[], const uint16_t source_size, 41 | const uint8_t bit_offset, const uint8_t bit_size, const float factor, 42 | const float offset); 43 | 44 | /* Public: Parse a CAN signal from a message and interpret it as a boolean. 45 | * 46 | * source - the payload containing the signal. 47 | * bit_offset - the starting bit for the signal. 48 | * bit_size - the width of the signal. 49 | * factor - the transformation factor for the signal value, applied after 50 | * pulling out the bit field. Use 1.0 for no factor. 51 | * offset - the transformation offset for the signal value, applied after 52 | * pulling out the bit field. Use 0 for no offset. 53 | * 54 | * Returns false if the value was 0, otherwise true. 55 | */ 56 | bool eightbyte_parse_bool(uint64_t source, uint8_t bit_offset, uint8_t bit_size, 57 | float factor, float offset); 58 | 59 | /* Public: Parse a CAN signal from a message storage as a byte array and 60 | * interpret it as a boolean. 61 | * 62 | * source - the payload containing the signal. 63 | * source_size - the size of the payload in bytes. 64 | * bit_offset - the starting bit for the signal. 65 | * bit_size - the width of the signal. 66 | * factor - the transformation factor for the signal value, applied after 67 | * pulling out the bit field. Use 1.0 for no factor. 68 | * offset - the transformation offset for the signal value, applied after 69 | * pulling out the bit field. Use 0 for no offset. 70 | * 71 | * Returns false if the value was 0, otherwise true. 72 | */ 73 | bool bitfield_parse_bool(const uint8_t source[], const uint16_t source_size, 74 | const uint8_t bit_offset, const uint8_t bit_size, const float factor, 75 | const float offset); 76 | 77 | #ifdef __cplusplus 78 | } 79 | #endif 80 | 81 | #endif // __READ_H__ 82 | -------------------------------------------------------------------------------- /src/canutil/write.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | uint64_t float_to_fixed_point(const double value, const float factor, 6 | const float offset) { 7 | double raw = (value - offset) / factor; 8 | if(raw > 0) { 9 | // round up to avoid losing precision when we cast to an int 10 | // TODO do we need a way to encode an int back to a signal without any 11 | // rounding? 12 | raw += 0.5; 13 | } 14 | return (uint64_t)raw; 15 | } 16 | 17 | uint64_t eightbyte_encode_float(double value, uint8_t bit_offset, uint8_t bit_size, 18 | float factor, float offset) { 19 | uint64_t result = 0; 20 | if(!eightbyte_set_bitfield(float_to_fixed_point(value, factor, offset), 21 | bit_offset, bit_size, &result)) { 22 | // debug("%f will not fit in a %d bit field", value, bit_size); 23 | } 24 | return result; 25 | } 26 | 27 | uint64_t eightbyte_encode_bool(const bool value, const uint8_t bit_offset, 28 | const uint8_t bit_size) { 29 | return eightbyte_encode_float(value, bit_offset, bit_size, 1.0, 0); 30 | } 31 | 32 | bool bitfield_encode_float(const double value, const uint8_t bit_offset, 33 | const uint8_t bit_size, const float factor, const float offset, 34 | uint8_t destination[], const uint8_t destination_length) { 35 | if(!set_bitfield(float_to_fixed_point(value, factor, offset), bit_offset, 36 | bit_size, destination, destination_length)) { 37 | // debug("%f will not fit in a %d bit field", value, bit_size); 38 | return false; 39 | } 40 | return true; 41 | } 42 | 43 | bool bitfield_encode_bool(const bool value, const uint8_t bit_offset, 44 | const uint8_t bit_size, uint8_t destination[], 45 | const uint16_t destination_length) { 46 | return bitfield_encode_float(value, bit_offset, bit_size, 1.0, 0, 47 | destination, destination_length); 48 | } 49 | -------------------------------------------------------------------------------- /src/canutil/write.h: -------------------------------------------------------------------------------- 1 | #ifndef __WRITE_H__ 2 | #define __WRITE_H__ 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | /* Public: Encode a floating point number into a fixed point, fixed bit width 12 | * field in a bit array. 13 | * 14 | * value - the floating point value to encode. 15 | * bit_offset - the starting point for the encoded bits in the returned value. 16 | * bit_size - The max width of the field in the resulting bit array. If bit_size 17 | * isn't big enough to store the fixed point version of the value, the 18 | * bitfeld will *not* be set. TODO some error reporting would be nice. 19 | * factor - a factor used to transform from floating to fixed point before 20 | * encoding. Use 1.0 for no factor. 21 | * offset - an offset used to transform from floating to fixed point before 22 | * encoding. Use 0 for no offset. 23 | * 24 | * Returns a big-endian uint64_t with the value encoded as a bitfield. 25 | */ 26 | uint64_t eightbyte_encode_float(double value, uint8_t bit_offset, 27 | uint8_t bit_size, float factor, float offset); 28 | 29 | uint64_t float_to_fixed_point(const double value, const float factor, 30 | const float offset); 31 | 32 | bool bitfield_encode_float(const double value, const uint8_t bit_offset, 33 | const uint8_t bit_size, const float factor, const float offset, 34 | uint8_t destination[], const uint8_t destination_length); 35 | 36 | /* Public: Encode a boolean into fixed bit width field in a bit array. 37 | * 38 | * value - the boolean value to encode - true will be 1, false will be 0. 39 | * bit_offset - the starting point for the encoded bits in the returned value. 40 | * bit_size - The max width of the field in the resulting bit array. If bit_size 41 | * isn't big enough to store the fixed point version of the value, the 42 | * bitfeld will *not* be set. TODO some error reporting would be nice. 43 | * 44 | * Returns a big-endian uint64_t with the value encoded as a bitfield. 45 | */ 46 | uint64_t eightbyte_encode_bool(const bool value, const uint8_t bit_offset, 47 | const uint8_t bit_size); 48 | 49 | bool bitfield_encode_bool(const bool value, const uint8_t bit_offset, const 50 | uint8_t bit_size, uint8_t destination[], 51 | const uint16_t destination_length); 52 | 53 | #ifdef __cplusplus 54 | } 55 | #endif 56 | 57 | #endif // __WRITE_H__ 58 | -------------------------------------------------------------------------------- /tests/8byte_tests.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | START_TEST (test_large_bitmask) 7 | { 8 | // 0 bits 9 | uint64_t result = bitmask(0); 10 | fail_if(result != 0); 11 | // 8 bits 12 | result = bitmask(8); 13 | fail_if(result != 0xff); 14 | // 16 bits 15 | result = bitmask(16); 16 | fail_if(result != 0xffff); 17 | // 32 bits 18 | result = bitmask(32); 19 | fail_if(result != 0xffffffff); 20 | // 64 bits 21 | result = bitmask(64); 22 | fail_if(result != 0xffffffffffffffff); 23 | // 128 bits 24 | result = bitmask(128); 25 | fail_if(result != 0xffffffffffffffff); 26 | } 27 | END_TEST 28 | 29 | START_TEST (test_one_bit_not_swapped) 30 | { 31 | uint64_t data = 0x80; 32 | uint64_t result = eightbyte_get_bitfield(data, 0, 1, false); 33 | fail_if(result == 1); 34 | } 35 | END_TEST 36 | 37 | START_TEST (test_one_bit) 38 | { 39 | uint64_t data = 0x8000000000000000; 40 | uint64_t result = eightbyte_get_bitfield(data, 0, 1, false); 41 | fail_unless(result == 0x1, 42 | "First bit in 0x%llx was 0x%llx instead of 0x1", data, result); 43 | } 44 | END_TEST 45 | 46 | START_TEST (test_32_bit_parse) 47 | { 48 | uint64_t data = 0x0402574d555a0401; 49 | uint64_t result = eightbyte_get_bitfield(data, 16, 32, false); 50 | uint64_t expectedValue = 0x574d555a; 51 | fail_unless(result == expectedValue, 52 | "Field retrieved in 0x%llx was 0x%llx instead of 0x%llx", data, 53 | result, expectedValue); 54 | } 55 | END_TEST 56 | 57 | START_TEST (test_16_bit_parse) 58 | { 59 | uint64_t data = 0xF34DFCFF00000000; 60 | uint64_t result = eightbyte_get_bitfield(data, 16, 16, false); 61 | uint64_t expectedValue = 0xFCFF; 62 | fail_unless(result == expectedValue, 63 | "Field retrieved in 0x%llx was 0x%llx instead of 0x%llx", data, 64 | result, expectedValue); 65 | } 66 | END_TEST 67 | 68 | START_TEST (test_one_byte) 69 | { 70 | uint64_t data = 0xFA00000000000000; 71 | uint64_t result = eightbyte_get_bitfield(data, 0, 4, false); 72 | fail_unless(result == 0xF, 73 | "First nibble in 0x%llx was 0x%llx instead of 0xF", data, result); 74 | result = eightbyte_get_bitfield(data, 4, 4, false); 75 | fail_unless(result == 0xA, 76 | "Second nibble in 0x%llx was 0x%llx instead of 0xA", data, result); 77 | result = eightbyte_get_bitfield(data, 0, 8, false); 78 | fail_unless(result == 0xFA, 79 | "All bits in 0x%llx were 0x%llx instead of 0x%llx", data, result, data); 80 | } 81 | END_TEST 82 | 83 | START_TEST (test_multi_byte) 84 | { 85 | uint64_t data = 0x12FA000000000000; 86 | uint64_t result = eightbyte_get_bitfield(data, 0, 4, false); 87 | fail_unless(result == 0x1, 88 | "First 4 bits in 0x%llx was 0x%llx instead of 0xF", (data >> 60) & 0xF, 89 | result); 90 | result = eightbyte_get_bitfield(data, 4, 4, false); 91 | fail_unless(result == 0x2, 92 | "Second 4 bits in 0x%llx was 0x%llx instead of 0xA", (data >> 56) & 0xF, 93 | result); 94 | result = eightbyte_get_bitfield(data, 8, 4, false); 95 | fail_unless(result == 0xF, 96 | "First 4 bits in 0x%llx was 0x%llx instead of 0x1", (data >> 52) & 0xF, 97 | result); 98 | result = eightbyte_get_bitfield(data, 12, 4, false); 99 | fail_unless(result == 0xA, 100 | "Second 4 bits in 0x%llx was 0x%llx instead of 0x2", (data >> 48) % 0xF, 101 | result); 102 | } 103 | END_TEST 104 | 105 | START_TEST (test_get_multi_byte) 106 | { 107 | uint64_t data = 0x12FA000000000000; 108 | uint64_t result = eightbyte_get_bitfield(data, 0, 9, false); 109 | ck_assert_int_eq(result, 0x25); 110 | } 111 | END_TEST 112 | 113 | START_TEST (test_get_off_byte_boundary) 114 | { 115 | uint64_t data = 0x000012FA00000000; 116 | uint64_t result = eightbyte_get_bitfield(data, 12, 8, false); 117 | ck_assert_int_eq(result, 0x01); 118 | } END_TEST 119 | 120 | START_TEST (test_set_wont_fit) 121 | { 122 | uint64_t data = 0; 123 | fail_if(eightbyte_set_bitfield(100, 0, 1, &data)); 124 | } 125 | END_TEST 126 | 127 | START_TEST (test_set_field) 128 | { 129 | uint64_t data = 0; 130 | fail_unless(eightbyte_set_bitfield(1, 0, 1, &data)); 131 | uint64_t result = eightbyte_get_bitfield(data, 0, 1, false); 132 | ck_assert_int_eq(result, 0x1); 133 | data = 0; 134 | fail_unless(eightbyte_set_bitfield(1, 1, 1, &data)); 135 | result = eightbyte_get_bitfield(data, 1, 1, false); 136 | ck_assert_int_eq(result, 0x1); 137 | 138 | data = 0; 139 | fail_unless(eightbyte_set_bitfield(0xf, 3, 4, &data)); 140 | result = eightbyte_get_bitfield(data, 3, 4, false); 141 | ck_assert_int_eq(result, 0xf); 142 | } 143 | END_TEST 144 | 145 | START_TEST (test_set_doesnt_clobber_existing_data) 146 | { 147 | uint64_t data = 0xFFFC4DF300000000; 148 | fail_unless(eightbyte_set_bitfield(0x4fc8, 16, 16, &data)); 149 | uint64_t result = eightbyte_get_bitfield(data, 16, 16, false); 150 | fail_unless(result == 0x4fc8, 151 | "Field retrieved in 0x%llx was 0x%llx instead of 0x%x", data, result, 152 | 0xc84f); 153 | 154 | data = 0x8000000000000000; 155 | fail_unless(eightbyte_set_bitfield(1, 21, 1, &data)); 156 | fail_unless(data == 0x8000040000000000LLU, 157 | "Expected combined value 0x8000040000000000 but got 0x%llx%llx", 158 | data >> 32, data); 159 | } 160 | END_TEST 161 | 162 | START_TEST (test_set_off_byte_boundary) 163 | { 164 | uint64_t data = 0xFFFC4DF300000000; 165 | fail_unless(eightbyte_set_bitfield(0x12, 12, 8, &data)); 166 | uint64_t result = eightbyte_get_bitfield(data, 12, 12, false); 167 | ck_assert_int_eq(result,0x12d); 168 | } 169 | END_TEST 170 | 171 | START_TEST (test_set_odd_number_of_bits) 172 | { 173 | uint64_t data = 0xFFFC4DF300000000LLU; 174 | fail_unless(eightbyte_set_bitfield(0x12, 11, 5, &data)); 175 | uint64_t result = eightbyte_get_bitfield(data, 11, 5, false); 176 | fail_unless(result == 0x12, 177 | "Field set in 0x%llx%llx%llx%llx was 0x%llx instead of 0x%llx", data, result, 178 | 0x12); 179 | 180 | data = 0xFFFC4DF300000000LLU; 181 | fail_unless(eightbyte_set_bitfield(0x2, 11, 5, &data)); 182 | result = eightbyte_get_bitfield(data, 11, 5, false); 183 | fail_unless(result == 0x2, 184 | "Field set in 0x%llx%llx%llx%llx was 0x%llx instead of 0x%llx", data, result, 185 | 0x2); 186 | } 187 | END_TEST 188 | 189 | START_TEST(test_eightbyte_get_byte) 190 | { 191 | uint64_t data = 0x00000000F34DFCFF; 192 | uint8_t result = eightbyte_get_byte(data, 0, false); 193 | uint8_t expected = 0x0; 194 | ck_assert_int_eq(result, expected); 195 | 196 | result = eightbyte_get_byte(data, 4, false); 197 | expected = 0xF3; 198 | ck_assert_int_eq(result, expected); 199 | 200 | result = eightbyte_get_byte(data, 5, false); 201 | expected = 0x4D; 202 | ck_assert_int_eq(result, expected); 203 | 204 | result = eightbyte_get_byte(data, 6, false); 205 | expected = 0xFC; 206 | ck_assert_int_eq(result, expected); 207 | 208 | result = eightbyte_get_byte(data, 7, false); 209 | expected = 0xFF; 210 | ck_assert_int_eq(result, expected); 211 | } 212 | END_TEST 213 | 214 | START_TEST(test_eightbyte_get_nibble) 215 | { 216 | uint64_t data = 0x00000000F34DFCFF; 217 | uint8_t result = eightbyte_get_nibble(data, 0, false); 218 | uint8_t expected = 0x0; 219 | ck_assert_int_eq(result, expected); 220 | 221 | result = eightbyte_get_nibble(data, 2, false); 222 | expected = 0x0; 223 | ck_assert_int_eq(result, expected); 224 | 225 | result = eightbyte_get_nibble(data, 8, false); 226 | expected = 0xF; 227 | ck_assert_int_eq(result, expected); 228 | 229 | result = eightbyte_get_nibble(data, 9, false); 230 | expected = 0x3; 231 | ck_assert_int_eq(result, expected); 232 | 233 | result = eightbyte_get_nibble(data, 10, false); 234 | expected = 0x4; 235 | ck_assert_int_eq(result, expected); 236 | 237 | result = eightbyte_get_nibble(data, 13, false); 238 | expected = 0xC; 239 | ck_assert_int_eq(result, expected); 240 | } 241 | END_TEST 242 | 243 | Suite* bitfieldSuite(void) { 244 | Suite* s = suite_create("bitfield"); 245 | TCase *tc_core = tcase_create("core"); 246 | tcase_add_test(tc_core, test_large_bitmask); 247 | tcase_add_test(tc_core, test_one_bit); 248 | tcase_add_test(tc_core, test_one_bit_not_swapped); 249 | tcase_add_test(tc_core, test_one_byte); 250 | tcase_add_test(tc_core, test_16_bit_parse); 251 | tcase_add_test(tc_core, test_32_bit_parse); 252 | tcase_add_test(tc_core, test_multi_byte); 253 | tcase_add_test(tc_core, test_get_multi_byte); 254 | tcase_add_test(tc_core, test_get_off_byte_boundary); 255 | tcase_add_test(tc_core, test_set_wont_fit); 256 | tcase_add_test(tc_core, test_set_field); 257 | tcase_add_test(tc_core, test_set_doesnt_clobber_existing_data); 258 | tcase_add_test(tc_core, test_set_off_byte_boundary); 259 | tcase_add_test(tc_core, test_set_odd_number_of_bits); 260 | tcase_add_test(tc_core, test_eightbyte_get_nibble); 261 | tcase_add_test(tc_core, test_eightbyte_get_byte); 262 | suite_add_tcase(s, tc_core); 263 | 264 | return s; 265 | } 266 | 267 | int main(void) { 268 | int numberFailed; 269 | Suite* s = bitfieldSuite(); 270 | SRunner *sr = srunner_create(s); 271 | // Don't fork so we can actually use gdb 272 | srunner_set_fork_status(sr, CK_NOFORK); 273 | srunner_run_all(sr, CK_NORMAL); 274 | numberFailed = srunner_ntests_failed(sr); 275 | srunner_free(sr); 276 | return (numberFailed == 0) ? 0 : 1; 277 | } 278 | -------------------------------------------------------------------------------- /tests/bitfield_tests.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | START_TEST (test_get_byte) 6 | { 7 | uint8_t data[4] = {0x12, 0x34, 0x56, 0x78}; 8 | uint8_t result = get_byte(data, sizeof(data), 0); 9 | ck_assert_int_eq(result, 0x12); 10 | result = get_byte(data, sizeof(data), 3); 11 | ck_assert_int_eq(result, 0x78); 12 | } 13 | END_TEST 14 | 15 | START_TEST (test_set_nibble) 16 | { 17 | uint8_t data[4] = {0}; 18 | fail_unless(set_nibble(0, 0x1, data, sizeof(data))); 19 | fail_unless(set_nibble(1, 0x2, data, sizeof(data))); 20 | fail_unless(set_nibble(2, 0x3, data, sizeof(data))); 21 | fail_unless(set_nibble(3, 0x4, data, sizeof(data))); 22 | fail_unless(set_nibble(4, 0x5, data, sizeof(data))); 23 | ck_assert_int_eq(data[0], 0x12); 24 | ck_assert_int_eq(data[1], 0x34); 25 | ck_assert_int_eq(data[2], 0x50); 26 | } 27 | END_TEST 28 | 29 | START_TEST (test_set_bitfield) 30 | { 31 | uint8_t data[4] = {0}; 32 | fail_unless(set_bitfield(0x12, 0, 8, data, sizeof(data))); 33 | fail_unless(set_bitfield(bitmask(3), 10, 3, data, sizeof(data))); 34 | ck_assert_int_eq(data[0], 0x12); 35 | ck_assert_int_eq(data[1], 0x38); 36 | fail_unless(set_bitfield(0x1, 23, 1, data, sizeof(data))); 37 | ck_assert_int_eq(data[2], 0x1); 38 | fail_unless(set_bitfield(0x1, 21, 2, data, sizeof(data))); 39 | ck_assert_int_eq(data[2], 0x3); 40 | } 41 | END_TEST 42 | 43 | START_TEST (test_set_bitfield_doesnt_fit) 44 | { 45 | uint8_t data[4] = {0}; 46 | fail_if(set_bitfield(0xffff, 0, 8, data, sizeof(data))); 47 | ck_assert_int_eq(data[0], 0); 48 | ck_assert_int_eq(data[1], 0); 49 | ck_assert_int_eq(data[2], 0); 50 | ck_assert_int_eq(data[3], 0); 51 | } 52 | END_TEST 53 | 54 | START_TEST (test_get_nibble) 55 | { 56 | uint8_t data[4] = {0x12, 0x34, 0x56, 0x78}; 57 | uint8_t result = get_nibble(data, sizeof(data), 0); 58 | ck_assert_int_eq(result, 0x1); 59 | result = get_nibble(data, sizeof(data), 1); 60 | ck_assert_int_eq(result, 0x2); 61 | result = get_nibble(data, sizeof(data), 2); 62 | ck_assert_int_eq(result, 0x3); 63 | } 64 | END_TEST 65 | 66 | START_TEST (test_get_bits_out_of_range) 67 | { 68 | uint8_t data[4] = {0x12, 0x34, 0x56, 0x78}; 69 | uint8_t result[4]; 70 | fail_if(copy_bits_right_aligned(data, sizeof(data), 25, 16, result, 71 | sizeof(result))); 72 | } 73 | END_TEST 74 | 75 | START_TEST (test_get_bits) 76 | { 77 | uint8_t data[4] = {0x12, 0x34, 0x56, 0x78}; 78 | uint8_t result[4] = {0}; 79 | fail_unless(copy_bits_right_aligned(data, sizeof(data), 0, 16, result, 80 | sizeof(result))); 81 | ck_assert_int_eq(result[2], 0x12); 82 | ck_assert_int_eq(result[3], 0x34); 83 | } 84 | END_TEST 85 | 86 | START_TEST (test_copy_bytes) 87 | { 88 | uint8_t data[4] = {0x12, 0x34, 0x56, 0x78}; 89 | uint8_t result[4] = {0}; 90 | fail_unless(copy_bytes_right_aligned(data, sizeof(data), 1, 3, result, 91 | sizeof(result))); 92 | ck_assert_int_eq(result[1], 0x34); 93 | ck_assert_int_eq(result[2], 0x56); 94 | ck_assert_int_eq(result[3], 0x78); 95 | } 96 | END_TEST 97 | 98 | START_TEST (test_get_uneven_bits) 99 | { 100 | uint8_t data[4] = {0x12, 0x34, 0x56, 0x78}; 101 | uint8_t result[4] = {0}; 102 | fail_unless(copy_bits_right_aligned(data, sizeof(data), 4, 12, result, 103 | sizeof(result))); 104 | ck_assert_int_eq(result[2], 0x2); 105 | ck_assert_int_eq(result[3], 0x34); 106 | } 107 | END_TEST 108 | 109 | START_TEST (test_get_bitfiled) 110 | { 111 | uint8_t data[4] = {0x12, 0x34, 0x56, 0x78}; 112 | uint64_t result = get_bitfield(data, sizeof(data), 8, 16); 113 | ck_assert_int_eq(result, 0x3456); 114 | } 115 | END_TEST 116 | 117 | Suite* bitfieldSuite(void) { 118 | Suite* s = suite_create("bitfield"); 119 | TCase *tc_core = tcase_create("core"); 120 | tcase_add_test(tc_core, test_get_byte); 121 | tcase_add_test(tc_core, test_get_nibble); 122 | tcase_add_test(tc_core, test_set_nibble); 123 | tcase_add_test(tc_core, test_set_bitfield); 124 | tcase_add_test(tc_core, test_set_bitfield_doesnt_fit); 125 | tcase_add_test(tc_core, test_get_bits); 126 | tcase_add_test(tc_core, test_copy_bytes); 127 | tcase_add_test(tc_core, test_get_bits_out_of_range); 128 | tcase_add_test(tc_core, test_get_uneven_bits); 129 | tcase_add_test(tc_core, test_get_bitfiled); 130 | suite_add_tcase(s, tc_core); 131 | 132 | return s; 133 | } 134 | 135 | int main(void) { 136 | int numberFailed; 137 | Suite* s = bitfieldSuite(); 138 | SRunner *sr = srunner_create(s); 139 | // Don't fork so we can actually use gdb 140 | srunner_set_fork_status(sr, CK_NOFORK); 141 | srunner_run_all(sr, CK_NORMAL); 142 | numberFailed = srunner_ntests_failed(sr); 143 | srunner_free(sr); 144 | return (numberFailed == 0) ? 0 : 1; 145 | } 146 | -------------------------------------------------------------------------------- /tests/read_tests.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | const uint64_t BIG_ENDIAN_TEST_DATA = __builtin_bswap64(0xEB00000000000000); 6 | const uint8_t ARRAY_TEST_DATA[] = {0xEB, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; 7 | 8 | START_TEST (test_eightbyte_parse_float) 9 | { 10 | float result = eightbyte_parse_float(BIG_ENDIAN_TEST_DATA, 2, 4, 1001.0, 11 | -30000.0); 12 | float correctResult = 0xA * 1001.0 - 30000.0; 13 | fail_unless(result == correctResult, 14 | "parse is incorrect: %f but should be %f", result, correctResult); 15 | } 16 | END_TEST 17 | 18 | START_TEST (test_eightbyte_parse_bool) 19 | { 20 | bool result = eightbyte_parse_bool(BIG_ENDIAN_TEST_DATA, 0, 1, 1.0, 0); 21 | bool correctResult = true; 22 | fail_unless(result == correctResult, 23 | "parse is incorrect: %d but should be %d", result, correctResult); 24 | } 25 | END_TEST 26 | 27 | START_TEST (test_bitfield_parse_float) 28 | { 29 | float result = bitfield_parse_float(ARRAY_TEST_DATA, 30 | sizeof(ARRAY_TEST_DATA), 2, 4, 1001.0, -30000.0); 31 | float correctResult = 0xA * 1001.0 - 30000.0; 32 | fail_unless(result == correctResult, 33 | "parse is incorrect: %f but should be %f", result, correctResult); 34 | } 35 | END_TEST 36 | 37 | START_TEST (test_bitfield_parse_bool) 38 | { 39 | fail_unless(bitfield_parse_bool(ARRAY_TEST_DATA, sizeof(ARRAY_TEST_DATA), 40 | 0, 1, 1.0, 0)); 41 | } 42 | END_TEST 43 | 44 | Suite* canreadSuite(void) { 45 | Suite* s = suite_create("read"); 46 | TCase *tc_core = tcase_create("core"); 47 | tcase_add_checked_fixture(tc_core, NULL, NULL); 48 | tcase_add_test(tc_core, test_eightbyte_parse_float); 49 | tcase_add_test(tc_core, test_eightbyte_parse_bool); 50 | tcase_add_test(tc_core, test_bitfield_parse_float); 51 | tcase_add_test(tc_core, test_bitfield_parse_bool); 52 | suite_add_tcase(s, tc_core); 53 | 54 | return s; 55 | } 56 | 57 | int main(void) { 58 | int numberFailed; 59 | Suite* s = canreadSuite(); 60 | SRunner *sr = srunner_create(s); 61 | // Don't fork so we can actually use gdb 62 | srunner_set_fork_status(sr, CK_NOFORK); 63 | srunner_run_all(sr, CK_NORMAL); 64 | numberFailed = srunner_ntests_failed(sr); 65 | srunner_free(sr); 66 | return (numberFailed == 0) ? 0 : 1; 67 | } 68 | -------------------------------------------------------------------------------- /tests/write_tests.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | START_TEST (test_eightbyte_encode_float_precision) 6 | { 7 | uint64_t value = eightbyte_encode_float(50, 2, 19, 0.001, 0); 8 | ck_assert_int_eq(value, 0x061a800000000000LLU); 9 | } 10 | END_TEST 11 | 12 | START_TEST (test_eightbyte_encode_float) 13 | { 14 | uint64_t value = eightbyte_encode_float(0, 1, 3, 1, 0); 15 | ck_assert_int_eq(value, 0); 16 | 17 | value = eightbyte_encode_float(1, 1, 3, 1, 0); 18 | ck_assert_int_eq(value, 0x1000000000000000LLU); 19 | } 20 | END_TEST 21 | 22 | START_TEST (test_eightbyte_encode_bool) 23 | { 24 | uint64_t value = eightbyte_encode_bool(true, 1, 3); 25 | ck_assert_int_eq(value, 0x1000000000000000LLU); 26 | value = eightbyte_encode_bool(false, 1, 3); 27 | ck_assert_int_eq(value, 0x0000000000000000LLU); 28 | } 29 | END_TEST 30 | 31 | START_TEST (test_bitfield_encode_float) 32 | { 33 | uint8_t data[8] = {0}; 34 | bitfield_encode_float(0, 1, 3, 1, 0, data, sizeof(data)); 35 | ck_assert_int_eq(data[0], 0); 36 | ck_assert_int_eq(data[1], 0); 37 | ck_assert_int_eq(data[2], 0); 38 | ck_assert_int_eq(data[3], 0); 39 | ck_assert_int_eq(data[4], 0); 40 | ck_assert_int_eq(data[5], 0); 41 | ck_assert_int_eq(data[6], 0); 42 | ck_assert_int_eq(data[7], 0); 43 | 44 | bitfield_encode_float(1, 1, 3, 1, 0, data, sizeof(data)); 45 | ck_assert_int_eq(data[0], 0x10); 46 | ck_assert_int_eq(data[1], 0); 47 | ck_assert_int_eq(data[2], 0); 48 | ck_assert_int_eq(data[3], 0); 49 | ck_assert_int_eq(data[4], 0); 50 | ck_assert_int_eq(data[5], 0); 51 | ck_assert_int_eq(data[6], 0); 52 | ck_assert_int_eq(data[7], 0); 53 | } 54 | END_TEST 55 | 56 | START_TEST (test_bitfield_encode_bool) 57 | { 58 | uint8_t data[8] = {0}; 59 | bitfield_encode_bool(true, 1, 3, data, sizeof(data)); 60 | ck_assert_int_eq(data[0], 0x10); 61 | ck_assert_int_eq(data[1], 0); 62 | ck_assert_int_eq(data[2], 0); 63 | ck_assert_int_eq(data[3], 0); 64 | ck_assert_int_eq(data[4], 0); 65 | ck_assert_int_eq(data[5], 0); 66 | ck_assert_int_eq(data[6], 0); 67 | ck_assert_int_eq(data[7], 0); 68 | 69 | bitfield_encode_bool(false, 1, 3, data, sizeof(data)); 70 | ck_assert_int_eq(data[0], 0); 71 | ck_assert_int_eq(data[1], 0); 72 | ck_assert_int_eq(data[2], 0); 73 | ck_assert_int_eq(data[3], 0); 74 | ck_assert_int_eq(data[4], 0); 75 | ck_assert_int_eq(data[5], 0); 76 | ck_assert_int_eq(data[6], 0); 77 | ck_assert_int_eq(data[7], 0); 78 | } 79 | END_TEST 80 | 81 | START_TEST (test_float_to_fixed_point) 82 | { 83 | //uint64_t value = 0x0123456789ABCDEF; //doesn't cast hex string to uint64_t correctly 84 | double value = 81985529216486896; 85 | uint64_t encoded_value = float_to_fixed_point(value, 1, 0); 86 | ck_assert_int_eq(encoded_value, value); 87 | } 88 | END_TEST 89 | 90 | Suite* canwriteSuite(void) { 91 | Suite* s = suite_create("write"); 92 | TCase *tc_core = tcase_create("core"); 93 | tcase_add_checked_fixture(tc_core, NULL, NULL); 94 | tcase_add_test(tc_core, test_eightbyte_encode_float); 95 | tcase_add_test(tc_core, test_eightbyte_encode_bool); 96 | tcase_add_test(tc_core, test_eightbyte_encode_float_precision); 97 | tcase_add_test(tc_core, test_bitfield_encode_float); 98 | tcase_add_test(tc_core, test_bitfield_encode_bool); 99 | tcase_add_test(tc_core, test_float_to_fixed_point); 100 | suite_add_tcase(s, tc_core); 101 | 102 | return s; 103 | } 104 | 105 | int main(void) { 106 | int numberFailed; 107 | Suite* s = canwriteSuite(); 108 | SRunner *sr = srunner_create(s); 109 | // Don't fork so we can actually use gdb 110 | srunner_set_fork_status(sr, CK_NOFORK); 111 | srunner_run_all(sr, CK_NORMAL); 112 | numberFailed = srunner_ntests_failed(sr); 113 | srunner_free(sr); 114 | return (numberFailed == 0) ? 0 : 1; 115 | } 116 | --------------------------------------------------------------------------------