From 8c209ecf480d59ce3cc0d7dc2a604f33a7f1bc99 Mon Sep 17 00:00:00 2001 From: Julian Bouzas <julian.bouzas@collabora.com> Date: Thu, 2 Apr 2020 14:19:59 -0400 Subject: [PATCH] lib: add spa pod API --- lib/wp/meson.build | 2 + lib/wp/private.h | 16 + lib/wp/spa-pod.c | 2909 ++++++++++++++++++++++++++++++++++++++++++ lib/wp/spa-pod.h | 459 +++++++ lib/wp/wp.h | 1 + tests/wp/meson.build | 6 + tests/wp/spa-pod.c | 978 ++++++++++++++ 7 files changed, 4371 insertions(+) create mode 100644 lib/wp/spa-pod.c create mode 100644 lib/wp/spa-pod.h create mode 100644 tests/wp/spa-pod.c diff --git a/lib/wp/meson.build b/lib/wp/meson.build index 1cc8685c..9cb987f7 100644 --- a/lib/wp/meson.build +++ b/lib/wp/meson.build @@ -17,6 +17,7 @@ wp_lib_sources = files( 'properties.c', 'proxy.c', 'session.c', + 'spa-pod.c', 'spa-type.c', 'spa-props.c', ) @@ -41,6 +42,7 @@ wp_lib_headers = files( 'properties.h', 'proxy.h', 'session.h', + 'spa-pod.h', 'spa-type.h', 'wp.h', ) diff --git a/lib/wp/private.h b/lib/wp/private.h index 14a9bde4..22c6881e 100644 --- a/lib/wp/private.h +++ b/lib/wp/private.h @@ -13,6 +13,7 @@ #include "object-manager.h" #include "proxy.h" #include "iterator.h" +#include "spa-type.h" #include <stdint.h> #include <pipewire/pipewire.h> @@ -132,6 +133,21 @@ WpIterator * wp_iterator_new (const WpIteratorMethods *methods, size_t user_size); gpointer wp_iterator_get_user_data (WpIterator *self); +/* spa pod */ + +typedef struct _WpSpaPod WpSpaPod; +WpSpaPod * wp_spa_pod_new_regular_wrap (struct spa_pod *pod); +WpSpaPod * wp_spa_pod_new_property_wrap (WpSpaTypeTable table, guint32 key, + guint32 flags, struct spa_pod *pod); +WpSpaPod * wp_spa_pod_new_control_wrap (guint32 offset, guint32 type, + struct spa_pod *pod); +WpSpaPod * wp_spa_pod_new_regular_wrap_copy (const struct spa_pod *pod); +WpSpaPod * wp_spa_pod_new_property_wrap_copy (WpSpaTypeTable table, guint32 key, + guint32 flags, const struct spa_pod *pod); +WpSpaPod * wp_spa_pod_new_control_wrap_copy (guint32 offset, guint32 type, + const struct spa_pod *pod); +struct spa_pod *wp_spa_pod_get_spa_pod (WpSpaPod *self); + /* spa props */ struct _WpSpaProps diff --git a/lib/wp/spa-pod.c b/lib/wp/spa-pod.c new file mode 100644 index 00000000..5a65af89 --- /dev/null +++ b/lib/wp/spa-pod.c @@ -0,0 +1,2909 @@ +/* WirePlumber + * + * Copyright © 2020 Collabora Ltd. + * @author Julian Bouzas <julian.bouzas@collabora.com> + * + * SPDX-License-Identifier: MIT + */ + +#include <spa/utils/type-info.h> +#include <spa/pod/builder.h> +#include <spa/pod/parser.h> + +#include "private.h" +#include "spa-pod.h" +#include "spa-type.h" + +#define WP_SPA_POD_BUILDER_REALLOC_STEP_SIZE 64 + +enum { + FLAG_NO_OWNERSHIP = (1 << 0) +}; + +typedef enum { + WP_SPA_POD_REGULAR = 0, + WP_SPA_POD_PROPERTY, + WP_SPA_POD_CONTROL, +} WpSpaPodType; + +struct _WpSpaPod +{ + grefcount ref; + guint32 flags; + + /* The pipewire spa pod API does not have a type for Property and Control, + * so we create our own and separate them with their data from the regular + * spa pod types */ + WpSpaPodType type; + + /* Pod */ + union { + struct spa_pod pod_none; + struct spa_pod_bool pod_bool; + struct spa_pod_id pod_id; + struct spa_pod_int pod_int; + struct spa_pod_long pod_long; + struct spa_pod_float pod_float; + struct spa_pod_double pod_double; + struct spa_pod_pointer pod_pointer; + struct spa_pod_fd pod_fd; + struct spa_pod_rectangle pod_rectangle; + struct spa_pod_fraction pod_fraction; + struct wp_property_data { + WpSpaTypeTable table; + guint32 key; + guint32 flags; + } data_property; /* Only used for property pods */ + struct wp_control_data { + guint32 offset; + guint32 type; + } data_control; /* Only used for control pods */ + } static_pod; /* Only used for statically allocated pods */ + WpSpaPodBuilder *builder; /* Only used for dynamically allocated pods */ + struct spa_pod *pod; +}; + +G_DEFINE_BOXED_TYPE (WpSpaPod, wp_spa_pod, wp_spa_pod_ref, wp_spa_pod_unref) + +struct _WpSpaPodBuilder +{ + size_t size; + guint8 *buf; + struct spa_pod_builder builder; + uint32_t type; + struct spa_pod_frame frame; + WpSpaTypeTable prop_table; /* Only used when parsing properties */ +}; + +G_DEFINE_BOXED_TYPE (WpSpaPodBuilder, wp_spa_pod_builder, + wp_spa_pod_builder_ref, wp_spa_pod_builder_unref) + +struct _WpSpaPodParser +{ + guint32 type; + struct spa_pod_parser parser; + WpSpaPod *pod; + struct spa_pod_frame frame; + WpSpaTypeTable prop_table; /* Only used when parsing properties */ +}; + +G_DEFINE_BOXED_TYPE (WpSpaPodParser, wp_spa_pod_parser, + wp_spa_pod_parser_ref, wp_spa_pod_parser_unref) + +static int +wp_spa_pod_builder_overflow (gpointer data, uint32_t size) +{ + WpSpaPodBuilder *self = data; + const uint32_t next_size = self->size + WP_SPA_POD_BUILDER_REALLOC_STEP_SIZE; + const uint32_t new_size = size > next_size ? size : next_size; + self->buf = g_realloc (self->buf, new_size); + self->builder.data = self->buf; + self->builder.size = new_size; + self->size = new_size; + return 0; +} + +static const struct spa_pod_builder_callbacks builder_callbacks = { + SPA_VERSION_POD_BUILDER_CALLBACKS, + .overflow = wp_spa_pod_builder_overflow +}; + +static WpSpaPodBuilder * +wp_spa_pod_builder_new (size_t size, uint32_t type) +{ + WpSpaPodBuilder *self = g_rc_box_new0 (WpSpaPodBuilder); + self->size = size; + self->buf = g_new0 (guint8, self->size); + self->builder = SPA_POD_BUILDER_INIT (self->buf, self->size); + self->type = type; + + spa_pod_builder_set_callbacks (&self->builder, &builder_callbacks, self); + + return self; +} + + +/** + * wp_spa_pod_ref: + * @self: a spa pod object + * + * Returns: (transfer full): @self with an additional reference count on it + */ +WpSpaPod * +wp_spa_pod_ref (WpSpaPod *self) +{ + g_ref_count_inc (&self->ref); + return self; +} + +static void +wp_spa_pod_free (WpSpaPod *self) +{ + g_clear_pointer (&self->builder, wp_spa_pod_builder_unref); + self->pod = NULL; + g_slice_free (WpSpaPod, self); +} + +/** + * wp_spa_pod_unref: + * @self: (transfer full): a spa pod object + * + * Decreases the reference count on @self and frees it when the ref count + * reaches zero. + */ +void +wp_spa_pod_unref (WpSpaPod *self) +{ + if (g_ref_count_dec (&self->ref)) + wp_spa_pod_free (self); +} + +WpSpaPod * +wp_spa_pod_new_regular_wrap (struct spa_pod *pod) +{ + WpSpaPod *self = g_slice_new0 (WpSpaPod); + g_ref_count_init (&self->ref); + self->flags = FLAG_NO_OWNERSHIP; + self->type = WP_SPA_POD_REGULAR; + self->pod = pod; + + /* Set the prop table if it is an object */ + if (pod->type == SPA_TYPE_Object) { + WpSpaTypeTable prop_table = 0; + wp_spa_type_get_by_id (WP_SPA_TYPE_TABLE_BASIC, + ((struct spa_pod_object *) pod)->body.type, NULL, NULL, &prop_table); + self->static_pod.data_property.table = prop_table; + } + + return self; +} + +WpSpaPod * +wp_spa_pod_new_property_wrap (WpSpaTypeTable table, guint32 key, guint32 flags, + struct spa_pod *pod) +{ + WpSpaPod *self = g_slice_new0 (WpSpaPod); + g_ref_count_init (&self->ref); + self->flags = FLAG_NO_OWNERSHIP; + self->type = WP_SPA_POD_PROPERTY; + self->pod = pod; + self->static_pod.data_property.table = table; + self->static_pod.data_property.key = key; + self->static_pod.data_property.flags = flags; + return self; +} + +WpSpaPod * +wp_spa_pod_new_control_wrap (guint32 offset, guint32 type, struct spa_pod *pod) +{ + WpSpaPod *self = g_slice_new0 (WpSpaPod); + g_ref_count_init (&self->ref); + self->flags = FLAG_NO_OWNERSHIP; + self->type = WP_SPA_POD_CONTROL; + self->pod = pod; + self->static_pod.data_control.offset = offset; + self->static_pod.data_control.type = type; + return self; +} + +WpSpaPod * +wp_spa_pod_new_regular_wrap_copy (const struct spa_pod *pod) +{ + WpSpaPod *self = g_slice_new0 (WpSpaPod); + g_ref_count_init (&self->ref); + self->type = WP_SPA_POD_REGULAR; + self->builder = wp_spa_pod_builder_new ( + SPA_ROUND_UP_N (sizeof (*pod) + pod->size, 8), pod->type); + self->pod = self->builder->builder.data; + spa_pod_builder_primitive (&self->builder->builder, pod); + + /* Set the prop table if it is an object */ + if (pod->type == SPA_TYPE_Object) { + WpSpaTypeTable prop_table = 0; + wp_spa_type_get_by_id (WP_SPA_TYPE_TABLE_BASIC, + ((struct spa_pod_object *) pod)->body.type, NULL, NULL, &prop_table); + self->static_pod.data_property.table = prop_table; + } + + return self; +} + +WpSpaPod * +wp_spa_pod_new_property_wrap_copy (WpSpaTypeTable table, guint32 key, + guint32 flags, const struct spa_pod *pod) +{ + WpSpaPod *self = wp_spa_pod_new_regular_wrap_copy (pod); + self->type = WP_SPA_POD_PROPERTY; + self->static_pod.data_property.table = table; + self->static_pod.data_property.key = key; + self->static_pod.data_property.flags = flags; + return self; +} + +WpSpaPod * +wp_spa_pod_new_control_wrap_copy (guint32 offset, guint32 type, + const struct spa_pod *pod) +{ + WpSpaPod *self = wp_spa_pod_new_regular_wrap_copy (pod); + self->type = WP_SPA_POD_CONTROL; + self->static_pod.data_control.offset = offset; + self->static_pod.data_control.type = type; + return self; +} + +struct spa_pod * +wp_spa_pod_get_spa_pod (WpSpaPod *self) +{ + return self->pod; +} + +/** + * wp_spa_pod_get_type_name: + * @self: a spa pod object + * + * Gets the type name of the spa pod object + * + * Returns: the type name of the spa pod object + */ +const char * +wp_spa_pod_get_type_name (const WpSpaPod *self) +{ + const char *nick = NULL; + if (!wp_spa_type_get_by_id (WP_SPA_TYPE_TABLE_BASIC, SPA_POD_TYPE (self->pod), + NULL, &nick, NULL)) + g_return_val_if_reached (NULL); + return nick; +} + +/** + * wp_spa_pod_copy: + * @other: a spa pod object + * + * Copies a spa pod object + * + * Returns: (transfer full): The newly copied spa pod + */ +WpSpaPod * +wp_spa_pod_copy (const WpSpaPod *other) +{ + g_return_val_if_fail (other, NULL); + switch (other->type) { + case WP_SPA_POD_PROPERTY: + return wp_spa_pod_new_property_wrap_copy ( + other->static_pod.data_property.table, + other->static_pod.data_property.key, + other->static_pod.data_property.flags, other->pod); + case WP_SPA_POD_CONTROL: + return wp_spa_pod_new_control_wrap_copy ( + other->static_pod.data_control.offset, + other->static_pod.data_control.type, other->pod); + case WP_SPA_POD_REGULAR: + default: + break; + } + return wp_spa_pod_new_regular_wrap_copy (other->pod); +} + +/** + * wp_spa_pod_is_unique_owner: + * @self: a spa pod object + * + * Checks if the pod is the unique owner of its data or not + * + * Returns: TRUE if the pod owns the data, FALSE otherwise. + */ +gboolean +wp_spa_pod_is_unique_owner (WpSpaPod *self) +{ + return g_ref_count_compare (&self->ref, 1) && + !(self->flags & FLAG_NO_OWNERSHIP); +} + +/** + * wp_spa_pod_ensure_unique_owner: + * @self (transfer full): a spa pod object + * + * If @self is not uniquely owned already, then it is unrefed and a copy of + * it is returned instead. You should always consider @self as unsafe to use + * after this call and you should use the returned object instead. + * + * Returns: (transfer full): the uniquely owned spa pod object which may or may + * not be the same as @self. + */ +WpSpaPod * +wp_spa_pod_ensure_unique_owner (WpSpaPod *self) +{ + WpSpaPod *copy = NULL; + + if (wp_spa_pod_is_unique_owner (self)) + return self; + + copy = wp_spa_pod_copy (self); + wp_spa_pod_unref (self); + return copy; +} + +/** + * wp_spa_pod_new_none: + * + * Creates a spa pod of type None + * + * Returns: (transfer full): The new spa pod + */ +WpSpaPod * +wp_spa_pod_new_none (void) +{ + WpSpaPod *self = g_slice_new0 (WpSpaPod); + g_ref_count_init (&self->ref); + self->type = WP_SPA_POD_REGULAR; + self->static_pod.pod_none = SPA_POD_INIT_None(); + self->pod = &self->static_pod.pod_none; + return self; +} + +/** + * wp_spa_pod_new_boolean: + * @value: the boolean value + * + * Creates a spa pod of type boolean + * + * Returns: (transfer full): The new spa pod + */ +WpSpaPod * +wp_spa_pod_new_boolean (gboolean value) +{ + WpSpaPod *self = g_slice_new0 (WpSpaPod); + g_ref_count_init (&self->ref); + self->type = WP_SPA_POD_REGULAR; + self->static_pod.pod_bool = SPA_POD_INIT_Bool (value ? true : false); + self->pod = &self->static_pod.pod_bool.pod; + return self; +} + +/** + * wp_spa_pod_new_id: + * @value: the Id value + * + * Creates a spa pod of type Id + * + * Returns: (transfer full): The new spa pod + */ +WpSpaPod * +wp_spa_pod_new_id (guint32 value) +{ + WpSpaPod *self = g_slice_new0 (WpSpaPod); + g_ref_count_init (&self->ref); + self->type = WP_SPA_POD_REGULAR; + self->static_pod.pod_id = SPA_POD_INIT_Id (value); + self->pod = &self->static_pod.pod_id.pod; + return self; +} + +/** + * wp_spa_pod_new_int: + * @value: the int value + * + * Creates a spa pod of type int + * + * Returns: (transfer full): The new spa pod + */ +WpSpaPod * +wp_spa_pod_new_int (gint value) +{ + WpSpaPod *self = g_slice_new0 (WpSpaPod); + g_ref_count_init (&self->ref); + self->static_pod.pod_int = SPA_POD_INIT_Int (value); + self->pod = &self->static_pod.pod_int.pod; + return self; +} + +/** + * wp_spa_pod_new_long: + * @value: the long value + * + * Creates a spa pod of type long + * + * Returns: (transfer full): The new spa pod + */ +WpSpaPod * +wp_spa_pod_new_long (glong value) +{ + WpSpaPod *self = g_slice_new0 (WpSpaPod); + g_ref_count_init (&self->ref); + self->type = WP_SPA_POD_REGULAR; + self->static_pod.pod_long = SPA_POD_INIT_Long (value); + self->pod = &self->static_pod.pod_long.pod; + return self; +} + +/** + * wp_spa_pod_new_float: + * @value: the float value + * + * Creates a spa pod of type float + * + * Returns: (transfer full): The new spa pod + */ +WpSpaPod * +wp_spa_pod_new_float (float value) +{ + WpSpaPod *self = g_slice_new0 (WpSpaPod); + g_ref_count_init (&self->ref); + self->type = WP_SPA_POD_REGULAR; + self->static_pod.pod_float = SPA_POD_INIT_Float (value); + self->pod = &self->static_pod.pod_float.pod; + return self; +} + +/** + * wp_spa_pod_new_double: + * @value: the double value + * + * Creates a spa pod of type double + * + * Returns: (transfer full): The new spa pod + */ +WpSpaPod * +wp_spa_pod_new_double (double value) +{ + WpSpaPod *self = g_slice_new0 (WpSpaPod); + g_ref_count_init (&self->ref); + self->type = WP_SPA_POD_REGULAR; + self->static_pod.pod_double = SPA_POD_INIT_Double (value); + self->pod = &self->static_pod.pod_double.pod; + return self; +} + +/** + * wp_spa_pod_new_string: + * @value: the string value + * + * Creates a spa pod of type string + * + * Returns: (transfer full): The new spa pod + */ +WpSpaPod * +wp_spa_pod_new_string (const char *value) +{ + const uint32_t len = value ? strlen (value) : 0; + const char *str = value ? value : ""; + WpSpaPod *self = g_slice_new0 (WpSpaPod); + g_ref_count_init (&self->ref); + self->type = WP_SPA_POD_REGULAR; + + struct spa_pod_string p = SPA_POD_INIT_String (len + 1); + self->builder = wp_spa_pod_builder_new ( + SPA_ROUND_UP_N (sizeof (p) + len + 1, 8), SPA_TYPE_String); + + self->pod = self->builder->builder.data; + + spa_pod_builder_raw (&self->builder->builder, &p, sizeof(p)); + spa_pod_builder_write_string (&self->builder->builder, str, len); + return self; +} + +/** + * wp_spa_pod_new_bytes: + * @value: the bytes value + * @len: the length of the bytes value + * + * Creates a spa pod of type bytes + * + * Returns: (transfer full): The new spa pod + */ +WpSpaPod * +wp_spa_pod_new_bytes (gconstpointer value, guint32 len) +{ + WpSpaPod *self = g_slice_new0 (WpSpaPod); + g_ref_count_init (&self->ref); + self->type = WP_SPA_POD_REGULAR; + const struct spa_pod_bytes p = SPA_POD_INIT_Bytes (len); + self->builder = wp_spa_pod_builder_new ( + SPA_ROUND_UP_N (sizeof (p) + p.pod.size, 8), + SPA_TYPE_Bytes); + + self->pod = self->builder->builder.data; + + spa_pod_builder_raw (&self->builder->builder, &p, sizeof(p)); + spa_pod_builder_raw_padded (&self->builder->builder, value, len); + return self; +} + +/** + * wp_spa_pod_new_pointer: + * @type_name: the type name the pointer points to + * @value: the pointer value + * + * Creates a spa pod of type pointer + * + * Returns: (transfer full): The new spa pod + */ +WpSpaPod * +wp_spa_pod_new_pointer (const char *type_name, gconstpointer value) +{ + WpSpaPod *self = g_slice_new0 (WpSpaPod); + g_ref_count_init (&self->ref); + self->type = WP_SPA_POD_REGULAR; + guint32 type = 0; + if (!wp_spa_type_get_by_nick (WP_SPA_TYPE_TABLE_BASIC, type_name, &type, + NULL, NULL)) + g_return_val_if_reached (NULL); + self->static_pod.pod_pointer = SPA_POD_INIT_Pointer (type, value); + self->pod = &self->static_pod.pod_pointer.pod; + return self; +} + +/** + * wp_spa_pod_new_fd: + * @value: the Fd value + * + * Creates a spa pod of type Fd + * + * Returns: (transfer full): The new spa pod + */ +WpSpaPod * +wp_spa_pod_new_fd (gint64 value) +{ + WpSpaPod *self = g_slice_new0 (WpSpaPod); + g_ref_count_init (&self->ref); + self->type = WP_SPA_POD_REGULAR; + self->static_pod.pod_fd = SPA_POD_INIT_Fd (value); + self->pod = &self->static_pod.pod_fd.pod; + return self; +} + +/** + * wp_spa_pod_new_rectangle: + * @width: the width value of the rectangle + * @height: the height value of the rectangle + * + * Creates a spa pod of type rectangle + * + * Returns: (transfer full): The new spa pod + */ +WpSpaPod * +wp_spa_pod_new_rectangle (guint32 width, guint32 height) +{ + WpSpaPod *self = g_slice_new0 (WpSpaPod); + g_ref_count_init (&self->ref); + self->type = WP_SPA_POD_REGULAR; + self->static_pod.pod_rectangle = + SPA_POD_INIT_Rectangle (SPA_RECTANGLE (width, height)); + self->pod = &self->static_pod.pod_rectangle.pod; + return self; +} + +/** + * wp_spa_pod_new_fraction: + * @num: the numerator value of the fraction + * @denom: the denominator value of the fraction + * + * Creates a spa pod of type fraction + * + * Returns: (transfer full): The new spa pod + */ +WpSpaPod * +wp_spa_pod_new_fraction (guint32 num, guint32 denom) +{ + WpSpaPod *self = g_slice_new0 (WpSpaPod); + g_ref_count_init (&self->ref); + self->type = WP_SPA_POD_REGULAR; + self->static_pod.pod_fraction = + SPA_POD_INIT_Fraction (SPA_FRACTION (num, denom)); + self->pod = &self->static_pod.pod_fraction.pod; + return self; +} + +/** + * wp_spa_pod_new_choice: + * @type_name: the type name of the choice type + * @...: a list of choice values, followed by %NULL + * + * Creates a spa pod of type choice + * + * Returns: (transfer full): The new spa pod + */ +WpSpaPod * +wp_spa_pod_new_choice (const char *type_name, ...) +{ + WpSpaPod *self; + va_list args; + + va_start (args, type_name); + self = wp_spa_pod_new_choice_valist (type_name, args); + va_end (args); + + return self; +} + +/** + * wp_spa_pod_new_choice_valist: + * @type_name: the type name of the choice type + * @args: the variable arguments passed to wp_spa_pod_new_choice() + * + * This is the `va_list` version of wp_spa_pod_new_choice() + * + * Returns: (transfer full): The new spa pod + */ +WpSpaPod * +wp_spa_pod_new_choice_valist (const char *type_name, va_list args) +{ + g_autoptr (WpSpaPodBuilder) b = wp_spa_pod_builder_new_choice (type_name); + wp_spa_pod_builder_add_valist (b, args); + return wp_spa_pod_builder_end (b); +} + +/** + * wp_spa_pod_new_object: + * @type_name: the type name of the object type + * @id_name: the id name of the object + * @...: a list of object properties with their values, followed by %NULL + * + * Creates a spa pod of type object + * + * Returns: (transfer full): The new spa pod + */ +WpSpaPod * +wp_spa_pod_new_object (const char *type_name, const char *id_name, ...) +{ + WpSpaPod *self; + va_list args; + + va_start (args, id_name); + self = wp_spa_pod_new_object_valist (type_name, id_name, args); + va_end (args); + + return self; +} + +/** + * wp_spa_pod_new_object_valist: + * @type_name: the type name of the object type + * @id_name: the id name of the object + * @args: the variable arguments passed to wp_spa_pod_new_object() + * + * This is the `va_list` version of wp_spa_pod_new_object() + * + * Returns: (transfer full): The new spa pod + */ +WpSpaPod * +wp_spa_pod_new_object_valist (const char *type_name, const char *id_name, + va_list args) +{ + g_autoptr (WpSpaPodBuilder) b = wp_spa_pod_builder_new_object (type_name, + id_name); + wp_spa_pod_builder_add_valist (b, args); + return wp_spa_pod_builder_end (b); +} + +/** + * wp_spa_pod_new_sequence: + * @unit: the unit of the sequence + * @...: a list of sequence controls with their values, followed by %NULL + * + * Creates a spa pod of type sequence + * + * Returns: (transfer full): The new spa pod + */ +WpSpaPod * +wp_spa_pod_new_sequence (guint unit, ...) +{ + WpSpaPod *self; + va_list args; + + va_start(args, unit); + self = wp_spa_pod_new_sequence_valist (unit, args); + va_end(args); + + return self; +} + +/** + * wp_spa_pod_new_sequence_valist: + * @unit: the unit of the sequence + * @args: the variable arguments passed to wp_spa_pod_new_sequence() + * + * This is the `va_list` version of wp_spa_pod_new_sequence() + * + * Returns: (transfer full): The new spa pod + */ +WpSpaPod * +wp_spa_pod_new_sequence_valist (guint unit, va_list args) +{ + g_autoptr (WpSpaPodBuilder) b = wp_spa_pod_builder_new_sequence (unit); + wp_spa_pod_builder_add_valist (b, args); + return wp_spa_pod_builder_end (b); +} + +/** + * wp_spa_pod_is_none: + * @self: the spa pod object + * + * Checks wether the spa pod is of type none or not + * + * Returns: TRUE if it is of type none, FALSE otherwise + */ +gboolean +wp_spa_pod_is_none (const WpSpaPod *self) +{ + return self->type == WP_SPA_POD_REGULAR && spa_pod_is_none (self->pod); +} + +/** + * wp_spa_pod_is_boolean: + * @self: the spa pod object + * + * Checks wether the spa pod is of type boolean or not + * + * Returns: TRUE if it is of type boolean, FALSE otherwise + */ +gboolean +wp_spa_pod_is_boolean (const WpSpaPod *self) +{ + return self->type == WP_SPA_POD_REGULAR && + spa_pod_is_bool (self->pod) ? TRUE : FALSE; +} + +/** + * wp_spa_pod_is_id: + * @self: the spa pod object + * + * Checks wether the spa pod is of type Id or not + * + * Returns: TRUE if it is of type Id, FALSE otherwise + */ +gboolean +wp_spa_pod_is_id (const WpSpaPod *self) +{ + return self->type == WP_SPA_POD_REGULAR && spa_pod_is_id (self->pod); +} + +/** + * wp_spa_pod_is_int: + * @self: the spa pod object + * + * Checks wether the spa pod is of type int or not + * + * Returns: TRUE if it is of type int, FALSE otherwise + */ +gboolean +wp_spa_pod_is_int (const WpSpaPod *self) +{ + return self->type == WP_SPA_POD_REGULAR && spa_pod_is_int (self->pod); +} + +/** + * wp_spa_pod_is_long: + * @self: the spa pod object + * + * Checks wether the spa pod is of type long or not + * + * Returns: TRUE if it is of type long, FALSE otherwise + */ +gboolean +wp_spa_pod_is_long (const WpSpaPod *self) +{ + return self->type == WP_SPA_POD_REGULAR && spa_pod_is_long (self->pod); +} + +/** + * wp_spa_pod_is_float: + * @self: the spa pod object + * + * Checks wether the spa pod is of type float or not + * + * Returns: TRUE if it is of type float, FALSE otherwise + */ +gboolean +wp_spa_pod_is_float (const WpSpaPod *self) +{ + return self->type == WP_SPA_POD_REGULAR && spa_pod_is_float (self->pod); +} + +/** + * wp_spa_pod_is_double: + * @self: the spa pod object + * + * Checks wether the spa pod is of type double or not + * + * Returns: TRUE if it is of type double, FALSE otherwise + */ +gboolean +wp_spa_pod_is_double (const WpSpaPod *self) +{ + return self->type == WP_SPA_POD_REGULAR && spa_pod_is_double (self->pod); +} + +/** + * wp_spa_pod_is_string: + * @self: the spa pod object + * + * Checks wether the spa pod is of type string or not + * + * Returns: TRUE if it is of type string, FALSE otherwise + */ +gboolean +wp_spa_pod_is_string (const WpSpaPod *self) +{ + return self->type == WP_SPA_POD_REGULAR && spa_pod_is_string (self->pod); +} + +/** + * wp_spa_pod_is_bytes: + * @self: the spa pod object + * + * Checks wether the spa pod is of type bytes or not + * + * Returns: TRUE if it is of type bytes, FALSE otherwise + */ +gboolean +wp_spa_pod_is_bytes (const WpSpaPod *self) +{ + return self->type == WP_SPA_POD_REGULAR && spa_pod_is_bytes (self->pod); +} + +/** + * wp_spa_pod_is_pointer: + * @self: the spa pod object + * + * Checks wether the spa pod is of type pointer or not + * + * Returns: TRUE if it is of type pointer, FALSE otherwise + */ +gboolean +wp_spa_pod_is_pointer (const WpSpaPod *self) +{ + return self->type == WP_SPA_POD_REGULAR && spa_pod_is_pointer (self->pod); +} + +/** + * wp_spa_pod_is_fd: + * @self: the spa pod object + * + * Checks wether the spa pod is of type Fd or not + * + * Returns: TRUE if it is of type Fd, FALSE otherwise + */ +gboolean +wp_spa_pod_is_fd (const WpSpaPod *self) +{ + return self->type == WP_SPA_POD_REGULAR && spa_pod_is_fd (self->pod); +} + +/** + * wp_spa_pod_is_rectangle: + * @self: the spa pod object + * + * Checks wether the spa pod is of type rectangle or not + * + * Returns: TRUE if it is of type rectangle, FALSE otherwise + */ +gboolean +wp_spa_pod_is_rectangle (const WpSpaPod *self) +{ + return self->type == WP_SPA_POD_REGULAR && spa_pod_is_rectangle (self->pod); +} + +/** + * wp_spa_pod_is_fraction: + * @self: the spa pod object + * + * Checks wether the spa pod is of type fraction or not + * + * Returns: TRUE if it is of type fraction, FALSE otherwise + */ +gboolean +wp_spa_pod_is_fraction (const WpSpaPod *self) +{ + return self->type == WP_SPA_POD_REGULAR && spa_pod_is_fraction (self->pod); +} + +/** + * wp_spa_pod_is_array: + * @self: the spa pod object + * + * Checks wether the spa pod is of type array or not + * + * Returns: TRUE if it is of type array, FALSE otherwise + */ +gboolean +wp_spa_pod_is_array (const WpSpaPod *self) +{ + return self->type == WP_SPA_POD_REGULAR && spa_pod_is_array (self->pod); +} + +/** + * wp_spa_pod_is_choice: + * @self: the spa pod object + * + * Checks wether the spa pod is of type choice or not + * + * Returns: TRUE if it is of type choice, FALSE otherwise + */ +gboolean +wp_spa_pod_is_choice (const WpSpaPod *self) +{ + return self->type == WP_SPA_POD_REGULAR && spa_pod_is_choice (self->pod); +} + +/** + * wp_spa_pod_is_object: + * @self: the spa pod object + * + * Checks wether the spa pod is of type object or not + * + * Returns: TRUE if it is of type object, FALSE otherwise + */ +gboolean +wp_spa_pod_is_object (const WpSpaPod *self) +{ + return self->type == WP_SPA_POD_REGULAR && spa_pod_is_object (self->pod); +} + +/** + * wp_spa_pod_is_struct: + * @self: the spa pod object + * + * Checks wether the spa pod is of type struct or not + * + * Returns: TRUE if it is of type struct, FALSE otherwise + */ +gboolean +wp_spa_pod_is_struct (const WpSpaPod *self) +{ + return self->type == WP_SPA_POD_REGULAR && spa_pod_is_struct (self->pod); +} + +/** + * wp_spa_pod_is_sequence: + * @self: the spa pod object + * + * Checks wether the spa pod is of type sequence or not + * + * Returns: TRUE if it is of type sequence, FALSE otherwise + */ +gboolean +wp_spa_pod_is_sequence (const WpSpaPod *self) +{ + return self->type == WP_SPA_POD_REGULAR && spa_pod_is_sequence (self->pod); +} + +/** + * wp_spa_pod_is_property: + * @self: the spa pod object + * + * Checks wether the spa pod is of type property or not + * + * Returns: TRUE if it is of type property, FALSE otherwise + */ +gboolean +wp_spa_pod_is_property (const WpSpaPod *self) +{ + return self->type == WP_SPA_POD_PROPERTY; +} + +/** + * wp_spa_pod_is_control: + * @self: the spa pod object + * + * Checks wether the spa pod is of type control or not + * + * Returns: TRUE if it is of type control, FALSE otherwise + */ +gboolean +wp_spa_pod_is_control (const WpSpaPod *self) +{ + return self->type == WP_SPA_POD_CONTROL; +} + +/** + * wp_spa_pod_get_boolean: + * @self: the spa pod object + * @value: (out): the boolean value + * + * Gets the boolean value of a spa pod object + * + * Returns: TRUE if the value was obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_get_boolean (const WpSpaPod *self, gboolean *value) +{ + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (value, FALSE); + bool v = FALSE; + const int res = spa_pod_get_bool (self->pod, &v); + *value = v ? TRUE : FALSE; + return res >= 0; +} + +/** + * wp_spa_pod_get_id: + * @self: the spa pod object + * @value: (out): the Id value + * + * Gets the Id value of a spa pod object + * + * Returns: TRUE if the value was obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_get_id (const WpSpaPod *self, guint32 *value) +{ + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (value, FALSE); + uint32_t v = 0; + const int res = spa_pod_get_id (self->pod, &v); + *value = v; + return res >= 0; +} + +/** + * wp_spa_pod_get_int: + * @self: the spa pod object + * @value: (out): the int value + * + * Gets the int value of a spa pod object + * + * Returns: TRUE if the value was obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_get_int (const WpSpaPod *self, gint *value) +{ + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (value, FALSE); + return spa_pod_get_int (self->pod, value) >= 0; +} + +/** + * wp_spa_pod_get_long: + * @self: the spa pod object + * @value: (out): the long value + * + * Gets the long value of a spa pod object + * + * Returns: TRUE if the value was obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_get_long (const WpSpaPod *self, glong *value) +{ + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (value, FALSE); + return spa_pod_get_long (self->pod, value) >= 0; +} + +/** + * wp_spa_pod_get_float: + * @self: the spa pod object + * @value: (out): the float value + * + * Gets the float value of a spa pod object + * + * Returns: TRUE if the value was obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_get_float (const WpSpaPod *self, float *value) +{ + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (value, FALSE); + return spa_pod_get_float (self->pod, value) >= 0; +} + +/** + * wp_spa_pod_get_double: + * @self: the spa pod object + * @value: (out): the double value + * + * Gets the double value of a spa pod object + * + * Returns: TRUE if the value was obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_get_double (const WpSpaPod *self, double *value) +{ + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (value, FALSE); + return spa_pod_get_double (self->pod, value) >= 0; +} + +/** + * wp_spa_pod_get_string: + * @self: the spa pod object + * @value: (out): the string value + * + * Gets the string value of a spa pod object + * + * Returns: TRUE if the value was obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_get_string (const WpSpaPod *self, const char **value) +{ + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (value, FALSE); + return spa_pod_get_string (self->pod, value) >= 0; +} + +/** + * wp_spa_pod_get_bytes: + * @self: the spa pod object + * @value: (out): the bytes value + * @len: (out): the length of the bytes value + * + * Gets the bytes value and its len of a spa pod object + * + * Returns: TRUE if the value was obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_get_bytes (const WpSpaPod *self, gconstpointer *value, guint32 *len) +{ + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (value, FALSE); + g_return_val_if_fail (len, FALSE); + return spa_pod_get_bytes (self->pod, value, len) >= 0; +} + +/** + * wp_spa_pod_get_pointer: + * @self: the spa pod object + * @type_name: (out): the type name of the pointer value + * @value: (out): the pointer value + * + * Gets the pointer value and its type name of a spa pod object + * + * Returns: TRUE if the value was obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_get_pointer (const WpSpaPod *self, const char **type_name, + gconstpointer *value) +{ + gboolean res; + + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (type_name, FALSE); + g_return_val_if_fail (value, FALSE); + + guint32 type = 0; + res = spa_pod_get_pointer (self->pod, &type, value) >= 0; + if (!wp_spa_type_get_by_id (WP_SPA_TYPE_TABLE_BASIC, type, NULL, type_name, + NULL)) + g_return_val_if_reached (FALSE); + return res; +} + +/** + * wp_spa_pod_get_fd: + * @self: the spa pod object + * @value: (out): the Fd value + * + * Gets the Fd value of a spa pod object + * + * Returns: TRUE if the value was obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_get_fd (const WpSpaPod *self, gint64 *value) +{ + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (value, FALSE); + return spa_pod_get_fd (self->pod, value) >= 0; +} + +/** + * wp_spa_pod_get_rectangle: + * @self: the spa pod object + * @width: (out): the rectangle's width value + * @height: (out): the rectangle's height value + * + * Gets the rectangle's width and height value of a spa pod object + * + * Returns: TRUE if the value was obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_get_rectangle (const WpSpaPod *self, guint32 *width, guint32 *height) +{ + g_return_val_if_fail (self, FALSE); + struct spa_rectangle rectangle = { 0, }; + const gboolean res = spa_pod_get_rectangle (self->pod, &rectangle) >= 0; + if (width) + *width = rectangle.width; + if (height) + *height = rectangle.height; + return res; +} + +/** + * wp_spa_pod_get_fraction: + * @self: the spa pod object + * @num: (out): the fractions's numerator value + * @denom: (out): the fractions's denominator value + * + * Gets the fractions's numerator and denominator value of a spa pod object + * + * Returns: TRUE if the value was obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_get_fraction (const WpSpaPod *self, guint32 *num, guint32 *denom) +{ + g_return_val_if_fail (self, FALSE); + struct spa_fraction fraction = { 0, }; + const gboolean res = spa_pod_get_fraction (self->pod, &fraction) >= 0; + if (num) + *num = fraction.num; + if (denom) + *denom = fraction.denom; + return res; +} + +/** + * wp_spa_pod_set_boolean: + * @self: the spa pod object + * @value: the boolean value + * + * Sets a boolean value in the spa pod object. + * + * Returns: TRUE if the value could be set, FALSE othewrise. + */ +gboolean +wp_spa_pod_set_boolean (WpSpaPod *self, gboolean value) +{ + g_return_val_if_fail (wp_spa_pod_is_boolean (self), FALSE); + ((struct spa_pod_bool *)self->pod)->value = value ? true : false; + return TRUE; +} + +/** + * wp_spa_pod_set_id: + * @self: the spa pod object + * @value: the Id value + * + * Sets an Id value in the spa pod object. + * + * Returns: TRUE if the value could be set, FALSE othewrise. + */ +gboolean +wp_spa_pod_set_id (WpSpaPod *self, guint32 value) +{ + g_return_val_if_fail (wp_spa_pod_is_id (self), FALSE); + ((struct spa_pod_id *)self->pod)->value = value; + return TRUE; +} + +/** + * wp_spa_pod_set_int: + * @self: the spa pod object + * @value: the int value + * + * Sets an int value in the spa pod object. + * + * Returns: TRUE if the value could be set, FALSE othewrise. + */ +gboolean +wp_spa_pod_set_int (WpSpaPod *self, gint value) +{ + g_return_val_if_fail (wp_spa_pod_is_int (self), FALSE); + ((struct spa_pod_int *)self->pod)->value = value; + return TRUE; +} + +/** + * wp_spa_pod_set_long: + * @self: the spa pod object + * @value: the long value + * + * Sets a long value in the spa pod object. + * + * Returns: TRUE if the value could be set, FALSE othewrise. + */ +gboolean +wp_spa_pod_set_long (WpSpaPod *self, glong value) +{ + g_return_val_if_fail (wp_spa_pod_is_long (self), FALSE); + ((struct spa_pod_long *)self->pod)->value = value; + return TRUE; +} + +/** + * wp_spa_pod_set_float: + * @self: the spa pod object + * @value: the float value + * + * Sets a float value in the spa pod object. + * + * Returns: TRUE if the value could be set, FALSE othewrise. + */ +gboolean +wp_spa_pod_set_float (WpSpaPod *self, float value) +{ + g_return_val_if_fail (wp_spa_pod_is_float (self), FALSE); + ((struct spa_pod_float *)self->pod)->value = value; + return TRUE; +} + +/** + * wp_spa_pod_set_double: + * @self: the spa pod object + * @value: the double value + * + * Sets a double value in the spa pod object. + * + * Returns: TRUE if the value could be set, FALSE othewrise. + */ +gboolean +wp_spa_pod_set_double (WpSpaPod *self, double value) +{ + g_return_val_if_fail (wp_spa_pod_is_double (self), FALSE); + ((struct spa_pod_double *)self->pod)->value = value; + return TRUE; +} + +/** + * wp_spa_pod_set_pointer: + * @self: the spa pod object + * @type_name: the type name the pointer points to + * @value: the pointer value + * + * Sets a pointer value with its type name in the spa pod object. + * + * Returns: TRUE if the value could be set, FALSE othewrise. + */ +gboolean +wp_spa_pod_set_pointer (WpSpaPod *self, const char *type_name, + gconstpointer value) +{ + guint32 id = 0; + + g_return_val_if_fail (wp_spa_pod_is_pointer (self), FALSE); + + if (!wp_spa_type_get_by_nick (WP_SPA_TYPE_TABLE_BASIC, type_name, &id, NULL, + NULL)) + g_return_val_if_reached (FALSE); + + ((struct spa_pod_pointer *)self->pod)->body.type = id; + ((struct spa_pod_pointer *)self->pod)->body.value = value; + return TRUE; +} + +/** + * wp_spa_pod_set_fd: + * @self: the spa pod object + * @value: the Fd value + * + * Sets a Fd value in the spa pod object. + * + * Returns: TRUE if the value could be set, FALSE othewrise. + */ +gboolean +wp_spa_pod_set_fd (WpSpaPod *self, gint64 value) +{ + g_return_val_if_fail (wp_spa_pod_is_fd (self), FALSE); + ((struct spa_pod_fd *)self->pod)->value = value; + return TRUE; +} + +/** + * wp_spa_pod_set_rectangle: + * @self: the spa pod object + * @width: the width value of the rectangle + * @height: the height value of the rectangle + * + * Sets the width and height values of a rectangle in the spa pod object. + * + * Returns: TRUE if the value could be set, FALSE othewrise. + */ +gboolean +wp_spa_pod_set_rectangle (WpSpaPod *self, guint32 width, guint32 height) +{ + g_return_val_if_fail (wp_spa_pod_is_rectangle (self), FALSE); + ((struct spa_pod_rectangle *)self->pod)->value.width = width; + ((struct spa_pod_rectangle *)self->pod)->value.height = height; + return TRUE; +} + +/** + * wp_spa_pod_set_fraction: + * @self: the spa pod object + * @num: the numerator value of the farction + * @denom: the denominator value of the fraction + * + * Sets the numerator and denominator values of a fraction in the spa pod object. + * + * Returns: TRUE if the value could be set, FALSE othewrise. + */ +gboolean +wp_spa_pod_set_fraction (WpSpaPod *self, guint32 num, guint32 denom) +{ + g_return_val_if_fail (wp_spa_pod_is_fraction (self), FALSE); + ((struct spa_pod_fraction *)self->pod)->value.num = num; + ((struct spa_pod_fraction *)self->pod)->value.denom = denom; + return TRUE; +} + +/** + * wp_spa_pod_set_pod: + * @self: the spa pod object + * @pod: the pod with the value to be set + * + * Sets the value of a spa pod object in the current spa pod object. THe spa pod + * objects must be of the same value. + * + * Returns: TRUE if the value could be set, FALSE othewrise. + */ +gboolean +wp_spa_pod_set_pod (WpSpaPod *self, const WpSpaPod *pod) +{ + g_return_val_if_fail (self->type == pod->type, FALSE); + g_return_val_if_fail (SPA_POD_TYPE (self->pod) == SPA_POD_TYPE (pod->pod), FALSE); + + switch (SPA_POD_TYPE (self->pod)) { + case SPA_TYPE_None: + break; + case SPA_TYPE_Bool: + ((struct spa_pod_bool *)self->pod)->value = ((struct spa_pod_bool *)pod->pod)->value; + break; + case SPA_TYPE_Id: + ((struct spa_pod_id *)self->pod)->value = ((struct spa_pod_id *)pod->pod)->value; + break; + case SPA_TYPE_Int: + ((struct spa_pod_int *)self->pod)->value = ((struct spa_pod_int *)pod->pod)->value; + break; + case SPA_TYPE_Long: + ((struct spa_pod_long *)self->pod)->value = ((struct spa_pod_long *)pod->pod)->value; + break; + case SPA_TYPE_Float: + ((struct spa_pod_float *)self->pod)->value = ((struct spa_pod_float *)pod->pod)->value; + break; + case SPA_TYPE_Double: + ((struct spa_pod_double *)self->pod)->value = ((struct spa_pod_double *)pod->pod)->value; + break; + case SPA_TYPE_Pointer: + ((struct spa_pod_pointer *)self->pod)->body.type = ((struct spa_pod_pointer *)pod->pod)->body.type; + ((struct spa_pod_pointer *)self->pod)->body.value = ((struct spa_pod_pointer *)pod->pod)->body.value; + break; + case SPA_TYPE_Fd: + ((struct spa_pod_fd *)self->pod)->value = ((struct spa_pod_fd *)pod->pod)->value; + break; + case SPA_TYPE_Rectangle: + ((struct spa_pod_rectangle *)self->pod)->value.width = ((struct spa_pod_rectangle *)pod->pod)->value.width; + ((struct spa_pod_rectangle *)self->pod)->value.height = ((struct spa_pod_rectangle *)pod->pod)->value.height; + break; + case SPA_TYPE_Fraction: + ((struct spa_pod_fraction *)self->pod)->value.num = ((struct spa_pod_fraction *)pod->pod)->value.num; + ((struct spa_pod_fraction *)self->pod)->value.denom = ((struct spa_pod_fraction *)pod->pod)->value.denom; + break; + default: + g_return_val_if_fail (self->pod->size >= pod->pod->size, FALSE); + memcpy (SPA_MEMBER (self->pod, sizeof (struct spa_pod), void), + SPA_MEMBER (pod->pod, sizeof (struct spa_pod), void), + SPA_MIN (self->pod->size, pod->pod->size)); + self->pod->type = pod->pod->type; + self->pod->size = pod->pod->size; + break; + } + + switch (self->type) { + case WP_SPA_POD_PROPERTY: + self->static_pod.data_property.table = pod->static_pod.data_property.table; + self->static_pod.data_property.key = pod->static_pod.data_property.key; + self->static_pod.data_property.flags = pod->static_pod.data_property.flags; + break; + case WP_SPA_POD_CONTROL: + self->static_pod.data_control.offset = pod->static_pod.data_control.offset; + self->static_pod.data_control.type = pod->static_pod.data_control.type; + break; + case WP_SPA_POD_REGULAR: + default: + break; + } + + return TRUE; +} + +/** + * wp_spa_pod_get_object: + * @self: the spa pod object + * @type_name: the type name of the object type + * @id_name: (out): the id name of the object + * @...: (out): the list of the object properties values, followed by %NULL + * + * Gets the object properties values of a spa pod object + * + * Returns: TRUE if the object properties values were obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_get_object (WpSpaPod *self, const char *type_name, + const char **id_name, ...) +{ + va_list args; + gboolean res; + va_start (args, id_name); + res = wp_spa_pod_get_object_valist (self, type_name, id_name, args); + va_end (args); + return res; +} + +/** + * wp_spa_pod_get_object_valist: + * @self: the spa pod object + * @type_name: the type name of the object type + * @id_name: (out): the id name of the object + * @args: (out): the variable arguments passed to wp_spa_pod_get_object() + * + * This is the `va_list` version of wp_spa_pod_get_object() + * + * Returns: TRUE if the object properties values were obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_get_object_valist (WpSpaPod *self, const char *type_name, + const char **id_name, va_list args) +{ + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (wp_spa_pod_is_object (self), FALSE); + g_autoptr (WpSpaPodParser) p = wp_spa_pod_parser_new_object (self, type_name, + id_name); + const gboolean res = wp_spa_pod_parser_get_valist (p, args); + wp_spa_pod_parser_end (p); + return res; +} + +/** + * wp_spa_pod_get_struct: + * @self: the spa pod object + * @...: (out): the list of the struct values, followed by %NULL + * + * Gets the struct's values of a spa pod object + * + * Returns: TRUE if the struct values were obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_get_struct (WpSpaPod *self, ...) +{ + va_list args; + gboolean res; + va_start (args, self); + res = wp_spa_pod_get_struct_valist (self, args); + va_end (args); + return res; +} + +/** + * wp_spa_pod_get_struct_valist: + * @self: the spa pod object + * @args: (out): the variable arguments passed to wp_spa_pod_get_struct() + * + * This is the `va_list` version of wp_spa_pod_get_struct() + * + * Returns: TRUE if the struct values were obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_get_struct_valist (WpSpaPod *self, va_list args) +{ + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (wp_spa_pod_is_struct (self), FALSE); + g_autoptr (WpSpaPodParser) p = wp_spa_pod_parser_new_struct (self); + const gboolean res = wp_spa_pod_parser_get_valist (p, args); + wp_spa_pod_parser_end (p); + return res; +} + +/** + * wp_spa_pod_get_property: + * @self: the spa pod object + * @key: (out) (optional): the name of the property + * @value: (out) (optional): the spa pod value of the property + * + * Gets the name, flags and spa pod value of a spa pod property + * + * Returns: TRUE if the value was obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_get_property (const WpSpaPod *self, const char **key, + WpSpaPod **value) +{ + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (wp_spa_pod_is_property (self), FALSE); + + if (key && !wp_spa_type_get_by_id (self->static_pod.data_property.table, + self->static_pod.data_property.key, NULL, key, NULL)) + return FALSE; + if (value) + *value = wp_spa_pod_new_regular_wrap (self->pod); + + return TRUE; +} + +/** + * wp_spa_pod_get_control: + * @self: the spa pod object + * @offset: (out) (optional): the offset of the control + * @type_name: (out) (optional): the type name of the control + * @value: (out) (optional): the spa pod value of the control + * + * Gets the offset, type name and spa pod value of a spa pod control + * + * Returns: TRUE if the value was obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_get_control (const WpSpaPod *self, guint32 *offset, + const char **type_name, WpSpaPod **value) +{ + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (wp_spa_pod_is_control (self), FALSE); + + if (offset) + *offset = self->static_pod.data_control.offset; + if (type_name && !wp_spa_type_get_by_id (WP_SPA_TYPE_TABLE_CONTROL, + self->static_pod.data_control.type, NULL, type_name, NULL)) + g_return_val_if_reached (FALSE); + if (value) + *value = wp_spa_pod_new_regular_wrap (self->pod); + + return TRUE; +} + +/** + * wp_spa_pod_get_choice_child: + * @self: a spa pod choice object + * + * Gets the child of a spa pod choice object + * + * Returns: (transfer full): the child of the spa pod choice object + */ +WpSpaPod * +wp_spa_pod_get_choice_child (WpSpaPod *self) +{ + g_return_val_if_fail (wp_spa_pod_is_choice (self), NULL); + return wp_spa_pod_new_regular_wrap (SPA_POD_CHOICE_CHILD (self->pod)); +} + +/** + * wp_spa_pod_get_array_child: + * @self: a spa pod choice object + * + * Gets the child of a spa pod array object + * + * Returns: (transfer full): the child of the spa pod array object + */ +WpSpaPod * +wp_spa_pod_get_array_child (WpSpaPod *self) +{ + g_return_val_if_fail (wp_spa_pod_is_array (self), NULL); + return wp_spa_pod_new_regular_wrap (SPA_POD_ARRAY_CHILD (self->pod)); +} + +/** + * wp_spa_pod_builder_ref: + * @self: a spa pod builder object + * + * Returns: (transfer full): @self with an additional reference count on it + */ +WpSpaPodBuilder * +wp_spa_pod_builder_ref (WpSpaPodBuilder *self) +{ + return (WpSpaPodBuilder *) g_rc_box_acquire ((gpointer) self); +} + +static void +wp_spa_pod_builder_free (WpSpaPodBuilder *self) +{ + g_clear_pointer (&self->buf, g_free); +} + +/** + * wp_spa_pod_builder_unref: + * @self: (transfer full): a spa pod builder object + * + * Decreases the reference count on @self and frees it when the ref count + * reaches zero. + */ +void +wp_spa_pod_builder_unref (WpSpaPodBuilder *self) +{ + g_rc_box_release_full (self, (GDestroyNotify) wp_spa_pod_builder_free); +} + +/** + * wp_spa_pod_builder_new_array: + * + * Creates a spa pod builder of type array + * + * Returns: (transfer full): the new spa pod builder + */ +WpSpaPodBuilder * +wp_spa_pod_builder_new_array (void) +{ + WpSpaPodBuilder *self = wp_spa_pod_builder_new ( + WP_SPA_POD_BUILDER_REALLOC_STEP_SIZE, SPA_TYPE_Array); + spa_pod_builder_push_array (&self->builder, &self->frame); + return self; +} + +/** + * wp_spa_pod_builder_new_choice: + * @type_name: the type name of the choice type + * + * Creates a spa pod builder of type choice + * + * Returns: (transfer full): the new spa pod builder + */ +WpSpaPodBuilder * +wp_spa_pod_builder_new_choice (const char *type_name) +{ + WpSpaPodBuilder *self = NULL; + guint32 type = 0; + + /* Construct the builder */ + self = wp_spa_pod_builder_new (WP_SPA_POD_BUILDER_REALLOC_STEP_SIZE, + SPA_TYPE_Choice); + + /* Find the choice type */ + if (!wp_spa_type_get_by_nick (WP_SPA_TYPE_TABLE_CHOICE, type_name, &type, + NULL, NULL)) + g_return_val_if_reached (NULL); + + /* Push the array */ + spa_pod_builder_push_choice (&self->builder, &self->frame, type, + WP_SPA_TYPE_TABLE_CHOICE); + + return self; +} + +/** + * wp_spa_pod_builder_new_object: + * @type_name: the type name of the object type + * @id_name: the Id name of the object + * + * Creates a spa pod builder of type object + * + * Returns: (transfer full): the new spa pod builder + */ +WpSpaPodBuilder * +wp_spa_pod_builder_new_object (const char *type_name, const char *id_name) +{ + WpSpaPodBuilder *self = NULL; + guint32 type = 0; + guint32 id = 0; + + /* Construct the builder */ + self = wp_spa_pod_builder_new (WP_SPA_POD_BUILDER_REALLOC_STEP_SIZE, + SPA_TYPE_Object); + + /* Find the type */ + if (!wp_spa_type_get_by_nick (WP_SPA_TYPE_TABLE_BASIC, type_name, &type, + NULL, &self->prop_table)) + g_return_val_if_reached (NULL); + + /* Find the id */ + if (!wp_spa_type_get_by_nick (WP_SPA_TYPE_TABLE_PARAM, id_name, &id, NULL, + NULL)) + g_return_val_if_reached (NULL); + + /* Push the object */ + spa_pod_builder_push_object (&self->builder, &self->frame, type, id); + + return self; +} + +/** + * wp_spa_pod_builder_new_struct: + * + * Creates a spa pod builder of type struct + * + * Returns: (transfer full): the new spa pod builder + */ +WpSpaPodBuilder * +wp_spa_pod_builder_new_struct (void) +{ + WpSpaPodBuilder *self = NULL; + self = wp_spa_pod_builder_new (WP_SPA_POD_BUILDER_REALLOC_STEP_SIZE, + SPA_TYPE_Struct); + spa_pod_builder_push_struct (&self->builder, &self->frame); + return self; +} + +/** + * wp_spa_pod_builder_new_sequence: + * + * Creates a spa pod builder of type sequence + * + * Returns: (transfer full): the new spa pod builder + */ +WpSpaPodBuilder * +wp_spa_pod_builder_new_sequence (guint unit) +{ + WpSpaPodBuilder *self = NULL; + self = wp_spa_pod_builder_new (WP_SPA_POD_BUILDER_REALLOC_STEP_SIZE, + SPA_TYPE_Sequence); + spa_pod_builder_push_sequence (&self->builder, &self->frame, unit); + return self; +} + +/** + * wp_spa_pod_builder_add_none: + * @self: the spa pod builder object + * + * Adds a none value into the builder + */ +void +wp_spa_pod_builder_add_none (WpSpaPodBuilder *self) +{ + spa_pod_builder_none (&self->builder); +} + +/** + * wp_spa_pod_builder_add_boolean: + * @self: the spa pod builder object + * @value: the boolean value + * + * Adds a boolean value into the builder + */ +void +wp_spa_pod_builder_add_boolean (WpSpaPodBuilder *self, gboolean value) +{ + spa_pod_builder_bool (&self->builder, value ? true : false); +} + +/** + * wp_spa_pod_builder_add_id: + * @self: the spa pod builder object + * @value: the Id value + * + * Adds a Id value into the builder + */ +void +wp_spa_pod_builder_add_id (WpSpaPodBuilder *self, guint32 value) +{ + spa_pod_builder_id (&self->builder, value); +} + +/** + * wp_spa_pod_builder_add_int: + * @self: the spa pod builder object + * @value: the int value + * + * Adds a int value into the builder + */ +void +wp_spa_pod_builder_add_int (WpSpaPodBuilder *self, gint value) +{ + spa_pod_builder_int (&self->builder, value); +} + +/** + * wp_spa_pod_builder_add_long: + * @self: the spa pod builder object + * @value: the long value + * + * Adds a long value into the builder + */ +void +wp_spa_pod_builder_add_long (WpSpaPodBuilder *self, glong value) +{ + spa_pod_builder_long (&self->builder, value); +} + +/** + * wp_spa_pod_builder_add_float: + * @self: the spa pod builder object + * @value: the float value + * + * Adds a float value into the builder + */ +void +wp_spa_pod_builder_add_float (WpSpaPodBuilder *self, float value) +{ + spa_pod_builder_float (&self->builder, value); +} + +/** + * wp_spa_pod_builder_add_double: + * @self: the spa pod builder object + * @value: the double value + * + * Adds a double value into the builder + */ +void +wp_spa_pod_builder_add_double (WpSpaPodBuilder *self, double value) +{ + spa_pod_builder_double (&self->builder, value); +} + +/** + * wp_spa_pod_builder_add_string: + * @self: the spa pod builder object + * @value: the string value + * + * Adds a string value into the builder + */ +void +wp_spa_pod_builder_add_string (WpSpaPodBuilder *self, const char *value) +{ + spa_pod_builder_string (&self->builder, value); +} + +/** + * wp_spa_pod_builder_add_bytes: + * @self: the spa pod builder object + * @value: the bytes value + * @len: the length of the bytes value + * + * Adds a bytes value with its length into the builder + */ +void +wp_spa_pod_builder_add_bytes (WpSpaPodBuilder *self, gconstpointer value, + guint32 len) +{ + spa_pod_builder_bytes (&self->builder, value, len); +} + +/** + * wp_spa_pod_builder_add_pointer: + * @self: the spa pod builder object + * @type_name: the type name that the pointer points to + * @value: the pointer vaue + * + * Adds a pointer value with its type name into the builder + */ +void +wp_spa_pod_builder_add_pointer (WpSpaPodBuilder *self, const char *type_name, + gconstpointer value) +{ + guint32 type = 0; + if (!wp_spa_type_get_by_nick (WP_SPA_TYPE_TABLE_BASIC, type_name, &type, + NULL, NULL)) + g_return_if_reached (); + spa_pod_builder_pointer (&self->builder, type, value); +} + +/** + * wp_spa_pod_builder_add_fd: + * @self: the spa pod builder object + * @value: the Fd value + * + * Adds a Fd value into the builder + */ +void +wp_spa_pod_builder_add_fd (WpSpaPodBuilder *self, gint64 value) +{ + spa_pod_builder_fd (&self->builder, value); +} + +/** + * wp_spa_pod_builder_add_rectangle: + * @self: the spa pod builder object + * @width: the width value of the rectangle + * @height: the height value of the rectangle + * + * Adds the width and height values of a rectangle into the builder + */ +void +wp_spa_pod_builder_add_rectangle (WpSpaPodBuilder *self, guint32 width, + guint32 height) +{ + spa_pod_builder_rectangle (&self->builder, width, height); +} + +/** + * wp_spa_pod_builder_add_fraction: + * @self: the spa pod builder object + * @num: the numerator value of the fraction + * @denom: the denominator value of the fraction + * + * Adds the numerator and denominator values of a fraction into the builder + */ +void +wp_spa_pod_builder_add_fraction (WpSpaPodBuilder *self, guint32 num, + guint32 denom) +{ + spa_pod_builder_fraction (&self->builder, num, denom); +} + +/** + * wp_spa_pod_builder_add_pod: + * @self: the spa pod builder object + * @pod: the pod value + * + * Adds a pod value into the builder + */ +void +wp_spa_pod_builder_add_pod (WpSpaPodBuilder *self, const WpSpaPod *pod) +{ + spa_pod_builder_primitive (&self->builder, pod->pod); +} + +/** + * wp_spa_pod_builder_add_property: + * @self: the spa pod builder object + * @key: the name of the property + * + * Adds a property into the builder + */ +void +wp_spa_pod_builder_add_property (WpSpaPodBuilder *self, const char *key) +{ + guint32 id = 0; + if (!wp_spa_type_get_by_nick (self->prop_table, key, &id, NULL, NULL)) + g_return_if_reached (); + spa_pod_builder_prop (&self->builder, id, 0); +} + +/** + * wp_spa_pod_builder_add_control: + * @self: the spa pod builder object + * @offset: the offset of the control + * @type_name: the type name of the control + * + * Adds a control into the builder + */ +void +wp_spa_pod_builder_add_control (WpSpaPodBuilder *self, guint32 offset, + const char *type_name) +{ + guint type = 0; + if (!wp_spa_type_get_by_nick (WP_SPA_TYPE_TABLE_CONTROL, type_name, &type, + NULL, NULL)) + g_return_if_reached (); + spa_pod_builder_control (&self->builder, offset, type); +} + +/** + * wp_spa_pod_builder_add: + * @self: the spa pod builder object + * @...: a list of additional values, followed by %NULL + * + * Adds a list of values into the builder + */ +void +wp_spa_pod_builder_add (WpSpaPodBuilder *self, ...) +{ + va_list args; + va_start (args, self); + wp_spa_pod_builder_add_valist (self, args); + va_end (args); +} + +/** + * wp_spa_pod_builder_add_valist: + * @self: the spa pod builder object + * @args: the variable arguments passed to wp_spa_pod_builder_add() + * + * Adds a list of values into the builder + */ +void +wp_spa_pod_builder_add_valist (WpSpaPodBuilder *self, va_list args) +{ + do { + const char *format; + int n_values = 1; + struct spa_pod_frame f; + gboolean choice; + + switch (self->type) { + case SPA_TYPE_Object: + { + guint32 key = 0; + const char *key_name = va_arg(args, const char *); + if (!key_name) + return; + if (!wp_spa_type_get_by_nick (self->prop_table, key_name, &key, NULL, + NULL)) + g_return_if_reached (); + if (key == 0) + return; + + spa_pod_builder_prop (&self->builder, key, 0); + break; + } + case SPA_TYPE_Sequence: + { + guint32 offset = va_arg(args, uint32_t); + guint32 type = 0; + if (offset == 0) + return; + const char *control_name = va_arg(args, const char *); + if (!control_name) + return; + if (!wp_spa_type_get_by_nick (WP_SPA_TYPE_TABLE_CONTROL, control_name, + &type, NULL, NULL)) + g_return_if_reached (); + if (type == 0) + return; + + spa_pod_builder_control (&self->builder, offset, type); + } + default: + break; + } + + if ((format = va_arg(args, const char *)) == NULL) + break; + + choice = *format == '?'; + if (choice) { + uint32_t type = spa_choice_from_id (*++format); + if (*format != '\0') + format++; + spa_pod_builder_push_choice (&self->builder, &f, type, 0); + n_values = va_arg(args, int); + } + while (n_values-- > 0) { + switch (*format) { + case 'P': /* Pod */ + case 'V': /* Choice */ + case 'O': /* Object */ + case 'T': /* Struct */ + spa_pod_builder_primitive (&self->builder, va_arg(args, WpSpaPod *)->pod); + break; + default: + SPA_POD_BUILDER_COLLECT(&self->builder, *format, args); + break; + } + } + + if (choice) + spa_pod_builder_pop (&self->builder, &f); + + } while (TRUE); +} + +/** + * wp_spa_pod_builder_end: + * @self: the spa pod builder object + * + * Ends the builder process and returns the constructed spa pod object + * + * Returns: (transfer full): the constructed spa pod object + */ +WpSpaPod * +wp_spa_pod_builder_end (WpSpaPodBuilder *self) +{ + WpSpaPod *ret = NULL; + + /* Construct the pod */ + ret = g_slice_new0 (WpSpaPod); + g_ref_count_init (&ret->ref); + ret->type = WP_SPA_POD_REGULAR; + ret->pod = spa_pod_builder_pop (&self->builder, &self->frame); + ret->builder = wp_spa_pod_builder_ref (self); + + /* Also copy the property table if it is an object type */ + if (ret->builder->type == SPA_TYPE_Object) + ret->static_pod.data_property.table = ret->builder->prop_table; + + return ret; +} + +/** + * wp_spa_pod_parser_ref: + * @self: a spa pod sparser object + * + * Returns: (transfer full): @self with an additional reference count on it + */ +WpSpaPodParser * +wp_spa_pod_parser_ref (WpSpaPodParser *self) +{ + return (WpSpaPodParser *) g_rc_box_acquire ((gpointer) self); +} + +static void +wp_spa_pod_parser_free (WpSpaPodParser *self) +{ + g_clear_pointer (&self->pod, wp_spa_pod_unref); +} + +/** + * wp_spa_pod_parser_unref: + * @self: (transfer full): a spa pod parser object + * + * Decreases the reference count on @self and frees it when the ref count + * reaches zero. + */ +void +wp_spa_pod_parser_unref (WpSpaPodParser *self) +{ + g_rc_box_release_full (self, (GDestroyNotify) wp_spa_pod_parser_free); +} + +static WpSpaPodParser * +wp_spa_pod_parser_new (WpSpaPod *pod, guint32 type) +{ + WpSpaPodParser *self = g_rc_box_new0 (WpSpaPodParser); + self->type = type; + self->pod = wp_spa_pod_ref (pod); + spa_pod_parser_pod (&self->parser, self->pod->pod); + return self; +} + +/** + * wp_spa_pod_parser_new: + * @pod: the object spa pod to parse + * @type_name: the type name of the object type + * @id_name: the Id name of the object + * + * Creates an object spa pod parser + * + * Returns: (transfer full): The new spa pod parser + */ +WpSpaPodParser * +wp_spa_pod_parser_new_object (WpSpaPod *pod, const char *type_name, + const char **id_name) +{ + WpSpaPodParser *self = NULL; + guint32 type = 0; + guint32 id = 0; + + g_return_val_if_fail (wp_spa_pod_is_object (pod), NULL); + + self = wp_spa_pod_parser_new (pod, SPA_TYPE_Object); + if (!wp_spa_type_get_by_nick (WP_SPA_TYPE_TABLE_BASIC, type_name, &type, + NULL, &self->prop_table)) + g_return_val_if_reached (NULL); + spa_pod_parser_push_object (&self->parser, &self->frame, type, &id); + if (!wp_spa_type_get_by_id (WP_SPA_TYPE_TABLE_PARAM, id, NULL, id_name, NULL)) + g_return_val_if_reached (NULL); + return self; +} + +/** + * wp_spa_pod_parser_new_struct: + * @pod: the struct spa pod to parse + * + * Creates an struct spa pod parser + * + * Returns: (transfer full): The new spa pod parser + */ +WpSpaPodParser * +wp_spa_pod_parser_new_struct (WpSpaPod *pod) +{ + WpSpaPodParser *self = NULL; + + g_return_val_if_fail (wp_spa_pod_is_struct (pod), NULL); + + self = wp_spa_pod_parser_new (pod, SPA_TYPE_Struct); + spa_pod_parser_push_struct (&self->parser, &self->frame); + return self; +} + +/** + * wp_spa_pod_parser_get_boolean: + * @self: the spa pod parser object + * @value: (out): the boolean value + * + * Gets the boolean value from a spa pod parser + * + * Returns: TRUE if the value was obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_parser_get_boolean (WpSpaPodParser *self, gboolean *value) +{ + g_return_val_if_fail (value, FALSE); + bool v = FALSE; + gboolean res = spa_pod_parser_get_bool (&self->parser, &v) >= 0; + *value = v ? TRUE : FALSE; + return res; +} + +/** + * wp_spa_pod_parser_get_id: + * @self: the spa pod parser object + * @value: (out): the Id value + * + * Gets the Id value from a spa pod parser object + * + * Returns: TRUE if the value was obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_parser_get_id (WpSpaPodParser *self, guint32 *value) +{ + g_return_val_if_fail (value, FALSE); + return spa_pod_parser_get_id (&self->parser, value) >= 0; +} + +/** + * wp_spa_pod_parser_get_int: + * @self: the spa pod parser object + * @value: (out): the int value + * + * Gets the int value from a spa pod parser object + * + * Returns: TRUE if the value was obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_parser_get_int (WpSpaPodParser *self, gint *value) +{ + g_return_val_if_fail (value, FALSE); + return spa_pod_parser_get_int (&self->parser, value) >= 0; +} + +/** + * wp_spa_pod_parser_get_long: + * @self: the spa pod parser object + * @value: (out): the long value + * + * Gets the long value from a spa pod parser object + * + * Returns: TRUE if the value was obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_parser_get_long (WpSpaPodParser *self, glong *value) +{ + g_return_val_if_fail (value, FALSE); + return spa_pod_parser_get_long (&self->parser, value) >= 0; +} + +/** + * wp_spa_pod_parser_get_float: + * @self: the spa pod parser object + * @value: (out): the float value + * + * Gets the float value from a spa pod parser object + * + * Returns: TRUE if the value was obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_parser_get_float (WpSpaPodParser *self, float *value) +{ + g_return_val_if_fail (value, FALSE); + return spa_pod_parser_get_float (&self->parser, value) >= 0; +} + +/** + * wp_spa_pod_parser_get_double: + * @self: the spa pod parser object + * @value: (out): the double value + * + * Gets the double value from a spa pod parser object + * + * Returns: TRUE if the value was obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_parser_get_double (WpSpaPodParser *self, double *value) +{ + g_return_val_if_fail (value, FALSE); + return spa_pod_parser_get_double (&self->parser, value) >= 0; +} + +/** + * wp_spa_pod_parser_get_string: + * @self: the spa pod parser object + * @value: (out): the string value + * + * Gets the string value from a spa pod parser object + * + * Returns: TRUE if the value was obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_parser_get_string (WpSpaPodParser *self, const char **value) +{ + g_return_val_if_fail (value, FALSE); + return spa_pod_parser_get_string (&self->parser, value) >= 0; +} + +/** + * wp_spa_pod_parser_get_bytes: + * @self: the spa pod parser object + * @value: (out): the bytes value + * @len: (out): the length of the bytes value + * + * Gets the bytes value and its length from a spa pod parser object + * + * Returns: TRUE if the value was obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_parser_get_bytes (WpSpaPodParser *self, gconstpointer *value, + guint32 *len) +{ + return spa_pod_parser_get_bytes (&self->parser, value, len) >= 0; +} + +/** + * wp_spa_pod_parser_get_pointer: + * @self: the spa pod parser object + * @type_name: (out): the type name of the pointer value + * @value: (out): the pointer value + * + * Gets the pointer value and its type name from a spa pod parser object + * + * Returns: TRUE if the value was obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_parser_get_pointer (WpSpaPodParser *self, const char **type_name, + gconstpointer *value) +{ + guint32 type = 0; + gboolean res; + + g_return_val_if_fail (value, FALSE); + + res = spa_pod_parser_get_pointer (&self->parser, &type, value) >= 0; + + if (!wp_spa_type_get_by_id (WP_SPA_TYPE_TABLE_BASIC, type, NULL, type_name, + NULL)) + g_return_val_if_reached (FALSE); + return res; +} + +/** + * wp_spa_pod_parser_get_fd: + * @self: the spa pod parser object + * @value: (out): the Fd value + * + * Gets the Fd value from a spa pod parser object + * + * Returns: TRUE if the value was obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_parser_get_fd (WpSpaPodParser *self, gint64 *value) +{ + g_return_val_if_fail (value, FALSE); + return spa_pod_parser_get_fd (&self->parser, value) >= 0; +} + +/** + * wp_spa_pod_parser_get_rectangle: + * @self: the spa pod parser object + * @width: (out): the rectangle's width value + * @height: (out): the rectangle's height value + * + * Gets the rectangle's width and height value from a spa pod parser object + * + * Returns: TRUE if the value was obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_parser_get_rectangle (WpSpaPodParser *self, guint32 *width, + guint32 *height) +{ + struct spa_rectangle r = { 0, }; + gboolean res = spa_pod_parser_get_rectangle (&self->parser, &r) >= 0; + if (width) + *width = r.width; + if (height) + *height = r.height; + return res; +} + +/** + * wp_spa_pod_parser_get_fraction: + * @self: the spa pod parser object + * @num: (out): the fractions's numerator value + * @denom: (out): the fractions's denominator value + * + * Gets the fractions's numerator and denominator value from a spa pod parser + * object + * + * Returns: TRUE if the value was obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_parser_get_fraction (WpSpaPodParser *self, guint32 *num, + guint32 *denom) +{ + struct spa_fraction f = { 0, }; + gboolean res = spa_pod_parser_get_fraction (&self->parser, &f) >= 0; + if (num) + *num = f.num; + if (denom) + *denom = f.denom; + return res; +} + +/** + * wp_spa_pod_parser_get_pod: + * @self: the spa pod parser object + * + * Gets the spa pod value from a spa pod parser object + * + * Returns: (transfer full): The spa pod value or NULL if it could not be + * obtained + */ +WpSpaPod * +wp_spa_pod_parser_get_pod (WpSpaPodParser *self) +{ + struct spa_pod *p = NULL; + gboolean res = spa_pod_parser_get_pod (&self->parser, &p) >= 0; + if (!res || !p) + return NULL; + + return wp_spa_pod_new_regular_wrap (p); +} + +/** + * wp_spa_pod_parser_get: + * @self: the spa pod parser object + * @...: (out): a list of values to get, followed by %NULL + * + * Gets a list of values from a spa pod parser object + * + * Returns: TRUE if the values were obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_parser_get (WpSpaPodParser *self, ...) +{ + gboolean res; + va_list args; + + va_start (args, self); + res = wp_spa_pod_parser_get_valist (self, args); + va_end (args); + + return res; +} + +/** + * wp_spa_pod_parser_get_valist: + * @self: the spa pod parser object + * @args: the variable arguments passed to wp_spa_pod_parser_get() + * + * This is the `va_list` version of wp_spa_pod_parser_get() + * + * Returns: TRUE if the values were obtained, FALSE otherwise + */ +gboolean +wp_spa_pod_parser_get_valist (WpSpaPodParser *self, va_list args) +{ + const struct spa_pod_prop *prop = NULL; + + do { + bool optional; + const struct spa_pod *pod = NULL; + const char *format; + + if (self->type == SPA_TYPE_Object) { + guint32 key = 0; + const struct spa_pod_object *object; + const char *key_name = va_arg(args, const char *); + if (!key_name) + break; + if (!wp_spa_type_get_by_nick (self->prop_table, key_name, &key, NULL, NULL)) + g_return_val_if_reached (FALSE); + if (key == 0) + break; + + object = (const struct spa_pod_object *)spa_pod_parser_frame ( + &self->parser, &self->frame); + prop = spa_pod_object_find_prop (object, prop, key); + pod = prop ? &prop->value : NULL; + } + + if ((format = va_arg(args, char *)) == NULL) + break; + + if (self->type == SPA_TYPE_Struct) + pod = spa_pod_parser_next (&self->parser); + + if ((optional = (*format == '?'))) + format++; + + if (!spa_pod_parser_can_collect (pod, *format)) { + if (!optional) + return FALSE; + + SPA_POD_PARSER_SKIP (*format, args); + } else { + if (pod->type == SPA_TYPE_Choice && *format != 'V' && + SPA_POD_CHOICE_TYPE(pod) == SPA_CHOICE_None) + pod = SPA_POD_CHOICE_CHILD(pod); + + switch (*format) { + case 'P': /* Pod */ + case 'V': /* Choice */ + case 'O': /* Object */ + case 'T': /* Struct */ + *va_arg(args, WpSpaPod**) = wp_spa_pod_new_regular_wrap_copy (pod); + break; + default: + SPA_POD_PARSER_COLLECT (pod, *format, args); + break; + } + } + } while (TRUE); + + return TRUE; +} + +/** + * wp_spa_pod_parser_end: + * @self: the spa pod parser object + * + * Ends the parser process + */ +void +wp_spa_pod_parser_end (WpSpaPodParser *self) +{ + spa_pod_parser_pop (&self->parser, &self->frame); +} + + +struct _WpSpaPodIterator +{ + WpSpaPod *pod; + union { + gpointer value; /* Array and Choice */ + struct spa_pod *pod; /* Struct */ + struct spa_pod_prop *prop; /* Object */ + struct spa_pod_control *control; /* Sequence */ + } curr; +}; +typedef struct _WpSpaPodIterator WpSpaPodIterator; + +static gboolean +wp_spa_pod_iterator_next_choice (WpSpaPodIterator *self, GValue *item) +{ + struct spa_pod_choice *pod_choice = (struct spa_pod_choice *) self->pod->pod; + + if (!self->curr.value) + self->curr.value = SPA_MEMBER (&pod_choice->body, sizeof(struct spa_pod_choice_body), void); + else + self->curr.value = SPA_MEMBER (self->curr.value, pod_choice->body.child.size, void); + + if (self->curr.value >= SPA_MEMBER(&pod_choice->body, SPA_POD_BODY_SIZE (pod_choice), void)) + return FALSE; + + if (item) { + g_value_init (item, G_TYPE_POINTER); + g_value_set_pointer (item, self->curr.value); + } + return TRUE; +} + +static gboolean +wp_spa_pod_iterator_next_array (WpSpaPodIterator *self, GValue *item) +{ + struct spa_pod_array *pod_arr = (struct spa_pod_array *) self->pod->pod; + + if (!self->curr.value) + self->curr.value = SPA_MEMBER (&pod_arr->body, sizeof(struct spa_pod_array_body), void); + else + self->curr.value = SPA_MEMBER (self->curr.value, pod_arr->body.child.size, void); + + if (self->curr.value >= SPA_MEMBER(&pod_arr->body, SPA_POD_BODY_SIZE (pod_arr), void)) + return FALSE; + + if (item) { + g_value_init (item, G_TYPE_POINTER); + g_value_set_pointer (item, self->curr.value); + } + return TRUE; +} + +static gboolean +wp_spa_pod_iterator_next_object (WpSpaPodIterator *self, GValue *item) +{ + struct spa_pod_object *pod_obj = (struct spa_pod_object *) self->pod->pod; + g_autoptr (WpSpaPod) pod = NULL; + + if (!self->curr.prop) + self->curr.prop = spa_pod_prop_first (&pod_obj->body); + else + self->curr.prop = spa_pod_prop_next (self->curr.prop); + + if (!spa_pod_prop_is_inside (&pod_obj->body, SPA_POD_BODY_SIZE (pod_obj), + self->curr.prop)) + return FALSE; + + if (item) { + g_value_init (item, WP_TYPE_SPA_POD); + g_value_take_boxed (item, wp_spa_pod_new_property_wrap ( + self->pod->static_pod.data_property.table, self->curr.prop->key, + self->curr.prop->flags, &self->curr.prop->value)); + } + return TRUE; +} + +static gboolean +wp_spa_pod_iterator_next_struct (WpSpaPodIterator *self, GValue *item) +{ + if (!self->curr.pod) + self->curr.pod = SPA_POD_BODY (self->pod->pod); + else + self->curr.pod = spa_pod_next (self->curr.pod); + + if (!spa_pod_is_inside (SPA_POD_BODY (self->pod->pod), + SPA_POD_BODY_SIZE (self->pod->pod), self->curr.pod)) + return FALSE; + + if (item) { + g_value_init (item, WP_TYPE_SPA_POD); + g_value_take_boxed (item, wp_spa_pod_new_regular_wrap (self->curr.pod)); + } + return TRUE; +} + +static gboolean +wp_spa_pod_iterator_next_sequence (WpSpaPodIterator *self, GValue *item) +{ + struct spa_pod_sequence *pod_seq = (struct spa_pod_sequence *) self->pod->pod; + g_autoptr (WpSpaPod) pod = NULL; + + if (!self->curr.control) + self->curr.control = spa_pod_control_first (&pod_seq->body); + else + self->curr.control = spa_pod_control_next (self->curr.control); + + if (!spa_pod_control_is_inside (&pod_seq->body, SPA_POD_BODY_SIZE (pod_seq), + self->curr.control)) + return FALSE; + + if (item) { + g_value_init (item, WP_TYPE_SPA_POD); + g_value_take_boxed (item, wp_spa_pod_new_control_wrap ( + self->curr.control->offset, self->curr.control->type, + &self->curr.control->value)); + } + return TRUE; +} + + +static void +wp_spa_pod_iterator_reset (WpIterator *iterator) +{ + WpSpaPodIterator *self = wp_iterator_get_user_data (iterator); + self->curr.value = NULL; + self->curr.pod = NULL; + self->curr.prop = NULL; + self->curr.control = NULL; +} + +static gboolean +wp_spa_pod_iterator_next (WpIterator *iterator, GValue *item) +{ + WpSpaPodIterator *self = wp_iterator_get_user_data (iterator); + + switch (self->pod->pod->type) { + case SPA_TYPE_Choice: + return wp_spa_pod_iterator_next_choice (self, item); + case SPA_TYPE_Array: + return wp_spa_pod_iterator_next_array (self, item); + case SPA_TYPE_Object: + return wp_spa_pod_iterator_next_object (self, item); + case SPA_TYPE_Struct: + return wp_spa_pod_iterator_next_struct (self, item); + case SPA_TYPE_Sequence: + return wp_spa_pod_iterator_next_sequence (self, item); + default: + break; + } + + return FALSE; +} + +static gboolean +wp_spa_pod_iterator_fold (WpIterator *iterator, WpIteratorFoldFunc func, + GValue *ret, gpointer data) +{ + WpSpaPodIterator *self = wp_iterator_get_user_data (iterator); + + wp_iterator_reset (iterator); + + switch (self->pod->pod->type) { + case SPA_TYPE_Choice: + { + struct spa_pod_choice *pod_choice = (struct spa_pod_choice *) self->pod->pod; + gpointer p = NULL; + SPA_POD_CHOICE_FOREACH (pod_choice, p) { + GValue v = G_VALUE_INIT; + g_value_init (&v, G_TYPE_POINTER); + g_value_set_pointer (&v, p); + const gboolean res = func (&v, ret, data); + g_value_unset (&v); + if (!res) + return FALSE; + } + break; + } + case SPA_TYPE_Array: + { + struct spa_pod_array *pod_arr = (struct spa_pod_array *) self->pod->pod; + gpointer p = NULL; + SPA_POD_ARRAY_FOREACH (pod_arr, p) { + GValue v = G_VALUE_INIT; + g_value_init (&v, G_TYPE_POINTER); + g_value_set_pointer (&v, p); + const gboolean res = func (&v, ret, data); + g_value_unset (&v); + if (!res) + return FALSE; + } + break; + } + case SPA_TYPE_Object: + { + struct spa_pod_object *pod_obj = (struct spa_pod_object *) self->pod->pod; + struct spa_pod_prop *p = NULL; + SPA_POD_OBJECT_FOREACH (pod_obj, p) { + GValue v = G_VALUE_INIT; + g_value_init (&v, WP_TYPE_SPA_POD); + g_value_take_boxed (&v, wp_spa_pod_new_property_wrap ( + self->pod->static_pod.data_property.table, p->key, p->flags, + &p->value)); + const gboolean res = func (&v, ret, data); + g_value_unset (&v); + if (!res) + return FALSE; + } + break; + } + case SPA_TYPE_Struct: + { + struct spa_pod *p = NULL; + SPA_POD_STRUCT_FOREACH (self->pod->pod, p) { + GValue v = G_VALUE_INIT; + g_value_init (&v, WP_TYPE_SPA_POD); + g_value_take_boxed (&v, wp_spa_pod_new_regular_wrap (p)); + const gboolean res = func (&v, ret, data); + g_value_unset (&v); + if (!res) + return FALSE; + } + break; + } + case SPA_TYPE_Sequence: + { + struct spa_pod_sequence *pod_seq = (struct spa_pod_sequence *) self->pod->pod; + struct spa_pod_control *p = NULL; + SPA_POD_SEQUENCE_FOREACH (pod_seq, p) { + GValue v = G_VALUE_INIT; + g_value_init (&v, WP_TYPE_SPA_POD); + g_value_take_boxed (&v, wp_spa_pod_new_control_wrap (p->offset, p->type, + &p->value)); + const gboolean res = func (&v, ret, data); + g_value_unset (&v); + if (!res) + return FALSE; + } + break; + } + default: + return FALSE; + } + + return TRUE; +} + +static void +wp_spa_pod_iterator_finalize (WpIterator *iterator) +{ + WpSpaPodIterator *self = wp_iterator_get_user_data (iterator); + g_clear_pointer (&self->pod, wp_spa_pod_unref); +} + +/** + * wp_spa_pod_iterator_new: + * @pod: a spa pod object + * + * Creates a new iterator for a spa pod object + * + * Returns: (transfer full): the new spa pod iterator + */ +WpIterator * +wp_spa_pod_iterator_new (WpSpaPod *pod) +{ + static const WpIteratorMethods methods = { + .reset = wp_spa_pod_iterator_reset, + .next = wp_spa_pod_iterator_next, + .fold = wp_spa_pod_iterator_fold, + .foreach = NULL, + .finalize = wp_spa_pod_iterator_finalize + }; + WpIterator *it = wp_iterator_new (&methods, sizeof (WpSpaPodIterator)); + WpSpaPodIterator *self = wp_iterator_get_user_data (it); + + self->pod = wp_spa_pod_ref (pod); + + return it; +} diff --git a/lib/wp/spa-pod.h b/lib/wp/spa-pod.h new file mode 100644 index 00000000..6b6f0f43 --- /dev/null +++ b/lib/wp/spa-pod.h @@ -0,0 +1,459 @@ +/* WirePlumber + * + * Copyright © 2020 Collabora Ltd. + * @author Julian Bouzas <julian.bouzas@collabora.com> + * + * SPDX-License-Identifier: MIT + */ + +#ifndef __WIREPLUMBER_SPA_POD_H__ +#define __WIREPLUMBER_SPA_POD_H__ + +#include <gio/gio.h> + +#include "defs.h" +#include "iterator.h" + +G_BEGIN_DECLS + +/** + * WP_TYPE_SPA_POD: + * + * The #WpSpaPod #GType + */ +#define WP_TYPE_SPA_POD (wp_spa_pod_get_type ()) +WP_API +GType wp_spa_pod_get_type (void); + +typedef struct _WpSpaPod WpSpaPod; + +WP_API +WpSpaPod *wp_spa_pod_ref (WpSpaPod *self); + +WP_API +void wp_spa_pod_unref (WpSpaPod *self); + +WP_API +const char *wp_spa_pod_get_type_name (const WpSpaPod *self); + +WP_API +WpSpaPod *wp_spa_pod_copy (const WpSpaPod *other); + +WP_API +gboolean wp_spa_pod_is_unique_owner ( WpSpaPod *self); + +WP_API +WpSpaPod *wp_spa_pod_ensure_unique_owner (WpSpaPod *self); + +WP_API +WpSpaPod *wp_spa_pod_new_none (void); + +WP_API +WpSpaPod *wp_spa_pod_new_boolean (gboolean value); + +WP_API +WpSpaPod *wp_spa_pod_new_id (guint32 value); + +WP_API +WpSpaPod *wp_spa_pod_new_int (gint value); + +WP_API +WpSpaPod *wp_spa_pod_new_long (glong value); + +WP_API +WpSpaPod *wp_spa_pod_new_float (float value); + +WP_API +WpSpaPod *wp_spa_pod_new_double (double value); + +WP_API +WpSpaPod *wp_spa_pod_new_string (const char *value); + +WP_API +WpSpaPod *wp_spa_pod_new_bytes (gconstpointer value, guint32 len); + +WP_API +WpSpaPod *wp_spa_pod_new_pointer (const char *type_name, gconstpointer value); + +WP_API +WpSpaPod *wp_spa_pod_new_fd (gint64 value); + +WP_API +WpSpaPod *wp_spa_pod_new_rectangle (guint32 width, guint32 height); + +WP_API +WpSpaPod *wp_spa_pod_new_fraction (guint32 num, guint32 denom); + +WP_API +WpSpaPod *wp_spa_pod_new_choice (const char *type_name, ...) + G_GNUC_NULL_TERMINATED; + +WP_API +WpSpaPod *wp_spa_pod_new_choice_valist (const char *type_name, va_list args); + +WP_API +WpSpaPod *wp_spa_pod_new_object (const char *type_name, const char *id_name, + ...) G_GNUC_NULL_TERMINATED; + +WP_API +WpSpaPod *wp_spa_pod_new_object_valist (const char *type_name, + const char *id_name, va_list args); + +WP_API +WpSpaPod *wp_spa_pod_new_sequence (guint unit, ...) G_GNUC_NULL_TERMINATED; + +WP_API +WpSpaPod *wp_spa_pod_new_sequence_valist (guint unit, va_list args); + +WP_API +gboolean wp_spa_pod_is_none (const WpSpaPod *self); + +WP_API +gboolean wp_spa_pod_is_boolean (const WpSpaPod *self); + +WP_API +gboolean wp_spa_pod_is_id (const WpSpaPod *self); + +WP_API +gboolean wp_spa_pod_is_int (const WpSpaPod *self); + +WP_API +gboolean wp_spa_pod_is_long (const WpSpaPod *self); + +WP_API +gboolean wp_spa_pod_is_float (const WpSpaPod *self); + +WP_API +gboolean wp_spa_pod_is_double (const WpSpaPod *self); + +WP_API +gboolean wp_spa_pod_is_string (const WpSpaPod *self); + +WP_API +gboolean wp_spa_pod_is_bytes (const WpSpaPod *self); + +WP_API +gboolean wp_spa_pod_is_pointer (const WpSpaPod *self); + +WP_API +gboolean wp_spa_pod_is_fd (const WpSpaPod *self); + +WP_API +gboolean wp_spa_pod_is_rectangle (const WpSpaPod *self); + +WP_API +gboolean wp_spa_pod_is_fraction (const WpSpaPod *self); + +WP_API +gboolean wp_spa_pod_is_array (const WpSpaPod *self); + +WP_API +gboolean wp_spa_pod_is_choice (const WpSpaPod *self); + +WP_API +gboolean wp_spa_pod_is_object (const WpSpaPod *self); + +WP_API +gboolean wp_spa_pod_is_struct (const WpSpaPod *self); + +WP_API +gboolean wp_spa_pod_is_sequence (const WpSpaPod *self); + +WP_API +gboolean wp_spa_pod_is_property (const WpSpaPod *self); + +WP_API +gboolean wp_spa_pod_is_control (const WpSpaPod *self); + +WP_API +gboolean wp_spa_pod_get_boolean (const WpSpaPod *self, gboolean *value); + +WP_API +gboolean wp_spa_pod_get_id (const WpSpaPod *self, guint32 *value); + +WP_API +gboolean wp_spa_pod_get_int (const WpSpaPod *self, gint *value); + +WP_API +gboolean wp_spa_pod_get_long (const WpSpaPod *self, glong *value); + +WP_API +gboolean wp_spa_pod_get_float (const WpSpaPod *self, float *value); + +WP_API +gboolean wp_spa_pod_get_double (const WpSpaPod *self, double *value); + +WP_API +gboolean wp_spa_pod_get_string (const WpSpaPod *self, const char **value); + +WP_API +gboolean wp_spa_pod_get_bytes (const WpSpaPod *self, gconstpointer *value, + guint32 *len); + +WP_API +gboolean wp_spa_pod_get_pointer (const WpSpaPod *self, const char **type_name, + gconstpointer *value); + +WP_API +gboolean wp_spa_pod_get_fd (const WpSpaPod *self, gint64 *value); + +WP_API +gboolean wp_spa_pod_get_rectangle (const WpSpaPod *self, guint32 *width, + guint32 *height); + +WP_API +gboolean wp_spa_pod_get_fraction (const WpSpaPod *self, guint32 *num, + guint32 *denom); + +WP_API +gboolean wp_spa_pod_set_boolean (WpSpaPod *self, gboolean value); + +WP_API +gboolean wp_spa_pod_set_id (WpSpaPod *self, guint32 value); + +WP_API +gboolean wp_spa_pod_set_int (WpSpaPod *self, gint value); + +WP_API +gboolean wp_spa_pod_set_long (WpSpaPod *self, glong value); + +WP_API +gboolean wp_spa_pod_set_float (WpSpaPod *self, float value); + +WP_API +gboolean wp_spa_pod_set_double (WpSpaPod *self, double value); + +WP_API +gboolean wp_spa_pod_set_pointer (WpSpaPod *self, const char *type_name, + gconstpointer value); + +WP_API +gboolean wp_spa_pod_set_fd (WpSpaPod *self, gint64 value); + +WP_API +gboolean wp_spa_pod_set_rectangle (WpSpaPod *self, guint32 width, + guint32 height); + +WP_API +gboolean wp_spa_pod_set_fraction (WpSpaPod *self, guint32 num, guint32 denom); + +WP_API +gboolean wp_spa_pod_set_pod (WpSpaPod *self, const WpSpaPod *pod); + +WP_API +gboolean wp_spa_pod_get_object (WpSpaPod *self, const char *type_name, + const char **id_name, ...) G_GNUC_NULL_TERMINATED; + +WP_API +gboolean wp_spa_pod_get_object_valist (WpSpaPod *self, + const char *type_name, const char **id_name, va_list args); + +WP_API +gboolean wp_spa_pod_get_struct (WpSpaPod *self, ...) + G_GNUC_NULL_TERMINATED; + +WP_API +gboolean wp_spa_pod_get_struct_valist (WpSpaPod *self, va_list args); + +WP_API +gboolean wp_spa_pod_get_property (const WpSpaPod *self, const char **key, + WpSpaPod **value); + +WP_API +gboolean wp_spa_pod_get_control (const WpSpaPod *self, guint32 *offset, + const char **type_name, WpSpaPod **value); + +WP_API +WpSpaPod *wp_spa_pod_get_choice_child (WpSpaPod *self); + +WP_API +WpSpaPod *wp_spa_pod_get_array_child (WpSpaPod *self); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (WpSpaPod, wp_spa_pod_unref) + + +/** + * WP_TYPE_SPA_POD_BUILDER: + * + * The #WpSpaPodBuilder #GType + */ +#define WP_TYPE_SPA_POD_BUILDER (wp_spa_pod_builder_get_type ()) +WP_API +GType wp_spa_pod_builder_get_type (void); + +typedef struct _WpSpaPodBuilder WpSpaPodBuilder; + +WP_API +WpSpaPodBuilder *wp_spa_pod_builder_ref (WpSpaPodBuilder *self); + +WP_API +void wp_spa_pod_builder_unref (WpSpaPodBuilder *self); + +WP_API +WpSpaPodBuilder *wp_spa_pod_builder_new_array (void); + +WP_API +WpSpaPodBuilder *wp_spa_pod_builder_new_choice (const char *type_name); + +WP_API +WpSpaPodBuilder *wp_spa_pod_builder_new_object (const char *type_name, + const char *id_name); + +WP_API +WpSpaPodBuilder *wp_spa_pod_builder_new_struct (void); + +WP_API +WpSpaPodBuilder *wp_spa_pod_builder_new_sequence (guint unit); + +WP_API +void wp_spa_pod_builder_add_none (WpSpaPodBuilder *self); + +WP_API +void wp_spa_pod_builder_add_boolean (WpSpaPodBuilder *self, gboolean value); + +WP_API +void wp_spa_pod_builder_add_id (WpSpaPodBuilder *self, guint32 value); + +WP_API +void wp_spa_pod_builder_add_int (WpSpaPodBuilder *self, gint value); + +WP_API +void wp_spa_pod_builder_add_long (WpSpaPodBuilder *self, glong value); + +WP_API +void wp_spa_pod_builder_add_float (WpSpaPodBuilder *self, float value); + +WP_API +void wp_spa_pod_builder_add_double (WpSpaPodBuilder *self, double value); + +WP_API +void wp_spa_pod_builder_add_string (WpSpaPodBuilder *self, const char *value); + +WP_API +void wp_spa_pod_builder_add_bytes (WpSpaPodBuilder *self, gconstpointer value, + guint32 len); + +WP_API +void wp_spa_pod_builder_add_pointer (WpSpaPodBuilder *self, + const char *type_name, gconstpointer value); + +WP_API +void wp_spa_pod_builder_add_fd (WpSpaPodBuilder *self, gint64 value); + +WP_API +void wp_spa_pod_builder_add_rectangle (WpSpaPodBuilder *self, guint32 width, + guint32 height); + +WP_API +void wp_spa_pod_builder_add_fraction (WpSpaPodBuilder *self, guint32 num, + guint32 denom); + +WP_API +void wp_spa_pod_builder_add_pod (WpSpaPodBuilder *self, const WpSpaPod *pod); + +WP_API +void wp_spa_pod_builder_add_property (WpSpaPodBuilder *self, const char *key); + +WP_API +void wp_spa_pod_builder_add_control (WpSpaPodBuilder *self, guint32 offset, + const char *type_name); + +WP_API +void wp_spa_pod_builder_add (WpSpaPodBuilder *self, ...) G_GNUC_NULL_TERMINATED; + +WP_API +void wp_spa_pod_builder_add_valist (WpSpaPodBuilder *self, va_list args); + +WP_API +WpSpaPod *wp_spa_pod_builder_end (WpSpaPodBuilder *self); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (WpSpaPodBuilder, wp_spa_pod_builder_unref) + + +/** + * WP_TYPE_SPA_POD_PARSER: + * + * The #WpSpaPodParser #GType + */ +#define WP_TYPE_SPA_POD_PARSER (wp_spa_pod_parser_get_type ()) +WP_API +GType wp_spa_pod_parser_get_type (void); + +typedef struct _WpSpaPodParser WpSpaPodParser; + +WP_API +WpSpaPodParser *wp_spa_pod_parser_ref (WpSpaPodParser *self); + +WP_API +void wp_spa_pod_parser_unref (WpSpaPodParser *self); + +WP_API +WpSpaPodParser *wp_spa_pod_parser_new_object (WpSpaPod *pod, + const char *type_name, const char **id_name); + +WP_API +WpSpaPodParser *wp_spa_pod_parser_new_struct (WpSpaPod *pod); + +WP_API +gboolean wp_spa_pod_parser_get_boolean (WpSpaPodParser *self, gboolean *value); + +WP_API +gboolean wp_spa_pod_parser_get_id (WpSpaPodParser *self, guint32 *value); + +WP_API +gboolean wp_spa_pod_parser_get_int (WpSpaPodParser *self, gint *value); + +WP_API +gboolean wp_spa_pod_parser_get_long (WpSpaPodParser *self, glong *value); + +WP_API +gboolean wp_spa_pod_parser_get_float (WpSpaPodParser *self, float *value); + +WP_API +gboolean wp_spa_pod_parser_get_double (WpSpaPodParser *self, double *value); + +WP_API +gboolean wp_spa_pod_parser_get_string (WpSpaPodParser *self, + const char **value); + +WP_API +gboolean wp_spa_pod_parser_get_bytes (WpSpaPodParser *self, + gconstpointer *value, guint32 *len); + +WP_API +gboolean wp_spa_pod_parser_get_pointer (WpSpaPodParser *self, + const char **type_name, gconstpointer *value); + +WP_API +gboolean wp_spa_pod_parser_get_fd (WpSpaPodParser *self, gint64 *value); + +WP_API +gboolean wp_spa_pod_parser_get_rectangle (WpSpaPodParser *self, guint32 *width, + guint32 *height); + +WP_API +gboolean wp_spa_pod_parser_get_fraction (WpSpaPodParser *self, guint32 *num, + guint32 *denom); + +WP_API +WpSpaPod *wp_spa_pod_parser_get_pod (WpSpaPodParser *self); + +WP_API +gboolean wp_spa_pod_parser_get (WpSpaPodParser *self, ...); + +WP_API +gboolean wp_spa_pod_parser_get_valist (WpSpaPodParser *self, + va_list args); + +WP_API +void wp_spa_pod_parser_end (WpSpaPodParser *self); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (WpSpaPodParser, wp_spa_pod_parser_unref) + + +WP_API +WpIterator *wp_spa_pod_iterator_new (WpSpaPod *pod); + +G_END_DECLS + +#endif diff --git a/lib/wp/wp.h b/lib/wp/wp.h index 3d2f345a..92baa8ae 100644 --- a/lib/wp/wp.h +++ b/lib/wp/wp.h @@ -25,4 +25,5 @@ #include "proxy.h" #include "session.h" #include "spa-type.h" +#include "spa-pod.h" #include "wpenums.h" diff --git a/tests/wp/meson.build b/tests/wp/meson.build index 917a35e7..046736c4 100644 --- a/tests/wp/meson.build +++ b/tests/wp/meson.build @@ -34,6 +34,12 @@ test( env: common_env, ) +test( + 'test-spa-pod', + executable('test-spa-pod', 'spa-pod.c', dependencies: common_deps), + env: common_env, +) + test( 'test-spa-type', executable('test-spa-type', 'spa-type.c', dependencies: common_deps), diff --git a/tests/wp/spa-pod.c b/tests/wp/spa-pod.c new file mode 100644 index 00000000..8b75340c --- /dev/null +++ b/tests/wp/spa-pod.c @@ -0,0 +1,978 @@ +/* WirePlumber + * + * Copyright © 2020 Collabora Ltd. + * @author Julian Bouzas <julian.bouzas@collabora.com> + * + * SPDX-License-Identifier: MIT + */ + +#include <wp/wp.h> + +static void +test_spa_pod_basic (void) +{ + wp_spa_type_init (TRUE); + + /* None */ + { + g_autoptr (WpSpaPod) pod = wp_spa_pod_new_none (); + g_assert_nonnull (pod); + g_assert_true (wp_spa_pod_is_none (pod)); + g_assert_false (wp_spa_pod_is_id (pod)); + g_assert_cmpstr ("None", ==, wp_spa_pod_get_type_name (pod)); + } + + /* Boolean */ + { + g_autoptr (WpSpaPod) copy = NULL; + g_assert_null (copy); + + { + g_autoptr (WpSpaPod) pod = wp_spa_pod_new_boolean (TRUE); + g_assert_nonnull (pod); + g_assert_true (wp_spa_pod_is_boolean (pod)); + gboolean value = FALSE; + g_assert_true (wp_spa_pod_get_boolean (pod, &value)); + g_assert_true (value); + g_assert_cmpstr ("Bool", ==, wp_spa_pod_get_type_name (pod)); + g_assert_true (wp_spa_pod_set_boolean (pod, FALSE)); + g_assert_true (wp_spa_pod_get_boolean (pod, &value)); + g_assert_false (value); + + copy = wp_spa_pod_copy (pod); + } + + g_assert_nonnull (copy); + g_assert_true (wp_spa_pod_is_boolean (copy)); + gboolean value = FALSE; + g_assert_true (wp_spa_pod_get_boolean (copy, &value)); + g_assert_false (value); + g_assert_cmpstr ("Bool", ==, wp_spa_pod_get_type_name (copy)); + } + + /* Id */ + { + g_autoptr (WpSpaPod) pod = wp_spa_pod_new_id (5); + g_assert_nonnull (pod); + g_assert_true (wp_spa_pod_is_id (pod)); + guint32 value = 0; + g_assert_true (wp_spa_pod_get_id (pod, &value)); + g_assert_cmpuint (value, ==, 5); + g_assert_cmpstr ("Id", ==, wp_spa_pod_get_type_name (pod)); + g_assert_true (wp_spa_pod_set_id (pod, 10)); + g_assert_true (wp_spa_pod_get_id (pod, &value)); + g_assert_cmpuint (value, ==, 10); + } + + /* Int */ + { + g_autoptr (WpSpaPod) pod = wp_spa_pod_new_int (-12); + g_assert_nonnull (pod); + g_assert_true (wp_spa_pod_is_int (pod)); + gint value = 0; + g_assert_true (wp_spa_pod_get_int (pod, &value)); + g_assert_cmpint (value, ==, -12); + g_assert_cmpstr ("Int", ==, wp_spa_pod_get_type_name (pod)); + g_assert_true (wp_spa_pod_set_int (pod, 9999)); + g_assert_true (wp_spa_pod_get_int (pod, &value)); + g_assert_cmpint (value, ==, 9999); + } + + /* Long */ + { + g_autoptr (WpSpaPod) pod = wp_spa_pod_new_long (LONG_MAX); + g_assert_nonnull (pod); + g_assert_true (wp_spa_pod_is_long (pod)); + long value = 0; + g_assert_true (wp_spa_pod_get_long (pod, &value)); + g_assert_cmpint (value, ==, LONG_MAX); + g_assert_cmpstr ("Long", ==, wp_spa_pod_get_type_name (pod)); + g_assert_true (wp_spa_pod_set_long (pod, LONG_MIN)); + g_assert_true (wp_spa_pod_get_long (pod, &value)); + g_assert_cmpuint (value, ==, LONG_MIN); + } + + /* Float */ + { + g_autoptr (WpSpaPod) pod = wp_spa_pod_new_float (3.14); + g_assert_nonnull (pod); + g_assert_true (wp_spa_pod_is_float (pod)); + float value = 0; + g_assert_true (wp_spa_pod_get_float (pod, &value)); + g_assert_cmpfloat_with_epsilon (value, 3.14, 0.001); + g_assert_cmpstr ("Float", ==, wp_spa_pod_get_type_name (pod)); + g_assert_true (wp_spa_pod_set_float (pod, 1.0)); + g_assert_true (wp_spa_pod_get_float (pod, &value)); + g_assert_cmpfloat_with_epsilon (value, 1.0, 0.001); + } + + /* Double */ + { + g_autoptr (WpSpaPod) pod = wp_spa_pod_new_double (2.718281828); + g_assert_nonnull (pod); + g_assert_true (wp_spa_pod_is_double (pod)); + double value = 0; + g_assert_true (wp_spa_pod_get_double (pod, &value)); + g_assert_cmpfloat_with_epsilon (value, 2.718281828, 0.0000000001); + g_assert_cmpstr ("Double", ==, wp_spa_pod_get_type_name (pod)); + g_assert_true (wp_spa_pod_set_double (pod, 2.0)); + g_assert_true (wp_spa_pod_get_double (pod, &value)); + g_assert_cmpfloat_with_epsilon (value, 2.0, 0.0000000001); + } + + /* String */ + { + g_autoptr (WpSpaPod) pod = wp_spa_pod_new_string ("WirePlumber"); + g_assert_nonnull (pod); + g_assert_true (wp_spa_pod_is_string (pod)); + const char *value = NULL; + g_assert_true (wp_spa_pod_get_string (pod, &value)); + g_assert_nonnull (value); + g_assert_cmpstr (value, ==, "WirePlumber"); + g_assert_cmpstr ("String", ==, wp_spa_pod_get_type_name (pod)); + + g_autoptr (WpSpaPod) other = wp_spa_pod_new_string ("Other"); + g_assert_nonnull (other); + g_assert_true (wp_spa_pod_set_pod (pod, other)); + g_assert_true (wp_spa_pod_get_string (pod, &value)); + g_assert_nonnull (value); + g_assert_cmpstr (value, ==, "Other"); + } + + /* Bytes */ + { + g_autoptr (WpSpaPod) pod = wp_spa_pod_new_bytes ("bytes", 5); + g_assert_nonnull (pod); + g_assert_true (wp_spa_pod_is_bytes (pod)); + gconstpointer value = NULL; + guint32 len = 0; + g_assert_true (wp_spa_pod_get_bytes (pod, &value, &len)); + g_assert_nonnull (value); + g_assert_cmpmem (value, len, "bytes", 5); + g_assert_cmpuint (len, ==, 5); + g_assert_cmpstr ("Bytes", ==, wp_spa_pod_get_type_name (pod)); + } + + /* Pointer */ + { + gint i = 3; + g_autoptr (WpSpaPod) pod = wp_spa_pod_new_pointer ("Int", &i); + g_assert_nonnull (pod); + g_assert_true (wp_spa_pod_is_pointer (pod)); + const char *type_name = NULL; + gconstpointer p = NULL; + g_assert_true (wp_spa_pod_get_pointer (pod, &type_name, &p)); + g_assert_nonnull (type_name); + g_assert_nonnull (p); + g_assert_cmpstr (type_name, ==, "Int"); + g_assert_true (p == &i); + g_assert_cmpint (*(gint *)p, ==, 3); + g_assert_cmpstr ("Pointer", ==, wp_spa_pod_get_type_name (pod)); + gboolean b = TRUE; + g_assert_true (wp_spa_pod_set_pointer (pod, "Bool", &b)); + g_assert_true (wp_spa_pod_get_pointer (pod, &type_name, &p)); + g_assert_nonnull (type_name); + g_assert_nonnull (p); + g_assert_cmpstr (type_name, ==, "Bool"); + g_assert_true (p == &b); + g_assert_cmpint (*(gboolean *)p, ==, TRUE); + } + + /* Fd */ + { + g_autoptr (WpSpaPod) pod = wp_spa_pod_new_fd (4); + g_assert_nonnull (pod); + g_assert_true (wp_spa_pod_is_fd (pod)); + gint64 value = 0; + g_assert_true (wp_spa_pod_get_fd (pod, &value)); + g_assert_cmpint (value, ==, 4); + g_assert_cmpstr ("Fd", ==, wp_spa_pod_get_type_name (pod)); + g_assert_true (wp_spa_pod_set_fd (pod, 1)); + g_assert_true (wp_spa_pod_get_fd (pod, &value)); + g_assert_cmpuint (value, ==, 1); + } + + /* Rectangle */ + { + g_autoptr (WpSpaPod) pod = wp_spa_pod_new_rectangle (1920, 1080); + g_assert_nonnull (pod); + g_assert_true (wp_spa_pod_is_rectangle (pod)); + guint32 width = 0; + guint32 height = 0; + g_assert_true (wp_spa_pod_get_rectangle (pod, &width, &height)); + g_assert_cmpint (width, ==, 1920); + g_assert_cmpint (height, ==, 1080); + g_assert_cmpstr ("Rectangle", ==, wp_spa_pod_get_type_name (pod)); + g_assert_true (wp_spa_pod_set_rectangle (pod, 640, 480)); + g_assert_true (wp_spa_pod_get_rectangle (pod, &width, &height)); + g_assert_cmpint (width, ==, 640); + g_assert_cmpint (height, ==, 480); + } + + /* Fraction */ + { + g_autoptr (WpSpaPod) pod = wp_spa_pod_new_fraction (16, 9); + g_assert_nonnull (pod); + g_assert_true (wp_spa_pod_is_fraction (pod)); + guint32 num = 0; + guint32 denom = 0; + g_assert_true (wp_spa_pod_get_fraction (pod, &num, &denom)); + g_assert_cmpint (num, ==, 16); + g_assert_cmpint (denom, ==, 9); + g_assert_cmpstr ("Fraction", ==, wp_spa_pod_get_type_name (pod)); + g_assert_true (wp_spa_pod_set_fraction (pod, 4, 3)); + g_assert_true (wp_spa_pod_get_fraction (pod, &num, &denom)); + g_assert_cmpint (num, ==, 4); + g_assert_cmpint (denom, ==, 3); + } + + wp_spa_type_deinit (); +} + +static void +test_spa_pod_choice (void) +{ + wp_spa_type_init (TRUE); + + /* Static Enum */ + { + g_autoptr (WpSpaPod) pod = wp_spa_pod_new_choice ( + "Enum", "i", 0, "i", 1, "i", 2, NULL); + g_assert_nonnull (pod); + g_assert_true (wp_spa_pod_is_choice (pod)); + g_assert_cmpstr ("Choice", ==, wp_spa_pod_get_type_name (pod)); + + g_autoptr (WpSpaPod) child = wp_spa_pod_get_choice_child (pod); + g_assert_nonnull (child); + g_assert_cmpstr ("Int", ==, wp_spa_pod_get_type_name (child)); + gint value = 1; + g_assert_true (wp_spa_pod_get_int (child, &value)); + g_assert_cmpint (value, ==, 0); + g_assert_true (wp_spa_pod_set_int (child, 3)); + g_assert_true (wp_spa_pod_get_int (child, &value)); + g_assert_cmpint (value, ==, 3); + } + + /* Static None */ + { + g_autoptr (WpSpaPod) pod = wp_spa_pod_new_choice ("None", "s", + "default value", NULL); + g_assert_nonnull (pod); + g_assert_true (wp_spa_pod_is_choice (pod)); + g_assert_cmpstr ("Choice", ==, wp_spa_pod_get_type_name (pod)); + + { + g_autoptr (WpSpaPod) child = wp_spa_pod_get_choice_child (pod); + g_assert_nonnull (child); + g_assert_cmpstr ("String", ==, wp_spa_pod_get_type_name (child)); + const char *value = NULL; + g_assert_true (wp_spa_pod_get_string (child, &value)); + g_assert_nonnull (value); + g_assert_cmpstr ("default value", ==, value); + g_autoptr (WpSpaPod) str_pod = wp_spa_pod_new_string ("new value"); + g_assert_true (wp_spa_pod_set_pod (child, str_pod)); + g_assert_true (wp_spa_pod_get_string (child, &value)); + g_assert_cmpstr ("new value", ==, value); + } + + { + g_autoptr (WpSpaPod) child = wp_spa_pod_get_choice_child (pod); + g_assert_nonnull (child); + g_assert_cmpstr ("String", ==, wp_spa_pod_get_type_name (child)); + const char *value = NULL; + g_assert_true (wp_spa_pod_get_string (child, &value)); + g_assert_nonnull (value); + g_assert_cmpstr ("new value", ==, value); + } + } + + /* Dynamic */ + { + g_autoptr (WpSpaPodBuilder) b = wp_spa_pod_builder_new_choice ("Enum"); + wp_spa_pod_builder_add (b, "i", 0, NULL); + wp_spa_pod_builder_add (b, "i", 1, NULL); + wp_spa_pod_builder_add (b, "i", 2, NULL); + g_autoptr (WpSpaPod) pod = wp_spa_pod_builder_end (b); + g_assert_nonnull (pod); + g_assert_true (wp_spa_pod_is_choice (pod)); + g_assert_cmpstr ("Choice", ==, wp_spa_pod_get_type_name (pod)); + } + + /* It is not possible to use the parser to get the contents of a choice, you + * need to use the iterator API to achieve that. This is because there is no + * `spa_pod_parser_get_choice` API in the SPA library */ + + wp_spa_type_deinit (); +} + +static void +test_spa_pod_array (void) +{ + wp_spa_type_init (TRUE); + + /* Dynamic */ + { + WpSpaPodBuilder *b = wp_spa_pod_builder_new_array (); + wp_spa_pod_builder_add (b, "b", FALSE, NULL); + wp_spa_pod_builder_add (b, "b", TRUE, NULL); + wp_spa_pod_builder_add (b, "b", TRUE, NULL); + wp_spa_pod_builder_add (b, "b", FALSE, NULL); + wp_spa_pod_builder_add (b, "b", TRUE, NULL); + g_autoptr (WpSpaPod) pod = wp_spa_pod_builder_end (b); + g_assert_nonnull (pod); + g_assert_true (wp_spa_pod_is_array (pod)); + g_assert_cmpstr ("Array", ==, wp_spa_pod_get_type_name (pod)); + wp_spa_pod_builder_unref (b); + g_assert_true (wp_spa_pod_is_array (pod)); + + g_autoptr (WpSpaPod) child = wp_spa_pod_get_array_child (pod); + g_assert_nonnull (child); + g_assert_cmpstr ("Bool", ==, wp_spa_pod_get_type_name (child)); + gboolean value = TRUE; + g_assert_true (wp_spa_pod_get_boolean (child, &value)); + g_assert_false (value); + } + + /* It is not possible to use the parser to get the contents of an array, you + * need to use the iterator API to achieve that. This is because there is no + * `spa_pod_parser_get_array` API in the SPA library. */ + + wp_spa_type_deinit (); +} + +static void +test_spa_pod_object (void) +{ + wp_spa_type_init (TRUE); + + /* Static */ + { + g_autoptr (WpSpaPod) pod = wp_spa_pod_new_object ( + "Props", "Props", + "mute", "b", FALSE, + "volume", "f", 0.5, + "frequency", "i", 440, + "device", "s", "device-name", + "deviceFd", "h", 5, + NULL); + g_assert_nonnull (pod); + g_assert_true (wp_spa_pod_is_object (pod)); + g_assert_cmpstr ("Object", ==, wp_spa_pod_get_type_name (pod)); + + const char *id_name; + gboolean mute = TRUE; + float vol = 0.0; + gint frequency; + const char *device; + gint64 device_fd; + g_assert_true (wp_spa_pod_get_object (pod, + "Props", &id_name, + "mute", "b", &mute, + "volume", "f", &vol, + "frequency", "i", &frequency, + "device", "s", &device, + "deviceFd", "h", &device_fd, + NULL)); + g_assert_cmpstr (id_name, ==, "Props"); + g_assert_false (mute); + g_assert_cmpfloat_with_epsilon (vol, 0.5, 0.01); + g_assert_cmpint (frequency, ==, 440); + g_assert_cmpstr (device, ==, "device-name"); + g_assert_cmpint (device_fd, ==, 5); + } + + /* Dynamic */ + { + g_autoptr (WpSpaPodBuilder) b = wp_spa_pod_builder_new_object ( + "Props", "Props"); + wp_spa_pod_builder_add_property (b, "mute"); + wp_spa_pod_builder_add_boolean (b, FALSE); + wp_spa_pod_builder_add_property (b, "volume"); + wp_spa_pod_builder_add_float (b, 0.5); + wp_spa_pod_builder_add_property (b, "frequency"); + wp_spa_pod_builder_add_int (b, 440); + wp_spa_pod_builder_add_property (b, "device"); + wp_spa_pod_builder_add_string (b, "device-name"); + wp_spa_pod_builder_add_property (b, "deviceFd"); + wp_spa_pod_builder_add_fd (b, 5); + g_autoptr (WpSpaPod) pod = wp_spa_pod_builder_end (b); + g_assert_nonnull (pod); + g_assert_true (wp_spa_pod_is_object (pod)); + g_assert_cmpstr ("Object", ==, wp_spa_pod_get_type_name (pod)); + + const char *id_name; + gboolean mute = TRUE; + float vol = 0.0; + gint frequency; + const char *device; + gint64 device_fd; + g_autoptr (WpSpaPodParser) p = wp_spa_pod_parser_new_object (pod, + "Props", &id_name); + g_assert_nonnull (pod); + g_assert_true (wp_spa_pod_parser_get (p, "mute", "b", &mute, NULL)); + g_assert_true (wp_spa_pod_parser_get (p, "volume", "f", &vol, NULL)); + g_assert_true (wp_spa_pod_parser_get (p, "frequency", "i", &frequency, NULL)); + g_assert_true (wp_spa_pod_parser_get (p, "device", "s", &device, NULL)); + g_assert_true (wp_spa_pod_parser_get (p, "deviceFd", "h", &device_fd, NULL)); + wp_spa_pod_parser_end (p); + g_assert_cmpstr (id_name, ==, "Props"); + g_assert_false (mute); + g_assert_cmpfloat_with_epsilon (vol, 0.5, 0.01); + g_assert_cmpint (frequency, ==, 440); + g_assert_cmpstr (device, ==, "device-name"); + g_assert_cmpint (device_fd, ==, 5); + } + + wp_spa_type_deinit (); +} + +static void +test_spa_pod_struct (void) +{ + wp_spa_type_init (TRUE); + + /* Dynamic */ + { + g_autoptr (WpSpaPodBuilder) b = wp_spa_pod_builder_new_struct (); + wp_spa_pod_builder_add_boolean (b, TRUE); + wp_spa_pod_builder_add_id (b, 2); + wp_spa_pod_builder_add_int (b, 8); + wp_spa_pod_builder_add_long (b, 64); + wp_spa_pod_builder_add_float (b, 3.14); + wp_spa_pod_builder_add_double (b, 2.718281828); + wp_spa_pod_builder_add_string (b, "WirePlumber"); + wp_spa_pod_builder_add_bytes (b, "bytes", 5); + wp_spa_pod_builder_add_pointer (b, "Struct", b); + wp_spa_pod_builder_add_fd (b, 4); + wp_spa_pod_builder_add_rectangle (b, 1920, 1080); + wp_spa_pod_builder_add_fraction (b, 16, 9); + { + g_autoptr (WpSpaPod) pod = wp_spa_pod_new_int (35254); + wp_spa_pod_builder_add_pod (b, pod); + } + { + g_autoptr (WpSpaPod) pod = wp_spa_pod_new_object ( + "Props", "Props", + "mute", "b", FALSE, + NULL); + wp_spa_pod_builder_add (b, "P", pod, NULL); + } + g_autoptr (WpSpaPod) pod = wp_spa_pod_builder_end (b); + g_assert_nonnull (pod); + g_assert_true (wp_spa_pod_is_struct (pod)); + g_assert_cmpstr ("Struct", ==, wp_spa_pod_get_type_name (pod)); + + g_autoptr (WpSpaPodParser) p = wp_spa_pod_parser_new_struct (pod); + g_assert_nonnull (pod); + + gboolean value_boolean; + g_assert_true (wp_spa_pod_parser_get_boolean (p, &value_boolean)); + g_assert_true (value_boolean); + + guint32 value_id; + g_assert_true (wp_spa_pod_parser_get_id (p, &value_id)); + g_assert_cmpuint (value_id, ==, 2); + + gint value_int; + g_assert_true (wp_spa_pod_parser_get_int (p, &value_int)); + g_assert_cmpint (value_int, ==, 8); + + glong value_long; + g_assert_true (wp_spa_pod_parser_get_long (p, &value_long)); + g_assert_cmpint (value_long, ==, 64); + + float value_float; + g_assert_true (wp_spa_pod_parser_get_float (p, &value_float)); + g_assert_cmpfloat_with_epsilon (value_float, 3.14, 0.001); + + double value_double; + g_assert_true (wp_spa_pod_parser_get_double (p, &value_double)); + g_assert_cmpfloat_with_epsilon (value_double, 2.718281828, 0.0000000001); + + const char *value_string; + g_assert_true (wp_spa_pod_parser_get_string (p, &value_string)); + g_assert_cmpstr (value_string, ==, "WirePlumber"); + + gconstpointer value_bytes; + guint32 len_bytes; + g_assert_true (wp_spa_pod_parser_get_bytes (p, &value_bytes, &len_bytes)); + g_assert_cmpmem (value_bytes, len_bytes, "bytes", 5); + g_assert_cmpuint (len_bytes, ==, 5); + + gconstpointer value_pointer; + const char *type_pointer; + g_assert_true (wp_spa_pod_parser_get_pointer (p, &type_pointer, &value_pointer)); + g_assert_nonnull (type_pointer); + g_assert_nonnull (value_pointer); + g_assert_cmpstr (type_pointer, ==, "Struct"); + g_assert_true (value_pointer == b); + + gint64 value_fd; + g_assert_true (wp_spa_pod_parser_get_fd (p, &value_fd)); + g_assert_cmpint (value_fd, ==, 4); + + guint32 value_width; + guint32 value_height; + g_assert_true (wp_spa_pod_parser_get_rectangle (p, &value_width, &value_height)); + g_assert_cmpuint (value_width, ==, 1920); + g_assert_cmpuint (value_height, ==, 1080); + + guint32 value_num; + guint32 value_denom; + g_assert_true (wp_spa_pod_parser_get_fraction (p, &value_num, &value_denom)); + g_assert_cmpuint (value_num, ==, 16); + g_assert_cmpuint (value_denom, ==, 9); + + g_autoptr (WpSpaPod) value_pod = wp_spa_pod_parser_get_pod (p); + g_assert_nonnull (value_pod); + gint value_pod_int; + g_assert_true (wp_spa_pod_get_int (value_pod, &value_pod_int)); + g_assert_cmpint (value_pod_int, ==, 35254); + + g_autoptr (WpSpaPod) value_object = NULL; + g_assert_true (wp_spa_pod_parser_get (p, "P", &value_object, NULL)); + g_assert_nonnull (value_object); + const char *id_name; + gboolean mute = TRUE; + + g_assert_true (wp_spa_pod_get_object (value_object, + "Props", &id_name, + "mute", "b", &mute, + NULL)); + g_assert_cmpstr (id_name, ==, "Props"); + g_assert_false (mute); + } + + wp_spa_type_deinit (); +} + +static void +test_spa_pod_sequence (void) +{ + wp_spa_type_init (TRUE); + + /* Static */ + { + g_autoptr (WpSpaPod) pod = wp_spa_pod_new_sequence (0, + 10, "Properties", "l", 9999, NULL); + g_assert_nonnull (pod); + g_assert_true (wp_spa_pod_is_sequence (pod)); + g_assert_cmpstr ("Sequence", ==, wp_spa_pod_get_type_name (pod)); + } + + /* Dynamic */ + { + g_autoptr (WpSpaPodBuilder) b = wp_spa_pod_builder_new_sequence (0); + wp_spa_pod_builder_add_control (b, 10, "Properties"); + wp_spa_pod_builder_add_long (b, 9999); + g_autoptr (WpSpaPod) pod = wp_spa_pod_builder_end (b); + g_assert_nonnull (pod); + g_assert_true (wp_spa_pod_is_sequence (pod)); + g_assert_cmpstr ("Sequence", ==, wp_spa_pod_get_type_name (pod)); + } + + /* It is not possible to use the parser to get the contents of a sequence, you + * need to use the iterator API to achieve that. This is because there is no + * `spa_pod_parser_get_sequence` API in the SPA library. */ + + wp_spa_type_deinit (); +} + +static void +choice_foreach (const GValue *item, gpointer data) +{ + gint *total = data; + const gint *value = g_value_get_pointer (item); + *total += *value; +} + +static void +array_foreach (const GValue *item, gpointer data) +{ + gint *total = data; + const gint *value = g_value_get_pointer (item); + *total += *value; +} + +static void +object_foreach (const GValue *item, gpointer data) +{ + guint32 *total_props = data; + const WpSpaPod *prop = g_value_get_boxed (item); + g_assert_true (wp_spa_pod_is_property (prop)); + *total_props += 1; +} + +static void +struct_foreach (const GValue *item, gpointer data) +{ + guint32 *total_fields = data; + *total_fields += 1; +} + +static void +sequence_foreach (const GValue *item, gpointer data) +{ + guint32 *offset_total = data; + const WpSpaPod *control = g_value_get_boxed (item); + g_assert_true (wp_spa_pod_is_control (control)); + guint32 offset = 0; + g_assert_true (wp_spa_pod_get_control (control, &offset, NULL, NULL)); + *offset_total += offset; +} + +static void +test_spa_pod_iterator (void) +{ + wp_spa_type_init (TRUE); + + /* Choice */ + { + g_autoptr (WpSpaPodBuilder) b = wp_spa_pod_builder_new_choice ("Enum"); + wp_spa_pod_builder_add (b, "i", 0, NULL); + wp_spa_pod_builder_add (b, "i", 1, NULL); + wp_spa_pod_builder_add (b, "i", 2, NULL); + g_autoptr (WpSpaPod) pod = wp_spa_pod_builder_end (b); + g_assert_nonnull (pod); + + g_autoptr (WpIterator) it = wp_spa_pod_iterator_new (pod); + g_assert_nonnull (it); + + { + GValue next = G_VALUE_INIT; + g_assert_true (wp_iterator_next (it, &next)); + gpointer p = g_value_get_pointer (&next); + g_assert_nonnull (p); + g_assert_cmpint (*(gint *)p, ==, 0); + g_value_unset (&next); + } + + { + GValue next = G_VALUE_INIT; + g_assert_true (wp_iterator_next (it, &next)); + gpointer p = g_value_get_pointer (&next); + g_assert_nonnull (p); + g_assert_cmpint (*(gint *)p, ==, 1); + g_value_unset (&next); + } + + { + GValue next = G_VALUE_INIT; + g_assert_true (wp_iterator_next (it, &next)); + gpointer p = g_value_get_pointer (&next); + g_assert_nonnull (p); + g_assert_cmpint (*(gint *)p, ==, 2); + g_value_unset (&next); + } + + { + g_assert_false (wp_iterator_next (it, NULL)); + } + + gint total = 0; + g_assert_true (wp_iterator_foreach (it, choice_foreach, &total)); + g_assert_cmpint (total, ==, 3); + + } + + /* Array */ + { + g_autoptr (WpSpaPodBuilder) b = wp_spa_pod_builder_new_array (); + wp_spa_pod_builder_add_int (b, 1); + wp_spa_pod_builder_add_int (b, 2); + wp_spa_pod_builder_add_int (b, 3); + g_autoptr (WpSpaPod) pod = wp_spa_pod_builder_end (b); + g_assert_nonnull (pod); + + g_autoptr (WpIterator) it = wp_spa_pod_iterator_new (pod); + g_assert_nonnull (it); + + { + GValue next = G_VALUE_INIT; + g_assert_true (wp_iterator_next (it, &next)); + gpointer p = g_value_get_pointer (&next); + g_assert_nonnull (p); + g_assert_cmpint (*(gint *)p, ==, 1); + g_value_unset (&next); + } + + { + GValue next = G_VALUE_INIT; + g_assert_true (wp_iterator_next (it, &next)); + gpointer p = g_value_get_pointer (&next); + g_assert_nonnull (p); + g_assert_cmpint (*(gint *)p, ==, 2); + g_value_unset (&next); + } + + { + GValue next = G_VALUE_INIT; + g_assert_true (wp_iterator_next (it, &next)); + gpointer p = g_value_get_pointer (&next); + g_assert_nonnull (p); + g_assert_cmpint (*(gint *)p, ==, 3); + g_value_unset (&next); + } + + { + g_assert_false (wp_iterator_next (it, NULL)); + } + + gint total = 0; + g_assert_true (wp_iterator_foreach (it, array_foreach, &total)); + g_assert_cmpint (total, ==, 6); + } + + /* Object */ + { + g_autoptr (WpSpaPodBuilder) b = wp_spa_pod_builder_new_object ( + "Props", "Props"); + wp_spa_pod_builder_add_property (b, "mute"); + wp_spa_pod_builder_add_boolean (b, FALSE); + wp_spa_pod_builder_add_property (b, "device"); + wp_spa_pod_builder_add_string (b, "device-name"); + g_autoptr (WpSpaPod) pod = wp_spa_pod_builder_end (b); + g_assert_nonnull (pod); + + g_autoptr (WpIterator) it = wp_spa_pod_iterator_new (pod); + g_assert_nonnull (it); + + { + GValue next = G_VALUE_INIT; + g_assert_true (wp_iterator_next (it, &next)); + WpSpaPod *p = g_value_get_boxed (&next); + g_assert_nonnull (p); + g_assert_true (wp_spa_pod_is_property (p)); + const char *key = NULL; + g_autoptr (WpSpaPod) value = NULL; + g_assert_true (wp_spa_pod_get_property (p, &key, &value)); + g_assert_cmpstr (key, ==, "mute"); + gboolean b = TRUE; + g_assert_true (wp_spa_pod_get_boolean (value, &b)); + g_assert_false (b); + g_value_unset (&next); + } + + { + GValue next = G_VALUE_INIT; + g_assert_true (wp_iterator_next (it, &next)); + WpSpaPod *p = g_value_get_boxed (&next); + g_assert_nonnull (p); + g_assert_true (wp_spa_pod_is_property (p)); + const char *key = NULL; + g_autoptr (WpSpaPod) value = NULL; + g_assert_true (wp_spa_pod_get_property (p, &key, &value)); + g_assert_cmpstr (key, ==, "device"); + const char *s = NULL; + g_assert_true (wp_spa_pod_get_string (value, &s)); + g_assert_cmpstr (s, ==, "device-name"); + g_value_unset (&next); + } + + { + g_assert_false (wp_iterator_next (it, NULL)); + } + + guint32 total_props = 0; + g_assert_true (wp_iterator_foreach (it, object_foreach, &total_props)); + g_assert_cmpuint (total_props, ==, 2); + } + + /* Struct */ + { + g_autoptr (WpSpaPodBuilder) b = wp_spa_pod_builder_new_struct (); + wp_spa_pod_builder_add_boolean (b, TRUE); + wp_spa_pod_builder_add_id (b, 2); + wp_spa_pod_builder_add_int (b, 8); + g_autoptr (WpSpaPod) pod = wp_spa_pod_builder_end (b); + g_assert_nonnull (pod); + + g_autoptr (WpIterator) it = wp_spa_pod_iterator_new (pod); + g_assert_nonnull (it); + + { + GValue next = G_VALUE_INIT; + g_assert_true (wp_iterator_next (it, &next)); + WpSpaPod *p = g_value_get_boxed (&next); + g_assert_nonnull (p); + gboolean v = FALSE; + g_assert_true (wp_spa_pod_get_boolean (p, &v)); + g_assert_true (v); + g_value_unset (&next); + } + + { + GValue next = G_VALUE_INIT; + g_assert_true (wp_iterator_next (it, &next)); + WpSpaPod *p = g_value_get_boxed (&next); + g_assert_nonnull (p); + guint32 v = 0; + g_assert_true (wp_spa_pod_get_id (p, &v)); + g_assert_cmpuint (v, ==, 2); + g_value_unset (&next); + } + + { + GValue next = G_VALUE_INIT; + g_assert_true (wp_iterator_next (it, &next)); + WpSpaPod *p = g_value_get_boxed (&next); + g_assert_nonnull (p); + gint v = 0; + g_assert_true (wp_spa_pod_get_int (p, &v)); + g_assert_cmpint (v, ==, 8); + g_value_unset (&next); + } + + { + g_assert_false (wp_iterator_next (it, NULL)); + } + + guint32 total_fields = 0; + g_assert_true (wp_iterator_foreach (it, struct_foreach, &total_fields)); + g_assert_cmpuint (total_fields, ==, 3); + } + + /* Sequence */ + { + g_autoptr (WpSpaPodBuilder) b = wp_spa_pod_builder_new_sequence (0); + wp_spa_pod_builder_add_control (b, 10, "Properties"); + wp_spa_pod_builder_add_float (b, 0.33); + wp_spa_pod_builder_add_control (b, 40, "Properties"); + wp_spa_pod_builder_add_float (b, 0.66); + g_autoptr (WpSpaPod) pod = wp_spa_pod_builder_end (b); + g_assert_nonnull (pod); + + g_autoptr (WpIterator) it = wp_spa_pod_iterator_new (pod); + g_assert_nonnull (it); + + { + GValue next = G_VALUE_INIT; + g_assert_true (wp_iterator_next (it, &next)); + WpSpaPod *p = g_value_get_boxed (&next); + g_assert_nonnull (p); + g_assert_true (wp_spa_pod_is_control (p)); + guint32 offset = 0; + const char *type_name = NULL; + g_autoptr (WpSpaPod) value = NULL; + g_assert_true (wp_spa_pod_get_control (p, &offset, &type_name, &value)); + g_assert_cmpuint (offset, ==, 10); + g_assert_cmpstr (type_name, ==, "Properties"); + float f = 0; + g_assert_true (wp_spa_pod_get_float (value, &f)); + g_assert_cmpfloat_with_epsilon (f, 0.33, 0.001); + g_value_unset (&next); + } + + { + GValue next = G_VALUE_INIT; + g_assert_true (wp_iterator_next (it, &next)); + WpSpaPod *p = g_value_get_boxed (&next); + g_assert_nonnull (p); + g_assert_true (wp_spa_pod_is_control (p)); + guint32 offset = 0; + const char *type_name = NULL; + g_autoptr (WpSpaPod) value = NULL; + g_assert_true (wp_spa_pod_get_control (p, &offset, &type_name, &value)); + g_assert_cmpuint (offset, ==, 40); + g_assert_cmpstr (type_name, ==, "Properties"); + float f = 0; + g_assert_true (wp_spa_pod_get_float (value, &f)); + g_assert_cmpfloat_with_epsilon (f, 0.66, 0.001); + g_value_unset (&next); + } + + { + g_assert_false (wp_iterator_next (it, NULL)); + } + + guint32 offset_total = 0; + g_assert_true (wp_iterator_foreach (it, sequence_foreach, &offset_total)); + g_assert_cmpuint (offset_total, ==, 50); + } + + wp_spa_type_deinit (); +} + +static void +test_spa_pod_unique_owner (void) +{ + wp_spa_type_init (TRUE); + + /* Create an object */ + WpSpaPod *pod = wp_spa_pod_new_object ( + "PropInfo", "PropInfo", + "id", "I", 1, + "name", "s", "prop-info-name", + NULL); + g_assert_nonnull (pod); + g_assert_true (wp_spa_pod_is_unique_owner (pod)); + + /* Get the first property using an iterator */ + GValue next = G_VALUE_INIT; + g_autoptr (WpSpaPod) property = NULL; + { + g_autoptr (WpIterator) it = wp_spa_pod_iterator_new (pod); + g_assert_nonnull (it); + g_assert_true (wp_iterator_next (it, &next)); + property = g_value_dup_boxed (&next); + } + g_assert_nonnull (property); + g_assert_true (wp_spa_pod_is_property (property)); + { + g_autoptr (WpSpaPod) value = NULL; + const char *key = NULL; + g_assert_true (wp_spa_pod_get_property (property, &key, &value)); + g_assert_nonnull (key); + g_assert_cmpstr (key, ==, "id"); + g_assert_nonnull (value); + guint32 id = 0; + g_assert_true (wp_spa_pod_get_id (value, &id)); + g_assert_cmpuint (id, ==, 1); + } + + /* Own the data */ + g_assert_true (wp_spa_pod_is_unique_owner (pod)); + g_assert_false (wp_spa_pod_is_unique_owner (property)); + property = wp_spa_pod_ensure_unique_owner (property); + g_assert_true (wp_spa_pod_is_unique_owner (pod)); + g_assert_true (wp_spa_pod_is_unique_owner (property)); + + /* Destroy the object */ + wp_spa_pod_unref (pod); + g_assert_true (wp_spa_pod_is_unique_owner (property)); + + /* Make sure the property data is still valid */ + { + g_autoptr (WpSpaPod) value = NULL; + const char *key = NULL; + g_assert_true (wp_spa_pod_get_property (property, &key, &value)); + g_assert_nonnull (key); + g_assert_cmpstr (key, ==, "id"); + g_assert_nonnull (value); + guint32 id = 0; + g_assert_true (wp_spa_pod_get_id (value, &id)); + g_assert_cmpuint (id, ==, 1); + } + + /* Destroy the property */ + g_value_unset (&next); + + wp_spa_type_deinit (); +} + +int +main (int argc, char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/wp/spa-pod/basic", test_spa_pod_basic); + g_test_add_func ("/wp/spa-pod/choice", test_spa_pod_choice); + g_test_add_func ("/wp/spa-pod/array", test_spa_pod_array); + g_test_add_func ("/wp/spa-pod/object", test_spa_pod_object); + g_test_add_func ("/wp/spa-pod/struct", test_spa_pod_struct); + g_test_add_func ("/wp/spa-pod/sequence", test_spa_pod_sequence); + g_test_add_func ("/wp/spa-pod/iterator", test_spa_pod_iterator); + g_test_add_func ("/wp/spa-pod/unique-owner", test_spa_pod_unique_owner); + + return g_test_run (); +} -- GitLab