diff options
author | Günther Wagner <info@gunibert.de> | 2021-12-28 16:46:10 +0100 |
---|---|---|
committer | Günther Wagner <info@gunibert.de> | 2022-01-12 19:47:12 +0100 |
commit | b6b20dae6f06a3badc6be09532d735a375b62e19 (patch) | |
tree | 3dd25cd3bea315d841076547dc61ecbeae4b2e05 | |
parent | b71c932a64e4060c2cbce05aac040e1c6d7dc43f (diff) | |
download | librest-b6b20dae6f06a3badc6be09532d735a375b62e19.tar.gz |
params: reworked to boxed and list
RestParams was implemented as HashTable. Limitations are that it did
not preserved the order of individual parameters aswell as duplicates
aren't allowed. Reworked it to a GList and introduced reference counting
and a boxed type.
-rw-r--r-- | rest-extras/meson.build | 3 | ||||
-rw-r--r-- | rest/meson.build | 7 | ||||
-rw-r--r-- | rest/rest-params.c | 196 | ||||
-rw-r--r-- | rest/rest-params.h | 52 | ||||
-rw-r--r-- | rest/rest-proxy-call.c | 2 | ||||
-rw-r--r-- | tests/meson.build | 7 | ||||
-rw-r--r-- | tests/params.c | 141 | ||||
-rw-r--r-- | tests/test-media.png | bin | 0 -> 2509 bytes |
8 files changed, 322 insertions, 86 deletions
diff --git a/rest-extras/meson.build b/rest-extras/meson.build index 1ad9ee1..cf14ee8 100644 --- a/rest-extras/meson.build +++ b/rest-extras/meson.build @@ -49,9 +49,12 @@ if get_option('introspection') librest_extras_gir = gnome.generate_gir(librest_extras_lib, sources: librest_extras_sources + librest_extras_headers, namespace: 'RestExtras', + symbol_prefix: 'rest_extras', + identifier_prefix: 'RestExtras', nsversion: librest_api_version, includes: [ 'GObject-2.0', 'Gio-2.0', 'Soup-@0@'.format(libsoup_api_version), librest_gir[0] ], extra_args: [ '--accept-unprefixed' ], + dependencies: librest_extras_deps, install: true, ) endif diff --git a/rest/meson.build b/rest/meson.build index a22742e..58dfbd8 100644 --- a/rest/meson.build +++ b/rest/meson.build @@ -87,12 +87,11 @@ if get_option('introspection') endforeach librest_gir = gnome.generate_gir(librest_lib, - sources: [ librest_headers, librest_sources, librest_enums[1] ], - dependencies: librest_deps, + sources: librest_sources + librest_headers + librest_enums, + nsversion: librest_api_version, namespace: 'Rest', - identifier_prefix: 'Rest', symbol_prefix: 'rest', - nsversion: librest_api_version, + identifier_prefix: 'Rest', includes: [ 'GObject-2.0', 'Gio-2.0', 'Soup-@0@'.format(libsoup_api_version) ], extra_args: librest_gir_extra_args, install: true, diff --git a/rest/rest-params.c b/rest/rest-params.c index f246cc2..f2ac286 100644 --- a/rest/rest-params.c +++ b/rest/rest-params.c @@ -30,18 +30,7 @@ * @see_also: #RestParam, #RestProxyCall. */ -/* - * RestParams is an alias for GHashTable achieved by opaque types in the public - * headers and casting internally. This has several limitations, mainly - * supporting multiple parameters with the same name and preserving the ordering - * of parameters. - * - * These are not requirements for the bulk of the web services, but this - * limitation does mean librest can't be used for a few web services. - * - * TODO: this should be a list to support multiple parameters with the same - * name. - */ +G_DEFINE_BOXED_TYPE (RestParams, rest_params, rest_params_ref, rest_params_unref) /** * rest_params_new: @@ -53,11 +42,13 @@ RestParams * rest_params_new (void) { - /* The key is a string that is owned by the RestParam, so we don't need to - explicitly free it on removal. */ - return (RestParams *) - g_hash_table_new_full (g_str_hash, g_str_equal, - NULL, (GDestroyNotify)rest_param_unref); + RestParams *self; + + self = g_slice_new0 (RestParams); + self->ref_count = 1; + self->params = NULL; + + return self; } /** @@ -67,13 +58,73 @@ rest_params_new (void) * Destroy the #RestParams and the #RestParam objects that it contains. **/ void -rest_params_free (RestParams *params) +rest_params_free (RestParams *self) { - GHashTable *hash = (GHashTable *)params; + g_assert (self); + g_assert_cmpint (self->ref_count, ==, 0); - g_return_if_fail (params); + g_list_free_full (g_steal_pointer (&self->params), (GDestroyNotify) rest_param_unref); + + g_slice_free (RestParams, self); +} + +/** + * rest_params_copy: + * @self: a #RestParams + * + * Makes a deep copy of a #RestParams. + * + * Returns: (transfer full): A newly created #RestParams with the same + * contents as @self + */ +RestParams * +rest_params_copy (RestParams *self) +{ + RestParams *copy; + + g_return_val_if_fail (self, NULL); + g_return_val_if_fail (self->ref_count, NULL); + + copy = rest_params_new (); + copy->params = g_list_copy_deep (self->params, (GCopyFunc) rest_param_ref, NULL); + + return copy; +} + +/** + * rest_params_ref: + * @self: A #RestParams + * + * Increments the reference count of @self by one. + * + * Returns: (transfer full): @self + */ +RestParams * +rest_params_ref (RestParams *self) +{ + g_return_val_if_fail (self, NULL); + g_return_val_if_fail (self->ref_count, NULL); + + g_atomic_int_inc (&self->ref_count); - g_hash_table_destroy (hash); + return self; +} + +/** + * rest_params_unref: + * @self: A #RestParams + * + * Decrements the reference count of @self by one, freeing the structure when + * the reference count reaches zero. + */ +void +rest_params_unref (RestParams *self) +{ + g_return_if_fail (self); + g_return_if_fail (self->ref_count); + + if (g_atomic_int_dec_and_test (&self->ref_count)) + rest_params_free (self); } /** @@ -84,14 +135,25 @@ rest_params_free (RestParams *params) * Add @param to @params. **/ void -rest_params_add (RestParams *params, RestParam *param) +rest_params_add (RestParams *self, + RestParam *param) { - GHashTable *hash = (GHashTable *)params; - - g_return_if_fail (params); + g_return_if_fail (self); g_return_if_fail (param); - g_hash_table_replace (hash, (gpointer)rest_param_get_name (param), param); + self->params = g_list_append (self->params, param); +} + +static gint +rest_params_find (gconstpointer self, + gconstpointer name) +{ + const RestParam *e = self; + const char *n = name; + const char *n2 = rest_param_get_name ((RestParam *)e); + + if (g_strcmp0 (n2, n) == 0) return 0; + return -1; } /** @@ -105,14 +167,13 @@ rest_params_add (RestParams *params, RestParam *param) * doesn't exist **/ RestParam * -rest_params_get (RestParams *params, const char *name) +rest_params_get (RestParams *self, + const char *name) { - GHashTable *hash = (GHashTable *)params; - - g_return_val_if_fail (params, NULL); + g_return_val_if_fail (self, NULL); g_return_val_if_fail (name, NULL); - return g_hash_table_lookup (hash, name); + return g_list_find_custom (self->params, name, rest_params_find)->data; } /** @@ -123,14 +184,16 @@ rest_params_get (RestParams *params, const char *name) * Remove the #RestParam called @name. **/ void -rest_params_remove (RestParams *params, const char *name) +rest_params_remove (RestParams *self, + const char *name) { - GHashTable *hash = (GHashTable *)params; + GList *elem = NULL; - g_return_if_fail (params); + g_return_if_fail (self); g_return_if_fail (name); - g_hash_table_remove (hash, name); + elem = g_list_find_custom (self->params, name, rest_params_find); + self->params = g_list_remove (self->params, elem->data); } /** @@ -143,27 +206,22 @@ rest_params_remove (RestParams *params, const char *name) * Returns: %TRUE if all of the parameters are simple strings, %FALSE otherwise. **/ gboolean -rest_params_are_strings (RestParams *params) +rest_params_are_strings (RestParams *self) { - GHashTable *hash = (GHashTable *)params; - GHashTableIter iter; - RestParam *param; - - g_return_val_if_fail (params, FALSE); + g_return_val_if_fail (self, FALSE); - g_hash_table_iter_init (&iter, hash); - while (g_hash_table_iter_next (&iter, NULL, (gpointer)¶m)) { - if (!rest_param_is_string (param)) - return FALSE; - } + for (GList *cur = self->params; cur; cur = g_list_next (cur)) + { + if (!rest_param_is_string (cur->data)) + return FALSE; + } return TRUE; - } /** * rest_params_as_string_hash_table: - * @params: a valid #RestParams + * @self: a valid #RestParams * * Create a new #GHashTable which contains the name and value of all string * (content type of text/plain) parameters. @@ -174,23 +232,19 @@ rest_params_are_strings (RestParams *params) * Returns: (element-type utf8 Rest.Param) (transfer container): a new #GHashTable. **/ GHashTable * -rest_params_as_string_hash_table (RestParams *params) +rest_params_as_string_hash_table (RestParams *self) { - GHashTable *hash, *strings; - GHashTableIter iter; - const char *name = NULL; - RestParam *param = NULL; + GHashTable *strings; - g_return_val_if_fail (params, NULL); + g_return_val_if_fail (self, NULL); - hash = (GHashTable *)params; strings = g_hash_table_new (g_str_hash, g_str_equal); - g_hash_table_iter_init (&iter, hash); - while (g_hash_table_iter_next (&iter, (gpointer)&name, (gpointer)¶m)) { - if (rest_param_is_string (param)) - g_hash_table_insert (strings, (gpointer)name, (gpointer)rest_param_get_content (param)); - } + for (GList *cur = self->params; cur; cur = g_list_next (cur)) + { + if (rest_param_is_string (cur->data)) + g_hash_table_insert (strings, (gpointer)rest_param_get_name (cur->data), (gpointer)rest_param_get_content (cur->data)); + } return strings; } @@ -214,12 +268,14 @@ rest_params_as_string_hash_table (RestParams *params) * ]| **/ void -rest_params_iter_init (RestParamsIter *iter, RestParams *params) +rest_params_iter_init (RestParamsIter *iter, + RestParams *params) { g_return_if_fail (iter); g_return_if_fail (params); - g_hash_table_iter_init ((GHashTableIter *)iter, (GHashTable *)params); + iter->params = params; + iter->position = -1; } /** @@ -235,9 +291,21 @@ rest_params_iter_init (RestParamsIter *iter, RestParams *params) * Returns: %FALSE if the end of the #RestParams has been reached, %TRUE otherwise. **/ gboolean -rest_params_iter_next (RestParamsIter *iter, const char **name, RestParam **param) +rest_params_iter_next (RestParamsIter *iter, + const char **name, + RestParam **param) { + GList *cur = NULL; + g_return_val_if_fail (iter, FALSE); - return g_hash_table_iter_next ((GHashTableIter *)iter, (gpointer)name, (gpointer)param); + iter->position++; + cur = g_list_nth (iter->params->params, iter->position); + + if (cur == NULL) return FALSE; + + *param = cur->data; + *name = rest_param_get_name (*param); + return TRUE; } + diff --git a/rest/rest-params.h b/rest/rest-params.h index caace9d..9f53f77 100644 --- a/rest/rest-params.h +++ b/rest/rest-params.h @@ -20,34 +20,52 @@ * */ -#ifndef _REST_PARAMS -#define _REST_PARAMS +#pragma once #include <glib-object.h> #include "rest-param.h" G_BEGIN_DECLS -typedef struct _RestParams RestParams; -typedef struct _GHashTableIter RestParamsIter; - -RestParams * rest_params_new (void); - -void rest_params_free (RestParams *params); +#define REST_TYPE_PARAMS (rest_params_get_type ()) -void rest_params_add (RestParams *params, RestParam *param); +typedef struct _RestParams RestParams; +typedef struct _RestParamsIter RestParamsIter; -RestParam *rest_params_get (RestParams *params, const char *name); +struct _RestParams +{ + /*< private >*/ + guint ref_count; -void rest_params_remove (RestParams *params, const char *name); + GList *params; +}; -gboolean rest_params_are_strings (RestParams *params); +struct _RestParamsIter +{ + /*< private >*/ + RestParams *params; + gint position; +}; -GHashTable * rest_params_as_string_hash_table (RestParams *params); +GType rest_params_get_type (void) G_GNUC_CONST; +RestParams *rest_params_new (void); +RestParams *rest_params_copy (RestParams *self); +RestParams *rest_params_ref (RestParams *self); +void rest_params_unref (RestParams *self); +void rest_params_add (RestParams *params, + RestParam *param); +RestParam *rest_params_get (RestParams *params, + const char *name); +void rest_params_remove (RestParams *params, + const char *name); +gboolean rest_params_are_strings (RestParams *params); +GHashTable *rest_params_as_string_hash_table (RestParams *params); +void rest_params_iter_init (RestParamsIter *iter, + RestParams *params); +gboolean rest_params_iter_next (RestParamsIter *iter, + const char **name, + RestParam **param); -void rest_params_iter_init (RestParamsIter *iter, RestParams *params); -gboolean rest_params_iter_next (RestParamsIter *iter, const char **name, RestParam **param); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (RestParams, rest_params_unref) G_END_DECLS - -#endif /* _REST_PARAMS */ diff --git a/rest/rest-proxy-call.c b/rest/rest-proxy-call.c index 24d952d..791a7dd 100644 --- a/rest/rest-proxy-call.c +++ b/rest/rest-proxy-call.c @@ -148,7 +148,7 @@ rest_proxy_call_dispose (GObject *object) g_clear_object (&priv->cancellable); } - g_clear_pointer (&priv->params, rest_params_free); + g_clear_pointer (&priv->params, rest_params_unref); g_clear_pointer (&priv->headers, g_hash_table_unref); g_clear_pointer (&priv->response_headers, g_hash_table_unref); g_clear_object (&priv->proxy); diff --git a/tests/meson.build b/tests/meson.build index c7c9170..b3087a7 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -6,6 +6,7 @@ test_suites = { 'xml', 'custom-serialize', 'oauth2', + 'params', ], 'rest-extras': [ 'flickr', @@ -20,6 +21,11 @@ test_deps = [ librest_extras_dep, ] +test_env = [ + 'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()), + 'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir()), +] + foreach suite, test_names : test_suites foreach name : test_names test_bin = executable(name, @@ -30,6 +36,7 @@ foreach suite, test_names : test_suites test(name, test_bin, suite: suite, + env: test_env, ) endforeach endforeach diff --git a/tests/params.c b/tests/params.c new file mode 100644 index 0000000..e477ba9 --- /dev/null +++ b/tests/params.c @@ -0,0 +1,141 @@ +#include <glib.h> +#include "rest/rest-params.h" +#include "rest/rest-param.h" +#include <glib-object.h> + +static void +test_params (void) +{ + RestParamsIter iter; + RestParam *param; + const char *name; + gint pos = 0; + g_autoptr(RestParams) params = NULL; + + struct { + char *name; + char *value; + } data[] = { + { + .name = "name1", + .value = "value1" + }, + { + .name = "name2", + .value = "value2" + } + }; + + params = rest_params_new (); + for (gint i = 0; i < sizeof (data)/sizeof (data[0]); i++) + { + RestParam *p = rest_param_new_string (data[i].name, REST_MEMORY_COPY, data[i].value); + rest_params_add (params, p); + } + + rest_params_iter_init (&iter, params); + while (rest_params_iter_next (&iter, &name, ¶m)) + { + g_assert_cmpstr (data[pos].name, ==, name); + g_assert_cmpstr (data[pos].value, ==, rest_param_get_content (param)); + pos++; + } + + rest_params_remove (params, "name2"); + pos = 0; + rest_params_iter_init (&iter, params); + while (rest_params_iter_next (&iter, &name, ¶m)) + { + g_assert_cmpstr (data[pos].name, ==, name); + g_assert_cmpstr (data[pos].value, ==, rest_param_get_content (param)); + pos++; + } +} + +static void +test_params_get (void) +{ + g_autoptr(RestParams) params; + RestParam *p1; + + struct { + char *name; + char *value; + } data[] = { + { + .name = "name1", + .value = "value1" + }, + { + .name = "name2", + .value = "value2" + } + }; + + params = rest_params_new (); + for (gint i = 0; i < sizeof (data)/sizeof (data[0]); i++) + { + RestParam *p = rest_param_new_string (data[i].name, REST_MEMORY_COPY, data[i].value); + rest_params_add (params, p); + } + + p1 = rest_params_get (params, "name2"); + + g_assert_cmpstr (rest_param_get_name (p1), ==, "name2"); + g_assert_cmpstr (rest_param_get_content (p1), ==, "value2"); +} + +static void +test_params_is_string (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(RestParams) params; + g_autofree char *file; + gsize length; + gchar *contents; + RestParam *p; + + struct { + char *name; + char *value; + } data[] = { + { + .name = "name1", + .value = "value1" + }, + { + .name = "name2", + .value = "value2" + } + }; + + params = rest_params_new (); + for (gint i = 0; i < sizeof (data)/sizeof (data[0]); i++) + { + RestParam *p = rest_param_new_string (data[i].name, REST_MEMORY_COPY, data[i].value); + rest_params_add (params, p); + } + + g_assert_true (rest_params_are_strings (params)); + + file = g_test_build_filename (G_TEST_DIST, "test-media.png", NULL); + g_file_get_contents(file, &contents, &length, &error); + + p = rest_param_new_full ("nostring", REST_MEMORY_COPY, contents, length, "image/png", "test-media.png"); + rest_params_add (params, p); + + g_assert_false (rest_params_are_strings (params)); +} + +gint +main (gint argc, + gchar *argv[]) +{ + g_test_init (&argc, &argv, NULL); + + g_test_add_func("/rest/params", test_params); + g_test_add_func("/rest/params_get", test_params_get); + g_test_add_func("/rest/params_is_strings", test_params_is_string); + + return g_test_run (); +} diff --git a/tests/test-media.png b/tests/test-media.png Binary files differnew file mode 100644 index 0000000..2dbbccc --- /dev/null +++ b/tests/test-media.png |