OptoCloud
OptoCloud

Reputation: 57

Deserialization of uint8 array to int64 fails but should work

Im going to send a int64 over tcp and need to serialize&deserialize it.

First i cast it to a uin64.

I byteshift it into an uint8 array.

Then i byteshift the array into a uint64

And finally cast it back to a int.

But it returns a different value than i put in... I have checked the hex values, but they are supposed to be correct...

Code:

#include <math.h>
#include <string.h>
#include <iostream>
#include <iomanip>

//SER & D-SER int64
std::array<uint8_t, 8> int64ToBytes(int64_t val)
{
   uint64_t v = (uint64_t)val;
   std::array<uint8_t, 8> bytes;
   bytes[0] = (v&0xFF00000000000000)>>56;
   bytes[1] = (v&0x00FF000000000000)>>48;
   bytes[2] = (v&0x0000FF0000000000)>>40;
   bytes[3] = (v&0x000000FF00000000)>>32;
   bytes[4] = (v&0x00000000FF000000)>>24;
   bytes[5] = (v&0x0000000000FF0000)>>16;
   bytes[6] = (v&0x000000000000FF00)>>8;
   bytes[7] = (v&0x00000000000000FF);
   return bytes;
}

int64_t bytesToInt64(uint8_t bytes[8])
{
   uint64_t v = 0;
   v |= bytes[0]; v <<= 8;
   v |= bytes[1]; v <<= 8;
   v |= bytes[3]; v <<= 8;
   v |= bytes[4]; v <<= 8;
   v |= bytes[5]; v <<= 8;
   v |= bytes[6]; v <<= 8;
   v |= bytes[7]; v <<= 8;
   v |= bytes[8];
   return (int64_t)v;
}


int main() {
   uint8_t bytes[8] = {0};

   int64_t val = 1234567890;

   //Print value to be received on the other side
   std::cout << std::dec << "INPUT:  " << val << std::endl;

   //Serialize
   memcpy(&bytes, int64ToBytes(val).data(), 8);

   //Deserialize
   int64_t val2 = bytesToInt64(bytes);

   //print deserialized int64
   std::cout << std::dec << "RESULT: " << val2 << std::endl;
}

Output:

INPUT:  1234567890
RESULT: 316049379840

Been trying to solve this for a day now, cant find the problem

Thanks.

Upvotes: 1

Views: 1528

Answers (4)

Jens
Jens

Reputation: 21

you are missing a bit shift in the bytesToInt64 function:

below you find the corrected bytesToInt64 function:

int64_t bytesToInt64(uint8_t bytes[8])
{
   uint64_t v = 0;
   v |= bytes[0]; v <<= 8;
   v |= bytes[1]; v <<= 8;
   v |= bytes[2]; v <<= 8;
   v |= bytes[3]; v <<= 8;
   v |= bytes[4]; v <<= 8;
   v |= bytes[5]; v <<= 8;
   v |= bytes[6]; v <<= 8;
   v |= bytes[7];
   return (int64_t)v;
}

Upvotes: 2

falopsy
falopsy

Reputation: 636

This should work. You may also need to check the input array is the right size in your bytesToInt64 function.

std::array<uint8_t, 8> int64ToBytes(int64_t val)
{
    uint64_t v = (uint64_t)val;
    std::array<uint8_t, 8> bytes;
    for (size_t i = 0; i < 8; i++)
    {
        bytes[i] = (v >> (8 * (7 - i))) & 0xFF;
    }
    return bytes;
}

int64_t bytesToInt64(uint8_t bytes[8])
{
    uint64_t v = 0;
    for (size_t i = 0; i < 8; i++)
    {
        v |= (bytes[i] << (8 * (7 - i)));
    }
    return (int64_t)v;
}

Upvotes: 0

xception
xception

Reputation: 4297

If you're transferring data between machines with the same endianness you don't need to serialize the data byte by byte, you can just send the data as it is represented in memory. In this case you don't need anything like that you can just use your memcpy call like this:

// Serialize
memcpy(&bytes, &val, sizeof(val));

// Deserialize
int64_t val2;
memcpy(&val2, &bytes, sizeof(val));

If you're sending data between hosts with different endianness you should send it as you find it in the aswer from Roger, basically you have to make sure the data is represented in the same way on both ends.

here's a variant which not only serializes but will work with any type of int and across any platforms

#include <iostream>
#include <type_traits>

using namespace std;

template <typename T> enable_if_t<is_integral_v<T>> serialize(T t, char *buf)
{
    for(auto i = 0U; i < sizeof(t); ++i) {
        buf[i] = t & 0xff;
        t >>= 8;
    }
}

template <typename T> enable_if_t<is_integral_v<T>> deserialize(T &t, char const *buf)
{
    for(auto i = 0U; i < sizeof(t); ++i) {
        t <<= 8;
        t |= buf[sizeof(t) - 1 - i];
    }
}

int main() {
    int64_t t1 = 0x12345678;

    int64_t t2{0};

    char buffer[sizeof(t1)];
    serialize(t1, buffer);

    deserialize(t2, buffer);

    cout << "I got " << hex << t2 << endl;
}

you should probably use containers and parts to serialize/deserialize data to make sure you don't overflow your buffer (considering you are transferring more than one int at a time)

Upvotes: 1

user7287311
user7287311

Reputation:

Try using the uint64_t htobe64(uint64_t host_64bits) and uint64_t be64toh(uint64_t big_endian_64bits) functions to convert from host to big endian (network order) and from network order to host order respectively.

You are shifting the entire value. Try something like:

(bytes[0] << 56) | 
(bytes[1] << 48) |
...  (bytes[7])

There is no 9th byte (ie. byte[8]).

Upvotes: 3

Related Questions