mirror of
https://github.com/DaveGamble/cJSON.git
synced 2025-01-14 19:48:00 +08:00
cJSON_CreateConfiguration, cJSON_ConfigurationChange{Allocators,Userdata}
This commit is contained in:
parent
877fac0f90
commit
9d801d64ea
136
cJSON.c
136
cJSON.c
@ -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
42
cJSON.h
@ -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. */
|
||||
|
@ -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
143
tests/configuration_tests.c
Normal 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();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user