/*
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 2002,2005,2006,2007,2008,2009  Free Software Foundation, Inc.
 *
 *  GRUB is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  GRUB is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef GRUB_TYPES_HEADER
#define GRUB_TYPES_HEADER	1

#include <config.h>
#include <stdbool.h>
#ifndef GRUB_UTIL
#include <grub/cpu/types.h>
#endif

#ifdef __MINGW32__
#define GRUB_PACKED __attribute__ ((packed,gcc_struct))
#else
#define GRUB_PACKED __attribute__ ((packed))
#endif

#ifdef GRUB_BUILD
# define GRUB_CPU_SIZEOF_VOID_P	BUILD_SIZEOF_VOID_P
# define GRUB_CPU_SIZEOF_LONG	BUILD_SIZEOF_LONG
# if BUILD_WORDS_BIGENDIAN
#  define GRUB_CPU_WORDS_BIGENDIAN	1
# else
#  undef GRUB_CPU_WORDS_BIGENDIAN
# endif
#elif defined (GRUB_UTIL)
# define GRUB_CPU_SIZEOF_VOID_P	SIZEOF_VOID_P
# define GRUB_CPU_SIZEOF_LONG	SIZEOF_LONG
# ifdef WORDS_BIGENDIAN
#  define GRUB_CPU_WORDS_BIGENDIAN	1
# else
#  undef GRUB_CPU_WORDS_BIGENDIAN
# endif
#else /* ! GRUB_UTIL */
# define GRUB_CPU_SIZEOF_VOID_P	GRUB_TARGET_SIZEOF_VOID_P
# define GRUB_CPU_SIZEOF_LONG	GRUB_TARGET_SIZEOF_LONG
# ifdef GRUB_TARGET_WORDS_BIGENDIAN
#  define GRUB_CPU_WORDS_BIGENDIAN	1
# else
#  undef GRUB_CPU_WORDS_BIGENDIAN
# endif
#endif /* ! GRUB_UTIL */

#if GRUB_CPU_SIZEOF_VOID_P != 4 && GRUB_CPU_SIZEOF_VOID_P != 8
# error "This architecture is not supported because sizeof(void *) != 4 and sizeof(void *) != 8"
#endif

#if GRUB_CPU_SIZEOF_LONG != 4 && GRUB_CPU_SIZEOF_LONG != 8
# error "This architecture is not supported because sizeof(long) != 4 and sizeof(long) != 8"
#endif

#if !defined (GRUB_UTIL) && !defined (GRUB_TARGET_WORDSIZE)
# if GRUB_TARGET_SIZEOF_VOID_P == 4
#  define GRUB_TARGET_WORDSIZE 32
# elif GRUB_TARGET_SIZEOF_VOID_P == 8
#  define GRUB_TARGET_WORDSIZE 64
# endif
#endif

#ifndef __CHAR_BIT__
# error __CHAR_BIT__ is not defined
#elif __CHAR_BIT__ != 8
# error __CHAR_BIT__ is not equal 8
#else
# define GRUB_CHAR_BIT	__CHAR_BIT__
#endif

#define GRUB_TYPE_BITS(type) (sizeof(type) * GRUB_CHAR_BIT)

/* Define various wide integers.  */
typedef signed char		grub_int8_t;
typedef short			grub_int16_t;
typedef int			grub_int32_t;
# define PRIxGRUB_INT32_T	"x"
# define PRIdGRUB_INT32_T	"d"
#if GRUB_CPU_SIZEOF_LONG == 8
typedef long			grub_int64_t;
# define PRIxGRUB_INT64_T	"lx"
# define PRIdGRUB_INT64_T	"ld"
#else
typedef long long		grub_int64_t;
# define PRIxGRUB_INT64_T	"llx"
# define PRIdGRUB_INT64_T	"lld"
#endif

typedef unsigned char		grub_uint8_t;
typedef unsigned short		grub_uint16_t;
typedef unsigned		grub_uint32_t;
# define PRIxGRUB_UINT32_T	"x"
# define PRIuGRUB_UINT32_T	"u"
#if GRUB_CPU_SIZEOF_LONG == 8
typedef unsigned long		grub_uint64_t;
# define PRIxGRUB_UINT64_T	"lx"
# define PRIuGRUB_UINT64_T	"lu"
#else
typedef unsigned long long	grub_uint64_t;
# define PRIxGRUB_UINT64_T	"llx"
# define PRIuGRUB_UINT64_T	"llu"
#endif

/* Misc types.  */

#if GRUB_CPU_SIZEOF_VOID_P == 8
typedef grub_uint64_t	grub_addr_t;
typedef grub_uint64_t	grub_size_t;
typedef grub_int64_t	grub_ssize_t;

# define GRUB_SIZE_MAX 18446744073709551615UL
# define GRUB_SSIZE_MAX 9223372036854775807L

# if GRUB_CPU_SIZEOF_LONG == 8
#  define PRIxGRUB_SIZE	 "lx"
#  define PRIxGRUB_ADDR	 "lx"
#  define PRIuGRUB_SIZE	 "lu"
#  define PRIdGRUB_SSIZE "ld"
# else
#  define PRIxGRUB_SIZE	 "llx"
#  define PRIxGRUB_ADDR	 "llx"
#  define PRIuGRUB_SIZE  "llu"
#  define PRIdGRUB_SSIZE "lld"
# endif
#else
typedef grub_uint32_t	grub_addr_t;
typedef grub_uint32_t	grub_size_t;
typedef grub_int32_t	grub_ssize_t;

# define GRUB_SIZE_MAX 4294967295UL
# define GRUB_SSIZE_MAX 2147483647L

# define PRIxGRUB_SIZE	"x"
# define PRIxGRUB_ADDR	"x"
# define PRIuGRUB_SIZE	"u"
# define PRIdGRUB_SSIZE	"d"
#endif

#define GRUB_SCHAR_MAX 127
#define GRUB_SCHAR_MIN (-GRUB_SCHAR_MAX - 1)
#define GRUB_UCHAR_MAX 0xFF
#define GRUB_USHRT_MAX 65535
#define GRUB_SHRT_MAX 0x7fff
#define GRUB_SHRT_MIN (-GRUB_SHRT_MAX - 1)
#define GRUB_UINT_MAX 4294967295U
#define GRUB_INT_MAX 0x7fffffff
#define GRUB_INT_MIN (-GRUB_INT_MAX - 1)
#define GRUB_INT32_MAX 2147483647
#define GRUB_INT32_MIN (-GRUB_INT32_MAX - 1)

#if GRUB_CPU_SIZEOF_LONG == 8
# define GRUB_ULONG_MAX 18446744073709551615UL
# define GRUB_LONG_MAX 9223372036854775807L
#else
# define GRUB_ULONG_MAX 4294967295UL
# define GRUB_LONG_MAX 2147483647L
#endif
# define GRUB_LONG_MIN (-GRUB_LONG_MAX - 1)

/*
 * Cast to unsigned long long so that the "return value" is always a consistent
 * type and to ensure an unsigned value regardless of type parameter.
 */
#define GRUB_TYPE_U_MAX(type) ((unsigned long long)((typeof (type))(~0)))
#define GRUB_TYPE_U_MIN(type) 0ULL

typedef grub_uint64_t grub_properly_aligned_t;

#define GRUB_PROPERLY_ALIGNED_ARRAY(name, size) grub_properly_aligned_t name[((size) + sizeof (grub_properly_aligned_t) - 1) / sizeof (grub_properly_aligned_t)]

/* The type for representing a file offset.  */
typedef grub_uint64_t		grub_off_t;
#define PRIxGRUB_OFFSET		PRIxGRUB_UINT64_T
#define PRIuGRUB_OFFSET		PRIuGRUB_UINT64_T

/* The type for representing a disk block address.  */
typedef grub_uint64_t		grub_disk_addr_t;
#define PRIxGRUB_DISK_ADDR	PRIxGRUB_UINT64_T

/* Byte-orders.  */
static inline grub_uint16_t grub_swap_bytes16(grub_uint16_t _x)
{
   return (grub_uint16_t) ((_x << 8) | (_x >> 8));
}

#define grub_swap_bytes16_compile_time(x) ((((x) & 0xff) << 8) | (((x) & 0xff00) >> 8))
#define grub_swap_bytes32_compile_time(x) ((((x) & 0xff) << 24) | (((x) & 0xff00) << 8) | (((x) & 0xff0000) >> 8) | (((x) & 0xff000000UL) >> 24))
#define grub_swap_bytes64_compile_time(x)	\
({ \
   grub_uint64_t _x = (x); \
   (grub_uint64_t) ((_x << 56) \
                    | ((_x & (grub_uint64_t) 0xFF00ULL) << 40) \
                    | ((_x & (grub_uint64_t) 0xFF0000ULL) << 24) \
                    | ((_x & (grub_uint64_t) 0xFF000000ULL) << 8) \
                    | ((_x & (grub_uint64_t) 0xFF00000000ULL) >> 8) \
                    | ((_x & (grub_uint64_t) 0xFF0000000000ULL) >> 24) \
                    | ((_x & (grub_uint64_t) 0xFF000000000000ULL) >> 40) \
                    | (_x >> 56)); \
})

#if (defined(__GNUC__) && (__GNUC__ > 3) && (__GNUC__ > 4 || __GNUC_MINOR__ >= 3)) || defined(__clang__)
static inline grub_uint32_t grub_swap_bytes32(grub_uint32_t x)
{
	return __builtin_bswap32(x);
}

static inline grub_uint64_t grub_swap_bytes64(grub_uint64_t x)
{
	return __builtin_bswap64(x);
}
#else					/* not gcc 4.3 or newer */
static inline grub_uint32_t grub_swap_bytes32(grub_uint32_t _x)
{
   return ((_x << 24)
	   | ((_x & (grub_uint32_t) 0xFF00UL) << 8)
	   | ((_x & (grub_uint32_t) 0xFF0000UL) >> 8)
	   | (_x >> 24));
}

static inline grub_uint64_t grub_swap_bytes64(grub_uint64_t _x)
{
   return ((_x << 56)
	   | ((_x & (grub_uint64_t) 0xFF00ULL) << 40)
	   | ((_x & (grub_uint64_t) 0xFF0000ULL) << 24)
	   | ((_x & (grub_uint64_t) 0xFF000000ULL) << 8)
	   | ((_x & (grub_uint64_t) 0xFF00000000ULL) >> 8)
	   | ((_x & (grub_uint64_t) 0xFF0000000000ULL) >> 24)
	   | ((_x & (grub_uint64_t) 0xFF000000000000ULL) >> 40)
	   | (_x >> 56));
}
#endif					/* not gcc 4.3 or newer */

#ifdef GRUB_CPU_WORDS_BIGENDIAN
# define grub_cpu_to_le16(x)	grub_swap_bytes16(x)
# define grub_cpu_to_le32(x)	grub_swap_bytes32(x)
# define grub_cpu_to_le64(x)	grub_swap_bytes64(x)
# define grub_le_to_cpu16(x)	grub_swap_bytes16(x)
# define grub_le_to_cpu32(x)	grub_swap_bytes32(x)
# define grub_le_to_cpu64(x)	grub_swap_bytes64(x)
# define grub_cpu_to_be16(x)	((grub_uint16_t) (x))
# define grub_cpu_to_be32(x)	((grub_uint32_t) (x))
# define grub_cpu_to_be64(x)	((grub_uint64_t) (x))
# define grub_be_to_cpu16(x)	((grub_uint16_t) (x))
# define grub_be_to_cpu32(x)	((grub_uint32_t) (x))
# define grub_be_to_cpu64(x)	((grub_uint64_t) (x))
# define grub_cpu_to_be16_compile_time(x)	((grub_uint16_t) (x))
# define grub_cpu_to_be32_compile_time(x)	((grub_uint32_t) (x))
# define grub_cpu_to_be64_compile_time(x)	((grub_uint64_t) (x))
# define grub_be_to_cpu64_compile_time(x)	((grub_uint64_t) (x))
# define grub_cpu_to_le32_compile_time(x)	grub_swap_bytes32_compile_time(x)
# define grub_cpu_to_le64_compile_time(x)	grub_swap_bytes64_compile_time(x)
# define grub_cpu_to_le16_compile_time(x)	grub_swap_bytes16_compile_time(x)
#else /* ! WORDS_BIGENDIAN */
# define grub_cpu_to_le16(x)	((grub_uint16_t) (x))
# define grub_cpu_to_le32(x)	((grub_uint32_t) (x))
# define grub_cpu_to_le64(x)	((grub_uint64_t) (x))
# define grub_le_to_cpu16(x)	((grub_uint16_t) (x))
# define grub_le_to_cpu32(x)	((grub_uint32_t) (x))
# define grub_le_to_cpu64(x)	((grub_uint64_t) (x))
# define grub_cpu_to_be16(x)	grub_swap_bytes16(x)
# define grub_cpu_to_be32(x)	grub_swap_bytes32(x)
# define grub_cpu_to_be64(x)	grub_swap_bytes64(x)
# define grub_be_to_cpu16(x)	grub_swap_bytes16(x)
# define grub_be_to_cpu32(x)	grub_swap_bytes32(x)
# define grub_be_to_cpu64(x)	grub_swap_bytes64(x)
# define grub_cpu_to_be16_compile_time(x)	grub_swap_bytes16_compile_time(x)
# define grub_cpu_to_be32_compile_time(x)	grub_swap_bytes32_compile_time(x)
# define grub_cpu_to_be64_compile_time(x)	grub_swap_bytes64_compile_time(x)
# define grub_be_to_cpu64_compile_time(x)	grub_swap_bytes64_compile_time(x)
# define grub_cpu_to_le16_compile_time(x)	((grub_uint16_t) (x))
# define grub_cpu_to_le32_compile_time(x)	((grub_uint32_t) (x))
# define grub_cpu_to_le64_compile_time(x)	((grub_uint64_t) (x))

#endif /* ! WORDS_BIGENDIAN */

struct grub_unaligned_uint16
{
  grub_uint16_t val;
} GRUB_PACKED;
struct grub_unaligned_uint32
{
  grub_uint32_t val;
} GRUB_PACKED;
struct grub_unaligned_uint64
{
  grub_uint64_t val;
} GRUB_PACKED;

typedef struct grub_unaligned_uint16 grub_unaligned_uint16_t;
typedef struct grub_unaligned_uint32 grub_unaligned_uint32_t;
typedef struct grub_unaligned_uint64 grub_unaligned_uint64_t;

static inline grub_uint16_t grub_get_unaligned16 (const void *ptr)
{
  const struct grub_unaligned_uint16 *dd
    = (const struct grub_unaligned_uint16 *) ptr;
  return dd->val;
}

static inline void grub_set_unaligned16 (void *ptr, grub_uint16_t val)
{
  struct grub_unaligned_uint16 *dd = (struct grub_unaligned_uint16 *) ptr;
  dd->val = val;
}

static inline grub_uint32_t grub_get_unaligned32 (const void *ptr)
{
  const struct grub_unaligned_uint32 *dd
    = (const struct grub_unaligned_uint32 *) ptr;
  return dd->val;
}

static inline void grub_set_unaligned32 (void *ptr, grub_uint32_t val)
{
  struct grub_unaligned_uint32 *dd = (struct grub_unaligned_uint32 *) ptr;
  dd->val = val;
}

static inline grub_uint64_t grub_get_unaligned64 (const void *ptr)
{
  const struct grub_unaligned_uint64 *dd
    = (const struct grub_unaligned_uint64 *) ptr;
  return dd->val;
}

static inline void grub_set_unaligned64 (void *ptr, grub_uint64_t val)
{
  struct grub_unaligned_uint64_t
  {
    grub_uint64_t d;
  } GRUB_PACKED;
  struct grub_unaligned_uint64_t *dd = (struct grub_unaligned_uint64_t *) ptr;
  dd->d = val;
}

/*
 * The grub_absolute_pointer() macro borrows the idea from Linux kernel of using
 * RELOC_HIDE() macro to stop GCC from checking the result of pointer arithmetic
 * and also its conversion to be inside the symbol's boundary [1]. The check
 * is sometimes false positive, especially it is controversial to emit the array
 * bounds [-Warray-bounds] warning on all hardwired literal pointers since GCC
 * 11/12 [2]. Unless a good solution can be settled, for the time being we
 * would be in favor of the macro instead of GCC pragmas which cannot match the
 * places the warning needs to be ignored in an exact way.
 *
 * [1] https://lists.linuxcoding.com/kernel/2006-q3/msg17979.html
 * [2] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99578
 */
#if defined(__GNUC__)
# define grub_absolute_pointer(val)					\
({									\
	grub_addr_t __ptr;						\
	asm ("" : "=r" (__ptr) : "0" ((void *) (val)));			\
	(void *) (__ptr);						\
})
#else
# define grub_absolute_pointer(val) ((void *) (val))
#endif

struct grub_guid
{
  grub_uint32_t data1;
  grub_uint16_t data2;
  grub_uint16_t data3;
  grub_uint8_t data4[8];
} __attribute__ ((aligned(4)));
typedef struct grub_guid grub_guid_t;

struct grub_packed_guid
{
  grub_uint32_t data1;
  grub_uint16_t data2;
  grub_uint16_t data3;
  grub_uint8_t data4[8];
} GRUB_PACKED;
typedef struct grub_packed_guid grub_packed_guid_t;

#endif /* ! GRUB_TYPES_HEADER */
