Search code examples
carraysuint8t

Slice up an uint8_t array


Let's say that I have an array of 16 uint8_t as follows:

uint8_t array[] = {0x13, 0x01, 0x4E, 0x52, 0x31, 0x4A, 0x35, 0x36, 0x4C, 0x11, 0x21, 0xC6, 0x3C, 0x73, 0xC2, 0x41};

This array stores the data contained in a 128 bits register of an external peripheral. Some of the information it represents are stored on 2, 3, 8, 12 bits ... and so on.

What is the best and elegant way to slice it up and bit mask the information I need? (The problem is that some things that I need overlaps the length of one cell of the array)

If that can help, this snippet I wrote converts the whole array into a char* string. But casting this into an int is not option because.. well 16 bytes.

int i;
char str[33];
for(i = 0; i < sizeof(array) / sizeof(*array) ; i++) {
    sprintf(str+2*i,"%02hX",array[i]);
}
puts(str); 

13014E52314A35364C1121C63C73C241


Solution

  • Actually such problem also occures when trying to parse all kind of bitstreams, like video or image files or compressed data by algorithms like LZ*. So the approach used there is to implement a bitstream reader.

    But in your case the bit sequence is fixed length and quite short, so one way is to manually check the field values using bitwise operations.

    Or you can use this function that I just wrote, which can extract arbitrary number of bits from a uint8 array, starting from desired bit position:

    uint32_t extract_bits(uint8_t *arr, unsigned int bit_index, unsigned int bit_count)
    {
        /* Assert that we are not requested to extract more than 32 bits */
        uint32_t result = 0;
        assert(bit_count <= sizeof(result)*8 && arr != NULL);
    
        /* You can additionally check if you are trying to extract bits exceeding the 16 byte range */
        assert(bit_index + bit_count <= 16 * 8);
    
        unsigned int arr_id = bit_index / 8;
        unsigned int bit_offset = bit_index % 8;
    
        if (bit_offset > 0) {
            /* Extract first 'unaligned_bit_count' bits, which happen to be non-byte-aligned.
             * When we do extract those bits, the remaining will be byte-aligned so
             * we will thread them in different manner.
             */
            unsigned int unaligned_bit_count = 8 - bit_offset;
    
            /* Check if we need less than the remaining unaligned bits */
            if (bit_count < unaligned_bit_count) {
                result = (arr[arr_id] >> bit_offset) & ((1 << bit_count) - 1);
                return result;
            }
    
            /* We need them all */
            result = arr[arr_id] >> bit_offset;
            bit_count -= unaligned_bit_count;
    
            /* Move to next byte element */
            arr_id++;
        }
    
        while (bit_count > 0) {
            /* Try to extract up to 8 bits per iteration */
            int bits_to_extract = bit_count > 8 ? 8 : bit_count;
    
            if (bits_to_extract < 8) {
                result = (result << bits_to_extract) | (arr[arr_id] & ((1 << bits_to_extract)-1));
            }else {
                result = (result << bits_to_extract) | arr[arr_id];
            }
    
            bit_count -= bits_to_extract;
            arr_id++;
        }
    
        return result;
    }
    

    Here is example of how it is used.

    uint32_t r;
    
    /* Extracts bits [7..8] and places them as most significant bits of 'r' */  
    r = extract_bits(arr, 7, 2)
    
    /* Extracts bits [4..35] and places them as most significant bits of 'r' */  
    r = extract_bits(arr, 4, 32);
    
    /* Visualize */
    printf("slice=%x\n", r);
    

    And then the visualisation of r is up to you. They can either be represented as hex dwords, characters, or however you decide.