0

Base on this question. I would like to store elements of arbitrary site, e.g., 5-bit, into an array of unsigned char.

My none working solution is based on the answer of @KamilCuk which is specific for 4-bit elements. The example below works for any bits, but only the first byte of the array. I know, that I have to change t[0] to t[1] once I want to write into the next byte. But when I write e.g. 5 bits each time, the 2nd write should write 3 bit in the first byte amd two into the 2nd byte (0x1F00 to 0xFF03). A dirty workaround would be to change uint8_t *t to something like uint32_t *t but this would only postpone the problem until you want to write more than 32 bit.

Can you think of a more clever way to write an arbitrary bit size element into an array of unsigned char without any padding?

#include <stdio.h>
#include <stdint.h>
#include <string.h>

# define VALUE_1_SIZE_BITS      2
# define VALUE_2_SIZE_BITS      3

# define FREE_BYTES             5

struct delta_measurement_struct {
    unsigned delta_time: VALUE_1_SIZE_BITS;
    unsigned delta_sensor_data: VALUE_2_SIZE_BITS;
} __attribute__((packed));  // avoid structure padding

void data_struct_array_set(uint8_t *t, unsigned idx, struct delta_measurement_struct delta_measurement) {
    const unsigned v = delta_measurement.delta_time << VALUE_2_SIZE_BITS | delta_measurement.delta_sensor_data;
    const unsigned mask = (VALUE_1_SIZE_BITS + VALUE_2_SIZE_BITS) * idx;
    // const unsigned mask = (VALUE_1_SIZE_BITS + VALUE_2_SIZE_BITS) * ((VALUE_1_SIZE_BITS + VALUE_2_SIZE_BITS) * idx / 8);
    t[0] |= v << mask;
}

int main() {
    uint8_t delta_measurements[FREE_BYTES];

    memset(&delta_measurements, 0, sizeof(delta_measurements));
    for (int i = 0; i < ((FREE_BYTES * 8) / (VALUE_1_SIZE_BITS + VALUE_2_SIZE_BITS)); ++i) {
        printf("set %d bits\n", (VALUE_1_SIZE_BITS + VALUE_2_SIZE_BITS));
        data_struct_array_set(delta_measurements, i, (struct delta_measurement_struct) {3, 7});

        printf("union_data = 0x");
        for (int j = 0; j < FREE_BYTES; ++j) {
            printf("%02X-", delta_measurements[j]);
        }
        printf("\n");
    }

    return 0;
}
HennyKo
  • 712
  • 1
  • 8
  • 19
  • So what’s “the problem” is exactly? Just follow along with the original premise of sometimes needing to write the value to two bytes. (Using a temporary char “buffer” variable might be useful; this is often done with a larger backing anyway— only the number of bits changes.) – user2864740 Nov 14 '21 at 19:14
  • You mean remembering the number of overflown bits (that do not fit in the current byte) and then writing them to the next byte? – HennyKo Nov 14 '21 at 19:24
  • Effectively yes. Then it’s just implementation. – user2864740 Nov 14 '21 at 19:28
  • It the question is changed from “arbitrary” (which I assumed to be 1 <= x <= 8) to a specific value the implementation might be simpler, especially if then packing by entire blocks (eg. 8 items x 5 bits = 5 bytes) and then handling the trailing. This is commonly used in a 6/8 packer such as found in base64. – user2864740 Nov 14 '21 at 19:39

0 Answers0