gstvalue: add hashing

Will be used to implement the Hash trait in gstreamer-rs, see
https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/-/merge_requests/1639

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8518>
This commit is contained in:
Guillaume Desmottes 2025-02-13 16:04:29 +01:00
parent bde69a4fab
commit 4e65d16bfc
5 changed files with 1112 additions and 11 deletions

View File

@ -11,6 +11,6 @@ variables:
CHECKS_TAG: '2025-02-04.0'
ABI_CHECK_TAG: '2025-03-13.0'
ABI_CHECK_TAG: '2025-04-01.0'
WINDOWS_TAG: '2025-02-23.0'

View File

@ -51890,6 +51890,24 @@ or GST_VALUE_UNORDERED</doc>
</parameter>
</parameters>
</callback>
<callback name="ValueHashFunc" c:type="GstValueHashFunc" version="1.28">
<doc xml:space="preserve" filename="../subprojects/gstreamer/gst/gstvalue.h">Used by gst_value_hash() to calculate a hash of @value.</doc>
<source-position filename="../subprojects/gstreamer/gst/gstvalue.h"/>
<return-value transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gstreamer/gst/gstvalue.h">%TRUE, or %FALSE if @value cannot be hashed.</doc>
<type name="gboolean" c:type="gboolean"/>
</return-value>
<parameters>
<parameter name="value" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gstreamer/gst/gstvalue.h">a #GValue</doc>
<type name="GObject.Value" c:type="const GValue*"/>
</parameter>
<parameter name="res" direction="out" caller-allocates="0" transfer-ownership="full">
<doc xml:space="preserve" filename="../subprojects/gstreamer/gst/gstvalue.h">a location to store the hash value</doc>
<type name="guint" c:type="guint*"/>
</parameter>
</parameters>
</callback>
<class name="ValueList" c:symbol-prefix="value_list" glib:type-name="GstValueList" glib:get-type="gst_value_list_get_type" glib:fundamental="1">
<doc xml:space="preserve" filename="../subprojects/gstreamer/gst/gstvalue.h">A fundamental type that describes an unordered list of #GValue</doc>
<function name="append_and_take_value" c:identifier="gst_value_list_append_and_take_value" version="1.2">
@ -52083,8 +52101,12 @@ Free-function: g_free</doc>
<doc xml:space="preserve" filename="../subprojects/gstreamer/gst/gstvalue.h">a #GstValueDeserializeWithPSpecFunc</doc>
<type name="ValueDeserializeWithPSpecFunc" c:type="GstValueDeserializeWithPSpecFunc"/>
</field>
<field name="hash" version="1.28" writable="1">
<doc xml:space="preserve" filename="../subprojects/gstreamer/gst/gstvalue.h">a #GstValueHashFunc</doc>
<type name="ValueHashFunc" c:type="GstValueHashFunc"/>
</field>
<field name="_gst_reserved" readable="0" private="1">
<array zero-terminated="0" fixed-size="3">
<array zero-terminated="0" fixed-size="2">
<type name="gpointer" c:type="gpointer"/>
</array>
</field>
@ -58598,6 +58620,25 @@ a reference to @v.</doc>
</parameter>
</parameters>
</function>
<function name="value_hash" c:identifier="gst_value_hash" version="1.28">
<doc xml:space="preserve" filename="../subprojects/gstreamer/gst/gstvalue.c">Compute a hash value of @value.
#GValue considered as equals by gst_value_compare() will have the same hash value.</doc>
<source-position filename="../subprojects/gstreamer/gst/gstvalue.h"/>
<return-value transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gstreamer/gst/gstvalue.c">%TRUE, or %FALSE if @value cannot be hashed.</doc>
<type name="gboolean" c:type="gboolean"/>
</return-value>
<parameters>
<parameter name="value" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gstreamer/gst/gstvalue.c">a #GValue to hash</doc>
<type name="GObject.Value" c:type="const GValue*"/>
</parameter>
<parameter name="res" direction="out" caller-allocates="0" transfer-ownership="full">
<doc xml:space="preserve" filename="../subprojects/gstreamer/gst/gstvalue.c">a location to store the hash value</doc>
<type name="guint" c:type="guint*"/>
</parameter>
</parameters>
</function>
<function name="value_init_and_copy" c:identifier="gst_value_init_and_copy">
<doc xml:space="preserve" filename="../subprojects/gstreamer/gst/gstvalue.c">Initialises the target value to be of the same type as source and then copies
the contents from source to target.</doc>

View File

@ -3219,6 +3219,7 @@ gst_value_serialize_tag_list (const GValue * value)
static gint
compare_buffer (GstBuffer * buf1, GstBuffer * buf2)
{
/* keep this implementation synced with gst_hash_buffer() */
gsize size1, size2;
GstMapInfo info1, info2;
gint result, mret;
@ -3355,6 +3356,7 @@ wrong_char:
static gint
gst_value_compare_sample (const GValue * value1, const GValue * value2)
{
/* keep this implementation synced with gst_value_hash_sample() */
GstBuffer *buf1 = gst_sample_get_buffer (gst_value_get_sample (value1));
GstBuffer *buf2 = gst_sample_get_buffer (gst_value_get_sample (value2));
@ -6139,7 +6141,8 @@ gst_value_compare (const GValue * value1, const GValue * value2)
value2_is_list = G_VALUE_TYPE (value2) == GST_TYPE_LIST;
/* Special cases: lists and scalar values ("{ 1 }" and "1" are equal),
as well as lists and ranges ("{ 1, 2 }" and "[ 1, 2 ]" are equal) */
as well as lists and ranges ("{ 1, 2 }" and "[ 1, 2 ]" are equal).
Make sure to update gst_value_hash() when adding new special cases. */
if (value1_is_list && !value2_is_list) {
gint i, n, ret;
@ -8059,6 +8062,548 @@ gst_value_compare_flagset (const GValue * value1, const GValue * value2)
return GST_VALUE_UNORDERED;
}
static gboolean
gst_value_hash_int (const GValue * v, guint * res)
{
gint i = g_value_get_int (v);
*res = g_int_hash ((gconstpointer) & i);
return TRUE;
}
static gboolean
gst_value_hash_int64 (const GValue * v, guint * res)
{
gint64 i = g_value_get_int64 (v);
*res = g_int64_hash ((gconstpointer) & i);
return TRUE;
}
static gboolean
gst_value_hash_long (const GValue * v, guint * res)
{
glong l = g_value_get_long (v);
#if GLIB_SIZEOF_LONG == 4
*res = g_int_hash ((gconstpointer) & l);
#else
*res = g_int64_hash ((gconstpointer) & l);
#endif
return TRUE;
}
static gboolean
gst_value_hash_uint (const GValue * v, guint * res)
{
guint u = g_value_get_uint (v);
*res = g_int_hash ((gconstpointer) & u);
return TRUE;
}
static gboolean
gst_value_hash_uint64 (const GValue * v, guint * res)
{
guint64 u = g_value_get_uint64 (v);
*res = g_int64_hash ((gconstpointer) & u);
return TRUE;
}
static gboolean
gst_value_hash_ulong (const GValue * v, guint * res)
{
gulong u = g_value_get_ulong (v);
*res = g_double_hash ((gconstpointer) & u);
return TRUE;
}
static gboolean
gst_value_hash_uchar (const GValue * v, guint * res)
{
guchar u = g_value_get_uchar (v);
gint i = u;
*res = g_int_hash ((gconstpointer) & i);
return TRUE;
}
static gboolean
gst_value_hash_gtype (const GValue * v, guint * res)
{
GType t = g_value_get_gtype (v);
#if GLIB_SIZEOF_SIZE_T == 4
*res = g_int_hash ((gconstpointer) & t);
#else
*res = g_int64_hash ((gconstpointer) & t);
#endif
return TRUE;
}
static gboolean
gst_value_hash_double (const GValue * v, guint * res)
{
gdouble d = g_value_get_double (v);
*res = g_double_hash ((gconstpointer) & d);
return TRUE;
}
static gboolean
gst_value_hash_float (const GValue * v, guint * res)
{
gfloat f = g_value_get_float (v);
*res = g_int_hash ((gconstpointer) & f);
return TRUE;
}
static gboolean
gst_value_hash_string (const GValue * v, guint * res)
{
const gchar *s = g_value_get_string (v);
*res = g_str_hash (s);
return TRUE;
}
static gboolean
gst_value_hash_boolean (const GValue * v, guint * res)
{
gboolean b = g_value_get_boolean (v);
*res = b ? 1 : 0;
return TRUE;
}
static gboolean
gst_value_hash_enum (const GValue * v, guint * res)
{
gint e = g_value_get_enum (v);
*res = g_int_hash ((gconstpointer) & e);
return TRUE;
}
static gboolean
gst_value_hash_gflags (const GValue * v, guint * res)
{
guint f = g_value_get_flags (v);
*res = g_int_hash ((gconstpointer) & f);
return TRUE;
}
/* helper to combine hash when hashing ordered container */
static void
combine_hash_ordered (guint * hash, guint value_hash)
{
/* perform a rotating shift to account for the position in the container */
*hash = ((*hash << 5) | (*hash >> 27)) ^ value_hash;
}
static gboolean
gst_value_hash_int_range (const GValue * v, guint * res)
{
gint min = gst_value_get_int_range_min (v);
gint max = gst_value_get_int_range_max (v);
gint step = gst_value_get_int_range_step (v);
gint i;
*res = 0;
/* range and list are considered as equal if they contain the same elements,
so they should have the same hash. Lists are not ordered so do not account
for element position when hashing. */
for (i = min; i <= max; i += step) {
*res ^= g_int_hash ((gconstpointer) & i);
}
return TRUE;
}
static gboolean
gst_value_hash_int64_range (const GValue * v, guint * res)
{
gint64 min = gst_value_get_int64_range_min (v);
gint64 max = gst_value_get_int64_range_max (v);
gint64 step = gst_value_get_int64_range_step (v);
gint64 i;
*res = 0;
for (i = min; i <= max; i += step) {
*res ^= g_int64_hash ((gconstpointer) & i);
}
return TRUE;
}
static gboolean
gst_value_hash_double_range (const GValue * v, guint * res)
{
gdouble min = gst_value_get_double_range_min (v);
gdouble max = gst_value_get_double_range_max (v);
*res = g_double_hash ((gconstpointer) & min) ^ g_double_hash ((gconstpointer)
& max);
return TRUE;
}
static gboolean
gst_value_hash_g_value_array (const GValue * v, guint * res)
{
guint i = 0;
GValueArray *array = g_value_get_boxed (v);
*res = 0;
for (i = 0; i < array->n_values; i++) {
guint value_hash;
if (!gst_value_hash (&array->values[i], &value_hash))
return FALSE;
combine_hash_ordered (res, value_hash);
}
return TRUE;
}
static gboolean
gst_hash_buffer (GstBuffer * buf, guint * res)
{
GstBufferMapInfo map;
guint i;
*res = 0;
if (!gst_buffer_map (buf, &map, GST_MAP_READ)) {
return FALSE;
}
for (i = 0; i < map.size; i++) {
combine_hash_ordered (res, map.data[i]);
}
gst_buffer_unmap (buf, &map);
return TRUE;
}
static gboolean
gst_value_hash_buffer (const GValue * v, guint * res)
{
GstBuffer *buf = gst_value_get_buffer (v);
/* compare_buffer() only compares the buffer content */
return gst_hash_buffer (buf, res);
}
static gboolean
gst_value_hash_sample (const GValue * v, guint * res)
{
GstSample *sample = gst_value_get_sample (v);
GstBuffer *buf = gst_sample_get_buffer (sample);
/* gst_value_compare_sample() only compares buffers */
return gst_hash_buffer (buf, res);
}
static gboolean
gst_value_hash_fraction (const GValue * v, guint * res)
{
gint num = gst_value_get_fraction_numerator (v);
gint den = gst_value_get_fraction_denominator (v);
gfloat div = ((gfloat) num) / ((gfloat) den);
*res = g_int_hash ((gconstpointer) & div);
return TRUE;
}
static gboolean
gst_value_hash_fraction_range (const GValue * v, guint * res)
{
const GValue *min = gst_value_get_fraction_range_min (v);
const GValue *max = gst_value_get_fraction_range_max (v);
gint num, den;
gfloat div;
num = gst_value_get_fraction_numerator (min);
den = gst_value_get_fraction_denominator (min);
div = ((gfloat) num) / ((gfloat) den);
*res = g_int_hash ((gconstpointer) & div);
num = gst_value_get_fraction_numerator (max);
den = gst_value_get_fraction_denominator (max);
div = ((gfloat) num) / ((gfloat) den);
*res ^= g_int_hash ((gconstpointer) & div);
return TRUE;
}
static gboolean
hash_structure_foreach (const GstIdStr * fieldname,
const GValue * value, gpointer user_data)
{
gint *hash = user_data;
guint value_hash;
*hash ^= g_str_hash (gst_id_str_as_str (fieldname));
if (!gst_value_hash (value, &value_hash))
return FALSE;
*hash ^= value_hash;
return TRUE;
}
static gboolean
gst_hash_structure (const GstStructure * s, guint * res)
{
*res = g_str_hash (gst_structure_get_name (s));
return gst_structure_foreach_id_str (s, hash_structure_foreach, res);
}
static gboolean
gst_value_hash_structure (const GValue * v, guint * res)
{
const GstStructure *s = gst_value_get_structure (v);
return gst_hash_structure (s, res);
}
static gboolean
gst_hash_caps_features (const GstCapsFeatures * features, guint * res)
{
guint i;
*res = 0;
for (i = 0; i < gst_caps_features_get_size (features); i++) {
const gchar *feature = gst_caps_features_get_nth (features, i);
*res ^= g_str_hash (feature);
}
return TRUE;
}
static gboolean
gst_value_hash_caps_features (const GValue * v, guint * res)
{
const GstCapsFeatures *features = gst_value_get_caps_features (v);
return gst_hash_caps_features (features, res);
}
static gboolean
gst_value_hash_caps (const GValue * v, guint * res)
{
const GstCaps *caps = gst_value_get_caps (v);
guint i;
guint n = gst_caps_get_size (caps);
*res = 0;
for (i = 0; i < n; i++) {
GstCapsFeatures *features = gst_caps_get_features (caps, i);
guint hash;
if (!gst_hash_structure (gst_caps_get_structure (caps, i), &hash))
return FALSE;
*res ^= hash;
if (!gst_hash_caps_features (features, &hash))
return FALSE;
*res ^= hash;
}
return TRUE;
}
static gboolean
gst_value_hash_tag_list (const GValue * v, guint * res)
{
const GstTagList *tags = g_value_get_boxed (v);
const GstStructure *s;
s = _gst_tag_list_structure (tags);
return gst_hash_structure (s, res);
}
static gboolean
gst_value_hash_date (const GValue * v, guint * res)
{
const GDate *date = g_value_get_boxed (v);
guint32 julian, year, month, day;
julian = g_date_get_julian (date);
if (julian) {
*res = g_int_hash ((gconstpointer) & julian);
return TRUE;
}
year = g_date_get_year (date);
month = g_date_get_month (date);
day = g_date_get_day (date);
*res = g_int_hash ((gconstpointer) & year);
combine_hash_ordered (res, g_int_hash ((gconstpointer) & month));
combine_hash_ordered (res, g_int_hash ((gconstpointer) & day));
return TRUE;
}
static gboolean
gst_hash_g_date_time (const GDateTime * dt, guint * res)
{
gint64 unix_time;
unix_time = g_date_time_to_unix ((GDateTime *) dt);
*res = g_int64_hash ((gconstpointer) & unix_time);
return TRUE;
}
static gboolean
gst_value_hash_g_date_time (const GValue * v, guint * res)
{
GDateTime *dt = g_value_get_boxed (v);
return gst_hash_g_date_time (dt, res);
}
static gboolean
gst_value_hash_date_time (const GValue * v, guint * res)
{
GstDateTime *dt = g_value_get_boxed (v);
GDateTime *gdt;
gboolean ret_val;
gdt = gst_date_time_to_g_date_time (dt);
ret_val = gst_hash_g_date_time (gdt, res);
g_date_time_unref (gdt);
return ret_val;
}
static gboolean
gst_value_hash_bytes (const GValue * v, guint * res)
{
GBytes *bytes = g_value_get_boxed (v);
const guint *data;
gsize size, i;
*res = 0;
data = g_bytes_get_data (bytes, &size);
for (i = 0; i < size; i++) {
combine_hash_ordered (res, data[i]);
}
return TRUE;
}
static gboolean
gst_value_hash_bitmask (const GValue * v, guint * res)
{
guint64 mask = gst_value_get_bitmask (v);
*res = g_int64_hash ((gconstpointer) & mask);
return TRUE;
}
static gboolean
gst_value_hash_flagset (const GValue * v, guint * res)
{
guint flags = gst_value_get_flagset_flags (v);
guint mask = gst_value_get_flagset_mask (v);
guint value = flags & mask;
*res = g_int_hash ((gconstpointer) & value);
return TRUE;
}
static gboolean
gst_value_hash_segment (const GValue * v, guint * res)
{
GstSegment *segment = g_value_get_boxed (v);
*res = g_int_hash ((gconstpointer) & segment->flags);
combine_hash_ordered (res, g_double_hash ((gconstpointer) & segment->rate));
combine_hash_ordered (res,
g_double_hash ((gconstpointer) & segment->applied_rate));
combine_hash_ordered (res, g_int_hash ((gconstpointer) & segment->format));
combine_hash_ordered (res, g_int64_hash ((gconstpointer) & segment->base));
combine_hash_ordered (res, g_int64_hash ((gconstpointer) & segment->offset));
combine_hash_ordered (res, g_int64_hash ((gconstpointer) & segment->start));
combine_hash_ordered (res, g_int64_hash ((gconstpointer) & segment->stop));
combine_hash_ordered (res, g_int64_hash ((gconstpointer) & segment->time));
combine_hash_ordered (res,
g_int64_hash ((gconstpointer) & segment->position));
combine_hash_ordered (res,
g_int64_hash ((gconstpointer) & segment->duration));
return TRUE;
}
static gboolean
gst_value_hash_allocation_params (const GValue * v, guint * res)
{
GstAllocationParams *params = g_value_get_boxed (v);
*res = g_int_hash ((gconstpointer) & params->flags);
combine_hash_ordered (res, g_int64_hash ((gconstpointer) & params->align));
combine_hash_ordered (res, g_int64_hash ((gconstpointer) & params->prefix));
combine_hash_ordered (res, g_int64_hash ((gconstpointer) & params->padding));
return TRUE;
}
static gboolean
gst_value_hash_object (const GValue * v, guint * res)
{
GObject *obj = g_value_get_object (v);
*res = g_direct_hash (obj);
return TRUE;
}
static gboolean
gst_value_hash_value_list (const GValue * v, guint * res)
{
guint i, n;
*res = 0;
/* value list not ordered */
n = gst_value_list_get_size (v);
for (i = 0; i < n; i++) {
guint value_hash;
if (!gst_value_hash (gst_value_list_get_value (v, i), &value_hash))
return FALSE;
*res ^= value_hash;
}
return TRUE;
}
static gboolean
gst_value_hash_value_array (const GValue * v, guint * res)
{
guint i, n;
*res = 0;
/* value array is ordered */
n = gst_value_array_get_size (v);
for (i = 0; i < n; i++) {
guint value_hash;
if (!gst_value_hash (gst_value_array_get_value (v, i), &value_hash))
return FALSE;
combine_hash_ordered (res, value_hash);
}
return TRUE;
}
static gboolean
gst_value_hash_strv (const GValue * v, guint * res)
{
GStrv strv = g_value_get_boxed (v);
guint i, n;
*res = 0;
/* GStrv is ordered */
n = g_strv_length (strv);
for (i = 0; i < n; i++) {
combine_hash_ordered (res, g_str_hash (strv[i]));
}
return TRUE;
}
/***********************
* GstAllocationParams *
***********************/
@ -8411,14 +8956,15 @@ gst_g_thread_get_type (void)
return G_TYPE_THREAD;
}
#define SERIAL_VTABLE(t,c,s,d) { t, c, s, d, NULL }
#define SERIAL_VTABLE_PSPEC(t,c,s,d) { t, c, s, NULL, d }
#define SERIAL_VTABLE(t,c,s,d,h) { t, c, s, d, NULL, h }
#define SERIAL_VTABLE_PSPEC(t,c,s,d,h) { t, c, s, NULL, d, h }
#define REGISTER_SERIALIZATION_CONST(_gtype, _type) \
G_STMT_START { \
static const GstValueTable gst_value = \
SERIAL_VTABLE (_gtype, gst_value_compare_ ## _type, \
gst_value_serialize_ ## _type, gst_value_deserialize_ ## _type); \
gst_value_serialize_ ## _type, gst_value_deserialize_ ## _type, \
gst_value_hash_ ## _type); \
gst_value_register (&gst_value); \
} G_STMT_END
@ -8426,7 +8972,8 @@ G_STMT_START { \
G_STMT_START { \
static GstValueTable gst_value = \
SERIAL_VTABLE (0, gst_value_compare_ ## _type, \
gst_value_serialize_ ## _type, gst_value_deserialize_ ## _type); \
gst_value_serialize_ ## _type, gst_value_deserialize_ ## _type, \
gst_value_hash_ ## _type); \
gst_value.type = _gtype; \
gst_value_register (&gst_value); \
} G_STMT_END
@ -8435,7 +8982,8 @@ G_STMT_START { \
G_STMT_START { \
static GstValueTable gst_value = \
SERIAL_VTABLE_PSPEC (0, gst_value_compare_ ## _type, \
gst_value_serialize_ ## _type, gst_value_deserialize_ ## _type); \
gst_value_serialize_ ## _type, gst_value_deserialize_ ## _type, \
gst_value_hash_ ## _type); \
gst_value.type = _gtype; \
gst_value_register (&gst_value); \
} G_STMT_END
@ -8444,7 +8992,8 @@ G_STMT_START { \
G_STMT_START { \
static GstValueTable gst_value = \
SERIAL_VTABLE (0, NULL, \
gst_value_serialize_ ## _type, gst_value_deserialize_ ## _type); \
gst_value_serialize_ ## _type, gst_value_deserialize_ ## _type, \
gst_value_hash_ ## _type); \
gst_value.type = _gtype; \
gst_value_register (&gst_value); \
} G_STMT_END
@ -8453,7 +9002,7 @@ G_STMT_START { \
G_STMT_START { \
static GstValueTable gst_value = \
SERIAL_VTABLE (0, gst_value_compare_ ## _type, \
NULL, NULL); \
NULL, NULL, gst_value_hash_ ## _type); \
gst_value.type = _gtype; \
gst_value_register (&gst_value); \
} G_STMT_END
@ -8703,3 +9252,58 @@ gst_flagset_register (GType flags_type)
return t;
}
/**
* gst_value_hash:
* @value: a #GValue to hash
* @res: (out): a location to store the hash value
*
* Compute a hash value of @value.
* #GValue considered as equals by gst_value_compare() will have the same hash value.
*
* Returns: %TRUE, or %FALSE if @value cannot be hashed.
*
* Since: 1.28
*/
gboolean
gst_value_hash (const GValue * value, guint * res)
{
GType type;
GstValueTable *table;
g_return_val_if_fail (res, FALSE);
*res = 0;
type = G_VALUE_TYPE (value);
table = gst_value_hash_lookup_type (type);
if (G_LIKELY (table && table->hash)) {
if (type == GST_TYPE_LIST && gst_value_list_get_size (value) == 1) {
/* { 1 } and 1 are considered as equals so hash the single element contained in the list */
const GValue *v = gst_value_list_get_value (value, 0);
return gst_value_hash (v, res);
} else if (type == GST_TYPE_INT_RANGE || type == GST_TYPE_INT64_RANGE) {
/* hash the list type as list and ranges of the same values are considered as equals */
type = GST_TYPE_LIST;
}
guint hash;
*res = g_int_hash ((gconstpointer) & type);
if (!table->hash (value, &hash))
return FALSE;
*res ^= hash;
return TRUE;
} else {
/* custom enums and flags are not registered through gst_value_register() and so don't have a table entry */
if G_VALUE_HOLDS_ENUM
(value) {
return gst_value_hash_enum (value, res);
} else if G_VALUE_HOLDS_FLAGS
(value) {
return gst_value_hash_gflags (value, res);
}
}
return FALSE;
}

View File

@ -487,6 +487,19 @@ typedef gboolean (* GstValueDeserializeWithPSpecFunc) (GValue *dest,
const gchar *s,
GParamSpec *pspec);
/**
* GstValueHashFunc:
* @value: a #GValue
* @res: (out): a location to store the hash value
*
* Used by gst_value_hash() to calculate a hash of @value.
*
* Returns: %TRUE, or %FALSE if @value cannot be hashed.
*
* Since: 1.28
*/
typedef gboolean (* GstValueHashFunc) (const GValue *value,
guint *res);
typedef struct _GstValueTable GstValueTable;
/**
@ -514,8 +527,17 @@ struct _GstValueTable {
*/
GstValueDeserializeWithPSpecFunc deserialize_with_pspec;
/**
* GstValueTable.hash:
*
* a #GstValueHashFunc
*
* Since: 1.28
*/
GstValueHashFunc hash;
/*< private >*/
gpointer _gst_reserved [GST_PADDING - 1];
gpointer _gst_reserved [GST_PADDING - 2];
};
GST_API
@ -569,6 +591,10 @@ gboolean gst_value_deserialize_with_pspec (GValue *dest,
const gchar *src,
GParamSpec *pspec);
GST_API
gboolean gst_value_hash (const GValue * value,
guint * res);
/* list */
GST_API

View File

@ -4063,6 +4063,435 @@ GST_START_TEST (test_serialize_deserialize_strv)
GST_END_TEST;
/* take ownership of @a and @b */
static void
test_hash_value_diff (GValue * a, GValue * b, gboolean compare)
{
guint hash_a, hash_b;
if (compare) {
fail_unless (gst_value_compare (a, a) == GST_VALUE_EQUAL,
"[%s] value should be equal: \"%s\" \"%s\"",
G_VALUE_TYPE_NAME (a), gst_value_serialize (a),
gst_value_serialize (a));
fail_unless (gst_value_compare (b, b) == GST_VALUE_EQUAL,
"[%s] value should be equal: \"%s\" \"%s\"", G_VALUE_TYPE_NAME (a),
gst_value_serialize (b), gst_value_serialize (b));
fail_unless (gst_value_compare (a, b) != GST_VALUE_EQUAL,
"[%s] value should be different: \"%s\" \"%s\"", G_VALUE_TYPE_NAME (a),
gst_value_serialize (a), gst_value_serialize (b));
}
fail_unless (gst_value_hash (a, &hash_a),
"[%s] failed to hash: \"%s\"",
G_VALUE_TYPE_NAME (a), gst_value_serialize (a));
fail_unless (gst_value_hash (b, &hash_b),
"[%s] failed to hash: \"%s\"",
G_VALUE_TYPE_NAME (b), gst_value_serialize (b));
fail_unless (hash_a != hash_b,
"[%s] hash should be different: \"%s\" \"%s\"",
G_VALUE_TYPE_NAME (a), gst_value_serialize (a), gst_value_serialize (b));
g_value_unset (a);
g_value_unset (b);
}
/* take ownership of @a and @b */
static void
test_hash_value_equal (GValue * a, GValue * b, gboolean compare)
{
guint hash_a, hash_b;
if (compare) {
fail_unless (gst_value_compare (a, a) == GST_VALUE_EQUAL,
"[%s] value should be equal: \"%s\" \"%s\"",
G_VALUE_TYPE_NAME (a), gst_value_serialize (a),
gst_value_serialize (a));
fail_unless (gst_value_compare (b, b) == GST_VALUE_EQUAL,
"[%s] value should be equal: \"%s\" \"%s\"", G_VALUE_TYPE_NAME (a),
gst_value_serialize (b), gst_value_serialize (b));
fail_unless (gst_value_compare (a, b) == GST_VALUE_EQUAL,
"[%s] value should be equal: \"%s\" \"%s\"", G_VALUE_TYPE_NAME (a),
gst_value_serialize (a), gst_value_serialize (b));
}
fail_unless (gst_value_hash (a, &hash_a),
"[%s] failed to hash: \"%s\"",
G_VALUE_TYPE_NAME (a), gst_value_serialize (a));
fail_unless (gst_value_hash (b, &hash_b),
"[%s] failed to hash: \"%s\"",
G_VALUE_TYPE_NAME (b), gst_value_serialize (b));
fail_unless (hash_a == hash_b,
"[%s] hash should be equal: \"%s\" \"%s\"",
G_VALUE_TYPE_NAME (a), gst_value_serialize (a), gst_value_serialize (b));
g_value_unset (a);
g_value_unset (b);
}
GST_START_TEST (test_hash)
{
guint i;
/* tests where we can rely on gst_value_deserialize() to set GValue */
struct HashTest
{
GType gtype;
const gchar *a;
const gchar *b;
gboolean compare;
};
/* check that a and b are different */
struct HashTest tests_diff[] = {
{G_TYPE_INT, "1", "2", TRUE},
{G_TYPE_INT64, "1", "2", TRUE},
{G_TYPE_LONG, "1", "2", TRUE},
{G_TYPE_UINT, "1", "2", TRUE},
{G_TYPE_UINT64, "1", "2", TRUE},
{G_TYPE_ULONG, "1", "2", TRUE},
{G_TYPE_UCHAR, "1", "2", TRUE},
{G_TYPE_GTYPE, "gint", "gint64", TRUE},
{G_TYPE_DOUBLE, "1.0", "2.0", TRUE},
{G_TYPE_FLOAT, "1.0", "2.0", TRUE},
{G_TYPE_STRING, "a", "b", TRUE},
{G_TYPE_BOOLEAN, "TRUE", "FALSE", TRUE},
{GST_TYPE_BUFFER, "ab", "abcd", TRUE},
/* only difference is the buffer content */
{GST_TYPE_SAMPLE,
"6275660a64617400:Y2FwcywgSW50PShpbnQpMjAsIFN0cmluZz0oc3RyaW5nKSJhXCBzdHJpbmciAA__:c2VnbWVudCwgZmxhZ3M9KEdzdFNlZ21lbnRGbGFncylHU1RfU0VHTUVOVF9GTEFHX1JFU0VULCByYXRlPShkb3VibGUpMS4yLCBhcHBsaWVkLXJhdGU9KGRvdWJsZSkxLCBmb3JtYXQ9KEdzdEZvcm1hdClkZWZhdWx0LCBiYXNlPShndWludDY0KTAsIG9mZnNldD0oZ3VpbnQ2NCkwLCBzdGFydD0oZ3VpbnQ2NCkyMCwgc3RvcD0oZ3VpbnQ2NCkzMCwgdGltZT0oZ3VpbnQ2NCkyMCwgcG9zaXRpb249KGd1aW50NjQpMjAsIGR1cmF0aW9uPShndWludDY0KTE4NDQ2NzQ0MDczNzA5NTUxNjE1OwA_:c3RydWN0dXJlLCBGbG9hdD0oZmxvYXQpLTIuNTsA",
"4255460a44415400:Y2FwcywgSW50PShpbnQpMjAsIFN0cmluZz0oc3RyaW5nKSJhXCBzdHJpbmciAA__:c2VnbWVudCwgZmxhZ3M9KEdzdFNlZ21lbnRGbGFncylHU1RfU0VHTUVOVF9GTEFHX1JFU0VULCByYXRlPShkb3VibGUpMS4yLCBhcHBsaWVkLXJhdGU9KGRvdWJsZSkxLCBmb3JtYXQ9KEdzdEZvcm1hdClkZWZhdWx0LCBiYXNlPShndWludDY0KTAsIG9mZnNldD0oZ3VpbnQ2NCkwLCBzdGFydD0oZ3VpbnQ2NCkyMCwgc3RvcD0oZ3VpbnQ2NCkzMCwgdGltZT0oZ3VpbnQ2NCkyMCwgcG9zaXRpb249KGd1aW50NjQpMjAsIGR1cmF0aW9uPShndWludDY0KTE4NDQ2NzQ0MDczNzA5NTUxNjE1OwA_:c3RydWN0dXJlLCBGbG9hdD0oZmxvYXQpLTIuNTsA",
TRUE},
{GST_TYPE_FRACTION, "1/3", "3/1", TRUE},
{GST_TYPE_STRUCTURE, "video/x-raw", "audio/x-raw", TRUE},
{GST_TYPE_STRUCTURE, "video/x-raw,width=1920", "video/x-raw,width=800",
TRUE},
{GST_TYPE_CAPS, "video/x-raw", "audio/x-raw", TRUE},
{GST_TYPE_CAPS, "video/x-raw,width=1920", "video/x-raw,width=800", TRUE},
{GST_TYPE_CAPS, "video/x-raw", "video/x-raw(memory:DMABuf)", TRUE},
{GST_TYPE_TAG_LIST, "taglist, title=(string)Title, artist=(string)Artist",
"taglist, title=(string)Title2, artist=(string)Artist", TRUE},
{G_TYPE_DATE, "1984-11-30", "1985-11-30", TRUE},
{G_TYPE_DATE_TIME, "2011-06-23T07:40:10Z", "2011-06-23T07:40:11Z", TRUE},
{GST_TYPE_DATE_TIME, "2011-06-23T07:40:10Z", "2011-06-23T07:40:11Z",
TRUE},
{G_TYPE_BYTES, "YQ==", "Yg==", TRUE},
{GST_TYPE_BITMASK, "0xaa", "0xbb", TRUE},
{GST_TYPE_SEEK_FLAGS, "+flush", "+flush+trickmode/trickmode-no-audio",
TRUE},
{GST_TYPE_LIST, "{1, 2}", "{1, 3}", TRUE},
{GST_TYPE_CAPS_FEATURES, "memory:DMABuf", "memory:GLMemory", FALSE},
};
/* check that a and b are equal */
struct HashTest tests_equal[] = {
/* structures fields are not ordered */
{GST_TYPE_STRUCTURE, "video/x-raw,width=1920,height=1080",
"video/x-raw,height=1080,width=1920", TRUE},
/* fixed and unfixed caps are not ordered */
{GST_TYPE_CAPS, "video/x-raw; audio/x-raw", "audio/x-raw; video/x-raw",
TRUE},
{GST_TYPE_CAPS,
"video/x-raw,format=NV12,width=[1, 100]; video/x-raw,format=GREY8,width=[1, 100]",
"video/x-raw,format=GREY8,width=[1, 100]; video/x-raw,format=NV12,width=[1, 100]",
TRUE},
{GST_TYPE_LIST, "{1, 2}", "{2, 1}", TRUE},
{GST_TYPE_LIST, "{1, 2}", "{2, 1}", TRUE},
/* caps features are not ordered */
{GST_TYPE_CAPS_FEATURES, "memory:DMABuf, memory:GLMemory",
"memory:GLMemory, memory:DMABuf", FALSE},
/* same actual fraction have the same hash */
{GST_TYPE_FRACTION, "1/2", "2/4", TRUE},
};
for (i = 0; i < G_N_ELEMENTS (tests_diff); i++) {
GValue a = G_VALUE_INIT;
GValue b = G_VALUE_INIT;
g_value_init (&a, tests_diff[i].gtype);
fail_unless (gst_value_deserialize (&a, tests_diff[i].a),
"[%s] could not deserialize %s", g_type_name (tests_diff[i].gtype),
tests_diff[i].a);
g_value_init (&b, tests_diff[i].gtype);
fail_unless (gst_value_deserialize (&b, tests_diff[i].b),
"[%s] could not deserialize %s", g_type_name (tests_diff[i].gtype),
tests_diff[i].b);
test_hash_value_diff (&a, &b, tests_diff[i].compare);
}
for (i = 0; i < G_N_ELEMENTS (tests_equal); i++) {
GValue a = G_VALUE_INIT;
GValue b = G_VALUE_INIT;
g_value_init (&a, tests_equal[i].gtype);
fail_unless (gst_value_deserialize (&a, tests_equal[i].a),
"[%s] could not deserialize %s", g_type_name (tests_equal[i].gtype),
tests_equal[i].a);
g_value_init (&b, tests_equal[i].gtype);
fail_unless (gst_value_deserialize (&b, tests_equal[i].b),
"[%s] could not deserialize %s", g_type_name (tests_equal[i].gtype),
tests_equal[i].b);
test_hash_value_equal (&a, &b, tests_equal[i].compare);
}
/* tests for types not implementing deserialize */
{
GValue a = G_VALUE_INIT;
GValue b = G_VALUE_INIT;
GValue v = G_VALUE_INIT;
GValueArray *array;
/* int range */
g_value_init (&a, GST_TYPE_INT_RANGE);
g_value_init (&b, GST_TYPE_INT_RANGE);
gst_value_set_int_range (&a, 0, 2);
gst_value_set_int_range (&b, 0, 3);
test_hash_value_diff (&a, &b, TRUE);
/* int range with step */
g_value_init (&a, GST_TYPE_INT_RANGE);
g_value_init (&b, GST_TYPE_INT_RANGE);
gst_value_set_int_range_step (&a, 0, 12, 2);
gst_value_set_int_range_step (&b, 0, 12, 4);
test_hash_value_diff (&a, &b, TRUE);
/* int64 range */
g_value_init (&a, GST_TYPE_INT64_RANGE);
g_value_init (&b, GST_TYPE_INT64_RANGE);
gst_value_set_int64_range (&a, 0, 2);
gst_value_set_int64_range (&b, 0, 3);
test_hash_value_diff (&a, &b, TRUE);
/* int64 range with step */
g_value_init (&a, GST_TYPE_INT64_RANGE);
g_value_init (&b, GST_TYPE_INT64_RANGE);
gst_value_set_int64_range_step (&a, 0, 12, 2);
gst_value_set_int64_range_step (&b, 0, 12, 4);
test_hash_value_diff (&a, &b, TRUE);
/* double range */
g_value_init (&a, GST_TYPE_DOUBLE_RANGE);
g_value_init (&b, GST_TYPE_DOUBLE_RANGE);
gst_value_set_double_range (&a, 0, 12);
gst_value_set_double_range (&b, 0, 13);
test_hash_value_diff (&a, &b, TRUE);
/* enum */
static const GEnumValue test_enum_vals[] = {
{0, "Badger", "badger"},
{1, "Mushroom", "mushroom"},
{0, NULL, NULL},
};
GType test_enum_type =
g_enum_register_static ("GstTestEnum", test_enum_vals);
g_value_init (&a, test_enum_type);
g_value_set_enum (&a, 0);
g_value_init (&b, test_enum_type);
g_value_set_enum (&b, 1);
test_hash_value_diff (&a, &b, TRUE);
/* flags */
static const GFlagsValue test_flags_vals[] = {
{0, "Badger", "badger"},
{1, "Mushroom", "mushroom"},
{0, NULL, NULL},
};
GType test_flags_type =
g_flags_register_static ("GstTestFlags", test_flags_vals);
g_value_init (&a, test_flags_type);
g_value_set_flags (&a, 0);
g_value_init (&b, test_flags_type);
g_value_set_flags (&b, 1);
test_hash_value_diff (&a, &b, TRUE);
/* value array */
setup_test_value_array (&a);
setup_test_value_array (&b);
GValueArray *arr = g_value_get_boxed (&b);
g_value_init (&v, G_TYPE_INT);
g_value_set_int (&v, 4);
g_value_array_append (arr, &v);
test_hash_value_diff (&a, &b, TRUE);
g_value_unset (&v);
g_value_init (&v, G_TYPE_INT);
g_value_init (&a, G_TYPE_VALUE_ARRAY);
array = g_value_array_new (2);
g_value_set_int (&v, 1);
g_value_array_append (array, &v);
g_value_set_int (&v, 2);
g_value_array_append (array, &v);
g_value_take_boxed (&a, array);
array = g_value_array_new (2);
g_value_init (&b, G_TYPE_VALUE_ARRAY);
g_value_set_int (&v, 2);
g_value_array_append (array, &v);
g_value_set_int (&v, 1);
g_value_array_append (array, &v);
g_value_take_boxed (&b, array);
test_hash_value_diff (&a, &b, TRUE);
g_value_unset (&v);
/* fraction range */
g_value_init (&a, GST_TYPE_FRACTION_RANGE);
g_value_init (&b, GST_TYPE_FRACTION_RANGE);
gst_value_set_fraction_range_full (&a, 1, 5, 4, 5);
gst_value_set_fraction_range_full (&a, 2, 5, 4, 5);
test_hash_value_diff (&a, &b, TRUE);
/* objet */
GstBufferPool *obj_a = gst_buffer_pool_new ();
GstBufferPool *obj_b = gst_buffer_pool_new ();
g_value_init (&a, G_TYPE_OBJECT);
g_value_take_object (&a, obj_a);
g_value_init (&b, G_TYPE_OBJECT);
g_value_take_object (&b, obj_b);
test_hash_value_diff (&a, &b, TRUE);
/* value array */
g_value_init (&a, GST_TYPE_ARRAY);
g_value_init (&v, G_TYPE_INT);
g_value_set_int (&v, 1);
gst_value_array_append_value (&a, &v);
g_value_set_int (&v, 2);
gst_value_array_append_value (&a, &v);
g_value_init (&b, GST_TYPE_ARRAY);
g_value_set_int (&v, 2);
gst_value_array_append_value (&b, &v);
g_value_set_int (&v, 2);
gst_value_array_append_value (&b, &v);
test_hash_value_diff (&a, &b, TRUE);
g_value_unset (&v);
/* AllocationParams */
GstAllocationParams params_a = { 0 }, params_b;
params_a.flags = 0;
params_a.align = 1;
params_a.prefix = 2;
params_a.padding = 3;
params_b = params_a;
params_b.padding = 4;
g_value_init (&a, GST_TYPE_ALLOCATION_PARAMS);
g_value_set_boxed (&a, &params_a);
g_value_init (&b, GST_TYPE_ALLOCATION_PARAMS);
g_value_set_boxed (&b, &params_b);
test_hash_value_diff (&a, &b, TRUE);
/* float NaN */
g_value_init (&a, G_TYPE_FLOAT);
g_value_set_float (&a, NAN);
g_value_init (&b, G_TYPE_FLOAT);
g_value_set_float (&b, 0);
test_hash_value_diff (&a, &b, FALSE);
}
/* types with specific compare function */
{
GValue a = G_VALUE_INIT;
GValue b = G_VALUE_INIT;
GstSegment seg_a, seg_b;
gst_segment_init (&seg_a, GST_FORMAT_DEFAULT);
fail_unless (gst_segment_do_seek (&seg_a, 1.2, GST_FORMAT_DEFAULT,
GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, 20, GST_SEEK_TYPE_SET, 30,
NULL));
seg_b = seg_a;
seg_b.rate = 1.0;
fail_unless (!gst_segment_is_equal (&seg_a, &seg_b));
g_value_init (&a, GST_TYPE_SEGMENT);
g_value_set_boxed (&a, &seg_a);
g_value_init (&b, GST_TYPE_SEGMENT);
g_value_set_boxed (&b, &seg_b);
test_hash_value_diff (&a, &b, FALSE);
g_value_init (&a, G_TYPE_STRV);
fail_unless (gst_value_deserialize (&a, "<\"foo\",\"bar\">"));
g_value_init (&b, G_TYPE_STRV);
fail_unless (gst_value_deserialize (&b, "<\"bar\",\"foo\">"));
fail_unless (!g_strv_equal (g_value_get_boxed (&a),
g_value_get_boxed (&b)));
test_hash_value_diff (&a, &b, FALSE);
}
// gst_value_compare() special cases
{
GValue a = G_VALUE_INIT;
GValue b = G_VALUE_INIT;
GValue v = G_VALUE_INIT;
/* { 1 } and 1 are considered as equals */
g_value_init (&a, GST_TYPE_LIST);
fail_unless (gst_value_deserialize (&a, "{ 1 }"));
g_value_init (&b, G_TYPE_INT);
fail_unless (gst_value_deserialize (&b, "1"));
test_hash_value_equal (&a, &b, TRUE);
/* list and range are considered as equal if they contain the same set of elements */
g_value_init (&a, GST_TYPE_LIST);
fail_unless (gst_value_deserialize (&a, "{ 1, 2 }"));
g_value_init (&b, GST_TYPE_INT_RANGE);
gst_value_set_int_range (&b, 1, 2);
test_hash_value_equal (&a, &b, TRUE);
/* same set with steps */
g_value_init (&a, GST_TYPE_LIST);
fail_unless (gst_value_deserialize (&a, "{ 2, 4, 6, 8 }"));
g_value_init (&b, GST_TYPE_INT_RANGE);
gst_value_set_int_range_step (&b, 2, 8, 2);
test_hash_value_equal (&a, &b, TRUE);
/* different step */
g_value_init (&a, GST_TYPE_LIST);
fail_unless (gst_value_deserialize (&a, "{ 1, 3 }"));
g_value_init (&b, GST_TYPE_INT_RANGE);
gst_value_set_int_range (&b, 1, 3);
test_hash_value_diff (&a, &b, TRUE);
/* same for int64 range */
g_value_init (&a, GST_TYPE_LIST);
g_value_init (&v, G_TYPE_INT64);
g_value_set_int64 (&v, 1);
gst_value_list_append_and_take_value (&a, &v);
g_value_init (&v, G_TYPE_INT64);
g_value_set_int64 (&v, 2);
gst_value_list_append_and_take_value (&a, &v);
g_value_init (&b, GST_TYPE_INT64_RANGE);
gst_value_set_int64_range (&b, 1, 2);
test_hash_value_equal (&a, &b, TRUE);
g_value_init (&a, GST_TYPE_LIST);
g_value_init (&v, G_TYPE_INT64);
g_value_set_int64 (&v, 2);
gst_value_list_append_and_take_value (&a, &v);
g_value_init (&v, G_TYPE_INT64);
g_value_set_int64 (&v, 4);
gst_value_list_append_and_take_value (&a, &v);
g_value_init (&v, G_TYPE_INT64);
g_value_set_int64 (&v, 6);
gst_value_list_append_and_take_value (&a, &v);
g_value_init (&v, G_TYPE_INT64);
g_value_set_int64 (&v, 8);
gst_value_list_append_and_take_value (&a, &v);
g_value_init (&b, GST_TYPE_INT64_RANGE);
gst_value_set_int64_range_step (&b, 2, 8, 2);
test_hash_value_equal (&a, &b, TRUE);
g_value_init (&a, GST_TYPE_LIST);
g_value_init (&v, G_TYPE_INT64);
g_value_set_int64 (&v, 1);
gst_value_list_append_and_take_value (&a, &v);
g_value_init (&v, G_TYPE_INT64);
g_value_set_int64 (&v, 3);
gst_value_list_append_and_take_value (&a, &v);
g_value_init (&b, GST_TYPE_INT64_RANGE);
gst_value_set_int64_range (&b, 1, 3);
test_hash_value_diff (&a, &b, TRUE);
}
}
GST_END_TEST;
static Suite *
gst_value_suite (void)
{
@ -4126,6 +4555,7 @@ gst_value_suite (void)
tcase_add_test (tc_chain, test_serialize_deserialize_tag_list);
tcase_add_test (tc_chain, test_serialize_deserialize_sample);
tcase_add_test (tc_chain, test_serialize_deserialize_strv);
tcase_add_test (tc_chain, test_hash);
return s;
}