TIP: Click on subject to list as thread! ANSI
echo: muffin
to: Bob Jones
from: William McBrine
date: 2003-08-18 08:58:30
subject: Re: QWK problem(s)

-=> Bob Jones wrote to William McBrine <=-

 BJ> what are you using in your code for dealing with

 BJ> (a) big vs little endian for data that needs to be stored or transfered
 BJ> that currently is being done in a binary format, and

 BJ> (b) packing of data structures for binary reading / writing in a
 BJ> portable way?

The key is defining everything in terms of arrays of unsigned chars. Mostly
that means that a "long" becomes an array of four chars, a
"short" an array
of two. This wasn't my idea, BTW -- credit goes to George Hatchew, who
included this scheme for the benefit of big-endian systems in later
revisions of bluewave.h (included in the MultiMail source or the Blue Wave
specs, if you want to see it). Not only does this make the structures
endian-neutral -- you just have to use a small function each time you
access a short or long -- but as a nice side effect, it eliminates padding
within structures. Structures themselves can still get padded at the end,
but I deal with that by specifying their size explictly when reading and
writing (where necessary -- i.e., where the size isn't divisible by four),
rather than using sizeof().

In my case, I use the BIG_ENDIAN (i.e., char-only) version of the structs
on both types of CPU. This adds a little overhead, but not much -- far less
than reading/writing numbers as ASCII, for example. Since it's only for
files on disk, the disk I/O time dwarfs it anyway. Of course, that means I
use a different set of structs (with native ints and longs) to represent
data in memory.

From bluewave.h:


#ifdef BIG_ENDIAN

typedef signed char    tCHAR;     /* 8 bit signed values           */
typedef unsigned char  tBYTE;     /* 8 bit unsigned values         */
typedef unsigned char  tINT[2];   /* little-endian 16 bit signed   */
typedef unsigned char  tWORD[2];  /* little-endian 16 bit unsigned */
typedef unsigned char  tLONG[4];  /* little-endian 32 bit signed   */
typedef unsigned char  tDWORD[4]; /* little-endian 32 bit unsigned */

#else

typedef signed char    tCHAR;     /* 8 bit signed values    */
typedef unsigned char  tBYTE;     /* 8 bit unsigned values  */
typedef signed short   tINT;      /* 16 bit signed values   */
typedef unsigned short tWORD;     /* 16 bit unsigned values */
typedef signed long    tLONG;     /* 32 bit signed values   */
typedef unsigned long  tDWORD;    /* 32 bit unsigned values */

#endif


(Those are only for Blue Wave packets, but I use similar types for other 
packet formats.) And the code I wrote to read and write each type (along 
with some endian-neutral code to handle big-endian longs, as found in SOUP 
packets):


/* get a little-endian short, return an int */
unsigned getshort(const unsigned char *x)
{
        return ((unsigned) x[1] << 8) + (unsigned) x[0];
}

/* get a little-endian long */
unsigned long getlong(const unsigned char *x)
{
        return ((unsigned long) x[3] << 24) + ((unsigned long) x[2]
<< 16) +
                ((unsigned long) x[1] << 8) + (unsigned long) x[0];
}

/* get a big-endian long */
unsigned long getblong(const unsigned char *x)
{
        return ((unsigned long) x[0] << 24) + ((unsigned long) x[1]
<< 16) +
                ((unsigned long) x[2] << 8) + (unsigned long) x[3];
}

/* put an int into a little-endian short */
void putshort(unsigned char *dest, unsigned source)
{
        dest[0] = source & 0xff;
        dest[1] = (source & 0xff00) >> 8;
}

/* put a long into a little-endian long */
void putlong(unsigned char *dest, unsigned long source)
{
        dest[0] = source & 0xff;
        dest[1] = (source & 0xff00) >> 8;
        dest[2] = (source & 0xff0000) >> 16;
        dest[3] = (source & 0xff000000) >> 24;
}

/* put a long into a big-endian long */
void putblong(unsigned char *dest, unsigned long source)
{
        dest[0] = (source & 0xff000000) >> 24;
        dest[1] = (source & 0xff0000) >> 16;
        dest[2] = (source & 0xff00) >> 8;
        dest[3] = source & 0xff;
}

... Life is much easier if you look at the source code.
--- MultiMail/Linux v0.45
* Origin: COMM Port OS/2 juge.com 204.89.247.1 (281) 980-9671 (1:106/2000)
SEEN-BY: 633/267 270
@PATH: 106/2000 633/267

SOURCE: echomail via fidonet.ozzmosis.com

Email questions or comments to sysop@ipingthereforeiam.com
All parts of this website painstakingly hand-crafted in the U.S.A.!
IPTIA BBS/MUD/Terminal/Game Server List, © 2025 IPTIA Consulting™.