Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #107349 > unrolled thread

Interpret this C code

Started byblackhat401@gmail.com
First post2017-03-28 17:50 -0700
Last post2017-03-29 17:24 -0700
Articles 6 — 5 participants

Back to article view | Back to comp.lang.c


Contents

  Interpret this C code blackhat401@gmail.com - 2017-03-28 17:50 -0700
    Re: Interpret this C code Jorgen Grahn <grahn+nntp@snipabacken.se> - 2017-03-29 05:43 +0000
    Re: Interpret this C code Ben Bacarisse <ben.usenet@bsb.me.uk> - 2017-03-29 10:50 +0100
      Re: Interpret this C code Philip Lantz <prl@canterey.us> - 2017-04-03 00:03 -0700
        Re: Interpret this C code Ben Bacarisse <ben.usenet@bsb.me.uk> - 2017-04-03 21:04 +0100
    Re: Interpret this C code praveen.bochetty@gmail.com - 2017-03-29 17:24 -0700

#107349 — Interpret this C code

Fromblackhat401@gmail.com
Date2017-03-28 17:50 -0700
SubjectInterpret this C code
Message-ID<ee7d53aa-9688-46e5-9dcb-d2a68cb5f31f@googlegroups.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 - 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)++))))

[toc] | [next] | [standalone]


#107362

FromJorgen Grahn <grahn+nntp@snipabacken.se>
Date2017-03-29 05:43 +0000
Message-ID<slrnodmibd.1jqn.grahn+nntp@frailea.sa.invalid>
In reply to#107349
On Wed, 2017-03-29, blackhat401@gmail.com wrote:
> 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   .

[toc] | [prev] | [next] | [standalone]


#107364

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2017-03-29 10:50 +0100
Message-ID<87poh0v2cl.fsf@bsb.me.uk>
In reply to#107349
blackhat401@gmail.com writes:

> 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...?).

> 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.

> /* 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.

[toc] | [prev] | [next] | [standalone]


#107661

FromPhilip Lantz <prl@canterey.us>
Date2017-04-03 00:03 -0700
Message-ID<MPG.334b939561f58a63136@news.eternal-september.org>
In reply to#107364
Ben Bacarisse wrote:

> > # 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.

[toc] | [prev] | [next] | [standalone]


#107696

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2017-04-03 21:04 +0100
Message-ID<877f31xnox.fsf@bsb.me.uk>
In reply to#107661
Philip Lantz <prl@canterey.us> writes:

> Ben Bacarisse wrote:
>
>> > # 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.

[toc] | [prev] | [next] | [standalone]


#107445

Frompraveen.bochetty@gmail.com
Date2017-03-29 17:24 -0700
Message-ID<dd21ac0a-5ec3-409b-bc41-ac234f7d9b0c@googlegroups.com>
In reply to#107349
Thank you so much for the detailed explanation. I wouldn't have figured this by myself. Much appreciated!

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.c


csiph-web