Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a function to print to pre-allocated buffer #72

Closed
wants to merge 12 commits into from
23 changes: 12 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ UTILS_LIBNAME = libcjson_utils
CJSON_TEST = cJSON_test
UTILS_TEST = cJSON_test_utils

CJSON_TEST_SRC = cJSON.c test.c
UTILS_TEST_SRC = cJSON.c cJSON_Utils.c test_utils.c

LDLIBS = -lm

LIBVERSION = 1.0.2
Expand All @@ -20,7 +23,7 @@ INSTALL_LIBRARY_PATH = $(DESTDIR)$(PREFIX)/$(LIBRARY_PATH)

INSTALL ?= cp -a

R_CFLAGS = -fPIC -std=c89 -pedantic -Wall -Werror -Wstrict-prototypes -Wwrite-strings $(CFLAGS)
R_CFLAGS = -fPIC -std=c89 -pedantic -Wall -Werror -Wstrict-prototypes -Wwrite-strings -Wshadow -Winit-self -Wcast-align -Wformat=2 -Wmissing-prototypes $(CFLAGS)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't try to readd changes that I merged to the master branch already. I will handle merge conflicts myself.

uname := $(shell sh -c 'uname -s 2>/dev/null || echo false')

Expand All @@ -45,8 +48,6 @@ UTILS_SHARED_VERSION = $(UTILS_LIBNAME).$(SHARED).$(LIBVERSION)
UTILS_SHARED_SO = $(UTILS_LIBNAME).$(SHARED).$(UTILS_SOVERSION)
UTILS_STATIC = $(UTILS_LIBNAME).$(STATIC)

SHARED_CMD = $(CC) -shared -o

.PHONY: all shared static tests clean install

all: shared static tests
Expand All @@ -62,15 +63,15 @@ test: tests
./$(UTILS_TEST)

.c.o:
$(CC) -ansi -pedantic -c $(R_CFLAGS) $<
$(CC) -c $(R_CFLAGS) $<

#tests
#cJSON
$(CJSON_TEST): cJSON.c cJSON.h test.c
$(CC) $^ -o $@ $(LDLIBS) -I.
$(CJSON_TEST): $(CJSON_TEST_SRC) cJSON.h
$(CC) $(R_CFLAGS) $(CJSON_TEST_SRC) -o $@ $(LDLIBS) -I.
#cJSON_Utils
$(UTILS_TEST): cJSON.c cJSON.h test.c
$(CC) $^ -o $@ $(LDLIBS) -I.
$(UTILS_TEST): $(UTILS_TEST_SRC) cJSON.h cJSON_Utils.h
$(CC) $(R_CFLAGS) $(UTILS_TEST_SRC) -o $@ $(LDLIBS) -I.

#static libraries
#cJSON
Expand All @@ -83,10 +84,10 @@ $(UTILS_STATIC): $(UTILS_OBJ)
#shared libraries .so.1.0.0
#cJSON
$(CJSON_SHARED_VERSION): $(CJSON_OBJ)
$(CC) -shared -o $@ $< $(LDFLAGS)
$(CC) $(R_CFLAGS) -shared -o $@ $< $(LDFLAGS)
#cJSON_Utils
$(UTILS_SHARED_VERSION): $(UTILS_OBJ)
$(CC) -shared -o $@ $< $(LDFLAGS)
$(UTILS_SHARED_VERSION): $(UTILS_OBJ) $(CJSON_OBJ)
$(CC) $(R_CFLAGS) -shared -o $@ $< $(CJSON_OBJ) $(LDFLAGS)

#objects
#cJSON
Expand Down
26 changes: 24 additions & 2 deletions cJSON.c
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ typedef struct
char *buffer;
int length;
int offset;
cjbool noalloc;
} printbuffer;

/* realloc printbuffer if necessary to have at least "needed" bytes more */
Expand All @@ -261,12 +262,17 @@ static char* ensure(printbuffer *p, int needed)
return p->buffer + p->offset;
}

if (p->noalloc) {
return NULL;
}

newsize = pow2gt(needed);
newbuffer = (char*)cJSON_malloc(newsize);
if (!newbuffer)
{
cJSON_free(p->buffer);
p->length = 0;
p->noalloc = false;
p->buffer = NULL;

return NULL;
Expand Down Expand Up @@ -882,10 +888,20 @@ char *cJSON_PrintBuffered(const cJSON *item, int prebuffer, cjbool fmt)
}
p.length = prebuffer;
p.offset = 0;
p.noalloc = false;

return print_value(item, 0, fmt, &p);
}

int cJSON_PrintPreallocated(cJSON *item,char *buf, const int len, const cjbool fmt)
{
printbuffer p;
p.buffer = buf;
p.length = len;
p.offset = 0;
p.noalloc = true;
return (print_value(item,0,fmt,&p) != NULL ? 0 : -1);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant it exactly the other way around. Think of the return value as a bool. So return print_value(...) != NULL. It returns 1 if it succeeds and 0 if it doesn't.

}

/* Parser core - when encountering text, process appropriately. */
static const char *parse_value(cJSON *item, const char *value, const char **ep)
Expand Down Expand Up @@ -1137,7 +1153,10 @@ static char *print_array(const cJSON *item, int depth, cjbool fmt, printbuffer *
child = item->child;
while (child && !fail)
{
print_value(child, depth + 1, fmt, p);
if (!print_value(child, depth + 1, fmt, p))
{
return NULL;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change this to:

if (!print_value(child, depth + 1, fmt, p))
{
    return NULL;
}

Thank you for finding this problem btw. after that I was taking a closer look to the rest of the library and there are quite some cases where out of memory errors are not handled properly.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will also fix these other cases in the next release.

p->offset = update(p);
if (child->next)
{
Expand Down Expand Up @@ -1451,7 +1470,10 @@ static char *print_object(const cJSON *item, int depth, cjbool fmt, printbuffer
p->offset+=len;

/* print value */
print_value(child, depth, fmt, p);
if (!print_value(child, depth, fmt, p))
{
return NULL;
};
p->offset = update(p);

/* print comma if not last */
Expand Down
2 changes: 2 additions & 0 deletions cJSON.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ extern char *cJSON_Print(const cJSON *item);
extern char *cJSON_PrintUnformatted(const cJSON *item);
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
extern char *cJSON_PrintBuffered(const cJSON *item, int prebuffer, int fmt);
/* Render a cJSON entity to text using a buffer already allocated in memory with length buf_len */
extern int cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const int fmt);
/* Delete a cJSON entity and all subentities. */
extern void cJSON_Delete(cJSON *c);

Expand Down
107 changes: 83 additions & 24 deletions test.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cJSON.h"

/* Parse text to JSON, then render back to text, and print! */
Expand Down Expand Up @@ -81,6 +82,63 @@ struct record
const char *country;
};


/* Create a bunch of objects as demonstration. */
int print_preallocated(cJSON *root)
{
/* declarations */
char *out = NULL;
char *buf = NULL;
char *buf_fail = NULL;
int len = 0;
int len_fail = 0;

/* formatted print */
out = cJSON_Print(root);

/* create buffer to succeed */
/* the extra 64 bytes are in case a floating point value is printed */
len = strlen(out) + 64;
buf = malloc(len);

/* create buffer to fail */
len_fail = strlen(out);
buf_fail = malloc(len_fail);

/* Print to buffer */
if (cJSON_PrintPreallocated(root, buf, len, 1) != 0) {
printf("cJSON_PrintPreallocated failed!\n");
if (strcmp(out, buf) != 0) {
printf("cJSON_PrintPreallocated not the same as cJSON_Print!\n");
printf("cJSON_Print result:\n%s\n", out);
printf("cJSON_PrintPreallocated result:\n%s\n", buf);
}
free(out);
free(buf_fail);
free(buf);
return -1;
}

/* success */
printf("%s\n", buf);

/* force it to fail */
if (cJSON_PrintPreallocated(root, buf_fail, len_fail, 1) == 0) {
printf("cJSON_PrintPreallocated failed to show error with insufficient memory!\n");
printf("cJSON_Print result:\n%s\n", out);
printf("cJSON_PrintPreallocated result:\n%s\n", buf_fail);
free(out);
free(buf_fail);
free(buf);
return -1;
}

free(out);
free(buf_fail);
free(buf);
return 0;
}

/* Create a bunch of objects as demonstration. */
void create_objects(void)
{
Expand All @@ -90,7 +148,6 @@ void create_objects(void)
cJSON *img = NULL;
cJSON *thm = NULL;
cJSON *fld = NULL;
char *out = NULL;
int i = 0;

/* Our "days of the week" array: */
Expand Down Expand Up @@ -152,21 +209,20 @@ void create_objects(void)
cJSON_AddNumberToObject(fmt, "frame rate", 24);

/* Print to text */
out = cJSON_Print(root);
/* Delete the cJSON */
if (print_preallocated(root) != 0) {
cJSON_Delete(root);
exit(EXIT_FAILURE);
}
cJSON_Delete(root);
/* print it */
printf("%s\n",out);
/* release the string */
free(out);

/* Our "days of the week" array: */
root = cJSON_CreateStringArray(strings, 7);

out = cJSON_Print(root);
if (print_preallocated(root) != 0) {
cJSON_Delete(root);
exit(EXIT_FAILURE);
}
cJSON_Delete(root);
printf("%s\n", out);
free(out);

/* Our matrix: */
root = cJSON_CreateArray();
Expand All @@ -177,11 +233,11 @@ void create_objects(void)

/* cJSON_ReplaceItemInArray(root, 1, cJSON_CreateString("Replacement")); */

out = cJSON_Print(root);
if (print_preallocated(root) != 0) {
cJSON_Delete(root);
exit(EXIT_FAILURE);
}
cJSON_Delete(root);
printf("%s\n", out);
free(out);


/* Our "gallery" item: */
root = cJSON_CreateObject();
Expand All @@ -195,13 +251,13 @@ void create_objects(void)
cJSON_AddStringToObject(thm, "Width", "100");
cJSON_AddItemToObject(img, "IDs", cJSON_CreateIntArray(ids, 4));

out = cJSON_Print(root);
if (print_preallocated(root) != 0) {
cJSON_Delete(root);
exit(EXIT_FAILURE);
}
cJSON_Delete(root);
printf("%s\n", out);
free(out);

/* Our array of "records": */

root = cJSON_CreateArray();
for (i = 0; i < 2; i++)
{
Expand All @@ -218,17 +274,20 @@ void create_objects(void)

/* cJSON_ReplaceItemInObject(cJSON_GetArrayItem(root, 1), "City", cJSON_CreateIntArray(ids, 4)); */

out = cJSON_Print(root);
if (print_preallocated(root) != 0) {
cJSON_Delete(root);
exit(EXIT_FAILURE);
}
cJSON_Delete(root);
printf("%s\n", out);
free(out);

root = cJSON_CreateObject();
cJSON_AddNumberToObject(root, "number", 1.0 / zero);
out = cJSON_Print(root);

if (print_preallocated(root) != 0) {
cJSON_Delete(root);
exit(EXIT_FAILURE);
}
cJSON_Delete(root);
printf("%s\n", out);
free(out);
}

int main(void)
Expand Down