Cinder Biscuits
Cinder Biscuits

Reputation: 5259

Unpacking integer from byte array

Forgive me if this is completely obvious, but I am sending data over the network using a byte array, and I need to stuff an integer into it and then get it back out at the other side.

Type definitions in the example:

uint8_t *dest;
uint8_t *ciphertext;
size_t  cbytes; // length of ciphertext
uint8_t iv[16];
uint8_t tag[16];

Relevant portion of the writer:

size_t bytes = 0;
memcpy(&dest[bytes], iv, sizeof(iv));
bytes = sizeof(iv);
memcpy(&dest[bytes], (void*)htonl(cbytes), sizeof(uint32_t));
bytes += sizeof(uint32_t);
memcpy(&dest[bytes], ciphertext, cbytes);
bytes += cbytes;
memcpy(&dest[bytes], tag, sizeof(tag));
bytes += sizeof(tag);

Is this a correct way of stuffing cbytes, as an integer into the byte array? If not, what is a better way of doing it?

Now, with this byte array, how do I read cbytes back out into an integer (or size_t)? The rest of it can just be copied back out, but not sure what to do about the integer.

Upvotes: 0

Views: 336

Answers (1)

You're asking about this piece of code:

memcpy(&dest[bytes], (void*)htonl(cbytes), sizeof(uint32_t));

No, it is not correct at all. You're converting the return value of htonl to a pointer. It is not a valid pointer however. You have to have an object of type uint32_t for sending:

uint32_t cbytes32 = htonl(cbytes);
memcpy(&dest[bytes], &cbytes32, sizeof(uint32_t));

It is possible to do this on one line too, in modern C, using a compound literal to create an array of one uint32_t inline:

memcpy(&dest[bytes], (uint32_t[]){ htonl(cbytes) }, sizeof(uint32_t));

but the syntax really does not look any nicer.

To read it in, you need to read it to an object of type uint32_t, then ntohl it, and the return value you can store into size_t:

uint32_t size32;
size_t size;
memcpy(&size32, src, sizeof size32)
size = ntohl(size32);

Then, I'd be extra careful with that you're using a possibly 64-bit size_t elsewhere but truncating it to 32 bits here. It might be OK but that would need to be documented. 64 bits should be enough for everyone, but unfortunately there is no htonll function.


Finally, instead of &dest[bytes] you can write dest + bytes for less keypresses. And for even less, you can make another pointer:

uint8_t *cur = dest;
memcpy(cur, iv, sizeof iv);
cur += sizeof iv;
uint32_t cbytes32 = htonl(cbytes);
memcpy(cur, &cbytes32, sizeof cbytes32);
cur += sizeof cbytes32;
memcpy(cur, ciphertext, cbytes32);
cur += cbytes32;
memcpy(cur, tag, sizeof tag);
cur += sizeof tag;
size_t nbytes = cur - dest;

Note that if you're using a streaming socket (TCP) there is usually no need to copy them into an intermediate buffer - just send the 4, 8 bytes using a separate read call - or at least, do not bother copying a long array into the same buffer after the size call.

Upvotes: 1

Related Questions