Files
wireplumber/lib/wp/spa-type.c
2020-04-09 13:09:57 -04:00

336 lines
9.1 KiB
C

/* WirePlumber
*
* Copyright © 2020 Collabora Ltd.
* @author Julian Bouzas <julian.bouzas@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#include <spa/utils/type-info.h>
#include "spa-type.h"
static gboolean
name_equal_func (gconstpointer a, gconstpointer b) {
const struct spa_type_info *ti = a;
const char *name = b;
return g_strcmp0 (ti->name, name) == 0;
}
static const struct spa_type_info *
spa_type_find (const struct spa_type_info *table, GEqualFunc func,
gconstpointer data)
{
for (guint32 i = 0; table[i].name; i++)
if (func (table + i, data))
return table + i;
return NULL;
}
static const struct spa_type_info *
spa_type_find_by_name (const struct spa_type_info *table, const char *name)
{
return spa_type_find (table, name_equal_func, name);
}
struct type_info {
gboolean is_spa_type;
union {
const struct spa_type_info *spa;
struct {
guint32 type;
char *name;
} custom;
} info;
char *nick;
};
static struct type_info *
type_info_new_spa (const struct spa_type_info *info, const char* nick) {
struct type_info *ti = g_slice_new0 (struct type_info);
ti->is_spa_type = TRUE;
ti->info.spa = info;
ti->nick = g_strdup (nick);
return ti;
}
static struct type_info *
type_info_new_custom (uint32_t type, const char *name, const char* nick) {
struct type_info *ti = g_slice_new0 (struct type_info);
ti->is_spa_type = FALSE;
ti->info.custom.type = type;
ti->info.custom.name = g_strdup (name);
ti->nick = g_strdup (nick);
return ti;
}
static void
type_info_free (struct type_info *ti) {
g_return_if_fail (ti);
if (!ti->is_spa_type)
g_clear_pointer (&ti->info.custom.name, g_free);
g_clear_pointer (&ti->nick, g_free);
g_slice_free (struct type_info, ti);
}
struct spa_type_table_data {
const struct spa_type_info *spa_table;
guint32 last_id;
GPtrArray *info_array;
GHashTable *id_table;
GHashTable *nick_table;
};
static struct spa_type_table_data s_tables [WP_SPA_TYPE_TABLE_LAST] = {
[WP_SPA_TYPE_TABLE_BASIC] = {spa_types, SPA_TYPE_VENDOR_Other, NULL, NULL, NULL },
[WP_SPA_TYPE_TABLE_PARAM] = {spa_type_param, SPA_N_ELEMENTS (spa_type_param), NULL, NULL, NULL, },
[WP_SPA_TYPE_TABLE_PROPS] = {spa_type_props, SPA_PROP_START_CUSTOM, NULL, NULL, NULL, },
[WP_SPA_TYPE_TABLE_PROP_INFO] = {spa_type_prop_info, SPA_N_ELEMENTS (spa_type_prop_info), NULL, NULL, NULL, },
[WP_SPA_TYPE_TABLE_CONTROL] = {spa_type_control, SPA_CONTROL_LAST, NULL, NULL, NULL, },
[WP_SPA_TYPE_TABLE_CHOICE] = {spa_type_choice, SPA_N_ELEMENTS (spa_type_choice), NULL, NULL, NULL, },
};
static WpSpaTypeTable
wp_spa_type_table_find_by_spa_table (const struct spa_type_info *spa_table)
{
for (guint32 i = 0; i < WP_SPA_TYPE_TABLE_LAST; i++)
if (s_tables[i].spa_table == spa_table)
return i;
return 0;
}
/**
* wp_spa_type_init:
* @register_spa: whether spa types will be registered or not
*
* Initializes the spa type registry
*/
void
wp_spa_type_init (gboolean register_spa)
{
/* Init the array and hash tables */
for (guint32 i = 0; i < WP_SPA_TYPE_TABLE_LAST; i++) {
struct spa_type_table_data *td = s_tables + i;
if (!td->info_array)
td->info_array = g_ptr_array_new_with_free_func ((GDestroyNotify)
type_info_free);
if (!td->id_table)
td->id_table = g_hash_table_new_full (g_direct_hash, g_direct_equal,
NULL, NULL);
if (!td->nick_table)
td->nick_table = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, NULL);
}
/* Register the spa types if requested */
if (register_spa) {
for (guint32 i = 0; i < WP_SPA_TYPE_TABLE_LAST; i++) {
struct spa_type_table_data *td = s_tables + i;
for (guint32 j = 0; td->spa_table && td->spa_table[j].name; j++) {
const struct spa_type_info *t = td->spa_table + j;
const char *nick = strrchr (t->name, ':');
if (nick && strlen (nick) > 1)
wp_spa_type_register (i, t->name, nick + 1);
}
}
}
}
/**
* wp_spa_type_deinit:
*
* Deinitializes the spa type registry
*/
void
wp_spa_type_deinit (void)
{
for (guint32 i = 0; i < WP_SPA_TYPE_TABLE_LAST; i++) {
struct spa_type_table_data *td = s_tables + i;
g_clear_pointer (&td->info_array, g_ptr_array_unref);
g_clear_pointer (&td->id_table, g_hash_table_unref);
g_clear_pointer (&td->nick_table, g_hash_table_unref);
}
}
/**
* wp_spa_type_get_table_size:
* @table: the table
*
* Gets the number of registered types in a given table
*
* Returns: The number of registered types
*/
size_t
wp_spa_type_get_table_size (WpSpaTypeTable table)
{
struct spa_type_table_data *td = NULL;
g_return_val_if_fail (table < WP_SPA_TYPE_TABLE_LAST, 0);
/* Get the table data */
td = s_tables + table;
return td->info_array->len;
}
/**
* wp_spa_type_register:
* @table: the table
* @name: the name of the type
* @nick: the nick name of the type
*
* Registers a type name with a nickname in the registry
*
* Returns: TRUE if the type could be registered, FALSE otherwise
*/
gboolean
wp_spa_type_register (WpSpaTypeTable table, const char *name, const char *nick)
{
struct spa_type_table_data *td = NULL;
const struct spa_type_info *spa_info = NULL;
struct type_info *info = NULL;
guint32 id;
g_return_val_if_fail (table < WP_SPA_TYPE_TABLE_LAST, FALSE);
g_return_val_if_fail (name, FALSE);
g_return_val_if_fail (nick, FALSE);
/* Get the table data */
td = s_tables + table;
/* Return false if the nick already exists in the nick table */
if (g_hash_table_contains (td->nick_table, nick))
return FALSE;
/* Add the type in the custom table */
spa_info = spa_type_find_by_name (td->spa_table, name);
if (spa_info) {
id = spa_info->type;
info = type_info_new_spa (spa_info, nick);
} else {
id = ++td->last_id;
info = type_info_new_custom (id, name, nick);
}
g_ptr_array_add (td->info_array, info);
/* Insert the id and nick in the hash tables */
return g_hash_table_insert (td->id_table, GUINT_TO_POINTER (id), info) &&
g_hash_table_insert (td->nick_table, g_strdup (nick), info);
}
/**
* wp_spa_type_unregister:
* @table: the table
* @nick: the nick name of the type
*
* Unregisters a type given its nick name
*/
void
wp_spa_type_unregister (WpSpaTypeTable table, const char *nick)
{
struct spa_type_table_data *td = NULL;
struct type_info *info = NULL;
guint32 id;
g_return_if_fail (table < WP_SPA_TYPE_TABLE_LAST);
g_return_if_fail (nick);
/* Get the table data */
td = s_tables + table;
/* Lookup the info by nick */
info = g_hash_table_lookup (td->nick_table, nick);
if (!info)
return;
/* Get id */
id = info->is_spa_type ? info->info.spa->type : info->info.custom.type;
/* Remove info from hash tables */
g_hash_table_remove (td->nick_table, nick);
g_hash_table_remove (td->id_table, GUINT_TO_POINTER (id));
/* Remove info from array */
g_ptr_array_remove_fast (td->info_array, info);
}
/**
* wp_spa_type_get_by_nick:
* @table: the table
* @nick: the nick name of the type
* @id: (out) (optional): the id of the type
* @name: (out) (optional): the name of the type
* @values_table: (out) (optional): the values table of the type
*
* Gets the id and name of the registered type given its nick name
*
* Returns: TRUE if the type was found, FALSE otherwise
*/
gboolean
wp_spa_type_get_by_nick (WpSpaTypeTable table, const char *nick,
guint32 *id, const char **name, WpSpaTypeTable *values_table)
{
struct spa_type_table_data *td = NULL;
const struct type_info *info = NULL;
g_return_val_if_fail (table < WP_SPA_TYPE_TABLE_LAST, FALSE);
/* Make sure nick is valid */
if (!nick)
return FALSE;
/* Get the table data */
td = s_tables + table;
/* Lookup the info by nick */
info = g_hash_table_lookup (td->nick_table, nick);
if (!info)
return FALSE;
if (id)
*id = info->is_spa_type ? info->info.spa->type : info->info.custom.type;
if (name)
*name = info->is_spa_type ? info->info.spa->name : info->info.custom.name;
if (values_table && info->is_spa_type)
*values_table = wp_spa_type_table_find_by_spa_table (info->info.spa->values);
return TRUE;
}
/**
* wp_spa_type_get_by_id:
* @table: the table
* @id: the id of the type
* @name: (out) (optional): the name of the type
* @nick: (out) (optional): the nick name of the type
* @values_table: (out) (optional): the values table of the type
*
* Gets the name and nick name of the registered type given its id
*
* Returns: TRUE if the type was found, FALSE otherwise
*/
gboolean
wp_spa_type_get_by_id (WpSpaTypeTable table, guint32 id,
const char **name, const char **nick, WpSpaTypeTable *values_table)
{
struct spa_type_table_data *td = NULL;
const struct type_info *info = NULL;
g_return_val_if_fail (table < WP_SPA_TYPE_TABLE_LAST, FALSE);
/* Get the table data */
td = s_tables + table;
/* Lookup the info by id */
info = g_hash_table_lookup (td->id_table, GUINT_TO_POINTER (id));
if (!info)
return FALSE;
if (name)
*name = info->is_spa_type ? info->info.spa->name : info->info.custom.name;
if (nick)
*nick = info->nick;
if (values_table && info->is_spa_type)
*values_table = wp_spa_type_table_find_by_spa_table (info->info.spa->values);
return TRUE;
}