eina: add EINA_VALUE_TYPE_OPTIONAL, 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.

Signed-off-by: Cedric BAIL <cedric@osg.samsung.com>
This commit is contained in:
Felipe Magno de Almeida 2015-07-09 00:57:38 -03:00 committed by Cedric BAIL
parent 0d1179abe3
commit 5d4038d74d
4 changed files with 529 additions and 0 deletions

View File

@ -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)

View File

@ -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;

View File

@ -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);
/**
* @}
*/

View File

@ -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