/* paper-stdint.h -- replacement stdint.h that does as much as it can
 * without outside help
 *
 * Copyright (C) 2025 Paper
 *
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software
 *    in a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 */

/**
 * This is a drop-in replacement stdint.h that provides a
 * subset of the features that the C99 equivalent provides.
 *
 * There are some caveats:
 *  - [u]intptr_t is not here. There's no way to do it portably.
 *    Thus INTPTR_MAX and friends are not here either. It may
 *    be possible to do it with __LP64__ or something, but that
 *    really isn't portable either :(
 *  - ptrdiff_t is not here.
 *  - int64_t relies on platform-specific extensions, or C99
 *    long long. Add your platform for funsies!
 *  - SIZE_MAX cannot be done portably. The closest thing we have
 *    is (~(size_t)0), which does not work in preprocessor
 *    expressions.
 *  - WINT_MIN cannot be done portably, nor can WCHAR_MIN,
 *    WINT_MAX, or WCHAR_MAX.
 *  - I have not implemented scanf specifiers. I don't use
 *    them, and neither should you. (the real reason is that
 *    "%hhd" was only added in C99).
 *
 * Maybe there's just something I missed. As far as I know
 * these are impossible to do without compiler-specific
 * extensions.  --paper
**/

#ifndef PAPER_STDINT_H_
#define PAPER_STDINT_H_

/* need limits.h for INT_MIN etc */
#include <limits.h>

#define INT8_MAX 0x7F
#define INT8_MIN (-INT8_MAX - 1)
#define INT16_MAX 0x7FFF
#define INT16_MIN (-INT16_MAX - 1)
#define INT32_MAX 0x7FFFFFFF
#define INT32_MIN (-INT32_MAX - 1)
#define INT64_MAX 0x7FFFFFFFFFFFFFFF
#define INT64_MIN (-INT64_MAX - 1)
#define UINT8_MAX 0xFFu
#define UINT16_MAX 0xFFFFu
#define UINT32_MAX 0xFFFFFFFFu
#define UINT64_MAX 0xFFFFFFFFFFFFFFFFu

/* ?? */
#ifndef SIZE_MAX
# define SIZE_MAX (~(size_t)0)
#endif

#if UCHAR_MAX == UINT8_MAX
typedef unsigned char uint8_t;
# define PRIu8 "u"
# define PRIo8 "o"
# define PRIx8 "x"
# define PRIX8 "X"
# define UINT8_C(x) x ## U
#else
# error "no unsigned 8-bit type"
#endif

#if UCHAR_MAX == UINT16_MAX
typedef unsigned char uint16_t;
# define PRIu16 "u"
# define PRIo16 "o"
# define PRIx16 "x"
# define PRIX16 "X"
# define UINT16_C(x) x ## U
#elif USHRT_MAX == UINT16_MAX
typedef unsigned short uint16_t;
# define PRIu16 "u"
# define PRIo16 "o"
# define PRIx16 "x"
# define PRIX16 "X"
# define UINT16_C(x) x ## U
#elif UINT_MAX == UINT16_MAX
typedef unsigned int uint16_t;
# define PRIu16 "u"
# define PRIo16 "o"
# define PRIx16 "x"
# define PRIX16 "X"
# define UINT16_C(x) x ## U
#else
# error "no unsigned 16-bit type"
#endif

#if UCHAR_MAX == UINT32_MAX
typedef unsigned char uint32_t;
# define PRIu32 "u"
# define PRIo32 "o"
# define PRIx32 "x"
# define PRIX32 "X"
# define UINT32_C(x) x ## U
#elif USHRT_MAX == UINT32_MAX
typedef unsigned short uint32_t;
# define PRIu32 "u"
# define PRIo32 "o"
# define PRIx32 "x"
# define PRIX32 "X"
# define UINT32_C(x) x ## U
#elif UINT_MAX == UINT32_MAX
typedef unsigned int uint32_t;
# define PRIu32 "u"
# define PRIo32 "o"
# define PRIx32 "x"
# define PRIX32 "X"
# define UINT32_C(x) x ## U
#elif ULONG_MAX == UINT32_MAX
typedef unsigned long uint32_t;
# define PRIu32 "lu"
# define PRIo32 "lo"
# define PRIx32 "lx"
# define PRIX32 "lX"
# define UINT32_C(x) x ## UL
#else
# error "no unsigned 32-bit type"
#endif

#if UCHAR_MAX == UINT64_MAX
typedef unsigned char uint64_t;
# define PRIu64 "u"
# define PRIo64 "o"
# define PRIx64 "x"
# define PRIX64 "X"
# define UINT64_C(x) x ## U
#elif USHRT_MAX == UINT64_MAX
typedef unsigned short uint64_t;
# define PRIu64 "u"
# define PRIo64 "o"
# define PRIx64 "x"
# define PRIX64 "X"
# define UINT64_C(x) x ## U
#elif UINT_MAX == UINT64_MAX
typedef unsigned int uint64_t;
# define PRIu64 "u"
# define PRIo64 "o"
# define PRIx64 "x"
# define PRIX64 "X"
# define UINT64_C(x) x ## U
#elif ULONG_MAX == UINT64_MAX
typedef unsigned long uint64_t;
# define PRIu64 "lu"
# define PRIo64 "lo"
# define PRIx64 "lx"
# define PRIX64 "lX"
# define UINT64_C(x) x ## UL
#elif defined(_MSC_VER)
typedef unsigned __int64 uint64_t;
# define PRIu64 "I64u"
# define PRIo64 "I64o"
# define PRIx64 "I64x"
# define PRIX64 "I64X"
# define UINT64_C(x) x ## Ui64
#elif ULLONG_MAX == UINT64_MAX
/* C99 */
typedef unsigned long long uint64_t;
# define PRIu64 "llu"
# define PRIo64 "llo"
# define PRIx64 "llx"
# define PRIX64 "llX"
# define UINT64_C(x) x ## ULL
#else
# error "no unsigned 64-bit type"
#endif

#if SCHAR_MIN == INT8_MIN && SCHAR_MAX == INT8_MAX
typedef signed char int8_t;
# define PRId8 "d"
# define INT8_C(x) x
#else
# error "no signed 8-bit type"
#endif

#if SCHAR_MIN == INT16_MIN && SCHAR_MAX == INT16_MAX
typedef signed char int16_t;
# define PRId16 "d"
# define INT16_C(x) x
#elif SHRT_MIN == INT16_MIN && SHRT_MAX == INT16_MAX
typedef signed short int16_t;
# define PRId16 "d"
# define INT16_C(x) x
#elif INT_MIN == INT16_MIN && INT_MAX == INT16_MAX
typedef signed int int16_t;
# define PRId16 "d"
# define INT16_C(x) x
#else
# error "no signed 16-bit type"
#endif

#if SCHAR_MIN == INT32_MIN && SCHAR_MAX == INT32_MAX
typedef signed char int32_t;
# define PRId32 "d"
# define INT32_C(x) x
#elif SHRT_MIN == INT32_MIN && SHRT_MAX == INT32_MAX
typedef signed short int32_t;
# define PRId32 "d"
# define INT32_C(x) x
#elif INT_MIN == INT32_MIN && INT_MAX == INT32_MAX
typedef signed int int32_t;
# define PRId32 "d"
# define INT32_C(x) x
#elif LONG_MIN == INT32_MIN && LONG_MAX == INT32_MAX
typedef signed long int32_t;
# define PRId32 "ld"
# define INT32_C(x) x ## L
#else
# error "no signed 32-bit type"
#endif

#if SCHAR_MIN == INT64_MIN && SCHAR_MAX == INT64_MAX
typedef signed char int64_t;
# define PRId64 "d"
# define INT64_C(x) x
#elif SHRT_MIN == INT64_MIN && SHRT_MAX == INT64_MAX
typedef signed short int64_t;
# define PRId64 "d"
# define INT64_C(x) x
#elif INT_MIN == INT64_MIN && INT_MAX == INT64_MAX
typedef signed int int64_t;
# define PRId64 "d"
# define INT64_C(x) x
#elif LONG_MIN == INT64_MIN && LONG_MAX == INT64_MAX
typedef signed long int64_t;
# define PRId64 "ld"
# define INT64_C(x) x ## L
#elif defined(_MSC_VER)
typedef signed __int64 int64_t;
# define PRId64 "I64d"
# define INT64_C(x) x ## i64
#elif LLONG_MIN == INT64_MIN && LLONG_MAX == INT64_MAX
typedef signed long long int64_t;
# define PRId64 "lld"
# define INT64_C(x) x ## LL
#else
# error "no signed 64-bit type"
#endif

#define PRIi8  PRId8
#define PRIi16 PRId16
#define PRIi32 PRId32
#define PRIi64 PRId64

/* -------------------------------------------------------------------------------- */
/* least types */

typedef int8_t int_least8_t;
typedef int16_t int_least16_t;
typedef int32_t int_least32_t;
typedef int64_t int_least64_t;

#define INT_LEAST8_MIN INT8_MIN
#define INT_LEAST8_MAX INT8_MAX
#define INT_LEAST16_MIN INT16_MIN
#define INT_LEAST16_MAX INT16_MAX
#define INT_LEAST32_MIN INT32_MIN
#define INT_LEAST32_MAX INT32_MAX
#define INT_LEAST64_MIN INT64_MIN
#define INT_LEAST64_MAX INT64_MAX

#define PRIdLEAST8  PRId8
#define PRIdLEAST16 PRId16
#define PRIdLEAST32 PRId32
#define PRIdLEAST64 PRId64
#define PRIxLEAST8  PRIx8
#define PRIxLEAST16 PRIx16
#define PRIxLEAST32 PRIx32
#define PRIxLEAST64 PRIx64
#define PRIoLEAST8  PRIo8
#define PRIoLEAST16 PRIo16
#define PRIoLEAST32 PRIo32
#define PRIoLEAST64 PRIo64
#define PRIiLEAST8  PRIi8
#define PRIiLEAST16 PRIi16
#define PRIiLEAST32 PRIi32
#define PRIiLEAST64 PRIi64
#define PRIXLEAST8  PRIX8
#define PRIXLEAST16 PRIX16
#define PRIXLEAST32 PRIX32
#define PRIXLEAST64 PRIX64
#define PRIuLEAST8  PRIu8
#define PRIuLEAST16 PRIu16
#define PRIuLEAST32 PRIu32
#define PRIuLEAST64 PRIu64

/* -------------------------------------------------------------------------------- */
/* fast types */

typedef int8_t int_fast8_t;
typedef int16_t int_fast16_t;
typedef int32_t int_fast32_t;
typedef int64_t int_fast64_t;

#define INT_FAST8_MIN INT8_MIN
#define INT_FAST8_MAX INT8_MAX
#define INT_FAST16_MIN INT16_MIN
#define INT_FAST16_MAX INT16_MAX
#define INT_FAST32_MIN INT32_MIN
#define INT_FAST32_MAX INT32_MAX
#define INT_FAST64_MIN INT64_MIN
#define INT_FAST64_MAX INT64_MAX

#define PRIdFAST8  PRId8
#define PRIdFAST16 PRId16
#define PRIdFAST32 PRId32
#define PRIdFAST64 PRId64
#define PRIxFAST8  PRIx8
#define PRIxFAST16 PRIx16
#define PRIxFAST32 PRIx32
#define PRIxFAST64 PRIx64
#define PRIoFAST8  PRIo8
#define PRIoFAST16 PRIo16
#define PRIoFAST32 PRIo32
#define PRIoFAST64 PRIo64
#define PRIiFAST8  PRIi8
#define PRIiFAST16 PRIi16
#define PRIiFAST32 PRIi32
#define PRIiFAST64 PRIi64
#define PRIXFAST8  PRIX8
#define PRIXFAST16 PRIX16
#define PRIXFAST32 PRIX32
#define PRIXFAST64 PRIX64
#define PRIuFAST8  PRIu8
#define PRIuFAST16 PRIu16
#define PRIuFAST32 PRIu32
#define PRIuFAST64 PRIu64

/* -------------------------------------------------------------------------------- */
/* [u]intmax_t -- this is hardcoded to 64-bit, beware! */

typedef int64_t intmax_t;
typedef uint64_t uintmax_t;

#define INTMAX_MIN INT64_MIN
#define INTMAX_MAX INT64_MAX
#define UINTMAX_MAX UINT64_MAX

#define PRIdMAX PRId64
#define PRIuMAX PRIu64
#define PRIxMAX PRIx64
#define PRIXMAX PRIX64
#define PRIoMAX PRIo64
#define PRIiMAX PRIi64

#define INTMAX_C(x) INT64_C(x)

#endif /* PAPER_STDINT_H_ */

