Commit a4907154 authored by Jussi Kivilinna's avatar Jussi Kivilinna Committed by Pekka Niemimaa
Browse files

netutils/cJSON: add printing to output stream

Patch provides new printing stream engine for cJSON. Benefits are: possibility
to use user provided putc function for output; avoids temporary heap memory
allocations for string printing.

From github:thingsee/thingsee-sdk pull request:

 "Improve cJSON to allow parse/print from/to stream (caller supplied getc/putc functions) for reducing peak memory usage"

 https://github.com/thingsee/thingsee-sdk/pull/6

Signed-off-by: default avatarJussi Kivilinna <jussi.kivilinna@haltian.com>
parent a764aefa
......@@ -33,6 +33,8 @@
#ifndef __APPS_INCLUDE_NETUTILS_JSON_H
#define __APPS_INCLUDE_NETUTILS_JSON_H
#include <stdbool.h>
#ifdef __cplusplus
extern "C"
{
......@@ -127,6 +129,17 @@ char *cJSON_Print(cJSON *item);
char *cJSON_PrintUnformatted(cJSON *item);
/* Render a cJSON entity to text for transfer/storage with or without
* formatting.
*/
void cJSON_Print_Stream(cJSON *item, bool formatted,
void (*putc_fn)(char c, void *priv), void *putc_priv);
/* Render a cJSON item/entity/structure to buffer. */
size_t cJSON_Print_Buf(cJSON *item, bool formatted, char *buf, size_t buflen);
/* Delete a cJSON entity and all subentities. */
void cJSON_Delete(cJSON *c);
......
......@@ -38,7 +38,7 @@
include $(APPDIR)/Make.defs
ASRCS =
CSRCS = cJSON.c cJSON_stream_parse.c
CSRCS = cJSON.c cJSON_stream_parse.c cJSON_stream_print.c
AOBJS = $(ASRCS:.S=$(OBJEXT))
COBJS = $(CSRCS:.c=$(OBJEXT))
......
......@@ -1166,12 +1166,12 @@ cJSON *cJSON_Parse_Old(const char *value)
/* Render a cJSON item/entity/structure to text. */
char *cJSON_Print(cJSON *item)
char *cJSON_Print_Old(cJSON *item)
{
return print_value(item, 0, 1);
}
char *cJSON_PrintUnformatted(cJSON *item)
char *cJSON_PrintUnformatted_Old(cJSON *item)
{
return print_value(item, 0, 0);
}
......
/****************************************************************************
* apps/netutils/json/cJSON_stream_print.c
*
* This file is a part of NuttX:
*
* Copyright (c) 2011 Gregory Nutt. All rights reserved.
* Copyright (c) 2015 Haltian Ltd.
* Author: Jussi Kivilinna <jussi.kivilinna@haltian.com>
*
* And derives from the cJSON Project which has an MIT license:
*
* Copyright (c) 2009 Dave Gamble
*
* 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <float.h>
#include <limits.h>
#include <ctype.h>
#include <unistd.h>
#include <apps/netutils/cJSON.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define cJSON_malloc malloc
#define cJSON_free free
#define cJSON_realloc realloc
#define cJSON_strdup strdup
/****************************************************************************
* Private Types
****************************************************************************/
typedef struct cJSON_outstream_s
{
void (*putc_fn)(char c, void *priv);
void *fn_priv;
} cJSON_outstream;
struct putc_string_priv_s
{
char *buf;
size_t buflen;
size_t numput;
};
/****************************************************************************
* Private Data
****************************************************************************/
/****************************************************************************
* Private Prototypes
****************************************************************************/
static void stream_print_value(cJSON *item, int depth, bool fmt,
cJSON_outstream *stream);
static void stream_print_array(cJSON *item, int depth, bool fmt,
cJSON_outstream *stream);
static void stream_print_object(cJSON *item, int depth, bool fmt,
cJSON_outstream *stream);
/****************************************************************************
* Private Functions
****************************************************************************/
static inline void stream_putc(cJSON_outstream *stream, char c)
{
stream->putc_fn(c, stream->fn_priv);
}
static inline void stream_puts(cJSON_outstream *stream, char *string)
{
while (*string)
{
stream_putc(stream, *string++);
}
}
/* Render the number nicely from the given item into a string. */
static void stream_print_number(cJSON *item, cJSON_outstream *stream)
{
char str[32];
double d = item->valuedouble;
double id = item->valueint;
size_t slen;
int type;
/* Get type and needed length output string. */
if ((d <= INT_MAX && d >= INT_MIN) &&
(fabs(id - d) <= DBL_EPSILON || fabs(floor(d) - d) <= DBL_EPSILON))
{
slen = snprintf(str, sizeof(str), "%d", item->valueint);
type = 1;
}
else if (fabs(d) < 1.0e-6 || fabs(d) > 1.0e9)
{
slen = snprintf(str, sizeof(str), "%e", d);
type = -1;
}
else
{
slen = snprintf(str, sizeof(str), "%.10f", d);
type = 0;
}
DEBUGASSERT(slen < sizeof(str) - 1);
if (type == 0 && strchr(str, '.'))
{
/* Remove trailing zeros. */
slen -= 1;
while (slen > 0)
{
if (str[--slen] != '0')
break;
str[slen] = '\0';
}
/* Remove trailing dot. */
if (slen > 0 && str[slen] == '.')
str[slen] = '\0';
}
stream_puts(stream, str);
}
/* Render the cstring provided to an escaped version that can be printed. */
static void stream_print_string_ptr(const char *str, cJSON_outstream *stream)
{
const char *ptr;
unsigned char token;
if (!str)
{
return;
}
ptr = str;
stream_putc(stream, '\"');
while (*ptr)
{
if ((unsigned char)*ptr > 31 && *ptr != '\"' && *ptr != '\\')
{
stream_putc(stream, *ptr++);
}
else
{
char tmp[6];
stream_putc(stream, '\\');
switch (token = *ptr++)
{
case '\\':
stream_putc(stream, '\\');
break;
case '\"':
stream_putc(stream, '\"');
break;
case '\b':
stream_putc(stream, 'b');
break;
case '\f':
stream_putc(stream, 'f');
break;
case '\n':
stream_putc(stream, 'n');
break;
case '\r':
stream_putc(stream, 'r');
break;
case '\t':
stream_putc(stream, 't');
break;
default:
/* Escape and print */
snprintf(tmp, sizeof(tmp), "u%04x", token);
stream_puts(stream, tmp);
break;
}
}
}
stream_putc(stream, '\"');
}
/* Invote print_string_ptr (which is useful) on an item. */
static void stream_print_string(cJSON *item, cJSON_outstream *stream)
{
stream_print_string_ptr(item->valuestring, stream);
}
/* Render a value to text. */
static void stream_print_value(cJSON *item, int depth, bool fmt,
cJSON_outstream *stream)
{
if (!item)
{
return;
}
switch ((item->type) & 255)
{
case cJSON_NULL:
stream_puts(stream, "null");
break;
case cJSON_False:
stream_puts(stream, "false");
break;
case cJSON_True:
stream_puts(stream, "true");
break;
case cJSON_Number:
stream_print_number(item, stream);
break;
case cJSON_String:
stream_print_string(item, stream);
break;
case cJSON_Array:
stream_print_array(item, depth, fmt, stream);
break;
case cJSON_Object:
stream_print_object(item, depth, fmt, stream);
break;
}
}
/* Render an array to text */
static void stream_print_array(cJSON *item, int depth, bool fmt,
cJSON_outstream *stream)
{
cJSON *child;
/* Compose the output array. */
stream_putc(stream, '[');
child = item->child;
while (child)
{
/* Print all childs. */
stream_print_value(child, depth + 1, fmt, stream);
child = child->next;
if (child)
{
stream_putc(stream, ',');
if (fmt)
{
stream_putc(stream, ' ');
}
}
}
stream_putc(stream, ']');
}
/* Render an object to text. */
static void stream_print_object(cJSON *item, int depth, bool fmt,
cJSON_outstream *stream)
{
cJSON *child;
int j, i;
/* Compose the output object. */
stream_putc(stream, '{');
if (fmt)
{
stream_putc(stream, '\n');
}
child = item->child;
while (child)
{
if (fmt)
{
for (j = 0; j < depth; j++)
{
stream_putc(stream, '\t');
}
}
/* Print object name. */
stream_print_string_ptr(child->string, stream);
stream_putc(stream, ':');
if (fmt)
{
stream_putc(stream, '\t');
}
/* Print object value. */
stream_print_value(child, depth + 1, fmt, stream);
child = child->next;
if (child)
{
stream_putc(stream, ',');
}
if (fmt)
{
stream_putc(stream, '\n');
}
}
if (fmt)
{
for (i = 0; i < depth - 1; i++)
{
stream_putc(stream, '\t');
}
}
stream_putc(stream, '}');
}
void putc_string(char c, void *_priv)
{
struct putc_string_priv_s *priv = _priv;
if (priv->numput >= priv->buflen)
{
priv->numput++;
return;
}
priv->buf[priv->numput++] = c;
}
/* Render a cJSON item/entity/structure to text. */
static char *cJSON_Print_String(cJSON *item, bool formatted)
{
struct putc_string_priv_s priv = {};
size_t num_chars;
char *out;
priv.buf = NULL;
priv.buflen = 0;
priv.numput = 0;
cJSON_Print_Stream(item, formatted, putc_string, &priv);
num_chars = priv.numput;
out = cJSON_malloc(num_chars + 1);
if (!out)
{
return NULL;
}
priv.buf = out;
priv.buflen = num_chars;
priv.numput = 0;
cJSON_Print_Stream(item, formatted, putc_string, &priv);
DEBUGASSERT(priv.numput == num_chars);
out[num_chars] = '\0';
return out;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/* Render a cJSON item/entity/structure to stream function. */
void cJSON_Print_Stream(cJSON *item, bool formatted,
void (*putc_fn)(char c, void *priv), void *putc_priv)
{
cJSON_outstream stream = {};
stream.putc_fn = putc_fn;
stream.fn_priv = putc_priv;
stream_print_value(item, 0, formatted, &stream);
}
/* Render a cJSON item/entity/structure to newly allocated text string. */
char *cJSON_Print(cJSON *item)
{
return cJSON_Print_String(item, true);
}
char *cJSON_PrintUnformatted(cJSON *item)
{
return cJSON_Print_String(item, false);
}
/* Render a cJSON item/entity/structure to buffer. */
size_t cJSON_Print_Buf(cJSON *item, bool formatted, char *buf, size_t buflen)
{
struct putc_string_priv_s priv = {};
priv.buf = buf;
priv.buflen = buflen;
priv.numput = 0;
cJSON_Print_Stream(item, formatted, putc_string, &priv);
return priv.numput;
}
......@@ -4,7 +4,7 @@ include $(APPDIR)/Make.defs
HOSTOBJEXT ?= .hobj
HOSTCSRCS := ../json/cJSON.c ../json/cJSON_stream_parse.c
HOSTCSRCS := ../json/cJSON.c ../json/cJSON_stream_parse.c ../json/cJSON_stream_print.c
HOSTCXXSRCS := cJSON_test.cc empty_arrays.cc empty_objects.cc
HOSTCSRCS += nuttx_glue.c
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment