C++11 support #1793 (#1795)

* * C++14 detection
* C++14 RWLock (disabled: std::shared_timed_mutex has separate read and write unlock)

* * C++11 Event - disable because
std::condition_variable nas no built-in concept of reset
std::condition_variable may be unblocked spuriously, must handle?

* * Fix std::condition_variable handling

* * C++11 semaphore

* * Use Semaphore max paramater

* * Implement manual reset Event and re-enable
* Disable std semaphore, C++ doesn't have native semaphore, better to use native ones for now

* * C++11 Thread (in progress)

* * Fix auto reset event

* * std::*mutex with timeout doesn't guarantee that the timeout will be respected
* Thread in unix has special member for signal handling

* * Workaround for AppleClang lack of thread_local

* * C++11 format

* * C++11 Logger

* * Fix Poco::format

* * std::function event delegate

* * Fix StdFunctionDelegate for GCC

* * C++11 move semantics

* * Fix format test

* * Add cpp11_changes files

* * Auto detect compiler C++11/14 support
* Option to force disable C++11/14

* * GCC/Clang need to enable C++11/14 support via command line switches

* * Fixes for VS2013 C++11 support

* * Makefile C++11/14 compiler detection (in progress)

* * Simple GCC version detection

* AppleClang version detection

* Fix gcc c++11 build script
Update c++11 status document

* Use predefined constant instead of --version

* Fix CC & CXX to gcc 4.8 for all matrix rows.

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Restore apt-get update -qq

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Display g++ version and full g++ command line

* Fix typo

* Use command line/environment variables for CC & CXX

* Display GCCVERSION

* Use $(CXX) for setting GCCVERSION

* Do not use <cstdint>, otherwise NumberFormatter wo'nt compile

* restore silent mode on make

* Use predefined constants __clang_major__ and __clang_minor__

* Reverse clang++ options

* Add c++11/14 detection

* -std=c++14 is not valid for clang 3.4

* Run tests for both gcc 4.6 & gcc 4.8

* Restore Apple clang setup

* Align setup of std=c++11 from CLANGVERSION with check of clang version in Platform_POSIX.h

On Linux ubuntu at Travis, clang 3.4 does not support std=c++14

* Add OSX run with clang

* Add forgetted Linux guard on install

* Display clang's predefined constants on OSX

* Comment out Apple temporarly

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* export CC & CXX for OSX

* Test when clang is from Apple

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Remove typo

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Align test with comment

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Add Cygwin-clang

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Install CppUnit for running the unit tests on OSX. 

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Looking for libCppUnit.1.dylib

* Install libCppUnit*.

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Exclude Redis for OSX. To be restored later on.

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Remove some display lines

* Reenable tests

* Add run tests on cmake build with ctest -VV

* Split runtests.sh into ignored.sh and excluded.sh. ignored.sh will be
used for excluding tests from ctest.

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Add ignored.sh to exclude CPPUNIT_IGNORE test on ctest

* Set proper path

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Exclude Data* from CTest. Run tests on ARM

* Add POCO_BASE for tests using test files under $POCO_BASE/component/testsuite

* Replace MB by KB otherwise Travis & AppVeyor are timing out.

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Specialize runtests.sh & excluded.sh according to OSX or Linux

* Split OSX/Linux runtests.sh

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Removed invalid comment

* Make runtests.sh executable

* c++11 atomic<> default constructor is never initialized

* Initialize to 0 in any case.

* Comment out set -ev for now

* Comment out set -ev for now

* reset verbose mode

* reset verbose mode

*  catch SIGPIPE as returnfrom testrunner to avoid failure in networking tests

* Use source for export ignored tests to avoid the permission denied problem

* chmod 775

* Apply patches from @RangeIReale

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Add cpp11-appleclang

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Initialize AtomicCounter to 0 since the c++11 default constructor don't
do it.

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* avoid SIGPIPE on send

* fix WbeSocketServer

* Remove SIGPIPE workaround

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Fix WebSocketServer

* Restore set -ev

* Restore set -ev

* Fix WebSocketServer responding loop

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Cleanup the exit test.

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Add important comment.

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Copy also cpp11-* so that mkrelease works!

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Avoid PocoDoc preprocessisng error.

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* * Sync script and Platform_POSIX.h compiler versions

* VS2013 & VS2015 fixups.

Signed-off-by: Francis ANDRE <zosrothko@orange.fr>

* Backport to c++03 so that Travis and Appveyor be ok

Signed-off-by: Francis ANDRE <zosrothko@orange.fr>

* Add Copyright (c) 2016, Applied Informatics Software Engineering GmbH.
and Contributors.

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* VS2013 fixup

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Backport to c++03

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* VS2013 fixup

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Backport to c++03

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* VS2013 fixup

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* BAckport c++03

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* OSX: clang++ rejects return (*static_cast<Derived*>(this));
cannot cast protected base class 'cpp_ex4::StoplightContext<cpp_ex4::Stoplight>' to 'cpp_ex4::Stoplight'


Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* BAckport to c++03

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Add generated file for now

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* deleted as source file name in lowercase.

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Rename stoplight_sm.h to Stoplight_sm.h

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Rename stoplight_sm.h to Stoplight_sm.h

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Rename stoplight_sm.h to Stoplight_sm.h

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Put cmake and ctest on the DOS PATH

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Cygwin does not support timed_mutex

* Put installed CMake /CTest on the DOS PATH.

* Repackaging

* Build VS2013 & VS2014 first.

* Add std::string Foundation_API format(const std::string& fmt, const Any& value);

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Restore INLINES macros when compiling with c++03

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Rename stoplight_sm.h to Stoplight_sm.h

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* VS2013 fixup.

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Baclport to c++03

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* cpp_ex4::StoplightContext<cpp_ex4::Stoplight> is an inaccessible base of ˜cpp_ex4::Stoplight

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Undef _XOPEN_SOURCE under Cygwin otherwise ulong is undefined.
usr/include/mysql/my_global.h:997:9: error: 'ulong' does not name a type
 typedef ulong nesting_map;  /* Used for flags of nesting constructs */
         ^
In file included from src/SQLExecutor.cpp:38:0:

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Fix merge failure. Set -ev option on shell.

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Removed

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Undefine _XOPEN_SOURCE so that typedef unsigned long ulong be defined.

Signed-off-by: FrancisANDRE <zosrothko@orange.fr>

* Remove __*VISIBLE defines as GNU forbids their usage outside the system includes

Signed-off-by: Francis ANDRE <zosrothko@orange.fr>

* Use c++03 as last c++ version.

Signed-off-by: Francis ANDRE <zosrothko@orange.fr>

* MySQL needs ulong which are not visible when compiling with
c++11 and above.

Signed-off-by: Francis ANDRE <zosrothko@orange.fr>

* Add BUILD_CC=gcc to avoid strip issue

* Remove reference to FSM

* make ?= operator does not work with

* Remove BUILD_CC

* Remove FSM since it is now in its own branch

* Don't build Cygwin64 with std=c++14 until this issue be fixed https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77441

* enable c++11

* remove changes tracking file (moved to project)

* C++11: fixes for Travis CI (#1798)

* Use c++11 compliant compiler: g++ 5 & clang 3.5.0

* Fix typo on IntType

* Use Linux-clang config instead of Linux

* Check arm-linux-gnueabi-g++ version. Dump compiler constants

* Fix missing namespace qualifier

* Ignore ltdl for Cygwin platform

* Fix typo

* Commented out arm-linux-gnueabi-g++ since the version 4.7.0 which does
not support c++11 standard.

* Restore logic as in develop branch

* Add CXXFLAGS for launching the CXX compilation. CXXFLAGS defaulted to
-std=c++11

* Replace auto_ptr by unique_ptr

* Comment out failing tests until be fixed

* C++11: Fix AppVeyor CI yaml (#1812)

* C++11 support #1793: It's time. g++4.9, clang3.3 and VS2015 will become
minimum compiler versions.

* Comment out image with VS2017 since it crashes.

* New release of OpenSSL: 1_1_0f

* Refactor the download steps of OpenSSL

* C++11 support: Add VS2017 Win32 & x64 (#1816)

* Add VS2017.

* Use VSSetup to get VS150COMNTOOLS

* Fix VS150COMNTOOLS value

* Ignore Install-Module

* Install VSSetUp PS module.

* Fix typo missing $

* Split InstallPath againt "="

* Adjust VS150COMMONTOOLS path.

* Exit failed is VS commontools is undefined.

* Get the InstalledPath property value

* Replace mysql-5.7.17 by mysql-5.7.18

* Replace mysql-5.7.17 by mysql-5.7.18

* c++11 thread priority and affinity, and more (#1811)

* * c++11 thread priority and affinity
* AtomicCounter wasn't using std::atomic
* Buffer wasn't setting all fields on move
* Poco::Event is not directly available in c++11, keep using the native versions
* UnWindows.h define NOMINMAX to reduce conflicts
* Remove "-static" from Linux GCC parameters, this conflicts with c++11 classes (https://stackoverflow.com/questions/7090623/c0x-thread-static-linking-problem)
* Link "-lrt" before "-lpthread" on Linux, otherwise won't build on GCC 5+

* * Fixed std::thread POSIX handle

* * Fix OSX compilation

* raise cmake min version to 3.2.0

* Remove CMake jobs since it is already tested on Travis (#1832)

* Remove CMake jobs since it is already tested on Travis

* Add clang-3.7

* Ubuntu Trusty LLVM repo for gettign clang versions.

* Remove 3.3 & 3.4

* Add clang 3.8, clang 3.9 & clang 4.0

* Add CXXFLAGS=-stdlib=libc++

* use libstdc++ instead of libc++

* Remove for now -g compiler option to avoid this spurious error on clang
error: debug information for auto is not yet supported

* Use -ftemplate-backtrace-limit=0 to dump the recursive template stack.

* Cleanup

* Use good gcc version

* Fix gcc-5 & g++-5 name

* Use libc++ instead of libstdc++

* Restore installation of gcc-5

* Readd agt-get update for gcc-5/g++-5

* Reswitch from libc++ to libstdc++

* Increase template evaluation stack size up to 340 to avoid a stack
overflow in parsing 
TupleType t = std::make_tuple(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14...

* Add clang 3.4, 3.5, 3.6

* Adjust template evaluation stack for clang  3.4, 3.5, 3.6, 3.7

* Update notifications

* Add gcc-4.9

* Backport Travis CXXFLAGS to config/Linux-clang

* Remove CXXFLAGS from clang tests

* Test g++ first, then clang++ and then arm

* remove all non-c++11 code and POCO_ENABLE_CPP11 (default now)

* remove old file

* windows c++11 fixes

* fix conversion warning

* C++11: Use Mutex_POSIX for Cygwin since it does not support std::timed_mutex (#1864)

* Use Mutex_POSIX for Cygwin since it does not support std::timed_mutex

* Use Mutex_POSIX for Cygwin since it does not support std::timed_mutex

* Add Mutext_POSIX.cpp

* Json/parser (#1858)

* move parser implementation in separate file

* parser replacement, first attempt (wip)

* making tests pass (mostly done, 3 to go)

* add missing headers

* honor no-null-byte-in-string setting

* few more fixes

* add -std=c99

* fix cmake JSON build and formatting

* accept utf-8, fix tests

* skip calling isdigit()

* #740, #1860, license update, a warning fix

* var emtpy()->clear(); json array and object tidy up

* vs 2015 32-bit project update

* update VS2017 projects

* remove ProGen and all VS files older than 2015

* fixed GH #1865: AbstractEvent::hasDelegates() is not thread-safe

* merge develop

* add missing files

* add VS generated dir to gitignore

* add WEC2013.cmake

* fix exception message

* fix VS2017 ODBC project filter

* integrate PageCompiler changes from poco-1.7.9

* vs2015 openssl build

* added POCO_DEPRECATED macro

* added POCO_NO_DEPRECATED to disarm POCO_DEPRECATED macro

* PageCompiler: support <%@ include file="<path>" %> syntax for includes.

* remove '$Id$' headers

* remove '$Id$' headers

* remove old VMS and VxWorks build support

* Poco::NamedMutex and Poco::NamedEvent (System V Semaphores implementation): files are now opened with O_RDONLY | O_CREAT instead of O_WRONLY | O_CREAT, allowing sharing between different users. Furthermore, ftok() is called with 'p' as project ID argument.

* C++11 (#1887)

* Use Mutex_POSIX for Cygwin since it does not support std::timed_mutex

* Use Mutex_POSIX for Cygwin since it does not support std::timed_mutex

* Add Mutext_POSIX.cpp

* Remove clang 3.4, 3.5, 3.6, 3.7, 3.8, 3.9 since LLVM does not provide
anymore their packages for Ubuntu Trusy

* Remove ubuntu-toolchain-r/test/ubuntu since it is already installed

* Comment our the llvl-toolchain-trusty since its checksum is buggy
See https://bugs.llvm.org/show_bug.cgi?id=34572

* Display g++ version (#1889)

* merge change from develop|

* Support for PKCS#12 (#1876)

* rebuild openssl binaries; warning and stlye fixes

* fix vs2015 projects and openssl linking

* add PKCS12Container

* remove comments

* style

* add ECKey* and CryptoException

* EC key, unify RSA and EC under same inheritance, add constructor from PKCS12, couple of EC key tests

* simplify EVPPKey, ad EC tests

* EVPPKey test and fixes

* fix linux build

* PKCS12 tests and fixes

* linux build, fix crash

* fix leaks

* uncomment ifstream tests, some minor fixes

* fix stream tests and some tidy-up

* remove $Id

* add ECDSA

* update makefile

* align PKCS12 constructors signatures with X509

* EVPPKey EC curve name constructor

* ECDSA fixes and tests

* linux build, wrap tests in try/catch to get full exception message

* style

* update VS projects

* remove openssl, modify VS projects for git submodule directories

* add openssl submodule

* add _CRT_SECURE_NO_WARNINGS

* port crypto dev fixes from 1.8

* lock whole Event:set()

* fix openssl include path

* Add <memory> for std::unique_ptr (#1891)

* Missing include <iostream> (#1893)

* add file/stream load/save capabilities to EVPPKey

* add EVPTest

* add #include <typeinfo>

* fix posix compile

* update samples VS projects

* fix g++ test compile error
This commit is contained in:
Aleksandar Fabijanic
2017-09-20 22:13:19 -05:00
committed by GitHub
parent d9a6954950
commit 238306a6ed
3106 changed files with 10221 additions and 612887 deletions

View File

@@ -26,16 +26,52 @@ namespace Poco {
namespace JSON {
Array::Array()
Array::Array(): _modified(false)
{
}
Array::Array(const Array& copy) : _values(copy._values)
Array::Array(const Array& other) : _values(other._values),
_pArray(other._pArray),
_modified(other._modified)
{
}
Array::Array(Array&& other) :
_values(std::move(other._values)),
_pArray(!other._modified ? other._pArray : 0),
_modified(other._modified)
{
_pArray = 0;
}
Array &Array::operator=(const Array& other)
{
if (&other != this)
{
_values = other._values;
_pArray = other._pArray;
_modified = other._modified;
}
return *this;
}
Array &Array::operator= (Array&& other)
{
if (&other != this)
{
_values = std::move(other._values);
_pArray = other._pArray;
other._pArray = 0;
_modified = other._modified;
}
return *this;
}
Array::~Array()
{
}
@@ -144,13 +180,26 @@ void Array::stringify(std::ostream& out, unsigned int indent, int step) const
}
Array::operator const Poco::Dynamic::Array& () const
void Array::resetDynArray() const
{
if (!_pArray)
_pArray = new Poco::Dynamic::Array;
else
_pArray->clear();
}
Array::operator const Poco::Dynamic::Array& () const
{
if (!_values.size())
{
resetDynArray();
}
else if (_modified)
{
ValueVec::const_iterator it = _values.begin();
ValueVec::const_iterator end = _values.end();
_pArray = new Poco::Dynamic::Array;
resetDynArray();
int index = 0;
for (; it != end; ++it, ++index)
{
@@ -167,6 +216,7 @@ Array::operator const Poco::Dynamic::Array& () const
_pArray->insert(_pArray->end(), *it);
}
}
_modified = false;
}
return *_pArray;

View File

@@ -26,14 +26,17 @@ namespace Poco {
namespace JSON {
Object::Object(bool preserveInsertionOrder): _preserveInsOrder(preserveInsertionOrder)
Object::Object(bool preserveInsOrder):
_preserveInsOrder(preserveInsOrder),
_modified(false)
{
}
Object::Object(const Object& copy) : _values(copy._values),
_preserveInsOrder(copy._preserveInsOrder),
_pStruct(0)
_pStruct(!copy._modified ? copy._pStruct : 0),
_modified(copy._modified)
{
if (_preserveInsOrder)
{
@@ -48,11 +51,49 @@ Object::Object(const Object& copy) : _values(copy._values),
}
Object::Object(Object&& other) :
_values(std::move(other._values)),
_keys(std::move(other._keys)),
_preserveInsOrder(other._preserveInsOrder),
_pStruct(!other._modified ? other._pStruct : 0),
_modified(other._modified)
{
}
Object::~Object()
{
}
Object &Object::operator= (const Object &other)
{
if (&other != this)
{
_values = other._values;
_keys = other._keys;
_preserveInsOrder = other._preserveInsOrder;
_pStruct = !other._modified ? other._pStruct : 0;
_modified = other._modified;
}
return *this;
}
Object &Object::operator= (Object &&other)
{
if (&other != this)
{
_values = std::move(other._values);
_keys = std::move(other._keys);
_preserveInsOrder = other._preserveInsOrder;
_pStruct = !other._modified ? other._pStruct : 0;
_modified = other._modified;
}
return *this;
}
Var Object::get(const std::string& key) const
{
ValueMap::const_iterator it = _values.find(key);
@@ -137,6 +178,7 @@ void Object::set(const std::string& key, const Dynamic::Var& value)
}
_keys.push_back(&ret.first->first);
}
_modified = true;
}
@@ -168,13 +210,26 @@ Poco::DynamicStruct Object::makeStruct(const Object::Ptr& obj)
}
Object::operator const Poco::DynamicStruct& () const
void Object::resetDynStruct() const
{
if (!_pStruct)
_pStruct = new Poco::DynamicStruct;
else
_pStruct->clear();
}
Object::operator const Poco::DynamicStruct& () const
{
if (!_values.size())
{
resetDynStruct();
}
else if (_modified)
{
ValueMap::const_iterator it = _values.begin();
ValueMap::const_iterator end = _values.end();
_pStruct = new Poco::DynamicStruct;
resetDynStruct();
for (; it != end; ++it)
{
if (isObject(it))
@@ -201,6 +256,7 @@ void Object::clear()
_values.clear();
_keys.clear();
_pStruct = 0;
_modified = true;
}

View File

@@ -16,6 +16,7 @@
#include "Poco/JSON/ParseHandler.h"
#include "Poco/JSON/Object.h"
#include "Poco/JSON/JSONException.h"
using Poco::Dynamic::Var;
@@ -47,7 +48,6 @@ void ParseHandler::reset()
void ParseHandler::startObject()
{
Object::Ptr newObj = new Object(_preserveObjectOrder);
if (_stack.empty()) // The first object
{
_result = newObj;
@@ -124,18 +124,25 @@ void ParseHandler::key(const std::string& k)
void ParseHandler::setValue(const Var& value)
{
Var parent = _stack.top();
if (_stack.size())
{
Var parent = _stack.top();
if (parent.type() == typeid(Array::Ptr))
{
Array::Ptr arr = parent.extract<Array::Ptr>();
arr->add(value);
if (parent.type() == typeid(Array::Ptr))
{
Array::Ptr arr = parent.extract<Array::Ptr>();
arr->add(value);
}
else if (parent.type() == typeid(Object::Ptr))
{
Object::Ptr obj = parent.extract<Object::Ptr>();
obj->set(_key, value);
_key.clear();
}
}
else if (parent.type() == typeid(Object::Ptr))
else
{
Object::Ptr obj = parent.extract<Object::Ptr>();
obj->set(_key, value);
_key.clear();
throw JSONException("Attempt to set value on an empty stack");
}
}

View File

@@ -31,95 +31,9 @@ namespace Poco {
namespace JSON {
static const unsigned char UTF8_LEAD_BITS[4] = { 0x00, 0xC0, 0xE0, 0xF0 };
const int Parser::_asciiClass[] =
{
xx, xx, xx, xx, xx, xx, xx, xx,
xx, C_WHITE, C_WHITE, xx, xx, C_WHITE, xx, xx,
xx, xx, xx, xx, xx, xx, xx, xx,
xx, xx, xx, xx, xx, xx, xx, xx,
C_SPACE, C_ETC, C_QUOTE, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,
C_ETC, C_ETC, C_STAR, C_PLUS, C_COMMA, C_MINUS, C_POINT, C_SLASH,
C_ZERO, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT,
C_DIGIT, C_DIGIT, C_COLON, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,
C_ETC, C_ABCDF, C_ABCDF, C_ABCDF, C_ABCDF, C_E, C_ABCDF, C_ETC,
C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,
C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,
C_ETC, C_ETC, C_ETC, C_LSQRB, C_BACKS, C_RSQRB, C_ETC, C_ETC,
C_ETC, C_LOW_A, C_LOW_B, C_LOW_C, C_LOW_D, C_LOW_E, C_LOW_F, C_ETC,
C_ETC, C_ETC, C_ETC, C_ETC, C_LOW_L, C_ETC, C_LOW_N, C_ETC,
C_ETC, C_ETC, C_LOW_R, C_LOW_S, C_LOW_T, C_LOW_U, C_ETC, C_ETC,
C_ETC, C_ETC, C_ETC, C_LCURB, C_ETC, C_RCURB, C_ETC, C_ETC
};
const int Parser::_stateTransitionTable[NR_STATES][NR_CLASSES] =
{
/*
white 1-9 ABCDF etc
space | { } [ ] : , " \ / + - . 0 | a b c d e f l n r s t u | E | * */
/*start GO*/ {GO,GO,-6,xx,-5,xx,xx,xx,xx,xx,CB,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*ok OK*/ {OK,OK,xx,-8,xx,-7,xx,-3,xx,xx,CB,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*object OB*/ {OB,OB,xx,-9,xx,xx,xx,xx,SB,xx,CB,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*key KE*/ {KE,KE,xx,xx,xx,xx,xx,xx,SB,xx,CB,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*colon CO*/ {CO,CO,xx,xx,xx,xx,-2,xx,xx,xx,CB,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*value VA*/ {VA,VA,-6,xx,-5,xx,xx,xx,SB,xx,CB,xx,MX,xx,ZX,IX,xx,xx,xx,xx,xx,FA,xx,NU,xx,xx,TR,xx,xx,xx,xx,xx},
/*array AR*/ {AR,AR,-6,xx,-5,-7,xx,xx,SB,xx,CB,xx,MX,xx,ZX,IX,xx,xx,xx,xx,xx,FA,xx,NU,xx,xx,TR,xx,xx,xx,xx,xx},
/*string ST*/ {ST,xx,ST,ST,ST,ST,ST,ST,-4,EX,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST},
/*escape EC*/ {xx,xx,xx,xx,xx,xx,xx,xx,ST,ST,ST,xx,xx,xx,xx,xx,xx,ST,xx,xx,xx,ST,xx,ST,ST,xx,ST,U1,xx,xx,xx,xx},
/*u1 U1*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,U2,U2,U2,U2,U2,U2,U2,U2,xx,xx,xx,xx,xx,xx,U2,U2,xx,xx},
/*u2 U2*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,U3,U3,U3,U3,U3,U3,U3,U3,xx,xx,xx,xx,xx,xx,U3,U3,xx,xx},
/*u3 U3*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,U4,U4,U4,U4,U4,U4,U4,U4,xx,xx,xx,xx,xx,xx,U4,U4,xx,xx},
/*u4 U4*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,UC,UC,UC,UC,UC,UC,UC,UC,xx,xx,xx,xx,xx,xx,UC,UC,xx,xx},
/*minus MI*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,ZE,IT,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*zero ZE*/ {OK,OK,xx,-8,xx,-7,xx,-3,xx,xx,CB,xx,xx,DF,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*int IT*/ {OK,OK,xx,-8,xx,-7,xx,-3,xx,xx,CB,xx,xx,DF,IT,IT,xx,xx,xx,xx,DE,xx,xx,xx,xx,xx,xx,xx,xx,DE,xx,xx},
/*frac FR*/ {OK,OK,xx,-8,xx,-7,xx,-3,xx,xx,CB,xx,xx,xx,FR,FR,xx,xx,xx,xx,E1,xx,xx,xx,xx,xx,xx,xx,xx,E1,xx,xx},
/*e E1*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,E2,E2,xx,E3,E3,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*ex E2*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,E3,E3,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*exp E3*/ {OK,OK,xx,-8,xx,-7,xx,-3,xx,xx,xx,xx,xx,xx,E3,E3,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*tr T1*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,T2,xx,xx,xx,xx,xx,xx,xx},
/*tru T2*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,T3,xx,xx,xx,xx},
/*1 T3*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,CB,xx,xx,xx,xx,xx,xx,xx,xx,xx,OK,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*fa F1*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,F2,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*fal F2*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,F3,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*fals F3*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,F4,xx,xx,xx,xx,xx,xx},
/*0 F4*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,CB,xx,xx,xx,xx,xx,xx,xx,xx,xx,OK,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*nu N1*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,N2,xx,xx,xx,xx},
/*nul N2*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,N3,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*null N3*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,CB,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,OK,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*/ C1*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,C2},
/*/* C2*/ {C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C3},
/** C3*/ {C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,CE,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C3},
/*_. FX*/ {OK,OK,xx,-8,xx,-7,xx,-3,xx,xx,xx,xx,xx,xx,FR,FR,xx,xx,xx,xx,E1,xx,xx,xx,xx,xx,xx,xx,xx,E1,xx,xx},
/*\ D1*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,D2,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*\ D2*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,U1,xx,xx,xx,xx},
};
Parser::Parser(const Handler::Ptr& pHandler, std::size_t bufSize):
_pHandler(pHandler),
_state(GO),
_beforeCommentState(0),
_type(JSON_T_NONE),
_escaped(0),
_comment(0),
_utf16HighSurrogate(0),
_depth(JSON_UNLIMITED_DEPTH),
_top(-1),
_stack(JSON_PARSER_STACK_SIZE),
_parseBuffer(bufSize),
_decimalPoint('.'),
_allowNullByte(true),
_allowComments(false)
ParserImpl(pHandler, bufSize)
{
_parseBuffer.resize(0);
push(MODE_DONE);
}
@@ -128,386 +42,4 @@ Parser::~Parser()
}
void Parser::reset()
{
_state = GO;
_beforeCommentState = 0;
_type = JSON_T_NONE;
_escaped = 0;
_utf16HighSurrogate = 0;
_top = -1;
_stack.clear();
_parseBuffer.resize(0);
push(MODE_DONE);
if (_pHandler) _pHandler->reset();
}
Dynamic::Var Parser::parse(const std::string& json)
{
std::string::const_iterator it = json.begin();
std::string::const_iterator end = json.end();
Source<std::string::const_iterator> source(it, end);
int c = 0;
while (source.nextChar(c))
{
if (0 == parseChar(c, source))
throw SyntaxException("JSON syntax error");
}
if (!done())
throw JSONException("JSON syntax error");
return asVar();
}
Dynamic::Var Parser::parse(std::istream& in)
{
std::istreambuf_iterator<char> it(in.rdbuf());
std::istreambuf_iterator<char> end;
Source<std::istreambuf_iterator<char> > source(it, end);
int c = 0;
while (source.nextChar(c))
{
if (0 == parseChar(c, source)) throw JSONException("JSON syntax error");
}
if (!done())
throw JSONException("JSON syntax error");
return asVar();
}
bool Parser::push(int mode)
{
_top += 1;
if (_depth < 0)
{
if (_top >= _stack.size())
_stack.resize(_stack.size() * 2, true);
}
else
{
if (_top >= _depth) return false;
}
_stack[_top] = mode;
return true;
}
bool Parser::pop(int mode)
{
if (_top < 0 || _stack[_top] != mode)
return false;
_top -= 1;
return true;
}
void Parser::clearBuffer()
{
_parseBuffer.resize(0);
}
void Parser::parseBufferPopBackChar()
{
poco_assert(_parseBuffer.size() >= 1);
_parseBuffer.resize(_parseBuffer.size() - 1);
}
void Parser::parseBufferPushBackChar(char c)
{
if (_parseBuffer.size() + 1 >= _parseBuffer.capacity())
_parseBuffer.setCapacity(_parseBuffer.capacity() * 2);
_parseBuffer.append(c);
}
void Parser::addEscapedCharToParseBuffer(CharIntType nextChar)
{
_escaped = 0;
// remove the backslash
parseBufferPopBackChar();
switch (nextChar)
{
case 'b':
parseBufferPushBackChar('\b');
break;
case 'f':
parseBufferPushBackChar('\f');
break;
case 'n':
parseBufferPushBackChar('\n');
break;
case 'r':
parseBufferPushBackChar('\r');
break;
case 't':
parseBufferPushBackChar('\t');
break;
case '"':
parseBufferPushBackChar('"');
break;
case '\\':
parseBufferPushBackChar('\\');
break;
case '/':
parseBufferPushBackChar('/');
break;
case 'u':
parseBufferPushBackChar('\\');
parseBufferPushBackChar('u');
break;
default:
break;
}
}
void Parser::addCharToParseBuffer(CharIntType nextChar, int nextClass)
{
if (_escaped)
{
addEscapedCharToParseBuffer(nextChar);
return;
}
else if (!_comment)
{
if ((_type != JSON_T_NONE) ||
!((nextClass == C_SPACE) || (nextClass == C_WHITE)))
{
parseBufferPushBackChar((char) nextChar);
}
}
}
Parser::CharIntType Parser::decodeUnicodeChar()
{
int i;
unsigned uc = 0;
char* p;
int trailBytes;
poco_assert(_parseBuffer.size() >= 6);
p = &_parseBuffer[_parseBuffer.size() - 4];
for (i = 12; i >= 0; i -= 4, ++p)
{
unsigned x = *p;
if (x >= 'a') x -= ('a' - 10);
else if (x >= 'A') x -= ('A' - 10);
else x &= ~0x30u;
poco_assert(x < 16);
uc |= x << i;
}
if (!_allowNullByte && uc == 0) return 0;
// clear UTF-16 char from buffer
_parseBuffer.resize(_parseBuffer.size() - 6);
if (_utf16HighSurrogate)
{
if (isLowSurrogate(uc))
{
uc = decodeSurrogatePair(_utf16HighSurrogate, uc);
trailBytes = 3;
_utf16HighSurrogate = 0;
}
else // high surrogate without a following low surrogate
{
return 0;
}
}
else
{
if (uc < 0x80)
{
trailBytes = 0;
}
else if (uc < 0x800)
{
trailBytes = 1;
}
else if (isHighSurrogate(uc))
{
// save the high surrogate and wait for the low surrogate
_utf16HighSurrogate = uc;
return 1;
}
else if (isLowSurrogate(uc))
{
// low surrogate without a preceding high surrogate
return 0;
}
else
{
trailBytes = 2;
}
}
_parseBuffer.append((char) ((uc >> (trailBytes * 6)) | UTF8_LEAD_BITS[trailBytes]));
for (i = trailBytes * 6 - 6; i >= 0; i -= 6)
{
_parseBuffer.append((char) (((uc >> i) & 0x3F) | 0x80));
}
return 1;
}
void Parser::parseBuffer()
{
if (_pHandler)
{
int type = _type; // just to silence g++
if (type != JSON_T_NONE)
{
assertNonContainer();
switch (type)
{
case JSON_T_TRUE:
{
_pHandler->value(true);
break;
}
case JSON_T_FALSE:
{
_pHandler->value(false);
break;
}
case JSON_T_NULL:
{
_pHandler->null();
break;
}
case JSON_T_FLOAT:
{
// Float can't end with a dot
if (_parseBuffer[_parseBuffer.size() - 1] == '.') throw SyntaxException("JSON syntax error");
double float_value = NumberParser::parseFloat(std::string(_parseBuffer.begin(), _parseBuffer.size()));
_pHandler->value(float_value);
break;
}
case JSON_T_INTEGER:
{
#if defined(POCO_HAVE_INT64)
std::string numStr(_parseBuffer.begin(), _parseBuffer.size());
try
{
Poco::trimInPlace(numStr);
Int64 value = NumberParser::parse64(numStr);
// if number is 32-bit, then handle as such
if (value > std::numeric_limits<int>::max()
|| value < std::numeric_limits<int>::min())
{
_pHandler->value(value);
}
else
{
_pHandler->value(static_cast<int>(value));
}
}
// try to handle error as unsigned in case of overflow
catch (const SyntaxException&)
{
UInt64 value = NumberParser::parseUnsigned64(numStr);
// if number is 32-bit, then handle as such
if (value > std::numeric_limits<unsigned>::max())
{
_pHandler->value(value);
}
else
{
_pHandler->value(static_cast<unsigned>(value));
}
}
#else
try
{
int value = NumberParser::parse(numStr);
_pHandler->value(value);
}
// try to handle error as unsigned in case of overflow
catch (const SyntaxException&)
{
unsigned value = NumberParser::parseUnsigned(numStr);
_pHandler->value(value);
}
#endif
}
break;
case JSON_T_STRING:
{
_pHandler->value(std::string(_parseBuffer.begin(), _parseBuffer.size()));
break;
}
}
}
}
clearBuffer();
}
int Parser::utf8CheckFirst(char byte)
{
unsigned char u = (unsigned char) byte;
if (u < 0x80)
return 1;
if (0x80 <= u && u <= 0xBF)
{
// second, third or fourth byte of a multi-byte
// sequence, i.e. a "continuation byte"
return 0;
}
else if (u == 0xC0 || u == 0xC1)
{
// overlong encoding of an ASCII byte
return 0;
}
else if (0xC2 <= u && u <= 0xDF)
{
// 2-byte sequence
return 2;
}
else if (0xE0 <= u && u <= 0xEF)
{
// 3-byte sequence
return 3;
}
else if (0xF0 <= u && u <= 0xF4)
{
// 4-byte sequence
return 4;
}
else
{
// u >= 0xF5
// Restricted (start of 4-, 5- or 6-byte sequence) or invalid UTF-8
return 0;
}
}
} } // namespace Poco::JSON

717
JSON/src/ParserImpl.cpp Normal file
View File

@@ -0,0 +1,717 @@
//
// Parser.cpp
//
// $Id$
//
// Library: JSON
// Package: JSON
// Module: Parser
//
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/JSON/Parser.h"
#include "Poco/JSON/JSONException.h"
#include "Poco/Ascii.h"
#include "Poco/Token.h"
#include "Poco/UTF8Encoding.h"
#include "Poco/String.h"
#include "Poco/StreamCopier.h"
#undef min
#undef max
#include <limits>
#include <clocale>
#include <istream>
namespace Poco {
namespace JSON {
#ifdef PD_JSON_PARSER
ParserImpl::ParserImpl(const Handler::Ptr& pHandler, std::size_t bufSize):
_pHandler(pHandler),
_depth(JSON_UNLIMITED_DEPTH),
_decimalPoint('.'),
_allowNullByte(true),
_allowComments(false)
{
}
ParserImpl::~ParserImpl()
{
}
void ParserImpl::handle(const std::string& json)
{
if (!_allowNullByte && json.find("\\u0000") != json.npos)
throw JSONException("Null bytes in strings not allowed.");
try
{
json_open_buffer(&_json, json.data(), json.size());
checkError();
//////////////////////////////////
// Underlying parser is capable of parsing multiple consecutive JSONs;
// we do not currently support this feature; to force error on
// excessive characters past valid JSON end, this MUST be called
// AFTER opening the buffer - otherwise it is overwritten by
// json_open*() call, which calls internal init()
json_set_streaming(&_json, false);
/////////////////////////////////
handle(); checkError();
if (JSON_DONE != json_next(&_json))
throw JSONException("Excess characters found after JSON end.");
json_close(&_json);
}
catch (std::exception&)
{
json_close(&_json);
throw;
}
}
Dynamic::Var ParserImpl::parseImpl(const std::string& json)
{
if (_allowComments)
{
std::string str = json;
stripComments(str);
handle(str);
}
else handle(json);
return asVarImpl();
}
Dynamic::Var ParserImpl::parseImpl(std::istream& in)
{
std::ostringstream os;
StreamCopier::copyStream(in, os);
return parseImpl(os.str());
}
void ParserImpl::stripComments(std::string& json)
{
if (_allowComments)
{
bool inString = false;
bool inComment = false;
char prevChar = 0;
std::string::iterator it = json.begin();
for (; it != json.end();)
{
if (*it == '"' && !inString) inString = true;
else inString = false;
if (!inString)
{
if (*it == '/' && it + 1 != json.end() && *(it + 1) == '*')
inComment = true;
}
if (inComment)
{
char c = *it;
it = json.erase(it);
if (prevChar == '*' && c == '/')
{
inComment = false;
prevChar = 0;
}
else prevChar = c;
}
else ++it;
}
}
}
void ParserImpl::handleArray()
{
json_type tok = json_peek(&_json);
while (tok != JSON_ARRAY_END && checkError())
{
handle();
tok = json_peek(&_json);
}
if (tok == JSON_ARRAY_END) handle();
else throw JSONException("JSON array end not found");
}
void ParserImpl::handleObject()
{
json_type tok = json_peek(&_json);
while (tok != JSON_OBJECT_END && checkError())
{
json_next(&_json);
if (_pHandler) _pHandler->key(std::string(json_get_string(&_json, NULL)));
handle();
tok = json_peek(&_json);
}
if (tok == JSON_OBJECT_END) handle();
else throw JSONException("JSON object end not found");
}
void ParserImpl::handle()
{
enum json_type type = json_next(&_json);
switch (type)
{
case JSON_DONE:
return;
case JSON_NULL:
_pHandler->null();
break;
case JSON_TRUE:
if (_pHandler) _pHandler->value(true);
break;
case JSON_FALSE:
if (_pHandler) _pHandler->value(false);
break;
case JSON_NUMBER:
{
if (_pHandler)
{
std::string str(json_get_string(&_json, NULL));
if (str.find(_decimalPoint) != str.npos || str.find('e') != str.npos || str.find('E') != str.npos)
{
_pHandler->value(NumberParser::parseFloat(str));
}
else
{
Poco::Int64 val;
if (NumberParser::tryParse64(str, val))
_pHandler->value(val);
else
_pHandler->value(NumberParser::parseUnsigned64(str));
}
}
break;
}
case JSON_STRING:
if (_pHandler) _pHandler->value(std::string(json_get_string(&_json, NULL)));
break;
case JSON_OBJECT:
if (_pHandler) _pHandler->startObject();
handleObject();
break;
case JSON_OBJECT_END:
if (_pHandler) _pHandler->endObject();
return;
case JSON_ARRAY:
if (_pHandler) _pHandler->startArray();
handleArray();
break;
case JSON_ARRAY_END:
if (_pHandler) _pHandler->endArray();
return;
case JSON_ERROR:
{
const char* pErr = json_get_error(&_json);
std::string err(pErr ? pErr : "JSON parser error.");
throw JSONException(err);
}
}
}
#elif defined(JSON_ORG_PARSER)
static const unsigned char UTF8_LEAD_BITS[4] = { 0x00, 0xC0, 0xE0, 0xF0 };
const int ParserImpl::_asciiClass[] =
{
xx, xx, xx, xx, xx, xx, xx, xx,
xx, C_WHITE, C_WHITE, xx, xx, C_WHITE, xx, xx,
xx, xx, xx, xx, xx, xx, xx, xx,
xx, xx, xx, xx, xx, xx, xx, xx,
C_SPACE, C_ETC, C_QUOTE, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,
C_ETC, C_ETC, C_STAR, C_PLUS, C_COMMA, C_MINUS, C_POINT, C_SLASH,
C_ZERO, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT,
C_DIGIT, C_DIGIT, C_COLON, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,
C_ETC, C_ABCDF, C_ABCDF, C_ABCDF, C_ABCDF, C_E, C_ABCDF, C_ETC,
C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,
C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,
C_ETC, C_ETC, C_ETC, C_LSQRB, C_BACKS, C_RSQRB, C_ETC, C_ETC,
C_ETC, C_LOW_A, C_LOW_B, C_LOW_C, C_LOW_D, C_LOW_E, C_LOW_F, C_ETC,
C_ETC, C_ETC, C_ETC, C_ETC, C_LOW_L, C_ETC, C_LOW_N, C_ETC,
C_ETC, C_ETC, C_LOW_R, C_LOW_S, C_LOW_T, C_LOW_U, C_ETC, C_ETC,
C_ETC, C_ETC, C_ETC, C_LCURB, C_ETC, C_RCURB, C_ETC, C_ETC
};
const int ParserImpl::_stateTransitionTable[NR_STATES][NR_CLASSES] =
{
/*
white 1-9 ABCDF etc
space | { } [ ] : , " \ / + - . 0 | a b c d e f l n r s t u | E | * */
/*start GO*/ {GO,GO,-6,xx,-5,xx,xx,xx,xx,xx,CB,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*ok OK*/ {OK,OK,xx,-8,xx,-7,xx,-3,xx,xx,CB,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*object OB*/ {OB,OB,xx,-9,xx,xx,xx,xx,SB,xx,CB,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*key KE*/ {KE,KE,xx,xx,xx,xx,xx,xx,SB,xx,CB,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*colon CO*/ {CO,CO,xx,xx,xx,xx,-2,xx,xx,xx,CB,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*value VA*/ {VA,VA,-6,xx,-5,xx,xx,xx,SB,xx,CB,xx,MX,xx,ZX,IX,xx,xx,xx,xx,xx,FA,xx,NU,xx,xx,TR,xx,xx,xx,xx,xx},
/*array AR*/ {AR,AR,-6,xx,-5,-7,xx,xx,SB,xx,CB,xx,MX,xx,ZX,IX,xx,xx,xx,xx,xx,FA,xx,NU,xx,xx,TR,xx,xx,xx,xx,xx},
/*string ST*/ {ST,xx,ST,ST,ST,ST,ST,ST,-4,EX,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST},
/*escape EC*/ {xx,xx,xx,xx,xx,xx,xx,xx,ST,ST,ST,xx,xx,xx,xx,xx,xx,ST,xx,xx,xx,ST,xx,ST,ST,xx,ST,U1,xx,xx,xx,xx},
/*u1 U1*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,U2,U2,U2,U2,U2,U2,U2,U2,xx,xx,xx,xx,xx,xx,U2,U2,xx,xx},
/*u2 U2*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,U3,U3,U3,U3,U3,U3,U3,U3,xx,xx,xx,xx,xx,xx,U3,U3,xx,xx},
/*u3 U3*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,U4,U4,U4,U4,U4,U4,U4,U4,xx,xx,xx,xx,xx,xx,U4,U4,xx,xx},
/*u4 U4*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,UC,UC,UC,UC,UC,UC,UC,UC,xx,xx,xx,xx,xx,xx,UC,UC,xx,xx},
/*minus MI*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,ZE,IT,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*zero ZE*/ {OK,OK,xx,-8,xx,-7,xx,-3,xx,xx,CB,xx,xx,DF,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*int IT*/ {OK,OK,xx,-8,xx,-7,xx,-3,xx,xx,CB,xx,xx,DF,IT,IT,xx,xx,xx,xx,DE,xx,xx,xx,xx,xx,xx,xx,xx,DE,xx,xx},
/*frac FR*/ {OK,OK,xx,-8,xx,-7,xx,-3,xx,xx,CB,xx,xx,xx,FR,FR,xx,xx,xx,xx,E1,xx,xx,xx,xx,xx,xx,xx,xx,E1,xx,xx},
/*e E1*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,E2,E2,xx,E3,E3,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*ex E2*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,E3,E3,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*exp E3*/ {OK,OK,xx,-8,xx,-7,xx,-3,xx,xx,xx,xx,xx,xx,E3,E3,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*tr T1*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,T2,xx,xx,xx,xx,xx,xx,xx},
/*tru T2*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,T3,xx,xx,xx,xx},
/*1 T3*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,CB,xx,xx,xx,xx,xx,xx,xx,xx,xx,OK,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*fa F1*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,F2,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*fal F2*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,F3,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*fals F3*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,F4,xx,xx,xx,xx,xx,xx},
/*0 F4*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,CB,xx,xx,xx,xx,xx,xx,xx,xx,xx,OK,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*nu N1*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,N2,xx,xx,xx,xx},
/*nul N2*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,N3,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*null N3*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,CB,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,OK,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*/ C1*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,C2},
/*/* C2*/ {C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C3},
/** C3*/ {C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,CE,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C3},
/*_. FX*/ {OK,OK,xx,-8,xx,-7,xx,-3,xx,xx,xx,xx,xx,xx,FR,FR,xx,xx,xx,xx,E1,xx,xx,xx,xx,xx,xx,xx,xx,E1,xx,xx},
/*\ D1*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,D2,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx},
/*\ D2*/ {xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,U1,xx,xx,xx,xx},
};
ParserImpl::ParserImpl(const Handler::Ptr& pHandler, std::size_t bufSize):
_pHandler(pHandler),
_state(GO),
_beforeCommentState(0),
_type(JSON_T_NONE),
_escaped(0),
_comment(0),
_utf16HighSurrogate(0),
_depth(JSON_UNLIMITED_DEPTH),
_top(-1),
_stack(JSON_PARSER_STACK_SIZE),
_parseBuffer(bufSize),
_decimalPoint('.'),
_allowNullByte(true),
_allowComments(false)
{
_parseBuffer.resize(0);
push(MODE_DONE);
}
ParserImpl::~ParserImpl()
{
}
void ParserImpl::resetImpl()
{
_state = GO;
_beforeCommentState = 0;
_type = JSON_T_NONE;
_escaped = 0;
_utf16HighSurrogate = 0;
_top = -1;
_stack.clear();
_parseBuffer.resize(0);
push(MODE_DONE);
if (_pHandler) _pHandler->reset();
}
Dynamic::Var ParserImpl::parseImpl(const std::string& json)
{
std::string::const_iterator it = json.begin();
std::string::const_iterator end = json.end();
Source<std::string::const_iterator> source(it, end);
int c = 0;
while (source.nextChar(c))
{
if (0 == parseChar(c, source))
throw SyntaxException("JSON syntax error");
}
if (!done())
throw JSONException("JSON syntax error");
return asVarImpl();
}
Dynamic::Var ParserImpl::parseImpl(std::istream& in)
{
std::istreambuf_iterator<char> it(in.rdbuf());
std::istreambuf_iterator<char> end;
Source<std::istreambuf_iterator<char> > source(it, end);
int c = 0;
while (source.nextChar(c))
{
if (0 == parseChar(c, source)) throw JSONException("JSON syntax error");
}
if (!done())
throw JSONException("JSON syntax error");
return asVarImpl();
}
bool ParserImpl::push(int mode)
{
_top += 1;
if (_depth < 0)
{
if (_top >= _stack.size())
_stack.resize(_stack.size() * 2, true);
}
else
{
if (_top >= _depth) return false;
}
_stack[_top] = mode;
return true;
}
bool ParserImpl::pop(int mode)
{
if (_top < 0 || _stack[_top] != mode)
return false;
_top -= 1;
return true;
}
void ParserImpl::clearBuffer()
{
_parseBuffer.resize(0);
}
void ParserImpl::parseBufferPopBackChar()
{
poco_assert(_parseBuffer.size() >= 1);
_parseBuffer.resize(_parseBuffer.size() - 1);
}
void ParserImpl::parseBufferPushBackChar(char c)
{
if (_parseBuffer.size() + 1 >= _parseBuffer.capacity())
_parseBuffer.setCapacity(_parseBuffer.capacity() * 2);
_parseBuffer.append(c);
}
void ParserImpl::addEscapedCharToParseBuffer(CharIntType nextChar)
{
_escaped = 0;
// remove the backslash
parseBufferPopBackChar();
switch (nextChar)
{
case 'b':
parseBufferPushBackChar('\b');
break;
case 'f':
parseBufferPushBackChar('\f');
break;
case 'n':
parseBufferPushBackChar('\n');
break;
case 'r':
parseBufferPushBackChar('\r');
break;
case 't':
parseBufferPushBackChar('\t');
break;
case '"':
parseBufferPushBackChar('"');
break;
case '\\':
parseBufferPushBackChar('\\');
break;
case '/':
parseBufferPushBackChar('/');
break;
case 'u':
parseBufferPushBackChar('\\');
parseBufferPushBackChar('u');
break;
default:
break;
}
}
void ParserImpl::addCharToParseBuffer(CharIntType nextChar, int nextClass)
{
if (_escaped)
{
addEscapedCharToParseBuffer(nextChar);
return;
}
else if (!_comment)
{
if ((_type != JSON_T_NONE) ||
!((nextClass == C_SPACE) || (nextClass == C_WHITE)))
{
parseBufferPushBackChar((char) nextChar);
}
}
}
ParserImpl::CharIntType ParserImpl::decodeUnicodeChar()
{
int i;
unsigned uc = 0;
char* p;
int trailBytes;
poco_assert(_parseBuffer.size() >= 6);
p = &_parseBuffer[_parseBuffer.size() - 4];
for (i = 12; i >= 0; i -= 4, ++p)
{
unsigned x = *p;
if (x >= 'a') x -= ('a' - 10);
else if (x >= 'A') x -= ('A' - 10);
else x &= ~0x30u;
poco_assert(x < 16);
uc |= x << i;
}
if (!_allowNullByte && uc == 0) return 0;
// clear UTF-16 char from buffer
_parseBuffer.resize(_parseBuffer.size() - 6);
if (_utf16HighSurrogate)
{
if (isLowSurrogate(uc))
{
uc = decodeSurrogatePair(_utf16HighSurrogate, uc);
trailBytes = 3;
_utf16HighSurrogate = 0;
}
else // high surrogate without a following low surrogate
{
return 0;
}
}
else
{
if (uc < 0x80)
{
trailBytes = 0;
}
else if (uc < 0x800)
{
trailBytes = 1;
}
else if (isHighSurrogate(uc))
{
// save the high surrogate and wait for the low surrogate
_utf16HighSurrogate = uc;
return 1;
}
else if (isLowSurrogate(uc))
{
// low surrogate without a preceding high surrogate
return 0;
}
else
{
trailBytes = 2;
}
}
_parseBuffer.append((char) ((uc >> (trailBytes * 6)) | UTF8_LEAD_BITS[trailBytes]));
for (i = trailBytes * 6 - 6; i >= 0; i -= 6)
{
_parseBuffer.append((char) (((uc >> i) & 0x3F) | 0x80));
}
return 1;
}
void ParserImpl::parseBuffer()
{
if (_pHandler)
{
int type = _type; // just to silence g++
if (type != JSON_T_NONE)
{
assertNonContainer();
switch (type)
{
case JSON_T_TRUE:
{
_pHandler->value(true);
break;
}
case JSON_T_FALSE:
{
_pHandler->value(false);
break;
}
case JSON_T_NULL:
{
_pHandler->null();
break;
}
case JSON_T_FLOAT:
{
// Float can't end with a dot
if (_parseBuffer[_parseBuffer.size() - 1] == '.') throw SyntaxException("JSON syntax error");
double float_value = NumberParser::parseFloat(std::string(_parseBuffer.begin(), _parseBuffer.size()));
_pHandler->value(float_value);
break;
}
case JSON_T_INTEGER:
{
#if defined(POCO_HAVE_INT64)
std::string numStr(_parseBuffer.begin(), _parseBuffer.size());
try
{
Poco::trimInPlace(numStr);
Int64 value = NumberParser::parse64(numStr);
// if number is 32-bit, then handle as such
if (value > std::numeric_limits<int>::max()
|| value < std::numeric_limits<int>::min())
{
_pHandler->value(value);
}
else
{
_pHandler->value(static_cast<int>(value));
}
}
// try to handle error as unsigned in case of overflow
catch (const SyntaxException&)
{
UInt64 value = NumberParser::parseUnsigned64(numStr);
// if number is 32-bit, then handle as such
if (value > std::numeric_limits<unsigned>::max())
{
_pHandler->value(value);
}
else
{
_pHandler->value(static_cast<unsigned>(value));
}
}
#else
try
{
int value = NumberParser::parse(numStr);
_pHandler->value(value);
}
// try to handle error as unsigned in case of overflow
catch (const SyntaxException&)
{
unsigned value = NumberParser::parseUnsigned(numStr);
_pHandler->value(value);
}
#endif
}
break;
case JSON_T_STRING:
{
_pHandler->value(std::string(_parseBuffer.begin(), _parseBuffer.size()));
break;
}
}
}
}
clearBuffer();
}
int ParserImpl::utf8CheckFirst(char byte)
{
unsigned char u = (unsigned char) byte;
if (u < 0x80)
return 1;
if (0x80 <= u && u <= 0xBF)
{
// second, third or fourth byte of a multi-byte
// sequence, i.e. a "continuation byte"
return 0;
}
else if (u == 0xC0 || u == 0xC1)
{
// overlong encoding of an ASCII byte
return 0;
}
else if (0xC2 <= u && u <= 0xDF)
{
// 2-byte sequence
return 2;
}
else if (0xE0 <= u && u <= 0xEF)
{
// 3-byte sequence
return 3;
}
else if (0xF0 <= u && u <= 0xF4)
{
// 4-byte sequence
return 4;
}
else
{
// u >= 0xF5
// Restricted (start of 4-, 5- or 6-byte sequence) or invalid UTF-8
return 0;
}
}
#endif // JSON_ORG_PARSER
} } // namespace Poco::JSON

803
JSON/src/pd_json.c Normal file
View File

@@ -0,0 +1,803 @@
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include "pd_json.h"
#define json_error(json, format, ...) \
if (!json->error) { \
json->error = 1; \
snprintf(json->errmsg, sizeof(json->errmsg), \
"error: %lu: " format, \
(unsigned long) json->lineno, \
__VA_ARGS__); \
} \
#define STACK_INC 4
static enum json_type
push(json_stream *json, enum json_type type)
{
json->stack_top++;
if (json->stack_top >= json->stack_size) {
struct json_stack *stack;
stack = json->alloc.realloc(json->stack,
(json->stack_size + STACK_INC) * sizeof(*json->stack));
if (stack == NULL) {
json_error(json, "%s", strerror(errno));
return JSON_ERROR;
}
json->stack_size += STACK_INC;
json->stack = stack;
}
json->stack[json->stack_top].type = type;
json->stack[json->stack_top].count = 0;
return type;
}
static enum json_type
pop(json_stream *json, int c, enum json_type expected)
{
if (json->stack == NULL || json->stack[json->stack_top].type != expected) {
json_error(json, "unexpected byte, '%c'", c);
json->alloc.free(json->stack);
return JSON_ERROR;
}
json->stack_top--;
return expected == JSON_ARRAY ? JSON_ARRAY_END : JSON_OBJECT_END;
}
static void pop_all(json_stream *json)
{
json->alloc.free(json->stack);
}
static int buffer_peek(struct json_source *source)
{
if (source->position < source->source.buffer.length)
return source->source.buffer.buffer[source->position];
else
return EOF;
}
static int buffer_get(struct json_source *source)
{
int c = source->peek(source);
source->position++;
return c;
}
static int stream_get(struct json_source *source)
{
source->position++;
return fgetc(source->source.stream.stream);
}
static int stream_peek(struct json_source *source)
{
int c = fgetc(source->source.stream.stream);
ungetc(c, source->source.stream.stream);
return c;
}
static void init(json_stream *json)
{
json->lineno = 1;
json->error = 0;
json->errmsg[0] = '\0';
json->ntokens = 0;
json->next = 0;
json->streaming = true;
json->stack = NULL;
json->stack_top = -1;
json->stack_size = 0;
json->data.string = NULL;
json->data.string_size = 0;
json->data.string_fill = 0;
json->source.position = 0;
json->alloc.malloc = malloc;
json->alloc.realloc = realloc;
json->alloc.free = free;
}
static enum json_type
is_match(json_stream *json, const char *pattern, enum json_type type)
{
for (const char *p = pattern; *p; p++)
if (*p != json->source.get(&json->source))
return JSON_ERROR;
return type;
}
static int pushchar(json_stream *json, int c)
{
if (json->data.string_fill == json->data.string_size) {
size_t size = json->data.string_size * 2;
char *buffer = json->alloc.realloc(json->data.string, size);
if (buffer == NULL) {
json_error(json, "%s", strerror(errno));
return -1;
} else {
json->data.string_size = size;
json->data.string = buffer;
}
}
json->data.string[json->data.string_fill++] = c;
return 0;
}
static int init_string(json_stream *json)
{
json->data.string_fill = 0;
if (json->data.string == NULL) {
json->data.string_size = 1024;
json->data.string = json->alloc.malloc(json->data.string_size);
if (json->data.string == NULL) {
json_error(json, "%s", strerror(errno));
return -1;
}
}
json->data.string[0] = '\0';
return 0;
}
static int encode_utf8(json_stream *json, unsigned long c)
{
if (c < 0x80UL) {
return pushchar(json, c);
} else if (c < 0x0800UL) {
return !((pushchar(json, (c >> 6 & 0x1F) | 0xC0) == 0) &&
(pushchar(json, (c >> 0 & 0x3F) | 0x80) == 0));
} else if (c < 0x010000UL) {
if (c >= 0xd800 && c <= 0xdfff) {
json_error(json, "invalid codepoint %06lx", c);
return -1;
}
return !((pushchar(json, (c >> 12 & 0x0F) | 0xE0) == 0) &&
(pushchar(json, (c >> 6 & 0x3F) | 0x80) == 0) &&
(pushchar(json, (c >> 0 & 0x3F) | 0x80) == 0));
} else if (c < 0x110000UL) {
return !((pushchar(json, (c >> 18 & 0x07) | 0xF0) == 0) &&
(pushchar(json, (c >> 12 & 0x3F) | 0x80) == 0) &&
(pushchar(json, (c >> 6 & 0x3F) | 0x80) == 0) &&
(pushchar(json, (c >> 0 & 0x3F) | 0x80) == 0));
} else {
json_error(json, "can't encode UTF-8 for %06lx", c);
return -1;
}
}
static int hexchar(int c)
{
switch (c) {
case '0': return 0;
case '1': return 1;
case '2': return 2;
case '3': return 3;
case '4': return 4;
case '5': return 5;
case '6': return 6;
case '7': return 7;
case '8': return 8;
case '9': return 9;
case 'a':
case 'A': return 10;
case 'b':
case 'B': return 11;
case 'c':
case 'C': return 12;
case 'd':
case 'D': return 13;
case 'e':
case 'E': return 14;
case 'f':
case 'F': return 15;
default:
return -1;
}
}
static long
read_unicode_cp(json_stream *json)
{
long cp = 0;
int shift = 12;
for (size_t i = 0; i < 4; i++) {
int c = json->source.get(&json->source);
int hc;
if (c == EOF) {
json_error(json, "%s", "unterminated string literal in unicode");
return -1;
} else if ((hc = hexchar(c)) == -1) {
json_error(json, "bad escape unicode byte, '%c'", c);
return -1;
}
cp += hc * (1 << shift);
shift -= 4;
}
return cp;
}
static int read_unicode(json_stream *json)
{
long cp, h, l;
if ((cp = read_unicode_cp(json)) == -1) {
return -1;
}
if (cp >= 0xd800 && cp <= 0xdbff) {
/* This is the high portion of a surrogate pair; we need to read the
* lower portion to get the codepoint
*/
h = cp;
int c = json->source.get(&json->source);
if (c == EOF) {
json_error(json, "%s", "unterminated string literal in unicode");
return -1;
} else if (c != '\\') {
json_error(json, "invalid continuation for surrogate pair: '%c', "
"expected '\\'", c);
return -1;
}
c = json->source.get(&json->source);
if (c == EOF) {
json_error(json, "%s", "unterminated string literal in unicode");
return -1;
} else if (c != 'u') {
json_error(json, "invalid continuation for surrogate pair: '%c', "
"expected 'u'", c);
return -1;
}
if ((l = read_unicode_cp(json)) == -1) {
return -1;
}
if (l < 0xdc00 || l > 0xdfff) {
json_error(json, "invalid surrogate pair continuation \\u%04lx out "
"of range (dc00-dfff)", l);
return -1;
}
cp = ((h - 0xd800) * 0x400) + ((l - 0xdc00) + 0x10000);
} else if (cp >= 0xdc00 && cp <= 0xdfff) {
json_error(json, "dangling surrogate \\u%04lx", cp);
return -1;
}
return encode_utf8(json, cp);
}
int read_escaped(json_stream *json)
{
int c = json->source.get(&json->source);
if (c == EOF) {
json_error(json, "%s", "unterminated string literal in escape");
return -1;
} else if (c == 'u') {
if (read_unicode(json) != 0)
return -1;
} else {
switch (c) {
case '\\':
case 'b':
case 'f':
case 'n':
case 'r':
case 't':
case '/':
case '"':
{
const char *codes = "\\bfnrt/\"";
char *p = strchr(codes, c);
if (pushchar(json, "\\\b\f\n\r\t/\""[p - codes]) != 0)
return -1;
}
break;
default:
json_error(json, "bad escaped byte, '%c'", c);
return -1;
}
}
return 0;
}
static int
char_needs_escaping(int c)
{
if ((c >= 0) && (c < 0x20 || c == 0x22 || c == 0x5c)) {
return 1;
}
return 0;
}
static int
utf8_seq_length(char byte)
{
unsigned char u = (unsigned char) byte;
if (u < 0x80) return 1;
if (0x80 <= u && u <= 0xBF)
{
// second, third or fourth byte of a multi-byte
// sequence, i.e. a "continuation byte"
return 0;
}
else if (u == 0xC0 || u == 0xC1)
{
// overlong encoding of an ASCII byte
return 0;
}
else if (0xC2 <= u && u <= 0xDF)
{
// 2-byte sequence
return 2;
}
else if (0xE0 <= u && u <= 0xEF)
{
// 3-byte sequence
return 3;
}
else if (0xF0 <= u && u <= 0xF4)
{
// 4-byte sequence
return 4;
}
else
{
// u >= 0xF5
// Restricted (start of 4-, 5- or 6-byte sequence) or invalid UTF-8
return 0;
}
}
static int
is_legal_utf8(const unsigned char *bytes, int length)
{
if (0 == bytes || 0 == length) return 0;
unsigned char a;
const unsigned char* srcptr = bytes + length;
switch (length)
{
default:
return 0;
// Everything else falls through when true.
case 4:
if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0;
case 3:
if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0;
case 2:
a = (*--srcptr);
switch (*bytes)
{
case 0xE0:
if (a < 0xA0 || a > 0xBF) return 0;
break;
case 0xED:
if (a < 0x80 || a > 0x9F) return 0;
break;
case 0xF0:
if (a < 0x90 || a > 0xBF) return 0;
break;
case 0xF4:
if (a < 0x80 || a > 0x8F) return 0;
break;
default:
if (a < 0x80 || a > 0xBF) return 0;
}
case 1:
if (*bytes >= 0x80 && *bytes < 0xC2) return 0;
}
return *bytes <= 0xF4;
}
static int
read_utf8(json_stream* json, int next_char)
{
int count = utf8_seq_length(next_char);
if (!count)
{
json_error(json, "%s", "Bad character.");
return -1;
}
char buffer[4];
buffer[0] = next_char;
for (int i = 1; i < count; ++i)
{
buffer[i] = json->source.get(&json->source);;
}
if (!is_legal_utf8((unsigned char*) buffer, count))
{
json_error(json, "%s", "No legal UTF8 found");
return -1;
}
for (int i = 0; i < count; ++i)
{
if (pushchar(json, buffer[i]) != 0)
return -1;
}
return 0;
}
static enum json_type
read_string(json_stream *json)
{
if (init_string(json) != 0)
return JSON_ERROR;
while (1) {
int c = json->source.get(&json->source);
if (c == EOF) {
json_error(json, "%s", "unterminated string literal");
return JSON_ERROR;
} else if (c == '"') {
if (pushchar(json, '\0') == 0)
return JSON_STRING;
else
return JSON_ERROR;
} else if (c == '\\') {
if (read_escaped(json) != 0)
return JSON_ERROR;
} else if ((unsigned) c >= 0x80) {
if (read_utf8(json, c) != 0)
return JSON_ERROR;
} else {
if (char_needs_escaping(c)) {
json_error(json, "%s:%u", "unescaped control character in string", (unsigned)c);
return JSON_ERROR;
}
if (pushchar(json, c) != 0)
return JSON_ERROR;
}
}
return JSON_ERROR;
}
static int
is_digit(int c)
{
return c >= 48 /*0*/ && c <= 57 /*9*/;
}
static int
read_digits(json_stream *json)
{
unsigned nread = 0;
while (is_digit(json->source.peek(&json->source))) {
if (pushchar(json, json->source.get(&json->source)) != 0)
return -1;
nread++;
}
if (nread == 0) {
return -1;
}
return 0;
}
static enum json_type
read_number(json_stream *json, int c)
{
if (pushchar(json, c) != 0)
return JSON_ERROR;
if (c == '-') {
c = json->source.get(&json->source);
if (is_digit(c)) {
return read_number(json, c);
} else {
json_error(json, "unexpected byte, '%c'", c);
}
} else if (strchr("123456789", c) != NULL) {
c = json->source.peek(&json->source);
if (is_digit(c)) {
if (read_digits(json) != 0)
return JSON_ERROR;
}
}
/* Up to decimal or exponent has been read. */
c = json->source.peek(&json->source);
if (strchr(".eE", c) == NULL) {
if (pushchar(json, '\0') != 0)
return JSON_ERROR;
else
return JSON_NUMBER;
}
if (c == '.') {
json->source.get(&json->source); // consume .
if (pushchar(json, c) != 0)
return JSON_ERROR;
if (read_digits(json) != 0)
return JSON_ERROR;
}
/* Check for exponent. */
c = json->source.peek(&json->source);
if (c == 'e' || c == 'E') {
json->source.get(&json->source); // consume e/E
if (pushchar(json, c) != 0)
return JSON_ERROR;
c = json->source.peek(&json->source);
if (c == '+' || c == '-') {
json->source.get(&json->source); // consume
if (pushchar(json, c) != 0)
return JSON_ERROR;
if (read_digits(json) != 0)
return JSON_ERROR;
} else if (is_digit(c)) {
if (read_digits(json) != 0)
return JSON_ERROR;
} else {
json_error(json, "unexpected byte in number, '%c'", c);
return JSON_ERROR;
}
}
if (pushchar(json, '\0') != 0)
return JSON_ERROR;
else
return JSON_NUMBER;
}
static int
json_isspace(int c)
{
switch (c) {
case 0x09:
case 0x0a:
case 0x0d:
case 0x20:
return 1;
}
return 0;
}
/* Returns the next non-whitespace character in the stream. */
static int next(json_stream *json)
{
int c;
while (json_isspace(c = json->source.get(&json->source)))
if (c == '\n')
json->lineno++;
return c;
}
static enum json_type
read_value(json_stream *json, int c)
{
json->ntokens++;
switch (c) {
case EOF:
json_error(json, "%s", "unexpected end of data");
return JSON_ERROR;
case '{':
return push(json, JSON_OBJECT);
case '[':
return push(json, JSON_ARRAY);
case '"':
return read_string(json);
case 'n':
return is_match(json, "ull", JSON_NULL);
case 'f':
return is_match(json, "alse", JSON_FALSE);
case 't':
return is_match(json, "rue", JSON_TRUE);
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
if (init_string(json) != 0)
return JSON_ERROR;
return read_number(json, c);
default:
json_error(json, "unexpected byte, '%c'", c);
return JSON_ERROR;
}
}
enum json_type json_peek(json_stream *json)
{
enum json_type next = json_next(json);
json->next = next;
return next;
}
enum json_type json_next(json_stream *json)
{
if (json->error)
return JSON_ERROR;
if (json->next != 0) {
enum json_type next = json->next;
json->next = 0;
return next;
}
if (json->ntokens > 0 && json->stack_top == (size_t)-1) {
int c;
do {
c = json->source.peek(&json->source);
if (json_isspace(c)) {
c = json->source.get(&json->source);
}
} while (json_isspace(c));
if (!json->streaming && c != EOF) {
return JSON_ERROR;
}
return JSON_DONE;
}
int c = next(json);
if (json->stack == NULL)
return read_value(json, c);
if (json->stack[json->stack_top].type == JSON_ARRAY) {
if (json->stack[json->stack_top].count == 0) {
if (c == ']') {
return pop(json, c, JSON_ARRAY);
}
json->stack[json->stack_top].count++;
return read_value(json, c);
} else if (c == ',') {
json->stack[json->stack_top].count++;
return read_value(json, next(json));
} else if (c == ']') {
return pop(json, c, JSON_ARRAY);
} else {
json_error(json, "unexpected byte, '%c'", c);
return JSON_ERROR;
}
} else if (json->stack[json->stack_top].type == JSON_OBJECT) {
if (json->stack[json->stack_top].count == 0) {
if (c == '}') {
return pop(json, c, JSON_OBJECT);
}
/* No property value pairs yet. */
enum json_type value = read_value(json, c);
if (value != JSON_STRING) {
json_error(json, "%s", "expected property name or '}'");
return JSON_ERROR;
} else {
json->stack[json->stack_top].count++;
return value;
}
} else if ((json->stack[json->stack_top].count % 2) == 0) {
/* Expecting comma followed by property name. */
if (c != ',' && c != '}') {
json_error(json, "%s", "expected ',' or '}'");
return JSON_ERROR;
} else if (c == '}') {
return pop(json, c, JSON_OBJECT);
} else {
enum json_type value = read_value(json, next(json));
if (value != JSON_STRING) {
json_error(json, "%s", "expected property name");
return JSON_ERROR;
} else {
json->stack[json->stack_top].count++;
return value;
}
}
} else if ((json->stack[json->stack_top].count % 2) == 1) {
/* Expecting colon followed by value. */
if (c != ':') {
json_error(json, "%s", "expected ':' after property name");
return JSON_ERROR;
} else {
json->stack[json->stack_top].count++;
return read_value(json, next(json));
}
}
}
json_error(json, "%s", "invalid parser state");
return JSON_ERROR;
}
void json_reset(json_stream *json)
{
pop_all(json);
json->ntokens = 0;
json->error = 0;
json->errmsg[0] = '\0';
}
const char *json_get_string(json_stream *json, size_t *length)
{
if (length != NULL)
*length = json->data.string_fill;
if (json->data.string == NULL)
return "";
else
return json->data.string;
}
double json_get_number(json_stream *json)
{
char *p = json->data.string;
return p == NULL ? 0 : strtod(p, NULL);
}
const char *json_get_error(json_stream *json)
{
return json->error ? json->errmsg : NULL;
}
size_t json_get_lineno(json_stream *json)
{
return json->lineno;
}
size_t json_get_position(json_stream *json)
{
return json->source.position;
}
size_t json_get_depth(json_stream *json)
{
return json->stack_top + 1;
}
void json_open_buffer(json_stream *json, const void *buffer, size_t size)
{
init(json);
json->source.get = buffer_get;
json->source.peek = buffer_peek;
json->source.source.buffer.buffer = buffer;
json->source.source.buffer.length = size;
}
void json_open_string(json_stream *json, const char *string)
{
json_open_buffer(json, string, strlen(string));
}
void json_open_stream(json_stream *json, FILE * stream)
{
init(json);
json->source.get = stream_get;
json->source.peek = stream_peek;
json->source.source.stream.stream = stream;
}
void json_set_allocator(json_stream *json, json_allocator *a)
{
json->alloc = *a;
}
void json_set_streaming(json_stream *json, bool streaming)
{
json->streaming = streaming;
}
void json_close(json_stream *json)
{
pop_all(json);
json->alloc.free(json->data.string);
}