mirror of
https://github.com/openvswitch/ovs
synced 2025-09-01 06:45:17 +00:00
Implement JSON parsing and serialization.
This will be used by the upcoming Open vSwitch configuration database.
This commit is contained in:
@@ -38,6 +38,8 @@ AC_USE_SYSTEM_EXTENSIONS
|
||||
AC_C_BIGENDIAN
|
||||
AC_SYS_LARGEFILE
|
||||
|
||||
AC_SEARCH_LIBS([pow], [m])
|
||||
|
||||
OVS_CHECK_COVERAGE
|
||||
OVS_CHECK_NDEBUG
|
||||
OVS_CHECK_NETLINK
|
||||
|
@@ -49,6 +49,8 @@ lib_libopenvswitch_a_SOURCES = \
|
||||
lib/hash.h \
|
||||
lib/hmap.c \
|
||||
lib/hmap.h \
|
||||
lib/json.c \
|
||||
lib/json.h \
|
||||
lib/leak-checker.c \
|
||||
lib/leak-checker.h \
|
||||
lib/learning-switch.c \
|
||||
@@ -107,6 +109,8 @@ lib_libopenvswitch_a_SOURCES = \
|
||||
lib/type-props.h \
|
||||
lib/unixctl.c \
|
||||
lib/unixctl.h \
|
||||
lib/unicode.c \
|
||||
lib/unicode.h \
|
||||
lib/util.c \
|
||||
lib/util.h \
|
||||
lib/valgrind.h \
|
||||
|
@@ -71,6 +71,31 @@ ds_put_char(struct ds *ds, char c)
|
||||
*ds_put_uninit(ds, 1) = c;
|
||||
}
|
||||
|
||||
/* Appends unicode code point 'uc' to 'ds' in UTF-8 encoding. */
|
||||
void
|
||||
ds_put_utf8(struct ds *ds, int uc)
|
||||
{
|
||||
if (uc <= 0x7f) {
|
||||
ds_put_char(ds, uc);
|
||||
} else if (uc <= 0x7ff) {
|
||||
ds_put_char(ds, 0xc0 | (uc >> 6));
|
||||
ds_put_char(ds, 0x80 | (uc & 0x3f));
|
||||
} else if (uc <= 0xffff) {
|
||||
ds_put_char(ds, 0xe0 | (uc >> 12));
|
||||
ds_put_char(ds, 0x80 | ((uc >> 6) & 0x3f));
|
||||
ds_put_char(ds, 0x80 | (uc & 0x3f));
|
||||
} else if (uc <= 0x10ffff) {
|
||||
ds_put_char(ds, 0xf0 | (uc >> 18));
|
||||
ds_put_char(ds, 0x80 | ((uc >> 12) & 0x3f));
|
||||
ds_put_char(ds, 0x80 | ((uc >> 6) & 0x3f));
|
||||
ds_put_char(ds, 0x80 | (uc & 0x3f));
|
||||
} else {
|
||||
/* Invalid code point. Insert the Unicode general substitute
|
||||
* REPLACEMENT CHARACTER. */
|
||||
ds_put_utf8(ds, 0xfffd);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ds_put_char_multiple(struct ds *ds, char c, size_t n)
|
||||
{
|
||||
|
@@ -40,6 +40,7 @@ void ds_truncate(struct ds *, size_t new_length);
|
||||
void ds_reserve(struct ds *, size_t min_length);
|
||||
char *ds_put_uninit(struct ds *, size_t n);
|
||||
void ds_put_char(struct ds *, char);
|
||||
void ds_put_utf8(struct ds *, int uc);
|
||||
void ds_put_char_multiple(struct ds *, char, size_t n);
|
||||
void ds_put_buffer(struct ds *, const char *, size_t n);
|
||||
void ds_put_cstr(struct ds *, const char *);
|
||||
|
1569
lib/json.c
Normal file
1569
lib/json.c
Normal file
File diff suppressed because it is too large
Load Diff
122
lib/json.h
Normal file
122
lib/json.h
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Nicira Networks.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef JSON_H
|
||||
#define JSON_H 1
|
||||
|
||||
/* This is an implementation of JavaScript Object Notation (JSON) as specified
|
||||
* by RFC 4627. It is intended to fully comply with RFC 4627, with the
|
||||
* following known exceptions and clarifications:
|
||||
*
|
||||
* - Null bytes (\u0000) are not allowed in strings.
|
||||
*
|
||||
* - Only UTF-8 encoding is supported (RFC 4627 allows for other Unicode
|
||||
* encodings).
|
||||
*
|
||||
* - Names within an object must be unique (RFC 4627 says that they
|
||||
* "should" be unique).
|
||||
*/
|
||||
|
||||
#include "shash.h"
|
||||
|
||||
/* Type of a JSON value. */
|
||||
enum json_type {
|
||||
JSON_NULL, /* null */
|
||||
JSON_FALSE, /* false */
|
||||
JSON_TRUE, /* true */
|
||||
JSON_OBJECT, /* {"a": b, "c": d, ...} */
|
||||
JSON_ARRAY, /* [1, 2, 3, ...] */
|
||||
JSON_INTEGER, /* 123. */
|
||||
JSON_REAL, /* 123.456. */
|
||||
JSON_STRING, /* "..." */
|
||||
JSON_N_TYPES
|
||||
};
|
||||
|
||||
const char *json_type_to_string(enum json_type);
|
||||
|
||||
/* A JSON array. */
|
||||
struct json_array {
|
||||
size_t n, n_allocated;
|
||||
struct json **elems;
|
||||
};
|
||||
|
||||
/* A JSON value. */
|
||||
struct json {
|
||||
enum json_type type;
|
||||
union {
|
||||
struct shash *object; /* Contains "struct json *"s. */
|
||||
struct json_array array;
|
||||
long long int integer;
|
||||
double real;
|
||||
char *string;
|
||||
} u;
|
||||
};
|
||||
|
||||
struct json *json_null_create(void);
|
||||
struct json *json_boolean_create(bool);
|
||||
struct json *json_string_create(const char *);
|
||||
struct json *json_string_create_nocopy(char *);
|
||||
struct json *json_integer_create(long long int);
|
||||
struct json *json_real_create(double);
|
||||
|
||||
struct json *json_array_create_empty(void);
|
||||
void json_array_add(struct json *, struct json *element);
|
||||
void json_array_trim(struct json *);
|
||||
struct json *json_array_create(struct json **, size_t n);
|
||||
struct json *json_array_create_2(struct json *, struct json *);
|
||||
struct json *json_array_create_3(struct json *, struct json *, struct json *);
|
||||
|
||||
struct json *json_object_create(void);
|
||||
void json_object_put(struct json *, const char *name, struct json *value);
|
||||
void json_object_put_string(struct json *,
|
||||
const char *name, const char *value);
|
||||
|
||||
const char *json_string(const struct json *);
|
||||
struct json_array *json_array(const struct json *);
|
||||
struct shash *json_object(const struct json *);
|
||||
bool json_boolean(const struct json *);
|
||||
double json_real(const struct json *);
|
||||
int64_t json_integer(const struct json *);
|
||||
|
||||
struct json *json_clone(const struct json *);
|
||||
void json_destroy(struct json *);
|
||||
|
||||
size_t json_hash(const struct json *, size_t basis);
|
||||
bool json_equal(const struct json *, const struct json *);
|
||||
|
||||
/* Parsing JSON. */
|
||||
enum {
|
||||
JSPF_TRAILER = 1 << 0 /* Check for garbage following input. */
|
||||
};
|
||||
|
||||
struct json_parser *json_parser_create(int flags);
|
||||
size_t json_parser_feed(struct json_parser *, const char *, size_t);
|
||||
bool json_parser_is_done(const struct json_parser *);
|
||||
struct json *json_parser_finish(struct json_parser *);
|
||||
void json_parser_abort(struct json_parser *);
|
||||
|
||||
struct json *json_from_string(const char *string);
|
||||
struct json *json_from_file(const char *file_name);
|
||||
|
||||
/* Serializing JSON. */
|
||||
|
||||
enum {
|
||||
JSSF_PRETTY = 1 << 0, /* Multiple lines with indentation, if true. */
|
||||
JSSF_SORT = 1 << 1 /* Object members in sorted order, if true. */
|
||||
};
|
||||
char *json_to_string(const struct json *, int flags);
|
||||
|
||||
#endif /* json.h */
|
38
lib/unicode.c
Normal file
38
lib/unicode.c
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Nicira Networks.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "unicode.h"
|
||||
|
||||
/* Returns the unicode code point corresponding to leading surrogate 'leading'
|
||||
* and trailing surrogate 'trailing'. The return value will not make any
|
||||
* sense if 'leading' or 'trailing' are not in the correct ranges for leading
|
||||
* or trailing surrogates. */
|
||||
int
|
||||
utf16_decode_surrogate_pair(int leading, int trailing)
|
||||
{
|
||||
/*
|
||||
* Leading surrogate: 110110wwwwxxxxxx
|
||||
* Trailing surrogate: 110111xxxxxxxxxx
|
||||
* Code point: 000uuuuuxxxxxxxxxxxxxxxx
|
||||
*/
|
||||
int w = (leading >> 6) & 0xf;
|
||||
int u = w + 1;
|
||||
int x0 = leading & 0x3f;
|
||||
int x1 = trailing & 0x3ff;
|
||||
return (u << 16) | (x0 << 10) | x1;
|
||||
}
|
53
lib/unicode.h
Normal file
53
lib/unicode.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Nicira Networks.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef UNICODE_H
|
||||
#define UNICODE_H 1
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Returns true if 'c' is a Unicode code point, otherwise false. */
|
||||
static inline bool
|
||||
uc_is_code_point(int c)
|
||||
{
|
||||
return c >= 0 && c <= 0x10ffff;
|
||||
}
|
||||
|
||||
/* Returns true if 'c' is a Unicode code point for a leading surrogate. */
|
||||
static inline bool
|
||||
uc_is_leading_surrogate(int c)
|
||||
{
|
||||
return c >= 0xd800 && c <= 0xdbff;
|
||||
}
|
||||
|
||||
/* Returns true if 'c' is a Unicode code point for a trailing surrogate. */
|
||||
static inline bool
|
||||
uc_is_trailing_surrogate(int c)
|
||||
{
|
||||
return c >= 0xdc00 && c <= 0xdfff;
|
||||
}
|
||||
|
||||
/* Returns true if 'c' is a Unicode code point for a leading or trailing
|
||||
* surrogate. */
|
||||
static inline bool
|
||||
uc_is_surrogate(int c)
|
||||
{
|
||||
return c >= 0xd800 && c <= 0xdfff;
|
||||
}
|
||||
|
||||
int utf16_decode_surrogate_pair(int leading, int trailing);
|
||||
|
||||
#endif /* unicode.h */
|
55
lib/util.c
55
lib/util.c
@@ -300,3 +300,58 @@ str_to_ullong(const char *s, int base, unsigned long long *ull)
|
||||
{
|
||||
return str_to_llong(s, base, (long long *) ull);
|
||||
}
|
||||
|
||||
/* Converts floating-point string 's' into a double. If successful, stores
|
||||
* the double in '*d' and returns true; on failure, stores 0 in '*d' and
|
||||
* returns false.
|
||||
*
|
||||
* Underflow (e.g. "1e-9999") is not considered an error, but overflow
|
||||
* (e.g. "1e9999)" is. */
|
||||
bool
|
||||
str_to_double(const char *s, double *d)
|
||||
{
|
||||
int save_errno = errno;
|
||||
char *tail;
|
||||
errno = 0;
|
||||
*d = strtod(s, &tail);
|
||||
if (errno == EINVAL || (errno == ERANGE && *d != 0)
|
||||
|| tail == s || *tail != '\0') {
|
||||
errno = save_errno;
|
||||
*d = 0;
|
||||
return false;
|
||||
} else {
|
||||
errno = save_errno;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns the value of 'c' as a hexadecimal digit. */
|
||||
int
|
||||
hexit_value(int c)
|
||||
{
|
||||
switch (c) {
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
return c - '0';
|
||||
|
||||
case 'a': case 'A':
|
||||
return 0xa;
|
||||
|
||||
case 'b': case 'B':
|
||||
return 0xb;
|
||||
|
||||
case 'c': case 'C':
|
||||
return 0xc;
|
||||
|
||||
case 'd': case 'D':
|
||||
return 0xd;
|
||||
|
||||
case 'e': case 'E':
|
||||
return 0xe;
|
||||
|
||||
case 'f': case 'F':
|
||||
return 0xf;
|
||||
}
|
||||
|
||||
NOT_REACHED();
|
||||
}
|
||||
|
@@ -121,6 +121,10 @@ bool str_to_uint(const char *, int base, unsigned int *);
|
||||
bool str_to_ulong(const char *, int base, unsigned long *);
|
||||
bool str_to_ullong(const char *, int base, unsigned long long *);
|
||||
|
||||
bool str_to_double(const char *, double *);
|
||||
|
||||
int hexit_value(int c);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
1
tests/.gitignore
vendored
1
tests/.gitignore
vendored
@@ -5,6 +5,7 @@
|
||||
/test-flows
|
||||
/test-hash
|
||||
/test-hmap
|
||||
/test-json
|
||||
/test-list
|
||||
/test-stp
|
||||
/test-type-props
|
||||
|
@@ -8,6 +8,7 @@ TESTSUITE_AT = \
|
||||
tests/testsuite.at \
|
||||
tests/lcov-pre.at \
|
||||
tests/library.at \
|
||||
tests/json.at \
|
||||
tests/timeval.at \
|
||||
tests/lockfile.at \
|
||||
tests/stp.at \
|
||||
@@ -60,6 +61,10 @@ noinst_PROGRAMS += tests/test-hmap
|
||||
tests_test_hmap_SOURCES = tests/test-hmap.c
|
||||
tests_test_hmap_LDADD = lib/libopenvswitch.a
|
||||
|
||||
noinst_PROGRAMS += tests/test-json
|
||||
tests_test_json_SOURCES = tests/test-json.c
|
||||
tests_test_json_LDADD = lib/libopenvswitch.a
|
||||
|
||||
noinst_PROGRAMS += tests/test-list
|
||||
tests_test_list_SOURCES = tests/test-list.c
|
||||
tests_test_list_LDADD = lib/libopenvswitch.a
|
||||
|
297
tests/json.at
Normal file
297
tests/json.at
Normal file
@@ -0,0 +1,297 @@
|
||||
m4_define([JSON_CHECK_POSITIVE],
|
||||
[AT_SETUP([$1])
|
||||
AT_KEYWORDS([json positive])
|
||||
AT_CHECK([printf %s "AS_ESCAPE([$2])" > input])
|
||||
OVS_CHECK_LCOV([test-json $4 input], [0], [$3
|
||||
], [])
|
||||
AT_CLEANUP])
|
||||
|
||||
m4_define([JSON_CHECK_NEGATIVE],
|
||||
[AT_SETUP([$1])
|
||||
AT_KEYWORDS([json negative])
|
||||
AT_CHECK([printf %s "AS_ESCAPE([$2])" > input])
|
||||
OVS_CHECK_LCOV([test-json $4 input], [1], [$3
|
||||
])
|
||||
AT_CLEANUP])
|
||||
|
||||
AT_BANNER([JSON -- arrays])
|
||||
|
||||
JSON_CHECK_POSITIVE([empty array], [[ [ ] ]], [[[]]])
|
||||
JSON_CHECK_POSITIVE([single-element array], [[ [ 1 ] ]], [[[1]]])
|
||||
JSON_CHECK_POSITIVE([2-element array], [[ [ 1, 2 ] ]], [[[1,2]]])
|
||||
JSON_CHECK_POSITIVE([many-element array],
|
||||
[[ [ 1, 2, 3, 4, 5 ] ]],
|
||||
[[[1,2,3,4,5]]])
|
||||
JSON_CHECK_NEGATIVE([missing comma], [[ [ 1, 2, 3 4, 5 ] ]],
|
||||
[error: syntax error expecting '@:>@' or ','])
|
||||
JSON_CHECK_NEGATIVE([trailing comma not allowed],
|
||||
[[[1,2,]]], [error: syntax error expecting value])
|
||||
JSON_CHECK_NEGATIVE([doubled comma not allowed],
|
||||
[[[1,,2]]], [error: syntax error expecting value])
|
||||
|
||||
AT_BANNER([JSON -- strings])
|
||||
|
||||
JSON_CHECK_POSITIVE([empty string], [[[ "" ]]], [[[""]]])
|
||||
JSON_CHECK_POSITIVE([1-character strings],
|
||||
[[[ "a", "b", "c" ]]],
|
||||
[[["a","b","c"]]])
|
||||
JSON_CHECK_POSITIVE([escape sequences],
|
||||
[[[ " \" \\ \/ \b \f \n \r \t" ]]],
|
||||
[[[" \" \\ / \b \f \n \r \t"]]])
|
||||
JSON_CHECK_POSITIVE([Unicode escape sequences],
|
||||
[[[ " \u0022 \u005c \u002F \u0008 \u000c \u000A \u000d \u0009" ]]],
|
||||
[[[" \" \\ / \b \f \n \r \t"]]])
|
||||
JSON_CHECK_POSITIVE([surrogate pairs],
|
||||
[[["\ud834\udd1e"]]],
|
||||
[[["𝄞"]]])
|
||||
JSON_CHECK_NEGATIVE([a string by itself is not valid JSON], ["xxx"],
|
||||
[error: syntax error at beginning of input])
|
||||
JSON_CHECK_NEGATIVE([end of line in quoted string],
|
||||
[[["xxx
|
||||
"]]],
|
||||
[error: U+000A must be escaped in quoted string])
|
||||
JSON_CHECK_NEGATIVE([formfeed in quoted string],
|
||||
[[["xxx"]]],
|
||||
[error: U+000C must be escaped in quoted string])
|
||||
JSON_CHECK_NEGATIVE([bad escape in quoted string],
|
||||
[[["\x12"]]],
|
||||
[error: bad escape \x])
|
||||
JSON_CHECK_NEGATIVE([\u must be followed by 4 hex digits],
|
||||
[[["\u1x"]]],
|
||||
[error: malformed \u escape])
|
||||
JSON_CHECK_NEGATIVE([isolated leading surrogate not allowed],
|
||||
[[["\ud834xxx"]]],
|
||||
[error: malformed escaped surrogate pair])
|
||||
JSON_CHECK_NEGATIVE([surrogatess must paired properly],
|
||||
[[["\ud834\u1234"]]],
|
||||
[error: second half of escaped surrogate pair is not trailing surrogate])
|
||||
JSON_CHECK_NEGATIVE([null bytes not allowed],
|
||||
[[["\u0000"]]],
|
||||
[error: null bytes not supported in quoted strings])
|
||||
|
||||
AT_SETUP([end of input in quoted string])
|
||||
AT_KEYWORDS([json negative])
|
||||
AT_CHECK([printf '\"xxx' | test-json -], [1],
|
||||
[error: unexpected end of input in quoted string
|
||||
])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_BANNER([JSON -- objects])
|
||||
|
||||
JSON_CHECK_POSITIVE([empty object], [[{ }]], [[{}]])
|
||||
JSON_CHECK_POSITIVE([simple object],
|
||||
[[{"b": 2, "a": 1, "c": 3}]],
|
||||
[[{"a":1,"b":2,"c":3}]])
|
||||
JSON_CHECK_NEGATIVE([bad value], [[{"a": }, "b": 2]],
|
||||
[error: syntax error expecting value])
|
||||
JSON_CHECK_NEGATIVE([missing colon], [[{"b": 2, "a" 1, "c": 3}]],
|
||||
[error: syntax error parsing object expecting ':'])
|
||||
JSON_CHECK_NEGATIVE([missing comma], [[{"b": 2 "a" 1, "c": 3}]],
|
||||
[error: syntax error expecting '}' or ','])
|
||||
JSON_CHECK_NEGATIVE([trailing comma not allowed],
|
||||
[[{"b": 2, "a": 1, "c": 3, }]],
|
||||
[[error: syntax error parsing object expecting string]])
|
||||
JSON_CHECK_NEGATIVE([doubled comma not allowed],
|
||||
[[{"b": 2, "a": 1,, "c": 3}]],
|
||||
[[error: syntax error parsing object expecting string]])
|
||||
JSON_CHECK_NEGATIVE([names must be strings],
|
||||
[[{1: 2}]],
|
||||
[[error: syntax error parsing object expecting string]])
|
||||
|
||||
AT_BANNER([JSON -- literal names])
|
||||
|
||||
JSON_CHECK_POSITIVE([null], [[[ null ]]], [[[null]]])
|
||||
JSON_CHECK_POSITIVE([false], [[[ false ]]], [[[false]]])
|
||||
JSON_CHECK_POSITIVE([true], [[[ true ]]], [[[true]]])
|
||||
JSON_CHECK_NEGATIVE([a literal by itself is not valid JSON], [null],
|
||||
[error: syntax error at beginning of input])
|
||||
JSON_CHECK_NEGATIVE([nullify is invalid], [[[ nullify ]]],
|
||||
[error: invalid keyword 'nullify'])
|
||||
JSON_CHECK_NEGATIVE([nubs is invalid], [[[ nubs ]]],
|
||||
[error: invalid keyword 'nubs'])
|
||||
JSON_CHECK_NEGATIVE([xxx is invalid], [[[ xxx ]]],
|
||||
[error: invalid keyword 'xxx'])
|
||||
|
||||
AT_BANNER([JSON -- numbers])
|
||||
|
||||
JSON_CHECK_POSITIVE(
|
||||
[integers expressed as reals],
|
||||
[[[1.0000000000,
|
||||
2.00000000000000000000000000000000000,
|
||||
2e5,
|
||||
2.1234e4,
|
||||
2.1230e3,
|
||||
0e-10000,
|
||||
0e10000]]],
|
||||
[[[1,2,200000,21234,2123,0,0]]])
|
||||
JSON_CHECK_POSITIVE(
|
||||
[large integers],
|
||||
[[[9223372036854775807, -9223372036854775808]]],
|
||||
[[[9223372036854775807,-9223372036854775808]]])
|
||||
JSON_CHECK_POSITIVE(
|
||||
[large integers expressed as reals],
|
||||
[[[9223372036854775807.0, -9223372036854775808.0,
|
||||
92233720.36854775807e11, -9.223372036854775808e18]]],
|
||||
[[[9223372036854775807,-9223372036854775808,9223372036854775807,-9223372036854775808]]])
|
||||
# It seems likely that the following test will fail on some system that
|
||||
# rounds slightly differently in arithmetic or in printf, but I'd like
|
||||
# to keep it this way until we run into such a system.
|
||||
JSON_CHECK_POSITIVE(
|
||||
[large integers that overflow to reals],
|
||||
[[[9223372036854775807000, -92233720368547758080000]]],
|
||||
[[[9.22337203685478e+21,-9.22337203685478e+22]]])
|
||||
|
||||
JSON_CHECK_POSITIVE(
|
||||
[negative zero],
|
||||
[[[-0, -0.0, 1e-9999, -1e-9999]]],
|
||||
[[[0,0,0,0]]])
|
||||
|
||||
JSON_CHECK_POSITIVE(
|
||||
[reals],
|
||||
[[[0.0, 1.0, 2.0, 3.0, 3.5, 81.250]]],
|
||||
[[[0,1,2,3,3.5,81.25]]])
|
||||
JSON_CHECK_POSITIVE(
|
||||
[scientific notation],
|
||||
[[[1e3, 1E3, 2.5E2, 1e+3, 125e-3, 3.125e-2, 3125e-05, 1.525878906e-5]]],
|
||||
[[[1000,1000,250,1000,0.125,0.03125,0.03125,1.525878906e-05]]])
|
||||
JSON_CHECK_POSITIVE(
|
||||
[negative reals],
|
||||
[[[-0, -1.0, -2.0, -3.0, -3.5, -8.1250]]],
|
||||
[[[0,-1,-2,-3,-3.5,-8.125]]])
|
||||
JSON_CHECK_POSITIVE(
|
||||
[negative scientific notation],
|
||||
[[[-1e3, -1E3, -2.5E2, -1e+3, -125e-3, -3.125e-2, -3125e-05, -1.525878906e-5]]],
|
||||
[[[-1000,-1000,-250,-1000,-0.125,-0.03125,-0.03125,-1.525878906e-05]]])
|
||||
JSON_CHECK_POSITIVE(
|
||||
[1e-9999 underflows to 0],
|
||||
[[[1e-9999]]],
|
||||
[[[0]]])
|
||||
JSON_CHECK_NEGATIVE([a number by itself is not valid JSON], [1],
|
||||
[error: syntax error at beginning of input])
|
||||
JSON_CHECK_NEGATIVE(
|
||||
[leading zeros not allowed],
|
||||
[[[0123]]],
|
||||
[error: leading zeros not allowed])
|
||||
JSON_CHECK_NEGATIVE(
|
||||
[1e9999 is too big],
|
||||
[[[1e9999]]],
|
||||
[error: number outside valid range])
|
||||
JSON_CHECK_NEGATIVE(
|
||||
[exponent bigger than INT_MAX],
|
||||
[[[1e9999999999999999999]]],
|
||||
[error: exponent outside valid range])
|
||||
JSON_CHECK_NEGATIVE(
|
||||
[decimal point must be followed by digit],
|
||||
[[[1.]]],
|
||||
[error: decimal point must be followed by digit])
|
||||
JSON_CHECK_NEGATIVE(
|
||||
[exponent must contain at least one digit (1)],
|
||||
[[[1e]]],
|
||||
[error: exponent must contain at least one digit])
|
||||
JSON_CHECK_NEGATIVE(
|
||||
[exponent must contain at least one digit (2)],
|
||||
[[[1e+]]],
|
||||
[error: exponent must contain at least one digit])
|
||||
JSON_CHECK_NEGATIVE(
|
||||
[exponent must contain at least one digit (3)],
|
||||
[[[1e-]]],
|
||||
[error: exponent must contain at least one digit])
|
||||
|
||||
AT_BANNER([JSON -- RFC 4627 examples])
|
||||
|
||||
JSON_CHECK_POSITIVE([RFC 4267 object example],
|
||||
[[{
|
||||
"Image": {
|
||||
"Width": 800,
|
||||
"Height": 600,
|
||||
"Title": "View from 15th Floor",
|
||||
"Thumbnail": {
|
||||
"Url": "http://www.example.com/image/481989943",
|
||||
"Height": 125,
|
||||
"Width": "100"
|
||||
},
|
||||
"IDs": [116, 943, 234, 38793]
|
||||
}
|
||||
}]],
|
||||
[[{"Image":{"Height":600,"IDs":[116,943,234,38793],"Thumbnail":{"Height":125,"Url":"http://www.example.com/image/481989943","Width":"100"},"Title":"View from 15th Floor","Width":800}}]])
|
||||
|
||||
JSON_CHECK_POSITIVE([RFC 4267 array example],
|
||||
[[[
|
||||
{
|
||||
"precision": "zip",
|
||||
"Latitude": 37.7668,
|
||||
"Longitude": -122.3959,
|
||||
"Address": "",
|
||||
"City": "SAN FRANCISCO",
|
||||
"State": "CA",
|
||||
"Zip": "94107",
|
||||
"Country": "US"
|
||||
},
|
||||
{
|
||||
"precision": "zip",
|
||||
"Latitude": 37.371991,
|
||||
"Longitude": -122.026020,
|
||||
"Address": "",
|
||||
"City": "SUNNYVALE",
|
||||
"State": "CA",
|
||||
"Zip": "94085",
|
||||
"Country": "US"
|
||||
}
|
||||
]]],
|
||||
[[[{"Address":"","City":"SAN FRANCISCO","Country":"US","Latitude":37.7668,"Longitude":-122.3959,"State":"CA","Zip":"94107","precision":"zip"},{"Address":"","City":"SUNNYVALE","Country":"US","Latitude":37.371991,"Longitude":-122.02602,"State":"CA","Zip":"94085","precision":"zip"}]]])
|
||||
|
||||
AT_BANNER([JSON -- pathological cases])
|
||||
|
||||
JSON_CHECK_NEGATIVE([trailing garbage], [[[1]null]],
|
||||
[error: trailing garbage at end of input])
|
||||
JSON_CHECK_NEGATIVE([formfeeds are not valid white space],
|
||||
[[[]]], [error: invalid character U+000c])
|
||||
JSON_CHECK_NEGATIVE([';' is not a valid token],
|
||||
[;], [error: invalid character ';'])
|
||||
JSON_CHECK_NEGATIVE([arrays nesting too deep],
|
||||
[m4_for([i], [0], [1002], [1], [@<:@])dnl
|
||||
m4_for([i], [0], [1002], [1], [@:>@])],
|
||||
[error: input exceeds maximum nesting depth 1000])
|
||||
JSON_CHECK_NEGATIVE([objects nesting too deep],
|
||||
[m4_for([i], [0], [1002], [1], [{"x":])dnl
|
||||
m4_for([i], [0], [1002], [1], [}])],
|
||||
[error: input exceeds maximum nesting depth 1000])
|
||||
|
||||
AT_SETUP([input may not be empty])
|
||||
AT_KEYWORDS([json negative])
|
||||
AT_CHECK([test-json /dev/null], [1], [error: empty input stream
|
||||
])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_BANNER([JSON -- multiple inputs])
|
||||
|
||||
JSON_CHECK_POSITIVE([multiple adjacent objects], [[{}{}{}]], [[{}
|
||||
{}
|
||||
{}]],
|
||||
[--multiple])
|
||||
|
||||
JSON_CHECK_POSITIVE([multiple space-separated objects], [[{} {} {}]], [[{}
|
||||
{}
|
||||
{}]],
|
||||
[--multiple])
|
||||
|
||||
JSON_CHECK_POSITIVE([multiple objects on separate lines], [[{}
|
||||
{}
|
||||
{}]], [[{}
|
||||
{}
|
||||
{}]],
|
||||
[--multiple])
|
||||
|
||||
JSON_CHECK_POSITIVE([multiple objects and arrays], [[{}[]{}[]]], [[{}
|
||||
[]
|
||||
{}
|
||||
[]]],
|
||||
[--multiple])
|
||||
|
||||
JSON_CHECK_NEGATIVE([garbage between multiple objects], [[{}x{}]], [[{}
|
||||
error: invalid keyword 'x'
|
||||
{}]], [--multiple])
|
||||
|
||||
JSON_CHECK_NEGATIVE([garbage after multiple objects], [[{}{}x]], [[{}
|
||||
{}
|
||||
error: invalid keyword 'x']], [--multiple])
|
160
tests/test-json.c
Normal file
160
tests/test-json.c
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Nicira Networks.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "json.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
/* --pretty: If set, the JSON output is pretty-printed, instead of printed as
|
||||
* compactly as possible. */
|
||||
static int pretty = 0;
|
||||
|
||||
/* --multiple: If set, the input is a sequence of JSON objects or arrays,
|
||||
* instead of exactly one object or array. */
|
||||
static int multiple = 0;
|
||||
|
||||
static bool
|
||||
print_and_free_json(struct json *json)
|
||||
{
|
||||
bool ok;
|
||||
if (json->type == JSON_STRING) {
|
||||
printf("error: %s\n", json->u.string);
|
||||
ok = false;
|
||||
} else {
|
||||
char *s = json_to_string(json, JSSF_SORT | (pretty ? JSSF_PRETTY : 0));
|
||||
puts(s);
|
||||
free(s);
|
||||
ok = true;
|
||||
}
|
||||
json_destroy(json);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool
|
||||
refill(FILE *file, void *buffer, size_t buffer_size, size_t *n, size_t *used)
|
||||
{
|
||||
*used = 0;
|
||||
if (feof(file)) {
|
||||
*n = 0;
|
||||
return false;
|
||||
} else {
|
||||
*n = fread(buffer, 1, buffer_size, file);
|
||||
if (ferror(file)) {
|
||||
ovs_fatal(errno, "Error reading input file");
|
||||
}
|
||||
return *n > 0;
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
parse_multiple(const char *input_file)
|
||||
{
|
||||
struct json_parser *parser;
|
||||
char buffer[BUFSIZ];
|
||||
size_t n, used;
|
||||
FILE *file;
|
||||
bool ok;
|
||||
|
||||
file = fopen(input_file, "r");
|
||||
if (!file) {
|
||||
ovs_fatal(errno, "Cannot open \"%s\"", input_file);
|
||||
}
|
||||
|
||||
parser = NULL;
|
||||
n = used = 0;
|
||||
ok = true;
|
||||
while (used < n || refill(file, buffer, sizeof buffer, &n, &used)) {
|
||||
if (!parser && isspace((unsigned char) buffer[used])) {
|
||||
/* Skip white space. */
|
||||
used++;
|
||||
} else {
|
||||
if (!parser) {
|
||||
parser = json_parser_create(0);
|
||||
}
|
||||
|
||||
used = n - json_parser_feed(parser, &buffer[used], n - used);
|
||||
if (used < n) {
|
||||
if (!print_and_free_json(json_parser_finish(parser))) {
|
||||
ok = false;
|
||||
}
|
||||
parser = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (parser) {
|
||||
if (!print_and_free_json(json_parser_finish(parser))) {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
const char *input_file;
|
||||
bool ok;
|
||||
|
||||
set_program_name(argv[0]);
|
||||
|
||||
for (;;) {
|
||||
static const struct option options[] = {
|
||||
{"pretty", no_argument, &pretty, 1},
|
||||
{"multiple", no_argument, &multiple, 1},
|
||||
};
|
||||
int option_index = 0;
|
||||
int c = getopt_long (argc, argv, "", options, &option_index);
|
||||
|
||||
if (c == -1) {
|
||||
break;
|
||||
}
|
||||
switch (c) {
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case '?':
|
||||
exit(1);
|
||||
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
if (argc - optind != 1) {
|
||||
ovs_fatal(0, "usage: %s [--pretty] [--multiple] INPUT.json",
|
||||
program_name);
|
||||
}
|
||||
|
||||
input_file = argv[optind];
|
||||
if (!strcmp(input_file, "-")) {
|
||||
input_file = "/dev/stdin";
|
||||
}
|
||||
|
||||
if (multiple) {
|
||||
ok = parse_multiple(input_file);
|
||||
} else {
|
||||
ok = print_and_free_json(json_from_file(input_file));
|
||||
}
|
||||
|
||||
return !ok;
|
||||
}
|
@@ -19,6 +19,7 @@ AT_TESTED([ovs-vsctl])
|
||||
|
||||
m4_include([tests/lcov-pre.at])
|
||||
m4_include([tests/library.at])
|
||||
m4_include([tests/json.at])
|
||||
m4_include([tests/timeval.at])
|
||||
m4_include([tests/lockfile.at])
|
||||
m4_include([tests/stp.at])
|
||||
|
Reference in New Issue
Block a user