
/*
 * @(#)LESwaps.h	1.3 00/03/15
 *
 * (C) Copyright IBM Corp. 1998, 1999, 2000, 2001, 2002 - All Rights Reserved
 * with additions by Sun Microsystems 2002
 *
 */

#ifndef __LESWAPS_H
#define __LESWAPS_H

#include "LETypes.h"

U_NAMESPACE_BEGIN

// the ALLOW_UNALIGNED hack prevents crashes with font files
// containing unaligned tables platforms that do not tolerate
// unaligned memory accesses; it works by assuming that every
// use of the SWAPx macros is intended for accessing a big endian
// value e.g. for "Watanabe Gothic"'s "mort" table
#define ALLOW_UNALIGNED_HACK

#ifndef ALLOW_UNALIGNED_HACK
    /**
     * These are convenience macros which invoke the swap functions
     * from a concise call.
     */
    #define SWAPW(value) (LESwaps::isBigEndian() ? (value) : LESwaps::swapWord(value))
    #define SWAPL(value) (LESwaps::isBigEndian() ? (value) : LESwaps::swapLong(value))
#else
    #define SWAPW(rValue) loadBigEndianWord(reinterpret_cast<const le_uint16&>(rValue))
    #define SWAPL(rValue) loadBigEndianLong(reinterpret_cast<const le_uint32&>(rValue))

inline le_uint16 loadBigEndianWord( const le_uint16& rValue )
{
    const le_uint8* p = reinterpret_cast<const le_uint8*>(&rValue);
    return ((p[0] << 8) + p[1]);
}

inline le_uint32 loadBigEndianLong( const le_uint32& rValue )
{
    const le_uint8* p = reinterpret_cast<const le_uint8*>(&rValue);
    return ((p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]);
}

#endif

/**
 * This class is used to access data which stored in big endian order
 * regardless of the conventions of the platform. It has been designed
 * to automatically detect the endian-ness of the platform, so that a
 * compilation flag is not needed.
 *
 * All methods are static and inline in an attempt to induce the compiler
 * to do most of the calculations at compile time.
 *
 * @draft ICU 2.2
 */
class LESwaps /* not : public UObject because all methods are static */ {
public:

    /**
     * This method detects the endian-ness of the platform by
     * casting a pointer to a word to a pointer to a byte. On
     * big endian platforms the FF will be in the byte with the
     * lowest address. On little endian platforms, the FF will
     * be in the byte with the highest address.
     *
     * @return true if the platform is big endian
     *
     * @draft ICU 2.2
     */
    static le_bool isBigEndian()
    {
        static const le_uint16 word = 0xFF00;

        return *((le_uint8 *) &word);
    };

    /**
     * This method does the byte swap required on little endian platforms
     * to correctly access a (16-bit) word.
     *
     * @param value - the word to be byte swapped
     *
     * @return the byte swapped word
     *
     * @draft ICU 2.2
     */
    static le_uint16 swapWord(le_uint16 value)
    {
        return (((le_uint8) (value >> 8)) | (value << 8));
    };

    /**
     * This method does the byte swapping required on little endian platforms
     * to correctly access a (32-bit) long.
     *
     * @param value - the long to be byte swapped
     *
     * @return the byte swapped long
     *
     * @draft ICU 2.2
     */
    static le_uint32 swapLong(le_uint32 value)
    {
        return swapWord((le_uint16) (value >> 16)) | (swapWord((le_uint16) value) << 16);
    };

private:
    LESwaps() {} // private - forbid instantiation
};

U_NAMESPACE_END
#endif
