/*
 * M*LIB - SHARED Pointer Module
 *
 * Copyright (c) 2017-2023, Patrick Pelissier
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * + Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * + 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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 MSTARLIB_SHARED_PTR_H
#define MSTARLIB_SHARED_PTR_H

#include "m-core.h"
#include "m-atomic.h"
#include "m-genint.h"

M_BEGIN_PROTECTED_CODE

/* Define shared pointer and its function.
   USAGE: SHARED_PTR_DEF(name, type, [, oplist]) */
#define M_SHARED_PTR_DEF(name, ...)                                           \
  M_SHARED_PTR_DEF_AS(name, M_F(name,_t), __VA_ARGS__)


/* Define shared pointer and its function
   as the given name name_t
   USAGE: SHARED_PTR_DEF_AS(name, name_t, type, [, oplist]) */
#define M_SHARED_PTR_DEF_AS(name, name_t, ...)                                \
  M_BEGIN_PROTECTED_CODE                                                      \
  M_SHAR3D_PTR_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__)                             \
                     ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_SHAR3D_ATOMIC_OPLIST, name_t ), \
                      (name, __VA_ARGS__ ,                                       M_SHAR3D_ATOMIC_OPLIST, name_t ))) \
  M_END_PROTECTED_CODE


/* Define the oplist of a shared pointer.
   USAGE: SHARED_OPLIST(name [, oplist_of_the_type]) */
#define M_SHARED_PTR_OPLIST(...)                                              \
  M_SHAR3D_PTR_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__)                          \
                        ((__VA_ARGS__, M_BASIC_OPLIST ),                      \
                         (__VA_ARGS__ )))


/* Define relaxed shared pointer and its function (thread unsafe).
   USAGE: SHARED_PTR_RELAXED_DEF(name, type, [, oplist]) */
#define M_SHARED_PTR_RELAXED_DEF(name, ...)                                   \
  M_SHARED_PTR_RELAXED_DEF_AS(name, M_F(name,_t), __VA_ARGS__)


/* Define relaxed shared pointer and its function (thread unsafe)
   as the given name name_t
   USAGE: SHARED_PTR_RELAXED_DEF(name, type, [, oplist]) */
#define M_SHARED_PTR_RELAXED_DEF_AS(name, name_t, ...)                        \
  M_BEGIN_PROTECTED_CODE                                                      \
  M_SHAR3D_PTR_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__)                             \
                     ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), M_SHAR3D_INTEGER_OPLIST, name_t ), \
                      (name, __VA_ARGS__,                                        M_SHAR3D_INTEGER_OPLIST, name_t ))) \
  M_END_PROTECTED_CODE


/* Define shared resource and its function.
   This is a bounded pool of resource shared by multiple owners.
   USAGE: SHARED_RESOURCE_DEF(name, type, [, oplist]) */
#define M_SHARED_RESOURCE_DEF(name, ...)                                      \
  M_SHARED_RESOURCE_DEF_AS(name, M_F(name,_t), M_F(name,_it_t), __VA_ARGS__)


/* Define shared resource and its function
   as the given name named_t and the iterator it_t
   This is a bounded pool of resource shared by multiple owners.
   USAGE: SHARED_RESOURCE_DEF_AS(name, name_t, it_t, type, [, oplist]) */
#define M_SHARED_RESOURCE_DEF_AS(name, name_t, it_t, ...)                     \
  M_BEGIN_PROTECTED_CODE                                                      \
  M_SHAR3D_RESOURCE_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__)                        \
                          ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t, it_t ), \
                           (name, __VA_ARGS__,                                        name_t, it_t ))) \
  M_END_PROTECTED_CODE



/*****************************************************************************/
/********************************** INTERNAL *********************************/
/*****************************************************************************/

// deferred evaluation
#define M_SHAR3D_PTR_OPLIST_P1(arg) M_SHAR3D_PTR_OPLIST_P2 arg

/* Validation of the given, shared_t oplist */
#define M_SHAR3D_PTR_OPLIST_P2(name, oplist)                                  \
  M_IF_OPLIST(oplist)(M_SHAR3D_PTR_OPLIST_P3, M_SHAR3D_PTR_OPLIST_FAILURE)(name, oplist)

/* Prepare a clean compilation failure */
#define M_SHAR3D_PTR_OPLIST_FAILURE(name, oplist)                             \
  ((M_LIB_ERROR(ARGUMENT_OF_SHARED_PTR_OPLIST_IS_NOT_AN_OPLIST, name, oplist)))

#define M_SHAR3D_PTR_OPLIST_P3(name, oplist) (                                \
  INIT(M_F(name, _init)),                                                     \
  CLEAR(M_F(name, _clear)),                                                   \
  INIT_SET(M_F(name, _init_set)),                                             \
  SET(M_F(name, _set))                                                        \
  INIT_MOVE(M_F(name, _init_move)),                                           \
  RESET(M_F(name, _reset)),                                                   \
  MOVE(M_F(name, _move)),                                                     \
  SWAP(M_F(name, _swap))                                                      \
  ,NAME(name)                                                                 \
  ,TYPE(M_F(name, _ct))                                                       \
  )

// OPLIST to handle a counter of atomic type
#define M_SHAR3D_ATOMIC_OPLIST (TYPE(atomic_int),                             \
                               INIT_SET(atomic_init),                         \
                               ADD(atomic_fetch_add),                         \
                               SUB(atomic_fetch_sub),                         \
                               IT_CREF(atomic_load))

// OPLIST to handle a counter of non-atomic type
#define M_SHAR3D_INTEGER_OPLIST (TYPE(int),                                   \
                                INIT_SET(m_shar3d_integer_init_set),          \
                                ADD(m_shar3d_integer_add),                    \
                                SUB(m_shar3d_integer_sub),                    \
                                IT_CREF(m_shar3d_integer_cref))

/* Atomic like interface for basic integers */
M_INLINE void m_shar3d_integer_init_set(int *p, int val) { *p = val; }
M_INLINE int m_shar3d_integer_add(int *p, int val) { int r = *p;  *p += val; return r; }
M_INLINE int m_shar3d_integer_sub(int *p, int val) { int r = *p;  *p -= val; return r; }
M_INLINE int m_shar3d_integer_cref(int *p) { return *p; }


/********************************** INTERNAL *********************************/

/* Contract of a shared pointer */
#define M_SHAR3D_CONTRACT(shared, cpt_oplist) do {                            \
    M_ASSERT(shared != NULL);                                                 \
    M_ASSERT(*shared == NULL || M_CALL_IT_CREF(cpt_oplist, &(*shared)->cpt) >= 1); \
  } while (0)

// deferred evaluation
#define M_SHAR3D_PTR_DEF_P1(arg) M_ID( M_SHAR3D_PTR_DEF_P2 arg )

/* Validate the oplist before going further */
#define M_SHAR3D_PTR_DEF_P2(name, type, oplist, cpt_oplist, shared_t)         \
  M_IF_OPLIST(oplist)(M_SHAR3D_PTR_DEF_P3, M_SHAR3D_PTR_DEF_FAILURE)(name, type, oplist, cpt_oplist, shared_t)

/* Stop processing with a compilation failure */
#define M_SHAR3D_PTR_DEF_FAILURE(name, type, oplist, cpt_oplist, shared_t)    \
  M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(SHARED_PTR_DEF): the given argument is not a valid oplist: " #oplist)

/* Code generation */
#define M_SHAR3D_PTR_DEF_P3(name, type, oplist, cpt_oplist, shared_t)         \
  M_SHAR3D_PTR_DEF_TYPE(name, type, oplist, cpt_oplist, shared_t)             \
  M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist)                            \
  M_SHAR3D_PTR_DEF_CORE(name, type, oplist, cpt_oplist, shared_t)             \
  M_EMPLACE_QUEUE_DEF(name, cpt_oplist, M_F(name, _init_with), oplist, M_SHAR3D_PTR_DEF_EMPLACE)

/* Define the types */
#define M_SHAR3D_PTR_DEF_TYPE(name, type, oplist, cpt_oplist, shared_t)       \
                                                                              \
  typedef struct M_F(name, _s){                                               \
    type *data;                        /* Pointer to the data */              \
    M_GET_TYPE cpt_oplist  cpt; /* Counter of how many refs the data */       \
    bool  combineAlloc; /* Does the data and the ptr share the slot? */       \
  } *shared_t[1];                                                             \
  typedef struct M_F(name, _s) *M_F(name, _ptr);                              \
  typedef const struct M_F(name, _s) *M_F(name, _srcptr);                     \
                                                                              \
  /* Internal type for oplist */                                              \
  typedef shared_t M_F(name, _ct);                                            \
  typedef type     M_F(name, _subtype_ct);                                    \
                                                                              \
  typedef struct M_F(name, _combine_s) {                                      \
    struct M_F(name, _s) ptr;                                                 \
    type data;                                                                \
  } M_F(name, combine_ct)[1];                                                 \

/* Define the core functions */
#define M_SHAR3D_PTR_DEF_CORE(name, type, oplist, cpt_oplist, shared_t)       \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _init)(shared_t shared)                                           \
  {                                                                           \
    *shared = NULL;                                                           \
  }                                                                           \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _init2)(shared_t shared, type *data)                              \
  {                                                                           \
    M_ASSERT (shared != NULL);                                                \
    /* The shared ptr get exclusive access to data */                         \
    struct M_F(name, _s) *ptr;                                                \
    if (M_UNLIKELY (data == NULL)) {                                          \
      *shared = NULL;                                                         \
      return;                                                                 \
    }                                                                         \
    ptr = M_CALL_NEW(oplist, struct M_F(name, _s));                           \
    if (M_UNLIKELY_NOMEM (ptr == NULL)) {                                     \
      M_MEMORY_FULL(sizeof(struct M_F(name, _s)));                            \
      return;                                                                 \
    }                                                                         \
    ptr->data = data;                                                         \
    M_CALL_INIT_SET(cpt_oplist, &ptr->cpt, 1);                                \
    ptr->combineAlloc = false;                                                \
    *shared = ptr;                                                            \
    M_SHAR3D_CONTRACT(shared, cpt_oplist);                                    \
  }                                                                           \
                                                                              \
  M_IF_METHOD(INIT, oplist)(                                                  \
  M_INLINE void                                                               \
  M_F(name, _init_new)(shared_t shared)                                       \
  {                                                                           \
    /* NOTE: Alloc 1 struct with both structures. */                          \
    struct M_F(name, _combine_s) *p =                                         \
      M_CALL_NEW(oplist, struct M_F(name, _combine_s));                       \
    if (M_UNLIKELY_NOMEM (p == NULL)) {                                       \
      M_MEMORY_FULL(sizeof(struct M_F(name, _combine_s)));                    \
      return;                                                                 \
    }                                                                         \
    struct M_F(name, _s) *ptr = &p->ptr;                                      \
    ptr->combineAlloc = true;                                                 \
    type *data = &p->data;                                                    \
    M_CALL_INIT( oplist, *data);                                              \
    ptr->data = data;                                                         \
    M_CALL_INIT_SET(cpt_oplist, &ptr->cpt, 1);                                \
    *shared = ptr;                                                            \
    M_SHAR3D_CONTRACT(shared, cpt_oplist);                                    \
  }                                                                           \
  , /* No INIT */ )                                                           \
                                                                              \
  M_INLINE bool                                                               \
  M_F(name, _NULL_p)(const shared_t shared)                                   \
  {                                                                           \
    M_SHAR3D_CONTRACT(shared, cpt_oplist);                                    \
    return *shared == NULL;                                                   \
  }                                                                           \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _init_set)(shared_t dest,                                         \
                       const shared_t shared)                                 \
  {                                                                           \
    M_SHAR3D_CONTRACT(shared, cpt_oplist);                                    \
    M_ASSERT (dest != shared);                                                \
    *dest = *shared;                                                          \
    if (*dest != NULL) {                                                      \
      int n = M_CALL_ADD(cpt_oplist, &((*dest)->cpt), 1);                     \
      (void) n;        /* unused return value */                              \
    }                                                                         \
    M_SHAR3D_CONTRACT(dest, cpt_oplist);                                      \
  }                                                                           \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _clear)(shared_t dest)                                            \
  {                                                                           \
    M_SHAR3D_CONTRACT(dest, cpt_oplist);                                      \
    if (*dest != NULL)        {                                               \
      if (M_CALL_SUB(cpt_oplist, &((*dest)->cpt), 1) == 1)        {           \
        bool combineAlloc = (*dest)->combineAlloc;                            \
        /* Note: if combineAlloc is true, the address of the slot             \
           combining both data & ptr is the same as the address of the        \
           first element, aka data itself. Static analyzer tools don't        \
           seem to detect this and report error. */                           \
        M_CALL_CLEAR(oplist, *(*dest)->data);                                 \
        if (combineAlloc == false) {                                          \
          M_CALL_DEL(oplist, (*dest)->data);                                  \
        }                                                                     \
        M_CALL_DEL(oplist, *dest);                                            \
      }                                                                       \
      *dest = NULL;                                                           \
    }                                                                         \
    M_SHAR3D_CONTRACT(dest, cpt_oplist);                                      \
  }                                                                           \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _reset)(shared_t dest)                                            \
  {                                                                           \
    /* NOTE: Clear will also set dest to NULL */                              \
    M_F(name, _clear)(dest);                                                  \
  }                                                                           \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _set)(shared_t dest,                                              \
                  const shared_t shared)                                      \
  {                                                                           \
    M_SHAR3D_CONTRACT(dest, cpt_oplist);                                      \
    M_SHAR3D_CONTRACT(shared, cpt_oplist);                                    \
    M_F(name, _clear)(dest);                                                  \
    M_F(name, _init_set)(dest, shared);                                       \
  }                                                                           \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _init_move)(shared_t dest,                                        \
                        shared_t shared)                                      \
  {                                                                           \
    M_SHAR3D_CONTRACT(shared, cpt_oplist);                                    \
    M_ASSERT (dest != NULL && dest != shared);                                \
    *dest = *shared;                                                          \
    *shared = NULL;                                                           \
    M_SHAR3D_CONTRACT(dest, cpt_oplist);                                      \
  }                                                                           \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _move)(shared_t dest,                                             \
                   shared_t shared)                                           \
  {                                                                           \
    M_SHAR3D_CONTRACT(dest, cpt_oplist);                                      \
    M_SHAR3D_CONTRACT(shared, cpt_oplist);                                    \
    M_ASSERT (dest != shared);                                                \
    M_F(name, _clear)(dest);                                                  \
    M_F(name, _init_move)(dest, shared);                                      \
  }                                                                           \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _swap)(shared_t p1,                                               \
                   shared_t p2)                                               \
  {                                                                           \
    M_SHAR3D_CONTRACT(p1, cpt_oplist);                                        \
    M_SHAR3D_CONTRACT(p2, cpt_oplist);                                        \
    /* NOTE: SWAP is not atomic */                                            \
    M_SWAP (struct M_F(name, _s)*, *p1, *p2);                                 \
    M_SHAR3D_CONTRACT(p1, cpt_oplist);                                        \
    M_SHAR3D_CONTRACT(p2, cpt_oplist);                                        \
  }                                                                           \
                                                                              \
  M_INLINE bool                                                               \
  M_F(name, _equal_p)(const shared_t p1,                                      \
                      const shared_t p2)                                      \
  {                                                                           \
    M_SHAR3D_CONTRACT(p1, cpt_oplist);                                        \
    M_SHAR3D_CONTRACT(p2, cpt_oplist);                                        \
    return *p1 == *p2;                                                        \
  }                                                                           \
                                                                              \
  M_INLINE type const *                                                       \
  M_F(name, _cref)(const shared_t shared)                                     \
  {                                                                           \
    M_SHAR3D_CONTRACT(shared, cpt_oplist);                                    \
    M_ASSERT(*shared != NULL);                                                \
    type *data = (*shared)->data;                                             \
    M_ASSERT (data != NULL);                                                  \
    return M_CONST_CAST (type, data);                                         \
  }                                                                           \
                                                                              \
  M_INLINE type *                                                             \
  M_F(name, _ref)(shared_t shared)                                            \
  {                                                                           \
    M_SHAR3D_CONTRACT(shared, cpt_oplist);                                    \
    M_ASSERT(*shared != NULL);                                                \
    type *data = (*shared)->data;                                             \
    M_ASSERT (data != NULL);                                                  \
    return data;                                                              \
  }                                                                           \

/* Definition of the emplace_back function for arrays */
#define M_SHAR3D_PTR_DEF_EMPLACE(name, cpt_oplist, function_name, oplist, init_func, exp_emplace_type) \
  M_INLINE void                                                               \
  function_name(M_F(name, _ct) shared                                         \
                M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) )                \
  {                                                                           \
    /* NOTE: Alloc 1 struct with both structures. */                          \
    struct M_F(name, _combine_s) *p =                                         \
      M_CALL_NEW(oplist, struct M_F(name, _combine_s));                       \
    if (M_UNLIKELY_NOMEM (p == NULL)) {                                       \
      M_MEMORY_FULL(sizeof(struct M_F(name, _combine_s)));                    \
      return;                                                                 \
    }                                                                         \
    struct M_F(name, _s) *ptr = &p->ptr;                                      \
    ptr->combineAlloc = true;                                                 \
    M_F(name, _subtype_ct) *data = &p->data;                                  \
    M_EMPLACE_CALL_FUNC(a, init_func, oplist, *data, exp_emplace_type);       \
    ptr->data = data;                                                         \
    M_CALL_INIT_SET(cpt_oplist, &ptr->cpt, 1);                                \
    *shared = ptr;                                                            \
    M_SHAR3D_CONTRACT(shared, cpt_oplist);                                    \
  }                                                                           \



/********************************** INTERNAL *********************************/

#define M_SHAR3D_RESOURCE_CONTRACT(s) do {                                    \
    M_ASSERT (s != NULL);                                                     \
    M_ASSERT (s->buffer != NULL);                                             \
  } while (0)

// deferred
#define M_SHAR3D_RESOURCE_DEF_P1(arg) M_ID( M_SHAR3D_RESOURCE_DEF_P2 arg )

/* Validate the oplist before going further */
#define M_SHAR3D_RESOURCE_DEF_P2(name, type, oplist, shared_t, it_t)          \
  M_IF_OPLIST(oplist)(M_SHAR3D_RESOURCE_DEF_P3, M_SHAR3D_RESOURCE_DEF_FAILURE)(name, type, oplist, shared_t, it_t)

/* Stop processing with a compilation failure */
#define M_SHAR3D_RESOURCE_DEF_FAILURE(name, type, oplist, shared_t, it_t)     \
  M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(SHARED_RESOURCE_DEF): the given argument is not a valid oplist: " #oplist)

#define M_SHAR3D_RESOURCE_DEF_P3(name, type, oplist, shared_t, it_t)          \
  M_SHAR3D_RESOURCE_DEF_TYPE(name, type, oplist, shared_t, it_t)              \
  M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist)                            \
  M_SHAR3D_RESOURCE_DEF_CORE(name, type, oplist, shared_t, it_t)              \

/* Define the types */
#define M_SHAR3D_RESOURCE_DEF_TYPE(name, type, oplist, shared_t, it_t)        \
                                                                              \
  /* Create an aligned type to avoid false sharing between threads */         \
  typedef struct M_F(name, _atype_s) {                                        \
    atomic_uint  cpt;                                                         \
    type         x;                                                           \
    M_CACHELINE_ALIGN(align, type, atomic_uint);                              \
  } M_F(name, _atype_ct);                                                     \
                                                                              \
  typedef struct M_F(name, _s) {                                              \
    m_genint_t             core;                                              \
    M_F(name, _atype_ct) *buffer;                                             \
  } shared_t[1];                                                              \
                                                                              \
  typedef struct M_F(name, _it_s) {                                           \
    unsigned int idx;                                                         \
    struct M_F(name, _s) *ref;                                                \
  } it_t[1];                                                                  \
                                                                              \
  /* Internal Types for oplist */                                             \
  typedef shared_t M_F(name, _ct);                                            \
  typedef type     M_F(name, _subtype_ct);                                    \

/* Define the core functions */
#define M_SHAR3D_RESOURCE_DEF_CORE(name, type, oplist, shared_t, it_t)        \
  M_INLINE void                                                               \
  M_F(name, _init)(shared_t s, size_t n)                                      \
  {                                                                           \
    M_ASSERT(s != NULL);                                                      \
    M_ASSERT (n > 0 && n < UINT_MAX);                                         \
    s->buffer = M_CALL_REALLOC(oplist, M_F(name, _atype_ct), NULL, n);        \
    if (M_UNLIKELY_NOMEM (s->buffer == NULL)) {                               \
      M_MEMORY_FULL(sizeof(M_F(name, _atype_ct)) * n);                        \
      return;                                                                 \
    }                                                                         \
    for(size_t i = 0; i < n; i++) {                                           \
      M_CALL_INIT(oplist, s->buffer[i].x);                                    \
      atomic_init (&s->buffer[i].cpt, 0U);                                    \
    }                                                                         \
    m_genint_init(s->core, (unsigned int) n);                                 \
    M_SHAR3D_RESOURCE_CONTRACT(s);                                            \
  }                                                                           \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _clear)(shared_t s)                                               \
  {                                                                           \
    M_SHAR3D_RESOURCE_CONTRACT(s);                                            \
    size_t n = m_genint_size(s->core);                                        \
    for(size_t i = 0; i < n; i++) {                                           \
      M_CALL_CLEAR(oplist, s->buffer[i].x);                                   \
    }                                                                         \
    M_CALL_FREE(oplist, s->buffer);                                           \
    s->buffer = NULL;                                                         \
    m_genint_clear(s->core);                                                  \
  }                                                                           \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _it)(it_t it, shared_t s)                                         \
  {                                                                           \
    M_SHAR3D_RESOURCE_CONTRACT(s);                                            \
    M_ASSERT (it != NULL);                                                    \
    unsigned int idx = m_genint_pop(s->core);                                 \
    it->idx = idx;                                                            \
    it->ref = s;                                                              \
    if (M_LIKELY (idx != M_GENINT_ERROR)) {                                   \
      M_ASSERT(atomic_load(&s->buffer[idx].cpt) == 0);                        \
      atomic_store(&s->buffer[idx].cpt, 1U);                                  \
    }                                                                         \
  }                                                                           \
                                                                              \
  M_INLINE bool                                                               \
  M_F(name, _end_p)(it_t it)                                                  \
  {                                                                           \
    M_ASSERT (it != NULL);                                                    \
    return it->idx == M_GENINT_ERROR;                                         \
  }                                                                           \
                                                                              \
  M_INLINE type *                                                             \
  M_F(name, _ref)(it_t it)                                                    \
  {                                                                           \
    M_ASSERT (it != NULL && it->ref != NULL && it->idx != M_GENINT_ERROR);    \
    M_SHAR3D_RESOURCE_CONTRACT(it->ref);                                      \
    return &it->ref->buffer[it->idx].x;                                       \
  }                                                                           \
                                                                              \
  M_INLINE type const *                                                       \
  M_F(name, _cref)(it_t it)                                                   \
  {                                                                           \
    M_ASSERT (it != NULL && it->ref != NULL && it->idx != M_GENINT_ERROR);    \
    M_SHAR3D_RESOURCE_CONTRACT(it->ref);                                      \
    return M_CONST_CAST (type, &it->ref->buffer[it->idx].x);                  \
  }                                                                           \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _end)(it_t it, shared_t s)                                        \
  {                                                                           \
    M_SHAR3D_RESOURCE_CONTRACT(s);                                            \
    M_ASSERT (it != NULL);                                                    \
    M_ASSERT (it->ref == s);                                                  \
    unsigned int idx = it->idx;                                               \
    if (M_LIKELY (idx != M_GENINT_ERROR)) {                                   \
      unsigned int c = atomic_fetch_sub (&it->ref->buffer[idx].cpt, 1U);      \
      if (c == 1) {                                                           \
        m_genint_push(it->ref->core, idx);                                    \
      }                                                                       \
      it->idx = M_GENINT_ERROR;                                               \
    }                                                                         \
  }                                                                           \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _it_set)(it_t itd, it_t its)                                      \
  {                                                                           \
    M_ASSERT (itd != NULL && its != NULL);                                    \
    M_SHAR3D_RESOURCE_CONTRACT(its->ref);                                     \
    itd->ref = its->ref;                                                      \
    unsigned int idx = its->idx;                                              \
    itd->idx = idx;                                                           \
    if (M_LIKELY (idx != M_GENINT_ERROR)) {                                   \
      unsigned int c = atomic_fetch_add(&itd->ref->buffer[idx].cpt, 1U);      \
      M_ASSERT (c >= 1);                                                      \
    }                                                                         \
  }                                                                           \

M_END_PROTECTED_CODE

/********************************** INTERNAL *********************************/

#if M_USE_SMALL_NAME
#define SHARED_PTR_OPLIST M_SHARED_PTR_OPLIST
#define SHARED_PTR_DEF M_SHARED_PTR_DEF
#define SHARED_PTR_DEF_AS M_SHARED_PTR_DEF_AS
#define SHARED_PTR_RELAXED_DEF M_SHARED_PTR_RELAXED_DEF
#define SHARED_PTR_RELAXED_DEF_AS M_SHARED_PTR_RELAXED_DEF_AS
#define SHARED_RESOURCE_DEF M_SHARED_RESOURCE_DEF
#define SHARED_RESOURCE_DEF_AS M_SHARED_RESOURCE_DEF_AS
#endif

#endif
