cJSON_CreateConfiguration, cJSON_ConfigurationChange{Allocators,Userdata}

This commit is contained in:
Max Bruckner 2018-02-01 22:13:39 +01:00
parent 877fac0f90
commit 9d801d64ea
4 changed files with 319 additions and 3 deletions

136
cJSON.c
View File

@ -60,6 +60,10 @@
#define true ((cJSON_bool)1)
#define false ((cJSON_bool)0)
#ifndef SIZE_MAX
#define SIZE_MAX ((size_t)-1)
#endif
typedef struct {
const unsigned char *json;
size_t position;
@ -124,8 +128,6 @@ typedef struct internal_configuration
cJSON_bool case_sensitive;
cJSON_Allocators allocators;
void *userdata;
} internal_configuration;
#if defined(_MSC_VER)
@ -193,6 +195,9 @@ static void deallocate(const internal_configuration * const configuration, void
NULL /* no userdata */\
}
/* this is necessary to assign the default configuration after initialization */
static const internal_configuration global_default_configuration = default_configuration;
static internal_configuration global_configuration = default_configuration;
static unsigned char* custom_strdup(const unsigned char* string, const internal_configuration * const configuration)
@ -2880,6 +2885,133 @@ CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item)
return (item->type & 0xFF) == cJSON_Raw;
}
static size_t get_size_from_number(const cJSON * const number)
{
if (number->valuedouble >= SIZE_MAX)
{
return SIZE_MAX;
}
if (number->valuedouble <= 0)
{
return 0;
}
return (size_t)number->valuedouble;
}
CJSON_PUBLIC(cJSON_Configuration) cJSON_CreateConfiguration(const cJSON * const json, const cJSON_Allocators * const allocators, void *allocator_userdata)
{
internal_configuration *configuration = NULL;
cJSON *option = NULL;
const cJSON_Allocators *local_allocators = &global_configuration.allocators;
if (allocators != NULL)
{
if ((allocators->allocate == NULL) || (allocators->deallocate == NULL))
{
goto fail;
}
local_allocators = allocators;
}
if ((json != NULL) && !cJSON_IsObject(json))
{
goto fail;
}
configuration = (internal_configuration*)local_allocators->allocate(sizeof(internal_configuration), allocator_userdata);
if (configuration == NULL)
{
goto fail;
}
/* initialize with the default */
*configuration = global_default_configuration;
configuration->userdata = allocator_userdata;
configuration->allocators = *local_allocators;
if (json == NULL)
{
/* default configuration */
return configuration;
}
/* then overwrite with other options if they exist */
option = get_object_item(json, "buffer_size", &global_configuration);
if (cJSON_IsNumber(option))
{
configuration->buffer_size = get_size_from_number(option);
}
option = get_object_item(json, "format", &global_configuration);
if (cJSON_IsTrue(option))
{
configuration->format = true;
}
else if (cJSON_IsFalse(option))
{
configuration->format = false;
}
option = get_object_item(json, "case_sensitive", &global_configuration);
if (cJSON_IsTrue(option))
{
configuration->case_sensitive = true;
}
else if (cJSON_IsFalse(option))
{
configuration->case_sensitive = false;
}
option = get_object_item(json, "allow_data_after_json", &global_configuration);
if (cJSON_IsTrue(option))
{
configuration->allow_data_after_json = true;
}
else if (cJSON_IsFalse(option))
{
configuration->allow_data_after_json = false;
}
return (cJSON_Configuration)configuration;
fail:
if (configuration != NULL)
{
local_allocators->deallocate(configuration, allocator_userdata);
}
return NULL;
}
CJSON_PUBLIC(cJSON_Configuration) cJSON_ConfigurationChangeAllocators(cJSON_Configuration configuration, const cJSON_Allocators allocators)
{
if ((configuration == NULL) || (allocators.allocate == NULL) || (allocators.deallocate == NULL))
{
return NULL;
}
((internal_configuration*)configuration)->allocators = allocators;
((internal_configuration*)configuration)->userdata = NULL;
return configuration;
}
/* Change the allocator userdata attached to a cJSON_Configuration */
CJSON_PUBLIC(cJSON_Configuration) cJSON_ConfigurationChangeUserdata(cJSON_Configuration configuration, void *userdata)
{
if (configuration == NULL)
{
return NULL;
}
((internal_configuration*)configuration)->userdata = userdata;
return configuration;
}
static cJSON_bool compare(const cJSON * const a, const cJSON * const b, const internal_configuration * const configuration)
{
if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a))

42
cJSON.h
View File

@ -87,6 +87,7 @@ typedef struct cJSON_Allocators
} cJSON_Allocators;
typedef int cJSON_bool;
typedef void* cJSON_Configuration;
#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
#define __WINDOWS__
@ -140,7 +141,46 @@ then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJ
/* returns the version of cJSON as a string */
CJSON_PUBLIC(const char*) cJSON_Version(void);
/* Supply malloc, realloc and free functions to cJSON */
/* Create a configuration object that can be passed to several functions
* to configure their behavior.
* A configuration is given in JSON form (case sensitive) and can optionally contain any
* of the following options:
* - buffer_size: number of bytes that the printbuffer should be initially
* - format: boolean that indicates if the output should be formatted
* - case_sensitive: boolean that indicates if object keys should be considered case_sensitive
* - allow_data_after_json: boolean that indicates if parsing succeeds if the JSON in the
* input is followed by non JSON data
*
*
* If NULL is passed to a function that expects an object of type cJSON_Configuration,
* the following default configuration is used:
* {
* "buffer_size": 256,
* "format": true,
* "case_sensitive": true,
* "allow_data_after_json": true
* }
*
* A cJSON_Configuration object is dynamically allocated and you are responsible to free it
* after use.
*
* If allocators is a NULL pointer, the global default allocators are used (the one that is set
* by cJSON_InitHooks, malloc/free by default).
* The allocator is automatically attached to the configuration, so it will be used by functions
* that the configuration is passed to. This can be changed later with
* cJSON_ConfigurationChangeAllocator.
*
* allocator_userdata can be used to pass custom data to your allocator. It also gets attached to
* the configuration automatically. This can later be changed with
* cJSON_ConfigurationChangeUserdata.
* */
CJSON_PUBLIC(cJSON_Configuration) cJSON_CreateConfiguration(const cJSON * const json, const cJSON_Allocators * const allocators, void *allocator_userdata);
/* Change the allocators of a cJSON_Configuration and reset the userdata */
CJSON_PUBLIC(cJSON_Configuration) cJSON_ConfigurationChangeAllocators(cJSON_Configuration configuration, const cJSON_Allocators allocators);
/* Change the allocator userdata attached to a cJSON_Configuration */
CJSON_PUBLIC(cJSON_Configuration) cJSON_ConfigurationChangeUserdata(cJSON_Configuration configuration, void *userdata);
/* Supply malloc and free functions to cJSON globally */
CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */

View File

@ -57,6 +57,7 @@ if(ENABLE_CJSON_TEST)
compare_tests
cjson_add
readme_examples
configuration_tests
)
option(ENABLE_VALGRIND OFF "Enable the valgrind memory checker for the tests.")

143
tests/configuration_tests.c Normal file
View File

@ -0,0 +1,143 @@
/*
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "unity/examples/unity_config.h"
#include "unity/src/unity.h"
#include "common.h"
static void create_configuration_should_create_a_configuration(void)
{
cJSON *json = NULL;
internal_configuration *configuration = NULL;
int userdata = 1;
json = cJSON_Parse("{\"buffer_size\":1024,\"format\":false,\"case_sensitive\":false,\"allow_data_after_json\":false}");
TEST_ASSERT_NOT_NULL(json);
configuration = (internal_configuration*)cJSON_CreateConfiguration(json, NULL, &userdata);
cJSON_Delete(json);
json = NULL;
TEST_ASSERT_NOT_NULL(configuration);
TEST_ASSERT_EQUAL_MESSAGE(configuration->buffer_size, 1024, "buffer_size has an incorrect value.");
TEST_ASSERT_FALSE_MESSAGE(configuration->format, "format has an incorrect value.");
TEST_ASSERT_FALSE_MESSAGE(configuration->case_sensitive, "case_sensitive has an incorrect value.");
TEST_ASSERT_FALSE_MESSAGE(configuration->allow_data_after_json, "allow_data_after_json has an incorrect value.");
TEST_ASSERT_TRUE_MESSAGE(configuration->userdata == &userdata, "Incorrect userdata");
TEST_ASSERT_TRUE_MESSAGE(global_allocate_wrapper == configuration->allocators.allocate, "Wrong malloc.");
TEST_ASSERT_TRUE_MESSAGE(global_reallocate_wrapper == configuration->allocators.reallocate, "Wrong realloc.");
TEST_ASSERT_TRUE_MESSAGE(global_deallocate_wrapper == configuration->allocators.deallocate, "Wrong realloc.");
free(configuration);
}
static void create_configuration_should_work_with_an_empty_object(void)
{
internal_configuration *configuration = NULL;
int userdata = 1;
configuration = (internal_configuration*)cJSON_CreateConfiguration(NULL, NULL, &userdata);
TEST_ASSERT_NOT_NULL(configuration);
TEST_ASSERT_EQUAL_MESSAGE(configuration->buffer_size, 256, "buffer_size has an incorrect value.");
TEST_ASSERT_TRUE_MESSAGE(configuration->format, "format has an incorrect value.");
TEST_ASSERT_TRUE_MESSAGE(configuration->case_sensitive, "case_sensitive has an incorrect value.");
TEST_ASSERT_TRUE_MESSAGE(configuration->allow_data_after_json, "allow_data_after_json has an incorrect value.");
TEST_ASSERT_TRUE_MESSAGE(configuration->userdata == &userdata, "Incorrect userdata");
TEST_ASSERT_TRUE_MESSAGE(global_allocate_wrapper == configuration->allocators.allocate, "Wrong malloc.");
TEST_ASSERT_TRUE_MESSAGE(global_reallocate_wrapper == configuration->allocators.reallocate, "Wrong realloc.");
TEST_ASSERT_TRUE_MESSAGE(global_deallocate_wrapper == configuration->allocators.deallocate, "Wrong free.");
free(configuration);
}
static void* custom_allocator(size_t size, void *userdata)
{
*((size_t*)userdata) = size;
return malloc(size);
}
static void custom_deallocator(void *pointer, void *userdata)
{
*((size_t*)userdata) = (size_t)pointer;
free(pointer);
}
static void create_configuration_should_take_custom_allocators(void)
{
internal_configuration *configuration = NULL;
cJSON_Allocators allocators = {custom_allocator, custom_deallocator, NULL};
size_t userdata = 0;
configuration = (internal_configuration*)cJSON_CreateConfiguration(NULL, &allocators, &userdata);
TEST_ASSERT_NOT_NULL(configuration);
TEST_ASSERT_EQUAL_MESSAGE(userdata, sizeof(internal_configuration), "custom allocator wasn't run properly.");
TEST_ASSERT_TRUE_MESSAGE(custom_allocator == configuration->allocators.allocate, "Wrong allocator.");
TEST_ASSERT_TRUE_MESSAGE(custom_deallocator == configuration->allocators.deallocate, "Wrong deallocator.");
TEST_ASSERT_NULL_MESSAGE(configuration->allocators.reallocate, "Reallocator is not null");
custom_deallocator(configuration, &userdata);
}
static void configuration_change_allocators_should_change_allocators(void)
{
internal_configuration *configuration = NULL;
cJSON_Allocators allocators = {custom_allocator, custom_deallocator, NULL};
size_t userdata = 0;
configuration = (internal_configuration*)cJSON_CreateConfiguration(NULL, &allocators, &userdata);
TEST_ASSERT_NOT_NULL(configuration);
configuration = (internal_configuration*)cJSON_ConfigurationChangeAllocators(configuration, allocators);
TEST_ASSERT_NOT_NULL(configuration);
TEST_ASSERT_TRUE_MESSAGE(custom_allocator == configuration->allocators.allocate, "Wrong allocator.");
TEST_ASSERT_TRUE_MESSAGE(custom_deallocator == configuration->allocators.deallocate, "Wrong deallocator.");
TEST_ASSERT_NULL_MESSAGE(configuration->allocators.reallocate, "Reallocator is not null");
custom_deallocator(configuration, &userdata);
}
static void configuration_change_userdata_should_change_userdata(void)
{
internal_configuration *configuration = NULL;
size_t userdata = 0;
configuration = (internal_configuration*)cJSON_CreateConfiguration(NULL, NULL, NULL);
TEST_ASSERT_NOT_NULL(configuration);
configuration = (internal_configuration*)cJSON_ConfigurationChangeUserdata(configuration, &userdata);
TEST_ASSERT_TRUE_MESSAGE(configuration->userdata == &userdata, "Userdata is incorrect.");
free(configuration);
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(create_configuration_should_create_a_configuration);
RUN_TEST(create_configuration_should_work_with_an_empty_object);
RUN_TEST(create_configuration_should_take_custom_allocators);
RUN_TEST(configuration_change_allocators_should_change_allocators);
RUN_TEST(configuration_change_userdata_should_change_userdata);
return UNITY_END();
}