Miguel Duran Diaz
Miguel Duran Diaz

Reputation: 312

How to set the values of an array to a single variable

I'm reading the values from a SD card in an ARM micro:

Res = f_read(&fil, (void*)buf, 6, &NumBytesRead);

where fil is a pointer, buf is a buffer where the data is stored.

And that's the problem: it's an array but I'd like to have the contents of that array in a single variable.

To give an actual example: the 6 bytes read from the file are:

buf[0] = 0x1B
buf[1] = 0x26
buf[2] = 0xB3
buf[3] = 0x54
buf[4] = 0xA1
buf[5] = 0xCF

And I'd like to have: uint64_t data be equal to 0x1B26B354A1CF. That is, all the elements of the array "concatenated" in one single 64 bit integer.

Upvotes: 0

Views: 650

Answers (3)

0___________
0___________

Reputation: 67476

Use union but remember about the endianes.

 union 
 {
      uint8_t u8[8];
      uint64_t u64;
  }u64;

typedef union { uint8_t u8[8]; uint64_t u64; }u64;

typedef enum
{
    LITTLE_E,
    BIG_E,
}ENDIANESS;

ENDIANESS checkEndianess(void)
{
    ENDIANESS result = BIG_E;
    u64 d64 = {.u64 = 0xff};
    if(d64.u8[0]) result = LITTLE_E;

    return result;
}

uint64_t arrayToU64(uint8_t *array, ENDIANESS e) // for the array BE
{
    u64 d64;
    if(e == LITTLE_E)
    {
        memmove(&d64, array, sizeof(d64.u64));
    }
    else
    {
        for(int index = sizeof(d64.u64) - 1; index >= 0; index--)
        {
            d64.u8[sizeof(d64.u64) - index - 1] = array[index];
        }
    }
    return d64.u64;
}

int main()
{
    uint8_t BIG_E_Array[] = {0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80};
    ENDIANESS e;

    printf("This system endianess: %s\n", (e = checkEndianess()) == BIG_E ? "BIG":"LITTLE");

    printf("Punned uint64_t for our system 0x%lx\n", arrayToU64(BIG_E_Array, e));
    printf("Punned uint64_t for the opposite endianess system 0x%lx\n", arrayToU64(BIG_E_Array, e == BIG_E ? LITTLE_E : BIG_E));

    return 0;
}

Upvotes: 3

alk
alk

Reputation: 70931

To things to take care of here:

  1. have the bytes be ordered correctly
  2. read the six bytes into one 64bit integer

Issue 1 can be taken care of by storing the byte coming in in network byte order (Big Endian) into the 64 bit integer in host byte order by for example using the two marcos below:

    /* below defines of htonll() and ntohll() are taken from this answer:
       https://stackoverflow.com/a/28592202/694576
     */
    #if __BIG_ENDIAN__
    # define htonll(x) (x)
    # define ntohll(x) (x)
    #else
    # define htonll(x) ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32))
    # define ntohll(x) ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32))
    #endif

Issue 2 can be solved in multiple ways:

  1. Extending your approach

    #define BUFFER_SIZE (6)
    
    ...
    
      assert(BUFFER_SIZE <= sizeof (uint64_t)); 
    
      uint8_t buffer[BUFFER_SIZE];
      FILE * pf = ...; /* open file here */
      /* test if file has been opened successfully here */
    
      ... result = f_read(pf, buffer, BUFFER_SIZE, ...);
      /* test result for success */
    
      uint64_t number = 0;
      memset(&number, buffer, BUFFER_SIZE)
      number = ntohll(number);
    
  2. Use "Type Punning" by using a union

    union buffer_wrapper
    {
      uint8_t u8[sizeof (uint64_t)];
      uint64_t u64;
    }
    

    Instead of

      uint8_t buffer[BUFFER_SIZE];
    

    use

      union buffer_wrapper buffer;
    

    and instead of

      memcpy(&number, buffer, BUFFER_SIZE)
      number = ntohll(number)
    

    use

      number = ntohll(buffer.u64)
    

Upvotes: 0

kiran Biradar
kiran Biradar

Reputation: 12732

Without type punning you can do as below.

uint64_t data = 0;
for (int i=0; i<6; i++)
{
  data <<= 8;
  data |= (uint64_t) buf[i];
}

Upvotes: 6

Related Questions