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)
set(PARSON_VERSION 1.4.0)
set(PARSON_VERSION 1.5.0)
add_library(parson parson.c)
target_include_directories(parson PUBLIC $<INSTALL_INTERFACE:include>)

View File

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

View File

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

View File

@ -1,7 +1,7 @@
/*
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
Permission is hereby granted, free of charge, to any person obtaining a copy
@ -31,7 +31,7 @@
#include "parson.h"
#define PARSON_IMPL_VERSION_MAJOR 1
#define PARSON_IMPL_VERSION_MINOR 4
#define PARSON_IMPL_VERSION_MINOR 5
#define PARSON_IMPL_VERSION_PATCH 0
#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 */
#endif
#ifndef PARSON_INDENT_STR
#define PARSON_INDENT_STR " "
#endif
#define SIZEOF_TOKEN(a) (sizeof(a) - 1)
#define SKIP_CHAR(str) ((*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 JSON_Number_Serialization_Function parson_number_serialization_function = NULL;
#define IS_CONT(b) (((unsigned char)(b) & 0xC0) == 0x80) /* is utf-8 continuation byte */
typedef int parson_bool_t;
@ -192,8 +198,6 @@ static JSON_Value * parse_value(const char **string, size_t nesting);
/* 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_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 */
static char * read_file(const char * filename) {
@ -1095,15 +1099,27 @@ static JSON_Value * parse_null_value(const char **string) {
}
/* 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));\
if (written < 0) { return -1; }\
if (buf != NULL) { buf += written; }\
written_total += written; } while(0)
/* APPEND_STRING() is only called on string literals.
It's a bit hacky because it makes plenty of assumptions about the external state
and should eventually be tidied up into a function (same goes for APPEND_INDENT)
*/
#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)
{
@ -1225,7 +1241,9 @@ static int json_serialize_to_buffer_r(const JSON_Value *value, char *buf, int le
if (buf != NULL) {
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);
} else {
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;
}
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_INDENT
@ -2450,3 +2452,7 @@ void json_set_float_serialization_format(const char *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
Parson 1.4.0 (https://github.com/kgabis/parson)
Parson 1.5.0 (https://github.com/kgabis/parson)
Copyright (c) 2012 - 2022 Krzysztof Gabis
Permission is hereby granted, free of charge, to any person obtaining a copy
@ -35,10 +35,10 @@ extern "C"
#endif
#define PARSON_VERSION_MAJOR 1
#define PARSON_VERSION_MINOR 4
#define PARSON_VERSION_MINOR 5
#define PARSON_VERSION_PATCH 0
#define PARSON_VERSION_STRING "1.4.0"
#define PARSON_VERSION_STRING "1.5.0"
#include <stddef.h> /* size_t */
@ -67,6 +67,12 @@ typedef int JSON_Status;
typedef void * (*JSON_Malloc_Function)(size_t);
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
from stdlib will be used for all allocations */
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. */
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 */
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_failing_allocations(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 persistence_example(void);
@ -132,6 +133,7 @@ int tests_main(int argc, char *argv[]) {
test_memory_leaks();
test_failing_allocations();
test_custom_number_format();
test_custom_number_serialization_function();
printf("Tests failed: %d\n", g_tests_failed);
printf("Tests passed: %d\n", g_tests_passed);
@ -704,6 +706,28 @@ void test_custom_number_format() {
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) {
JSON_Value *root_value;
JSON_Array *commits;