summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelipe Magno de Almeida <felipe@expertisesolutions.com.br>2015-07-09 00:57:38 -0300
committerFelipe Magno de Almeida <felipe@expertisesolutions.com.br>2015-08-29 16:40:10 -0300
commite0b12153c84446c0588e5f163a3e86f4cb75f620 (patch)
tree56ce6f84af9b838171071b54469d45293b9d9660
parentd71182af7a3c787b30cd6a5747b9d5f055fee003 (diff)
downloadefl-e0b12153c84446c0588e5f163a3e86f4cb75f620.tar.gz
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.
-rw-r--r--src/lib/eina/eina_inline_value.x68
-rw-r--r--src/lib/eina/eina_value.c227
-rw-r--r--src/lib/eina/eina_value.h90
-rw-r--r--src/tests/eina/eina_test_value.c144
4 files changed, 529 insertions, 0 deletions
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
@@ -3482,6 +3482,96 @@ static inline Eina_Bool eina_value_type_pget(const Eina_Value_Type *type, const
* @}
*/
+/**
+ * @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);
+
+/**
+ * @}
+ */
+
#include "eina_inline_value.x"
/**
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