diff --git a/legacy/eina/src/include/eina_inline_value.x b/legacy/eina/src/include/eina_inline_value.x index 31b330f87b..e059407c27 100644 --- a/legacy/eina/src/include/eina_inline_value.x +++ b/legacy/eina/src/include/eina_inline_value.x @@ -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) diff --git a/legacy/eina/src/include/eina_value.h b/legacy/eina/src/include/eina_value.h index 365302e856..6a4c217afd 100644 --- a/legacy/eina/src/include/eina_value.h +++ b/legacy/eina/src/include/eina_value.h @@ -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 /** @@ -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 * diff --git a/legacy/eina/src/lib/eina_value.c b/legacy/eina/src/lib/eina_value.c index 5751e0636d..22b28c57c7 100644 --- a/legacy/eina/src/lib/eina_value.c +++ b/legacy/eina/src/lib/eina_value.c @@ -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) { diff --git a/legacy/eina/src/tests/eina_test_value.c b/legacy/eina/src/tests/eina_test_value.c index 101890b880..5688365bc4 100644 --- a/legacy/eina/src/tests/eina_test_value.c +++ b/legacy/eina/src/tests/eina_test_value.c @@ -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); }