unpacking ints

C

Chris

Hi

I'm attempting to write a client for an existing p2p network.
The protocol defines that ints are packed into 4 bytes for transfer.

// Creating the byte vector using the following is fine:
for(int i=3; i>=0; i--) {
vecBuff.push_back(value&0xff);
value/=256;
}

// but reading out an int value from the array using
value = (int)data[0] + (int)data[1]*256 + (int)data[2]*65536 +
(int)data[3]*16777216;

Gives varying results. Often correct but sometimes the results is a
negative number, which i don't believe to be correct.

Is my method above for unpacking the int correct?

Thanks loads in advance.
Chris
 
K

Karl Heinz Buchegger

Chris said:
Hi

I'm attempting to write a client for an existing p2p network.
The protocol defines that ints are packed into 4 bytes for transfer.

// Creating the byte vector using the following is fine:
for(int i=3; i>=0; i--) {
vecBuff.push_back(value&0xff);
value/=256;
}

// but reading out an int value from the array using
value = (int)data[0] + (int)data[1]*256 + (int)data[2]*65536 +
(int)data[3]*16777216;

Gives varying results. Often correct but sometimes the results is a
negative number, which i don't believe to be correct.

Is my method above for unpacking the int correct?

The casts are wrong. You need to cast the individual bytes to unsigned int
first and cast the total result to int only after the number is constructed.

Since packing and unpacking are platform dependent in any way,
why don't you try if the following works on your machine. Strictly
speaking it is illegal to use a union in this way, but it works on
most compilers.


union Bridge
{
unsigned char Bytes[4];
int TheInt;
};

.....

Bridge Converter;

Converter.TheInt = 25; // eg. to convert 25
for( i = 0; i < 4; ++i )
do_somthing_with( Converter.Bytes );

.....

Bridge Converter;

for( i = 0; i < 4; ++i )
Converter.Bytes = get_byte_from_somewhere;

do_something_with_int( Converter.TheInt );

Thanks loads in advance.
Chris


--
Karl Heinz Buchegger, GASCAD GmbH
Teichstrasse 2
A-4595 Waldneukirchen
Tel ++43/7258/7545-0 Fax ++43/7258/7545-99
email: (e-mail address removed) Web: www.gascad.com

Fuer sehr grosse Werte von 2 gilt: 2 + 2 = 5
 
A

Alberto Barbati

Chris said:
// but reading out an int value from the array using
value = (int)data[0] + (int)data[1]*256 + (int)data[2]*65536 +
(int)data[3]*16777216;

Gives varying results. Often correct but sometimes the results is a
negative number, which i don't believe to be correct.

Is my method above for unpacking the int correct?

You should use unsigned ints for all intermediate calculations, even if
the final result is an int. Also, you have to be careful about type char
being signed or unsigned (from your message, it appears that your
compiler uses signed chars). The whole, pedantic, but safe method is:

value = (int)(
(unsigned int)(unsigned char)data[0] +
(unsigned int)(unsigned char)data[1] * 256u +
(unsigned int)(unsigned char)data[2] * 65536u +
(unsigned int)(unsigned char)data[3] * 16777216u);

The four "(unsigned int)" are probably redundant, but some pedantic
compiler may emit a warning if you don't put them. On the other hand,
the four "(unsigned char)" are essential.

If you want to be more human-friendly, you can also write:

value = (int)(
(unsigned int)(unsigned char)data[0] +
(unsigned int)(unsigned char)data[1] << 8 +
(unsigned int)(unsigned char)data[2] << 16 +
(unsigned int)(unsigned char)data[3] << 24);

The compiler would probably generate the same code, as it knows how to
optimize multiplications with shifts, but it's way more readable, IMHO.

Regards,

Alberto Barbati
 
A

Alberto Barbati

Karl said:
The casts are wrong. You need to cast the individual bytes to unsigned int
first and cast the total result to int only after the number is constructed.

Wrong. You need to cast the individual bytes to unsigned char not
unsigned int. Otherwise a signed char will be promoted to (signed) int
before being converted to unsigned int. In fact:

(unsigned)'\xff' == 4294967295u
(unsigned)(unsigned char)'\xff' == 255u
Since packing and unpacking are platform dependent in any way,
why don't you try if the following works on your machine. Strictly
speaking it is illegal to use a union in this way, but it works on
most compilers.


union Bridge
{
unsigned char Bytes[4];
int TheInt;
};

Be careful about endianness...

Alberto Barbati
 
O

Old Wolf

I'm attempting to write a client for an existing p2p network.
The protocol defines that ints are packed into 4 bytes for transfer.

// Creating the byte vector using the following is fine:
for(int i=3; i>=0; i--) {
vecBuff.push_back(value&0xff);
value/=256;
}

// but reading out an int value from the array using
value = (int)data[0] + (int)data[1]*256 + (int)data[2]*65536 +
(int)data[3]*16777216;

Gives varying results. Often correct but sometimes the results is a
negative number, which i don't believe to be correct.

Use | (bitwise or) instead of + (add). This avoids signs
making a difference. Some people would also advise you to use
left-shifts , eg:

int char_4_to_int(const char *cp)
{
return cp[0] | (cp[1] << CHAR_BIT)
| (cp[2] << (CHAR_BIT*2)) | (cp[3] << (CHAR_BIT*3);
}

Also, consider making your int unsigned (depends what you want to do
with it later).
 
K

Karl Heinz Buchegger

Alberto said:
Wrong. You need to cast the individual bytes to unsigned char not
unsigned int. Otherwise a signed char will be promoted to (signed) int
before being converted to unsigned int.

Right.
I assumed that anybody working at the bytelevel uses unsigned char
to store bytes and not plain char.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,801
Messages
2,569,658
Members
45,421
Latest member
DoreenCorn

Latest Threads

Top