root/crypto/libecc/include/libecc/words/types.h
/*
 *  Copyright (C) 2017 - This file is part of libecc project
 *
 *  Authors:
 *      Ryad BENADJILA <ryadbenadjila@gmail.com>
 *      Arnaud EBALARD <arnaud.ebalard@ssi.gouv.fr>
 *      Jean-Pierre FLORI <jean-pierre.flori@ssi.gouv.fr>
 *
 *  Contributors:
 *      Nicolas VIVET <nicolas.vivet@ssi.gouv.fr>
 *      Karim KHALFALLAH <karim.khalfallah@ssi.gouv.fr>
 *
 *  This software is licensed under a dual BSD and GPL v2 license.
 *  See LICENSE file at the root folder of the project.
 */
#ifndef __TYPES_H__
#define __TYPES_H__

/*** Handling the target compiler and its specificities ***/
#ifdef __GNUC__
/* gcc and clang */
#define ATTRIBUTE_UNUSED __attribute__((unused))
#define ATTRIBUTE_USED __attribute__((used))
#define ATTRIBUTE_PACKED __attribute__((packed))
#define ATTRIBUTE_SECTION(a) __attribute__((__section__((a))))
#ifdef USE_WARN_UNUSED_RET
  #define ATTRIBUTE_WARN_UNUSED_RET __attribute__((warn_unused_result))
  static inline void ignore_result(int unused_result) {
    (void) unused_result;
  }
  /* NOTE: this trick using a dummy function call is here
   * to explicitly avoid "unused return values" when we know
   * what we are doing!
   */
  #define IGNORE_RET_VAL(a) ignore_result((int)(a))
#else
  #define ATTRIBUTE_WARN_UNUSED_RET
  #define IGNORE_RET_VAL(a) (a)
#endif /* USE_WARN_UNUSED_RET */
#else
#define ATTRIBUTE_UNUSED
#define ATTRIBUTE_USED
#define ATTRIBUTE_PACKED
#define ATTRIBUTE_SECTION(a)
#define ATTRIBUTE_WARN_UNUSED_RET
#define IGNORE_RET_VAL(a) (a)
#endif

/* Macro to trick the compiler of thinking a variable is used.
 * Although this should not happen, sometimes because of #define
 * oddities we might force this.
 */
#define FORCE_USED_VAR(a) ((void)(a))

/*** Handling the types ****/
#ifdef WITH_STDLIB

/*
 * User explicitly needs to build w/ stdlib. Let's include the headers
 * we need to get basic types: (uint*_t), NULL, etc. You can see below
 * (i.e. under #else) what is precisely needed.
 */
#include <stdint.h>
#include <inttypes.h>
#include <stddef.h>
#else /* WITH_STDLIB */

/*
 * User does not want to build w/ stdlib. Let's define basic types:
 * (uint*_t), NULL, etc.
 */
#define NULL ((void *)0)
typedef unsigned int size_t;
typedef int ssize_t;
/* Here is the big picture of the main programming models
 * and their primitive types sizes in bits:
 * (see http://www.unix.org/whitepapers/64bit.html)
 *
 *           | LP32 | ILP32 | LLP64 | ILP64 | LP64 |
 *           ---------------------------------------
 *  char     |   8  |   8   |   8   |   8   |   8  |
 *  short    |  16  |  16   |  16   |  16   |  16  |
 *  int      |  16  |  32   |  32   |  64   |  32  |
 *  long     |  32  |  32   |  32   |  64   |  64  |
 *  long long|  64  |  64   |  64   |  64   |  64  |
 *           --------------------------------------
 *           (long long type existence depends on the C compiler
 *            but should be *mandatory* in C99 compliant ones)
 *
 * This means that:
 *      1) We are sure that long long are 64-bit, short are 16-bit,
 *         and char are 8-bit (on the vast majority of platforms).
 *      2) The two types that are not consistent across platforms are
 *      the int and long types (e.g., int is 16 bits and long is 32 bits
 *      on msp430-gcc LP32, while int is 32 bits and long is 32 bits
 *      on x86_64 Linux platforms LLP64, and long becomes 64 bits on
 *      x86_64 Windows platforms LP64).
 *
 * Hence, we take a wild guess for uint32_t mapping on a primitive type
 * and check this at compilation time using the check_data_types 'union'
 * defined below.
 * Our guess depends on the WORDSIZE the user provides us, since we assume
 * this corresponds to a 'native' word size of the platform (8-bit platforms
 * such as AVR and 16-bit platforms such as MSP430 are either LP32 and ILP32,
 * which means that a 'long' type will be mapped to a 32 bits native type).
 * Consequently, when the user provides us a WORDSIZE=16, we infer that
 * uint32_t is mapped on a long type.
 *
 */
typedef unsigned long long uint64_t;
/* GCC defines the size of an int, use this information
 * if it is provided
 */
#ifdef __SIZEOF_INT__
#if(__SIZEOF_INT__ == 2)
#define UINT32_IS_LONG /* This will be useful for print formatting */
typedef unsigned long uint32_t;
#else
typedef unsigned int uint32_t;
#endif /* (__SIZEOF_INT__ == 2) */
#else
#if(WORDSIZE == 16)
/* The user has provided WORDSIZE=16, so we guess that
 * we have LP32 or ILP32: a long type would be 32-bit.
 */
#define UINT32_IS_LONG /* This will be useful for print formatting */
typedef unsigned long uint32_t;
#else
/* Wild guess for uint32_t mapping */
typedef unsigned int uint32_t;
#endif /* (WORDSIZE == 16) */
#endif /* __SIZEOF_INT__ */
typedef unsigned short uint16_t;
typedef unsigned char uint8_t;
/* Useful macros for our new defined types */
#define UINT8_MAX  (0xff)
#define UINT16_MAX (0xffff)
#define UINT32_MAX (0xffffffff)
#define UINT64_MAX (0xffffffffffffffffULL)
#define UINT8_C(c) ((uint8_t)(c ## UL))
#define UINT16_C(c) ((uint16_t)(c ## UL))
#define UINT32_C(c) ((uint32_t)(c ## UL))
#define UINT64_C(c) (c ## ULL)

/* Sanity check on our guess for primitive types sizes.
 * See https://barrgroup.com/Embedded-Systems/How-To/C-Fixed-Width-Integers-C99
 *
 * TODO: if you get a compilation error at this point, this means that we failed
 * at guessing the C primitive types sizes for the current platform. You should
 * try to adapt the uint8_t/uint16_t/uint32_t/uint64_t types definitions in this
 * file, or find the C99 compliant stdint headers for your compiler/platform
 * and include it.
 */
typedef union {
        char uint8_t_incorrect[sizeof(uint8_t) == 1 ? 1 : -1];
        char uint16_t_incorrect[sizeof(uint16_t) == 2 ? 1 : -1];
        char uint32_t_incorrect[sizeof(uint32_t) == 4 ? 1 : -1];
        char uint64_t_incorrect[sizeof(uint64_t) == 8 ? 1 : -1];
} check_data_types;

#endif /* WITH_STDLIB */

#endif /* __TYPES_H__ */