From d57d0c5ce081c82b8d1ca2cef40e559e07637d6f Mon Sep 17 00:00:00 2001 From: Felipe Magno de Almeida Date: Thu, 9 Jul 2015 00:57:38 -0300 Subject: [PATCH] eina: Created EINA_VALUE_TYPE_OPTIONAL that is a single-element container that can be empty Eina Value Optional can be used to create a eina value that can be set or be empty and can be embedded in a eina_value_struct. --- src/lib/eina/eina_inline_value.x | 68 +++++++++ src/lib/eina/eina_value.c | 227 +++++++++++++++++++++++++++++++ src/lib/eina/eina_value.h | 90 ++++++++++++ src/tests/eina/eina_test_value.c | 144 ++++++++++++++++++++ 4 files changed, 529 insertions(+) diff --git a/src/lib/eina/eina_inline_value.x b/src/lib/eina/eina_inline_value.x index 9aff401168..a9ceea16ad 100644 --- a/src/lib/eina/eina_inline_value.x +++ b/src/lib/eina/eina_inline_value.x @@ -1652,6 +1652,74 @@ eina_value_struct_member_value_set(Eina_Value *dst, const Eina_Value_Struct_Memb #undef EINA_VALUE_TYPE_STRUCT_CHECK_RETURN_VAL +#define EINA_VALUE_TYPE_OPTIONAL_CHECK_RETURN_VAL(value, retval) \ + EINA_SAFETY_ON_NULL_RETURN_VAL(value, retval); \ + EINA_SAFETY_ON_FALSE_RETURN_VAL(value->type->setup == EINA_VALUE_TYPE_OPTIONAL->setup, retval) + +static inline Eina_Value* +eina_value_optional_empty_new() +{ + return eina_value_new(EINA_VALUE_TYPE_OPTIONAL); +} + +struct _Eina_Value_Optional_Outer +{ + Eina_Value_Type const* subtype; + void* value; +}; +typedef struct _Eina_Value_Optional_Outer Eina_Value_Optional_Outer; + +struct _Eina_Value_Optional_Inner +{ + Eina_Value_Type const* subtype; + char value[]; +}; +typedef struct _Eina_Value_Optional_Inner Eina_Value_Optional_Inner; + +static inline Eina_Bool +eina_value_optional_empty_is(const Eina_Value *value, Eina_Bool *is_empty) +{ + EINA_VALUE_TYPE_OPTIONAL_CHECK_RETURN_VAL(value, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(is_empty, EINA_FALSE); + + void *mem = eina_value_memory_get(value); + if (!mem) + return EINA_FALSE; + if(2*sizeof(void*) <= sizeof(Eina_Value_Union)) + { + Eina_Value_Optional_Outer* opt = (Eina_Value_Optional_Outer*)mem; + *is_empty = !opt->subtype; + } + else + { + *is_empty = ! *(void**)mem; + } + return EINA_TRUE; +} + +static inline const Eina_Value_Type * +eina_value_optional_type_get(Eina_Value *value) +{ + EINA_VALUE_TYPE_OPTIONAL_CHECK_RETURN_VAL(value, (const Eina_Value_Type *)NULL); + + void *mem = eina_value_memory_get(value); + if (!mem) + return EINA_FALSE; + + if(2*sizeof(void*) <= sizeof(Eina_Value_Union)) + { + Eina_Value_Optional_Outer* opt = (Eina_Value_Optional_Outer*)mem; + return opt->subtype; + } + else + { + Eina_Value_Optional_Inner* opt = *(Eina_Value_Optional_Inner**)mem; + if(!opt) + return NULL; + return opt->subtype; + } +} +#undef EINA_VALUE_TYPE_OPTIONAL_CHECK_RETURN_VAL static inline Eina_Bool eina_value_type_setup(const Eina_Value_Type *type, void *mem) diff --git a/src/lib/eina/eina_value.c b/src/lib/eina/eina_value.c index 824f11d7bd..8b45856cb9 100644 --- a/src/lib/eina/eina_value.c +++ b/src/lib/eina/eina_value.c @@ -4349,6 +4349,229 @@ static const Eina_Value_Type _EINA_VALUE_TYPE_STRUCT = { _eina_value_type_struct_pget }; +static Eina_Bool +_eina_value_type_optional_setup(const Eina_Value_Type *type EINA_UNUSED, void *mem) +{ + memset(mem, 0, type->value_size); + return EINA_TRUE; +} + +static Eina_Bool +_eina_value_type_optional_flush(const Eina_Value_Type *type EINA_UNUSED, void *mem EINA_UNUSED) +{ + if(sizeof(Eina_Value_Optional_Outer) <= sizeof(Eina_Value_Union)) + { + Eina_Value_Optional_Outer* opt = mem; + if(opt->subtype) + { + if(!eina_value_type_flush(opt->subtype, opt->value)) + return EINA_FALSE; + free(opt->value); + memset(mem, 0, sizeof(Eina_Value_Optional_Outer)); + } + } + else + { + Eina_Value_Optional_Inner* opt = *(void**)mem; + if(opt) + { + if(!eina_value_type_flush(opt->subtype, opt->value)) + return EINA_FALSE; + free(*(void**)mem); + *(void**)mem = NULL; + } + } + return EINA_TRUE; +} + +EAPI Eina_Bool +eina_value_optional_reset(Eina_Value *value) +{ + void *mem = eina_value_memory_get(value); + if (!mem) + return EINA_FALSE; + return _eina_value_type_optional_flush(EINA_VALUE_TYPE_OPTIONAL, mem); +} + +EAPI Eina_Value* +eina_value_optional_new(Eina_Value_Type const *subtype, + const void* initial_value) EINA_ARG_NONNULL(1, 2) +{ + Eina_Value *value; + + value = eina_mempool_malloc(_eina_value_mp, sizeof(Eina_Value)); + if (!value) + return NULL; + + if (!eina_value_setup(value, EINA_VALUE_TYPE_OPTIONAL)) + { + eina_mempool_free(_eina_value_mp, value); + return EINA_FALSE; + } + if (!eina_value_optional_pset(value, subtype, initial_value)) + { + eina_mempool_free(_eina_value_mp, value); + return EINA_FALSE; + } + return value; +} + +EAPI Eina_Bool +eina_value_optional_pset(Eina_Value *value, + Eina_Value_Type const* subtype, + const void *subvalue) EINA_ARG_NONNULL(1, 2, 3) +{ + eina_value_optional_reset(value); + + if(sizeof(Eina_Value_Optional_Outer) <= sizeof(Eina_Value_Union)) + { + Eina_Value_Optional_Outer outer; + outer.subtype = subtype; + outer.value = malloc(subtype->value_size); + eina_value_type_setup(subtype, outer.value); + eina_value_type_pset(subtype, outer.value, subvalue); + if (!eina_value_pset(value, &outer)) + { + return EINA_FALSE; + } + } + else + { + Eina_Value_Optional_Inner *inner = + malloc(sizeof(Eina_Value_Optional_Inner) + subtype->value_size); + inner->subtype = subtype; + eina_value_type_setup(subtype, inner->value); + eina_value_type_pset(subtype, inner->value, subvalue); + if (!eina_value_pset(value, &inner)) + return EINA_FALSE; + } + + return EINA_TRUE; +} + +EAPI Eina_Bool +eina_value_optional_pget(Eina_Value *value, void *subvalue) EINA_ARG_NONNULL(1, 2, 3) +{ + if(sizeof(Eina_Value_Optional_Outer) <= sizeof(Eina_Value_Union)) + { + Eina_Value_Optional_Outer outer; + + if (!eina_value_pget(value, &outer)) + return EINA_FALSE; + + if(outer.subtype) + eina_value_type_copy(outer.subtype, outer.value, subvalue); + else + return EINA_FALSE; + } + else + { + Eina_Value_Optional_Inner *inner; + + if (!eina_value_pget(value, &inner)) + return EINA_FALSE; + + if(inner) + eina_value_type_copy(inner->subtype, inner->value, subvalue); + else + return EINA_FALSE; + } + + return EINA_TRUE; +} + +static Eina_Bool +_eina_value_type_optional_copy(const Eina_Value_Type *type EINA_UNUSED, const void *src_raw, void *dst_raw) +{ + if(sizeof(Eina_Value_Optional_Outer) <= sizeof(Eina_Value_Union)) + { + Eina_Value_Optional_Outer const* src = src_raw; + Eina_Value_Optional_Outer* dst = dst_raw; + if(src->subtype) + { + dst->subtype = src->subtype; + dst->value = malloc(src->subtype->value_size); + eina_value_type_copy(src->subtype, src->value, dst->value); + } + else + memset(dst_raw, 0, sizeof(Eina_Value_Optional_Outer)); + } + else if(src_raw) + { + Eina_Value_Optional_Inner* src = *(void**)src_raw; + Eina_Value_Optional_Inner* dst = *(void**)dst_raw + = malloc(sizeof(Eina_Value_Optional_Inner) + src->subtype->value_size); + dst->subtype = src->subtype; + + eina_value_type_copy(src->subtype, src->value, dst->value); + } + else + *(void**)dst_raw = NULL; + return EINA_TRUE; +} + +static int +_eina_value_type_optional_compare(const Eina_Value_Type *type EINA_UNUSED, const void *lhs_raw, const void *rhs_raw) +{ + if(sizeof(Eina_Value_Optional_Outer) <= sizeof(Eina_Value_Union)) + { + Eina_Value_Optional_Outer const *lhs = lhs_raw + , *rhs = rhs_raw; + if(!lhs->subtype) + return !rhs->subtype ? 0 : -1; + else if(!rhs->subtype) + return 1; + else if(lhs->subtype != rhs->subtype) + return lhs->subtype < rhs->subtype ? -1 : 1; + else + return eina_value_type_compare(lhs->subtype, lhs->value, rhs->value); + } + else + { + Eina_Value_Optional_Inner const * const* lhs_p = lhs_raw; + Eina_Value_Optional_Inner const * const* rhs_p = rhs_raw; + + if(!*lhs_p) + return *rhs_p ? -1 : 0; + else if(!*rhs_p) + return 1; + else if((*lhs_p)->subtype != (*rhs_p)->subtype) + return (*lhs_p)->subtype < (*rhs_p)->subtype ? -1 : 1; + else + return eina_value_type_compare((*lhs_p)->subtype, (*lhs_p)->value, (*rhs_p)->value); + } +} + +static Eina_Bool +_eina_value_type_optional_pset(const Eina_Value_Type *type EINA_UNUSED, void *mem, const void *ptr) +{ + _eina_value_type_optional_flush(type, mem); + _eina_value_type_optional_copy(type, ptr, mem); + return EINA_TRUE; +} + +static Eina_Bool +_eina_value_type_optional_pget(const Eina_Value_Type *type EINA_UNUSED, const void *mem, void *ptr) +{ + memcpy(ptr, mem, sizeof(Eina_Value_Union)); + return EINA_TRUE; +} + +EAPI const Eina_Value_Type _EINA_VALUE_TYPE_OPTIONAL = { + EINA_VALUE_TYPE_VERSION, + sizeof(Eina_Value_Union), + "optional", + _eina_value_type_optional_setup, + _eina_value_type_optional_flush, + _eina_value_type_optional_copy, + _eina_value_type_optional_compare, + NULL, + NULL, + NULL, + _eina_value_type_optional_pset, + _eina_value_type_optional_pget +}; + /* no model for now static Eina_Bool _eina_value_type_model_setup(const Eina_Value_Type *type EINA_UNUSED, void *mem) @@ -4911,6 +5134,8 @@ eina_value_init(void) EINA_VALUE_TYPE_MODEL = &_EINA_VALUE_TYPE_MODEL; */ + EINA_VALUE_TYPE_OPTIONAL = &_EINA_VALUE_TYPE_OPTIONAL; + EINA_VALUE_BLOB_OPERATIONS_MALLOC = &_EINA_VALUE_BLOB_OPERATIONS_MALLOC; EINA_VALUE_STRUCT_OPERATIONS_BINSEARCH = &_EINA_VALUE_STRUCT_OPERATIONS_BINSEARCH; @@ -4995,6 +5220,8 @@ EAPI const Eina_Value_Type *EINA_VALUE_TYPE_STRUCT = NULL; /* no model for now EAPI const Eina_Value_Type *EINA_VALUE_TYPE_MODEL = NULL; */ +EAPI const Eina_Value_Type *EINA_VALUE_TYPE_OPTIONAL = NULL; + EAPI const Eina_Value_Blob_Operations *EINA_VALUE_BLOB_OPERATIONS_MALLOC = NULL; diff --git a/src/lib/eina/eina_value.h b/src/lib/eina/eina_value.h index a64d3b4dd8..b273673df5 100644 --- a/src/lib/eina/eina_value.h +++ b/src/lib/eina/eina_value.h @@ -3478,6 +3478,96 @@ static inline Eina_Bool eina_value_type_pset(const Eina_Value_Type *type, void * */ static inline Eina_Bool eina_value_type_pget(const Eina_Value_Type *type, const void *mem, void *ptr); +/** + * @} + */ + +/** + * @defgroup Eina_Value_Optional_Group Generic Value Optional management + * + * @{ + */ + +/** + * @var EINA_VALUE_TYPE_OPTIONAL + * manages optional type. + * + * @since 1.16 + */ +EAPI extern const Eina_Value_Type *EINA_VALUE_TYPE_OPTIONAL; + +/** + * @typedef Eina_Value_Optional type to be used with Eina_Value_Struct + * + * @since 1.16 + */ +typedef Eina_Value_Union Eina_Value_Optional; + +/** + * @brief Create an empty optional. This is the same as eina_value_new(EINA_VALUE_TYPE_OPTIONAL). + * @return returns an empty optional eina value. + */ +static inline Eina_Value *eina_value_optional_empty_new(); + +/** + * @brief Create an optional eina value with the passed value + * @param subtype Eina_Value_Type of parameter value + * @param value The value to be used to construct optional eina value + * @return #EINA_TRUE on success, #EINA_FALSE otherwise. + * @since 1.16 + */ +EAPI Eina_Value *eina_value_optional_new(const Eina_Value_Type *subtype, + const void* value) EINA_ARG_NONNULL(1, 2); + +/** + * @brief Function to know if an eina optional is empty or not + * @param value Eina Value Optional + * @param is_empty #EINA_TRUE if optional is empty, #EINA_FALSE otherwise. + * @return #EINA_TRUE on success, #EINA_FALSE otherwise. + * @since 1.16 + */ +static inline Eina_Bool eina_value_optional_empty_is(const Eina_Value *value, + Eina_Bool *is_empty) EINA_ARG_NONNULL(1, 2); + +/** + * @brief Set the optional with a value + * @param value Eina Value Optional to be set with subvalue + * @param subtype Type of subvalue + * @param subvalue Value to be set in optional + * @return #EINA_TRUE on success, #EINA_FALSE otherwise. + * @since 1.16 + */ +EAPI Eina_Bool eina_value_optional_pset(Eina_Value *value, + Eina_Value_Type const* subtype, + const void *subvalue) EINA_ARG_NONNULL(1, 2, 3); + +/** + * @brief Get the value from an optional + * @param value Eina Value Optional to get value from + * @param subvalue Pointer to where value is to be copied to. You must use + * the correct type according to eina_value_optional_type_get + * @return #EINA_TRUE on success, #EINA_FALSE otherwise. + * @since 1.16 + */ +EAPI Eina_Bool eina_value_optional_pget(Eina_Value *value, + void *subvalue) EINA_ARG_NONNULL(1, 2); + +/** + * @brief Resets eina optional to empty + * @param value Eina Value Optional + * @return #EINA_TRUE on success, #EINA_FALSE otherwise. + * @since 1.16 + */ +EAPI Eina_Bool eina_value_optional_reset(Eina_Value *value) EINA_ARG_NONNULL(1); + +/** + * @brief Get type from value that is stored on Eina Value Optional + * @param value Eina Value Optional + * @return The optional sub-type. + * @since 1.16 + */ +static inline const Eina_Value_Type *eina_value_optional_type_get(Eina_Value *value) EINA_ARG_NONNULL(1); + /** * @} */ diff --git a/src/tests/eina/eina_test_value.c b/src/tests/eina/eina_test_value.c index 454aa6011d..9e1b8081eb 100644 --- a/src/tests/eina/eina_test_value.c +++ b/src/tests/eina/eina_test_value.c @@ -2731,6 +2731,147 @@ START_TEST(eina_value_test_array_of_struct) } END_TEST +START_TEST(eina_value_test_optional_int) +{ + eina_init(); + + /* Eina_Value *value = eina_value_new(EINA_VALUE_TYPE_OPTIONAL); */ + /* Eina_Bool is_empty; */ + /* ck_assert(eina_value_optional_empty_is(value, &is_empty)); */ + /* ck_assert(is_empty); */ + + /* // sets expectation */ + /* int expected_value = -12345; */ + /* ck_assert(eina_value_optional_pset(value, EINA_VALUE_TYPE_INT, &expected_value)); */ + /* ck_assert(eina_value_optional_empty_is(value, &is_empty)); */ + /* ck_assert(!is_empty); */ + + /* // gets the actual value */ + /* int actual_value; */ + /* ck_assert(eina_value_optional_pget(value, &actual_value)); */ + /* ck_assert_int_eq(expected_value, actual_value); */ + + /* // resets the optional */ + /* ck_assert(eina_value_optional_reset(value)); */ + /* ck_assert(eina_value_optional_empty_is(value, &is_empty)); */ + /* ck_assert(is_empty); */ + + /* // Sets expectation again after reset */ + /* expected_value = -54321; */ + /* ck_assert(eina_value_optional_pset(value, EINA_VALUE_TYPE_INT, &expected_value)); */ + /* ck_assert(eina_value_optional_empty_is(value, &is_empty)); */ + /* ck_assert(!is_empty); */ + + /* // gets the actual value */ + /* ck_assert(eina_value_optional_pget(value, &actual_value)); */ + /* ck_assert_int_eq(expected_value, actual_value); */ + + /* eina_value_free(value); */ + eina_shutdown(); +} +END_TEST + +START_TEST(eina_value_test_optional_string) +{ + eina_init(); + + Eina_Value *value = eina_value_new(EINA_VALUE_TYPE_OPTIONAL); + Eina_Bool is_empty; + ck_assert(eina_value_optional_empty_is(value, &is_empty)); + ck_assert(is_empty); + ck_assert(EINA_VALUE_TYPE_OPTIONAL); + + // sets expectation + const char *expected_value = "Hello world!"; + ck_assert(eina_value_optional_pset(value, EINA_VALUE_TYPE_STRING, &expected_value)); + ck_assert(eina_value_optional_empty_is(value, &is_empty)); + ck_assert(!is_empty); + + // gets the actual value + const char *actual_value; + ck_assert(eina_value_optional_pget(value, &actual_value)); + ck_assert_str_eq(expected_value, actual_value); + + // resets the optional + ck_assert(eina_value_optional_reset(value)); + ck_assert(eina_value_optional_empty_is(value, &is_empty)); + ck_assert(is_empty); + + // Sets expectation again after reset + expected_value = "!dlrow olleH"; + ck_assert(eina_value_optional_pset(value, EINA_VALUE_TYPE_STRING, &expected_value)); + ck_assert(eina_value_optional_empty_is(value, &is_empty)); + ck_assert(!is_empty); + + // gets the actual value + ck_assert(eina_value_optional_pget(value, &actual_value)); + ck_assert_str_eq(expected_value, actual_value); + + eina_value_free(value); + eina_shutdown(); +} +END_TEST + +START_TEST(eina_value_test_optional_struct_members) +{ + eina_init(); + + struct s { + int64_t a; + Eina_Value_Optional text; + int64_t b; + }; + const Eina_Value_Struct_Member members[] = { + EINA_VALUE_STRUCT_MEMBER(EINA_VALUE_TYPE_INT64, struct s, a), + EINA_VALUE_STRUCT_MEMBER(EINA_VALUE_TYPE_OPTIONAL, struct s, text), + EINA_VALUE_STRUCT_MEMBER(EINA_VALUE_TYPE_INT64, struct s, b), + EINA_VALUE_STRUCT_MEMBER_SENTINEL + }; + const Eina_Value_Struct_Desc desc = { + EINA_VALUE_STRUCT_DESC_VERSION, + NULL, members, 3, sizeof(struct s) + }; + + Eina_Value *value = eina_value_struct_new(&desc); + ck_assert_ptr_ne(NULL, value); + + int64_t expected_a = 0x1234567887654321ll; + fail_unless(eina_value_struct_set(value, "a", expected_a)); + int64_t actual_a; + fail_unless(eina_value_struct_get(value, "a", &actual_a)); + ck_assert_int_eq(expected_a, actual_a); + + int64_t expected_b = 0xEEDCBA9889ABCDEFll; + fail_unless(eina_value_struct_set(value, "b", expected_b)); + int64_t actual_b; + fail_unless(eina_value_struct_get(value, "b", &actual_b)); + ck_assert_int_eq(expected_b, actual_b); + + Eina_Value expected_value; + fail_unless(eina_value_setup(&expected_value, EINA_VALUE_TYPE_OPTIONAL)); + const char* str = "Hello world!"; + fail_unless(eina_value_optional_pset(&expected_value, EINA_VALUE_TYPE_STRING, &str)); + fail_unless(eina_value_struct_value_set(value, "text", &expected_value)); + + Eina_Value actual_value; + fail_unless(eina_value_struct_value_get(value, "text", &actual_value)); + fail_unless(eina_value_compare(&expected_value, &actual_value) == 0); + + // tests if the value have been overriden + fail_unless(eina_value_struct_get(value, "a", &actual_a)); + ck_assert_int_eq(expected_a, actual_a); + fail_unless(eina_value_struct_get(value, "b", &actual_b)); + ck_assert_int_eq(expected_b, actual_b); + + eina_value_flush(&actual_value); + eina_value_flush(&expected_value); + + eina_value_free(value); + + eina_shutdown(); +} +END_TEST + #if 0 START_TEST(eina_value_test_model) { @@ -2801,6 +2942,9 @@ eina_test_value(TCase *tc) tcase_add_test(tc, eina_value_test_blob); tcase_add_test(tc, eina_value_test_struct); tcase_add_test(tc, eina_value_test_array_of_struct); + tcase_add_test(tc, eina_value_test_optional_int); + tcase_add_test(tc, eina_value_test_optional_string); + tcase_add_test(tc, eina_value_test_optional_struct_members); #if 0 tcase_add_test(tc, eina_value_test_model); #endif