/* json-array.c - JSON array implementation * * This file is part of JSON-GLib * Copyright (C) 2007 OpenedHand Ltd. * Copyright (C) 2009 Intel Corp. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #include "config.h" #include "json-types-private.h" /** * JsonArray: * * `JsonArray` is the representation of the array type inside JSON. * * A `JsonArray` contains [struct@Json.Node] elements, which may contain * fundamental types, other arrays or objects. * * Since arrays can be arbitrarily big, copying them can be expensive; for * this reason, they are reference counted. You can control the lifetime of * a `JsonArray` using [method@Json.Array.ref] and [method@Json.Array.unref]. * * To append an element, use [method@Json.Array.add_element]. * * To extract an element at a given index, use [method@Json.Array.get_element]. * * To retrieve the entire array in list form, use [method@Json.Array.get_elements]. * * To retrieve the length of the array, use [method@Json.Array.get_length]. */ G_DEFINE_BOXED_TYPE (JsonArray, json_array, json_array_ref, json_array_unref); /** * json_array_new: (constructor) * * Creates a new array. * * Return value: (transfer full): the newly created array */ JsonArray * json_array_new (void) { JsonArray *array; array = g_slice_new0 (JsonArray); array->ref_count = 1; array->elements = g_ptr_array_new (); return array; } /** * json_array_sized_new: (constructor) * @n_elements: number of slots to pre-allocate * * Creates a new array with `n_elements` slots already allocated. * * Return value: (transfer full): the newly created array */ JsonArray * json_array_sized_new (guint n_elements) { JsonArray *array; array = g_slice_new0 (JsonArray); array->ref_count = 1; array->elements = g_ptr_array_sized_new (n_elements); return array; } /** * json_array_ref: * @array: the array to reference * * Acquires a reference on the given array. * * Return value: (transfer none): the passed array, with the reference count * increased by one */ JsonArray * json_array_ref (JsonArray *array) { g_return_val_if_fail (array != NULL, NULL); g_return_val_if_fail (array->ref_count > 0, NULL); array->ref_count++; return array; } /** * json_array_unref: * @array: the array to unreference * * Releases a reference on the given array. * * If the reference count reaches zero, the array is destroyed and all * its allocated resources are freed. */ void json_array_unref (JsonArray *array) { g_return_if_fail (array != NULL); g_return_if_fail (array->ref_count > 0); if (--array->ref_count == 0) { guint i; for (i = 0; i < array->elements->len; i++) json_node_unref (g_ptr_array_index (array->elements, i)); g_ptr_array_free (array->elements, TRUE); array->elements = NULL; g_slice_free (JsonArray, array); } } /** * json_array_seal: * @array: the array to seal * * Seals the given array, making it immutable to further changes. * * This function will recursively seal all elements in the array too. * * If the `array` is already immutable, this is a no-op. * * Since: 1.2 */ void json_array_seal (JsonArray *array) { guint i; g_return_if_fail (array != NULL); g_return_if_fail (array->ref_count > 0); if (array->immutable) return; /* Propagate to all members. */ for (i = 0; i < array->elements->len; i++) json_node_seal (g_ptr_array_index (array->elements, i)); array->immutable_hash = json_array_hash (array); array->immutable = TRUE; } /** * json_array_is_immutable: * @array: a JSON array * * Check whether the given `array` has been marked as immutable by calling * [method@Json.Array.seal] on it. * * Since: 1.2 * Returns: %TRUE if the array is immutable */ gboolean json_array_is_immutable (JsonArray *array) { g_return_val_if_fail (array != NULL, FALSE); g_return_val_if_fail (array->ref_count > 0, FALSE); return array->immutable; } /** * json_array_get_elements: * @array: a JSON array * * Retrieves all the elements of an array as a list of nodes. * * Return value: (element-type JsonNode) (transfer container) (nullable): the elements * of the array */ GList * json_array_get_elements (JsonArray *array) { GList *retval; guint i; g_return_val_if_fail (array != NULL, NULL); retval = NULL; for (i = 0; i < array->elements->len; i++) retval = g_list_prepend (retval, g_ptr_array_index (array->elements, i)); return g_list_reverse (retval); } /** * json_array_dup_element: * @array: a JSON array * @index_: the index of the element to retrieve * * Retrieves a copy of the element at the given position in the array. * * Return value: (transfer full): a copy of the element at the given position * * Since: 0.6 */ JsonNode * json_array_dup_element (JsonArray *array, guint index_) { JsonNode *retval; g_return_val_if_fail (array != NULL, NULL); g_return_val_if_fail (index_ < array->elements->len, NULL); retval = json_array_get_element (array, index_); if (!retval) return NULL; return json_node_copy (retval); } /** * json_array_get_element: * @array: a JSON array * @index_: the index of the element to retrieve * * Retrieves the element at the given position in the array. * * Return value: (transfer none): the element at the given position */ JsonNode * json_array_get_element (JsonArray *array, guint index_) { g_return_val_if_fail (array != NULL, NULL); g_return_val_if_fail (index_ < array->elements->len, NULL); return g_ptr_array_index (array->elements, index_); } /** * json_array_get_int_element: * @array: a JSON array * @index_: the index of the element to retrieve * * Conveniently retrieves the integer value of the element at the given * position inside an array. * * See also: [method@Json.Array.get_element], [method@Json.Node.get_int] * * Return value: the integer value * * Since: 0.8 */ gint64 json_array_get_int_element (JsonArray *array, guint index_) { JsonNode *node; g_return_val_if_fail (array != NULL, 0); g_return_val_if_fail (index_ < array->elements->len, 0); node = g_ptr_array_index (array->elements, index_); g_return_val_if_fail (node != NULL, 0); g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE, 0); return json_node_get_int (node); } /** * json_array_get_double_element: * @array: a JSON array * @index_: the index of the element to retrieve * * Conveniently retrieves the floating point value of the element at * the given position inside an array. * * See also: [method@Json.Array.get_element], [method@Json.Node.get_double] * * Return value: the floating point value * * Since: 0.8 */ gdouble json_array_get_double_element (JsonArray *array, guint index_) { JsonNode *node; g_return_val_if_fail (array != NULL, 0.0); g_return_val_if_fail (index_ < array->elements->len, 0.0); node = g_ptr_array_index (array->elements, index_); g_return_val_if_fail (node != NULL, 0.0); g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE, 0.0); return json_node_get_double (node); } /** * json_array_get_boolean_element: * @array: a JSON array * @index_: the index of the element to retrieve * * Conveniently retrieves the boolean value of the element at the given * position inside an array. * * See also: [method@Json.Array.get_element], [method@Json.Node.get_boolean] * * Return value: the boolean value * * Since: 0.8 */ gboolean json_array_get_boolean_element (JsonArray *array, guint index_) { JsonNode *node; g_return_val_if_fail (array != NULL, FALSE); g_return_val_if_fail (index_ < array->elements->len, FALSE); node = g_ptr_array_index (array->elements, index_); g_return_val_if_fail (node != NULL, FALSE); g_return_val_if_fail (JSON_NODE_TYPE (node) == JSON_NODE_VALUE, FALSE); return json_node_get_boolean (node); } /** * json_array_get_string_element: * @array: a JSON array * @index_: the index of the element to retrieve * * Conveniently retrieves the string value of the element at the given * position inside an array. * * See also: [method@Json.Array.get_element], [method@Json.Node.get_string] * * Return value: (transfer none): the string value * * Since: 0.8 */ const gchar * json_array_get_string_element (JsonArray *array, guint index_) { JsonNode *node; g_return_val_if_fail (array != NULL, NULL); g_return_val_if_fail (index_ < array->elements->len, NULL); node = g_ptr_array_index (array->elements, index_); g_return_val_if_fail (node != NULL, NULL); g_return_val_if_fail (JSON_NODE_HOLDS_VALUE (node) || JSON_NODE_HOLDS_NULL (node), NULL); if (JSON_NODE_HOLDS_NULL (node)) return NULL; return json_node_get_string (node); } /** * json_array_get_null_element: * @array: a JSON array * @index_: the index of the element to retrieve * * Conveniently checks whether the element at the given position inside the * array contains a `null` value. * * See also: [method@Json.Array.get_element], [method@Json.Node.is_null] * * Return value: `TRUE` if the element is `null` * * Since: 0.8 */ gboolean json_array_get_null_element (JsonArray *array, guint index_) { JsonNode *node; g_return_val_if_fail (array != NULL, FALSE); g_return_val_if_fail (index_ < array->elements->len, FALSE); node = g_ptr_array_index (array->elements, index_); g_return_val_if_fail (node != NULL, FALSE); if (JSON_NODE_HOLDS_NULL (node)) return TRUE; if (JSON_NODE_HOLDS_ARRAY (node)) return json_node_get_array (node) == NULL; if (JSON_NODE_HOLDS_OBJECT (node)) return json_node_get_object (node) == NULL; return FALSE; } /** * json_array_get_array_element: * @array: a JSON array * @index_: the index of the element to retrieve * * Conveniently retrieves the array at the given position inside an array. * * See also: [method@Json.Array.get_element], [method@Json.Node.get_array] * * Return value: (transfer none): the array * * Since: 0.8 */ JsonArray * json_array_get_array_element (JsonArray *array, guint index_) { JsonNode *node; g_return_val_if_fail (array != NULL, NULL); g_return_val_if_fail (index_ < array->elements->len, NULL); node = g_ptr_array_index (array->elements, index_); g_return_val_if_fail (node != NULL, NULL); g_return_val_if_fail (JSON_NODE_HOLDS_ARRAY (node) || JSON_NODE_HOLDS_NULL (node), NULL); if (JSON_NODE_HOLDS_NULL (node)) return NULL; return json_node_get_array (node); } /** * json_array_get_object_element: * @array: a JSON array * @index_: the index of the element to retrieve * * Conveniently retrieves the object at the given position inside an array. * * See also: [method@Json.Array.get_element], [method@Json.Node.get_object] * * Return value: (transfer none): the object * * Since: 0.8 */ JsonObject * json_array_get_object_element (JsonArray *array, guint index_) { JsonNode *node; g_return_val_if_fail (array != NULL, NULL); g_return_val_if_fail (index_ < array->elements->len, NULL); node = g_ptr_array_index (array->elements, index_); g_return_val_if_fail (node != NULL, NULL); g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (node) || JSON_NODE_HOLDS_NULL (node), NULL); if (JSON_NODE_HOLDS_NULL (node)) return NULL; return json_node_get_object (node); } /** * json_array_get_length: * @array: a JSON array * * Retrieves the length of the given array * * Return value: the length of the array */ guint json_array_get_length (JsonArray *array) { g_return_val_if_fail (array != NULL, 0); return array->elements->len; } /** * json_array_add_element: * @array: a JSON array * @node: (transfer full): the element to add * * Appends the given `node` inside an array. */ void json_array_add_element (JsonArray *array, JsonNode *node) { g_return_if_fail (array != NULL); g_return_if_fail (node != NULL); g_ptr_array_add (array->elements, node); } /** * json_array_add_int_element: * @array: a JSON array * @value: the integer value to add * * Conveniently adds the given integer value into an array. * * See also: [method@Json.Array.add_element], [method@Json.Node.set_int] * * Since: 0.8 */ void json_array_add_int_element (JsonArray *array, gint64 value) { g_return_if_fail (array != NULL); json_array_add_element (array, json_node_init_int (json_node_alloc (), value)); } /** * json_array_add_double_element: * @array: a JSON array * @value: the floating point value to add * * Conveniently adds the given floating point value into an array. * * See also: [method@Json.Array.add_element], [method@Json.Node.set_double] * * Since: 0.8 */ void json_array_add_double_element (JsonArray *array, gdouble value) { g_return_if_fail (array != NULL); json_array_add_element (array, json_node_init_double (json_node_alloc (), value)); } /** * json_array_add_boolean_element: * @array: a JSON array * @value: the boolean value to add * * Conveniently adds the given boolean value into an array. * * See also: [method@Json.Array.add_element], [method@Json.Node.set_boolean] * * Since: 0.8 */ void json_array_add_boolean_element (JsonArray *array, gboolean value) { g_return_if_fail (array != NULL); json_array_add_element (array, json_node_init_boolean (json_node_alloc (), value)); } /** * json_array_add_string_element: * @array: a JSON array * @value: the string value to add * * Conveniently adds the given string value into an array. * * See also: [method@Json.Array.add_element], [method@Json.Node.set_string] * * Since: 0.8 */ void json_array_add_string_element (JsonArray *array, const gchar *value) { JsonNode *node; g_return_if_fail (array != NULL); node = json_node_alloc (); if (value != NULL) json_node_init_string (node, value); else json_node_init_null (node); json_array_add_element (array, node); } /** * json_array_add_null_element: * @array: a JSON array * * Conveniently adds a `null` element into an array * * See also: [method@Json.Array.add_element], `JSON_NODE_NULL` * * Since: 0.8 */ void json_array_add_null_element (JsonArray *array) { g_return_if_fail (array != NULL); json_array_add_element (array, json_node_init_null (json_node_alloc ())); } /** * json_array_add_array_element: * @array: a JSON array * @value: (nullable) (transfer full): the array to add * * Conveniently adds an array element into an array. * * If `value` is `NULL`, a `null` element will be added instead. * * See also: [method@Json.Array.add_element], [method@Json.Node.take_array] * * Since: 0.8 */ void json_array_add_array_element (JsonArray *array, JsonArray *value) { JsonNode *node; g_return_if_fail (array != NULL); node = json_node_alloc (); if (value != NULL) { json_node_init_array (node, value); json_array_unref (value); } else json_node_init_null (node); json_array_add_element (array, node); } /** * json_array_add_object_element: * @array: a JSON array * @value: (transfer full) (nullable): the object to add * * Conveniently adds an object into an array. * * If `value` is `NULL`, a `null` element will be added instead. * * See also: [method@Json.Array.add_element], [method@Json.Node.take_object] * * Since: 0.8 */ void json_array_add_object_element (JsonArray *array, JsonObject *value) { JsonNode *node; g_return_if_fail (array != NULL); node = json_node_alloc (); if (value != NULL) { json_node_init_object (node, value); json_object_unref (value); } else json_node_init_null (node); json_array_add_element (array, node); } /** * json_array_remove_element: * @array: a JSON array * @index_: the position of the element to be removed * * Removes the element at the given position inside an array. * * This function will release the reference held on the element. */ void json_array_remove_element (JsonArray *array, guint index_) { g_return_if_fail (array != NULL); g_return_if_fail (index_ < array->elements->len); json_node_unref (g_ptr_array_remove_index (array->elements, index_)); } /** * json_array_foreach_element: * @array: a JSON array * @func: (scope call): the function to be called on each element * @data: (closure): data to be passed to the function * * Iterates over all elements of an array, and calls a function on * each one of them. * * It is safe to change the value of an element of the array while * iterating over it, but it is not safe to add or remove elements * from the array. * * Since: 0.8 */ void json_array_foreach_element (JsonArray *array, JsonArrayForeach func, gpointer data) { g_return_if_fail (array != NULL); g_return_if_fail (func != NULL); for (guint i = 0; i < array->elements->len; i++) { JsonNode *element_node; element_node = g_ptr_array_index (array->elements, i); (* func) (array, i, element_node, data); } } /** * json_array_hash: * @key: (type JsonArray) (not nullable): a JSON array to hash * * Calculates a hash value for the given `key`. * * The hash is calculated over the array and all its elements, recursively. * * If the array is immutable, this is a fast operation; otherwise, it scales * proportionally with the length of the array. * * Returns: hash value for the key * Since: 1.2 */ guint json_array_hash (gconstpointer key) { JsonArray *array; /* unowned */ guint hash = 0; guint i; g_return_val_if_fail (key != NULL, 0); array = (JsonArray *) key; /* If the array is immutable, we can use the calculated hash. */ if (array->immutable) return array->immutable_hash; /* Otherwise, calculate the hash. */ for (i = 0; i < array->elements->len; i++) { JsonNode *node = g_ptr_array_index (array->elements, i); hash ^= (i ^ json_node_hash (node)); } return hash; } /** * json_array_equal: * @a: (type JsonArray) (not nullable): a JSON array * @b: (type JsonArray) (not nullable): another JSON array * * Check whether two arrays are equal. * * Equality is defined as: * * - the array have the same number of elements * - the values of elements in corresponding positions are equal * * Returns: `TRUE` if the arrays are equal, and `FALSE` otherwise * Since: 1.2 */ gboolean json_array_equal (gconstpointer a, gconstpointer b) { JsonArray *array_a, *array_b; /* unowned */ guint length_a, length_b, i; g_return_val_if_fail (a != NULL, FALSE); g_return_val_if_fail (b != NULL, FALSE); array_a = (JsonArray *) a; array_b = (JsonArray *) b; /* Identity comparison. */ if (array_a == array_b) return TRUE; /* Check lengths. */ length_a = json_array_get_length (array_a); length_b = json_array_get_length (array_b); if (length_a != length_b) return FALSE; /* Check elements. */ for (i = 0; i < length_a; i++) { JsonNode *child_a, *child_b; /* unowned */ child_a = json_array_get_element (array_a, i); child_b = json_array_get_element (array_b, i); if (!json_node_equal (child_a, child_b)) return FALSE; } return TRUE; }