1

Suppose I have a buffer filled with uint8_t, like what I would get by executing

uint8_t buffer[4];    
[inputStream read:buffer maxLength:4];

Again, suppose I know that this buffer now contains two signed 16-bit integers in big-endian format (even though the type of the buffer is uint8_t). How would I go about getting the results of each of the numbers?

I think that you have to use bitwise operators, and that you have to also convert from unsigned to signed, but I can't seem to figure it out completely.

A similar example with only uint8_t values would look something like this:

uint8_t buffer[2];
[inputStream read:buffer maxLength:2];
uint8_t value1 = buffer[0];
uint8_t value2 = buffer[1];

Any help would be most appreciated, as I'm sure this is a relatively simple question.

Update: To further clarify the problem: I know in advance that I'm receiving numbers from 0 to 255, even though they are 16-bit signed. So any negative numbers received are incorrect, and should probably be shifted up by 127, to account for the signed/unsigned difference.

Ethan Gill
  • 76
  • 1
  • 9

2 Answers2

1

to convert from big endian to little endian a signed short this function may be used

(from: convert big endian to little endian in C [without using provided func] )

int16_t swap_int16( int16_t val ) 
{
    return (val << 8) | ((val >> 8) & 0xFF);
}

So going back to your code

uint8_t buffer[4];    
[inputStream read:buffer maxLength:4];

You may try:

int16_t *value1;
int16_t *value2;

// set the pointers to the correct buffer location

value1 = (int16_t *)(&buffer[0]);
value2 = (int16_t *)(&buffer[2]);

// perform the conversion

*value1 = swap_int16( *value1 );
*value2 = swap_int16( *value2 );

// finally you may want to check the result

printf("Value1: %d\n", *value1 );
printf("Value2: %d\n", *value2 );

Note that this approach will swap the original bytes in buffer

Community
  • 1
  • 1
Paolo
  • 15,233
  • 27
  • 70
  • 91
  • Your answer ends up giving the distance away from 127 of the expected answer. So, for example, if the expected value for value1 was 134, your answer would return 7. If the expected value was 118, your answer would return -9. I'm assuming this is because no sign adjustment is being done. Obviously, I could just add 127 to each struct, but I'd like to understand what's going on. Other than that, excellent answer, and thanks for the quick reply! – Ethan Gill Dec 01 '13 at 08:26
  • Tried my best to help. I'll check if I've made any mistake. I agree to stay away from "add 127 to fix but don't know why". – Paolo Dec 01 '13 at 08:48
  • This is not only wrong but it also messes with pointer aliasing unnecessarily, resulting in UB. The right thing to do here is use unsigned arithmetic and bitwise operators, such as `uint16_t word = (byte1 << 8) | byte0`. –  Dec 01 '13 at 08:55
  • @Ethan Gill: I updated my answer. The sign was actually not handled properly in the previuos version. Also I tried to make the code more concise and clear. I'd be interested in receiving feedback. Hope it works now. – Paolo Dec 01 '13 at 15:46
  • @Paolo I'm officially an idiot. I have been incorrectly sending data through my stream. Your new solution works perfectly! Thank you so much for your help. – Ethan Gill Dec 01 '13 at 20:46
  • @Ethan Gill: I was about to suggest you to take a look at your source data. Don't worry, it happens to me all the time to miss something just in front of my face. I'm glad the code worked. – Paolo Dec 01 '13 at 21:17
1

The safest way is to use the following

#import <arpa/inet.h>
#import <stdint.h>
#import <string.h>


uint8_t* p = buffer;
uint16_t x0;
memcpy(&x0, p, sizeof(x0));
x0 =ntohs(x0);

uint16_t x1;
p += sizeof(uint16_t);
memcpy(&x1, p, sizeof(x1));
x1 = ntohs(x1);

This works even when the buffer's bytes are misaligned with respect to the uint16_t values (thus, using memcpy).

Since it happens that network byte order equals "big endian", you can use ntohs which reads "swap short in network byte order to host byte order".

See also:

$man ntohs
CouchDeveloper
  • 18,174
  • 3
  • 45
  • 67
  • 2
    There are also the slightly clearer CF methods for byte swapping, eg `CFSwapInt16BigToHost(bigEndian16)` – Tark Dec 01 '13 at 14:41