0

I'm trying to implement this RFC 4.1. Integer

An XDR signed integer is a 32-bit datum that encodes an integer in the range [-2147483648,2147483647]. The integer is represented in two's complement notation. The most and least significant bytes are 0 and 3, respectively. Integers are declared as follows:

     int identifier;

       (MSB)                   (LSB)
     +-------+-------+-------+-------+
     |byte 0 |byte 1 |byte 2 |byte 3 |                      INTEGER
     +-------+-------+-------+-------+
     <------------32 bits------------>

and here's my code I need to know if there is a better way to do that ?

void packInteger(char *buf,long int i)
{
    if(i>=0) {
        *buf++ = i>>24;
        *buf++ = i>>16;
        *buf++ = i>>8;
        *buf++ = i;
    }
    if(i<0) {
        i = i*-1;
        i =  1 + (unsigned int)(0xffffffffu - i);
        buf[0] = (unsigned int)i>>24;
        buf[1] = (unsigned int)i>>16;
        buf[2] = (unsigned int)i>>8;
        buf[3] = (unsigned int)i;
    }   
}
long int unpackInteger(char *buf)
{
    unsigned long int i2 = ((unsigned long int)buf[0]<<24) |
                           ((unsigned long int)buf[1]<<16) |
                           ((unsigned long int)buf[2]<<8)  |
                           buf[3];
    long int i;
    // change unsigned numbers to signed
    if (i2 <= 0x7fffffffu) { i = i2; }
    else { i = -1 - (long int)(0xffffffffu - i2); }
    return i;
}
int main(void) 
{
    char buf[4];
    packInteger(buf,-31);
    printf("%u %u %u %u\n",buf[0],buf[1],buf[2],buf[3]);
    long int n = unpackInteger(buf);
    printf("%ld",n);
    return 0;
}

if someone on 64 bit system is it working or noT ?

version 2

void packInteger(unsigned char *buf,long int i)
{
unsigned long int j = i; // this will convert to 2's complement
*buf++ = i>>24;
*buf++ = i>>16;
*buf++ = i>>8;
*buf++ = i;
}
Community
  • 1
  • 1
cap10ibrahim
  • 587
  • 1
  • 6
  • 16
  • You can edit your question. So you can put your comment into the question. – Mysticial Sep 25 '11 at 08:04
  • Can't you use the `htonl` and `ntohl` functions? Also: http://stackoverflow.com/questions/105252/how-do-i-convert-between-big-endian-and-little-endian-values-in-c – Mat Sep 25 '11 at 08:08
  • @Mat I need to do it manually so.. – cap10ibrahim Sep 25 '11 at 08:22
  • @Mysticial done . if you have 64 bit system is it acting as expected ? – cap10ibrahim Sep 25 '11 at 08:22
  • @cap10Ibrahim: then look at the question I linked. – Mat Sep 25 '11 at 08:31
  • 32/64-bit won't matter for this code. I haven't run your code yet so I don't know if it works or not. One issue you might have is that `long int` is 64-bit on some systems while 32-bit on others. It's pretty clear from your code that you want `long int` to be the same size as 4 `chars`. – Mysticial Sep 25 '11 at 08:32
  • @Mysticial , how to work around this issue , should I write two versions of the functions ? – cap10ibrahim Sep 25 '11 at 08:41
  • There's no 100% portable way of doing it. But you can almost assume that `uint32_t` and `int` are equal to 4 `char`s on every modern system. Or you could make your own type and #define it whatever is the correct integer type for each system. – Mysticial Sep 25 '11 at 08:45
  • @Mysticialthanks for your time – cap10ibrahim Sep 25 '11 at 08:56

1 Answers1

2

You should be using unsigned char for your buffers.

A cast to unsigned in C performs the mathematical equivalent of a conversion to 2s complement, so your pack function can be simplified:

void packInteger(unsigned char *buf, long int i)
{
    unsigned long u = i;

    buf[0] = (u >> 24) & 0xffUL;
    buf[1] = (u >> 16) & 0xffUL;
    buf[2] = (u >> 8) & 0xffUL;
    buf[3] = u & 0xffUL;
}

Your unpack function seems fine (with the change to unsigned char).

caf
  • 233,326
  • 40
  • 323
  • 462
  • The constants don't need to be UL unless you do shifting on the constants themselves. They'll be promoted to `unsigned long` when given as operands to `&` by the "usual arithmetic conversions". – Dietrich Epp Sep 25 '11 at 11:09
  • @DietrichEpp: True - I didn't mean to imply it was a correctness issue, so I've removed that text. – caf Sep 25 '11 at 12:08
  • @DietrichEpp thanks for the tip on casting , but why &0xffUL check my new function i'll add it to the original post – cap10ibrahim Sep 25 '11 at 18:24
  • @cap10Ibrahim: The `& 0xffUL` accounts for the case where `unsigned char` is wider than 8 bits - on common systems it will be entirely optimised out. Note that you do need to use `long` and `unsigned long`, because plain `int` is not necessarily any bigger than 16 bits, whereas `long` is guaranteed to be at least 32 bits. The code will still work fine where `long` is wider than that. – caf Sep 26 '11 at 03:46
  • @caf is short int guaranteed to be 16 bits ? – cap10ibrahim Sep 26 '11 at 13:48
  • @cap10Ibrahim: Yes, both `short int` and `int` are guaranteed to be at least 16 bits; `long` at least 32 bits; and `long long` at least 64 bits. – caf Sep 27 '11 at 01:02