Commit fe602d2d authored by Johny Mattsson's avatar Johny Mattsson
Browse files

Removed all currently-unused code & docs.

Heading towards having only ESP32-aware/capable code in this branch.
parent ddeb26c4
Network Working Group D. Crockford
Request for Comments: 4627 JSON.org
Category: Informational July 2006
The application/json Media Type for JavaScript Object Notation (JSON)
Status of This Memo
This memo provides information for the Internet community. It does
not specify an Internet standard of any kind. Distribution of this
memo is unlimited.
Copyright Notice
Copyright (C) The Internet Society (2006).
Abstract
JavaScript Object Notation (JSON) is a lightweight, text-based,
language-independent data interchange format. It was derived from
the ECMAScript Programming Language Standard. JSON defines a small
set of formatting rules for the portable representation of structured
data.
1. Introduction
JavaScript Object Notation (JSON) is a text format for the
serialization of structured data. It is derived from the object
literals of JavaScript, as defined in the ECMAScript Programming
Language Standard, Third Edition [ECMA].
JSON can represent four primitive types (strings, numbers, booleans,
and null) and two structured types (objects and arrays).
A string is a sequence of zero or more Unicode characters [UNICODE].
An object is an unordered collection of zero or more name/value
pairs, where a name is a string and a value is a string, number,
boolean, null, object, or array.
An array is an ordered sequence of zero or more values.
The terms "object" and "array" come from the conventions of
JavaScript.
JSON's design goals were for it to be minimal, portable, textual, and
a subset of JavaScript.
Crockford Informational [Page 1]
RFC 4627 JSON July 2006
1.1. Conventions Used in This Document
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
document are to be interpreted as described in [RFC2119].
The grammatical rules in this document are to be interpreted as
described in [RFC4234].
2. JSON Grammar
A JSON text is a sequence of tokens. The set of tokens includes six
structural characters, strings, numbers, and three literal names.
A JSON text is a serialized object or array.
JSON-text = object / array
These are the six structural characters:
begin-array = ws %x5B ws ; [ left square bracket
begin-object = ws %x7B ws ; { left curly bracket
end-array = ws %x5D ws ; ] right square bracket
end-object = ws %x7D ws ; } right curly bracket
name-separator = ws %x3A ws ; : colon
value-separator = ws %x2C ws ; , comma
Insignificant whitespace is allowed before or after any of the six
structural characters.
ws = *(
%x20 / ; Space
%x09 / ; Horizontal tab
%x0A / ; Line feed or New line
%x0D ; Carriage return
)
2.1. Values
A JSON value MUST be an object, array, number, or string, or one of
the following three literal names:
false null true
Crockford Informational [Page 2]
RFC 4627 JSON July 2006
The literal names MUST be lowercase. No other literal names are
allowed.
value = false / null / true / object / array / number / string
false = %x66.61.6c.73.65 ; false
null = %x6e.75.6c.6c ; null
true = %x74.72.75.65 ; true
2.2. Objects
An object structure is represented as a pair of curly brackets
surrounding zero or more name/value pairs (or members). A name is a
string. A single colon comes after each name, separating the name
from the value. A single comma separates a value from a following
name. The names within an object SHOULD be unique.
object = begin-object [ member *( value-separator member ) ]
end-object
member = string name-separator value
2.3. Arrays
An array structure is represented as square brackets surrounding zero
or more values (or elements). Elements are separated by commas.
array = begin-array [ value *( value-separator value ) ] end-array
2.4. Numbers
The representation of numbers is similar to that used in most
programming languages. A number contains an integer component that
may be prefixed with an optional minus sign, which may be followed by
a fraction part and/or an exponent part.
Octal and hex forms are not allowed. Leading zeros are not allowed.
A fraction part is a decimal point followed by one or more digits.
An exponent part begins with the letter E in upper or lowercase,
which may be followed by a plus or minus sign. The E and optional
sign are followed by one or more digits.
Numeric values that cannot be represented as sequences of digits
(such as Infinity and NaN) are not permitted.
Crockford Informational [Page 3]
RFC 4627 JSON July 2006
number = [ minus ] int [ frac ] [ exp ]
decimal-point = %x2E ; .
digit1-9 = %x31-39 ; 1-9
e = %x65 / %x45 ; e E
exp = e [ minus / plus ] 1*DIGIT
frac = decimal-point 1*DIGIT
int = zero / ( digit1-9 *DIGIT )
minus = %x2D ; -
plus = %x2B ; +
zero = %x30 ; 0
2.5. Strings
The representation of strings is similar to conventions used in the C
family of programming languages. A string begins and ends with
quotation marks. All Unicode characters may be placed within the
quotation marks except for the characters that must be escaped:
quotation mark, reverse solidus, and the control characters (U+0000
through U+001F).
Any character may be escaped. If the character is in the Basic
Multilingual Plane (U+0000 through U+FFFF), then it may be
represented as a six-character sequence: a reverse solidus, followed
by the lowercase letter u, followed by four hexadecimal digits that
encode the character's code point. The hexadecimal letters A though
F can be upper or lowercase. So, for example, a string containing
only a single reverse solidus character may be represented as
"\u005C".
Alternatively, there are two-character sequence escape
representations of some popular characters. So, for example, a
string containing only a single reverse solidus character may be
represented more compactly as "\\".
To escape an extended character that is not in the Basic Multilingual
Plane, the character is represented as a twelve-character sequence,
encoding the UTF-16 surrogate pair. So, for example, a string
containing only the G clef character (U+1D11E) may be represented as
"\uD834\uDD1E".
Crockford Informational [Page 4]
RFC 4627 JSON July 2006
string = quotation-mark *char quotation-mark
char = unescaped /
escape (
%x22 / ; " quotation mark U+0022
%x5C / ; \ reverse solidus U+005C
%x2F / ; / solidus U+002F
%x62 / ; b backspace U+0008
%x66 / ; f form feed U+000C
%x6E / ; n line feed U+000A
%x72 / ; r carriage return U+000D
%x74 / ; t tab U+0009
%x75 4HEXDIG ) ; uXXXX U+XXXX
escape = %x5C ; \
quotation-mark = %x22 ; "
unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
3. Encoding
JSON text SHALL be encoded in Unicode. The default encoding is
UTF-8.
Since the first two characters of a JSON text will always be ASCII
characters [RFC0020], it is possible to determine whether an octet
stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking
at the pattern of nulls in the first four octets.
00 00 00 xx UTF-32BE
00 xx 00 xx UTF-16BE
xx 00 00 00 UTF-32LE
xx 00 xx 00 UTF-16LE
xx xx xx xx UTF-8
4. Parsers
A JSON parser transforms a JSON text into another representation. A
JSON parser MUST accept all texts that conform to the JSON grammar.
A JSON parser MAY accept non-JSON forms or extensions.
An implementation may set limits on the size of texts that it
accepts. An implementation may set limits on the maximum depth of
nesting. An implementation may set limits on the range of numbers.
An implementation may set limits on the length and character contents
of strings.
Crockford Informational [Page 5]
RFC 4627 JSON July 2006
5. Generators
A JSON generator produces JSON text. The resulting text MUST
strictly conform to the JSON grammar.
6. IANA Considerations
The MIME media type for JSON text is application/json.
Type name: application
Subtype name: json
Required parameters: n/a
Optional parameters: n/a
Encoding considerations: 8bit if UTF-8; binary if UTF-16 or UTF-32
JSON may be represented using UTF-8, UTF-16, or UTF-32. When JSON
is written in UTF-8, JSON is 8bit compatible. When JSON is
written in UTF-16 or UTF-32, the binary content-transfer-encoding
must be used.
Security considerations:
Generally there are security issues with scripting languages. JSON
is a subset of JavaScript, but it is a safe subset that excludes
assignment and invocation.
A JSON text can be safely passed into JavaScript's eval() function
(which compiles and executes a string) if all the characters not
enclosed in strings are in the set of characters that form JSON
tokens. This can be quickly determined in JavaScript with two
regular expressions and calls to the test and replace methods.
var my_JSON_object = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
text.replace(/"(\\.|[^"\\])*"/g, ''))) &&
eval('(' + text + ')');
Interoperability considerations: n/a
Published specification: RFC 4627
Crockford Informational [Page 6]
RFC 4627 JSON July 2006
Applications that use this media type:
JSON has been used to exchange data between applications written
in all of these programming languages: ActionScript, C, C#,
ColdFusion, Common Lisp, E, Erlang, Java, JavaScript, Lua,
Objective CAML, Perl, PHP, Python, Rebol, Ruby, and Scheme.
Additional information:
Magic number(s): n/a
File extension(s): .json
Macintosh file type code(s): TEXT
Person & email address to contact for further information:
Douglas Crockford
douglas@crockford.com
Intended usage: COMMON
Restrictions on usage: none
Author:
Douglas Crockford
douglas@crockford.com
Change controller:
Douglas Crockford
douglas@crockford.com
7. Security Considerations
See Security Considerations in Section 6.
8. Examples
This is a JSON object:
{
"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]
Crockford Informational [Page 7]
RFC 4627 JSON July 2006
}
}
Its Image member is an object whose Thumbnail member is an object
and whose IDs member is an array of numbers.
This is a JSON array containing two objects:
[
{
"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"
}
]
9. References
9.1. Normative References
[ECMA] European Computer Manufacturers Association, "ECMAScript
Language Specification 3rd Edition", December 1999,
<http://www.ecma-international.org/publications/files/
ecma-st/ECMA-262.pdf>.
[RFC0020] Cerf, V., "ASCII format for network interchange", RFC 20,
October 1969.
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119, March 1997.
[RFC4234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
Specifications: ABNF", RFC 4234, October 2005.
Crockford Informational [Page 8]
RFC 4627 JSON July 2006
[UNICODE] The Unicode Consortium, "The Unicode Standard Version 4.0",
2003, <http://www.unicode.org/versions/Unicode4.1.0/>.
Author's Address
Douglas Crockford
JSON.org
EMail: douglas@crockford.com
Crockford Informational [Page 9]
RFC 4627 JSON July 2006
Full Copyright Statement
Copyright (C) The Internet Society (2006).
This document is subject to the rights, licenses and restrictions
contained in BCP 78, and except as set forth therein, the authors
retain all their rights.
This document and the information contained herein are provided on an
"AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
Intellectual Property
The IETF takes no position regarding the validity or scope of any
Intellectual Property Rights or other rights that might be claimed to
pertain to the implementation or use of the technology described in
this document or the extent to which any license under such rights
might or might not be available; nor does it represent that it has
made any independent effort to identify any such rights. Information
on the procedures with respect to rights in RFC documents can be
found in BCP 78 and BCP 79.
Copies of IPR disclosures made to the IETF Secretariat and any
assurances of licenses to be made available, or the result of an
attempt made to obtain a general license or permission for the use of
such proprietary rights by implementers or users of this
specification can be obtained from the IETF on-line IPR repository at
http://www.ietf.org/ipr.
The IETF invites any interested party to bring to its attention any
copyrights, patents or patent applications, or other proprietary
rights that may cover technology that may be required to implement
this standard. Please address the information to the IETF at
ietf-ipr@ietf.org.
Acknowledgement
Funding for the RFC Editor function is provided by the IETF
Administrative Support Activity (IASA).
Crockford Informational [Page 10]
/* strbuf - String buffer routines
*
* Copyright (c) 2010-2012 Mark Pulford <mark@kyne.com.au>
*
* 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "strbuf.h"
#include "cjson_mem.h"
int strbuf_init(strbuf_t *s, int len)
{
int size;
if (len <= 0)
size = STRBUF_DEFAULT_SIZE;
else
size = len + 1; /* \0 terminator */
s->buf = NULL;
s->size = size;
s->length = 0;
s->increment = STRBUF_DEFAULT_INCREMENT;
s->dynamic = 0;
s->reallocs = 0;
s->debug = 0;
s->buf = (char *)cjson_mem_malloc(size);
if (!s->buf){
NODE_ERR("not enough memory\n");
return -1;
}
strbuf_ensure_null(s);
return 0;
}
strbuf_t *strbuf_new(int len)
{
strbuf_t *s;
s = (strbuf_t *)cjson_mem_malloc(sizeof(strbuf_t));
if (!s){
NODE_ERR("not enough memory\n");
return NULL;
}
strbuf_init(s, len);
/* Dynamic strbuf allocation / deallocation */
s->dynamic = 1;
return s;
}
int strbuf_set_increment(strbuf_t *s, int increment)
{
/* Increment > 0: Linear buffer growth rate
* Increment < -1: Exponential buffer growth rate */
if (increment == 0 || increment == -1){
NODE_ERR("BUG: Invalid string increment");
return -1;
}
s->increment = increment;
return 0;
}
static inline void debug_stats(strbuf_t *s)
{
if (s->debug) {
NODE_ERR("strbuf(%lx) reallocs: %d, length: %d, size: %d\n",
(long)s, s->reallocs, s->length, s->size);
}
}
/* If strbuf_t has not been dynamically allocated, strbuf_free() can
* be called any number of times strbuf_init() */
void strbuf_free(strbuf_t *s)
{
debug_stats(s);
if (s->buf) {
free(s->buf);
s->buf = NULL;
}
if (s->dynamic)
free(s);
}
char *strbuf_free_to_string(strbuf_t *s, int *len)
{
char *buf;
debug_stats(s);
strbuf_ensure_null(s);
buf = s->buf;
if (len)
*len = s->length;
if (s->dynamic)
free(s);
return buf;
}
static int calculate_new_size(strbuf_t *s, int len)
{
int reqsize, newsize;
if (len <= 0){
NODE_ERR("BUG: Invalid strbuf length requested");
return 0;
}
/* Ensure there is room for optional NULL termination */
reqsize = len + 1;
/* If the user has requested to shrink the buffer, do it exactly */
if (s->size > reqsize)
return reqsize;
newsize = s->size;
if (s->increment < 0) {
/* Exponential sizing */
while (newsize < reqsize)
newsize *= -s->increment;
} else {
/* Linear sizing */
newsize = (((reqsize -1) / s->increment) + 1) * s->increment;
}
return newsize;
}
/* Ensure strbuf can handle a string length bytes long (ignoring NULL
* optional termination). */
int strbuf_resize(strbuf_t *s, int len)
{
int newsize;
newsize = calculate_new_size(s, len);
if (s->debug > 1) {
NODE_ERR("strbuf(%lx) resize: %d => %d\n",
(long)s, s->size, newsize);
}
s->buf = (char *)cjson_mem_realloc(s->buf, newsize);
if (!s->buf){
NODE_ERR("not enough memory");
return -1;
}
s->size = newsize;
s->reallocs++;
return 0;
}
void strbuf_append_string(strbuf_t *s, const char *str)
{
int space, i;
space = strbuf_empty_length(s);
for (i = 0; str[i]; i++) {
if (space < 1) {
strbuf_resize(s, s->length + 1);
space = strbuf_empty_length(s);
}
s->buf[s->length] = str[i];
s->length++;
space--;
}
}
#if 0
/* strbuf_append_fmt() should only be used when an upper bound
* is known for the output string. */
void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...)
{
va_list arg;
int fmt_len;
strbuf_ensure_empty_length(s, len);
va_start(arg, fmt);
fmt_len = vsnprintf(s->buf + s->length, len, fmt, arg);
va_end(arg);
if (fmt_len < 0)
die("BUG: Unable to convert number"); /* This should never happen.. */
s->length += fmt_len;
}
/* strbuf_append_fmt_retry() can be used when the there is no known
* upper bound for the output string. */
void strbuf_append_fmt_retry(strbuf_t *s, const char *fmt, ...)
{
va_list arg;
int fmt_len, try;
int empty_len;
/* If the first attempt to append fails, resize the buffer appropriately
* and try again */
for (try = 0; ; try++) {
va_start(arg, fmt);
/* Append the new formatted string */
/* fmt_len is the length of the string required, excluding the
* trailing NULL */
empty_len = strbuf_empty_length(s);
/* Add 1 since there is also space to store the terminating NULL. */
fmt_len = vsnprintf(s->buf + s->length, empty_len + 1, fmt, arg);
va_end(arg);
if (fmt_len <= empty_len)
break; /* SUCCESS */
if (try > 0)
die("BUG: length of formatted string changed");
strbuf_resize(s, s->length + fmt_len);
}
s->length += fmt_len;
}
#endif
/* vi:ai et sw=4 ts=4:
*/
/* strbuf - String buffer routines
*
* Copyright (c) 2010-2012 Mark Pulford <mark@kyne.com.au>
*
* 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.
*/
#include <stdlib.h>
#include <stdarg.h>
#include "user_config.h"
/* Size: Total bytes allocated to *buf
* Length: String length, excluding optional NULL terminator.
* Increment: Allocation increments when resizing the string buffer.
* Dynamic: True if created via strbuf_new()
*/
typedef struct {
char *buf;
int size;
int length;
int increment;
int dynamic;
int reallocs;
int debug;
} strbuf_t;
#ifndef STRBUF_DEFAULT_SIZE
#define STRBUF_DEFAULT_SIZE 1023
#endif
#ifndef STRBUF_DEFAULT_INCREMENT
#define STRBUF_DEFAULT_INCREMENT -2
#endif
/* Initialise */
extern strbuf_t *strbuf_new(int len);
extern int strbuf_init(strbuf_t *s, int len);
extern int strbuf_set_increment(strbuf_t *s, int increment);
/* Release */
extern void strbuf_free(strbuf_t *s);
extern char *strbuf_free_to_string(strbuf_t *s, int *len);
/* Management */
extern int strbuf_resize(strbuf_t *s, int len);
static int strbuf_empty_length(strbuf_t *s);
static int strbuf_length(strbuf_t *s);
static char *strbuf_string(strbuf_t *s, int *len);
static void strbuf_ensure_empty_length(strbuf_t *s, int len);
static char *strbuf_empty_ptr(strbuf_t *s);
static void strbuf_extend_length(strbuf_t *s, int len);
/* Update */
extern void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...);
extern void strbuf_append_fmt_retry(strbuf_t *s, const char *format, ...);
static void strbuf_append_mem(strbuf_t *s, const char *c, int len);
extern void strbuf_append_string(strbuf_t *s, const char *str);
static void strbuf_append_char(strbuf_t *s, const char c);
static void strbuf_ensure_null(strbuf_t *s);
/* Reset string for before use */
static inline void strbuf_reset(strbuf_t *s)
{
s->length = 0;
}
static inline int strbuf_allocated(strbuf_t *s)
{
return s->buf != NULL;
}
/* Return bytes remaining in the string buffer
* Ensure there is space for a NULL terminator. */
static inline int strbuf_empty_length(strbuf_t *s)
{
return s->size - s->length - 1;
}
static inline void strbuf_ensure_empty_length(strbuf_t *s, int len)
{
if (len > strbuf_empty_length(s))
strbuf_resize(s, s->length + len);
}
static inline char *strbuf_empty_ptr(strbuf_t *s)
{
return s->buf + s->length;
}
static inline void strbuf_extend_length(strbuf_t *s, int len)
{
s->length += len;
}
static inline int strbuf_length(strbuf_t *s)
{
return s->length;
}
static inline void strbuf_append_char(strbuf_t *s, const char c)
{
strbuf_ensure_empty_length(s, 1);
s->buf[s->length++] = c;
}
static inline void strbuf_append_char_unsafe(strbuf_t *s, const char c)
{
s->buf[s->length++] = c;
}
static inline void strbuf_append_mem(strbuf_t *s, const char *c, int len)
{
strbuf_ensure_empty_length(s, len);
memcpy(s->buf + s->length, c, len);
s->length += len;
}
static inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, int len)
{
memcpy(s->buf + s->length, c, len);
s->length += len;
}
static inline void strbuf_ensure_null(strbuf_t *s)
{
s->buf[s->length] = 0;
}
static inline char *strbuf_string(strbuf_t *s, int *len)
{
if (len)
*len = s->length;
return s->buf;
}
/* vi:ai et sw=4 ts=4:
*/
These JSON examples were taken from the JSON website
(http://json.org/example.html) and RFC 4627.
Used with permission.
#!/usr/bin/env lua
-- This benchmark script measures wall clock time and should be
-- run on an unloaded system.
--
-- Your Mileage May Vary.
--
-- Mark Pulford <mark@kyne.com.au>
local json_module = os.getenv("JSON_MODULE") or "cjson"
require "socket"
local json = require(json_module)
local util = require "cjson.util"
local function find_func(mod, funcnames)
for _, v in ipairs(funcnames) do
if mod[v] then
return mod[v]
end
end
return nil
end
local json_encode = find_func(json, { "encode", "Encode", "to_string", "stringify", "json" })
local json_decode = find_func(json, { "decode", "Decode", "to_value", "parse" })
local function average(t)
local total = 0
for _, v in ipairs(t) do
total = total + v
end
return total / #t
end
function benchmark(tests, seconds, rep)
local function bench(func, iter)
-- Use socket.gettime() to measure microsecond resolution
-- wall clock time.
local t = socket.gettime()
for i = 1, iter do
func(i)
end
t = socket.gettime() - t
-- Don't trust any results when the run lasted for less than a
-- millisecond - return nil.
if t < 0.001 then
return nil
end
return (iter / t)
end
-- Roughly calculate the number of interations required
-- to obtain a particular time period.
local function calc_iter(func, seconds)
local iter = 1
local rate
-- Warm up the bench function first.
func()
while not rate do
rate = bench(func, iter)
iter = iter * 10
end
return math.ceil(seconds * rate)
end
local test_results = {}
for name, func in pairs(tests) do
-- k(number), v(string)
-- k(string), v(function)
-- k(number), v(function)
if type(func) == "string" then
name = func
func = _G[name]
end
local iter = calc_iter(func, seconds)
local result = {}
for i = 1, rep do
result[i] = bench(func, iter)
end
-- Remove the slowest half (round down) of the result set
table.sort(result)
for i = 1, math.floor(#result / 2) do
table.remove(result, 1)
end
test_results[name] = average(result)
end
return test_results
end
function bench_file(filename)
local data_json = util.file_load(filename)
local data_obj = json_decode(data_json)
local function test_encode()
json_encode(data_obj)
end
local function test_decode()
json_decode(data_json)
end
local tests = {}
if json_encode then tests.encode = test_encode end
if json_decode then tests.decode = test_decode end
return benchmark(tests, 0.1, 5)
end
-- Optionally load any custom configuration required for this module
local success, data = pcall(util.file_load, ("bench-%s.lua"):format(json_module))
if success then
util.run_script(data, _G)
configure(json)
end
for i = 1, #arg do
local results = bench_file(arg[i])
for k, v in pairs(results) do
print(("%s\t%s\t%d"):format(arg[i], k, v))
end
end
-- vi:ai et sw=4 ts=4:
{
"glossary": {
"title": "example glossary",
"GlossDiv": {
"title": "S",
"GlossList": {
"GlossEntry": {
"ID": "SGML",
"SortAs": "SGML",
"GlossTerm": "Standard Generalized Mark up Language",
"Acronym": "SGML",
"Abbrev": "ISO 8879:1986",
"GlossDef": {
"para": "A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso": ["GML", "XML"]
},
"GlossSee": "markup"
}
}
}
}
}
{"menu": {
"id": "file",
"value": "File",
"popup": {
"menuitem": [
{"value": "New", "onclick": "CreateNewDoc()"},
{"value": "Open", "onclick": "OpenDoc()"},
{"value": "Close", "onclick": "CloseDoc()"}
]
}
}}
{"widget": {
"debug": "on",
"window": {
"title": "Sample Konfabulator Widget",
"name": "main_window",
"width": 500,
"height": 500
},
"image": {
"src": "Images/Sun.png",
"name": "sun1",
"hOffset": 250,
"vOffset": 250,
"alignment": "center"
},
"text": {
"data": "Click Here",
"size": 36,
"style": "bold",
"name": "text1",
"hOffset": 250,
"vOffset": 100,
"alignment": "center",
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
}
}}
{"web-app": {
"servlet": [
{
"servlet-name": "cofaxCDS",
"servlet-class": "org.cofax.cds.CDSServlet",
"init-param": {
"configGlossary:installationAt": "Philadelphia, PA",
"configGlossary:adminEmail": "ksm@pobox.com",
"configGlossary:poweredBy": "Cofax",
"configGlossary:poweredByIcon": "/images/cofax.gif",
"configGlossary:staticPath": "/content/static",
"templateProcessorClass": "org.cofax.WysiwygTemplate",
"templateLoaderClass": "org.cofax.FilesTemplateLoader",
"templatePath": "templates",
"templateOverridePath": "",
"defaultListTemplate": "listTemplate.htm",
"defaultFileTemplate": "articleTemplate.htm",
"useJSP": false,
"jspListTemplate": "listTemplate.jsp",
"jspFileTemplate": "articleTemplate.jsp",
"cachePackageTagsTrack": 200,
"cachePackageTagsStore": 200,
"cachePackageTagsRefresh": 60,
"cacheTemplatesTrack": 100,
"cacheTemplatesStore": 50,
"cacheTemplatesRefresh": 15,
"cachePagesTrack": 200,
"cachePagesStore": 100,
"cachePagesRefresh": 10,
"cachePagesDirtyRead": 10,
"searchEngineListTemplate": "forSearchEnginesList.htm",
"searchEngineFileTemplate": "forSearchEngines.htm",
"searchEngineRobotsDb": "WEB-INF/robots.db",
"useDataStore": true,
"dataStoreClass": "org.cofax.SqlDataStore",
"redirectionClass": "org.cofax.SqlRedirection",
"dataStoreName": "cofax",
"dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver",
"dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon",
"dataStoreUser": "sa",
"dataStorePassword": "dataStoreTestQuery",
"dataStoreTestQuery": "SET NOCOUNT ON;select test='test';",
"dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log",
"dataStoreInitConns": 10,
"dataStoreMaxConns": 100,
"dataStoreConnUsageLimit": 100,
"dataStoreLogLevel": "debug",
"maxUrlLength": 500}},
{
"servlet-name": "cofaxEmail",
"servlet-class": "org.cofax.cds.EmailServlet",
"init-param": {
"mailHost": "mail1",
"mailHostOverride": "mail2"}},
{
"servlet-name": "cofaxAdmin",
"servlet-class": "org.cofax.cds.AdminServlet"},
{
"servlet-name": "fileServlet",
"servlet-class": "org.cofax.cds.FileServlet"},
{
"servlet-name": "cofaxTools",
"servlet-class": "org.cofax.cms.CofaxToolsServlet",
"init-param": {
"templatePath": "toolstemplates/",
"log": 1,
"logLocation": "/usr/local/tomcat/logs/CofaxTools.log",
"logMaxSize": "",
"dataLog": 1,
"dataLogLocation": "/usr/local/tomcat/logs/dataLog.log",
"dataLogMaxSize": "",
"removePageCache": "/content/admin/remove?cache=pages&id=",
"removeTemplateCache": "/content/admin/remove?cache=templates&id=",
"fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder",
"lookInContext": 1,
"adminGroupID": 4,
"betaServer": true}}],
"servlet-mapping": {
"cofaxCDS": "/",
"cofaxEmail": "/cofaxutil/aemail/*",
"cofaxAdmin": "/admin/*",
"fileServlet": "/static/*",
"cofaxTools": "/tools/*"},
"taglib": {
"taglib-uri": "cofax.tld",
"taglib-location": "/WEB-INF/tlds/cofax.tld"}}}
{"menu": {
"header": "SVG Viewer",
"items": [
{"id": "Open"},
{"id": "OpenNew", "label": "Open New"},
null,
{"id": "ZoomIn", "label": "Zoom In"},
{"id": "ZoomOut", "label": "Zoom Out"},
{"id": "OriginalView", "label": "Original View"},
null,
{"id": "Quality"},
{"id": "Pause"},
{"id": "Mute"},
null,
{"id": "Find", "label": "Find..."},
{"id": "FindAgain", "label": "Find Again"},
{"id": "Copy"},
{"id": "CopyAgain", "label": "Copy Again"},
{"id": "CopySVG", "label": "Copy SVG"},
{"id": "ViewSVG", "label": "View SVG"},
{"id": "ViewSource", "label": "View Source"},
{"id": "SaveAs", "label": "Save As"},
null,
{"id": "Help"},
{"id": "About", "label": "About Adobe CVG Viewer..."}
]
}}
#!/usr/bin/env perl
# Create test comparison data using a different UTF-8 implementation.
# The generated utf8.dat file must have the following MD5 sum:
# cff03b039d850f370a7362f3313e5268
use strict;
# 0xD800 - 0xDFFF are used to encode supplementary codepoints
# 0x10000 - 0x10FFFF are supplementary codepoints
my (@codepoints) = (0 .. 0xD7FF, 0xE000 .. 0x10FFFF);
my $utf8 = pack("U*", @codepoints);
defined($utf8) or die "Unable create UTF-8 string\n";
open(FH, ">:utf8", "utf8.dat")
or die "Unable to open utf8.dat: $!\n";
print FH $utf8
or die "Unable to write utf8.dat\n";
close(FH);
# vi:ai et sw=4 ts=4:
[ 0.110001,
0.12345678910111,
0.412454033640,
2.6651441426902,
2.718281828459,
3.1415926535898,
2.1406926327793 ]
"\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#$%&'()*+,-.\/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u007f"
\ No newline at end of file
{
"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]
}
}
[
{
"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"
}
]
#!/usr/bin/env lua
-- Lua CJSON tests
--
-- Mark Pulford <mark@kyne.com.au>
--
-- Note: The output of this script is easier to read with "less -S"
local json = require "cjson"
local json_safe = require "cjson.safe"
local util = require "cjson.util"
local function gen_raw_octets()
local chars = {}
for i = 0, 255 do chars[i + 1] = string.char(i) end
return table.concat(chars)
end
-- Generate every UTF-16 codepoint, including supplementary codes
local function gen_utf16_escaped()
-- Create raw table escapes
local utf16_escaped = {}
local count = 0
local function append_escape(code)
local esc = ('\\u%04X'):format(code)
table.insert(utf16_escaped, esc)
end
table.insert(utf16_escaped, '"')
for i = 0, 0xD7FF do
append_escape(i)
end
-- Skip 0xD800 - 0xDFFF since they are used to encode supplementary
-- codepoints
for i = 0xE000, 0xFFFF do
append_escape(i)
end
-- Append surrogate pair for each supplementary codepoint
for high = 0xD800, 0xDBFF do
for low = 0xDC00, 0xDFFF do
append_escape(high)
append_escape(low)
end
end
table.insert(utf16_escaped, '"')
return table.concat(utf16_escaped)
end
function load_testdata()
local data = {}
-- Data for 8bit raw <-> escaped octets tests
data.octets_raw = gen_raw_octets()
data.octets_escaped = util.file_load("octets-escaped.dat")
-- Data for \uXXXX -> UTF-8 test
data.utf16_escaped = gen_utf16_escaped()
-- Load matching data for utf16_escaped
local utf8_loaded
utf8_loaded, data.utf8_raw = pcall(util.file_load, "utf8.dat")
if not utf8_loaded then
data.utf8_raw = "Failed to load utf8.dat - please run genutf8.pl"
end
data.table_cycle = {}
data.table_cycle[1] = data.table_cycle
local big = {}
for i = 1, 1100 do
big = { { 10, false, true, json.null }, "string", a = big }
end
data.deeply_nested_data = big
return data
end
function test_decode_cycle(filename)
local obj1 = json.decode(util.file_load(filename))
local obj2 = json.decode(json.encode(obj1))
return util.compare_values(obj1, obj2)
end
-- Set up data used in tests
local Inf = math.huge;
local NaN = math.huge * 0;
local testdata = load_testdata()
local cjson_tests = {
-- Test API variables
{ "Check module name, version",
function () return json._NAME, json._VERSION end, { },
true, { "cjson", "2.1devel" } },
-- Test decoding simple types
{ "Decode string",
json.decode, { '"test string"' }, true, { "test string" } },
{ "Decode numbers",
json.decode, { '[ 0.0, -5e3, -1, 0.3e-3, 1023.2, 0e10 ]' },
true, { { 0.0, -5000, -1, 0.0003, 1023.2, 0 } } },
{ "Decode null",
json.decode, { 'null' }, true, { json.null } },
{ "Decode true",
json.decode, { 'true' }, true, { true } },
{ "Decode false",
json.decode, { 'false' }, true, { false } },
{ "Decode object with numeric keys",
json.decode, { '{ "1": "one", "3": "three" }' },
true, { { ["1"] = "one", ["3"] = "three" } } },
{ "Decode object with string keys",
json.decode, { '{ "a": "a", "b": "b" }' },
true, { { a = "a", b = "b" } } },
{ "Decode array",
json.decode, { '[ "one", null, "three" ]' },
true, { { "one", json.null, "three" } } },
-- Test decoding errors
{ "Decode UTF-16BE [throw error]",
json.decode, { '\0"\0"' },
false, { "JSON parser does not support UTF-16 or UTF-32" } },
{ "Decode UTF-16LE [throw error]",
json.decode, { '"\0"\0' },
false, { "JSON parser does not support UTF-16 or UTF-32" } },
{ "Decode UTF-32BE [throw error]",
json.decode, { '\0\0\0"' },
false, { "JSON parser does not support UTF-16 or UTF-32" } },
{ "Decode UTF-32LE [throw error]",
json.decode, { '"\0\0\0' },
false, { "JSON parser does not support UTF-16 or UTF-32" } },
{ "Decode partial JSON [throw error]",
json.decode, { '{ "unexpected eof": ' },
false, { "Expected value but found T_END at character 21" } },
{ "Decode with extra comma [throw error]",
json.decode, { '{ "extra data": true }, false' },
false, { "Expected the end but found T_COMMA at character 23" } },
{ "Decode invalid escape code [throw error]",
json.decode, { [[ { "bad escape \q code" } ]] },
false, { "Expected object key string but found invalid escape code at character 16" } },
{ "Decode invalid unicode escape [throw error]",
json.decode, { [[ { "bad unicode \u0f6 escape" } ]] },
false, { "Expected object key string but found invalid unicode escape code at character 17" } },
{ "Decode invalid keyword [throw error]",
json.decode, { ' [ "bad barewood", test ] ' },
false, { "Expected value but found invalid token at character 20" } },
{ "Decode invalid number #1 [throw error]",
json.decode, { '[ -+12 ]' },
false, { "Expected value but found invalid number at character 3" } },
{ "Decode invalid number #2 [throw error]",
json.decode, { '-v' },
false, { "Expected value but found invalid number at character 1" } },
{ "Decode invalid number exponent [throw error]",
json.decode, { '[ 0.4eg10 ]' },
false, { "Expected comma or array end but found invalid token at character 6" } },
-- Test decoding nested arrays / objects
{ "Set decode_max_depth(5)",
json.decode_max_depth, { 5 }, true, { 5 } },
{ "Decode array at nested limit",
json.decode, { '[[[[[ "nested" ]]]]]' },
true, { {{{{{ "nested" }}}}} } },
{ "Decode array over nested limit [throw error]",
json.decode, { '[[[[[[ "nested" ]]]]]]' },
false, { "Found too many nested data structures (6) at character 6" } },
{ "Decode object at nested limit",
json.decode, { '{"a":{"b":{"c":{"d":{"e":"nested"}}}}}' },
true, { {a={b={c={d={e="nested"}}}}} } },
{ "Decode object over nested limit [throw error]",
json.decode, { '{"a":{"b":{"c":{"d":{"e":{"f":"nested"}}}}}}' },
false, { "Found too many nested data structures (6) at character 26" } },
{ "Set decode_max_depth(1000)",
json.decode_max_depth, { 1000 }, true, { 1000 } },
{ "Decode deeply nested array [throw error]",
json.decode, { string.rep("[", 1100) .. '1100' .. string.rep("]", 1100)},
false, { "Found too many nested data structures (1001) at character 1001" } },
-- Test encoding nested tables
{ "Set encode_max_depth(5)",
json.encode_max_depth, { 5 }, true, { 5 } },
{ "Encode nested table as array at nested limit",
json.encode, { {{{{{"nested"}}}}} }, true, { '[[[[["nested"]]]]]' } },
{ "Encode nested table as array after nested limit [throw error]",
json.encode, { { {{{{{"nested"}}}}} } },
false, { "Cannot serialise, excessive nesting (6)" } },
{ "Encode nested table as object at nested limit",
json.encode, { {a={b={c={d={e="nested"}}}}} },
true, { '{"a":{"b":{"c":{"d":{"e":"nested"}}}}}' } },
{ "Encode nested table as object over nested limit [throw error]",
json.encode, { {a={b={c={d={e={f="nested"}}}}}} },
false, { "Cannot serialise, excessive nesting (6)" } },
{ "Encode table with cycle [throw error]",
json.encode, { testdata.table_cycle },
false, { "Cannot serialise, excessive nesting (6)" } },
{ "Set encode_max_depth(1000)",
json.encode_max_depth, { 1000 }, true, { 1000 } },
{ "Encode deeply nested data [throw error]",
json.encode, { testdata.deeply_nested_data },
false, { "Cannot serialise, excessive nesting (1001)" } },
-- Test encoding simple types
{ "Encode null",
json.encode, { json.null }, true, { 'null' } },
{ "Encode true",
json.encode, { true }, true, { 'true' } },
{ "Encode false",
json.encode, { false }, true, { 'false' } },
{ "Encode empty object",
json.encode, { { } }, true, { '{}' } },
{ "Encode integer",
json.encode, { 10 }, true, { '10' } },
{ "Encode string",
json.encode, { "hello" }, true, { '"hello"' } },
{ "Encode Lua function [throw error]",
json.encode, { function () end },
false, { "Cannot serialise function: type not supported" } },
-- Test decoding invalid numbers
{ "Set decode_invalid_numbers(true)",
json.decode_invalid_numbers, { true }, true, { true } },
{ "Decode hexadecimal",
json.decode, { '0x6.ffp1' }, true, { 13.9921875 } },
{ "Decode numbers with leading zero",
json.decode, { '[ 0123, 00.33 ]' }, true, { { 123, 0.33 } } },
{ "Decode +-Inf",
json.decode, { '[ +Inf, Inf, -Inf ]' }, true, { { Inf, Inf, -Inf } } },
{ "Decode +-Infinity",
json.decode, { '[ +Infinity, Infinity, -Infinity ]' },
true, { { Inf, Inf, -Inf } } },
{ "Decode +-NaN",
json.decode, { '[ +NaN, NaN, -NaN ]' }, true, { { NaN, NaN, NaN } } },
{ "Decode Infrared (not infinity) [throw error]",
json.decode, { 'Infrared' },
false, { "Expected the end but found invalid token at character 4" } },
{ "Decode Noodle (not NaN) [throw error]",
json.decode, { 'Noodle' },
false, { "Expected value but found invalid token at character 1" } },
{ "Set decode_invalid_numbers(false)",
json.decode_invalid_numbers, { false }, true, { false } },
{ "Decode hexadecimal [throw error]",
json.decode, { '0x6' },
false, { "Expected value but found invalid number at character 1" } },
{ "Decode numbers with leading zero [throw error]",
json.decode, { '[ 0123, 00.33 ]' },
false, { "Expected value but found invalid number at character 3" } },
{ "Decode +-Inf [throw error]",
json.decode, { '[ +Inf, Inf, -Inf ]' },
false, { "Expected value but found invalid token at character 3" } },
{ "Decode +-Infinity [throw error]",
json.decode, { '[ +Infinity, Infinity, -Infinity ]' },
false, { "Expected value but found invalid token at character 3" } },
{ "Decode +-NaN [throw error]",
json.decode, { '[ +NaN, NaN, -NaN ]' },
false, { "Expected value but found invalid token at character 3" } },
{ 'Set decode_invalid_numbers("on")',
json.decode_invalid_numbers, { "on" }, true, { true } },
-- Test encoding invalid numbers
{ "Set encode_invalid_numbers(false)",
json.encode_invalid_numbers, { false }, true, { false } },
{ "Encode NaN [throw error]",
json.encode, { NaN },
false, { "Cannot serialise number: must not be NaN or Infinity" } },
{ "Encode Infinity [throw error]",
json.encode, { Inf },
false, { "Cannot serialise number: must not be NaN or Infinity" } },
{ "Set encode_invalid_numbers(\"null\")",
json.encode_invalid_numbers, { "null" }, true, { "null" } },
{ "Encode NaN as null",
json.encode, { NaN }, true, { "null" } },
{ "Encode Infinity as null",
json.encode, { Inf }, true, { "null" } },
{ "Set encode_invalid_numbers(true)",
json.encode_invalid_numbers, { true }, true, { true } },
{ "Encode NaN",
json.encode, { NaN }, true, { "NaN" } },
{ "Encode +Infinity",
json.encode, { Inf }, true, { "Infinity" } },
{ "Encode -Infinity",
json.encode, { -Inf }, true, { "-Infinity" } },
{ 'Set encode_invalid_numbers("off")',
json.encode_invalid_numbers, { "off" }, true, { false } },
-- Test encoding tables
{ "Set encode_sparse_array(true, 2, 3)",
json.encode_sparse_array, { true, 2, 3 }, true, { true, 2, 3 } },
{ "Encode sparse table as array #1",
json.encode, { { [3] = "sparse test" } },
true, { '[null,null,"sparse test"]' } },
{ "Encode sparse table as array #2",
json.encode, { { [1] = "one", [4] = "sparse test" } },
true, { '["one",null,null,"sparse test"]' } },
{ "Encode sparse array as object",
json.encode, { { [1] = "one", [5] = "sparse test" } },
true, { '{"1":"one","5":"sparse test"}' } },
{ "Encode table with numeric string key as object",
json.encode, { { ["2"] = "numeric string key test" } },
true, { '{"2":"numeric string key test"}' } },
{ "Set encode_sparse_array(false)",
json.encode_sparse_array, { false }, true, { false, 2, 3 } },
{ "Encode table with incompatible key [throw error]",
json.encode, { { [false] = "wrong" } },
false, { "Cannot serialise boolean: table key must be a number or string" } },
-- Test escaping
{ "Encode all octets (8-bit clean)",
json.encode, { testdata.octets_raw }, true, { testdata.octets_escaped } },
{ "Decode all escaped octets",
json.decode, { testdata.octets_escaped }, true, { testdata.octets_raw } },
{ "Decode single UTF-16 escape",
json.decode, { [["\uF800"]] }, true, { "\239\160\128" } },
{ "Decode all UTF-16 escapes (including surrogate combinations)",
json.decode, { testdata.utf16_escaped }, true, { testdata.utf8_raw } },
{ "Decode swapped surrogate pair [throw error]",
json.decode, { [["\uDC00\uD800"]] },
false, { "Expected value but found invalid unicode escape code at character 2" } },
{ "Decode duplicate high surrogate [throw error]",
json.decode, { [["\uDB00\uDB00"]] },
false, { "Expected value but found invalid unicode escape code at character 2" } },
{ "Decode duplicate low surrogate [throw error]",
json.decode, { [["\uDB00\uDB00"]] },
false, { "Expected value but found invalid unicode escape code at character 2" } },
{ "Decode missing low surrogate [throw error]",
json.decode, { [["\uDB00"]] },
false, { "Expected value but found invalid unicode escape code at character 2" } },
{ "Decode invalid low surrogate [throw error]",
json.decode, { [["\uDB00\uD"]] },
false, { "Expected value but found invalid unicode escape code at character 2" } },
-- Test locale support
--
-- The standard Lua interpreter is ANSI C online doesn't support locales
-- by default. Force a known problematic locale to test strtod()/sprintf().
{ "Set locale to cs_CZ (comma separator)", function ()
os.setlocale("cs_CZ")
json.new()
end },
{ "Encode number under comma locale",
json.encode, { 1.5 }, true, { '1.5' } },
{ "Decode number in array under comma locale",
json.decode, { '[ 10, "test" ]' }, true, { { 10, "test" } } },
{ "Revert locale to POSIX", function ()
os.setlocale("C")
json.new()
end },
-- Test encode_keep_buffer() and enable_number_precision()
{ "Set encode_keep_buffer(false)",
json.encode_keep_buffer, { false }, true, { false } },
{ "Set encode_number_precision(3)",
json.encode_number_precision, { 3 }, true, { 3 } },
{ "Encode number with precision 3",
json.encode, { 1/3 }, true, { "0.333" } },
{ "Set encode_number_precision(14)",
json.encode_number_precision, { 14 }, true, { 14 } },
{ "Set encode_keep_buffer(true)",
json.encode_keep_buffer, { true }, true, { true } },
-- Test config API errors
-- Function is listed as '?' due to pcall
{ "Set encode_number_precision(0) [throw error]",
json.encode_number_precision, { 0 },
false, { "bad argument #1 to '?' (expected integer between 1 and 14)" } },
{ "Set encode_number_precision(\"five\") [throw error]",
json.encode_number_precision, { "five" },
false, { "bad argument #1 to '?' (number expected, got string)" } },
{ "Set encode_keep_buffer(nil, true) [throw error]",
json.encode_keep_buffer, { nil, true },
false, { "bad argument #2 to '?' (found too many arguments)" } },
{ "Set encode_max_depth(\"wrong\") [throw error]",
json.encode_max_depth, { "wrong" },
false, { "bad argument #1 to '?' (number expected, got string)" } },
{ "Set decode_max_depth(0) [throw error]",
json.decode_max_depth, { "0" },
false, { "bad argument #1 to '?' (expected integer between 1 and 2147483647)" } },
{ "Set encode_invalid_numbers(-2) [throw error]",
json.encode_invalid_numbers, { -2 },
false, { "bad argument #1 to '?' (invalid option '-2')" } },
{ "Set decode_invalid_numbers(true, false) [throw error]",
json.decode_invalid_numbers, { true, false },
false, { "bad argument #2 to '?' (found too many arguments)" } },
{ "Set encode_sparse_array(\"not quite on\") [throw error]",
json.encode_sparse_array, { "not quite on" },
false, { "bad argument #1 to '?' (invalid option 'not quite on')" } },
{ "Reset Lua CJSON configuration", function () json = json.new() end },
-- Wrap in a function to ensure the table returned by json.new() is used
{ "Check encode_sparse_array()",
function (...) return json.encode_sparse_array(...) end, { },
true, { false, 2, 10 } },
{ "Encode (safe) simple value",
json_safe.encode, { true },
true, { "true" } },
{ "Encode (safe) argument validation [throw error]",
json_safe.encode, { "arg1", "arg2" },
false, { "bad argument #1 to '?' (expected 1 argument)" } },
{ "Decode (safe) error generation",
json_safe.decode, { "Oops" },
true, { nil, "Expected value but found invalid token at character 1" } },
{ "Decode (safe) error generation after new()",
function(...) return json_safe.new().decode(...) end, { "Oops" },
true, { nil, "Expected value but found invalid token at character 1" } },
}
print(("==> Testing Lua CJSON version %s\n"):format(json._VERSION))
util.run_test_group(cjson_tests)
for _, filename in ipairs(arg) do
util.run_test("Decode cycle " .. filename, test_decode_cycle, { filename },
true, { true })
end
local pass, total = util.run_test_summary()
if pass == total then
print("==> Summary: all tests succeeded")
else
print(("==> Summary: %d/%d tests failed"):format(total - pass, total))
os.exit(1)
end
-- vi:ai et sw=4 ts=4:
{ "array": [ 10, true, null ] }
Copyright (c) 2013 Toby Jaffey <toby@1248.io>
2015 Zeroday Hong <zeroday@nodemcu.com> nodemcu.com
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.
#############################################################
# Required variables for each makefile
# Discard this section from all parent makefiles
# Expected variables (with automatic defaults):
# CSRCS (all "C" files in the dir)
# SUBDIRS (all subdirs with a Makefile)
# GEN_LIBS - list of libs to be generated ()
# GEN_IMAGES - list of images to be generated ()
# COMPONENTS_xxx - a list of libs/objs in the form
# subdir/lib to be extracted and rolled up into
# a generated lib/image xxx.a ()
#
ifndef PDIR
GEN_LIBS = coap.a
endif
#############################################################
# Configuration i.e. compile options etc.
# Target specific stuff (defines etc.) goes in here!
# Generally values applying to a tree are captured in the
# makefile at its root level - these are then overridden
# for a subtree within the makefile rooted therein
#
#DEFINES +=
#############################################################
# Recursion Magic - Don't touch this!!
#
# Each subtree potentially has an include directory
# corresponding to the common APIs applicable to modules
# rooted at that subtree. Accordingly, the INCLUDE PATH
# of a module can only contain the include directories up
# its parent path, and not its siblings
#
# Required for each makefile to inherit from the parent
#
INCLUDES := $(INCLUDES) -I $(PDIR)include
INCLUDES += -I ./
INCLUDES += -I ../libc
INCLUDES += -I ../lua
PDIR := ../$(PDIR)
sinclude $(PDIR)Makefile
#include "user_config.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "coap.h"
#include "uri.h"
extern void endpoint_setup(void);
extern const coap_endpoint_t endpoints[];
#ifdef COAP_DEBUG
void coap_dumpHeader(coap_header_t *hdr)
{
printf("Header:\n");
printf(" ver 0x%02X\n", hdr->ver);
printf(" t 0x%02X\n", hdr->ver);
printf(" tkl 0x%02X\n", hdr->tkl);
printf(" code 0x%02X\n", hdr->code);
printf(" id 0x%02X%02X\n", hdr->id[0], hdr->id[1]);
}
void coap_dump(const uint8_t *buf, size_t buflen, bool bare)
{
if (bare)
{
while(buflen--)
printf("%02X%s", *buf++, (buflen > 0) ? " " : "");
}
else
{
printf("Dump: ");
while(buflen--)
printf("%02X%s", *buf++, (buflen > 0) ? " " : "");
printf("\n");
}
}
#endif
int coap_parseHeader(coap_header_t *hdr, const uint8_t *buf, size_t buflen)
{
if (buflen < 4)
return COAP_ERR_HEADER_TOO_SHORT;
hdr->ver = (buf[0] & 0xC0) >> 6;
if (hdr->ver != 1)
return COAP_ERR_VERSION_NOT_1;
hdr->t = (buf[0] & 0x30) >> 4;
hdr->tkl = buf[0] & 0x0F;
hdr->code = buf[1];
hdr->id[0] = buf[2];
hdr->id[1] = buf[3];
return 0;
}
int coap_buildHeader(const coap_header_t *hdr, uint8_t *buf, size_t buflen)
{
// build header
if (buflen < 4)
return COAP_ERR_BUFFER_TOO_SMALL;
buf[0] = (hdr->ver & 0x03) << 6;
buf[0] |= (hdr->t & 0x03) << 4;
buf[0] |= (hdr->tkl & 0x0F);
buf[1] = hdr->code;
buf[2] = hdr->id[0];
buf[3] = hdr->id[1];
return 4;
}
int coap_parseToken(coap_buffer_t *tokbuf, const coap_header_t *hdr, const uint8_t *buf, size_t buflen)
{
if (hdr->tkl == 0)
{
tokbuf->p = NULL;
tokbuf->len = 0;
return 0;
}
else
if (hdr->tkl <= 8)
{
if (4U + hdr->tkl > buflen)
return COAP_ERR_TOKEN_TOO_SHORT; // tok bigger than packet
tokbuf->p = buf+4; // past header
tokbuf->len = hdr->tkl;
return 0;
}
else
{
// invalid size
return COAP_ERR_TOKEN_TOO_SHORT;
}
}
int coap_buildToken(const coap_buffer_t *tokbuf, const coap_header_t *hdr, uint8_t *buf, size_t buflen)
{
// inject token
uint8_t *p;
if (buflen < (4U + hdr->tkl))
return COAP_ERR_BUFFER_TOO_SMALL;
p = buf + 4;
if ((hdr->tkl > 0) && (hdr->tkl != tokbuf->len))
return COAP_ERR_UNSUPPORTED;
if (hdr->tkl > 0)
memcpy(p, tokbuf->p, hdr->tkl);
// http://tools.ietf.org/html/rfc7252#section-3.1
// inject options
return hdr->tkl;
}
// advances p
int coap_parseOption(coap_option_t *option, uint16_t *running_delta, const uint8_t **buf, size_t buflen)
{
const uint8_t *p = *buf;
uint8_t headlen = 1;
uint16_t len, delta;
if (buflen < headlen) // too small
return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
delta = (p[0] & 0xF0) >> 4;
len = p[0] & 0x0F;
// These are untested and may be buggy
if (delta == 13)
{
headlen++;
if (buflen < headlen)
return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
delta = p[1] + 13;
p++;
}
else
if (delta == 14)
{
headlen += 2;
if (buflen < headlen)
return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
delta = ((p[1] << 8) | p[2]) + 269;
p+=2;
}
else
if (delta == 15)
return COAP_ERR_OPTION_DELTA_INVALID;
if (len == 13)
{
headlen++;
if (buflen < headlen)
return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
len = p[1] + 13;
p++;
}
else
if (len == 14)
{
headlen += 2;
if (buflen < headlen)
return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
len = ((p[1] << 8) | p[2]) + 269;
p+=2;
}
else
if (len == 15)
return COAP_ERR_OPTION_LEN_INVALID;
if ((p + 1 + len) > (*buf + buflen))
return COAP_ERR_OPTION_TOO_BIG;
//printf("option num=%d\n", delta + *running_delta);
option->num = delta + *running_delta;
option->buf.p = p+1;
option->buf.len = len;
//coap_dump(p+1, len, false);
// advance buf
*buf = p + 1 + len;
*running_delta += delta;
return 0;
}
// http://tools.ietf.org/html/rfc7252#section-3.1
int coap_parseOptionsAndPayload(coap_option_t *options, uint8_t *numOptions, coap_buffer_t *payload, const coap_header_t *hdr, const uint8_t *buf, size_t buflen)
{
size_t optionIndex = 0;
uint16_t delta = 0;
const uint8_t *p = buf + 4 + hdr->tkl;
const uint8_t *end = buf + buflen;
int rc;
if (p > end)
return COAP_ERR_OPTION_OVERRUNS_PACKET; // out of bounds
//coap_dump(p, end - p);
// 0xFF is payload marker
while((optionIndex < *numOptions) && (p < end) && (*p != 0xFF))
{
if (0 != (rc = coap_parseOption(&options[optionIndex], &delta, &p, end-p)))
return rc;
optionIndex++;
}
*numOptions = optionIndex;
if (p+1 < end && *p == 0xFF) // payload marker
{
payload->p = p+1;
payload->len = end-(p+1);
}
else
{
payload->p = NULL;
payload->len = 0;
}
return 0;
}
int coap_buildOptionHeader(uint32_t optDelta, size_t length, uint8_t *buf, size_t buflen)
{
int n = 0;
uint8_t *p = buf;
uint8_t len, delta = 0;
if (buflen < 5)
return COAP_ERR_BUFFER_TOO_SMALL;
coap_option_nibble(optDelta, &delta);
coap_option_nibble(length, &len);
*p++ = (0xFF & (delta << 4 | len));
n++;
if (delta == 13)
{
*p++ = (optDelta - 13);
n++;
}
else
if (delta == 14)
{
*p++ = ((optDelta-269) >> 8);
*p++ = (0xFF & (optDelta-269));
n+=2;
}
if (len == 13)
{
*p++ = (length - 13);
n++;
}
else
if (len == 14)
{
*p++ = (length >> 8);
*p++ = (0xFF & (length-269));
n+=2;
}
return n;
}
#ifdef COAP_DEBUG
void coap_dumpOptions(coap_option_t *opts, size_t numopt)
{
size_t i;
printf(" Options:\n");
for (i=0;i<numopt;i++)
{
printf(" 0x%02X [ ", opts[i].num);
coap_dump(opts[i].buf.p, opts[i].buf.len, true);
printf(" ]\n");
}
}
void coap_dumpPacket(coap_packet_t *pkt)
{
coap_dumpHeader(&pkt->hdr);
coap_dumpOptions(pkt->opts, pkt->numopts);
printf("Payload: ");
coap_dump(pkt->payload.p, pkt->payload.len, true);
printf("\n");
}
#endif
int coap_parse(coap_packet_t *pkt, const uint8_t *buf, size_t buflen)
{
int rc;
// coap_dump(buf, buflen, false);
if (0 != (rc = coap_parseHeader(&pkt->hdr, buf, buflen)))
return rc;
// coap_dumpHeader(&hdr);
if (0 != (rc = coap_parseToken(&pkt->tok, &pkt->hdr, buf, buflen)))
return rc;
pkt->numopts = MAXOPT;
if (0 != (rc = coap_parseOptionsAndPayload(pkt->opts, &(pkt->numopts), &(pkt->payload), &pkt->hdr, buf, buflen)))
return rc;
// coap_dumpOptions(opts, numopt);
return 0;
}
// options are always stored consecutively, so can return a block with same option num
const coap_option_t *coap_findOptions(const coap_packet_t *pkt, uint8_t num, uint8_t *count)
{
// FIXME, options is always sorted, can find faster than this
size_t i;
const coap_option_t *first = NULL;
*count = 0;
for (i=0;i<pkt->numopts;i++)
{
if (pkt->opts[i].num == num)
{
if (NULL == first)
first = &pkt->opts[i];
(*count)++;
}
else
{
if (NULL != first)
break;
}
}
return first;
}
int coap_buffer_to_string(char *strbuf, size_t strbuflen, const coap_buffer_t *buf)
{
if (buf->len+1 > strbuflen)
return COAP_ERR_BUFFER_TOO_SMALL;
memcpy(strbuf, buf->p, buf->len);
strbuf[buf->len] = 0;
return 0;
}
int coap_build(uint8_t *buf, size_t *buflen, const coap_packet_t *pkt)
{
size_t opts_len = 0, hdr_len = 0, tok_len = 0;
size_t i;
uint8_t *p = buf;
size_t left = *buflen;
uint16_t running_delta = 0;
hdr_len = coap_buildHeader(&(pkt->hdr), buf, *buflen);
p += hdr_len;
left -= hdr_len;
tok_len = coap_buildToken(&(pkt->tok), &(pkt->hdr), buf, *buflen);
p += tok_len;
left -= tok_len;
for (i=0;i<pkt->numopts;i++)
{
uint8_t len, delta = 0;
uint16_t optDelta = 0;
int rc = 0;
if (((size_t)(p-buf)) > *buflen)
return COAP_ERR_BUFFER_TOO_SMALL;
optDelta = pkt->opts[i].num - running_delta;
rc = coap_buildOptionHeader(optDelta, pkt->opts[i].buf.len, p, left);
p += rc;
left -= rc;
memcpy(p, pkt->opts[i].buf.p, pkt->opts[i].buf.len);
p += pkt->opts[i].buf.len;
left -= pkt->opts[i].buf.len;
running_delta = pkt->opts[i].num;
}
opts_len = (p - buf) - 4; // number of bytes used by options
if (pkt->payload.len > 0)
{
if (*buflen < 4 + 1 + pkt->payload.len + opts_len)
return COAP_ERR_BUFFER_TOO_SMALL;
buf[4 + opts_len] = 0xFF; // payload marker
memcpy(buf+5 + opts_len, pkt->payload.p, pkt->payload.len);
*buflen = opts_len + 5 + pkt->payload.len;
}
else
*buflen = opts_len + 4;
return 0;
}
void coap_option_nibble(uint32_t value, uint8_t *nibble)
{
if (value<13)
{
*nibble = (0xFF & value);
}
else
if (value<=0xFF+13)
{
*nibble = 13;
} else if (value<=0xFFFF+269)
{
*nibble = 14;
}
}
int coap_make_response(coap_rw_buffer_t *scratch, coap_packet_t *pkt, const uint8_t *content, size_t content_len, uint8_t msgid_hi, uint8_t msgid_lo, const coap_buffer_t* tok, coap_responsecode_t rspcode, coap_content_type_t content_type)
{
pkt->hdr.ver = 0x01;
pkt->hdr.t = COAP_TYPE_ACK;
pkt->hdr.tkl = 0;
pkt->hdr.code = rspcode;
pkt->hdr.id[0] = msgid_hi;
pkt->hdr.id[1] = msgid_lo;
pkt->numopts = 1;
// need token in response
if (tok) {
pkt->hdr.tkl = tok->len;
pkt->tok = *tok;
}
// safe because 1 < MAXOPT
pkt->opts[0].num = COAP_OPTION_CONTENT_FORMAT;
pkt->opts[0].buf.p = scratch->p;
if (scratch->len < 2)
return COAP_ERR_BUFFER_TOO_SMALL;
scratch->p[0] = ((uint16_t)content_type & 0xFF00) >> 8;
scratch->p[1] = ((uint16_t)content_type & 0x00FF);
pkt->opts[0].buf.len = 2;
pkt->payload.p = content;
pkt->payload.len = content_len;
return 0;
}
unsigned int coap_encode_var_bytes(unsigned char *buf, unsigned int val) {
unsigned int n, i;
for (n = 0, i = val; i && n < sizeof(val); ++n)
i >>= 8;
i = n;
while (i--) {
buf[i] = val & 0xff;
val >>= 8;
}
return n;
}
static uint8_t _token_data[4] = {'n','o','d','e'};
coap_buffer_t the_token = { _token_data, 4 };
static unsigned short message_id;
int coap_make_request(coap_rw_buffer_t *scratch, coap_packet_t *pkt, coap_msgtype_t t, coap_method_t m, coap_uri_t *uri, const uint8_t *payload, size_t payload_len)
{
int res;
pkt->hdr.ver = 0x01;
pkt->hdr.t = t;
pkt->hdr.tkl = 0;
pkt->hdr.code = m;
pkt->hdr.id[0] = (message_id >> 8) & 0xFF; //msgid_hi;
pkt->hdr.id[1] = message_id & 0xFF; //msgid_lo;
message_id++;
NODE_DBG("message_id: %d.\n", message_id);
pkt->numopts = 0;
if (the_token.len) {
pkt->hdr.tkl = the_token.len;
pkt->tok = the_token;
}
if (scratch->len < 2) // TBD...
return COAP_ERR_BUFFER_TOO_SMALL;
uint8_t *saved = scratch->p;
/* split arg into Uri-* options */
// const char *addr = uri->host.s;
// if(uri->host.length && (strlen(addr) != uri->host.length || memcmp(addr, uri->host.s, uri->host.length) != 0)){
if(uri->host.length){
/* add Uri-Host */
// addr is destination address
pkt->opts[pkt->numopts].num = COAP_OPTION_URI_HOST;
pkt->opts[pkt->numopts].buf.p = uri->host.s;
pkt->opts[pkt->numopts].buf.len = uri->host.length;
pkt->numopts++;
}
if (uri->port != COAP_DEFAULT_PORT) {
pkt->opts[pkt->numopts].num = COAP_OPTION_URI_PORT;
res = coap_encode_var_bytes(scratch->p, uri->port);
pkt->opts[pkt->numopts].buf.len = res;
pkt->opts[pkt->numopts].buf.p = scratch->p;
scratch->p += res;
scratch->len -= res;
pkt->numopts++;
}
if (uri->path.length) {
res = coap_split_path(scratch, pkt, uri->path.s, uri->path.length);
}
if (uri->query.length) {
res = coap_split_query(scratch, pkt, uri->query.s, uri->query.length);
}
pkt->payload.p = payload;
pkt->payload.len = payload_len;
scratch->p = saved; // save back the pointer.
return 0;
}
// FIXME, if this looked in the table at the path before the method then
// it could more easily return 405 errors
int coap_handle_req(coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt)
{
const coap_option_t *opt;
int i;
uint8_t count;
const coap_endpoint_t *ep = endpoints;
while(NULL != ep->handler)
{
if (ep->method != inpkt->hdr.code)
goto next;
if (NULL != (opt = coap_findOptions(inpkt, COAP_OPTION_URI_PATH, &count)))
{
// if (count != ep->path->count)
if ((count != ep->path->count ) && (count != ep->path->count + 1)) // +1 for /f/[function], /v/[variable]
goto next;
for (i=0;i<ep->path->count;i++)
{
if (opt[i].buf.len != strlen(ep->path->elems[i]))
goto next;
if (0 != memcmp(ep->path->elems[i], opt[i].buf.p, opt[i].buf.len))
goto next;
}
// pre-path match!
if (count==ep->path->count+1 && ep->user_entry == NULL)
goto next;
return ep->handler(ep, scratch, inpkt, outpkt, inpkt->hdr.id[0], inpkt->hdr.id[1]);
}
next:
ep++;
}
coap_make_response(scratch, outpkt, NULL, 0, inpkt->hdr.id[0], inpkt->hdr.id[1], &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE);
return 0;
}
void coap_setup(void)
{
message_id = (unsigned short)rand(); // calculate only once
}
inline int
check_token(coap_packet_t *pkt) {
return pkt->tok.len == the_token.len && memcmp(pkt->tok.p, the_token.p, the_token.len) == 0;
}
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