From cb723840d9ad24bb1db6d4dc800f7e5c730aba5f Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Tue, 10 Apr 2018 17:43:27 +0200 Subject: [PATCH] Added `deserializeMsgPack()` (issue #358) --- .travis.yml | 6 - CHANGELOG.md | 1 + src/ArduinoJson.hpp | 6 +- src/ArduinoJson/Configuration.hpp | 10 + src/ArduinoJson/Data/ValueSaver.hpp | 2 +- .../{ => Json}/Deserialization/Comments.hpp | 0 .../{ => Json}/Deserialization/JsonParser.hpp | 10 +- .../Deserialization/JsonParserImpl.hpp | 2 +- src/ArduinoJson/{Data => Json}/Encoding.hpp | 0 .../{ => Json}/Serialization/FloatParts.hpp | 10 +- .../Serialization/IndentedPrint.hpp | 0 .../Serialization/JsonSerializer.hpp | 14 +- .../Serialization/JsonSerializerImpl.hpp | 10 +- .../{ => Json}/Serialization/JsonWriter.hpp | 8 +- .../{ => Json}/Serialization/Prettyfier.hpp | 0 src/ArduinoJson/JsonArray.hpp | 2 +- src/ArduinoJson/JsonError.hpp | 17 +- src/ArduinoJson/JsonObject.hpp | 2 +- src/ArduinoJson/JsonVariantComparisons.hpp | 6 +- src/ArduinoJson/JsonVariantImpl.hpp | 8 +- src/ArduinoJson/JsonVariantSubscripts.hpp | 6 +- .../MsgPack/MsgPackDeserializer.hpp | 328 ++++++++++++++++++ src/ArduinoJson/MsgPack/MsgPackError.hpp | 67 ++++ src/ArduinoJson/MsgPack/endianess.hpp | 47 +++ src/ArduinoJson/MsgPack/ieee754.hpp | 18 + .../{Serialization => Print}/DummyPrint.hpp | 0 .../DynamicStringBuilder.hpp | 6 +- .../StaticStringBuilder.hpp | 0 .../StreamPrintAdapter.hpp | 0 .../ArduinoStream.hpp | 0 .../{StringTraits => Strings}/CharPointer.hpp | 0 .../{StringTraits => Strings}/FlashString.hpp | 0 .../{StringTraits => Strings}/StdStream.hpp | 0 .../{StringTraits => Strings}/StdString.hpp | 0 .../StringTraits.hpp | 0 .../StringWriter.hpp | 0 .../{Polyfills => Text}/isFloat.hpp | 6 +- .../{Polyfills => Text}/isInteger.hpp | 6 +- .../{Polyfills => Text}/parseFloat.hpp | 8 +- .../{Polyfills => Text}/parseInteger.hpp | 6 +- src/ArduinoJson/deserializeJson.hpp | 2 +- src/ArduinoJson/deserializeMsgPack.hpp | 49 +++ test/CMakeLists.txt | 1 + test/JsonWriter/writeFloat.cpp | 4 +- test/JsonWriter/writeString.cpp | 4 +- test/Misc/FloatParts.cpp | 2 +- test/MsgPack/CMakeLists.txt | 16 + test/MsgPack/MsgPackError.cpp | 40 +++ test/MsgPack/deserializationErrors.cpp | 58 ++++ test/MsgPack/deserializeArray.cpp | 85 +++++ test/MsgPack/deserializeObject.cpp | 86 +++++ test/MsgPack/deserializeStaticVariant.cpp | 131 +++++++ test/MsgPack/deserializeVariant.cpp | 277 +++++++++++++++ test/MsgPack/doubleToFloat.cpp | 25 ++ test/Polyfills/isFloat.cpp | 2 +- test/Polyfills/isInteger.cpp | 2 +- test/Polyfills/parseFloat.cpp | 2 +- test/Polyfills/parseInteger.cpp | 2 +- 58 files changed, 1311 insertions(+), 89 deletions(-) rename src/ArduinoJson/{ => Json}/Deserialization/Comments.hpp (100%) rename src/ArduinoJson/{ => Json}/Deserialization/JsonParser.hpp (94%) rename src/ArduinoJson/{ => Json}/Deserialization/JsonParserImpl.hpp (99%) rename src/ArduinoJson/{Data => Json}/Encoding.hpp (100%) rename src/ArduinoJson/{ => Json}/Serialization/FloatParts.hpp (92%) rename src/ArduinoJson/{ => Json}/Serialization/IndentedPrint.hpp (100%) rename src/ArduinoJson/{ => Json}/Serialization/JsonSerializer.hpp (95%) rename src/ArduinoJson/{ => Json}/Serialization/JsonSerializerImpl.hpp (90%) rename src/ArduinoJson/{ => Json}/Serialization/JsonWriter.hpp (95%) rename src/ArduinoJson/{ => Json}/Serialization/Prettyfier.hpp (100%) create mode 100644 src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp create mode 100644 src/ArduinoJson/MsgPack/MsgPackError.hpp create mode 100644 src/ArduinoJson/MsgPack/endianess.hpp create mode 100644 src/ArduinoJson/MsgPack/ieee754.hpp rename src/ArduinoJson/{Serialization => Print}/DummyPrint.hpp (100%) rename src/ArduinoJson/{Serialization => Print}/DynamicStringBuilder.hpp (87%) rename src/ArduinoJson/{Serialization => Print}/StaticStringBuilder.hpp (100%) rename src/ArduinoJson/{Serialization => Print}/StreamPrintAdapter.hpp (100%) rename src/ArduinoJson/{StringTraits => Strings}/ArduinoStream.hpp (100%) rename src/ArduinoJson/{StringTraits => Strings}/CharPointer.hpp (100%) rename src/ArduinoJson/{StringTraits => Strings}/FlashString.hpp (100%) rename src/ArduinoJson/{StringTraits => Strings}/StdStream.hpp (100%) rename src/ArduinoJson/{StringTraits => Strings}/StdString.hpp (100%) rename src/ArduinoJson/{StringTraits => Strings}/StringTraits.hpp (100%) rename src/ArduinoJson/{Deserialization => Strings}/StringWriter.hpp (100%) rename src/ArduinoJson/{Polyfills => Text}/isFloat.hpp (87%) rename src/ArduinoJson/{Polyfills => Text}/isInteger.hpp (76%) rename src/ArduinoJson/{Polyfills => Text}/parseFloat.hpp (94%) rename src/ArduinoJson/{Polyfills => Text}/parseInteger.hpp (87%) create mode 100644 src/ArduinoJson/deserializeMsgPack.hpp create mode 100644 test/MsgPack/CMakeLists.txt create mode 100644 test/MsgPack/MsgPackError.cpp create mode 100644 test/MsgPack/deserializationErrors.cpp create mode 100644 test/MsgPack/deserializeArray.cpp create mode 100644 test/MsgPack/deserializeObject.cpp create mode 100644 test/MsgPack/deserializeStaticVariant.cpp create mode 100644 test/MsgPack/deserializeVariant.cpp create mode 100644 test/MsgPack/doubleToFloat.cpp diff --git a/.travis.yml b/.travis.yml index 9a9dddcd..9605e80c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,12 +2,6 @@ sudo: false language: cpp matrix: include: - - compiler: gcc - addons: - apt: - sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-4.4'] - env: SCRIPT=cmake GCC=4.4 - compiler: gcc addons: apt: diff --git a/CHANGELOG.md b/CHANGELOG.md index cf06e942..0783de86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ HEAD * Added `deserializeJson()` * Added `serializeJson()` and `serializeJsonPretty()` * Added `measureJson()` and `measureJsonPretty()` +* Added `deserializeMsgPack()` (issue #358) * Removed `JsonBuffer::parseArray()`, `parseObject()` and `parse()` * Removed `JsonBuffer::createArray()` and `createObject()` * Removed `printTo()` and `prettyPrintTo()` diff --git a/src/ArduinoJson.hpp b/src/ArduinoJson.hpp index c06d0116..2830ce26 100644 --- a/src/ArduinoJson.hpp +++ b/src/ArduinoJson.hpp @@ -7,13 +7,15 @@ #include "ArduinoJson/DynamicJsonArray.hpp" #include "ArduinoJson/DynamicJsonObject.hpp" #include "ArduinoJson/DynamicJsonVariant.hpp" +#include "ArduinoJson/MsgPack/MsgPackDeserializer.hpp" #include "ArduinoJson/StaticJsonArray.hpp" #include "ArduinoJson/StaticJsonObject.hpp" #include "ArduinoJson/StaticJsonVariant.hpp" #include "ArduinoJson/deserializeJson.hpp" +#include "ArduinoJson/deserializeMsgPack.hpp" -#include "ArduinoJson/Deserialization/JsonParserImpl.hpp" +#include "ArduinoJson/Json/Deserialization/JsonParserImpl.hpp" +#include "ArduinoJson/Json/Serialization/JsonSerializerImpl.hpp" #include "ArduinoJson/JsonArrayImpl.hpp" #include "ArduinoJson/JsonObjectImpl.hpp" #include "ArduinoJson/JsonVariantImpl.hpp" -#include "ArduinoJson/Serialization/JsonSerializerImpl.hpp" diff --git a/src/ArduinoJson/Configuration.hpp b/src/ArduinoJson/Configuration.hpp index 2d0d1476..af4ea0b5 100644 --- a/src/ArduinoJson/Configuration.hpp +++ b/src/ArduinoJson/Configuration.hpp @@ -144,3 +144,13 @@ #if ARDUINOJSON_USE_LONG_LONG && ARDUINOJSON_USE_INT64 #error ARDUINOJSON_USE_LONG_LONG and ARDUINOJSON_USE_INT64 cannot be set together #endif + +#ifndef ARDUINOJSON_LITTLE_ENDIAN +#if defined(_MSC_VER) || \ + (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \ + (defined(__LITTLE_ENDIAN__)) +#define ARDUINOJSON_LITTLE_ENDIAN 1 +#else +#define ARDUINOJSON_LITTLE_ENDIAN 0 +#endif +#endif diff --git a/src/ArduinoJson/Data/ValueSaver.hpp b/src/ArduinoJson/Data/ValueSaver.hpp index 4e1904d6..4416ebc9 100644 --- a/src/ArduinoJson/Data/ValueSaver.hpp +++ b/src/ArduinoJson/Data/ValueSaver.hpp @@ -6,7 +6,7 @@ #include "../JsonVariant.hpp" #include "../Memory/JsonBuffer.hpp" -#include "../StringTraits/StringTraits.hpp" +#include "../Strings/StringTraits.hpp" #include "../TypeTraits/EnableIf.hpp" namespace ArduinoJson { diff --git a/src/ArduinoJson/Deserialization/Comments.hpp b/src/ArduinoJson/Json/Deserialization/Comments.hpp similarity index 100% rename from src/ArduinoJson/Deserialization/Comments.hpp rename to src/ArduinoJson/Json/Deserialization/Comments.hpp diff --git a/src/ArduinoJson/Deserialization/JsonParser.hpp b/src/ArduinoJson/Json/Deserialization/JsonParser.hpp similarity index 94% rename from src/ArduinoJson/Deserialization/JsonParser.hpp rename to src/ArduinoJson/Json/Deserialization/JsonParser.hpp index d4a9c8f4..c1a34a70 100644 --- a/src/ArduinoJson/Deserialization/JsonParser.hpp +++ b/src/ArduinoJson/Json/Deserialization/JsonParser.hpp @@ -4,11 +4,11 @@ #pragma once -#include "../JsonError.hpp" -#include "../JsonVariant.hpp" -#include "../Memory/JsonBuffer.hpp" -#include "../TypeTraits/IsConst.hpp" -#include "StringWriter.hpp" +#include "../../JsonError.hpp" +#include "../../JsonVariant.hpp" +#include "../../Memory/JsonBuffer.hpp" +#include "../../Strings/StringWriter.hpp" +#include "../../TypeTraits/IsConst.hpp" namespace ArduinoJson { namespace Internals { diff --git a/src/ArduinoJson/Deserialization/JsonParserImpl.hpp b/src/ArduinoJson/Json/Deserialization/JsonParserImpl.hpp similarity index 99% rename from src/ArduinoJson/Deserialization/JsonParserImpl.hpp rename to src/ArduinoJson/Json/Deserialization/JsonParserImpl.hpp index e9cbd2cf..dd43c0ca 100644 --- a/src/ArduinoJson/Deserialization/JsonParserImpl.hpp +++ b/src/ArduinoJson/Json/Deserialization/JsonParserImpl.hpp @@ -4,7 +4,7 @@ #pragma once -#include "../Data/Encoding.hpp" +#include "../Encoding.hpp" #include "Comments.hpp" #include "JsonParser.hpp" diff --git a/src/ArduinoJson/Data/Encoding.hpp b/src/ArduinoJson/Json/Encoding.hpp similarity index 100% rename from src/ArduinoJson/Data/Encoding.hpp rename to src/ArduinoJson/Json/Encoding.hpp diff --git a/src/ArduinoJson/Serialization/FloatParts.hpp b/src/ArduinoJson/Json/Serialization/FloatParts.hpp similarity index 92% rename from src/ArduinoJson/Serialization/FloatParts.hpp rename to src/ArduinoJson/Json/Serialization/FloatParts.hpp index c14e3b55..4f42a9c2 100644 --- a/src/ArduinoJson/Serialization/FloatParts.hpp +++ b/src/ArduinoJson/Json/Serialization/FloatParts.hpp @@ -4,9 +4,9 @@ #pragma once -#include "../Configuration.hpp" -#include "../Polyfills/math.hpp" -#include "../TypeTraits/FloatTraits.hpp" +#include "../../Configuration.hpp" +#include "../../Polyfills/math.hpp" +#include "../../TypeTraits/FloatTraits.hpp" namespace ArduinoJson { namespace Internals { @@ -85,5 +85,5 @@ struct FloatParts { return powersOf10; } }; -} -} +} // namespace Internals +} // namespace ArduinoJson diff --git a/src/ArduinoJson/Serialization/IndentedPrint.hpp b/src/ArduinoJson/Json/Serialization/IndentedPrint.hpp similarity index 100% rename from src/ArduinoJson/Serialization/IndentedPrint.hpp rename to src/ArduinoJson/Json/Serialization/IndentedPrint.hpp diff --git a/src/ArduinoJson/Serialization/JsonSerializer.hpp b/src/ArduinoJson/Json/Serialization/JsonSerializer.hpp similarity index 95% rename from src/ArduinoJson/Serialization/JsonSerializer.hpp rename to src/ArduinoJson/Json/Serialization/JsonSerializer.hpp index 1bf8810d..a8c6e328 100644 --- a/src/ArduinoJson/Serialization/JsonSerializer.hpp +++ b/src/ArduinoJson/Json/Serialization/JsonSerializer.hpp @@ -4,15 +4,15 @@ #pragma once -#include "DummyPrint.hpp" -#include "DynamicStringBuilder.hpp" -#include "IndentedPrint.hpp" -#include "JsonWriter.hpp" -#include "Prettyfier.hpp" -#include "StaticStringBuilder.hpp" +#include "../../Print/DummyPrint.hpp" +#include "../../Print/DynamicStringBuilder.hpp" +#include "../../Print/StaticStringBuilder.hpp" +#include "./IndentedPrint.hpp" +#include "./JsonWriter.hpp" +#include "./Prettyfier.hpp" #if ARDUINOJSON_ENABLE_STD_STREAM -#include "StreamPrintAdapter.hpp" +#include "../../Print/StreamPrintAdapter.hpp" #endif namespace ArduinoJson { diff --git a/src/ArduinoJson/Serialization/JsonSerializerImpl.hpp b/src/ArduinoJson/Json/Serialization/JsonSerializerImpl.hpp similarity index 90% rename from src/ArduinoJson/Serialization/JsonSerializerImpl.hpp rename to src/ArduinoJson/Json/Serialization/JsonSerializerImpl.hpp index da24cd93..2060cae5 100644 --- a/src/ArduinoJson/Serialization/JsonSerializerImpl.hpp +++ b/src/ArduinoJson/Json/Serialization/JsonSerializerImpl.hpp @@ -4,11 +4,11 @@ #pragma once -#include "../JsonArray.hpp" -#include "../JsonArraySubscript.hpp" -#include "../JsonObject.hpp" -#include "../JsonObjectSubscript.hpp" -#include "../JsonVariant.hpp" +#include "../../JsonArray.hpp" +#include "../../JsonArraySubscript.hpp" +#include "../../JsonObject.hpp" +#include "../../JsonObjectSubscript.hpp" +#include "../../JsonVariant.hpp" #include "JsonSerializer.hpp" template diff --git a/src/ArduinoJson/Serialization/JsonWriter.hpp b/src/ArduinoJson/Json/Serialization/JsonWriter.hpp similarity index 95% rename from src/ArduinoJson/Serialization/JsonWriter.hpp rename to src/ArduinoJson/Json/Serialization/JsonWriter.hpp index d5682863..f613f7e5 100644 --- a/src/ArduinoJson/Serialization/JsonWriter.hpp +++ b/src/ArduinoJson/Json/Serialization/JsonWriter.hpp @@ -5,10 +5,10 @@ #pragma once #include -#include "../Data/Encoding.hpp" -#include "../Data/JsonInteger.hpp" -#include "../Polyfills/attributes.hpp" -#include "../Serialization/FloatParts.hpp" +#include "../../Data/JsonInteger.hpp" +#include "../../Polyfills/attributes.hpp" +#include "../Encoding.hpp" +#include "./FloatParts.hpp" namespace ArduinoJson { namespace Internals { diff --git a/src/ArduinoJson/Serialization/Prettyfier.hpp b/src/ArduinoJson/Json/Serialization/Prettyfier.hpp similarity index 100% rename from src/ArduinoJson/Serialization/Prettyfier.hpp rename to src/ArduinoJson/Json/Serialization/Prettyfier.hpp diff --git a/src/ArduinoJson/JsonArray.hpp b/src/ArduinoJson/JsonArray.hpp index 2a77cf81..51757f20 100644 --- a/src/ArduinoJson/JsonArray.hpp +++ b/src/ArduinoJson/JsonArray.hpp @@ -9,7 +9,7 @@ #include "Data/ValueSaver.hpp" #include "JsonVariant.hpp" #include "Memory/JsonBufferAllocated.hpp" -#include "StringTraits/StringTraits.hpp" +#include "Strings/StringTraits.hpp" #include "TypeTraits/EnableIf.hpp" #include "TypeTraits/IsArray.hpp" #include "TypeTraits/IsFloatingPoint.hpp" diff --git a/src/ArduinoJson/JsonError.hpp b/src/ArduinoJson/JsonError.hpp index 6349a98b..d7f4c903 100644 --- a/src/ArduinoJson/JsonError.hpp +++ b/src/ArduinoJson/JsonError.hpp @@ -42,15 +42,7 @@ class JsonError { } const char* c_str() const { - return to_string(_code); - } - - friend const char* to_string(const JsonError err) { - return to_string(err._code); - } - - friend const char* to_string(JsonError::Code code) { - switch (code) { + switch (_code) { case Ok: return "Ok"; case OpeningBraceExpected: @@ -78,12 +70,7 @@ class JsonError { #if ARDUINOJSON_ENABLE_STD_STREAM inline std::ostream& operator<<(std::ostream& s, const JsonError& e) { - s << to_string(e); - return s; -} - -inline std::ostream& operator<<(std::ostream& s, JsonError::Code e) { - s << to_string(e); + s << e.c_str(); return s; } #endif diff --git a/src/ArduinoJson/JsonObject.hpp b/src/ArduinoJson/JsonObject.hpp index 8c432c44..43e39ec7 100644 --- a/src/ArduinoJson/JsonObject.hpp +++ b/src/ArduinoJson/JsonObject.hpp @@ -9,7 +9,7 @@ #include "Data/ValueSaver.hpp" #include "JsonPair.hpp" #include "Memory/JsonBufferAllocated.hpp" -#include "StringTraits/StringTraits.hpp" +#include "Strings/StringTraits.hpp" #include "TypeTraits/EnableIf.hpp" #include "TypeTraits/IsArray.hpp" #include "TypeTraits/IsFloatingPoint.hpp" diff --git a/src/ArduinoJson/JsonVariantComparisons.hpp b/src/ArduinoJson/JsonVariantComparisons.hpp index cae53372..dbd84d97 100644 --- a/src/ArduinoJson/JsonVariantComparisons.hpp +++ b/src/ArduinoJson/JsonVariantComparisons.hpp @@ -4,7 +4,7 @@ #pragma once -#include "StringTraits/StringTraits.hpp" +#include "Strings/StringTraits.hpp" #include "TypeTraits/EnableIf.hpp" #include "TypeTraits/IsVariant.hpp" @@ -134,5 +134,5 @@ class JsonVariantComparisons { return false; } }; -} -} +} // namespace Internals +} // namespace ArduinoJson diff --git a/src/ArduinoJson/JsonVariantImpl.hpp b/src/ArduinoJson/JsonVariantImpl.hpp index 93f70f09..138827cb 100644 --- a/src/ArduinoJson/JsonVariantImpl.hpp +++ b/src/ArduinoJson/JsonVariantImpl.hpp @@ -8,10 +8,10 @@ #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 "Text/isFloat.hpp" +#include "Text/isInteger.hpp" +#include "Text/parseFloat.hpp" +#include "Text/parseInteger.hpp" #include // for strcmp diff --git a/src/ArduinoJson/JsonVariantSubscripts.hpp b/src/ArduinoJson/JsonVariantSubscripts.hpp index 279ee019..ec28c686 100644 --- a/src/ArduinoJson/JsonVariantSubscripts.hpp +++ b/src/ArduinoJson/JsonVariantSubscripts.hpp @@ -6,7 +6,7 @@ #include "Data/JsonVariantAs.hpp" #include "Polyfills/attributes.hpp" -#include "StringTraits/StringTraits.hpp" +#include "Strings/StringTraits.hpp" #include "TypeTraits/EnableIf.hpp" namespace ArduinoJson { @@ -82,5 +82,5 @@ class JsonVariantSubscripts { return static_cast(this); } }; -} -} +} // namespace Internals +} // namespace ArduinoJson diff --git a/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp b/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp new file mode 100644 index 00000000..0cf05c56 --- /dev/null +++ b/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp @@ -0,0 +1,328 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "../JsonVariant.hpp" +#include "../Memory/JsonBuffer.hpp" +#include "../Strings/StringWriter.hpp" +#include "../TypeTraits/IsConst.hpp" +#include "./MsgPackError.hpp" +#include "./endianess.hpp" +#include "./ieee754.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 MsgPackDeserializer { + public: + MsgPackDeserializer(JsonBuffer *buffer, TReader reader, TWriter writer, + uint8_t nestingLimit) + : _buffer(buffer), + _reader(reader), + _writer(writer), + _nestingLimit(nestingLimit) {} + + MsgPackError parse(JsonArray &array) { + uint8_t c = readOne(); + size_t n; + + if ((c & 0xF0) == 0x90) { + n = c & 0x0F; + } else if (c == 0xdc) { + n = readInteger(); + } else if (c == 0xdd) { + n = readInteger(); + } else { + return MsgPackError::NotAnArray; + } + + return readArray(array, n); + } + + MsgPackError parse(JsonObject &object) { + uint8_t c = readOne(); + size_t n; + + if ((c & 0xf0) == 0x80) { + n = c & 0x0f; + } else if (c == 0xde) { + n = readInteger(); + } else if (c == 0xdf) { + n = readInteger(); + } else { + return MsgPackError::NotAnObject; + } + + return readObject(object, n); + } + + MsgPackError parse(JsonVariant &variant) { + uint8_t c = readOne(); + + if ((c & 0x80) == 0) { + variant = c; + return MsgPackError::Ok; + } + + if ((c & 0xe0) == 0xe0) { + variant = static_cast(c); + return MsgPackError::Ok; + } + + if ((c & 0xe0) == 0xa0) { + return readString(variant, c & 0x1f); + } + + if ((c & 0xf0) == 0x90) return readArray(variant, c & 0x0F); + + if ((c & 0xf0) == 0x80) return readObject(variant, c & 0x0F); + + switch (c) { + case 0xc0: + variant = static_cast(0); + return MsgPackError::Ok; + + case 0xc2: + variant = false; + return MsgPackError::Ok; + + case 0xc3: + variant = true; + return MsgPackError::Ok; + + case 0xcc: + variant = readInteger(); + return MsgPackError::Ok; + + case 0xcd: + variant = readInteger(); + return MsgPackError::Ok; + + case 0xce: + variant = readInteger(); + return MsgPackError::Ok; + + case 0xcf: +#if ARDUINOJSON_USE_LONG_LONG || ARDUINOJSON_USE_INT64 + variant = readInteger(); +#else + readInteger(); + variant = readInteger(); +#endif + return MsgPackError::Ok; + + case 0xd0: + variant = readInteger(); + return MsgPackError::Ok; + + case 0xd1: + variant = readInteger(); + return MsgPackError::Ok; + + case 0xd2: + variant = readInteger(); + return MsgPackError::Ok; + + case 0xd3: +#if ARDUINOJSON_USE_LONG_LONG || ARDUINOJSON_USE_INT64 + variant = readInteger(); +#else + readInteger(); + variant = readInteger(); +#endif + return MsgPackError::Ok; + + case 0xca: + variant = readFloat(); + return MsgPackError::Ok; + + case 0xcb: + variant = readDouble(); + return MsgPackError::Ok; + + case 0xd9: { + uint8_t n = readInteger(); + return readString(variant, n); + } + + case 0xda: { + uint16_t n = readInteger(); + return readString(variant, n); + } + + case 0xdb: { + uint32_t n = readInteger(); + return readString(variant, n); + } + + case 0xdc: + return readArray(variant, readInteger()); + + case 0xdd: + return readArray(variant, readInteger()); + + case 0xde: + return readObject(variant, readInteger()); + + case 0xdf: + return readObject(variant, readInteger()); + + default: + return MsgPackError::NotSupported; + } + } + + private: + // Prevent VS warning "assignment operator could not be generated" + MsgPackDeserializer &operator=(const MsgPackDeserializer &); + + uint8_t readOne() { + char c = _reader.current(); + _reader.move(); + return static_cast(c); + } + + void read(uint8_t *p, size_t n) { + for (size_t i = 0; i < n; i++) p[i] = readOne(); + } + + template + void read(T &value) { + read(reinterpret_cast(&value), sizeof(value)); + } + + template + T readInteger() { + T value; + read(value); + fixEndianess(value); + return value; + } + + template + typename EnableIf::type readFloat() { + T value; + read(value); + fixEndianess(value); + return value; + } + + template + typename EnableIf::type readDouble() { + T value; + read(value); + fixEndianess(value); + return value; + } + + template + typename EnableIf::type readDouble() { + uint8_t i[8]; // input is 8 bytes + T value; // output is 4 bytes + uint8_t *o = reinterpret_cast(&value); + read(i, 8); + doubleToFloat(i, o); + fixEndianess(value); + return value; + } + + MsgPackError readString(JsonVariant &variant, size_t n) { + typename RemoveReference::type::String str = _writer.startString(); + for (; n; --n) str.append(static_cast(readOne())); + const char *s = str.c_str(); + if (s == NULL) return MsgPackError::NoMemory; + variant = s; + return MsgPackError::Ok; + } + + MsgPackError readArray(JsonVariant &variant, size_t n) { + JsonArray *array = new (_buffer) JsonArray(_buffer); + if (!array) return MsgPackError::NoMemory; + variant = array; + return readArray(*array, n); + } + + MsgPackError readArray(JsonArray &array, size_t n) { + if (_nestingLimit == 0) return MsgPackError::TooDeep; + --_nestingLimit; + for (; n; --n) { + JsonVariant variant; + MsgPackError err = parse(variant); + if (err) return err; + if (!array.add(variant)) return MsgPackError::NoMemory; + } + ++_nestingLimit; + return MsgPackError::Ok; + } + + MsgPackError readObject(JsonVariant &variant, size_t n) { + JsonObject *object = new (_buffer) JsonObject(_buffer); + if (!object) return MsgPackError::NoMemory; + variant = object; + return readObject(*object, n); + } + + MsgPackError readObject(JsonObject &object, size_t n) { + if (_nestingLimit == 0) return MsgPackError::TooDeep; + --_nestingLimit; + for (; n; --n) { + MsgPackError err; + JsonVariant variant; + err = parse(variant); + if (err) return err; + const char *key = variant.as(); + if (!key) return MsgPackError::NotSupported; + err = parse(variant); + if (err) return err; + if (!object.set(key, variant)) return MsgPackError::NoMemory; + } + ++_nestingLimit; + return MsgPackError::Ok; + } + + JsonBuffer *_buffer; + TReader _reader; + TWriter _writer; + uint8_t _nestingLimit; +}; + +template +struct MsgPackDeserializerBuilder { + typedef typename StringTraits::Reader InputReader; + typedef MsgPackDeserializer TParser; + + static TParser makeMsgPackDeserializer(TJsonBuffer *buffer, TString &json, + uint8_t nestingLimit) { + return TParser(buffer, InputReader(json), *buffer, nestingLimit); + } +}; + +template +struct MsgPackDeserializerBuilder< + TJsonBuffer, TChar *, typename EnableIf::value>::type> { + typedef typename StringTraits::Reader TReader; + typedef StringWriter TWriter; + typedef MsgPackDeserializer TParser; + + static TParser makeMsgPackDeserializer(TJsonBuffer *buffer, TChar *json, + uint8_t nestingLimit) { + return TParser(buffer, TReader(json), TWriter(json), nestingLimit); + } +}; + +template +inline typename MsgPackDeserializerBuilder::TParser +makeMsgPackDeserializer(TJsonBuffer *buffer, TString &json, + uint8_t nestingLimit) { + return MsgPackDeserializerBuilder< + TJsonBuffer, TString>::makeMsgPackDeserializer(buffer, json, + nestingLimit); +} +} // namespace Internals +} // namespace ArduinoJson diff --git a/src/ArduinoJson/MsgPack/MsgPackError.hpp b/src/ArduinoJson/MsgPack/MsgPackError.hpp new file mode 100644 index 00000000..0162aac6 --- /dev/null +++ b/src/ArduinoJson/MsgPack/MsgPackError.hpp @@ -0,0 +1,67 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ArduinoJson { + +class MsgPackError { + public: + enum Code { Ok, NotSupported, NoMemory, NotAnArray, NotAnObject, TooDeep }; + + MsgPackError() {} + + MsgPackError(Code code) : _code(code) {} + + operator bool() const { + return _code != Ok; + } + + friend bool operator==(const MsgPackError& err, Code code) { + return err._code == code; + } + + friend bool operator==(Code code, const MsgPackError& err) { + return err._code == code; + } + + friend bool operator!=(const MsgPackError& err, Code code) { + return err._code != code; + } + + friend bool operator!=(Code code, const MsgPackError& err) { + return err._code != code; + } + + const char* c_str() const { + switch (_code) { + case Ok: + return "Ok"; + case NotSupported: + return "NotSupported"; + case NoMemory: + return "NoMemory"; + case NotAnArray: + return "NotAnArray"; + case NotAnObject: + return "NotAnObject"; + case TooDeep: + return "TooDeep"; + default: + return "???"; + } + } + + private: + Code _code; +}; + +#if ARDUINOJSON_ENABLE_STD_STREAM +inline std::ostream& operator<<(std::ostream& os, const MsgPackError& err) { + os << err.c_str(); + return os; +} +#endif + +} // namespace ArduinoJson diff --git a/src/ArduinoJson/MsgPack/endianess.hpp b/src/ArduinoJson/MsgPack/endianess.hpp new file mode 100644 index 00000000..26f2b906 --- /dev/null +++ b/src/ArduinoJson/MsgPack/endianess.hpp @@ -0,0 +1,47 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ArduinoJson { +namespace Internals { + +template +struct integral_constant {}; + +template +inline void swap(T& a, T& b) { + T t(a); + a = b; + b = t; +} + +inline void fixEndianess(uint8_t* p, integral_constant) { + swap(p[0], p[7]); + swap(p[1], p[6]); + swap(p[2], p[5]); + swap(p[3], p[4]); +} + +inline void fixEndianess(uint8_t* p, integral_constant) { + swap(p[0], p[3]); + swap(p[1], p[2]); +} + +inline void fixEndianess(uint8_t* p, integral_constant) { + swap(p[0], p[1]); +} + +inline void fixEndianess(uint8_t*, integral_constant) {} + +template +inline void fixEndianess(T& value) { +#if ARDUINOJSON_LITTLE_ENDIAN + fixEndianess(reinterpret_cast(&value), + integral_constant()); +#endif +} + +} // namespace Internals +} // namespace ArduinoJson diff --git a/src/ArduinoJson/MsgPack/ieee754.hpp b/src/ArduinoJson/MsgPack/ieee754.hpp new file mode 100644 index 00000000..19e7f285 --- /dev/null +++ b/src/ArduinoJson/MsgPack/ieee754.hpp @@ -0,0 +1,18 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ArduinoJson { +namespace Internals { + +inline void doubleToFloat(const uint8_t d[8], uint8_t f[4]) { + f[0] = uint8_t((d[0] & 0xC0) | (d[0] << 3 & 0x3f) | (d[1] >> 5)); + f[1] = uint8_t((d[1] << 3) | (d[2] >> 5)); + f[2] = uint8_t((d[2] << 3) | (d[3] >> 5)); + f[3] = uint8_t((d[3] << 3) | (d[4] >> 5)); +} + +} // namespace Internals +} // namespace ArduinoJson diff --git a/src/ArduinoJson/Serialization/DummyPrint.hpp b/src/ArduinoJson/Print/DummyPrint.hpp similarity index 100% rename from src/ArduinoJson/Serialization/DummyPrint.hpp rename to src/ArduinoJson/Print/DummyPrint.hpp diff --git a/src/ArduinoJson/Serialization/DynamicStringBuilder.hpp b/src/ArduinoJson/Print/DynamicStringBuilder.hpp similarity index 87% rename from src/ArduinoJson/Serialization/DynamicStringBuilder.hpp rename to src/ArduinoJson/Print/DynamicStringBuilder.hpp index 41be6392..931066ae 100644 --- a/src/ArduinoJson/Serialization/DynamicStringBuilder.hpp +++ b/src/ArduinoJson/Print/DynamicStringBuilder.hpp @@ -4,7 +4,7 @@ #pragma once -#include "../StringTraits/StringTraits.hpp" +#include "../Strings/StringTraits.hpp" namespace ArduinoJson { namespace Internals { @@ -31,5 +31,5 @@ class DynamicStringBuilder { TString &_str; }; -} -} +} // namespace Internals +} // namespace ArduinoJson diff --git a/src/ArduinoJson/Serialization/StaticStringBuilder.hpp b/src/ArduinoJson/Print/StaticStringBuilder.hpp similarity index 100% rename from src/ArduinoJson/Serialization/StaticStringBuilder.hpp rename to src/ArduinoJson/Print/StaticStringBuilder.hpp diff --git a/src/ArduinoJson/Serialization/StreamPrintAdapter.hpp b/src/ArduinoJson/Print/StreamPrintAdapter.hpp similarity index 100% rename from src/ArduinoJson/Serialization/StreamPrintAdapter.hpp rename to src/ArduinoJson/Print/StreamPrintAdapter.hpp diff --git a/src/ArduinoJson/StringTraits/ArduinoStream.hpp b/src/ArduinoJson/Strings/ArduinoStream.hpp similarity index 100% rename from src/ArduinoJson/StringTraits/ArduinoStream.hpp rename to src/ArduinoJson/Strings/ArduinoStream.hpp diff --git a/src/ArduinoJson/StringTraits/CharPointer.hpp b/src/ArduinoJson/Strings/CharPointer.hpp similarity index 100% rename from src/ArduinoJson/StringTraits/CharPointer.hpp rename to src/ArduinoJson/Strings/CharPointer.hpp diff --git a/src/ArduinoJson/StringTraits/FlashString.hpp b/src/ArduinoJson/Strings/FlashString.hpp similarity index 100% rename from src/ArduinoJson/StringTraits/FlashString.hpp rename to src/ArduinoJson/Strings/FlashString.hpp diff --git a/src/ArduinoJson/StringTraits/StdStream.hpp b/src/ArduinoJson/Strings/StdStream.hpp similarity index 100% rename from src/ArduinoJson/StringTraits/StdStream.hpp rename to src/ArduinoJson/Strings/StdStream.hpp diff --git a/src/ArduinoJson/StringTraits/StdString.hpp b/src/ArduinoJson/Strings/StdString.hpp similarity index 100% rename from src/ArduinoJson/StringTraits/StdString.hpp rename to src/ArduinoJson/Strings/StdString.hpp diff --git a/src/ArduinoJson/StringTraits/StringTraits.hpp b/src/ArduinoJson/Strings/StringTraits.hpp similarity index 100% rename from src/ArduinoJson/StringTraits/StringTraits.hpp rename to src/ArduinoJson/Strings/StringTraits.hpp diff --git a/src/ArduinoJson/Deserialization/StringWriter.hpp b/src/ArduinoJson/Strings/StringWriter.hpp similarity index 100% rename from src/ArduinoJson/Deserialization/StringWriter.hpp rename to src/ArduinoJson/Strings/StringWriter.hpp diff --git a/src/ArduinoJson/Polyfills/isFloat.hpp b/src/ArduinoJson/Text/isFloat.hpp similarity index 87% rename from src/ArduinoJson/Polyfills/isFloat.hpp rename to src/ArduinoJson/Text/isFloat.hpp index 973b89fe..5e241ede 100644 --- a/src/ArduinoJson/Polyfills/isFloat.hpp +++ b/src/ArduinoJson/Text/isFloat.hpp @@ -5,7 +5,7 @@ #pragma once #include // for strcmp -#include "./ctype.hpp" +#include "../Polyfills/ctype.hpp" namespace ArduinoJson { namespace Internals { @@ -34,5 +34,5 @@ inline bool isFloat(const char* s) { return *s == '\0'; } -} -} +} // namespace Internals +} // namespace ArduinoJson diff --git a/src/ArduinoJson/Polyfills/isInteger.hpp b/src/ArduinoJson/Text/isInteger.hpp similarity index 76% rename from src/ArduinoJson/Polyfills/isInteger.hpp rename to src/ArduinoJson/Text/isInteger.hpp index 21f16689..b87ebc25 100644 --- a/src/ArduinoJson/Polyfills/isInteger.hpp +++ b/src/ArduinoJson/Text/isInteger.hpp @@ -4,7 +4,7 @@ #pragma once -#include "./ctype.hpp" +#include "../Polyfills/ctype.hpp" namespace ArduinoJson { namespace Internals { @@ -15,5 +15,5 @@ inline bool isInteger(const char* s) { while (isdigit(*s)) s++; return *s == '\0'; } -} -} +} // namespace Internals +} // namespace ArduinoJson diff --git a/src/ArduinoJson/Polyfills/parseFloat.hpp b/src/ArduinoJson/Text/parseFloat.hpp similarity index 94% rename from src/ArduinoJson/Polyfills/parseFloat.hpp rename to src/ArduinoJson/Text/parseFloat.hpp index 49b0f6fc..763ab3d8 100644 --- a/src/ArduinoJson/Polyfills/parseFloat.hpp +++ b/src/ArduinoJson/Text/parseFloat.hpp @@ -4,9 +4,9 @@ #pragma once +#include "../Polyfills/ctype.hpp" +#include "../Polyfills/math.hpp" #include "../TypeTraits/FloatTraits.hpp" -#include "./ctype.hpp" -#include "./math.hpp" namespace ArduinoJson { namespace Internals { @@ -86,5 +86,5 @@ inline T parseFloat(const char* s) { return negative_result ? -result : result; } -} -} +} // namespace Internals +} // namespace ArduinoJson diff --git a/src/ArduinoJson/Polyfills/parseInteger.hpp b/src/ArduinoJson/Text/parseInteger.hpp similarity index 87% rename from src/ArduinoJson/Polyfills/parseInteger.hpp rename to src/ArduinoJson/Text/parseInteger.hpp index e8f19749..11f2257b 100644 --- a/src/ArduinoJson/Polyfills/parseInteger.hpp +++ b/src/ArduinoJson/Text/parseInteger.hpp @@ -7,7 +7,7 @@ #include #include "../Configuration.hpp" -#include "./ctype.hpp" +#include "../Polyfills/ctype.hpp" namespace ArduinoJson { namespace Internals { @@ -37,5 +37,5 @@ T parseInteger(const char *s) { return negative_result ? T(~result + 1) : result; } -} -} +} // namespace Internals +} // namespace ArduinoJson diff --git a/src/ArduinoJson/deserializeJson.hpp b/src/ArduinoJson/deserializeJson.hpp index b6e4659f..d0932a99 100644 --- a/src/ArduinoJson/deserializeJson.hpp +++ b/src/ArduinoJson/deserializeJson.hpp @@ -4,7 +4,7 @@ #pragma once -#include "Deserialization/JsonParser.hpp" +#include "Json/Deserialization/JsonParser.hpp" namespace ArduinoJson { // JsonError deserializeJson(TDestination& destination, TString json); diff --git a/src/ArduinoJson/deserializeMsgPack.hpp b/src/ArduinoJson/deserializeMsgPack.hpp new file mode 100644 index 00000000..d55a7e77 --- /dev/null +++ b/src/ArduinoJson/deserializeMsgPack.hpp @@ -0,0 +1,49 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "MsgPack/MsgPackDeserializer.hpp" + +namespace ArduinoJson { +// MsgPackError deserializeMsgPack(TDestination& destination, TString json); +// TDestination = JsonArray, JsonObject, JsonVariant +// TString = const std::string&, const String& +template +typename Internals::EnableIf::value, + MsgPackError>::type +deserializeMsgPack(TDestination &destination, const TString &json, + uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { + destination.clear(); + return Internals::makeMsgPackDeserializer(&destination.buffer(), json, + nestingLimit) + .parse(destination); +} +// +// MsgPackError deserializeMsgPack(TDestination& destination, TString json); +// TDestination = JsonArray, JsonObject, JsonVariant +// TString = const char*, const char[N], const FlashStringHelper* +template +MsgPackError deserializeMsgPack( + TDestination &destination, TString *json, + uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { + destination.clear(); + return Internals::makeMsgPackDeserializer(&destination.buffer(), json, + nestingLimit) + .parse(destination); +} +// +// MsgPackError deserializeMsgPack(TDestination& destination, TString json); +// TDestination = JsonArray, JsonObject, JsonVariant +// TString = std::istream&, Stream& +template +MsgPackError deserializeMsgPack( + TDestination &destination, TString &json, + uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { + destination.clear(); + return Internals::makeMsgPackDeserializer(&destination.buffer(), json, + nestingLimit) + .parse(destination); +} +} // namespace ArduinoJson diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 578937a9..b33dfbb4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -73,5 +73,6 @@ add_subdirectory(JsonSerializer) add_subdirectory(JsonVariant) add_subdirectory(JsonWriter) add_subdirectory(Misc) +add_subdirectory(MsgPack) add_subdirectory(Polyfills) add_subdirectory(StaticJsonBuffer) diff --git a/test/JsonWriter/writeFloat.cpp b/test/JsonWriter/writeFloat.cpp index 532de5dc..aa06552b 100644 --- a/test/JsonWriter/writeFloat.cpp +++ b/test/JsonWriter/writeFloat.cpp @@ -6,8 +6,8 @@ #include #include -#include -#include +#include +#include using namespace ArduinoJson::Internals; diff --git a/test/JsonWriter/writeString.cpp b/test/JsonWriter/writeString.cpp index e583fbcf..eded06e3 100644 --- a/test/JsonWriter/writeString.cpp +++ b/test/JsonWriter/writeString.cpp @@ -4,8 +4,8 @@ #include -#include -#include +#include +#include using namespace ArduinoJson::Internals; diff --git a/test/Misc/FloatParts.cpp b/test/Misc/FloatParts.cpp index aeed52e7..d9cf18a1 100644 --- a/test/Misc/FloatParts.cpp +++ b/test/Misc/FloatParts.cpp @@ -2,7 +2,7 @@ // Copyright Benoit Blanchon 2014-2018 // MIT License -#include +#include #include using namespace ArduinoJson::Internals; diff --git a/test/MsgPack/CMakeLists.txt b/test/MsgPack/CMakeLists.txt new file mode 100644 index 00000000..b816549e --- /dev/null +++ b/test/MsgPack/CMakeLists.txt @@ -0,0 +1,16 @@ +# ArduinoJson - arduinojson.org +# Copyright Benoit Blanchon 2014-2018 +# MIT License + +add_executable(MsgPackTests + deserializationErrors.cpp + deserializeArray.cpp + deserializeObject.cpp + deserializeVariant.cpp + deserializeStaticVariant.cpp + doubleToFloat.cpp + MsgPackError.cpp +) + +target_link_libraries(MsgPackTests catch) +add_test(MsgPack MsgPackTests) diff --git a/test/MsgPack/MsgPackError.cpp b/test/MsgPack/MsgPackError.cpp new file mode 100644 index 00000000..8de7c79a --- /dev/null +++ b/test/MsgPack/MsgPackError.cpp @@ -0,0 +1,40 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +void testStringification(MsgPackError error, std::string expected) { + REQUIRE(error.c_str() == expected); +} + +void testBoolification(MsgPackError error, bool expected) { + CHECK(error == expected); +} + +#define TEST_STRINGIFICATION(symbol) \ + testStringification(MsgPackError::symbol, #symbol) + +#define TEST_BOOLIFICATION(symbol, expected) \ + testBoolification(MsgPackError::symbol, expected) + +TEST_CASE("MsgPackError") { + SECTION("c_str()") { + TEST_STRINGIFICATION(Ok); + TEST_STRINGIFICATION(NotSupported); + TEST_STRINGIFICATION(NoMemory); + TEST_STRINGIFICATION(NotAnArray); + TEST_STRINGIFICATION(NotAnObject); + TEST_STRINGIFICATION(TooDeep); + } + + SECTION("as boolean") { + TEST_BOOLIFICATION(Ok, false); + TEST_BOOLIFICATION(NotSupported, true); + TEST_BOOLIFICATION(NoMemory, true); + TEST_BOOLIFICATION(NotAnArray, true); + TEST_BOOLIFICATION(NotAnObject, true); + TEST_BOOLIFICATION(TooDeep, true); + } +} diff --git a/test/MsgPack/deserializationErrors.cpp b/test/MsgPack/deserializationErrors.cpp new file mode 100644 index 00000000..8483674b --- /dev/null +++ b/test/MsgPack/deserializationErrors.cpp @@ -0,0 +1,58 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +static void check(const char* input, MsgPackError expected, + uint8_t nestingLimit = 10) { + DynamicJsonVariant variant; + + MsgPackError error = deserializeMsgPack(variant, input, nestingLimit); + + REQUIRE(error == expected); +} + +TEST_CASE("Errors returned by deserializeMsgPack()") { + SECTION("unsupported") { + check("\xc4", MsgPackError::NotSupported); // bin 8 + check("\xc5", MsgPackError::NotSupported); // bin 16 + check("\xc6", MsgPackError::NotSupported); // bin 32 + check("\xc7", MsgPackError::NotSupported); // ext 8 + check("\xc8", MsgPackError::NotSupported); // ext 16 + check("\xc9", MsgPackError::NotSupported); // ext 32 + check("\xd4", MsgPackError::NotSupported); // fixext 1 + check("\xd5", MsgPackError::NotSupported); // fixext 2 + check("\xd6", MsgPackError::NotSupported); // fixext 4 + check("\xd7", MsgPackError::NotSupported); // fixext 8 + check("\xd8", MsgPackError::NotSupported); // fixext 16 + } + + SECTION("unsupported in array") { + check("\x91\xc4", MsgPackError::NotSupported); + } + + SECTION("unsupported in map") { + check("\x81\xc4\x00\xA1H", MsgPackError::NotSupported); + check("\x81\xA1H\xc4\x00", MsgPackError::NotSupported); + } + + SECTION("integer as key") { + check("\x81\x01\xA1H", MsgPackError::NotSupported); + } + + SECTION("object too deep") { + check("\x80", MsgPackError::TooDeep, 0); // {} + check("\x80", MsgPackError::Ok, 1); // {} + check("\x81\xA1H\x80", MsgPackError::TooDeep, 1); // {H:{}} + check("\x81\xA1H\x80", MsgPackError::Ok, 2); // {H:{}} + } + + SECTION("array too deep") { + check("\x90", MsgPackError::TooDeep, 0); // [] + check("\x90", MsgPackError::Ok, 1); // [] + check("\x91\x90", MsgPackError::TooDeep, 1); // [[]] + check("\x91\x90", MsgPackError::Ok, 2); // [[]] + } +} diff --git a/test/MsgPack/deserializeArray.cpp b/test/MsgPack/deserializeArray.cpp new file mode 100644 index 00000000..a0c492d6 --- /dev/null +++ b/test/MsgPack/deserializeArray.cpp @@ -0,0 +1,85 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +TEST_CASE("deserializeMsgPack(JsonArray&)") { + DynamicJsonArray array; + + SECTION("not an array") { + const char* input = "\xA0"; + + MsgPackError error = deserializeMsgPack(array, input); + + REQUIRE(error == MsgPackError::NotAnArray); + } + + SECTION("fixarray") { + SECTION("empty") { + const char* input = "\x90"; + + MsgPackError error = deserializeMsgPack(array, input); + + REQUIRE(error == MsgPackError::Ok); + REQUIRE(array.size() == 0); + } + + SECTION("two integers") { + const char* input = "\x92\x01\x02"; + + MsgPackError error = deserializeMsgPack(array, input); + + REQUIRE(error == MsgPackError::Ok); + REQUIRE(array.size() == 2); + REQUIRE(array[0] == 1); + REQUIRE(array[1] == 2); + } + } + + SECTION("array 16") { + SECTION("empty") { + const char* input = "\xDC\x00\x00"; + + MsgPackError error = deserializeMsgPack(array, input); + + REQUIRE(error == MsgPackError::Ok); + REQUIRE(array.size() == 0); + } + + SECTION("two strings") { + const char* input = "\xDC\x00\x02\xA5hello\xA5world"; + + MsgPackError error = deserializeMsgPack(array, input); + + REQUIRE(error == MsgPackError::Ok); + REQUIRE(array.size() == 2); + REQUIRE(array[0] == "hello"); + REQUIRE(array[1] == "world"); + } + } + + SECTION("array 32") { + SECTION("empty") { + const char* input = "\xDD\x00\x00\x00\x00"; + + MsgPackError error = deserializeMsgPack(array, input); + + REQUIRE(error == MsgPackError::Ok); + REQUIRE(array.size() == 0); + } + + SECTION("two floats") { + const char* input = + "\xDD\x00\x00\x00\x02\xCA\x00\x00\x00\x00\xCA\x40\x48\xF5\xC3"; + + MsgPackError error = deserializeMsgPack(array, input); + + REQUIRE(error == MsgPackError::Ok); + REQUIRE(array.size() == 2); + REQUIRE(array[0] == 0.0f); + REQUIRE(array[1] == 3.14f); + } + } +} diff --git a/test/MsgPack/deserializeObject.cpp b/test/MsgPack/deserializeObject.cpp new file mode 100644 index 00000000..508b93bc --- /dev/null +++ b/test/MsgPack/deserializeObject.cpp @@ -0,0 +1,86 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +TEST_CASE("deserializeMsgPack(JsonObject&)") { + DynamicJsonObject object; + + SECTION("not an object") { + const char* input = "\xA0"; + + MsgPackError error = deserializeMsgPack(object, input); + + REQUIRE(error == MsgPackError::NotAnObject); + } + + SECTION("fixmap") { + SECTION("empty") { + const char* input = "\x80"; + + MsgPackError error = deserializeMsgPack(object, input); + + REQUIRE(error == MsgPackError::Ok); + REQUIRE(object.size() == 0); + } + + SECTION("two integers") { + const char* input = "\x82\xA3one\x01\xA3two\x02"; + + MsgPackError error = deserializeMsgPack(object, input); + + REQUIRE(error == MsgPackError::Ok); + REQUIRE(object.size() == 2); + REQUIRE(object["one"] == 1); + REQUIRE(object["two"] == 2); + } + } + + SECTION("map 16") { + SECTION("empty") { + const char* input = "\xDE\x00\x00"; + + MsgPackError error = deserializeMsgPack(object, input); + + REQUIRE(error == MsgPackError::Ok); + REQUIRE(object.size() == 0); + } + + SECTION("two strings") { + const char* input = "\xDE\x00\x02\xA1H\xA5hello\xA1W\xA5world"; + + MsgPackError error = deserializeMsgPack(object, input); + + REQUIRE(error == MsgPackError::Ok); + REQUIRE(object.size() == 2); + REQUIRE(object["H"] == "hello"); + REQUIRE(object["W"] == "world"); + } + } + + SECTION("map 32") { + SECTION("empty") { + const char* input = "\xDF\x00\x00\x00\x00"; + + MsgPackError error = deserializeMsgPack(object, input); + + REQUIRE(error == MsgPackError::Ok); + REQUIRE(object.size() == 0); + } + + SECTION("two floats") { + const char* input = + "\xDF\x00\x00\x00\x02\xA4zero\xCA\x00\x00\x00\x00\xA2pi\xCA\x40\x48" + "\xF5\xC3"; + + MsgPackError error = deserializeMsgPack(object, input); + + REQUIRE(error == MsgPackError::Ok); + REQUIRE(object.size() == 2); + REQUIRE(object["zero"] == 0.0f); + REQUIRE(object["pi"] == 3.14f); + } + } +} diff --git a/test/MsgPack/deserializeStaticVariant.cpp b/test/MsgPack/deserializeStaticVariant.cpp new file mode 100644 index 00000000..01ceb21d --- /dev/null +++ b/test/MsgPack/deserializeStaticVariant.cpp @@ -0,0 +1,131 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +static const size_t epsilon = sizeof(void*); + +template +static void check(const char* input, MsgPackError expected) { + StaticJsonVariant variant; + + MsgPackError error = deserializeMsgPack(variant, input); + + REQUIRE(error == expected); +} + +TEST_CASE("deserializeMsgPack(StaticJsonVariant&)") { + SECTION("single values always fit") { + check<0>("\xc0", MsgPackError::Ok); // nil + check<0>("\xc2", MsgPackError::Ok); // false + check<0>("\xc3", MsgPackError::Ok); // true + check<0>("\xcc\x00", MsgPackError::Ok); // uint 8 + check<0>("\xcd\x30\x39", MsgPackError::Ok); // uint 16 + check<0>("\xCE\x12\x34\x56\x78", MsgPackError::Ok); // uint 32 + } + + SECTION("fixstr") { + check<0>("\xA0", MsgPackError::Ok); + check<0>("\xA1H", MsgPackError::NoMemory); + check<4>("\xA1H", MsgPackError::Ok); + check<4>("\xA5Hello", MsgPackError::NoMemory); + } + + SECTION("str 8") { + check<0>("\xD9\x00", MsgPackError::Ok); + check<0>("\xD9\x01H", MsgPackError::NoMemory); + check<4>("\xD9\x01H", MsgPackError::Ok); + check<4>("\xD9\x05Hello", MsgPackError::NoMemory); + } + + SECTION("str 16") { + check<0>("\xDA\x00\x00", MsgPackError::Ok); + check<0>("\xDA\x00\x01H", MsgPackError::NoMemory); + check<4>("\xDA\x00\x01H", MsgPackError::Ok); + check<4>("\xDA\x00\x05Hello", MsgPackError::NoMemory); + } + + SECTION("str 32") { + check<0>("\xDB\x00\x00\x00\x00", MsgPackError::Ok); + check<0>("\xDB\x00\x00\x00\x01H", MsgPackError::NoMemory); + check<4>("\xDB\x00\x00\x00\x01H", MsgPackError::Ok); + check<4>("\xDB\x00\x00\x00\x05Hello", MsgPackError::NoMemory); + } + + SECTION("fixarray") { + check("\x90", MsgPackError::Ok); // [] + check("\x91\x01", MsgPackError::NoMemory); // [1] + check("\x91\x01", MsgPackError::Ok); // [1] + check("\x92\x01\x02", MsgPackError::NoMemory); // [1,2] + } + + SECTION("array 16") { + check("\xDC\x00\x00", MsgPackError::Ok); + check("\xDC\x00\x01\x01", MsgPackError::NoMemory); + check("\xDC\x00\x01\x01", MsgPackError::Ok); + check("\xDC\x00\x02\x01\x02", MsgPackError::NoMemory); + } + + SECTION("array 32") { + check("\xDD\x00\x00\x00\x00", MsgPackError::Ok); + check("\xDD\x00\x00\x00\x01\x01", + MsgPackError::NoMemory); + check("\xDD\x00\x00\x00\x01\x01", MsgPackError::Ok); + check("\xDD\x00\x00\x00\x02\x01\x02", + MsgPackError::NoMemory); + } + + SECTION("fixmap") { + SECTION("{}") { + check("\x80", MsgPackError::Ok); + } + SECTION("{H:1}") { + check("\x81\xA1H\x01", + MsgPackError::NoMemory); + check("\x81\xA1H\x01", MsgPackError::Ok); + } + SECTION("{H:1,W:2}") { + check("\x82\xA1H\x01\xA1W\x02", + MsgPackError::NoMemory); + check("\x82\xA1H\x01\xA1W\x02", + MsgPackError::Ok); + } + } + + SECTION("map 16") { + SECTION("{}") { + check("\xDE\x00\x00", MsgPackError::Ok); + } + SECTION("{H:1}") { + check("\xDE\x00\x01\xA1H\x01", + MsgPackError::NoMemory); + check("\xDE\x00\x01\xA1H\x01", MsgPackError::Ok); + } + SECTION("{H:1,W:2}") { + check("\xDE\x00\x02\xA1H\x01\xA1W\x02", + MsgPackError::NoMemory); + check("\xDE\x00\x02\xA1H\x01\xA1W\x02", + MsgPackError::Ok); + } + } + + SECTION("map 32") { + SECTION("{}") { + check("\xDF\x00\x00\x00\x00", MsgPackError::Ok); + } + SECTION("{H:1}") { + check("\xDF\x00\x00\x00\x01\xA1H\x01", + MsgPackError::NoMemory); + check("\xDF\x00\x00\x00\x01\xA1H\x01", + MsgPackError::Ok); + } + SECTION("{H:1,W:2}") { + check("\xDF\x00\x00\x00\x02\xA1H\x01\xA1W\x02", + MsgPackError::NoMemory); + check("\xDF\x00\x00\x00\x02\xA1H\x01\xA1W\x02", + MsgPackError::Ok); + } + } +} diff --git a/test/MsgPack/deserializeVariant.cpp b/test/MsgPack/deserializeVariant.cpp new file mode 100644 index 00000000..f25ed897 --- /dev/null +++ b/test/MsgPack/deserializeVariant.cpp @@ -0,0 +1,277 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +template +static void check(const char* input, U expected) { + DynamicJsonVariant variant; + + MsgPackError error = deserializeMsgPack(variant, input); + + REQUIRE(error == MsgPackError::Ok); + REQUIRE(variant.is()); + REQUIRE(variant.as() == expected); +} + +TEST_CASE("deserializeMsgPack(JsonVariant&)") { + SECTION("nil") { + const char* nil = 0; // ArduinoJson uses a string for null + check("\xc0", nil); + } + + SECTION("bool") { + check("\xc2", false); + check("\xc3", true); + } + + SECTION("positive fixint") { + check("\x00", 0); + check("\x7F", 127); + } + + SECTION("negative fixint") { + check("\xe0", -32); + check("\xff", -1); + } + + SECTION("uint 8") { + check("\xcc\x00", 0); + check("\xcc\xff", 255); + } + + SECTION("uint 16") { + check("\xcd\x00\x00", 0); + check("\xcd\xFF\xFF", 65535); + check("\xcd\x30\x39", 12345); + } + + SECTION("uint 32") { + check("\xCE\x00\x00\x00\x00", 0x00000000U); + check("\xCE\xFF\xFF\xFF\xFF", 0xFFFFFFFFU); + check("\xCE\x12\x34\x56\x78", 0x12345678U); + } + + SECTION("uint 64") { +#if ARDUINOJSON_USE_LONG_LONG || ARDUINOJSON_USE_INT64 + check("\xCF\x00\x00\x00\x00\x00\x00\x00\x00", 0U); + check("\xCF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", + 0xFFFFFFFFFFFFFFFFU); + check("\xCF\x12\x34\x56\x78\x9A\xBC\xDE\xF0", + 0x123456789ABCDEF0U); +#else + check("\xCF\x00\x00\x00\x00\x00\x00\x00\x00", 0U); + check("\xCF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 0xFFFFFFFF); + check("\xCF\x12\x34\x56\x78\x9A\xBC\xDE\xF0", 0x9ABCDEF0); +#endif + } + + SECTION("int 8") { + check("\xd0\x00", 0); + check("\xd0\xff", -1); + } + + SECTION("int 16") { + check("\xD1\x00\x00", 0); + check("\xD1\xFF\xFF", -1); + check("\xD1\xCF\xC7", -12345); + } + + SECTION("int 32") { + check("\xD2\x00\x00\x00\x00", 0); + check("\xD2\xFF\xFF\xFF\xFF", -1); + check("\xD2\xB6\x69\xFD\x2E", -1234567890); + } + + SECTION("int 64") { +#if ARDUINOJSON_USE_LONG_LONG || ARDUINOJSON_USE_INT64 + check("\xD3\x00\x00\x00\x00\x00\x00\x00\x00", 0U); + check("\xD3\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", + 0xFFFFFFFFFFFFFFFFU); + check("\xD3\x12\x34\x56\x78\x9A\xBC\xDE\xF0", + 0x123456789ABCDEF0U); +#else + check("\xD3\x00\x00\x00\x00\x00\x00\x00\x00", 0U); + check("\xD3\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 0xFFFFFFFF); + check("\xD3\x12\x34\x56\x78\x9A\xBC\xDE\xF0", 0x9ABCDEF0); +#endif + } + + SECTION("float 32") { + check("\xCA\x00\x00\x00\x00", 0.0f); + check("\xCA\x40\x48\xF5\xC3", 3.14f); + } + + SECTION("float 64") { + check("\xCB\x00\x00\x00\x00\x00\x00\x00\x00", 0.0); + check("\xCB\x40\x09\x21\xCA\xC0\x83\x12\x6F", 3.1415); + } + + SECTION("fixstr") { + check("\xA0", std::string("")); + check("\xABhello world", std::string("hello world")); + check("\xBFhello world hello world hello !", + std::string("hello world hello world hello !")); + } + + SECTION("str 8") { + check("\xd9\x05hello", std::string("hello")); + } + + SECTION("str 16") { + check("\xda\x00\x05hello", std::string("hello")); + } + + SECTION("str 32") { + check("\xdb\x00\x00\x00\x05hello", std::string("hello")); + } + + SECTION("fixarray") { + DynamicJsonVariant variant; + + SECTION("empty") { + const char* input = "\x90"; + + MsgPackError error = deserializeMsgPack(variant, input); + + REQUIRE(error == MsgPackError::Ok); + REQUIRE(variant.size() == 0); + } + + SECTION("two integers") { + const char* input = "\x92\x01\x02"; + + MsgPackError error = deserializeMsgPack(variant, input); + + REQUIRE(error == MsgPackError::Ok); + REQUIRE(variant.size() == 2); + REQUIRE(variant[0] == 1); + REQUIRE(variant[1] == 2); + } + } + + SECTION("array 16") { + DynamicJsonVariant variant; + + SECTION("empty") { + const char* input = "\xDC\x00\x00"; + + MsgPackError error = deserializeMsgPack(variant, input); + + REQUIRE(error == MsgPackError::Ok); + REQUIRE(variant.size() == 0); + } + + SECTION("two strings") { + const char* input = "\xDC\x00\x02\xA5hello\xA5world"; + + MsgPackError error = deserializeMsgPack(variant, input); + + REQUIRE(error == MsgPackError::Ok); + REQUIRE(variant.size() == 2); + REQUIRE(variant[0] == "hello"); + REQUIRE(variant[1] == "world"); + } + } + + SECTION("array 32") { + DynamicJsonVariant variant; + + SECTION("empty") { + const char* input = "\xDD\x00\x00\x00\x00"; + + MsgPackError error = deserializeMsgPack(variant, input); + + REQUIRE(error == MsgPackError::Ok); + REQUIRE(variant.size() == 0); + } + + SECTION("two floats") { + const char* input = + "\xDD\x00\x00\x00\x02\xCA\x00\x00\x00\x00\xCA\x40\x48\xF5\xC3"; + + MsgPackError error = deserializeMsgPack(variant, input); + + REQUIRE(error == MsgPackError::Ok); + REQUIRE(variant.size() == 2); + REQUIRE(variant[0] == 0.0f); + REQUIRE(variant[1] == 3.14f); + } + } + + SECTION("fixmap") { + DynamicJsonVariant variant; + + SECTION("empty") { + const char* input = "\x80"; + + MsgPackError error = deserializeMsgPack(variant, input); + + REQUIRE(error == MsgPackError::Ok); + REQUIRE(variant.size() == 0); + } + + SECTION("two integers") { + const char* input = "\x82\xA3one\x01\xA3two\x02"; + + MsgPackError error = deserializeMsgPack(variant, input); + + REQUIRE(error == MsgPackError::Ok); + REQUIRE(variant.size() == 2); + REQUIRE(variant["one"] == 1); + REQUIRE(variant["two"] == 2); + } + } + + SECTION("map 16") { + DynamicJsonVariant variant; + + SECTION("empty") { + const char* input = "\xDE\x00\x00"; + + MsgPackError error = deserializeMsgPack(variant, input); + + REQUIRE(error == MsgPackError::Ok); + REQUIRE(variant.size() == 0); + } + + SECTION("two strings") { + const char* input = "\xDE\x00\x02\xA1H\xA5hello\xA1W\xA5world"; + + MsgPackError error = deserializeMsgPack(variant, input); + + REQUIRE(error == MsgPackError::Ok); + REQUIRE(variant.size() == 2); + REQUIRE(variant["H"] == "hello"); + REQUIRE(variant["W"] == "world"); + } + } + + SECTION("map 32") { + DynamicJsonVariant variant; + + SECTION("empty") { + const char* input = "\xDF\x00\x00\x00\x00"; + + MsgPackError error = deserializeMsgPack(variant, input); + + REQUIRE(error == MsgPackError::Ok); + REQUIRE(variant.size() == 0); + } + + SECTION("two floats") { + const char* input = + "\xDF\x00\x00\x00\x02\xA4zero\xCA\x00\x00\x00\x00\xA2pi\xCA\x40\x48" + "\xF5\xC3"; + + MsgPackError error = deserializeMsgPack(variant, input); + + REQUIRE(error == MsgPackError::Ok); + REQUIRE(variant.size() == 2); + REQUIRE(variant["zero"] == 0.0f); + REQUIRE(variant["pi"] == 3.14f); + } + } +} diff --git a/test/MsgPack/doubleToFloat.cpp b/test/MsgPack/doubleToFloat.cpp new file mode 100644 index 00000000..da37bd56 --- /dev/null +++ b/test/MsgPack/doubleToFloat.cpp @@ -0,0 +1,25 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +using namespace ArduinoJson::Internals; + +template +static void check(const char* input, T expected) { + T actual; + uint8_t* f = reinterpret_cast(&actual); + const uint8_t* d = reinterpret_cast(input); + doubleToFloat(d, f); + fixEndianess(actual); + CHECK(actual == expected); +} + +TEST_CASE("Internals::doubleToFloat()") { + check("\x40\x09\x21\xCA\xC0\x83\x12\x6F", 3.1415f); + check("\x00\x00\x00\x00\x00\x00\x00\x00", 0.0f); + check("\x80\x00\x00\x00\x00\x00\x00\x00", -0.0f); + check("\xC0\x5E\xDC\xCC\xCC\xCC\xCC\xCD", -123.45f); +} diff --git a/test/Polyfills/isFloat.cpp b/test/Polyfills/isFloat.cpp index 225d5221..f4fbe16b 100644 --- a/test/Polyfills/isFloat.cpp +++ b/test/Polyfills/isFloat.cpp @@ -2,7 +2,7 @@ // Copyright Benoit Blanchon 2014-2018 // MIT License -#include +#include #include using namespace ArduinoJson::Internals; diff --git a/test/Polyfills/isInteger.cpp b/test/Polyfills/isInteger.cpp index f0bec4ae..8cc74dd1 100644 --- a/test/Polyfills/isInteger.cpp +++ b/test/Polyfills/isInteger.cpp @@ -2,7 +2,7 @@ // Copyright Benoit Blanchon 2014-2018 // MIT License -#include +#include #include using namespace ArduinoJson::Internals; diff --git a/test/Polyfills/parseFloat.cpp b/test/Polyfills/parseFloat.cpp index 362e46d6..bd481ea2 100644 --- a/test/Polyfills/parseFloat.cpp +++ b/test/Polyfills/parseFloat.cpp @@ -2,7 +2,7 @@ // Copyright Benoit Blanchon 2014-2018 // MIT License -#include +#include #include using namespace ArduinoJson::Internals; diff --git a/test/Polyfills/parseInteger.cpp b/test/Polyfills/parseInteger.cpp index 3d25bdb2..afac3bf5 100644 --- a/test/Polyfills/parseInteger.cpp +++ b/test/Polyfills/parseInteger.cpp @@ -3,7 +3,7 @@ // MIT License #include -#include +#include #include using namespace ArduinoJson::Internals;