/*
 * uvectorP.h - internal macros for the uniform vector module
 *
 *   Copyright (c) 2000-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_UVECTOR_P_H
#define GAUCHE_UVECTOR_P_H

/*--------------------------------------------------------
 * inline functions used privately
 */

/*-----------------------------------------------------------
 * range checks
 */
static void range_error(const char *type, ScmObj obj)
{
    if (SCM_INTP(obj) && SCM_INT_VALUE(obj) == 0) {
        Scm_Error("value out of domain for %svector", type);
    } else {
        Scm_Error("value out of domain for %svector: %S", type, obj);
    }
}

static inline long range_s8hi(long val, int clamp)
{
    if (!(clamp & SCM_CLAMP_HI)) range_error("s8", Scm_MakeInteger(val));
    return 127;
}

static inline long range_s8lo(long val, int clamp)
{
    if (!(clamp & SCM_CLAMP_LO)) range_error("s8", Scm_MakeInteger(val));
    return -128;
}

static inline long clamp_s8(long val, int clamp)
{
    if (val > 127)  return range_s8hi(val, clamp);
    if (val < -128) return range_s8lo(val, clamp);
    return val;
}

static inline long range_u8hi(long val, int clamp)
{
    if (!(clamp & SCM_CLAMP_HI)) range_error("u8", Scm_MakeInteger(val));
    return 255;
}

static inline long range_u8lo(long val, int clamp)
{
    if (!(clamp & SCM_CLAMP_LO)) range_error("u8", Scm_MakeInteger(val));
    return 0;
}

static inline long clamp_u8(long val, int clamp)
{
    if (val > 255)  return range_u8hi(val, clamp);
    if (val < 0)    return range_u8lo(val, clamp);
    return val;
}

static inline long range_s16hi(long val, int clamp)
{
    if (!(clamp & SCM_CLAMP_HI)) range_error("s16", Scm_MakeInteger(val));
    return 32767;
}

static inline long range_s16lo(long val, int clamp)
{
    if (!(clamp & SCM_CLAMP_LO)) range_error("s16", Scm_MakeInteger(val));
    return -32768;
}

static inline long clamp_s16(long val, int clamp)
{
    if (val > 32767)  return range_s16hi(val, clamp);
    if (val < -32768) return range_s16lo(val, clamp);
    return val;
}

static inline long range_u16hi(long val, int clamp)
{
    if (!(clamp & SCM_CLAMP_HI)) range_error("u16", Scm_MakeInteger(val));
    return 65535;
}

static inline long range_u16lo(long val, int clamp)
{
    if (!(clamp & SCM_CLAMP_LO)) range_error("u16", Scm_MakeInteger(val));
    return 0;
}

static inline long clamp_u16(long val, int clamp)
{
    if (val > 65535)  return range_u16hi(val, clamp);
    if (val < 0)      return range_u16lo(val, clamp);
    return val;
}

static inline long range_s32hi(long val, int clamp)
{
    if (!(clamp & SCM_CLAMP_HI)) range_error("s32", Scm_MakeInteger(val));
    return 2147483647L;
}

static inline long range_s32lo(long val, int clamp)
{
    if (!(clamp & SCM_CLAMP_LO)) range_error("s32", Scm_MakeInteger(val));
    return -2147483647L-1;
}

#if SIZEOF_LONG == 4
#define clamp_s32(val, clamp)   (val)
#else
static inline long clamp_s32(long val, int clamp)
{
    if (val > 2147483647L)  return range_s32hi(val, clamp);
    if (val < -2147483648L) return range_s32lo(val, clamp);
    return val;
}
#endif

static inline long range_u32hi(u_long val, int clamp)
{
    if (!(clamp & SCM_CLAMP_HI)) range_error("u32", Scm_MakeIntegerU(val));
    return 4294967295UL;
}

static inline long range_u32lo(u_long val, int clamp)
{
    if (!(clamp & SCM_CLAMP_LO)) range_error("u32", Scm_MakeIntegerU(val));
    return 0;
}

#if SIZEOF_LONG == 4
#define clamp_u32(val, clamp)   (val)
#else
static inline long clamp_u32(u_long val, int clamp)
{
    if (val > 4294967295UL)    return range_u32hi(val, clamp);
    return val;
}
#endif

static inline int64_t range_s64hi(int64_t val, int clamp)
{
    if (!(clamp & SCM_CLAMP_HI)) range_error("s64", Scm_MakeInteger64(val));
    val = INT64_MAX;
    return val;
}

static inline int64_t range_s64lo(int64_t val, int clamp)
{
    if (!(clamp & SCM_CLAMP_LO)) range_error("s64", Scm_MakeInteger64(val));
    val = INT64_MIN;
    return val;
}

static inline uint64_t range_u64hi(uint64_t val, int clamp)
{
    if (!(clamp & SCM_CLAMP_HI)) range_error("u64", Scm_MakeIntegerU64(val));
    val = UINT64_MAX;
    return val;
}

static inline uint64_t range_u64lo(uint64_t val, int clamp)
{
    if (!(clamp & SCM_CLAMP_LO)) range_error("u64", Scm_MakeIntegerU64(val));
    val = 0;
    return val;
}

/*
 * 'option' argument for Scm_UVectorSwapBytes.
 */

enum {
    SWAPB_STD,                  /* simple swapping */
    SWAPB_ARM_LE,               /* arm-little-endian <-> little-endian */
    SWAPB_ARM_BE                /* arm-little-endian <-> big-endian */
};


#endif /* GAUCHE_UVECTOR_P_H */
