diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..42a550ba --- /dev/null +++ b/.gitignore @@ -0,0 +1,508 @@ +##### Custom +Index/ +build_*/ + +##### C.gitignore +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + + +##### C++.gitignore +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + + +##### CMake.gitignore +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake + + +##### KDevelop4.gitignore +*.kdev4 +.kdev4/ + + +##### VisualStudio.gitignore +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + + +##### Xcode.gitignore +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## User settings +xcuserdata/ + +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout + +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +build/ +DerivedData/ +*.moved-aside +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 + + +##### macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + + +##### Linux.gitignore +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* diff --git a/Makefile b/Makefile index dcbdd89d..b3e86056 100644 --- a/Makefile +++ b/Makefile @@ -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] @@ -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 - diff --git a/README.md b/README.md index f8249f3d..ddd0aa7b 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ 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] @@ -11,7 +11,7 @@ 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 ---------- @@ -19,11 +19,11 @@ 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 @@ -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` @@ -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` @@ -81,7 +80,7 @@ Usage Download `jsmn.h`, include it, done. -``` +```c #include "jsmn.h" ... @@ -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" @@ -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: -* 't', 'f' - boolean +* 't', 'f' - boolean * 'n' - null * '-', '0'..'9' - 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. @@ -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 diff --git a/example/explode.c b/example/explode.c new file mode 100644 index 00000000..73945093 --- /dev/null +++ b/example/explode.c @@ -0,0 +1,29 @@ +#define JSMN_HEADER +#include "../jsmn_utils.h" + +#include +#include + +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; +} diff --git a/jsmn.h b/jsmn.h index 3178dcc9..c0a36b07 100644 --- a/jsmn.h +++ b/jsmn.h @@ -2,6 +2,7 @@ * MIT License * * Copyright (c) 2010 Serge Zaitsev + * Copyright (c) 2020 Mark Conway (mark.conway@themobiusproject.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 @@ -21,445 +22,1040 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#ifndef JSMN_H -#define JSMN_H +#ifndef JSMN_H_ +#define JSMN_H_ -#include +#define JSMN_VERSION_MAJOR 2 +#define JSMN_VERSION_MINOR 0 +#define JSMN_VERSION_PATCH 0 -#ifdef __cplusplus -extern "C" { +#if defined(UNIT_TESTING) +#include +#include +#include #endif -#ifdef JSMN_STATIC -#define JSMN_API static +#include +#include + +#include "jsmn_defines.h" + +#if defined(JSMN_SHORT_TOKENS) +typedef unsigned short jsmnint_t; +# define JSMNINT_MAX USHRT_MAX #else -#define JSMN_API extern +typedef unsigned int jsmnint_t; +# define JSMNINT_MAX UINT_MAX #endif +#define JSMN_NEG ((jsmnint_t)-1) /** * JSON type identifier. Basic types are: - * o Object - * o Array - * o String - * o Other primitive: number, boolean (true/false) or null */ typedef enum { - JSMN_UNDEFINED = 0, - JSMN_OBJECT = 1, - JSMN_ARRAY = 2, - JSMN_STRING = 3, - JSMN_PRIMITIVE = 4 + 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 */ + + JSMN_KEY = 0x0010, /*!< is a key */ + JSMN_VALUE = 0x0020, /*!< is a value */ + + /* Complex elements */ + JSMN_CONTAINER = JSMN_OBJECT | JSMN_ARRAY, +#if !defined(JSMN_PERMISSIVE_KEY) + JSMN_KEY_TYPE = JSMN_STRING, +#else + JSMN_KEY_TYPE = JSMN_STRING | JSMN_PRIMITIVE, +#endif + JSMN_VAL_TYPE = JSMN_OBJECT | JSMN_ARRAY | JSMN_STRING | JSMN_PRIMITIVE, + + JSMN_OBJ_VAL = JSMN_OBJECT | JSMN_VALUE, + JSMN_ARR_VAL = JSMN_ARRAY | JSMN_VALUE, + JSMN_STR_KEY = JSMN_STRING | JSMN_KEY, + JSMN_STR_VAL = JSMN_STRING | JSMN_VALUE, + JSMN_PRI_VAL = JSMN_PRIMITIVE | JSMN_VALUE, +#if defined(JSMN_PERMISSIVE_KEY) + JSMN_PRI_KEY = JSMN_PRIMITIVE | JSMN_KEY, +#endif + + /* Primitive extension */ + JSMN_PRI_LITERAL = 0x0040, /*!< true, false, null */ + JSMN_PRI_INTEGER = 0x0080, /*!< 0, 1 - 9 */ + JSMN_PRI_SIGN = 0x0100, /*!< minus sign, '-' or plus sign, '+' */ + JSMN_PRI_DECIMAL = 0x0200, /*!< deminal point '.' */ + JSMN_PRI_EXPONENT = 0x0400, /*!< exponent, 'e' or 'E' */ + + JSMN_PRI_MINUS = JSMN_PRI_SIGN, + + /* Parsing validation, expectations, and state information */ + JSMN_PRI_CONTINUE = 0x0800, /*!< Allow a continuation of a PRIMITIVE */ + JSMN_CLOSE = 0x1000, /*!< Close OBJECT '}' or ARRAY ']' */ + JSMN_COLON = 0x2000, /*!< Colon ':' expected after KEY */ + JSMN_COMMA = 0x4000, /*!< Comma ',' expected after VALUE */ + JSMN_INSD_OBJ = 0x8000, /*!< Inside an OBJECT */ + + /* Parsing rules */ +#if !defined(JSMN_PERMISSIVE_RULESET) + JSMN_ROOT_INIT = JSMN_VAL_TYPE | JSMN_VALUE, +# if !defined(JSMN_MULTIPLE_JSON) + JSMN_ROOT = JSMN_UNDEFINED, +# else + JSMN_ROOT = JSMN_VAL_TYPE | JSMN_VALUE, +# endif + JSMN_OPEN_OBJECT = JSMN_KEY_TYPE | JSMN_KEY | JSMN_CLOSE | JSMN_INSD_OBJ, + JSMN_AFTR_OBJ_KEY = JSMN_VALUE | JSMN_INSD_OBJ | JSMN_COLON, + JSMN_AFTR_OBJ_VAL = JSMN_CLOSE | JSMN_INSD_OBJ | JSMN_COMMA, + JSMN_OPEN_ARRAY = JSMN_VAL_TYPE | JSMN_VALUE | JSMN_CLOSE, + JSMN_AFTR_ARR_VAL = JSMN_VALUE | JSMN_CLOSE | JSMN_COMMA, + JSMN_AFTR_CLOSE = JSMN_CLOSE | JSMN_COMMA, + JSMN_AFTR_COLON = JSMN_VAL_TYPE | JSMN_VALUE | JSMN_INSD_OBJ, + JSMN_AFTR_COMMA_O = JSMN_KEY_TYPE | JSMN_KEY | JSMN_INSD_OBJ, + JSMN_AFTR_COMMA_A = JSMN_VAL_TYPE | JSMN_VALUE, + +#else + + JSMN_ROOT_INIT = JSMN_VAL_TYPE | JSMN_VALUE | JSMN_KEY, +# if !defined(JSMN_MULTIPLE_JSON_FAIL) + JSMN_ROOT = JSMN_VAL_TYPE | JSMN_COLON | JSMN_COMMA, + JSMN_ROOT_AFTR_O = JSMN_VAL_TYPE | JSMN_COMMA, +# else + JSMN_ROOT = JSMN_UNDEFINED, + JSMN_ROOT_AFTR_O = JSMN_UNDEFINED, +# endif + JSMN_OPEN_OBJECT = JSMN_KEY_TYPE | JSMN_KEY | JSMN_CLOSE | JSMN_INSD_OBJ, + JSMN_AFTR_OBJ_KEY = JSMN_VALUE | JSMN_INSD_OBJ | JSMN_COLON, + JSMN_AFTR_OBJ_VAL = JSMN_VAL_TYPE | JSMN_CLOSE | JSMN_INSD_OBJ | JSMN_COMMA, + JSMN_OPEN_ARRAY = JSMN_VAL_TYPE | JSMN_VALUE | JSMN_CLOSE, + JSMN_AFTR_ARR_VAL = JSMN_VAL_TYPE | JSMN_CLOSE | JSMN_COMMA, + JSMN_AFTR_CLOSE = JSMN_VAL_TYPE | JSMN_CLOSE | JSMN_COMMA, + JSMN_AFTR_COLON = JSMN_VAL_TYPE | JSMN_VALUE | JSMN_INSD_OBJ, + JSMN_AFTR_COLON_R = JSMN_VAL_TYPE | JSMN_VALUE, + JSMN_AFTR_COMMA_O = JSMN_KEY_TYPE | JSMN_KEY | JSMN_INSD_OBJ, + JSMN_AFTR_COMMA_A = JSMN_VAL_TYPE | JSMN_VALUE, + JSMN_AFTR_COMMA_R = JSMN_VAL_TYPE, +#endif } jsmntype_t; -enum jsmnerr { - /* Not enough tokens were provided */ - JSMN_ERROR_NOMEM = -1, - /* Invalid character inside JSON string */ - JSMN_ERROR_INVAL = -2, - /* The string is not a full JSON packet, more bytes expected */ - JSMN_ERROR_PART = -3 -}; +/*! + * JSMN Error Codes + */ +typedef enum jsmnerr { + JSMN_SUCCESS = 0, + JSMN_ERROR_NOMEM = -1, /*!< Not enough tokens were provided */ + JSMN_ERROR_LENGTH = -2, /*!< Input data too long */ + JSMN_ERROR_INVAL = -3, /*!< Invalid character inside JSON string */ + JSMN_ERROR_PART = -4, /*!< The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_BRACKETS = -5, /*!< The JSON string has unmatched brackets */ + + JSMN_ERROR_MAX = -5, /*!< "MAX" value to be tested against when checking for errors */ +} jsmnerr; + +/*! + * JSMN Boolean + */ +typedef enum jsmnbool { + JSMN_FALSE = 0, /*!< false */ + JSMN_TRUE = 1, /*!< true */ +} jsmnbool; /** * JSON token description. - * type type (object, array, string etc.) - * start start position in JSON data string - * end end position in JSON data string */ -typedef struct jsmntok { - jsmntype_t type; - int start; - int end; - int size; -#ifdef JSMN_PARENT_LINKS - int parent; +typedef struct jsmntok_t { + 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 */ +#if defined(JSMN_PARENT_LINKS) + jsmnint_t parent; /*!< parent id */ +#endif +#if defined(JSMN_NEXT_SIBLING) + jsmnint_t next_sibling; /*!< next sibling id */ #endif } jsmntok_t; /** - * JSON parser. Contains an array of token blocks available. Also stores + * JSON parser + * + * Contains an array of token blocks available. Also stores * the string being parsed now and current position in that string. */ typedef struct jsmn_parser { - unsigned int pos; /* offset in the JSON string */ - unsigned int toknext; /* next token to allocate */ - int toksuper; /* superior token node, e.g. parent object or array */ + jsmnint_t pos; /*!< offset in the JSON string */ + jsmnint_t toknext; /*!< next token to allocate */ + /*!< when tokens == NULL, keeps track of container types to a depth of (sizeof(jsmnint_t) * 8) */ + jsmnint_t toksuper; /*!< superior token node, e.g. parent object or array */ + /*!< when tokens == NULL, toksuper represents container depth */ + jsmnint_t count; /*!< useful to have in the parser when you are continuing a failed parse with NULL tokens */ + jsmntype_t expected; /*!< Expected jsmn type(s) */ } jsmn_parser; +#ifdef __cplusplus +extern "C" { +#endif + /** - * Create JSON parser over an array of tokens + * @brief Create JSON parser over an array of tokens + * + * @param[out] parser jsmn parser */ -JSMN_API void jsmn_init(jsmn_parser *parser); +JSMN_API +void jsmn_init(jsmn_parser *parser); /** - * Run JSON parser. It parses a JSON data string into and array of tokens, each - * describing - * a single JSON object. + * @brief Run JSON parser + * + * It parses a JSON data string into and array of tokens, each + * describing a single JSON object. + * + * @param[in,out] parser jsmn parser + * @param[in] js JSON data string + * @param[in] len JSON data string length + * @param[in,out] tokens pointer to memory allocated for tokens or NULL + * @param[in] num_tokens number of tokens allocated + * @return jsmnint_t number of tokens found or ERRNO */ -JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, - jsmntok_t *tokens, const unsigned int num_tokens); +JSMN_API +jsmnint_t jsmn_parse(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens); + +#if !defined(JSMN_HEADER) -#ifndef JSMN_HEADER /** * Allocates a fresh unused token from the token pool. */ -static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, - const size_t num_tokens) { - jsmntok_t *tok; +static +jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, + const size_t num_tokens) +{ if (parser->toknext >= num_tokens) { return NULL; } - tok = &tokens[parser->toknext++]; - tok->start = tok->end = -1; + + jsmntok_t *tok = &tokens[parser->toknext++]; + tok->start = tok->end = JSMN_NEG; tok->size = 0; -#ifdef JSMN_PARENT_LINKS - tok->parent = -1; +#if defined(JSMN_PARENT_LINKS) + tok->parent = JSMN_NEG; #endif +#if defined(JSMN_NEXT_SIBLING) + tok->next_sibling = JSMN_NEG; +#endif + parser->count++; return tok; } /** * Fills token type and boundaries. */ -static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type, - const int start, const int end) { +static +void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type, + const jsmnint_t start, const jsmnint_t end) +{ token->type = type; token->start = start; token->end = end; token->size = 0; } +#if defined(JSMN_NEXT_SIBLING) +/** + * Set previous child's next_sibling to current token + */ +static +void jsmn_next_sibling(jsmn_parser *parser, jsmntok_t *tokens) +{ + jsmnint_t sibling; + + /* Start with parent's first child */ + if (parser->toksuper != JSMN_NEG) { + sibling = parser->toksuper + 1; + } else { + sibling = 0; + } + + /* If the first child is the current token */ + if (sibling == parser->toknext - 1) { + return; + } + + /* Loop until we find previous sibling */ + while (tokens[sibling].next_sibling != JSMN_NEG) { + sibling = tokens[sibling].next_sibling; + } + + /* Set previous sibling's next_sibling to current token */ + tokens[sibling].next_sibling = parser->toknext - 1; +} +#endif + +static +jsmnbool isWhitespace(const char c) +{ + if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { + return JSMN_TRUE; + } + return JSMN_FALSE; +} + +static +jsmnbool isHexadecimal(const char c) +{ + if ((c >= '0' && c <= '9') || + (c >= 'A' && c <= 'F') || + (c >= 'a' && c <= 'f')) { + return JSMN_TRUE; + } + return JSMN_FALSE; +} + +static +jsmnbool isCharacter(const char c) +{ + if (c >= ' ' && c != '"' && c != '\\') { + return JSMN_TRUE; + } + return JSMN_FALSE; +} + +static +jsmnbool isSpecialChar(const char c) +{ + if (c == '{' || c == '}' || c == '[' || c == ']' || + c == '"' || c == ':' || c == ',') { + return JSMN_TRUE; + } + return JSMN_FALSE; +} + /** * Fills next available token with JSON primitive. */ -static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, - const size_t len, jsmntok_t *tokens, - const size_t num_tokens) { - jsmntok_t *token; - int start; - - start = parser->pos; - - for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { - switch (js[parser->pos]) { -#ifndef JSMN_STRICT - /* In strict mode primitive must be followed by "," or "}" or "]" */ - case ':': -#endif - case '\t': - case '\r': - case '\n': - case ' ': - case ',': - case ']': - case '}': +static +jsmnint_t jsmn_parse_primitive(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) +{ + /* If a PRIMITIVE wasn't expected */ + if (!(parser->expected & (JSMN_PRIMITIVE | JSMN_PRI_CONTINUE))) { + return JSMN_ERROR_INVAL; + } + + jsmnint_t pos; + jsmntype_t type; + jsmntype_t expected = JSMN_CLOSE; + + if (!(parser->expected & JSMN_PRI_CONTINUE)) { + pos = parser->pos; + } else { + if (tokens != NULL) { + pos = tokens[parser->toknext - 1].start; + } else { + pos = parser->pos; + while (pos != JSMN_NEG && + !isWhitespace(js[pos]) && + !isSpecialChar(js[pos]) && + isCharacter(js[pos])) { + pos--; + } + pos++; + } + } + type = JSMN_PRIMITIVE; + +#if !defined(JSMN_PERMISSIVE_PRIMITIVE) +# if !defined(JSMN_PERMISSIVE_LITERALS) + char literal[][6] = { "true", "false", "null" }; +# else + char literal[][9] = { "true", "false", "null", "NaN", "Infinity" }; +# endif + jsmnint_t i; + for (i = 0; i < sizeof(literal) / sizeof(literal[0]); i++) { + if (js[pos] != literal[i][0]) { + continue; + } + jsmnint_t j; + for (j = 1, pos++; literal[i][j] != '\0'; j++, pos++) { + if (pos == len || + js[pos] == '\0') { + return JSMN_ERROR_PART; + } + if (js[pos] != literal[i][j]) { + return JSMN_ERROR_INVAL; + } + } + type |= JSMN_PRI_LITERAL; + if (pos == len) { goto found; - default: - /* to quiet a warning from gcc*/ - break; } - if (js[parser->pos] < 32 || js[parser->pos] >= 127) { - parser->pos = start; + goto check_primitive_border; + } + + expected = JSMN_PRI_MINUS | JSMN_PRI_INTEGER; + for (; pos < len; pos++) { + if (js[pos] == '0') { + if (!(expected & JSMN_PRI_INTEGER)) { + return JSMN_ERROR_INVAL; + } + if (type & JSMN_PRI_EXPONENT) { + expected = JSMN_PRI_INTEGER | JSMN_CLOSE; + } else if (type & JSMN_PRI_DECIMAL) { + expected = JSMN_PRI_INTEGER | JSMN_PRI_EXPONENT | JSMN_CLOSE; + } else if (parser->pos == pos || + (parser->pos + 1 == pos && (type & JSMN_PRI_MINUS))) { + expected = JSMN_PRI_DECIMAL | JSMN_PRI_EXPONENT | JSMN_CLOSE; + } else { + expected = JSMN_PRI_INTEGER | JSMN_PRI_DECIMAL | JSMN_PRI_EXPONENT | JSMN_CLOSE; + } + continue; + } + + if (js[pos] >= '1' && js[pos] <= '9') { + if (!(expected & JSMN_PRI_INTEGER)) { + return JSMN_ERROR_INVAL; + } + if (type & JSMN_PRI_EXPONENT) { + expected = JSMN_PRI_INTEGER | JSMN_CLOSE; + } else if (type & JSMN_PRI_DECIMAL) { + expected = JSMN_PRI_INTEGER | JSMN_PRI_EXPONENT | JSMN_CLOSE; + } else { + expected = JSMN_PRI_INTEGER | JSMN_PRI_DECIMAL | JSMN_PRI_EXPONENT | JSMN_CLOSE; + } + continue; + } + + if (js[pos] == '-') { + if (!(expected & JSMN_PRI_MINUS)) { + return JSMN_ERROR_INVAL; + } + if (parser->pos == pos) { + type |= JSMN_PRI_MINUS; + } + expected = JSMN_PRI_INTEGER; + continue; + } + + if (js[pos] == '+') { + if (!(expected & JSMN_PRI_SIGN)) { + return JSMN_ERROR_INVAL; + } + expected = JSMN_PRI_INTEGER; + continue; + } + + if (js[pos] == '.') { + if (!(expected & JSMN_PRI_DECIMAL)) { + return JSMN_ERROR_INVAL; + } + type |= JSMN_PRI_DECIMAL; + expected = JSMN_PRI_INTEGER; + continue; + } + + if (js[pos] == 'e' || js[pos] == 'E') { + if (!(expected & JSMN_PRI_EXPONENT)) { + return JSMN_ERROR_INVAL; + } + type |= JSMN_PRI_EXPONENT; + expected = JSMN_PRI_SIGN | JSMN_PRI_INTEGER; + continue; + } + + if (!(expected & JSMN_CLOSE)) { + return JSMN_ERROR_INVAL; + } + goto check_primitive_border; + } + if (!(expected & JSMN_CLOSE)) { + return JSMN_ERROR_INVAL; + } + goto found; + +check_primitive_border: + switch (js[pos]) { + case ' ': + case '\t': + case '\n': + case '\r': + case ',': + case '}': + case ']': + case '\0': + goto found; + case '"': + case ':': + case '{': + case '[': + default: + return JSMN_ERROR_INVAL; + } +#else + for (; pos < len && js[pos] != '\0'; pos++) { + if (isWhitespace(js[pos]) || + isSpecialChar(js[pos])) { + goto found; + } + if (!isCharacter(js[pos])) { return JSMN_ERROR_INVAL; } } -#ifdef JSMN_STRICT - /* In strict mode primitive must be followed by a comma/object/array */ - parser->pos = start; - return JSMN_ERROR_PART; #endif found: + expected = parser->expected; + if (parser->toksuper != JSMN_NEG) { +#if defined(JSMN_PERMISSIVE_KEY) + /* OBJECT KEY, strict query */ + if ((expected & (JSMN_KEY | JSMN_INSD_OBJ)) == (JSMN_KEY | JSMN_INSD_OBJ)) { + parser->expected = JSMN_AFTR_OBJ_KEY; + type |= JSMN_KEY | JSMN_INSD_OBJ; + } else +#endif + /* OBJECT VALUE, VALUE is implicit */ + if (expected & JSMN_INSD_OBJ) { + parser->expected = JSMN_AFTR_OBJ_VAL; + type |= JSMN_VALUE | JSMN_INSD_OBJ; + /* ARRAY VALUE, VALUE is implicit */ + } else { + parser->expected = JSMN_AFTR_ARR_VAL; + type |= JSMN_VALUE; + } + } else { + parser->expected = JSMN_ROOT; +#if defined(JSMN_PERMISSIVE_RULESET) && defined(JSMN_MULTIPLE_JSON_FAIL) + if (expected & JSMN_KEY) { + parser->expected |= JSMN_COLON; + } +#endif + type |= JSMN_VALUE; + } + if (pos == len) { + parser->expected |= JSMN_PRI_CONTINUE; + } + if (tokens == NULL) { - parser->pos--; - return 0; + parser->pos = pos - 1; + if (!(expected & JSMN_PRI_CONTINUE)) { + parser->count++; + } + return JSMN_SUCCESS; } - token = jsmn_alloc_token(parser, tokens, num_tokens); - if (token == NULL) { - parser->pos = start; - return JSMN_ERROR_NOMEM; + + jsmntok_t *token; + if (!(expected & JSMN_PRI_CONTINUE)) { + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->expected = expected; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, type, parser->pos, pos); + } else { + token = &tokens[parser->toknext - 1]; + jsmn_fill_token(token, type, token->start, pos); } - jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); -#ifdef JSMN_PARENT_LINKS + parser->pos = pos; +#if defined(JSMN_PARENT_LINKS) token->parent = parser->toksuper; #endif +#if defined(JSMN_NEXT_SIBLING) + jsmn_next_sibling(parser, tokens); +#endif + + if (parser->toksuper != JSMN_NEG) { + if (!(expected & JSMN_PRI_CONTINUE)) { + tokens[parser->toksuper].size++; + } + + if (!(tokens[parser->toksuper].type & JSMN_CONTAINER)) { +#if defined(JSMN_PARENT_LINKS) + parser->toksuper = tokens[parser->toksuper].parent; +#else + jsmnint_t i; + for (i = parser->toksuper; i != JSMN_NEG; i--) { + if (tokens[i].type & JSMN_CONTAINER && tokens[i].end == JSMN_NEG) { + parser->toksuper = i; + break; + } + } +# if defined(JSMN_PERMISSIVE_RULESET) + if (i == JSMN_NEG) { + parser->toksuper = i; + } +# endif +#endif + } + } parser->pos--; - return 0; + + return JSMN_SUCCESS; } /** * Fills next token with JSON string. */ -static int jsmn_parse_string(jsmn_parser *parser, const char *js, - const size_t len, jsmntok_t *tokens, - const size_t num_tokens) { - jsmntok_t *token; - - int start = parser->pos; +static +jsmnint_t jsmn_parse_string(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) +{ + /* If a STRING wasn't expected */ + if (!(parser->expected & JSMN_STRING)) { + return JSMN_ERROR_INVAL; + } - parser->pos++; + jsmnint_t pos = parser->pos; /* Skip starting quote */ - for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { - char c = js[parser->pos]; + pos++; + + char c; + for (; pos < len && js[pos] != '\0'; pos++) { + c = js[pos]; /* Quote: end of string */ - if (c == '\"') { + if (c == '"') { + jsmntype_t expected = parser->expected; + jsmntype_t type; + if (parser->toksuper != JSMN_NEG) { + /* OBJECT KEY, strict query */ + if ((expected & (JSMN_INSD_OBJ | JSMN_KEY)) == (JSMN_INSD_OBJ | JSMN_KEY)) { + parser->expected = JSMN_AFTR_OBJ_KEY; + type = JSMN_STRING | JSMN_KEY | JSMN_INSD_OBJ; + /* OBJECT VALUE, VALUE is implicit */ + } else if (expected & JSMN_INSD_OBJ) { + parser->expected = JSMN_AFTR_OBJ_VAL; + type = JSMN_STRING | JSMN_VALUE | JSMN_INSD_OBJ; + /* ARRAY VALUE, VALUE is implicit */ + } else { + parser->expected = JSMN_AFTR_ARR_VAL; + type = JSMN_STRING | JSMN_VALUE; + } + } else { + parser->expected = JSMN_ROOT; +#if defined(JSMN_PERMISSIVE_RULESET) && defined(JSMN_MULTIPLE_JSON_FAIL) + if (expected & JSMN_KEY) { + parser->expected |= JSMN_COLON; + } +#endif + type = JSMN_STRING | JSMN_VALUE; + } + if (tokens == NULL) { - return 0; + parser->pos = pos; + parser->count++; + return JSMN_SUCCESS; } - token = jsmn_alloc_token(parser, tokens, num_tokens); + + jsmntok_t *token = jsmn_alloc_token(parser, tokens, num_tokens); if (token == NULL) { - parser->pos = start; + parser->expected = expected; return JSMN_ERROR_NOMEM; } - jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos); -#ifdef JSMN_PARENT_LINKS + jsmn_fill_token(token, type, parser->pos + 1, pos); + parser->pos = pos; +#if defined(JSMN_PARENT_LINKS) token->parent = parser->toksuper; #endif - return 0; +#if defined(JSMN_NEXT_SIBLING) + jsmn_next_sibling(parser, tokens); +#endif + + if (parser->toksuper != JSMN_NEG) { + tokens[parser->toksuper].size++; + + if (!(tokens[parser->toksuper].type & JSMN_CONTAINER)) { +#if defined(JSMN_PARENT_LINKS) + parser->toksuper = tokens[parser->toksuper].parent; +#else + jsmnint_t i; + for (i = parser->toksuper; i != JSMN_NEG; i--) { + if (tokens[i].type & JSMN_CONTAINER && tokens[i].end == JSMN_NEG) { + parser->toksuper = i; + break; + } + } +# if defined(JSMN_PERMISSIVE_RULESET) + if (i == JSMN_NEG) { + parser->toksuper = i; + } +# endif +#endif + } + } + + return JSMN_SUCCESS; } /* Backslash: Quoted symbol expected */ - if (c == '\\' && parser->pos + 1 < len) { - int i; - parser->pos++; - switch (js[parser->pos]) { + if (c == '\\' && pos + 1 < len) { + pos++; + switch (js[pos]) { /* Allowed escaped symbols */ - case '\"': - case '/': + case '"': case '\\': + case '/': case 'b': case 'f': - case 'r': case 'n': + case 'r': case 't': break; - /* Allows escaped symbol \uXXXX */ + /* Allows escaped symbol \uhhhh */ case 'u': - parser->pos++; - for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; - i++) { + pos++; + jsmnint_t i; + for (i = pos + 4; pos < i; pos++) { + if (pos == len || + js[pos] == '\0') { + return JSMN_ERROR_PART; + } /* If it isn't a hex character we have an error */ - if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ - (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ - (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ - parser->pos = start; + if (!isHexadecimal(js[pos])) { return JSMN_ERROR_INVAL; } - parser->pos++; } - parser->pos--; + pos--; break; +#if defined(JSMN_PERMISSIVE_UTF32) + /* Allows escaped symbol \Uhhhhhhhh */ + case 'U': + pos++; + for (i = pos + 8; pos < i; pos++) { + if (pos == len || + js[pos] == '\0') { + return JSMN_ERROR_PART; + } + /* If it isn't a hex character we have an error */ + if (!isHexadecimal(js[pos])) { + return JSMN_ERROR_INVAL; + } + } + pos--; + break; +#endif /* Unexpected symbol */ default: - parser->pos = start; return JSMN_ERROR_INVAL; } } + + /* form feed, new line, carraige return, tab, and vertical tab not allowed */ + else if (c == '\f' || + c == '\n' || + c == '\r' || + c == '\t' || + c == '\v') { + return JSMN_ERROR_INVAL; + } } - parser->pos = start; return JSMN_ERROR_PART; } -/** - * Parse JSON string and fill tokens. - */ -JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, - jsmntok_t *tokens, const unsigned int num_tokens) { - int r; - int i; - jsmntok_t *token; - int count = parser->toknext; +static +jsmnint_t jsmn_parse_container_open(jsmn_parser *parser, const char c, + jsmntok_t *tokens, const size_t num_tokens) +{ + /* If an OBJECT or ARRAY wasn't expected */ + if (!(parser->expected & JSMN_CONTAINER)) { + return JSMN_ERROR_INVAL; + } - for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { - char c; - jsmntype_t type; + jsmntype_t type; + if (c == '{') { + parser->expected = JSMN_OPEN_OBJECT; + type = JSMN_OBJECT | JSMN_VALUE; + } else { + parser->expected = JSMN_OPEN_ARRAY; + type = JSMN_ARRAY | JSMN_VALUE; + } - c = js[parser->pos]; - switch (c) { - case '{': - case '[': - count++; - if (tokens == NULL) { - break; - } - token = jsmn_alloc_token(parser, tokens, num_tokens); - if (token == NULL) { - return JSMN_ERROR_NOMEM; - } - if (parser->toksuper != -1) { - jsmntok_t *t = &tokens[parser->toksuper]; -#ifdef JSMN_STRICT - /* In strict mode an object or array can't become a key */ - if (t->type == JSMN_OBJECT) { - return JSMN_ERROR_INVAL; - } + if (tokens == NULL) { + parser->toksuper++; + if (parser->toksuper < (sizeof(jsmnint_t) * CHAR_BIT) && + parser->expected & JSMN_INSD_OBJ) { + parser->toknext |= (1 << parser->toksuper); + } + parser->count++; + return JSMN_SUCCESS; + } + + if (parser->toksuper != JSMN_NEG && + tokens[parser->toksuper].type & JSMN_INSD_OBJ) { + type |= JSMN_INSD_OBJ; + } + + jsmntok_t *token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, type, parser->pos, JSMN_NEG); +#if defined(JSMN_PARENT_LINKS) + token->parent = parser->toksuper; #endif - t->size++; -#ifdef JSMN_PARENT_LINKS - token->parent = parser->toksuper; +#if defined(JSMN_NEXT_SIBLING) + jsmn_next_sibling(parser, tokens); #endif + + if (parser->toksuper != JSMN_NEG) { + tokens[parser->toksuper].size++; + } + parser->toksuper = parser->toknext - 1; + + return JSMN_SUCCESS; +} + +static +jsmnint_t jsmn_parse_container_close(jsmn_parser *parser, const char c, + jsmntok_t *tokens) +{ + /* If an OBJECT or ARRAY CLOSE wasn't expected */ + if (!(parser->expected & JSMN_CLOSE)) { + return JSMN_ERROR_INVAL; + } + + if (tokens == NULL) { + if (parser->toksuper < (sizeof(jsmnint_t) * CHAR_BIT)) { + jsmntype_t type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); + if ((((parser->toknext & (1 << parser->toksuper)) == 1) && !(type & JSMN_OBJECT)) || + (((parser->toknext & (1 << parser->toksuper)) == 0) && !(type & JSMN_ARRAY))) { + return JSMN_ERROR_BRACKETS; } - token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); - token->start = parser->pos; - parser->toksuper = parser->toknext - 1; - break; - case '}': - case ']': - if (tokens == NULL) { - break; - } - type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); -#ifdef JSMN_PARENT_LINKS - if (parser->toknext < 1) { - return JSMN_ERROR_INVAL; - } - token = &tokens[parser->toknext - 1]; - for (;;) { - if (token->start != -1 && token->end == -1) { - if (token->type != type) { - return JSMN_ERROR_INVAL; - } - token->end = parser->pos + 1; - parser->toksuper = token->parent; - break; - } - if (token->parent == -1) { - if (token->type != type || parser->toksuper == -1) { - return JSMN_ERROR_INVAL; - } - break; - } - token = &tokens[token->parent]; - } + parser->toknext &= ~(1 << parser->toksuper); + } + parser->toksuper--; + } else { +#if defined(JSMN_PERMISSIVE_RULESET) + if (parser->toksuper == JSMN_NEG) { + return JSMN_ERROR_BRACKETS; + } +#endif + jsmntype_t type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); + jsmntok_t *token = &tokens[parser->toksuper]; + + if (!(token->type & type) || + token->end != JSMN_NEG) { + return JSMN_ERROR_BRACKETS; + } + token->end = parser->pos + 1; +#if defined(JSMN_PARENT_LINKS) + if (token->type & JSMN_INSD_OBJ && + !(tokens[token->parent].type & JSMN_CONTAINER)) { + parser->toksuper = tokens[token->parent].parent; + } else { + parser->toksuper = token->parent; + } #else - for (i = parser->toknext - 1; i >= 0; i--) { - token = &tokens[i]; - if (token->start != -1 && token->end == -1) { - if (token->type != type) { - return JSMN_ERROR_INVAL; - } - parser->toksuper = -1; - token->end = parser->pos + 1; - break; - } - } - /* Error if unmatched closing bracket */ - if (i == -1) { - return JSMN_ERROR_INVAL; - } - for (; i >= 0; i--) { - token = &tokens[i]; - if (token->start != -1 && token->end == -1) { - parser->toksuper = i; - break; - } + jsmnint_t i; + for (i = parser->toksuper - 1; i != JSMN_NEG; i--) { + if (tokens[i].type & JSMN_CONTAINER && tokens[i].end == JSMN_NEG) { + parser->toksuper = i; + break; } + } + if (i == JSMN_NEG) { + parser->toksuper = i; + } +#endif + } + + if (parser->toksuper != JSMN_NEG) { + parser->expected = JSMN_AFTR_CLOSE; + } else { + parser->expected = JSMN_ROOT; + } + + return JSMN_SUCCESS; +} + +static +jsmnint_t jsmn_parse_colon(jsmn_parser *parser, jsmntok_t *tokens) +{ + /* If a COLON wasn't expected */ + if (!(parser->expected & JSMN_COLON)) { + return JSMN_ERROR_INVAL; + } + + if (parser->toksuper != JSMN_NEG) { + parser->expected = JSMN_AFTR_COLON; +#if defined(JSMN_PERMISSIVE_RULESET) + } else { + parser->expected = JSMN_AFTR_COLON_R; +#endif + } + + if (tokens == NULL) { + return JSMN_SUCCESS; + } + +#if defined(JSMN_PERMISSIVE_RULESET) + tokens[parser->toknext - 1].type &= ~JSMN_VALUE; + tokens[parser->toknext - 1].type |= JSMN_KEY; +#endif + + parser->toksuper = parser->toknext - 1; + + return JSMN_SUCCESS; +} + +static +jsmnint_t jsmn_parse_comma(jsmn_parser *parser, jsmntok_t *tokens) +{ + /* If a COMMA wasn't expected */ + if (!(parser->expected & JSMN_COMMA)) { + return JSMN_ERROR_INVAL; + } + + jsmntype_t type = JSMN_UNDEFINED; /*!< parent's type */ + if (tokens == NULL) { + if (parser->toksuper < (sizeof(jsmnint_t) * CHAR_BIT) && + parser->toknext & (1 << parser->toksuper)) { + type = JSMN_OBJECT; + } + } else { + if (parser->toksuper != JSMN_NEG) { + type = tokens[parser->toksuper].type; + } + } + + if (parser->toksuper != JSMN_NEG) { + if (type & JSMN_OBJECT) { + parser->expected = JSMN_AFTR_COMMA_O; + } else { + parser->expected = JSMN_AFTR_COMMA_A; + } +#if defined(JSMN_PERMISSIVE_RULESET) + } else { + parser->expected = JSMN_AFTR_COMMA_R; #endif + } + + if (tokens == NULL) { + return JSMN_SUCCESS; + } + +#if defined(JSMN_PERMISSIVE_RULESET) + tokens[parser->toknext - 1].type |= JSMN_VALUE; +#endif + + return JSMN_SUCCESS; +} + +/** + * Parse JSON string and fill tokens. + */ +JSMN_API +jsmnint_t jsmn_parse(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) +{ + if (((jsmnint_t)-1 > 0 && len >= (jsmnint_t)JSMN_ERROR_MAX) || + len > JSMNINT_MAX) { + return JSMN_ERROR_LENGTH; + } + + jsmnint_t r; + + for (; parser->pos < len; parser->pos++) { +#if !defined(JSMN_MULTIPLE_JSON_FAIL) + if (parser->expected == JSMN_UNDEFINED) { break; - case '\"': - r = jsmn_parse_string(parser, js, len, tokens, num_tokens); - if (r < 0) { + } +#endif + char c = js[parser->pos]; + if (c == '{' || c == '[') { + r = jsmn_parse_container_open(parser, c, tokens, num_tokens); + if (r != JSMN_SUCCESS) { return r; } - count++; - if (parser->toksuper != -1 && tokens != NULL) { - tokens[parser->toksuper].size++; + continue; + } + + if (c == '}' || c == ']') { + r = jsmn_parse_container_close(parser, c, tokens); + if (r != JSMN_SUCCESS) { + return r; } - break; - case '\t': - case '\r': - case '\n': - case ' ': - break; - case ':': - parser->toksuper = parser->toknext - 1; - break; - case ',': - if (tokens != NULL && parser->toksuper != -1 && - tokens[parser->toksuper].type != JSMN_ARRAY && - tokens[parser->toksuper].type != JSMN_OBJECT) { -#ifdef JSMN_PARENT_LINKS - parser->toksuper = tokens[parser->toksuper].parent; -#else - for (i = parser->toknext - 1; i >= 0; i--) { - if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { - if (tokens[i].start != -1 && tokens[i].end == -1) { - parser->toksuper = i; - break; - } - } - } -#endif + continue; + } + + if (c == '"') { + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r != JSMN_SUCCESS) { + return r; } - break; -#ifdef JSMN_STRICT - /* In strict mode primitives are: numbers and booleans */ - case '-': - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case 't': - case 'f': - case 'n': - /* And they must not be keys of the object */ - if (tokens != NULL && parser->toksuper != -1) { - const jsmntok_t *t = &tokens[parser->toksuper]; - if (t->type == JSMN_OBJECT || - (t->type == JSMN_STRING && t->size != 0)) { - return JSMN_ERROR_INVAL; - } + continue; + } + + if (c == ':') { + r = jsmn_parse_colon(parser, tokens); + if (r != JSMN_SUCCESS) { + return r; + } + continue; + } + + if (c == ',') { + r = jsmn_parse_comma(parser, tokens); + if (r != JSMN_SUCCESS) { + return r; } + continue; + } + + /* Valid whitespace */ + if (isWhitespace(c)) { + continue; + } + +#if !defined(JSMN_PERMISSIVE_PRIMITIVE) + /* rfc8259: PRIMITIVEs are numbers and booleans */ + if (c == '-' || (c >= '0' && c <= '9') || + c == 'n' || c == 't' || c == 'f') { #else - /* In non-strict mode every unquoted value is a primitive */ - default: + /* In permissive mode every unquoted value is a PRIMITIVE */ + if (isCharacter(c) || c == '\\') { #endif r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); - if (r < 0) { + if (r != JSMN_SUCCESS) { return r; } - count++; - if (parser->toksuper != -1 && tokens != NULL) { - tokens[parser->toksuper].size++; - } - break; - -#ifdef JSMN_STRICT - /* Unexpected char in strict mode */ - default: - return JSMN_ERROR_INVAL; -#endif + continue; } + + /* Unexpected char */ + return JSMN_ERROR_INVAL; } - if (tokens != NULL) { - for (i = parser->toknext - 1; i >= 0; i--) { - /* Unmatched opened object or array */ - if (tokens[i].start != -1 && tokens[i].end == -1) { - return JSMN_ERROR_PART; - } - } + if (parser->toksuper != JSMN_NEG) { + return JSMN_ERROR_PART; + } + + if (parser->count == 0) { + return JSMN_ERROR_INVAL; + } + + while (parser->pos < len && isWhitespace(js[parser->pos])) { + parser->pos++; } - return count; + return parser->count; } /** * Creates a new parser based over a given buffer with an array of tokens * available. */ -JSMN_API void jsmn_init(jsmn_parser *parser) { +JSMN_API +void jsmn_init(jsmn_parser *parser) +{ parser->pos = 0; parser->toknext = 0; - parser->toksuper = -1; + parser->toksuper = JSMN_NEG; + parser->count = 0; + parser->expected = JSMN_ROOT_INIT; } #endif /* JSMN_HEADER */ @@ -468,4 +1064,4 @@ JSMN_API void jsmn_init(jsmn_parser *parser) { } #endif -#endif /* JSMN_H */ +#endif /* JSMN_H_ */ diff --git a/jsmn_defines.h b/jsmn_defines.h new file mode 100644 index 00000000..1bdee4dd --- /dev/null +++ b/jsmn_defines.h @@ -0,0 +1,159 @@ +#ifndef JSMN_DEFINES_H_ +#define JSMN_DEFINES_H_ + +/*! + * If nothing is defined, the default definitions are JSMN_PARENT_LINKS and * + * JSMN_NEXT_SIBLING with a jsmntok_t field size of 4 bytes (unsigned int). * + * This will parse one json object in a buffer at a time and return after a * + * successful json object parse. To check if there is more data in the * + * buffer that hasn't been parsed, run jsmn_eof. !*/ + +/*! @def JSMN_PARENT_LINKS + * @brief Adds a parent field to the token + * + * This decreases the initial time required to parse a json buffer and + * simplifies the post-processing of token array by adding a link to the id of + * a token's parent. + * This is enabled by default and highly recommended. + */ + +/*! @def JSMN_NEXT_SIBLING + * @brief Adds a next_sibling field to the token + * + * This simplifies the post-processing of token array by adding a link to the id + * of a token's next sibling. + * This is enabled by default and highly recommended. + */ + +/*! @def JSMN_UTF8 + * @brief Add UTF-8 functionality + * + * This allows for stricter parsing of json strings and also allows for the + * conversion of escaped characters (\uXXXX) to UTF-8 and back. + */ + +/*! @def JSMN_LOW_MEMORY + * @brief Enables defintions that reduce jsmn's memory footprint for small + * devices and doesn't enable definitions that increase it's footprint. + * + * This enables definitions that reduce jsmn's memory footprint at the cost of + * CPU usage. This is useful for small devices that don't parse json objects + * often and have restrictive memory requirements. + */ + +/*! @def JSMN_SHORT_TOKENS + * @brief Changes the tokens field size from a uint32_t to a uint16_t + * + * This reduces the jsmntok_t size by half by changing jsmntok_t field sizes + * from an unsigned int to an unsigned short. NOTE: This reduces the maximum + * possible json string length from 4,294,967,295 to 65,535 minus the size of + * jsmnerr (JSMN_ERROR_MAX). + */ + +/*! @def JSMN_PERMISSIVE + * @brief Enables all PERMISSIVE definitions + * + * Enables JSMN_PERMISSIVE_RULESET, JSMN_PERMISSIVE_KEY, + * JSMN_PERMISSIVE_PRIMITIVE, JSMN_PERMISSIVE_LITERALS, + * JSMN_PERMISSIVE_UTF32, and JSMN_MULTIPLE_JSON + */ + +/*! @def JSMN_PERMISSIVE_RULESET + * @brief Enables the PERMISSIVE set of rules + */ + +/*! @def JSMN_PERMISSIVE_KEY + * @brief Allows PRIMITIVEs to be OBJECT KEYs + */ + +/*! @def JSMN_PERMISSIVE_PRIMITIVE + * @brief Allows PRIMITIVEs to be any contiguous value + * + * This allows PRIMIVITEs to be any contiguous value that does not contain a + * character that has a special meaning to json (`{}[]",:`). NOTE: There is no + * validation of JSMN_PRI_LITERAL, JSMN_PRI_MINUS, JSNM_PRI_DECIMAL, or + * JSMN_PRI_EXPONENT; everything is the base type JSMN_PRIMITIVE. + */ + +/*! @def JSMN_PERMISSIVE_LITERALS + * @brief Extends the PRIMITIVE literals to include 'NaN' and 'Infinity' + */ + +/*! @def JSMN_PERMISSIVE_UTF32 + * @brief Allows escaped characters in the style \Uhhhhhhhh + */ + +/*! @def JSMN_MULTIPLE_JSON + * @brief Allows multiple json objects in a complete buffer + * + * This allows jsmn to parse multiple json objects in a single buffer. + * NOTE: If a single json object is malformed jsmn_parse will return with + * an error. + */ + +/*! @def JSMN_MULTIPLE_JSON_FAIL + * @brief Fails if there is more than one json object in a buffer. + */ + +#if !defined(JSMN_API) +# if defined(JSMN_STATIC) +# define JSMN_API static +# else +# define JSMN_API extern +# endif +#endif + +#if !defined(JSMN_LOW_MEMORY) + +# if !defined(JSMN_PARENT_LINKS) +# define JSMN_PARENT_LINKS +# endif +# if !defined(JSMN_NEXT_SIBLING) +# define JSMN_NEXT_SIBLING +# endif + +#else + +# if !defined(JSMN_SHORT_TOKENS) +# define JSMN_SHORT_TOKENS +# endif + +#endif + +#if defined(JSMN_PERMISSIVE) +# if !defined(JSMN_PERMISSIVE_RULESET) +# define JSMN_PERMISSIVE_RULESET +# endif +# if !defined(JSMN_PERMISSIVE_KEY) +# define JSMN_PERMISSIVE_KEY +# endif +# if !defined(JSMN_PERMISSIVE_PRIMITIVE) +# define JSMN_PERMISSIVE_PRIMITIVE +# endif +# if !defined(JSMN_PERMISSIVE_LITERALS) +# define JSMN_PERMISSIVE_LITERALS +# endif +# if !defined(JSMN_PERMISSIVE_UTF32) +# define JSMN_PERMISSIVE_UTF32 +# endif +# if !defined(JSMN_MULTIPLE_JSON) +# define JSMN_MULTIPLE_JSON +# endif +#endif + +#if defined(JSMN_MULTIPLE_JSON_FAIL) +# undef JSMN_MULTIPLE_JSON +#endif + +#if (defined(__linux__) || defined(__APPLE__) || defined(ARDUINO)) +# define JSMN_EXPORT __attribute__((visibility("default"))) +# define JSMN_LOCAL __attribute__((visibility("hidden"))) +#elif (defined(_WIN32)) +# define JSMN_EXPORT __declspec(dllexport) +# define JSMN_LOCAL +#else +# define JSMN_EXPORT +# define JSMN_LOCAL +#endif + +#endif /* JSMN_DEFINES_H_ */ diff --git a/jsmn_utils.c b/jsmn_utils.c new file mode 100644 index 00000000..3218b6e9 --- /dev/null +++ b/jsmn_utils.c @@ -0,0 +1,299 @@ +#include "jsmn_utils.h" + +#ifdef UNIT_TESTING +#include +#include +#endif + +#include +#include +#include +#include +#include + +JSMN_EXPORT +const char *jsmn_strerror(jsmnerr errno) +{ + switch (errno) { + case JSMN_SUCCESS: + return "*** Success, should not be printing an error. ***"; + case JSMN_ERROR_NOMEM: + return "Not enough tokens were provided."; + case JSMN_ERROR_LENGTH: + return "Input data too long."; + case JSMN_ERROR_INVAL: + return "Invalid character inside JSON string."; + case JSMN_ERROR_PART: + return "The string is not a full JSON packet, more bytes expected."; + case JSMN_ERROR_BRACKETS: + return "The JSON string has unmatched brackets."; + } + + return NULL; +} + +JSMN_EXPORT +jsmntok_t *jsmn_tokenize(const char *json, const size_t json_len, jsmnint_t *rv) +{ + jsmn_parser p; + + jsmn_init(&p); + *rv = jsmn_parse(&p, json, json_len, NULL, 0); + + /* enum jsmnerr has four errors, thus */ + if (*rv >= (jsmnint_t)JSMN_ERROR_MAX) { + fprintf(stderr, "jsmn_parse error: %s\n", jsmn_strerror(*rv)); + return NULL; + } + +/* fprintf(stderr, "jsmn_parse: %d tokens found.\n", *rv); */ + + jsmntok_t *tokens = calloc(*rv, sizeof(jsmntok_t)); + + jsmn_init(&p); + *rv = jsmn_parse(&p, json, json_len, tokens, *rv); + + return tokens; +} + +JSMN_EXPORT +jsmnint_t jsmn_tokenize_noalloc(jsmntok_t *tokens, const uint32_t num_tokens, const char *json, const size_t json_len) +{ + jsmn_parser p; + jsmn_init(&p); + + jsmnint_t rv; + + rv = jsmn_parse(&p, json, json_len, tokens, num_tokens); + + /* enum jsmnerr has four errors, thus */ + if (rv >= (jsmnint_t)JSMN_ERROR_MAX) { + fprintf(stderr, "jsmn_parse error: %s\n", jsmn_strerror(rv)); + return rv; + } + +/* fprintf(stderr, "jsmn_parse: %d tokens found.\n", rv); */ + + return rv; +} + +JSMN_EXPORT +int jsmn_streq(const char *json, const jsmntok_t *tok, const char *s) +{ + if ((tok->type & JSMN_STRING) && strlen(s) == tok->end - tok->start && + strncmp(json + tok->start, s, tok->end - tok->start) == 0) { + return JSMN_SUCCESS; + } + return -1; +} + +JSMN_EXPORT +jsmnint_t jsmn_get_prev_sibling(const jsmntok_t *tokens, const jsmnint_t t) +{ +#if defined(JSMN_NEXT_SIBLING) && defined(JSMN_PARENT_LINKS) + jsmnint_t sibling; + + /* Start with parent's first child */ + if (tokens[t].parent == JSMN_NEG) { + return JSMN_NEG; + } + + sibling = tokens[t].parent + 1; + + /* If the first child is the current token */ + if (sibling == t) { + return JSMN_NEG; + } + + /* Loop until we find previous sibling */ + while (tokens[sibling].next_sibling != t) { + sibling = tokens[sibling].next_sibling; + } + + return sibling; +#else + jsmnint_t remaining, sibling = t; + for (remaining = JSMN_NEG; remaining != 1 && sibling != JSMN_NEG; remaining++, sibling--) { + remaining -= tokens[sibling]->size; + } + return sibling; +#endif +} + +JSMN_EXPORT +jsmnint_t jsmn_get_next_sibling(const jsmntok_t *tokens, const jsmnint_t t) +{ +#if defined(JSMN_NEXT_SIBLING) + return tokens[t].next_sibling; +#else + jsmnint_t remaining, sibling = t; + for (remaining = 1; remaining != JSMN_NEG; remaining--, sibling++) { + remaining += tokens[sibling]->size; + } + return sibling; +#endif +} + +static +jsmnint_t jsmn_lookup_object(const char *json, const jsmntok_t *tokens, const jsmnint_t parent, const char *key) +{ + /* first child is the first token after the parent */ + jsmnint_t child = parent + 1; + + /* loop through children */ + while (child != JSMN_NEG) { + /* if child's string is equal to key */ + if (jsmn_streq(json, &tokens[child], key) == JSMN_SUCCESS) { + /* return current child */ + return child; + } + + /* move to the next child */ + child = jsmn_get_next_sibling(tokens, child); + } + + /* key didn't match any of the json keys */ + return JSMN_NEG; +} + +static +jsmnint_t jsmn_lookup_array(const jsmntok_t *tokens, const jsmnint_t parent, const jsmnint_t key) +{ + /* if parent's children is less than or equal to key, key is bad */ + if (tokens[parent].size <= key) + return JSMN_NEG; + + /* first child is the first token after the parent */ + jsmnint_t i, child = parent + 1; + /* loop through children until you reach the nth child */ + for (i = 0; i < key; i++) { + child = jsmn_get_next_sibling(tokens, child); + } + + /* return nth child */ + return child; +} + +JSMN_EXPORT +jsmnint_t jsmn_lookup(const char *json, const jsmntok_t *tokens, const size_t num_keys, ...) +{ + jsmnint_t i, pos; + + /* keys may be either const char * or jsmnint_t, at this point we don't care */ + va_list keys; + va_start(keys, num_keys); + + /* start at position zero */ + pos = 0; + for (i = 0; i < num_keys; i++) { + if (tokens[pos].type & JSMN_OBJECT) { + /* if `pos`.type is an object, treat key as a const char * */ + pos = jsmn_lookup_object(json, tokens, pos, va_arg(keys, void *)); + if (pos == JSMN_NEG) { break; } + /* move position to current key's value (with check) */ + if (tokens[pos].type & JSMN_KEY) { + pos++; + } + } else if (tokens[pos].type & JSMN_ARRAY) { + /* if `pos`.type is an array, treat key as a jsmnint_t (by way of uintptr_t) */ + pos = jsmn_lookup_array(tokens, pos, (uintptr_t)va_arg(keys, void *)); + } else { + /* `pos` must be either an object or array */ + pos = JSMN_NEG; + break; + } + + /* if jsmn_parse_{object,array} returns JSMN_NEG, break */ + if (pos == JSMN_NEG) { + break; + } + } + + va_end(keys); + return pos; +} + +JSMN_EXPORT +void jsmn_explodeJSON(const char *json, const size_t len) +{ + jsmnint_t rv, i, depth; + char c; + + jsmntok_t *tokens = jsmn_tokenize(json, len, &rv); + jsmntok_t *token; + + if (rv >= (jsmnint_t)-4) { + printf("jsmn_parse error: %s\n", jsmn_strerror(rv)); + return; + } + + const char *jsmntype[] = { "", "Object", "Array", "", "String", "", "", "", "Primitive", }; + const char *jsmnextr[] = { "", "Key ", "Value", "", "", "", "", "", "", }; + + printf("\n"); + printf(" Token | Type | Start | End | Length | Children | Parent | Sibling | K/V | \n"); + printf("----------+-----------+----------+----------+----------+----------+----------+----------+-------+-\n"); + for (i = 0, depth = 0; i < rv; i++) { + token = &tokens[i]; + printf( "%9d", i); + printf(" | %9s", jsmntype[token->type & JSMN_VAL_TYPE]); + printf(" | %8d", token->start); + printf(" | %8d", token->end); + printf(" | %8d", token->end - token->start); + printf(" | %8d", token->size); + printf(" | %8d", token->parent != JSMN_NEG ? token->parent : -1); + printf(" | %8d", token->next_sibling != JSMN_NEG ? token->next_sibling : -1); + printf(" | %5s", jsmnextr[(token->type & (JSMN_KEY | JSMN_VALUE)) >> 4]); + printf(" |"); + + if (token->type & JSMN_CONTAINER) { + printf("%*s %s\n", depth << 2, "", token->type & JSMN_OBJECT ? "{" : "["); + depth += 1; + continue; + } + + if (token->type & JSMN_KEY) { + c = (token->type & JSMN_STRING) ? '\"' : ' '; + printf("%*s%c%.*s%c :\n", depth << 2, "", c, token->end - token->start, &json[token->start], c); + continue; + } + + printf("%*s", depth << 2, ""); + if (token->type & JSMN_INSD_OBJ) + printf(" "); + c = (token->type & JSMN_STRING) ? '\"' : ' '; + printf("%c%.*s%c", c, token->end - token->start, &json[token->start], c); + if ((token->type & JSMN_INSD_OBJ && tokens[token->parent].next_sibling != JSMN_NEG) || + token->next_sibling != JSMN_NEG) { + printf(",\n"); + continue; + } + + printf("\n | | | | | | | | Close |"); + + depth -= 1; + if (depth == JSMN_NEG) { + printf(" %c\n", tokens[0].type == JSMN_OBJECT ? '}' : ']'); + continue; + } + + if (tokens[token->parent].type & JSMN_ARRAY) { + printf("%*s ]", depth << 2, ""); + if (tokens[token->parent].parent != 0 && + tokens[tokens[token->parent].parent].next_sibling != JSMN_NEG) { + printf(","); + } + printf("\n"); + } + else if (tokens[tokens[token->parent].parent].type & JSMN_OBJECT) { + printf("%*s }", depth << 2, ""); + if (tokens[tokens[token->parent].parent].parent != JSMN_NEG && + tokens[tokens[tokens[token->parent].parent].parent].next_sibling != JSMN_NEG) { + printf(","); + } + printf("\n"); + } + } + + free(tokens); +} diff --git a/jsmn_utils.h b/jsmn_utils.h new file mode 100644 index 00000000..1a942d2d --- /dev/null +++ b/jsmn_utils.h @@ -0,0 +1,94 @@ +#ifndef JSMN_UTILS_H_ +#define JSMN_UTILS_H_ + +#include +#include + +#include "jsmn_defines.h" + +#include "jsmn.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Return a pointer to jsmn error message + * + * @param[in] errno jsmn error number + * @return const char* jsmn error message + */ +const char *jsmn_strerror(jsmnerr errno); + +/** + * @brief Tokenizes JSON string + * + * @param[in] json JSON String + * @param[in] json_len Length of JSON String + * @param[out] rv Return Value + * @return Allocated jsmntok_t array pointer + */ +jsmntok_t *jsmn_tokenize(const char *json, const size_t json_len, jsmnint_t *rv); + +/** + * @brief Tokenize JSON string + * + * @param[out] tokens Pointer to preallocated Tokens + * @param[in] num_tokens Number of Tokens + * @param[in] json JSON String + * @param[in] json_len Length of JSON String + * @return Return Value + */ +jsmnint_t jsmn_tokenize_noalloc(jsmntok_t *tokens, const uint32_t num_tokens, const char *json, const size_t json_len); + +/** + * @brief String comparison between token and string + * + * @param[in] json JSON String + * @param[in] tok Token to compare + * @param[in] s String to complare + * @return 0 when token string and s are equal, -1 otherwise + */ +int jsmn_streq(const char *json, const jsmntok_t *tok, const char *s); + +/** + * @brief Find the previous sibling of token at position t + * + * @param[in] tokens jsmn tokens + * @param[in] t the position of the token + * @return jsmnint_t the position of t's previous sibling, else JSMN_NEG + */ +jsmnint_t jsmn_get_prev_sibling(const jsmntok_t *tokens, const jsmnint_t t); + +/** + * @brief Find the next sibling of token at position t + * + * @param[in] tokens jsmn tokens + * @param[in] t the position of the token + * @return jsmnint_t the position of t's next sibling, else JSMN_NEG + */ +jsmnint_t jsmn_get_next_sibling(const jsmntok_t *tokens, const jsmnint_t t); + +/** + * @brief Look for a value in a JSON string + * + * @param[in] json json string + * @param[in] tokens jsmn tokens + * @param[in] num_keys number of keys + * @return jsmnint_t position of value requested + */ +jsmnint_t jsmn_lookup(const char *json, const jsmntok_t *tokens, const size_t num_keys, ...); + +/** + * @brief Print an extremely verbose description of JSON string + * + * @param[in] json JSON String + * @param[in] len Length of JSON String + */ +void jsmn_explodeJSON(const char *json, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* JSMN_UTILS_H_ */ diff --git a/test/tests.c b/test/tests.c index d8a4d922..a39a5236 100644 --- a/test/tests.c +++ b/test/tests.c @@ -6,6 +6,1390 @@ #include "test.h" #include "testutil.h" +int test_jsmn_test_suite_i_(void) { + /* i_number_double_huge_neg_exp.json */ + check(parse("[123.456e-789]", 2, 2, + JSMN_ARRAY, 0, 14, 1, + JSMN_PRIMITIVE, "123.456e-789")); + + /* i_number_huge_exp.json */ + check(parse("[0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006]", 2, 2, + JSMN_ARRAY, 0, 137, 1, + JSMN_PRIMITIVE, "0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006")); + + /* i_number_neg_int_huge_exp.json */ + check(parse("[-1e+9999]", 2, 2, + JSMN_ARRAY, 0, 10, 1, + JSMN_PRIMITIVE, "-1e+9999")); + + /* i_number_pos_double_huge_exp.json */ + check(parse("[1.5e+9999]", 2, 2, + JSMN_ARRAY, 0, 11, 1, + JSMN_PRIMITIVE, "1.5e+9999")); + + /* i_number_real_neg_overflow.json */ + check(parse("[-123123e100000]", 2, 2, + JSMN_ARRAY, 0, 16, 1, + JSMN_PRIMITIVE, "-123123e100000")); + + /* i_number_real_pos_overflow.json */ + check(parse("[123123e100000]", 2, 2, + JSMN_ARRAY, 0, 15, 1, + JSMN_PRIMITIVE, "123123e100000")); + + /* i_number_real_underflow.json */ + check(parse("[123e-10000000]", 2, 2, + JSMN_ARRAY, 0, 15, 1, + JSMN_PRIMITIVE, "123e-10000000")); + + /* i_number_too_big_neg_int.json */ + check(parse("[-123123123123123123123123123123]", 2, 2, + JSMN_ARRAY, 0, 33, 1, + JSMN_PRIMITIVE, "-123123123123123123123123123123")); + + /* i_number_too_big_pos_int.json */ + check(parse("[100000000000000000000]", 2, 2, + JSMN_ARRAY, 0, 23, 1, + JSMN_PRIMITIVE, "100000000000000000000")); + + /* i_number_very_big_negative_int.json */ + check(parse("[-237462374673276894279832749832423479823246327846]", 2, 2, + JSMN_ARRAY, 0, 51, 1, + JSMN_PRIMITIVE, "-237462374673276894279832749832423479823246327846")); + + /* i_object_key_lone_2nd_surrogate.json */ + check(parse("{\"\\uDFAA\":0}", 3, 3, + JSMN_OBJECT, 0, 12, 1, + JSMN_STRING, "\\uDFAA", 1, + JSMN_PRIMITIVE, "0")); + + /* i_string_1st_surrogate_but_2nd_missing.json */ + check(parse("[\"\\uDADA\"]", 2, 2, + JSMN_ARRAY, 0, 10, 1, + JSMN_STRING, "\\uDADA", 0)); + + /* i_string_1st_valid_surrogate_2nd_invalid.json */ + check(parse("[\"\\uD888\\u1234\"]", 2, 2, + JSMN_ARRAY, 0, 16, 1, + JSMN_STRING, "\\uD888\\u1234", 0)); + + /* i_string_incomplete_surrogate_and_escape_valid.json */ + check(parse("[\"\\uD800\\n\"]", 2, 2, + JSMN_ARRAY, 0, 12, 1, + JSMN_STRING, "\\uD800\\n", 0)); + + /* i_string_incomplete_surrogate_pair.json */ + check(parse("[\"\\uDd1ea\"]", 2, 2, + JSMN_ARRAY, 0, 11, 1, + JSMN_STRING, "\\uDd1ea", 0)); + + /* i_string_incomplete_surrogates_escape_valid.json */ + check(parse("[\"\\uD800\\uD800\\n\"]", 2, 2, + JSMN_ARRAY, 0, 18, 1, + JSMN_STRING, "\\uD800\\uD800\\n", 0)); + + /* i_string_invalid_lonely_surrogate.json */ + check(parse("[\"\\ud800\"]", 2, 2, + JSMN_ARRAY, 0, 10, 1, + JSMN_STRING, "\\ud800", 0)); + + /* i_string_invalid_surrogate.json */ + check(parse("[\"\\ud800abc\"]", 2, 2, + JSMN_ARRAY, 0, 13, 1, + JSMN_STRING, "\\ud800abc", 0)); + + /* i_string_invalid_utf-8.json */ + check(parse("[\"\"]", 2, 2, + JSMN_ARRAY, 0, 5, 1, + JSMN_STRING, "", 0)); + + /* i_string_inverted_surrogates_U+1D11E.json */ + check(parse("[\"\\uDd1e\\uD834\"]", 2, 2, + JSMN_ARRAY, 0, 16, 1, + JSMN_STRING, "\\uDd1e\\uD834", 0)); + + /* i_string_iso_latin_1.json */ + check(parse("[\"\"]", 2, 2, + JSMN_ARRAY, 0, 5, 1, + JSMN_STRING, "", 0)); + + /* i_string_lone_second_surrogate.json */ + check(parse("[\"\\uDFAA\"]", 2, 2, + JSMN_ARRAY, 0, 10, 1, + JSMN_STRING, "\\uDFAA", 0)); + + /* i_string_lone_utf8_continuation_byte.json */ + check(parse("[\"\"]", 2, 2, + JSMN_ARRAY, 0, 5, 1, + JSMN_STRING, "", 0)); + + /* i_string_not_in_unicode_range.json */ + check(parse("[\"\"]", 2, 2, + JSMN_ARRAY, 0, 8, 1, + JSMN_STRING, "", 0)); + + /* i_string_overlong_sequence_2_bytes.json */ + check(parse("[\"\"]", 2, 2, + JSMN_ARRAY, 0, 6, 1, + JSMN_STRING, "", 0)); + + /* i_string_overlong_sequence_6_bytes.json */ + check(parse("[\"\"]", 2, 2, + JSMN_ARRAY, 0, 10, 1, + JSMN_STRING, "", 0)); + + /* i_string_overlong_sequence_6_bytes_null.json */ + check(parse("[\"\"]", 2, 2, + JSMN_ARRAY, 0, 10, 1, + JSMN_STRING, "", 0)); + + /* i_string_truncated-utf-8.json */ + check(parse("[\"\"]", 2, 2, + JSMN_ARRAY, 0, 6, 1, + JSMN_STRING, "", 0)); + + /* i_string_utf16BE_no_BOM.json failed to parse. */ + + /* i_string_utf16LE_no_BOM.json failed to parse. */ + + /* i_string_UTF-16LE_with_BOM.json failed to parse. */ + + /* i_string_UTF-8_invalid_sequence.json */ + check(parse("[\"日ш\"]", 2, 2, + JSMN_ARRAY, 0, 10, 1, + JSMN_STRING, "日ш", 0)); + + /* i_string_UTF8_surrogate_U+D800.json */ + check(parse("[\"\"]", 2, 2, + JSMN_ARRAY, 0, 7, 1, + JSMN_STRING, "", 0)); + + /* i_structure_500_nested_arrays.json */ + check(parse("[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]", 500, 500, + JSMN_ARRAY, 0,1000, 1, JSMN_ARRAY, 1, 999, 1, JSMN_ARRAY, 2, 998, 1, JSMN_ARRAY, 3, 997, 1, JSMN_ARRAY, 4, 996, 1, + JSMN_ARRAY, 5, 995, 1, JSMN_ARRAY, 6, 994, 1, JSMN_ARRAY, 7, 993, 1, JSMN_ARRAY, 8, 992, 1, JSMN_ARRAY, 9, 991, 1, + JSMN_ARRAY, 10, 990, 1, JSMN_ARRAY, 11, 989, 1, JSMN_ARRAY, 12, 988, 1, JSMN_ARRAY, 13, 987, 1, JSMN_ARRAY, 14, 986, 1, + JSMN_ARRAY, 15, 985, 1, JSMN_ARRAY, 16, 984, 1, JSMN_ARRAY, 17, 983, 1, JSMN_ARRAY, 18, 982, 1, JSMN_ARRAY, 19, 981, 1, + JSMN_ARRAY, 20, 980, 1, JSMN_ARRAY, 21, 979, 1, JSMN_ARRAY, 22, 978, 1, JSMN_ARRAY, 23, 977, 1, JSMN_ARRAY, 24, 976, 1, + JSMN_ARRAY, 25, 975, 1, JSMN_ARRAY, 26, 974, 1, JSMN_ARRAY, 27, 973, 1, JSMN_ARRAY, 28, 972, 1, JSMN_ARRAY, 29, 971, 1, + JSMN_ARRAY, 30, 970, 1, JSMN_ARRAY, 31, 969, 1, JSMN_ARRAY, 32, 968, 1, JSMN_ARRAY, 33, 967, 1, JSMN_ARRAY, 34, 966, 1, + JSMN_ARRAY, 35, 965, 1, JSMN_ARRAY, 36, 964, 1, JSMN_ARRAY, 37, 963, 1, JSMN_ARRAY, 38, 962, 1, JSMN_ARRAY, 39, 961, 1, + JSMN_ARRAY, 40, 960, 1, JSMN_ARRAY, 41, 959, 1, JSMN_ARRAY, 42, 958, 1, JSMN_ARRAY, 43, 957, 1, JSMN_ARRAY, 44, 956, 1, + JSMN_ARRAY, 45, 955, 1, JSMN_ARRAY, 46, 954, 1, JSMN_ARRAY, 47, 953, 1, JSMN_ARRAY, 48, 952, 1, JSMN_ARRAY, 49, 951, 1, + JSMN_ARRAY, 50, 950, 1, JSMN_ARRAY, 51, 949, 1, JSMN_ARRAY, 52, 948, 1, JSMN_ARRAY, 53, 947, 1, JSMN_ARRAY, 54, 946, 1, + JSMN_ARRAY, 55, 945, 1, JSMN_ARRAY, 56, 944, 1, JSMN_ARRAY, 57, 943, 1, JSMN_ARRAY, 58, 942, 1, JSMN_ARRAY, 59, 941, 1, + JSMN_ARRAY, 60, 940, 1, JSMN_ARRAY, 61, 939, 1, JSMN_ARRAY, 62, 938, 1, JSMN_ARRAY, 63, 937, 1, JSMN_ARRAY, 64, 936, 1, + JSMN_ARRAY, 65, 935, 1, JSMN_ARRAY, 66, 934, 1, JSMN_ARRAY, 67, 933, 1, JSMN_ARRAY, 68, 932, 1, JSMN_ARRAY, 69, 931, 1, + JSMN_ARRAY, 70, 930, 1, JSMN_ARRAY, 71, 929, 1, JSMN_ARRAY, 72, 928, 1, JSMN_ARRAY, 73, 927, 1, JSMN_ARRAY, 74, 926, 1, + JSMN_ARRAY, 75, 925, 1, JSMN_ARRAY, 76, 924, 1, JSMN_ARRAY, 77, 923, 1, JSMN_ARRAY, 78, 922, 1, JSMN_ARRAY, 79, 921, 1, + JSMN_ARRAY, 80, 920, 1, JSMN_ARRAY, 81, 919, 1, JSMN_ARRAY, 82, 918, 1, JSMN_ARRAY, 83, 917, 1, JSMN_ARRAY, 84, 916, 1, + JSMN_ARRAY, 85, 915, 1, JSMN_ARRAY, 86, 914, 1, JSMN_ARRAY, 87, 913, 1, JSMN_ARRAY, 88, 912, 1, JSMN_ARRAY, 89, 911, 1, + JSMN_ARRAY, 90, 910, 1, JSMN_ARRAY, 91, 909, 1, JSMN_ARRAY, 92, 908, 1, JSMN_ARRAY, 93, 907, 1, JSMN_ARRAY, 94, 906, 1, + JSMN_ARRAY, 95, 905, 1, JSMN_ARRAY, 96, 904, 1, JSMN_ARRAY, 97, 903, 1, JSMN_ARRAY, 98, 902, 1, JSMN_ARRAY, 99, 901, 1, + JSMN_ARRAY, 100, 900, 1, JSMN_ARRAY, 101, 899, 1, JSMN_ARRAY, 102, 898, 1, JSMN_ARRAY, 103, 897, 1, JSMN_ARRAY, 104, 896, 1, + JSMN_ARRAY, 105, 895, 1, JSMN_ARRAY, 106, 894, 1, JSMN_ARRAY, 107, 893, 1, JSMN_ARRAY, 108, 892, 1, JSMN_ARRAY, 109, 891, 1, + JSMN_ARRAY, 110, 890, 1, JSMN_ARRAY, 111, 889, 1, JSMN_ARRAY, 112, 888, 1, JSMN_ARRAY, 113, 887, 1, JSMN_ARRAY, 114, 886, 1, + JSMN_ARRAY, 115, 885, 1, JSMN_ARRAY, 116, 884, 1, JSMN_ARRAY, 117, 883, 1, JSMN_ARRAY, 118, 882, 1, JSMN_ARRAY, 119, 881, 1, + JSMN_ARRAY, 120, 880, 1, JSMN_ARRAY, 121, 879, 1, JSMN_ARRAY, 122, 878, 1, JSMN_ARRAY, 123, 877, 1, JSMN_ARRAY, 124, 876, 1, + JSMN_ARRAY, 125, 875, 1, JSMN_ARRAY, 126, 874, 1, JSMN_ARRAY, 127, 873, 1, JSMN_ARRAY, 128, 872, 1, JSMN_ARRAY, 129, 871, 1, + JSMN_ARRAY, 130, 870, 1, JSMN_ARRAY, 131, 869, 1, JSMN_ARRAY, 132, 868, 1, JSMN_ARRAY, 133, 867, 1, JSMN_ARRAY, 134, 866, 1, + JSMN_ARRAY, 135, 865, 1, JSMN_ARRAY, 136, 864, 1, JSMN_ARRAY, 137, 863, 1, JSMN_ARRAY, 138, 862, 1, JSMN_ARRAY, 139, 861, 1, + JSMN_ARRAY, 140, 860, 1, JSMN_ARRAY, 141, 859, 1, JSMN_ARRAY, 142, 858, 1, JSMN_ARRAY, 143, 857, 1, JSMN_ARRAY, 144, 856, 1, + JSMN_ARRAY, 145, 855, 1, JSMN_ARRAY, 146, 854, 1, JSMN_ARRAY, 147, 853, 1, JSMN_ARRAY, 148, 852, 1, JSMN_ARRAY, 149, 851, 1, + JSMN_ARRAY, 150, 850, 1, JSMN_ARRAY, 151, 849, 1, JSMN_ARRAY, 152, 848, 1, JSMN_ARRAY, 153, 847, 1, JSMN_ARRAY, 154, 846, 1, + JSMN_ARRAY, 155, 845, 1, JSMN_ARRAY, 156, 844, 1, JSMN_ARRAY, 157, 843, 1, JSMN_ARRAY, 158, 842, 1, JSMN_ARRAY, 159, 841, 1, + JSMN_ARRAY, 160, 840, 1, JSMN_ARRAY, 161, 839, 1, JSMN_ARRAY, 162, 838, 1, JSMN_ARRAY, 163, 837, 1, JSMN_ARRAY, 164, 836, 1, + JSMN_ARRAY, 165, 835, 1, JSMN_ARRAY, 166, 834, 1, JSMN_ARRAY, 167, 833, 1, JSMN_ARRAY, 168, 832, 1, JSMN_ARRAY, 169, 831, 1, + JSMN_ARRAY, 170, 830, 1, JSMN_ARRAY, 171, 829, 1, JSMN_ARRAY, 172, 828, 1, JSMN_ARRAY, 173, 827, 1, JSMN_ARRAY, 174, 826, 1, + JSMN_ARRAY, 175, 825, 1, JSMN_ARRAY, 176, 824, 1, JSMN_ARRAY, 177, 823, 1, JSMN_ARRAY, 178, 822, 1, JSMN_ARRAY, 179, 821, 1, + JSMN_ARRAY, 180, 820, 1, JSMN_ARRAY, 181, 819, 1, JSMN_ARRAY, 182, 818, 1, JSMN_ARRAY, 183, 817, 1, JSMN_ARRAY, 184, 816, 1, + JSMN_ARRAY, 185, 815, 1, JSMN_ARRAY, 186, 814, 1, JSMN_ARRAY, 187, 813, 1, JSMN_ARRAY, 188, 812, 1, JSMN_ARRAY, 189, 811, 1, + JSMN_ARRAY, 190, 810, 1, JSMN_ARRAY, 191, 809, 1, JSMN_ARRAY, 192, 808, 1, JSMN_ARRAY, 193, 807, 1, JSMN_ARRAY, 194, 806, 1, + JSMN_ARRAY, 195, 805, 1, JSMN_ARRAY, 196, 804, 1, JSMN_ARRAY, 197, 803, 1, JSMN_ARRAY, 198, 802, 1, JSMN_ARRAY, 199, 801, 1, + JSMN_ARRAY, 200, 800, 1, JSMN_ARRAY, 201, 799, 1, JSMN_ARRAY, 202, 798, 1, JSMN_ARRAY, 203, 797, 1, JSMN_ARRAY, 204, 796, 1, + JSMN_ARRAY, 205, 795, 1, JSMN_ARRAY, 206, 794, 1, JSMN_ARRAY, 207, 793, 1, JSMN_ARRAY, 208, 792, 1, JSMN_ARRAY, 209, 791, 1, + JSMN_ARRAY, 210, 790, 1, JSMN_ARRAY, 211, 789, 1, JSMN_ARRAY, 212, 788, 1, JSMN_ARRAY, 213, 787, 1, JSMN_ARRAY, 214, 786, 1, + JSMN_ARRAY, 215, 785, 1, JSMN_ARRAY, 216, 784, 1, JSMN_ARRAY, 217, 783, 1, JSMN_ARRAY, 218, 782, 1, JSMN_ARRAY, 219, 781, 1, + JSMN_ARRAY, 220, 780, 1, JSMN_ARRAY, 221, 779, 1, JSMN_ARRAY, 222, 778, 1, JSMN_ARRAY, 223, 777, 1, JSMN_ARRAY, 224, 776, 1, + JSMN_ARRAY, 225, 775, 1, JSMN_ARRAY, 226, 774, 1, JSMN_ARRAY, 227, 773, 1, JSMN_ARRAY, 228, 772, 1, JSMN_ARRAY, 229, 771, 1, + JSMN_ARRAY, 230, 770, 1, JSMN_ARRAY, 231, 769, 1, JSMN_ARRAY, 232, 768, 1, JSMN_ARRAY, 233, 767, 1, JSMN_ARRAY, 234, 766, 1, + JSMN_ARRAY, 235, 765, 1, JSMN_ARRAY, 236, 764, 1, JSMN_ARRAY, 237, 763, 1, JSMN_ARRAY, 238, 762, 1, JSMN_ARRAY, 239, 761, 1, + JSMN_ARRAY, 240, 760, 1, JSMN_ARRAY, 241, 759, 1, JSMN_ARRAY, 242, 758, 1, JSMN_ARRAY, 243, 757, 1, JSMN_ARRAY, 244, 756, 1, + JSMN_ARRAY, 245, 755, 1, JSMN_ARRAY, 246, 754, 1, JSMN_ARRAY, 247, 753, 1, JSMN_ARRAY, 248, 752, 1, JSMN_ARRAY, 249, 751, 1, + JSMN_ARRAY, 250, 750, 1, JSMN_ARRAY, 251, 749, 1, JSMN_ARRAY, 252, 748, 1, JSMN_ARRAY, 253, 747, 1, JSMN_ARRAY, 254, 746, 1, + JSMN_ARRAY, 255, 745, 1, JSMN_ARRAY, 256, 744, 1, JSMN_ARRAY, 257, 743, 1, JSMN_ARRAY, 258, 742, 1, JSMN_ARRAY, 259, 741, 1, + JSMN_ARRAY, 260, 740, 1, JSMN_ARRAY, 261, 739, 1, JSMN_ARRAY, 262, 738, 1, JSMN_ARRAY, 263, 737, 1, JSMN_ARRAY, 264, 736, 1, + JSMN_ARRAY, 265, 735, 1, JSMN_ARRAY, 266, 734, 1, JSMN_ARRAY, 267, 733, 1, JSMN_ARRAY, 268, 732, 1, JSMN_ARRAY, 269, 731, 1, + JSMN_ARRAY, 270, 730, 1, JSMN_ARRAY, 271, 729, 1, JSMN_ARRAY, 272, 728, 1, JSMN_ARRAY, 273, 727, 1, JSMN_ARRAY, 274, 726, 1, + JSMN_ARRAY, 275, 725, 1, JSMN_ARRAY, 276, 724, 1, JSMN_ARRAY, 277, 723, 1, JSMN_ARRAY, 278, 722, 1, JSMN_ARRAY, 279, 721, 1, + JSMN_ARRAY, 280, 720, 1, JSMN_ARRAY, 281, 719, 1, JSMN_ARRAY, 282, 718, 1, JSMN_ARRAY, 283, 717, 1, JSMN_ARRAY, 284, 716, 1, + JSMN_ARRAY, 285, 715, 1, JSMN_ARRAY, 286, 714, 1, JSMN_ARRAY, 287, 713, 1, JSMN_ARRAY, 288, 712, 1, JSMN_ARRAY, 289, 711, 1, + JSMN_ARRAY, 290, 710, 1, JSMN_ARRAY, 291, 709, 1, JSMN_ARRAY, 292, 708, 1, JSMN_ARRAY, 293, 707, 1, JSMN_ARRAY, 294, 706, 1, + JSMN_ARRAY, 295, 705, 1, JSMN_ARRAY, 296, 704, 1, JSMN_ARRAY, 297, 703, 1, JSMN_ARRAY, 298, 702, 1, JSMN_ARRAY, 299, 701, 1, + JSMN_ARRAY, 300, 700, 1, JSMN_ARRAY, 301, 699, 1, JSMN_ARRAY, 302, 698, 1, JSMN_ARRAY, 303, 697, 1, JSMN_ARRAY, 304, 696, 1, + JSMN_ARRAY, 305, 695, 1, JSMN_ARRAY, 306, 694, 1, JSMN_ARRAY, 307, 693, 1, JSMN_ARRAY, 308, 692, 1, JSMN_ARRAY, 309, 691, 1, + JSMN_ARRAY, 310, 690, 1, JSMN_ARRAY, 311, 689, 1, JSMN_ARRAY, 312, 688, 1, JSMN_ARRAY, 313, 687, 1, JSMN_ARRAY, 314, 686, 1, + JSMN_ARRAY, 315, 685, 1, JSMN_ARRAY, 316, 684, 1, JSMN_ARRAY, 317, 683, 1, JSMN_ARRAY, 318, 682, 1, JSMN_ARRAY, 319, 681, 1, + JSMN_ARRAY, 320, 680, 1, JSMN_ARRAY, 321, 679, 1, JSMN_ARRAY, 322, 678, 1, JSMN_ARRAY, 323, 677, 1, JSMN_ARRAY, 324, 676, 1, + JSMN_ARRAY, 325, 675, 1, JSMN_ARRAY, 326, 674, 1, JSMN_ARRAY, 327, 673, 1, JSMN_ARRAY, 328, 672, 1, JSMN_ARRAY, 329, 671, 1, + JSMN_ARRAY, 330, 670, 1, JSMN_ARRAY, 331, 669, 1, JSMN_ARRAY, 332, 668, 1, JSMN_ARRAY, 333, 667, 1, JSMN_ARRAY, 334, 666, 1, + JSMN_ARRAY, 335, 665, 1, JSMN_ARRAY, 336, 664, 1, JSMN_ARRAY, 337, 663, 1, JSMN_ARRAY, 338, 662, 1, JSMN_ARRAY, 339, 661, 1, + JSMN_ARRAY, 340, 660, 1, JSMN_ARRAY, 341, 659, 1, JSMN_ARRAY, 342, 658, 1, JSMN_ARRAY, 343, 657, 1, JSMN_ARRAY, 344, 656, 1, + JSMN_ARRAY, 345, 655, 1, JSMN_ARRAY, 346, 654, 1, JSMN_ARRAY, 347, 653, 1, JSMN_ARRAY, 348, 652, 1, JSMN_ARRAY, 349, 651, 1, + JSMN_ARRAY, 350, 650, 1, JSMN_ARRAY, 351, 649, 1, JSMN_ARRAY, 352, 648, 1, JSMN_ARRAY, 353, 647, 1, JSMN_ARRAY, 354, 646, 1, + JSMN_ARRAY, 355, 645, 1, JSMN_ARRAY, 356, 644, 1, JSMN_ARRAY, 357, 643, 1, JSMN_ARRAY, 358, 642, 1, JSMN_ARRAY, 359, 641, 1, + JSMN_ARRAY, 360, 640, 1, JSMN_ARRAY, 361, 639, 1, JSMN_ARRAY, 362, 638, 1, JSMN_ARRAY, 363, 637, 1, JSMN_ARRAY, 364, 636, 1, + JSMN_ARRAY, 365, 635, 1, JSMN_ARRAY, 366, 634, 1, JSMN_ARRAY, 367, 633, 1, JSMN_ARRAY, 368, 632, 1, JSMN_ARRAY, 369, 631, 1, + JSMN_ARRAY, 370, 630, 1, JSMN_ARRAY, 371, 629, 1, JSMN_ARRAY, 372, 628, 1, JSMN_ARRAY, 373, 627, 1, JSMN_ARRAY, 374, 626, 1, + JSMN_ARRAY, 375, 625, 1, JSMN_ARRAY, 376, 624, 1, JSMN_ARRAY, 377, 623, 1, JSMN_ARRAY, 378, 622, 1, JSMN_ARRAY, 379, 621, 1, + JSMN_ARRAY, 380, 620, 1, JSMN_ARRAY, 381, 619, 1, JSMN_ARRAY, 382, 618, 1, JSMN_ARRAY, 383, 617, 1, JSMN_ARRAY, 384, 616, 1, + JSMN_ARRAY, 385, 615, 1, JSMN_ARRAY, 386, 614, 1, JSMN_ARRAY, 387, 613, 1, JSMN_ARRAY, 388, 612, 1, JSMN_ARRAY, 389, 611, 1, + JSMN_ARRAY, 390, 610, 1, JSMN_ARRAY, 391, 609, 1, JSMN_ARRAY, 392, 608, 1, JSMN_ARRAY, 393, 607, 1, JSMN_ARRAY, 394, 606, 1, + JSMN_ARRAY, 395, 605, 1, JSMN_ARRAY, 396, 604, 1, JSMN_ARRAY, 397, 603, 1, JSMN_ARRAY, 398, 602, 1, JSMN_ARRAY, 399, 601, 1, + JSMN_ARRAY, 400, 600, 1, JSMN_ARRAY, 401, 599, 1, JSMN_ARRAY, 402, 598, 1, JSMN_ARRAY, 403, 597, 1, JSMN_ARRAY, 404, 596, 1, + JSMN_ARRAY, 405, 595, 1, JSMN_ARRAY, 406, 594, 1, JSMN_ARRAY, 407, 593, 1, JSMN_ARRAY, 408, 592, 1, JSMN_ARRAY, 409, 591, 1, + JSMN_ARRAY, 410, 590, 1, JSMN_ARRAY, 411, 589, 1, JSMN_ARRAY, 412, 588, 1, JSMN_ARRAY, 413, 587, 1, JSMN_ARRAY, 414, 586, 1, + JSMN_ARRAY, 415, 585, 1, JSMN_ARRAY, 416, 584, 1, JSMN_ARRAY, 417, 583, 1, JSMN_ARRAY, 418, 582, 1, JSMN_ARRAY, 419, 581, 1, + JSMN_ARRAY, 420, 580, 1, JSMN_ARRAY, 421, 579, 1, JSMN_ARRAY, 422, 578, 1, JSMN_ARRAY, 423, 577, 1, JSMN_ARRAY, 424, 576, 1, + JSMN_ARRAY, 425, 575, 1, JSMN_ARRAY, 426, 574, 1, JSMN_ARRAY, 427, 573, 1, JSMN_ARRAY, 428, 572, 1, JSMN_ARRAY, 429, 571, 1, + JSMN_ARRAY, 430, 570, 1, JSMN_ARRAY, 431, 569, 1, JSMN_ARRAY, 432, 568, 1, JSMN_ARRAY, 433, 567, 1, JSMN_ARRAY, 434, 566, 1, + JSMN_ARRAY, 435, 565, 1, JSMN_ARRAY, 436, 564, 1, JSMN_ARRAY, 437, 563, 1, JSMN_ARRAY, 438, 562, 1, JSMN_ARRAY, 439, 561, 1, + JSMN_ARRAY, 440, 560, 1, JSMN_ARRAY, 441, 559, 1, JSMN_ARRAY, 442, 558, 1, JSMN_ARRAY, 443, 557, 1, JSMN_ARRAY, 444, 556, 1, + JSMN_ARRAY, 445, 555, 1, JSMN_ARRAY, 446, 554, 1, JSMN_ARRAY, 447, 553, 1, JSMN_ARRAY, 448, 552, 1, JSMN_ARRAY, 449, 551, 1, + JSMN_ARRAY, 450, 550, 1, JSMN_ARRAY, 451, 549, 1, JSMN_ARRAY, 452, 548, 1, JSMN_ARRAY, 453, 547, 1, JSMN_ARRAY, 454, 546, 1, + JSMN_ARRAY, 455, 545, 1, JSMN_ARRAY, 456, 544, 1, JSMN_ARRAY, 457, 543, 1, JSMN_ARRAY, 458, 542, 1, JSMN_ARRAY, 459, 541, 1, + JSMN_ARRAY, 460, 540, 1, JSMN_ARRAY, 461, 539, 1, JSMN_ARRAY, 462, 538, 1, JSMN_ARRAY, 463, 537, 1, JSMN_ARRAY, 464, 536, 1, + JSMN_ARRAY, 465, 535, 1, JSMN_ARRAY, 466, 534, 1, JSMN_ARRAY, 467, 533, 1, JSMN_ARRAY, 468, 532, 1, JSMN_ARRAY, 469, 531, 1, + JSMN_ARRAY, 470, 530, 1, JSMN_ARRAY, 471, 529, 1, JSMN_ARRAY, 472, 528, 1, JSMN_ARRAY, 473, 527, 1, JSMN_ARRAY, 474, 526, 1, + JSMN_ARRAY, 475, 525, 1, JSMN_ARRAY, 476, 524, 1, JSMN_ARRAY, 477, 523, 1, JSMN_ARRAY, 478, 522, 1, JSMN_ARRAY, 479, 521, 1, + JSMN_ARRAY, 480, 520, 1, JSMN_ARRAY, 481, 519, 1, JSMN_ARRAY, 482, 518, 1, JSMN_ARRAY, 483, 517, 1, JSMN_ARRAY, 484, 516, 1, + JSMN_ARRAY, 485, 515, 1, JSMN_ARRAY, 486, 514, 1, JSMN_ARRAY, 487, 513, 1, JSMN_ARRAY, 488, 512, 1, JSMN_ARRAY, 489, 511, 1, + JSMN_ARRAY, 490, 510, 1, JSMN_ARRAY, 491, 509, 1, JSMN_ARRAY, 492, 508, 1, JSMN_ARRAY, 493, 507, 1, JSMN_ARRAY, 494, 506, 1, + JSMN_ARRAY, 495, 505, 1, JSMN_ARRAY, 496, 504, 1, JSMN_ARRAY, 497, 503, 1, JSMN_ARRAY, 498, 502, 1, JSMN_ARRAY, 499, 501, 0)); + + /* i_structure_UTF-8_BOM_empty_object.json failed to parse. */ + + return 0; +} + +int test_jsmn_test_suite_n_(void) { +#ifndef JSMN_PERMISSIVE + /* n_array_1_true_without_comma.json */ + check(query("[1 true]", JSMN_ERROR_INVAL)); + + /* n_array_a_invalid_utf8.json */ + check(query("[a]", JSMN_ERROR_INVAL)); + + /* n_array_colon_instead_of_comma.json */ + check(query("[\"\": 1]", JSMN_ERROR_INVAL)); + + /* n_array_comma_after_close.json */ +#if defined(JSMN_MULTIPLE_JSON) || defined(JSMN_MULTIPLE_JSON_FAIL) + check(query("[\"\"],", JSMN_ERROR_INVAL)); +#else + check(query("[\"\"],", 2)); +#endif + + /* n_array_comma_and_number.json */ + check(query("[,1]", JSMN_ERROR_INVAL)); + + /* n_array_double_comma.json */ + check(query("[1,,2]", JSMN_ERROR_INVAL)); + + /* n_array_double_extra_comma.json */ + check(query("[\"x\",,]", JSMN_ERROR_INVAL)); + + /* n_array_extra_close.json */ +#if defined(JSMN_MULTIPLE_JSON) || defined(JSMN_MULTIPLE_JSON_FAIL) + check(query("[\"x\"]]", JSMN_ERROR_INVAL)); +#else + check(query("[\"x\"]]", 2)); +#endif + + /* n_array_extra_comma.json */ + check(query("[\"\",]", JSMN_ERROR_INVAL)); + + /* n_array_incomplete_invalid_value.json */ + check(query("[x", JSMN_ERROR_INVAL)); + + /* n_array_incomplete.json */ + check(query("[\"x\"", JSMN_ERROR_PART)); + + /* n_array_inner_array_no_comma.json */ + check(query("[3[4]]", JSMN_ERROR_INVAL)); + + /* n_array_invalid_utf8.json */ + check(query("[]", JSMN_ERROR_INVAL)); + + /* n_array_items_separated_by_semicolon.json */ + check(query("[1:2]", JSMN_ERROR_INVAL)); + + /* n_array_just_comma.json */ + check(query("[,]", JSMN_ERROR_INVAL)); + + /* n_array_just_minus.json */ + check(query("[-]", JSMN_ERROR_INVAL)); + + /* n_array_missing_value.json */ + check(query("[ , \"\"]", JSMN_ERROR_INVAL)); + + /* n_array_newlines_unclosed.json */ + check(query("[\"a\",\n4\n,1,", JSMN_ERROR_PART)); + + /* n_array_number_and_comma.json */ + check(query("[1,]", JSMN_ERROR_INVAL)); + + /* n_array_number_and_several_commas.json */ + check(query("[1,,]", JSMN_ERROR_INVAL)); + + /* n_array_spaces_vertical_tab_formfeed.json */ + check(query("[\" a\"\f]", JSMN_ERROR_INVAL)); + + /* n_array_star_inside.json */ + check(query("[*]", JSMN_ERROR_INVAL)); + + /* n_array_unclosed.json */ + check(query("[\"\"", JSMN_ERROR_PART)); + + /* n_array_unclosed_trailing_comma.json */ + check(query("[1,", JSMN_ERROR_PART)); + + /* n_array_unclosed_with_new_lines.json */ + check(query("[1,\n1\n,1", JSMN_ERROR_PART)); + + /* n_array_unclosed_with_object_inside.json */ + check(query("[{}", JSMN_ERROR_PART)); + + /* n_incomplete_false.json */ + check(query("[fals]", JSMN_ERROR_INVAL)); + + /* n_incomplete_null.json */ + check(query("[nul]", JSMN_ERROR_INVAL)); + + /* n_incomplete_true.json */ + check(query("[tru]", JSMN_ERROR_INVAL)); + + /* n_multidigit_number_then_00.json has a null byte in it. */ + + /* n_number_0.1.2.json */ + check(query("[0.1.2]", JSMN_ERROR_INVAL)); + + /* n_number_-01.json */ + check(query("[-01]", JSMN_ERROR_INVAL)); + + /* n_number_0.3e+.json */ + check(query("[0.3e+]", JSMN_ERROR_INVAL)); + + /* n_number_0.3e.json */ + check(query("[0.3e]", JSMN_ERROR_INVAL)); + + /* n_number_0_capital_E+.json */ + check(query("[0E+]", JSMN_ERROR_INVAL)); + + /* n_number_0_capital_E.json */ + check(query("[0E]", JSMN_ERROR_INVAL)); + + /* n_number_0.e1.json */ + check(query("[0.e1]", JSMN_ERROR_INVAL)); + + /* n_number_0e+.json */ + check(query("[0e+]", JSMN_ERROR_INVAL)); + + /* n_number_0e.json */ + check(query("[0e]", JSMN_ERROR_INVAL)); + + /* n_number_1_000.json */ + check(query("[1 000.0]", JSMN_ERROR_INVAL)); + + /* n_number_1.0e+.json */ + check(query("[1.0e+]", JSMN_ERROR_INVAL)); + + /* n_number_1.0e-.json */ + check(query("[1.0e-]", JSMN_ERROR_INVAL)); + + /* n_number_1.0e.json */ + check(query("[1.0e]", JSMN_ERROR_INVAL)); + + /* n_number_-1.0..json */ + check(query("[-1.0.]", JSMN_ERROR_INVAL)); + + /* n_number_1eE2.json */ + check(query("[1eE2]", JSMN_ERROR_INVAL)); + + /* n_number_+1.json */ + check(query("[+1]", JSMN_ERROR_INVAL)); + + /* n_number_.-1.json */ + check(query("[.-1]", JSMN_ERROR_INVAL)); + + /* n_number_2.e+3.json */ + check(query("[2.e+3]", JSMN_ERROR_INVAL)); + + /* n_number_2.e-3.json */ + check(query("[2.e-3]", JSMN_ERROR_INVAL)); + + /* n_number_2.e3.json */ + check(query("[2.e3]", JSMN_ERROR_INVAL)); + + /* n_number_.2e-3.json */ + check(query("[.2e-3]", JSMN_ERROR_INVAL)); + + /* n_number_-2..json */ + check(query("[-2.]", JSMN_ERROR_INVAL)); + + /* n_number_9.e+.json */ + check(query("[9.e+]", JSMN_ERROR_INVAL)); + + /* n_number_expression.json */ + check(query("[1+2]", JSMN_ERROR_INVAL)); + + /* n_number_hex_1_digit.json */ + check(query("[0x1]", JSMN_ERROR_INVAL)); + + /* n_number_hex_2_digits.json */ + check(query("[0x42]", JSMN_ERROR_INVAL)); + + /* n_number_infinity.json */ + check(query("[Infinity]", JSMN_ERROR_INVAL)); + + /* n_number_+Inf.json */ + check(query("[+Inf]", JSMN_ERROR_INVAL)); + + /* n_number_Inf.json */ + check(query("[Inf]", JSMN_ERROR_INVAL)); + + /* n_number_invalid+-.json */ + check(query("[0e+-1]", JSMN_ERROR_INVAL)); + + /* n_number_invalid-negative-real.json */ + check(query("[-123.123foo]", JSMN_ERROR_INVAL)); + + /* n_number_invalid-utf-8-in-bigger-int.json */ + check(query("[123]", JSMN_ERROR_INVAL)); + + /* n_number_invalid-utf-8-in-exponent.json */ + check(query("[1e1]", JSMN_ERROR_INVAL)); + + /* n_number_invalid-utf-8-in-int.json */ + check(query("[0]", JSMN_ERROR_INVAL)); + + /* n_number_++.json */ + check(query("[++1234]", JSMN_ERROR_INVAL)); + + /* n_number_minus_infinity.json */ + check(query("[-Infinity]", JSMN_ERROR_INVAL)); + + /* n_number_minus_sign_with_trailing_garbage.json */ + check(query("[-foo]", JSMN_ERROR_INVAL)); + + /* n_number_minus_space_1.json */ + check(query("[- 1]", JSMN_ERROR_INVAL)); + + /* n_number_-NaN.json */ + check(query("[-NaN]", JSMN_ERROR_INVAL)); + + /* n_number_NaN.json */ + check(query("[NaN]", JSMN_ERROR_INVAL)); + + /* n_number_neg_int_starting_with_zero.json */ + check(query("[-012]", JSMN_ERROR_INVAL)); + + /* n_number_neg_real_without_int_part.json */ + check(query("[-.123]", JSMN_ERROR_INVAL)); + + /* n_number_neg_with_garbage_at_end.json */ + check(query("[-1x]", JSMN_ERROR_INVAL)); + + /* n_number_real_garbage_after_e.json */ + check(query("[1ea]", JSMN_ERROR_INVAL)); + + /* n_number_real_with_invalid_utf8_after_e.json */ + check(query("[1e]", JSMN_ERROR_INVAL)); + + /* n_number_real_without_fractional_part.json */ + check(query("[1.]", JSMN_ERROR_INVAL)); + + /* n_number_starting_with_dot.json */ + check(query("[.123]", JSMN_ERROR_INVAL)); + + /* n_number_U+FF11_fullwidth_digit_one.json */ + check(query("[1]", JSMN_ERROR_INVAL)); + + /* n_number_with_alpha_char.json */ + check(query("[1.8011670033376514H-308]", JSMN_ERROR_INVAL)); + + /* n_number_with_alpha.json */ + check(query("[1.2a-3]", JSMN_ERROR_INVAL)); + + /* n_number_with_leading_zero.json */ + check(query("[012]", JSMN_ERROR_INVAL)); + + /* n_object_bad_value.json */ + check(query("[\"x\", truth]", JSMN_ERROR_INVAL)); + + /* n_object_bracket_key.json */ + check(query("{[: \"x\"}", JSMN_ERROR_INVAL)); + + /* n_object_comma_instead_of_colon.json */ + check(query("{\"x\", null}", JSMN_ERROR_INVAL)); + + /* n_object_double_colon.json */ + check(query("{\"x\"::\"b\"}", JSMN_ERROR_INVAL)); + + /* n_object_emoji.json */ + check(query("{🇨🇭}", JSMN_ERROR_INVAL)); + + /* n_object_garbage_at_end.json */ + check(query("{\"a\":\"a\" 123}", JSMN_ERROR_INVAL)); + + /* n_object_key_with_single_quotes.json */ + check(query("{key: 'value'}", JSMN_ERROR_INVAL)); + + /* n_object_lone_continuation_byte_in_key_and_trailing_comma.json */ + check(query("{\"\":\"0\",}", JSMN_ERROR_INVAL)); + + /* n_object_missing_colon.json */ + check(query("{\"a\" b}", JSMN_ERROR_INVAL)); + + /* n_object_missing_key.json */ + check(query("{:\"b\"}", JSMN_ERROR_INVAL)); + + /* n_object_missing_semicolon.json */ + check(query("{\"a\" \"b\"}", JSMN_ERROR_INVAL)); + + /* n_object_missing_value.json */ + check(query("{\"a\":", JSMN_ERROR_PART)); + + /* n_object_no-colon.json */ + check(query("{\"a\"", JSMN_ERROR_PART)); + + /* n_object_non_string_key_but_huge_number_instead.json */ + check(query("{9999E9999:1}", JSMN_ERROR_INVAL)); + + /* n_object_non_string_key.json */ + check(query("{1:1}", JSMN_ERROR_INVAL)); + + /* n_object_repeated_null_null.json */ + check(query("{null:null,null:null}", JSMN_ERROR_INVAL)); + + /* n_object_several_trailing_commas.json */ + check(query("{\"id\":0,,,,,}", JSMN_ERROR_INVAL)); + + /* n_object_single_quote.json */ + check(query("{'a':0}", JSMN_ERROR_INVAL)); + + /* n_object_trailing_comma.json */ + check(query("{\"id\":0,}", JSMN_ERROR_INVAL)); + + /* n_object_trailing_comment.json */ +#if defined(JSMN_MULTIPLE_JSON) || defined(JSMN_MULTIPLE_JSON_FAIL) + check(query("{\"a\":\"b\"}/**/", JSMN_ERROR_INVAL)); +#else + check(query("{\"a\":\"b\"}/**/", 3)); +#endif + + /* n_object_trailing_comment_open.json */ +#if defined(JSMN_MULTIPLE_JSON) || defined(JSMN_MULTIPLE_JSON_FAIL) + check(query("{\"a\":\"b\"}/**//", JSMN_ERROR_INVAL)); +#else + check(query("{\"a\":\"b\"}/**//", 3)); +#endif + + /* n_object_trailing_comment_slash_open_incomplete.json */ +#if defined(JSMN_MULTIPLE_JSON) || defined(JSMN_MULTIPLE_JSON_FAIL) + check(query("{\"a\":\"b\"}/", JSMN_ERROR_INVAL)); +#else + check(query("{\"a\":\"b\"}/", 3)); +#endif + + /* n_object_trailing_comment_slash_open.json */ +#if defined(JSMN_MULTIPLE_JSON) || defined(JSMN_MULTIPLE_JSON_FAIL) + check(query("{\"a\":\"b\"}//", JSMN_ERROR_INVAL)); +#else + check(query("{\"a\":\"b\"}//", 3)); +#endif + + /* n_object_two_commas_in_a_row.json */ + check(query("{\"a\":\"b\",,\"c\":\"d\"}", JSMN_ERROR_INVAL)); + + /* n_object_unquoted_key.json */ + check(query("{a: \"b\"}", JSMN_ERROR_INVAL)); + + /* n_object_unterminated-value.json */ + check(query("{\"a\":\"a", JSMN_ERROR_PART)); + + /* n_object_with_single_string.json */ + check(query("{ \"foo\" : \"bar\", \"a\" }", JSMN_ERROR_INVAL)); + + /* n_object_with_trailing_garbage.json */ +#if defined(JSMN_MULTIPLE_JSON) || defined(JSMN_MULTIPLE_JSON_FAIL) + check(query("{\"a\":\"b\"}#", JSMN_ERROR_INVAL)); +#else + check(query("{\"a\":\"b\"}#", 3)); +#endif + + /* n_single_space.json */ + check(query(" ", JSMN_ERROR_INVAL)); + + /* n_string_1_surrogate_then_escape.json */ + check(query("[\"\\uD800\\\"]", JSMN_ERROR_PART)); + + /* n_string_1_surrogate_then_escape_u1.json */ + check(query("[\"\\uD800\\u1\"]", JSMN_ERROR_INVAL)); + + /* n_string_1_surrogate_then_escape_u1x.json */ + check(query("[\"\\uD800\\u1x\"]", JSMN_ERROR_INVAL)); + + /* n_string_1_surrogate_then_escape_u.json */ + check(query("[\"\\uD800\\u\"]", JSMN_ERROR_INVAL)); + + /* n_string_accentuated_char_no_quotes.json */ + check(query("[é]", JSMN_ERROR_INVAL)); + + /* n_string_backslash_00.json has a null byte in it. */ + + /* n_string_escaped_backslash_bad.json */ + check(query("[\"\\\\\\\"]", JSMN_ERROR_PART)); + + /* n_string_escaped_ctrl_char_tab.json has a null byte in it. */ + + /* n_string_escaped_emoji.json */ + check(query("[\"\\🌀\"]", JSMN_ERROR_INVAL)); + + /* n_string_escape_x.json */ + check(query("[\"\\x00\"]", JSMN_ERROR_INVAL)); + + /* n_string_incomplete_escaped_character.json */ + check(query("[\"\\u00A\"]", JSMN_ERROR_INVAL)); + + /* n_string_incomplete_escape.json */ + check(query("[\"\\\"]", JSMN_ERROR_PART)); + + /* n_string_incomplete_surrogate_escape_invalid.json */ + check(query("[\"\\uD800\\uD800\\x\"]", JSMN_ERROR_INVAL)); + + /* n_string_incomplete_surrogate.json */ + check(query("[\"\\uD834\\uDd\"]", JSMN_ERROR_INVAL)); + + /* n_string_invalid_backslash_esc.json */ + check(query("[\"\\a\"]", JSMN_ERROR_INVAL)); + + /* n_string_invalid_unicode_escape.json */ + check(query("[\"\\uqqqq\"]", JSMN_ERROR_INVAL)); + + /* n_string_invalid_utf8_after_escape.json */ + check(query("[\"\\\"]", JSMN_ERROR_INVAL)); + + /* n_string_invalid-utf-8-in-escape.json */ + check(query("[\"\\u\"]", JSMN_ERROR_INVAL)); + + /* n_string_leading_uescaped_thinspace.json */ + check(query("[\\u0020\"asd\"]", JSMN_ERROR_INVAL)); + + /* n_string_no_quotes_with_bad_escape.json */ + check(query("[\\n]", JSMN_ERROR_INVAL)); + + /* n_string_single_doublequote.json */ + check(query("\"", JSMN_ERROR_PART)); + + /* n_string_single_quote.json */ + check(query("['single quote']", JSMN_ERROR_INVAL)); + + /* n_string_single_string_no_double_quotes.json */ + check(query("abc", JSMN_ERROR_INVAL)); + + /* n_string_start_escape_unclosed.json */ + check(query("[\"\\", JSMN_ERROR_PART)); + + /* n_string_unescaped_crtl_char.json has a null byte in it. */ + + /* n_string_unescaped_newline.json */ + check(query("[\"new\nline\"]", JSMN_ERROR_INVAL)); + + /* n_string_unescaped_tab.json */ + check(query("[\"\t\"]", JSMN_ERROR_INVAL)); + + /* n_string_unicode_CapitalU.json */ + check(query("\"\\UA66D\"", JSMN_ERROR_INVAL)); + + /* n_string_with_trailing_garbage.json */ +#if defined(JSMN_MULTIPLE_JSON) || defined(JSMN_MULTIPLE_JSON_FAIL) + check(query("\"\"x", JSMN_ERROR_INVAL)); +#else + check(query("\"\"x", 1)); +#endif + + /* n_structure_angle_bracket_..json */ + check(query("<.>", JSMN_ERROR_INVAL)); + + /* n_structure_angle_bracket_null.json */ + check(query("[]", JSMN_ERROR_INVAL)); + + /* n_structure_array_trailing_garbage.json */ +#if defined(JSMN_MULTIPLE_JSON) || defined(JSMN_MULTIPLE_JSON_FAIL) + check(query("[1]x", JSMN_ERROR_INVAL)); +#else + check(query("[1]x", 2)); +#endif + + /* n_structure_array_with_extra_array_close.json */ +#if defined(JSMN_MULTIPLE_JSON) || defined(JSMN_MULTIPLE_JSON_FAIL) + check(query("[1]]", JSMN_ERROR_INVAL)); +#else + check(query("[1]]", 2)); +#endif + + /* n_structure_array_with_unclosed_string.json */ + check(query("[\"asd]", JSMN_ERROR_PART)); + + /* n_structure_ascii-unicode-identifier.json */ + check(query("aå", JSMN_ERROR_INVAL)); + + /* n_structure_capitalized_True.json */ + check(query("[True]", JSMN_ERROR_INVAL)); + + /* n_structure_close_unopened_array.json */ +#if defined(JSMN_MULTIPLE_JSON) || defined(JSMN_MULTIPLE_JSON_FAIL) + check(query("1]", JSMN_ERROR_INVAL)); +#else + check(query("1]", 1)); +#endif + + /* n_structure_comma_instead_of_closing_brace.json */ + check(query("{\"x\": true,", JSMN_ERROR_PART)); + + /* n_structure_double_array.json */ +#if defined(JSMN_MULTIPLE_JSON_FAIL) + check(query("[][]", JSMN_ERROR_INVAL)); +#elif defined(JSMN_MULTIPLE_JSON) + check(query("[][]", 2)); +#else + check(query("[][]", 1)); +#endif + + /* n_structure_end_array.json */ + check(query("]", JSMN_ERROR_INVAL)); + + /* n_structure_incomplete_UTF8_BOM.json */ + check(query("{}", JSMN_ERROR_INVAL)); + + /* n_structure_lone-invalid-utf-8.json */ + check(query("", JSMN_ERROR_INVAL)); + + /* n_structure_lone-open-bracket.json */ + check(query("[", JSMN_ERROR_PART)); + + /* n_structure_no_data.json */ + check(query("", JSMN_ERROR_INVAL)); + + /* n_structure_null-byte-outside-string.json has a null byte in it. */ + + /* n_structure_number_with_trailing_garbage.json */ + check(query("2@", JSMN_ERROR_INVAL)); + + /* n_structure_object_followed_by_closing_object.json */ +#if defined(JSMN_MULTIPLE_JSON) || defined(JSMN_MULTIPLE_JSON_FAIL) + check(query("{}}", JSMN_ERROR_INVAL)); +#else + check(query("{}}", 1)); +#endif + + /* n_structure_object_unclosed_no_value.json */ + check(query("{\"\":", JSMN_ERROR_PART)); + + /* n_structure_object_with_comment.json */ + check(query("{\"a\":/*comment*/\"b\"}", JSMN_ERROR_INVAL)); + + /* n_structure_object_with_trailing_garbage.json */ +#if defined(JSMN_MULTIPLE_JSON) || defined(JSMN_MULTIPLE_JSON_FAIL) + check(query("{\"a\": true} \"x\"", JSMN_ERROR_INVAL)); +#else + check(query("{\"a\": true} \"x\"", 3)); +#endif + + /* n_structure_open_array_apostrophe.json */ + check(query("['", JSMN_ERROR_INVAL)); + + /* n_structure_open_array_comma.json */ + check(query("[,", JSMN_ERROR_INVAL)); + + /* n_structure_open_array_object.json is too big. */ + + /* n_structure_open_array_open_object.json */ + check(query("[{", JSMN_ERROR_PART)); + + /* n_structure_open_array_open_string.json */ + check(query("[\"a", JSMN_ERROR_PART)); + + /* n_structure_open_array_string.json */ + check(query("[\"a\"", JSMN_ERROR_PART)); + + /* n_structure_open_object_close_array.json */ + check(query("{]", JSMN_ERROR_BRACKETS)); + + /* n_structure_open_object_comma.json */ + check(query("{,", JSMN_ERROR_INVAL)); + + /* n_structure_open_object.json */ + check(query("{", JSMN_ERROR_PART)); + + /* n_structure_open_object_open_array.json */ + check(query("{[", JSMN_ERROR_INVAL)); + + /* n_structure_open_object_open_string.json */ + check(query("{\"a", JSMN_ERROR_PART)); + + /* n_structure_open_object_string_with_apostrophes.json */ + check(query("{'a'", JSMN_ERROR_INVAL)); + + /* n_structure_open_open.json */ + check(query("[\"\\{[\"\\{[\"\\{[\"\\{", JSMN_ERROR_INVAL)); + + /* n_structure_single_eacute.json */ + check(query("", JSMN_ERROR_INVAL)); + + /* n_structure_single_star.json */ + check(query("*", JSMN_ERROR_INVAL)); + + /* n_structure_trailing_#.json */ +#if defined(JSMN_MULTIPLE_JSON) || defined(JSMN_MULTIPLE_JSON_FAIL) + check(query("{\"a\":\"b\"}#{}", JSMN_ERROR_INVAL)); +#else + check(query("{\"a\":\"b\"}#{}", 3)); +#endif + + /* n_structure_U+2060_word_joined.json */ + check(query("[⁠]", JSMN_ERROR_INVAL)); + + /* n_structure_uescaped_LF_before_string.json */ + check(query("[\\u000A\"\"]", JSMN_ERROR_INVAL)); + + /* n_structure_unclosed_array.json */ + check(query("[1", JSMN_ERROR_PART)); + + /* n_structure_unclosed_array_partial_null.json */ + check(query("[ false, nul", JSMN_ERROR_PART)); + + /* n_structure_unclosed_array_unfinished_false.json */ + check(query("[ true, fals", JSMN_ERROR_PART)); + + /* n_structure_unclosed_array_unfinished_true.json */ + check(query("[ false, tru", JSMN_ERROR_PART)); + + /* n_structure_unclosed_object.json */ + check(query("{\"asd\":\"asd\"", JSMN_ERROR_PART)); + + /* n_structure_unicode-identifier.json */ + check(query("å", JSMN_ERROR_INVAL)); + + /* n_structure_UTF8_BOM_no_data.json */ + check(query("", JSMN_ERROR_INVAL)); + + /* n_structure_whitespace_formfeed.json */ + check(query("[\f]", JSMN_ERROR_INVAL)); + + /* n_structure_whitespace_U+2060_word_joiner.json */ + check(query("[⁠]", JSMN_ERROR_INVAL)); + +#endif /* JSMN_PERMISSIVE */ + return 0; +} + +int test_jsmn_test_suite_y_(void) { + /* y_array_arraysWithSpaces.json */ + check(parse("[[] ]", 2, 2, + JSMN_ARRAY, 0, 7, 1, + JSMN_ARRAY, 1, 3, 0)); + + /* y_array_empty.json */ + check(parse("[]", 1, 1, + JSMN_ARRAY, 0, 2, 0)); + + /* y_array_empty-string.json */ + check(parse("[\"\"]", 2, 2, + JSMN_ARRAY, 0, 4, 1, + JSMN_STRING, "", 0)); + + /* y_array_false.json */ + check(parse("[false]", 2, 2, + JSMN_ARRAY, 0, 7, 1, + JSMN_PRIMITIVE, "false")); + + /* y_array_heterogeneous.json */ + check(parse("[null, 1, \"1\", {}]", 5, 5, + JSMN_ARRAY, 0, 18, 4, + JSMN_PRIMITIVE, "null", + JSMN_PRIMITIVE, "1", + JSMN_STRING, "1", 0, + JSMN_OBJECT, 15, 17, 0)); + + /* y_array_null.json */ + check(parse("[null]", 2, 2, + JSMN_ARRAY, 0, 6, 1, + JSMN_PRIMITIVE, "null")); + + /* y_array_with_1_and_newline.json */ + check(parse("[1\n]", 2, 2, + JSMN_ARRAY, 0, 4, 1, + JSMN_PRIMITIVE, "1")); + + /* y_array_with_leading_space.json */ + check(parse(" [1]", 2, 2, + JSMN_ARRAY, 1, 4, 1, + JSMN_PRIMITIVE, "1")); + + /* y_array_with_several_null.json */ + check(parse("[1,null,null,null,2]", 6, 6, + JSMN_ARRAY, 0, 20, 5, + JSMN_PRIMITIVE, "1", + JSMN_PRIMITIVE, "null", + JSMN_PRIMITIVE, "null", + JSMN_PRIMITIVE, "null", + JSMN_PRIMITIVE, "2")); + + /* y_array_with_trailing_space.json */ + check(parse("[2] ", 2, 2, + JSMN_ARRAY, 0, 3, 1, + JSMN_PRIMITIVE, "2")); + + /* y_number_0e+1.json */ + check(parse("[0e+1]", 2, 2, + JSMN_ARRAY, 0, 6, 1, + JSMN_PRIMITIVE, "0e+1")); + + /* y_number_0e1.json */ + check(parse("[0e1]", 2, 2, + JSMN_ARRAY, 0, 5, 1, + JSMN_PRIMITIVE, "0e1")); + + /* y_number_after_space.json */ + check(parse("[ 4]", 2, 2, + JSMN_ARRAY, 0, 4, 1, + JSMN_PRIMITIVE, "4")); + + /* y_number_double_close_to_zero.json */ + check(parse("[-0.000000000000000000000000000000000000000000000000000000000000000000000000000001]", 2, 2, + JSMN_ARRAY, 0, 83, 1, + JSMN_PRIMITIVE, "-0.000000000000000000000000000000000000000000000000000000000000000000000000000001")); + + /* y_number_int_with_exp.json */ + check(parse("[20e1]", 2, 2, + JSMN_ARRAY, 0, 6, 1, + JSMN_PRIMITIVE, "20e1")); + + /* y_number.json */ + check(parse("[123e65]", 2, 2, + JSMN_ARRAY, 0, 8, 1, + JSMN_PRIMITIVE, "123e65")); + + /* y_number_minus_zero.json */ + check(parse("[-0]", 2, 2, + JSMN_ARRAY, 0, 4, 1, + JSMN_PRIMITIVE, "-0")); + + /* y_number_negative_int.json */ + check(parse("[-123]", 2, 2, + JSMN_ARRAY, 0, 6, 1, + JSMN_PRIMITIVE, "-123")); + + /* y_number_negative_one.json */ + check(parse("[-1]", 2, 2, + JSMN_ARRAY, 0, 4, 1, + JSMN_PRIMITIVE, "-1")); + + /* y_number_negative_zero.json */ + check(parse("[-0]", 2, 2, + JSMN_ARRAY, 0, 4, 1, + JSMN_PRIMITIVE, "-0")); + + /* y_number_real_capital_e.json */ + check(parse("[1E22]", 2, 2, + JSMN_ARRAY, 0, 6, 1, + JSMN_PRIMITIVE, "1E22")); + + /* y_number_real_capital_e_neg_exp.json */ + check(parse("[1E-2]", 2, 2, + JSMN_ARRAY, 0, 6, 1, + JSMN_PRIMITIVE, "1E-2")); + + /* y_number_real_capital_e_pos_exp.json */ + check(parse("[1E+2]", 2, 2, + JSMN_ARRAY, 0, 6, 1, + JSMN_PRIMITIVE, "1E+2")); + + /* y_number_real_exponent.json */ + check(parse("[123e45]", 2, 2, + JSMN_ARRAY, 0, 8, 1, + JSMN_PRIMITIVE, "123e45")); + + /* y_number_real_fraction_exponent.json */ + check(parse("[123.456e78]", 2, 2, + JSMN_ARRAY, 0, 12, 1, + JSMN_PRIMITIVE, "123.456e78")); + + /* y_number_real_neg_exp.json */ + check(parse("[1e-2]", 2, 2, + JSMN_ARRAY, 0, 6, 1, + JSMN_PRIMITIVE, "1e-2")); + + /* y_number_real_pos_exponent.json */ + check(parse("[1e+2]", 2, 2, + JSMN_ARRAY, 0, 6, 1, + JSMN_PRIMITIVE, "1e+2")); + + /* y_number_simple_int.json */ + check(parse("[123]", 2, 2, + JSMN_ARRAY, 0, 5, 1, + JSMN_PRIMITIVE, "123")); + + /* y_number_simple_real.json */ + check(parse("[123.456789]", 2, 2, + JSMN_ARRAY, 0, 12, 1, + JSMN_PRIMITIVE, "123.456789")); + + /* y_object_basic.json */ + check(parse("{\"asd\":\"sdf\"}", 3, 3, + JSMN_OBJECT, 0, 13, 1, + JSMN_STRING, "asd", 1, + JSMN_STRING, "sdf", 0)); + + /* y_object_duplicated_key_and_value.json */ + check(parse("{\"a\":\"b\",\"a\":\"b\"}", 5, 5, + JSMN_OBJECT, 0, 17, 2, + JSMN_STRING, "a", 1, + JSMN_STRING, "b", 0, + JSMN_STRING, "a", 1, + JSMN_STRING, "b", 0)); + + /* y_object_duplicated_key.json */ + check(parse("{\"a\":\"b\",\"a\":\"c\"}", 5, 5, + JSMN_OBJECT, 0, 17, 2, + JSMN_STRING, "a", 1, + JSMN_STRING, "b", 0, + JSMN_STRING, "a", 1, + JSMN_STRING, "c", 0)); + + /* y_object_empty.json */ + check(parse("{}", 1, 1, + JSMN_OBJECT, 0, 2, 0)); + + /* y_object_empty_key.json */ + check(parse("{\"\":0}", 3, 3, + JSMN_OBJECT, 0, 6, 1, + JSMN_STRING, "", 1, + JSMN_PRIMITIVE, "0")); + + /* y_object_escaped_null_in_key.json */ + check(parse("{\"foo\\u0000bar\": 42}", 3, 3, + JSMN_OBJECT, 0, 20, 1, + JSMN_STRING, "foo\\u0000bar", 1, + JSMN_PRIMITIVE, "42")); + + /* y_object_extreme_numbers.json */ + check(parse("{ \"min\": -1.0e+28, \"max\": 1.0e+28 }", 5, 5, + JSMN_OBJECT, 0, 35, 2, + JSMN_STRING, "min", 1, + JSMN_PRIMITIVE, "-1.0e+28", + JSMN_STRING, "max", 1, + JSMN_PRIMITIVE, "1.0e+28")); + + /* y_object.json */ + check(parse("{\"asd\":\"sdf\", \"dfg\":\"fgh\"}", 5, 5, + JSMN_OBJECT, 0, 26, 2, + JSMN_STRING, "asd", 1, + JSMN_STRING, "sdf", 0, + JSMN_STRING, "dfg", 1, + JSMN_STRING, "fgh", 0)); + + /* y_object_long_strings.json */ + check(parse("{\"x\":[{\"id\": \"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"}], \"id\": \"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"}", 8, 8, + JSMN_OBJECT, 0, 108, 2, + JSMN_STRING, "x", 1, + JSMN_ARRAY, 5, 57, 1, + JSMN_OBJECT, 6, 56, 1, + JSMN_STRING, "id", 1, + JSMN_STRING, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 0, + JSMN_STRING, "id", 1, + JSMN_STRING, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 0)); + + /* y_object_simple.json */ + check(parse("{\"a\":[]}", 3, 3, + JSMN_OBJECT, 0, 8, 1, + JSMN_STRING, "a", 1, + JSMN_ARRAY, 5, 7, 0)); + + /* y_object_string_unicode.json */ + check(parse("{\"title\":\"\\u041f\\u043e\\u043b\\u0442\\u043e\\u0440\\u0430 \\u0417\\u0435\\u043c\\u043b\\u0435\\u043a\\u043e\\u043f\\u0430\" }", 3, 3, + JSMN_OBJECT, 0, 110, 1, + JSMN_STRING, "title", 1, + JSMN_STRING, "\\u041f\\u043e\\u043b\\u0442\\u043e\\u0440\\u0430 \\u0417\\u0435\\u043c\\u043b\\u0435\\u043a\\u043e\\u043f\\u0430", 0)); + + /* y_object_with_newlines.json */ + check(parse("{\n\"a\": \"b\"\n}", 3, 3, + JSMN_OBJECT, 0, 12, 1, + JSMN_STRING, "a", 1, + JSMN_STRING, "b", 0)); + + /* y_string_1_2_3_bytes_UTF-8_sequences.json */ + check(parse("[\"\\u0060\\u012a\\u12AB\"]", 2, 2, + JSMN_ARRAY, 0, 22, 1, + JSMN_STRING, "\\u0060\\u012a\\u12AB", 0)); + + /* y_string_accepted_surrogate_pair.json */ + check(parse("[\"\\uD801\\udc37\"]", 2, 2, + JSMN_ARRAY, 0, 16, 1, + JSMN_STRING, "\\uD801\\udc37", 0)); + + /* y_string_accepted_surrogate_pairs.json */ + check(parse("[\"\\ud83d\\ude39\\ud83d\\udc8d\"]", 2, 2, + JSMN_ARRAY, 0, 28, 1, + JSMN_STRING, "\\ud83d\\ude39\\ud83d\\udc8d", 0)); + + /* y_string_allowed_escapes.json */ + check(parse("[\"\\\"\\\\\\/\\b\\f\\n\\r\\t\"]", 2, 2, + JSMN_ARRAY, 0, 20, 1, + JSMN_STRING, "\\\"\\\\\\/\\b\\f\\n\\r\\t", 0)); + + /* y_string_backslash_and_u_escaped_zero.json */ + check(parse("[\"\\\\u0000\"]", 2, 2, + JSMN_ARRAY, 0, 11, 1, + JSMN_STRING, "\\\\u0000", 0)); + + /* y_string_backslash_doublequotes.json */ + check(parse("[\"\\\"\"]", 2, 2, + JSMN_ARRAY, 0, 6, 1, + JSMN_STRING, "\\\"", 0)); + + /* y_string_comments.json */ + check(parse("[\"a/*b*/c/*d//e\"]", 2, 2, + JSMN_ARRAY, 0, 17, 1, + JSMN_STRING, "a/*b*/c/*d//e", 0)); + + /* y_string_double_escape_a.json */ + check(parse("[\"\\\\a\"]", 2, 2, + JSMN_ARRAY, 0, 7, 1, + JSMN_STRING, "\\\\a", 0)); + + /* y_string_double_escape_n.json */ + check(parse("[\"\\\\n\"]", 2, 2, + JSMN_ARRAY, 0, 7, 1, + JSMN_STRING, "\\\\n", 0)); + + /* y_string_escaped_control_character.json */ + check(parse("[\"\\u0012\"]", 2, 2, + JSMN_ARRAY, 0, 10, 1, + JSMN_STRING, "\\u0012", 0)); + + /* y_string_escaped_noncharacter.json */ + check(parse("[\"\\uFFFF\"]", 2, 2, + JSMN_ARRAY, 0, 10, 1, + JSMN_STRING, "\\uFFFF", 0)); + + /* y_string_in_array.json */ + check(parse("[\"asd\"]", 2, 2, + JSMN_ARRAY, 0, 7, 1, + JSMN_STRING, "asd", 0)); + + /* y_string_in_array_with_leading_space.json */ + check(parse("[ \"asd\"]", 2, 2, + JSMN_ARRAY, 0, 8, 1, + JSMN_STRING, "asd", 0)); + + /* y_string_last_surrogates_1_and_2.json */ + check(parse("[\"\\uDBFF\\uDFFF\"]", 2, 2, + JSMN_ARRAY, 0, 16, 1, + JSMN_STRING, "\\uDBFF\\uDFFF", 0)); + + /* y_string_nbsp_uescaped.json */ + check(parse("[\"new\\u00A0line\"]", 2, 2, + JSMN_ARRAY, 0, 17, 1, + JSMN_STRING, "new\\u00A0line", 0)); + + /* y_string_nonCharacterInUTF-8_U+10FFFF.json */ + check(parse("[\"􏿿\"]", 2, 2, + JSMN_ARRAY, 0, 8, 1, + JSMN_STRING, "􏿿", 0)); + + /* y_string_nonCharacterInUTF-8_U+FFFF.json */ + check(parse("[\"￿\"]", 2, 2, + JSMN_ARRAY, 0, 7, 1, + JSMN_STRING, "￿", 0)); + + /* y_string_null_escape.json */ + check(parse("[\"\\u0000\"]", 2, 2, + JSMN_ARRAY, 0, 10, 1, + JSMN_STRING, "\\u0000", 0)); + + /* y_string_one-byte-utf-8.json */ + check(parse("[\"\\u002c\"]", 2, 2, + JSMN_ARRAY, 0, 10, 1, + JSMN_STRING, "\\u002c", 0)); + + /* y_string_pi.json */ + check(parse("[\"π\"]", 2, 2, + JSMN_ARRAY, 0, 6, 1, + JSMN_STRING, "π", 0)); + + /* y_string_reservedCharacterInUTF-8_U+1BFFF.json */ + check(parse("[\"𛿿\"]", 2, 2, + JSMN_ARRAY, 0, 8, 1, + JSMN_STRING, "𛿿", 0)); + + /* y_string_simple_ascii.json */ + check(parse("[\"asd \"]", 2, 2, + JSMN_ARRAY, 0, 8, 1, + JSMN_STRING, "asd ", 0)); + + /* y_string_space.json */ + check(parse("\" \"", 1, 1, + JSMN_STRING, " ", 0)); + + /* y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json */ + check(parse("[\"\\uD834\\uDd1e\"]", 2, 2, + JSMN_ARRAY, 0, 16, 1, + JSMN_STRING, "\\uD834\\uDd1e", 0)); + + /* y_string_three-byte-utf-8.json */ + check(parse("[\"\\u0821\"]", 2, 2, + JSMN_ARRAY, 0, 10, 1, + JSMN_STRING, "\\u0821", 0)); + + /* y_string_two-byte-utf-8.json */ + check(parse("[\"\\u0123\"]", 2, 2, + JSMN_ARRAY, 0, 10, 1, + JSMN_STRING, "\\u0123", 0)); + + /* y_string_u+2028_line_sep.json */ + check(parse("[\"
\"]", 2, 2, + JSMN_ARRAY, 0, 7, 1, + JSMN_STRING, "
", 0)); + + /* y_string_u+2029_par_sep.json */ + check(parse("[\"
\"]", 2, 2, + JSMN_ARRAY, 0, 7, 1, + JSMN_STRING, "
", 0)); + + /* y_string_uescaped_newline.json */ + check(parse("[\"new\\u000Aline\"]", 2, 2, + JSMN_ARRAY, 0, 17, 1, + JSMN_STRING, "new\\u000Aline", 0)); + + /* y_string_uEscape.json */ + check(parse("[\"\\u0061\\u30af\\u30EA\\u30b9\"]", 2, 2, + JSMN_ARRAY, 0, 28, 1, + JSMN_STRING, "\\u0061\\u30af\\u30EA\\u30b9", 0)); + + /* y_string_unescaped_char_delete.json */ + check(parse("[\"\"]", 2, 2, + JSMN_ARRAY, 0, 5, 1, + JSMN_STRING, "", 0)); + + /* y_string_unicode_2.json */ + check(parse("[\"⍂㈴⍂\"]", 2, 2, + JSMN_ARRAY, 0, 13, 1, + JSMN_STRING, "⍂㈴⍂", 0)); + + /* y_string_unicodeEscapedBackslash.json */ + check(parse("[\"\\u005C\"]", 2, 2, + JSMN_ARRAY, 0, 10, 1, + JSMN_STRING, "\\u005C", 0)); + + /* y_string_unicode_escaped_double_quote.json */ + check(parse("[\"\\u0022\"]", 2, 2, + JSMN_ARRAY, 0, 10, 1, + JSMN_STRING, "\\u0022", 0)); + + /* y_string_unicode.json */ + check(parse("[\"\\uA66D\"]", 2, 2, + JSMN_ARRAY, 0, 10, 1, + JSMN_STRING, "\\uA66D", 0)); + + /* y_string_unicode_U+10FFFE_nonchar.json */ + check(parse("[\"\\uDBFF\\uDFFE\"]", 2, 2, + JSMN_ARRAY, 0, 16, 1, + JSMN_STRING, "\\uDBFF\\uDFFE", 0)); + + /* y_string_unicode_U+1FFFE_nonchar.json */ + check(parse("[\"\\uD83F\\uDFFE\"]", 2, 2, + JSMN_ARRAY, 0, 16, 1, + JSMN_STRING, "\\uD83F\\uDFFE", 0)); + + /* y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json */ + check(parse("[\"\\u200B\"]", 2, 2, + JSMN_ARRAY, 0, 10, 1, + JSMN_STRING, "\\u200B", 0)); + + /* y_string_unicode_U+2064_invisible_plus.json */ + check(parse("[\"\\u2064\"]", 2, 2, + JSMN_ARRAY, 0, 10, 1, + JSMN_STRING, "\\u2064", 0)); + + /* y_string_unicode_U+FDD0_nonchar.json */ + check(parse("[\"\\uFDD0\"]", 2, 2, + JSMN_ARRAY, 0, 10, 1, + JSMN_STRING, "\\uFDD0", 0)); + + /* y_string_unicode_U+FFFE_nonchar.json */ + check(parse("[\"\\uFFFE\"]", 2, 2, + JSMN_ARRAY, 0, 10, 1, + JSMN_STRING, "\\uFFFE", 0)); + + /* y_string_utf8.json */ + check(parse("[\"€𝄞\"]", 2, 2, + JSMN_ARRAY, 0, 11, 1, + JSMN_STRING, "€𝄞", 0)); + + /* y_string_with_del_character.json */ + check(parse("[\"aa\"]", 2, 2, + JSMN_ARRAY, 0, 7, 1, + JSMN_STRING, "aa", 0)); + + /* y_structure_lonely_false.json */ + check(parse("false", 1, 1, + JSMN_PRIMITIVE, "false")); + + /* y_structure_lonely_int.json */ + check(parse("42", 1, 1, + JSMN_PRIMITIVE, "42")); + + /* y_structure_lonely_negative_real.json */ + check(parse("-0.1", 1, 1, + JSMN_PRIMITIVE, "-0.1")); + + /* y_structure_lonely_null.json */ + check(parse("null", 1, 1, + JSMN_PRIMITIVE, "null")); + + /* y_structure_lonely_string.json */ + check(parse("\"asd\"", 1, 1, + JSMN_STRING, "asd", 0)); + + /* y_structure_lonely_true.json */ + check(parse("true", 1, 1, + JSMN_PRIMITIVE, "true")); + + /* y_structure_string_empty.json */ + check(parse("\"\"", 1, 1, + JSMN_STRING, "", 0)); + + /* y_structure_trailing_newline.json */ + check(parse("[\"a\"]\n", 2, 2, + JSMN_ARRAY, 0, 5, 1, + JSMN_STRING, "a", 0)); + + /* y_structure_true_in_array.json */ + check(parse("[true]", 2, 2, + JSMN_ARRAY, 0, 6, 1, + JSMN_PRIMITIVE, "true")); + + /* y_structure_whitespace_array.json */ + check(parse(" [] ", 1, 1, + JSMN_ARRAY, 1, 3, 0)); + + return 0; +} + + int test_empty(void) { check(parse("{}", 1, 1, JSMN_OBJECT, 0, 2, 0)); check(parse("[]", 1, 1, JSMN_ARRAY, 0, 2, 0)); @@ -19,6 +1403,8 @@ int test_object(void) { JSMN_PRIMITIVE, "0")); check(parse("{\"a\":[]}", 3, 3, JSMN_OBJECT, 0, 8, 1, JSMN_STRING, "a", 1, JSMN_ARRAY, 5, 7, 0)); + check(parse("{\"a\":[1,2]}", 5, 5, JSMN_OBJECT, 0, 11, 1, JSMN_STRING, "a", 1, + JSMN_ARRAY, 5, 10, 2, JSMN_PRIMITIVE, "1", JSMN_PRIMITIVE, "2")); check(parse("{\"a\":{},\"b\":{}}", 5, 5, JSMN_OBJECT, -1, -1, 2, JSMN_STRING, "a", 1, JSMN_OBJECT, -1, -1, 0, JSMN_STRING, "b", 1, JSMN_OBJECT, -1, -1, 0)); @@ -30,31 +1416,37 @@ int test_object(void) { JSMN_STRING, "a", 1, JSMN_PRIMITIVE, "0", JSMN_STRING, "b", 1, JSMN_STRING, "c", 0)); -#ifdef JSMN_STRICT - check(parse("{\"a\"\n0}", JSMN_ERROR_INVAL, 3)); - check(parse("{\"a\", 0}", JSMN_ERROR_INVAL, 3)); - check(parse("{\"a\": {2}}", JSMN_ERROR_INVAL, 3)); - check(parse("{\"a\": {2: 3}}", JSMN_ERROR_INVAL, 3)); - check(parse("{\"a\": {\"a\": 2 3}}", JSMN_ERROR_INVAL, 5)); -/* FIXME */ -/*check(parse("{\"a\"}", JSMN_ERROR_INVAL, 2));*/ -/*check(parse("{\"a\": 1, \"b\"}", JSMN_ERROR_INVAL, 4));*/ -/*check(parse("{\"a\",\"b\":1}", JSMN_ERROR_INVAL, 4));*/ -/*check(parse("{\"a\":1,}", JSMN_ERROR_INVAL, 4));*/ -/*check(parse("{\"a\":\"b\":\"c\"}", JSMN_ERROR_INVAL, 4));*/ -/*check(parse("{,}", JSMN_ERROR_INVAL, 4));*/ +#ifndef JSMN_PERMISSIVE + check(query("{\"a\"\n0}", JSMN_ERROR_INVAL)); + check(query("{\"a\", 0}", JSMN_ERROR_INVAL)); + check(query("{\"a\": {2}}", JSMN_ERROR_INVAL)); + check(query("{\"a\": {2: 3}}", JSMN_ERROR_INVAL)); + check(query("{\"a\": {\"a\": 2 3}}", JSMN_ERROR_INVAL)); + check(query("{\"a\"}", JSMN_ERROR_INVAL)); + check(query("{\"a\": 1, \"b\"}", JSMN_ERROR_INVAL)); + check(query("{\"a\",\"b\":1}", JSMN_ERROR_INVAL)); + check(query("{\"a\":1,}", JSMN_ERROR_INVAL)); + check(query("{\"a\":\"b\":\"c\"}", JSMN_ERROR_INVAL)); + check(query("{,}", JSMN_ERROR_INVAL)); + + check(query("{\"a\":}", JSMN_ERROR_INVAL)); + check(query("{\"a\" \"b\"}", JSMN_ERROR_INVAL)); + check(query("{\"a\" ::::: \"b\"}", JSMN_ERROR_INVAL)); + check(query("{\"a\": [1 \"b\"]}", JSMN_ERROR_INVAL)); + check(query("{\"a\"\"\"}", JSMN_ERROR_INVAL)); + check(query("{\"a\":1\"\"}", JSMN_ERROR_INVAL)); + check(query("{\"a\":1\"b\":1}", JSMN_ERROR_INVAL)); + check(query("{\"a\":\"b\", \"c\":\"d\", {\"e\": \"f\"}}", JSMN_ERROR_INVAL)); #endif return 0; } int test_array(void) { - /* FIXME */ - /*check(parse("[10}", JSMN_ERROR_INVAL, 3));*/ - /*check(parse("[1,,3]", JSMN_ERROR_INVAL, 3)*/ + check(query("[10}", JSMN_ERROR_BRACKETS)); + check(query("[1,,3]", JSMN_ERROR_INVAL)); check(parse("[10]", 2, 2, JSMN_ARRAY, -1, -1, 1, JSMN_PRIMITIVE, "10")); - check(parse("{\"a\": 1]", JSMN_ERROR_INVAL, 3)); - /* FIXME */ - /*check(parse("[\"a\": 1]", JSMN_ERROR_INVAL, 3));*/ + check(query("{\"a\": 1]", JSMN_ERROR_BRACKETS)); + check(query("[\"a\": 1]", JSMN_ERROR_INVAL)); return 0; } @@ -90,20 +1482,21 @@ int test_string(void) { JSMN_STRING, "a", 1, JSMN_ARRAY, -1, -1, 1, JSMN_STRING, "\\u0280", 0)); - check(parse("{\"a\":\"str\\uFFGFstr\"}", JSMN_ERROR_INVAL, 3)); - check(parse("{\"a\":\"str\\u@FfF\"}", JSMN_ERROR_INVAL, 3)); - check(parse("{{\"a\":[\"\\u028\"]}", JSMN_ERROR_INVAL, 4)); + check(query("{\"a\":\"str\\uFFGFstr\"}", JSMN_ERROR_INVAL)); + check(query("{\"a\":\"str\\u@FfF\"}", JSMN_ERROR_INVAL)); + check(query("{\"a\":[\"\\u028\"]}", JSMN_ERROR_INVAL)); return 0; } int test_partial_string(void) { - int r; - unsigned long i; - jsmn_parser p; + jsmnint_t r; jsmntok_t tok[5]; + jsmn_parser p; + jsmn_init(&p); + const char *js = "{\"x\": \"va\\\\ue\", \"y\": \"value y\"}"; - jsmn_init(&p); + size_t i; for (i = 1; i <= strlen(js); i++) { r = jsmn_parse(&p, js, i, tok, sizeof(tok) / sizeof(tok[0])); if (i == strlen(js)) { @@ -112,21 +1505,22 @@ int test_partial_string(void) { JSMN_STRING, "va\\\\ue", 0, JSMN_STRING, "y", 1, JSMN_STRING, "value y", 0)); } else { - check(r == JSMN_ERROR_PART); + check(r == (jsmnint_t)JSMN_ERROR_PART); } } return 0; } int test_partial_array(void) { -#ifdef JSMN_STRICT - int r; - unsigned long i; - jsmn_parser p; +#ifndef JSMN_PERMISSIVE + jsmnint_t r; jsmntok_t tok[10]; + jsmn_parser p; + jsmn_init(&p); + const char *js = "[ 1, true, [123, \"hello\"]]"; - jsmn_init(&p); + size_t i; for (i = 1; i <= strlen(js); i++) { r = jsmn_parse(&p, js, i, tok, sizeof(tok) / sizeof(tok[0])); if (i == strlen(js)) { @@ -135,7 +1529,7 @@ int test_partial_array(void) { JSMN_PRIMITIVE, "true", JSMN_ARRAY, -1, -1, 2, JSMN_PRIMITIVE, "123", JSMN_STRING, "hello", 0)); } else { - check(r == JSMN_ERROR_PART); + check(r == (jsmnint_t)JSMN_ERROR_PART); } } #endif @@ -143,20 +1537,19 @@ int test_partial_array(void) { } int test_array_nomem(void) { - int i; - int r; - jsmn_parser p; + jsmnint_t r; jsmntok_t toksmall[10], toklarge[10]; - const char *js; + jsmn_parser p; - js = " [ 1, true, [123, \"hello\"]]"; + const char *js = " [ 1, true, [123, \"hello\"]]"; + size_t i; for (i = 0; i < 6; i++) { jsmn_init(&p); memset(toksmall, 0, sizeof(toksmall)); memset(toklarge, 0, sizeof(toklarge)); r = jsmn_parse(&p, js, strlen(js), toksmall, i); - check(r == JSMN_ERROR_NOMEM); + check(r == (jsmnint_t)JSMN_ERROR_NOMEM); memcpy(toklarge, toksmall, sizeof(toksmall)); @@ -170,14 +1563,13 @@ int test_array_nomem(void) { } int test_unquoted_keys(void) { -#ifndef JSMN_STRICT - int r; - jsmn_parser p; +#ifdef JSMN_PERMISSIVE + jsmnint_t r; jsmntok_t tok[10]; - const char *js; - + jsmn_parser p; jsmn_init(&p); - js = "key1: \"value\"\nkey2 : 123"; + + const char *js = "key1: \"value\"\nkey2 : 123"; r = jsmn_parse(&p, js, strlen(js), tok, 10); check(r >= 0); @@ -188,12 +1580,7 @@ int test_unquoted_keys(void) { } int test_issue_22(void) { - int r; - jsmn_parser p; - jsmntok_t tokens[128]; - const char *js; - - js = + const char *js = "{ \"height\":10, \"layers\":[ { \"data\":[6,6], \"height\":10, " "\"name\":\"Calque de Tile 1\", \"opacity\":1, \"type\":\"tilelayer\", " "\"visible\":true, \"width\":10, \"x\":0, \"y\":0 }], " @@ -204,28 +1591,34 @@ int test_issue_22(void) { "\"properties\":{}, \"spacing\":0, \"tileheight\":32, \"tilewidth\":32 " "}], " "\"tilewidth\":32, \"version\":1, \"width\":10 }"; - jsmn_init(&p); - r = jsmn_parse(&p, js, strlen(js), tokens, 128); - check(r >= 0); + check(query(js, 61)); return 0; } int test_issue_27(void) { const char *js = "{ \"name\" : \"Jack\", \"age\" : 27 } { \"name\" : \"Anna\", "; - check(parse(js, JSMN_ERROR_PART, 8)); +#ifndef JSMN_MULTIPLE_JSON + check(parse(js, 5, 5, + JSMN_OBJECT, -1, -1, 2, + JSMN_STRING, "name", 1, + JSMN_STRING, "Jack", 0, + JSMN_STRING, "age", 1, + JSMN_PRIMITIVE, "27")); +#else + check(query(js, JSMN_ERROR_PART)); +#endif return 0; } int test_input_length(void) { - const char *js; - int r; - jsmn_parser p; + jsmnint_t r; jsmntok_t tokens[10]; + jsmn_parser p; + jsmn_init(&p); - js = "{\"a\": 0}garbage"; + const char *js = "{\"a\": 0}garbage"; - jsmn_init(&p); r = jsmn_parse(&p, js, 8, tokens, 10); check(r == 3); check(tokeq(js, tokens, 3, JSMN_OBJECT, -1, -1, 1, JSMN_STRING, "a", 1, @@ -234,54 +1627,23 @@ int test_input_length(void) { } int test_count(void) { - jsmn_parser p; - const char *js; - - js = "{}"; - jsmn_init(&p); - check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 1); - - js = "[]"; - jsmn_init(&p); - check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 1); - - js = "[[]]"; - jsmn_init(&p); - check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 2); - - js = "[[], []]"; - jsmn_init(&p); - check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 3); - - js = "[[], []]"; - jsmn_init(&p); - check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 3); - - js = "[[], [[]], [[], []]]"; - jsmn_init(&p); - check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 7); - - js = "[\"a\", [[], []]]"; - jsmn_init(&p); - check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 5); - - js = "[[], \"[], [[]]\", [[]]]"; - jsmn_init(&p); - check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 5); - - js = "[1, 2, 3]"; - jsmn_init(&p); - check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 4); - - js = "[1, 2, [3, \"a\"], null]"; - jsmn_init(&p); - check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 7); - + check(query("{}", 1)); + check(query("[]", 1)); + check(query("[[]]", 2)); + check(query("[[], []]", 3)); + check(query("[[], []]", 3)); + check(query("[[], [[]], [[], []]]", 7)); + check(query("[\"a\", [[], []]]", 5)); + check(query("[[], \"[], [[]]\", [[]]]", 5)); + check(query("[1, 2, 3]", 4)); + check(query("[1, 2, [3, \"a\"], null]", 7)); + check(query("[}", JSMN_ERROR_BRACKETS)); + check(query("{]", JSMN_ERROR_BRACKETS)); return 0; } int test_nonstrict(void) { -#ifndef JSMN_STRICT +#ifdef JSMN_PERMISSIVE const char *js; js = "a: 0garbage"; check(parse(js, 2, 2, JSMN_PRIMITIVE, "a", JSMN_PRIMITIVE, "0garbage")); @@ -294,48 +1656,65 @@ int test_nonstrict(void) { /* nested {s don't cause a parse error. */ js = "\"key {1\": 1234"; check(parse(js, 2, 2, JSMN_STRING, "key {1", 1, JSMN_PRIMITIVE, "1234")); - #endif return 0; } int test_unmatched_brackets(void) { - const char *js; - js = "\"key 1\": 1234}"; - check(parse(js, JSMN_ERROR_INVAL, 2)); - js = "{\"key 1\": 1234"; - check(parse(js, JSMN_ERROR_PART, 3)); - js = "{\"key 1\": 1234}}"; - check(parse(js, JSMN_ERROR_INVAL, 3)); - js = "\"key 1\"}: 1234"; - check(parse(js, JSMN_ERROR_INVAL, 3)); - js = "{\"key {1\": 1234}"; - check(parse(js, 3, 3, JSMN_OBJECT, 0, 16, 1, JSMN_STRING, "key {1", 1, +#if defined(JSMN_MULTIPLE_JSON) || defined(JSMN_MULTIPLE_JSON_FAIL) + check(query("\"key 1\": 1234}", JSMN_ERROR_INVAL)); +#else + check(parse("\"key 1\": 1234}", 1, 1, JSMN_STRING, "key 1", 0)); +#endif + check(query("{\"key 1\": 1234", JSMN_ERROR_PART)); +#if defined(JSMN_MULTIPLE_JSON) || defined(JSMN_MULTIPLE_JSON_FAIL) + check(query("{\"key 1\": 1234}}", JSMN_ERROR_INVAL)); + check(query("\"key 1\"}: 1234", JSMN_ERROR_INVAL)); +#else + check(parse("{\"key 1\": 1234}}", 3, 3, JSMN_OBJECT, 0, 15, 1, + JSMN_STRING, "key 1", 1, JSMN_PRIMITIVE, "1234")); + check(parse("\"key 1\"}: 1234", 1, 1, JSMN_STRING, "key 1", 0)); +#endif + check(parse("{\"key {1\": 1234}", 3, 3, + JSMN_OBJECT, 0, 16, 1, + JSMN_STRING, "key {1", 1, JSMN_PRIMITIVE, "1234")); - js = "{\"key 1\":{\"key 2\": 1234}"; - check(parse(js, JSMN_ERROR_PART, 5)); + check(query("{\"key 1\":{\"key 2\": 1234}", JSMN_ERROR_PART)); return 0; } int test_object_key(void) { - const char *js; - - js = "{\"key\": 1}"; - check(parse(js, 3, 3, JSMN_OBJECT, 0, 10, 1, JSMN_STRING, "key", 1, + check(parse("{\"key\": 1}", 3, 3, + JSMN_OBJECT, 0, 10, 1, + JSMN_STRING, "key", 1, JSMN_PRIMITIVE, "1")); -#ifdef JSMN_STRICT - js = "{true: 1}"; - check(parse(js, JSMN_ERROR_INVAL, 3)); - js = "{1: 1}"; - check(parse(js, JSMN_ERROR_INVAL, 3)); - js = "{{\"key\": 1}: 2}"; - check(parse(js, JSMN_ERROR_INVAL, 5)); - js = "{[1,2]: 2}"; - check(parse(js, JSMN_ERROR_INVAL, 5)); +#ifndef JSMN_PERMISSIVE + check(query("{true: 1}", JSMN_ERROR_INVAL)); + check(query("{1: 1}", JSMN_ERROR_INVAL)); + check(query("{{\"key\": 1}: 2}", JSMN_ERROR_INVAL)); + check(query("{[1,2]: 2}", JSMN_ERROR_INVAL)); #endif return 0; } +int test_simple(void) { + check(parse("{\"user\": \"johndoe\", \"admin\": false, \"uid\": 1000,\"groups\": [\"users\", \"wheel\", \"audio\", \"video\"]}", 13, 13, + JSMN_OBJECT, 0, 95, 4, + JSMN_STRING, "user", 1, + JSMN_STRING, "johndoe", 0, + JSMN_STRING, "admin", 1, + JSMN_PRIMITIVE, "false", + JSMN_STRING, "uid", 1, + JSMN_PRIMITIVE, "1000", + JSMN_STRING, "groups", 1, + JSMN_ARRAY, 58, 94, 4, + JSMN_STRING, "users", 0, + JSMN_STRING, "wheel", 0, + JSMN_STRING, "audio", 0, + JSMN_STRING, "video", 0)); + return 0; +} + int main(void) { test(test_empty, "test for a empty JSON objects/arrays"); test(test_object, "test for a JSON objects"); @@ -354,6 +1733,12 @@ int main(void) { test(test_nonstrict, "test for non-strict mode"); test(test_unmatched_brackets, "test for unmatched brackets"); test(test_object_key, "test for key type"); + test(test_simple, "test for jsmn string from simple.c"); + + test(test_jsmn_test_suite_i_, "test jsmn test suite implementation"); + test(test_jsmn_test_suite_n_, "test jsmn test suite should fail"); + test(test_jsmn_test_suite_y_, "test jsmn test suite should pass"); + printf("\nPASSED: %d\nFAILED: %d\n", test_passed, test_failed); return (test_failed > 0); } diff --git a/test/testutil.h b/test/testutil.h index bdee1393..9143787d 100644 --- a/test/testutil.h +++ b/test/testutil.h @@ -3,36 +3,36 @@ #include "../jsmn.h" -static int vtokeq(const char *s, jsmntok_t *t, unsigned long numtok, +static int vtokeq(const char *s, const jsmntok_t *t, const size_t numtok, va_list ap) { if (numtok > 0) { - unsigned long i; - int start, end, size; + size_t i; + jsmnint_t start, end, size; jsmntype_t type; char *value; - size = -1; + size = JSMN_NEG; value = NULL; for (i = 0; i < numtok; i++) { type = va_arg(ap, jsmntype_t); if (type == JSMN_STRING) { value = va_arg(ap, char *); size = va_arg(ap, int); - start = end = -1; + start = end = JSMN_NEG; } else if (type == JSMN_PRIMITIVE) { value = va_arg(ap, char *); - start = end = size = -1; + start = end = size = JSMN_NEG; } else { start = va_arg(ap, int); end = va_arg(ap, int); size = va_arg(ap, int); value = NULL; } - if (t[i].type != type) { + if (!(t[i].type & type)) { printf("token %lu type is %d, not %d\n", i, t[i].type, type); return 0; } - if (start != -1 && end != -1) { + if (start != JSMN_NEG && end != JSMN_NEG) { if (t[i].start != start) { printf("token %lu start is %d, not %d\n", i, t[i].start, start); return 0; @@ -42,14 +42,14 @@ static int vtokeq(const char *s, jsmntok_t *t, unsigned long numtok, return 0; } } - if (size != -1 && t[i].size != size) { + if (size != JSMN_NEG && t[i].size != size) { printf("token %lu size is %d, not %d\n", i, t[i].size, size); return 0; } if (s != NULL && value != NULL) { const char *p = s + t[i].start; - if (strlen(value) != (unsigned long)(t[i].end - t[i].start) || + if (strlen(value) != (size_t)(t[i].end - t[i].start) || strncmp(p, value, t[i].end - t[i].start) != 0) { printf("token %lu value is %.*s, not %s\n", i, t[i].end - t[i].start, s + t[i].start, value); @@ -61,7 +61,7 @@ static int vtokeq(const char *s, jsmntok_t *t, unsigned long numtok, return 1; } -static int tokeq(const char *s, jsmntok_t *tokens, unsigned long numtok, ...) { +static int tokeq(const char *s, const jsmntok_t *tokens, const size_t numtok, ...) { int ok; va_list args; va_start(args, numtok); @@ -70,8 +70,18 @@ static int tokeq(const char *s, jsmntok_t *tokens, unsigned long numtok, ...) { return ok; } -static int parse(const char *s, int status, unsigned long numtok, ...) { - int r; +static int query(const char *s, const jsmnint_t status) { + jsmnint_t r; + jsmn_parser p; + + jsmn_init(&p); + r = jsmn_parse(&p, s, strlen(s), NULL, 0); + + return (status == r); +} + +static int parse(const char *s, const jsmnint_t status, const size_t numtok, ...) { + jsmnint_t r; int ok = 1; va_list args; jsmn_parser p;