/**************************************************************************
 *{@C
 *      Copyright:      1988-2023 Paul Obermeier (obermeier@poSoft.de)
 *
 *      Module:         Utilities
 *      Filename:       UT_Portable.c
 *
 *      Author:         Paul Obermeier
 *
 *      Description:    Functions to convert integer and floating point
 *                      numbers between the processor's data format and
 *                      a machine-independent representation.
 *
 *      Additional documentation:
 *                      None.
 *
 *      Exported functions:
 *                      UT_PortIsIntel
 *                      UT_PortInt16ToByte
 *                      UT_PortUInt16ToByte
 *                      UT_PortInt32ToByte
 *                      UT_PortUInt32ToByte
 *                      UT_PortFloat32ToByte
 *                      UT_PortFloat32To3Byte
 *                      UT_PortFloat32To2Byte
 *                      UT_PortFloat64ToByte
 *                      UT_PortByteToInt16
 *                      UT_PortByteToUInt16
 *                      UT_PortByteToInt32
 *                      UT_PortByteToUInt32
 *                      UT_PortByteToFloat32
 *                      UT_Port3ByteToFloat32
 *                      UT_Port2ByteToFloat32
 *                      UT_PortByteToFloat64
 *
 **************************************************************************/

#include <math.h>

#include "UT_Compat.h"
#include "UT_Macros.h"
#include "UT_Portable.h"

/***************************************************************************
 *[@e
 *      Name:           UT_PortIsIntel
 *
 *      Usage:          Determine at runtime, whether we are on an Intel system.
 *
 *      Synopsis:       UT_Bool UT_PortIsIntel (void)
 *
 *      Description:    Determine at runtime, whether we are on an Intel system.
 *                      Intel systems have byte order little-endian.
 *
 *      Return value:   UT_True, if processor architecture is Intel.
 *                      UT_False, if processor architecure is Motorola.
 *
 *      See also:
 *
 ***************************************************************************/

UT_Bool UT_PortIsIntel (void)
{
    char order[] = { 1, 2, 3, 4 };
    unsigned long val = (unsigned long)*((short *)order);
    /* On Intel (little-endian) systems this value is equal to 513.
       On big-endian systems this value equals 258. */
    return (val == 513)? UT_True: UT_False;
}

/***************************************************************************
 *[@e
 *      Name:           UT_PortInt16ToByte
 *
 *      Usage:          Convert a Int16 into a Byte vector.
 *
 *      Synopsis:       void UT_PortInt16ToByte (Int16 x, Int8 buf[2])
 *
 *      Description:    "Buf" is filled with a machine-independent
 *                      representation of "x" (Two's complement with the
 *                      most significant byte at the lowest address),
 *                      which can be used to transfer the value of "x"
 *                      between different types of processors.
 *
 *      Return value:   None.
 *
 *      See also:
 *
 ***************************************************************************/

void UT_PortInt16ToByte (Int16 x, Int8 buf[2])
{
    buf[1] = (Int8)(x);
    buf[0] = (Int8)(x >> 8);
    return;
}

/***************************************************************************
 *[@e
 *      Name:           UT_PortUInt16ToByte
 *
 *      Usage:          Convert a UInt16 into a Byte vector.
 *
 *      Synopsis:       void UT_PortUInt16ToByte (UInt16 x, Int8 buf[2])
 *
 *      Description:    "Buf" is filled with a machine-independent
 *                      representation of "x" (Two's complement with the
 *                      most significant byte at the lowest address),
 *                      which can be used to transfer the value of "x"
 *                      between different types of processors.
 *
 *      Return value:   None.
 *
 *      See also:
 *
 ***************************************************************************/

void UT_PortUInt16ToByte (UInt16 x, Int8 buf[2])
{
    buf[1] = (UInt8)(x);
    buf[0] = (UInt8)(x >> 8);
    return;
}

/***************************************************************************
 *[@e
 *      Name:           UT_PortInt32ToByte
 *
 *      Usage:          Convert an Int32 into a Byte vector.
 *
 *      Synopsis:       void UT_PortInt32ToByte (Int32 x, Int8 buf[4])
 *
 *      Description:    "Buf" is filled with a machine-independent
 *                      representation of "x" (Two's complement with the
 *                      most significant byte at the lowest address),
 *                      which can be used to transfer the value of "x"
 *                      between different types of processors.
 *
 *      Return value:   None.
 *
 *      See also:
 *
 ***************************************************************************/

void UT_PortInt32ToByte (Int32 x, Int8 buf[4])
{
    buf[3] = (Int8)(x);
    buf[2] = (Int8)(x >> 8);
    buf[1] = (Int8)(x >> 16);
    buf[0] = (Int8)(x >> 24);
    return;
}

/***************************************************************************
 *[@e
 *      Name:           UT_PortUInt32ToByte
 *
 *      Usage:          Convert an UInt32 into a Byte vector.
 *
 *      Synopsis:       void UT_PortUInt32ToByte (UInt32 x, Int8 buf[4])
 *
 *      Description:    "Buf" is filled with a machine-independent
 *                      representation of "x" (Two's complement with the
 *                      most significant byte at the lowest address),
 *                      which can be used to transfer the value of "x"
 *                      between different types of processors.
 *
 *      Return value:   None.
 *
 *      See also:
 *
 ***************************************************************************/

void UT_PortUInt32ToByte (UInt32 x, Int8 buf[4])
{
    buf[3] = (UInt8)(x);
    buf[2] = (UInt8)(x >> 8);
    buf[1] = (UInt8)(x >> 16);
    buf[0] = (UInt8)(x >> 24);
    return;
}

/***************************************************************************
 *[@e
 *      Name:           UT_PortFloat32ToByte
 *
 *      Usage:          Convert a Float32 into a 4-Byte vector.
 *
 *      Synopsis:       void UT_PortFloat32ToByte (Float32 x, Int8 buf[4])
 *
 *      Description:    "Buf" is filled with a machine-independent
 *                      representation of "x" (IEEE single-precision
 *                      normalized number), which can be used to
 *                      transfer the value of "x" between different
 *                      types of processors.
 *
 *                      The UT_PortFloat32ToByte function converts "x" into a
 *                      four byte representation, which is sufficient to
 *                      preserve the value of "x" exactly for most processors.
 *
 *                      Conversion Function     Precision of Mantissa
 *                      -------------------------------------------------
 *                      UT_PortFloat32ToByte            23 bits
 *                      UT_PortFloat32To3Byte           15 bits
 *                      UT_PortFloat32To2Byte            7 bits
 *
 *      Return value:   None.
 *
 *      See also:       UT_PortFloat32To3Byte
 *                      UT_PortFloat32To2Byte
 *
 ***************************************************************************/

void UT_PortFloat32ToByte (Float32 x, Int8 buf[4])
{
    #if !defined (USE_EXACTCONV)

        if (UT_PortIsIntel ()) {
            buf[3] = ((Int8 *)&x)[0];
            buf[2] = ((Int8 *)&x)[1];
            buf[1] = ((Int8 *)&x)[2];
            buf[0] = ((Int8 *)&x)[3];
        } else {
            buf[0] = ((Int8 *)&x)[0];
            buf[1] = ((Int8 *)&x)[1];
            buf[2] = ((Int8 *)&x)[2];
            buf[3] = ((Int8 *)&x)[3];
        }
        return;

    #else

        double  m;
        int     exponent;
        Int32   mantissa;

        m = frexp ((double)x, &exponent);
        exponent += 126;

        if (m == 0.0 || exponent < 0) {
            buf[0] = 0;
            buf[1] = 0;
            buf[2] = 0;
            buf[3] = 0;
        } else {
            mantissa = (Int32)(UT_ABS(m) * (double)(1 << 24));
            buf[0] = (Int8)((m >= 0.0? 0x00 : 0x80) | ((exponent >> 1) & 0x7f));
            buf[1] = (Int8)((exponent << 7) | ((mantissa >> 16) & 0x7f));
            buf[2] = (Int8)(mantissa >> 8);
            buf[3] = (Int8)(mantissa);
        }
        return;
    #endif
}

/***************************************************************************
 *[@e
 *      Name:           UT_PortFloat32To3Byte
 *
 *      Usage:          Convert a Float32 into a 3-Byte vector.
 *
 *      Synopsis:       void UT_PortFloat32To3Byte (Float32 x, Int8 buf[3])
 *
 *      Description:    "Buf" is filled with a machine-independent
 *                      representation of "x" (IEEE single-precision
 *                      normalized number), which can be used to
 *                      transfer the value of "x" between different
 *                      types of processors.
 *
 *                      The UT_PortFloat32To3Byte function discards some of
 *                      the least significant bits of "x" in order to produce
 *                      a more compact representation of "x" than 
 *                      UT_PortFloat32ToByte.
 *
 *                      Conversion Function     Precision of Mantissa
 *                      -------------------------------------------------
 *                      UT_PortFloat32ToByte            23 bits
 *                      UT_PortFloat32To3Byte           15 bits
 *                      UT_PortFloat32To2Byte            7 bits
 *
 *      Return value:   None.
 *
 *      See also:       UT_PortFloat32ToByte
 *                      UT_PortFloat32To2Byte
 *
 ***************************************************************************/

void UT_PortFloat32To3Byte (Float32 x, Int8 buf[3])
{
    double      m;
    int         exponent;
    Int32       mantissa;

    m = frexp ((double)x, &exponent);
    exponent += 126;

    if (m == 0.0 || exponent < 0) {
        buf[0] = 0;
        buf[1] = 0;
        buf[2] = 0;
    } else {
        mantissa = (Int32)(UT_ABS(m) * (double)(1 << 24));
        buf[0] = (Int8)((m >= 0.0? 0x00 : 0x80) | ((exponent >> 1) & 0x7f));
        buf[1] = (Int8)((exponent << 7) | ((mantissa >> 16) & 0x7f));
        buf[2] = (Int8)(mantissa >> 8);
    }

    return;
}

/***************************************************************************
 *[@e
 *      Name:           UT_PortFloat32To2Byte
 *
 *      Usage:          Convert a Float32 into a 2-Byte vector.
 *
 *      Synopsis:       void UT_PortFloat32To2Byte (Float32 x, Int8 buf[2])
 *
 *      Description:    "Buf" is filled with a machine-independent
 *                      representation of "x" (IEEE single-precision
 *                      normalized number), which can be used to
 *                      transfer the value of "x" between different
 *                      types of processors.
 *
 *                      The UT_PortFloat32To2Byte function discards some of
 *                      the least significant bits of "x" in order to produce
 *                      a more compact representation of "x" than
 *                      UT_PortFloat32ToByte.
 *
 *                      Conversion Function     Precision of Mantissa
 *                      -------------------------------------------------
 *                      UT_PortFloat32ToByte            23 bits
 *                      UT_PortFloat32To3Byte           15 bits
 *                      UT_PortFloat32To2Byte            7 bits
 *
 *      Return value:   None.
 *
 *      See also:       UT_PortFloat32ToByte
 *                      UT_PortFloat32To3Byte
 *
 ***************************************************************************/

void UT_PortFloat32To2Byte (Float32 x, Int8 buf[2])
{
    double      m;
    int         exponent;
    Int32       mantissa;

    m = frexp ((double)x, &exponent);
    exponent += 126;

    if (m == 0.0 || exponent < 0) {
        buf[0] = 0;
        buf[1] = 0;
    } else {
        mantissa = (Int32)(UT_ABS(m) * (double)(1 << 24));
        buf[0] = (Int8)((m >= 0.0? 0x00 : 0x80) | ((exponent >> 1) & 0x7f));
        buf[1] = (Int8)((exponent << 7) | ((mantissa >> 16) & 0x7f));
    }

    return;
}

/***************************************************************************
 *[@e
 *      Name:           UT_PortFloat64ToByte
 *
 *      Usage:          Convert a Float64 into a 8-Byte vector.
 *
 *      Synopsis:       void UT_PortFloat64ToByte (Float64 x, Int8 buf[8])
 *
 *      Description:    "Buf" is filled with a machine-independent
 *                      representation of "x" (IEEE double-precision
 *                      normalized number), which can be used to
 *                      transfer the value of "x" between different
 *                      types of processors.
 *
 *      Return value:   None.
 *
 *      See also:
 *
 ***************************************************************************/

void UT_PortFloat64ToByte (Float64 x, Int8 buf[8])
{
    #if !defined (USE_EXACTCONV)
        if (UT_PortIsIntel ()) {
            buf[7] = ((Int8 *)&x)[0];
            buf[6] = ((Int8 *)&x)[1];
            buf[5] = ((Int8 *)&x)[2];
            buf[4] = ((Int8 *)&x)[3];
            buf[3] = ((Int8 *)&x)[4];
            buf[2] = ((Int8 *)&x)[5];
            buf[1] = ((Int8 *)&x)[6];
            buf[0] = ((Int8 *)&x)[7];
        } else {
            buf[0] = ((Int8 *)&x)[0];
            buf[1] = ((Int8 *)&x)[1];
            buf[2] = ((Int8 *)&x)[2];
            buf[3] = ((Int8 *)&x)[3];
            buf[4] = ((Int8 *)&x)[4];
            buf[5] = ((Int8 *)&x)[5];
            buf[6] = ((Int8 *)&x)[6];
            buf[7] = ((Int8 *)&x)[7];
        }
        return;

    #else
        double  m, tmp;
        int     exponent, sgn;
        Int32   m1, m2;

        m = frexp ((double)x, &exponent);
        exponent += 1022;

        if (m == 0.0 || exponent < 0) {
            buf[0] = 0;
            buf[1] = 0;
            buf[2] = 0;
            buf[3] = 0;
            buf[4] = 0;
            buf[5] = 0;
            buf[6] = 0;
            buf[7] = 0;
        } else {
            sgn = (m < 0.0);
            tmp = UT_ABS(m) * (double)(1 << 29);
            m2 = (Int32)tmp;
            m1 = (Int32)((tmp - (double)m2) * (double)(1 << 24));
            buf[0] = (Int8)((sgn << 7) | ((exponent >> 4) & 0x7f));
            buf[1] = (Int8)(((exponent << 4) & 0xf0) | ((m2 >> 24) & 0x0f));
            buf[2] = (Int8)(m2 >> 16);
            buf[3] = (Int8)(m2 >> 8);
            buf[4] = (Int8)(m2);
            buf[5] = (Int8)(m1 >> 16);
            buf[6] = (Int8)(m1 >> 8);
            buf[7] = (Int8)(m1);
        }
        return;
    #endif
}

/***************************************************************************
 *[@e
 *      Name:           UT_PortByteToInt16
 *
 *      Usage:          Convert a Byte vector into a Int16.
 *
 *      Synopsis:       Int16 UT_PortByteToInt16 (const Int8 buf[2])
 *
 *      Description:    "Buf" must be the machine-independent representation
 *                      of a Int16 number as produced by UT_PortInt16ToByte.
 *
 *      Return value:   The machine's native representation of the number is
 *                      returned.
 *
 *      See also:
 *
 ***************************************************************************/

Int16 UT_PortByteToInt16 (const Int8 buf[2])
{
    return  (buf[1] & 0x00ff) |
            (buf[0] << 8);
}

/***************************************************************************
 *[@e
 *      Name:           UT_PortByteToUInt16
 *
 *      Usage:          Convert a Byte vector into a UInt16.
 *
 *      Synopsis:       UInt16 UT_PortByteToUInt16 (const Int8 buf[2])
 *
 *      Description:    "Buf" must be the machine-independent representation
 *                      of a UInt16 number as produced by UT_PortUInt16ToByte.
 *
 *      Return value:   The machine's native representation of the number is
 *                      returned.
 *
 *      See also:
 *
 ***************************************************************************/

UInt16 UT_PortByteToUInt16 (const Int8 buf[2])
{
    return  (buf[1] & 0x00ff) |
            (buf[0] << 8);
}

/***************************************************************************
 *[@e
 *      Name:           UT_PortByteToInt32
 *
 *      Usage:          Convert a Byte vector into an Int32.
 *
 *      Synopsis:       Int32 UT_PortByteToInt32 (const Int8 buf[4])
 *
 *      Description:    "Buf" must be the machine-independent representation
 *                      of an Int32 number produced by UT_PortInt32ToByte.
 *
 *      Return value:   The machine's native representation of the number is
 *                      returned.
 *
 *      See also:
 *
 ***************************************************************************/

Int32 UT_PortByteToInt32 (const Int8 buf[4])
{
    return   (buf[3] & 0x000000ff) |
            ((buf[2] << 8) & 0x0000ff00) |
            ((buf[1] << 16) & 0x00ff0000) |
             (buf[0] << 24);
}

/***************************************************************************
 *[@e
 *      Name:           UT_PortByteToUInt32
 *
 *      Usage:          Convert a Byte vector into an UInt32.
 *
 *      Synopsis:       UInt32 UT_PortByteToUInt32 (const Int8 buf[4])
 *
 *      Description:    "Buf" must be the machine-independent representation
 *                      of an UInt32 number produced by UT_PortUInt32ToByte.
 *
 *      Return value:   The machine's native representation of the number is
 *                      returned.
 *
 *      See also:
 *
 ***************************************************************************/

UInt32 UT_PortByteToUInt32 (const Int8 buf[4])
{
    return   (buf[3] & 0x000000ff) |
            ((buf[2] << 8) & 0x0000ff00) |
            ((buf[1] << 16) & 0x00ff0000) |
             (buf[0] << 24);
}

/***************************************************************************
 *[@e
 *      Name:           UT_PortByteToFloat32
 *
 *      Usage:          Convert a 4-Byte vector into a Float32.
 *
 *      Synopsis:       Float32 UT_PortByteToFloat32 (const Int8 buf[4])
 *
 *      Description:    For UT_PortByteToFloat32, "buf" must be the machine-
 *                      independent representation of a Float32 number as
 *                      produced by UT_PortFloat32ToByte.  The machine's native
 *                      representation of the number is returned.
 *
 *      Return value:   See above.
 *
 *      See also:       UT_Port3ByteToFloat32
 *                      UT_Port2ByteToFloat32
 *
 ***************************************************************************/

Float32 UT_PortByteToFloat32 (const Int8 buf[4])
{
    #if !defined (USE_EXACTCONV)
        Float32 m;

        if (UT_PortIsIntel ()) {
            ((UInt8 *)&m)[0] = buf[3];
            ((UInt8 *)&m)[1] = buf[2];
            ((UInt8 *)&m)[2] = buf[1];
            ((UInt8 *)&m)[3] = buf[0];
        } else {
            ((UInt8 *)&m)[0] = buf[0];
            ((UInt8 *)&m)[1] = buf[1];
            ((UInt8 *)&m)[2] = buf[2];
            ((UInt8 *)&m)[3] = buf[3];
        }
        return m;

    #else
        double  m;
        int     exponent, mantissa;

        mantissa =
                ((buf[1] << 16) & 0x007f0000) |
                ((buf[2] << 8)  & 0x0000ff00) |
                 (buf[3]        & 0x000000ff);
        exponent =
                ((buf[0] << 1)  & 0x000000fe) |
                ((buf[1] >> 7)  & 0x00000001);

        if (mantissa == 0 && exponent == 0)
            return 0.0;

        m = 1.0 + (double)mantissa * (1.0 / (double)(1 << 23));
        return (Float32) (ldexp (m, exponent-127) * (buf[0] & 0x80? -1.0: 1.0));
    #endif
}

/***************************************************************************
 *[@e
 *      Name:           UT_Port3ByteToFloat32
 *
 *      Usage:          Convert a 3-Byte vector into a Float32.
 *
 *      Synopsis:       Float32 UT_Port3ByteToFloat32 (const Int8 buf[3])
 *
 *      Description:    For UT_Port3ByteToFloat32, "buf"
 *                      must be the machine-independent representation of
 *                      a Float32 number as produced by UT_PortFloat32To3Byte. 
 *                      The machine's native representation of the number,
 *                      with the least significant bits of the mantissa set
 *                      to zero, is returned.
 *
 *      Return value:   See above.
 *
 *      See also:       UT_PortByteToFloat32
 *                      UT_Port2ByteToFloat32
 *
 ***************************************************************************/

Float32 UT_Port3ByteToFloat32 (const Int8 buf[3])
{
    double      m;
    int exponent, mantissa;

    mantissa =
            ((buf[1] << 16) & 0x007f0000) |
            ((buf[2] << 8)  & 0x0000ff00);
    exponent =
            ((buf[0] << 1)  & 0x000000fe) |
            ((buf[1] >> 7)  & 0x00000001);

    if (mantissa == 0 && exponent == 0)
        return 0.0;

    m = 1.0 + (double)mantissa * (1.0 / (double)(1 << 23));
    return (Float32) (ldexp (m, exponent-127) * (buf[0] & 0x80? -1.0: 1.0));
}

/***************************************************************************
 *[@e
 *      Name:           UT_Port2ByteToFloat32
 *
 *      Usage:          Convert a 2-Byte vector into a Float32.
 *
 *      Synopsis:       Float32 UT_Port2ByteToFloat32 (const Int8 buf[2])
 *
 *      Description:    For UT_Port2ByteToFloat32, "buf" must be the
 *                      machine-independent representation of a Float32 number
 *                      as produced by UT_PortFloat32To2Byte.
 *                      The machine's native representation of the number,
 *                      with the least significant bits of the mantissa set
 *                      to zero, is returned.
 *
 *      Return value:   See above.
 *
 *      See also:       UT_PortByteToFloat32
 *                      UT_Port3ByteToFloat32
 *
 ***************************************************************************/

Float32 UT_Port2ByteToFloat32 (const Int8 buf[2])
{
    double      m;
    int exponent, mantissa;

    mantissa =
             (buf[1] << 16) & 0x007f0000;
    exponent =
            ((buf[0] << 1)  & 0x000000fe) |
            ((buf[1] >> 7)  & 0x00000001);

    if (mantissa == 0 && exponent == 0)
        return 0.0;

    m = 1.0 + (double)mantissa * (1.0 / (double)(1 << 23));
    return (Float32) (ldexp (m, exponent-127) * (buf[0] & 0x80? -1.0: 1.0));
}

/***************************************************************************
 *[@e
 *      Name:           UT_PortByteToFloat64
 *
 *      Usage:          Convert a 8-Byte vector into a Float64.
 *
 *      Synopsis:       Float64 UT_PortByteToFloat64 (const Int8 buf[8])
 *
 *      Description:    "Buf" must be the machine-independent representation
 *                      of a Float64 number produced by UT_PortFloat64ToByte.
 *
 *      Return value:   The machine's native representation of the number is
 *                      returned.
 *
 *      See also:
 *
 ***************************************************************************/

Float64 UT_PortByteToFloat64 (const Int8 buf[8])
{
    #if !defined (USE_EXACTCONV)
        Float64 m;

        if (UT_PortIsIntel ()) {
            ((UInt8 *)&m)[0] = buf[7];
            ((UInt8 *)&m)[1] = buf[6];
            ((UInt8 *)&m)[2] = buf[5];
            ((UInt8 *)&m)[3] = buf[4];
            ((UInt8 *)&m)[4] = buf[3];
            ((UInt8 *)&m)[5] = buf[2];
            ((UInt8 *)&m)[6] = buf[1];
            ((UInt8 *)&m)[7] = buf[0];
        } else {
            ((UInt8 *)&m)[0] = buf[0];
            ((UInt8 *)&m)[1] = buf[1];
            ((UInt8 *)&m)[2] = buf[2];
            ((UInt8 *)&m)[3] = buf[3];
            ((UInt8 *)&m)[4] = buf[4];
            ((UInt8 *)&m)[5] = buf[5];
            ((UInt8 *)&m)[6] = buf[6];
            ((UInt8 *)&m)[7] = buf[7];
        }
        return m;

    #else
        double  m;
        int     exponent, m1, m2;

        m1 =    ((buf[5] << 16) & 0x00ff0000) |
                ((buf[6] << 8)  & 0x0000ff00) |
                 (buf[7]        & 0x000000ff);
        m2 =    ((buf[1] << 24) & 0x0f000000) |
                ((buf[2] << 16) & 0x00ff0000) |
                ((buf[3] << 8)  & 0x0000ff00) |
                 (buf[4]        & 0x000000ff);
        exponent =
                ((buf[0] << 4)  & 0x000007f0) |
                ((buf[1] >> 4)  & 0x0000000f);

        if (m1 == 0 && m2 == 0 && exponent == 0)
            return 0.0;

        m =     (1.0 / ((double)(1 << 28) * (double)(1 << 24))) *
                (((double)(1 << 28) * (double)(1 << 24)) +
                (double)m2 * (double)(1 << 24) +
                (double)m1);
        return  ldexp (m, exponent - 1023) * (buf[0] & 0x80? -1.0: 1.0);
    #endif
}
