/*
 * gauche/bytesP.h - Some speed-sensitive byte-swapping routines
 *
 *   Copyright (c) 2009-2020  Shiro Kawai  <shiro@acm.org>
 *
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions
 *   are met:
 *
 *   1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *
 *   2. Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *
 *   3. Neither the name of the authors nor the names of its contributors
 *      may be used to endorse or promote products derived from this
 *      software without specific prior written permission.
 *
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 *   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 *   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef GAUCHE_PRIV_BYTESP_H
#define GAUCHE_PRIV_BYTESP_H

#include "gauche/priv/builtin-syms.h"

/* Gauche use big-endian / little-endian.   scheme.bytevector requires
   big / little.   We recognize both.  */
#define SCM_IS_BE(endian)                               \
    (SCM_EQ(SCM_OBJ(endian), SCM_SYM_BIG_ENDIAN)        \
     || (SCM_EQ(SCM_OBJ(endian), SCM_SYM_BIG)))
#define SCM_IS_LE(endian)                               \
    (SCM_EQ(SCM_OBJ(endian), SCM_SYM_LITTLE_ENDIAN)     \
     || SCM_EQ(SCM_OBJ(endian), SCM_SYM_LITTLE))
#define SCM_IS_ARM_LE(endian)                           \
    (SCM_EQ(SCM_OBJ(endian), SCM_SYM_ARM_LITTLE_ENDIAN))

#if WORDS_BIGENDIAN
#define SWAP_REQUIRED(endian)   (!SCM_IS_BE(endian))
#else  /*!WORDS_BIGENDIAN.  Covers both little-endian and arm-little-endian. */
#define SWAP_REQUIRED(endian)   SCM_IS_BE(endian)
#endif


/*
 * Swapping macros.   They can be used both ways (native <-> external)
 */

typedef union { char buf[2]; int16_t val; }      swap_s16_t;
typedef union { char buf[2]; uint16_t val; }     swap_u16_t;
typedef union { char buf[4]; int32_t val; }      swap_s32_t;
typedef union { char buf[4]; uint32_t val; }     swap_u32_t;
typedef union { char buf[8]; int64_t val; }      swap_s64_t;
typedef union { char buf[8]; uint64_t val; }     swap_u64_t;

typedef union { char buf[2]; ScmHalfFloat val; } swap_f16_t;
typedef union { char buf[4]; float val; }        swap_f32_t;
typedef union { char buf[8]; double val; }       swap_f64_t;

#define CSWAP(buf, tmp, n, m) (tmp=buf[n], buf[n]=buf[m], buf[m]=tmp)

/* In the following macros, 'v' is of type one of the suitable swap_*_t. */

#define SWAP_2(v) \
    do { char tmp; CSWAP(v.buf, tmp, 0, 1); } while (0)

#define SWAP_4(v)                                               \
    do { char tmp;                                              \
        CSWAP(v.buf, tmp, 0, 3); CSWAP(v.buf, tmp, 1, 2);       \
    } while (0)

#define SWAP_8(v)                                               \
    do { char tmp;                                              \
        CSWAP(v.buf, tmp, 0, 7); CSWAP(v.buf, tmp, 1, 6);       \
        CSWAP(v.buf, tmp, 2, 5); CSWAP(v.buf, tmp, 3, 4);       \
    } while (0)

/* Swapping for double float is a bit tricky for ARM_LE case:
     BE<->ARM : [01234567] -> [32107654]
     LE<->ARM : [01234567] -> [45670123]
*/
#define SWAP_ARM2BE(v)                                          \
    do { char tmp;                                              \
        CSWAP(v.buf, tmp, 0, 3); CSWAP(v.buf, tmp, 1, 2);       \
        CSWAP(v.buf, tmp, 4, 7); CSWAP(v.buf, tmp, 5, 6);       \
    } while (0)

#define SWAP_ARM2LE(v)                                          \
    do { char tmp;                                              \
        CSWAP(v.buf, tmp, 0, 4); CSWAP(v.buf, tmp, 1, 5);       \
        CSWAP(v.buf, tmp, 2, 6); CSWAP(v.buf, tmp, 3, 7);       \
    } while (0)


#endif /*GAUCHE_PRIV_BYTESP_H*/
