#ifndef ULIB__TYPE_H
#define ULIB__TYPE_H


/* related from SO for gcc:
* https://stackoverflow.com/questions/1188939/representing-128-bit-numbers-in-c
*******************************************************************************
  typedef unsigned int uint128_t __attribute__((mode(TI)));

   uint64_t x = 0xABCDEF01234568;
   uint64_t y = ~x;

   uint128_t result = ((uint128_t) x * y);

   printf("%016llX * %016llX -> ", x, y);

   uint64_t r1 = (result >> 64);
   uint64_t r2 = result;

   printf("%016llX %016llX\n", r1, r2);
*/


/* Quad_t/U64 first appear in 5.00563 (8175356b44).
    #ifdef Quad_t
      typedef I64TYPE I64;
      typedef U64TYPE U64;
    #endif
 * U64TYPE also first appears in 8175356b44.
 * HAS_QUAD also appears in 5.00563 (de1c261475),
 * but did not take over the typedefs until 6b8eaf9322,
 * (also 5.00563) where U64 type was restricted to core.
 * QUADKIND also first appears in 6b8eaf9322.
 *
 * U64 made available outside core in 5.27.7.
*/
#ifndef U64
#  ifdef U64TYPE
     typedef U64TYPE U64;
#  else
     typedef uint64_t U64;
#  endif
#endif

typedef union {
  struct {
    /* keep this struct first for namespace init */
    U32 time_low;
    U16 time_mid;
    U16 time_high_and_version;
    U16 clock_seq_and_variant;
    U8  node[6];
  } v1;
  struct {
    U64 low;
    U64 high;
  } v0;
  struct {
    U32 md5_high32;
    U16 md5_high16;
    U16 md5_mid_and_version;
    U32 md5_low_and_variant;
    U32 md5_low;
  } v3;
  struct {
    U32 rand_a;
    U32 rand_b_and_version;
    U32 rand_c_and_variant;
    U32 rand_d;
  } v4;
  struct {
    U32 sha1_high32;
    U16 sha1_high16;
    U16 sha1_mid_and_version;
    U32 sha1_low_and_variant;
    U32 sha1_low;
  } v5;
  struct {
    U32 time_high;
    U16 time_mid;
    U16 time_low_and_version;
    U16 clock_seq_and_variant;
    U8  node[6];
  } v6;
  struct {
    U32 time_high;
    U16 time_low;
    U16 rand_a_and_version;
    U64 rand_b_and_variant;
  } v7;
  U64 __align;
} struct_uu_t;

typedef unsigned char UCHAR;

#define CC_STATESZ  16           /* words: 4 constant, 8 key, 2 counter, 2 nonce */
#define CC_KEYSZ    40           /* bytes of user supplied key+nonce */
#define CC_CORESZ   64           /* bytes output by core */
#define CC_BUFSZ    16*CC_CORESZ /* bytes we get at a time (1024) */
#define CC_ROUNDS   20

typedef struct {
  U32    state[CC_STATESZ];
  UCHAR  buf[CC_BUFSZ];
  U16    have;
  U64    __align;
} chacha_t;

/* not including trailing null */
#define MAX_PERSIST_LEN  512 - sizeof(UV) - 1

typedef struct {
  STRLEN  len; /* not including trailing null, so <= MAX_PERSIST_LEN */
  UCHAR   path[MAX_PERSIST_LEN];
} persist_t;

#if defined(HAVE_SRWLOCK)
typedef SRWLOCK              uu_lock_t;
#elif defined(USE_WIN32_ALIEN) || defined(USE_WIN32_NATIVE)
typedef CRITICAL_SECTION     uu_lock_t;
#elif defined(__OpenBSD__)
typedef uint32_t             uu_lock_t;
#elif defined(__APPLE__) && defined(HAVE_DISPATCH_DISPATCH_H)
typedef dispatch_semaphore_t uu_lock_t;
#else
typedef sem_t                uu_lock_t;
#endif

typedef struct {
  uu_lock_t         LOCK;               /*   ?? bytes (on Linux) */
  U8                __pad0[64 - sizeof(uu_lock_t)];
  /* -- 64 bytes -- */
  struct timeval    clock_last;         /*   16 bytes (8 on win32) */
  U64               clock_prev_reg;     /*    8 bytes */
  U64               clock_defer_100ns;  /*    8 bytes */
  I16               clock_adj;          /*    2 bytes */
  U16               clock_seq;          /*    2 bytes */
  U8                __pad1[64 - 20 - sizeof(struct timeval)];
  /* -- 64 bytes -- */
  U64               gen_epoch;          /*    8 bytes */
  U8                gen_node[6];        /*    6 bytes */  /* need 64bit align */
  U16               gen_has_real_node;  /*    2 bytes */
  U8                gen_real_node[6];   /*    6 bytes */  /* need 64bit align */
  U16               gen_use_unique;     /*    2 bytes */
  U8                __pad2[64 - 24];
  /* -- 64 bytes -- */
  chacha_t          cc;                 /* 1104 bytes */  /* aligned 64bit */
  U64               xo_s[4];            /*   32 bytes */
  U64               sm_x;               /*    8 bytes */
  U8                __pad3[1152 - 40 - sizeof(chacha_t)];
  /* -- 18 * 64 bytes -- */
  persist_t         clock_persist;      /*  512 bytes */
  /* -- 8 * 64 bytes -- */
} shared_mem_t;

/* this should be aligned at least 4 bytes, better yet 16 */
typedef U8 uu_t[16];

typedef struct {
  shared_mem_t      *shared;            /*    8 bytes */
  IV                shared_len;         /*    8 bytes */
  FILE              *clock_state_f;     /*    8 bytes */
  I32               clock_state_fd;     /*    4 bytes */
  U8                __pad5[64 - 4 - sizeof(shared_mem_t*) - sizeof(IV) - sizeof(FILE*)];
  /* -- 64 bytes -- */
  persist_t         clock_persist;      /*  512 bytes */
  /* -- 8 * 64 bytes -- */
} my_cxt_t;
/*} __attribute__ ((aligned (64))) my_cxt_t;
*
*   Align 64 bytes vs 8.
*
*   It's a toss-up as to whether this helps -- faster some places,
*   slower others.
*/

/* no *UCXT_ variants, just use a comma */
#define pUCXT pTHX_ my_cxt_t *my_cxtp
#define aUCXT aTHX_ my_cxtp
#define UCXT  (*my_cxtp)

#define SMEM UCXT.shared

#endif
/* ex:set ts=2 sw=2 itab=spaces: */
