mirror of
https://github.com/pocoproject/poco.git
synced 2025-10-29 20:59:45 +01:00
* * 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:
committed by
GitHub
parent
d9a6954950
commit
238306a6ed
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
717
JSON/src/ParserImpl.cpp
Normal 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
803
JSON/src/pd_json.c
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user