#ifdef HAVE_CONFIG_H
# include <config.h>
#endif /* ifdef HAVE_CONFIG_H */

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include <limits.h>

#include <Eina.h>

#include "Eet.h"
#include "Eet_private.h"

/*
 * routines for doing data -> struct and struct -> data conversion
 *
 * types:
 *
 * basic types:
 *   a sequence of...
 *
 *   char
 *   short
 *   int
 *   long long
 *   float
 *   double
 *   unsigned char
 *   unsigned short
 *   unsigned int
 *   unsgined long long
 *   string
 *
 * groupings:
 *   multiple entries ordered as...
 *
 *   fixed size array    [ of basic types ]
 *   variable size array [ of basic types ]
 *   linked list         [ of basic types ]
 *   hash table          [ of basic types ]
 *
 * need to provide builder/accessor funcs for:
 *
 *   list_next
 *   list_append
 *
 *   hash_foreach
 *   hash_add
 *
 */

/*---*/

typedef struct _Eet_Data_Element          Eet_Data_Element;
typedef struct _Eet_Data_Basic_Type_Codec Eet_Data_Basic_Type_Codec;
typedef struct _Eet_Data_Group_Type_Codec Eet_Data_Group_Type_Codec;
typedef struct _Eet_Data_Chunk            Eet_Data_Chunk;
typedef struct _Eet_Data_Stream           Eet_Data_Stream;
typedef struct _Eet_Data_Descriptor_Hash  Eet_Data_Descriptor_Hash;
typedef struct _Eet_Data_Encode_Hash_Info Eet_Data_Encode_Hash_Info;
typedef struct _Eet_Free                  Eet_Free;
typedef struct _Eet_Free_Context          Eet_Free_Context;
typedef struct _Eet_Variant_Unknow        Eet_Variant_Unknow;

/*---*/

/* TODO:
 * Eet_Data_Basic_Type_Codec (Coder, Decoder)
 * Eet_Data_Group_Type_Codec (Coder, Decoder)
 */
struct _Eet_Data_Basic_Type_Codec
{
   int         size;
   const char *name;
   int         (*get)(const Eet_Dictionary *ed,
                      const void           *src,
                      const void           *src_end,
                      void                 *dest);
   void      * (*put)(Eet_Dictionary *ed, const void *src, int *size_ret);
};

struct _Eet_Data_Group_Type_Codec
{
   int  (*get)(Eet_Free_Context *context,
               const             Eet_Dictionary *ed,
               Eet_Data_Descriptor *edd,
               Eet_Data_Element *ede,
               Eet_Data_Chunk *echnk,
               int               type,
               int               group_type,
               void             *data_in,
               char            **p,
               int              *size);
   void (*put)(Eet_Dictionary *ed,
               Eet_Data_Descriptor *edd,
               Eet_Data_Element *ede,
               Eet_Data_Stream *ds,
               void           *data_in);
};

struct _Eet_Data_Chunk
{
   const char   *name;
   int           len;
   int           size;
   int           hash;
   void         *data;
   unsigned char type;
   unsigned char group_type;
};

struct _Eet_Data_Stream
{
   void *data;
   int   size;
   int   pos;
};

struct _Eet_Data_Descriptor_Hash
{
   Eet_Data_Element         *element;
   Eet_Data_Descriptor_Hash *next;
};

struct _Eet_Data_Descriptor
{
   const char           *name;
   const Eet_Dictionary *ed;
   int                   size;
   struct
   {
      void      * (*mem_alloc)(size_t size);
      void        (*mem_free)(void *mem);
      char      * (*str_alloc)(const char *str);
      char      * (*str_direct_alloc)(const char *str);
      void        (*str_free)(const char *str);
      void        (*str_direct_free)(const char *str);
      void      * (*list_next)(void *l);
      void      * (*list_append)(void *l, void *d);
      void      * (*list_data)(void *l);
      void      * (*list_free)(void *l);
      void        (*hash_foreach)(void *h,
                                  int (*func)(void *h,
					      const char *k,
					      void       *dt,
					      void       *fdt),
                                  void *fdt);
      void      * (*hash_add)(void *h, const char *k, void *d);
      void        (*hash_free)(void *h);
      const char *(*type_get)(const void *data, Eina_Bool *unknow);
      Eina_Bool   (*type_set)(const char *type,
                              void       *data,
                              Eina_Bool   unknow);
      void      * (*array_alloc)(size_t size);
      void        (*array_free)(void *mem);
   } func;
   struct
   {
      int               num;
      Eet_Data_Element *set;
      struct
      {
         int                       size;
         Eet_Data_Descriptor_Hash *buckets;
      } hash;
   } elements;

   Eina_Bool unified_type : 1;
//   char *strings;
//   int   strings_len;
};

struct _Eet_Data_Element
{
   const char          *name;
   const char          *counter_name;
   const char          *directory_name_ptr;
   Eet_Data_Descriptor *subtype;
   int                  offset;  /* offset in bytes from the base element */
   int                  count;  /* number of elements for a fixed array */
   int                  counter_offset;  /* for a variable array we need the offset of the count variable */
   unsigned char        type;  /* EET_T_XXX */
   unsigned char        group_type;  /* EET_G_XXX */
   Eina_Bool            subtype_free : 1;
};

struct _Eet_Data_Encode_Hash_Info
{
   Eet_Data_Stream  *ds;
   Eet_Data_Element *ede;
   Eet_Dictionary   *ed;
};

struct _Eet_Free
{
   int        ref;
   Eina_Array list;
};

struct _Eet_Free_Context
{
   Eet_Free freelist;
   Eet_Free freelist_array;
   Eet_Free freelist_list;
   Eet_Free freelist_hash;
   Eet_Free freelist_str;
   Eet_Free freelist_direct_str;
};

struct _Eet_Variant_Unknow
{
   EINA_MAGIC

   int  size;
   char data[1];
};

/*---*/

static void
 eet_free_context_init(Eet_Free_Context *context);
static void
 eet_free_context_shutdown(Eet_Free_Context *context);

static int
eet_data_get_char(const Eet_Dictionary *ed,
                  const void           *src,
                  const void           *src_end,
                  void                 *dest);
static void *
eet_data_put_char(Eet_Dictionary *ed,
                  const void     *src,
                  int            *size_ret);
static int
eet_data_get_short(const Eet_Dictionary *ed,
                   const void           *src,
                   const void           *src_end,
                   void                 *dest);
static void *
eet_data_put_short(Eet_Dictionary *ed,
                   const void     *src,
                   int            *size_ret);
static inline int
eet_data_get_int(const Eet_Dictionary *ed,
                 const void           *src,
                 const void           *src_end,
                 void                 *dest);
static void *
eet_data_put_int(Eet_Dictionary *ed,
                 const void     *src,
                 int            *size_ret);
static int
eet_data_get_long_long(const Eet_Dictionary *ed,
                       const void           *src,
                       const void           *src_end,
                       void                 *dest);
static void *
eet_data_put_long_long(Eet_Dictionary *ed,
                       const void     *src,
                       int            *size_ret);
static int
eet_data_get_float(const Eet_Dictionary *ed,
                   const void           *src,
                   const void           *src_end,
                   void                 *dest);
static void *
eet_data_put_float(Eet_Dictionary *ed,
                   const void     *src,
                   int            *size_ret);
static int
eet_data_get_double(const Eet_Dictionary *ed,
                    const void           *src,
                    const void           *src_end,
                    void                 *dest);
static void *
eet_data_put_double(Eet_Dictionary *ed,
                    const void     *src,
                    int            *size_ret);
static int
eet_data_get_f32p32(const Eet_Dictionary *ed,
                    const void           *src,
                    const void           *src_end,
                    void                 *dest);
static void *
eet_data_put_f32p32(Eet_Dictionary *ed,
                    const void     *src,
                    int            *size_ret);
static int
eet_data_get_f16p16(const Eet_Dictionary *ed,
                    const void           *src,
                    const void           *src_end,
                    void                 *dest);
static void *
eet_data_put_f16p16(Eet_Dictionary *ed,
                    const void     *src,
                    int            *size_ret);
static int
eet_data_get_f8p24(const Eet_Dictionary *ed,
                   const void           *src,
                   const void           *src_end,
                   void                 *dest);
static void *
eet_data_put_f8p24(Eet_Dictionary *ed,
                   const void     *src,
                   int            *size_ret);
static inline int
eet_data_get_string(const Eet_Dictionary *ed,
                    const void           *src,
                    const void           *src_end,
                    void                 *dest);
static void *
eet_data_put_string(Eet_Dictionary *ed,
                    const void     *src,
                    int            *size_ret);
static int
eet_data_get_istring(const Eet_Dictionary *ed,
                     const void           *src,
                     const void           *src_end,
                     void                 *dest);
static void *
eet_data_put_istring(Eet_Dictionary *ed,
                     const void     *src,
                     int            *size_ret);
static int
eet_data_get_null(const Eet_Dictionary *ed,
                  const void           *src,
                  const void           *src_end,
                  void                 *dest);
static void *
eet_data_put_null(Eet_Dictionary *ed,
                  const void     *src,
                  int            *size_ret);

static int
eet_data_get_value(const Eet_Dictionary *ed,
		   const void           *src,
		   const void           *src_end,
		   void                 *dst);

static void *
eet_data_put_value(Eet_Dictionary *ed,
                   const void     *src,
                   int            *size_ret);

static int
eet_data_get_type(const Eet_Dictionary *ed,
                  int                   type,
                  const void           *src,
                  const void           *src_end,
                  void                 *dest);
static void *
eet_data_put_type(Eet_Dictionary *ed,
                  int             type,
                  const void     *src,
                  int            *size_ret);

static Eet_Node *
eet_data_node_simple_type(int         type,
                          const char *name,
                          void       *dd);

static int
eet_data_get_unknown(Eet_Free_Context     *context,
                     const Eet_Dictionary *ed,
                     Eet_Data_Descriptor  *edd,
                     Eet_Data_Element     *ede,
                     Eet_Data_Chunk       *echnk,
                     int                   type,
                     int                   group_type,
                     void                 *data_in,
                     char                **p,
                     int                  *size);
static void
eet_data_put_unknown(Eet_Dictionary      *ed,
                     Eet_Data_Descriptor *edd,
                     Eet_Data_Element    *ede,
                     Eet_Data_Stream     *ds,
                     void                *data_in);
static void
eet_data_put_array(Eet_Dictionary      *ed,
                   Eet_Data_Descriptor *edd,
                   Eet_Data_Element    *ede,
                   Eet_Data_Stream     *ds,
                   void                *data_in);
static int
eet_data_get_array(Eet_Free_Context     *context,
                   const Eet_Dictionary *ed,
                   Eet_Data_Descriptor  *edd,
                   Eet_Data_Element     *ede,
                   Eet_Data_Chunk       *echnk,
                   int                   type,
                   int                   group_type,
                   void                 *data,
                   char                **p,
                   int                  *size);
static int
eet_data_get_list(Eet_Free_Context     *context,
                  const Eet_Dictionary *ed,
                  Eet_Data_Descriptor  *edd,
                  Eet_Data_Element     *ede,
                  Eet_Data_Chunk       *echnk,
                  int                   type,
                  int                   group_type,
                  void                 *data_in,
                  char                **p,
                  int                  *size);
static void
eet_data_put_list(Eet_Dictionary      *ed,
                  Eet_Data_Descriptor *edd,
                  Eet_Data_Element    *ede,
                  Eet_Data_Stream     *ds,
                  void                *data_in);
static void
eet_data_put_hash(Eet_Dictionary      *ed,
                  Eet_Data_Descriptor *edd,
                  Eet_Data_Element    *ede,
                  Eet_Data_Stream     *ds,
                  void                *data_in);
static int
eet_data_get_hash(Eet_Free_Context     *context,
                  const Eet_Dictionary *ed,
                  Eet_Data_Descriptor  *edd,
                  Eet_Data_Element     *ede,
                  Eet_Data_Chunk       *echnk,
                  int                   type,
                  int                   group_type,
                  void                 *data,
                  char                **p,
                  int                  *size);
static void
eet_data_put_union(Eet_Dictionary      *ed,
                   Eet_Data_Descriptor *edd,
                   Eet_Data_Element    *ede,
                   Eet_Data_Stream     *ds,
                   void                *data_in);
static int
eet_data_get_union(Eet_Free_Context     *context,
                   const Eet_Dictionary *ed,
                   Eet_Data_Descriptor  *edd,
                   Eet_Data_Element     *ede,
                   Eet_Data_Chunk       *echnk,
                   int                   type,
                   int                   group_type,
                   void                 *data,
                   char                **p,
                   int                  *size);
static void
eet_data_put_variant(Eet_Dictionary      *ed,
                     Eet_Data_Descriptor *edd,
                     Eet_Data_Element    *ede,
                     Eet_Data_Stream     *ds,
                     void                *data_in);
static int
eet_data_get_variant(Eet_Free_Context     *context,
                     const Eet_Dictionary *ed,
                     Eet_Data_Descriptor  *edd,
                     Eet_Data_Element     *ede,
                     Eet_Data_Chunk       *echnk,
                     int                   type,
                     int                   group_type,
                     void                 *data,
                     char                **p,
                     int                  *size);

static void
eet_data_chunk_get(const Eet_Dictionary *ed,
                   Eet_Data_Chunk       *chnk,
                   const void           *src,
                   int                   size);
static Eet_Data_Chunk *
eet_data_chunk_new(void       *data,
                   int         size,
                   const char *name,
                   int         type,
                   int         group_type);
static void
eet_data_chunk_free(Eet_Data_Chunk *chnk);

static Eet_Data_Stream *
 eet_data_stream_new(void);
static void
 eet_data_stream_write(Eet_Data_Stream *ds,
                      const void       *data,
                      int               size);
static void
eet_data_stream_free(Eet_Data_Stream *ds);

static void
eet_data_chunk_put(Eet_Dictionary  *ed,
                   Eet_Data_Chunk  *chnk,
                   Eet_Data_Stream *ds);

static int
eet_data_descriptor_encode_hash_cb(void       *hash,
                                   const char *key,
                                   void       *hdata,
                                   void       *fdata);
static void *_eet_data_descriptor_encode(Eet_Dictionary      *ed,
                                         Eet_Data_Descriptor *edd,
                                         const void          *data_in,
                                         int                 *size_ret);
static void *_eet_data_descriptor_decode(Eet_Free_Context     *context,
                                         const Eet_Dictionary *ed,
                                         Eet_Data_Descriptor  *edd,
                                         const void           *data_in,
                                         int                   size_in,
                                         void                 *data_out,
                                         int                   size_out);

/*---*/

static const Eet_Data_Basic_Type_Codec eet_basic_codec[] =
{
   {sizeof(char), "char", eet_data_get_char, eet_data_put_char     },
   {sizeof(short), "short", eet_data_get_short, eet_data_put_short    },
   {sizeof(int), "int", eet_data_get_int, eet_data_put_int      },
   {sizeof(long long), "long_long", eet_data_get_long_long, eet_data_put_long_long},
   {sizeof(float), "float", eet_data_get_float, eet_data_put_float    },
   {sizeof(double), "double", eet_data_get_double, eet_data_put_double   },
   {sizeof(char), "uchar", eet_data_get_char, eet_data_put_char     },
   {sizeof(short), "ushort", eet_data_get_short, eet_data_put_short    },
   {sizeof(int), "uint", eet_data_get_int, eet_data_put_int      },
   {sizeof(long long), "ulong_long", eet_data_get_long_long, eet_data_put_long_long},
   {sizeof(char *), "string", eet_data_get_string, eet_data_put_string   },
   {sizeof(char *), "inlined", eet_data_get_istring, eet_data_put_istring  },
   {sizeof(void *), "NULL", eet_data_get_null, eet_data_put_null     },
   {sizeof(Eina_F32p32), "f32p32", eet_data_get_f32p32, eet_data_put_f32p32   },
   {sizeof(Eina_F16p16), "f16p16", eet_data_get_f16p16, eet_data_put_f16p16   },
   {sizeof(Eina_F8p24), "f8p24", eet_data_get_f8p24, eet_data_put_f8p24    },
   {sizeof(Eina_Value*), "eina_value*", eet_data_get_value, eet_data_put_value }
};

static const Eet_Data_Group_Type_Codec eet_group_codec[] =
{
   { eet_data_get_unknown, eet_data_put_unknown },
   { eet_data_get_array, eet_data_put_array },
   { eet_data_get_array, eet_data_put_array },
   { eet_data_get_list, eet_data_put_list },
   { eet_data_get_hash, eet_data_put_hash },
   { eet_data_get_union, eet_data_put_union },
   { eet_data_get_variant, eet_data_put_variant },
   { eet_data_get_unknown, eet_data_put_unknown }
};

static int _eet_data_words_bigendian = -1;

/*---*/

#define SWAP64(x) (x) =                                        \
  ((((unsigned long long)(x) & 0x00000000000000ffULL) << 56) | \
   (((unsigned long long)(x) & 0x000000000000ff00ULL) << 40) | \
   (((unsigned long long)(x) & 0x0000000000ff0000ULL) << 24) | \
   (((unsigned long long)(x) & 0x00000000ff000000ULL) << 8) |  \
   (((unsigned long long)(x) & 0x000000ff00000000ULL) >> 8) |  \
   (((unsigned long long)(x) & 0x0000ff0000000000ULL) >> 24) | \
   (((unsigned long long)(x) & 0x00ff000000000000ULL) >> 40) | \
   (((unsigned long long)(x) & 0xff00000000000000ULL) >> 56))
#define SWAP32(x) (x) =              \
  ((((int)(x) & 0x000000ff) << 24) | \
   (((int)(x) & 0x0000ff00) << 8) |  \
   (((int)(x) & 0x00ff0000) >> 8) |  \
   (((int)(x) & 0xff000000) >> 24))
#define SWAP16(x) (x) =           \
  ((((short)(x) & 0x00ff) << 8) | \
   (((short)(x) & 0xff00) >> 8))

#ifdef CONV8
# undef CONV8
#endif /* ifdef CONV8 */
#ifdef CONV16
# undef CONV16
#endif /* ifdef CONV16 */
#ifdef CONV32
# undef CONV32
#endif /* ifdef CONV32 */
#ifdef CONV64
# undef CONV64
#endif /* ifdef CONV64 */

#define CONV8(x)
#define CONV16(x)             {if (_eet_data_words_bigendian) {SWAP16(x); }}
#define CONV32(x)             {if (_eet_data_words_bigendian) {SWAP32(x); }}
#define CONV64(x)             {if (_eet_data_words_bigendian) {SWAP64(x); }}

#define IS_SIMPLE_TYPE(Type)  (Type > EET_T_UNKNOW && Type < EET_T_LAST)
#define IS_POINTER_TYPE(Type) ((Type >= EET_T_STRING && Type <= EET_T_NULL) || Type == EET_T_VALUE)

#define POINTER_TYPE_DECODE(Context,                    \
                            Ed,                         \
                            Edd,                        \
                            Ede,                        \
                            Echnk,                      \
                            Type,                       \
                            Data,                       \
                            P,                          \
                            Size,                       \
                            Label)                      \
  do {                                                  \
       int ___r;                                        \
       ___r = eet_data_get_unknown(Context,             \
                                   Ed,                  \
                                   Edd, Ede,            \
                                   Echnk,               \
                                   Type, EET_G_UNKNOWN, \
                                   Data, P, Size);      \
       if (!___r) { goto Label; }                       \
    } while (0)

#define STRUCT_TYPE_DECODE(Data_Ret, Context, Ed, Ede, Data, Size, SubSize, Label) \
  do {                                                                             \
       Data_Ret = _eet_data_descriptor_decode(Context,                             \
                                              Ed,                                  \
                                              Ede,                                 \
                                              Data,                                \
                                              Size,                                \
                                              SubSize > 0 ? Data_Ret : NULL,       \
                                              SubSize);                            \
       EINA_SAFETY_ON_NULL_GOTO(Data_Ret, Label);                                 \
    } while (0)

#define EET_I_STRING         1 << 4
#define EET_I_INLINED_STRING 2 << 4
#define EET_I_NULL           3 << 4
#define EET_I_VALUE          4 << 4

#define EET_MAGIC_VARIANT    0xF1234BC
/*---*/

/* CHAR TYPE */
static int
eet_data_get_char(const Eet_Dictionary *ed EINA_UNUSED,
                  const void           *src,
                  const void           *src_end,
                  void                 *dst)
{
   char *s, *d;

   if (((char *)src + sizeof(char)) > (char *)src_end)
     return -1;

   s = (char *)src;
   d = (char *)dst;
   *d = *s;
   CONV8(*d);
   return sizeof(char);
}

static void *
eet_data_put_char(Eet_Dictionary *ed EINA_UNUSED,
                  const void     *src,
                  int            *size_ret)
{
   char *s, *d;

   d = (char *)malloc(sizeof(char));
   if (!d)
     return NULL;

   s = (char *)src;
   *d = *s;
   CONV8(*d);
   *size_ret = sizeof(char);
   return d;
}

/* SHORT TYPE */
static int
eet_data_get_short(const Eet_Dictionary *ed EINA_UNUSED,
                   const void           *src,
                   const void           *src_end,
                   void                 *dst)
{
   short *d;

   if (((char *)src + sizeof(short)) > (char *)src_end)
     return -1;

   memcpy(dst, src, sizeof(short));
   d = (short *)dst;
   CONV16(*d);
   return sizeof(short);
}

static void *
eet_data_put_short(Eet_Dictionary *ed EINA_UNUSED,
                   const void     *src,
                   int            *size_ret)
{
   short *s, *d;

   d = (short *)malloc(sizeof(short));
   if (!d)
     return NULL;

   s = (short *)src;
   *d = *s;
   CONV16(*d);
   *size_ret = sizeof(short);
   return d;
}

/* INT TYPE */
static inline int
eet_data_get_int(const Eet_Dictionary *ed EINA_UNUSED,
                 const void           *src,
                 const void           *src_end,
                 void                 *dst)
{
   int *d;

   if (((char *)src + sizeof(int)) > (char *)src_end)
     return -1;

   memcpy(dst, src, sizeof(int));
   d = (int *)dst;
   CONV32(*d);
   return sizeof(int);
}

static void *
eet_data_put_int(Eet_Dictionary *ed EINA_UNUSED,
                 const void     *src,
                 int            *size_ret)
{
   int *s, *d;

   d = (int *)malloc(sizeof(int));
   if (!d)
     return NULL;

   s = (int *)src;
   *d = *s;
   CONV32(*d);
   *size_ret = sizeof(int);
   return d;
}

/* LONG LONG TYPE */
static int
eet_data_get_long_long(const Eet_Dictionary *ed EINA_UNUSED,
                       const void           *src,
                       const void           *src_end,
                       void                 *dst)
{
   unsigned long long *d;

   if (((char *)src + sizeof(unsigned long long)) > (char *)src_end)
     return -1;

   memcpy(dst, src, sizeof(unsigned long long));
   d = (unsigned long long *)dst;
   CONV64(*d);
   return sizeof(unsigned long long);
}

static void *
eet_data_put_long_long(Eet_Dictionary *ed EINA_UNUSED,
                       const void     *src,
                       int            *size_ret)
{
   unsigned long long *s, *d;

   d = (unsigned long long *)malloc(sizeof(unsigned long long));
   if (!d)
     return NULL;

   s = (unsigned long long *)src;
   *d = *s;
   CONV64(*d);
   *size_ret = sizeof(unsigned long long);
   return d;
}

/* STRING TYPE */
static inline int
eet_data_get_string_hash(const Eet_Dictionary *ed,
                         const void           *src,
                         const void           *src_end)
{
   if (ed)
     {
        int idx;

        if (eet_data_get_int(ed, src, src_end, &idx) < 0)
          return -1;

        return eet_dictionary_string_get_hash_unlocked(ed, idx);
     }

   return -1;
}

static inline int
eet_data_get_string(const Eet_Dictionary *ed,
                    const void           *src,
                    const void           *src_end,
                    void                 *dst)
{
   char *s, **d;

   d = (char **)dst;

   if (ed)
     {
        const char *str;
        int idx;

        if (eet_data_get_int(ed, src, src_end, &idx) < 0)
          return -1;

        str = eet_dictionary_string_get_char_unlocked(ed, idx);
        if (!str)
          return -1;

        *d = (char *)str;
        return eet_dictionary_string_get_size_unlocked(ed, idx);
     }

   s = (char *)src;
   if (!s)
     {
        *d = NULL;
        return 0;
     }

   *d = s;
   return strlen(s) + 1;
}

static void *
eet_data_put_string(Eet_Dictionary *ed,
                    const void     *src,
                    int            *size_ret)
{
   char *s, *d;
   int len;

   if (ed)
     {
        const char *str;
        int idx;

        str = *((const char **)src);
        if (!str)
          return NULL;

        idx = eet_dictionary_string_add(ed, str);
        if (idx == -1)
          return NULL;

        return eet_data_put_int(ed, &idx, size_ret);
     }

   s = (char *)(*((char **)src));
   if (!s)
     return NULL;

   len = strlen(s);
   d = malloc(len + 1);
   if (!d)
     return NULL;

   memcpy(d, s, len + 1);
   *size_ret = len + 1;
   return d;
}

/* ALWAYS INLINED STRING TYPE */
static int
eet_data_get_istring(const Eet_Dictionary *ed EINA_UNUSED,
                     const void           *src,
                     const void           *src_end,
                     void                 *dst)
{
   return eet_data_get_string(NULL, src, src_end, dst);
}

static void *
eet_data_put_istring(Eet_Dictionary *ed EINA_UNUSED,
                     const void     *src,
                     int            *size_ret)
{
   return eet_data_put_string(NULL, src, size_ret);
}

/* ALWAYS NULL TYPE */
static int
eet_data_get_null(const Eet_Dictionary *ed EINA_UNUSED,
                  const void           *src EINA_UNUSED,
                  const void           *src_end EINA_UNUSED,
                  void                 *dst)
{
   char **d;

   d = (char **)dst;

   *d = NULL;
   return 1;
}

static void *
eet_data_put_null(Eet_Dictionary *ed EINA_UNUSED,
                  const void     *src EINA_UNUSED,
                  int            *size_ret)
{
   *size_ret = 0;
   return NULL;
}

/**
 * Fast lookups of simple doubles/floats.
 *
 * These aren't properly a cache because they don't store pre-calculated
 * values, but have a so simple math that is almost as fast.
 */
static inline int
_eet_data_float_cache_get(const char *s,
                          int         len,
                          float      *d)
{
   /* fast handle of simple case 0xMp+E*/
    if ((len == 6) && (s[0] == '0') && (s[1] == 'x') && (s[3] == 'p'))
      {
         int mantisse = (s[2] >= 'a') ? (s[2] - 'a' + 10) : (s[2] - '0');
         int exponent = (s[5] - '0');

         if (s[4] == '+')
           *d = (float)(mantisse << exponent);
         else
           *d = (float)mantisse / (float)(1 << exponent);

         return 1;
      }

    return 0;
}

static inline int
_eet_data_double_cache_get(const char *s,
                           int         len,
                           double     *d)
{
   /* fast handle of simple case 0xMp+E*/
    if ((len == 6) && (s[0] == '0') && (s[1] == 'x') && (s[3] == 'p'))
      {
         int mantisse = (s[2] >= 'a') ? (s[2] - 'a' + 10) : (s[2] - '0');
         int exponent = (s[5] - '0');

         if (s[4] == '+')
           *d = (double)(mantisse << exponent);
         else
           *d = (double)mantisse / (double)(1 << exponent);

         return 1;
      }

    return 0;
}

/* FLOAT TYPE */
static int
eet_data_get_float(const Eet_Dictionary *ed,
                   const void           *src,
                   const void           *src_end,
                   void                 *dst)
{
   float *d;
   int idx;

   d = (float *)dst;
   if (!ed)
     {
        const char *s, *p;
        long long mantisse;
        long exponent;
        int len;

        s = (const char *)src;
        p = s;
        len = 0;
        while ((p < (const char *)src_end) && (*p != 0)) {len++; p++; }

        if (_eet_data_float_cache_get(s, len, d) != 0)
          return len + 1;

        if (eina_convert_atod(s, len, &mantisse, &exponent) == EINA_FALSE)
          return -1;

        *d = (float)ldexp((double)mantisse, exponent);

        return len + 1;
     }

   if (eet_data_get_int(ed, src, src_end, &idx) < 0)
     return -1;

   if (!eet_dictionary_string_get_float_unlocked(ed, idx, d))
     return -1;

   return 1;
}

static void *
eet_data_put_float(Eet_Dictionary *ed,
                   const void     *src,
                   int            *size_ret)
{
   char buf[128];
   int idx;

   if (!eina_convert_dtoa((double)(*(float *)src), buf))
     return NULL;

   if (!ed)
     {
        char *d;
        int len;

        len = strlen(buf);
        d = malloc(len + 1);
        if (!d)
          return NULL;

        memcpy(d, buf, len + 1);
        *size_ret = len + 1;
        return d;
     }

   idx = eet_dictionary_string_add(ed, buf);
   if (idx == -1)
     return NULL;

   return eet_data_put_int(ed, &idx, size_ret);
}

/* DOUBLE TYPE */
static int
eet_data_get_double(const Eet_Dictionary *ed,
                    const void           *src,
                    const void           *src_end,
                    void                 *dst)
{
   double *d;
   int idx;

   d = (double *)dst;

   if (!ed)
     {
        const char *s, *p;
        long long mantisse = 0;
        long exponent = 0;
        int len;

        s = (const char *)src;
        p = s;
        len = 0;
        while ((p < (const char *)src_end) && (*p != 0)) {len++; p++; }

        if (_eet_data_double_cache_get(s, len, d) != 0)
          return len + 1;

        if (eina_convert_atod(s, len, &mantisse, &exponent) == EINA_FALSE)
          return -1;

        *d = ldexp((double)mantisse, exponent);

        return len + 1;
     }

   if (eet_data_get_int(ed, src, src_end, &idx) < 0)
     return -1;

   if (!eet_dictionary_string_get_double_unlocked(ed, idx, d))
     return -1;

   return 1;
}

static void *
eet_data_put_double(Eet_Dictionary *ed,
                    const void     *src,
                    int            *size_ret)
{
   char buf[128];
   int idx;

   if (!eina_convert_dtoa((double)(*(double *)src), buf))
     return NULL;

   if (!ed)
     {
        char *d;
        int len;

        len = strlen(buf);
        d = malloc(len + 1);
        if (!d)
          return NULL;

        memcpy(d, buf, len + 1);
        *size_ret = len + 1;

        return d;
     }

   idx = eet_dictionary_string_add(ed, buf);
   if (idx == -1)
     return NULL;

   return eet_data_put_int(ed, &idx, size_ret);
}

static int
eet_data_get_f32p32(const Eet_Dictionary *ed,
                    const void           *src,
                    const void           *src_end,
                    void                 *dst)
{
   Eina_F32p32 *fp;
   int idx;

   fp = (Eina_F32p32 *)dst;

   if (!ed)
     {
        const char *s, *p;
        int len;

        s = (const char *)src;
        p = s;
        len = 0;
        while ((p < (const char *)src_end) && (*p != 0)) { len++; p++; }

        if (!(eina_convert_atofp(s, len, fp)))
          return -1;

        return 1;
     }

   if (eet_data_get_int(ed, src, src_end, &idx) < 0)
     return -1;

   if (!eet_dictionary_string_get_fp_unlocked(ed, idx, fp))
     return -1;

   return 1;
}

static void *
eet_data_put_f32p32(Eet_Dictionary *ed,
                    const void     *src,
                    int            *size_ret)
{
   char buf[128];
   int idx;

   eina_convert_fptoa((Eina_F32p32)(*(Eina_F32p32 *)src), buf);

   if (!ed)
     {
        char *d;
        int len;

        len = strlen(buf);
        d = malloc(len + 1);
        if (!d)
          return NULL;

        memcpy(d, buf, len + 1);
        *size_ret = len + 1;

        return d;
     }

   idx = eet_dictionary_string_add(ed, buf);
   if (idx == -1)
     return NULL;

   return eet_data_put_int(ed, &idx, size_ret);
}

static int
eet_data_get_f16p16(const Eet_Dictionary *ed,
                    const void           *src,
                    const void           *src_end,
                    void                 *dst)
{
   Eina_F32p32 tmp;
   Eina_F16p16 *fp;

   fp = (Eina_F16p16 *)dst;

   if (eet_data_get_f32p32(ed, src, src_end, &tmp) < 0)
     return -1;

   *fp = eina_f32p32_to_f16p16(tmp);
   return 1;
}

static void *
eet_data_put_f16p16(Eet_Dictionary *ed,
                    const void     *src,
                    int            *size_ret)
{
   Eina_F32p32 tmp;

   tmp = eina_f16p16_to_f32p32((Eina_F16p16)(*(Eina_F16p16 *)src));
   return eet_data_put_f32p32(ed, &tmp, size_ret);
}

static int
eet_data_get_f8p24(const Eet_Dictionary *ed,
                   const void           *src,
                   const void           *src_end,
                   void                 *dst)
{
   Eina_F32p32 tmp;
   Eina_F8p24 *fp;

   fp = (Eina_F8p24 *)dst;

   if (eet_data_get_f32p32(ed, src, src_end, &tmp) < 0)
     return -1;

   *fp = eina_f32p32_to_f8p24(tmp);
   return 1;
}

static void *
eet_data_put_f8p24(Eet_Dictionary *ed,
                   const void     *src,
                   int            *size_ret)
{
   Eina_F32p32 tmp;

   tmp = eina_f8p24_to_f32p32((Eina_F8p24)(*(Eina_F8p24 *)src));
   return eet_data_put_f32p32(ed, &tmp, size_ret);
}

static const Eina_Value_Type *
_eet_type_to_eina_value_get(int eet_type)
{
   switch (eet_type)
     {
      case EET_T_UCHAR: return EINA_VALUE_TYPE_UCHAR;
      case EET_T_USHORT: return EINA_VALUE_TYPE_USHORT;
      case EET_T_UINT: return EINA_VALUE_TYPE_UINT;
#if SIZEOF_LONG == SIZEOF_INT
      /* case EET_T_UINT: return EINA_VALUE_TYPE_ULONG; */
      /* case EET_T_UINT: return EINA_VALUE_TYPE_TIMESTAMP; */
#else
      /* case EET_T_ULONG_LONG: return EINA_VALUE_TYPE_ULONG; */
      /* case EET_T_ULONG_LONG: return EINA_VALUE_TYPE_TIMESTAMP; */
#endif
      case EET_T_ULONG_LONG: return EINA_VALUE_TYPE_UINT64;
      case EET_T_CHAR: return EINA_VALUE_TYPE_CHAR;
      case EET_T_SHORT: return EINA_VALUE_TYPE_SHORT;
      case EET_T_INT: return EINA_VALUE_TYPE_INT;
#if SIZEOF_LONG == SIZEOF_INT
      /* case EET_T_INT: return EINA_VALUE_TYPE_LONG; */
#else
      /* case EET_T_LONG_LONG: return EINA_VALUE_TYPE_LONG; */
#endif
      case EET_T_LONG_LONG: return EINA_VALUE_TYPE_INT64;
      case EET_T_FLOAT: return EINA_VALUE_TYPE_FLOAT;
      case EET_T_DOUBLE: return EINA_VALUE_TYPE_DOUBLE;
      case EET_T_STRING: return EINA_VALUE_TYPE_STRING;
      /* case EET_T_STRING: return EINA_VALUE_TYPE_STRINGSHARE; */
     }

   return NULL;
}

static int
_eina_value_to_eet_type_get(const Eina_Value_Type *eina_type)
{
   if (eina_type == EINA_VALUE_TYPE_UCHAR) return EET_T_UCHAR;
   else if (eina_type == EINA_VALUE_TYPE_USHORT) return EET_T_USHORT;
   else if (eina_type == EINA_VALUE_TYPE_UINT) return EET_T_UINT;
#if SIZEOF_LONG == SIZEOF_INT
   else if (eina_type == EINA_VALUE_TYPE_ULONG) return EET_T_UINT;
   else if (eina_type == EINA_VALUE_TYPE_TIMESTAMP) return EET_T_UINT;
#else
   else if (eina_type == EINA_VALUE_TYPE_ULONG) return EET_T_ULONG_LONG;
   else if (eina_type == EINA_VALUE_TYPE_TIMESTAMP) return EET_T_ULONG_LONG;
#endif
   else if (eina_type == EINA_VALUE_TYPE_UINT64) return EET_T_ULONG_LONG;
   else if (eina_type == EINA_VALUE_TYPE_CHAR) return EET_T_CHAR;
   else if (eina_type == EINA_VALUE_TYPE_SHORT) return EET_T_SHORT;
   else if (eina_type == EINA_VALUE_TYPE_INT) return EET_T_INT;
#if SIZEOF_LONG == SIZEOF_INT
   else if (eina_type == EINA_VALUE_TYPE_LONG) return EET_T_INT;
#else
   else if (eina_type == EINA_VALUE_TYPE_LONG) return EET_T_LONG_LONG;
#endif
   else if (eina_type == EINA_VALUE_TYPE_INT64) return EET_T_LONG_LONG;
   else if (eina_type == EINA_VALUE_TYPE_FLOAT) return EET_T_FLOAT;
   else if (eina_type == EINA_VALUE_TYPE_DOUBLE) return EET_T_DOUBLE;
   else if (eina_type == EINA_VALUE_TYPE_STRING) return EET_T_STRING;
   else if (eina_type == EINA_VALUE_TYPE_STRINGSHARE) return EET_T_STRING;
   // always fallback to try a conversion to string if possible
   return EET_T_STRING;
}

static int
eet_data_get_value(const Eet_Dictionary *ed,
		   const void           *src,
		   const void           *src_end,
		   void                 *dst)
{
   const Eina_Value_Type *eina_type;
   void *tmp;
   int eet_type;
   int eet_size, type_size;

   eet_size = eet_data_get_int(ed, src, src_end, &eet_type);
   if (eet_size < 0 ||
       eet_type <= EET_T_UNKNOW ||
       eet_type >= EET_T_VALUE)
     return -1;

   tmp = alloca(eet_basic_codec[eet_type - 1].size);
   type_size = eet_basic_codec[eet_type - 1].get(ed, (char*) src + eet_size, src_end, tmp);

   if (eet_type == EET_T_NULL)
     {
        Eina_Value **value = dst;

        *value = NULL;

        return eet_size + type_size;
     }

   eina_type = _eet_type_to_eina_value_get(eet_type);
   if (eina_type)
     {
        Eina_Value **value = dst;

        *value = eina_value_new(eina_type);
        if (!eina_value_pset(*value, tmp)) return -1;

        return eet_size + type_size;
     }

   return -1;
}

static void *
eet_data_put_value(Eet_Dictionary *ed,
		   const void     *src,
		   int            *size_ret)
{
   const Eina_Value *value = *(Eina_Value **)src;
   const Eina_Value_Type *value_type;
   void *int_data;
   void *type_data;
   int int_size, type_size;
   int eet_type;
   void *tmp;
   Eina_Bool v2s = EINA_FALSE;

   // map empty Eina_Value to EET_T_NULL;
   if (!value)
     {
        eet_type = EET_T_NULL;
        goto lookup_done;
     }

   value_type = eina_value_type_get(value);
   eet_type = _eina_value_to_eet_type_get(value_type);

 lookup_done:
   tmp = alloca(eet_basic_codec[eet_type - 1].size);
   if (value) eina_value_get(value, tmp);
   else *(void**) tmp = NULL;

   // handle non simple case by forcing them to convert to string
   if ((eet_type == EET_T_STRING) &&
       (*(char**)tmp == NULL))
     {
        *(char**)tmp = eina_value_to_string(value);
        v2s = EINA_TRUE;
     }

   int_data = eet_data_put_int(ed, &eet_type, &int_size);
   type_data = eet_basic_codec[eet_type - 1].put(ed, tmp, &type_size);

   // free temporary string as it is not needed anymore
   if (v2s) free(*(char**)tmp);

   // pack data with type first, then the data
   *size_ret = int_size + type_size;
   tmp = malloc(*size_ret);
   memcpy(tmp, int_data, int_size);
   memcpy(((char*)tmp) + int_size, type_data, type_size);

   free(int_data);
   free(type_data);

   return tmp;
}

static inline int
eet_data_get_type(const Eet_Dictionary *ed,
                  int                   type,
                  const void           *src,
                  const void           *src_end,
                  void                 *dest)
{
   int ret;

   ret = eet_basic_codec[type - 1].get(ed, src, src_end, dest);
   return ret;
}

static inline void *
eet_data_put_type(Eet_Dictionary *ed,
                  int             type,
                  const void     *src,
                  int            *size_ret)
{
   void *ret;

   ret = eet_basic_codec[type - 1].put(ed, src, size_ret);
   return ret;
}

static inline Eina_Bool
eet_data_type_match(int type1,
                    int type2)
{
   if (type1 == type2)
     return EINA_TRUE;

   /* Note: All floating point type are equivalent and could be read
      without problem by any other floating point getter. */
   switch (type1)
     {
      case EET_T_FLOAT:
      case EET_T_DOUBLE:
      case EET_T_F32P32:
      case EET_T_F16P16:
      case EET_T_F8P24:
        switch (type2)
          {
           case EET_T_FLOAT:
           case EET_T_DOUBLE:
           case EET_T_F32P32:
           case EET_T_F16P16:
           case EET_T_F8P24:
             return EINA_TRUE;

           default:
             break;
          }  /* switch */
        break;

      default:
        break;
     }

   return EINA_FALSE;
}

/* chunk format...
 *
 * char[4] = "CHnK"; // untyped data ... or
 * char[4] = "CHKx"; // typed data - x == type
 *
 * int     = chunk size (including magic string);
 * char[]  = chunk magic/name string (0 byte terminated);
 * ... sub-chunks (a chunk can contain chuncks recusrively) ...
 * or
 * ... payload data ...
 *
 */

static inline void
eet_data_chunk_get(const Eet_Dictionary *ed,
                   Eet_Data_Chunk       *chnk,
                   const void           *src,
                   int                   size)
{
   const char *s;
   int ret1, ret2;

   if (!src)
     return;

   if (size <= 8)
     return;

   if (!chnk)
     return;

   s = src;
   if (s[2] == 'K')
     {
        if ((s[0] != 'C') || (s[1] != 'H') || (s[2] != 'K'))
          return;

        chnk->type = (unsigned char)(s[3]);
        if (chnk->type >= EET_I_LIMIT)
          {
             chnk->group_type =
               ((chnk->type - EET_I_LIMIT) & 0xF) + EET_G_UNKNOWN;
             switch ((chnk->type - EET_I_LIMIT) & 0xF0)
               {
#define EET_UNMATCH_TYPE(Type) \
case EET_I_ ## Type: chnk->type = EET_T_ ## Type; break;

                  EET_UNMATCH_TYPE(STRING);
                  EET_UNMATCH_TYPE(INLINED_STRING);
                  EET_UNMATCH_TYPE(VALUE);
                  EET_UNMATCH_TYPE(NULL);

                default:
                  return;
               }
          }
        else if (chnk->type > EET_T_LAST)
          {
             chnk->group_type = chnk->type;
             chnk->type = EET_T_UNKNOW;
          }
        else
          chnk->group_type = EET_G_UNKNOWN;
        if ((chnk->type >= EET_T_LAST) ||
            (chnk->group_type >=
             EET_G_LAST))
          {
             chnk->type = 0;
             chnk->group_type = 0;
          }
     }
   else if ((s[0] != 'C') || (s[1] != 'H') || (s[2] != 'n') || (s[3] != 'K'))
     return;

   ret1 = eet_data_get_type(ed, EET_T_INT, (s + 4), (s + size), &(chnk->size));

   if (ret1 <= 0)
     return;

   if ((chnk->size < 0) || ((chnk->size + 8) > size))
     return;

   ret2 = eet_data_get_type(ed, EET_T_STRING, (s + 8), (s + size), &(chnk->name));

   if (ret2 <= 0)
     return;

   chnk->len = ret2;

   /* Precalc hash */
   chnk->hash = eet_data_get_string_hash(ed, (s + 8), (s + size));

   if (ed)
     {
        chnk->data = (char *)src + 4 + ret1 + sizeof(int);
        chnk->size -= sizeof(int);
     }
   else
     {
        chnk->data = (char *)src + 4 + ret1 + chnk->len;
        chnk->size -= chnk->len;
     }

   return;
}

static inline Eet_Data_Chunk *
eet_data_chunk_new(void       *data,
                   int         size,
                   const char *name,
                   int         type,
                   int         group_type)
{
   Eet_Data_Chunk *chnk;

   if (!name)
     return NULL;

   chnk = calloc(1, sizeof(Eet_Data_Chunk));
   if (!chnk)
     return NULL;

   /* Note: Another security, so older eet library could read file
      saved with fixed point value. */
   if (type == EET_T_F32P32
       || type == EET_T_F16P16
       || type == EET_T_F8P24)
     type = EET_T_DOUBLE;

   chnk->name = name;
   chnk->len = strlen(name) + 1;
   chnk->size = size;
   chnk->data = data;
   chnk->type = type;
   chnk->group_type = group_type;
   return chnk;
}

static inline void
eet_data_chunk_free(Eet_Data_Chunk *chnk)
{
   free(chnk);
}

static inline Eet_Data_Stream *
eet_data_stream_new(void)
{
   Eet_Data_Stream *ds;

   ds = calloc(1, sizeof(Eet_Data_Stream));
   if (!ds)
     return NULL;

   return ds;
}

static inline void
eet_data_stream_free(Eet_Data_Stream *ds)
{
   if (ds->data)
     free(ds->data);

   free(ds);
}

static inline void
eet_data_stream_flush(Eet_Data_Stream *ds)
{
   free(ds);
}

static inline void
eet_data_stream_write(Eet_Data_Stream *ds,
                      const void      *data,
                      int              size)
{
   char *p;

   if ((ds->pos + size) > ds->size)
     {
        ds->data = realloc(ds->data, ds->size + size + 512);
        if (!ds->data)
          {
             ds->pos = 0;
             ds->size = 0;
             return;
          }

        ds->size = ds->size + size + 512;
     }
   p = ds->data;
   if (!p) return;
   memcpy(p + ds->pos, data, size);
   ds->pos += size;
}

static void
eet_data_chunk_put(Eet_Dictionary  *ed,
                   Eet_Data_Chunk  *chnk,
                   Eet_Data_Stream *ds)
{
   int *size;
   void *string;
   int s;
   int size_ret = 0;
   int string_ret = 0;
   unsigned char buf[4] = "CHK";

   /* disable this check - it will allow empty chunks to be written. this is
    * right for corner-cases when y have a struct with empty fields (empty
    * strings or empty list ptrs etc.) */
   /* if (!chnk->data && chnk->type != EET_T_NULL) return; */
   /* chunk head */

/*   eet_data_stream_write(ds, "CHnK", 4);*/
   if (chnk->type != EET_T_UNKNOW)
     {
        if (chnk->group_type != EET_G_UNKNOWN)
          {
             int type = EET_I_LIMIT + chnk->group_type - EET_G_UNKNOWN;

             switch (chnk->type)
               {
     /* Only make sense with pointer type. */
#define EET_MATCH_TYPE(Type) \
case EET_T_ ## Type: type += EET_I_ ## Type; break;

                   EET_MATCH_TYPE(STRING);
                   EET_MATCH_TYPE(INLINED_STRING);
                   EET_MATCH_TYPE(VALUE);
                   EET_MATCH_TYPE(NULL);

                 default:
                   return;
               }

             buf[3] = type;
          }
        else
          buf[3] = chnk->type;
     }
   else
     buf[3] = chnk->group_type;

   string = eet_data_put_string(ed, &chnk->name, &string_ret);
   if (!string)
     return;

   /* size of chunk payload data + name */
   s = chnk->size + string_ret;
   size = eet_data_put_int(ed, &s, &size_ret);

   /* FIXME: If something goes wrong the resulting file will be corrupted. */
   EINA_SAFETY_ON_TRUE_GOTO(!size, on_error);

   eet_data_stream_write(ds, buf, 4);

   /* write chunk length */
   eet_data_stream_write(ds, size, size_ret);

   /* write chunk name */
   eet_data_stream_write(ds, string, string_ret);

   /* write payload */
   if (chnk->data)
     eet_data_stream_write(ds, chnk->data, chnk->size);

   free(size);
on_error:
   free(string);
}

/*---*/

static void
_eet_descriptor_hash_new(Eet_Data_Descriptor *edd)
{
   int i;

   edd->elements.hash.size = 1 << 6;
   edd->elements.hash.buckets = calloc(
       1,
       sizeof(Eet_Data_Descriptor_Hash) *
       edd->elements.hash.size);
   for (i = 0; i < edd->elements.num; i++)
     {
        Eet_Data_Element *ede;
        int hash;

        ede = &(edd->elements.set[i]);
        hash = _eet_hash_gen((char *)ede->name, 6);
        if (!edd->elements.hash.buckets[hash].element)
          edd->elements.hash.buckets[hash].element = ede;
        else
          {
             Eet_Data_Descriptor_Hash *bucket;

             bucket = calloc(1, sizeof(Eet_Data_Descriptor_Hash));
             bucket->element = ede;
             bucket->next = edd->elements.hash.buckets[hash].next;
             edd->elements.hash.buckets[hash].next = bucket;
          }
     }
}

static void
_eet_descriptor_hash_free(Eet_Data_Descriptor *edd)
{
   int i;

   for (i = 0; i < edd->elements.hash.size; i++)
     {
        Eet_Data_Descriptor_Hash *bucket, *pbucket;

        bucket = edd->elements.hash.buckets[i].next;
        while (bucket)
          {
             pbucket = bucket;
             bucket = bucket->next;
             free(pbucket);
          }
     }
   if (edd->elements.hash.buckets)
     free(edd->elements.hash.buckets);
}

static Eet_Data_Element *
_eet_descriptor_hash_find(Eet_Data_Descriptor *edd,
                          const char          *name,
                          int                  hash)
{
   Eet_Data_Descriptor_Hash *bucket;

   if (hash < 0)
     hash = _eet_hash_gen(name, 6);
   else
     hash &= 0x3f;

   if (!edd->elements.hash.buckets[hash].element)
     return NULL;  /*
                      When we use the dictionary as a source for chunk name, we will always
                      have the same pointer in name. It's a good idea to just compare pointer
                      instead of running strcmp on both string.
                    */

   if (edd->elements.hash.buckets[hash].element->directory_name_ptr == name)
     return edd->elements.hash.buckets[hash].element;

   if (!strcmp(edd->elements.hash.buckets[hash].element->name, name))
     {
        edd->elements.hash.buckets[hash].element->directory_name_ptr = name;
        return edd->elements.hash.buckets[hash].element;
     }

   bucket = edd->elements.hash.buckets[hash].next;
   while (bucket)
     {
        if (bucket->element->directory_name_ptr == name)
          return bucket->element;

        if (!strcmp(bucket->element->name, name))
          {
             bucket->element->directory_name_ptr = name;
             return bucket->element;
          }

        bucket = bucket->next;
     }
   return NULL;
}

static void *
_eet_mem_alloc(size_t size)
{
   return calloc(1, size);
}

static void
_eet_mem_free(void *mem)
{
   free(mem);
}

static char *
_eet_str_alloc(const char *str)
{
   return strdup(str);
}

static void
_eet_str_free(const char *str)
{
   free((char *)str);
}

static Eina_Hash *
_eet_eina_hash_add_alloc(Eina_Hash  *hash,
                         const char *key,
                         void       *data)
{
   if (!hash)
     hash = eina_hash_string_small_new(NULL);

   if (!hash)
     return NULL;

   eina_hash_add(hash, key, data);
   return hash;
}

static Eina_Hash *
_eet_eina_hash_direct_add_alloc(Eina_Hash  *hash,
                                const char *key,
                                void       *data)
{
   if (!hash)
     hash = eina_hash_string_small_new(NULL);

   if (!hash)
     return NULL;

   eina_hash_direct_add(hash, key, data);
   return hash;
}

static char *
_eet_str_direct_alloc(const char *str)
{
   return (char *)str;
}

static void
_eet_str_direct_free(const char *str EINA_UNUSED)
{
}

static void
_eet_eina_hash_foreach(void             *hash,
                       Eina_Hash_Foreach cb,
                       void             *fdata)
{
   if (hash)
     eina_hash_foreach(hash, cb, fdata);
}

static void
_eet_eina_hash_free(void *hash)
{
   if (hash)
     eina_hash_free(hash);
}

/*---*/
EAPI Eina_Bool
eet_eina_stream_data_descriptor_class_set(Eet_Data_Descriptor_Class *eddc,
     /* When we change the structure content in the future, we need to handle old structure type too */
                                          unsigned int               eddc_size,
                                          const char                *name,
                                          int                        size)
{
   if (!eddc || !name || eddc_size != sizeof (Eet_Data_Descriptor_Class))
     return EINA_FALSE;

   eddc->name = name;
   eddc->size = size;
   eddc->version = EET_DATA_DESCRIPTOR_CLASS_VERSION;

   eddc->func.mem_alloc = _eet_mem_alloc;
   eddc->func.mem_free = _eet_mem_free;
   eddc->func.str_alloc = (char *(*)(const char *))eina_stringshare_add;
   eddc->func.str_free = eina_stringshare_del;
   eddc->func.str_direct_alloc = NULL;
   eddc->func.str_direct_free = NULL;
   eddc->func.list_next = (void *(*)(void *))eina_list_next;
   eddc->func.list_append = (void *(*)(void *, void *))eina_list_append;
   eddc->func.list_data = (void *(*)(void *))eina_list_data_get;
   eddc->func.list_free = (void *(*)(void *))eina_list_free;
   eddc->func.hash_foreach = (void (*)(void *, int (*)(void *, const char *, void *, void *), void *))_eet_eina_hash_foreach;
   eddc->func.hash_add = (void *(*)(void *, const char *, void *))_eet_eina_hash_add_alloc;
   eddc->func.hash_free = (void (*)(void *))_eet_eina_hash_free;

   /* This will cause an ABI incompatibility */
   eddc->func.array_alloc = _eet_mem_alloc;
   eddc->func.array_free = _eet_mem_free;

   return EINA_TRUE;
}

EAPI Eina_Bool
eet_eina_file_data_descriptor_class_set(Eet_Data_Descriptor_Class *eddc,
     /* When we change the structure content in the future, we need to handle old structure type too */
                                        unsigned int               eddc_size,
                                        const char                *name,
                                        int                        size)
{
   if (!eet_eina_stream_data_descriptor_class_set(eddc, eddc_size, name, size))
     return EINA_FALSE;

   eddc->version = EET_DATA_DESCRIPTOR_CLASS_VERSION;

   eddc->func.hash_add = (void *(*)(void *, const char *, void *))_eet_eina_hash_direct_add_alloc;
   eddc->func.str_direct_alloc = _eet_str_direct_alloc;
   eddc->func.str_direct_free = _eet_str_direct_free;

   return EINA_TRUE;
}

static Eet_Data_Descriptor *
_eet_data_descriptor_new(const Eet_Data_Descriptor_Class *eddc,
                         int                              version)
{
   Eet_Data_Descriptor *edd;

   if (!eddc)
     return NULL;

   edd = calloc(1, sizeof (Eet_Data_Descriptor));
   if (!edd)
     return NULL;

   edd->name = eddc->name;
   edd->ed = NULL;
   edd->size = eddc->size;
   edd->func.mem_alloc = _eet_mem_alloc;
   edd->func.mem_free = _eet_mem_free;
   edd->func.str_alloc = _eet_str_alloc;
   edd->func.str_free = _eet_str_free;
   if (eddc->func.mem_alloc)
     edd->func.mem_alloc = eddc->func.mem_alloc;

   if (eddc->func.mem_free)
     edd->func.mem_free = eddc->func.mem_free;

   if (eddc->func.str_alloc)
     edd->func.str_alloc = eddc->func.str_alloc;

   if (eddc->func.str_free)
     edd->func.str_free = eddc->func.str_free;

   edd->func.list_next = eddc->func.list_next;
   edd->func.list_append = eddc->func.list_append;
   edd->func.list_data = eddc->func.list_data;
   edd->func.list_free = eddc->func.list_free;
   edd->func.hash_foreach = eddc->func.hash_foreach;
   edd->func.hash_add = eddc->func.hash_add;
   edd->func.hash_free = eddc->func.hash_free;

   if (eddc->version > 1 && version > 1)
     {
        edd->func.str_direct_alloc = eddc->func.str_direct_alloc;
        edd->func.str_direct_free = eddc->func.str_direct_free;
     }

   if (eddc->version > 2)
     {
        edd->func.type_get = eddc->func.type_get;
        edd->func.type_set = eddc->func.type_set;
     }

   if (eddc->version > 3)
     {
        edd->func.array_alloc = eddc->func.array_alloc;
        edd->func.array_free = eddc->func.array_free;
     }

   return edd;
}

EAPI Eet_Data_Descriptor *
eet_data_descriptor_new(const char                          *name,
                        int                                  size,
                        Eet_Descriptor_List_Next_Callback    func_list_next,
                        Eet_Descriptor_List_Append_Callback  func_list_append,
                        Eet_Descriptor_List_Data_Callback    func_list_data,
                        Eet_Descriptor_List_Free_Callback    func_list_free,
                        Eet_Descriptor_Hash_Foreach_Callback func_hash_foreach,
                        Eet_Descriptor_Hash_Add_Callback     func_hash_add,
                        Eet_Descriptor_Hash_Free_Callback    func_hash_free)
{
   Eet_Data_Descriptor_Class eddc;

   if (!name)
     return NULL;

   memset(&eddc, 0, sizeof (Eet_Data_Descriptor_Class));

   eddc.name = name;
   eddc.size = size;
   eddc.version = 0;

   eddc.func.list_next = func_list_next;
   eddc.func.list_append = func_list_append;
   eddc.func.list_data = func_list_data;
   eddc.func.list_free = func_list_free;
   eddc.func.hash_foreach = func_hash_foreach;
   eddc.func.hash_add = func_hash_add;
   eddc.func.hash_free = func_hash_free;

   return _eet_data_descriptor_new(&eddc, 0);
}

EAPI Eet_Data_Descriptor *
eet_data_descriptor2_new(const Eet_Data_Descriptor_Class *eddc)
{
   return _eet_data_descriptor_new(eddc, 1);
}

EAPI Eet_Data_Descriptor *
eet_data_descriptor3_new(const Eet_Data_Descriptor_Class *eddc)
{
   return _eet_data_descriptor_new(eddc, 2);
}

EAPI Eet_Data_Descriptor *
eet_data_descriptor_stream_new(const Eet_Data_Descriptor_Class *eddc)
{
   return _eet_data_descriptor_new(eddc, 1);
}

EAPI Eet_Data_Descriptor *
eet_data_descriptor_file_new(const Eet_Data_Descriptor_Class *eddc)
{
   return _eet_data_descriptor_new(eddc, 2);
}

EAPI const char *
eet_data_descriptor_name_get(const Eet_Data_Descriptor *edd)
{
   EINA_SAFETY_ON_NULL_RETURN_VAL(edd, NULL);
   return edd->name;
}


EAPI void
eet_data_descriptor_free(Eet_Data_Descriptor *edd)
{
   if (!edd)
     return;

   _eet_descriptor_hash_free(edd);
   if (edd->elements.set)
     {
        int i;
        for (i = 0; i < edd->elements.num; i++)
          {
             if (edd->elements.set[i].subtype_free)
               eet_data_descriptor_free(edd->elements.set[i].subtype);
          }
        free(edd->elements.set);
     }

   free(edd);
}

EAPI void
eet_data_descriptor_element_add(Eet_Data_Descriptor *edd,
                                const char          *name,
                                int                  type,
                                int                  group_type,
                                int                  offset,
                                int                  count,
/*        int counter_offset, */
                                const char          *counter_name /* FIXME: Useless should go on a major release */,
                                Eet_Data_Descriptor *subtype)
{
   Eet_Data_Element *ede;
   Eet_Data_Element *tmp;

   EINA_SAFETY_ON_NULL_RETURN(edd);

   /* Sanity check to avoid crash later at runtime */
   if (type < EET_T_UNKNOW ||
       type >= EET_T_LAST)
     {
        CRI("Preventing later bug due to unknown type: %i", type);
        return;
     }
   if (offset < 0)
     {
        CRI("Preventing later buffer underrun : offset = %i", offset);
        return;
     }
   if (offset > edd->size)
     {
        CRI("Preventing later buffer overrun : offset = %i in a structure of %i bytes", offset, edd->size);
        return;
     }
   if (group_type == EET_G_UNKNOWN && type != EET_T_UNKNOW)
     {
        if (offset + eet_basic_codec[type - 1].size > edd->size)
          {
             CRI("Preventing later buffer overrun : offset = %i, size = %i in a structure of %i bytes", offset, eet_basic_codec[type - 1].size, edd->size);
             return;
          }
     }
   else if ((offset + sizeof (void*)) > (unsigned int) edd->size)
     {
        CRI("Preventing later buffer overrun : offset = %i, estimated size = %zu in a structure of %i bytes", offset, sizeof (void*), edd->size);
        return;
     }

   /* UNION, VARIANT type would not work with simple type, we need a way to map the type. */
   if ((group_type == EET_G_UNION
        || group_type == EET_G_VARIANT)
       &&
       (type != EET_T_UNKNOW
        || !subtype
        || !subtype->func.type_get
        || !subtype->func.type_set))
     return;

   /* VARIANT type will only work if the map only contains EET_G_*, but not UNION, VARIANT and ARRAY. */
   if (group_type == EET_G_VARIANT)
     {
        int i;

        for (i = 0; i < subtype->elements.num; ++i)
          if (subtype->elements.set[i].type != EET_T_UNKNOW
              && subtype->elements.set[i].group_type > EET_G_VAR_ARRAY
              && subtype->elements.set[i].group_type < EET_G_UNION)
            return;

        subtype->unified_type = EINA_TRUE;
     }

   if (subtype
       && subtype->unified_type
       && (type != EET_T_UNKNOW
           || group_type < EET_G_UNION))
     return;

   /* Sanity check done, let allocate ! */
   edd->elements.num++;
   tmp = realloc(edd->elements.set, edd->elements.num * sizeof(Eet_Data_Element));
   if (!tmp)
     return;

   edd->elements.set = tmp;
   ede = &(edd->elements.set[edd->elements.num - 1]);
   ede->name = name;
   ede->directory_name_ptr = NULL;
   ede->subtype_free = EINA_FALSE;

   /*
    * We do a special case when we do list,hash or whatever group of simple type.
    * Instead of handling it in encode/decode/dump/undump, we create an
    * implicit structure with only the simple type.
    */
   if ((group_type > EET_G_UNKNOWN)
       && (group_type < EET_G_LAST)
       && (((type >= EET_T_CHAR) && (type <= EET_T_ULONG_LONG))
           || ((type >= EET_T_F32P32) && (type <= EET_T_F8P24)))
       && (!subtype))
     {
        subtype = calloc(1, sizeof (Eet_Data_Descriptor));
        if (!subtype)
          return;

        subtype->name = "implicit";
        subtype->size = eet_basic_codec[type - 1].size;
        memcpy(&subtype->func, &edd->func, sizeof(subtype->func));

        eet_data_descriptor_element_add(subtype,
                                        eet_basic_codec[type - 1].name,
                                        type,
                                        EET_G_UNKNOWN,
                                        0,
                                        0,
                                        /* 0,  */ NULL,
                                        NULL);
        type = EET_T_UNKNOW;
        ede->subtype_free = EINA_TRUE;
     }

   ede->type = type;
   ede->group_type = group_type;
   ede->offset = offset;
   ede->count = count;
   /* FIXME: For the time being, VAR_ARRAY, UNION and VARIANT  will put the counter_offset in count. */
   ede->counter_offset = count;
/*    ede->counter_offset = counter_offset; */
   ede->counter_name = counter_name;

   if (subtype)
     INF("Adding '%s' of size %i to '%s' at offset %i.",
         subtype->name, subtype->size,
         edd->name, offset);

   ede->subtype = subtype;
}

EAPI void *
eet_data_read_cipher(Eet_File            *ef,
                     Eet_Data_Descriptor *edd,
                     const char          *name,
                     const char          *cipher_key)
{
   const Eet_Dictionary *ed = NULL;
   const void *data = NULL;
   void *data_dec;
   Eet_Free_Context context;
   int required_free = 0;
   int size;

   EINA_SAFETY_ON_NULL_RETURN_VAL(edd, NULL);
   ed = eet_dictionary_get(ef);

   if (!cipher_key)
     data = eet_read_direct(ef, name, &size);

   if (!data)
     {
        required_free = 1;
        data = eet_read_cipher(ef, name, &size, cipher_key);
        if (!data)
          return NULL;
     }

   if (ed) eet_dictionary_lock_read(ed); // XXX: get manual eet_dictionary lock
   eet_free_context_init(&context);
   data_dec = _eet_data_descriptor_decode(&context, ed, edd, data, size, NULL, 0);
   eet_free_context_shutdown(&context);
   if (ed) eet_dictionary_unlock(ed); // XXX: release manual eet_dictionary lock

   if (required_free)
     free((void *)data);

   return data_dec;
}

EAPI void *
eet_data_read_cipher_buffer(Eet_File            *ef,
                            Eet_Data_Descriptor *edd,
                            const char          *name,
                            const char          *cipher_key,
                            char* buffer,
                            int buffer_size)
{
   const Eet_Dictionary *ed = NULL;
   const void *data = NULL;
   void *data_dec;
   Eet_Free_Context context;
   int required_free = 0;
   int size;

   EINA_SAFETY_ON_NULL_RETURN_VAL(edd, NULL);
   ed = eet_dictionary_get(ef);

   if (!cipher_key)
     data = eet_read_direct(ef, name, &size);

   if (!data)
     {
        required_free = 1;
        data = eet_read_cipher(ef, name, &size, cipher_key);
        if (!data)
          return NULL;
     }

   if (ed) eet_dictionary_lock_read(ed); // XXX: get manual eet_dictionary lock
   eet_free_context_init(&context);
   data_dec = _eet_data_descriptor_decode(&context, ed, edd, data, size, buffer, buffer_size);
   eet_free_context_shutdown(&context);
   if (ed) eet_dictionary_unlock(ed); // XXX: release manual eet_dictionary lock

   if (required_free)
     free((void *)data);

   return data_dec;
}

EAPI Eet_Node *
eet_data_node_read_cipher(Eet_File   *ef,
                          const char *name,
                          const char *cipher_key)
{
   const Eet_Dictionary *ed = NULL;
   const void *data = NULL;
   Eet_Node *result;
   Eet_Free_Context context;
   int required_free = 0;
   int size;

   ed = eet_dictionary_get(ef);

   if (!cipher_key)
     data = eet_read_direct(ef, name, &size);

   if (!data)
     {
        required_free = 1;
        data = eet_read_cipher(ef, name, &size, cipher_key);
        if (!data)
          return NULL;
     }

   if (ed) eet_dictionary_lock_read(ed); // XXX: get manual eet_dictionary lock
   eet_free_context_init(&context);
   result = _eet_data_descriptor_decode(&context, ed, NULL, data, size, NULL, 0);
   eet_free_context_shutdown(&context);
   if (ed) eet_dictionary_unlock(ed); // XXX: release manual eet_dictionary lock

   if (required_free)
     free((void *)data);

   return result;
}

EAPI void *
eet_data_read(Eet_File            *ef,
              Eet_Data_Descriptor *edd,
              const char          *name)
{
   return eet_data_read_cipher(ef, edd, name, NULL);
}

EAPI int
eet_data_write_cipher(Eet_File            *ef,
                      Eet_Data_Descriptor *edd,
                      const char          *name,
                      const char          *cipher_key,
                      const void          *data,
                      int                  comp)
{
   Eet_Dictionary *ed;
   void *data_enc;
   int size;
   int val;

   EINA_SAFETY_ON_NULL_RETURN_VAL(edd, 0);
   EINA_SAFETY_ON_NULL_RETURN_VAL(data, 0);

   ed = eet_dictionary_get(ef);

   // XXX: future manual lock?
   data_enc = _eet_data_descriptor_encode(ed, edd, data, &size);
   // XXX: release manual eet_dictionary lock
   if (!data_enc)
     return 0;

   val = eet_write_cipher(ef, name, data_enc, size, comp, cipher_key);
   free(data_enc);
   return val;
}

EAPI int
eet_data_write(Eet_File            *ef,
               Eet_Data_Descriptor *edd,
               const char          *name,
               const void          *data,
               int                  comp)
{
   return eet_data_write_cipher(ef, edd, name, NULL, data, comp);
}

static void
eet_free_context_init(Eet_Free_Context *context)
{
   memset(context, 0, sizeof (Eet_Free_Context));

   eina_array_step_set(&context->freelist.list,
                       sizeof (context->freelist.list),
                       32);
   eina_array_step_set(&context->freelist_array.list,
                       sizeof (context->freelist.list),
                       32);
   eina_array_step_set(&context->freelist_list.list,
                       sizeof (context->freelist.list),
                       32);
   eina_array_step_set(&context->freelist_hash.list,
                       sizeof (context->freelist.list),
                       32);
   eina_array_step_set(&context->freelist_str.list,
                       sizeof (context->freelist.list),
                       32);
   eina_array_step_set(&context->freelist_direct_str.list,
                       sizeof (context->freelist.list),
                       32);
}

static void
eet_free_context_shutdown(Eet_Free_Context *context)
{
   eina_array_flush(&context->freelist.list);
   eina_array_flush(&context->freelist_array.list);
   eina_array_flush(&context->freelist_list.list);
   eina_array_flush(&context->freelist_hash.list);
   eina_array_flush(&context->freelist_str.list);
   eina_array_flush(&context->freelist_direct_str.list);
}

static void
_eet_free_add(Eet_Free *ef,
              void     *data)
{
   eina_array_push(&ef->list, data);
}

static void
_eet_free_reset(Eet_Free *ef)
{
   if (ef->ref > 0)
     return;

   eina_array_clean(&ef->list);
}

static void
_eet_free_ref(Eet_Free *ef)
{
   ef->ref++;
}

static void
_eet_free_unref(Eet_Free *ef)
{
   ef->ref--;
}

#define _eet_freelist_add(Ctx, Data) _eet_free_add(&Ctx->freelist, Data);
#define _eet_freelist_reset(Ctx)     _eet_free_reset(&Ctx->freelist);
#define _eet_freelist_ref(Ctx)       _eet_free_ref(&Ctx->freelist);
#define _eet_freelist_unref(Ctx)     _eet_free_unref(&Ctx->freelist);

static void
_eet_freelist_free(Eet_Free_Context    *context,
                   Eet_Data_Descriptor *edd)
{
   void *track;
   Eina_Array_Iterator it;
   unsigned int i;

   if (context->freelist.ref > 0)
     return;

   EINA_ARRAY_ITER_NEXT(&context->freelist.list, i, track, it)
     if (track)
       {
          if (edd)
            edd->func.mem_free(track);
          else
            free(track);
       }
   _eet_free_reset(&context->freelist);
}

#define _eet_freelist_array_add(Ctx, Data) _eet_free_add(&Ctx->freelist_array, Data);
#define _eet_freelist_array_reset(Ctx)     _eet_free_reset(&Ctx->freelist_array);
#define _eet_freelist_array_ref(Ctx)       _eet_free_ref(&Ctx->freelist_array);
#define _eet_freelist_array_unref(Ctx)     _eet_free_unref(&Ctx->freelist_array);

static void
_eet_freelist_array_free(Eet_Free_Context    *context,
                         Eet_Data_Descriptor *edd)
{
   void *track;
   Eina_Array_Iterator it;
   unsigned int i;

   if (context->freelist_array.ref > 0)
     return;

   EINA_ARRAY_ITER_NEXT(&context->freelist_array.list, i, track, it)
     if (track)
       {
          if (edd)
            {
               if (edd->func.array_free)
                 edd->func.array_free(track);
               else
                 edd->func.mem_free(track);
            }
          else
            free(track);
       }
   _eet_free_reset(&context->freelist_array);
}

#define _eet_freelist_list_add(Ctx, Data) _eet_free_add(&Ctx->freelist_list, Data);
#define _eet_freelist_list_reset(Ctx)     _eet_free_reset(&Ctx->freelist_list);
#define _eet_freelist_list_ref(Ctx)       _eet_free_ref(&Ctx->freelist_list);
#define _eet_freelist_list_unref(Ctx)     _eet_free_unref(&Ctx->freelist_list);

static void
_eet_freelist_list_free(Eet_Free_Context    *context,
                        Eet_Data_Descriptor *edd)
{
   void *track;
   Eina_Array_Iterator it;
   unsigned int i;

   if (context->freelist_list.ref > 0)
     return;

   EINA_ARRAY_ITER_NEXT(&context->freelist_list.list, i, track, it)
     if (track)
       {
          if (edd)
            edd->func.list_free(*((void **)(track)));
       }
   _eet_free_reset(&context->freelist_list);
}

#define _eet_freelist_str_add(Ctx, Data) _eet_free_add(&Ctx->freelist_str, Data);
#define _eet_freelist_str_reset(Ctx)     _eet_free_reset(&Ctx->freelist_str);
#define _eet_freelist_str_ref(Ctx)       _eet_free_ref(&Ctx->freelist_str);
#define _eet_freelist_str_unref(Ctx)     _eet_free_unref(&Ctx->freelist_str);

static void
_eet_freelist_str_free(Eet_Free_Context    *context,
                       Eet_Data_Descriptor *edd)
{
   void *track;
   Eina_Array_Iterator it;
   unsigned int i;

   if (context->freelist_str.ref > 0)
     return;

   EINA_ARRAY_ITER_NEXT(&context->freelist_str.list, i, track, it)
     if (track)
       {
          if (edd)
            edd->func.str_free(track);
          else
            free(track);
       }
   _eet_free_reset(&context->freelist_str);
}

#define _eet_freelist_direct_str_add(Ctx, Data) _eet_free_add(&Ctx->freelist_direct_str, Data);
#define _eet_freelist_direct_str_reset(Ctx)     _eet_free_reset(&Ctx->freelist_direct_str);
#define _eet_freelist_direct_str_ref(Ctx)       _eet_free_ref(&Ctx->freelist_direct_str);
#define _eet_freelist_direct_str_unref(Ctx)     _eet_free_unref(&Ctx->freelist_direct_str);

static void
_eet_freelist_direct_str_free(Eet_Free_Context    *context,
                              Eet_Data_Descriptor *edd)
{
   void *track;
   Eina_Array_Iterator it;
   unsigned int i;

   if (context->freelist_direct_str.ref > 0)
     return;

   EINA_ARRAY_ITER_NEXT(&context->freelist_str.list, i, track, it)
     if (track)
       {
          if (edd)
            edd->func.str_direct_free(track);
          else
            free(track);
       }
   _eet_free_reset(&context->freelist_direct_str);
}

#define _eet_freelist_hash_add(Ctx, Data) _eet_free_add(&Ctx->freelist_hash, Data);
#define _eet_freelist_hash_reset(Ctx)     _eet_free_reset(&Ctx->freelist_hash);
#define _eet_freelist_hash_ref(Ctx)       _eet_free_ref(&Ctx->freelist_hash);
#define _eet_freelist_hash_unref(Ctx)     _eet_free_unref(&Ctx->freelist_hash);

static void
_eet_freelist_hash_free(Eet_Free_Context    *context,
                        Eet_Data_Descriptor *edd)
{
   void *track;
   Eina_Array_Iterator it;
   unsigned int i;

   if (context->freelist_hash.ref > 0)
     return;

   EINA_ARRAY_ITER_NEXT(&context->freelist_hash.list, i, track, it)
     if (track)
       {
          if (edd)
            edd->func.hash_free(track);
          else
            free(track);
       }
   _eet_free_reset(&context->freelist_hash);
}

static void
_eet_freelist_all_ref(Eet_Free_Context *freelist_context)
{
   _eet_freelist_ref(freelist_context);
   _eet_freelist_str_ref(freelist_context);
   _eet_freelist_list_ref(freelist_context);
   _eet_freelist_hash_ref(freelist_context);
   _eet_freelist_direct_str_ref(freelist_context);
}

static void
_eet_freelist_all_unref(Eet_Free_Context *freelist_context)
{
   _eet_freelist_unref(freelist_context);
   _eet_freelist_str_unref(freelist_context);
   _eet_freelist_list_unref(freelist_context);
   _eet_freelist_hash_unref(freelist_context);
   _eet_freelist_direct_str_unref(freelist_context);
}

static int
eet_data_descriptor_encode_hash_cb(void       *hash EINA_UNUSED,
                                   const char *cipher_key,
                                   void       *hdata,
                                   void       *fdata)
{
   Eet_Dictionary *ed;
   Eet_Data_Encode_Hash_Info *edehi;
   Eet_Data_Stream *ds;
   Eet_Data_Element *ede;
   Eet_Data_Chunk *echnk;
   void *data = NULL;
   int size;

   edehi = fdata;
   ede = edehi->ede;
   ds = edehi->ds;
   ed = edehi->ed;

   /* Store key */
   data = eet_data_put_type(ed,
                            EET_T_STRING,
                            &cipher_key,
                            &size);
   if (data)
     {
        echnk = eet_data_chunk_new(data,
                                   size,
                                   ede->name,
                                   ede->type,
                                   ede->group_type);
        eet_data_chunk_put(ed, echnk, ds);
        eet_data_chunk_free(echnk);
        free(data);
        data = NULL;
     }

   EET_ASSERT(!((ede->type > EET_T_UNKNOW) && (ede->type < EET_T_STRING)), return );

   /* Store data */
   if (ede->type >= EET_T_STRING)
     eet_data_put_unknown(ed, NULL, ede, ds, &hdata);
   else
     {
        if (ede->subtype)
          data = _eet_data_descriptor_encode(ed,
                                             ede->subtype,
                                             hdata,
                                             &size);

        if (data)
          {
             echnk = eet_data_chunk_new(data,
                                        size,
                                        ede->name,
                                        ede->type,
                                        ede->group_type);
             eet_data_chunk_put(ed, echnk, ds);
             eet_data_chunk_free(echnk);
             free(data);
             data = NULL;
          }
     }

   return 1;
}

static char *
_eet_data_dump_token_get(const char *src,
                         int        *len)
{
   const char *p;
   char *tok = NULL, *temp;
   int in_token = 0;
   int in_quote = 0;
   int in_escape = 0;
   int tlen = 0, tsize = 0;

#define TOK_ADD(x)                        \
  do {                                    \
       tlen++;                            \
       if (tlen >= tsize)                 \
         {                                \
            tsize += 32;                  \
            temp = tok;                   \
            tok = realloc(tok, tsize);    \
            if (!tok)                     \
              {                           \
                 tok = temp;              \
                 ERR("Realloc failed\n"); \
                 goto realloc_error;      \
              }                           \
         }                                \
       tok[tlen - 1] = x;                 \
    } while (0)

   for (p = src; *len > 0; p++, (*len)--)
     {
        if (in_token)
          {
             if (in_escape)
               {
                  switch (p[0]) {
                   case 'n':
                      TOK_ADD('\n');
                      break;
                   case '"':
                   case '\'':
                   case '\\':
                      TOK_ADD(p[0]);
                      break;
                   default:
                      ERR("Unknown escape character %#x (%c). Append as is",
                          p[0], p[0]);
                      TOK_ADD(p[0]);
                  }
                  in_escape = 0;
               }
             else if (p[0] == '\\')
               {
                  in_escape = 1;
               }
             else if (in_quote)
               {
                  if (p[0] == '\"')
                    in_quote = 0;
                  else
                    TOK_ADD(p[0]);
               }
             else
               {
                  if (p[0] == '\"')
                    in_quote = 1;
                  else
                    {
                       if ((isspace(p[0])) || (p[0] == ';')) /* token ends here */
                         {
                            TOK_ADD(0);
                            (*len)--;
                            return tok;
                         }
                       else
                         TOK_ADD(p[0]);
                    }
               }
          }
        else if (!((isspace(p[0])) || (p[0] == ';')))
          {
             in_token = 1;
             (*len)++;
             p--;
          }
     }
   if (in_token)
     {
        TOK_ADD(0);
        return tok;
     }

realloc_error:
   free(tok);

   return NULL;
}

static void
eet_data_encode(Eet_Dictionary  *ed,
                Eet_Data_Stream *ds,
                void            *data,
                const char      *name,
                int              size,
                int              type,
                int              group_type,
                Eina_Bool        free_data)
{
   Eet_Data_Chunk *echnk;

   if (!data)
     type = EET_T_NULL;

   if (group_type != EET_G_UNKNOWN)
     if (type >= EET_T_LAST)
       type = EET_T_UNKNOW;

   echnk = eet_data_chunk_new(data, size, name, type, group_type);
   eet_data_chunk_put(ed, echnk, ds);
   eet_data_chunk_free(echnk);
   if (free_data) free(data);
}

static void *
_eet_data_dump_encode(int             parent_type,
                      Eet_Dictionary *ed,
                      Eet_Node       *node,
                      int            *size_ret)
{
   Eet_Data_Chunk *chnk = NULL;
   Eet_Data_Stream *ds;
   void *cdata, *data;
   int csize, size = 0;
   int count;
   int child_type;
   Eet_Node *n;

   if (_eet_data_words_bigendian == -1)
     {
        unsigned long int v;

        v = eina_htonl(0x12345678);
        if (v == 0x12345678)
          _eet_data_words_bigendian = 1;
        else
          _eet_data_words_bigendian = 0;
     }

   if (!node)
     return NULL;

   ds = eet_data_stream_new();
   if (!ds)
     return NULL;

   switch (node->type)
     {
      case EET_G_UNKNOWN:
        for (n = node->values; n; n = n->next)
          {
             data = _eet_data_dump_encode(node->type, ed, n, &size);
             if (data)
               {
                  eet_data_stream_write(ds, data, size);
                  free(data);
               }
          }
        break;

      case EET_G_ARRAY:
      case EET_G_VAR_ARRAY:
        for (child_type = EET_T_NULL, n = node->values; n; n = n->next)
          {
             if (n->type != EET_T_NULL)
               {
                  child_type = n->type;
                  break;
               }
          }

        data = eet_data_put_type(ed,
                                 EET_T_INT,
                                 &node->count,
                                 &size);
        eet_data_encode(ed,
                        ds,
                        data,
                        node->name,
                        size,
                        child_type,
                        node->type,
                        EINA_TRUE);

        count = node->count;

        for (n = node->values; n; n = n->next)
          {
             int pos = ds->pos;

             switch (n->type)
               {
                case EET_T_STRING:
                case EET_T_INLINED_STRING:
                  data = eet_data_put_type(ed,
                                           n->type,
                                           &(n->data.value.str),
                                           &size);
                  if (data)
                    eet_data_encode(ed,
                                    ds,
                                    data,
                                    node->name,
                                    size,
                                    n->type,
                                    node->type,
                                    EINA_TRUE);

                  break;

                case EET_T_VALUE:
                case EET_T_NULL:
                  continue;

                default:
                  data = _eet_data_dump_encode(n->type, ed, n, &size);
                  eet_data_encode(ed,
                                  ds,
                                  data,
                                  node->name,
                                  size,
                                  n->type,
                                  node->type,
                                  EINA_TRUE);
                  break;
               }  /* switch */
             if (ds->pos != pos)
               count--;
          }

        for (; count; count--)
          {
             eet_data_encode(ed,
                             ds,
                             NULL,
                             node->name,
                             0,
                             EET_T_NULL,
                             node->type,
                             EINA_TRUE);
          }

        /* Array is somekind of special case, so we should embed it inside another chunk. */
        *size_ret = ds->pos;
        cdata = ds->data;

        ds->data = NULL;
        ds->size = 0;
        eet_data_stream_free(ds);

        return cdata;
        break;

      case EET_G_LIST:
        for (n = node->values; n; n = n->next)
          {
             switch (n->type)
               {
                case EET_T_STRING:
                case EET_T_INLINED_STRING:
                  data = eet_data_put_type(ed,
                                           n->type,
                                           &(n->data.value.str),
                                           &size);
                  if (data)
                    eet_data_encode(ed,
                                    ds,
                                    data,
                                    node->name,
                                    size,
                                    n->type,
                                    node->type,
                                    EINA_TRUE);

                  break;

                case EET_T_VALUE:
                case EET_T_NULL:
                  continue;

                default:
                  data = _eet_data_dump_encode(node->type, ed, n, &size);
                  eet_data_encode(ed,
                                  ds,
                                  data,
                                  node->name,
                                  size,
                                  n->type,
                                  node->type,
                                  EINA_TRUE);
               }  /* switch */
          }

        /* List is another somekind of special case, every chunk is embed inside a list chunk. */
        *size_ret = ds->pos;
        cdata = ds->data;

        ds->data = NULL;
        ds->size = 0;
        eet_data_stream_free(ds);

        return cdata;
        break;

      case EET_G_HASH:
        if (node->key)
          {
             data = eet_data_put_type(ed,
                                      EET_T_STRING,
                                      &node->key,
                                      &size);
             eet_data_encode(ed,
                             ds,
                             data,
                             node->name,
                             size,
                             node->type,
                             node->type,
                             EINA_TRUE);
          }
        else
          {
             /* A Hash without key will not decode correctly. */

             ds->data = NULL;
             ds->size = 0;
             eet_data_stream_free(ds);

             return NULL;
          }

        for (n = node->values; n; n = n->next)
          {
             switch (n->type)
               {
                case EET_T_STRING:
                case EET_T_INLINED_STRING:
                  data = eet_data_put_type(ed,
                                           n->type,
                                           &(n->data.value.str),
                                           &size);
                  if (data)
                    eet_data_encode(ed,
                                    ds,
                                    data,
                                    node->name,
                                    size,
                                    n->type,
                                    node->type,
                                    EINA_TRUE);

                  break;

                case EET_T_VALUE:
                case EET_T_NULL:
                  continue;

                default:
                  data = _eet_data_dump_encode(node->type, ed, n, &size);
                  eet_data_encode(ed,
                                  ds,
                                  data,
                                  node->name,
                                  size,
                                  n->type,
                                  node->type,
                                  EINA_TRUE);
               }  /* switch */
          }

        /* Hash is somekind of special case, so we should embed it inside another chunk. */
        *size_ret = ds->pos;
        cdata = ds->data;

        eet_data_stream_flush(ds);

        return cdata;

      case EET_T_VALUE:
      case EET_T_NULL:
        break;

#define EET_DATA_NODE_ENCODE(Eet_Type, Type)                                 \
case Eet_Type:                                                               \
  data = eet_data_put_type(ed, node->type, &(node->data.value.Type), &size); \
  if (data)                                                                  \
    {                                                                        \
       eet_data_encode(ed,                                                   \
                       ds,                                                   \
                       data,                                                 \
                       node->name,                                           \
                       size,                                                 \
                       node->type,                                           \
                       parent_type,                                          \
                       EINA_TRUE);                                           \
       cdata = ds->data;                                                     \
       *size_ret = ds->pos;                                                  \
       eet_data_stream_flush(ds);                                            \
       return cdata;                                                         \
    }   /* switch */                                                         \
  break;

        EET_DATA_NODE_ENCODE(EET_T_CHAR, c);
        EET_DATA_NODE_ENCODE(EET_T_SHORT, s);
        EET_DATA_NODE_ENCODE(EET_T_INT, i);
        EET_DATA_NODE_ENCODE(EET_T_LONG_LONG, l);
        EET_DATA_NODE_ENCODE(EET_T_FLOAT, f);
        EET_DATA_NODE_ENCODE(EET_T_DOUBLE, d);
        EET_DATA_NODE_ENCODE(EET_T_UCHAR, uc);
        EET_DATA_NODE_ENCODE(EET_T_USHORT, us);
        EET_DATA_NODE_ENCODE(EET_T_UINT, ui);
        EET_DATA_NODE_ENCODE(EET_T_ULONG_LONG, ul);
        EET_DATA_NODE_ENCODE(EET_T_INLINED_STRING, str);
        EET_DATA_NODE_ENCODE(EET_T_STRING, str);

      default:
        break;
     }

   if ((node->type >= EET_G_UNKNOWN) && (node->type < EET_G_LAST))
     chnk = eet_data_chunk_new(ds->data,
                               ds->pos,
                               node->name,
                               EET_T_UNKNOW,
                               node->type);
   else
     chnk = eet_data_chunk_new(ds->data,
                               ds->pos,
                               node->name,
                               node->type,
                               EET_G_UNKNOWN);

   eet_data_stream_flush(ds);

   ds = eet_data_stream_new();
   eet_data_chunk_put(ed, chnk, ds);
   cdata = ds->data;
   csize = ds->pos;

   eet_data_stream_flush(ds);
   *size_ret = csize;

   free(chnk->data);
   eet_data_chunk_free(chnk);

   return cdata;
}

static void *
_eet_data_dump_parse(Eet_Dictionary *ed,
                     int            *size_ret,
                     const char     *src,
                     int             size)
{
   void *cdata = NULL;
   const char *p = NULL;
#define M_NONE   0
#define M_STRUCT 1
#define M_       2
   int left, jump;
   Eet_Node *node_base = NULL;
   Eet_Node *node = NULL;
   Eet_Node *n = NULL, *nn = NULL;

   /* FIXME; handle parse errors */
#define TOK_GET(t) \
  jump = left; t = _eet_data_dump_token_get(p, &left); p += jump - left;
   left = size;
   for (p = src; p < (src + size); )
     {
        char *tok1, *tok2, *tok3, *tok4;

        TOK_GET(tok1);
        if (tok1)
          {
             if (!strcmp(tok1, "group"))
               {
                  TOK_GET(tok2);
                  if (tok2)
                    {
                       TOK_GET(tok3);
                       if (tok3)
                         {
                            TOK_GET(tok4);
                            if (tok4)
                              {
                                 if (!strcmp(tok4, "{"))
                                   {
/* we have 'group NAM TYP {' */
                                       n = eet_node_new();
                                       if (n)
                                         {
                                            n->parent = node;
                                            if (!node_base)
                                              node_base = n;

                                            if (node)
                                              {
/* append node */
                                                  if (!node->values)
                                                    node->values = n;
                                                  else
                                                    for (nn = node->values; nn;
                                                         nn = nn->next)
                                                      {
                                                         if (!nn->next)
                                                           {
                                                              nn->next = n;
                                                              break;
                                                           }
                                                      }
                                              }

                                            n->name = eina_stringshare_add(tok2);
                                            if      (!strcmp(tok3, "struct"))
                                              n->type = EET_G_UNKNOWN;
                                            else if (!strcmp(tok3, "array"))
                                              n->type = EET_G_ARRAY;
                                            else if (!strcmp(tok3, "var_array"))
                                              n->type = EET_G_VAR_ARRAY;
                                            else if (!strcmp(tok3, "list"))
                                              n->type = EET_G_LIST;
                                            else if (!strcmp(tok3, "hash"))
                                              n->type = EET_G_HASH;
                                            else
                                              ERR(
                                                "ERROR: group type '%s' invalid.",
                                                tok3);

                                            node = n;
                                         }
                                   }

                                 free(tok4);
                              }

                            free(tok3);
                         }

                       free(tok2);
                    }
               }
             else if (!strcmp(tok1, "value"))
               {
                  TOK_GET(tok2);
                  if (tok2)
                    {
                       TOK_GET(tok3);
                       if (tok3)
                         {
                            TOK_GET(tok4);
                            if (tok4)
                              {
/* we have 'value NAME TYP XXX' */
                                  if (node_base)
                                    {
                                       n = eet_node_new();
                                       if (n)
                                         {
                                            n->parent = node;
/* append node */
                                            if (!node->values)
                                              node->values = n;
                                            else
                                              for (nn = node->values; nn;
                                                   nn = nn->next)
                                                {
                                                   if (!nn->next)
                                                     {
                                                        nn->next = n;
                                                        break;
                                                     }
                                                }

                                            n->name = eina_stringshare_add(tok2);
                                            if      (!strcmp(tok3, "char:"))
                                              {
                                                 n->type = EET_T_CHAR;
                                                 sscanf(tok4, "%hhi",
                                                        &(n->data.value.c));
                                              }
                                            else if (!strcmp(tok3, "short:"))
                                              {
                                                 n->type = EET_T_SHORT;
                                                 sscanf(tok4, "%hi",
                                                        &(n->data.value.s));
                                              }
                                            else if (!strcmp(tok3, "int:"))
                                              {
                                                 n->type = EET_T_INT;
                                                 sscanf(tok4, "%i",
                                                        &(n->data.value.i));
                                              }
                                            else if (!strcmp(tok3, "long_long:"))
                                              {
                                                 n->type = EET_T_LONG_LONG;
                                                 sscanf(tok4, "%lli",
                                                        &(n->data.value.l));
                                              }
                                            else if (!strcmp(tok3, "float:"))
                                              {
                                                 n->type = EET_T_FLOAT;
                                                 sscanf(tok4, "%f",
                                                        &(n->data.value.f));
                                              }
                                            else if (!strcmp(tok3, "double:"))
                                              {
                                                 n->type = EET_T_DOUBLE;
                                                 sscanf(tok4, "%lf",
                                                        &(n->data.value.d));
                                              }
                                            else if (!strcmp(tok3, "uchar:"))
                                              {
                                                 n->type = EET_T_UCHAR;
                                                 sscanf(tok4, "%hhu",
                                                        &(n->data.value.uc));
                                              }
                                            else if (!strcmp(tok3, "ushort:"))
                                              {
                                                 n->type = EET_T_USHORT;
                                                 sscanf(tok4, "%hu",
                                                        &(n->data.value.us));
                                              }
                                            else if (!strcmp(tok3, "uint:"))
                                              {
                                                 n->type = EET_T_UINT;
                                                 sscanf(tok4, "%u",
                                                        &(n->data.value.ui));
                                              }
                                            else if (!strcmp(tok3, "ulong_long:"))
                                              {
                                                 n->type = EET_T_ULONG_LONG;
                                                 sscanf(tok4, "%llu",
                                                        &(n->data.value.ul));
                                              }
                                            else if (!strcmp(tok3, "string:"))
                                              {
                                                 n->type = EET_T_STRING;
                                                 n->data.value.str =
                                                   eina_stringshare_add(tok4);
                                              }
                                            else if (!strcmp(tok3, "inlined:"))
                                              {
                                                 n->type = EET_T_INLINED_STRING;
                                                 n->data.value.str =
                                                   eina_stringshare_add(tok4);
                                              }
                                            else if (!strcmp(tok3, "null"))
                                              {
                                                 n->type = EET_T_NULL;
                                                 n->data.value.str = NULL;
                                              }
                                            else
                                              ERR(
                                                "ERROR: value type '%s' invalid.",
                                                tok4);
                                         }
                                    }

                                  free(tok4);
                              }

                            free(tok3);
                         }

                       free(tok2);
                    }
               }
             else if (!strcmp(tok1, "key"))
               {
                  TOK_GET(tok2);
                  if (tok2)
                    {
/* we have 'key NAME' */
                        if (node)
                          node->key = eina_stringshare_add(tok2);

                        free(tok2);
                    }
               }
             else if (!strcmp(tok1, "count"))
               {
                  TOK_GET(tok2);
                  if (tok2)
                    {
/* we have a 'count COUNT' */
                        if (node)
                          sscanf(tok2, "%i", &(node->count));

                        free(tok2);
                    }
               }
             else if (!strcmp(tok1, "}"))
               /* we have an end of the group */
               if (node)
                 node = node->parent;

             free(tok1);
          }
     }

   if (node_base)
     {
        cdata = _eet_data_dump_encode(EET_G_UNKNOWN, ed, node_base, size_ret);
        eet_node_del(node_base);
     }

   return cdata;
}

#define NEXT_CHUNK(P, Size, Echnk, Ed)                    \
  {                                                       \
     int __tmp;                                           \
     __tmp = Ed ? (int)(sizeof(int) * 2) : Echnk.len + 4; \
     P += (4 + Echnk.size + __tmp);                       \
     Size -= (4 + Echnk.size + __tmp);                    \
  }

static void *
_eet_data_descriptor_decode(Eet_Free_Context     *context,
                            const Eet_Dictionary *ed,
                            Eet_Data_Descriptor  *edd,
                            const void           *data_in,
                            int                   size_in,
                            void                 *data_out,
                            int                   size_out)
{
   Eet_Node *result = NULL;
   void *data = NULL;
   char *p;
   int size, i;
   Eet_Data_Chunk chnk;
   Eina_Bool need_free = EINA_FALSE;

   if (_eet_data_words_bigendian == -1)
     {
        unsigned long int v;

        v = eina_htonl(0x12345678);
        if (v == 0x12345678)
          _eet_data_words_bigendian = 1;
        else
          _eet_data_words_bigendian = 0;
     }

   if (edd)
     {
        if (data_out)
          {
             if (size_out <= edd->size)
               data = data_out;
          }
        else
          {
             data = edd->func.mem_alloc(edd->size);
             need_free = EINA_TRUE;
          }

        if (!data)
          return NULL;

        if (edd->ed != ed)
          {
             for (i = 0; i < edd->elements.num; i++)
               edd->elements.set[i].directory_name_ptr = NULL;
             edd->ed = ed;
          }
     }

   _eet_freelist_all_ref(context);

   memset(&chnk, 0, sizeof(Eet_Data_Chunk));
   eet_data_chunk_get(ed, &chnk, data_in, size_in);
   EINA_SAFETY_ON_NULL_GOTO(chnk.name, error);

   if (edd)
     {
        EINA_SAFETY_ON_TRUE_GOTO(strcmp(chnk.name, edd->name), error);
     }

   p = chnk.data;
   if (ed)
     size = size_in - (4 + sizeof(int) * 2);
   else
     size = size_in - (4 + 4 + chnk.len);

   if (edd)
     {
        if (!edd->elements.hash.buckets)
          _eet_descriptor_hash_new(edd);
     }
   else
     {
        switch (chnk.group_type)
          {
           case EET_G_UNKNOWN:
             switch (chnk.type)
               {
                case EET_T_STRING:
                  return eet_node_string_new(chnk.name, chnk.data);

                case EET_T_INLINED_STRING:
                  return eet_node_inlined_string_new(chnk.name, chnk.data);

                case EET_T_VALUE:
                case EET_T_NULL:
                  return eet_node_null_new(chnk.name);

                default:
                  result = eet_node_struct_new(chnk.name, NULL);
               }  /* switch */
             break;

           case EET_G_VAR_ARRAY:
             return eet_node_var_array_new(chnk.name, NULL);

           case EET_G_LIST:
           case EET_G_HASH:
           case EET_G_ARRAY:
           case EET_G_UNION:
           case EET_G_VARIANT:
           default:
             ERR("Decoding error!");
             goto error;
          }
     }

   while (size > 0)
     {
        Eet_Data_Chunk echnk;
        Eet_Data_Element *ede = NULL;
        Eet_Node *child = NULL;
        int group_type = EET_G_UNKNOWN, type = EET_T_UNKNOW;
        int ret = 0;

        /* get next data chunk */
        memset(&echnk, 0, sizeof(Eet_Data_Chunk));
        eet_data_chunk_get(ed, &echnk, p, size);
        EINA_SAFETY_ON_NULL_GOTO(echnk.name, error); /* FIXME: don't REPLY on edd - work without */

        if (edd)
          {
             ede = _eet_descriptor_hash_find(edd, echnk.name, echnk.hash);
             if (ede)
               {
                  group_type = ede->group_type;
                  type = ede->type;
                  if ((echnk.type == 0) && (echnk.group_type == 0))
                    {
                       type = ede->type;
                       group_type = ede->group_type;
                    }
                  else
                    {
                       if (IS_SIMPLE_TYPE(echnk.type) &&
                           eet_data_type_match(echnk.type, ede->type))
/* Needed when converting on the fly from FP to Float */
                         type = ede->type;
                       else if (IS_SIMPLE_TYPE(echnk.type) &&
                                echnk.type == EET_T_NULL &&
                                ede->type == EET_T_VALUE)
/* EET_T_NULL can become an EET_T_VALUE as EET_T_VALUE are pointer to */
                         type = echnk.type;
                       else if ((echnk.group_type > EET_G_UNKNOWN) &&
                                (echnk.group_type < EET_G_LAST) &&
                                (echnk.group_type == ede->group_type))
                         group_type = echnk.group_type;
                    }
               }
          }
        /*...... dump to node */
        else
          {
             type = echnk.type;
             group_type = echnk.group_type;
          }

        if (!edd && group_type == EET_G_UNKNOWN && IS_SIMPLE_TYPE(type))
          {
             unsigned long long dd[128];

             ret = eet_data_get_type(ed,
                                     type,
                                     echnk.data,
                                     ((char *)echnk.data) + echnk.size,
                                     dd);
             EINA_SAFETY_ON_TRUE_GOTO(ret <= 0, error);

             child = eet_data_node_simple_type(type, echnk.name, dd);

             eet_node_struct_append(result, echnk.name, child);
          }
        else
          {
             ret = eet_group_codec[group_type - 100].get(
                 context,
                 ed,
                 edd,
                 ede,
                 &echnk,
                 type,
                 group_type,
                 ede ? (void *)(((char *)data) + ede->offset) : (void **)&result,
                 &p,
                 &size);

             EINA_SAFETY_ON_TRUE_GOTO(ret <= 0, error);
          }

        /* advance to next chunk */
        NEXT_CHUNK(p, size, echnk, ed);
     }

   _eet_freelist_all_unref(context);
   if (!edd)
     {
        _eet_freelist_str_free(context, edd);
        _eet_freelist_direct_str_free(context, edd);
        _eet_freelist_list_free(context, edd);
        _eet_freelist_hash_free(context, edd);
        _eet_freelist_array_free(context, edd);
        _eet_freelist_free(context, edd);
     }
   else
     {
        _eet_freelist_reset(context);
        _eet_freelist_str_reset(context);
        _eet_freelist_list_reset(context);
        _eet_freelist_hash_reset(context);
        _eet_freelist_direct_str_reset(context);
        _eet_freelist_array_reset(context);
     }

   if (!edd)
     return result;

   return data;

error:
   if (need_free) free(data);
   eet_node_del(result);

   _eet_freelist_all_unref(context);
   _eet_freelist_str_free(context, edd);
   _eet_freelist_direct_str_free(context, edd);
   _eet_freelist_list_free(context, edd);
   _eet_freelist_hash_free(context, edd);
   _eet_freelist_array_free(context, edd);
   _eet_freelist_free(context, edd);

   /* FIXME: Warn that something goes wrong here. */
   return NULL;
}

static int
eet_data_get_list(Eet_Free_Context     *context,
                  const Eet_Dictionary *ed,
                  Eet_Data_Descriptor  *edd,
                  Eet_Data_Element     *ede,
                  Eet_Data_Chunk       *echnk,
                  int                   type,
                  int                   group_type EINA_UNUSED,
                  void                 *data,
                  char                **p,
                  int                  *size)
{
   Eet_Data_Descriptor *subtype = NULL;
   void *list = NULL;
   void **ptr;
   void *data_ret;

   EET_ASSERT(!((type > EET_T_UNKNOW) && (type < EET_T_STRING)), return 0);

   if (ede)
     {
        subtype = ede->subtype;

        if (type != ede->type)
          return 0;
     }

   ptr = (void **)data;
   list = *ptr;
   data_ret = NULL;

   if (IS_POINTER_TYPE(type))
     POINTER_TYPE_DECODE(context,
                         ed,
                         edd,
                         ede,
                         echnk,
                         type,
                         &data_ret,
                         p,
                         size,
                         on_error);
   else
     {
        STRUCT_TYPE_DECODE(data_ret,
                           context,
                           ed,
                           subtype,
                           echnk->data,
                           echnk->size,
                           -1,
                           on_error);
        if (subtype) _eet_freelist_add(context, data_ret);
     }

   if (edd)
     {
        void *oldlist = list;

        list = edd->func.list_append(list, data_ret);
        *ptr = list;
        if (oldlist != list)
          _eet_freelist_list_add(context, ptr);
     }
   else
     eet_node_list_append(*((Eet_Node **)data), echnk->name, data_ret);

   return 1;

on_error:
   return 0;
}

static int
eet_data_get_hash(Eet_Free_Context     *context,
                  const Eet_Dictionary *ed,
                  Eet_Data_Descriptor  *edd,
                  Eet_Data_Element     *ede,
                  Eet_Data_Chunk       *echnk,
                  int                   type,
                  int                   group_type EINA_UNUSED,
                  void                 *data,
                  char                **p,
                  int                  *size)
{
   void **ptr;
   void *hash = NULL;
   char *key = NULL;
   void *data_ret = NULL;
   int ret = 0;

   EET_ASSERT(!((type > EET_T_UNKNOW) && (type < EET_T_STRING)), return 0);

   ptr = (void **)data;
   hash = *ptr;

   /* Read key */
   ret = eet_data_get_type(ed,
                           EET_T_STRING,
                           echnk->data,
                           ((char *)echnk->data) + echnk->size,
                           &key);
   EINA_SAFETY_ON_TRUE_GOTO(ret <= 0, on_error);

   EINA_SAFETY_ON_NULL_GOTO(key, on_error);

   /* Advance to next chunk */
   NEXT_CHUNK((*p), (*size), (*echnk), ed);
   memset(echnk, 0, sizeof(Eet_Data_Chunk));

   /* Read value */
   eet_data_chunk_get(ed, echnk, *p, *size);
   EINA_SAFETY_ON_NULL_GOTO(echnk->name, on_error);

   if (ede)
     if ((ede->group_type != echnk->group_type)
         || (ede->type != echnk->type))
       {
          ERR("ERROR!");
          goto on_error;
       }

   if (IS_POINTER_TYPE(echnk->type))
     POINTER_TYPE_DECODE(context,
                         ed,
                         edd,
                         ede,
                         echnk,
                         echnk->type,
                         &data_ret,
                         p,
                         size,
                         on_error);
   else
     {
        STRUCT_TYPE_DECODE(data_ret,
                           context,
                           ed,
                           ede ? ede->subtype : NULL,
                           echnk->data,
                           echnk->size,
                           -1,
                           on_error);

        if (ede ? ede->subtype : NULL) _eet_freelist_add(context, data_ret);
     }

   if (edd)
     {
        void *oldhash = hash;

        hash = edd->func.hash_add(hash, key, data_ret);
        *ptr = hash;
        if (oldhash != hash)
          _eet_freelist_hash_add(context, hash);
     }
   else
     eet_node_hash_add(*((Eet_Node **)data), echnk->name, key, data_ret);

   return 1;

on_error:
   return 0;
}

/* var arrays and fixed arrays have to
 * get all chunks at once. for fixed arrays
 * we can get each chunk and increment a
 * counter stored on the element itself but
 * it wont be thread safe. for var arrays
 * we still need a way to get the number of
 * elements from the data, so storing the
 * number of elements and the element data on
 * each chunk is pointless.
 */
static int
eet_data_get_array(Eet_Free_Context     *context,
                   const Eet_Dictionary *ed,
                   Eet_Data_Descriptor  *edd,
                   Eet_Data_Element     *ede,
                   Eet_Data_Chunk       *echnk,
                   int                   type,
                   int                   group_type,
                   void                 *data,
                   char                **p,
                   int                  *size)
{
   Eina_List *childs = NULL;
   const char *name;
   Eet_Node *tmp;
   void *ptr;
   int count;
   int ret;
   int subsize = 0;
   int i;

   EET_ASSERT(!((type > EET_T_UNKNOW) && (type < EET_T_STRING)), return 0);

   ptr = data;
   /* read the number of elements */
   ret = eet_data_get_type(ed,
                           EET_T_INT,
                           echnk->data,
                           ((char *)echnk->data) + echnk->size,
                           &count);
   if (ret <= 0)
     return ret;

   name = echnk->name;

   if (ede)
     {
        if (IS_POINTER_TYPE(type))
          subsize = eet_basic_codec[ede->type - 1].size;
        else
          subsize = ede->subtype->size;

        if (group_type == EET_G_VAR_ARRAY)
          {
             /* store the number of elements
              * on the counter offset */
               *(int *)(((char *)data) + ede->count - ede->offset) = count;
     /* allocate space for the array of elements */
               if (edd->func.array_alloc)
                 *(void **)ptr = edd->func.array_alloc(count * subsize);
               else
                 *(void **)ptr = edd->func.mem_alloc(count * subsize);

               if (!*(void **)ptr)
                 return 0;

               memset(*(void **)ptr, 0, count * subsize);

               _eet_freelist_array_add(context, *(void **)ptr);
          }
     }

   /* get all array elements */
   for (i = 0; i < count; i++)
     {
        void *dst = NULL;

        /* Advance to next chunk */
        NEXT_CHUNK((*p), (*size), (*echnk), ed);
        memset(echnk, 0, sizeof(Eet_Data_Chunk));

        eet_data_chunk_get(ed, echnk, *p, *size);
        if (!echnk->name || strcmp(echnk->name, name) != 0)
          {
             ERR("ERROR!");
             goto on_error;
          }

        if ((echnk->group_type != group_type)
            || ((echnk->type != type) && (echnk->type != EET_T_NULL)))
          {
             ERR("ERROR!");
             goto on_error;
          }

        if (ede)
          if ((ede->group_type != echnk->group_type)
              || ((echnk->type != ede->type) && (echnk->type != EET_T_NULL)))
            {
               ERR("ERROR!");
               goto on_error;
            }
        /* get the data */
        /* get the destination pointer */
        if (ede)
          {
             if (group_type == EET_G_ARRAY)
               dst = (char *)ptr + (subsize * i);
             else
               dst = *(char **)ptr + (subsize * i);
          }

        if (IS_POINTER_TYPE(echnk->type))
          {
             void *data_ret = NULL;

             POINTER_TYPE_DECODE(context,
                                 ed,
                                 edd,
                                 ede,
                                 echnk,
                                 echnk->type,
                                 &data_ret,
                                 p,
                                 size,
                                 on_error);
             if (dst)
               memcpy(dst, &data_ret, subsize);

             if (!edd)
               childs = eina_list_append(childs, data_ret);
          }
        else
          {
             STRUCT_TYPE_DECODE(dst,
                                context,
                                ed,
                                ede ? ede->subtype : NULL,
                                echnk->data,
                                echnk->size,
                                subsize,
                                on_error);

             if (!edd)
               childs = eina_list_append(childs, dst);
          }
     }

   if (!edd)
     {
        Eet_Node *parent = *((Eet_Node **)data);
        Eet_Node *array;

        if (group_type == EET_G_ARRAY)
          array = eet_node_array_new(name, count, childs);
        else
          array = eet_node_var_array_new(name, childs);

        EINA_SAFETY_ON_NULL_GOTO(array, on_error);

        eet_node_struct_append(parent, name, array);
     }

   return 1;

on_error:
   EINA_LIST_FREE(childs, tmp)
     eet_node_del(tmp);

   return 0;
}

static void
eet_data_put_union(Eet_Dictionary      *ed,
                   Eet_Data_Descriptor *edd EINA_UNUSED,
                   Eet_Data_Element    *ede,
                   Eet_Data_Stream     *ds,
                   void                *data_in)
{
   const char *union_type;
   int i;

   EET_ASSERT(!((ede->type != EET_T_UNKNOW) || (!ede->subtype)), return );

   union_type = ede->subtype->func.type_get(
       ((char *)data_in) + ede->count - ede->offset,
       NULL);

   if (!union_type)
     return;

   /* Search the structure of the union to encode. */
   for (i = 0; i < ede->subtype->elements.num; ++i)
     if (strcmp(ede->subtype->elements.set[i].name, union_type) == 0)
       {
          Eet_Data_Element *sede;
          void *data;
          int size;

          /* Yeah we found it ! */
          data = eet_data_put_type(ed, EET_T_STRING, &union_type, &size);
          if (data)
            eet_data_encode(ed,
                            ds,
                            data,
                            ede->name,
                            size,
                            ede->type,
                            ede->group_type,
                            EINA_TRUE);

          sede = &(ede->subtype->elements.set[i]);

          if (IS_SIMPLE_TYPE(sede->type))
            data = eet_data_put_type(ed, sede->type, data_in, &size);
          else
            data = _eet_data_descriptor_encode(ed,
                                             sede->subtype,
                                             data_in,
                                             &size);
          if (data)
            eet_data_encode(ed,
                            ds,
                            data,
                            ede->name,
                            size,
                            ede->type,
                            ede->group_type,
                            EINA_TRUE);

          break;
       }
}

static int
eet_data_get_union(Eet_Free_Context     *context,
                   const Eet_Dictionary *ed,
                   Eet_Data_Descriptor  *edd EINA_UNUSED,
                   Eet_Data_Element     *ede,
                   Eet_Data_Chunk       *echnk,
                   int                   type,
                   int                   group_type,
                   void                 *data,
                   char                **p,
                   int                  *size)
{
   const char *union_type;
   void *data_ret = NULL;
   int ret = 0;
   int i;

   /* Read type */
   ret = eet_data_get_type(ed,
                           EET_T_STRING,
                           echnk->data,
                           ((char *)echnk->data) + echnk->size,
                           &union_type);
   EINA_SAFETY_ON_TRUE_GOTO(ret <= 0, on_error);

   /* Advance to next chunk */
   NEXT_CHUNK((*p), (*size), (*echnk), ed);
   memset(echnk, 0, sizeof(Eet_Data_Chunk));

   /* Read value */
   eet_data_chunk_get(ed, echnk, *p, *size);
   EINA_SAFETY_ON_NULL_GOTO(echnk->name, on_error);

   if (ede)
     {
        EET_ASSERT(!(ede->group_type != group_type || ede->type != type),
                   ERR("ERROR!"); goto on_error);

        /* Search the structure of the union to decode */
        for (i = 0; i < ede->subtype->elements.num; ++i)
          if (strcmp(ede->subtype->elements.set[i].name, union_type) == 0)
            {
               Eet_Data_Element *sede;
               char *ut;

               /* Yeah we found it ! */
               sede = &(ede->subtype->elements.set[i]);

               if (IS_SIMPLE_TYPE(sede->type))
                 {
                    ret = eet_data_get_type(ed,
                          sede->type,
                          echnk->data,
                          ((char *)echnk->data) + echnk->size,
                          (char *)data);

                    if (ret <= 0)
                      return ret;
                 }
               else
                 {
                  EET_ASSERT(sede->subtype, ERR("ERROR!"); goto on_error);
                  data_ret = _eet_data_descriptor_decode(context,
                                                      ed,
                                                      sede->subtype,
                                                      echnk->data,
                                                      echnk->size,
                                                      data,
                                                      sede->subtype->size);
                  EINA_SAFETY_ON_NULL_GOTO(data_ret, on_error);
               }

               /* Set union type. */
               if ((!ed) || (!ede->subtype->func.str_direct_alloc))
                 {
                    ut = ede->subtype->func.str_alloc(union_type);
                    _eet_freelist_str_add(context, ut);
                 }
               else
                 {
                    ut = ede->subtype->func.str_direct_alloc(union_type);
                    _eet_freelist_direct_str_add(context, ut);
                 }

               ede->subtype->func.type_set(
                 ut,
                 ((char *)data) + ede->count -
                 ede->offset,
                 EINA_FALSE);

               break;
            }
     }
   else
     {
        /* FIXME: generate node structure. */
        _eet_data_descriptor_decode(context,
                                    ed, NULL,
                                    echnk->data, echnk->size,
                                    NULL, 0);
        ERR("ERROR!");
        goto on_error;
     }

   return 1;

on_error:
   return 0;
}

static void
eet_data_put_variant(Eet_Dictionary      *ed,
                     Eet_Data_Descriptor *edd EINA_UNUSED,
                     Eet_Data_Element    *ede,
                     Eet_Data_Stream     *ds,
                     void                *data_in)
{
   const char *union_type;
   void *data;
   Eina_Bool unknow = EINA_FALSE;
   int size;
   int i;

   EET_ASSERT(!((ede->type != EET_T_UNKNOW) || (!ede->subtype)), return );

   union_type = ede->subtype->func.type_get(
       ((char *)data_in) + ede->count - ede->offset,
       &unknow);

   if (!union_type && unknow == EINA_FALSE)
     return;

   if (unknow)
     {
        /* Handle opaque internal representation */
         Eet_Variant_Unknow *evu;

         data = eet_data_put_type(ed, EET_T_STRING, &union_type, &size);
         if (data)
           eet_data_encode(ed,
                           ds,
                           data,
                           ede->name,
                           size,
                           ede->type,
                           ede->group_type,
                           EINA_TRUE);

         evu = (Eet_Variant_Unknow *)data_in;
         if (evu && EINA_MAGIC_CHECK(evu, EET_MAGIC_VARIANT))
           eet_data_encode(ed,
                           ds,
                           evu->data,
                           ede->name,
                           evu->size,
                           ede->type,
                           ede->group_type,
                           EINA_FALSE);
     }
   else
     /* Search the structure of the union to encode. */
     for (i = 0; i < ede->subtype->elements.num; ++i)
       if (strcmp(ede->subtype->elements.set[i].name, union_type) == 0)
         {
            Eet_Data_Element *sede;

            /* Yeah we found it ! */
            data = eet_data_put_type(ed, EET_T_STRING, &union_type, &size);
            if (data)
              eet_data_encode(ed,
                              ds,
                              data,
                              ede->name,
                              size,
                              ede->type,
                              ede->group_type,
                              EINA_TRUE);

            sede = &(ede->subtype->elements.set[i]);

            if (sede->group_type != EET_G_UNKNOWN)
              {
                 Eet_Data_Stream *lds;

                 lds = eet_data_stream_new();
                 eet_group_codec[sede->group_type - 100].put(ed,
                                                             sede->subtype,
                                                             sede,
                                                             lds,
                                                             data_in);
                 if (lds->size != 0)
                   {
                      eet_data_encode(ed, ds, lds->data, ede->name, lds->pos,
                                      ede->type, ede->group_type,
                                      EINA_TRUE);

                      lds->data = NULL;
                      lds->size = 0;
                   }
                 else
                   eet_data_encode(ed, ds, NULL, ede->name, 0,
                                   EET_T_NULL, ede->group_type,
                                   EINA_TRUE);

                 eet_data_stream_free(lds);
              }
            else
              {
                 data = _eet_data_descriptor_encode(ed,
                                                    sede->subtype,
                                                    *(void **)data_in,
                                                    &size);
                 if (data)
                   eet_data_encode(ed,
                                   ds,
                                   data,
                                   ede->name,
                                   size,
                                   ede->type,
                                   ede->group_type,
                                   EINA_TRUE);
              }

            break;
         }
}

static int
eet_data_get_variant(Eet_Free_Context     *context,
                     const Eet_Dictionary *ed,
                     Eet_Data_Descriptor  *edd EINA_UNUSED,
                     Eet_Data_Element     *ede,
                     Eet_Data_Chunk       *echnk,
                     int                   type EINA_UNUSED,
                     int                   group_type EINA_UNUSED,
                     void                 *data,
                     char                **p,
                     int                  *size)
{
   const char *union_type;
   void *data_ret = NULL;
   int ret = 0;
   int i;

   /* Read type */
   ret = eet_data_get_type(ed,
                           EET_T_STRING,
                           echnk->data,
                           ((char *)echnk->data) + echnk->size,
                           &union_type);
   EINA_SAFETY_ON_TRUE_GOTO(ret <= 0, on_error);

   /* Advance to next chunk */
   NEXT_CHUNK((*p), (*size), (*echnk), ed);
   memset(echnk, 0, sizeof(Eet_Data_Chunk));

   /* Read value */
   eet_data_chunk_get(ed, echnk, *p, *size);
   EINA_SAFETY_ON_NULL_GOTO(echnk->name, on_error);

   if (ede)
     {
        char *ut;

        EET_ASSERT(ede->subtype, ERR("ERROR!"); goto on_error);

        if ((!ed) || (!ede->subtype->func.str_direct_alloc))
          {
             ut = ede->subtype->func.str_alloc(union_type);
             _eet_freelist_str_add(context, ut);
          }
        else
          {
             ut = ede->subtype->func.str_direct_alloc(union_type);
             _eet_freelist_direct_str_add(context, ut);
          }

        /* Search the structure of the union to decode */
        for (i = 0; i < ede->subtype->elements.num; ++i)
          if (strcmp(ede->subtype->elements.set[i].name, union_type) == 0)
            {
               Eet_Data_Element *sede;

               /* Yeah we found it ! */
               sede = &(ede->subtype->elements.set[i]);

               if (sede->group_type != EET_G_UNKNOWN)
                 {
                    Eet_Data_Chunk chnk;
                    char *p2;
                    int size2;

                    p2 = echnk->data;
                    size2 = echnk->size;

     /* Didn't find a proper way to provide this
        without duplicating code */
                    while (size2 > 0)
                      {
                         memset(&chnk, 0, sizeof(Eet_Data_Chunk));
                         eet_data_chunk_get(ed, &chnk, p2, size2);

                         EINA_SAFETY_ON_NULL_GOTO(chnk.name, on_error);

                         ret = eet_group_codec[sede->group_type - 100].get
                             (context, ed, sede->subtype, sede, &chnk, sede->type,
                             sede->group_type, data, &p2, &size2);

                         EINA_SAFETY_ON_TRUE_GOTO(ret <= 0, on_error);

/* advance to next chunk */
                         NEXT_CHUNK(p2, size2, chnk, ed);
                      }

     /* Put garbage so that we will not put eet_variant_unknow in it */
                    data_ret = (void *)data;

     /* Set variant type. */
                    ede->subtype->func.type_set
                      (ut, ((char *)data) + ede->count - ede->offset,
                      EINA_FALSE);
                    break;
                 }

               data_ret = _eet_data_descriptor_decode(context,
                                                      ed,
                                                      sede->subtype,
                                                      echnk->data,
                                                      echnk->size,
                                                      NULL, 0);
               EINA_SAFETY_ON_TRUE_GOTO(!data_ret, on_error);

               /* And point to the variant data. */
               *(void **)data = data_ret;

               /* Set variant type. */
               ede->subtype->func.type_set
                 (ut, ((char *)data) + ede->count - ede->offset, EINA_FALSE);
               break;
            }

        if (!data_ret)
          {
             Eet_Variant_Unknow *evu;

             evu = calloc(1, sizeof (Eet_Variant_Unknow) + echnk->size - 1);
             EINA_SAFETY_ON_NULL_GOTO(evu, on_error);

             evu->size = echnk->size;
             memcpy(evu->data, echnk->data, evu->size);
             EINA_MAGIC_SET(evu, EET_MAGIC_VARIANT);

             /* And point to the opaque internal data scructure */
             *(void **)data = evu;

             /* Set variant type. */
             ede->subtype->func.type_set
               (ut, ((char *)data) + ede->count - ede->offset, EINA_TRUE);
          }
     }
   else
     {
        /* FIXME: dump node structure. */
        _eet_data_descriptor_decode(context,
                                    ed, NULL,
                                    echnk->data, echnk->size,
                                    NULL, 0);
        ERR("ERROR!");
        goto on_error;
     }

   return 1;

on_error:
   return 0;
}

static Eet_Node *
eet_data_node_simple_type(int         type,
                          const char *name,
                          void       *dd)
{
#ifdef EET_T_TYPE
# undef EET_T_TYPE
#endif /* ifdef EET_T_TYPE */

#define EET_T_TYPE(Eet_Type, Eet_Node_Type, Type)                 \
case Eet_Type:                                                    \
  return eet_node_ ## Eet_Node_Type ## _new(name, *((Type *)dd)); \

   switch (type)
     {
        EET_T_TYPE(EET_T_CHAR, char, char);
        EET_T_TYPE(EET_T_SHORT, short, short);
        EET_T_TYPE(EET_T_INT, int, int);
        EET_T_TYPE(EET_T_LONG_LONG, long_long, long long);
        EET_T_TYPE(EET_T_FLOAT, float, float);
        EET_T_TYPE(EET_T_DOUBLE, double, double);
        EET_T_TYPE(EET_T_UCHAR, unsigned_char, unsigned char);
        EET_T_TYPE(EET_T_USHORT, unsigned_short, unsigned short);
        EET_T_TYPE(EET_T_UINT, unsigned_int, unsigned int);
        EET_T_TYPE(EET_T_ULONG_LONG, unsigned_long_long, unsigned long long);
        EET_T_TYPE(EET_T_STRING, string, char *);
        EET_T_TYPE(EET_T_INLINED_STRING, inlined_string, char *);

      case EET_T_NULL:
        return eet_node_null_new(name);

      case EET_T_VALUE:
         return eet_node_null_new(name);

      default:
        ERR("Unknown type passed to eet_data_node_simple_type");
        return NULL;
     }
}

static int
eet_data_get_unknown(Eet_Free_Context     *context,
                     const Eet_Dictionary *ed,
                     Eet_Data_Descriptor  *edd,
                     Eet_Data_Element     *ede,
                     Eet_Data_Chunk       *echnk,
                     int                   type,
                     int                   group_type EINA_UNUSED,
                     void                 *data,
                     char                **p EINA_UNUSED,
                     int                  *size EINA_UNUSED)
{
   int ret;
   void *data_ret;

   if (IS_SIMPLE_TYPE(type))
     {
        unsigned long long dd[128];

        ret = eet_data_get_type(ed,
                                type,
                                echnk->data,
                                ((char *)echnk->data) + echnk->size,
                                edd ? (char *)data : (char *)dd);
        if (ret <= 0)
          return ret;

        if (!edd)
          {
             Eet_Node **parent = data;
             Eet_Node *node;

             node = eet_data_node_simple_type(type, echnk->name, dd);

             if (*parent)
               eet_node_struct_append(*parent, echnk->name, node);
             else
               *parent = node;
          }
        else
          {
             if (type == EET_T_STRING)
               {
                  char **str = data;

                  if (*str)
                    {
                       if ((!ed) || (!edd->func.str_direct_alloc))
                         {
                            *str = edd->func.str_alloc(*str);
                            _eet_freelist_str_add(context, *str);
                         }
                       else
                         {
                            *str = edd->func.str_direct_alloc(*str);
                            _eet_freelist_direct_str_add(context, *str);
                         }
                    }
               }
             else if (edd && type == EET_T_INLINED_STRING)
               {
                  char **str = data;

                  if (*str)
                    {
                       *str = edd->func.str_alloc(*str);
                       _eet_freelist_str_add(context, *str);
                    }
               }
          }
     }
   else
     {
        Eet_Data_Descriptor *subtype;

        subtype = ede ? ede->subtype : NULL;

        if (subtype || !edd)
          {
             Eet_Node **parent = data;
             void **ptr;

             data_ret = _eet_data_descriptor_decode(context,
                                                    ed,
                                                    subtype,
                                                    echnk->data,
                                                    echnk->size,
                                                    NULL, 0);
             if (!data_ret)
               return 0;

             if (edd)
               {
                  if (subtype && ede->group_type == EET_G_UNKNOWN_NESTED)
                    {
                       memcpy(data, data_ret, subtype->size);
                       free(data_ret);
                    }
                  else
                    {
                       ptr = data;
                       *ptr = (void *)data_ret;
                       _eet_freelist_add(context, data_ret);
                    }
               }
             else
               {
                  Eet_Node *node = data_ret;

                  if (*parent)
                    {
                       node = eet_node_struct_child_new(echnk->name, node);
                       eet_node_struct_append(*parent, echnk->name, node);
                    }
                  else
                    *parent = node;
               }
          }
     }

   return 1;
}

static void
eet_data_put_array(Eet_Dictionary      *ed,
                   Eet_Data_Descriptor *edd EINA_UNUSED,
                   Eet_Data_Element    *ede,
                   Eet_Data_Stream     *ds,
                   void                *data_in)
{
   void *data;
   int offset = 0;
   int subsize;
   int count;
   int size;
   int j;

   EET_ASSERT(!((ede->type > EET_T_UNKNOW) && (ede->type < EET_T_STRING)),
              return );

   if (ede->group_type == EET_G_ARRAY)
     count = ede->counter_offset;
   else
     count = *(int *)(((char *)data_in) + ede->count - ede->offset);

   if (count <= 0)
     return;  /* Store number of elements */

   data = eet_data_put_type(ed, EET_T_INT, &count, &size);
   if (data)
     eet_data_encode(ed, ds, data, ede->name, size, ede->type, ede->group_type, EINA_TRUE);

   if (IS_POINTER_TYPE(ede->type))
     subsize = eet_basic_codec[ede->type - 1].size;
   else
     subsize = ede->subtype->size;

   for (j = 0; j < count; j++)
     {
        void *d;
        int pos = ds->pos;

        if (ede->group_type == EET_G_ARRAY)
          d = ((char *)data_in) + offset;
        else
          d = *(((char **)data_in)) + offset;

        if (IS_POINTER_TYPE(ede->type))
          {
             if (*(char **)d)
               eet_data_put_unknown(ed, NULL, ede, ds, d);
          }
        else
          {
             data = _eet_data_descriptor_encode(ed, ede->subtype, d, &size);
             if (data)
               eet_data_encode(ed,
                               ds,
                               data,
                               ede->name,
                               size,
                               ede->type,
                               ede->group_type,
                               EINA_TRUE);
          }

        if (pos == ds->pos)
          /* Add a NULL element just to have the correct array layout. */
          eet_data_encode(ed,
                          ds,
                          NULL,
                          ede->name,
                          0,
                          EET_T_NULL,
                          ede->group_type,
                          EINA_TRUE);

        offset += subsize;
     }
}

static void
eet_data_put_unknown(Eet_Dictionary      *ed,
                     Eet_Data_Descriptor *edd EINA_UNUSED,
                     Eet_Data_Element    *ede,
                     Eet_Data_Stream     *ds,
                     void                *data_in)
{
   void *data = NULL;
   int size;

   if (IS_SIMPLE_TYPE(ede->type))
     data = eet_data_put_type(ed, ede->type, data_in, &size);
   else if (ede->subtype)
     {
        if (ede->group_type == EET_G_UNKNOWN_NESTED)
          data = _eet_data_descriptor_encode(ed,
                                             ede->subtype,
                                             data_in,
                                             &size);
        else if (*((char **)data_in))
          data = _eet_data_descriptor_encode(ed,
                                             ede->subtype,
                                             *((char **)((char *)(data_in))),
                                             &size);
     }

   if (data)
     eet_data_encode(ed,
                     ds,
                     data,
                     ede->name,
                     size,
                     ede->type,
                     ede->group_type,
                     EINA_TRUE);
}

static void
eet_data_put_list(Eet_Dictionary      *ed,
                  Eet_Data_Descriptor *edd,
                  Eet_Data_Element    *ede,
                  Eet_Data_Stream     *ds,
                  void                *data_in)
{
   void *data;
   void *l;
   int size;

   EET_ASSERT(!(((ede->type > EET_T_UNKNOW) && (ede->type < EET_T_STRING))
                || ((ede->type > EET_T_NULL) && (ede->type < EET_T_LAST))),
              return );

   l = *((void **)(((char *)data_in)));
   for (; l; l = edd->func.list_next(l))
     {
        if (IS_POINTER_TYPE(ede->type))
          {
             const void *str = edd->func.list_data(l);
             eet_data_put_unknown(ed, NULL, ede, ds, &str);
          }
        else
          {
             data = _eet_data_descriptor_encode(ed,
                                                ede->subtype,
                                                edd->func.list_data(l),
                                                &size);
             if (data)
               eet_data_encode(ed,
                               ds,
                               data,
                               ede->name,
                               size,
                               ede->type,
                               ede->group_type,
                               EINA_TRUE);
          }
     }
}

static void
eet_data_put_hash(Eet_Dictionary      *ed,
                  Eet_Data_Descriptor *edd,
                  Eet_Data_Element    *ede,
                  Eet_Data_Stream     *ds,
                  void                *data_in)
{
   Eet_Data_Encode_Hash_Info fdata;
   void *l;

   l = *((void **)(((char *)data_in)));
   fdata.ds = ds;
   fdata.ede = ede;
   fdata.ed = ed;
   edd->func.hash_foreach(l, eet_data_descriptor_encode_hash_cb, &fdata);
}

EAPI int
eet_data_dump_cipher(Eet_File         *ef,
                     const char       *name,
                     const char       *cipher_key,
                     Eet_Dump_Callback dumpfunc,
                     void             *dumpdata)
{
   const Eet_Dictionary *ed = NULL;
   const void *data = NULL;
   Eet_Node *result;
   Eet_Free_Context context;
   int required_free = 0;
   int size;

   ed = eet_dictionary_get(ef);

   if (!cipher_key)
     data = eet_read_direct(ef, name, &size);

   if (!data)
     {
        required_free = 1;
        data = eet_read_cipher(ef, name, &size, cipher_key);
        if (!data)
          return 0;
     }

   if (ed) eet_dictionary_lock_read(ed); // XXX: get manual eet_dictionary lock
   eet_free_context_init(&context);
   result = _eet_data_descriptor_decode(&context, ed, NULL, data, size, NULL, 0);
   eet_free_context_shutdown(&context);
   if (ed) eet_dictionary_unlock(ed); // XXX: release manual eet_dictionary lock

   eet_node_dump(result, 0, dumpfunc, dumpdata);

   eet_node_del(result);

   if (required_free)
     free((void *)data);

   return result ? 1 : 0;
}

EAPI int
eet_data_dump(Eet_File         *ef,
              const char       *name,
              Eet_Dump_Callback dumpfunc,
              void             *dumpdata)
{
   return eet_data_dump_cipher(ef, name, NULL, dumpfunc, dumpdata);
}

EAPI int
eet_data_text_dump_cipher(const void       *data_in,
                          const char       *cipher_key,
                          int               size_in,
                          Eet_Dump_Callback dumpfunc,
                          void             *dumpdata)
{
   void *ret = NULL;
   Eet_Node *result;
   Eet_Free_Context context;
   unsigned int ret_len = 0;

   if (!data_in)
     return 0;

   if (cipher_key)
     {
        if (eet_decipher(data_in, size_in, cipher_key,
                         strlen(cipher_key), &ret, &ret_len))
          {
             if (ret)
               free(ret);

             return 0;
          }
     }
   else
     {
        ret = (void *)data_in;
        ret_len = size_in;
     }

   eet_free_context_init(&context);
   result = _eet_data_descriptor_decode(&context, NULL, NULL, ret, ret_len, NULL, 0);
   eet_free_context_shutdown(&context);

   eet_node_dump(result, 0, dumpfunc, dumpdata);

   eet_node_del(result);
   if (cipher_key)
     free(ret);

   return result ? 1 : 0;
}

EAPI int
eet_data_text_dump(const void       *data_in,
                   int               size_in,
                   Eet_Dump_Callback dumpfunc,
                   void             *dumpdata)
{
   return eet_data_text_dump_cipher(data_in, NULL, size_in, dumpfunc, dumpdata);
}

EAPI void *
eet_data_text_undump_cipher(const char *text,
                            const char *cipher_key,
                            int         textlen,
                            int        *size_ret)
{
   void *ret = NULL;

   ret = _eet_data_dump_parse(NULL, size_ret, text, textlen);
   if (ret && cipher_key)
     {
        void *ciphered = NULL;
        unsigned int ciphered_len;

        if (eet_cipher(ret, *size_ret, cipher_key,
                       strlen(cipher_key), &ciphered, &ciphered_len))
          {
             if (ciphered)
               free(ciphered);

             size_ret = 0;
             free(ret);
             return NULL;
          }

        free(ret);
        *size_ret = ciphered_len;
        ret = ciphered;
     }

   return ret;
}

EAPI void *
eet_data_text_undump(const char *text,
                     int         textlen,
                     int        *size_ret)
{
   return eet_data_text_undump_cipher(text, NULL, textlen, size_ret);
}

EAPI int
eet_data_undump_cipher(Eet_File   *ef,
                       const char *name,
                       const char *cipher_key,
                       const char *text,
                       int         textlen,
                       int         comp)
{
   Eet_Dictionary *ed;
   void *data_enc;
   int size;
   int val;

   ed = eet_dictionary_get(ef);

   // XXX: in future put lock outside here - rare to dump
   data_enc = _eet_data_dump_parse(ed, &size, text, textlen);
   // XXX: release manual eet_dictionary lock
   if (!data_enc)
     return 0;

   val = eet_write_cipher(ef, name, data_enc, size, comp, cipher_key);
   free(data_enc);
   return val;
}

EAPI int
eet_data_undump(Eet_File   *ef,
                const char *name,
                const char *text,
                int         textlen,
                int         comp)
{
   return eet_data_undump_cipher(ef, name, NULL, text, textlen, comp);
}

EAPI void *
eet_data_descriptor_decode_cipher(Eet_Data_Descriptor *edd,
                                  const void          *data_in,
                                  const char          *cipher_key,
                                  int                  size_in)
{
   void *deciphered = (void *)data_in;
   void *ret;
   Eet_Free_Context context;
   unsigned int deciphered_len = size_in;

   EINA_SAFETY_ON_NULL_RETURN_VAL(edd, NULL);

   if (cipher_key && data_in)
     if (eet_decipher(data_in, size_in, cipher_key,
                      strlen(cipher_key), &deciphered, &deciphered_len))
       {
          if (deciphered)
            free(deciphered);

          return NULL;
       }

   eet_free_context_init(&context);
   ret = _eet_data_descriptor_decode(&context,
                                     NULL,
                                     edd,
                                     deciphered,
                                     deciphered_len,
                                     NULL, 0);
   eet_free_context_shutdown(&context);

   if (data_in != deciphered)
     free(deciphered);

   return ret;
}

EAPI void *
eet_data_descriptor_decode(Eet_Data_Descriptor *edd,
                           const void          *data_in,
                           int                  size_in)
{
   return eet_data_descriptor_decode_cipher(edd, data_in, NULL, size_in);
}

EAPI Eet_Node *
eet_data_node_decode_cipher(const void *data_in,
                            const char *cipher_key,
                            int         size_in)
{
   void *deciphered = (void *)data_in;
   Eet_Node *ret;
   Eet_Free_Context context;
   unsigned int deciphered_len = size_in;

   if (cipher_key && data_in)
     if (eet_decipher(data_in, size_in, cipher_key,
                      strlen(cipher_key), &deciphered, &deciphered_len))
       {
          if (deciphered)
            free(deciphered);

          return NULL;
       }

   eet_free_context_init(&context);
   ret = _eet_data_descriptor_decode(&context,
                                     NULL,
                                     NULL,
                                     deciphered,
                                     deciphered_len,
                                     NULL, 0);
   eet_free_context_shutdown(&context);

   if (data_in != deciphered)
     free(deciphered);

   return ret;
}

static void *
_eet_data_descriptor_encode(Eet_Dictionary      *ed,
                            Eet_Data_Descriptor *edd,
                            const void          *data_in,
                            int                 *size_ret)
{
   Eet_Data_Stream *ds;
   Eet_Data_Chunk *chnk;
   void *cdata;
   int csize;
   int i;

   if (_eet_data_words_bigendian == -1)
     {
        unsigned long int v;

        v = eina_htonl(0x12345678);
        if (v == 0x12345678)
          _eet_data_words_bigendian = 1;
        else
          _eet_data_words_bigendian = 0;
     }

   ds = eet_data_stream_new();
   for (i = 0; i < edd->elements.num; i++)
     {
        Eet_Data_Element *ede;

        ede = &(edd->elements.set[i]);
        eet_group_codec[ede->group_type - 100].put(
          ed,
          edd,
          ede,
          ds,
          ((char *)data_in) +
          ede->offset);
     }
   chnk = eet_data_chunk_new(ds->data,
                             ds->pos,
                             edd->name,
                             EET_T_UNKNOW,
                             EET_G_UNKNOWN);
   ds->data = NULL;
   ds->size = 0;
   eet_data_stream_free(ds);

   ds = eet_data_stream_new();
   eet_data_chunk_put(ed, chnk, ds);
   cdata = ds->data;
   csize = ds->pos;

   ds->data = NULL;
   ds->size = 0;
   eet_data_stream_free(ds);
   *size_ret = csize;

   free(chnk->data);
   eet_data_chunk_free(chnk);

   return cdata;
}

EAPI int
eet_data_node_write_cipher(Eet_File   *ef,
                           const char *name,
                           const char *cipher_key,
                           Eet_Node   *node,
                           int         comp)
{
   Eet_Dictionary *ed;
   void *data_enc;
   int size;
   int val;

   ed = eet_dictionary_get(ef);

   // XXX: in future put lock outside here?
   data_enc = _eet_data_dump_encode(EET_G_UNKNOWN, ed, node, &size);
   // XXX: release manual eet_dictionary lock
   if (!data_enc)
     return 0;

   val = eet_write_cipher(ef, name, data_enc, size, comp, cipher_key);
   free(data_enc);
   return val;
}

EAPI void *
eet_data_node_encode_cipher(Eet_Node   *node,
                            const char *cipher_key,
                            int        *size_ret)
{
   void *ret = NULL;
   void *ciphered = NULL;
   unsigned int ciphered_len = 0;
   int size;

   ret = _eet_data_dump_encode(EET_G_UNKNOWN, NULL, node, &size);
   if (cipher_key && ret)
     {
        if (eet_cipher(ret, size, cipher_key,
                       strlen(cipher_key), &ciphered, &ciphered_len))
          {
             if (ciphered)
               free(ciphered);

             if (size_ret)
               *size_ret = 0;

             free(ret);
             return NULL;
          }

        free(ret);
        size = (int)ciphered_len;
        ret = ciphered;
     }

   if (size_ret)
     *size_ret = size;

   return ret;
}

EAPI void *
eet_data_descriptor_encode_cipher(Eet_Data_Descriptor *edd,
                                  const void          *data_in,
                                  const char          *cipher_key,
                                  int                 *size_ret)
{
   void *ret = NULL;
   void *ciphered = NULL;
   unsigned int ciphered_len = 0;
   int size;

   EINA_SAFETY_ON_NULL_RETURN_VAL(edd, NULL);
   EINA_SAFETY_ON_NULL_RETURN_VAL(data_in, NULL);

   ret = _eet_data_descriptor_encode(NULL, edd, data_in, &size);
   if (cipher_key && ret)
     {
        if (eet_cipher(ret, size, cipher_key,
                       strlen(cipher_key), &ciphered, &ciphered_len))
          {
             if (ciphered)
               free(ciphered);

             if (size_ret)
               *size_ret = 0;

             free(ret);
             return NULL;
          }

        free(ret);
        size = ciphered_len;
        ret = ciphered;
     }

   if (size_ret)
     *size_ret = size;

   return ret;
}

EAPI void *
eet_data_descriptor_encode(Eet_Data_Descriptor *edd,
                           const void          *data_in,
                           int                 *size_ret)
{
   return eet_data_descriptor_encode_cipher(edd, data_in, NULL, size_ret);
}

EAPI void *
eet_data_xattr_cipher_get(const char          *filename,
                          const char          *attribute,
                          Eet_Data_Descriptor *edd,
                          const char          *cipher_key)
{
   void *blob;
   void *ret;
   ssize_t size;

   EINA_SAFETY_ON_NULL_RETURN_VAL(edd, NULL);

   blob = eina_xattr_get(filename, attribute, &size);
   if (!blob) return NULL;

   ret = eet_data_descriptor_decode_cipher(edd, blob, cipher_key, size);
   free(blob);

   return ret;
}

EAPI Eina_Bool
eet_data_xattr_cipher_set(const char          *filename,
                          const char          *attribute,
                          Eet_Data_Descriptor *edd,
                          const char          *cipher_key,
                          const void          *data,
                          Eina_Xattr_Flags     flags)
{
   void *blob;
   int size;
   Eina_Bool ret;

   EINA_SAFETY_ON_NULL_RETURN_VAL(edd, EINA_FALSE);

   blob = eet_data_descriptor_encode_cipher(edd, data, cipher_key, &size);
   if (!blob) return EINA_FALSE;

   ret = eina_xattr_set(filename, attribute, blob, size, flags);
   free(blob);

   return ret;
}
