Reputation: 3086
I am trying to stuff 16 unsigned values into 8 bytes (64 bit), and access them using an array-like syntax.
Every entry in the "array" will be one nibble - 4 bit long. (The values I plan to store are never bigger than 15).
My first attempt was this:
int main(int argc, char* argv[]) {
union nibbles_array {
uint64_t as_long;
struct inner_array {
unsigned entry0 : 4;
unsigned entry1 : 4;
unsigned entry2 : 4;
unsigned entry3 : 4;
unsigned entry4 : 4;
unsigned entry5 : 4;
unsigned entry6 : 4;
unsigned entry7 : 4;
unsigned entry8 : 4;
unsigned entry9 : 4;
unsigned entry10 : 4;
unsigned entry11 : 4;
unsigned entry12 : 4;
unsigned entry13 : 4;
unsigned entry14 : 4;
unsigned entry15 : 4;
} as_array;
} array;
array.as_long = 0x0123456789abcdef;
printf("%d \n", array.as_array.entry0);
printf("%d \n", array.as_array.entry1);
printf("%d \n", array.as_array.entry2);
printf("%d \n", array.as_array.entry3);
printf("%d \n", array.as_array.entry4);
return 0;
}
Two problems arises from this implementation: the first is that the values are stored in reverse order. I can, of course, assign the values in reverse order to get the desired result: array.as_long = 0xfedcba9876543210
, but I want this code to be portable, and not endianness-dependent.
The second is that I can't access the nibbles with an index, in an array-like syntax.
The second attempt was this:
int main(int argc, char* argv[]) {
uint64_t pseudo_array = 0x0123456789abcdef;
#define Array(i) (unsigned)((pseudo_array & (0xfUL << i)) >> i)
int i;
for (i = 0; i < 16; ++i) {
printf("%d ", Array(i));
}
printf("\n");
return 0;
}
The above can solve the second problem (array-like syntax); now I can access "elements" with index, but the problem of endianness remains, plus this produces wrong output:
15 7 11 13 14 15 7 11 13 6 3 9 12 14 15 7
Upvotes: 4
Views: 3553
Reputation: 144949
Your first attempt definitely has portability issues: the mapping between bitfields and actual bits in memory is not defined in the Standard. It is not exactly an endianness issue, and you cannot have an array like syntax either.
Your second attempt is much more portable. The issue is not endianness either, but your own conception of what is the n-th element of the array. The macro is erroneous because you do not shift by the correct number of bits: i
must be multiplied by 4. I would suggest this macro to fit your understanding:
#define Array(a, i) ((unsigned)(((a) >> (60 - 4 * (i))) & 0xf))
Upvotes: 2
Reputation: 609
I believe @chqrlie is correct concerning endianness issues, but I think it would be easier to just use an array.
uint8_t ary[20]; /* 40 nibbles */
#define getnibblelen(ary) (sizeof(ary) * 2) /* works because it's an ARRAY of uint8_t, wouldn't work if you're passing the array to a function. */
#define getnibble(ary, idx) (idx % 2 ? /* if odd */ ary[idx/2] & 0xf0 >> 4 : /* if even */ ary[idx/2] & 0x0f)
#define setnibble(ary, idx, val) (idx % 2 ? ary[idx/2] = (ary[idx/2] & 0x0f) | ((val & 0x0f) << 4) : ary[idx/2] = (ary[idx/2] & 0xf0) | (val & 0x0f))
Upvotes: 0
Reputation: 78803
How about something like this:
#include <stdio>
int main(int argc, char* argv[]) {
unsigned char array[8] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF };
#define Array(i) (i%2 ? (array[i/2] & 0x0F) : ((array[i/2] & 0xF0) >> 4))
int i;
for (i = 0; i < 16; ++i) {
printf("%d ", Array(i));
}
printf("\n");
return 0;
}
Upvotes: 0
Reputation: 17605
The undesired output is caused by the fact that the macro is defined in the wrong way; you mean to shift nibble-wise, not bit-wise. The desired behaviour can be achieved by defining the macro as follows.
#define Array(i) (unsigned)((pseudo_array & ((uint64_t)0x0f << (4*i))) >> (4*i))
Upvotes: 1
Reputation: 409364
Instead of storing the "vector" as bitfields, could use a dynamically allocated array, and a set of functions for accessing individual "indexes" of your array as well as functions to set or get larger parts of the "array" using larger types (like e.g. uint64_t
).
Something like the following API
// The "na" prefix stands for Nibble Array
struct nibble_array
{
uint8_t *data; // The actual "array" of nibbles
size_t size; // Current size of "array" (number of elements)
};
// Create an array containing a number of elements
struct nibble_array *na_create(const size_t elements);
// Get a value from specified index in array
int na_get(const struct nibble_array *array, const size_t index);
// Set a specified index in the array to a value
void na_set(struct nibble_aray *array, const size_t index, const int value);
// Get the number of elements (nibbles) in the array
size_t na_size(const struct nibble_array *array);
// Set a larger part of the array to some values
void na_set64(struct nibble_array *array, size_t start_index, const uint64_t value);
You don't have to provide the definition of the nibble_array
structure if you want the data to be private (read about opaque pointers).
Upvotes: 0