diff --git a/subprojects/gstreamer/gst/gst.h b/subprojects/gstreamer/gst/gst.h index e599bbe6a5..7cb55a0715 100644 --- a/subprojects/gstreamer/gst/gst.h +++ b/subprojects/gstreamer/gst/gst.h @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include diff --git a/subprojects/gstreamer/gst/gstidstr-private.h b/subprojects/gstreamer/gst/gstidstr-private.h new file mode 100644 index 0000000000..7b749408de --- /dev/null +++ b/subprojects/gstreamer/gst/gstidstr-private.h @@ -0,0 +1,288 @@ +/* GStreamer + * + * Copyright (C) 2024 Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_ID_STR_PRIVATE_H__ +#define __GST_ID_STR_PRIVATE_H__ + +#include +#include +#include +#include + +G_BEGIN_DECLS + +typedef struct { + /* < private > */ + union { + struct { + guint8 padding[15]; + // t == 0: Inline-allocated short_string + // t == 1: Heap-allocated pointer_string that needs freeing + // t == 2: Statically allocated pointer_string that needs no freeing + guint8 t; + } string_type; + struct { + gchar s[16]; + // t == 0 is the NUL terminator + } short_string; + struct { + gchar *s; // to be freed if t == 1 + guint32 len; // Length of the string without NUL-terminator +#if GLIB_SIZEOF_VOID_P == 8 + guint8 padding[3]; // always zero +#elif GLIB_SIZEOF_VOID_P == 4 + guint8 padding[7]; // always zero +#else + #error "Only 32 bit and 64 bit pointers supported currently" +#endif + guint8 t; // always 1 or 2, see above + } pointer_string; + } s; +} GstIdStrPrivate; + +static inline void +_gst_id_str_init_inline (GstIdStr * s) +{ + GstIdStrPrivate *sp = (GstIdStrPrivate *) s; + memset (sp, 0, sizeof (*sp)); +} + +static inline gsize +_gst_id_str_get_len (const GstIdStr * s) +{ + GstIdStrPrivate *sp = (GstIdStrPrivate *) s; + + switch (sp->s.string_type.t) { + case 0: + return strlen (sp->s.short_string.s); + case 1: + case 2: + return sp->s.pointer_string.len; + default: + g_assert_not_reached (); + return 0; + } +} + +static inline void +_gst_id_str_set_with_len_inline (GstIdStr * s, const gchar * value, gsize len) +{ + GstIdStrPrivate *sp = (GstIdStrPrivate *) s; + + g_assert (len <= G_MAXUINT32); + + if (sp->s.string_type.t == 1) { + if (sp->s.pointer_string.s == value) + return; + g_free (sp->s.pointer_string.s); + } + + if (len <= 15) { + memcpy (sp->s.short_string.s, value, len); + memset (&sp->s.short_string.s[len], 0, 16 - len); + } else { + sp->s.pointer_string.t = 1; + sp->s.pointer_string.len = len; + sp->s.pointer_string.s = (gchar *) g_malloc (len + 1); + memcpy (sp->s.pointer_string.s, value, len); + sp->s.pointer_string.s[len] = '\0'; + } +} + +static inline void +_gst_id_str_set_inline (GstIdStr * s, const gchar * value) +{ + gsize len = strlen (value); + _gst_id_str_set_with_len_inline (s, value, len); +} + +static inline void +_gst_id_str_set_static_str_with_len_inline (GstIdStr * s, const gchar * value, gsize len) +{ + GstIdStrPrivate *sp = (GstIdStrPrivate *) s; + + g_assert (len <= G_MAXUINT32); + + if (sp->s.string_type.t == 1) + g_free (sp->s.pointer_string.s); + + if (len <= 15) { + memcpy (sp->s.short_string.s, value, len); + memset (&sp->s.short_string.s[len], 0, 16 - len); + } else { + sp->s.pointer_string.t = 2; + sp->s.pointer_string.len = len; + sp->s.pointer_string.s = (gchar *) value; + } +} + +static inline void +_gst_id_str_set_static_str_inline (GstIdStr * s, const gchar * value) +{ + gsize len = strlen (value); + _gst_id_str_set_static_str_with_len_inline (s, value, len); +} + +static inline void +_gst_id_str_clear_inline (GstIdStr * s) +{ + GstIdStrPrivate *sp = (GstIdStrPrivate *) s; + + if (sp->s.string_type.t == 1) { + g_free (sp->s.pointer_string.s); + } + memset (sp, 0, sizeof (*sp)); +} + +static inline void +_gst_id_str_copy_into_inline (GstIdStr * d, + const GstIdStr * s) +{ + GstIdStrPrivate *sp = (GstIdStrPrivate *) s; + GstIdStrPrivate *dp = (GstIdStrPrivate *) d; + + _gst_id_str_clear_inline (d); + + *dp = *sp; + if (dp->s.string_type.t == 1) { +#if GLIB_CHECK_VERSION (2, 68, 0) + dp->s.pointer_string.s = (gchar *) g_memdup2 (dp->s.pointer_string.s, dp->s.pointer_string.len + 1); +#else + dp->s.pointer_string.s = (gchar *) g_memdup (dp->s.pointer_string.s, dp->s.pointer_string.len + 1); +#endif + } +} + +static inline void +_gst_id_str_move_inline (GstIdStr * d, + GstIdStr * s) +{ + GstIdStrPrivate *sp = (GstIdStrPrivate *) s; + GstIdStrPrivate *dp = (GstIdStrPrivate *) d; + + _gst_id_str_clear_inline (d); + + memcpy (dp, s, sizeof (*sp)); + memset (sp, 0, sizeof (*sp)); +} + +static inline GstIdStr * +_gst_id_str_new_inline (void) +{ + return (GstIdStr *) g_new0 (GstIdStrPrivate, 1); +} + +static inline GstIdStr * +_gst_id_str_copy_inline (const GstIdStr * s) +{ + GstIdStr *copy = _gst_id_str_new_inline (); + + _gst_id_str_copy_into_inline (copy, s); + + return copy; +} + +static inline void +_gst_id_str_free_inline (GstIdStr * s) +{ + _gst_id_str_clear_inline (s); + g_free (s); +} + +static inline const gchar * +_gst_id_str_as_str_inline (const GstIdStr * s) +{ + GstIdStrPrivate *sp = (GstIdStrPrivate *) s; + + switch (sp->s.string_type.t) { + case 0: + return sp->s.short_string.s; + case 1: + case 2: + return sp->s.pointer_string.s; + default: + g_assert_not_reached (); + return NULL; + } +} + +static inline gboolean +_gst_id_str_is_equal_inline (const GstIdStr * s1, + const GstIdStr * s2) +{ + GstIdStrPrivate *sp1 = (GstIdStrPrivate *) s1; + GstIdStrPrivate *sp2 = (GstIdStrPrivate *) s2; + + // Covers the short_string case and equal pointer_string pointers + if (sp1 == sp2 || memcmp (sp1, sp2, sizeof (*sp1)) == 0) + return TRUE; + + // If one of the strings is a short_string then they can't be equal at this + // point: either they're both short_strings and not the same, or one is a + // short_string and the other a pointer_string which would mean that they have + // different lengths. + if (sp1->s.string_type.t == 0 || sp2->s.string_type.t == 0) + return FALSE; + + // Otherwise they're both pointer_strings + if (sp1->s.pointer_string.len != sp2->s.pointer_string.len) + return FALSE; + return memcmp (sp1->s.pointer_string.s, sp2->s.pointer_string.s, sp1->s.pointer_string.len) == 0; +} + +static inline gboolean +_gst_id_str_is_equal_to_str_inline (const GstIdStr * s1, + const gchar * s2) +{ + return strcmp (_gst_id_str_as_str_inline (s1), s2) == 0; +} + +static inline gboolean +_gst_id_str_is_equal_to_str_with_len_inline (const GstIdStr * s1, + const gchar * s2, gsize len) +{ + GstIdStr s2_int = GST_ID_STR_INIT; + + _gst_id_str_set_static_str_with_len_inline (&s2_int, s2, len); + return _gst_id_str_is_equal_inline (s1, &s2_int); +} + +#ifndef GST_ID_STR_DISABLE_INLINES +#define gst_id_str_init(s) _gst_id_str_init_inline(s) +#define gst_id_str_get_len(s) _gst_id_str_get_len(s) +#define gst_id_str_set(s, value) _gst_id_str_set_inline(s, value) +#define gst_id_str_set_with_len(s, value, len) _gst_id_str_set_with_len_inline(s, value, len) +#define gst_id_str_set_static_str(s, value) _gst_id_str_set_static_str_inline(s, value) +#define gst_id_str_set_static_str_with_len(s, value, len) _gst_id_str_set_static_str_with_len_inline(s, value, len) +#define gst_id_str_clear(s) _gst_id_str_clear_inline(s) +#define gst_id_str_copy(s) _gst_id_str_copy_inline(s) +#define gst_id_str_new() _gst_id_str_new_inline() +#define gst_id_str_free(s) _gst_id_str_free_inline(s) +#define gst_id_str_copy_into(d, s) _gst_id_str_copy_into_inline(d, s) +#define gst_id_str_move(d, s) _gst_id_str_move_inline(d, s) +#define gst_id_str_is_equal(s1, s2) _gst_id_str_is_equal_inline(s1, s2) +#define gst_id_str_is_equal_to_str(s1, s2) _gst_id_str_is_equal_to_str_inline(s1, s2) +#define gst_id_str_is_equal_to_str_with_len(s1, s2, len) _gst_id_str_is_equal_to_str_with_len_inline(s1, s2, len) +#define gst_id_str_as_str(s) _gst_id_str_as_str_inline(s) +#endif + +G_END_DECLS + +#endif diff --git a/subprojects/gstreamer/gst/gstidstr.c b/subprojects/gstreamer/gst/gstidstr.c new file mode 100644 index 0000000000..9e3674aa34 --- /dev/null +++ b/subprojects/gstreamer/gst/gstidstr.c @@ -0,0 +1,331 @@ +/* GStreamer + * + * Copyright (C) 2024 Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:gstidstr + * @title: GstIdStr + * @short_description: String type optimized for short strings + * @see_also: #GstStructure + * + * A #GstIdStr is string type optimized for short strings and used for structure + * names, structure field names and in other places. + * + * Strings up to 16 bytes (including NUL terminator) are stored inline, other + * strings are stored on the heap. + * + * ```cpp + * GstIdStr s = GST_ID_STR_INIT; + * + * gst_id_str_set (&s, "Hello, World!"); + * g_print ("%s\n", gst_id_str_as_str (&s)); + * + * gst_id_str_clear (&s); + * ``` + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define GST_ID_STR_DISABLE_INLINES + +#include "gstidstr.h" +#include "gstidstr-private.h" + +G_DEFINE_BOXED_TYPE (GstIdStr, gst_id_str, gst_id_str_copy, gst_id_str_free); + +// Make sure the string type fields are in the same place for each variant +// See https://developercommunity.visualstudio.com/t/C-offsetof-an-array-item-is-accepted-by/10736612 +#ifndef _MSC_VER +G_STATIC_ASSERT (G_STRUCT_OFFSET (GstIdStrPrivate, + s.string_type.t) == G_STRUCT_OFFSET (GstIdStrPrivate, + s.short_string.s[15])); +#endif +G_STATIC_ASSERT (G_STRUCT_OFFSET (GstIdStrPrivate, + s.string_type.t) == G_STRUCT_OFFSET (GstIdStrPrivate, + s.pointer_string.t)); +// The overall struct should be 16 bytes large and at least pointer aligned +G_STATIC_ASSERT (sizeof (GstIdStrPrivate) == 16); +// See https://developercommunity.visualstudio.com/t/C-offsetof-an-array-item-is-accepted-by/10736612 +#ifndef _MSC_VER +G_STATIC_ASSERT (G_ALIGNOF (GstIdStrPrivate) >= G_ALIGNOF (gpointer)); +// Alignment and size of the private and public type must be the same +G_STATIC_ASSERT (G_ALIGNOF (GstIdStrPrivate) == G_ALIGNOF (GstIdStr)); +#endif +G_STATIC_ASSERT (sizeof (GstIdStrPrivate) == sizeof (GstIdStr)); + +/** + * gst_id_str_init: + * @s: A %GstIdStr + * + * Initializes a (usually stack-allocated) id string @s. The newly-initialized + * id string will contain an empty string by default as value. + * + * Since: 1.26 + */ +void +gst_id_str_init (GstIdStr * s) +{ + _gst_id_str_init_inline (s); +} + +/** + * gst_id_str_get_len: + * @s: A %GstIdStr + * + * Returns the length of @s, exluding the NUL-terminator. This is equivalent to + * calling `strcmp()` but potentially faster. + * + * Since: 1.26 + */ +gsize +gst_id_str_get_len (const GstIdStr * s) +{ + return _gst_id_str_get_len (s); +} + +/** + * gst_id_str_set: + * @s: A %GstIdStr + * @value: A NUL-terminated string + * + * Sets @s to the string @value. + * + * Since: 1.26 + */ +void +gst_id_str_set (GstIdStr * s, const gchar * value) +{ + _gst_id_str_set_inline (s, value); +} + +/** + * gst_id_str_set_with_len: + * @s: A %GstIdStr + * @value: A string + * @len: Length of the string + * + * Sets @s to the string @value of length @len. @value does not have to be + * NUL-terminated and @len should not include the NUL-terminator. + * + * Since: 1.26 + */ +void +gst_id_str_set_with_len (GstIdStr * s, const gchar * value, gsize len) +{ + _gst_id_str_set_with_len_inline (s, value, len); +} + +/** + * gst_id_str_set_static_str: + * @s: A %GstIdStr + * @value: A NUL-terminated string + * + * Sets @s to the string @value. @value needs to be valid for the remaining + * lifetime of the process, e.g. has to be a static string. + * + * Since: 1.26 + */ +void +gst_id_str_set_static_str (GstIdStr * s, const gchar * value) +{ + _gst_id_str_set_static_str_inline (s, value); +} + +/** + * gst_id_str_set_static_str_with_len: + * @s: A %GstIdStr + * @value: A string + * @len: Length of the string + * + * Sets @s to the string @value of length @len. @value needs to be valid for the + * remaining lifetime of the process, e.g. has to be a static string. + * + * @value does not have to be NUL-terminated and @len should not include the + * NUL-terminator. + * + * Since: 1.26 + */ +void +gst_id_str_set_static_str_with_len (GstIdStr * s, const gchar * value, + gsize len) +{ + _gst_id_str_set_static_str_with_len_inline (s, value, len); +} + +/** + * gst_id_str_clear: + * @s: A %GstIdStr + * + * Clears @s and sets it to the empty string. + * + * Since: 1.26 + */ +void +gst_id_str_clear (GstIdStr * s) +{ + _gst_id_str_clear_inline (s); +} + +/** + * gst_id_str_copy: + * @s: A %GstIdStr + * + * Copies @s into newly allocated heap memory. + * + * Returns: (transfer full): A heap-allocated copy of @s. + * + * Since: 1.26 + */ +GstIdStr * +gst_id_str_copy (const GstIdStr * s) +{ + return _gst_id_str_copy_inline (s); +} + +/** + * gst_id_str_new: + * + * Returns a newly heap allocated empty string. + * + * Returns: (transfer full): A heap-allocated string. + * + * Since: 1.26 + */ +GstIdStr * +gst_id_str_new (void) +{ + return _gst_id_str_new_inline (); +} + +/** + * gst_id_str_free: + * @s: A heap allocated %GstIdStr + * + * Frees @s. This should only be called for heap-allocated #GstIdStr. + * + * Since: 1.26 + */ +void +gst_id_str_free (GstIdStr * s) +{ + _gst_id_str_free_inline (s); +} + +/** + * gst_id_str_copy_into: + * @d: The destination %GstIdStr + * @s: The source %GstIdStr + * + * Copies @s into @d. + * + * Since: 1.26 + */ +void +gst_id_str_copy_into (GstIdStr * d, const GstIdStr * s) +{ + _gst_id_str_copy_into_inline (d, s); +} + +/** + * gst_id_str_move: + * @d: The destination %GstIdStr + * @s: The source %GstIdStr + * + * Moves @s into @d and resets @s. + * + * Since: 1.26 + */ +void +gst_id_str_move (GstIdStr * d, GstIdStr * s) +{ + _gst_id_str_move_inline (d, s); +} + +/** + * gst_id_str_is_equal: + * @s1: A %GstIdStr + * @s2: A %GstIdStr + * + * Compares @s1 and @s2 for equality. + * + * Returns: %TRUE if @s1 and @s2 are equal. + * + * Since: 1.26 + */ +gboolean +gst_id_str_is_equal (const GstIdStr * s1, const GstIdStr * s2) +{ + return _gst_id_str_is_equal_inline (s1, s2); +} + +/** + * gst_id_str_is_equal_to_str: + * @s1: A %GstIdStr + * @s2: A string + * + * Compares @s1 and @s2 for equality. + * + * Returns: %TRUE if @s1 and @s2 are equal. + * + * Since: 1.26 + */ +gboolean +gst_id_str_is_equal_to_str (const GstIdStr * s1, const gchar * s2) +{ + return _gst_id_str_is_equal_to_str_inline (s1, s2); +} + +/** + * gst_id_str_is_equal_to_str_with_len: + * @s1: A %GstIdStr + * @s2: A string + * @len: Length of @s2. + * + * Compares @s1 and @s2 with length @len for equality. @s2 does not have to be + * NUL-terminated and @len should not include the NUL-terminator. + * + * This is generally faster than gst_id_str_is_equal_to_str() if the length is + * already known. + * + * Returns: %TRUE if @s1 and @s2 are equal. + * + * Since: 1.26 + */ +gboolean +gst_id_str_is_equal_to_str_with_len (const GstIdStr * s1, const gchar * s2, + gsize len) +{ + return _gst_id_str_is_equal_to_str_with_len_inline (s1, s2, len); +} + +/** + * gst_id_str_as_str: + * @s: A %GstIdStr + * + * Returns: the NUL-terminated string representation of @s. + * + * Since: 1.26 + */ +const gchar * +gst_id_str_as_str (const GstIdStr * s) +{ + return _gst_id_str_as_str_inline (s); +} diff --git a/subprojects/gstreamer/gst/gstidstr.h b/subprojects/gstreamer/gst/gstidstr.h new file mode 100644 index 0000000000..1bf951e52c --- /dev/null +++ b/subprojects/gstreamer/gst/gstidstr.h @@ -0,0 +1,114 @@ +/* GStreamer + * + * Copyright (C) 2024 Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_ID_STR_H__ +#define __GST_ID_STR_H__ + +#include +#include + +G_BEGIN_DECLS + +/** + * GstIdStr: + * + * String type optimized for short strings. + * + * Strings are usually stack- or inline-allocated, and for short strings smaller + * than 16 bytes (including NUL terminator) no heap allocations are performed. + * + * Since: 1.26 + */ +typedef struct { + /* < private > */ + gpointer pointer; +#if GLIB_SIZEOF_VOID_P == 8 + guint8 padding[8]; +#elif GLIB_SIZEOF_VOID_P == 4 + guint8 padding[12]; +#else + #error "Only 32 bit and 64 bit pointers supported currently" +#endif +} GstIdStr; + +/** + * GST_ID_STR_INIT: + * + * Initializer for #GstIdStr. + * + * Since: 1.26 + */ +#define GST_ID_STR_INIT { .pointer = NULL, .padding = {0, } } + +GST_API +GType gst_id_str_get_type (void); + +GST_API +gsize gst_id_str_get_len (const GstIdStr *s) G_GNUC_PURE; + +GST_API +void gst_id_str_set (GstIdStr *s, const gchar *value); + +GST_API +void gst_id_str_set_with_len (GstIdStr *s, const gchar *value, gsize len); + +GST_API +void gst_id_str_set_static_str (GstIdStr *s, const gchar *value); + +GST_API +void gst_id_str_set_static_str_with_len (GstIdStr *s, const gchar *value, gsize len); + +GST_API +void gst_id_str_init (GstIdStr *s); + +GST_API +void gst_id_str_clear (GstIdStr *s); + +GST_API +GstIdStr * gst_id_str_new (void) G_GNUC_MALLOC; + +GST_API +GstIdStr * gst_id_str_copy (const GstIdStr *s) G_GNUC_MALLOC; + +GST_API +void gst_id_str_free (GstIdStr *s); + +GST_API +void gst_id_str_copy_into (GstIdStr *d, const GstIdStr *s); + +GST_API +void gst_id_str_move (GstIdStr *d, GstIdStr *s); + +GST_API +const gchar * gst_id_str_as_str (const GstIdStr *s) G_GNUC_PURE; + +GST_API +gboolean gst_id_str_is_equal (const GstIdStr *s1, const GstIdStr *s2) G_GNUC_PURE; + +GST_API +gboolean gst_id_str_is_equal_to_str (const GstIdStr *s1, const gchar *s2) G_GNUC_PURE; + +GST_API +gboolean gst_id_str_is_equal_to_str_with_len (const GstIdStr *s1, const gchar *s2, gsize len) G_GNUC_PURE; + +G_END_DECLS + +#endif + diff --git a/subprojects/gstreamer/gst/meson.build b/subprojects/gstreamer/gst/meson.build index bc77e50a20..15dbf3a004 100644 --- a/subprojects/gstreamer/gst/meson.build +++ b/subprojects/gstreamer/gst/meson.build @@ -27,6 +27,7 @@ gst_sources = files( 'gstformat.c', 'gstghostpad.c', 'gstdevicemonitor.c', + 'gstidstr.c', 'gstinfo.c', 'gstiterator.c', 'gstatomicqueue.c', @@ -106,6 +107,7 @@ gst_headers = files( 'gstformat.h', 'gstghostpad.h', 'gstdevicemonitor.h', + 'gstidstr.h', 'gstinfo.h', 'gstiterator.h', 'gstatomicqueue.h', diff --git a/subprojects/gstreamer/tests/check/gst/gstidstr-noinline.c b/subprojects/gstreamer/tests/check/gst/gstidstr-noinline.c new file mode 100644 index 0000000000..5a221f10f6 --- /dev/null +++ b/subprojects/gstreamer/tests/check/gst/gstidstr-noinline.c @@ -0,0 +1,22 @@ +/* GStreamer + * + * Copyright (C) 2024 Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#define GST_ID_STR_DISABLE_INLINES +#include "gstidstr.c" diff --git a/subprojects/gstreamer/tests/check/gst/gstidstr.c b/subprojects/gstreamer/tests/check/gst/gstidstr.c new file mode 100644 index 0000000000..b902c70d62 --- /dev/null +++ b/subprojects/gstreamer/tests/check/gst/gstidstr.c @@ -0,0 +1,208 @@ +/* GStreamer + * + * unit test for GstIdStr + * + * Copyright (C) 2024 Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include "gst/gstidstr-private.h" + +#define GST_ID_STR_PRIVATE(s) ((GstIdStrPrivate *)s) + +GST_START_TEST (test_init) +{ + GstIdStr s = GST_ID_STR_INIT; + GstIdStr s2 = GST_ID_STR_INIT; + const gchar short_without_nul[] = + { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; + const gchar long_without_nul[] = + { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', ' ', + 'G', 'o', 'o', 'd', 'b', 'y', 'e', ',', ' ', 'W', 'o', 'r', 'l', 'd', + '!' + }; + + fail_unless_equals_string (gst_id_str_as_str (&s), ""); + fail_unless_equals_int (gst_id_str_get_len (&s), 0); + + // Should be stack allocated + gst_id_str_set (&s, "Hello, World!"); + fail_unless_equals_int (GST_ID_STR_PRIVATE (&s)->s.string_type.t, 0); + fail_unless_equals_string (gst_id_str_as_str (&s), "Hello, World!"); + fail_unless_equals_int (gst_id_str_get_len (&s), 13); + + gst_id_str_set_with_len (&s2, short_without_nul, sizeof (short_without_nul)); + fail_unless_equals_int (GST_ID_STR_PRIVATE (&s2)->s.string_type.t, 0); + fail_unless_equals_string (gst_id_str_as_str (&s2), "Hello, World!"); + fail_unless (gst_id_str_is_equal (&s, &s2)); + fail_unless_equals_int (gst_id_str_get_len (&s2), 13); + + // Should become the empty string again + gst_id_str_clear (&s); + fail_unless_equals_string (gst_id_str_as_str (&s), ""); + fail_unless_equals_int (gst_id_str_get_len (&s), 0); + + // Should be heap allocated + gst_id_str_set (&s, "Hello, World! Goodbye, World!"); + fail_unless_equals_int (GST_ID_STR_PRIVATE (&s)->s.string_type.t, 1); + fail_unless_equals_string (gst_id_str_as_str (&s), + "Hello, World! Goodbye, World!"); + fail_unless_equals_int (gst_id_str_get_len (&s), 29); + + gst_id_str_set_with_len (&s2, long_without_nul, sizeof (long_without_nul)); + fail_unless_equals_int (GST_ID_STR_PRIVATE (&s2)->s.string_type.t, 1); + fail_unless_equals_string (gst_id_str_as_str (&s2), + "Hello, World! Goodbye, World!"); + fail_unless (gst_id_str_is_equal (&s, &s2)); + fail_unless_equals_int (gst_id_str_get_len (&s2), 29); + + // Should become the empty string again + gst_id_str_clear (&s); + fail_unless_equals_string (gst_id_str_as_str (&s), ""); + fail_unless_equals_int (gst_id_str_get_len (&s), 0); + gst_id_str_clear (&s2); + fail_unless_equals_string (gst_id_str_as_str (&s2), ""); + fail_unless_equals_int (gst_id_str_get_len (&s2), 0); +} + +GST_END_TEST; + +GST_START_TEST (test_alloc) +{ + GstIdStr *s = gst_id_str_new (); + GstIdStr *copy; + + fail_unless_equals_string (gst_id_str_as_str (s), ""); + + // Should be stack allocated + gst_id_str_set (s, "Hello, World!"); + fail_unless_equals_int (GST_ID_STR_PRIVATE (s)->s.string_type.t, 0); + fail_unless_equals_string (gst_id_str_as_str (s), "Hello, World!"); + + // Should be a full copy + copy = gst_id_str_copy (s); + fail_unless_equals_int (GST_ID_STR_PRIVATE (copy)->s.string_type.t, 0); + fail_unless_equals_string (gst_id_str_as_str (copy), "Hello, World!"); + // Strings are the same, but pointers should be different because the strings should be inlined + fail_unless_equals_string (gst_id_str_as_str (s), gst_id_str_as_str (copy)); + fail_unless (gst_id_str_as_str (s) != gst_id_str_as_str (copy)); + gst_id_str_free (copy); + + // Should become the empty string again + gst_id_str_clear (s); + fail_unless_equals_string (gst_id_str_as_str (s), ""); + + // Should be heap allocated + gst_id_str_set (s, "Hello, World! Goodbye, World!"); + fail_unless_equals_int (GST_ID_STR_PRIVATE (s)->s.string_type.t, 1); + fail_unless_equals_string (gst_id_str_as_str (s), + "Hello, World! Goodbye, World!"); + + // Should be a full copy + copy = gst_id_str_copy (s); + fail_unless_equals_int (GST_ID_STR_PRIVATE (copy)->s.string_type.t, 1); + fail_unless_equals_string (gst_id_str_as_str (copy), + "Hello, World! Goodbye, World!"); + gst_id_str_free (copy); + + // Should be stored by pointer but not heap allocated + gst_id_str_set_static_str (s, "Hello, World! Goodbye, World!"); + fail_unless_equals_int (GST_ID_STR_PRIVATE (s)->s.string_type.t, 2); + fail_unless_equals_string (gst_id_str_as_str (s), + "Hello, World! Goodbye, World!"); + + // Should be a shallow copy because it's a static string + copy = gst_id_str_copy (s); + fail_unless_equals_int (GST_ID_STR_PRIVATE (copy)->s.string_type.t, 2); + fail_unless_equals_string (gst_id_str_as_str (copy), + "Hello, World! Goodbye, World!"); + fail_unless_equals_pointer (GST_ID_STR_PRIVATE (copy)->s.pointer_string.s, + GST_ID_STR_PRIVATE (s)->s.pointer_string.s); + gst_id_str_free (copy); + + // Should become the empty string again + gst_id_str_clear (s); + fail_unless_equals_string (gst_id_str_as_str (s), ""); + + gst_id_str_free (s); +} + +GST_END_TEST; + +GST_START_TEST (test_compare) +{ + GstIdStr s1 = GST_ID_STR_INIT; + GstIdStr s2 = GST_ID_STR_INIT; + + fail_unless (gst_id_str_is_equal (&s1, &s2)); + fail_unless (gst_id_str_is_equal (&s1, &s1)); + fail_unless (gst_id_str_is_equal_to_str (&s1, "")); + fail_if (gst_id_str_is_equal_to_str (&s1, "Hello, World!")); + + // Should be stack allocated + gst_id_str_set (&s1, "Hello, World!"); + + fail_if (gst_id_str_is_equal (&s1, &s2)); + fail_unless (gst_id_str_is_equal (&s1, &s1)); + fail_unless (gst_id_str_is_equal_to_str (&s1, "Hello, World!")); + fail_if (gst_id_str_is_equal_to_str (&s1, "Hello, World?")); + fail_if (gst_id_str_is_equal_to_str (&s1, "")); + + // Should be heap allocated + gst_id_str_set (&s1, "Hello, World! Goodbye, World!"); + + fail_if (gst_id_str_is_equal (&s1, &s2)); + fail_unless (gst_id_str_is_equal (&s1, &s1)); + fail_unless (gst_id_str_is_equal_to_str (&s1, + "Hello, World! Goodbye, World!")); + fail_if (gst_id_str_is_equal_to_str (&s1, "")); + fail_if (gst_id_str_is_equal_to_str (&s1, "Hello, World? Goodbye, World!")); + + gst_id_str_set (&s2, "Hello, World!"); + fail_if (gst_id_str_is_equal (&s1, &s2)); + + gst_id_str_set (&s1, "Hello, World!"); + fail_unless (gst_id_str_is_equal (&s1, &s2)); + + gst_id_str_clear (&s1); + gst_id_str_clear (&s2); +} + +GST_END_TEST; + +static Suite * +gst_id_str_suite (void) +{ + Suite *s = suite_create ("GstIdStr"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + + tcase_add_test (tc_chain, test_init); + tcase_add_test (tc_chain, test_alloc); + tcase_add_test (tc_chain, test_compare); + + return s; +} + +GST_CHECK_MAIN (gst_id_str); diff --git a/subprojects/gstreamer/tests/check/meson.build b/subprojects/gstreamer/tests/check/meson.build index 48bb37c08d..e8419fc435 100644 --- a/subprojects/gstreamer/tests/check/meson.build +++ b/subprojects/gstreamer/tests/check/meson.build @@ -20,6 +20,8 @@ core_tests = [ [ 'gst/gstelement.c', not gst_registry or not gst_parse], [ 'gst/gstelementfactory.c', not gst_registry ], [ 'gst/gstghostpad.c', not gst_registry ], + [ 'gst/gstidstr.c' ], + [ 'gst/gstidstr-noinline.c' ], [ 'gst/gstinfo.c' ], [ 'gst/gstiterator.c' ], [ 'gst/gstmessage.c' ],