1
0
mirror of https://github.com/kgabis/parson.git synced 2025-01-14 06:12:54 +08:00

1.5.0: Using memcpy instead of sprintf for string literals, adds json_set_number_serialization_function

This commit is contained in:
Alexandru Ardelean 2022-11-12 23:00:17 +02:00 committed by GitHub
parent 29db75d970
commit 1314bf8ad6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 75 additions and 35 deletions

View File

@ -3,7 +3,7 @@ project(parson C)
include (GNUInstallDirs) include (GNUInstallDirs)
set(PARSON_VERSION 1.4.0) set(PARSON_VERSION 1.5.0)
add_library(parson parson.c) add_library(parson parson.c)
target_include_directories(parson PUBLIC $<INSTALL_INTERFACE:include>) target_include_directories(parson PUBLIC $<INSTALL_INTERFACE:include>)

View File

@ -1,5 +1,5 @@
project('parson', 'c', project('parson', 'c',
version : '1.4.0', # !!! also increment lib_so_version !!! version : '1.5.0',
license : 'MIT', license : 'MIT',
meson_version : '>=0.46.0', meson_version : '>=0.46.0',
default_options : [ default_options : [

View File

@ -1,6 +1,6 @@
{ {
"name": "parson", "name": "parson",
"version": "1.4.0", "version": "1.5.0",
"repo": "kgabis/parson", "repo": "kgabis/parson",
"description": "Small json parser and reader", "description": "Small json parser and reader",
"keywords": [ "json", "parser" ], "keywords": [ "json", "parser" ],

View File

@ -1,7 +1,7 @@
/* /*
SPDX-License-Identifier: MIT SPDX-License-Identifier: MIT
Parson 1.4.0 (https://github.com/kgabis/parson) Parson 1.5.0 (https://github.com/kgabis/parson)
Copyright (c) 2012 - 2022 Krzysztof Gabis Copyright (c) 2012 - 2022 Krzysztof Gabis
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
@ -31,7 +31,7 @@
#include "parson.h" #include "parson.h"
#define PARSON_IMPL_VERSION_MAJOR 1 #define PARSON_IMPL_VERSION_MAJOR 1
#define PARSON_IMPL_VERSION_MINOR 4 #define PARSON_IMPL_VERSION_MINOR 5
#define PARSON_IMPL_VERSION_PATCH 0 #define PARSON_IMPL_VERSION_PATCH 0
#if (PARSON_VERSION_MAJOR != PARSON_IMPL_VERSION_MAJOR)\ #if (PARSON_VERSION_MAJOR != PARSON_IMPL_VERSION_MAJOR)\
@ -71,6 +71,10 @@
#define PARSON_NUM_BUF_SIZE 64 /* double printed with "%1.17g" shouldn't be longer than 25 bytes so let's be paranoid and use 64 */ #define PARSON_NUM_BUF_SIZE 64 /* double printed with "%1.17g" shouldn't be longer than 25 bytes so let's be paranoid and use 64 */
#endif #endif
#ifndef PARSON_INDENT_STR
#define PARSON_INDENT_STR " "
#endif
#define SIZEOF_TOKEN(a) (sizeof(a) - 1) #define SIZEOF_TOKEN(a) (sizeof(a) - 1)
#define SKIP_CHAR(str) ((*str)++) #define SKIP_CHAR(str) ((*str)++)
#define SKIP_WHITESPACES(str) while (isspace((unsigned char)(**str))) { SKIP_CHAR(str); } #define SKIP_WHITESPACES(str) while (isspace((unsigned char)(**str))) { SKIP_CHAR(str); }
@ -94,6 +98,8 @@ static int parson_escape_slashes = 1;
static char *parson_float_format = NULL; static char *parson_float_format = NULL;
static JSON_Number_Serialization_Function parson_number_serialization_function = NULL;
#define IS_CONT(b) (((unsigned char)(b) & 0xC0) == 0x80) /* is utf-8 continuation byte */ #define IS_CONT(b) (((unsigned char)(b) & 0xC0) == 0x80) /* is utf-8 continuation byte */
typedef int parson_bool_t; typedef int parson_bool_t;
@ -192,8 +198,6 @@ static JSON_Value * parse_value(const char **string, size_t nesting);
/* Serialization */ /* Serialization */
static int json_serialize_to_buffer_r(const JSON_Value *value, char *buf, int level, parson_bool_t is_pretty, char *num_buf); static int json_serialize_to_buffer_r(const JSON_Value *value, char *buf, int level, parson_bool_t is_pretty, char *num_buf);
static int json_serialize_string(const char *string, size_t len, char *buf); static int json_serialize_string(const char *string, size_t len, char *buf);
static int append_indent(char *buf, int level);
static int append_string(char *buf, const char *string);
/* Various */ /* Various */
static char * read_file(const char * filename) { static char * read_file(const char * filename) {
@ -1095,15 +1099,27 @@ static JSON_Value * parse_null_value(const char **string) {
} }
/* Serialization */ /* Serialization */
#define APPEND_STRING(str) do { written = append_string(buf, (str));\
if (written < 0) { return -1; }\
if (buf != NULL) { buf += written; }\
written_total += written; } while(0)
#define APPEND_INDENT(level) do { written = append_indent(buf, (level));\ /* APPEND_STRING() is only called on string literals.
if (written < 0) { return -1; }\ It's a bit hacky because it makes plenty of assumptions about the external state
if (buf != NULL) { buf += written; }\ and should eventually be tidied up into a function (same goes for APPEND_INDENT)
written_total += written; } while(0) */
#define APPEND_STRING(str) do {\
written = SIZEOF_TOKEN((str));\
if (buf != NULL) {\
memcpy(buf, (str), written);\
buf[written] = '\0';\
buf += written;\
}\
written_total += written;\
} while (0)
#define APPEND_INDENT(level) do {\
int level_i = 0;\
for (level_i = 0; level_i < (level); level_i++) {\
APPEND_STRING(PARSON_INDENT_STR);\
}\
} while (0)
static int json_serialize_to_buffer_r(const JSON_Value *value, char *buf, int level, parson_bool_t is_pretty, char *num_buf) static int json_serialize_to_buffer_r(const JSON_Value *value, char *buf, int level, parson_bool_t is_pretty, char *num_buf)
{ {
@ -1225,7 +1241,9 @@ static int json_serialize_to_buffer_r(const JSON_Value *value, char *buf, int le
if (buf != NULL) { if (buf != NULL) {
num_buf = buf; num_buf = buf;
} }
if (parson_float_format) { if (parson_number_serialization_function) {
written = parson_number_serialization_function(num, num_buf);
} else if (parson_float_format) {
written = sprintf(num_buf, parson_float_format, num); written = sprintf(num_buf, parson_float_format, num);
} else { } else {
written = sprintf(num_buf, PARSON_DEFAULT_FLOAT_FORMAT, num); written = sprintf(num_buf, PARSON_DEFAULT_FLOAT_FORMAT, num);
@ -1315,22 +1333,6 @@ static int json_serialize_string(const char *string, size_t len, char *buf) {
return written_total; return written_total;
} }
static int append_indent(char *buf, int level) {
int i;
int written = -1, written_total = 0;
for (i = 0; i < level; i++) {
APPEND_STRING(" ");
}
return written_total;
}
static int append_string(char *buf, const char *string) {
if (buf == NULL) {
return (int)strlen(string);
}
return sprintf(buf, "%s", string);
}
#undef APPEND_STRING #undef APPEND_STRING
#undef APPEND_INDENT #undef APPEND_INDENT
@ -2450,3 +2452,7 @@ void json_set_float_serialization_format(const char *format) {
} }
parson_float_format = parson_strdup(format); parson_float_format = parson_strdup(format);
} }
void json_set_number_serialization_function(JSON_Number_Serialization_Function func) {
parson_number_serialization_function = func;
}

View File

@ -1,7 +1,7 @@
/* /*
SPDX-License-Identifier: MIT SPDX-License-Identifier: MIT
Parson 1.4.0 (https://github.com/kgabis/parson) Parson 1.5.0 (https://github.com/kgabis/parson)
Copyright (c) 2012 - 2022 Krzysztof Gabis Copyright (c) 2012 - 2022 Krzysztof Gabis
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
@ -35,10 +35,10 @@ extern "C"
#endif #endif
#define PARSON_VERSION_MAJOR 1 #define PARSON_VERSION_MAJOR 1
#define PARSON_VERSION_MINOR 4 #define PARSON_VERSION_MINOR 5
#define PARSON_VERSION_PATCH 0 #define PARSON_VERSION_PATCH 0
#define PARSON_VERSION_STRING "1.4.0" #define PARSON_VERSION_STRING "1.5.0"
#include <stddef.h> /* size_t */ #include <stddef.h> /* size_t */
@ -67,6 +67,12 @@ typedef int JSON_Status;
typedef void * (*JSON_Malloc_Function)(size_t); typedef void * (*JSON_Malloc_Function)(size_t);
typedef void (*JSON_Free_Function)(void *); typedef void (*JSON_Free_Function)(void *);
/* A function used for serializing numbers (see json_set_number_serialization_function).
If 'buf' is null then it should return number of bytes that would've been written
(but not more than PARSON_NUM_BUF_SIZE).
*/
typedef int (*JSON_Number_Serialization_Function)(double num, char *buf);
/* Call only once, before calling any other function from parson API. If not called, malloc and free /* Call only once, before calling any other function from parson API. If not called, malloc and free
from stdlib will be used for all allocations */ from stdlib will be used for all allocations */
void json_set_allocation_functions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun); void json_set_allocation_functions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun);
@ -80,6 +86,10 @@ void json_set_escape_slashes(int escape_slashes);
If format is null then the default format is used. */ If format is null then the default format is used. */
void json_set_float_serialization_format(const char *format); void json_set_float_serialization_format(const char *format);
/* Sets a function that will be used for serialization of numbers.
If function is null then the default serialization function is used. */
void json_set_number_serialization_function(JSON_Number_Serialization_Function fun);
/* Parses first JSON value in a file, returns NULL in case of error */ /* Parses first JSON value in a file, returns NULL in case of error */
JSON_Value * json_parse_file(const char *filename); JSON_Value * json_parse_file(const char *filename);

24
tests.c
View File

@ -63,6 +63,7 @@ void test_suite_11(void); /* Additional things that require testing */
void test_memory_leaks(void); void test_memory_leaks(void);
void test_failing_allocations(void); void test_failing_allocations(void);
void test_custom_number_format(void); void test_custom_number_format(void);
void test_custom_number_serialization_function(void);
void print_commits_info(const char *username, const char *repo); void print_commits_info(const char *username, const char *repo);
void persistence_example(void); void persistence_example(void);
@ -132,6 +133,7 @@ int tests_main(int argc, char *argv[]) {
test_memory_leaks(); test_memory_leaks();
test_failing_allocations(); test_failing_allocations();
test_custom_number_format(); test_custom_number_format();
test_custom_number_serialization_function();
printf("Tests failed: %d\n", g_tests_failed); printf("Tests failed: %d\n", g_tests_failed);
printf("Tests passed: %d\n", g_tests_passed); printf("Tests passed: %d\n", g_tests_passed);
@ -704,6 +706,28 @@ void test_custom_number_format() {
json_value_free(val); json_value_free(val);
} }
static int custom_serialization_func_called = 0;
static int custom_serialization_func(double num, char *buf) {
char num_buf[32];
custom_serialization_func_called = 1;
if (buf == NULL)
buf = num_buf;
return sprintf(buf, "%.1f", num);
}
void test_custom_number_serialization_function() {
/* We just test that custom_serialization_func() gets called, not it's performance */
char *serialized = NULL;
JSON_Value *val = json_value_init_number(0.6);
json_set_number_serialization_function(custom_serialization_func);
serialized = json_serialize_to_string(val);
TEST(STREQ(serialized, "0.6"));
TEST(custom_serialization_func_called);
json_set_number_serialization_function(NULL);
json_free_serialized_string(serialized);
json_value_free(val);
}
void print_commits_info(const char *username, const char *repo) { void print_commits_info(const char *username, const char *repo) {
JSON_Value *root_value; JSON_Value *root_value;
JSON_Array *commits; JSON_Array *commits;