eina_value: add hash.

Similar to list and array, but takes string keys instead of position.

It can convert to string, I've added tests for it, but hash algorithm
changes may break the simple comparison I did... and I don't want to
parse the string to be more accurate.



SVN revision: 67103
This commit is contained in:
Gustavo Sverzut Barbieri 2012-01-11 23:54:35 +00:00
parent f33a71339c
commit 4e74c624d8
4 changed files with 1099 additions and 8 deletions

View File

@ -1220,6 +1220,208 @@ eina_value_list_pappend(Eina_Value *value, const void *ptr)
}
#undef EINA_VALUE_TYPE_LIST_CHECK_RETURN_VAL
#define EINA_VALUE_TYPE_HASH_CHECK_RETURN_VAL(value, retval) \
EINA_SAFETY_ON_NULL_RETURN_VAL(value, retval); \
EINA_SAFETY_ON_FALSE_RETURN_VAL(value->type == EINA_VALUE_TYPE_HASH, retval)
static inline Eina_Bool
eina_value_hash_setup(Eina_Value *value, const Eina_Value_Type *subtype, unsigned int buckets_power_size)
{
Eina_Value_Hash desc = { subtype, buckets_power_size, NULL };
if (!eina_value_setup(value, EINA_VALUE_TYPE_HASH))
return EINA_FALSE;
if (!eina_value_pset(value, &desc))
{
eina_value_flush(value);
return EINA_FALSE;
}
return EINA_TRUE;
}
static inline unsigned int
eina_value_hash_population(const Eina_Value *value)
{
Eina_Value_Hash *desc;
EINA_VALUE_TYPE_HASH_CHECK_RETURN_VAL(value, 0);
desc = eina_value_memory_get(value);
if (!desc)
return 0;
return eina_hash_population(desc->hash);
}
static inline Eina_Bool
eina_value_hash_del(Eina_Value *value, const char *key)
{
Eina_Value_Hash *desc;
void *mem;
EINA_VALUE_TYPE_HASH_CHECK_RETURN_VAL(value, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(key, EINA_FALSE);
desc = eina_value_memory_get(value);
if (!desc)
return EINA_FALSE;
mem = eina_hash_find(desc->hash, key);
if (!mem)
return EINA_FALSE;
eina_value_type_flush(desc->subtype, mem);
free(mem);
eina_hash_del_by_key(desc->hash, key);
return EINA_TRUE;
}
static inline Eina_Bool
eina_value_hash_vset(Eina_Value *value, const char *key, va_list args)
{
Eina_Value_Hash *desc;
void *mem;
EINA_VALUE_TYPE_HASH_CHECK_RETURN_VAL(value, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(key, EINA_FALSE);
desc = eina_value_memory_get(value);
if (!desc)
return EINA_FALSE;
mem = eina_hash_find(desc->hash, key);
if (mem)
eina_value_type_flush(desc->subtype, mem);
else
{
mem = malloc(desc->subtype->value_size);
if (!mem)
{
eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
return EINA_FALSE;
}
if (!eina_hash_add(desc->hash, key, mem))
{
free(mem);
return EINA_FALSE;
}
}
if (!eina_value_type_setup(desc->subtype, mem)) goto error_setup;
if (!eina_value_type_vset(desc->subtype, mem, args)) goto error_set;
return EINA_TRUE;
error_set:
eina_value_type_flush(desc->subtype, mem);
error_setup:
eina_hash_del_by_key(desc->hash, key);
free(mem);
return EINA_FALSE;
}
static inline Eina_Bool
eina_value_hash_vget(const Eina_Value *value, const char *key, va_list args)
{
Eina_Value_Hash *desc;
const void *mem;
void *ptr;
Eina_Bool ret;
EINA_VALUE_TYPE_HASH_CHECK_RETURN_VAL(value, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(key, EINA_FALSE);
desc = eina_value_memory_get(value);
if (!desc)
return EINA_FALSE;
mem = eina_hash_find(desc->hash, key);
if (!mem)
return EINA_FALSE;
ptr = va_arg(args, void *);
ret = eina_value_type_pget(desc->subtype, mem, ptr);
return ret;
}
static inline Eina_Bool
eina_value_hash_set(Eina_Value *value, const char *key, ...)
{
va_list args;
Eina_Bool ret;
va_start(args, key);
ret = eina_value_hash_vset(value, key, args);
va_end(args);
return ret;
}
static inline Eina_Bool
eina_value_hash_get(const Eina_Value *value, const char *key, ...)
{
va_list args;
Eina_Bool ret;
va_start(args, key);
ret = eina_value_hash_vget(value, key, args);
va_end(args);
return ret;
}
static inline Eina_Bool
eina_value_hash_pset(Eina_Value *value, const char *key, const void *ptr)
{
Eina_Value_Hash *desc;
void *mem;
EINA_VALUE_TYPE_HASH_CHECK_RETURN_VAL(value, 0);
EINA_SAFETY_ON_NULL_RETURN_VAL(key, EINA_FALSE);
desc = eina_value_memory_get(value);
if (!desc)
return EINA_FALSE;
mem = eina_hash_find(desc->hash, key);
if (mem)
eina_value_type_flush(desc->subtype, mem);
else
{
mem = malloc(desc->subtype->value_size);
if (!mem)
{
eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
return EINA_FALSE;
}
if (!eina_hash_add(desc->hash, key, mem))
{
free(mem);
return EINA_FALSE;
}
}
if (!eina_value_type_setup(desc->subtype, mem)) goto error_setup;
if (!eina_value_type_pset(desc->subtype, mem, ptr)) goto error_set;
return EINA_TRUE;
error_set:
eina_value_type_flush(desc->subtype, mem);
error_setup:
eina_hash_del_by_key(desc->hash, key);
free(mem);
return EINA_FALSE;
}
static inline Eina_Bool
eina_value_hash_pget(const Eina_Value *value, const char *key, void *ptr)
{
Eina_Value_Hash *desc;
const void *mem;
Eina_Bool ret;
EINA_VALUE_TYPE_HASH_CHECK_RETURN_VAL(value, 0);
EINA_SAFETY_ON_NULL_RETURN_VAL(key, EINA_FALSE);
desc = eina_value_memory_get(value);
if (!desc)
return EINA_FALSE;
mem = eina_hash_find(desc->hash, key);
if (!mem)
return EINA_FALSE;
ret = eina_value_type_pget(desc->subtype, mem, ptr);
return ret;
}
#undef EINA_VALUE_TYPE_HASH_CHECK_RETURN_VAL
static inline Eina_Bool
eina_value_type_setup(const Eina_Value_Type *type, void *mem)

View File

@ -23,6 +23,7 @@
#include "eina_fp.h" /* defines int64_t and uint64_t */
#include "eina_inarray.h"
#include "eina_list.h"
#include "eina_hash.h"
#include <stdarg.h>
/**
@ -222,6 +223,19 @@ EAPI extern const Eina_Value_Type *EINA_VALUE_TYPE_ARRAY;
*/
EAPI extern const Eina_Value_Type *EINA_VALUE_TYPE_LIST;
/**
* @var EINA_VALUE_TYPE_HASH
*
* manages hash type. The value get/set are the type of elements in
* the hash, use the alternaties:
* @li eina_value_hash_get() and eina_value_hash_set()
* @li eina_value_hash_vget() and eina_value_hash_vset()
* @li eina_value_hash_pget() and eina_value_hash_pset()
*
* @since 1.2
*/
EAPI extern const Eina_Value_Type *EINA_VALUE_TYPE_HASH;
/**
* @var EINA_ERROR_VALUE_FAILED
@ -383,6 +397,7 @@ static inline int eina_value_compare(const Eina_Value *a,
* @li EINA_VALUE_TYPE_STRING: const char *
* @li EINA_VALUE_TYPE_ARRAY: Eina_Value_Array
* @li EINA_VALUE_TYPE_LIST: Eina_Value_List
* @li EINA_VALUE_TYPE_HASH: Eina_Value_Hash
*
* @code
* Eina_Value *value = eina_value_new(EINA_VALUE_TYPE_INT);
@ -400,6 +415,7 @@ static inline int eina_value_compare(const Eina_Value *a,
*
* @note for array member see eina_value_array_set()
* @note for list member see eina_value_list_set()
* @note for hash member see eina_value_hash_set()
*
* @see eina_value_get()
* @see eina_value_vset()
@ -439,6 +455,7 @@ static inline Eina_Bool eina_value_set(Eina_Value *value,
* @li EINA_VALUE_TYPE_STRING: const char **
* @li EINA_VALUE_TYPE_ARRAY: Eina_Value_Array*
* @li EINA_VALUE_TYPE_LIST: Eina_Value_List*
* @li EINA_VALUE_TYPE_HASH: Eina_Value_Hash*
*
* @code
* Eina_Value *value = eina_value_new(EINA_VALUE_TYPE_INT);
@ -459,6 +476,7 @@ static inline Eina_Bool eina_value_set(Eina_Value *value,
*
* @note for array member see eina_value_array_get()
* @note for list member see eina_value_list_get()
* @note for hash member see eina_value_hash_get()
*
* @see eina_value_set()
* @see eina_value_vset()
@ -477,6 +495,7 @@ static inline Eina_Bool eina_value_get(const Eina_Value *value,
*
* @note for array member see eina_value_array_vset()
* @note for list member see eina_value_list_vset()
* @note for hash member see eina_value_hash_vset()
*
* @see eina_value_vget()
* @see eina_value_set()
@ -500,6 +519,7 @@ static inline Eina_Bool eina_value_vset(Eina_Value *value,
*
* @note for array member see eina_value_array_vget()
* @note for list member see eina_value_list_vget()
* @note for hash member see eina_value_hash_vget()
*
* @see eina_value_vset()
* @see eina_value_get()
@ -535,6 +555,7 @@ static inline Eina_Bool eina_value_vget(const Eina_Value *value,
* @li EINA_VALUE_TYPE_STRING: const char **
* @li EINA_VALUE_TYPE_ARRAY: Eina_Value_Array*
* @li EINA_VALUE_TYPE_LIST: Eina_Value_List*
* @li EINA_VALUE_TYPE_HASH: Eina_Value_Hash*
*
* @note the pointer contents are written using the size defined by
* type. It can be larger than void* or uint64_t.
@ -556,6 +577,7 @@ static inline Eina_Bool eina_value_vget(const Eina_Value *value,
*
* @note for array member see eina_value_array_pset()
* @note for list member see eina_value_list_pset()
* @note for hash member see eina_value_hash_pset()
*
* @see eina_value_pget()
* @see eina_value_set()
@ -596,6 +618,7 @@ static inline Eina_Bool eina_value_pset(Eina_Value *value,
* @li EINA_VALUE_TYPE_STRING: const char **
* @li EINA_VALUE_TYPE_ARRAY: Eina_Value_Array*
* @li EINA_VALUE_TYPE_LIST: Eina_Value_List*
* @li EINA_VALUE_TYPE_HASH: Eina_Value_Hash*
*
* @code
* Eina_Value *value = eina_value_new(EINA_VALUE_TYPE_INT);
@ -616,6 +639,7 @@ static inline Eina_Bool eina_value_pset(Eina_Value *value,
*
* @note for array member see eina_value_array_get()
* @note for list member see eina_value_list_get()
* @note for hash member see eina_value_hash_get()
*
* @see eina_value_set()
* @see eina_value_vset()
@ -800,9 +824,11 @@ static inline Eina_Bool eina_value_array_remove(Eina_Value *value,
* @li EINA_VALUE_TYPE_STRINGSHARE: const char *
* @li EINA_VALUE_TYPE_STRING: const char *
* @li EINA_VALUE_TYPE_ARRAY: Eina_Value_Array
* @li EINA_VALUE_TYPE_LIST: Eina_Value_List
* @li EINA_VALUE_TYPE_HASH: Eina_Value_Hash
*
* @code
* Eina_Value *value = eina_value_array_new(EINA_VALUE_TYPE_INT);
* Eina_Value *value = eina_value_array_new(EINA_VALUE_TYPE_INT, 0);
* int x;
*
* eina_value_array_append(value, 1234);
@ -856,9 +882,11 @@ static inline Eina_Bool eina_value_array_set(Eina_Value *value,
* @li EINA_VALUE_TYPE_STRINGSHARE: const char **
* @li EINA_VALUE_TYPE_STRING: const char **
* @li EINA_VALUE_TYPE_ARRAY: Eina_Value_Array*
* @li EINA_VALUE_TYPE_LIST: Eina_Value_List*
* @li EINA_VALUE_TYPE_HASH: Eina_Value_Hash*
*
* @code
* Eina_Value *value = eina_value_array_new(EINA_VALUE_TYPE_INT);
* Eina_Value *value = eina_value_array_new(EINA_VALUE_TYPE_INT, 0);
* int x;
*
* eina_value_array_append(value, 1234);
@ -900,9 +928,11 @@ static inline Eina_Bool eina_value_array_get(const Eina_Value *value,
* @li EINA_VALUE_TYPE_STRINGSHARE: const char *
* @li EINA_VALUE_TYPE_STRING: const char *
* @li EINA_VALUE_TYPE_ARRAY: Eina_Value_Array
* @li EINA_VALUE_TYPE_LIST: Eina_Value_List
* @li EINA_VALUE_TYPE_HASH: Eina_Value_Hash
*
* @code
* Eina_Value *value = eina_value_array_new(EINA_VALUE_TYPE_INT);
* Eina_Value *value = eina_value_array_new(EINA_VALUE_TYPE_INT, 0);
* int x;
*
* eina_value_array_insert(value, 0, 1234);
@ -950,9 +980,11 @@ static inline Eina_Bool eina_value_array_insert(Eina_Value *value,
* @li EINA_VALUE_TYPE_STRINGSHARE: const char *
* @li EINA_VALUE_TYPE_STRING: const char *
* @li EINA_VALUE_TYPE_ARRAY: Eina_Value_Array
* @li EINA_VALUE_TYPE_LIST: Eina_Value_List
* @li EINA_VALUE_TYPE_HASH: Eina_Value_Hash
*
* @code
* Eina_Value *value = eina_value_array_new(EINA_VALUE_TYPE_INT);
* Eina_Value *value = eina_value_array_new(EINA_VALUE_TYPE_INT, 0);
* int x;
*
* eina_value_array_append(value, 1234);
@ -1086,12 +1118,14 @@ static inline Eina_Bool eina_value_array_vappend(Eina_Value *value,
* @li EINA_VALUE_TYPE_STRINGSHARE: const char **
* @li EINA_VALUE_TYPE_STRING: const char **
* @li EINA_VALUE_TYPE_ARRAY: Eina_Value_Array*
* @li EINA_VALUE_TYPE_LIST: Eina_Value_List
* @li EINA_VALUE_TYPE_HASH: Eina_Value_Hash
*
* @note the pointer contents are written using the size defined by
* type. It can be larger than void* or uint64_t.
*
* @code
* Eina_Value *value = eina_value_array_new(EINA_VALUE_TYPE_INT);
* Eina_Value *value = eina_value_array_new(EINA_VALUE_TYPE_INT, 0);
* int x = 1234;
*
* eina_value_array_append(value, 1234);
@ -1146,9 +1180,11 @@ static inline Eina_Bool eina_value_array_pset(Eina_Value *value,
* @li EINA_VALUE_TYPE_STRINGSHARE: const char **
* @li EINA_VALUE_TYPE_STRING: const char **
* @li EINA_VALUE_TYPE_ARRAY: Eina_Value_Array*
* @li EINA_VALUE_TYPE_LIST: Eina_Value_List
* @li EINA_VALUE_TYPE_HASH: Eina_Value_Hash
*
* @code
* Eina_Value *value = eina_value_array_new(EINA_VALUE_TYPE_INT);
* Eina_Value *value = eina_value_array_new(EINA_VALUE_TYPE_INT, 0);
* int x;
*
* eina_value_array_append(value, 1234);
@ -1191,12 +1227,14 @@ static inline Eina_Bool eina_value_array_pget(const Eina_Value *value,
* @li EINA_VALUE_TYPE_STRINGSHARE: const char **
* @li EINA_VALUE_TYPE_STRING: const char **
* @li EINA_VALUE_TYPE_ARRAY: Eina_Value_Array*
* @li EINA_VALUE_TYPE_LIST: Eina_Value_List
* @li EINA_VALUE_TYPE_HASH: Eina_Value_Hash
*
* @note the pointer contents are written using the size defined by
* type. It can be larger than void* or uint64_t.
*
* @code
* Eina_Value *value = eina_value_array_new(EINA_VALUE_TYPE_INT);
* Eina_Value *value = eina_value_array_new(EINA_VALUE_TYPE_INT, 0);
* int x = 1234;
*
* eina_value_array_pinsert(value, 0, &x);
@ -1244,12 +1282,14 @@ static inline Eina_Bool eina_value_array_pinsert(Eina_Value *value,
* @li EINA_VALUE_TYPE_STRINGSHARE: const char **
* @li EINA_VALUE_TYPE_STRING: const char **
* @li EINA_VALUE_TYPE_ARRAY: Eina_Value_Array*
* @li EINA_VALUE_TYPE_LIST: Eina_Value_List
* @li EINA_VALUE_TYPE_HASH: Eina_Value_Hash
*
* @note the pointer contents are written using the size defined by
* type. It can be larger than void* or uint64_t.
*
* @code
* Eina_Value *value = eina_value_array_new(EINA_VALUE_TYPE_INT);
* Eina_Value *value = eina_value_array_new(EINA_VALUE_TYPE_INT, 0);
* int x = 1234;
*
* eina_value_array_pappend(value, &x);
@ -1867,6 +1907,336 @@ static inline Eina_Bool eina_value_list_pappend(Eina_Value *value,
* @}
*/
/**
* @defgroup Eina_Value_Hash_Group Generic Value Hash management
*
* @{
*/
/**
* @typedef Eina_Value_Hash
* Value type for #EINA_VALUE_TYPE_HASH
*
* @since 1.2
*/
typedef struct _Eina_Value_Hash Eina_Value_Hash;
/**
* @struct _Eina_Value_Hash
* Used to store the hash and its subtype.
*/
struct _Eina_Value_Hash
{
const Eina_Value_Type *subtype; /**< how to allocate and access items */
unsigned int buckets_power_size; /**< how to allocate hash buckets, if zero a sane default is chosen. */
Eina_Hash *hash; /**< the hash that holds data, members are of subtype->value_size bytes. */
};
/**
* @brief Create generic value storage of type hash.
* @param subtype how to manage this hash members.
* @param buckets_power_size how to allocate hash buckets (2 ^
* buckets_power_size), if zero then a sane value is chosen.
* @return The new value or @c NULL on failure.
*
* Create a new generic value storage of type hash. The members are
* managed using the description specified by @a subtype.
*
* On failure, @c NULL is returned and #EINA_ERROR_OUT_OF_MEMORY or
* #EINA_ERROR_VALUE_FAILED is set.
*
* @note this is a helper around eina_value_hash_setup() doing malloc
* for you.
*
* @see eina_value_free()
* @see eina_value_hash_setup()
*
* @since 1.2
*/
EAPI Eina_Value *eina_value_hash_new(const Eina_Value_Type *subtype, unsigned int buckets_power_size) EINA_ARG_NONNULL(1);
/**
* @brief Setup generic value storage of type hash.
* @param value value object
* @param subtype how to manage this hash members.
* @param buckets_power_size how to allocate hash buckets (2 ^
* buckets_power_size), if zero then a sane value is chosen.
* @return #EINA_TRUE on success, #EINA_FALSE otherwise.
*
* Setups new generic value storage of type hash with the given
* @a subtype.
*
* This is the same as calling eina_value_set() with
* #EINA_VALUE_TYPE_HASH followed by eina_value_pset() with the
* #Eina_Value_Hash description configured.
*
* @note Existing memory is ignored! If it was previously set, then
* use eina_value_flush() first.
*
* On failure, #EINA_FALSE is returned and #EINA_ERROR_OUT_OF_MEMORY
* or #EINA_ERROR_VALUE_FAILED is set.
*
* @see eina_value_flush()
*
* @since 1.2
*/
static inline Eina_Bool eina_value_hash_setup(Eina_Value *value,
const Eina_Value_Type *subtype,
unsigned int buckets_power_size) EINA_ARG_NONNULL(1, 2);
/**
* @brief Query number of elements in value of hash type.
* @param value value object.
* @return number of child elements.
* @since 1.2
*/
static inline unsigned int eina_value_hash_population(const Eina_Value *value);
/**
* @brief Remove element at given position in value of hash type.
* @param value value object.
* @param key key to find the member
* @return #EINA_TRUE on success, #EINA_FALSE otherwise.
* @since 1.2
*/
static inline Eina_Bool eina_value_hash_del(Eina_Value *value,
const char *key) EINA_ARG_NONNULL(1);
/**
* @brief Set the generic value in an hash member.
* @param value source value object
* @param key key to find the member
* @return #EINA_TRUE on success, #EINA_FALSE otherwise.
*
* The variable argument is dependent on chosen subtype. The hash for
* basic types:
*
* @li EINA_VALUE_TYPE_UCHAR: unsigned char
* @li EINA_VALUE_TYPE_USHORT: unsigned short
* @li EINA_VALUE_TYPE_UINT: unsigned int
* @li EINA_VALUE_TYPE_ULONG: unsigned long
* @li EINA_VALUE_TYPE_UINT64: uint64_t
* @li EINA_VALUE_TYPE_CHAR: char
* @li EINA_VALUE_TYPE_SHORT: short
* @li EINA_VALUE_TYPE_INT: int
* @li EINA_VALUE_TYPE_LONG: long
* @li EINA_VALUE_TYPE_INT64: int64_t
* @li EINA_VALUE_TYPE_FLOAT: float
* @li EINA_VALUE_TYPE_DOUBLE: double
* @li EINA_VALUE_TYPE_STRINGSHARE: const char *
* @li EINA_VALUE_TYPE_STRING: const char *
* @li EINA_VALUE_TYPE_HASH: Eina_Value_Hash
*
* @code
* Eina_Value *value = eina_value_hash_new(EINA_VALUE_TYPE_INT, 0);
* int x;
*
* eina_value_hash_set(value, "abc", 5678);
* eina_value_hash_get(value, "abc", &x);
* eina_value_free(value);
* @endcode
*
* @see eina_value_hash_get()
* @see eina_value_hash_vset()
* @see eina_value_hash_pset()
* @see eina_value_hash_del()
*
* @since 1.2
*/
static inline Eina_Bool eina_value_hash_set(Eina_Value *value,
const char *key,
...) EINA_ARG_NONNULL(1);
/**
* @brief Get the generic value from an hash member.
* @param value source value object
* @param key key to find the member
* @return #EINA_TRUE on success, #EINA_FALSE otherwise.
*
* The value is returned in the variable argument parameter, the
* actual value is type-dependent, but usually it will be what is
* stored inside the object. There shouldn't be any memory allocation,
* thus the contents should @b not be free'd.
*
* The variable argument is dependent on chosen subtype. The hash for
* basic types:
*
* @li EINA_VALUE_TYPE_UCHAR: unsigned char*
* @li EINA_VALUE_TYPE_USHORT: unsigned short*
* @li EINA_VALUE_TYPE_UINT: unsigned int*
* @li EINA_VALUE_TYPE_ULONG: unsigned long*
* @li EINA_VALUE_TYPE_UINT64: uint64_t*
* @li EINA_VALUE_TYPE_CHAR: char*
* @li EINA_VALUE_TYPE_SHORT: short*
* @li EINA_VALUE_TYPE_INT: int*
* @li EINA_VALUE_TYPE_LONG: long*
* @li EINA_VALUE_TYPE_INT64: int64_t*
* @li EINA_VALUE_TYPE_FLOAT: float*
* @li EINA_VALUE_TYPE_DOUBLE: double*
* @li EINA_VALUE_TYPE_STRINGSHARE: const char **
* @li EINA_VALUE_TYPE_STRING: const char **
* @li EINA_VALUE_TYPE_HASH: Eina_Value_Hash*
*
* @code
* Eina_Value *value = eina_value_hash_new(EINA_VALUE_TYPE_INT, 0);
* int x;
*
* eina_value_hash_set(value, "abc", 1234);
* eina_value_hash_get(value, "abc", &x);
* eina_value_free(value);
* @endcode
*
* @see eina_value_hash_set()
* @see eina_value_hash_vset()
* @see eina_value_hash_pset()
*
* @since 1.2
*/
static inline Eina_Bool eina_value_hash_get(const Eina_Value *value,
const char *key,
...) EINA_ARG_NONNULL(1);
/**
* @brief Set the generic value in an hash member.
* @param value source value object
* @param key key to find the member
* @param args variable argument
* @return #EINA_TRUE on success, #EINA_FALSE otherwise.
* @see eina_value_hash_set()
* @see eina_value_hash_get()
* @see eina_value_hash_pset()
*
* @since 1.2
*/
static inline Eina_Bool eina_value_hash_vset(Eina_Value *value,
const char *key,
va_list args) EINA_ARG_NONNULL(1);
/**
* @brief Get the generic value from an hash member.
* @param value source value object
* @param key key to find the member
* @param args variable argument
* @return #EINA_TRUE on success, #EINA_FALSE otherwise.
*
* The value is returned in the variable argument parameter, the
* actual value is type-dependent, but usually it will be what is
* stored inside the object. There shouldn't be any memory allocation,
* thus the contents should @b not be free'd.
*
* @see eina_value_hash_vset()
* @see eina_value_hash_get()
* @see eina_value_hash_pget()
*
* @since 1.2
*/
static inline Eina_Bool eina_value_hash_vget(const Eina_Value *value,
const char *key,
va_list args) EINA_ARG_NONNULL(1);
/**
* @brief Set the generic value in an hash member from pointer.
* @param value source value object
* @param key key to find the member
* @param ptr pointer to specify the contents.
* @return #EINA_TRUE on success, #EINA_FALSE otherwise.
*
* The pointer type is dependent on chosen value type. The hash for
* basic types:
*
* @li EINA_VALUE_TYPE_UCHAR: unsigned char*
* @li EINA_VALUE_TYPE_USHORT: unsigned short*
* @li EINA_VALUE_TYPE_UINT: unsigned int*
* @li EINA_VALUE_TYPE_ULONG: unsigned long*
* @li EINA_VALUE_TYPE_UINT64: uint64_t*
* @li EINA_VALUE_TYPE_CHAR: char*
* @li EINA_VALUE_TYPE_SHORT: short*
* @li EINA_VALUE_TYPE_INT: int*
* @li EINA_VALUE_TYPE_LONG: long*
* @li EINA_VALUE_TYPE_INT64: int64_t*
* @li EINA_VALUE_TYPE_FLOAT: float*
* @li EINA_VALUE_TYPE_DOUBLE: double*
* @li EINA_VALUE_TYPE_STRINGSHARE: const char **
* @li EINA_VALUE_TYPE_STRING: const char **
* @li EINA_VALUE_TYPE_HASH: Eina_Value_Hash*
*
* @note the pointer contents are written using the size defined by
* type. It can be larger than void* or uint64_t.
*
* @code
* Eina_Value *value = eina_value_hash_new(EINA_VALUE_TYPE_INT, 0);
* int x = 1234;
*
* eina_value_hash_pset(value, "abc", &x);
* eina_value_hash_pget(value, "abc", &x);
* eina_value_free(value);
* @endcode
*
* @see eina_value_hash_set()
* @see eina_value_hash_get()
* @see eina_value_hash_vset()
*
* @since 1.2
*/
static inline Eina_Bool eina_value_hash_pset(Eina_Value *value,
const char *key,
const void *ptr) EINA_ARG_NONNULL(1, 3);
/**
* @brief Get the generic value to pointer from an hash member.
* @param value source value object
* @param key key to find the member
* @param ptr pointer to receive the contents.
* @return #EINA_TRUE on success, #EINA_FALSE otherwise.
*
* The value is returned in pointer contents, the actual value is
* type-dependent, but usually it will be what is stored inside the
* object. There shouldn't be any memory allocation, thus the contents
* should @b not be free'd.
*
* The pointer type is dependent on chosen value type. The hash for
* basic types:
*
* @li EINA_VALUE_TYPE_UCHAR: unsigned char*
* @li EINA_VALUE_TYPE_USHORT: unsigned short*
* @li EINA_VALUE_TYPE_UINT: unsigned int*
* @li EINA_VALUE_TYPE_ULONG: unsigned long*
* @li EINA_VALUE_TYPE_UINT64: uint64_t*
* @li EINA_VALUE_TYPE_CHAR: char*
* @li EINA_VALUE_TYPE_SHORT: short*
* @li EINA_VALUE_TYPE_INT: int*
* @li EINA_VALUE_TYPE_LONG: long*
* @li EINA_VALUE_TYPE_INT64: int64_t*
* @li EINA_VALUE_TYPE_FLOAT: float*
* @li EINA_VALUE_TYPE_DOUBLE: double*
* @li EINA_VALUE_TYPE_STRINGSHARE: const char **
* @li EINA_VALUE_TYPE_STRING: const char **
* @li EINA_VALUE_TYPE_HASH: Eina_Value_Hash*
*
* @code
* Eina_Value *value = eina_value_hash_new(EINA_VALUE_TYPE_INT, 0);
* int x;
*
* eina_value_hash_set(value, "abc", 1234);
* eina_value_hash_pget(value, "abc", &x);
* eina_value_free(value);
* @endcode
*
* @see eina_value_hash_set()
* @see eina_value_hash_vset()
* @see eina_value_hash_pset()
*
* @since 1.2
*/
static inline Eina_Bool eina_value_hash_pget(const Eina_Value *value,
const char *key,
void *ptr) EINA_ARG_NONNULL(1, 3);
/**
* @}
*/
/**
* @defgroup Eina_Value_Type_Group Generic Value Type management
*

View File

@ -2962,6 +2962,371 @@ static const Eina_Value_Type _EINA_VALUE_TYPE_LIST = {
_eina_value_type_list_pget
};
static Eina_Bool
_eina_value_type_hash_setup(const Eina_Value_Type *type __UNUSED__, void *mem)
{
memset(mem, 0, sizeof(Eina_Value_Hash));
return EINA_TRUE;
}
struct _eina_value_type_hash_flush_each_ctx
{
const Eina_Value_Type *subtype;
Eina_Bool ret;
};
static Eina_Bool
_eina_value_type_hash_flush_each(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *mem, void *user_data)
{
struct _eina_value_type_hash_flush_each_ctx *ctx = user_data;
ctx->ret &= eina_value_type_flush(ctx->subtype, mem);
return EINA_TRUE;
}
static Eina_Bool
_eina_value_type_hash_flush_elements(Eina_Value_Hash *tmem)
{
struct _eina_value_type_hash_flush_each_ctx ctx = {
tmem->subtype,
EINA_TRUE
};
if (!tmem->hash) return EINA_TRUE;
eina_hash_foreach(tmem->hash, _eina_value_type_hash_flush_each, &ctx);
eina_hash_free(tmem->hash);
tmem->hash = NULL;
return ctx.ret;
}
static Eina_Bool
_eina_value_type_hash_flush(const Eina_Value_Type *type __UNUSED__, void *mem)
{
Eina_Value_Hash *tmem = mem;
Eina_Bool ret =_eina_value_type_hash_flush_elements(tmem);
tmem->subtype = NULL;
return ret;
}
static unsigned int
_eina_value_hash_key_length(const void *key)
{
if (!key)
return 0;
return (int)strlen(key) + 1;
}
static int
_eina_value_hash_key_cmp(const void *key1, int key1_len, const void *key2, int key2_len)
{
int r = key1_len - key2_len;
if (r != 0)
return r;
return strcmp(key1, key2);
}
static Eina_Bool
_eina_value_type_hash_create(Eina_Value_Hash *desc)
{
if (!desc->buckets_power_size)
desc->buckets_power_size = 5;
desc->hash = eina_hash_new(_eina_value_hash_key_length,
_eina_value_hash_key_cmp,
EINA_KEY_HASH(eina_hash_superfast),
NULL, desc->buckets_power_size);
return !!desc->hash;
}
struct _eina_value_type_hash_copy_each_ctx
{
const Eina_Value_Type *subtype;
Eina_Value_Hash *dest;
Eina_Bool ret;
};
static Eina_Bool
_eina_value_type_hash_copy_each(const Eina_Hash *hash __UNUSED__, const void *key, void *_ptr, void *user_data)
{
struct _eina_value_type_hash_copy_each_ctx *ctx = user_data;
const void *ptr = _ptr;
void *imem = malloc(ctx->subtype->value_size);
if (!imem)
{
ctx->ret = EINA_FALSE;
return EINA_FALSE;
}
if (!ctx->subtype->copy(ctx->subtype, ptr, imem))
{
free(imem);
ctx->ret = EINA_FALSE;
return EINA_FALSE;
}
if (!eina_hash_add(ctx->dest->hash, key, imem))
{
eina_value_type_flush(ctx->subtype, imem);
free(imem);
ctx->ret = EINA_FALSE;
return EINA_FALSE;
}
return EINA_TRUE;
}
static Eina_Bool
_eina_value_type_hash_copy(const Eina_Value_Type *type __UNUSED__, const void *src, void *dst)
{
const Eina_Value_Hash *s = src;
Eina_Value_Hash *d = dst;
struct _eina_value_type_hash_copy_each_ctx ctx = {s->subtype, d, EINA_TRUE};
d->subtype = s->subtype;
d->buckets_power_size = s->buckets_power_size;
if ((!s->hash) || (!s->subtype))
{
d->hash = NULL;
return EINA_TRUE;
}
if (!s->subtype->copy)
{
eina_error_set(EINA_ERROR_VALUE_FAILED);
return EINA_FALSE;
}
if (!_eina_value_type_hash_create(d))
return EINA_FALSE;
eina_hash_foreach(s->hash, _eina_value_type_hash_copy_each, &ctx);
if (!ctx.ret)
{
_eina_value_type_hash_flush_elements(d);
return EINA_FALSE;
}
return EINA_TRUE;
}
struct _eina_value_type_hash_compare_each_ctx
{
const Eina_Value_Type *subtype;
const Eina_Hash *other;
int cmp;
};
static Eina_Bool
_eina_value_type_hash_compare_each(const Eina_Hash *hash __UNUSED__, const void *key, void *_ptr, void *user_data)
{
struct _eina_value_type_hash_compare_each_ctx *ctx = user_data;
const void *self_ptr = _ptr;
const void *other_ptr = eina_hash_find(ctx->other, key);
if (!other_ptr) return EINA_TRUE;
ctx->cmp = ctx->subtype->compare(ctx->subtype, self_ptr, other_ptr);
return ctx->cmp == 0;
}
static int
_eina_value_type_hash_compare(const Eina_Value_Type *type __UNUSED__, const void *a, const void *b)
{
const Eina_Value_Hash *eva_a = a, *eva_b = b;
struct _eina_value_type_hash_compare_each_ctx ctx = {
eva_a->subtype, eva_b->hash, 0
};
if (eva_a->subtype != eva_b->subtype)
{
eina_error_set(EINA_ERROR_VALUE_FAILED);
return -1;
}
if (!eva_a->subtype->compare)
{
eina_error_set(EINA_ERROR_VALUE_FAILED);
return 0;
}
if ((!eva_a->hash) && (!eva_b->hash))
return 0;
else if (!eva_a->hash)
return -1;
else if (!eva_b->hash)
return 1;
eina_hash_foreach(eva_a->hash, _eina_value_type_hash_compare_each, &ctx);
if (ctx.cmp == 0)
{
unsigned int count_a = eina_hash_population(eva_a->hash);
unsigned int count_b = eina_hash_population(eva_b->hash);
if (count_a < count_b)
return -1;
else if (count_a > count_b)
return 1;
return 0;
}
return ctx.cmp;
}
static Eina_Bool
_eina_value_type_hash_find_first(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *ptr, void *user_data)
{
void **ret = user_data;
*ret = ptr;
return EINA_FALSE;
}
struct _eina_value_type_hash_convert_to_string_each_ctx
{
const Eina_Value_Type *subtype;
Eina_Strbuf *str;
Eina_Value tmp;
Eina_Bool first;
};
static Eina_Bool
_eina_value_type_hash_convert_to_string_each(const Eina_Hash *hash __UNUSED__, const void *_key, void *_ptr, void *user_data)
{
struct _eina_value_type_hash_convert_to_string_each_ctx *ctx = user_data;
const char *key = _key;
const void *ptr = _ptr;
Eina_Bool r = EINA_FALSE;
if (ctx->first) ctx->first = EINA_FALSE;
else eina_strbuf_append_length(ctx->str, ", ", 2);
eina_strbuf_append(ctx->str, key);
eina_strbuf_append_length(ctx->str, ": ", 2);
if (ctx->subtype->convert_to)
{
r = ctx->subtype->convert_to(ctx->subtype, EINA_VALUE_TYPE_STRING,
ptr, ctx->tmp.value.buf);
if (r)
{
eina_strbuf_append(ctx->str, ctx->tmp.value.ptr);
free(ctx->tmp.value.ptr);
ctx->tmp.value.ptr = NULL;
}
}
if (!r)
eina_strbuf_append_char(ctx->str, '?');
return EINA_TRUE;
}
static Eina_Bool
_eina_value_type_hash_convert_to(const Eina_Value_Type *type __UNUSED__, const Eina_Value_Type *convert, const void *type_mem, void *convert_mem)
{
const Eina_Value_Hash *tmem = type_mem;
Eina_Bool ret = EINA_FALSE;
if ((convert == EINA_VALUE_TYPE_STRING) ||
(convert == EINA_VALUE_TYPE_STRINGSHARE))
{
Eina_Strbuf *str = eina_strbuf_new();
if (!tmem->hash) eina_strbuf_append(str, "{}");
else
{
struct _eina_value_type_hash_convert_to_string_each_ctx ctx;
const char *s;
ctx.subtype = tmem->subtype;
ctx.str = str;
ctx.first = EINA_TRUE;
eina_value_setup(&ctx.tmp, EINA_VALUE_TYPE_STRING);
eina_strbuf_append_char(str, '{');
eina_hash_foreach(tmem->hash,
_eina_value_type_hash_convert_to_string_each,
&ctx);
eina_strbuf_append_char(str, '}');
s = eina_strbuf_string_get(str);
ret = eina_value_type_pset(convert, convert_mem, &s);
eina_strbuf_free(str);
}
}
else if ((tmem->hash) && (eina_hash_population(tmem->hash) == 1))
{
const Eina_Value_Type *subtype = tmem->subtype;
void *imem = NULL;
eina_hash_foreach(tmem->hash, _eina_value_type_hash_find_first, &imem);
if (!imem) /* shouldn't happen... */
ret = EINA_FALSE;
else
{
if (subtype->convert_to)
ret = subtype->convert_to(subtype, convert, imem, convert_mem);
if ((!ret) && (convert->convert_from))
ret = convert->convert_from(convert, subtype, convert_mem, imem);
}
}
if (!ret)
{
eina_error_set(EINA_ERROR_VALUE_FAILED);
return EINA_FALSE;
}
return EINA_TRUE;
}
static Eina_Bool
_eina_value_type_hash_pset(const Eina_Value_Type *type __UNUSED__, void *mem, const void *ptr)
{
Eina_Value_Hash *tmem = mem;
const Eina_Value_Hash *desc = ptr;
unsigned int buckets_power;
if ((!tmem->subtype) && (!desc->subtype))
return EINA_TRUE;
if (desc->buckets_power_size)
buckets_power = desc->buckets_power_size;
else
buckets_power = 5;
if (tmem->hash) _eina_value_type_hash_flush_elements(tmem);
if (!_eina_value_type_hash_create(tmem))
return EINA_FALSE;
tmem->subtype = desc->subtype;
return EINA_TRUE;
}
static Eina_Bool
_eina_value_type_hash_vset(const Eina_Value_Type *type, void *mem, va_list args)
{
const Eina_Value_Hash desc = va_arg(args, Eina_Value_Hash);
_eina_value_type_hash_pset(type, mem, &desc);
return EINA_TRUE;
}
static Eina_Bool
_eina_value_type_hash_pget(const Eina_Value_Type *type __UNUSED__, const void *mem, void *ptr)
{
memcpy(ptr, mem, sizeof(Eina_Value_Hash));
return EINA_TRUE;
}
static const Eina_Value_Type _EINA_VALUE_TYPE_HASH = {
EINA_VALUE_TYPE_VERSION,
sizeof(Eina_Value_Hash),
"Eina_Value_Hash",
_eina_value_type_hash_setup,
_eina_value_type_hash_flush,
_eina_value_type_hash_copy,
_eina_value_type_hash_compare,
_eina_value_type_hash_convert_to,
NULL, /* no convert from */
_eina_value_type_hash_vset,
_eina_value_type_hash_pset,
_eina_value_type_hash_pget
};
/* keep all basic types inlined in an array so we can compare if it's
* a basic type using pointer arithmetic.
*
@ -3221,6 +3586,7 @@ eina_value_init(void)
EINA_VALUE_TYPE_ARRAY = &_EINA_VALUE_TYPE_ARRAY;
EINA_VALUE_TYPE_LIST = &_EINA_VALUE_TYPE_LIST;
EINA_VALUE_TYPE_HASH = &_EINA_VALUE_TYPE_HASH;
return EINA_TRUE;
}
@ -3271,6 +3637,7 @@ EAPI const Eina_Value_Type *EINA_VALUE_TYPE_STRINGSHARE = NULL;
EAPI const Eina_Value_Type *EINA_VALUE_TYPE_STRING = NULL;
EAPI const Eina_Value_Type *EINA_VALUE_TYPE_ARRAY = NULL;
EAPI const Eina_Value_Type *EINA_VALUE_TYPE_LIST = NULL;
EAPI const Eina_Value_Type *EINA_VALUE_TYPE_HASH = NULL;
EAPI Eina_Error EINA_ERROR_VALUE_FAILED = 0;
@ -3421,6 +3788,26 @@ eina_value_list_new(const Eina_Value_Type *subtype)
return value;
}
EAPI Eina_Value *
eina_value_hash_new(const Eina_Value_Type *subtype, unsigned int buckets_power_size)
{
Eina_Value *value;
EINA_SAFETY_ON_FALSE_RETURN_VAL(eina_value_type_check(subtype), EINA_FALSE);
value = calloc(1, sizeof(Eina_Value));
if (!value)
return NULL;
if (!eina_value_hash_setup(value, subtype, buckets_power_size))
{
free(value);
return NULL;
}
return value;
}
EAPI Eina_Bool
eina_value_type_check(const Eina_Value_Type *type)
{

View File

@ -380,6 +380,52 @@ START_TEST(eina_value_test_compare)
fail_unless(eina_value_list_set(b, 0, 10));
fail_unless(eina_value_compare(a, b) < 0);
eina_value_flush(a);
eina_value_flush(b);
fail_unless(eina_value_hash_setup(a, EINA_VALUE_TYPE_CHAR, 0));
fail_unless(eina_value_hash_setup(b, EINA_VALUE_TYPE_CHAR, 0));
fail_unless(eina_value_compare(a, b) == 0);
fail_unless(eina_value_hash_set(a, "abc", 1));
fail_unless(eina_value_hash_set(a, "xyz", 2));
fail_unless(eina_value_hash_set(a, "hello", 3));
fail_unless(eina_value_hash_set(b, "abc", 1));
fail_unless(eina_value_hash_set(b, "xyz", 2));
fail_unless(eina_value_hash_set(b, "hello", 3));
fail_unless(eina_value_compare(a, b) == 0);
fail_unless(eina_value_hash_set(a, "abc", 0));
fail_unless(eina_value_compare(a, b) < 0);
fail_unless(eina_value_hash_set(a, "abc", 10));
fail_unless(eina_value_compare(a, b) > 0);
fail_unless(eina_value_hash_set(a, "abc", 1));
fail_unless(eina_value_hash_set(b, "abc", 0));
fail_unless(eina_value_compare(a, b) > 0);
fail_unless(eina_value_hash_set(b, "abc", 10));
fail_unless(eina_value_compare(a, b) < 0);
fail_unless(eina_value_hash_set(b, "abc", 1));
fail_unless(eina_value_compare(a, b) == 0);
/* bigger hashs are greater */
fail_unless(eina_value_hash_set(b,"newkey", 0));
fail_unless(eina_value_compare(a, b) < 0);
fail_unless(eina_value_hash_set(a, "newkey", 0));
fail_unless(eina_value_hash_set(a, "onemorenewkey", 0));
fail_unless(eina_value_compare(a, b) > 0);
/* bigger hashs are greater, unless an element says otherwise */
fail_unless(eina_value_hash_set(b, "abc", 10));
fail_unless(eina_value_compare(a, b) < 0);
eina_value_free(a);
eina_value_free(b);
eina_shutdown();
@ -1176,6 +1222,91 @@ START_TEST(eina_value_test_list)
}
END_TEST
START_TEST(eina_value_test_hash)
{
Eina_Value *value, other;
char c;
char buf[1024];
char *str;
eina_init();
value = eina_value_hash_new(EINA_VALUE_TYPE_CHAR, 0);
fail_unless(value != NULL);
fail_unless(eina_value_hash_set(value, "first", 'k'));
fail_unless(eina_value_hash_set(value, "second", '-'));
fail_unless(eina_value_hash_set(value, "third", 's'));
fail_unless(eina_value_hash_get(value, "first", &c));
fail_unless(c == 'k');
fail_unless(eina_value_hash_get(value, "second", &c));
fail_unless(c == '-');
fail_unless(eina_value_hash_get(value, "third", &c));
fail_unless(c == 's');
fail_unless(eina_value_hash_set(value, "first", '!'));
fail_unless(eina_value_hash_get(value, "first", &c));
fail_unless(c == '!');
fail_unless(eina_value_hash_get(value, "second", &c));
fail_unless(c == '-');
fail_unless(eina_value_hash_get(value, "third", &c));
fail_unless(c == 's');
puts("testing hash to string -- may fail due hash algorithm changes!");
/* watchout, this is the order I got -- hash algorithm changes may change
* the order!
*/
snprintf(buf, sizeof(buf), "{first: %hhd, second: %hhd, third: %hhd}",
'!', '-', 's');
str = eina_value_to_string(value);
fail_unless(str != NULL);
printf("want: %s\n", buf);
printf("got.: %s\n", str);
fail_unless(strcmp(str, buf) == 0);
free(str);
eina_value_flush(value);
fail_unless(eina_value_hash_setup(value, EINA_VALUE_TYPE_STRINGSHARE, 0));
fail_unless(eina_value_hash_set(value, "a", "Enlightenment.org"));
fail_unless(eina_value_hash_set(value, "b", "X11"));
fail_unless(eina_value_hash_set(value, "c", "Pants"));
fail_unless(eina_value_hash_set(value, "d", "on!!!"));
fail_unless(eina_value_hash_set(value, "e", "k-s"));
/* watchout, this is the order I got -- hash algorithm changes may change
* the order!
*/
strcpy(buf, "{e: k-s, d: on!!!, a: Enlightenment.org, b: X11, c: Pants}");
str = eina_value_to_string(value);
fail_unless(str != NULL);
printf("want: %s\n", buf);
printf("got.: %s\n", str);
fail_unless(strcmp(str, buf) == 0);
free(str);
eina_value_flush(value);
fail_unless(eina_value_hash_setup(value, EINA_VALUE_TYPE_CHAR, 0));
fail_unless(eina_value_setup(&other, EINA_VALUE_TYPE_CHAR));
fail_unless(eina_value_set(&other, 100));
fail_unless(eina_value_get(&other, &c));
fail_unless(c == 100);
fail_unless(eina_value_hash_set(value, "first", 33));
fail_unless(eina_value_convert(value, &other));
fail_unless(eina_value_get(&other, &c));
fail_unless(c == 33);
eina_value_free(value);
eina_shutdown();
}
END_TEST
void
eina_test_value(TCase *tc)
{
@ -1189,4 +1320,5 @@ eina_test_value(TCase *tc)
// TODO: other converters...
tcase_add_test(tc, eina_value_test_array);
tcase_add_test(tc, eina_value_test_list);
tcase_add_test(tc, eina_value_test_hash);
}