From 5d416b9c1f48f833cf53f4fcc04036718a6717b3 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Thu, 23 Jul 2026 10:38:05 +0200 Subject: [PATCH 1/2] fix: handle small buffered dump workspaces --- json_object.h | 2 +- json_print.c | 33 +++++++++++++++----------- tests/Makefile.am | 1 + tests/test_dump_buffered.c | 39 +++++++++++++++++++++++++++++++ tests/test_dump_buffered.expected | 2 ++ tests/test_dump_buffered.test | 6 +++++ 6 files changed, 68 insertions(+), 15 deletions(-) create mode 100644 tests/test_dump_buffered.c create mode 100644 tests/test_dump_buffered.expected create mode 100755 tests/test_dump_buffered.test diff --git a/json_object.h b/json_object.h index ee86acd..92fb0fd 100644 --- a/json_object.h +++ b/json_object.h @@ -249,7 +249,7 @@ extern size_t fjson_object_dump_ext(struct fjson_object *obj, int flags, fjson_w * @param obj object to be written * @param flags extra flags * @param temp your temporary buffer that is used to group calls - * @param size size of your temporary buffer + * @param size size of your temporary buffer; temp may be NULL when size is zero * @param func your function that will be called to write the data * @param ptr pointer that will be passed as first argument to your function */ diff --git a/json_print.c b/json_print.c index ef59826..6479593 100644 --- a/json_print.c +++ b/json_print.c @@ -156,22 +156,30 @@ static size_t buffer_printf(struct buffer *buffer, const char *format, ...) va_list arguments; char *tmp; int size; + size_t available; // make sure we have sufficient room in our buffer - if (buffer->size - buffer->filled < 32) result += buffer_flush(buffer); + if (buffer->size - buffer->filled < 32 && buffer->filled > 0) + result += buffer_flush(buffer); - // initialize varargs - va_start(arguments, format); + available = buffer->size - buffer->filled; + if (available > 0) { + // initialize varargs + va_start(arguments, format); - // write to the buffer (note the extra char for the extra null that is written by vsnprintf()) - size = vsnprintf(buffer->buffer + buffer->filled, buffer->size - buffer->filled - 1, format, arguments); + // write to the buffer + size = vsnprintf(buffer->buffer + buffer->filled, available, format, arguments); - // clean up varargs (it is not possible to reuse the vararg arguments later on, - // the have to be reset and possible reinitialized later on) - va_end(arguments); + // clean up varargs (it is not possible to reuse the vararg arguments later on, + // they have to be reset and possibly reinitialized later on) + va_end(arguments); + } else { + // force use of a dynamically allocated buffer when there is no room + size = 0; + } // was this all successful? - if (size >= 0 && size < (int)(buffer->size - buffer->filled)) + if (size >= 0 && size < (int)available) { // this was a major success buffer->filled += size; @@ -187,17 +195,14 @@ static size_t buffer_printf(struct buffer *buffer, const char *format, ...) va_start(arguments, format); // format into the buffer, again - buffer->size += vsnprintf(buffer->buffer + buffer->filled, - buffer->size - buffer->filled - 1, format, arguments); + buffer->filled += vsnprintf(buffer->buffer + buffer->filled, + buffer->size - buffer->filled, format, arguments); // clean up varargs va_end(arguments); } else { - // initialize varargs - va_start(arguments, format); - // our own buffer is not big enough to fit the text, we are going to use // a dynamically allocated buffer using vasprintf(), init varargs first va_start(arguments, format); diff --git a/tests/Makefile.am b/tests/Makefile.am index 588a22d..ecd99df 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -7,6 +7,7 @@ AM_CFLAGS = $(WARN_CFLAGS) TESTS_DEFAULT= TESTS_DEFAULT+= ucs_copyright_char.test TESTS_DEFAULT+= test_float.test +TESTS_DEFAULT+= test_dump_buffered.test TESTS_DEFAULT+= test1.test TESTS_DEFAULT+= test2.test TESTS_DEFAULT+= test4.test diff --git a/tests/test_dump_buffered.c b/tests/test_dump_buffered.c new file mode 100644 index 0000000..158fd20 --- /dev/null +++ b/tests/test_dump_buffered.c @@ -0,0 +1,39 @@ +/* Copyright (C) 2026 by Rainer Gerhards + * Released under ASL 2.0 */ +#include "config.h" +#include +#include +#include "../json_object.h" + +struct output { + char data[32]; + size_t filled; +}; + +static size_t append(void *ptr, const char *buffer, size_t size) +{ + struct output *output = ptr; + + memcpy(output->data + output->filled, buffer, size); + output->filled += size; + return size; +} + +static void dump(const char *name, char *temp, size_t size, struct fjson_object *json) +{ + struct output output = {{0}, 0}; + + fjson_object_dump_buffered(json, FJSON_TO_STRING_PLAIN, temp, size, append, &output); + printf("%s:%.*s\n", name, (int)output.filled, output.data); +} + +int main(void) +{ + struct fjson_object *json = fjson_object_new_int64(123456789012345); + char small[2]; + + dump("zero", NULL, 0, json); + dump("small", small, sizeof(small), json); + fjson_object_put(json); + return 0; +} diff --git a/tests/test_dump_buffered.expected b/tests/test_dump_buffered.expected new file mode 100644 index 0000000..30ff064 --- /dev/null +++ b/tests/test_dump_buffered.expected @@ -0,0 +1,2 @@ +zero:123456789012345 +small:123456789012345 diff --git a/tests/test_dump_buffered.test b/tests/test_dump_buffered.test new file mode 100755 index 0000000..3cb90f8 --- /dev/null +++ b/tests/test_dump_buffered.test @@ -0,0 +1,6 @@ +#!/bin/sh + +. "${srcdir-.}/test-defs.sh" + +run_output_test test_dump_buffered +exit $? From 2e0085a6f003108f4adeaac85f2123abec30a3db Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Thu, 23 Jul 2026 14:12:06 +0200 Subject: [PATCH 2/2] tests: distribute buffered dump fixture --- tests/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Makefile.am b/tests/Makefile.am index ecd99df..8ca27d8 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -77,6 +77,7 @@ EXTRA_DIST += test2Formatted_spaced.expected EXTRA_DIST += test4.expected EXTRA_DIST += ucs_copyright_char.expected EXTRA_DIST += test_float.expected +EXTRA_DIST += test_dump_buffered.expected EXTRA_DIST += test_cast.expected EXTRA_DIST += test_charcase.expected EXTRA_DIST += test_locale.expected