Also add the json_object_new_array_ext, array_list_new2, and array_list_shrink functions.
Deprecated and removed features:
--------------------------------
-...none yet...
+* array_list_new() has been deprecated in favor of array_list_new2()
Other changes
--------------
less memory usage.
Memory used just for json_object structures decreased 27%, so use cases
with fewer arrays and/or strings would benefit more.
+* Minimize memory usage in array handling in json_tokener by shrinking
+ arrays to the exact number of elements parsed. On bench/ benchmark:
+ 9% faster test time, 39%(max RSS)-50%(peak heap) less memory usage.
+ Add json_object_array_shrink() and array_list_shrink() functions.
+* Add json_object_new_array_ext(int) and array_list_new_2(int) to allow
+ arrays to be allocated with the exact size needed, when known.
***
#include "arraylist.h"
struct array_list *array_list_new(array_list_free_fn *free_fn)
+{
+ return array_list_new2(free_fn, ARRAY_LIST_DEFAULT_SIZE);
+}
+
+struct array_list *array_list_new2(array_list_free_fn *free_fn, int initial_size)
{
struct array_list *arr;
arr = (struct array_list *)malloc(sizeof(struct array_list));
if (!arr)
return NULL;
- arr->size = ARRAY_LIST_DEFAULT_SIZE;
+ arr->size = initial_size;
arr->length = 0;
arr->free_fn = free_fn;
if (!(arr->array = (void **)malloc(arr->size * sizeof(void *))))
return 0;
}
+int array_list_shrink(struct array_list *arr, size_t empty_slots)
+{
+ void *t;
+ size_t new_size;
+
+ new_size = arr->length + empty_slots;
+ if (new_size == arr->size)
+ return 0;
+ if (new_size > arr->size)
+ return array_list_expand_internal(arr, new_size);
+ if (new_size == 0)
+ new_size = 1;
+
+ if (!(t = realloc(arr->array, new_size * sizeof(void *))))
+ return -1;
+ arr->array = (void **)t;
+ arr->size = new_size;
+ return 0;
+}
+
//static inline int _array_list_put_idx(struct array_list *arr, size_t idx, void *data)
int array_list_put_idx(struct array_list *arr, size_t idx, void *data)
{
return -1;
for (i = idx; i < stop; ++i)
{
+ // Because put_idx can skip entries, we need to check if
+ // there's actually anything in each slot we're erasing.
if (arr->array[i])
arr->free_fn(arr->array[i]);
}
};
typedef struct array_list array_list;
+/**
+ * Allocate an array_list of the default size (32).
+ * @deprecated Use array_list_new2() instead.
+ */
extern struct array_list *array_list_new(array_list_free_fn *free_fn);
+/**
+ * Allocate an array_list of the desired size.
+ *
+ * If possible, the size should be chosen to closely match
+ * the actual number of elements expected to be used.
+ * If the exact size is unknown, there are tradeoffs to be made:
+ * - too small - the array_list code will need to call realloc() more
+ * often (which might incur an additional memory copy).
+ * - too large - will waste memory, but that can be mitigated
+ * by calling array_list_shrink() once the final size is known.
+ *
+ * @see array_list_shrink
+ */
+extern struct array_list *array_list_new2(array_list_free_fn *free_fn, int initial_size);
+
extern void array_list_free(struct array_list *al);
extern void *array_list_get_idx(struct array_list *al, size_t i);
extern int array_list_del_idx(struct array_list *arr, size_t idx, size_t count);
+
+/**
+ * Shrink the array list to just enough to fit the number of elements in it,
+ * plus empty_slots.
+ */
+extern int array_list_shrink(struct array_list *arr, size_t empty_slots);
+
+
#ifdef __cplusplus
}
#endif
}
struct json_object *json_object_new_array(void)
+{
+ return json_object_new_array_ext(ARRAY_LIST_DEFAULT_SIZE);
+}
+struct json_object *json_object_new_array_ext(int initial_size)
{
struct json_object_array *jso = JSON_OBJECT_NEW(array);
if (!jso)
return NULL;
- jso->c_array = array_list_new(&json_object_array_entry_free);
+ jso->c_array = array_list_new2(&json_object_array_entry_free, initial_size);
if (jso->c_array == NULL)
{
free(jso);
return 1;
}
+int json_object_array_shrink(struct json_object *jso, int empty_slots)
+{
+ if (empty_slots < 0)
+ json_abort("json_object_array_shrink called with negative empty_slots");
+ return array_list_shrink(JC_ARRAY(jso)->c_array, empty_slots);
+}
+
struct json_object *json_object_new_null(void)
{
return NULL;
/* Array type methods */
/** Create a new empty json_object of type json_type_array
+ * If you know the array size you'll need ahead of time, use
+ * json_object_new_array_ext() instead.
+ * @see json_object_new_array_ext()
+ * @see json_object_array_shrink()
* @returns a json_object of type json_type_array
*/
JSON_EXPORT struct json_object *json_object_new_array(void);
+JSON_EXPORT struct json_object *json_object_new_array_ext(int initial_size);
+
/** Get the arraylist of a json_object of type json_type_array
* @param obj the json_object instance
* @returns an arraylist
*/
JSON_EXPORT int json_object_array_del_idx(struct json_object *obj, size_t idx, size_t count);
+/**
+ * Shrink the internal memory allocation of the array to just
+ * enough to fit the number of elements in it, plus empty_slots.
+ *
+ * @param jso the json_object instance, must be json_type_array
+ * @param empty_slots the number of empty slots to leave allocated
+ */
+JSON_EXPORT int json_object_array_shrink(struct json_object *jso, int empty_slots);
+
/* json_bool type methods */
/** Create a new empty json_object of type json_type_boolean
case json_tokener_state_array:
if (c == ']')
{
+ // Minimize memory usage; assume parsed objs are unlikely to be changed
+ json_object_array_shrink(current, 0);
+
if (state == json_tokener_state_array_after_sep &&
(tok->flags & JSON_TOKENER_STRICT))
{
case json_tokener_state_array_sep:
if (c == ']')
{
+ // Minimize memory usage; assume parsed objs are unlikely to be changed
+ json_object_array_shrink(current, 0);
+
saved_state = json_tokener_state_finish;
state = json_tokener_state_eatws;
}