Discussion:
Interpret this C code
(too old to reply)
b***@gmail.com
2017-03-29 00:50:48 UTC
Permalink
Raw Message
Hello All,

I am not a C expert by any means. I would like to understand this opensource code below. Can someone please explain the below declarations?

Please advise if there are other ways to self-learn these things for future reference.

# define c2l(c,l) (l = ((unsigned long)(*((c)++))) , \
l|=(((unsigned long)(*((c)++)))<< 8), \
l|=(((unsigned long)(*((c)++)))<<16), \
l|=(((unsigned long)(*((c)++)))<<24))

/* NOTE - c is not incremented as per c2l */
# define c2ln(c,l1,l2,n) { \
c+=n; \
l1=l2=0; \
switch (n) { \
case 8: l2 =((unsigned long)(*(--(c))))<<24; \
case 7: l2|=((unsigned long)(*(--(c))))<<16; \
case 6: l2|=((unsigned long)(*(--(c))))<< 8; \
case 5: l2|=((unsigned long)(*(--(c)))); \
case 4: l1 =((unsigned long)(*(--(c))))<<24; \
case 3: l1|=((unsigned long)(*(--(c))))<<16; \
case 2: l1|=((unsigned long)(*(--(c))))<< 8; \
case 1: l1|=((unsigned long)(*(--(c)))); \
} \
}

# define l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \
*((c)++)=(unsigned char)(((l)>> 8)&0xff), \
*((c)++)=(unsigned char)(((l)>>16)&0xff), \
*((c)++)=(unsigned char)(((l)>>24)&0xff))

# define n2l(c,l) (l =((unsigned long)(*((c)++)))<<24, \
l|=((unsigned long)(*((c)++)))<<16, \
l|=((unsigned long)(*((c)++)))<< 8, \
l|=((unsigned long)(*((c)++))))

# define n2l8(c,l) (l =((uint64_t)(*((c)++)))<<56, \
l|=((uint64_t)(*((c)++)))<<48, \
l|=((uint64_t)(*((c)++)))<<40, \
l|=((uint64_t)(*((c)++)))<<32, \
l|=((uint64_t)(*((c)++)))<<24, \
l|=((uint64_t)(*((c)++)))<<16, \
l|=((uint64_t)(*((c)++)))<< 8, \
l|=((uint64_t)(*((c)++))))
Stefan Ram
2017-03-29 01:10:50 UTC
Permalink
Raw Message
Post by b***@gmail.com
Can someone please explain the below declarations?
These are #define preprocessing directives for macros which
will read octet bytes from a buffer and merge them into a
larger value or vice versa.
Post by b***@gmail.com
Please advise if there are other ways to self-learn these
things for future reference.
A C book should explain this. I don't know. Maybe »C, how to
program« by Deitel and Deitel?
Jorgen Grahn
2017-03-29 05:43:09 UTC
Permalink
Raw Message
Post by b***@gmail.com
Hello All,
I am not a C expert by any means. I would like to understand this
opensource code below. Can someone please explain the below
declarations?
Please advise if there are other ways to self-learn these things for future reference.
# define c2l(c,l) (l = ((unsigned long)(*((c)++))) , \
l|=(((unsigned long)(*((c)++)))<< 8), \
l|=(((unsigned long)(*((c)++)))<<16), \
l|=(((unsigned long)(*((c)++)))<<24))
Note that it's not good code:
- macros when (inline) functions would have been better and safer
- no useful documentation
- cryptic names (especially when it reaches "l1|")

The good thing about it is that it decodes integers from (I think)
arrays of char or unsigned char. That's IMO much better than casting
a char* to unsigned long* and hoping it will work and do something
meaningful.

[snip]

/Jorgen
--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .
Ben Bacarisse
2017-03-29 09:50:18 UTC
Permalink
Raw Message
Post by b***@gmail.com
I am not a C expert by any means. I would like to understand this
opensource code below. Can someone please explain the below
declarations?
The big picture is that they convert a sequence of bytes into integer
types. The code presumes the bytes are ordered in one of two ways
called big- and little-endian. This determines whether the first byte
(the one with the lowest address) is the most or the least significant
value byte of the resulting integer.

It might be networking code, since most network protocols use big-endian
and the macros that converts bytes in that format start with "n2"
(network to...?).
Post by b***@gmail.com
Please advise if there are other ways to self-learn these things for future reference.
# define c2l(c,l) (l = ((unsigned long)(*((c)++))) , \
l|=(((unsigned long)(*((c)++)))<< 8), \
l|=(((unsigned long)(*((c)++)))<<16), \
l|=(((unsigned long)(*((c)++)))<<24))
There's a slightly odd mixture of too many and too few brackets here.
When not in a macro, this would be written

l = (unsigned long)*c++;
l |= (unsigned long)*c++ << 8;
l |= (unsigned long)*c++ << 16;
l |= (unsigned long)*c++ << 24;

The author has decided to make this a comma expression (essentially the
"one thing after another" way to write what would otherwise be a
sequence of expression statements). That's a sound idea.

And in a macro, the advice is to put brackets round the whole thing and
round each use of the parameters so I'd write

((l) = (unsigned long)*(c)++, \
(l) |= (unsigned long)*(c)++ << 8, \
(l) |= (unsigned long)*(c)++ << 16, \
(l) |= (unsigned long)*(c)++ << 24)

The ()s round l will probably not help at all but it's just simpler to
put them there rather that worry about whether they might matter or not.

The others are all variation on this this theme: extracting the bytes
from an integer or building an integer from constituent bytes. The
other variations being the sizes of the integers and the order in which
the bytes appear.

In one case, a large integer has been represented as two, presumably for
those implementations that don't have a 64-bit integer type.
Post by b***@gmail.com
/* NOTE - c is not incremented as per c2l */
# define c2ln(c,l1,l2,n) { \
c+=n; \
l1=l2=0; \
switch (n) { \
case 8: l2 =((unsigned long)(*(--(c))))<<24; \
case 7: l2|=((unsigned long)(*(--(c))))<<16; \
case 6: l2|=((unsigned long)(*(--(c))))<< 8; \
case 5: l2|=((unsigned long)(*(--(c)))); \
case 4: l1 =((unsigned long)(*(--(c))))<<24; \
case 3: l1|=((unsigned long)(*(--(c))))<<16; \
case 2: l1|=((unsigned long)(*(--(c))))<< 8; \
case 1: l1|=((unsigned long)(*(--(c)))); \
} \
}
A thing to note is that, in C, the switch statement "falls through", so
here the switch simply determines where to start. It can, in effect,
read a big-endian integer of any size from 1 to 8 bytes. (It has a
small technical flaw, in the it is not permitted to decrement a pointer
that poits to the start of an object, an this this macro may do that.
It's rarely a problem in practice.)

<snip>
--
Ben.
Philip Lantz
2017-04-03 07:03:24 UTC
Permalink
Raw Message
Post by Ben Bacarisse
Post by b***@gmail.com
# define c2ln(c,l1,l2,n) { \
c+=n; \
l1=l2=0; \
switch (n) { \
case 8: l2 =((unsigned long)(*(--(c))))<<24; \
case 7: l2|=((unsigned long)(*(--(c))))<<16; \
case 6: l2|=((unsigned long)(*(--(c))))<< 8; \
case 5: l2|=((unsigned long)(*(--(c)))); \
case 4: l1 =((unsigned long)(*(--(c))))<<24; \
case 3: l1|=((unsigned long)(*(--(c))))<<16; \
case 2: l1|=((unsigned long)(*(--(c))))<< 8; \
case 1: l1|=((unsigned long)(*(--(c)))); \
} \
}
A thing to note is that, in C, the switch statement "falls through", so
here the switch simply determines where to start. It can, in effect,
read a big-endian integer of any size from 1 to 8 bytes. (It has a
small technical flaw, in that it is not permitted to decrement a pointer
that points to the start of an object, and this this macro may do that.
...)
Might it? It appears to me that c always ends up pointing to the same place
that it started.* Am I missing something?

Philip

* Unless n > 8, in which case c ends up at c+n.
Ben Bacarisse
2017-04-03 20:04:30 UTC
Permalink
Raw Message
Post by Philip Lantz
Post by Ben Bacarisse
Post by b***@gmail.com
# define c2ln(c,l1,l2,n) { \
c+=n; \
l1=l2=0; \
switch (n) { \
case 8: l2 =((unsigned long)(*(--(c))))<<24; \
case 7: l2|=((unsigned long)(*(--(c))))<<16; \
case 6: l2|=((unsigned long)(*(--(c))))<< 8; \
case 5: l2|=((unsigned long)(*(--(c)))); \
case 4: l1 =((unsigned long)(*(--(c))))<<24; \
case 3: l1|=((unsigned long)(*(--(c))))<<16; \
case 2: l1|=((unsigned long)(*(--(c))))<< 8; \
case 1: l1|=((unsigned long)(*(--(c)))); \
} \
}
A thing to note is that, in C, the switch statement "falls through", so
here the switch simply determines where to start. It can, in effect,
read a big-endian integer of any size from 1 to 8 bytes. (It has a
small technical flaw, in that it is not permitted to decrement a pointer
that points to the start of an object, and this this macro may do that.
...)
Might it? It appears to me that c always ends up pointing to the same place
that it started.* Am I missing something?
No, you are right. I was jumping to conclusions. The prefix operator
is the correct way here.

<snip>
--
Ben.
p***@gmail.com
2017-03-30 00:24:32 UTC
Permalink
Raw Message
Thank you so much for the detailed explanation. I wouldn't have figured this by myself. Much appreciated!
Loading...