/* Eina - EFL data type library
 * Copyright (C) 2012 ProFUSION embedded systems
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library;
 * if not, see <http://www.gnu.org/licenses/>.
 */

#ifndef EINA_INLINE_VALUE_X_
#define EINA_INLINE_VALUE_X_

#include <stdlib.h>
#include <string.h>

#include "eina_stringshare.h"

/* NOTE: most of value is implemented here for performance reasons */

//#define EINA_VALUE_NO_OPTIMIZE 1
#ifdef EINA_VALUE_NO_OPTIMIZE
#define EINA_VALUE_TYPE_DEFAULT(type) (0)
#else

/**
 * @var _EINA_VALUE_TYPE_BASICS_START
 * pointer to the first basic type.
 * @since 1.2
 * @private
 */
EAPI extern const Eina_Value_Type *_EINA_VALUE_TYPE_BASICS_START;

/**
 * @var _EINA_VALUE_TYPE_BASICS_END
 * pointer to the last (inclusive) basic type.
 * @since 1.2
 * @private
 */
EAPI extern const Eina_Value_Type *_EINA_VALUE_TYPE_BASICS_END;
#define EINA_VALUE_TYPE_DEFAULT(type)           \
  ((_EINA_VALUE_TYPE_BASICS_START <= type) &&   \
   (type <= _EINA_VALUE_TYPE_BASICS_END))
#endif

#define EINA_VALUE_TYPE_CHECK_RETURN(value)     \
  EINA_SAFETY_ON_NULL_RETURN(value);            \
  EINA_SAFETY_ON_FALSE_RETURN(eina_value_type_check(value->type))

#define EINA_VALUE_TYPE_CHECK_RETURN_VAL(value, retval)                 \
  EINA_SAFETY_ON_NULL_RETURN_VAL(value, retval);                        \
  EINA_SAFETY_ON_FALSE_RETURN_VAL(eina_value_type_check(value->type), retval)

#define EINA_VALUE_TYPE_DISPATCH(type, method, no_method_err, ...)      \
  do                                                                    \
    {                                                                   \
       if (type->method)                                                \
         type->method(type, ##__VA_ARGS__);                             \
       else                                                             \
         eina_error_set(no_method_err);                                 \
    }                                                                   \
  while (0)

#define EINA_VALUE_TYPE_DISPATCH_RETURN(value, method, no_method_err, def_ret, ...) \
  do                                                                    \
    {                                                                   \
       if (type->method)                                                \
         return type->method(type, ##__VA_ARGS__);                      \
       eina_error_set(no_method_err);                                   \
       return def_ret;                                                  \
    }                                                                   \
  while (0)

/**
 * @brief Get memory for given value (inline or allocated buffer).
 * @since 1.2
 * @private
 */
static inline void *
eina_value_memory_get(const Eina_Value *value)
{
   if (value->type->value_size <= 8)
     return (void *)value->value.buf;
   return value->value.ptr;
}

/**
 * @brief Allocate memory for internal value types.
 * @since 1.2
 * @private
 */
EAPI void *eina_value_inner_alloc(size_t size);
/**
 * @brief Releases memory for internal value types.
 * @since 1.2
 * @private
 */
EAPI void eina_value_inner_free(size_t size, void *mem);

static inline Eina_Bool
eina_value_setup(Eina_Value *value, const Eina_Value_Type *type)
{
   void *mem;

   EINA_SAFETY_ON_FALSE_RETURN_VAL(eina_value_type_check(type), EINA_FALSE);
   EINA_SAFETY_ON_FALSE_RETURN_VAL(type->value_size > 0, EINA_FALSE);

   value->type = type;

   if (type->value_size <= 8) mem = &value->value;
   else
     {
        mem = value->value.ptr = eina_value_inner_alloc(type->value_size);
        EINA_SAFETY_ON_NULL_RETURN_VAL(mem, EINA_FALSE);
     }

   memset(mem, 0, type->value_size);

   if (EINA_VALUE_TYPE_DEFAULT(type))
     {
        eina_error_set(0);
        return EINA_TRUE;
     }

   EINA_VALUE_TYPE_DISPATCH_RETURN(type, setup,
                                   EINA_ERROR_VALUE_FAILED, EINA_FALSE, mem);
}

static inline void
eina_value_flush(Eina_Value *value)
{
   const Eina_Value_Type *type;
   void *mem;

   EINA_VALUE_TYPE_CHECK_RETURN(value);

   type = value->type;
   mem = eina_value_memory_get(value);

   if (EINA_VALUE_TYPE_DEFAULT(type))
     {
        if (type == EINA_VALUE_TYPE_STRINGSHARE)
          {
             if (value->value.ptr) eina_stringshare_del((const char*) value->value.ptr);
          }
        else if (type == EINA_VALUE_TYPE_STRING)
          {
             if (value->value.ptr) free(value->value.ptr);
          }
        else if (type->value_size > 8)
          eina_value_inner_free(type->value_size, mem);
        eina_error_set(0);
        return;
     }

   EINA_VALUE_TYPE_DISPATCH(type, flush, EINA_ERROR_VALUE_FAILED, mem);
   if (type->value_size > 8)
     eina_value_inner_free(type->value_size, mem);
   value->type = NULL;
}

static inline int
eina_value_compare(const Eina_Value *a, const Eina_Value *b)
{
   const Eina_Value_Type *type;
   void *pa, *pb;

   EINA_VALUE_TYPE_CHECK_RETURN_VAL(a, -1);
   EINA_SAFETY_ON_NULL_RETURN_VAL(b, -1);
   EINA_SAFETY_ON_FALSE_RETURN_VAL(a->type == b->type, -1);

   eina_error_set(0);
   type = a->type;
   pa = eina_value_memory_get(a);
   pb = eina_value_memory_get(b);

#ifndef EINA_VALUE_NO_OPTIMIZE
   if (type == EINA_VALUE_TYPE_UCHAR)
     {
        unsigned char *ta = (unsigned char *) pa, *tb = (unsigned char *) pb;
        if (*ta < *tb)
          return -1;
        else if (*ta > *tb)
          return 1;
        return 0;
     }
   else if (type == EINA_VALUE_TYPE_USHORT)
     {
        unsigned short *ta = (unsigned short *) pa, *tb = (unsigned short *) pb;
        if (*ta < *tb)
          return -1;
        else if (*ta > *tb)
          return 1;
        return 0;
     }
   else if (type == EINA_VALUE_TYPE_UINT)
     {
        unsigned int *ta = (unsigned int *) pa, *tb = (unsigned int *) pb;
        if (*ta < *tb)
          return -1;
        else if (*ta > *tb)
          return 1;
        return 0;
     }
   else if ((type == EINA_VALUE_TYPE_ULONG) || (type == EINA_VALUE_TYPE_TIMESTAMP))
     {
        unsigned long *ta = (unsigned long *) pa, *tb = (unsigned long *) pb;
        if (*ta < *tb)
          return -1;
        else if (*ta > *tb)
          return 1;
        return 0;
     }
   else if (type == EINA_VALUE_TYPE_UINT64)
     {
        uint64_t *ta = (uint64_t *) pa, *tb = (uint64_t *) pb;
        if (*ta < *tb)
          return -1;
        else if (*ta > *tb)
          return 1;
        return 0;
     }
   else if (type == EINA_VALUE_TYPE_CHAR)
     {
        signed char *ta = (signed char *) pa, *tb = (signed char *) pb;
        if (*ta < *tb)
          return -1;
        else if (*ta > *tb)
          return 1;
        return 0;
     }
   else if (type == EINA_VALUE_TYPE_SHORT)
     {
        short *ta = (short *) pa, *tb = (short *) pb;
        if (*ta < *tb)
          return -1;
        else if (*ta > *tb)
          return 1;
        return 0;
     }
   else if (type == EINA_VALUE_TYPE_INT)
     {
        int *ta = (int *) pa, *tb = (int *) pb;
        if (*ta < *tb)
          return -1;
        else if (*ta > *tb)
          return 1;
        return 0;
     }
   else if (type == EINA_VALUE_TYPE_LONG)
     {
        long *ta = (long *) pa, *tb = (long *) pb;
        if (*ta < *tb)
          return -1;
        else if (*ta > *tb)
          return 1;
        return 0;
     }
   else if (type == EINA_VALUE_TYPE_INT64)
     {
        int64_t *ta = (int64_t *) pa, *tb = (int64_t *) pb;
        if (*ta < *tb)
          return -1;
        else if (*ta > *tb)
          return 1;
        return 0;
     }
   else if (type == EINA_VALUE_TYPE_FLOAT)
     {
        float *ta = (float *) pa, *tb = (float *) pb;
        if (*ta < *tb)
          return -1;
        else if (*ta > *tb)
          return 1;
        return 0;
     }
   else if (type == EINA_VALUE_TYPE_DOUBLE)
     {
        double *ta = (double *) pa, *tb = (double *) pb;
        if (*ta < *tb)
          return -1;
        else if (*ta > *tb)
          return 1;
        return 0;
     }
   else if (type == EINA_VALUE_TYPE_STRINGSHARE ||
            type == EINA_VALUE_TYPE_STRING)
     {
        const char *sa = *(const char **)pa;
        const char *sb = *(const char **)pb;
        if (sa == sb)
          return 0;
        if (sa == NULL)
          return -1;
        if (sb == NULL)
          return 1;
        return strcmp(sa, sb);
     }
#endif

   EINA_VALUE_TYPE_DISPATCH_RETURN(type, compare, EINA_ERROR_VALUE_FAILED,
                                   EINA_FALSE, pa, pb);
}

static inline Eina_Bool
eina_value_set(Eina_Value *value, ...)
{
   va_list args;
   Eina_Bool ret;
   va_start(args, value);
   ret = eina_value_vset(value, args);
   va_end(args);
   return ret;
}

static inline Eina_Bool
eina_value_get(const Eina_Value *value, ...)
{
   va_list args;
   Eina_Bool ret;
   va_start(args, value);
   ret = eina_value_vget(value, args);
   va_end(args);
   return ret;
}

static inline Eina_Bool
eina_value_vset(Eina_Value *value, va_list args)
{
   const Eina_Value_Type *type;
   void *mem;

   EINA_VALUE_TYPE_CHECK_RETURN_VAL(value, EINA_FALSE);

   type = value->type;
   mem = eina_value_memory_get(value);
   eina_error_set(0);
#ifndef EINA_VALUE_NO_OPTIMIZE
   if (type == EINA_VALUE_TYPE_UCHAR)
     {
        unsigned char *tmem = (unsigned char *) mem;
        *tmem = va_arg(args, unsigned int); /* promoted by va_arg */
        return EINA_TRUE;
     }
   else if (type == EINA_VALUE_TYPE_USHORT)
     {
        unsigned short *tmem = (unsigned short *) mem;
        *tmem = va_arg(args, unsigned int); /* promoted by va_arg */
        return EINA_TRUE;
     }
   else if (type == EINA_VALUE_TYPE_UINT)
     {
        unsigned int *tmem = (unsigned int *) mem;
        *tmem = va_arg(args, unsigned int);
        return EINA_TRUE;
     }
   else if ((type == EINA_VALUE_TYPE_ULONG) || (type == EINA_VALUE_TYPE_TIMESTAMP))
     {
        unsigned long *tmem = (unsigned long *) mem;
        *tmem = va_arg(args, unsigned long);
        return EINA_TRUE;
     }
   else if (type == EINA_VALUE_TYPE_UINT64)
     {
        uint64_t *tmem = (uint64_t *) mem;
        *tmem = va_arg(args, uint64_t);
        return EINA_TRUE;
     }
   else if (type == EINA_VALUE_TYPE_CHAR)
     {
        signed char *tmem = (signed char *) mem;
        *tmem = va_arg(args, int); /* promoted by va_arg */
        return EINA_TRUE;
     }
   else if (type == EINA_VALUE_TYPE_SHORT)
     {
        short *tmem = (short *) mem;
        *tmem = va_arg(args, int); /* promoted by va_arg */
        return EINA_TRUE;
     }
   else if (type == EINA_VALUE_TYPE_INT)
     {
        int *tmem = (int *) mem;
        *tmem = va_arg(args, int);
        return EINA_TRUE;
     }
   else if (type == EINA_VALUE_TYPE_LONG)
     {
        long *tmem = (long *) mem;
        *tmem = va_arg(args, long);
        return EINA_TRUE;
     }
   else if (type == EINA_VALUE_TYPE_INT64)
     {
        int64_t *tmem = (int64_t *) mem;
        *tmem = va_arg(args, int64_t);
        return EINA_TRUE;
     }
   else if (type == EINA_VALUE_TYPE_FLOAT)
     {
        float *tmem = (float *) mem;
        *tmem = va_arg(args, double); /* promoted by va_arg */
        return EINA_TRUE;
     }
   else if (type == EINA_VALUE_TYPE_DOUBLE)
     {
        double *tmem = (double *) mem;
        *tmem = va_arg(args, double);
        return EINA_TRUE;
     }
   else if (type == EINA_VALUE_TYPE_STRINGSHARE)
     {
        const char *str = (const char *) va_arg(args, const char *);
        return eina_stringshare_replace((const char **)&value->value.ptr, str);
     }
   else if (type == EINA_VALUE_TYPE_STRING)
     {
        const char *str = (const char *) va_arg(args, const char *);
        if (value->value.ptr == str) return EINA_TRUE;
        if (!str)
          {
             free(value->value.ptr);
             value->value.ptr = NULL;
          }
        else
          {
             char *tmp = strdup(str);
             if (!tmp)
               {
                  eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
                  return EINA_FALSE;
               }
             free(value->value.ptr);
             value->value.ptr = tmp;
          }
        return EINA_TRUE;
     }
#endif

   EINA_VALUE_TYPE_DISPATCH_RETURN(value, vset, EINA_ERROR_VALUE_FAILED,
                                   EINA_FALSE, mem, args);
}

static inline Eina_Bool
eina_value_vget(const Eina_Value *value, va_list args)
{
   const Eina_Value_Type *type;
   const void *mem;
   void *ptr;

   EINA_VALUE_TYPE_CHECK_RETURN_VAL(value, EINA_FALSE);

   type = value->type;
   mem = eina_value_memory_get(value);
   ptr = va_arg(args, void *);
   eina_error_set(0);
   if (EINA_VALUE_TYPE_DEFAULT(type))
     {
        memcpy(ptr, mem, type->value_size);
        return EINA_TRUE;
     }

   EINA_VALUE_TYPE_DISPATCH_RETURN(value, pget, EINA_ERROR_VALUE_FAILED,
                                   EINA_FALSE, mem, ptr);
}

static inline Eina_Bool
eina_value_pset(Eina_Value *value, const void *ptr)
{
   const Eina_Value_Type *type;
   void *mem;

   EINA_VALUE_TYPE_CHECK_RETURN_VAL(value, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(ptr, EINA_FALSE);

   type = value->type;
   mem = eina_value_memory_get(value);
   eina_error_set(0);

   if (EINA_VALUE_TYPE_DEFAULT(type))
     {
        if (type == EINA_VALUE_TYPE_STRINGSHARE)
          {
             const char *str = *((const char * const *) ptr);

             return eina_stringshare_replace((const char **)&value->value.ptr,
                                             str);
          }
        else if (type == EINA_VALUE_TYPE_STRING)
          {
             const char *str = *((const char * const *) ptr);
             if (value->value.ptr == str) return EINA_TRUE;
             if (!str)
               {
                  free(value->value.ptr);
                  value->value.ptr = NULL;
               }
             else
               {
                  char *tmp = strdup(str);
                  if (!tmp)
                    {
                       eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
                       return EINA_FALSE;
                    }
                  free(value->value.ptr);
                  value->value.ptr = tmp;
               }
             return EINA_TRUE;
          }
        else
          memcpy(mem, ptr, type->value_size);
        return EINA_TRUE;
     }

   EINA_VALUE_TYPE_DISPATCH_RETURN(value, pset, EINA_ERROR_VALUE_FAILED,
                                   EINA_FALSE, mem, ptr);
}

static inline Eina_Bool
eina_value_pget(const Eina_Value *value, void *ptr)
{
   const Eina_Value_Type *type;
   const void *mem;

   EINA_VALUE_TYPE_CHECK_RETURN_VAL(value, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(ptr, EINA_FALSE);

   type = value->type;
   mem = eina_value_memory_get(value);
   eina_error_set(0);
   if (EINA_VALUE_TYPE_DEFAULT(type))
     {
        memcpy(ptr, mem, type->value_size);
        return EINA_TRUE;
     }

   EINA_VALUE_TYPE_DISPATCH_RETURN(value, pget, EINA_ERROR_VALUE_FAILED,
                                   EINA_FALSE, mem, ptr);
}

static inline const Eina_Value_Type *
eina_value_type_get(const Eina_Value *value)
{
   EINA_VALUE_TYPE_CHECK_RETURN_VAL(value, NULL);
   return value->type;
}

#define EINA_VALUE_TYPE_ARRAY_CHECK_RETURN_VAL(value, retval)   \
  EINA_SAFETY_ON_NULL_RETURN_VAL(value, retval);                \
  EINA_SAFETY_ON_FALSE_RETURN_VAL(value->type == EINA_VALUE_TYPE_ARRAY, retval)

static inline Eina_Bool
eina_value_array_setup(Eina_Value *value, const Eina_Value_Type *subtype, unsigned int step)
{
   Eina_Value_Array desc = { subtype, step, NULL };
   if (!eina_value_setup(value, EINA_VALUE_TYPE_ARRAY))
     return EINA_FALSE;
   if (!eina_value_pset(value, &desc))
     {
        eina_value_flush(value);
        return EINA_FALSE;
     }
   return EINA_TRUE;
}

static inline unsigned int
eina_value_array_count(const Eina_Value *value)
{
   Eina_Value_Array desc;
   EINA_VALUE_TYPE_ARRAY_CHECK_RETURN_VAL(value, 0);
   if (!eina_value_pget(value, &desc))
     return 0;
   return eina_inarray_count(desc.array);
}

static inline Eina_Bool
eina_value_array_remove(Eina_Value *value, unsigned int position)
{
   Eina_Value_Array desc;
   void *mem;

   EINA_VALUE_TYPE_ARRAY_CHECK_RETURN_VAL(value, 0);
   if (!eina_value_pget(value, &desc))
     return EINA_FALSE;

   mem = eina_inarray_nth(desc.array, position);
   if (!mem)
     return EINA_FALSE;

   eina_value_type_flush(desc.subtype, mem);
   return eina_inarray_remove_at(desc.array, position);
}

static inline Eina_Bool
eina_value_array_vset(Eina_Value *value, unsigned int position, va_list args)
{
   Eina_Value_Array desc;
   void *mem;

   EINA_VALUE_TYPE_ARRAY_CHECK_RETURN_VAL(value, 0);
   if (!eina_value_pget(value, &desc))
     return EINA_FALSE;

   mem = eina_inarray_nth(desc.array, position);
   if (!mem)
     return EINA_FALSE;

   return eina_value_type_vset(desc.subtype, mem, args);
}

static inline Eina_Bool
eina_value_array_vget(const Eina_Value *value, unsigned int position, va_list args)
{
   Eina_Value_Array desc;
   const void *mem;
   void *ptr;
   Eina_Bool ret;

   EINA_VALUE_TYPE_ARRAY_CHECK_RETURN_VAL(value, 0);
   if (!eina_value_pget(value, &desc))
     return EINA_FALSE;

   mem = eina_inarray_nth(desc.array, position);
   if (!mem)
     return EINA_FALSE;

   ptr = va_arg(args, void *);
   ret = eina_value_type_pget(desc.subtype, mem, ptr);
   return ret;
}

static inline Eina_Bool
eina_value_array_vinsert(Eina_Value *value, unsigned int position, va_list args)
{
   Eina_Value_Array desc;
   void *mem;

   EINA_VALUE_TYPE_ARRAY_CHECK_RETURN_VAL(value, 0);
   if (!eina_value_pget(value, &desc))
     return EINA_FALSE;

   mem = eina_inarray_alloc_at(desc.array, position, 1);
   if (!mem)
     return EINA_FALSE;

   if (!eina_value_type_setup(desc.subtype, mem)) goto error_setup;
   if (!eina_value_type_vset(desc.subtype, mem, args)) goto error_set;
   return EINA_TRUE;

 error_set:
   eina_value_type_flush(desc.subtype, mem);
 error_setup:
   eina_inarray_remove_at(desc.array, position);
   return EINA_FALSE;
}

static inline Eina_Bool
eina_value_array_vappend(Eina_Value *value, va_list args)
{
   Eina_Value_Array desc;
   void *mem;
   int position;

   EINA_VALUE_TYPE_ARRAY_CHECK_RETURN_VAL(value, 0);
   if (!eina_value_pget(value, &desc))
     return EINA_FALSE;

   position = eina_inarray_count(desc.array);
   mem = eina_inarray_alloc_at(desc.array, position, 1);
   if (!mem)
     return EINA_FALSE;

   if (!eina_value_type_setup(desc.subtype, mem)) goto error_setup;
   if (!eina_value_type_vset(desc.subtype, mem, args)) goto error_set;
   return EINA_TRUE;

 error_set:
   eina_value_type_flush(desc.subtype, mem);
 error_setup:
   eina_inarray_remove_at(desc.array, position);
   return EINA_FALSE;
}

static inline Eina_Bool
eina_value_array_set(Eina_Value *value, unsigned int position, ...)
{
   va_list args;
   Eina_Bool ret;
   va_start(args, position);
   ret = eina_value_array_vset(value, position, args);
   va_end(args);
   return ret;
}

static inline Eina_Bool
eina_value_array_get(const Eina_Value *value, unsigned int position, ...)
{
   va_list args;
   Eina_Bool ret;
   va_start(args, position);
   ret = eina_value_array_vget(value, position, args);
   va_end(args);
   return ret;
}

static inline Eina_Bool
eina_value_array_insert(Eina_Value *value, unsigned int position, ...)
{
   va_list args;
   Eina_Bool ret;
   va_start(args, position);
   ret = eina_value_array_vinsert(value, position, args);
   va_end(args);
   return ret;
}

static inline Eina_Bool eina_value_array_append(Eina_Value *value, ...)
{
   va_list args;
   Eina_Bool ret;
   va_start(args, value);
   ret = eina_value_array_vappend(value, args);
   va_end(args);
   return ret;
}

static inline Eina_Bool
eina_value_array_pset(Eina_Value *value, unsigned int position, const void *ptr)
{
   Eina_Value_Array desc;
   void *mem;

   EINA_VALUE_TYPE_ARRAY_CHECK_RETURN_VAL(value, 0);
   if (!eina_value_pget(value, &desc))
     return EINA_FALSE;

   mem = eina_inarray_nth(desc.array, position);
   if (!mem)
     return EINA_FALSE;

   return eina_value_type_pset(desc.subtype, mem, ptr);
}

static inline Eina_Bool
eina_value_array_pget(const Eina_Value *value, unsigned int position, void *ptr)
{
   Eina_Value_Array desc;
   const void *mem;
   Eina_Bool ret;

   EINA_VALUE_TYPE_ARRAY_CHECK_RETURN_VAL(value, 0);
   if (!eina_value_pget(value, &desc))
     return EINA_FALSE;

   mem = eina_inarray_nth(desc.array, position);
   if (!mem)
     return EINA_FALSE;

   ret = eina_value_type_pget(desc.subtype, mem, ptr);
   return ret;
}

static inline Eina_Bool
eina_value_array_pinsert(Eina_Value *value, unsigned int position, const void *ptr)
{
   Eina_Value_Array desc;
   void *mem;

   EINA_VALUE_TYPE_ARRAY_CHECK_RETURN_VAL(value, 0);
   if (!eina_value_pget(value, &desc))
     return EINA_FALSE;

   mem = eina_inarray_alloc_at(desc.array, position, 1);
   if (!mem)
     return EINA_FALSE;

   if (!eina_value_type_setup(desc.subtype, mem)) goto error_setup;
   if (!eina_value_type_pset(desc.subtype, mem, ptr)) goto error_set;
   return EINA_TRUE;

 error_set:
   eina_value_type_flush(desc.subtype, mem);
 error_setup:
   eina_inarray_remove_at(desc.array, position);
   return EINA_FALSE;
}

static inline Eina_Bool
eina_value_array_pappend(Eina_Value *value, const void *ptr)
{
   Eina_Value_Array desc;
   void *mem;
   int position;

   EINA_VALUE_TYPE_ARRAY_CHECK_RETURN_VAL(value, 0);
   if (!eina_value_pget(value, &desc))
     return EINA_FALSE;

   position = eina_inarray_count(desc.array);
   mem = eina_inarray_alloc_at(desc.array, position, 1);
   if (!mem)
     return EINA_FALSE;

   if (!eina_value_type_setup(desc.subtype, mem)) goto error_setup;
   if (!eina_value_type_pset(desc.subtype, mem, ptr)) goto error_set;
   return EINA_TRUE;

 error_set:
   eina_value_type_flush(desc.subtype, mem);
 error_setup:
   eina_inarray_remove_at(desc.array, position);
   return EINA_FALSE;
}

static inline Eina_Bool
eina_value_array_value_get(const Eina_Value *src, unsigned int position, Eina_Value *dst)
{
   Eina_Value_Array desc;

   EINA_VALUE_TYPE_ARRAY_CHECK_RETURN_VAL(src, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(dst, EINA_FALSE);

   if (!eina_value_pget(src, &desc))
     return EINA_FALSE;
   if (position >= eina_inarray_count(desc.array))
     return EINA_FALSE;
   if (!eina_value_setup(dst, desc.subtype))
     return EINA_FALSE;
   if (!eina_value_pset(dst, eina_inarray_nth(desc.array, position)))
     {
        eina_value_flush(dst);
        return EINA_FALSE;
     }

   return EINA_TRUE;
}

#undef EINA_VALUE_TYPE_ARRAY_CHECK_RETURN_VAL

#define EINA_VALUE_TYPE_LIST_CHECK_RETURN_VAL(value, retval)   \
  EINA_SAFETY_ON_NULL_RETURN_VAL(value, retval);                \
  EINA_SAFETY_ON_FALSE_RETURN_VAL(value->type == EINA_VALUE_TYPE_LIST, retval)

static inline void *
eina_value_list_node_memory_get(const Eina_Value_Type *type, const Eina_List *node)
{
   if (node == NULL || type == NULL) return NULL;
   if (type->value_size <= sizeof(void*))
     return (void *)&(node->data);
   return node->data;
}

static inline void *
eina_value_list_node_memory_setup(const Eina_Value_Type *type, Eina_List *node)
{
   if (type->value_size <= sizeof(void*))
     return (void *)&(node->data);
   node->data = malloc(type->value_size);
   return node->data;
}

static inline void
eina_value_list_node_memory_flush(const Eina_Value_Type *type, Eina_List *node)
{
   if (type->value_size <= sizeof(void*))
     return;
   free(node->data);
}

static inline Eina_Bool
eina_value_list_setup(Eina_Value *value, const Eina_Value_Type *subtype)
{
   Eina_Value_List desc = { subtype, NULL };
   if (!eina_value_setup(value, EINA_VALUE_TYPE_LIST))
     return EINA_FALSE;
   if (!eina_value_pset(value, &desc))
     {
        eina_value_flush(value);
        return EINA_FALSE;
     }
   return EINA_TRUE;
}

static inline unsigned int
eina_value_list_count(const Eina_Value *value)
{
   Eina_Value_List *desc;
   EINA_VALUE_TYPE_LIST_CHECK_RETURN_VAL(value, 0);
   desc = (Eina_Value_List *)eina_value_memory_get(value);
   if (!desc)
     return 0;
   return eina_list_count(desc->list);
}

static inline Eina_Bool
eina_value_list_remove(Eina_Value *value, unsigned int position)
{
   Eina_Value_List *desc;
   Eina_List *node;
   void *mem;

   EINA_VALUE_TYPE_LIST_CHECK_RETURN_VAL(value, 0);
   desc = (Eina_Value_List *)eina_value_memory_get(value);
   if (!desc)
     return EINA_FALSE;

   node = eina_list_nth_list(desc->list, position);
   mem = eina_value_list_node_memory_get(desc->subtype, node);
   if (!mem)
     return EINA_FALSE;

   eina_value_type_flush(desc->subtype, mem);
   eina_value_list_node_memory_flush(desc->subtype, node);
   desc->list = eina_list_remove_list(desc->list, node);
   return EINA_TRUE;
}

static inline Eina_Bool
eina_value_list_vset(Eina_Value *value, unsigned int position, va_list args)
{
   Eina_Value_List *desc;
   Eina_List *node;
   void *mem;

   EINA_VALUE_TYPE_LIST_CHECK_RETURN_VAL(value, 0);
   desc = (Eina_Value_List *)eina_value_memory_get(value);
   if (!desc)
     return EINA_FALSE;

   node = eina_list_nth_list(desc->list, position);
   mem = eina_value_list_node_memory_get(desc->subtype, node);
   if (!mem)
     return EINA_FALSE;

   return eina_value_type_vset(desc->subtype, mem, args);
}

static inline Eina_Bool
eina_value_list_vget(const Eina_Value *value, unsigned int position, va_list args)
{
   const Eina_Value_List *desc;
   const Eina_List *node;
   const void *mem;
   void *ptr;
   Eina_Bool ret;

   EINA_VALUE_TYPE_LIST_CHECK_RETURN_VAL(value, 0);
   desc = (const Eina_Value_List *)eina_value_memory_get(value);
   if (!desc)
     return EINA_FALSE;

   node = eina_list_nth_list(desc->list, position);
   mem = eina_value_list_node_memory_get(desc->subtype, node);
   if (!mem)
     return EINA_FALSE;

   ptr = va_arg(args, void *);
   ret = eina_value_type_pget(desc->subtype, mem, ptr);
   return ret;
}

static inline Eina_Bool
eina_value_list_vinsert(Eina_Value *value, unsigned int position, va_list args)
{
   Eina_Value_List *desc;
   Eina_List *node;
   void *mem;

   EINA_VALUE_TYPE_LIST_CHECK_RETURN_VAL(value, 0);
   desc = (Eina_Value_List *)eina_value_memory_get(value);
   if (!desc)
     return EINA_FALSE;

   if (!desc->list)
     node = desc->list = eina_list_append(NULL, (void*)1L);
   else if (position == 0)
     node = desc->list = eina_list_prepend(desc->list, (void*)1L);
   else
     {
        Eina_List *rel = eina_list_nth_list(desc->list, position - 1);
        desc->list = eina_list_append_relative_list(desc->list, (void*)1L, rel);
        node = rel->next;
     }
   EINA_SAFETY_ON_NULL_RETURN_VAL(node, EINA_FALSE);
   EINA_SAFETY_ON_FALSE_RETURN_VAL(node->data == (void*)1L, EINA_FALSE);

   mem = eina_value_list_node_memory_setup(desc->subtype, node);
   if (!mem)
     {
        desc->list = eina_list_remove_list(desc->list, node);
        return EINA_FALSE;
     }

   if (!eina_value_type_setup(desc->subtype, mem)) goto error_setup;
   if (!eina_value_type_vset(desc->subtype, mem, args)) goto error_set;
   return EINA_TRUE;

 error_set:
   eina_value_type_flush(desc->subtype, mem);
 error_setup:
   eina_value_list_node_memory_flush(desc->subtype, node);
   desc->list = eina_list_remove_list(desc->list, node);
   return EINA_FALSE;
}

static inline Eina_Bool
eina_value_list_vappend(Eina_Value *value, va_list args)
{
   Eina_Value_List *desc;
   Eina_List *node;
   void *mem;

   EINA_VALUE_TYPE_LIST_CHECK_RETURN_VAL(value, 0);
   desc = (Eina_Value_List *)eina_value_memory_get(value);
   if (!desc)
     return EINA_FALSE;

   desc->list = eina_list_append(desc->list, (void*)1L);
   node = eina_list_last(desc->list);
   EINA_SAFETY_ON_NULL_RETURN_VAL(node, EINA_FALSE);
   EINA_SAFETY_ON_FALSE_RETURN_VAL(node->data == (void*)1L, EINA_FALSE);

   mem = eina_value_list_node_memory_setup(desc->subtype, node);
   if (!mem)
     {
        desc->list = eina_list_remove_list(desc->list, node);
        return EINA_FALSE;
     }

   if (!eina_value_type_setup(desc->subtype, mem)) goto error_setup;
   if (!eina_value_type_vset(desc->subtype, mem, args)) goto error_set;
   return EINA_TRUE;

 error_set:
   eina_value_type_flush(desc->subtype, mem);
 error_setup:
   eina_value_list_node_memory_flush(desc->subtype, node);
   desc->list = eina_list_remove_list(desc->list, node);
   return EINA_FALSE;
}

static inline Eina_Bool
eina_value_list_set(Eina_Value *value, unsigned int position, ...)
{
   va_list args;
   Eina_Bool ret;
   va_start(args, position);
   ret = eina_value_list_vset(value, position, args);
   va_end(args);
   return ret;
}

static inline Eina_Bool
eina_value_list_get(const Eina_Value *value, unsigned int position, ...)
{
   va_list args;
   Eina_Bool ret;
   va_start(args, position);
   ret = eina_value_list_vget(value, position, args);
   va_end(args);
   return ret;
}

static inline Eina_Bool
eina_value_list_insert(Eina_Value *value, unsigned int position, ...)
{
   va_list args;
   Eina_Bool ret;
   va_start(args, position);
   ret = eina_value_list_vinsert(value, position, args);
   va_end(args);
   return ret;
}

static inline Eina_Bool eina_value_list_append(Eina_Value *value, ...)
{
   va_list args;
   Eina_Bool ret;
   va_start(args, value);
   ret = eina_value_list_vappend(value, args);
   va_end(args);
   return ret;
}

static inline Eina_Bool
eina_value_list_pset(Eina_Value *value, unsigned int position, const void *ptr)
{
   Eina_Value_List *desc;
   Eina_List *node;
   void *mem;

   EINA_VALUE_TYPE_LIST_CHECK_RETURN_VAL(value, 0);
   desc = (Eina_Value_List *)eina_value_memory_get(value);
   if (!desc)
     return EINA_FALSE;

   node = eina_list_nth_list(desc->list, position);
   mem = eina_value_list_node_memory_get(desc->subtype, node);
   if (!mem)
     return EINA_FALSE;

   return eina_value_type_pset(desc->subtype, mem, ptr);
}

static inline Eina_Bool
eina_value_list_pget(const Eina_Value *value, unsigned int position, void *ptr)
{
   const Eina_Value_List *desc;
   const Eina_List *node;
   const void *mem;
   Eina_Bool ret;

   EINA_VALUE_TYPE_LIST_CHECK_RETURN_VAL(value, 0);
   desc = (const Eina_Value_List *)eina_value_memory_get(value);
   if (!desc)
     return EINA_FALSE;

   node = eina_list_nth_list(desc->list, position);
   mem = eina_value_list_node_memory_get(desc->subtype, node);
   if (!mem)
     return EINA_FALSE;

   ret = eina_value_type_pget(desc->subtype, mem, ptr);
   return ret;
}

static inline Eina_Bool
eina_value_list_pinsert(Eina_Value *value, unsigned int position, const void *ptr)
{
   Eina_Value_List *desc;
   Eina_List *node;
   void *mem;

   EINA_VALUE_TYPE_LIST_CHECK_RETURN_VAL(value, 0);
   desc = (Eina_Value_List *)eina_value_memory_get(value);
   if (!desc)
     return EINA_FALSE;

   if (!desc->list)
     node = desc->list = eina_list_append(NULL, (void*)1L);
   else if (position == 0)
     node = desc->list = eina_list_prepend(desc->list, (void*)1L);
   else
     {
        Eina_List *rel = eina_list_nth_list(desc->list, position - 1);
        desc->list = eina_list_append_relative_list(desc->list, (void*)1L, rel);
        node = rel->next;
     }
   EINA_SAFETY_ON_NULL_RETURN_VAL(node, EINA_FALSE);
   EINA_SAFETY_ON_FALSE_RETURN_VAL(node->data == (void*)1L, EINA_FALSE);

   mem = eina_value_list_node_memory_setup(desc->subtype, node);
   if (!mem)
     {
        desc->list = eina_list_remove_list(desc->list, node);
        return EINA_FALSE;
     }

   if (!eina_value_type_setup(desc->subtype, mem)) goto error_setup;
   if (!eina_value_type_pset(desc->subtype, mem, ptr)) goto error_set;
   return EINA_TRUE;

 error_set:
   eina_value_type_flush(desc->subtype, mem);
 error_setup:
   eina_value_list_node_memory_flush(desc->subtype, node);
   desc->list = eina_list_remove_list(desc->list, node);
   return EINA_FALSE;
}

static inline Eina_Bool
eina_value_list_pappend(Eina_Value *value, const void *ptr)
{
   Eina_Value_List *desc;
   Eina_List *node;
   void *mem;

   EINA_VALUE_TYPE_LIST_CHECK_RETURN_VAL(value, 0);
   desc = (Eina_Value_List *)eina_value_memory_get(value);
   if (!desc)
     return EINA_FALSE;

   desc->list = eina_list_append(desc->list, (void*)1L);
   node = eina_list_last(desc->list);
   EINA_SAFETY_ON_NULL_RETURN_VAL(node, EINA_FALSE);
   EINA_SAFETY_ON_FALSE_RETURN_VAL(node->data == (void*)1L, EINA_FALSE);

   mem = eina_value_list_node_memory_setup(desc->subtype, node);
   if (!mem)
     {
        desc->list = eina_list_remove_list(desc->list, node);
        return EINA_FALSE;
     }

   if (!eina_value_type_setup(desc->subtype, mem)) goto error_setup;
   if (!eina_value_type_pset(desc->subtype, mem, ptr)) goto error_set;
   return EINA_TRUE;

 error_set:
   eina_value_type_flush(desc->subtype, mem);
 error_setup:
   eina_value_list_node_memory_flush(desc->subtype, node);
   desc->list = eina_list_remove_list(desc->list, node);
   return EINA_FALSE;
}
#undef EINA_VALUE_TYPE_LIST_CHECK_RETURN_VAL

#define EINA_VALUE_TYPE_HASH_CHECK_RETURN_VAL(value, retval)   \
  EINA_SAFETY_ON_NULL_RETURN_VAL(value, retval);                \
  EINA_SAFETY_ON_FALSE_RETURN_VAL(value->type == EINA_VALUE_TYPE_HASH, retval)

static inline Eina_Bool
eina_value_hash_setup(Eina_Value *value, const Eina_Value_Type *subtype, unsigned int buckets_power_size)
{
   Eina_Value_Hash desc = { subtype, buckets_power_size, NULL };
   if (!eina_value_setup(value, EINA_VALUE_TYPE_HASH))
     return EINA_FALSE;
   if (!eina_value_pset(value, &desc))
     {
        eina_value_flush(value);
        return EINA_FALSE;
     }
   return EINA_TRUE;
}

static inline unsigned int
eina_value_hash_population(const Eina_Value *value)
{
   Eina_Value_Hash *desc;
   EINA_VALUE_TYPE_HASH_CHECK_RETURN_VAL(value, 0);
   desc = (Eina_Value_Hash *)eina_value_memory_get(value);
   if (!desc)
     return 0;
   return eina_hash_population(desc->hash);
}

static inline Eina_Bool
eina_value_hash_del(Eina_Value *value, const char *key)
{
   Eina_Value_Hash *desc;
   void *mem;

   EINA_VALUE_TYPE_HASH_CHECK_RETURN_VAL(value, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(key, EINA_FALSE);
   desc = (Eina_Value_Hash *)eina_value_memory_get(value);
   if (!desc)
     return EINA_FALSE;

   mem = eina_hash_find(desc->hash, key);
   if (!mem)
     return EINA_FALSE;

   eina_value_type_flush(desc->subtype, mem);
   free(mem);
   eina_hash_del_by_key(desc->hash, key);
   return EINA_TRUE;
}

static inline Eina_Bool
eina_value_hash_vset(Eina_Value *value, const char *key, va_list args)
{
   Eina_Value_Hash *desc;
   void *mem;

   EINA_VALUE_TYPE_HASH_CHECK_RETURN_VAL(value, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(key, EINA_FALSE);
   desc = (Eina_Value_Hash *)eina_value_memory_get(value);
   if (!desc)
     return EINA_FALSE;

   mem = eina_hash_find(desc->hash, key);
   if (!mem)
     {
        mem = malloc(desc->subtype->value_size);
        if (!mem)
          {
             eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
             return EINA_FALSE;
          }
        if (!eina_hash_add(desc->hash, key, mem))
          {
             free(mem);
             return EINA_FALSE;
          }
        if (!eina_value_type_setup(desc->subtype, mem))
          {
             eina_value_type_flush(desc->subtype, mem);
             eina_hash_del_by_key(desc->hash, key);
             free(mem);
          }
     }

   return eina_value_type_vset(desc->subtype, mem, args);
}

static inline Eina_Bool
eina_value_hash_vget(const Eina_Value *value, const char *key, va_list args)
{
   const Eina_Value_Hash *desc;
   const void *mem;
   void *ptr;
   Eina_Bool ret;

   EINA_VALUE_TYPE_HASH_CHECK_RETURN_VAL(value, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(key, EINA_FALSE);
   desc = (const Eina_Value_Hash *)eina_value_memory_get(value);
   if (!desc)
     return EINA_FALSE;

   mem = eina_hash_find(desc->hash, key);
   if (!mem)
     return EINA_FALSE;

   ptr = va_arg(args, void *);
   ret = eina_value_type_pget(desc->subtype, mem, ptr);
   return ret;
}

static inline Eina_Bool
eina_value_hash_set(Eina_Value *value, const char *key, ...)
{
   va_list args;
   Eina_Bool ret;
   va_start(args, key);
   ret = eina_value_hash_vset(value, key, args);
   va_end(args);
   return ret;
}

static inline Eina_Bool
eina_value_hash_get(const Eina_Value *value, const char *key, ...)
{
   va_list args;
   Eina_Bool ret;
   va_start(args, key);
   ret = eina_value_hash_vget(value, key, args);
   va_end(args);
   return ret;
}

static inline Eina_Bool
eina_value_hash_pset(Eina_Value *value, const char *key, const void *ptr)
{
   Eina_Value_Hash *desc;
   void *mem;

   EINA_VALUE_TYPE_HASH_CHECK_RETURN_VAL(value, 0);
   EINA_SAFETY_ON_NULL_RETURN_VAL(key, EINA_FALSE);
   desc = (Eina_Value_Hash *)eina_value_memory_get(value);
   if (!desc)
     return EINA_FALSE;

   mem = eina_hash_find(desc->hash, key);
   if (!mem)
     {
        mem = malloc(desc->subtype->value_size);
        if (!mem)
          {
             eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
             return EINA_FALSE;
          }
        if (!eina_hash_add(desc->hash, key, mem))
          {
             free(mem);
             return EINA_FALSE;
          }
        if (!eina_value_type_setup(desc->subtype, mem))
          {
             eina_value_type_flush(desc->subtype, mem);
             eina_hash_del_by_key(desc->hash, key);
             free(mem);
          }
     }

   return eina_value_type_pset(desc->subtype, mem, ptr);
}

static inline Eina_Bool
eina_value_hash_pget(const Eina_Value *value, const char *key, void *ptr)
{
   const Eina_Value_Hash *desc;
   const void *mem;
   Eina_Bool ret;

   EINA_VALUE_TYPE_HASH_CHECK_RETURN_VAL(value, 0);
   EINA_SAFETY_ON_NULL_RETURN_VAL(key, EINA_FALSE);
   desc = (const Eina_Value_Hash *)eina_value_memory_get(value);
   if (!desc)
     return EINA_FALSE;

   mem = eina_hash_find(desc->hash, key);
   if (!mem)
     return EINA_FALSE;

   ret = eina_value_type_pget(desc->subtype, mem, ptr);
   return ret;
}
#undef EINA_VALUE_TYPE_HASH_CHECK_RETURN_VAL

#define EINA_VALUE_TYPE_STRUCT_CHECK_RETURN_VAL(value, retval)          \
  EINA_SAFETY_ON_NULL_RETURN_VAL(value, retval);                        \
  EINA_SAFETY_ON_FALSE_RETURN_VAL(value->type == EINA_VALUE_TYPE_STRUCT, retval)

/**
 * @brief Find member of struct
 * @since 1.2
 * @internal
 */
EAPI const Eina_Value_Struct_Member *eina_value_struct_member_find(const Eina_Value_Struct *st, const char *name) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT;

static inline Eina_Bool
eina_value_struct_setup(Eina_Value *value, const Eina_Value_Struct_Desc *sdesc)
{
   Eina_Value_Struct desc = {sdesc, NULL};
   if (!eina_value_setup(value, EINA_VALUE_TYPE_STRUCT))
     return EINA_FALSE;
   if (!eina_value_pset(value, &desc))
     {
        eina_value_flush(value);
        return EINA_FALSE;
     }
   return EINA_TRUE;
}

static inline void *
eina_value_struct_member_memory_get(const Eina_Value_Struct *st, const Eina_Value_Struct_Member *member)
{
   unsigned char *base = (unsigned char *)st->memory;
   if (!base) return NULL;
   return base + member->offset;
}

static inline Eina_Bool
eina_value_struct_vset(Eina_Value *value, const char *name, va_list args)
{
   const Eina_Value_Struct_Member *member;
   Eina_Value_Struct *st;
   void *mem;

   EINA_VALUE_TYPE_STRUCT_CHECK_RETURN_VAL(value, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE);
   st = (Eina_Value_Struct *)eina_value_memory_get(value);
   if (!st)
     return EINA_FALSE;
   member = eina_value_struct_member_find(st, name);
   if (!member)
     return EINA_FALSE;
   mem = eina_value_struct_member_memory_get(st, member);
   if (!mem)
     return EINA_FALSE;

   return eina_value_type_vset(member->type, mem, args);
}

static inline Eina_Bool
eina_value_struct_vget(const Eina_Value *value, const char *name, va_list args)
{
   const Eina_Value_Struct_Member *member;
   const Eina_Value_Struct *st;
   const void *mem;
   void *ptr;
   Eina_Bool ret;

   EINA_VALUE_TYPE_STRUCT_CHECK_RETURN_VAL(value, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE);
   st = (const Eina_Value_Struct *)eina_value_memory_get(value);
   if (!st)
     return EINA_FALSE;
   member = eina_value_struct_member_find(st, name);
   if (!member)
     return EINA_FALSE;
   mem = eina_value_struct_member_memory_get(st, member);
   if (!mem)
     return EINA_FALSE;

   ptr = va_arg(args, void *);
   ret = eina_value_type_pget(member->type, mem, ptr);
   return ret;
}

static inline Eina_Bool
eina_value_struct_set(Eina_Value *value, const char *name, ...)
{
   va_list args;
   Eina_Bool ret;
   va_start(args, name);
   ret = eina_value_struct_vset(value, name, args);
   va_end(args);
   return ret;
}

static inline Eina_Bool
eina_value_struct_get(const Eina_Value *value, const char *name, ...)
{
   va_list args;
   Eina_Bool ret;
   va_start(args, name);
   ret = eina_value_struct_vget(value, name, args);
   va_end(args);
   return ret;
}

static inline Eina_Bool
eina_value_struct_pset(Eina_Value *value, const char *name, const void *ptr)
{
   const Eina_Value_Struct_Member *member;
   Eina_Value_Struct *st;
   void *mem;

   EINA_VALUE_TYPE_STRUCT_CHECK_RETURN_VAL(value, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(ptr, EINA_FALSE);
   st = (Eina_Value_Struct *)eina_value_memory_get(value);
   if (!st)
     return EINA_FALSE;
   member = eina_value_struct_member_find(st, name);
   if (!member)
     return EINA_FALSE;
   mem = eina_value_struct_member_memory_get(st, member);
   if (!mem)
     return EINA_FALSE;

   return eina_value_type_pset(member->type, mem, ptr);
}

static inline Eina_Bool
eina_value_struct_pget(const Eina_Value *value, const char *name, void *ptr)
{
   const Eina_Value_Struct_Member *member;
   const Eina_Value_Struct *st;
   const void *mem;
   Eina_Bool ret;

   EINA_VALUE_TYPE_STRUCT_CHECK_RETURN_VAL(value, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(ptr, EINA_FALSE);
   st = (const Eina_Value_Struct *)eina_value_memory_get(value);
   if (!st)
     return EINA_FALSE;
   member = eina_value_struct_member_find(st, name);
   if (!member)
     return EINA_FALSE;
   mem = eina_value_struct_member_memory_get(st, member);
   if (!mem)
     return EINA_FALSE;

   ret = eina_value_type_pget(member->type, mem, ptr);
   return ret;
}

static inline Eina_Bool
eina_value_struct_value_get(const Eina_Value *src, const char *name, Eina_Value *dst)
{
   const Eina_Value_Struct_Member *member;
   const Eina_Value_Struct *st;
   const void *mem;

   EINA_VALUE_TYPE_STRUCT_CHECK_RETURN_VAL(src, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(dst, EINA_FALSE);
   st = (const Eina_Value_Struct *)eina_value_memory_get(src);
   if (!st)
     return EINA_FALSE;
   member = eina_value_struct_member_find(st, name);
   if (!member)
     return EINA_FALSE;
   mem = eina_value_struct_member_memory_get(st, member);
   if (!mem)
     return EINA_FALSE;
   if (!eina_value_setup(dst, member->type))
     return EINA_FALSE;
   if (!eina_value_pset(dst, mem))
     {
        eina_value_flush(dst);
        return EINA_FALSE;
     }
   return EINA_TRUE;
}

static inline Eina_Bool
eina_value_struct_value_set(Eina_Value *dst, const char *name, const Eina_Value *src)
{
   const Eina_Value_Struct_Member *member;
   Eina_Value_Struct *st;
   void *mem;
   const void *ptr;

   EINA_VALUE_TYPE_STRUCT_CHECK_RETURN_VAL(dst, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(src, EINA_FALSE);

   st = (Eina_Value_Struct *)eina_value_memory_get(dst);
   if (!st)
     return EINA_FALSE;
   member = eina_value_struct_member_find(st, name);
   if (!member)
     return EINA_FALSE;
   EINA_SAFETY_ON_FALSE_RETURN_VAL(src->type == member->type, EINA_FALSE);

   mem = eina_value_struct_member_memory_get(st, member);
   if (!mem)
     return EINA_FALSE;

   ptr = eina_value_memory_get(src);
   if (!ptr)
     return EINA_FALSE;

   return eina_value_type_pset(member->type, mem, ptr);
}

static inline Eina_Bool
eina_value_struct_member_value_get(const Eina_Value *src, const Eina_Value_Struct_Member *member, Eina_Value *dst)
{
   const Eina_Value_Struct *st;
   const void *mem;

   EINA_VALUE_TYPE_STRUCT_CHECK_RETURN_VAL(src, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(member, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(dst, EINA_FALSE);
   st = (const Eina_Value_Struct *)eina_value_memory_get(src);
   if (!st)
     return EINA_FALSE;
   mem = eina_value_struct_member_memory_get(st, member);
   if (!mem)
     return EINA_FALSE;
   if (!eina_value_setup(dst, member->type))
     return EINA_FALSE;
   if (!eina_value_pset(dst, mem))
     {
        eina_value_flush(dst);
        return EINA_FALSE;
     }
   return EINA_TRUE;
}

static inline Eina_Bool
eina_value_struct_member_value_set(Eina_Value *dst, const Eina_Value_Struct_Member *member, const Eina_Value *src)
{
   Eina_Value_Struct *st;
   void *mem;
   const void *ptr;

   EINA_VALUE_TYPE_STRUCT_CHECK_RETURN_VAL(dst, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(member, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(src, EINA_FALSE);

   st = (Eina_Value_Struct *)eina_value_memory_get(dst);
   if (!st)
     return EINA_FALSE;
   EINA_SAFETY_ON_FALSE_RETURN_VAL(src->type == member->type, EINA_FALSE);

   mem = eina_value_struct_member_memory_get(st, member);
   if (!mem)
     return EINA_FALSE;

   ptr = eina_value_memory_get(src);
   if (!ptr)
     return EINA_FALSE;

   return eina_value_type_pset(member->type, mem, ptr);
}

#undef EINA_VALUE_TYPE_STRUCT_CHECK_RETURN_VAL


static inline Eina_Bool
eina_value_type_setup(const Eina_Value_Type *type, void *mem)
{
   EINA_SAFETY_ON_FALSE_RETURN_VAL(eina_value_type_check(type), EINA_FALSE);
   if (!type->setup)
     {
        eina_error_set(EINA_ERROR_VALUE_FAILED);
        return EINA_FALSE;
     }
   return type->setup(type, mem);
}

static inline Eina_Bool
eina_value_type_flush(const Eina_Value_Type *type, void *mem)
{
   EINA_SAFETY_ON_FALSE_RETURN_VAL(eina_value_type_check(type), EINA_FALSE);
   if (!type->flush)
     {
        eina_error_set(EINA_ERROR_VALUE_FAILED);
        return EINA_FALSE;
     }
   return type->flush(type, mem);
}

static inline Eina_Bool
eina_value_type_copy(const Eina_Value_Type *type, const void *src, void *dst)
{
   EINA_SAFETY_ON_FALSE_RETURN_VAL(eina_value_type_check(type), EINA_FALSE);
   if (!type->copy)
     {
        eina_error_set(EINA_ERROR_VALUE_FAILED);
        return EINA_FALSE;
     }
   return type->copy(type, src, dst);
}

static inline int
eina_value_type_compare(const Eina_Value_Type *type, const void *a, const void *b)
{
   EINA_SAFETY_ON_FALSE_RETURN_VAL(eina_value_type_check(type), EINA_FALSE);
   if (!type->compare)
     {
        eina_error_set(EINA_ERROR_VALUE_FAILED);
        return EINA_FALSE;
     }
   return type->compare(type, a, b);
}

static inline Eina_Bool
eina_value_type_convert_to(const Eina_Value_Type *type, const Eina_Value_Type *convert, const void *type_mem, void *convert_mem)
{
   EINA_SAFETY_ON_FALSE_RETURN_VAL(eina_value_type_check(type), EINA_FALSE);
   if (!type->convert_to)
     {
        eina_error_set(EINA_ERROR_VALUE_FAILED);
        return EINA_FALSE;
     }
   return type->convert_to(type, convert, type_mem, convert_mem);
}

static inline Eina_Bool
eina_value_type_convert_from(const Eina_Value_Type *type, const Eina_Value_Type *convert, void *type_mem, const void *convert_mem)
{
   EINA_SAFETY_ON_FALSE_RETURN_VAL(eina_value_type_check(type), EINA_FALSE);
   if (!type->convert_from)
     {
        eina_error_set(EINA_ERROR_VALUE_FAILED);
        return EINA_FALSE;
     }
   return type->convert_from(type, convert, type_mem, convert_mem);
}

static inline Eina_Bool
eina_value_type_vset(const Eina_Value_Type *type, void *mem, va_list args)
{
   EINA_SAFETY_ON_FALSE_RETURN_VAL(eina_value_type_check(type), EINA_FALSE);
   if (!type->vset)
     {
        eina_error_set(EINA_ERROR_VALUE_FAILED);
        return EINA_FALSE;
     }
   return type->vset(type, mem, args);
}

static inline Eina_Bool
eina_value_type_pset(const Eina_Value_Type *type, void *mem, const void *ptr)
{
   EINA_SAFETY_ON_FALSE_RETURN_VAL(eina_value_type_check(type), EINA_FALSE);
   if (!type->pset)
     {
        eina_error_set(EINA_ERROR_VALUE_FAILED);
        return EINA_FALSE;
     }
   return type->pset(type, mem, ptr);
}

static inline Eina_Bool
eina_value_type_pget(const Eina_Value_Type *type, const void *mem, void *ptr)
{
   EINA_SAFETY_ON_FALSE_RETURN_VAL(eina_value_type_check(type), EINA_FALSE);
   if (!type->pget)
     {
        eina_error_set(EINA_ERROR_VALUE_FAILED);
        return EINA_FALSE;
     }
   return type->pget(type, mem, ptr);
}

#undef EINA_VALUE_TYPE_DEFAULT
#undef EINA_VALUE_TYPE_CHECK_RETURN
#undef EINA_VALUE_TYPE_CHECK_RETURN_VAL
#undef EINA_VALUE_TYPE_DISPATCH
#undef EINA_VALUE_TYPE_DISPATCH_RETURN
#endif
