//! @file //! Source code for the gnulliver library. //! // gnulliver.c - Travels the middle of the road to swiftly gnullify // the Big-endian / Little-endian controversy. // Only works on machines with IEEE floating point. // Copyright (C) 1994-2004 Paul Hardy // Copyright (C) 2011 Alan W. Irwin // // This file is part of the timeephem software project. // // timeephem is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as published // by the Free Software Foundation; version 2 of the License. // // timeephem is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with timeephem; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // Swaps values between host order and network order without calls // to htonl(), etc. This is a symmetric conversion, so the same // function works in both directions. For example, to convert an // integer between host byte ordering and network byte ordering (in // either direction), use // // int i, j; // j = gnulliver32(i); // // then use j in an equation (if you just read i from the net), or // write j to the net (if i is an integer on your machine). // // The gnulliver routines work by setting a double to a certain value, // then examining a unioned byte array. // // The functions are: // // char gnulliver() - determine (or recalculate) endianness of host: // GNULLIVER_BIG, GNULLIVER_LITTLE, or GNULLIVER_DEC, or whether to // just do no swapping at all (GNULLIVER_NATIVE) to read and // write native endian result. // unsigned short gnulliver16(unsigned short input) // unsigned gnulliver32(unsigned input) // unsigned long gnulliver64(unsigned long input) // unsigned long long gnulliver128(unsigned long long input) // float gnulliver32f(float input) // double gnulliver64f(double input) // long double gnulliver128f(long double input) // // The code in the routines might appear largely redundant, and it is. // Loops are also unrolled. All this should minimize function calls and // maximize speed. // #include "gnulliver.h" // // GNULLIVER_TEST is the magic number we use to determine byte swapping in // a 64-bit word. It assumes IEEE 754 floating point, and encodes to // hexadecimal 0x40302010 00000000 on a Big-endian (network order) machine. // The mantissa is large enough that it might work on a non-IEEE 754 compatible // machine just by coincidence, but no guarantees. // // gnulliver currently assumes that if we swap 32-bit values in a 64-bit word, // then we also swap 64-bit values in a 128-bit word (long double), which is // true if the machine is Little-endian. If this does not hold true on a // particular platform, let me know and I'll modify the code. // #define GNULLIVER_TEST ( 16.0 + ( 1.0 / 8.0 + 1.0 / 4096.0 ) ) //! Discover the endianess of the host. //! //! @returns GNULLIVER_LITTLE, GNULLIVER_DEC, GNULLIVER_BIG depending on //! whether the host uses little-, DEC-, or big-endian order for its bytes. //! This information is used to swap bytes between host byte order and //! assumed network order (big-endian) for the binary file. //! Alternatively, gnulliver() returns GNULLIVER_NATIVE for the case //! where no byte swaps are desired for i/o on a binary file. //! unsigned char gnulliver() { #ifdef NO_GNULLIVER_SWAP return ( GNULLIVER_NATIVE ); #else unsigned char result; union { double d; unsigned char ch[8]; } bytes; bytes.d = GNULLIVER_TEST; result = 0; if ( bytes.ch[0] == 0 ) // swap 32-bit words in a 64-bit number { if ( bytes.ch[4] == 0x10 ) // Assume 0x00000000 10203040 result = GNULLIVER_LITTLE; else // Assume 0x00000000 20104030 result = GNULLIVER_DEC; } else // Assume 0x40302010 00000000 result = GNULLIVER_BIG; return ( result ); #endif } //! Optional byteswap of short depending on gnulliver() result. //! //! @param input [IN ONLY]Value of integer to be optionally byteswapped. //! @returns (optionally) byteswapped integer. //! unsigned short gnulliver16( unsigned short input ) { static unsigned endian, init = 1; unsigned char tmpch; union { unsigned short u; unsigned char ch[2]; } bytes; if ( init ) { endian = gnulliver(); init = 0; } bytes.u = input; if ( endian != GNULLIVER_BIG && endian != GNULLIVER_NATIVE ) { tmpch = bytes.ch[0]; bytes.ch[0] = bytes.ch[1]; bytes.ch[1] = tmpch; } return ( bytes.u ); } //! Optional byteswap of integer depending on gnulliver() result. //! //! @param input [IN ONLY]Value of integer to be optionally byteswapped. //! @returns (optionally) byteswapped integer. //! unsigned gnulliver32( unsigned input ) { static unsigned endian, init = 1; char tmpch; union { unsigned u; unsigned char ch[4]; } bytes; if ( init ) { endian = gnulliver(); init = 0; } bytes.u = input; if ( endian != GNULLIVER_BIG && endian != GNULLIVER_NATIVE ) { if ( endian == GNULLIVER_LITTLE ) { tmpch = bytes.ch[0]; bytes.ch[0] = bytes.ch[3]; bytes.ch[3] = tmpch; tmpch = bytes.ch[1]; bytes.ch[1] = bytes.ch[2]; bytes.ch[2] = tmpch; } else // endian == GNULLIVER_DEC { tmpch = bytes.ch[0]; bytes.ch[0] = bytes.ch[2]; bytes.ch[2] = tmpch; tmpch = bytes.ch[1]; bytes.ch[1] = bytes.ch[3]; bytes.ch[3] = tmpch; } } return ( bytes.u ); } //! Optional byteswap of long depending on gnulliver() result. //! //! @param input [IN ONLY]Value of integer to be optionally byteswapped. //! @returns (optionally) byteswapped integer. //! unsigned long gnulliver64( unsigned long input ) { static unsigned endian, init = 1; char tmpch; union { unsigned long u; char ch[8]; } bytes; if ( init ) { endian = gnulliver(); init = 0; } bytes.u = input; if ( endian != GNULLIVER_BIG && endian != GNULLIVER_NATIVE ) { if ( endian == GNULLIVER_LITTLE ) { tmpch = bytes.ch[0]; bytes.ch[0] = bytes.ch[7]; bytes.ch[7] = tmpch; tmpch = bytes.ch[1]; bytes.ch[1] = bytes.ch[6]; bytes.ch[6] = tmpch; tmpch = bytes.ch[2]; bytes.ch[2] = bytes.ch[5]; bytes.ch[5] = tmpch; tmpch = bytes.ch[3]; bytes.ch[3] = bytes.ch[4]; bytes.ch[4] = tmpch; } else // Assume GNULLIVER_DEC { tmpch = bytes.ch[0]; bytes.ch[0] = bytes.ch[6]; bytes.ch[6] = tmpch; tmpch = bytes.ch[1]; bytes.ch[1] = bytes.ch[7]; bytes.ch[7] = tmpch; tmpch = bytes.ch[2]; bytes.ch[2] = bytes.ch[4]; bytes.ch[4] = tmpch; tmpch = bytes.ch[3]; bytes.ch[3] = bytes.ch[5]; bytes.ch[5] = tmpch; } } return ( bytes.u ); } //! Optional byteswap of long long depending on gnulliver() result. //! //! @param input [IN ONLY]Value of integer to be optionally byteswapped. //! @returns (optionally) byteswapped integer. //! unsigned long long gnulliver128( unsigned long long input ) { static unsigned endian, init = 1; unsigned char tmpch; union { unsigned long long u; char ch[16]; } bytes; if ( init ) { endian = gnulliver(); init = 0; } bytes.u = input; if ( endian != GNULLIVER_BIG && endian != GNULLIVER_NATIVE ) { if ( endian == GNULLIVER_LITTLE ) { tmpch = bytes.ch[0]; bytes.ch[0] = bytes.ch[15]; bytes.ch[15] = tmpch; tmpch = bytes.ch[1]; bytes.ch[1] = bytes.ch[14]; bytes.ch[14] = tmpch; tmpch = bytes.ch[2]; bytes.ch[2] = bytes.ch[13]; bytes.ch[13] = tmpch; tmpch = bytes.ch[3]; bytes.ch[3] = bytes.ch[12]; bytes.ch[12] = tmpch; tmpch = bytes.ch[4]; bytes.ch[4] = bytes.ch[11]; bytes.ch[11] = tmpch; tmpch = bytes.ch[5]; bytes.ch[5] = bytes.ch[10]; bytes.ch[10] = tmpch; tmpch = bytes.ch[6]; bytes.ch[6] = bytes.ch[ 9]; bytes.ch[ 9] = tmpch; tmpch = bytes.ch[7]; bytes.ch[7] = bytes.ch[ 8]; bytes.ch[ 8] = tmpch; } else // Assume GNULLIVER_DEC { tmpch = bytes.ch[0]; bytes.ch[0] = bytes.ch[14]; bytes.ch[14] = tmpch; tmpch = bytes.ch[1]; bytes.ch[1] = bytes.ch[15]; bytes.ch[15] = tmpch; tmpch = bytes.ch[2]; bytes.ch[2] = bytes.ch[12]; bytes.ch[12] = tmpch; tmpch = bytes.ch[3]; bytes.ch[3] = bytes.ch[13]; bytes.ch[13] = tmpch; tmpch = bytes.ch[4]; bytes.ch[4] = bytes.ch[10]; bytes.ch[10] = tmpch; tmpch = bytes.ch[5]; bytes.ch[5] = bytes.ch[11]; bytes.ch[11] = tmpch; tmpch = bytes.ch[6]; bytes.ch[6] = bytes.ch[ 8]; bytes.ch[ 8] = tmpch; tmpch = bytes.ch[7]; bytes.ch[7] = bytes.ch[ 9]; bytes.ch[ 9] = tmpch; } } return ( bytes.u ); } //! Optional byteswap of float depending on gnulliver() result. //! //! @param input [IN ONLY]Value of floating-point variable to be //! optionally byteswapped. //! @returns (optionally) byteswapped floating point value. //! float gnulliver32f( float input ) { static unsigned endian, init = 1; unsigned char tmpch; union { float x; unsigned char ch[4]; } bytes; if ( init ) { endian = gnulliver(); init = 0; } bytes.x = input; if ( endian != GNULLIVER_BIG && endian != GNULLIVER_NATIVE ) { if ( endian == GNULLIVER_LITTLE ) { tmpch = bytes.ch[0]; bytes.ch[0] = bytes.ch[3]; bytes.ch[3] = tmpch; tmpch = bytes.ch[1]; bytes.ch[1] = bytes.ch[2]; bytes.ch[2] = tmpch; } else // Assume GNULLIVER_DEC { tmpch = bytes.ch[0]; bytes.ch[0] = bytes.ch[2]; bytes.ch[2] = tmpch; tmpch = bytes.ch[1]; bytes.ch[1] = bytes.ch[3]; bytes.ch[3] = tmpch; } } return ( bytes.x ); } //! Optional byteswap of double depending on gnulliver() result. //! //! @param input [IN ONLY]Value of floating-point variable to be //! optionally byteswapped. //! @returns (optionally) byteswapped floating point value. //! double gnulliver64f( double input ) { static unsigned endian, init = 1; unsigned char tmpch; union { double x; unsigned char ch[8]; } bytes; if ( init ) { endian = gnulliver(); init = 0; } bytes.x = input; if ( endian != GNULLIVER_BIG && endian != GNULLIVER_NATIVE ) { if ( endian == GNULLIVER_LITTLE ) { tmpch = bytes.ch[0]; bytes.ch[0] = bytes.ch[7]; bytes.ch[7] = tmpch; tmpch = bytes.ch[1]; bytes.ch[1] = bytes.ch[6]; bytes.ch[6] = tmpch; tmpch = bytes.ch[2]; bytes.ch[2] = bytes.ch[5]; bytes.ch[5] = tmpch; tmpch = bytes.ch[3]; bytes.ch[3] = bytes.ch[4]; bytes.ch[4] = tmpch; } else // Assume GNULLIVER_DEC { tmpch = bytes.ch[0]; bytes.ch[0] = bytes.ch[6]; bytes.ch[6] = tmpch; tmpch = bytes.ch[1]; bytes.ch[1] = bytes.ch[7]; bytes.ch[7] = tmpch; tmpch = bytes.ch[2]; bytes.ch[2] = bytes.ch[4]; bytes.ch[4] = tmpch; tmpch = bytes.ch[3]; bytes.ch[3] = bytes.ch[5]; bytes.ch[5] = tmpch; } } return ( bytes.x ); } //! Optional byteswap of long double depending on gnulliver() result. //! Note: This function's result will be unpredictable if the type //! long double is not a 128-bit word. //! //! @param input [IN ONLY]Value of floating-point variable to be //! optionally byteswapped. //! @returns (optionally) byteswapped floating point value. //! long double gnulliver128f( long double input ) { static unsigned endian, init = 1; unsigned char tmpch; union { long double x; unsigned char ch[16]; } bytes; if ( init ) { endian = gnulliver(); init = 0; } bytes.x = input; if ( endian != GNULLIVER_BIG && endian != GNULLIVER_NATIVE ) { if ( endian == GNULLIVER_LITTLE ) { tmpch = bytes.ch[0]; bytes.ch[0] = bytes.ch[15]; bytes.ch[15] = tmpch; tmpch = bytes.ch[1]; bytes.ch[1] = bytes.ch[14]; bytes.ch[14] = tmpch; tmpch = bytes.ch[2]; bytes.ch[2] = bytes.ch[13]; bytes.ch[13] = tmpch; tmpch = bytes.ch[3]; bytes.ch[3] = bytes.ch[12]; bytes.ch[12] = tmpch; tmpch = bytes.ch[4]; bytes.ch[4] = bytes.ch[11]; bytes.ch[11] = tmpch; tmpch = bytes.ch[5]; bytes.ch[5] = bytes.ch[10]; bytes.ch[10] = tmpch; tmpch = bytes.ch[6]; bytes.ch[6] = bytes.ch[ 9]; bytes.ch[ 9] = tmpch; tmpch = bytes.ch[7]; bytes.ch[7] = bytes.ch[ 8]; bytes.ch[ 8] = tmpch; } else // Assume GNULLIVER_DEC { tmpch = bytes.ch[0]; bytes.ch[0] = bytes.ch[14]; bytes.ch[14] = tmpch; tmpch = bytes.ch[1]; bytes.ch[1] = bytes.ch[15]; bytes.ch[15] = tmpch; tmpch = bytes.ch[2]; bytes.ch[2] = bytes.ch[12]; bytes.ch[12] = tmpch; tmpch = bytes.ch[3]; bytes.ch[3] = bytes.ch[13]; bytes.ch[13] = tmpch; tmpch = bytes.ch[4]; bytes.ch[4] = bytes.ch[10]; bytes.ch[10] = tmpch; tmpch = bytes.ch[5]; bytes.ch[5] = bytes.ch[11]; bytes.ch[11] = tmpch; tmpch = bytes.ch[6]; bytes.ch[6] = bytes.ch[ 8]; bytes.ch[ 8] = tmpch; tmpch = bytes.ch[7]; bytes.ch[7] = bytes.ch[ 9]; bytes.ch[ 9] = tmpch; } } return ( bytes.x ); } //! Optional swap of char array elements depending on gnulliver() //! result. //! //! @param ch [IN AND OUT ]Pointer to char array containing 8 elements //! which upon return will be optionally swapped. //! @returns pointer to char array which contains the (optionally) //! swapped elements. //! unsigned char * gnulliver64c( unsigned char *ch ) { static unsigned endian, init = 1; unsigned char tmpch; if ( init ) { endian = gnulliver(); init = 0; } if ( endian != GNULLIVER_BIG && endian != GNULLIVER_NATIVE ) { if ( endian == GNULLIVER_LITTLE ) { tmpch = ch[0]; ch[0] = ch[7]; ch[7] = tmpch; tmpch = ch[1]; ch[1] = ch[6]; ch[6] = tmpch; tmpch = ch[2]; ch[2] = ch[5]; ch[5] = tmpch; tmpch = ch[3]; ch[3] = ch[4]; ch[4] = tmpch; } else // Assume GNULLIVER_DEC { tmpch = ch[0]; ch[0] = ch[6]; ch[6] = tmpch; tmpch = ch[1]; ch[1] = ch[7]; ch[7] = tmpch; tmpch = ch[2]; ch[2] = ch[4]; ch[4] = tmpch; tmpch = ch[3]; ch[3] = ch[5]; ch[5] = tmpch; } } return ( ch ); }