pack: Add format specifiers s#, + and +#
This commit is contained in:
parent
49ad5328c7
commit
3196ad48ed
@ -357,7 +357,6 @@ information, see :ref:`rfc-conformance`.
|
|||||||
should use ``json_int_t`` explicitly.
|
should use ``json_int_t`` explicitly.
|
||||||
|
|
||||||
``JSON_INTEGER_IS_LONG_LONG``
|
``JSON_INTEGER_IS_LONG_LONG``
|
||||||
|
|
||||||
This is a preprocessor variable that holds the value 1 if
|
This is a preprocessor variable that holds the value 1 if
|
||||||
:type:`json_int_t` is ``long long``, and 0 if it's ``long``. It
|
:type:`json_int_t` is ``long long``, and 0 if it's ``long``. It
|
||||||
can be used as follows::
|
can be used as follows::
|
||||||
@ -369,7 +368,6 @@ information, see :ref:`rfc-conformance`.
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
``JSON_INTEGER_FORMAT``
|
``JSON_INTEGER_FORMAT``
|
||||||
|
|
||||||
This is a macro that expands to a :func:`printf()` conversion
|
This is a macro that expands to a :func:`printf()` conversion
|
||||||
specifier that corresponds to :type:`json_int_t`, without the
|
specifier that corresponds to :type:`json_int_t`, without the
|
||||||
leading ``%`` sign, i.e. either ``"lld"`` or ``"ld"``. This macro
|
leading ``%`` sign, i.e. either ``"lld"`` or ``"ld"``. This macro
|
||||||
@ -1072,13 +1070,25 @@ items::
|
|||||||
/* Create the JSON array ["foo", "bar", true] */
|
/* Create the JSON array ["foo", "bar", true] */
|
||||||
json_pack("[ssb]", "foo", "bar", 1);
|
json_pack("[ssb]", "foo", "bar", 1);
|
||||||
|
|
||||||
Here's the full list of format characters. The type in parentheses
|
Here's the full list of format specifiers. The type in parentheses
|
||||||
denotes the resulting JSON type, and the type in brackets (if any)
|
denotes the resulting JSON type, and the type in brackets (if any)
|
||||||
denotes the C type that is expected as the corresponding argument.
|
denotes the C type that is expected as the corresponding argument or
|
||||||
|
arguments.
|
||||||
|
|
||||||
``s`` (string) [const char \*]
|
``s`` (string) [const char \*]
|
||||||
Convert a NULL terminated UTF-8 string to a JSON string.
|
Convert a NULL terminated UTF-8 string to a JSON string.
|
||||||
|
|
||||||
|
``s#`` (string) [const char \*, int]
|
||||||
|
Convert a UTF-8 buffer of a given length to a JSON string.
|
||||||
|
|
||||||
|
``+`` [const char \*]
|
||||||
|
Like ``s``, but concatenate to the previous string. Only valid
|
||||||
|
after ``s``, ``s#``, ``+`` or ``+#``.
|
||||||
|
|
||||||
|
``+#`` [const char \*, int]
|
||||||
|
Like ``s#``, but concatenate to the previous string. Only valid
|
||||||
|
after ``s``, ``s#``, ``+`` or ``+#``.
|
||||||
|
|
||||||
``n`` (null)
|
``n`` (null)
|
||||||
Output a JSON null value. No argument is consumed.
|
Output a JSON null value. No argument is consumed.
|
||||||
|
|
||||||
@ -1113,10 +1123,11 @@ denotes the C type that is expected as the corresponding argument.
|
|||||||
|
|
||||||
``{fmt}`` (object)
|
``{fmt}`` (object)
|
||||||
Build an object with contents from the inner format string
|
Build an object with contents from the inner format string
|
||||||
``fmt``. The first, third, etc. format character represent a key,
|
``fmt``. The first, third, etc. format specifier represent a key,
|
||||||
and must be ``s`` (as object keys are always strings). The second,
|
and must be a string (see ``s``, ``s#``, ``+`` and ``+#`` above),
|
||||||
fourth, etc. format character represent a value. Any value may be
|
as object keys are always strings. The second, fourth, etc. format
|
||||||
an object or array, i.e. recursive value building is supported.
|
specifier represent a value. Any value may be an object or array,
|
||||||
|
i.e. recursive value building is supported.
|
||||||
|
|
||||||
Whitespace, ``:`` and ``,`` are ignored.
|
Whitespace, ``:`` and ``,`` are ignored.
|
||||||
|
|
||||||
@ -1127,9 +1138,9 @@ The following functions compose the value building API:
|
|||||||
.. refcounting:: new
|
.. refcounting:: new
|
||||||
|
|
||||||
Build a new JSON value according to the format string *fmt*. For
|
Build a new JSON value according to the format string *fmt*. For
|
||||||
each format character (except for ``{}[]n``), one argument is
|
each format specifier (except for ``{}[]n``), one or more arguments
|
||||||
consumed and used to build the corresponding value. Returns *NULL*
|
are consumed and used to build the corresponding value. Returns
|
||||||
on error.
|
*NULL* on error.
|
||||||
|
|
||||||
.. function:: json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...)
|
.. function:: json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...)
|
||||||
json_t *json_vpack_ex(json_error_t *error, size_t flags, const char *fmt, va_list ap)
|
json_t *json_vpack_ex(json_error_t *error, size_t flags, const char *fmt, va_list ap)
|
||||||
@ -1158,6 +1169,13 @@ More examples::
|
|||||||
/* Build the JSON array [[1, 2], {"cool": true}] */
|
/* Build the JSON array [[1, 2], {"cool": true}] */
|
||||||
json_pack("[[i,i],{s:b}]", 1, 2, "cool", 1);
|
json_pack("[[i,i],{s:b}]", 1, 2, "cool", 1);
|
||||||
|
|
||||||
|
/* Build a string from a non-NUL terminated buffer */
|
||||||
|
char buffer[4] = {'t', 'e', 's', 't'};
|
||||||
|
json_pack("s#", buffer, 4);
|
||||||
|
|
||||||
|
/* Concatentate strings together to build the JSON string "foobarbaz" */
|
||||||
|
json_pack("s++", "foo", "bar", "baz");
|
||||||
|
|
||||||
|
|
||||||
.. _apiref-unpack:
|
.. _apiref-unpack:
|
||||||
|
|
||||||
@ -1172,10 +1190,10 @@ While a JSON value is unpacked, the type specified in the format
|
|||||||
string is checked to match that of the JSON value. This is the
|
string is checked to match that of the JSON value. This is the
|
||||||
validation part of the process. In addition to this, the unpacking
|
validation part of the process. In addition to this, the unpacking
|
||||||
functions can also check that all items of arrays and objects are
|
functions can also check that all items of arrays and objects are
|
||||||
unpacked. This check be enabled with the format character ``!`` or by
|
unpacked. This check be enabled with the format specifier ``!`` or by
|
||||||
using the flag ``JSON_STRICT``. See below for details.
|
using the flag ``JSON_STRICT``. See below for details.
|
||||||
|
|
||||||
Here's the full list of format characters. The type in parentheses
|
Here's the full list of format specifiers. The type in parentheses
|
||||||
denotes the JSON type, and the type in brackets (if any) denotes the C
|
denotes the JSON type, and the type in brackets (if any) denotes the C
|
||||||
type whose address should be passed.
|
type whose address should be passed.
|
||||||
|
|
||||||
@ -1217,10 +1235,10 @@ type whose address should be passed.
|
|||||||
|
|
||||||
``{fmt}`` (object)
|
``{fmt}`` (object)
|
||||||
Convert each item in the JSON object according to the inner format
|
Convert each item in the JSON object according to the inner format
|
||||||
string ``fmt``. The first, third, etc. format character represent
|
string ``fmt``. The first, third, etc. format specifier represent
|
||||||
a key, and must be ``s``. The corresponding argument to unpack
|
a key, and must be ``s``. The corresponding argument to unpack
|
||||||
functions is read as the object key. The second fourth, etc.
|
functions is read as the object key. The second fourth, etc.
|
||||||
format character represent a value and is written to the address
|
format specifier represent a value and is written to the address
|
||||||
given as the corresponding argument. **Note** that every other
|
given as the corresponding argument. **Note** that every other
|
||||||
argument is read from and every other is written to.
|
argument is read from and every other is written to.
|
||||||
|
|
||||||
@ -1233,17 +1251,17 @@ type whose address should be passed.
|
|||||||
extracted. See below for an example.
|
extracted. See below for an example.
|
||||||
|
|
||||||
``!``
|
``!``
|
||||||
This special format character is used to enable the check that
|
This special format specifier is used to enable the check that
|
||||||
all object and array items are accessed, on a per-value basis. It
|
all object and array items are accessed, on a per-value basis. It
|
||||||
must appear inside an array or object as the last format character
|
must appear inside an array or object as the last format specifier
|
||||||
before the closing bracket or brace. To enable the check globally,
|
before the closing bracket or brace. To enable the check globally,
|
||||||
use the ``JSON_STRICT`` unpacking flag.
|
use the ``JSON_STRICT`` unpacking flag.
|
||||||
|
|
||||||
``*``
|
``*``
|
||||||
This special format character is the opposite of ``!``. If the
|
This special format specifier is the opposite of ``!``. If the
|
||||||
``JSON_STRICT`` flag is used, ``*`` can be used to disable the
|
``JSON_STRICT`` flag is used, ``*`` can be used to disable the
|
||||||
strict check on a per-value basis. It must appear inside an array
|
strict check on a per-value basis. It must appear inside an array
|
||||||
or object as the last format character before the closing bracket
|
or object as the last format specifier before the closing bracket
|
||||||
or brace.
|
or brace.
|
||||||
|
|
||||||
Whitespace, ``:`` and ``,`` are ignored.
|
Whitespace, ``:`` and ``,`` are ignored.
|
||||||
@ -1268,13 +1286,13 @@ The following functions compose the parsing and validation API:
|
|||||||
|
|
||||||
The first argument of all unpack functions is ``json_t *root``
|
The first argument of all unpack functions is ``json_t *root``
|
||||||
instead of ``const json_t *root``, because the use of ``O`` format
|
instead of ``const json_t *root``, because the use of ``O`` format
|
||||||
character causes the reference count of ``root``, or some value
|
specifier causes the reference count of ``root``, or some value
|
||||||
reachable from ``root``, to be increased. Furthermore, the ``o``
|
reachable from ``root``, to be increased. Furthermore, the ``o``
|
||||||
format character may be used to extract a value as-is, which allows
|
format specifier may be used to extract a value as-is, which allows
|
||||||
modifying the structure or contents of a value reachable from
|
modifying the structure or contents of a value reachable from
|
||||||
``root``.
|
``root``.
|
||||||
|
|
||||||
If the ``O`` and ``o`` format characters are not used, it's
|
If the ``O`` and ``o`` format specifiers are not used, it's
|
||||||
perfectly safe to cast a ``const json_t *`` variable to plain
|
perfectly safe to cast a ``const json_t *`` variable to plain
|
||||||
``json_t *`` when used with these functions.
|
``json_t *`` when used with these functions.
|
||||||
|
|
||||||
@ -1283,7 +1301,7 @@ The following unpacking flags are available:
|
|||||||
``JSON_STRICT``
|
``JSON_STRICT``
|
||||||
Enable the extra validation step checking that all object and
|
Enable the extra validation step checking that all object and
|
||||||
array items are unpacked. This is equivalent to appending the
|
array items are unpacked. This is equivalent to appending the
|
||||||
format character ``!`` to the end of every array and object in the
|
format specifier ``!`` to the end of every array and object in the
|
||||||
format string.
|
format string.
|
||||||
|
|
||||||
``JSON_VALIDATE_ONLY``
|
``JSON_VALIDATE_ONLY``
|
||||||
|
@ -81,6 +81,7 @@ int jsonp_dtostr(char *buffer, size_t size, double value);
|
|||||||
/* Wrappers for custom memory functions */
|
/* Wrappers for custom memory functions */
|
||||||
void* jsonp_malloc(size_t size);
|
void* jsonp_malloc(size_t size);
|
||||||
void jsonp_free(void *ptr);
|
void jsonp_free(void *ptr);
|
||||||
|
char *jsonp_strndup(const char *str, size_t length);
|
||||||
char *jsonp_strdup(const char *str);
|
char *jsonp_strdup(const char *str);
|
||||||
|
|
||||||
/* Windows compatibility */
|
/* Windows compatibility */
|
||||||
|
@ -11,16 +11,28 @@
|
|||||||
#include "jansson_private.h"
|
#include "jansson_private.h"
|
||||||
#include "utf.h"
|
#include "utf.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int line;
|
||||||
|
int column;
|
||||||
|
size_t pos;
|
||||||
|
char token;
|
||||||
|
} token_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char *start;
|
const char *start;
|
||||||
const char *fmt;
|
const char *fmt;
|
||||||
char token;
|
token_t prev_token;
|
||||||
|
token_t token;
|
||||||
|
token_t next_token;
|
||||||
json_error_t *error;
|
json_error_t *error;
|
||||||
size_t flags;
|
size_t flags;
|
||||||
int line;
|
int line;
|
||||||
int column;
|
int column;
|
||||||
|
size_t pos;
|
||||||
} scanner_t;
|
} scanner_t;
|
||||||
|
|
||||||
|
#define token(scanner) ((scanner)->token.token)
|
||||||
|
|
||||||
static const char * const type_names[] = {
|
static const char * const type_names[] = {
|
||||||
"object",
|
"object",
|
||||||
"array",
|
"array",
|
||||||
@ -43,14 +55,28 @@ static void scanner_init(scanner_t *s, json_error_t *error,
|
|||||||
s->error = error;
|
s->error = error;
|
||||||
s->flags = flags;
|
s->flags = flags;
|
||||||
s->fmt = s->start = fmt;
|
s->fmt = s->start = fmt;
|
||||||
|
memset(&s->prev_token, 0, sizeof(token_t));
|
||||||
|
memset(&s->token, 0, sizeof(token_t));
|
||||||
|
memset(&s->next_token, 0, sizeof(token_t));
|
||||||
s->line = 1;
|
s->line = 1;
|
||||||
s->column = 0;
|
s->column = 0;
|
||||||
|
s->pos = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void next_token(scanner_t *s)
|
static void next_token(scanner_t *s)
|
||||||
{
|
{
|
||||||
const char *t = s->fmt;
|
const char *t;
|
||||||
|
s->prev_token = s->token;
|
||||||
|
|
||||||
|
if(s->next_token.line) {
|
||||||
|
s->token = s->next_token;
|
||||||
|
s->next_token.line = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
t = s->fmt;
|
||||||
s->column++;
|
s->column++;
|
||||||
|
s->pos++;
|
||||||
|
|
||||||
/* skip space and ignored chars */
|
/* skip space and ignored chars */
|
||||||
while(*t == ' ' || *t == '\t' || *t == '\n' || *t == ',' || *t == ':') {
|
while(*t == ' ' || *t == '\t' || *t == '\n' || *t == ',' || *t == ':') {
|
||||||
@ -61,23 +87,32 @@ static void next_token(scanner_t *s)
|
|||||||
else
|
else
|
||||||
s->column++;
|
s->column++;
|
||||||
|
|
||||||
|
s->pos++;
|
||||||
t++;
|
t++;
|
||||||
}
|
}
|
||||||
|
|
||||||
s->token = *t;
|
s->token.token = *t;
|
||||||
|
s->token.line = s->line;
|
||||||
|
s->token.column = s->column;
|
||||||
|
s->token.pos = s->pos;
|
||||||
|
|
||||||
t++;
|
t++;
|
||||||
s->fmt = t;
|
s->fmt = t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void prev_token(scanner_t *s)
|
||||||
|
{
|
||||||
|
s->next_token = s->token;
|
||||||
|
s->token = s->prev_token;
|
||||||
|
}
|
||||||
|
|
||||||
static void set_error(scanner_t *s, const char *source, const char *fmt, ...)
|
static void set_error(scanner_t *s, const char *source, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
size_t pos;
|
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
|
|
||||||
pos = (size_t)(s->fmt - s->start);
|
jsonp_error_vset(s->error, s->token.line, s->token.column, s->token.pos,
|
||||||
jsonp_error_vset(s->error, s->line, s->column, pos, fmt, ap);
|
fmt, ap);
|
||||||
|
|
||||||
jsonp_error_set_source(s->error, source);
|
jsonp_error_set_source(s->error, source);
|
||||||
|
|
||||||
@ -86,35 +121,107 @@ static void set_error(scanner_t *s, const char *source, const char *fmt, ...)
|
|||||||
|
|
||||||
static json_t *pack(scanner_t *s, va_list *ap);
|
static json_t *pack(scanner_t *s, va_list *ap);
|
||||||
|
|
||||||
|
|
||||||
|
/* ours will be set to 1 if jsonp_free() must be called for the result
|
||||||
|
afterwards */
|
||||||
|
static char *read_string(scanner_t *s, va_list *ap,
|
||||||
|
const char *purpose, int *ours)
|
||||||
|
{
|
||||||
|
char t;
|
||||||
|
strbuffer_t strbuff;
|
||||||
|
const char *str;
|
||||||
|
size_t length;
|
||||||
|
char *result;
|
||||||
|
|
||||||
|
next_token(s);
|
||||||
|
t = token(s);
|
||||||
|
prev_token(s);
|
||||||
|
|
||||||
|
if(t != '#' && t != '+') {
|
||||||
|
/* Optimize the simple case */
|
||||||
|
str = va_arg(*ap, const char *);
|
||||||
|
|
||||||
|
if(!str) {
|
||||||
|
set_error(s, "<args>", "NULL string argument");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!utf8_check_string(str, -1)) {
|
||||||
|
set_error(s, "<args>", "Invalid UTF-8 %s", purpose);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ours = 0;
|
||||||
|
return (char *)str;
|
||||||
|
}
|
||||||
|
|
||||||
|
strbuffer_init(&strbuff);
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
str = va_arg(*ap, const char *);
|
||||||
|
if(!str) {
|
||||||
|
set_error(s, "<args>", "NULL string argument");
|
||||||
|
strbuffer_close(&strbuff);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
next_token(s);
|
||||||
|
|
||||||
|
if(token(s) == '#') {
|
||||||
|
length = va_arg(*ap, int);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
prev_token(s);
|
||||||
|
length = strlen(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strbuffer_append_bytes(&strbuff, str, length) == -1) {
|
||||||
|
set_error(s, "<internal>", "Out of memory");
|
||||||
|
strbuffer_close(&strbuff);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
next_token(s);
|
||||||
|
if(token(s) != '+') {
|
||||||
|
prev_token(s);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = strbuffer_steal_value(&strbuff);
|
||||||
|
|
||||||
|
if(!utf8_check_string(result, -1)) {
|
||||||
|
set_error(s, "<args>", "Invalid UTF-8 %s", purpose);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ours = 1;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static json_t *pack_object(scanner_t *s, va_list *ap)
|
static json_t *pack_object(scanner_t *s, va_list *ap)
|
||||||
{
|
{
|
||||||
json_t *object = json_object();
|
json_t *object = json_object();
|
||||||
next_token(s);
|
next_token(s);
|
||||||
|
|
||||||
while(s->token != '}') {
|
while(token(s) != '}') {
|
||||||
const char *key;
|
char *key;
|
||||||
|
int ours;
|
||||||
json_t *value;
|
json_t *value;
|
||||||
|
|
||||||
if(!s->token) {
|
if(!token(s)) {
|
||||||
set_error(s, "<format>", "Unexpected end of format string");
|
set_error(s, "<format>", "Unexpected end of format string");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(s->token != 's') {
|
if(token(s) != 's') {
|
||||||
set_error(s, "<format>", "Expected format 's', got '%c'", s->token);
|
set_error(s, "<format>", "Expected format 's', got '%c'", token(s));
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
key = va_arg(*ap, const char *);
|
key = read_string(s, ap, "object key", &ours);
|
||||||
if(!key) {
|
if(!key)
|
||||||
set_error(s, "<args>", "NULL object key");
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
|
||||||
|
|
||||||
if(!utf8_check_string(key, -1)) {
|
|
||||||
set_error(s, "<args>", "Invalid UTF-8 in object key");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
next_token(s);
|
next_token(s);
|
||||||
|
|
||||||
@ -123,10 +230,16 @@ static json_t *pack_object(scanner_t *s, va_list *ap)
|
|||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if(json_object_set_new_nocheck(object, key, value)) {
|
if(json_object_set_new_nocheck(object, key, value)) {
|
||||||
|
if(ours)
|
||||||
|
jsonp_free(key);
|
||||||
|
|
||||||
set_error(s, "<internal>", "Unable to add key \"%s\"", key);
|
set_error(s, "<internal>", "Unable to add key \"%s\"", key);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(ours)
|
||||||
|
jsonp_free(key);
|
||||||
|
|
||||||
next_token(s);
|
next_token(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,10 +255,10 @@ static json_t *pack_array(scanner_t *s, va_list *ap)
|
|||||||
json_t *array = json_array();
|
json_t *array = json_array();
|
||||||
next_token(s);
|
next_token(s);
|
||||||
|
|
||||||
while(s->token != ']') {
|
while(token(s) != ']') {
|
||||||
json_t *value;
|
json_t *value;
|
||||||
|
|
||||||
if(!s->token) {
|
if(!token(s)) {
|
||||||
set_error(s, "<format>", "Unexpected end of format string");
|
set_error(s, "<format>", "Unexpected end of format string");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
@ -170,25 +283,27 @@ error:
|
|||||||
|
|
||||||
static json_t *pack(scanner_t *s, va_list *ap)
|
static json_t *pack(scanner_t *s, va_list *ap)
|
||||||
{
|
{
|
||||||
switch(s->token) {
|
switch(token(s)) {
|
||||||
case '{':
|
case '{':
|
||||||
return pack_object(s, ap);
|
return pack_object(s, ap);
|
||||||
|
|
||||||
case '[':
|
case '[':
|
||||||
return pack_array(s, ap);
|
return pack_array(s, ap);
|
||||||
|
|
||||||
case 's': /* string */
|
case 's': { /* string */
|
||||||
{
|
char *str;
|
||||||
const char *str = va_arg(*ap, const char *);
|
int ours;
|
||||||
if(!str) {
|
json_t *result;
|
||||||
set_error(s, "<args>", "NULL string argument");
|
|
||||||
|
str = read_string(s, ap, "string", &ours);
|
||||||
|
if(!str)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
|
||||||
if(!utf8_check_string(str, -1)) {
|
result = json_string_nocheck(str);
|
||||||
set_error(s, "<args>", "Invalid UTF-8 string");
|
if(ours)
|
||||||
return NULL;
|
jsonp_free(str);
|
||||||
}
|
|
||||||
return json_string_nocheck(str);
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'n': /* null */
|
case 'n': /* null */
|
||||||
@ -214,7 +329,7 @@ static json_t *pack(scanner_t *s, va_list *ap)
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
set_error(s, "<format>", "Unexpected format character '%c'",
|
set_error(s, "<format>", "Unexpected format character '%c'",
|
||||||
s->token);
|
token(s));
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -245,30 +360,30 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
|
|||||||
}
|
}
|
||||||
next_token(s);
|
next_token(s);
|
||||||
|
|
||||||
while(s->token != '}') {
|
while(token(s) != '}') {
|
||||||
const char *key;
|
const char *key;
|
||||||
json_t *value;
|
json_t *value;
|
||||||
int opt = 0;
|
int opt = 0;
|
||||||
|
|
||||||
if(strict != 0) {
|
if(strict != 0) {
|
||||||
set_error(s, "<format>", "Expected '}' after '%c', got '%c'",
|
set_error(s, "<format>", "Expected '}' after '%c', got '%c'",
|
||||||
(strict == 1 ? '!' : '*'), s->token);
|
(strict == 1 ? '!' : '*'), token(s));
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!s->token) {
|
if(!token(s)) {
|
||||||
set_error(s, "<format>", "Unexpected end of format string");
|
set_error(s, "<format>", "Unexpected end of format string");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(s->token == '!' || s->token == '*') {
|
if(token(s) == '!' || token(s) == '*') {
|
||||||
strict = (s->token == '!' ? 1 : -1);
|
strict = (token(s) == '!' ? 1 : -1);
|
||||||
next_token(s);
|
next_token(s);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(s->token != 's') {
|
if(token(s) != 's') {
|
||||||
set_error(s, "<format>", "Expected format 's', got '%c'", s->token);
|
set_error(s, "<format>", "Expected format 's', got '%c'", token(s));
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,7 +395,7 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
|
|||||||
|
|
||||||
next_token(s);
|
next_token(s);
|
||||||
|
|
||||||
if(s->token == '?') {
|
if(token(s) == '?') {
|
||||||
opt = 1;
|
opt = 1;
|
||||||
next_token(s);
|
next_token(s);
|
||||||
}
|
}
|
||||||
@ -331,30 +446,30 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
|
|||||||
}
|
}
|
||||||
next_token(s);
|
next_token(s);
|
||||||
|
|
||||||
while(s->token != ']') {
|
while(token(s) != ']') {
|
||||||
json_t *value;
|
json_t *value;
|
||||||
|
|
||||||
if(strict != 0) {
|
if(strict != 0) {
|
||||||
set_error(s, "<format>", "Expected ']' after '%c', got '%c'",
|
set_error(s, "<format>", "Expected ']' after '%c', got '%c'",
|
||||||
(strict == 1 ? '!' : '*'),
|
(strict == 1 ? '!' : '*'),
|
||||||
s->token);
|
token(s));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!s->token) {
|
if(!token(s)) {
|
||||||
set_error(s, "<format>", "Unexpected end of format string");
|
set_error(s, "<format>", "Unexpected end of format string");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(s->token == '!' || s->token == '*') {
|
if(token(s) == '!' || token(s) == '*') {
|
||||||
strict = (s->token == '!' ? 1 : -1);
|
strict = (token(s) == '!' ? 1 : -1);
|
||||||
next_token(s);
|
next_token(s);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!strchr(unpack_value_starters, s->token)) {
|
if(!strchr(unpack_value_starters, token(s))) {
|
||||||
set_error(s, "<format>", "Unexpected format character '%c'",
|
set_error(s, "<format>", "Unexpected format character '%c'",
|
||||||
s->token);
|
token(s));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,7 +507,7 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
|
|||||||
|
|
||||||
static int unpack(scanner_t *s, json_t *root, va_list *ap)
|
static int unpack(scanner_t *s, json_t *root, va_list *ap)
|
||||||
{
|
{
|
||||||
switch(s->token)
|
switch(token(s))
|
||||||
{
|
{
|
||||||
case '{':
|
case '{':
|
||||||
return unpack_object(s, root, ap);
|
return unpack_object(s, root, ap);
|
||||||
@ -521,7 +636,7 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
set_error(s, "<format>", "Unexpected format character '%c'",
|
set_error(s, "<format>", "Unexpected format character '%c'",
|
||||||
s->token);
|
token(s));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -551,7 +666,7 @@ json_t *json_vpack_ex(json_error_t *error, size_t flags,
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
next_token(&s);
|
next_token(&s);
|
||||||
if(s.token) {
|
if(token(&s)) {
|
||||||
json_decref(value);
|
json_decref(value);
|
||||||
set_error(&s, "<format>", "Garbage after format string");
|
set_error(&s, "<format>", "Garbage after format string");
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -614,7 +729,7 @@ int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags,
|
|||||||
va_end(ap_copy);
|
va_end(ap_copy);
|
||||||
|
|
||||||
next_token(&s);
|
next_token(&s);
|
||||||
if(s.token) {
|
if(token(&s)) {
|
||||||
set_error(&s, "<format>", "Garbage after format string");
|
set_error(&s, "<format>", "Garbage after format string");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,9 @@ int strbuffer_init(strbuffer_t *strbuff)
|
|||||||
|
|
||||||
void strbuffer_close(strbuffer_t *strbuff)
|
void strbuffer_close(strbuffer_t *strbuff)
|
||||||
{
|
{
|
||||||
|
if(strbuff->value)
|
||||||
jsonp_free(strbuff->value);
|
jsonp_free(strbuff->value);
|
||||||
|
|
||||||
strbuff->size = 0;
|
strbuff->size = 0;
|
||||||
strbuff->length = 0;
|
strbuff->length = 0;
|
||||||
strbuff->value = NULL;
|
strbuff->value = NULL;
|
||||||
@ -54,7 +56,7 @@ const char *strbuffer_value(const strbuffer_t *strbuff)
|
|||||||
char *strbuffer_steal_value(strbuffer_t *strbuff)
|
char *strbuffer_steal_value(strbuffer_t *strbuff)
|
||||||
{
|
{
|
||||||
char *result = strbuff->value;
|
char *result = strbuff->value;
|
||||||
strbuffer_init(strbuff);
|
strbuff->value = NULL;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,8 @@ void strbuffer_close(strbuffer_t *strbuff);
|
|||||||
void strbuffer_clear(strbuffer_t *strbuff);
|
void strbuffer_clear(strbuffer_t *strbuff);
|
||||||
|
|
||||||
const char *strbuffer_value(const strbuffer_t *strbuff);
|
const char *strbuffer_value(const strbuffer_t *strbuff);
|
||||||
|
|
||||||
|
/* Steal the value and close the strbuffer */
|
||||||
char *strbuffer_steal_value(strbuffer_t *strbuff);
|
char *strbuffer_steal_value(strbuffer_t *strbuff);
|
||||||
|
|
||||||
int strbuffer_append(strbuffer_t *strbuff, const char *string);
|
int strbuffer_append(strbuffer_t *strbuff, const char *string);
|
||||||
|
@ -21,6 +21,7 @@ static void run_tests()
|
|||||||
{
|
{
|
||||||
json_t *value;
|
json_t *value;
|
||||||
int i;
|
int i;
|
||||||
|
char buffer[4] = {'t', 'e', 's', 't'};
|
||||||
json_error_t error;
|
json_error_t error;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -82,6 +83,38 @@ static void run_tests()
|
|||||||
fail("json_pack string refcount failed");
|
fail("json_pack string refcount failed");
|
||||||
json_decref(value);
|
json_decref(value);
|
||||||
|
|
||||||
|
/* string and length */
|
||||||
|
value = json_pack("s#", "test asdf", 4);
|
||||||
|
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
|
||||||
|
fail("json_pack string and length failed");
|
||||||
|
if(value->refcount != (size_t)1)
|
||||||
|
fail("json_pack string and length refcount failed");
|
||||||
|
json_decref(value);
|
||||||
|
|
||||||
|
/* string and length, non-NUL terminated string */
|
||||||
|
value = json_pack("s#", buffer, 4);
|
||||||
|
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
|
||||||
|
fail("json_pack string and length failed");
|
||||||
|
if(value->refcount != (size_t)1)
|
||||||
|
fail("json_pack string and length refcount failed");
|
||||||
|
json_decref(value);
|
||||||
|
|
||||||
|
/* string concatenation */
|
||||||
|
value = json_pack("s++", "te", "st", "ing");
|
||||||
|
if(!json_is_string(value) || strcmp("testing", json_string_value(value)))
|
||||||
|
fail("json_pack string concatenation failed");
|
||||||
|
if(value->refcount != (size_t)1)
|
||||||
|
fail("json_pack string concatenation refcount failed");
|
||||||
|
json_decref(value);
|
||||||
|
|
||||||
|
/* string concatenation and length */
|
||||||
|
value = json_pack("s#+#+", "test", 1, "test", 2, "test");
|
||||||
|
if(!json_is_string(value) || strcmp("ttetest", json_string_value(value)))
|
||||||
|
fail("json_pack string concatenation and length failed");
|
||||||
|
if(value->refcount != (size_t)1)
|
||||||
|
fail("json_pack string concatenation and length refcount failed");
|
||||||
|
json_decref(value);
|
||||||
|
|
||||||
/* empty object */
|
/* empty object */
|
||||||
value = json_pack("{}", 1.0);
|
value = json_pack("{}", 1.0);
|
||||||
if(!json_is_object(value) || json_object_size(value) != 0)
|
if(!json_is_object(value) || json_object_size(value) != 0)
|
||||||
@ -125,6 +158,16 @@ static void run_tests()
|
|||||||
fail("json_pack object refcount failed");
|
fail("json_pack object refcount failed");
|
||||||
json_decref(value);
|
json_decref(value);
|
||||||
|
|
||||||
|
/* object with complex key */
|
||||||
|
value = json_pack("{s+#+: []}", "foo", "barbar", 3, "baz");
|
||||||
|
if(!json_is_object(value) || json_object_size(value) != 1)
|
||||||
|
fail("json_pack array failed");
|
||||||
|
if(!json_is_array(json_object_get(value, "foobarbaz")))
|
||||||
|
fail("json_pack array failed");
|
||||||
|
if(json_object_get(value, "foobarbaz")->refcount != (size_t)1)
|
||||||
|
fail("json_pack object refcount failed");
|
||||||
|
json_decref(value);
|
||||||
|
|
||||||
/* simple array */
|
/* simple array */
|
||||||
value = json_pack("[i,i,i]", 0, 1, 2);
|
value = json_pack("[i,i,i]", 0, 1, 2);
|
||||||
if(!json_is_array(value) || json_array_size(value) != 3)
|
if(!json_is_array(value) || json_array_size(value) != 3)
|
||||||
@ -198,6 +241,11 @@ static void run_tests()
|
|||||||
fail("json_pack failed to catch null argument string");
|
fail("json_pack failed to catch null argument string");
|
||||||
check_error("NULL string argument", "<args>", 1, 1, 1);
|
check_error("NULL string argument", "<args>", 1, 1, 1);
|
||||||
|
|
||||||
|
/* + on its own */
|
||||||
|
if(json_pack_ex(&error, 0, "+", NULL))
|
||||||
|
fail("json_pack failed to a lone +");
|
||||||
|
check_error("Unexpected format character '+'", "<format>", 1, 1, 1);
|
||||||
|
|
||||||
/* NULL format */
|
/* NULL format */
|
||||||
if(json_pack_ex(&error, 0, NULL))
|
if(json_pack_ex(&error, 0, NULL))
|
||||||
fail("json_pack failed to catch NULL format string");
|
fail("json_pack failed to catch NULL format string");
|
||||||
@ -206,7 +254,7 @@ static void run_tests()
|
|||||||
/* NULL key */
|
/* NULL key */
|
||||||
if(json_pack_ex(&error, 0, "{s:i}", NULL, 1))
|
if(json_pack_ex(&error, 0, "{s:i}", NULL, 1))
|
||||||
fail("json_pack failed to catch NULL key");
|
fail("json_pack failed to catch NULL key");
|
||||||
check_error("NULL object key", "<args>", 1, 2, 2);
|
check_error("NULL string argument", "<args>", 1, 2, 2);
|
||||||
|
|
||||||
/* More complicated checks for row/columns */
|
/* More complicated checks for row/columns */
|
||||||
if(json_pack_ex(&error, 0, "{ {}: s }", "foo"))
|
if(json_pack_ex(&error, 0, "{ {}: s }", "foo"))
|
||||||
@ -226,7 +274,7 @@ static void run_tests()
|
|||||||
/* Invalid UTF-8 in object key */
|
/* Invalid UTF-8 in object key */
|
||||||
if(json_pack_ex(&error, 0, "{s:i}", "\xff\xff", 42))
|
if(json_pack_ex(&error, 0, "{s:i}", "\xff\xff", 42))
|
||||||
fail("json_pack failed to catch invalid UTF-8 in an object key");
|
fail("json_pack failed to catch invalid UTF-8 in an object key");
|
||||||
check_error("Invalid UTF-8 in object key", "<args>", 1, 2, 2);
|
check_error("Invalid UTF-8 object key", "<args>", 1, 2, 2);
|
||||||
|
|
||||||
/* Invalid UTF-8 in a string */
|
/* Invalid UTF-8 in a string */
|
||||||
if(json_pack_ex(&error, 0, "{s:s}", "foo", "\xff\xff"))
|
if(json_pack_ex(&error, 0, "{s:s}", "foo", "\xff\xff"))
|
||||||
|
Loading…
Reference in New Issue
Block a user