Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

RFC 8259, bitwise checks, next siblings, doxygen comments, and more #197

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
d6f6f1a
bitwise checks on all things jsmntype_t
themobiusproject Jun 2, 2020
14ecc27
Update README.md
themobiusproject Jun 2, 2020
d5d1a50
README.md verbage (PR #114)
themobiusproject Jun 2, 2020
025c4b3
Order escapes and whitespace to match rfc8259.
themobiusproject Jun 3, 2020
8c715ec
Replace possibly confusing bitwise checks with inline functions.
themobiusproject Jun 3, 2020
9895329
Make strict mode default, add JSMN_PERMISSIVE for old default.
themobiusproject Jun 3, 2020
5287a50
Extend the parser->expected functionality to PERMISSIVE mode.
themobiusproject Jun 3, 2020
b595be1
prepare for better primitive checks
themobiusproject Jun 3, 2020
96d7221
rfc 8259 primitive parsing
themobiusproject Jun 4, 2020
3c00c19
c89 compliance
themobiusproject Jun 4, 2020
53e8d57
Stronger parsing validation, break jsmn_parse apart
themobiusproject Jun 7, 2020
e213f2f
Validate closing containers even when `tokens == NULL`.
themobiusproject Jun 7, 2020
b5d5a90
Pack jsmntype_t only when JSMN_SHORT_TOKENS is defined.
themobiusproject Jun 7, 2020
51c8ce4
c89 comments again, removed loop that validation takes care of.
themobiusproject Jun 7, 2020
0f792d7
Adding 300+ validation checks from JSONTestSuite
themobiusproject Jun 10, 2020
3135d95
Add .gitignore
themobiusproject Jun 10, 2020
550a6d5
Update jsmn, add jsmn_utils and explode, update tests.
themobiusproject Jun 14, 2020
fe07a15
Final revisions.
themobiusproject Jul 4, 2020
9f7e9de
Fix {"a":[1,2]} case
themobiusproject Sep 8, 2020
0560339
Add simple.c test to tests/tests.c
themobiusproject Nov 14, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
508 changes: 508 additions & 0 deletions .gitignore

Large diffs are not rendered by default.

38 changes: 24 additions & 14 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,25 +1,31 @@
# You can put your build options here
-include config.mk

test: test_default test_strict test_links test_strict_links
test_default: test/tests.c jsmn.h
$(CC) $(CFLAGS) $(LDFLAGS) $< -o test/$@
CFLAGS:=${CFLAGS} -std=c89 -Wno-invalid-source-encoding

test: test_default test_default_low_memory test_permissive test_permissive_low_memory
test_default: test/tests.c
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o test/$@
./test/$@
test_strict: test/tests.c jsmn.h
$(CC) -DJSMN_STRICT=1 $(CFLAGS) $(LDFLAGS) $< -o test/$@
test_default_low_memory: test/tests.c
$(CC) -DJSMN_LOW_MEMORY $(CFLAGS) $(LDFLAGS) $^ -o test/$@
./test/$@
test_links: test/tests.c jsmn.h
$(CC) -DJSMN_PARENT_LINKS=1 $(CFLAGS) $(LDFLAGS) $< -o test/$@

test_permissive: test/tests.c
$(CC) -DJSMN_PERMISSIVE $(CFLAGS) $(LDFLAGS) $^ -o test/$@
./test/$@
test_strict_links: test/tests.c jsmn.h
$(CC) -DJSMN_STRICT=1 -DJSMN_PARENT_LINKS=1 $(CFLAGS) $(LDFLAGS) $< -o test/$@
test_permissive_low_memory: test/tests.c
$(CC) -DJSMN_PERMISSIVE -DJSMN_LOW_MEMORY $(CFLAGS) $(LDFLAGS) $^ -o test/$@
./test/$@

simple_example: example/simple.c jsmn.h
$(CC) $(LDFLAGS) $< -o $@
simple_example: example/simple.c
$(CC) $(LDFLAGS) $^ -o $@

jsondump: example/jsondump.c jsmn.h
$(CC) $(LDFLAGS) $< -o $@
jsondump: example/jsondump.c
$(CC) $(LDFLAGS) $^ -o $@

explode: example/explode.c jsmn_utils.c
$(CC) $(LDFLAGS) $^ -o $@

fmt:
clang-format -i jsmn.h test/*.[ch] example/*.[ch]
Expand All @@ -31,6 +37,10 @@ clean:
rm -f *.o example/*.o
rm -f simple_example
rm -f jsondump
rm -f explode
rm -f test/test_default
rm -f test/test_default_low_memory
rm -f test/test_permissive
rm -f test/test_permissive_low_memory

.PHONY: clean test

120 changes: 68 additions & 52 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
JSMN
====

[![Build Status](https://travis-ci.org/zserge/jsmn.svg?branch=master)](https://travis-ci.org/zserge/jsmn)
[![pipeline status](https://gitlab.com/themobiusproject/jsmn/badges/master/pipeline.svg)](https://gitlab.com/themobiusproject/jsmn/-/commits/master) [![coverage report](https://gitlab.com/themobiusproject/jsmn/badges/master/coverage.svg)](https://gitlab.com/themobiusproject/jsmn/-/commits/master)

jsmn (pronounced like 'jasmine') is a minimalistic JSON parser in C. It can be
jsmn (pronounced like 'jasmine') is a minimalistic JSON parser in C. It can be
easily integrated into resource-limited or embedded projects.

You can find more information about JSON format at [json.org][1]

Library sources are available at https://github.com/zserge/jsmn

The web page with some information about jsmn can be found at
[http://zserge.com/jsmn.html][2]
[https://zserge.com/jsmn.html][2]

Philosophy
----------

Most JSON parsers offer you a bunch of functions to load JSON data, parse it
and extract any value by its name. jsmn proves that checking the correctness of
every JSON packet or allocating temporary objects to store parsed JSON fields
often is an overkill.
often is an overkill.

JSON format itself is extremely simple, so why should we complicate it?

jsmn is designed to be **robust** (it should work fine even with erroneous
jsmn is designed to be **robust** (it should work fine even with erroneous
data), **fast** (it should parse data on the fly), **portable** (no superfluous
dependencies or non-standard C extensions). And of course, **simplicity** is a
key feature - simple code style, simple algorithm, simple integration into
Expand All @@ -46,12 +46,12 @@ Design
------

The rudimentary jsmn object is a **token**. Let's consider a JSON string:

'{ "name" : "Jack", "age" : 27 }'

```json
{ "name" : "Jack", "age" : 27 }
```
It holds the following tokens:

* Object: `{ "name" : "Jack", "age" : 27}` (the whole object)
* Object: `{ "name" : "Jack", "age" : 27 }` (the whole object)
* Strings: `"name"`, `"Jack"`, `"age"` (keys and some values)
* Number: `27`

Expand All @@ -64,8 +64,7 @@ token. jsmn supports the following token types:

* Object - a container of key-value pairs, e.g.:
`{ "foo":"bar", "x":0.3 }`
* Array - a sequence of values, e.g.:
`[ 1, 2, 3 ]`
* Array - a sequence of values, e.g.: `[ 1, 2, 3 ]`
* String - a quoted sequence of chars, e.g.: `"foo"`
* Primitive - a number, a boolean (`true`, `false`) or `null`

Expand All @@ -81,7 +80,7 @@ Usage

Download `jsmn.h`, include it, done.

```
```c
#include "jsmn.h"

...
Expand All @@ -95,9 +94,10 @@ r = jsmn_parse(&p, s, strlen(s), t, 128);
Since jsmn is a single-header, header-only library, for more complex use cases
you might need to define additional macros. `#define JSMN_STATIC` hides all
jsmn API symbols by making them static. Also, if you want to include `jsmn.h`
from multiple C files, to avoid duplication of symbols you may define `JSMN_HEADER` macro.
from multiple C files, to avoid duplication of symbols you may define
`JSMN_HEADER` macro.

```
```c
/* In every .c file that uses jsmn include only declarations: */
#define JSMN_HEADER
#include "jsmn.h"
Expand All @@ -110,53 +110,57 @@ API
---

Token types are described by `jsmntype_t`:

typedef enum {
JSMN_UNDEFINED = 0,
JSMN_OBJECT = 1,
JSMN_ARRAY = 2,
JSMN_STRING = 3,
JSMN_PRIMITIVE = 4
} jsmntype_t;

```c
typedef enum {
JSMN_UNDEFINED = 0x0000,
JSMN_OBJECT = 0x0001, /*!< Object */
JSMN_ARRAY = 0x0002, /*!< Array */
JSMN_STRING = 0x0004, /*!< String */
JSMN_PRIMITIVE = 0x0008, /*!< Other primitive: number, boolean (true/false) or null */
...
} jsmntype_t;
```
**Note:** Unlike JSON data types, primitive tokens are not divided into
numbers, booleans and null, because one can easily tell the type using the
first character:

* <code>'t', 'f'</code> - boolean
* <code>'t', 'f'</code> - boolean
* <code>'n'</code> - null
* <code>'-', '0'..'9'</code> - number

Token is an object of `jsmntok_t` type:

typedef struct {
jsmntype_t type; // Token type
int start; // Token start position
int end; // Token end position
int size; // Number of child (nested) tokens
} jsmntok_t;

**Note:** string tokens point to the first character after
the opening quote and the previous symbol before final quote. This was made
to simplify string extraction from JSON data.

All job is done by `jsmn_parser` object. You can initialize a new parser using:

jsmn_parser parser;
jsmntok_t tokens[10];

jsmn_init(&parser);

// js - pointer to JSON string
// tokens - an array of tokens available
// 10 - number of tokens available
jsmn_parse(&parser, js, strlen(js), tokens, 10);

```c
typedef struct {
jsmntype_t type; /*!< type (object, array, string etc.) */
jsmnint_t start; /*!< start position in JSON data string */
jsmnint_t end; /*!< end position in JSON data string */
jsmnint_t size; /*!< number of children */
...
} jsmntok_t;
```
**Note:** string tokens point to the first character after the opening quote
and the previous symbol before final quote. This was made to simplify string
extraction from JSON data.

All jobs are done by the `jsmn_parser` object. You can initialize a new parser
using:
```c
jsmn_parser parser;
jsmntok_t tokens[10];

jsmn_init(&parser);

/* js - pointer to JSON string */
/* tokens - an array of tokens available */
/* 10 - number of tokens available */
jsmn_parse(&parser, js, strlen(js), tokens, 10);
```
This will create a parser, and then it tries to parse up to 10 JSON tokens from
the `js` string.

A non-negative return value of `jsmn_parse` is the number of tokens actually
used by the parser.

Passing NULL instead of the tokens array would not store parsing results, but
instead the function will return the number of tokens needed to parse the given
string. This can be useful if you don't know yet how many tokens to allocate.
Expand All @@ -166,17 +170,29 @@ If something goes wrong, you will get an error. Error will be one of these:
* `JSMN_ERROR_INVAL` - bad token, JSON string is corrupted
* `JSMN_ERROR_NOMEM` - not enough tokens, JSON string is too large
* `JSMN_ERROR_PART` - JSON string is too short, expecting more JSON data
* `JSMN_ERROR_LEN` - JSON string is too long (see note)

If you get `JSMN_ERROR_NOMEM`, you can re-allocate more tokens and call
`jsmn_parse` once more. If you read json data from the stream, you can
periodically call `jsmn_parse` and check if return value is `JSMN_ERROR_PART`.
You will get this error until you reach the end of JSON data.

**Note:** The amount of input data jsmn can parse is limited by the size of
jsmnint_t. Currently typedefed as an unsigned int.

Thus follows max len = 2^(sizeof(jsmnint_t)*8) -1 for various int sizes:

* 16bit ints - 65535
* 32bit ints - 4294967295
* 64bit ints - ~1.8e19

feed more data into `jsmn_parse` and you will get `JSMN_ERROR_LEN`.

Other info
----------

This software is distributed under [MIT license](http://www.opensource.org/licenses/mit-license.php),
so feel free to integrate it in your commercial products.
This software is distributed under [MIT license](https://www.opensource.org/licenses/mit-license.php),
so feel free to integrate it in your commercial products.

[1]: http://www.json.org/
[2]: http://zserge.com/jsmn.html
[1]: https://www.json.org/
[2]: https://zserge.com/jsmn.html
29 changes: 29 additions & 0 deletions example/explode.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#define JSMN_HEADER
#include "../jsmn_utils.h"

#include <stdlib.h>
#include <stdio.h>

int main(void)
{
FILE *fp;
size_t size;
char *JSON_STRING;

fp = fopen("library.json", "r");

fseek(fp, 0, SEEK_END);
size = ftell(fp);
rewind(fp);

JSON_STRING = calloc(size, sizeof(char));
(void)fread(JSON_STRING, sizeof(char), size, fp);

fclose(fp);

jsmn_explodeJSON(JSON_STRING, size);

free(JSON_STRING);

return EXIT_SUCCESS;
}
Loading