From bde78d0b5996612043b225dac037ce4938238803 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Tue, 22 Dec 2020 20:03:20 +0100 Subject: [PATCH] Prototype working on GCC and Clang --- extras/tests/JsonDeserializer/CMakeLists.txt | 1 + .../JsonDeserializer/JsonDeserializer.cpp | 126 ++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 extras/tests/JsonDeserializer/JsonDeserializer.cpp diff --git a/extras/tests/JsonDeserializer/CMakeLists.txt b/extras/tests/JsonDeserializer/CMakeLists.txt index 805ee0e8..bf387321 100644 --- a/extras/tests/JsonDeserializer/CMakeLists.txt +++ b/extras/tests/JsonDeserializer/CMakeLists.txt @@ -10,6 +10,7 @@ add_executable(JsonDeserializerTests incomplete_input.cpp input_types.cpp invalid_input.cpp + JsonDeserializer.cpp misc.cpp nestingLimit.cpp number.cpp diff --git a/extras/tests/JsonDeserializer/JsonDeserializer.cpp b/extras/tests/JsonDeserializer/JsonDeserializer.cpp new file mode 100644 index 00000000..f40b8cdf --- /dev/null +++ b/extras/tests/JsonDeserializer/JsonDeserializer.cpp @@ -0,0 +1,126 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2020 +// MIT License + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4611) // interaction between '_setjmp' and C++ object + // destruction is non-portable +#endif + +size_t STACK = 1000; + +class IncrementalDeserializer { + struct Reader { + IncrementalDeserializer* _parent; + + public: + Reader(IncrementalDeserializer* parent) : _parent(parent) {} + + Reader(Reader& other) : _parent(other._parent) {} + + int read() { + // std::cout << "read()" << std::endl; + if (_parent->_size == 0) + pause(); + // std::cout << "avail = " << _parent->_size << std::endl; + // std::cout << "avail = " << _parent->_data << std::endl; + int result = _parent->_data[0]; + consume(1); + // std::cout << "read() -> " << result << std::endl; + return result; + } + + size_t readBytes(char* buffer, size_t length) { + // std::cout << "readBytes()" << std::endl; + if (_parent->_size == 0) + pause(); + if (length > _parent->_size) + length = _parent->_size; + memcpy(buffer, _parent->_data, length); + consume(length); + return length; + } + + private: + void pause() { + // std::cout << "pause" << std::endl; + if (setjmp(_parent->_back_to_reader) == 0) { + // std::cout << "paused" << std::endl; + longjmp(_parent->_back_to_program, 1); + } + // std::cout << "resumed" << std::endl; + } + + void consume(size_t n) { + _parent->_data += n; + _parent->_size -= n; + } + }; + + jmp_buf _back_to_program; + jmp_buf _back_to_reader; + JsonDocument* _doc; + const char* _data; + size_t _size; + DeserializationError _result; + + public: + IncrementalDeserializer(JsonDocument& doc) : _doc(&doc) {} + + void start() { + // std::cout << "start()" << std::endl; + _data = 0; + _size = 0; + _result = DeserializationError::IncompleteInput; + if (setjmp(_back_to_program) == 0) { + char space[STACK]; // Reserve enough space for main to run + space[STACK - 1] = 1; // Do not optimize array out of existence + (void)space; + Reader reader(this); + _result = deserializeJson(*_doc, reader); + longjmp(_back_to_program, 2); + } + } + + bool feed(const char* data, size_t size) { + // std::cout << "feed()" << std::endl; + _data = data; + _size = size; + switch (setjmp(_back_to_program)) { + case 0: + longjmp(_back_to_reader, 1); + case 1: + // std::cout << "feed() -> false" << std::endl; + return false; // need more data + default: + // std::cout << "feed() -> true" << std::endl; + return true; // done + } + } + + DeserializationError result() const { + return _result; + } +}; + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +TEST_CASE("prout") { + StaticJsonDocument<1024> doc; + IncrementalDeserializer deserializer(doc); + + SECTION("basics") { + deserializer.start(); + REQUIRE(deserializer.feed("{\"answ", 6) == false); + REQUIRE(deserializer.feed("er\":42}", 7) == true); + REQUIRE(doc["answer"] == 42); + } +}