replace bitfields with shifts and masks

Discussion in 'C Programming' started by Dave, Jun 4, 2006.

  1. Dave

    Dave Guest

    I need to rewrite the following using shifts and masks for portability
    sakes, and to get rid of the BYTE_ORDER issue.

    /* CODE */
    struct test {
    #if BYTE_ORDER == BIG_ENDIAN
    unsigned char ver:4;
    unsigned char res1:4;
    #else
    unsigned char res1:4;
    unsigned char ver:4;
    #endif
    unsigned char reserved2;
    unsigned short checksum;
    };

    void
    function(unsigned char *data)
    {
    struct test *t = (struct test *)data;

    if (t.ver == 1) {
    /* match */
    }
    }

    /* END */

    I was thinking I could just do the following, but it does not seem to work.

    void
    function(unsigned char *data)
    {
    uint32_t test = *(uint32_t *)data;

    /* shift 28 bits, mask 4 */
    if (((test >> 28) & 0x0f) == 1) {
    /* match */
    }
    }

    I am thinking the declaration is not correct.
    Thanks for any help

    - Dave
     
    Dave, Jun 4, 2006
    #1
    1. Advertising

  2. In article <>,
    Dave <> wrote:
    >I need to rewrite the following using shifts and masks for portability
    >sakes, and to get rid of the BYTE_ORDER issue.


    >/* CODE */
    >struct test {
    >#if BYTE_ORDER == BIG_ENDIAN
    > unsigned char ver:4;
    > unsigned char res1:4;
    >#else
    > unsigned char res1:4;
    > unsigned char ver:4;
    >#endif
    > unsigned char reserved2;
    > unsigned short checksum;
    >};


    >void
    >function(unsigned char *data)
    >{
    > struct test *t = (struct test *)data;
    >
    > if (t.ver == 1) {
    > /* match */
    > }
    >}
    >
    >/* END */


    >I was thinking I could just do the following, but it does not seem to work.


    >void
    >function(unsigned char *data)
    >{
    > uint32_t test = *(uint32_t *)data;


    > /* shift 28 bits, mask 4 */
    > if (((test >> 28) & 0x0f) == 1) {
    > /* match */
    > }
    >}


    >I am thinking the declaration is not correct.


    I'm not surprised it didn't work. You've made an improper
    assumption that just because a byte occurs first in a structure,
    that the byte will be the Most Significant Byte when the
    structure is reinterpreted as an integer value. That assumption
    mostly works out on strict big-endian machines, but it rarely
    works out on little-endian machines.

    What you need to know is that when a CPU loads several bytes
    of memory to become a numeric value, that the byte order in
    memory may well be not be the order the bytes will appear in the
    numeric value. For example, if memory happens to have consequative
    bytes 1 2 3 4, then when that block of bytes is loaded as an integer
    value, the CPU register may hold 2 1 4 3 or 4 3 2 1 or any of
    several other possible orders. Numeric value order is not certain
    to be the same as the order written to memory. (This is not
    a problem for the CPU because the scrambling/descrabling algorithm
    is -consistant-.)

    It is not sufficient to know that the machine is "big endian" or
    "little endian", as there are 24 possible orders that can be used
    to store 4 bytes. Two of those orders are in very common use,
    but at least one other of them is common enough that it likely
    will be encountered by a program that is intended to be portable.

    There are easy run-time tests to determine what the byte
    ordering is, and easy reordering routines.
    --
    There are some ideas so wrong that only a very intelligent person
    could believe in them. -- George Orwell
     
    Walter Roberson, Jun 4, 2006
    #2
    1. Advertising

  3. Dave

    Dave Guest

    Walter Roberson wrote:
    > In article <>,
    > Dave <> wrote:
    >> I need to rewrite the following using shifts and masks for portability
    >> sakes, and to get rid of the BYTE_ORDER issue.

    >
    >> /* CODE */
    >> struct test {
    >> #if BYTE_ORDER == BIG_ENDIAN
    >> unsigned char ver:4;
    >> unsigned char res1:4;
    >> #else
    >> unsigned char res1:4;
    >> unsigned char ver:4;
    >> #endif
    >> unsigned char reserved2;
    >> unsigned short checksum;
    >> };

    >
    >> void
    >> function(unsigned char *data)
    >> {
    >> struct test *t = (struct test *)data;
    >>
    >> if (t.ver == 1) {
    >> /* match */
    >> }
    >> }
    >>
    >> /* END */

    >
    >> I was thinking I could just do the following, but it does not seem to work.

    >
    >> void
    >> function(unsigned char *data)
    >> {
    >> uint32_t test = *(uint32_t *)data;

    >
    >> /* shift 28 bits, mask 4 */
    >> if (((test >> 28) & 0x0f) == 1) {
    >> /* match */
    >> }
    >> }

    >
    >> I am thinking the declaration is not correct.

    >
    > I'm not surprised it didn't work. You've made an improper
    > assumption that just because a byte occurs first in a structure,
    > that the byte will be the Most Significant Byte when the
    > structure is reinterpreted as an integer value. That assumption
    > mostly works out on strict big-endian machines, but it rarely
    > works out on little-endian machines.
    >
    > What you need to know is that when a CPU loads several bytes
    > of memory to become a numeric value, that the byte order in
    > memory may well be not be the order the bytes will appear in the
    > numeric value. For example, if memory happens to have consequative
    > bytes 1 2 3 4, then when that block of bytes is loaded as an integer
    > value, the CPU register may hold 2 1 4 3 or 4 3 2 1 or any of
    > several other possible orders. Numeric value order is not certain
    > to be the same as the order written to memory. (This is not
    > a problem for the CPU because the scrambling/descrabling algorithm
    > is -consistant-.)
    >
    > It is not sufficient to know that the machine is "big endian" or
    > "little endian", as there are 24 possible orders that can be used
    > to store 4 bytes. Two of those orders are in very common use,
    > but at least one other of them is common enough that it likely
    > will be encountered by a program that is intended to be portable.
    >
    > There are easy run-time tests to determine what the byte
    > ordering is, and easy reordering routines.


    Doh, I forgot ntohl() :p

    Thanks
     
    Dave, Jun 4, 2006
    #3
  4. Dave

    Chris Torek Guest

    >In article <>,
    >Dave <> wrote:
    >>I need to rewrite the following using shifts and masks for portability
    >>sakes, and to get rid of the BYTE_ORDER issue.

    >
    >>/* CODE */
    >>struct test {
    >>#if BYTE_ORDER == BIG_ENDIAN
    >> unsigned char ver:4;
    >> unsigned char res1:4;
    >>#else
    >> unsigned char res1:4;
    >> unsigned char ver:4;
    >>#endif
    >> unsigned char reserved2;
    >> unsigned short checksum;
    >>};


    This is apparently an attempt to "fake up" a representation clause
    for something that, in C, is best described as:

    unsigned char buf[4];

    (but note that the "checksum" is now in individual "C byte" pieces
    rather than one probably-larger unit; now *you* control the order
    of the pieces, instead of letting the compiler do it, much as you
    now control the order of the bitfields within the first "C byte").

    If your target machine has greater-than-8-bit "C bytes" ("char"s),
    you may still have some problems, but they are problems you were
    going to have no matter what.

    >>void
    >>function(unsigned char *data)
    >>{
    >> uint32_t test = *(uint32_t *)data;

    >
    >> /* shift 28 bits, mask 4 */
    >> if (((test >> 28) & 0x0f) == 1) {
    >> /* match */
    >> }
    >>}

    >
    >>I am thinking the declaration is not correct.


    In article <e5tucf$7ke$>
    Walter Roberson <-cnrc.gc.ca> wrote:
    >It is not sufficient to know that the machine is "big endian" or
    >"little endian", as there are 24 possible orders that can be used
    >to store 4 bytes. Two of those orders are in very common use,
    >but at least one other of them is common enough that it likely
    >will be encountered by a program that is intended to be portable.


    Or, more simply, treat the array of bytes as an array of bytes.
    Stop giving up control of the layout. Extract byte #0 and shift
    and mask as needed:

    if (((data[0] >> 4) & 0x0f) == 1) {
    ...
    }

    (If you deal with the individual bytes of data as individual bytes
    of data, you never need tricks like "htonl" either.)
    --
    In-Real-Life: Chris Torek, Wind River Systems
    Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
    email: forget about it http://web.torek.net/torek/index.html
    Reading email is like searching for food in the garbage, thanks to spammers.
     
    Chris Torek, Jun 4, 2006
    #4
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Reid Nichol
    Replies:
    11
    Views:
    1,214
    Francesco Bochicchio
    Sep 11, 2004
  2. bobrics
    Replies:
    2
    Views:
    755
    Flash Gordon
    Mar 21, 2006
  3. Dave

    shifts and masks

    Dave, Jun 3, 2006, in forum: C Programming
    Replies:
    4
    Views:
    467
    Malcolm
    Jun 4, 2006
  4. Wojtek

    masks and enums

    Wojtek, Jul 24, 2007, in forum: Java
    Replies:
    21
    Views:
    939
    Wojtek
    Jul 31, 2007
  5. moogyd

    Bit masks and nature of equality

    moogyd, Jun 2, 2010, in forum: C Programming
    Replies:
    7
    Views:
    684
    Seebs
    Jun 2, 2010
Loading...

Share This Page