From 4f6253aa9fec99260b8bb7b9b2e9003f5259b600 Mon Sep 17 00:00:00 2001 From: Daniel Friesel Date: Mon, 17 Sep 2018 10:02:07 +0200 Subject: Import arduinojson and ubjson. Only partially working at the moment --- include/lib/ArduinoJson.h | 19 + include/lib/ArduinoJson/Configuration.hpp | 151 +++++ include/lib/ArduinoJson/Data/Encoding.hpp | 37 ++ .../lib/ArduinoJson/Data/JsonBufferAllocated.hpp | 22 + include/lib/ArduinoJson/Data/JsonFloat.hpp | 18 + include/lib/ArduinoJson/Data/JsonInteger.hpp | 23 + include/lib/ArduinoJson/Data/JsonVariantAs.hpp | 42 ++ .../lib/ArduinoJson/Data/JsonVariantContent.hpp | 27 + .../lib/ArduinoJson/Data/JsonVariantDefault.hpp | 23 + include/lib/ArduinoJson/Data/JsonVariantType.hpp | 27 + include/lib/ArduinoJson/Data/List.hpp | 94 ++++ include/lib/ArduinoJson/Data/ListConstIterator.hpp | 50 ++ include/lib/ArduinoJson/Data/ListIterator.hpp | 60 ++ include/lib/ArduinoJson/Data/ListNode.hpp | 24 + include/lib/ArduinoJson/Data/NonCopyable.hpp | 23 + include/lib/ArduinoJson/Data/ReferenceType.hpp | 24 + include/lib/ArduinoJson/Data/ValueSaver.hpp | 52 ++ .../lib/ArduinoJson/Deserialization/Comments.hpp | 61 ++ .../lib/ArduinoJson/Deserialization/JsonParser.hpp | 102 ++++ .../ArduinoJson/Deserialization/JsonParserImpl.hpp | 189 +++++++ .../ArduinoJson/Deserialization/StringWriter.hpp | 41 ++ include/lib/ArduinoJson/DynamicJsonBuffer.hpp | 170 ++++++ include/lib/ArduinoJson/JsonArray.hpp | 227 ++++++++ include/lib/ArduinoJson/JsonArrayImpl.hpp | 26 + include/lib/ArduinoJson/JsonArraySubscript.hpp | 122 ++++ include/lib/ArduinoJson/JsonBuffer.hpp | 78 +++ include/lib/ArduinoJson/JsonBufferBase.hpp | 127 +++++ include/lib/ArduinoJson/JsonBufferImpl.hpp | 17 + include/lib/ArduinoJson/JsonObject.hpp | 328 +++++++++++ include/lib/ArduinoJson/JsonObjectImpl.hpp | 28 + include/lib/ArduinoJson/JsonObjectSubscript.hpp | 110 ++++ include/lib/ArduinoJson/JsonPair.hpp | 16 + include/lib/ArduinoJson/JsonVariant.hpp | 355 ++++++++++++ include/lib/ArduinoJson/JsonVariantBase.hpp | 24 + include/lib/ArduinoJson/JsonVariantCasts.hpp | 59 ++ include/lib/ArduinoJson/JsonVariantComparisons.hpp | 139 +++++ include/lib/ArduinoJson/JsonVariantImpl.hpp | 126 +++++ include/lib/ArduinoJson/JsonVariantOr.hpp | 52 ++ include/lib/ArduinoJson/JsonVariantSubscripts.hpp | 86 +++ include/lib/ArduinoJson/Polyfills/attributes.hpp | 29 + include/lib/ArduinoJson/Polyfills/ctype.hpp | 18 + include/lib/ArduinoJson/Polyfills/isFloat.hpp | 38 ++ include/lib/ArduinoJson/Polyfills/isInteger.hpp | 19 + include/lib/ArduinoJson/Polyfills/math.hpp | 19 + include/lib/ArduinoJson/Polyfills/parseFloat.hpp | 90 +++ include/lib/ArduinoJson/Polyfills/parseInteger.hpp | 41 ++ include/lib/ArduinoJson/RawJson.hpp | 46 ++ .../lib/ArduinoJson/Serialization/DummyPrint.hpp | 22 + .../Serialization/DynamicStringBuilder.hpp | 35 ++ .../lib/ArduinoJson/Serialization/FloatParts.hpp | 89 +++ .../ArduinoJson/Serialization/IndentedPrint.hpp | 68 +++ .../ArduinoJson/Serialization/JsonPrintable.hpp | 117 ++++ .../ArduinoJson/Serialization/JsonSerializer.hpp | 32 ++ .../Serialization/JsonSerializerImpl.hpp | 103 ++++ .../lib/ArduinoJson/Serialization/JsonWriter.hpp | 155 ++++++ .../lib/ArduinoJson/Serialization/Prettyfier.hpp | 133 +++++ .../Serialization/StaticStringBuilder.hpp | 36 ++ .../Serialization/StreamPrintAdapter.hpp | 39 ++ include/lib/ArduinoJson/StaticJsonBuffer.hpp | 126 +++++ .../lib/ArduinoJson/StringTraits/ArduinoStream.hpp | 61 ++ .../lib/ArduinoJson/StringTraits/CharPointer.hpp | 64 +++ .../lib/ArduinoJson/StringTraits/FlashString.hpp | 61 ++ include/lib/ArduinoJson/StringTraits/StdStream.hpp | 60 ++ include/lib/ArduinoJson/StringTraits/StdString.hpp | 77 +++ .../lib/ArduinoJson/StringTraits/StringTraits.hpp | 36 ++ include/lib/ArduinoJson/TypeTraits/EnableIf.hpp | 19 + include/lib/ArduinoJson/TypeTraits/FloatTraits.hpp | 171 ++++++ include/lib/ArduinoJson/TypeTraits/IsArray.hpp | 24 + include/lib/ArduinoJson/TypeTraits/IsBaseOf.hpp | 27 + include/lib/ArduinoJson/TypeTraits/IsChar.hpp | 23 + include/lib/ArduinoJson/TypeTraits/IsConst.hpp | 21 + .../lib/ArduinoJson/TypeTraits/IsFloatingPoint.hpp | 18 + include/lib/ArduinoJson/TypeTraits/IsIntegral.hpp | 26 + include/lib/ArduinoJson/TypeTraits/IsSame.hpp | 21 + .../ArduinoJson/TypeTraits/IsSignedIntegral.hpp | 28 + .../ArduinoJson/TypeTraits/IsUnsignedIntegral.hpp | 28 + include/lib/ArduinoJson/TypeTraits/IsVariant.hpp | 17 + include/lib/ArduinoJson/TypeTraits/RemoveConst.hpp | 20 + .../lib/ArduinoJson/TypeTraits/RemoveReference.hpp | 20 + include/lib/ArduinoJson/version.hpp | 10 + include/lib/ubjson/ubj.h | 230 ++++++++ include/lib/ubjson/ubj_internal.h | 163 ++++++ src/app/prototest/Makefile.inc | 3 + src/app/prototest/main.cc | 30 + src/lib/ubjson/ubjr.c | 516 +++++++++++++++++ src/lib/ubjson/ubjrw.c | 169 ++++++ src/lib/ubjson/ubjw.c | 618 +++++++++++++++++++++ 87 files changed, 7015 insertions(+) create mode 100644 include/lib/ArduinoJson.h create mode 100644 include/lib/ArduinoJson/Configuration.hpp create mode 100644 include/lib/ArduinoJson/Data/Encoding.hpp create mode 100644 include/lib/ArduinoJson/Data/JsonBufferAllocated.hpp create mode 100644 include/lib/ArduinoJson/Data/JsonFloat.hpp create mode 100644 include/lib/ArduinoJson/Data/JsonInteger.hpp create mode 100644 include/lib/ArduinoJson/Data/JsonVariantAs.hpp create mode 100644 include/lib/ArduinoJson/Data/JsonVariantContent.hpp create mode 100644 include/lib/ArduinoJson/Data/JsonVariantDefault.hpp create mode 100644 include/lib/ArduinoJson/Data/JsonVariantType.hpp create mode 100644 include/lib/ArduinoJson/Data/List.hpp create mode 100644 include/lib/ArduinoJson/Data/ListConstIterator.hpp create mode 100644 include/lib/ArduinoJson/Data/ListIterator.hpp create mode 100644 include/lib/ArduinoJson/Data/ListNode.hpp create mode 100644 include/lib/ArduinoJson/Data/NonCopyable.hpp create mode 100644 include/lib/ArduinoJson/Data/ReferenceType.hpp create mode 100644 include/lib/ArduinoJson/Data/ValueSaver.hpp create mode 100644 include/lib/ArduinoJson/Deserialization/Comments.hpp create mode 100644 include/lib/ArduinoJson/Deserialization/JsonParser.hpp create mode 100644 include/lib/ArduinoJson/Deserialization/JsonParserImpl.hpp create mode 100644 include/lib/ArduinoJson/Deserialization/StringWriter.hpp create mode 100644 include/lib/ArduinoJson/DynamicJsonBuffer.hpp create mode 100644 include/lib/ArduinoJson/JsonArray.hpp create mode 100644 include/lib/ArduinoJson/JsonArrayImpl.hpp create mode 100644 include/lib/ArduinoJson/JsonArraySubscript.hpp create mode 100644 include/lib/ArduinoJson/JsonBuffer.hpp create mode 100644 include/lib/ArduinoJson/JsonBufferBase.hpp create mode 100644 include/lib/ArduinoJson/JsonBufferImpl.hpp create mode 100644 include/lib/ArduinoJson/JsonObject.hpp create mode 100644 include/lib/ArduinoJson/JsonObjectImpl.hpp create mode 100644 include/lib/ArduinoJson/JsonObjectSubscript.hpp create mode 100644 include/lib/ArduinoJson/JsonPair.hpp create mode 100644 include/lib/ArduinoJson/JsonVariant.hpp create mode 100644 include/lib/ArduinoJson/JsonVariantBase.hpp create mode 100644 include/lib/ArduinoJson/JsonVariantCasts.hpp create mode 100644 include/lib/ArduinoJson/JsonVariantComparisons.hpp create mode 100644 include/lib/ArduinoJson/JsonVariantImpl.hpp create mode 100644 include/lib/ArduinoJson/JsonVariantOr.hpp create mode 100644 include/lib/ArduinoJson/JsonVariantSubscripts.hpp create mode 100644 include/lib/ArduinoJson/Polyfills/attributes.hpp create mode 100644 include/lib/ArduinoJson/Polyfills/ctype.hpp create mode 100644 include/lib/ArduinoJson/Polyfills/isFloat.hpp create mode 100644 include/lib/ArduinoJson/Polyfills/isInteger.hpp create mode 100644 include/lib/ArduinoJson/Polyfills/math.hpp create mode 100644 include/lib/ArduinoJson/Polyfills/parseFloat.hpp create mode 100644 include/lib/ArduinoJson/Polyfills/parseInteger.hpp create mode 100644 include/lib/ArduinoJson/RawJson.hpp create mode 100644 include/lib/ArduinoJson/Serialization/DummyPrint.hpp create mode 100644 include/lib/ArduinoJson/Serialization/DynamicStringBuilder.hpp create mode 100644 include/lib/ArduinoJson/Serialization/FloatParts.hpp create mode 100644 include/lib/ArduinoJson/Serialization/IndentedPrint.hpp create mode 100644 include/lib/ArduinoJson/Serialization/JsonPrintable.hpp create mode 100644 include/lib/ArduinoJson/Serialization/JsonSerializer.hpp create mode 100644 include/lib/ArduinoJson/Serialization/JsonSerializerImpl.hpp create mode 100644 include/lib/ArduinoJson/Serialization/JsonWriter.hpp create mode 100644 include/lib/ArduinoJson/Serialization/Prettyfier.hpp create mode 100644 include/lib/ArduinoJson/Serialization/StaticStringBuilder.hpp create mode 100644 include/lib/ArduinoJson/Serialization/StreamPrintAdapter.hpp create mode 100644 include/lib/ArduinoJson/StaticJsonBuffer.hpp create mode 100644 include/lib/ArduinoJson/StringTraits/ArduinoStream.hpp create mode 100644 include/lib/ArduinoJson/StringTraits/CharPointer.hpp create mode 100644 include/lib/ArduinoJson/StringTraits/FlashString.hpp create mode 100644 include/lib/ArduinoJson/StringTraits/StdStream.hpp create mode 100644 include/lib/ArduinoJson/StringTraits/StdString.hpp create mode 100644 include/lib/ArduinoJson/StringTraits/StringTraits.hpp create mode 100644 include/lib/ArduinoJson/TypeTraits/EnableIf.hpp create mode 100644 include/lib/ArduinoJson/TypeTraits/FloatTraits.hpp create mode 100644 include/lib/ArduinoJson/TypeTraits/IsArray.hpp create mode 100644 include/lib/ArduinoJson/TypeTraits/IsBaseOf.hpp create mode 100644 include/lib/ArduinoJson/TypeTraits/IsChar.hpp create mode 100644 include/lib/ArduinoJson/TypeTraits/IsConst.hpp create mode 100644 include/lib/ArduinoJson/TypeTraits/IsFloatingPoint.hpp create mode 100644 include/lib/ArduinoJson/TypeTraits/IsIntegral.hpp create mode 100644 include/lib/ArduinoJson/TypeTraits/IsSame.hpp create mode 100644 include/lib/ArduinoJson/TypeTraits/IsSignedIntegral.hpp create mode 100644 include/lib/ArduinoJson/TypeTraits/IsUnsignedIntegral.hpp create mode 100644 include/lib/ArduinoJson/TypeTraits/IsVariant.hpp create mode 100644 include/lib/ArduinoJson/TypeTraits/RemoveConst.hpp create mode 100644 include/lib/ArduinoJson/TypeTraits/RemoveReference.hpp create mode 100644 include/lib/ArduinoJson/version.hpp create mode 100644 include/lib/ubjson/ubj.h create mode 100644 include/lib/ubjson/ubj_internal.h create mode 100644 src/app/prototest/Makefile.inc create mode 100644 src/app/prototest/main.cc create mode 100644 src/lib/ubjson/ubjr.c create mode 100644 src/lib/ubjson/ubjrw.c create mode 100644 src/lib/ubjson/ubjw.c diff --git a/include/lib/ArduinoJson.h b/include/lib/ArduinoJson.h new file mode 100644 index 0000000..c493c06 --- /dev/null +++ b/include/lib/ArduinoJson.h @@ -0,0 +1,19 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "ArduinoJson/version.hpp" + +#include "ArduinoJson/DynamicJsonBuffer.hpp" +#include "ArduinoJson/JsonArray.hpp" +#include "ArduinoJson/JsonObject.hpp" +#include "ArduinoJson/StaticJsonBuffer.hpp" + +#include "ArduinoJson/Deserialization/JsonParserImpl.hpp" +#include "ArduinoJson/JsonArrayImpl.hpp" +#include "ArduinoJson/JsonBufferImpl.hpp" +#include "ArduinoJson/JsonObjectImpl.hpp" +#include "ArduinoJson/JsonVariantImpl.hpp" +#include "ArduinoJson/Serialization/JsonSerializerImpl.hpp" diff --git a/include/lib/ArduinoJson/Configuration.hpp b/include/lib/ArduinoJson/Configuration.hpp new file mode 100644 index 0000000..82483ad --- /dev/null +++ b/include/lib/ArduinoJson/Configuration.hpp @@ -0,0 +1,151 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +// Small or big machine? +#ifndef ARDUINOJSON_EMBEDDED_MODE +#if defined(ARDUINO) || defined(__IAR_SYSTEMS_ICC__) || defined(__XC) || \ + defined(__ARMCC_VERSION) +#define ARDUINOJSON_EMBEDDED_MODE 1 +#else +#define ARDUINOJSON_EMBEDDED_MODE 0 +#endif +#endif + +#if ARDUINOJSON_EMBEDDED_MODE + +// Store floats by default to reduce the memory usage (issue #134) +#ifndef ARDUINOJSON_USE_DOUBLE +#define ARDUINOJSON_USE_DOUBLE 0 +#endif + +// Store longs by default, because they usually match the size of a float. +#ifndef ARDUINOJSON_USE_LONG_LONG +#define ARDUINOJSON_USE_LONG_LONG 0 +#endif +#ifndef ARDUINOJSON_USE_INT64 +#define ARDUINOJSON_USE_INT64 0 +#endif + +// Embedded systems usually don't have std::string +#ifndef ARDUINOJSON_ENABLE_STD_STRING +#define ARDUINOJSON_ENABLE_STD_STRING 0 +#endif + +// Embedded systems usually don't have std::stream +#ifndef ARDUINOJSON_ENABLE_STD_STREAM +#define ARDUINOJSON_ENABLE_STD_STREAM 0 +#endif + +// Limit nesting as the stack is likely to be small +#ifndef ARDUINOJSON_DEFAULT_NESTING_LIMIT +#define ARDUINOJSON_DEFAULT_NESTING_LIMIT 10 +#endif + +#else // ARDUINOJSON_EMBEDDED_MODE + +// On a computer we have plenty of memory so we can use doubles +#ifndef ARDUINOJSON_USE_DOUBLE +#define ARDUINOJSON_USE_DOUBLE 1 +#endif + +// Use long long when available +#ifndef ARDUINOJSON_USE_LONG_LONG +#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800) +#define ARDUINOJSON_USE_LONG_LONG 1 +#else +#define ARDUINOJSON_USE_LONG_LONG 0 +#endif +#endif + +// Use _int64 on old versions of Visual Studio +#ifndef ARDUINOJSON_USE_INT64 +#if defined(_MSC_VER) && _MSC_VER <= 1700 +#define ARDUINOJSON_USE_INT64 1 +#else +#define ARDUINOJSON_USE_INT64 0 +#endif +#endif + +// On a computer, we can use std::string +#ifndef ARDUINOJSON_ENABLE_STD_STRING +#define ARDUINOJSON_ENABLE_STD_STRING 1 +#endif + +// On a computer, we can assume std::stream +#ifndef ARDUINOJSON_ENABLE_STD_STREAM +#define ARDUINOJSON_ENABLE_STD_STREAM 1 +#endif + +// On a computer, the stack is large so we can increase nesting limit +#ifndef ARDUINOJSON_DEFAULT_NESTING_LIMIT +#define ARDUINOJSON_DEFAULT_NESTING_LIMIT 50 +#endif + +#endif // ARDUINOJSON_EMBEDDED_MODE + +#ifdef ARDUINO + +// Enable support for Arduino String +#ifndef ARDUINOJSON_ENABLE_ARDUINO_STRING +#define ARDUINOJSON_ENABLE_ARDUINO_STRING 1 +#endif + +// Enable support for Arduino Stream +#ifndef ARDUINOJSON_ENABLE_ARDUINO_STREAM +#define ARDUINOJSON_ENABLE_ARDUINO_STREAM 1 +#endif + +#else // ARDUINO + +// Disable support for Arduino String +#ifndef ARDUINOJSON_ENABLE_ARDUINO_STRING +#define ARDUINOJSON_ENABLE_ARDUINO_STRING 0 +#endif + +// Disable support for Arduino Stream +#ifndef ARDUINOJSON_ENABLE_ARDUINO_STREAM +#define ARDUINOJSON_ENABLE_ARDUINO_STREAM 0 +#endif + +#endif // ARDUINO + +#ifndef ARDUINOJSON_ENABLE_PROGMEM +#ifdef PROGMEM +#define ARDUINOJSON_ENABLE_PROGMEM 1 +#else +#define ARDUINOJSON_ENABLE_PROGMEM 0 +#endif +#endif + +#ifndef ARDUINOJSON_ENABLE_ALIGNMENT +#ifdef ARDUINO_ARCH_AVR +// alignment isn't needed for 8-bit AVR +#define ARDUINOJSON_ENABLE_ALIGNMENT 0 +#else +// but most processors need pointers to be align on word size +#define ARDUINOJSON_ENABLE_ALIGNMENT 1 +#endif +#endif + +// Enable deprecated functions by default +#ifndef ARDUINOJSON_ENABLE_DEPRECATED +#define ARDUINOJSON_ENABLE_DEPRECATED 1 +#endif + +// Control the exponentiation threshold for big numbers +// CAUTION: cannot be more that 1e9 !!!! +#ifndef ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD +#define ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD 1e7 +#endif + +// Control the exponentiation threshold for small numbers +#ifndef ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD +#define ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD 1e-5 +#endif + +#if ARDUINOJSON_USE_LONG_LONG && ARDUINOJSON_USE_INT64 +#error ARDUINOJSON_USE_LONG_LONG and ARDUINOJSON_USE_INT64 cannot be set together +#endif diff --git a/include/lib/ArduinoJson/Data/Encoding.hpp b/include/lib/ArduinoJson/Data/Encoding.hpp new file mode 100644 index 0000000..a0efa2c --- /dev/null +++ b/include/lib/ArduinoJson/Data/Encoding.hpp @@ -0,0 +1,37 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ArduinoJson { +namespace Internals { + +class Encoding { + public: + // Optimized for code size on a 8-bit AVR + static char escapeChar(char c) { + const char *p = escapeTable(false); + while (p[0] && p[1] != c) { + p += 2; + } + return p[0]; + } + + // Optimized for code size on a 8-bit AVR + static char unescapeChar(char c) { + const char *p = escapeTable(true); + for (;;) { + if (p[0] == '\0') return c; + if (p[0] == c) return p[1]; + p += 2; + } + } + + private: + static const char *escapeTable(bool excludeIdenticals) { + return &"\"\"\\\\b\bf\fn\nr\rt\t"[excludeIdenticals ? 4 : 0]; + } +}; +} +} diff --git a/include/lib/ArduinoJson/Data/JsonBufferAllocated.hpp b/include/lib/ArduinoJson/Data/JsonBufferAllocated.hpp new file mode 100644 index 0000000..443aae4 --- /dev/null +++ b/include/lib/ArduinoJson/Data/JsonBufferAllocated.hpp @@ -0,0 +1,22 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "../JsonBuffer.hpp" + +namespace ArduinoJson { +namespace Internals { + +class JsonBufferAllocated { + public: + void *operator new(size_t n, JsonBuffer *jsonBuffer) throw() { + if (!jsonBuffer) return NULL; + return jsonBuffer->alloc(n); + } + + void operator delete(void *, JsonBuffer *)throw(); +}; +} +} diff --git a/include/lib/ArduinoJson/Data/JsonFloat.hpp b/include/lib/ArduinoJson/Data/JsonFloat.hpp new file mode 100644 index 0000000..0ed4214 --- /dev/null +++ b/include/lib/ArduinoJson/Data/JsonFloat.hpp @@ -0,0 +1,18 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "../Configuration.hpp" + +namespace ArduinoJson { +namespace Internals { + +#if ARDUINOJSON_USE_DOUBLE +typedef double JsonFloat; +#else +typedef float JsonFloat; +#endif +} +} diff --git a/include/lib/ArduinoJson/Data/JsonInteger.hpp b/include/lib/ArduinoJson/Data/JsonInteger.hpp new file mode 100644 index 0000000..c8ddd00 --- /dev/null +++ b/include/lib/ArduinoJson/Data/JsonInteger.hpp @@ -0,0 +1,23 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "../Configuration.hpp" + +namespace ArduinoJson { +namespace Internals { + +#if ARDUINOJSON_USE_LONG_LONG +typedef long long JsonInteger; +typedef unsigned long long JsonUInt; +#elif ARDUINOJSON_USE_INT64 +typedef __int64 JsonInteger; +typedef unsigned _int64 JsonUInt; +#else +typedef long JsonInteger; +typedef unsigned long JsonUInt; +#endif +} +} diff --git a/include/lib/ArduinoJson/Data/JsonVariantAs.hpp b/include/lib/ArduinoJson/Data/JsonVariantAs.hpp new file mode 100644 index 0000000..8f202c5 --- /dev/null +++ b/include/lib/ArduinoJson/Data/JsonVariantAs.hpp @@ -0,0 +1,42 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ArduinoJson { +namespace Internals { + +// A metafunction that returns the type of the value returned by +// JsonVariant::as() +template +struct JsonVariantAs { + typedef T type; +}; + +template <> +struct JsonVariantAs { + typedef const char* type; +}; + +template <> +struct JsonVariantAs { + typedef JsonArray& type; +}; + +template <> +struct JsonVariantAs { + typedef const JsonArray& type; +}; + +template <> +struct JsonVariantAs { + typedef JsonObject& type; +}; + +template <> +struct JsonVariantAs { + typedef const JsonObject& type; +}; +} +} diff --git a/include/lib/ArduinoJson/Data/JsonVariantContent.hpp b/include/lib/ArduinoJson/Data/JsonVariantContent.hpp new file mode 100644 index 0000000..c525a60 --- /dev/null +++ b/include/lib/ArduinoJson/Data/JsonVariantContent.hpp @@ -0,0 +1,27 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "JsonFloat.hpp" +#include "JsonInteger.hpp" + +namespace ArduinoJson { + +// Forward declarations +class JsonArray; +class JsonObject; + +namespace Internals { +// A union that defines the actual content of a JsonVariant. +// The enum JsonVariantType determines which member is in use. +union JsonVariantContent { + JsonFloat asFloat; // used for double and float + JsonUInt asInteger; // used for bool, char, short, int and longs + const char* asString; // asString can be null + JsonArray* asArray; // asArray cannot be null + JsonObject* asObject; // asObject cannot be null +}; +} +} diff --git a/include/lib/ArduinoJson/Data/JsonVariantDefault.hpp b/include/lib/ArduinoJson/Data/JsonVariantDefault.hpp new file mode 100644 index 0000000..57ecc83 --- /dev/null +++ b/include/lib/ArduinoJson/Data/JsonVariantDefault.hpp @@ -0,0 +1,23 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ArduinoJson { +namespace Internals { + +template +struct JsonVariantDefault { + static T get() { + return T(); + } +}; + +template +struct JsonVariantDefault : JsonVariantDefault {}; + +template +struct JsonVariantDefault : JsonVariantDefault {}; +} +} diff --git a/include/lib/ArduinoJson/Data/JsonVariantType.hpp b/include/lib/ArduinoJson/Data/JsonVariantType.hpp new file mode 100644 index 0000000..21f890e --- /dev/null +++ b/include/lib/ArduinoJson/Data/JsonVariantType.hpp @@ -0,0 +1,27 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ArduinoJson { +class JsonArray; +class JsonObject; + +namespace Internals { + +// Enumerated type to know the current type of a JsonVariant. +// The value determines which member of JsonVariantContent is used. +enum JsonVariantType { + JSON_UNDEFINED, // JsonVariant has not been initialized + JSON_UNPARSED, // JsonVariant contains an unparsed string + JSON_STRING, // JsonVariant stores a const char* + JSON_BOOLEAN, // JsonVariant stores a bool + JSON_POSITIVE_INTEGER, // JsonVariant stores an JsonUInt + JSON_NEGATIVE_INTEGER, // JsonVariant stores an JsonUInt that must be negated + JSON_ARRAY, // JsonVariant stores a pointer to a JsonArray + JSON_OBJECT, // JsonVariant stores a pointer to a JsonObject + JSON_FLOAT // JsonVariant stores a JsonFloat +}; +} +} diff --git a/include/lib/ArduinoJson/Data/List.hpp b/include/lib/ArduinoJson/Data/List.hpp new file mode 100644 index 0000000..506308c --- /dev/null +++ b/include/lib/ArduinoJson/Data/List.hpp @@ -0,0 +1,94 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "../JsonBuffer.hpp" +#include "ListConstIterator.hpp" +#include "ListIterator.hpp" + +namespace ArduinoJson { +namespace Internals { + +// A singly linked list of T. +// The linked list is composed of ListNode. +// It is derived by JsonArray and JsonObject +template +class List { + public: + typedef T value_type; + typedef ListNode node_type; + typedef ListIterator iterator; + typedef ListConstIterator const_iterator; + + // Creates an empty List attached to a JsonBuffer. + // The JsonBuffer allows to allocate new nodes. + // When buffer is NULL, the List is not able to grow and success() returns + // false. This is used to identify bad memory allocations and parsing + // failures. + explicit List(JsonBuffer *buffer) : _buffer(buffer), _firstNode(NULL) {} + + // Returns true if the object is valid + // Would return false in the following situation: + // - the memory allocation failed (StaticJsonBuffer was too small) + // - the JSON parsing failed + bool success() const { + return _buffer != NULL; + } + + // Returns the numbers of elements in the list. + // For a JsonObject, it would return the number of key-value pairs + size_t size() const { + size_t nodeCount = 0; + for (node_type *node = _firstNode; node; node = node->next) nodeCount++; + return nodeCount; + } + + iterator add() { + node_type *newNode = new (_buffer) node_type(); + + if (_firstNode) { + node_type *lastNode = _firstNode; + while (lastNode->next) lastNode = lastNode->next; + lastNode->next = newNode; + } else { + _firstNode = newNode; + } + + return iterator(newNode); + } + + iterator begin() { + return iterator(_firstNode); + } + iterator end() { + return iterator(NULL); + } + + const_iterator begin() const { + return const_iterator(_firstNode); + } + const_iterator end() const { + return const_iterator(NULL); + } + + void remove(iterator it) { + node_type *nodeToRemove = it._node; + if (!nodeToRemove) return; + if (nodeToRemove == _firstNode) { + _firstNode = nodeToRemove->next; + } else { + for (node_type *node = _firstNode; node; node = node->next) + if (node->next == nodeToRemove) node->next = nodeToRemove->next; + } + } + + protected: + JsonBuffer *_buffer; + + private: + node_type *_firstNode; +}; +} +} diff --git a/include/lib/ArduinoJson/Data/ListConstIterator.hpp b/include/lib/ArduinoJson/Data/ListConstIterator.hpp new file mode 100644 index 0000000..a6af685 --- /dev/null +++ b/include/lib/ArduinoJson/Data/ListConstIterator.hpp @@ -0,0 +1,50 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "ListNode.hpp" + +namespace ArduinoJson { +namespace Internals { + +// A read-only forward itertor for List +template +class ListConstIterator { + public: + explicit ListConstIterator(const ListNode *node = NULL) : _node(node) {} + + const T &operator*() const { + return _node->content; + } + const T *operator->() { + return &_node->content; + } + + bool operator==(const ListConstIterator &other) const { + return _node == other._node; + } + + bool operator!=(const ListConstIterator &other) const { + return _node != other._node; + } + + ListConstIterator &operator++() { + if (_node) _node = _node->next; + return *this; + } + + ListConstIterator &operator+=(size_t distance) { + while (_node && distance) { + _node = _node->next; + --distance; + } + return *this; + } + + private: + const ListNode *_node; +}; +} +} diff --git a/include/lib/ArduinoJson/Data/ListIterator.hpp b/include/lib/ArduinoJson/Data/ListIterator.hpp new file mode 100644 index 0000000..01fa287 --- /dev/null +++ b/include/lib/ArduinoJson/Data/ListIterator.hpp @@ -0,0 +1,60 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "ListConstIterator.hpp" +#include "ListNode.hpp" + +namespace ArduinoJson { +namespace Internals { + +template +class List; + +// A read-write forward iterator for List +template +class ListIterator { + friend class List; + + public: + explicit ListIterator(ListNode *node = NULL) : _node(node) {} + + T &operator*() const { + return _node->content; + } + T *operator->() { + return &_node->content; + } + + bool operator==(const ListIterator &other) const { + return _node == other._node; + } + + bool operator!=(const ListIterator &other) const { + return _node != other._node; + } + + ListIterator &operator++() { + if (_node) _node = _node->next; + return *this; + } + + ListIterator &operator+=(size_t distance) { + while (_node && distance) { + _node = _node->next; + --distance; + } + return *this; + } + + operator ListConstIterator() const { + return ListConstIterator(_node); + } + + private: + ListNode *_node; +}; +} +} diff --git a/include/lib/ArduinoJson/Data/ListNode.hpp b/include/lib/ArduinoJson/Data/ListNode.hpp new file mode 100644 index 0000000..c090712 --- /dev/null +++ b/include/lib/ArduinoJson/Data/ListNode.hpp @@ -0,0 +1,24 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include // for NULL + +#include "JsonBufferAllocated.hpp" + +namespace ArduinoJson { +namespace Internals { + +// A node for a singly-linked list. +// Used by List and its iterators. +template +struct ListNode : public Internals::JsonBufferAllocated { + ListNode() throw() : next(NULL) {} + + ListNode *next; + T content; +}; +} +} diff --git a/include/lib/ArduinoJson/Data/NonCopyable.hpp b/include/lib/ArduinoJson/Data/NonCopyable.hpp new file mode 100644 index 0000000..73f3d8e --- /dev/null +++ b/include/lib/ArduinoJson/Data/NonCopyable.hpp @@ -0,0 +1,23 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ArduinoJson { +namespace Internals { + +// A type that cannot be copied +class NonCopyable { + protected: + NonCopyable() {} + + private: + // copy constructor is private + NonCopyable(const NonCopyable&); + + // copy operator is private + NonCopyable& operator=(const NonCopyable&); +}; +} +} diff --git a/include/lib/ArduinoJson/Data/ReferenceType.hpp b/include/lib/ArduinoJson/Data/ReferenceType.hpp new file mode 100644 index 0000000..1e49117 --- /dev/null +++ b/include/lib/ArduinoJson/Data/ReferenceType.hpp @@ -0,0 +1,24 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ArduinoJson { +namespace Internals { + +// A type that is meant to be used by reference only (JsonArray and JsonObject) +class ReferenceType { + public: + bool operator==(const ReferenceType& other) const { + // two JsonArray are equal if they are the same instance + // (we don't compare the content) + return this == &other; + } + + bool operator!=(const ReferenceType& other) const { + return this != &other; + } +}; +} +} diff --git a/include/lib/ArduinoJson/Data/ValueSaver.hpp b/include/lib/ArduinoJson/Data/ValueSaver.hpp new file mode 100644 index 0000000..9750f1a --- /dev/null +++ b/include/lib/ArduinoJson/Data/ValueSaver.hpp @@ -0,0 +1,52 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "../JsonBuffer.hpp" +#include "../JsonVariant.hpp" +#include "../StringTraits/StringTraits.hpp" +#include "../TypeTraits/EnableIf.hpp" + +namespace ArduinoJson { +namespace Internals { + +template +struct ValueSaver { + template + static bool save(JsonBuffer*, Destination& destination, Source source) { + destination = source; + return true; + } +}; + +template +struct ValueSaver< + Source, typename EnableIf::should_duplicate>::type> { + template + static bool save(JsonBuffer* buffer, Destination& dest, Source source) { + if (!StringTraits::is_null(source)) { + typename StringTraits::duplicate_t dup = + StringTraits::duplicate(source, buffer); + if (!dup) return false; + dest = dup; + } else { + dest = reinterpret_cast(0); + } + return true; + } +}; + +// const char*, const signed char*, const unsigned char* +template +struct ValueSaver< + Char*, typename EnableIf::should_duplicate>::type> { + template + static bool save(JsonBuffer*, Destination& dest, Char* source) { + dest = reinterpret_cast(source); + return true; + } +}; +} +} diff --git a/include/lib/ArduinoJson/Deserialization/Comments.hpp b/include/lib/ArduinoJson/Deserialization/Comments.hpp new file mode 100644 index 0000000..c2c48eb --- /dev/null +++ b/include/lib/ArduinoJson/Deserialization/Comments.hpp @@ -0,0 +1,61 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ArduinoJson { +namespace Internals { +template +void skipSpacesAndComments(TInput& input) { + for (;;) { + switch (input.current()) { + // spaces + case ' ': + case '\t': + case '\r': + case '\n': + input.move(); + continue; + + // comments + case '/': + switch (input.next()) { + // C-style block comment + case '*': + input.move(); // skip '/' + // no need to skip '*' + for (;;) { + input.move(); + if (input.current() == '\0') return; + if (input.current() == '*' && input.next() == '/') { + input.move(); // skip '*' + input.move(); // skip '/' + break; + } + } + break; + + // C++-style line comment + case '/': + // not need to skip "//" + for (;;) { + input.move(); + if (input.current() == '\0') return; + if (input.current() == '\n') break; + } + break; + + // not a comment, just a '/' + default: + return; + } + break; + + default: + return; + } + } +} +} +} diff --git a/include/lib/ArduinoJson/Deserialization/JsonParser.hpp b/include/lib/ArduinoJson/Deserialization/JsonParser.hpp new file mode 100644 index 0000000..4cbaf45 --- /dev/null +++ b/include/lib/ArduinoJson/Deserialization/JsonParser.hpp @@ -0,0 +1,102 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "../JsonBuffer.hpp" +#include "../JsonVariant.hpp" +#include "../TypeTraits/IsConst.hpp" +#include "StringWriter.hpp" + +namespace ArduinoJson { +namespace Internals { + +// Parse JSON string to create JsonArrays and JsonObjects +// This internal class is not indended to be used directly. +// Instead, use JsonBuffer.parseArray() or .parseObject() +template +class JsonParser { + public: + JsonParser(JsonBuffer *buffer, TReader reader, TWriter writer, + uint8_t nestingLimit) + : _buffer(buffer), + _reader(reader), + _writer(writer), + _nestingLimit(nestingLimit) {} + + JsonArray &parseArray(); + JsonObject &parseObject(); + + JsonVariant parseVariant() { + JsonVariant result; + parseAnythingTo(&result); + return result; + } + + private: + JsonParser &operator=(const JsonParser &); // non-copiable + + static bool eat(TReader &, char charToSkip); + FORCE_INLINE bool eat(char charToSkip) { + return eat(_reader, charToSkip); + } + + const char *parseString(); + bool parseAnythingTo(JsonVariant *destination); + + inline bool parseArrayTo(JsonVariant *destination); + inline bool parseObjectTo(JsonVariant *destination); + inline bool parseStringTo(JsonVariant *destination); + + static inline bool isBetween(char c, char min, char max) { + return min <= c && c <= max; + } + + static inline bool canBeInNonQuotedString(char c) { + return isBetween(c, '0', '9') || isBetween(c, '_', 'z') || + isBetween(c, 'A', 'Z') || c == '+' || c == '-' || c == '.'; + } + + static inline bool isQuote(char c) { + return c == '\'' || c == '\"'; + } + + JsonBuffer *_buffer; + TReader _reader; + TWriter _writer; + uint8_t _nestingLimit; +}; + +template +struct JsonParserBuilder { + typedef typename StringTraits::Reader InputReader; + typedef JsonParser TParser; + + static TParser makeParser(TJsonBuffer *buffer, TString &json, + uint8_t nestingLimit) { + return TParser(buffer, InputReader(json), *buffer, nestingLimit); + } +}; + +template +struct JsonParserBuilder::value>::type> { + typedef typename StringTraits::Reader TReader; + typedef StringWriter TWriter; + typedef JsonParser TParser; + + static TParser makeParser(TJsonBuffer *buffer, TChar *json, + uint8_t nestingLimit) { + return TParser(buffer, TReader(json), TWriter(json), nestingLimit); + } +}; + +template +inline typename JsonParserBuilder::TParser makeParser( + TJsonBuffer *buffer, TString &json, uint8_t nestingLimit) { + return JsonParserBuilder::makeParser(buffer, json, + nestingLimit); +} +} // namespace Internals +} // namespace ArduinoJson diff --git a/include/lib/ArduinoJson/Deserialization/JsonParserImpl.hpp b/include/lib/ArduinoJson/Deserialization/JsonParserImpl.hpp new file mode 100644 index 0000000..5042673 --- /dev/null +++ b/include/lib/ArduinoJson/Deserialization/JsonParserImpl.hpp @@ -0,0 +1,189 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "Comments.hpp" +#include "JsonParser.hpp" + +template +inline bool ArduinoJson::Internals::JsonParser::eat( + TReader &reader, char charToSkip) { + skipSpacesAndComments(reader); + if (reader.current() != charToSkip) return false; + reader.move(); + return true; +} + +template +inline bool +ArduinoJson::Internals::JsonParser::parseAnythingTo( + JsonVariant *destination) { + skipSpacesAndComments(_reader); + + switch (_reader.current()) { + case '[': + return parseArrayTo(destination); + + case '{': + return parseObjectTo(destination); + + default: + return parseStringTo(destination); + } +} + +template +inline ArduinoJson::JsonArray & +ArduinoJson::Internals::JsonParser::parseArray() { + if (_nestingLimit == 0) return JsonArray::invalid(); + _nestingLimit--; + + // Create an empty array + JsonArray &array = _buffer->createArray(); + + // Check opening braket + if (!eat('[')) goto ERROR_MISSING_BRACKET; + if (eat(']')) goto SUCCESS_EMPTY_ARRAY; + + // Read each value + for (;;) { + // 1 - Parse value + JsonVariant value; + if (!parseAnythingTo(&value)) goto ERROR_INVALID_VALUE; + if (!array.add(value)) goto ERROR_NO_MEMORY; + + // 2 - More values? + if (eat(']')) goto SUCCES_NON_EMPTY_ARRAY; + if (!eat(',')) goto ERROR_MISSING_COMMA; + } + +SUCCESS_EMPTY_ARRAY: +SUCCES_NON_EMPTY_ARRAY: + _nestingLimit++; + return array; + +ERROR_INVALID_VALUE: +ERROR_MISSING_BRACKET: +ERROR_MISSING_COMMA: +ERROR_NO_MEMORY: + return JsonArray::invalid(); +} + +template +inline bool ArduinoJson::Internals::JsonParser::parseArrayTo( + JsonVariant *destination) { + JsonArray &array = parseArray(); + if (!array.success()) return false; + + *destination = array; + return true; +} + +template +inline ArduinoJson::JsonObject & +ArduinoJson::Internals::JsonParser::parseObject() { + if (_nestingLimit == 0) return JsonObject::invalid(); + _nestingLimit--; + + // Create an empty object + JsonObject &object = _buffer->createObject(); + + // Check opening brace + if (!eat('{')) goto ERROR_MISSING_BRACE; + if (eat('}')) goto SUCCESS_EMPTY_OBJECT; + + // Read each key value pair + for (;;) { + // 1 - Parse key + const char *key = parseString(); + if (!key) goto ERROR_INVALID_KEY; + if (!eat(':')) goto ERROR_MISSING_COLON; + + // 2 - Parse value + JsonVariant value; + if (!parseAnythingTo(&value)) goto ERROR_INVALID_VALUE; + if (!object.set(key, value)) goto ERROR_NO_MEMORY; + + // 3 - More keys/values? + if (eat('}')) goto SUCCESS_NON_EMPTY_OBJECT; + if (!eat(',')) goto ERROR_MISSING_COMMA; + } + +SUCCESS_EMPTY_OBJECT: +SUCCESS_NON_EMPTY_OBJECT: + _nestingLimit++; + return object; + +ERROR_INVALID_KEY: +ERROR_INVALID_VALUE: +ERROR_MISSING_BRACE: +ERROR_MISSING_COLON: +ERROR_MISSING_COMMA: +ERROR_NO_MEMORY: + return JsonObject::invalid(); +} + +template +inline bool ArduinoJson::Internals::JsonParser::parseObjectTo( + JsonVariant *destination) { + JsonObject &object = parseObject(); + if (!object.success()) return false; + + *destination = object; + return true; +} + +template +inline const char * +ArduinoJson::Internals::JsonParser::parseString() { + typename RemoveReference::type::String str = _writer.startString(); + + skipSpacesAndComments(_reader); + char c = _reader.current(); + + if (isQuote(c)) { // quotes + _reader.move(); + char stopChar = c; + for (;;) { + c = _reader.current(); + if (c == '\0') break; + _reader.move(); + + if (c == stopChar) break; + + if (c == '\\') { + // replace char + c = Encoding::unescapeChar(_reader.current()); + if (c == '\0') break; + _reader.move(); + } + + str.append(c); + } + } else { // no quotes + for (;;) { + if (!canBeInNonQuotedString(c)) break; + _reader.move(); + str.append(c); + c = _reader.current(); + } + } + + return str.c_str(); +} + +template +inline bool ArduinoJson::Internals::JsonParser::parseStringTo( + JsonVariant *destination) { + bool hasQuotes = isQuote(_reader.current()); + const char *value = parseString(); + if (value == NULL) return false; + if (hasQuotes) { + *destination = value; + } else { + *destination = RawJson(value); + } + return true; +} diff --git a/include/lib/ArduinoJson/Deserialization/StringWriter.hpp b/include/lib/ArduinoJson/Deserialization/StringWriter.hpp new file mode 100644 index 0000000..fd5507e --- /dev/null +++ b/include/lib/ArduinoJson/Deserialization/StringWriter.hpp @@ -0,0 +1,41 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ArduinoJson { +namespace Internals { + +template +class StringWriter { + public: + class String { + public: + String(TChar** ptr) : _writePtr(ptr), _startPtr(*ptr) {} + + void append(char c) { + *(*_writePtr)++ = TChar(c); + } + + const char* c_str() const { + *(*_writePtr)++ = 0; + return reinterpret_cast(_startPtr); + } + + private: + TChar** _writePtr; + TChar* _startPtr; + }; + + StringWriter(TChar* buffer) : _ptr(buffer) {} + + String startString() { + return String(&_ptr); + } + + private: + TChar* _ptr; +}; +} +} diff --git a/include/lib/ArduinoJson/DynamicJsonBuffer.hpp b/include/lib/ArduinoJson/DynamicJsonBuffer.hpp new file mode 100644 index 0000000..bdbd5dd --- /dev/null +++ b/include/lib/ArduinoJson/DynamicJsonBuffer.hpp @@ -0,0 +1,170 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "JsonBufferBase.hpp" + +#include + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnon-virtual-dtor" +#elif defined(__GNUC__) +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +#pragma GCC diagnostic push +#endif +#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" +#endif + +namespace ArduinoJson { +namespace Internals { +class DefaultAllocator { + public: + void* allocate(size_t size) { + return malloc(size); + } + void deallocate(void* pointer) { + free(pointer); + } +}; + +template +class DynamicJsonBufferBase + : public JsonBufferBase > { + struct Block; + struct EmptyBlock { + Block* next; + size_t capacity; + size_t size; + }; + struct Block : EmptyBlock { + uint8_t data[1]; + }; + + public: + enum { EmptyBlockSize = sizeof(EmptyBlock) }; + + DynamicJsonBufferBase(size_t initialSize = 256) + : _head(NULL), _nextBlockCapacity(initialSize) {} + + ~DynamicJsonBufferBase() { + clear(); + } + + // Gets the number of bytes occupied in the buffer + size_t size() const { + size_t total = 0; + for (const Block* b = _head; b; b = b->next) total += b->size; + return total; + } + + // Allocates the specified amount of bytes in the buffer + virtual void* alloc(size_t bytes) { + alignNextAlloc(); + return canAllocInHead(bytes) ? allocInHead(bytes) : allocInNewBlock(bytes); + } + + // Resets the buffer. + // USE WITH CAUTION: this invalidates all previously allocated data + void clear() { + Block* currentBlock = _head; + while (currentBlock != NULL) { + _nextBlockCapacity = currentBlock->capacity; + Block* nextBlock = currentBlock->next; + _allocator.deallocate(currentBlock); + currentBlock = nextBlock; + } + _head = 0; + } + + class String { + public: + String(DynamicJsonBufferBase* parent) + : _parent(parent), _start(NULL), _length(0) {} + + void append(char c) { + if (_parent->canAllocInHead(1)) { + char* end = static_cast(_parent->allocInHead(1)); + *end = c; + if (_length == 0) _start = end; + } else { + char* newStart = + static_cast(_parent->allocInNewBlock(_length + 1)); + if (_start && newStart) memcpy(newStart, _start, _length); + if (newStart) newStart[_length] = c; + _start = newStart; + } + _length++; + } + + const char* c_str() { + append(0); + return _start; + } + + private: + DynamicJsonBufferBase* _parent; + char* _start; + size_t _length; + }; + + String startString() { + return String(this); + } + + private: + void alignNextAlloc() { + if (_head) _head->size = this->round_size_up(_head->size); + } + + bool canAllocInHead(size_t bytes) const { + return _head != NULL && _head->size + bytes <= _head->capacity; + } + + void* allocInHead(size_t bytes) { + void* p = _head->data + _head->size; + _head->size += bytes; + return p; + } + + void* allocInNewBlock(size_t bytes) { + size_t capacity = _nextBlockCapacity; + if (bytes > capacity) capacity = bytes; + if (!addNewBlock(capacity)) return NULL; + _nextBlockCapacity *= 2; + return allocInHead(bytes); + } + + bool addNewBlock(size_t capacity) { + size_t bytes = EmptyBlockSize + capacity; + Block* block = static_cast(_allocator.allocate(bytes)); + if (block == NULL) return false; + block->capacity = capacity; + block->size = 0; + block->next = _head; + _head = block; + return true; + } + + TAllocator _allocator; + Block* _head; + size_t _nextBlockCapacity; +}; +} + +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +#pragma GCC diagnostic pop +#endif +#endif + +// Implements a JsonBuffer with dynamic memory allocation. +// You are strongly encouraged to consider using StaticJsonBuffer which is much +// more suitable for embedded systems. +typedef Internals::DynamicJsonBufferBase + DynamicJsonBuffer; +} diff --git a/include/lib/ArduinoJson/JsonArray.hpp b/include/lib/ArduinoJson/JsonArray.hpp new file mode 100644 index 0000000..2acd2a1 --- /dev/null +++ b/include/lib/ArduinoJson/JsonArray.hpp @@ -0,0 +1,227 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "Data/JsonBufferAllocated.hpp" +#include "Data/List.hpp" +#include "Data/ReferenceType.hpp" +#include "Data/ValueSaver.hpp" +#include "JsonVariant.hpp" +#include "Serialization/JsonPrintable.hpp" +#include "StringTraits/StringTraits.hpp" +#include "TypeTraits/EnableIf.hpp" +#include "TypeTraits/IsArray.hpp" +#include "TypeTraits/IsFloatingPoint.hpp" +#include "TypeTraits/IsSame.hpp" + +// Returns the size (in bytes) of an array with n elements. +// Can be very handy to determine the size of a StaticJsonBuffer. +#define JSON_ARRAY_SIZE(NUMBER_OF_ELEMENTS) \ + (sizeof(JsonArray) + (NUMBER_OF_ELEMENTS) * sizeof(JsonArray::node_type)) + +namespace ArduinoJson { + +// Forward declarations +class JsonObject; +class JsonBuffer; +namespace Internals { +class JsonArraySubscript; +} + +// An array of JsonVariant. +// +// The constructor is private, instances must be created via +// JsonBuffer::createArray() or JsonBuffer::parseArray(). +// A JsonArray can be serialized to a JSON string via JsonArray::printTo(). +// It can also be deserialized from a JSON string via JsonBuffer::parseArray(). +class JsonArray : public Internals::JsonPrintable, + public Internals::ReferenceType, + public Internals::NonCopyable, + public Internals::List, + public Internals::JsonBufferAllocated { + public: + // Create an empty JsonArray attached to the specified JsonBuffer. + // You should not call this constructor directly. + // Instead, use JsonBuffer::createArray() or JsonBuffer::parseArray(). + explicit JsonArray(JsonBuffer *buffer) throw() + : Internals::List(buffer) {} + + // Gets the value at the specified index + const Internals::JsonArraySubscript operator[](size_t index) const; + + // Gets or sets the value at specified index + Internals::JsonArraySubscript operator[](size_t index); + + // Adds the specified value at the end of the array. + // + // bool add(TValue); + // TValue = bool, long, int, short, float, double, RawJson, JsonVariant, + // std::string, String, JsonArray, JsonObject + template + bool add(const T &value) { + return add_impl(value); + } + // + // bool add(TValue); + // TValue = char*, const char*, const FlashStringHelper* + template + bool add(T *value) { + return add_impl(value); + } + // + // bool add(TValue value, uint8_t decimals); + // TValue = float, double + template + DEPRECATED("Second argument is not supported anymore") + bool add(T value, uint8_t) { + return add_impl(JsonVariant(value)); + } + + // Sets the value at specified index. + // + // bool add(size_t index, const TValue&); + // TValue = bool, long, int, short, float, double, RawJson, JsonVariant, + // std::string, String, JsonArray, JsonObject + template + bool set(size_t index, const T &value) { + return set_impl(index, value); + } + // + // bool add(size_t index, TValue); + // TValue = char*, const char*, const FlashStringHelper* + template + bool set(size_t index, T *value) { + return set_impl(index, value); + } + // + // bool set(size_t index, TValue value, uint8_t decimals); + // TValue = float, double + template + typename Internals::EnableIf::value, bool>::type + set(size_t index, T value, uint8_t decimals) { + return set_impl(index, JsonVariant(value, decimals)); + } + + // Gets the value at the specified index. + template + typename Internals::JsonVariantAs::type get(size_t index) const { + const_iterator it = begin() += index; + return it != end() ? it->as() : Internals::JsonVariantDefault::get(); + } + + // Check the type of the value at specified index. + template + bool is(size_t index) const { + const_iterator it = begin() += index; + return it != end() ? it->is() : false; + } + + // Creates a JsonArray and adds a reference at the end of the array. + // It's a shortcut for JsonBuffer::createArray() and JsonArray::add() + JsonArray &createNestedArray(); + + // Creates a JsonObject and adds a reference at the end of the array. + // It's a shortcut for JsonBuffer::createObject() and JsonArray::add() + JsonObject &createNestedObject(); + + // Removes element at specified index. + void remove(size_t index) { + remove(begin() += index); + } + using Internals::List::remove; + + // Returns a reference an invalid JsonArray. + // This object is meant to replace a NULL pointer. + // This is used when memory allocation or JSON parsing fail. + static JsonArray &invalid() { + static JsonArray instance(NULL); + return instance; + } + + // Imports a 1D array + template + bool copyFrom(T (&array)[N]) { + return copyFrom(array, N); + } + + // Imports a 1D array + template + bool copyFrom(T *array, size_t len) { + bool ok = true; + for (size_t i = 0; i < len; i++) { + ok &= add(array[i]); + } + return ok; + } + + // Imports a 2D array + template + bool copyFrom(T (&array)[N1][N2]) { + bool ok = true; + for (size_t i = 0; i < N1; i++) { + JsonArray &nestedArray = createNestedArray(); + for (size_t j = 0; j < N2; j++) { + ok &= nestedArray.add(array[i][j]); + } + } + return ok; + } + + // Exports a 1D array + template + size_t copyTo(T (&array)[N]) const { + return copyTo(array, N); + } + + // Exports a 1D array + template + size_t copyTo(T *array, size_t len) const { + size_t i = 0; + for (const_iterator it = begin(); it != end() && i < len; ++it) + array[i++] = *it; + return i; + } + + // Exports a 2D array + template + void copyTo(T (&array)[N1][N2]) const { + size_t i = 0; + for (const_iterator it = begin(); it != end() && i < N1; ++it) { + it->as().copyTo(array[i++]); + } + } + +#if ARDUINOJSON_ENABLE_DEPRECATED + DEPRECATED("use remove() instead") + FORCE_INLINE void removeAt(size_t index) { + return remove(index); + } +#endif + + private: + template + bool set_impl(size_t index, TValueRef value) { + iterator it = begin() += index; + if (it == end()) return false; + return Internals::ValueSaver::save(_buffer, *it, value); + } + + template + bool add_impl(TValueRef value) { + iterator it = Internals::List::add(); + if (it == end()) return false; + return Internals::ValueSaver::save(_buffer, *it, value); + } +}; + +namespace Internals { +template <> +struct JsonVariantDefault { + static JsonArray &get() { + return JsonArray::invalid(); + } +}; +} +} diff --git a/include/lib/ArduinoJson/JsonArrayImpl.hpp b/include/lib/ArduinoJson/JsonArrayImpl.hpp new file mode 100644 index 0000000..924b7ea --- /dev/null +++ b/include/lib/ArduinoJson/JsonArrayImpl.hpp @@ -0,0 +1,26 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "JsonArray.hpp" +#include "JsonArraySubscript.hpp" +#include "JsonObject.hpp" + +namespace ArduinoJson { + +inline JsonArray &JsonArray::createNestedArray() { + if (!_buffer) return JsonArray::invalid(); + JsonArray &array = _buffer->createArray(); + add(array); + return array; +} + +inline JsonObject &JsonArray::createNestedObject() { + if (!_buffer) return JsonObject::invalid(); + JsonObject &object = _buffer->createObject(); + add(object); + return object; +} +} diff --git a/include/lib/ArduinoJson/JsonArraySubscript.hpp b/include/lib/ArduinoJson/JsonArraySubscript.hpp new file mode 100644 index 0000000..afb4dc1 --- /dev/null +++ b/include/lib/ArduinoJson/JsonArraySubscript.hpp @@ -0,0 +1,122 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "Configuration.hpp" +#include "JsonVariantBase.hpp" + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4522) +#endif + +namespace ArduinoJson { +namespace Internals { +class JsonArraySubscript : public JsonVariantBase { + public: + FORCE_INLINE JsonArraySubscript(JsonArray& array, size_t index) + : _array(array), _index(index) {} + + FORCE_INLINE JsonArraySubscript& operator=(const JsonArraySubscript& src) { + _array.set(_index, src); + return *this; + } + + // Replaces the value + // + // operator=(const TValue&) + // TValue = bool, long, int, short, float, double, RawJson, JsonVariant, + // std::string, String, JsonArray, JsonObject + template + FORCE_INLINE JsonArraySubscript& operator=(const T& src) { + _array.set(_index, src); + return *this; + } + // + // operator=(TValue) + // TValue = char*, const char*, const FlashStringHelper* + template + FORCE_INLINE JsonArraySubscript& operator=(T* src) { + _array.set(_index, src); + return *this; + } + + FORCE_INLINE bool success() const { + return _index < _array.size(); + } + + template + FORCE_INLINE typename JsonVariantAs::type as() const { + return _array.get(_index); + } + + template + FORCE_INLINE bool is() const { + return _array.is(_index); + } + + // Replaces the value + // + // bool set(const TValue&) + // TValue = bool, long, int, short, float, double, RawJson, JsonVariant, + // std::string, String, JsonArray, JsonObject + template + FORCE_INLINE bool set(const TValue& value) { + return _array.set(_index, value); + } + // + // bool set(TValue) + // TValue = char*, const char*, const FlashStringHelper* + template + FORCE_INLINE bool set(TValue* value) { + return _array.set(_index, value); + } + // + // bool set(TValue, uint8_t decimals); + // TValue = float, double + template + DEPRECATED("Second argument is not supported anymore") + FORCE_INLINE bool set(const TValue& value, uint8_t) { + return _array.set(_index, value); + } + + private: + JsonArray& _array; + const size_t _index; +}; + +template +inline JsonArraySubscript JsonVariantSubscripts::operator[]( + size_t index) { + return impl()->template as()[index]; +} + +template +inline const JsonArraySubscript JsonVariantSubscripts::operator[]( + size_t index) const { + return impl()->template as()[index]; +} + +#if ARDUINOJSON_ENABLE_STD_STREAM +inline std::ostream& operator<<(std::ostream& os, + const JsonArraySubscript& source) { + return source.printTo(os); +} +#endif +} + +inline Internals::JsonArraySubscript JsonArray::operator[](size_t index) { + return Internals::JsonArraySubscript(*this, index); +} + +inline const Internals::JsonArraySubscript JsonArray::operator[]( + size_t index) const { + return Internals::JsonArraySubscript(*const_cast(this), index); +} +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif diff --git a/include/lib/ArduinoJson/JsonBuffer.hpp b/include/lib/ArduinoJson/JsonBuffer.hpp new file mode 100644 index 0000000..26101e0 --- /dev/null +++ b/include/lib/ArduinoJson/JsonBuffer.hpp @@ -0,0 +1,78 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include // for size_t +#include // for uint8_t +#include + +#include "Data/NonCopyable.hpp" +#include "JsonVariant.hpp" +#include "TypeTraits/EnableIf.hpp" +#include "TypeTraits/IsArray.hpp" + +namespace ArduinoJson { +class JsonArray; +class JsonObject; + +// Entry point for using the library. +// +// Handle the memory management (done in derived classes) and calls the parser. +// This abstract class is implemented by StaticJsonBuffer which implements a +// fixed memory allocation. +class JsonBuffer : Internals::NonCopyable { + public: + // Allocates an empty JsonArray. + // + // Returns a reference to the new JsonArray or JsonArray::invalid() if the + // allocation fails. + JsonArray &createArray(); + + // Allocates an empty JsonObject. + // + // Returns a reference to the new JsonObject or JsonObject::invalid() if the + // allocation fails. + JsonObject &createObject(); + + // Duplicates a string + // + // const char* strdup(TValue); + // TValue = const std::string&, const String&, + template + DEPRECATED("char* are duplicated, you don't need strdup() anymore") + typename Internals::EnableIf::value, + const char *>::type strdup(const TString &src) { + return Internals::StringTraits::duplicate(src, this); + } + // + // const char* strdup(TValue); + // TValue = char*, const char*, const FlashStringHelper* + template + DEPRECATED("char* are duplicated, you don't need strdup() anymore") + const char *strdup(TString *src) { + return Internals::StringTraits::duplicate(src, this); + } + + // Allocates n bytes in the JsonBuffer. + // Return a pointer to the allocated memory or NULL if allocation fails. + virtual void *alloc(size_t size) = 0; + + protected: + // CAUTION: NO VIRTUAL DESTRUCTOR! + // If we add a virtual constructor the Arduino compiler will add malloc() + // and free() to the binary, adding 706 useless bytes. + ~JsonBuffer() {} + + // Preserve aligment if necessary + static FORCE_INLINE size_t round_size_up(size_t bytes) { +#if ARDUINOJSON_ENABLE_ALIGNMENT + const size_t x = sizeof(void *) - 1; + return (bytes + x) & ~x; +#else + return bytes; +#endif + } +}; +} diff --git a/include/lib/ArduinoJson/JsonBufferBase.hpp b/include/lib/ArduinoJson/JsonBufferBase.hpp new file mode 100644 index 0000000..1e771bf --- /dev/null +++ b/include/lib/ArduinoJson/JsonBufferBase.hpp @@ -0,0 +1,127 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "Deserialization/JsonParser.hpp" + +namespace ArduinoJson { +namespace Internals { +template +class JsonBufferBase : public JsonBuffer { + public: + // Allocates and populate a JsonArray from a JSON string. + // + // The First argument is a pointer to the JSON string, the memory must be + // writable + // because the parser will insert null-terminators and replace escaped chars. + // + // The second argument set the nesting limit + // + // Returns a reference to the new JsonObject or JsonObject::invalid() if the + // allocation fails. + // With this overload, the JsonBuffer will make a copy of the string + // + // JsonArray& parseArray(TString); + // TString = const std::string&, const String& + template + typename Internals::EnableIf::value, + JsonArray &>::type + parseArray(const TString &json, + uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { + return Internals::makeParser(that(), json, nestingLimit).parseArray(); + } + // + // JsonArray& parseArray(TString); + // TString = const char*, const char[N], const FlashStringHelper* + template + JsonArray &parseArray( + TString *json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { + return Internals::makeParser(that(), json, nestingLimit).parseArray(); + } + // + // JsonArray& parseArray(TString); + // TString = std::istream&, Stream& + template + JsonArray &parseArray( + TString &json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { + return Internals::makeParser(that(), json, nestingLimit).parseArray(); + } + + // Allocates and populate a JsonObject from a JSON string. + // + // The First argument is a pointer to the JSON string, the memory must be + // writable + // because the parser will insert null-terminators and replace escaped chars. + // + // The second argument set the nesting limit + // + // Returns a reference to the new JsonObject or JsonObject::invalid() if the + // allocation fails. + // + // JsonObject& parseObject(TString); + // TString = const std::string&, const String& + template + typename Internals::EnableIf::value, + JsonObject &>::type + parseObject(const TString &json, + uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { + return Internals::makeParser(that(), json, nestingLimit).parseObject(); + } + // + // JsonObject& parseObject(TString); + // TString = const char*, const char[N], const FlashStringHelper* + template + JsonObject &parseObject( + TString *json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { + return Internals::makeParser(that(), json, nestingLimit).parseObject(); + } + // + // JsonObject& parseObject(TString); + // TString = std::istream&, Stream& + template + JsonObject &parseObject( + TString &json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { + return Internals::makeParser(that(), json, nestingLimit).parseObject(); + } + + // Generalized version of parseArray() and parseObject(), also works for + // integral types. + // + // JsonVariant parse(TString); + // TString = const std::string&, const String& + template + typename Internals::EnableIf::value, + JsonVariant>::type + parse(const TString &json, + uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { + return Internals::makeParser(that(), json, nestingLimit).parseVariant(); + } + // + // JsonVariant parse(TString); + // TString = const char*, const char[N], const FlashStringHelper* + template + JsonVariant parse(TString *json, + uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { + return Internals::makeParser(that(), json, nestingLimit).parseVariant(); + } + // + // JsonVariant parse(TString); + // TString = std::istream&, Stream& + template + JsonVariant parse(TString &json, + uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { + return Internals::makeParser(that(), json, nestingLimit).parseVariant(); + } + + protected: + ~JsonBufferBase() {} + + private: + TDerived *that() { + return static_cast(this); + } +}; +} +} diff --git a/include/lib/ArduinoJson/JsonBufferImpl.hpp b/include/lib/ArduinoJson/JsonBufferImpl.hpp new file mode 100644 index 0000000..cdea374 --- /dev/null +++ b/include/lib/ArduinoJson/JsonBufferImpl.hpp @@ -0,0 +1,17 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "Deserialization/JsonParser.hpp" + +inline ArduinoJson::JsonArray &ArduinoJson::JsonBuffer::createArray() { + JsonArray *ptr = new (this) JsonArray(this); + return ptr ? *ptr : JsonArray::invalid(); +} + +inline ArduinoJson::JsonObject &ArduinoJson::JsonBuffer::createObject() { + JsonObject *ptr = new (this) JsonObject(this); + return ptr ? *ptr : JsonObject::invalid(); +} diff --git a/include/lib/ArduinoJson/JsonObject.hpp b/include/lib/ArduinoJson/JsonObject.hpp new file mode 100644 index 0000000..caf698a --- /dev/null +++ b/include/lib/ArduinoJson/JsonObject.hpp @@ -0,0 +1,328 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "Data/JsonBufferAllocated.hpp" +#include "Data/List.hpp" +#include "Data/ReferenceType.hpp" +#include "Data/ValueSaver.hpp" +#include "JsonPair.hpp" +#include "Serialization/JsonPrintable.hpp" +#include "StringTraits/StringTraits.hpp" +#include "TypeTraits/EnableIf.hpp" +#include "TypeTraits/IsArray.hpp" +#include "TypeTraits/IsFloatingPoint.hpp" +#include "TypeTraits/IsSame.hpp" + +// Returns the size (in bytes) of an object with n elements. +// Can be very handy to determine the size of a StaticJsonBuffer. +#define JSON_OBJECT_SIZE(NUMBER_OF_ELEMENTS) \ + (sizeof(JsonObject) + (NUMBER_OF_ELEMENTS) * sizeof(JsonObject::node_type)) + +namespace ArduinoJson { + +// Forward declarations +class JsonArray; +class JsonBuffer; +namespace Internals { +template +class JsonObjectSubscript; +} + +// A dictionary of JsonVariant indexed by string (char*) +// +// The constructor is private, instances must be created via +// JsonBuffer::createObject() or JsonBuffer::parseObject(). +// A JsonObject can be serialized to a JSON string via JsonObject::printTo(). +// It can also be deserialized from a JSON string via JsonBuffer::parseObject(). +class JsonObject : public Internals::JsonPrintable, + public Internals::ReferenceType, + public Internals::NonCopyable, + public Internals::List, + public Internals::JsonBufferAllocated { + public: + // Create an empty JsonArray attached to the specified JsonBuffer. + // You should not use this constructor directly. + // Instead, use JsonBuffer::createObject() or JsonBuffer.parseObject(). + explicit JsonObject(JsonBuffer* buffer) throw() + : Internals::List(buffer) {} + + // Gets or sets the value associated with the specified key. + // + // JsonObjectSubscript operator[](TKey) + // TKey = const std::string&, const String& + template + Internals::JsonObjectSubscript operator[]( + const TString& key) { + return Internals::JsonObjectSubscript(*this, key); + } + // + // JsonObjectSubscript operator[](TKey) + // TKey = char*, const char*, char[], const char[N], const FlashStringHelper* + template + Internals::JsonObjectSubscript operator[](TString* key) { + return Internals::JsonObjectSubscript(*this, key); + } + + // Gets the value associated with the specified key. + // + // const JsonObjectSubscript operator[](TKey) const; + // TKey = const std::string&, const String& + template + const Internals::JsonObjectSubscript operator[]( + const TString& key) const { + return Internals::JsonObjectSubscript( + *const_cast(this), key); + } + // + // const JsonObjectSubscript operator[](TKey) const; + // TKey = const char*, const char[N], const FlashStringHelper* + template + const Internals::JsonObjectSubscript operator[]( + TString* key) const { + return Internals::JsonObjectSubscript( + *const_cast(this), key); + } + + // Sets the specified key with the specified value. + // + // bool set(TKey, TValue); + // TKey = const std::string&, const String& + // TValue = bool, long, int, short, float, double, RawJson, JsonVariant, + // std::string, String, JsonArray, JsonObject + template + bool set(const TString& key, const TValue& value) { + return set_impl(key, value); + } + // + // bool set(TKey, TValue); + // TKey = const std::string&, const String& + // TValue = char*, const char*, const FlashStringHelper* + template + bool set(const TString& key, TValue* value) { + return set_impl(key, value); + } + // + // bool set(TKey, const TValue&); + // TKey = char*, const char*, const FlashStringHelper* + // TValue = bool, long, int, short, float, double, RawJson, JsonVariant, + // std::string, String, JsonArray, JsonObject + template + bool set(TString* key, const TValue& value) { + return set_impl(key, value); + } + // + // bool set(TKey, TValue); + // TKey = char*, const char*, const FlashStringHelper* + // TValue = char*, const char*, const FlashStringHelper* + template + bool set(TString* key, TValue* value) { + return set_impl(key, value); + } + // + // bool set(TKey, TValue, uint8_t decimals); + // TKey = const std::string&, const String& + // TValue = float, double + template + DEPRECATED("Second argument is not supported anymore") + typename Internals::EnableIf::value, + bool>::type + set(const TString& key, TValue value, uint8_t) { + return set_impl(key, + JsonVariant(value)); + } + // + // bool set(TKey, TValue, uint8_t decimals); + // TKey = char*, const char*, const FlashStringHelper* + // TValue = float, double + template + DEPRECATED("Second argument is not supported anymore") + typename Internals::EnableIf::value, + bool>::type + set(TString* key, TValue value, uint8_t) { + return set_impl(key, JsonVariant(value)); + } + + // Gets the value associated with the specified key. + // + // TValue get(TKey) const; + // TKey = const std::string&, const String& + // TValue = bool, char, long, int, short, float, double, + // std::string, String, JsonArray, JsonObject + template + typename Internals::JsonVariantAs::type get( + const TString& key) const { + return get_impl(key); + } + // + // TValue get(TKey) const; + // TKey = char*, const char*, const FlashStringHelper* + // TValue = bool, char, long, int, short, float, double, + // std::string, String, JsonArray, JsonObject + template + typename Internals::JsonVariantAs::type get(TString* key) const { + return get_impl(key); + } + + // Checks the type of the value associated with the specified key. + // + // + // bool is(TKey) const; + // TKey = const std::string&, const String& + // TValue = bool, char, long, int, short, float, double, + // std::string, String, JsonArray, JsonObject + template + bool is(const TString& key) const { + return is_impl(key); + } + // + // bool is(TKey) const; + // TKey = char*, const char*, const FlashStringHelper* + // TValue = bool, char, long, int, short, float, double, + // std::string, String, JsonArray, JsonObject + template + bool is(TString* key) const { + return is_impl(key); + } + + // Creates and adds a JsonArray. + // + // JsonArray& createNestedArray(TKey); + // TKey = const std::string&, const String& + template + JsonArray& createNestedArray(const TString& key) { + return createNestedArray_impl(key); + } + // JsonArray& createNestedArray(TKey); + // TKey = char*, const char*, char[], const char[], const FlashStringHelper* + template + JsonArray& createNestedArray(TString* key) { + return createNestedArray_impl(key); + } + + // Creates and adds a JsonObject. + // + // JsonObject& createNestedObject(TKey); + // TKey = const std::string&, const String& + template + JsonObject& createNestedObject(const TString& key) { + return createNestedObject_impl(key); + } + // + // JsonObject& createNestedObject(TKey); + // TKey = char*, const char*, char[], const char[], const FlashStringHelper* + template + JsonObject& createNestedObject(TString* key) { + return createNestedObject_impl(key); + } + + // Tells weither the specified key is present and associated with a value. + // + // bool containsKey(TKey); + // TKey = const std::string&, const String& + template + bool containsKey(const TString& key) const { + return findKey(key) != end(); + } + // + // bool containsKey(TKey); + // TKey = char*, const char*, char[], const char[], const FlashStringHelper* + template + bool containsKey(TString* key) const { + return findKey(key) != end(); + } + + // Removes the specified key and the associated value. + // + // void remove(TKey); + // TKey = const std::string&, const String& + template + void remove(const TString& key) { + remove(findKey(key)); + } + // + // void remove(TKey); + // TKey = char*, const char*, char[], const char[], const FlashStringHelper* + template + void remove(TString* key) { + remove(findKey(key)); + } + // + // void remove(iterator) + using Internals::List::remove; + + // Returns a reference an invalid JsonObject. + // This object is meant to replace a NULL pointer. + // This is used when memory allocation or JSON parsing fail. + static JsonObject& invalid() { + static JsonObject instance(NULL); + return instance; + } + + private: + // Returns the list node that matches the specified key. + template + iterator findKey(TStringRef key) { + iterator it; + for (it = begin(); it != end(); ++it) { + if (Internals::StringTraits::equals(key, it->key)) break; + } + return it; + } + template + const_iterator findKey(TStringRef key) const { + return const_cast(this)->findKey(key); + } + + template + typename Internals::JsonVariantAs::type get_impl( + TStringRef key) const { + const_iterator it = findKey(key); + return it != end() ? it->value.as() + : Internals::JsonVariantDefault::get(); + } + + template + bool set_impl(TStringRef key, TValueRef value) { + // ignore null key + if (Internals::StringTraits::is_null(key)) return false; + + // search a matching key + iterator it = findKey(key); + if (it == end()) { + // add the key + it = Internals::List::add(); + if (it == end()) return false; + bool key_ok = + Internals::ValueSaver::save(_buffer, it->key, key); + if (!key_ok) return false; + } + + // save the value + return Internals::ValueSaver::save(_buffer, it->value, value); + } + + template + bool is_impl(TStringRef key) const { + const_iterator it = findKey(key); + return it != end() ? it->value.is() : false; + } + + template + JsonArray& createNestedArray_impl(TStringRef key); + + template + JsonObject& createNestedObject_impl(TStringRef key); +}; + +namespace Internals { +template <> +struct JsonVariantDefault { + static JsonObject& get() { + return JsonObject::invalid(); + } +}; +} // namespace Internals +} // namespace ArduinoJson diff --git a/include/lib/ArduinoJson/JsonObjectImpl.hpp b/include/lib/ArduinoJson/JsonObjectImpl.hpp new file mode 100644 index 0000000..e7689b5 --- /dev/null +++ b/include/lib/ArduinoJson/JsonObjectImpl.hpp @@ -0,0 +1,28 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "JsonArray.hpp" +#include "JsonObject.hpp" +#include "JsonObjectSubscript.hpp" + +namespace ArduinoJson { + +template +inline JsonArray &JsonObject::createNestedArray_impl(TStringRef key) { + if (!_buffer) return JsonArray::invalid(); + JsonArray &array = _buffer->createArray(); + set(key, array); + return array; +} + +template +inline JsonObject &JsonObject::createNestedObject_impl(TStringRef key) { + if (!_buffer) return JsonObject::invalid(); + JsonObject &object = _buffer->createObject(); + set(key, object); + return object; +} +} diff --git a/include/lib/ArduinoJson/JsonObjectSubscript.hpp b/include/lib/ArduinoJson/JsonObjectSubscript.hpp new file mode 100644 index 0000000..6ac4763 --- /dev/null +++ b/include/lib/ArduinoJson/JsonObjectSubscript.hpp @@ -0,0 +1,110 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "Configuration.hpp" +#include "JsonVariantBase.hpp" +#include "TypeTraits/EnableIf.hpp" + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4522) +#endif + +namespace ArduinoJson { +namespace Internals { + +template +class JsonObjectSubscript + : public JsonVariantBase > { + typedef JsonObjectSubscript this_type; + + public: + FORCE_INLINE JsonObjectSubscript(JsonObject& object, TStringRef key) + : _object(object), _key(key) {} + + FORCE_INLINE this_type& operator=(const this_type& src) { + _object.set(_key, src); + return *this; + } + + // Set the specified value + // + // operator=(const TValue&); + // TValue = bool, char, long, int, short, float, double, + // std::string, String, JsonArray, JsonObject + template + FORCE_INLINE typename EnableIf::value, this_type&>::type + operator=(const TValue& src) { + _object.set(_key, src); + return *this; + } + // + // operator=(TValue); + // TValue = char*, const char*, const FlashStringHelper* + template + FORCE_INLINE this_type& operator=(TValue* src) { + _object.set(_key, src); + return *this; + } + + FORCE_INLINE bool success() const { + return _object.containsKey(_key); + } + + template + FORCE_INLINE typename JsonVariantAs::type as() const { + return _object.get(_key); + } + + template + FORCE_INLINE bool is() const { + return _object.is(_key); + } + + // Sets the specified value. + // + // bool set(const TValue&); + // TValue = bool, char, long, int, short, float, double, RawJson, JsonVariant, + // std::string, String, JsonArray, JsonObject + template + FORCE_INLINE typename EnableIf::value, bool>::type set( + const TValue& value) { + return _object.set(_key, value); + } + // + // bool set(TValue); + // TValue = char*, const char, const FlashStringHelper* + template + FORCE_INLINE bool set(const TValue* value) { + return _object.set(_key, value); + } + // + // bool set(TValue, uint8_t decimals); + // TValue = float, double + template + DEPRECATED("Second argument is not supported anymore") + FORCE_INLINE bool set(const TValue& value, uint8_t) { + return _object.set(_key, value); + } + + private: + JsonObject& _object; + TStringRef _key; +}; + +#if ARDUINOJSON_ENABLE_STD_STREAM +template +inline std::ostream& operator<<(std::ostream& os, + const JsonObjectSubscript& source) { + return source.printTo(os); +} +#endif +} +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif diff --git a/include/lib/ArduinoJson/JsonPair.hpp b/include/lib/ArduinoJson/JsonPair.hpp new file mode 100644 index 0000000..4172430 --- /dev/null +++ b/include/lib/ArduinoJson/JsonPair.hpp @@ -0,0 +1,16 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "JsonVariant.hpp" + +namespace ArduinoJson { + +// A key value pair for JsonObject. +struct JsonPair { + const char* key; + JsonVariant value; +}; +} diff --git a/include/lib/ArduinoJson/JsonVariant.hpp b/include/lib/ArduinoJson/JsonVariant.hpp new file mode 100644 index 0000000..8326cbe --- /dev/null +++ b/include/lib/ArduinoJson/JsonVariant.hpp @@ -0,0 +1,355 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include +#include // for uint8_t + +#include "Data/JsonVariantContent.hpp" +#include "Data/JsonVariantDefault.hpp" +#include "Data/JsonVariantType.hpp" +#include "JsonVariantBase.hpp" +#include "RawJson.hpp" +#include "Serialization/JsonPrintable.hpp" +#include "TypeTraits/EnableIf.hpp" +#include "TypeTraits/IsChar.hpp" +#include "TypeTraits/IsFloatingPoint.hpp" +#include "TypeTraits/IsIntegral.hpp" +#include "TypeTraits/IsSame.hpp" +#include "TypeTraits/IsSignedIntegral.hpp" +#include "TypeTraits/IsUnsignedIntegral.hpp" +#include "TypeTraits/RemoveConst.hpp" +#include "TypeTraits/RemoveReference.hpp" + +namespace ArduinoJson { + +// Forward declarations. +class JsonArray; +class JsonObject; + +// A variant that can be a any value serializable to a JSON value. +// +// It can be set to: +// - a boolean +// - a char, short, int or a long (signed or unsigned) +// - a string (const char*) +// - a reference to a JsonArray or JsonObject +class JsonVariant : public Internals::JsonVariantBase { + template + friend class Internals::JsonSerializer; + + public: + // Creates an uninitialized JsonVariant + JsonVariant() : _type(Internals::JSON_UNDEFINED) {} + + // Create a JsonVariant containing a boolean value. + // It will be serialized as "true" or "false" in JSON. + JsonVariant(bool value) { + using namespace Internals; + _type = JSON_BOOLEAN; + _content.asInteger = static_cast(value); + } + + // Create a JsonVariant containing a floating point value. + // JsonVariant(double value); + // JsonVariant(float value); + template + JsonVariant(T value, typename Internals::EnableIf< + Internals::IsFloatingPoint::value>::type * = 0) { + using namespace Internals; + _type = JSON_FLOAT; + _content.asFloat = static_cast(value); + } + template + DEPRECATED("Second argument is not supported anymore") + JsonVariant(T value, uint8_t, + typename Internals::EnableIf< + Internals::IsFloatingPoint::value>::type * = 0) { + using namespace Internals; + _type = JSON_FLOAT; + _content.asFloat = static_cast(value); + } + + // Create a JsonVariant containing an integer value. + // JsonVariant(char) + // JsonVariant(signed short) + // JsonVariant(signed int) + // JsonVariant(signed long) + // JsonVariant(signed char) + template + JsonVariant( + T value, + typename Internals::EnableIf::value || + Internals::IsSame::value>::type * = + 0) { + using namespace Internals; + if (value >= 0) { + _type = JSON_POSITIVE_INTEGER; + _content.asInteger = static_cast(value); + } else { + _type = JSON_NEGATIVE_INTEGER; + _content.asInteger = static_cast(-value); + } + } + // JsonVariant(unsigned short) + // JsonVariant(unsigned int) + // JsonVariant(unsigned long) + template + JsonVariant(T value, + typename Internals::EnableIf< + Internals::IsUnsignedIntegral::value>::type * = 0) { + using namespace Internals; + _type = JSON_POSITIVE_INTEGER; + _content.asInteger = static_cast(value); + } + + // Create a JsonVariant containing a string. + // JsonVariant(const char*); + // JsonVariant(const signed char*); + // JsonVariant(const unsigned char*); + template + JsonVariant( + const TChar *value, + typename Internals::EnableIf::value>::type * = + 0) { + _type = Internals::JSON_STRING; + _content.asString = reinterpret_cast(value); + } + + // Create a JsonVariant containing an unparsed string + JsonVariant(Internals::RawJsonString value) { + _type = Internals::JSON_UNPARSED; + _content.asString = value; + } + + // Create a JsonVariant containing a reference to an array. + // CAUTION: we are lying about constness, because the array can be modified if + // the variant is converted back to a JsonArray& + JsonVariant(const JsonArray &array); + + // Create a JsonVariant containing a reference to an object. + // CAUTION: we are lying about constness, because the object can be modified + // if the variant is converted back to a JsonObject& + JsonVariant(const JsonObject &object); + + // Get the variant as the specified type. + // + // char as() const; + // signed char as() const; + // signed short as() const; + // signed int as() const; + // signed long as() const; + // unsigned char as() const; + // unsigned short as() const; + // unsigned int as() const; + // unsigned long as() const; + template + const typename Internals::EnableIf::value, T>::type + as() const { + return variantAsInteger(); + } + // bool as() const + template + const typename Internals::EnableIf::value, T>::type + as() const { + return variantAsInteger() != 0; + } + // + // double as() const; + // float as() const; + template + const typename Internals::EnableIf::value, + T>::type + as() const { + return variantAsFloat(); + } + // + // const char* as() const; + // const char* as() const; + template + typename Internals::EnableIf::value || + Internals::IsSame::value, + const char *>::type + as() const { + return variantAsString(); + } + // + // std::string as() const; + // String as() const; + template + typename Internals::EnableIf::has_append, T>::type + as() const { + const char *cstr = variantAsString(); + if (cstr) return T(cstr); + T s; + printTo(s); + return s; + } + // + // JsonArray& as const; + // JsonArray& as const; + template + typename Internals::EnableIf< + Internals::IsSame::type, + JsonArray>::value, + JsonArray &>::type + as() const { + return variantAsArray(); + } + // + // const JsonArray& as const; + template + typename Internals::EnableIf< + Internals::IsSame::type, + const JsonArray>::value, + const JsonArray &>::type + as() const { + return variantAsArray(); + } + // + // JsonObject& as const; + // JsonObject& as const; + template + typename Internals::EnableIf< + Internals::IsSame::type, + JsonObject>::value, + JsonObject &>::type + as() const { + return variantAsObject(); + } + // + // JsonObject& as const; + // JsonObject& as const; + template + typename Internals::EnableIf< + Internals::IsSame::type, + const JsonObject>::value, + const JsonObject &>::type + as() const { + return variantAsObject(); + } + // + // JsonVariant as const; + template + typename Internals::EnableIf::value, + T>::type + as() const { + return *this; + } + + // Tells weither the variant has the specified type. + // Returns true if the variant has type type T, false otherwise. + // + // bool is() const; + // bool is() const; + // bool is() const; + // bool is() const; + // bool is() const; + // bool is() const; + // bool is() const; + // bool is() const; + // bool is() const; + template + typename Internals::EnableIf::value, bool>::type is() + const { + return variantIsInteger(); + } + // + // bool is() const; + // bool is() const; + template + typename Internals::EnableIf::value, bool>::type + is() const { + return variantIsFloat(); + } + // + // bool is() const + template + typename Internals::EnableIf::value, bool>::type + is() const { + return variantIsBoolean(); + } + // + // bool is() const; + // bool is() const; + template + typename Internals::EnableIf::value || + Internals::IsSame::value, + bool>::type + is() const { + return variantIsString(); + } + // + // bool is const; + // bool is const; + // bool is const; + template + typename Internals::EnableIf< + Internals::IsSame::type>::type, + JsonArray>::value, + bool>::type + is() const { + return variantIsArray(); + } + // + // bool is const; + // bool is const; + // bool is const; + template + typename Internals::EnableIf< + Internals::IsSame::type>::type, + JsonObject>::value, + bool>::type + is() const { + return variantIsObject(); + } + + // Returns true if the variant has a value + bool success() const { + return _type != Internals::JSON_UNDEFINED; + } + + private: + JsonArray &variantAsArray() const; + JsonObject &variantAsObject() const; + const char *variantAsString() const; + template + T variantAsFloat() const; + template + T variantAsInteger() const; + bool variantIsBoolean() const; + bool variantIsFloat() const; + bool variantIsInteger() const; + bool variantIsArray() const { + return _type == Internals::JSON_ARRAY; + } + bool variantIsObject() const { + return _type == Internals::JSON_OBJECT; + } + bool variantIsString() const { + return _type == Internals::JSON_STRING || + (_type == Internals::JSON_UNPARSED && _content.asString && + !strcmp("null", _content.asString)); + } + + // The current type of the variant + Internals::JsonVariantType _type; + + // The various alternatives for the value of the variant. + Internals::JsonVariantContent _content; +}; + +DEPRECATED("Decimal places are ignored, use the float value instead") +inline JsonVariant float_with_n_digits(float value, uint8_t) { + return JsonVariant(value); +} + +DEPRECATED("Decimal places are ignored, use the double value instead") +inline JsonVariant double_with_n_digits(double value, uint8_t) { + return JsonVariant(value); +} +} diff --git a/include/lib/ArduinoJson/JsonVariantBase.hpp b/include/lib/ArduinoJson/JsonVariantBase.hpp new file mode 100644 index 0000000..44acf2e --- /dev/null +++ b/include/lib/ArduinoJson/JsonVariantBase.hpp @@ -0,0 +1,24 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "JsonVariantCasts.hpp" +#include "JsonVariantComparisons.hpp" +#include "JsonVariantOr.hpp" +#include "JsonVariantSubscripts.hpp" +#include "Serialization/JsonPrintable.hpp" + +namespace ArduinoJson { +namespace Internals { + +template +class JsonVariantBase : public JsonPrintable, + public JsonVariantCasts, + public JsonVariantComparisons, + public JsonVariantOr, + public JsonVariantSubscripts, + public JsonVariantTag {}; +} +} diff --git a/include/lib/ArduinoJson/JsonVariantCasts.hpp b/include/lib/ArduinoJson/JsonVariantCasts.hpp new file mode 100644 index 0000000..68f5bd7 --- /dev/null +++ b/include/lib/ArduinoJson/JsonVariantCasts.hpp @@ -0,0 +1,59 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "Data/JsonVariantAs.hpp" +#include "Polyfills/attributes.hpp" + +namespace ArduinoJson { +namespace Internals { + +template +class JsonVariantCasts { + public: +#if ARDUINOJSON_ENABLE_DEPRECATED + DEPRECATED("use as() instead") + FORCE_INLINE JsonArray &asArray() const { + return impl()->template as(); + } + + DEPRECATED("use as() instead") + FORCE_INLINE JsonObject &asObject() const { + return impl()->template as(); + } + + DEPRECATED("use as() instead") + FORCE_INLINE const char *asString() const { + return impl()->template as(); + } +#endif + + // Gets the variant as an array. + // Returns a reference to the JsonArray or JsonArray::invalid() if the + // variant + // is not an array. + FORCE_INLINE operator JsonArray &() const { + return impl()->template as(); + } + + // Gets the variant as an object. + // Returns a reference to the JsonObject or JsonObject::invalid() if the + // variant is not an object. + FORCE_INLINE operator JsonObject &() const { + return impl()->template as(); + } + + template + FORCE_INLINE operator T() const { + return impl()->template as(); + } + + private: + const TImpl *impl() const { + return static_cast(this); + } +}; +} +} diff --git a/include/lib/ArduinoJson/JsonVariantComparisons.hpp b/include/lib/ArduinoJson/JsonVariantComparisons.hpp new file mode 100644 index 0000000..47f9d63 --- /dev/null +++ b/include/lib/ArduinoJson/JsonVariantComparisons.hpp @@ -0,0 +1,139 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "StringTraits/StringTraits.hpp" +#include "TypeTraits/EnableIf.hpp" +#include "TypeTraits/IsVariant.hpp" + +namespace ArduinoJson { +namespace Internals { + +template +class JsonVariantComparisons { + public: + template + friend bool operator==(const JsonVariantComparisons &variant, + TComparand comparand) { + return variant.equals(comparand); + } + + template + friend typename EnableIf::value, bool>::type + operator==(TComparand comparand, const JsonVariantComparisons &variant) { + return variant.equals(comparand); + } + + template + friend bool operator!=(const JsonVariantComparisons &variant, + TComparand comparand) { + return !variant.equals(comparand); + } + + template + friend typename EnableIf::value, bool>::type + operator!=(TComparand comparand, const JsonVariantComparisons &variant) { + return !variant.equals(comparand); + } + + template + friend bool operator<=(const JsonVariantComparisons &left, TComparand right) { + return left.as() <= right; + } + + template + friend bool operator<=(TComparand comparand, + const JsonVariantComparisons &variant) { + return comparand <= variant.as(); + } + + template + friend bool operator>=(const JsonVariantComparisons &variant, + TComparand comparand) { + return variant.as() >= comparand; + } + + template + friend bool operator>=(TComparand comparand, + const JsonVariantComparisons &variant) { + return comparand >= variant.as(); + } + + template + friend bool operator<(const JsonVariantComparisons &varian, + TComparand comparand) { + return varian.as() < comparand; + } + + template + friend bool operator<(TComparand comparand, + const JsonVariantComparisons &variant) { + return comparand < variant.as(); + } + + template + friend bool operator>(const JsonVariantComparisons &variant, + TComparand comparand) { + return variant.as() > comparand; + } + + template + friend bool operator>(TComparand comparand, + const JsonVariantComparisons &variant) { + return comparand > variant.as(); + } + + private: + const TImpl *impl() const { + return static_cast(this); + } + + template + const typename JsonVariantAs::type as() const { + return impl()->template as(); + } + + template + bool is() const { + return impl()->template is(); + } + + template + typename EnableIf::has_equals, bool>::type equals( + const TString &comparand) const { + const char *value = as(); + return StringTraits::equals(comparand, value); + } + + template + typename EnableIf::value && + !StringTraits::has_equals, + bool>::type + equals(const TComparand &comparand) const { + return as() == comparand; + } + + template + bool equals(const JsonVariantComparisons &right) const { + using namespace Internals; + if (is() && right.template is()) + return as() == right.template as(); + if (is() && right.template is()) + return as() == right.template as(); + if (is() && right.template is()) + return as() == right.template as(); + if (is() && right.template is()) + return as() == right.template as(); + if (is() && right.template is()) + return as() == right.template as(); + if (is() && right.template is()) + return StringTraits::equals(as(), + right.template as()); + + return false; + } +}; +} // namespace Internals +} // namespace ArduinoJson diff --git a/include/lib/ArduinoJson/JsonVariantImpl.hpp b/include/lib/ArduinoJson/JsonVariantImpl.hpp new file mode 100644 index 0000000..31f96ce --- /dev/null +++ b/include/lib/ArduinoJson/JsonVariantImpl.hpp @@ -0,0 +1,126 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "Configuration.hpp" +#include "JsonArray.hpp" +#include "JsonObject.hpp" +#include "JsonVariant.hpp" +#include "Polyfills/isFloat.hpp" +#include "Polyfills/isInteger.hpp" +#include "Polyfills/parseFloat.hpp" +#include "Polyfills/parseInteger.hpp" + +#include // for strcmp + +namespace ArduinoJson { + +inline JsonVariant::JsonVariant(const JsonArray &array) { + if (array.success()) { + _type = Internals::JSON_ARRAY; + _content.asArray = const_cast(&array); + } else { + _type = Internals::JSON_UNDEFINED; + } +} + +inline JsonVariant::JsonVariant(const JsonObject &object) { + if (object.success()) { + _type = Internals::JSON_OBJECT; + _content.asObject = const_cast(&object); + } else { + _type = Internals::JSON_UNDEFINED; + } +} + +inline JsonArray &JsonVariant::variantAsArray() const { + if (_type == Internals::JSON_ARRAY) return *_content.asArray; + return JsonArray::invalid(); +} + +inline JsonObject &JsonVariant::variantAsObject() const { + if (_type == Internals::JSON_OBJECT) return *_content.asObject; + return JsonObject::invalid(); +} + +template +inline T JsonVariant::variantAsInteger() const { + using namespace Internals; + switch (_type) { + case JSON_UNDEFINED: + return 0; + case JSON_POSITIVE_INTEGER: + case JSON_BOOLEAN: + return T(_content.asInteger); + case JSON_NEGATIVE_INTEGER: + return T(~_content.asInteger + 1); + case JSON_STRING: + case JSON_UNPARSED: + return parseInteger(_content.asString); + default: + return T(_content.asFloat); + } +} + +inline const char *JsonVariant::variantAsString() const { + using namespace Internals; + if (_type == JSON_UNPARSED && _content.asString && + !strcmp("null", _content.asString)) + return NULL; + if (_type == JSON_STRING || _type == JSON_UNPARSED) return _content.asString; + return NULL; +} + +template +inline T JsonVariant::variantAsFloat() const { + using namespace Internals; + switch (_type) { + case JSON_UNDEFINED: + return 0; + case JSON_POSITIVE_INTEGER: + case JSON_BOOLEAN: + return static_cast(_content.asInteger); + case JSON_NEGATIVE_INTEGER: + return -static_cast(_content.asInteger); + case JSON_STRING: + case JSON_UNPARSED: + return parseFloat(_content.asString); + default: + return static_cast(_content.asFloat); + } +} + +inline bool JsonVariant::variantIsBoolean() const { + using namespace Internals; + if (_type == JSON_BOOLEAN) return true; + + if (_type != JSON_UNPARSED || _content.asString == NULL) return false; + + return !strcmp(_content.asString, "true") || + !strcmp(_content.asString, "false"); +} + +inline bool JsonVariant::variantIsInteger() const { + using namespace Internals; + + return _type == JSON_POSITIVE_INTEGER || _type == JSON_NEGATIVE_INTEGER || + (_type == JSON_UNPARSED && isInteger(_content.asString)); +} + +inline bool JsonVariant::variantIsFloat() const { + using namespace Internals; + + return _type == JSON_FLOAT || _type == JSON_POSITIVE_INTEGER || + _type == JSON_NEGATIVE_INTEGER || + (_type == JSON_UNPARSED && isFloat(_content.asString)); +} + +#if ARDUINOJSON_ENABLE_STD_STREAM +inline std::ostream &operator<<(std::ostream &os, const JsonVariant &source) { + return source.printTo(os); +} +#endif + +} // namespace ArduinoJson diff --git a/include/lib/ArduinoJson/JsonVariantOr.hpp b/include/lib/ArduinoJson/JsonVariantOr.hpp new file mode 100644 index 0000000..d8022fc --- /dev/null +++ b/include/lib/ArduinoJson/JsonVariantOr.hpp @@ -0,0 +1,52 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "Data/JsonVariantAs.hpp" +#include "Polyfills/attributes.hpp" +#include "TypeTraits/EnableIf.hpp" +#include "TypeTraits/IsIntegral.hpp" + +namespace ArduinoJson { +namespace Internals { + +template +class JsonVariantOr { + public: + // Returns the default value if the JsonVariant is undefined of incompatible + template + typename EnableIf::value, T>::type operator|( + const T &defaultValue) const { + if (impl()->template is()) + return impl()->template as(); + else + return defaultValue; + } + + // Returns the default value if the JsonVariant is undefined of incompatible + // Special case for string: null is treated as undefined + const char *operator|(const char *defaultValue) const { + const char *value = impl()->template as(); + return value ? value : defaultValue; + } + + // Returns the default value if the JsonVariant is undefined of incompatible + // Special case for integers: we also accept double + template + typename EnableIf::value, Integer>::type operator|( + const Integer &defaultValue) const { + if (impl()->template is()) + return impl()->template as(); + else + return defaultValue; + } + + private: + const TImpl *impl() const { + return static_cast(this); + } +}; +} // namespace Internals +} // namespace ArduinoJson diff --git a/include/lib/ArduinoJson/JsonVariantSubscripts.hpp b/include/lib/ArduinoJson/JsonVariantSubscripts.hpp new file mode 100644 index 0000000..279ee01 --- /dev/null +++ b/include/lib/ArduinoJson/JsonVariantSubscripts.hpp @@ -0,0 +1,86 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "Data/JsonVariantAs.hpp" +#include "Polyfills/attributes.hpp" +#include "StringTraits/StringTraits.hpp" +#include "TypeTraits/EnableIf.hpp" + +namespace ArduinoJson { +namespace Internals { + +// Forward declarations. +class JsonArraySubscript; +template +class JsonObjectSubscript; + +template +class JsonVariantSubscripts { + public: + // Mimics an array or an object. + // Returns the size of the array or object if the variant has that type. + // Returns 0 if the variant is neither an array nor an object + size_t size() const { + return impl()->template as().size() + + impl()->template as().size(); + } + + // Mimics an array. + // Returns the element at specified index if the variant is an array. + // Returns JsonVariant::invalid() if the variant is not an array. + FORCE_INLINE const JsonArraySubscript operator[](size_t index) const; + FORCE_INLINE JsonArraySubscript operator[](size_t index); + + // Mimics an object. + // Returns the value associated with the specified key if the variant is + // an object. + // Return JsonVariant::invalid() if the variant is not an object. + // + // const JsonObjectSubscript operator[](TKey) const; + // TKey = const std::string&, const String& + template + FORCE_INLINE + typename EnableIf::has_equals, + const JsonObjectSubscript >::type + operator[](const TString &key) const { + return impl()->template as()[key]; + } + // + // const JsonObjectSubscript operator[](TKey) const; + // TKey = const std::string&, const String& + template + FORCE_INLINE typename EnableIf::has_equals, + JsonObjectSubscript >::type + operator[](const TString &key) { + return impl()->template as()[key]; + } + // + // JsonObjectSubscript operator[](TKey); + // TKey = const char*, const char[N], const FlashStringHelper* + template + FORCE_INLINE typename EnableIf::has_equals, + JsonObjectSubscript >::type + operator[](const TString *key) { + return impl()->template as()[key]; + } + // + // JsonObjectSubscript operator[](TKey); + // TKey = const char*, const char[N], const FlashStringHelper* + template + FORCE_INLINE + typename EnableIf::has_equals, + const JsonObjectSubscript >::type + operator[](const TString *key) const { + return impl()->template as()[key]; + } + + private: + const TImpl *impl() const { + return static_cast(this); + } +}; +} +} diff --git a/include/lib/ArduinoJson/Polyfills/attributes.hpp b/include/lib/ArduinoJson/Polyfills/attributes.hpp new file mode 100644 index 0000000..b49091d --- /dev/null +++ b/include/lib/ArduinoJson/Polyfills/attributes.hpp @@ -0,0 +1,29 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#ifdef _MSC_VER // Visual Studio + +#define FORCE_INLINE // __forceinline causes C4714 when returning std::string +#define NO_INLINE __declspec(noinline) +#define DEPRECATED(msg) __declspec(deprecated(msg)) + +#elif defined(__GNUC__) // GCC or Clang + +#define FORCE_INLINE __attribute__((always_inline)) +#define NO_INLINE __attribute__((noinline)) +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) +#define DEPRECATED(msg) __attribute__((deprecated(msg))) +#else +#define DEPRECATED(msg) __attribute__((deprecated)) +#endif + +#else // Other compilers + +#define FORCE_INLINE +#define NO_INLINE +#define DEPRECATED(msg) + +#endif diff --git a/include/lib/ArduinoJson/Polyfills/ctype.hpp b/include/lib/ArduinoJson/Polyfills/ctype.hpp new file mode 100644 index 0000000..2d52703 --- /dev/null +++ b/include/lib/ArduinoJson/Polyfills/ctype.hpp @@ -0,0 +1,18 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ArduinoJson { +namespace Internals { + +inline bool isdigit(char c) { + return '0' <= c && c <= '9'; +} + +inline bool issign(char c) { + return '-' == c || c == '+'; +} +} +} diff --git a/include/lib/ArduinoJson/Polyfills/isFloat.hpp b/include/lib/ArduinoJson/Polyfills/isFloat.hpp new file mode 100644 index 0000000..973b89f --- /dev/null +++ b/include/lib/ArduinoJson/Polyfills/isFloat.hpp @@ -0,0 +1,38 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include // for strcmp +#include "./ctype.hpp" + +namespace ArduinoJson { +namespace Internals { + +inline bool isFloat(const char* s) { + if (!s) return false; + + if (!strcmp(s, "NaN")) return true; + if (issign(*s)) s++; + if (!strcmp(s, "Infinity")) return true; + if (*s == '\0') return false; + + while (isdigit(*s)) s++; + + if (*s == '.') { + s++; + while (isdigit(*s)) s++; + } + + if (*s == 'e' || *s == 'E') { + s++; + if (issign(*s)) s++; + if (!isdigit(*s)) return false; + while (isdigit(*s)) s++; + } + + return *s == '\0'; +} +} +} diff --git a/include/lib/ArduinoJson/Polyfills/isInteger.hpp b/include/lib/ArduinoJson/Polyfills/isInteger.hpp new file mode 100644 index 0000000..8049079 --- /dev/null +++ b/include/lib/ArduinoJson/Polyfills/isInteger.hpp @@ -0,0 +1,19 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "./ctype.hpp" + +namespace ArduinoJson { +namespace Internals { + +inline bool isInteger(const char* s) { + if (!s || !*s) return false; + if (issign(*s)) s++; + while (isdigit(*s)) s++; + return *s == '\0'; +} +} // namespace Internals +} // namespace ArduinoJson diff --git a/include/lib/ArduinoJson/Polyfills/math.hpp b/include/lib/ArduinoJson/Polyfills/math.hpp new file mode 100644 index 0000000..48773ed --- /dev/null +++ b/include/lib/ArduinoJson/Polyfills/math.hpp @@ -0,0 +1,19 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ArduinoJson { +namespace Internals { +template +bool isNaN(T x) { + return x != x; +} + +template +bool isInfinity(T x) { + return x != 0.0 && x * 2 == x; +} +} +} diff --git a/include/lib/ArduinoJson/Polyfills/parseFloat.hpp b/include/lib/ArduinoJson/Polyfills/parseFloat.hpp new file mode 100644 index 0000000..49b0f6f --- /dev/null +++ b/include/lib/ArduinoJson/Polyfills/parseFloat.hpp @@ -0,0 +1,90 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "../TypeTraits/FloatTraits.hpp" +#include "./ctype.hpp" +#include "./math.hpp" + +namespace ArduinoJson { +namespace Internals { + +template +inline T parseFloat(const char* s) { + typedef FloatTraits traits; + typedef typename traits::mantissa_type mantissa_t; + typedef typename traits::exponent_type exponent_t; + + if (!s) return 0; // NULL + + bool negative_result = false; + switch (*s) { + case '-': + negative_result = true; + s++; + break; + case '+': + s++; + break; + } + + if (*s == 't') return 1; // true + if (*s == 'n' || *s == 'N') return traits::nan(); + if (*s == 'i' || *s == 'I') + return negative_result ? -traits::inf() : traits::inf(); + + mantissa_t mantissa = 0; + exponent_t exponent_offset = 0; + + while (isdigit(*s)) { + if (mantissa < traits::mantissa_max / 10) + mantissa = mantissa * 10 + (*s - '0'); + else + exponent_offset++; + s++; + } + + if (*s == '.') { + s++; + while (isdigit(*s)) { + if (mantissa < traits::mantissa_max / 10) { + mantissa = mantissa * 10 + (*s - '0'); + exponent_offset--; + } + s++; + } + } + + int exponent = 0; + if (*s == 'e' || *s == 'E') { + s++; + bool negative_exponent = false; + if (*s == '-') { + negative_exponent = true; + s++; + } else if (*s == '+') { + s++; + } + + while (isdigit(*s)) { + exponent = exponent * 10 + (*s - '0'); + if (exponent + exponent_offset > traits::exponent_max) { + if (negative_exponent) + return negative_result ? -0.0f : 0.0f; + else + return negative_result ? -traits::inf() : traits::inf(); + } + s++; + } + if (negative_exponent) exponent = -exponent; + } + exponent += exponent_offset; + + T result = traits::make_float(static_cast(mantissa), exponent); + + return negative_result ? -result : result; +} +} +} diff --git a/include/lib/ArduinoJson/Polyfills/parseInteger.hpp b/include/lib/ArduinoJson/Polyfills/parseInteger.hpp new file mode 100644 index 0000000..e8f1974 --- /dev/null +++ b/include/lib/ArduinoJson/Polyfills/parseInteger.hpp @@ -0,0 +1,41 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include + +#include "../Configuration.hpp" +#include "./ctype.hpp" + +namespace ArduinoJson { +namespace Internals { +template +T parseInteger(const char *s) { + if (!s) return 0; // NULL + + if (*s == 't') return 1; // "true" + + T result = 0; + bool negative_result = false; + + switch (*s) { + case '-': + negative_result = true; + s++; + break; + case '+': + s++; + break; + } + + while (isdigit(*s)) { + result = T(result * 10 + T(*s - '0')); + s++; + } + + return negative_result ? T(~result + 1) : result; +} +} +} diff --git a/include/lib/ArduinoJson/RawJson.hpp b/include/lib/ArduinoJson/RawJson.hpp new file mode 100644 index 0000000..4beb980 --- /dev/null +++ b/include/lib/ArduinoJson/RawJson.hpp @@ -0,0 +1,46 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ArduinoJson { + +namespace Internals { +// A special type of data that can be used to insert pregenerated JSON portions. +template +class RawJsonString { + public: + explicit RawJsonString(T str) : _str(str) {} + operator T() const { + return _str; + } + + private: + T _str; +}; + +template +struct StringTraits, void> { + static bool is_null(RawJsonString source) { + return StringTraits::is_null(static_cast(source)); + } + + typedef RawJsonString duplicate_t; + + template + static duplicate_t duplicate(RawJsonString source, Buffer* buffer) { + return duplicate_t(StringTraits::duplicate(source, buffer)); + } + + static const bool has_append = false; + static const bool has_equals = false; + static const bool should_duplicate = StringTraits::should_duplicate; +}; +} + +template +inline Internals::RawJsonString RawJson(T str) { + return Internals::RawJsonString(str); +} +} diff --git a/include/lib/ArduinoJson/Serialization/DummyPrint.hpp b/include/lib/ArduinoJson/Serialization/DummyPrint.hpp new file mode 100644 index 0000000..9fdf2d6 --- /dev/null +++ b/include/lib/ArduinoJson/Serialization/DummyPrint.hpp @@ -0,0 +1,22 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ArduinoJson { +namespace Internals { + +// A dummy Print implementation used in JsonPrintable::measureLength() +class DummyPrint { + public: + size_t print(char) { + return 1; + } + + size_t print(const char* s) { + return strlen(s); + } +}; +} +} diff --git a/include/lib/ArduinoJson/Serialization/DynamicStringBuilder.hpp b/include/lib/ArduinoJson/Serialization/DynamicStringBuilder.hpp new file mode 100644 index 0000000..41be639 --- /dev/null +++ b/include/lib/ArduinoJson/Serialization/DynamicStringBuilder.hpp @@ -0,0 +1,35 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "../StringTraits/StringTraits.hpp" + +namespace ArduinoJson { +namespace Internals { + +// A Print implementation that allows to write in a String +template +class DynamicStringBuilder { + public: + DynamicStringBuilder(TString &str) : _str(str) {} + + size_t print(char c) { + StringTraits::append(_str, c); + return 1; + } + + size_t print(const char *s) { + size_t initialLen = _str.length(); + StringTraits::append(_str, s); + return _str.length() - initialLen; + } + + private: + DynamicStringBuilder &operator=(const DynamicStringBuilder &); + + TString &_str; +}; +} +} diff --git a/include/lib/ArduinoJson/Serialization/FloatParts.hpp b/include/lib/ArduinoJson/Serialization/FloatParts.hpp new file mode 100644 index 0000000..c14e3b5 --- /dev/null +++ b/include/lib/ArduinoJson/Serialization/FloatParts.hpp @@ -0,0 +1,89 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "../Configuration.hpp" +#include "../Polyfills/math.hpp" +#include "../TypeTraits/FloatTraits.hpp" + +namespace ArduinoJson { +namespace Internals { + +template +struct FloatParts { + uint32_t integral; + uint32_t decimal; + int16_t exponent; + int8_t decimalPlaces; + + FloatParts(TFloat value) { + uint32_t maxDecimalPart = sizeof(TFloat) >= 8 ? 1000000000 : 1000000; + decimalPlaces = sizeof(TFloat) >= 8 ? 9 : 6; + + exponent = normalize(value); + + integral = uint32_t(value); + // reduce number of decimal places by the number of integral places + for (uint32_t tmp = integral; tmp >= 10; tmp /= 10) { + maxDecimalPart /= 10; + decimalPlaces--; + } + + TFloat remainder = (value - TFloat(integral)) * TFloat(maxDecimalPart); + + decimal = uint32_t(remainder); + remainder = remainder - TFloat(decimal); + + // rounding: + // increment by 1 if remainder >= 0.5 + decimal += uint32_t(remainder * 2); + if (decimal >= maxDecimalPart) { + decimal = 0; + integral++; + if (exponent && integral >= 10) { + exponent++; + integral = 1; + } + } + + // remove trailing zeros + while (decimal % 10 == 0 && decimalPlaces > 0) { + decimal /= 10; + decimalPlaces--; + } + } + + static int16_t normalize(TFloat& value) { + typedef FloatTraits traits; + int16_t powersOf10 = 0; + + int8_t index = sizeof(TFloat) == 8 ? 8 : 5; + int bit = 1 << index; + + if (value >= ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD) { + for (; index >= 0; index--) { + if (value >= traits::positiveBinaryPowerOfTen(index)) { + value *= traits::negativeBinaryPowerOfTen(index); + powersOf10 = int16_t(powersOf10 + bit); + } + bit >>= 1; + } + } + + if (value > 0 && value <= ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD) { + for (; index >= 0; index--) { + if (value < traits::negativeBinaryPowerOfTenPlusOne(index)) { + value *= traits::positiveBinaryPowerOfTen(index); + powersOf10 = int16_t(powersOf10 - bit); + } + bit >>= 1; + } + } + + return powersOf10; + } +}; +} +} diff --git a/include/lib/ArduinoJson/Serialization/IndentedPrint.hpp b/include/lib/ArduinoJson/Serialization/IndentedPrint.hpp new file mode 100644 index 0000000..864f9aa --- /dev/null +++ b/include/lib/ArduinoJson/Serialization/IndentedPrint.hpp @@ -0,0 +1,68 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ArduinoJson { +namespace Internals { + +// Decorator on top of Print to allow indented output. +// This class is used by JsonPrintable::prettyPrintTo() but can also be used +// for your own purpose, like logging. +template +class IndentedPrint { + public: + explicit IndentedPrint(Print &p) : sink(&p) { + level = 0; + tabSize = 2; + isNewLine = true; + } + + size_t print(char c) { + size_t n = 0; + if (isNewLine) n += writeTabs(); + n += sink->print(c); + isNewLine = c == '\n'; + return n; + } + + size_t print(const char *s) { + // TODO: optimize + size_t n = 0; + while (*s) n += print(*s++); + return n; + } + + // Adds one level of indentation + void indent() { + if (level < MAX_LEVEL) level++; + } + + // Removes one level of indentation + void unindent() { + if (level > 0) level--; + } + + // Set the number of space printed for each level of indentation + void setTabSize(uint8_t n) { + if (n < MAX_TAB_SIZE) tabSize = n & MAX_TAB_SIZE; + } + + private: + Print *sink; + uint8_t level : 4; + uint8_t tabSize : 3; + bool isNewLine : 1; + + size_t writeTabs() { + size_t n = 0; + for (int i = 0; i < level * tabSize; i++) n += sink->print(' '); + return n; + } + + static const int MAX_LEVEL = 15; // because it's only 4 bits + static const int MAX_TAB_SIZE = 7; // because it's only 3 bits +}; +} +} diff --git a/include/lib/ArduinoJson/Serialization/JsonPrintable.hpp b/include/lib/ArduinoJson/Serialization/JsonPrintable.hpp new file mode 100644 index 0000000..43d413a --- /dev/null +++ b/include/lib/ArduinoJson/Serialization/JsonPrintable.hpp @@ -0,0 +1,117 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "../Configuration.hpp" +#include "../TypeTraits/EnableIf.hpp" +#include "DummyPrint.hpp" +#include "DynamicStringBuilder.hpp" +#include "IndentedPrint.hpp" +#include "JsonSerializer.hpp" +#include "JsonWriter.hpp" +#include "Prettyfier.hpp" +#include "StaticStringBuilder.hpp" + +#if ARDUINOJSON_ENABLE_STD_STREAM +#include "StreamPrintAdapter.hpp" +#endif + +namespace ArduinoJson { +namespace Internals { + +// Implements all the overloads of printTo() and prettyPrintTo() +// Caution: this class use a template parameter to avoid virtual methods. +// This is a bit curious but allows to reduce the size of JsonVariant, JsonArray +// and JsonObject. +template +class JsonPrintable { + public: + template + typename EnableIf::has_append, size_t>::type printTo( + Print &print) const { + JsonWriter writer(print); + JsonSerializer >::serialize(downcast(), writer); + return writer.bytesWritten(); + } + +#if ARDUINOJSON_ENABLE_STD_STREAM + std::ostream &printTo(std::ostream &os) const { + StreamPrintAdapter adapter(os); + printTo(adapter); + return os; + } +#endif + + size_t printTo(char *buffer, size_t bufferSize) const { + StaticStringBuilder sb(buffer, bufferSize); + return printTo(sb); + } + + template + size_t printTo(char (&buffer)[N]) const { + return printTo(buffer, N); + } + + template + typename EnableIf::has_append, size_t>::type printTo( + TString &str) const { + DynamicStringBuilder sb(str); + return printTo(sb); + } + + template + size_t prettyPrintTo(IndentedPrint &print) const { + Prettyfier p(print); + return printTo(p); + } + + size_t prettyPrintTo(char *buffer, size_t bufferSize) const { + StaticStringBuilder sb(buffer, bufferSize); + return prettyPrintTo(sb); + } + + template + size_t prettyPrintTo(char (&buffer)[N]) const { + return prettyPrintTo(buffer, N); + } + + template + typename EnableIf::has_append, size_t>::type + prettyPrintTo(Print &print) const { + IndentedPrint indentedPrint(print); + return prettyPrintTo(indentedPrint); + } + + template + typename EnableIf::has_append, size_t>::type + prettyPrintTo(TString &str) const { + DynamicStringBuilder sb(str); + return prettyPrintTo(sb); + } + + size_t measureLength() const { + DummyPrint dp; + return printTo(dp); + } + + size_t measurePrettyLength() const { + DummyPrint dp; + return prettyPrintTo(dp); + } + + private: + const T &downcast() const { + return *static_cast(this); + } +}; + +#if ARDUINOJSON_ENABLE_STD_STREAM +template +inline std::ostream &operator<<(std::ostream &os, const JsonPrintable &v) { + return v.printTo(os); +} +#endif +} +} diff --git a/include/lib/ArduinoJson/Serialization/JsonSerializer.hpp b/include/lib/ArduinoJson/Serialization/JsonSerializer.hpp new file mode 100644 index 0000000..0cb537f --- /dev/null +++ b/include/lib/ArduinoJson/Serialization/JsonSerializer.hpp @@ -0,0 +1,32 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "JsonWriter.hpp" + +namespace ArduinoJson { + +class JsonArray; +class JsonObject; +class JsonVariant; + +namespace Internals { + +class JsonArraySubscript; +template +class JsonObjectSubscript; + +template +class JsonSerializer { + public: + static void serialize(const JsonArray &, Writer &); + static void serialize(const JsonArraySubscript &, Writer &); + static void serialize(const JsonObject &, Writer &); + template + static void serialize(const JsonObjectSubscript &, Writer &); + static void serialize(const JsonVariant &, Writer &); +}; +} +} diff --git a/include/lib/ArduinoJson/Serialization/JsonSerializerImpl.hpp b/include/lib/ArduinoJson/Serialization/JsonSerializerImpl.hpp new file mode 100644 index 0000000..0faae27 --- /dev/null +++ b/include/lib/ArduinoJson/Serialization/JsonSerializerImpl.hpp @@ -0,0 +1,103 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "../JsonArray.hpp" +#include "../JsonArraySubscript.hpp" +#include "../JsonObject.hpp" +#include "../JsonObjectSubscript.hpp" +#include "../JsonVariant.hpp" +#include "JsonSerializer.hpp" + +template +inline void ArduinoJson::Internals::JsonSerializer::serialize( + const JsonArray& array, Writer& writer) { + writer.beginArray(); + + JsonArray::const_iterator it = array.begin(); + while (it != array.end()) { + serialize(*it, writer); + + ++it; + if (it == array.end()) break; + + writer.writeComma(); + } + + writer.endArray(); +} + +template +inline void ArduinoJson::Internals::JsonSerializer::serialize( + const JsonArraySubscript& arraySubscript, Writer& writer) { + serialize(arraySubscript.as(), writer); +} + +template +inline void ArduinoJson::Internals::JsonSerializer::serialize( + const JsonObject& object, Writer& writer) { + writer.beginObject(); + + JsonObject::const_iterator it = object.begin(); + while (it != object.end()) { + writer.writeString(it->key); + writer.writeColon(); + serialize(it->value, writer); + + ++it; + if (it == object.end()) break; + + writer.writeComma(); + } + + writer.endObject(); +} + +template +template +inline void ArduinoJson::Internals::JsonSerializer::serialize( + const JsonObjectSubscript& objectSubscript, Writer& writer) { + serialize(objectSubscript.template as(), writer); +} + +template +inline void ArduinoJson::Internals::JsonSerializer::serialize( + const JsonVariant& variant, Writer& writer) { + switch (variant._type) { + case JSON_FLOAT: + writer.writeFloat(variant._content.asFloat); + return; + + case JSON_ARRAY: + serialize(*variant._content.asArray, writer); + return; + + case JSON_OBJECT: + serialize(*variant._content.asObject, writer); + return; + + case JSON_STRING: + writer.writeString(variant._content.asString); + return; + + case JSON_UNPARSED: + writer.writeRaw(variant._content.asString); + return; + + case JSON_NEGATIVE_INTEGER: + writer.writeRaw('-'); // Falls through. + + case JSON_POSITIVE_INTEGER: + writer.writeInteger(variant._content.asInteger); + return; + + case JSON_BOOLEAN: + writer.writeBoolean(variant._content.asInteger != 0); + return; + + default: // JSON_UNDEFINED + return; + } +} diff --git a/include/lib/ArduinoJson/Serialization/JsonWriter.hpp b/include/lib/ArduinoJson/Serialization/JsonWriter.hpp new file mode 100644 index 0000000..146d51d --- /dev/null +++ b/include/lib/ArduinoJson/Serialization/JsonWriter.hpp @@ -0,0 +1,155 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include +#include "../Data/Encoding.hpp" +#include "../Data/JsonInteger.hpp" +#include "../Polyfills/attributes.hpp" +#include "../Serialization/FloatParts.hpp" + +namespace ArduinoJson { +namespace Internals { + +// Writes the JSON tokens to a Print implementation +// This class is used by: +// - JsonArray::writeTo() +// - JsonObject::writeTo() +// - JsonVariant::writeTo() +// Its derived by PrettyJsonWriter that overrides some members to add +// indentation. +template +class JsonWriter { + public: + explicit JsonWriter(Print &sink) : _sink(sink), _length(0) {} + + // Returns the number of bytes sent to the Print implementation. + // This is very handy for implementations of printTo() that must return the + // number of bytes written. + size_t bytesWritten() const { + return _length; + } + + void beginArray() { + writeRaw('['); + } + void endArray() { + writeRaw(']'); + } + + void beginObject() { + writeRaw('{'); + } + void endObject() { + writeRaw('}'); + } + + void writeColon() { + writeRaw(':'); + } + void writeComma() { + writeRaw(','); + } + + void writeBoolean(bool value) { + writeRaw(value ? "true" : "false"); + } + + void writeString(const char *value) { + if (!value) { + writeRaw("null"); + } else { + writeRaw('\"'); + while (*value) writeChar(*value++); + writeRaw('\"'); + } + } + + void writeChar(char c) { + char specialChar = Encoding::escapeChar(c); + if (specialChar) { + writeRaw('\\'); + writeRaw(specialChar); + } else { + writeRaw(c); + } + } + + template + void writeFloat(TFloat value) { + if (isNaN(value)) return writeRaw("NaN"); + + if (value < 0.0) { + writeRaw('-'); + value = -value; + } + + if (isInfinity(value)) return writeRaw("Infinity"); + + FloatParts parts(value); + + writeInteger(parts.integral); + if (parts.decimalPlaces) writeDecimals(parts.decimal, parts.decimalPlaces); + + if (parts.exponent < 0) { + writeRaw("e-"); + writeInteger(-parts.exponent); + } + + if (parts.exponent > 0) { + writeRaw('e'); + writeInteger(parts.exponent); + } + } + + template + void writeInteger(UInt value) { + char buffer[22]; + char *end = buffer + sizeof(buffer) - 1; + char *ptr = end; + + *ptr = 0; + do { + *--ptr = char(value % 10 + '0'); + value = UInt(value / 10); + } while (value); + + writeRaw(ptr); + } + + void writeDecimals(uint32_t value, int8_t width) { + // buffer should be big enough for all digits, the dot and the null + // terminator + char buffer[16]; + char *ptr = buffer + sizeof(buffer) - 1; + + // write the string in reverse order + *ptr = 0; + while (width--) { + *--ptr = char(value % 10 + '0'); + value /= 10; + } + *--ptr = '.'; + + // and dump it in the right order + writeRaw(ptr); + } + + void writeRaw(const char *s) { + _length += _sink.print(s); + } + void writeRaw(char c) { + _length += _sink.print(c); + } + + protected: + Print &_sink; + size_t _length; + + private: + JsonWriter &operator=(const JsonWriter &); // cannot be assigned +}; +} +} diff --git a/include/lib/ArduinoJson/Serialization/Prettyfier.hpp b/include/lib/ArduinoJson/Serialization/Prettyfier.hpp new file mode 100644 index 0000000..8b4f0d2 --- /dev/null +++ b/include/lib/ArduinoJson/Serialization/Prettyfier.hpp @@ -0,0 +1,133 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "IndentedPrint.hpp" + +namespace ArduinoJson { +namespace Internals { + +// Converts a compact JSON string into an indented one. +template +class Prettyfier { + public: + explicit Prettyfier(IndentedPrint& p) : _sink(p) { + _previousChar = 0; + _inString = false; + } + + size_t print(char c) { + size_t n = _inString ? handleStringChar(c) : handleMarkupChar(c); + _previousChar = c; + return n; + } + + size_t print(const char* s) { + // TODO: optimize + size_t n = 0; + while (*s) n += print(*s++); + return n; + } + + private: + Prettyfier& operator=(const Prettyfier&); // cannot be assigned + + bool inEmptyBlock() { + return _previousChar == '{' || _previousChar == '['; + } + + size_t handleStringChar(char c) { + bool isQuote = c == '"' && _previousChar != '\\'; + + if (isQuote) _inString = false; + + return _sink.print(c); + } + + size_t handleMarkupChar(char c) { + switch (c) { + case '{': + case '[': + return writeBlockOpen(c); + + case '}': + case ']': + return writeBlockClose(c); + + case ':': + return writeColon(); + + case ',': + return writeComma(); + + case '"': + return writeQuoteOpen(); + + default: + return writeNormalChar(c); + } + } + + size_t writeBlockClose(char c) { + size_t n = 0; + n += unindentIfNeeded(); + n += _sink.print(c); + return n; + } + + size_t writeBlockOpen(char c) { + size_t n = 0; + n += indentIfNeeded(); + n += _sink.print(c); + return n; + } + + size_t writeColon() { + size_t n = 0; + n += _sink.print(": "); + return n; + } + + size_t writeComma() { + size_t n = 0; + n += _sink.print(",\r\n"); + return n; + } + + size_t writeQuoteOpen() { + _inString = true; + size_t n = 0; + n += indentIfNeeded(); + n += _sink.print('"'); + return n; + } + + size_t writeNormalChar(char c) { + size_t n = 0; + n += indentIfNeeded(); + n += _sink.print(c); + return n; + } + + size_t indentIfNeeded() { + if (!inEmptyBlock()) return 0; + + _sink.indent(); + return _sink.print("\r\n"); + } + + size_t unindentIfNeeded() { + if (inEmptyBlock()) return 0; + + _sink.unindent(); + return _sink.print("\r\n"); + } + + char _previousChar; + IndentedPrint& _sink; + bool _inString; +}; +} +} diff --git a/include/lib/ArduinoJson/Serialization/StaticStringBuilder.hpp b/include/lib/ArduinoJson/Serialization/StaticStringBuilder.hpp new file mode 100644 index 0000000..9617bbd --- /dev/null +++ b/include/lib/ArduinoJson/Serialization/StaticStringBuilder.hpp @@ -0,0 +1,36 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ArduinoJson { +namespace Internals { + +// A Print implementation that allows to write in a char[] +class StaticStringBuilder { + public: + StaticStringBuilder(char *buf, size_t size) : end(buf + size - 1), p(buf) { + *p = '\0'; + } + + size_t print(char c) { + if (p >= end) return 0; + *p++ = c; + *p = '\0'; + return 1; + } + + size_t print(const char *s) { + char *begin = p; + while (p < end && *s) *p++ = *s++; + *p = '\0'; + return size_t(p - begin); + } + + private: + char *end; + char *p; +}; +} +} diff --git a/include/lib/ArduinoJson/Serialization/StreamPrintAdapter.hpp b/include/lib/ArduinoJson/Serialization/StreamPrintAdapter.hpp new file mode 100644 index 0000000..60f0af4 --- /dev/null +++ b/include/lib/ArduinoJson/Serialization/StreamPrintAdapter.hpp @@ -0,0 +1,39 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "../Configuration.hpp" + +#if ARDUINOJSON_ENABLE_STD_STREAM + +#include + +namespace ArduinoJson { +namespace Internals { + +class StreamPrintAdapter { + public: + explicit StreamPrintAdapter(std::ostream& os) : _os(os) {} + + size_t print(char c) { + _os << c; + return 1; + } + + size_t print(const char* s) { + _os << s; + return strlen(s); + } + + private: + // cannot be assigned + StreamPrintAdapter& operator=(const StreamPrintAdapter&); + + std::ostream& _os; +}; +} +} + +#endif // ARDUINOJSON_ENABLE_STD_STREAM diff --git a/include/lib/ArduinoJson/StaticJsonBuffer.hpp b/include/lib/ArduinoJson/StaticJsonBuffer.hpp new file mode 100644 index 0000000..267d9d0 --- /dev/null +++ b/include/lib/ArduinoJson/StaticJsonBuffer.hpp @@ -0,0 +1,126 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "JsonBufferBase.hpp" + +namespace ArduinoJson { +namespace Internals { + +class StaticJsonBufferBase : public JsonBufferBase { + public: + class String { + public: + String(StaticJsonBufferBase* parent) : _parent(parent) { + _start = parent->_buffer + parent->_size; + } + + void append(char c) { + if (_parent->canAlloc(1)) { + char* last = static_cast(_parent->doAlloc(1)); + *last = c; + } + } + + const char* c_str() const { + if (_parent->canAlloc(1)) { + char* last = static_cast(_parent->doAlloc(1)); + *last = '\0'; + return _start; + } else { + return NULL; + } + } + + private: + StaticJsonBufferBase* _parent; + char* _start; + }; + + StaticJsonBufferBase(char* buffer, size_t capa) + : _buffer(buffer), _capacity(capa), _size(0) {} + + // Gets the capacity of the buffer in bytes + size_t capacity() const { + return _capacity; + } + + // Gets the current usage of the buffer in bytes + size_t size() const { + return _size; + } + + // Allocates the specified amount of bytes in the buffer + virtual void* alloc(size_t bytes) { + alignNextAlloc(); + if (!canAlloc(bytes)) return NULL; + return doAlloc(bytes); + } + + // Resets the buffer. + // USE WITH CAUTION: this invalidates all previously allocated data + void clear() { + _size = 0; + } + + String startString() { + return String(this); + } + + protected: + ~StaticJsonBufferBase() {} + + private: + void alignNextAlloc() { + _size = round_size_up(_size); + } + + bool canAlloc(size_t bytes) const { + return _size + bytes <= _capacity; + } + + void* doAlloc(size_t bytes) { + void* p = &_buffer[_size]; + _size += bytes; + return p; + } + + char* _buffer; + size_t _capacity; + size_t _size; +}; +} + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnon-virtual-dtor" +#elif defined(__GNUC__) +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +#pragma GCC diagnostic push +#endif +#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" +#endif + +// Implements a JsonBuffer with fixed memory allocation. +// The template paramenter CAPACITY specifies the capacity of the buffer in +// bytes. +template +class StaticJsonBuffer : public Internals::StaticJsonBufferBase { + public: + explicit StaticJsonBuffer() + : Internals::StaticJsonBufferBase(_buffer, CAPACITY) {} + + private: + char _buffer[CAPACITY]; +}; +} + +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +#pragma GCC diagnostic pop +#endif +#endif diff --git a/include/lib/ArduinoJson/StringTraits/ArduinoStream.hpp b/include/lib/ArduinoJson/StringTraits/ArduinoStream.hpp new file mode 100644 index 0000000..5db0852 --- /dev/null +++ b/include/lib/ArduinoJson/StringTraits/ArduinoStream.hpp @@ -0,0 +1,61 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#if ARDUINOJSON_ENABLE_ARDUINO_STREAM + +#include + +namespace ArduinoJson { +namespace Internals { + +struct ArduinoStreamTraits { + class Reader { + Stream& _stream; + char _current, _next; + + public: + Reader(Stream& stream) : _stream(stream), _current(0), _next(0) {} + + void move() { + _current = _next; + _next = 0; + } + + char current() { + if (!_current) _current = read(); + return _current; + } + + char next() { + // assumes that current() has been called + if (!_next) _next = read(); + return _next; + } + + private: + char read() { + // don't use _stream.read() as it ignores the timeout + char c = 0; + _stream.readBytes(&c, 1); + return c; + } + }; + + static const bool has_append = false; + static const bool has_equals = false; +}; + +template +struct StringTraits< + TStream, + // match any type that is derived from Stream: + typename EnableIf< + IsBaseOf::type>::value>::type> + : ArduinoStreamTraits {}; +} +} + +#endif diff --git a/include/lib/ArduinoJson/StringTraits/CharPointer.hpp b/include/lib/ArduinoJson/StringTraits/CharPointer.hpp new file mode 100644 index 0000000..98896cc --- /dev/null +++ b/include/lib/ArduinoJson/StringTraits/CharPointer.hpp @@ -0,0 +1,64 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ArduinoJson { +namespace Internals { + +template +struct CharPointerTraits { + class Reader { + const TChar* _ptr; + + public: + Reader(const TChar* ptr) + : _ptr(ptr ? ptr : reinterpret_cast("")) {} + + void move() { + ++_ptr; + } + + char current() const { + return char(_ptr[0]); + } + + char next() const { + return char(_ptr[1]); + } + }; + + static bool equals(const TChar* str, const char* expected) { + const char* actual = reinterpret_cast(str); + if (!actual || !expected) return actual == expected; + return strcmp(actual, expected) == 0; + } + + static bool is_null(const TChar* str) { + return !str; + } + + typedef const char* duplicate_t; + + template + static duplicate_t duplicate(const TChar* str, Buffer* buffer) { + if (!str) return NULL; + size_t size = strlen(reinterpret_cast(str)) + 1; + void* dup = buffer->alloc(size); + if (dup != NULL) memcpy(dup, str, size); + return static_cast(dup); + } + + static const bool has_append = false; + static const bool has_equals = true; + static const bool should_duplicate = !IsConst::value; +}; + +// char*, unsigned char*, signed char* +// const char*, const unsigned char*, const signed char* +template +struct StringTraits::value>::type> + : CharPointerTraits {}; +} // namespace Internals +} // namespace ArduinoJson diff --git a/include/lib/ArduinoJson/StringTraits/FlashString.hpp b/include/lib/ArduinoJson/StringTraits/FlashString.hpp new file mode 100644 index 0000000..0701b9b --- /dev/null +++ b/include/lib/ArduinoJson/StringTraits/FlashString.hpp @@ -0,0 +1,61 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#if ARDUINOJSON_ENABLE_PROGMEM + +namespace ArduinoJson { +namespace Internals { +template <> +struct StringTraits { + class Reader { + const char* _ptr; + + public: + Reader(const __FlashStringHelper* ptr) + : _ptr(reinterpret_cast(ptr)) {} + + void move() { + _ptr++; + } + + char current() const { + return pgm_read_byte_near(_ptr); + } + + char next() const { + return pgm_read_byte_near(_ptr + 1); + } + }; + + static bool equals(const __FlashStringHelper* str, const char* expected) { + const char* actual = reinterpret_cast(str); + if (!actual || !expected) return actual == expected; + return strcmp_P(expected, actual) == 0; + } + + static bool is_null(const __FlashStringHelper* str) { + return !str; + } + + typedef const char* duplicate_t; + + template + static duplicate_t duplicate(const __FlashStringHelper* str, Buffer* buffer) { + if (!str) return NULL; + size_t size = strlen_P((const char*)str) + 1; + void* dup = buffer->alloc(size); + if (dup != NULL) memcpy_P(dup, (const char*)str, size); + return static_cast(dup); + } + + static const bool has_append = false; + static const bool has_equals = true; + static const bool should_duplicate = true; +}; +} // namespace Internals +} // namespace ArduinoJson + +#endif diff --git a/include/lib/ArduinoJson/StringTraits/StdStream.hpp b/include/lib/ArduinoJson/StringTraits/StdStream.hpp new file mode 100644 index 0000000..227c744 --- /dev/null +++ b/include/lib/ArduinoJson/StringTraits/StdStream.hpp @@ -0,0 +1,60 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#if ARDUINOJSON_ENABLE_STD_STREAM + +#include + +namespace ArduinoJson { +namespace Internals { + +struct StdStreamTraits { + class Reader { + std::istream& _stream; + char _current, _next; + + public: + Reader(std::istream& stream) : _stream(stream), _current(0), _next(0) {} + + void move() { + _current = _next; + _next = 0; + } + + char current() { + if (!_current) _current = read(); + return _current; + } + + char next() { + // assumes that current() has been called + if (!_next) _next = read(); + return _next; + } + + private: + Reader& operator=(const Reader&); // Visual Studio C4512 + + char read() { + return _stream.eof() ? '\0' : static_cast(_stream.get()); + } + }; + + static const bool has_append = false; + static const bool has_equals = false; +}; + +template +struct StringTraits< + TStream, + // match any type that is derived from std::istream: + typename EnableIf::type>::value>::type> + : StdStreamTraits {}; +} +} + +#endif diff --git a/include/lib/ArduinoJson/StringTraits/StdString.hpp b/include/lib/ArduinoJson/StringTraits/StdString.hpp new file mode 100644 index 0000000..35f4461 --- /dev/null +++ b/include/lib/ArduinoJson/StringTraits/StdString.hpp @@ -0,0 +1,77 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#if ARDUINOJSON_ENABLE_STD_STRING || ARDUINOJSON_ENABLE_ARDUINO_STRING + +#if ARDUINOJSON_ENABLE_ARDUINO_STRING +#include +#endif + +#if ARDUINOJSON_ENABLE_STD_STRING +#include +#endif + +namespace ArduinoJson { +namespace Internals { + +template +struct StdStringTraits { + typedef const char* duplicate_t; + + template + static duplicate_t duplicate(const TString& str, Buffer* buffer) { + if (!str.c_str()) return NULL; // <- Arduino string can return NULL + size_t size = str.length() + 1; + void* dup = buffer->alloc(size); + if (dup != NULL) memcpy(dup, str.c_str(), size); + return static_cast(dup); + } + + static bool is_null(const TString& str) { + // Arduino's String::c_str() can return NULL + return !str.c_str(); + } + + struct Reader : CharPointerTraits::Reader { + Reader(const TString& str) : CharPointerTraits::Reader(str.c_str()) {} + }; + + static bool equals(const TString& str, const char* expected) { + // Arduino's String::c_str() can return NULL + const char* actual = str.c_str(); + if (!actual || !expected) return actual == expected; + return 0 == strcmp(actual, expected); + } + + static void append(TString& str, char c) { + str += c; + } + + static void append(TString& str, const char* s) { + str += s; + } + + static const bool has_append = true; + static const bool has_equals = true; + static const bool should_duplicate = true; +}; + +#if ARDUINOJSON_ENABLE_ARDUINO_STRING +template <> +struct StringTraits : StdStringTraits {}; +template <> +struct StringTraits : StdStringTraits { +}; +#endif + +#if ARDUINOJSON_ENABLE_STD_STRING +template <> +struct StringTraits : StdStringTraits {}; +#endif +} // namespace Internals +} // namespace ArduinoJson + +#endif diff --git a/include/lib/ArduinoJson/StringTraits/StringTraits.hpp b/include/lib/ArduinoJson/StringTraits/StringTraits.hpp new file mode 100644 index 0000000..dd5694b --- /dev/null +++ b/include/lib/ArduinoJson/StringTraits/StringTraits.hpp @@ -0,0 +1,36 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include +#include "../Configuration.hpp" +#include "../TypeTraits/EnableIf.hpp" +#include "../TypeTraits/IsBaseOf.hpp" +#include "../TypeTraits/IsChar.hpp" +#include "../TypeTraits/IsConst.hpp" +#include "../TypeTraits/RemoveReference.hpp" + +namespace ArduinoJson { +namespace Internals { + +template +struct StringTraits { + static const bool has_append = false; + static const bool has_equals = false; +}; + +template +struct StringTraits : StringTraits {}; + +template +struct StringTraits : StringTraits {}; +} +} + +#include "ArduinoStream.hpp" +#include "CharPointer.hpp" +#include "FlashString.hpp" +#include "StdStream.hpp" +#include "StdString.hpp" diff --git a/include/lib/ArduinoJson/TypeTraits/EnableIf.hpp b/include/lib/ArduinoJson/TypeTraits/EnableIf.hpp new file mode 100644 index 0000000..83fc5e0 --- /dev/null +++ b/include/lib/ArduinoJson/TypeTraits/EnableIf.hpp @@ -0,0 +1,19 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ArduinoJson { +namespace Internals { + +// A meta-function that return the type T if Condition is true. +template +struct EnableIf {}; + +template +struct EnableIf { + typedef T type; +}; +} +} diff --git a/include/lib/ArduinoJson/TypeTraits/FloatTraits.hpp b/include/lib/ArduinoJson/TypeTraits/FloatTraits.hpp new file mode 100644 index 0000000..648cc82 --- /dev/null +++ b/include/lib/ArduinoJson/TypeTraits/FloatTraits.hpp @@ -0,0 +1,171 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include +#include // for size_t +#include "../Configuration.hpp" +#include "../Polyfills/math.hpp" + +namespace ArduinoJson { +namespace Internals { + +template +struct FloatTraits {}; + +template +struct FloatTraits { + typedef int64_t mantissa_type; + static const short mantissa_bits = 52; + static const mantissa_type mantissa_max = + (static_cast(1) << mantissa_bits) - 1; + + typedef int16_t exponent_type; + static const exponent_type exponent_max = 308; + + template + static T make_float(T m, TExponent e) { + if (e > 0) { + for (uint8_t index = 0; e != 0; index++) { + if (e & 1) m *= positiveBinaryPowerOfTen(index); + e >>= 1; + } + } else { + e = TExponent(-e); + for (uint8_t index = 0; e != 0; index++) { + if (e & 1) m *= negativeBinaryPowerOfTen(index); + e >>= 1; + } + } + return m; + } + + static T positiveBinaryPowerOfTen(int index) { + static T factors[] = { + 1e1, + 1e2, + 1e4, + 1e8, + 1e16, + forge(0x4693B8B5, 0xB5056E17), // 1e32 + forge(0x4D384F03, 0xE93FF9F5), // 1e64 + forge(0x5A827748, 0xF9301D32), // 1e128 + forge(0x75154FDD, 0x7F73BF3C) // 1e256 + }; + return factors[index]; + } + + static T negativeBinaryPowerOfTen(int index) { + static T factors[] = { + forge(0x3FB99999, 0x9999999A), // 1e-1 + forge(0x3F847AE1, 0x47AE147B), // 1e-2 + forge(0x3F1A36E2, 0xEB1C432D), // 1e-4 + forge(0x3E45798E, 0xE2308C3A), // 1e-8 + forge(0x3C9CD2B2, 0x97D889BC), // 1e-16 + forge(0x3949F623, 0xD5A8A733), // 1e-32 + forge(0x32A50FFD, 0x44F4A73D), // 1e-64 + forge(0x255BBA08, 0xCF8C979D), // 1e-128 + forge(0x0AC80628, 0x64AC6F43) // 1e-256 + }; + return factors[index]; + } + + static T negativeBinaryPowerOfTenPlusOne(int index) { + static T factors[] = { + 1e0, + forge(0x3FB99999, 0x9999999A), // 1e-1 + forge(0x3F50624D, 0xD2F1A9FC), // 1e-3 + forge(0x3E7AD7F2, 0x9ABCAF48), // 1e-7 + forge(0x3CD203AF, 0x9EE75616), // 1e-15 + forge(0x398039D6, 0x65896880), // 1e-31 + forge(0x32DA53FC, 0x9631D10D), // 1e-63 + forge(0x25915445, 0x81B7DEC2), // 1e-127 + forge(0x0AFE07B2, 0x7DD78B14) // 1e-255 + }; + return factors[index]; + } + + static T nan() { + return forge(0x7ff80000, 0x00000000); + } + + static T inf() { + return forge(0x7ff00000, 0x00000000); + } + + // constructs a double floating point values from its binary representation + // we use this function to workaround platforms with single precision literals + // (for example, when -fsingle-precision-constant is passed to GCC) + static T forge(uint32_t msb, uint32_t lsb) { + union { + uint64_t integerBits; + T floatBits; + }; + integerBits = (uint64_t(msb) << 32) | lsb; + return floatBits; + } +}; + +template +struct FloatTraits { + typedef int32_t mantissa_type; + static const short mantissa_bits = 23; + static const mantissa_type mantissa_max = + (static_cast(1) << mantissa_bits) - 1; + + typedef int8_t exponent_type; + static const exponent_type exponent_max = 38; + + template + static T make_float(T m, TExponent e) { + if (e > 0) { + for (uint8_t index = 0; e != 0; index++) { + if (e & 1) m *= positiveBinaryPowerOfTen(index); + e >>= 1; + } + } else { + e = -e; + for (uint8_t index = 0; e != 0; index++) { + if (e & 1) m *= negativeBinaryPowerOfTen(index); + e >>= 1; + } + } + return m; + } + + static T positiveBinaryPowerOfTen(int index) { + static T factors[] = {1e1f, 1e2f, 1e4f, 1e8f, 1e16f, 1e32f}; + return factors[index]; + } + + static T negativeBinaryPowerOfTen(int index) { + static T factors[] = {1e-1f, 1e-2f, 1e-4f, 1e-8f, 1e-16f, 1e-32f}; + return factors[index]; + } + + static T negativeBinaryPowerOfTenPlusOne(int index) { + static T factors[] = {1e0f, 1e-1f, 1e-3f, 1e-7f, 1e-15f, 1e-31f}; + return factors[index]; + } + + static T forge(uint32_t bits) { + union { + uint32_t integerBits; + T floatBits; + }; + integerBits = bits; + return floatBits; + } + + static T nan() { + return forge(0x7fc00000); + } + + static T inf() { + return forge(0x7f800000); + } +}; +} +} diff --git a/include/lib/ArduinoJson/TypeTraits/IsArray.hpp b/include/lib/ArduinoJson/TypeTraits/IsArray.hpp new file mode 100644 index 0000000..2599231 --- /dev/null +++ b/include/lib/ArduinoJson/TypeTraits/IsArray.hpp @@ -0,0 +1,24 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ArduinoJson { +namespace Internals { + +// A meta-function that return the type T without the const modifier +template +struct IsArray { + static const bool value = false; +}; +template +struct IsArray { + static const bool value = true; +}; +template +struct IsArray { + static const bool value = true; +}; +} +} diff --git a/include/lib/ArduinoJson/TypeTraits/IsBaseOf.hpp b/include/lib/ArduinoJson/TypeTraits/IsBaseOf.hpp new file mode 100644 index 0000000..bf24e96 --- /dev/null +++ b/include/lib/ArduinoJson/TypeTraits/IsBaseOf.hpp @@ -0,0 +1,27 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ArduinoJson { +namespace Internals { + +// A meta-function that returns true if Derived inherits from TBase is an +// integral type. +template +class IsBaseOf { + protected: // <- to avoid GCC's "all member functions in class are private" + typedef char Yes[1]; + typedef char No[2]; + + static Yes &probe(const TBase *); + static No &probe(...); + + public: + enum { + value = sizeof(probe(reinterpret_cast(0))) == sizeof(Yes) + }; +}; +} +} diff --git a/include/lib/ArduinoJson/TypeTraits/IsChar.hpp b/include/lib/ArduinoJson/TypeTraits/IsChar.hpp new file mode 100644 index 0000000..d97cec2 --- /dev/null +++ b/include/lib/ArduinoJson/TypeTraits/IsChar.hpp @@ -0,0 +1,23 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "IsSame.hpp" + +namespace ArduinoJson { +namespace Internals { + +// A meta-function that returns true if T is a charater +template +struct IsChar { + static const bool value = IsSame::value || + IsSame::value || + IsSame::value; +}; + +template +struct IsChar : IsChar {}; +} +} diff --git a/include/lib/ArduinoJson/TypeTraits/IsConst.hpp b/include/lib/ArduinoJson/TypeTraits/IsConst.hpp new file mode 100644 index 0000000..512ee5c --- /dev/null +++ b/include/lib/ArduinoJson/TypeTraits/IsConst.hpp @@ -0,0 +1,21 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ArduinoJson { +namespace Internals { + +// A meta-function that return the type T without the const modifier +template +struct IsConst { + static const bool value = false; +}; + +template +struct IsConst { + static const bool value = true; +}; +} +} diff --git a/include/lib/ArduinoJson/TypeTraits/IsFloatingPoint.hpp b/include/lib/ArduinoJson/TypeTraits/IsFloatingPoint.hpp new file mode 100644 index 0000000..e41a682 --- /dev/null +++ b/include/lib/ArduinoJson/TypeTraits/IsFloatingPoint.hpp @@ -0,0 +1,18 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "IsSame.hpp" + +namespace ArduinoJson { +namespace Internals { + +// A meta-function that returns true if T is a floating point type +template +struct IsFloatingPoint { + static const bool value = IsSame::value || IsSame::value; +}; +} +} diff --git a/include/lib/ArduinoJson/TypeTraits/IsIntegral.hpp b/include/lib/ArduinoJson/TypeTraits/IsIntegral.hpp new file mode 100644 index 0000000..17ae5f2 --- /dev/null +++ b/include/lib/ArduinoJson/TypeTraits/IsIntegral.hpp @@ -0,0 +1,26 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "IsSame.hpp" +#include "IsSignedIntegral.hpp" +#include "IsUnsignedIntegral.hpp" + +namespace ArduinoJson { +namespace Internals { + +// A meta-function that returns true if T is an integral type. +template +struct IsIntegral { + static const bool value = IsSignedIntegral::value || + IsUnsignedIntegral::value || + IsSame::value; + // CAUTION: differs from std::is_integral as it doesn't include bool +}; + +template +struct IsIntegral : IsIntegral {}; +} +} diff --git a/include/lib/ArduinoJson/TypeTraits/IsSame.hpp b/include/lib/ArduinoJson/TypeTraits/IsSame.hpp new file mode 100644 index 0000000..06567c9 --- /dev/null +++ b/include/lib/ArduinoJson/TypeTraits/IsSame.hpp @@ -0,0 +1,21 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ArduinoJson { +namespace Internals { + +// A meta-function that returns true if types T and U are the same. +template +struct IsSame { + static const bool value = false; +}; + +template +struct IsSame { + static const bool value = true; +}; +} +} diff --git a/include/lib/ArduinoJson/TypeTraits/IsSignedIntegral.hpp b/include/lib/ArduinoJson/TypeTraits/IsSignedIntegral.hpp new file mode 100644 index 0000000..7334eb9 --- /dev/null +++ b/include/lib/ArduinoJson/TypeTraits/IsSignedIntegral.hpp @@ -0,0 +1,28 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "../Configuration.hpp" +#include "IsSame.hpp" + +namespace ArduinoJson { +namespace Internals { + +// A meta-function that returns true if T is an integral type. +template +struct IsSignedIntegral { + static const bool value = + IsSame::value || IsSame::value || + IsSame::value || IsSame::value || +#if ARDUINOJSON_USE_LONG_LONG + IsSame::value || +#endif +#if ARDUINOJSON_USE_INT64 + IsSame::value || +#endif + false; +}; +} +} diff --git a/include/lib/ArduinoJson/TypeTraits/IsUnsignedIntegral.hpp b/include/lib/ArduinoJson/TypeTraits/IsUnsignedIntegral.hpp new file mode 100644 index 0000000..938423f --- /dev/null +++ b/include/lib/ArduinoJson/TypeTraits/IsUnsignedIntegral.hpp @@ -0,0 +1,28 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "../Configuration.hpp" +#include "IsSame.hpp" + +namespace ArduinoJson { +namespace Internals { + +// A meta-function that returns true if T is an integral type. +template +struct IsUnsignedIntegral { + static const bool value = + IsSame::value || IsSame::value || + IsSame::value || IsSame::value || +#if ARDUINOJSON_USE_LONG_LONG + IsSame::value || +#endif +#if ARDUINOJSON_USE_INT64 + IsSame::value || +#endif + false; +}; +} +} diff --git a/include/lib/ArduinoJson/TypeTraits/IsVariant.hpp b/include/lib/ArduinoJson/TypeTraits/IsVariant.hpp new file mode 100644 index 0000000..f8b299f --- /dev/null +++ b/include/lib/ArduinoJson/TypeTraits/IsVariant.hpp @@ -0,0 +1,17 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "IsBaseOf.hpp" + +namespace ArduinoJson { +namespace Internals { + +class JsonVariantTag {}; + +template +struct IsVariant : IsBaseOf {}; +} +} diff --git a/include/lib/ArduinoJson/TypeTraits/RemoveConst.hpp b/include/lib/ArduinoJson/TypeTraits/RemoveConst.hpp new file mode 100644 index 0000000..39d4cb5 --- /dev/null +++ b/include/lib/ArduinoJson/TypeTraits/RemoveConst.hpp @@ -0,0 +1,20 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ArduinoJson { +namespace Internals { + +// A meta-function that return the type T without the const modifier +template +struct RemoveConst { + typedef T type; +}; +template +struct RemoveConst { + typedef T type; +}; +} +} diff --git a/include/lib/ArduinoJson/TypeTraits/RemoveReference.hpp b/include/lib/ArduinoJson/TypeTraits/RemoveReference.hpp new file mode 100644 index 0000000..395a128 --- /dev/null +++ b/include/lib/ArduinoJson/TypeTraits/RemoveReference.hpp @@ -0,0 +1,20 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ArduinoJson { +namespace Internals { + +// A meta-function that return the type T without the reference modifier. +template +struct RemoveReference { + typedef T type; +}; +template +struct RemoveReference { + typedef T type; +}; +} +} diff --git a/include/lib/ArduinoJson/version.hpp b/include/lib/ArduinoJson/version.hpp new file mode 100644 index 0000000..a71c3ab --- /dev/null +++ b/include/lib/ArduinoJson/version.hpp @@ -0,0 +1,10 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#define ARDUINOJSON_VERSION "5.13.2" +#define ARDUINOJSON_VERSION_MAJOR 5 +#define ARDUINOJSON_VERSION_MINOR 13 +#define ARDUINOJSON_VERSION_REVISION 2 diff --git a/include/lib/ubjson/ubj.h b/include/lib/ubjson/ubj.h new file mode 100644 index 0000000..a65d104 --- /dev/null +++ b/include/lib/ubjson/ubj.h @@ -0,0 +1,230 @@ +#ifndef UBJ_H +#define UBJ_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +typedef enum +{ + UBJ_MIXED=0, //NOT a type...or the type is mixed + + UBJ_NULLTYPE, + UBJ_NOOP, + UBJ_BOOL_TRUE, + UBJ_BOOL_FALSE, + + UBJ_CHAR, + UBJ_STRING, + UBJ_HIGH_PRECISION, + + UBJ_INT8, + UBJ_UINT8 , + UBJ_INT16, + UBJ_INT32, + UBJ_INT64, + UBJ_FLOAT32 , + UBJ_FLOAT64, + + UBJ_ARRAY, + UBJ_OBJECT, + + UBJ_NUM_TYPES //this is the size of how many types there are (chris' trick) +} UBJ_TYPE; + + + +//////////here is the declarations for the writer API//////////////////////////////////// + + + +///////////////////////////////////////////////////////////////////////////////////////// + +struct ubjw_context_t_s; +typedef struct ubjw_context_t_s ubjw_context_t; + +ubjw_context_t* ubjw_open_callback(void* userdata, + size_t(*write_cb)(const void* data, size_t size, size_t count, void* userdata), + int(*close_cb)(void* userdata), + void(*error_cb)(const char* error_msg) + ); +ubjw_context_t* ubjw_open_file(FILE*); +ubjw_context_t* ubjw_open_memory(uint8_t* dst_b, uint8_t* dst_e); + +size_t ubjw_close_context(ubjw_context_t* ctx); + +void ubjw_write_string(ubjw_context_t* dst, const char* out); +void ubjw_write_char(ubjw_context_t* dst, char out); + +void ubjw_write_uint8(ubjw_context_t* dst, uint8_t out); +void ubjw_write_int8(ubjw_context_t* dst, int8_t out); +void ubjw_write_int16(ubjw_context_t* dst, int16_t out); +void ubjw_write_int32(ubjw_context_t* dst, int32_t out); +void ubjw_write_int64(ubjw_context_t* dst, int64_t out); +void ubjw_write_high_precision(ubjw_context_t* dst, const char* hp); + +void ubjw_write_integer(ubjw_context_t* dst, int64_t out); +UBJ_TYPE ubjw_min_integer_type(int64_t in); + +void ubjw_write_float32(ubjw_context_t* dst, float out); +void ubjw_write_float64(ubjw_context_t* dst, double out); + +void ubjw_write_floating_point(ubjw_context_t* dst, double out); + +void ubjw_write_noop(ubjw_context_t* dst); +void ubjw_write_null(ubjw_context_t* dst); +void ubjw_write_bool(ubjw_context_t* dst, uint8_t out); + +void ubjw_begin_array(ubjw_context_t* dst, UBJ_TYPE type, size_t count); + +void ubjw_begin_object(ubjw_context_t* dst, UBJ_TYPE type, size_t count); +void ubjw_write_key(ubjw_context_t* dst, const char* key); +void ubjw_end(ubjw_context_t* dst); + +//output an efficient buffer of types +void ubjw_write_buffer(ubjw_context_t* dst, const uint8_t* data, UBJ_TYPE type, size_t count); + +//Proposal for N-D arrays +void ubjw_begin_ndarray(ubjw_context_t* dst, UBJ_TYPE type, const size_t* dims, uint8_t ndims); +void ubjw_write_ndbuffer(ubjw_context_t* dst,const uint8_t* data, UBJ_TYPE type, const size_t* dims, uint8_t ndims); + + +//////////here is the declarations for the reader API//////////////////////////////////// + + + +///////////////////////////////////////////////////////////////////////////////////////// + +struct ubjr_context_t_s; +typedef struct ubjr_context_t_s ubjr_context_t; + +//Open up a reader context for reading using a custom calllback +ubjr_context_t* ubjr_open_callback(void* userdata, + size_t(*read_cb)(void* data, size_t size, size_t count, void* userdata), + int(*peek_cb)(void* userdata), + int(*close_cb)(void* userdata), + void(*error_cb)(const char* error_msg) + ); + +//Open a context initialized to a UBJ file +ubjr_context_t* ubjr_open_file(FILE*); + +//Open up a context initialized to a memory dump of a UBJ file (or a segment of a UBJ file) +ubjr_context_t* ubjr_open_memory(const uint8_t* dst_b, const uint8_t* dst_e); + +//Close a reader context +size_t ubjr_close_context(ubjr_context_t* ctx); + +typedef char* ubjr_string_t; + +//An array that you read from the stream +typedef struct ubjr_array_t_s +{ + uint8_t originally_sized; + UBJ_TYPE type; + size_t size; //total number of elements + void* values; + uint8_t num_dims; + size_t* dims; //this could be faster if it was constant size, but would also make the size of the dynamic object a LOT bigger + +} ubjr_array_t; + +//a map that you read from the stream +typedef struct ubjr_object_t_s +{ + uint8_t originally_sized; + UBJ_TYPE type; + size_t size; + void* values; + ubjr_string_t* keys; + void* metatable; //don't use this..only useful for computing object_lookup +} ubjr_object_t; + +//a dynamic type that you parsed. +typedef struct ubjr_dynamic_t_s +{ + UBJ_TYPE type; + union + { + uint8_t boolean; + double real; + int64_t integer; + ubjr_string_t string; + ubjr_array_t container_array; + ubjr_object_t container_object; + }; +} ubjr_dynamic_t; + +//Parse a dynamic object from the stream +ubjr_dynamic_t ubjr_read_dynamic(ubjr_context_t* ctx); +void ubjr_cleanup_dynamic(ubjr_dynamic_t* dyn); + +ubjr_dynamic_t ubjr_object_lookup(ubjr_object_t* obj, const char* key); +size_t ubjr_local_type_size(UBJ_TYPE typ);//should be equivalent to sizeof() +size_t ubjr_ndarray_index(const ubjr_array_t* arr, const size_t* indices); + + +//output an efficient buffer of types +///void ubjr_read_buffer(struct ubjr_context_t* dst, const uint8_t* data, UBJ_TYPE type, size_t count); + +void ubjr_cleanup_dynamic(ubjr_dynamic_t* dyn); +void ubjr_cleanup_array(ubjr_array_t* arr); +void ubjr_cleanup_object(ubjr_object_t* obj); + + + +///////UBJ_RW api + +void ubjrw_write_dynamic(ubjw_context_t* ctx, ubjr_dynamic_t dobj,uint8_t optimize); +//ubjrw_append_object(ubjw_context_t* ctx, ubjr_dynamic_t dobj); +//ubjrw_append_array(ubjw_context_t* ctx, ubjr_dynamic_t dobj); + +#ifdef __cplusplus +} + +#include + +static size_t write_os(const void* data, size_t size, size_t count, void* userdata) +{ + size_t n = size*count; + reinterpret_cast(userdata)->write(data, n); + return n; +} +static void close_os(void* userdata) +{ + reinterpret_cast(userdata)->close(); +} + +static size_t read_is(void* data, size_t size, size_t count, void* userdata) +{ + size_t n = size*count; + reinterpret_cast(userdata)->read(data, n); + return n; +} +static int peek_is(void* userdata) +{ + return reinterpret_cast(userdata)->peek(); +} +static void close_is(void* userdata) +{ + reinterpret_cast(userdata)->close(); +} + +static ubjw_context_t* ubjw_open_stream(std::ostream& outstream) +{ + return ubjw_open_callback((void*)&outstream, write_os, close_os, NULL); +} + +static ubjr_context_t* ubjr_open_stream(std::istream& instream) +{ + return ubjr_open_callback((void*)&instream, read_is, peek_is, close_is, NULL); +} + + + +#endif + +#endif diff --git a/include/lib/ubjson/ubj_internal.h b/include/lib/ubjson/ubj_internal.h new file mode 100644 index 0000000..fc61697 --- /dev/null +++ b/include/lib/ubjson/ubj_internal.h @@ -0,0 +1,163 @@ +#ifndef UBJ_INTERNAL_H +#define UBJ_INTERNAL_H + +#include "ubj.h" +#include +#include + +#if _MSC_VER +#define inline __inline +#endif + + +static const uint8_t UBJI_TYPEC_convert[UBJ_NUM_TYPES] = "\x00ZNTFCSHiUIlLdD[{"; + +static const int UBJI_TYPE_size[UBJ_NUM_TYPES] = + { -1, //MIXED + 0, //NULLTYPE + 0, //NOOP + 0, //BOOL_TRUE + 0, //BOOL_FALSE + 1, //CHAR + sizeof(const char*), //STRING + sizeof(const char*), //high-precision + 1, //INT8 + 1, //UINT8 + 2, //int16 + 4, //int32 + 8, //int64 + 4, //float32 + 8, //float64 + -1, //array + -1 //object + }; + +static const size_t UBJR_TYPE_localsize[UBJ_NUM_TYPES] = +{ + sizeof(ubjr_dynamic_t), //MIXED + 0, //NULLTYPE + 0, //NOOP + 0, //BOOL_TRUE + 0, //BOOL_FALSE + sizeof(ubjr_string_t), //CHAR + sizeof(ubjr_string_t), //STRING + sizeof(ubjr_string_t), //high-precision + sizeof(int8_t), //INT8 + sizeof(uint8_t), //UINT8 + sizeof(int16_t), //int16 + sizeof(int32_t), //int32 + sizeof(int64_t), //int64 + sizeof(float), //float32 + sizeof(double), //float64 + sizeof(ubjr_array_t), //array + sizeof(ubjr_object_t) //object +}; + +static inline void _to_bigendian16(uint8_t* outbuffer, uint16_t input) +{ + *outbuffer++ = (input >> 8); // Get top order byte (guaranteed endian-independent since machine registers) + *outbuffer++ = input & 0xFF; // Get bottom order byte +} +static inline void _to_bigendian32(uint8_t* outbuffer, uint32_t input) +{ + _to_bigendian16(outbuffer, (uint16_t)(input >> 16)); // Get top order 2 bytes + _to_bigendian16(outbuffer + 2, (uint16_t)(input & 0xFFFF)); // Get bottom order 2 bytes +} +static inline void _to_bigendian64(uint8_t* outbuffer, uint64_t input) +{ + _to_bigendian32(outbuffer, (uint32_t)(input >> 32)); + _to_bigendian32(outbuffer + 4, (uint32_t)(input & 0xFFFFFFFF)); +} + +static inline uint8_t _is_bigendian() +{ + int i = 1; + char *low = (char*)&i; + return *low ? 0 : 1; +} + +#define BUF_BIG_ENDIAN_SWAP(type,func,ptr,num) \ + { \ + size_t i;type* d = (type*)ptr; \ + for (i = 0; i < num; i++) \ + { \ + func((uint8_t*)&d[i], d[i]); \ + } \ + } \ + +static inline void buf_endian_swap(uint8_t* buf, size_t sz, size_t n) +{ + if (!_is_bigendian()) + { + switch (sz) + { + case 1: + case 0: + break; + case 2: + BUF_BIG_ENDIAN_SWAP(uint16_t, _to_bigendian16,buf,n); + break; + case 4: + BUF_BIG_ENDIAN_SWAP(uint32_t, _to_bigendian32,buf,n); + break; + case 8: + BUF_BIG_ENDIAN_SWAP(uint64_t, _to_bigendian64,buf,n); + break; + }; + } +} + +//warning...null-terminated strings are assumed...when this is not necessarily valid. FIXED: we don't use null-terminated strings in the reader (NOT FIXED...string type is awkward) +static inline ubjr_dynamic_t priv_ubjr_pointer_to_dynamic(UBJ_TYPE typ, const void* dat) +{ + ubjr_dynamic_t outdyn; + outdyn.type = typ; + size_t n = 1; + switch (typ) + { + case UBJ_NULLTYPE: + case UBJ_NOOP: + break; + case UBJ_BOOL_TRUE: + case UBJ_BOOL_FALSE: + outdyn.boolean = (typ == UBJ_BOOL_TRUE ? 1 : 0); + break; + case UBJ_HIGH_PRECISION: + case UBJ_STRING: + case UBJ_CHAR://possibly if char allocate, otherwise don't + outdyn.string = *(const ubjr_string_t*)dat; + break; + case UBJ_INT8: + outdyn.integer = *(const int8_t*)dat; + break; + case UBJ_UINT8: + outdyn.integer = *(const uint8_t*)dat; + break; + case UBJ_INT16: + outdyn.integer = *(const int16_t*)dat; + break; + case UBJ_INT32: + outdyn.integer = *(const int32_t*)dat; + break; + case UBJ_INT64: + outdyn.integer = *(const int64_t*)dat; + break; + case UBJ_FLOAT32: + outdyn.real = *(const float*)dat; + break; + case UBJ_FLOAT64: + outdyn.real = *(const double*)dat; + break; + case UBJ_ARRAY: + outdyn.container_array = *(const ubjr_array_t*)dat; + break; + case UBJ_OBJECT: + outdyn.container_object = *(const ubjr_object_t*)dat; + break; + case UBJ_MIXED: + outdyn = *(const ubjr_dynamic_t*)dat; + }; + return outdyn; +} + +#endif \ No newline at end of file diff --git a/src/app/prototest/Makefile.inc b/src/app/prototest/Makefile.inc new file mode 100644 index 0000000..d501de1 --- /dev/null +++ b/src/app/prototest/Makefile.inc @@ -0,0 +1,3 @@ +loop ?= 1 + +TARGETS += src/lib/ubjson/ubjr.c src/lib/ubjson/ubjw.c diff --git a/src/app/prototest/main.cc b/src/app/prototest/main.cc new file mode 100644 index 0000000..c7d0838 --- /dev/null +++ b/src/app/prototest/main.cc @@ -0,0 +1,30 @@ +#include "arch.h" +#include "driver/gpio.h" +#include "driver/stdout.h" +#include "lib/ArduinoJson.h" + +void loop(void) +{ + char json[] = "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}"; + ArduinoJson::StaticJsonBuffer<200> jsonBuffer; + ArduinoJson::JsonObject& root = jsonBuffer.parseObject(json); + const char *sensor = root["sensor"]; + kout << "sensor: " << sensor << endl; + gpio.led_toggle(1); +#ifdef TIMER_S + kout << dec << uptime.get_s() << endl; +#endif +} + +int main(void) +{ + arch.setup(); + gpio.setup(); + kout.setup(); + + gpio.led_on(0); + kout << "Hello, World!" << endl; + arch.idle_loop(); + + return 0; +} diff --git a/src/lib/ubjson/ubjr.c b/src/lib/ubjson/ubjr.c new file mode 100644 index 0000000..461459e --- /dev/null +++ b/src/lib/ubjson/ubjr.c @@ -0,0 +1,516 @@ +#include "lib/ubjson/ubj.h" +#include "lib/ubjson/ubj_internal.h" +#include +#include + +#if _MSC_VER +#define inline __inline +#endif + +typedef struct ubjr_context_t_s +{ + size_t (*read_cb )(void* data, size_t size, size_t count, void* userdata); + int (*peek_cb )(void* userdata); + int (*close_cb)(void* userdata); + void (*error_cb)(const char* error_msg); + + void* userdata; + +// struct _ubjr_container_t container_stack[CONTAINER_STACK_MAX]; +// struct _ubjr_container_t* head; + + uint8_t ignore_container_flags; + + uint16_t last_error_code; + + size_t total_read; +} ubjr_context_t; + +ubjr_context_t* ubjr_open_callback(void* userdata, + size_t(*read_cb)(void* data, size_t size, size_t count, void* userdata), + int(*peek_cb)(void* userdata), + int(*close_cb)(void* userdata), + void(*error_cb)(const char* error_msg) + ) +{ + ubjr_context_t* ctx = (ubjr_context_t*)malloc(sizeof(ubjr_context_t)); + ctx->userdata = userdata; + ctx->read_cb = read_cb; + ctx->peek_cb = peek_cb; + ctx->close_cb = close_cb; + ctx->error_cb = error_cb; + + +/* ctx->head = ctx->container_stack; + ctx->head->flags = 0; + ctx->head->type = UBJ_MIXED; + ctx->head->elements_remaining = 0; + + ctx->ignore_container_flags = 0;*/ + + ctx->last_error_code = 0; + + ctx->total_read = 0; + return ctx; +} + +size_t ubjr_close_context(ubjr_context_t* ctx) +{ + size_t n = ctx->total_read; + free(ctx); + return n; +} + +static inline uint8_t priv_ubjr_context_getc(ubjr_context_t* ctx) +{ + uint8_t a; + ctx->total_read += 1; + ctx->read_cb(&a, 1, 1, ctx->userdata); + return a; +} + +static int fpeek(void* fp) +{ + int c; + c = fgetc(fp); + ungetc(c, fp); + + return c; +} + +ubjr_context_t* ubjr_open_file(FILE* fd) +{ + return ubjr_open_callback(fd, (void*)fread,(void*)fpeek,(void*)fclose, NULL); +} + +struct mem_r_fd +{ + const uint8_t *begin, *current, *end; +}; +static int memclose(void* mfd) +{ + //free(mfd); + return 0; +} +static size_t memread(void* data, size_t size, size_t count, struct mem_r_fd* fp) +{ + size_t n = size*count; + size_t lim = fp->end - fp->current; + if (lim < n) + { + n = lim; + } + memcpy(data, fp->current, n); + fp->current += n; + return n; +} +static int mempeek(struct mem_r_fd* mfd) +{ + return *mfd->current; +} + +ubjr_context_t* ubjr_open_memory(const uint8_t* be, const uint8_t* en) +{ + struct mem_r_fd* mfd = (struct mem_r_fd*)malloc(sizeof(struct mem_r_fd)); + mfd->current = be; + mfd->begin = be; + mfd->end = en; + return ubjr_open_callback(mfd, (void*)memread, (void*)mempeek,(void*)memclose, NULL); +} + +static inline int priv_ubjr_context_peek(ubjr_context_t* ctx) +{ + return ctx->peek_cb(ctx->userdata); +} +static inline size_t priv_ubjr_context_read(ubjr_context_t* ctx,uint8_t* dst,size_t n) +{ + size_t nr=ctx->read_cb(dst,n,1,ctx->userdata); + ctx->total_read+=nr; + return nr; +} + +typedef struct priv_ubjr_sorted_key_t_s +{ + ubjr_string_t key; + const uint8_t* value; + +} priv_ubjr_sorted_key_t; + +static int _obj_key_cmp(const void* av, const void* bv) +{ + const priv_ubjr_sorted_key_t *a, *b; + a = (const priv_ubjr_sorted_key_t*)av; + b = (const priv_ubjr_sorted_key_t*)bv; + return strcmp(a->key,b->key); +} + +static inline UBJ_TYPE priv_ubjr_type_from_char(uint8_t c) +{ + int i = 0; //TODO: Benchmark this and see if it should be a switch statement where the compiler implements fastest switch e.g. binary search (17 cases might be binary search fast) + for (i = 0; i < UBJ_NUM_TYPES && UBJI_TYPEC_convert[i] != c; i++); + return (UBJ_TYPE)i; +} + +size_t ubjr_local_type_size(UBJ_TYPE typ) +{ + return UBJR_TYPE_localsize[typ]; +} + + +static inline priv_ubjr_sorted_key_t* priv_ubjr_object_build_sorted_keys(ubjr_object_t* obj) +{ + priv_ubjr_sorted_key_t* sorted_keysmem = malloc(obj->size*sizeof(priv_ubjr_sorted_key_t)); + size_t i; + for (i = 0; i < obj->size; i++) + { + sorted_keysmem[i].key = obj->keys[i]; + sorted_keysmem[i].value = (const uint8_t*)obj->values + i*UBJR_TYPE_localsize[obj->type]; + } + qsort(sorted_keysmem, obj->size, sizeof(priv_ubjr_sorted_key_t), _obj_key_cmp); + return sorted_keysmem; +} + +static inline uint8_t priv_ubjr_read_1b(ubjr_context_t* ctx) +{ + return priv_ubjr_context_getc(ctx); +} +static inline uint16_t priv_ubjr_read_2b(ubjr_context_t* ctx) +{ + return (uint16_t)priv_ubjr_read_1b(ctx) << 8 | (uint16_t)priv_ubjr_read_1b(ctx); +} +static inline uint32_t priv_ubjr_read_4b(ubjr_context_t* ctx) +{ + return (uint32_t)priv_ubjr_read_2b(ctx) << 16 | (uint32_t)priv_ubjr_read_2b(ctx); +} +static inline uint64_t priv_ubjr_read_8b(ubjr_context_t* ctx) +{ + return (uint64_t)priv_ubjr_read_4b(ctx) << 32 | (uint64_t)priv_ubjr_read_4b(ctx); +} + +static inline int64_t priv_ubjw_read_integer(ubjr_context_t* ctx) +{ + ubjr_dynamic_t d = ubjr_read_dynamic(ctx); + if (d.type >= UBJ_INT8 && d.type <= UBJ_INT64) + return d.integer; + return 0;//error +} + +static inline ubjr_object_t priv_ubjr_read_raw_object(ubjr_context_t* ctx); +static inline ubjr_array_t priv_ubjr_read_raw_array(ubjr_context_t* ctx); +static inline void priv_ubjr_read_to_ptr(ubjr_context_t* ctx, uint8_t* dst, UBJ_TYPE typ) +{ + int64_t n = 1; + char *tstr; + switch (typ) + { + case UBJ_MIXED: + { + *(ubjr_dynamic_t*)dst = ubjr_read_dynamic(ctx); + break; + } + case UBJ_STRING: + case UBJ_HIGH_PRECISION: + { + n = priv_ubjw_read_integer(ctx); + } + case UBJ_CHAR: + { + tstr = malloc(n + 1); + priv_ubjr_context_read(ctx, tstr, n); + tstr[n] = 0; + *(ubjr_string_t*)dst = tstr; + break; + } + case UBJ_INT8: + case UBJ_UINT8: + { + *dst = priv_ubjr_read_1b(ctx); + break; + } + case UBJ_INT16: + { + *(uint16_t*)dst = priv_ubjr_read_2b(ctx); + break; + } + case UBJ_INT32: + case UBJ_FLOAT32: + { + *(uint32_t*)dst = priv_ubjr_read_4b(ctx); + break; + } + case UBJ_INT64: + case UBJ_FLOAT64: + { + *(uint64_t*)dst = priv_ubjr_read_8b(ctx); + break; + } + case UBJ_ARRAY: + { + *(ubjr_array_t*)dst = priv_ubjr_read_raw_array(ctx); + break; + } + case UBJ_OBJECT: + { + *(ubjr_object_t*)dst = priv_ubjr_read_raw_object(ctx); + break; + } + }; +} + +ubjr_dynamic_t ubjr_object_lookup(ubjr_object_t* obj, const char* key) +{ + if (obj->metatable == NULL) + { + //memcpy(obj->sorted_keys,obj->keys) + obj->metatable = priv_ubjr_object_build_sorted_keys(obj); + } + void* result=bsearch(key, obj->metatable,obj->size, sizeof(priv_ubjr_sorted_key_t),_obj_key_cmp); + if (result == NULL) + { + ubjr_dynamic_t nulldyn; + nulldyn.type = UBJ_NULLTYPE; + return nulldyn; + } + const priv_ubjr_sorted_key_t* result_key = (const priv_ubjr_sorted_key_t*)result; + return priv_ubjr_pointer_to_dynamic(obj->type,result_key->value); +} + +size_t ubjr_ndarray_index(const ubjr_array_t* arr, const size_t* indices) +{ + //multi-dimensional array to linear array lookup + size_t cstride = 1; + size_t cdex = 0; + uint8_t i; + uint8_t nd = arr->num_dims; + const size_t* dims = arr->dims; + for (i = 0; i= (1ULL << arrpot)) + { + arrpot ++; + myarray.values = realloc(myarray.values, (1ULL << arrpot)*ls+1); + } + priv_ubjr_read_to_ptr(ctx,(uint8_t*)myarray.values + ls*myarray.size,myarray.type); + } + priv_ubjr_context_getc(ctx); // read the closing ']' + } + else + { + myarray.originally_sized = 1; + size_t i; + myarray.values = malloc(ls*myarray.size+1); + size_t sz = UBJI_TYPE_size[myarray.type]; + + if (sz >= 0 && myarray.type != UBJ_STRING && myarray.type != UBJ_HIGH_PRECISION && myarray.type != UBJ_CHAR && myarray.type != UBJ_MIXED) //constant size,fastread + { + priv_ubjr_context_read(ctx, myarray.values, sz*myarray.size); + buf_endian_swap(myarray.values, sz, myarray.size); //do nothing for 0-sized buffers + } + else + { + for (i = 0; i < myarray.size; i++) + { + priv_ubjr_read_to_ptr(ctx, (uint8_t*)myarray.values + ls*i, myarray.type); + } + } + } + if (myarray.dims == NULL) + { + myarray.dims = malloc(sizeof(size_t)); + myarray.dims[0] = myarray.size; + } + return myarray; +} + +static inline ubjr_object_t priv_ubjr_read_raw_object(ubjr_context_t* ctx) +{ + ubjr_object_t myobject; + myobject.metatable = NULL; + priv_read_container_params(ctx, &myobject.type, &myobject.size); + + size_t ls = UBJR_TYPE_localsize[myobject.type]; + if (myobject.size == 0) + { + myobject.originally_sized = 0; + size_t arrpot = 0; + myobject.values = malloc(1 * ls + 1); //the +1 is for memory for the 0-size elements + myobject.keys = malloc(1 * sizeof(ubjr_string_t)); + for (myobject.size = 0; priv_ubjr_context_peek(ctx) != '}'; myobject.size++) + { + if (myobject.size >= (1ULL << arrpot)) + { + arrpot++; + myobject.values = realloc(myobject.values, (1ULL << arrpot)*ls + 1); + myobject.keys = realloc((uint8_t*)myobject.keys, (1ULL << arrpot)*sizeof(ubjr_string_t)); + } + priv_ubjr_read_to_ptr(ctx, (uint8_t*)(myobject.keys + myobject.size), UBJ_STRING); + priv_ubjr_read_to_ptr(ctx, (uint8_t*)myobject.values + ls*myobject.size, myobject.type); + } + priv_ubjr_context_getc(ctx); // read the closing '}' + } + else + { + size_t i; + myobject.originally_sized = 1; + myobject.values = malloc(ls*myobject.size + 1); + myobject.keys = malloc(myobject.size * sizeof(ubjr_string_t)); + + for (i = 0; i < myobject.size; i++) + { + priv_ubjr_read_to_ptr(ctx, (uint8_t*)(myobject.keys + i), UBJ_STRING); + priv_ubjr_read_to_ptr(ctx, (uint8_t*)myobject.values + ls*i, myobject.type); + } + } + return myobject; +} +static inline void priv_ubjr_cleanup_pointer(UBJ_TYPE typ,void* value); +static inline priv_ubjr_cleanup_container(UBJ_TYPE type,size_t size,void* values) +{ + if(type == UBJ_MIXED || type == UBJ_ARRAY || type == UBJ_OBJECT || type == UBJ_STRING) + { + size_t ls=UBJR_TYPE_localsize[type]; + uint8_t *viter,*vend; + viter=values; + vend=viter+ls*size; + for(;viter != vend;viter+=ls) + { + priv_ubjr_cleanup_pointer(type,(void*)viter); + } + } + free(values); +} +static inline void priv_ubjr_cleanup_pointer(UBJ_TYPE typ,void* value) +{ + switch(typ) + { + case UBJ_MIXED: + { + ubjr_dynamic_t* dyn=(ubjr_dynamic_t*)value; + switch(dyn->type) + { + case UBJ_STRING: + priv_ubjr_cleanup_pointer(UBJ_STRING,&dyn->string); + break; + case UBJ_ARRAY: + priv_ubjr_cleanup_pointer(UBJ_ARRAY,&dyn->container_array); + break; + case UBJ_OBJECT: + priv_ubjr_cleanup_pointer(UBJ_OBJECT,&dyn->container_object); + break; + }; + break; + } + case UBJ_STRING: + { + ubjr_string_t* st=(ubjr_string_t*)value; + free((void*)*st); + break; + } + case UBJ_ARRAY: + { + ubjr_array_t* arr=(ubjr_array_t*)value; + priv_ubjr_cleanup_container(arr->type,arr->size,arr->values); + free(arr->dims); + break; + } + case UBJ_OBJECT: + { + ubjr_object_t* obj=(ubjr_object_t*)value; + priv_ubjr_cleanup_container(obj->type,obj->size,obj->values); + priv_ubjr_cleanup_container(UBJ_STRING,obj->size,obj->keys); + if(obj->metatable) + { + free(obj->metatable); + } + break; + } + }; +} + +void ubjr_cleanup_dynamic(ubjr_dynamic_t* dyn) +{ + priv_ubjr_cleanup_pointer(UBJ_MIXED,dyn); +} +void ubjr_cleanup_array(ubjr_array_t* arr) +{ + priv_ubjr_cleanup_pointer(UBJ_ARRAY,arr); +} +void ubjr_cleanup_object(ubjr_object_t* obj) +{ + priv_ubjr_cleanup_pointer(UBJ_OBJECT,obj); +} + diff --git a/src/lib/ubjson/ubjrw.c b/src/lib/ubjson/ubjrw.c new file mode 100644 index 0000000..5b8b102 --- /dev/null +++ b/src/lib/ubjson/ubjrw.c @@ -0,0 +1,169 @@ +#include "lib/ubjson/ubj_internal.h" + +static uint32_t compute_typemask(ubjr_dynamic_t* vals, size_t sz) +{ + uint32_t typemask = 0; + size_t i; + for (i = 0; i < sz; i++) + { + typemask |= 1UL << vals[i].type; + } + return typemask; +} + +static inline UBJ_TYPE typemask2type(uint32_t v) +{ + unsigned int r = 0; // r will be lg(v) + + while (v >>= 1) // unroll for more speed... + { + r++; + } + return (UBJ_TYPE)r; +} +static UBJ_TYPE compute_best_integer_type(ubjr_dynamic_t* vals, size_t sz) +{ + uint32_t typemask = 0; + size_t i; + for (i = 0; i < sz; i++) + { + typemask |= 1UL << ubjw_min_integer_type(vals[i].integer); + } + return typemask2type(typemask); +} +static uint32_t compute_best_string_type(ubjr_dynamic_t* vals, size_t sz) +{ + size_t i; + for (i = 0; i < sz; i++) + { + if (strlen(vals[i].string) > 1) + { + return UBJ_STRING; + } + } + return UBJ_CHAR; +} +static UBJ_TYPE optimize_type(UBJ_TYPE typein,ubjr_dynamic_t* vals, size_t sz) +{ + static const uint32_t intmask = (1 << UBJ_INT8) | (1 << UBJ_UINT8) | (1 << UBJ_INT16) | (1 << UBJ_INT32) | (1 << UBJ_INT64); + static const uint32_t stringmask = (1 << UBJ_STRING) | (1 << UBJ_CHAR); + if (typein != UBJ_MIXED) + return typein; + //integer optimization can be done here... + uint32_t tm = compute_typemask(vals, sz); + if ((tm & intmask) == tm) //if all values are integers + { + return compute_best_integer_type(vals,sz); //calculate the optimum type given the data + } + else if ((tm & stringmask) == tm) + { + return compute_best_string_type(vals,sz); + } + else if(tm && !(tm & (tm- 1))) //if only one bit is set in typemask + { + return typemask2type(tm); //figure out which bit is set. + } + else + { + return UBJ_MIXED; + } +} + +void ubjrw_write_dynamic(ubjw_context_t* ctx, ubjr_dynamic_t dobj,uint8_t optimize) +{ + UBJ_TYPE ctyp,otyp; + size_t csize; + uint8_t* cvalues; + switch (dobj.type) + { + case UBJ_MIXED: + return;///error, can't be mixed + case UBJ_NULLTYPE: + ubjw_write_null(ctx); + return; + case UBJ_NOOP: + ubjw_write_noop(ctx); + return; + case UBJ_BOOL_FALSE: + ubjw_write_bool(ctx, 0); + return; + case UBJ_BOOL_TRUE: + ubjw_write_bool(ctx, 1); + return; + case UBJ_CHAR: + ubjw_write_char(ctx, *dobj.string);//first character of string + return; + case UBJ_STRING: + ubjw_write_string(ctx, dobj.string); + return; + case UBJ_HIGH_PRECISION: + ubjw_write_high_precision(ctx, dobj.string); + return; + case UBJ_INT8: + ubjw_write_int8(ctx, (int8_t)dobj.integer); + return; + case UBJ_UINT8: + ubjw_write_uint8(ctx, (uint8_t)dobj.integer); + return; + case UBJ_INT16: + ubjw_write_int16(ctx, (int16_t)dobj.integer); + return; + case UBJ_INT32: + ubjw_write_int32(ctx, (int32_t)dobj.integer); + return; + case UBJ_INT64: + ubjw_write_int64(ctx, dobj.integer); + return; + case UBJ_FLOAT32: + ubjw_write_float32(ctx, (float)dobj.real); + return; + case UBJ_FLOAT64: + ubjw_write_float64(ctx, dobj.real); + return; + case UBJ_ARRAY: + if ((dobj.container_array.originally_sized || optimize) //if we optimize an unsized array to a sized one or the original is sized + && dobj.container_array.type != UBJ_MIXED + && dobj.container_array.type != UBJ_OBJECT + && dobj.container_array.type != UBJ_ARRAY) + { + ubjw_write_buffer(ctx, dobj.container_array.values, dobj.container_array.type, dobj.container_array.size); + return; + } + else + { + ctyp = dobj.container_array.type; + csize = dobj.container_array.size; + cvalues = dobj.container_array.values; + otyp = optimize ? optimize_type(ctyp,(ubjr_dynamic_t*)cvalues,csize) : ctyp; + ubjw_begin_array(ctx, otyp, (dobj.container_array.originally_sized || optimize) ? csize : 0); + break; + } + case UBJ_OBJECT: + { + ctyp = dobj.container_object.type; + csize = dobj.container_object.size; + cvalues = dobj.container_object.values; + otyp = optimize ? optimize_type(ctyp, (ubjr_dynamic_t*)cvalues, csize) : ctyp; + ubjw_begin_object(ctx, otyp, (dobj.container_object.originally_sized || optimize) ? csize : 0); + break; + } + }; + { + size_t i; + ubjr_dynamic_t scratch; + size_t ls = UBJR_TYPE_localsize[ctyp]; + + for (i = 0; i < csize; i++) + { + if (dobj.type == UBJ_OBJECT) + { + ubjw_write_key(ctx, dobj.container_object.keys[i]); + } + scratch = priv_ubjr_pointer_to_dynamic(ctyp, cvalues + ls*i); + scratch.type = (otyp == UBJ_MIXED ? scratch.type : otyp); + ubjrw_write_dynamic(ctx, scratch,optimize); + } + ubjw_end(ctx); + } + +} diff --git a/src/lib/ubjson/ubjw.c b/src/lib/ubjson/ubjw.c new file mode 100644 index 0000000..78eec83 --- /dev/null +++ b/src/lib/ubjson/ubjw.c @@ -0,0 +1,618 @@ +#include "lib/ubjson/ubj.h" +#include "lib/ubjson/ubj_internal.h" + +#define CONTAINER_IS_SIZED 0x1 +#define CONTAINER_IS_TYPED 0x2 +#define CONTAINER_IS_UBJ_ARRAY 0x4 +#define CONTAINER_IS_UBJ_OBJECT 0x8 + +#define CONTAINER_EXPECTS_KEY 0x10 + +#define CONTAINER_STACK_MAX 64 +#define BUFFER_OUT_SIZE 1024 + +#define MAX_DIMS 8 + + +struct priv_ubjw_container_t +{ + uint8_t flags; + UBJ_TYPE type; + size_t elements_remaining; +}; + +struct ubjw_context_t_s +{ + size_t(*write_cb)(const void* data, size_t size, size_t count, void* userdata); + int(*close_cb)(void* userdata); + void (*error_cb)(const char* error_msg); + + void* userdata; + + struct priv_ubjw_container_t container_stack[CONTAINER_STACK_MAX]; + struct priv_ubjw_container_t* head; + + uint8_t ignore_container_flags; + + uint16_t last_error_code; + + size_t total_written; +}; + + + +ubjw_context_t* ubjw_open_callback(void* userdata, + size_t(*write_cb)(const void* data, size_t size, size_t count, void* userdata), + int(*close_cb)(void* userdata), + void (*error_cb)(const char* error_msg) + ) +{ + ubjw_context_t* ctx = (ubjw_context_t*)malloc(sizeof(ubjw_context_t)); + ctx->userdata = userdata; + ctx->write_cb = write_cb; + ctx->close_cb = close_cb; + ctx->error_cb = error_cb; + + ctx->head = ctx->container_stack; + ctx->head->flags = 0; + ctx->head->type = UBJ_MIXED; + ctx->head->elements_remaining = 0; + //ctx->head->num_dims=1; + + ctx->ignore_container_flags = 0; + + ctx->last_error_code = 0; + + ctx->total_written = 0; + return ctx; +} +ubjw_context_t* ubjw_open_file(FILE* fd) +{ + return ubjw_open_callback(fd, (void*)fwrite,(void*)fclose,NULL); +} + +struct mem_w_fd +{ + uint8_t *begin,*current, *end; +}; + +static int memclose(void* mfd) +{ + free(mfd); + return 0; +} +static size_t memwrite(const void* data, size_t size, size_t count, struct mem_w_fd* fp) +{ + size_t n = size*count; + size_t lim = fp->end - fp->current; + if (lim < n) + { + n = lim; + } + memcpy(fp->current, data, n); + fp->current += n; + return n; +} + +ubjw_context_t* ubjw_open_memory(uint8_t* be, uint8_t* en) +{ + struct mem_w_fd* mfd = (struct mem_w_fd*)malloc(sizeof(struct mem_w_fd)); + mfd->current = be; + mfd->begin = be; + mfd->end = en; + return ubjw_open_callback(mfd, (void*)memwrite, (void*)memclose,NULL); +} + +static inline void priv_ubjw_context_append(ubjw_context_t* ctx, uint8_t a) +{ + ctx->total_written += 1; + ctx->write_cb(&a, 1, 1, ctx->userdata); +} + +static inline void priv_disassembly_begin(ubjw_context_t* ctx) +{ +#ifdef UBJW_DISASSEMBLY_MODE + priv_ubjw_context_append(ctx, (uint8_t)'['); +#endif +} +static inline void priv_disassembly_end(ubjw_context_t* ctx) +{ +#ifdef UBJW_DISASSEMBLY_MODE + priv_ubjw_context_append(ctx, (uint8_t)']'); +#endif +} +static inline void priv_disassembly_indent(ubjw_context_t* ctx) +{ +#ifdef UBJW_DISASSEMBLY_MODE + int n = ctx->head - ctx->container_stack; + int i; + priv_ubjw_context_append(ctx, (uint8_t)'\n'); + for (i = 0; i < n; i++) + { + priv_ubjw_context_append(ctx, (uint8_t)'\t'); + } +#endif +} + +static inline void priv_ubjw_context_finish_container(ubjw_context_t* ctx, struct priv_ubjw_container_t* head) +{ + if (head->flags & CONTAINER_IS_SIZED) + { + if (head->elements_remaining > 0) + { + //error not all elements written + } + } + else + { + priv_disassembly_begin(ctx); + if (head->flags & CONTAINER_IS_UBJ_ARRAY) + { + priv_ubjw_context_append(ctx, (uint8_t)']'); + } + else if (head->flags & CONTAINER_IS_UBJ_OBJECT) + { + priv_ubjw_context_append(ctx, (uint8_t)'}'); + } + priv_disassembly_end(ctx); + } +} + +static inline priv_ubjw_container_stack_push(ubjw_context_t* ctx, const struct priv_ubjw_container_t* cnt) +{ + size_t height = ctx->head-ctx->container_stack+1; + if(height < CONTAINER_STACK_MAX) + { + *(++(ctx->head))=*cnt; + } + else + { + //todo::error + } +} +static inline struct priv_ubjw_container_t priv_ubjw_container_stack_pop(ubjw_context_t* ctx) +{ + return *ctx->head--; +} + +size_t ubjw_close_context(ubjw_context_t* ctx) +{ + while (ctx->head > ctx->container_stack) + { + struct priv_ubjw_container_t cnt = priv_ubjw_container_stack_pop(ctx); + priv_ubjw_context_finish_container(ctx, &cnt); + }; + size_t n = ctx->total_written; + if (ctx->close_cb) + ctx->close_cb(ctx->userdata); + free(ctx); + return n; +} + + +static inline size_t priv_ubjw_context_write(ubjw_context_t* ctx, const uint8_t* data, size_t sz) +{ + ctx->total_written += sz; + return ctx->write_cb(data, 1, sz, ctx->userdata); +} + +static inline void priv_ubjw_tag_public(ubjw_context_t* ctx, UBJ_TYPE tid) +{ + struct priv_ubjw_container_t* ch = ctx->head; + if (!ctx->ignore_container_flags) + { + + /*if ( + (!(ch->flags & (CONTAINER_IS_UBJ_ARRAY | CONTAINER_IS_UBJ_OBJECT))) && + (tid != UBJ_ARRAY && tid !=UBJ_OBJECT)) + { + //error, only array and object can be first written + }*/ + + if (ch->flags & CONTAINER_IS_UBJ_OBJECT) + { + if (ch->flags & CONTAINER_EXPECTS_KEY) + { + //error,a key expected + return; + } + ch->flags |= CONTAINER_EXPECTS_KEY; //set key expected next time in this context + } + else + { + priv_disassembly_indent(ctx); + } + + if (ch->flags & CONTAINER_IS_SIZED) + { + ch->elements_remaining--; //todo: error if elements remaining is 0; + } + + if ((ch->flags & CONTAINER_IS_TYPED) && ch->type == tid) + { + return; + } + } + priv_disassembly_begin(ctx); + priv_ubjw_context_append(ctx, UBJI_TYPEC_convert[tid]); + priv_disassembly_end(ctx); +} + +static inline void priv_ubjw_write_raw_string(ubjw_context_t* ctx, const char* out)//TODO: possibly use a safe string +{ + size_t n = strlen(out); + ctx->ignore_container_flags = 1; + ubjw_write_integer(ctx, (int64_t)n); + ctx->ignore_container_flags = 0; + priv_disassembly_begin(ctx); + priv_ubjw_context_write(ctx, (const uint8_t*)out, n); + priv_disassembly_end(ctx); +} +void ubjw_write_string(ubjw_context_t* ctx, const char* out) +{ + priv_ubjw_tag_public(ctx,UBJ_STRING); + priv_ubjw_write_raw_string(ctx, out); +} + +static inline void priv_ubjw_write_raw_char(ubjw_context_t* ctx, char out) +{ + priv_disassembly_begin(ctx); + priv_ubjw_context_append(ctx, (uint8_t)out); + priv_disassembly_end(ctx); +} +void ubjw_write_char(ubjw_context_t* ctx, char out) +{ + priv_ubjw_tag_public(ctx,UBJ_CHAR); + priv_ubjw_write_raw_char(ctx, out); +} + +#ifndef min +static inline size_t min(size_t x,size_t y) +{ + return x < y ? x : y; +} +#endif + +#ifdef UBJW_DISASSEMBLY_MODE +#include +#define DISASSEMBLY_PRINT_BUFFER_SIZE 1024 + +static inline priv_disassembly_print(ubjw_context_t* ctx, const char* format,...) +{ + char buffer[DISASSEMBLY_PRINT_BUFFER_SIZE]; + va_list args; + va_start(args, format); + int n=vsnprintf(buffer, DISASSEMBLY_PRINT_BUFFER_SIZE, format, args); + n = min(n, DISASSEMBLY_PRINT_BUFFER_SIZE); + priv_ubjw_context_write(ctx, buffer,n); + va_end(args); +} +#endif + +static inline void priv_ubjw_write_raw_uint8(ubjw_context_t* ctx, uint8_t out) +{ + priv_disassembly_begin(ctx); +#ifndef UBJW_DISASSEMBLY_MODE + priv_ubjw_context_append(ctx, out); +#else + priv_disassembly_print(ctx, "%hhu", out); +#endif + priv_disassembly_end(ctx); +} +void ubjw_write_uint8(ubjw_context_t* ctx, uint8_t out) +{ + priv_ubjw_tag_public(ctx,UBJ_UINT8); + priv_ubjw_write_raw_uint8(ctx, out); +} + +static inline void priv_ubjw_write_raw_int8(ubjw_context_t* ctx, int8_t out) +{ + priv_disassembly_begin(ctx); +#ifndef UBJW_DISASSEMBLY_MODE + priv_ubjw_context_append(ctx, *(uint8_t*)&out); +#else + priv_disassembly_print(ctx, "%hhd", out); +#endif + priv_disassembly_end(ctx); +} +void ubjw_write_int8(ubjw_context_t* ctx, int8_t out) +{ + priv_ubjw_tag_public(ctx,UBJ_INT8); + priv_ubjw_write_raw_int8(ctx, out); +} + +static inline void priv_ubjw_write_raw_int16(ubjw_context_t* ctx, int16_t out) +{ + priv_disassembly_begin(ctx); +#ifndef UBJW_DISASSEMBLY_MODE + uint8_t buf[2]; + _to_bigendian16(buf, *(uint16_t*)&out); + priv_ubjw_context_write(ctx, buf, 2); +#else + priv_disassembly_print(ctx, "%hd", out); +#endif + priv_disassembly_end(ctx); +} +void ubjw_write_int16(ubjw_context_t* ctx, int16_t out) +{ + priv_ubjw_tag_public(ctx,UBJ_INT16); + priv_ubjw_write_raw_int16(ctx, out); +} +static inline void priv_ubjw_write_raw_int32(ubjw_context_t* ctx, int32_t out) +{ + priv_disassembly_begin(ctx); +#ifndef UBJW_DISASSEMBLY_MODE + uint8_t buf[4]; + _to_bigendian32(buf, *(uint32_t*)&out); + priv_ubjw_context_write(ctx, buf, 4); +#else + priv_disassembly_print(ctx, "%ld", out); +#endif + priv_disassembly_end(ctx); +} +void ubjw_write_int32(ubjw_context_t* ctx, int32_t out) +{ + priv_ubjw_tag_public(ctx,UBJ_INT32); + priv_ubjw_write_raw_int32(ctx, out); +} +static inline void priv_ubjw_write_raw_int64(ubjw_context_t* ctx, int64_t out) +{ + priv_disassembly_begin(ctx); +#ifndef UBJW_DISASSEMBLY_MODE + uint8_t buf[8]; + _to_bigendian64(buf, *(uint64_t*)&out); + priv_ubjw_context_write(ctx, buf, 8); +#else + priv_disassembly_print(ctx, "%lld", out); +#endif + priv_disassembly_end(ctx); +} +void ubjw_write_int64(ubjw_context_t* ctx, int64_t out) +{ + priv_ubjw_tag_public(ctx,UBJ_INT64); + priv_ubjw_write_raw_int64(ctx, out); +} + +void ubjw_write_high_precision(ubjw_context_t* ctx, const char* hp) +{ + priv_ubjw_tag_public(ctx,UBJ_HIGH_PRECISION); + priv_ubjw_write_raw_string(ctx, hp); +} +UBJ_TYPE ubjw_min_integer_type(int64_t in) +{ + uint64_t mc = llabs(in); + if (mc < 0x80) + { + return UBJ_INT8; + } + else if (in > 0 && mc < 0x100) + { + return UBJ_UINT8; + } + else if (mc < 0x8000) + { + return UBJ_INT16; + } + else if (mc < 0x80000000) + { + return UBJ_INT32; + } + else + { + return UBJ_INT64; + } +} + +void ubjw_write_integer(ubjw_context_t* ctx, int64_t out) +{ + switch (ubjw_min_integer_type(out)) + { + case UBJ_INT8: + ubjw_write_int8(ctx, (int8_t)out); + break; + case UBJ_UINT8: + ubjw_write_uint8(ctx, (uint8_t)out); + break; + case UBJ_INT16: + ubjw_write_int16(ctx, (int16_t)out); + break; + case UBJ_INT32: + ubjw_write_int32(ctx, (int32_t)out); + break; + default: + ubjw_write_int64(ctx, out); + break; + }; +} + +static inline void priv_ubjw_write_raw_float32(ubjw_context_t* ctx, float out) +{ + priv_disassembly_begin(ctx); +#ifndef UBJW_DISASSEMBLY_MODE + uint32_t fout = *(uint32_t*)&out; + uint8_t outbuf[4]; + _to_bigendian32(outbuf, fout); + priv_ubjw_context_write(ctx, outbuf, 4); +#else + priv_disassembly_print(ctx, "%g", out); +#endif + priv_disassembly_end(ctx); + +} +void ubjw_write_float32(ubjw_context_t* ctx, float out) +{ + priv_ubjw_tag_public(ctx,UBJ_FLOAT32); + priv_ubjw_write_raw_float32(ctx, out); +} +static inline void priv_ubjw_write_raw_float64(ubjw_context_t* ctx, double out) +{ + priv_disassembly_begin(ctx); +#ifndef UBJW_DISASSEMBLY_MODE + uint64_t fout = *(uint64_t*)&out; + uint8_t outbuf[8]; + _to_bigendian64(outbuf, fout); + priv_ubjw_context_write(ctx, outbuf, 8); +#else + priv_disassembly_print(ctx, "%g", out); +#endif + priv_disassembly_end(ctx); +} +void ubjw_write_float64(ubjw_context_t* ctx, double out) +{ + priv_ubjw_tag_public(ctx,UBJ_FLOAT64); + priv_ubjw_write_raw_float64(ctx, out); +} + +void ubjw_write_floating_point(ubjw_context_t* ctx, double out) +{ + //this may not be possible to implement correctly...for now we just write it as a float64' + ubjw_write_float64(ctx,out); +} + +void ubjw_write_noop(ubjw_context_t* ctx) +{ + priv_ubjw_tag_public(ctx,UBJ_NOOP); +} +void ubjw_write_null(ubjw_context_t* ctx) +{ + priv_ubjw_tag_public(ctx,UBJ_NULLTYPE); +} +void ubjw_write_bool(ubjw_context_t* ctx, uint8_t out) +{ + priv_ubjw_tag_public(ctx,(out ? UBJ_BOOL_TRUE : UBJ_BOOL_FALSE)); +} + +void priv_ubjw_begin_container(struct priv_ubjw_container_t* cnt, ubjw_context_t* ctx, UBJ_TYPE typ, size_t count) +{ + cnt->flags=0; + cnt->elements_remaining = count; + cnt->type = typ; + + if (typ != UBJ_MIXED) + { + if (count == 0) + { + //error and return; + } + priv_disassembly_begin(ctx); + priv_ubjw_context_append(ctx, '$'); + priv_disassembly_end(ctx); + + priv_disassembly_begin(ctx); + priv_ubjw_context_append(ctx, UBJI_TYPEC_convert[typ]); + priv_disassembly_end(ctx); + + cnt->flags |= CONTAINER_IS_TYPED; + } + if (count != 0) + { + priv_disassembly_begin(ctx); + priv_ubjw_context_append(ctx, '#'); + priv_disassembly_end(ctx); + + ctx->ignore_container_flags = 1; + ubjw_write_integer(ctx, (int64_t)count); + ctx->ignore_container_flags = 0; + + cnt->flags |= CONTAINER_IS_SIZED; + cnt->elements_remaining = count; + } +} +void ubjw_begin_array(ubjw_context_t* ctx, UBJ_TYPE type, size_t count) +{ + priv_ubjw_tag_public(ctx, UBJ_ARRAY); //todo: should this happen before any erro potential? + struct priv_ubjw_container_t ch; + priv_ubjw_begin_container(&ch, ctx, type, count); + ch.flags |= CONTAINER_IS_UBJ_ARRAY; + priv_ubjw_container_stack_push(ctx, &ch); +} +void ubjw_begin_ndarray(ubjw_context_t* dst, UBJ_TYPE type, const size_t* dims, uint8_t ndims); +void ubjw_write_ndbuffer(ubjw_context_t* dst, const uint8_t* data, UBJ_TYPE type, const size_t* dims, uint8_t ndims); + +void ubjw_begin_object(ubjw_context_t* ctx, UBJ_TYPE type, size_t count) +{ + priv_ubjw_tag_public(ctx, UBJ_OBJECT); + struct priv_ubjw_container_t ch; + priv_ubjw_begin_container(&ch, ctx, type, count); + ch.flags |= CONTAINER_EXPECTS_KEY | CONTAINER_IS_UBJ_OBJECT; + priv_ubjw_container_stack_push(ctx, &ch); +} +void ubjw_write_key(ubjw_context_t* ctx, const char* key) +{ + if (ctx->head->flags & CONTAINER_EXPECTS_KEY && ctx->head->flags & CONTAINER_IS_UBJ_OBJECT) + { + priv_disassembly_indent(ctx); + priv_ubjw_write_raw_string(ctx, key); + ctx->head->flags ^= CONTAINER_EXPECTS_KEY; //turn off container + } + else + { + //error unexpected key + } +} +void ubjw_end(ubjw_context_t* ctx) +{ + struct priv_ubjw_container_t ch = priv_ubjw_container_stack_pop(ctx); + if ((ch.flags & CONTAINER_IS_UBJ_OBJECT) && !(ch.flags & CONTAINER_EXPECTS_KEY)) + { + //error expected value + } + priv_disassembly_indent(ctx); + priv_ubjw_context_finish_container(ctx, &ch); +} + + +static inline void priv_ubjw_write_byteswap(ubjw_context_t* ctx, const uint8_t* data, int sz, size_t count) +{ + uint8_t buf[BUFFER_OUT_SIZE]; + + size_t i; + size_t nbytes = sz*count; + for (i = 0; i < nbytes; i+=BUFFER_OUT_SIZE) + { + size_t npass = min(nbytes - i, BUFFER_OUT_SIZE); + memcpy(buf, data + i, npass); + buf_endian_swap(buf, sz, npass/sz); + priv_ubjw_context_write(ctx, buf, npass); + } +} +void ubjw_write_buffer(ubjw_context_t* ctx, const uint8_t* data, UBJ_TYPE type, size_t count) +{ + int typesz = UBJI_TYPE_size[type]; + if (typesz < 0) + { + //error cannot write an STC buffer of this type. + } + ubjw_begin_array(ctx, type, count); + if (type == UBJ_STRING || type == UBJ_HIGH_PRECISION) + { + const char** databufs = (const char**)data; + size_t i; + for (i = 0; i < count; i++) + { + priv_ubjw_write_raw_string(ctx, databufs[i]); + } + } +#ifndef UBJW_DISASSEMBLY_MODE + else if (typesz == 1 || _is_bigendian()) + { + size_t n = typesz*count; + priv_ubjw_context_write(ctx, data, typesz*count); + } + else if (typesz > 1) //and not big-endian + { + priv_ubjw_write_byteswap(ctx, data,typesz,count); + } +#else + else + { + size_t i; + for (i = 0; i < count; i++) + { + ubjr_dynamic_t dyn = priv_ubjr_pointer_to_dynamic(type, data + i*typesz); + ubjrw_write_dynamic(ctx, dyn, 0); + } + } +#endif + ubjw_end(ctx); +} -- cgit v1.2.3