Przeglądaj źródła

Added support for non zero-terminated strings (fixes #704)

Benoit Blanchon 7 lat temu
rodzic
commit
ccb54136a2
54 zmienionych plików z 2187 dodań i 1354 usunięć
  1. 1 1
      .travis.yml
  2. 1 0
      CHANGELOG.md
  3. 0 2
      src/ArduinoJson.hpp
  4. 0 61
      src/ArduinoJson/Json/Deserialization/Comments.hpp
  5. 145 62
      src/ArduinoJson/Json/Deserialization/JsonDeserializer.hpp
  6. 7 1
      src/ArduinoJson/JsonError.hpp
  7. 117 100
      src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp
  8. 7 1
      src/ArduinoJson/MsgPack/MsgPackError.hpp
  9. 41 0
      src/ArduinoJson/Reading/ArduinoStreamReader.hpp
  10. 64 0
      src/ArduinoJson/Reading/CharPointerReader.hpp
  11. 56 0
      src/ArduinoJson/Reading/FlashStringReader.hpp
  12. 34 0
      src/ArduinoJson/Reading/IteratorReader.hpp
  13. 11 0
      src/ArduinoJson/Reading/Reader.hpp
  14. 40 0
      src/ArduinoJson/Reading/StdStreamReader.hpp
  15. 0 61
      src/ArduinoJson/Strings/ArduinoStream.hpp
  16. 0 20
      src/ArduinoJson/Strings/CharPointer.hpp
  17. 0 20
      src/ArduinoJson/Strings/FlashString.hpp
  18. 0 60
      src/ArduinoJson/Strings/StdStream.hpp
  19. 0 4
      src/ArduinoJson/Strings/StdString.hpp
  20. 0 2
      src/ArduinoJson/Strings/StringTraits.hpp
  21. 27 0
      src/ArduinoJson/Writing/JsonBufferWriter.hpp
  22. 0 0
      src/ArduinoJson/Writing/StringWriter.hpp
  23. 44 0
      src/ArduinoJson/Writing/Writer.hpp
  24. 32 13
      src/ArduinoJson/deserializeJson.hpp
  25. 41 19
      src/ArduinoJson/deserializeMsgPack.hpp
  26. 1 0
      test/JsonArray/CMakeLists.txt
  27. 39 0
      test/JsonArray/std_string.cpp
  28. 2 0
      test/JsonDeserializer/CMakeLists.txt
  29. 2 0
      test/JsonDeserializer/JsonError.cpp
  30. 38 24
      test/JsonDeserializer/deserializeJsonArray.cpp
  31. 2 18
      test/JsonDeserializer/deserializeJsonArrayStatic.cpp
  32. 220 6
      test/JsonDeserializer/deserializeJsonObject.cpp
  33. 2 18
      test/JsonDeserializer/deserializeJsonObjectStatic.cpp
  34. 92 23
      test/JsonDeserializer/deserializeJsonValue.cpp
  35. 73 0
      test/JsonDeserializer/std_istream.cpp
  36. 35 0
      test/JsonDeserializer/std_string.cpp
  37. 1 0
      test/JsonObject/CMakeLists.txt
  38. 173 0
      test/JsonObject/std_string.cpp
  39. 2 0
      test/JsonSerializer/CMakeLists.txt
  40. 64 0
      test/JsonSerializer/std_stream.cpp
  41. 47 0
      test/JsonSerializer/std_string.cpp
  42. 1 3
      test/Misc/CMakeLists.txt
  43. 0 88
      test/Misc/std_stream.cpp
  44. 0 259
      test/Misc/std_string.cpp
  45. 181 173
      test/Misc/unsigned_char.cpp
  46. 252 253
      test/Misc/vla.cpp
  47. 7 3
      test/MsgPack/CMakeLists.txt
  48. 2 0
      test/MsgPack/MsgPackError.cpp
  49. 0 59
      test/MsgPack/deserializationErrors.cpp
  50. 106 0
      test/MsgPack/incompleteInput.cpp
  51. 31 0
      test/MsgPack/nestingLimit.cpp
  52. 73 0
      test/MsgPack/notSupported.cpp
  53. 29 0
      test/MsgPack/std_istream.cpp
  54. 44 0
      test/MsgPack/std_string.cpp

+ 1 - 1
.travis.yml

@@ -31,7 +31,7 @@ matrix:
         apt:
         apt:
           sources: ['ubuntu-toolchain-r-test']
           sources: ['ubuntu-toolchain-r-test']
           packages: ['g++-5']
           packages: ['g++-5']
-      env: SCRIPT=cmake GCC=5 SANITIZE=undefined
+      env: SCRIPT=cmake GCC=5 # SANITIZE=undefined 
     - compiler: gcc
     - compiler: gcc
       addons:
       addons:
         apt:
         apt:

+ 1 - 0
CHANGELOG.md

@@ -12,6 +12,7 @@ HEAD
 * Added `measureJson()` and `measureJsonPretty()`
 * Added `measureJson()` and `measureJsonPretty()`
 * Added `deserializeMsgPack()` (issue #358)
 * Added `deserializeMsgPack()` (issue #358)
 * Added example `MsgPackParser.ino` (issue #358)
 * Added example `MsgPackParser.ino` (issue #358)
+* Added support for non zero-terminated strings (issue #704)
 * Removed `JsonBuffer::parseArray()`, `parseObject()` and `parse()`
 * Removed `JsonBuffer::parseArray()`, `parseObject()` and `parse()`
 * Removed `JsonBuffer::createArray()` and `createObject()`
 * Removed `JsonBuffer::createArray()` and `createObject()`
 * Removed `printTo()` and `prettyPrintTo()`
 * Removed `printTo()` and `prettyPrintTo()`

+ 0 - 2
src/ArduinoJson.hpp

@@ -5,12 +5,10 @@
 #pragma once
 #pragma once
 
 
 #include "ArduinoJson/DynamicJsonDocument.hpp"
 #include "ArduinoJson/DynamicJsonDocument.hpp"
-#include "ArduinoJson/MsgPack/MsgPackDeserializer.hpp"
 #include "ArduinoJson/StaticJsonDocument.hpp"
 #include "ArduinoJson/StaticJsonDocument.hpp"
 #include "ArduinoJson/deserializeJson.hpp"
 #include "ArduinoJson/deserializeJson.hpp"
 #include "ArduinoJson/deserializeMsgPack.hpp"
 #include "ArduinoJson/deserializeMsgPack.hpp"
 
 
-#include "ArduinoJson/Json/Deserialization/JsonDeserializer.hpp"
 #include "ArduinoJson/Json/Serialization/JsonSerializer.hpp"
 #include "ArduinoJson/Json/Serialization/JsonSerializer.hpp"
 #include "ArduinoJson/JsonArrayImpl.hpp"
 #include "ArduinoJson/JsonArrayImpl.hpp"
 #include "ArduinoJson/JsonObjectImpl.hpp"
 #include "ArduinoJson/JsonObjectImpl.hpp"

+ 0 - 61
src/ArduinoJson/Json/Deserialization/Comments.hpp

@@ -1,61 +0,0 @@
-// ArduinoJson - arduinojson.org
-// Copyright Benoit Blanchon 2014-2018
-// MIT License
-
-#pragma once
-
-namespace ArduinoJson {
-namespace Internals {
-template <typename TInput>
-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;
-    }
-  }
-}
-}
-}

+ 145 - 62
src/ArduinoJson/Json/Deserialization/JsonDeserializer.hpp

@@ -7,10 +7,9 @@
 #include "../../JsonError.hpp"
 #include "../../JsonError.hpp"
 #include "../../JsonVariant.hpp"
 #include "../../JsonVariant.hpp"
 #include "../../Memory/JsonBuffer.hpp"
 #include "../../Memory/JsonBuffer.hpp"
-#include "../../Strings/StringWriter.hpp"
+#include "../../Reading/Reader.hpp"
 #include "../../TypeTraits/IsConst.hpp"
 #include "../../TypeTraits/IsConst.hpp"
 #include "../Encoding.hpp"
 #include "../Encoding.hpp"
-#include "./Comments.hpp"
 
 
 namespace ArduinoJson {
 namespace ArduinoJson {
 namespace Internals {
 namespace Internals {
@@ -23,11 +22,13 @@ class JsonDeserializer {
       : _buffer(buffer),
       : _buffer(buffer),
         _reader(reader),
         _reader(reader),
         _writer(writer),
         _writer(writer),
-        _nestingLimit(nestingLimit) {}
+        _nestingLimit(nestingLimit),
+        _loaded(false) {}
   JsonError parse(JsonVariant &variant) {
   JsonError parse(JsonVariant &variant) {
-    skipSpacesAndComments(_reader);
+    JsonError err = skipSpacesAndComments();
+    if (err) return err;
 
 
-    switch (_reader.current()) {
+    switch (current()) {
       case '[':
       case '[':
         return parseArray(variant);
         return parseArray(variant);
 
 
@@ -42,15 +43,25 @@ class JsonDeserializer {
  private:
  private:
   JsonDeserializer &operator=(const JsonDeserializer &);  // non-copiable
   JsonDeserializer &operator=(const JsonDeserializer &);  // non-copiable
 
 
-  static bool eat(TReader &reader, char charToSkip) {
-    skipSpacesAndComments(reader);
-    if (reader.current() != charToSkip) return false;
-    reader.move();
-    return true;
+  char current() {
+    if (!_loaded) {
+      if (_reader.ended())
+        _current = 0;
+      else
+        _current = _reader.read();
+      _loaded = true;
+    }
+    return _current;
+  }
+
+  void move() {
+    _loaded = false;
   }
   }
 
 
   FORCE_INLINE bool eat(char charToSkip) {
   FORCE_INLINE bool eat(char charToSkip) {
-    return eat(_reader, charToSkip);
+    if (current() != charToSkip) return false;
+    move();
+    return true;
   }
   }
 
 
   JsonError parseArray(JsonVariant &variant) {
   JsonError parseArray(JsonVariant &variant) {
@@ -62,6 +73,12 @@ class JsonDeserializer {
 
 
     // Check opening braket
     // Check opening braket
     if (!eat('[')) return JsonError::InvalidInput;
     if (!eat('[')) return JsonError::InvalidInput;
+
+    // Skip spaces
+    JsonError err = skipSpacesAndComments();
+    if (err) return err;
+
+    // Empty array?
     if (eat(']')) return JsonError::Ok;
     if (eat(']')) return JsonError::Ok;
 
 
     // Read each value
     // Read each value
@@ -69,12 +86,16 @@ class JsonDeserializer {
       // 1 - Parse value
       // 1 - Parse value
       JsonVariant value;
       JsonVariant value;
       _nestingLimit--;
       _nestingLimit--;
-      JsonError error = parse(value);
+      err = parse(value);
       _nestingLimit++;
       _nestingLimit++;
-      if (error != JsonError::Ok) return error;
+      if (err) return err;
       if (!array->add(value)) return JsonError::NoMemory;
       if (!array->add(value)) return JsonError::NoMemory;
 
 
-      // 2 - More values?
+      // 2 - Skip spaces
+      err = skipSpacesAndComments();
+      if (err) return err;
+
+      // 3 - More values?
       if (eat(']')) return JsonError::Ok;
       if (eat(']')) return JsonError::Ok;
       if (!eat(',')) return JsonError::InvalidInput;
       if (!eat(',')) return JsonError::InvalidInput;
     }
     }
@@ -89,31 +110,52 @@ class JsonDeserializer {
 
 
     // Check opening brace
     // Check opening brace
     if (!eat('{')) return JsonError::InvalidInput;
     if (!eat('{')) return JsonError::InvalidInput;
+
+    // Skip spaces
+    JsonError err = skipSpacesAndComments();
+    if (err) return err;
+
+    // Empty object?
     if (eat('}')) return JsonError::Ok;
     if (eat('}')) return JsonError::Ok;
 
 
     // Read each key value pair
     // Read each key value pair
     for (;;) {
     for (;;) {
-      // 1 - Parse key
+      // Parse key
       const char *key;
       const char *key;
-      JsonError error = parseString(&key);
-      if (error) return error;
+      err = parseString(&key);
+      if (err) return err;
+
+      // Skip spaces
+      err = skipSpacesAndComments();
+      if (err) return err;
+
+      // Colon
       if (!eat(':')) return JsonError::InvalidInput;
       if (!eat(':')) return JsonError::InvalidInput;
 
 
-      // 2 - Parse value
+      // Parse value
       JsonVariant value;
       JsonVariant value;
       _nestingLimit--;
       _nestingLimit--;
-      error = parse(value);
+      err = parse(value);
       _nestingLimit++;
       _nestingLimit++;
-      if (error != JsonError::Ok) return error;
+      if (err) return err;
       if (!object->set(key, value)) return JsonError::NoMemory;
       if (!object->set(key, value)) return JsonError::NoMemory;
 
 
-      // 3 - More keys/values?
+      // Skip spaces
+      err = skipSpacesAndComments();
+      if (err) return err;
+
+      // More keys/values?
       if (eat('}')) return JsonError::Ok;
       if (eat('}')) return JsonError::Ok;
       if (!eat(',')) return JsonError::InvalidInput;
       if (!eat(',')) return JsonError::InvalidInput;
+
+      // Skip spaces
+      err = skipSpacesAndComments();
+      if (err) return err;
     }
     }
   }
   }
+
   JsonError parseValue(JsonVariant &variant) {
   JsonError parseValue(JsonVariant &variant) {
-    bool hasQuotes = isQuote(_reader.current());
+    bool hasQuotes = isQuote(current());
     const char *value;
     const char *value;
     JsonError error = parseString(&value);
     JsonError error = parseString(&value);
     if (error) return error;
     if (error) return error;
@@ -128,33 +170,35 @@ class JsonDeserializer {
   JsonError parseString(const char **result) {
   JsonError parseString(const char **result) {
     typename RemoveReference<TWriter>::type::String str = _writer.startString();
     typename RemoveReference<TWriter>::type::String str = _writer.startString();
 
 
-    skipSpacesAndComments(_reader);
-    char c = _reader.current();
+    char c = current();
+    if (c == '\0') return JsonError::IncompleteInput;
 
 
     if (isQuote(c)) {  // quotes
     if (isQuote(c)) {  // quotes
-      _reader.move();
+      move();
       char stopChar = c;
       char stopChar = c;
       for (;;) {
       for (;;) {
-        c = _reader.current();
-        if (c == '\0') break;
-        _reader.move();
-
+        c = current();
+        move();
         if (c == stopChar) break;
         if (c == stopChar) break;
 
 
+        if (c == '\0') return JsonError::IncompleteInput;
+
         if (c == '\\') {
         if (c == '\\') {
+          c = current();
+          if (c == 0) return JsonError::IncompleteInput;
           // replace char
           // replace char
-          c = Encoding::unescapeChar(_reader.current());
+          c = Encoding::unescapeChar(c);
           if (c == '\0') return JsonError::InvalidInput;
           if (c == '\0') return JsonError::InvalidInput;
-          _reader.move();
+          move();
         }
         }
 
 
         str.append(c);
         str.append(c);
       }
       }
     } else if (canBeInNonQuotedString(c)) {  // no quotes
     } else if (canBeInNonQuotedString(c)) {  // no quotes
       do {
       do {
-        _reader.move();
+        move();
         str.append(c);
         str.append(c);
-        c = _reader.current();
+        c = current();
       } while (canBeInNonQuotedString(c));
       } while (canBeInNonQuotedString(c));
     } else {
     } else {
       return JsonError::InvalidInput;
       return JsonError::InvalidInput;
@@ -178,41 +222,80 @@ class JsonDeserializer {
     return c == '\'' || c == '\"';
     return c == '\'' || c == '\"';
   }
   }
 
 
+  JsonError skipSpacesAndComments() {
+    for (;;) {
+      switch (current()) {
+        // end of string
+        case '\0':
+          return JsonError::IncompleteInput;
+
+        // spaces
+        case ' ':
+        case '\t':
+        case '\r':
+        case '\n':
+          move();
+          continue;
+
+        // comments
+        case '/':
+          move();  // skip '/'
+          switch (current()) {
+            // block comment
+            case '*': {
+              move();  // skip '*'
+              bool wasStar = false;
+              for (;;) {
+                char c = current();
+                if (c == '\0') return JsonError::IncompleteInput;
+                if (c == '/' && wasStar) {
+                  move();
+                  break;
+                }
+                wasStar = c == '*';
+                move();
+              }
+              break;
+            }
+
+            // trailing comment
+            case '/':
+              // no need to skip "//"
+              for (;;) {
+                move();
+                char c = current();
+                if (c == '\0') return JsonError::IncompleteInput;
+                if (c == '\n') break;
+              }
+              break;
+
+            // not a comment, just a '/'
+            default:
+              return JsonError::InvalidInput;
+          }
+          break;
+
+        default:
+          return JsonError::Ok;
+      }
+    }
+  }
+
   JsonBuffer *_buffer;
   JsonBuffer *_buffer;
   TReader _reader;
   TReader _reader;
   TWriter _writer;
   TWriter _writer;
   uint8_t _nestingLimit;
   uint8_t _nestingLimit;
+  char _current;
+  bool _loaded;
 };
 };
 
 
-template <typename TJsonBuffer, typename TString, typename Enable = void>
-struct JsonParserBuilder {
-  typedef typename StringTraits<TString>::Reader InputReader;
-  typedef JsonDeserializer<InputReader, TJsonBuffer &> TParser;
-
-  static TParser makeParser(TJsonBuffer *buffer, TString &json,
-                            uint8_t nestingLimit) {
-    return TParser(buffer, InputReader(json), *buffer, nestingLimit);
-  }
-};
-
-template <typename TJsonBuffer, typename TChar>
-struct JsonParserBuilder<TJsonBuffer, TChar *,
-                         typename EnableIf<!IsConst<TChar>::value>::type> {
-  typedef typename StringTraits<TChar *>::Reader TReader;
-  typedef StringWriter<TChar> TWriter;
-  typedef JsonDeserializer<TReader, TWriter> TParser;
-
-  static TParser makeParser(TJsonBuffer *buffer, TChar *json,
-                            uint8_t nestingLimit) {
-    return TParser(buffer, TReader(json), TWriter(json), nestingLimit);
-  }
-};
-
-template <typename TJsonBuffer, typename TString>
-inline typename JsonParserBuilder<TJsonBuffer, TString>::TParser makeParser(
-    TJsonBuffer *buffer, TString &json, uint8_t nestingLimit) {
-  return JsonParserBuilder<TJsonBuffer, TString>::makeParser(buffer, json,
-                                                             nestingLimit);
+template <typename TJsonBuffer, typename TReader, typename TWriter>
+JsonDeserializer<TReader, TWriter> makeJsonDeserializer(TJsonBuffer *buffer,
+                                                        TReader reader,
+                                                        TWriter writer,
+                                                        uint8_t nestingLimit) {
+  return JsonDeserializer<TReader, TWriter>(buffer, reader, writer,
+                                            nestingLimit);
 }
 }
 }  // namespace Internals
 }  // namespace Internals
 }  // namespace ArduinoJson
 }  // namespace ArduinoJson

+ 7 - 1
src/ArduinoJson/JsonError.hpp

@@ -4,11 +4,15 @@
 
 
 #pragma once
 #pragma once
 
 
+#if ARDUINOJSON_ENABLE_STD_STREAM
+#include <ostream>
+#endif
+
 namespace ArduinoJson {
 namespace ArduinoJson {
 
 
 class JsonError {
 class JsonError {
  public:
  public:
-  enum Code { Ok, TooDeep, NoMemory, InvalidInput };
+  enum Code { Ok, TooDeep, NoMemory, InvalidInput, IncompleteInput };
 
 
   JsonError(Code code) : _code(code) {}
   JsonError(Code code) : _code(code) {}
 
 
@@ -42,6 +46,8 @@ class JsonError {
         return "NoMemory";
         return "NoMemory";
       case InvalidInput:
       case InvalidInput:
         return "InvalidInput";
         return "InvalidInput";
+      case IncompleteInput:
+        return "IncompleteInput";
       default:
       default:
         return "???";
         return "???";
     }
     }

+ 117 - 100
src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp

@@ -6,8 +6,9 @@
 
 
 #include "../JsonVariant.hpp"
 #include "../JsonVariant.hpp"
 #include "../Memory/JsonBuffer.hpp"
 #include "../Memory/JsonBuffer.hpp"
-#include "../Strings/StringWriter.hpp"
+#include "../Reading/Reader.hpp"
 #include "../TypeTraits/IsConst.hpp"
 #include "../TypeTraits/IsConst.hpp"
+#include "../Writing/Writer.hpp"
 #include "./MsgPackError.hpp"
 #include "./MsgPackError.hpp"
 #include "./endianess.hpp"
 #include "./endianess.hpp"
 #include "./ieee754.hpp"
 #include "./ieee754.hpp"
@@ -29,27 +30,28 @@ class MsgPackDeserializer {
         _nestingLimit(nestingLimit) {}
         _nestingLimit(nestingLimit) {}
 
 
   MsgPackError parse(JsonVariant &variant) {
   MsgPackError parse(JsonVariant &variant) {
-    uint8_t c = readOne();
+    uint8_t code;
+    if (!readByte(code)) return MsgPackError::IncompleteInput;
 
 
-    if ((c & 0x80) == 0) {
-      variant = c;
+    if ((code & 0x80) == 0) {
+      variant = code;
       return MsgPackError::Ok;
       return MsgPackError::Ok;
     }
     }
 
 
-    if ((c & 0xe0) == 0xe0) {
-      variant = static_cast<int8_t>(c);
+    if ((code & 0xe0) == 0xe0) {
+      variant = static_cast<int8_t>(code);
       return MsgPackError::Ok;
       return MsgPackError::Ok;
     }
     }
 
 
-    if ((c & 0xe0) == 0xa0) {
-      return readString(variant, c & 0x1f);
+    if ((code & 0xe0) == 0xa0) {
+      return readString(variant, code & 0x1f);
     }
     }
 
 
-    if ((c & 0xf0) == 0x90) return readArray(variant, c & 0x0F);
+    if ((code & 0xf0) == 0x90) return readArray(variant, code & 0x0F);
 
 
-    if ((c & 0xf0) == 0x80) return readObject(variant, c & 0x0F);
+    if ((code & 0xf0) == 0x80) return readObject(variant, code & 0x0F);
 
 
-    switch (c) {
+    switch (code) {
       case 0xc0:
       case 0xc0:
         variant = static_cast<char *>(0);
         variant = static_cast<char *>(0);
         return MsgPackError::Ok;
         return MsgPackError::Ok;
@@ -63,81 +65,65 @@ class MsgPackDeserializer {
         return MsgPackError::Ok;
         return MsgPackError::Ok;
 
 
       case 0xcc:
       case 0xcc:
-        variant = readInteger<uint8_t>();
-        return MsgPackError::Ok;
+        return readInteger<uint8_t>(variant);
 
 
       case 0xcd:
       case 0xcd:
-        variant = readInteger<uint16_t>();
-        return MsgPackError::Ok;
+        return readInteger<uint16_t>(variant);
 
 
       case 0xce:
       case 0xce:
-        variant = readInteger<uint32_t>();
-        return MsgPackError::Ok;
+        return readInteger<uint32_t>(variant);
 
 
       case 0xcf:
       case 0xcf:
 #if ARDUINOJSON_USE_LONG_LONG || ARDUINOJSON_USE_INT64
 #if ARDUINOJSON_USE_LONG_LONG || ARDUINOJSON_USE_INT64
-        variant = readInteger<uint64_t>();
+        return readInteger<uint64_t>(variant);
 #else
 #else
         readInteger<uint32_t>();
         readInteger<uint32_t>();
-        variant = readInteger<uint32_t>();
+        return readInteger<uint32_t>(variant);
 #endif
 #endif
-        return MsgPackError::Ok;
 
 
       case 0xd0:
       case 0xd0:
-        variant = readInteger<int8_t>();
-        return MsgPackError::Ok;
+        return readInteger<int8_t>(variant);
 
 
       case 0xd1:
       case 0xd1:
-        variant = readInteger<int16_t>();
-        return MsgPackError::Ok;
+        return readInteger<int16_t>(variant);
 
 
       case 0xd2:
       case 0xd2:
-        variant = readInteger<int32_t>();
-        return MsgPackError::Ok;
+        return readInteger<int32_t>(variant);
 
 
       case 0xd3:
       case 0xd3:
 #if ARDUINOJSON_USE_LONG_LONG || ARDUINOJSON_USE_INT64
 #if ARDUINOJSON_USE_LONG_LONG || ARDUINOJSON_USE_INT64
-        variant = readInteger<int64_t>();
+        return readInteger<int64_t>(variant);
 #else
 #else
-        readInteger<int32_t>();
-        variant = readInteger<int32_t>();
+        if (!skip(4)) return MsgPackError::IncompleteInput;
+        return readInteger<int32_t>(variant);
 #endif
 #endif
-        return MsgPackError::Ok;
 
 
       case 0xca:
       case 0xca:
-        variant = readFloat<float>();
-        return MsgPackError::Ok;
+        return readFloat<float>(variant);
 
 
       case 0xcb:
       case 0xcb:
-        variant = readDouble<double>();
-        return MsgPackError::Ok;
+        return readDouble<double>(variant);
 
 
-      case 0xd9: {
-        uint8_t n = readInteger<uint8_t>();
-        return readString(variant, n);
-      }
+      case 0xd9:
+        return readString<uint8_t>(variant);
 
 
-      case 0xda: {
-        uint16_t n = readInteger<uint16_t>();
-        return readString(variant, n);
-      }
+      case 0xda:
+        return readString<uint16_t>(variant);
 
 
-      case 0xdb: {
-        uint32_t n = readInteger<uint32_t>();
-        return readString(variant, n);
-      }
+      case 0xdb:
+        return readString<uint32_t>(variant);
 
 
       case 0xdc:
       case 0xdc:
-        return readArray(variant, readInteger<uint16_t>());
+        return readArray<uint16_t>(variant);
 
 
       case 0xdd:
       case 0xdd:
-        return readArray(variant, readInteger<uint32_t>());
+        return readArray<uint32_t>(variant);
 
 
       case 0xde:
       case 0xde:
-        return readObject(variant, readInteger<uint16_t>());
+        return readObject<uint16_t>(variant);
 
 
       case 0xdf:
       case 0xdf:
-        return readObject(variant, readInteger<uint32_t>());
+        return readObject<uint32_t>(variant);
 
 
       default:
       default:
         return MsgPackError::NotSupported;
         return MsgPackError::NotSupported;
@@ -148,65 +134,115 @@ class MsgPackDeserializer {
   // Prevent VS warning "assignment operator could not be generated"
   // Prevent VS warning "assignment operator could not be generated"
   MsgPackDeserializer &operator=(const MsgPackDeserializer &);
   MsgPackDeserializer &operator=(const MsgPackDeserializer &);
 
 
-  uint8_t readOne() {
-    char c = _reader.current();
-    _reader.move();
-    return static_cast<uint8_t>(c);
+  bool skip(uint8_t n) {
+    while (n--) {
+      if (_reader.ended()) return false;
+      _reader.read();
+    }
+    return true;
   }
   }
 
 
-  void read(uint8_t *p, size_t n) {
-    for (size_t i = 0; i < n; i++) p[i] = readOne();
+  bool readByte(uint8_t &value) {
+    if (_reader.ended()) return false;
+    value = static_cast<uint8_t>(_reader.read());
+    return true;
+  }
+
+  bool readBytes(uint8_t *p, size_t n) {
+    for (size_t i = 0; i < n; i++) {
+      if (!readByte(p[i])) return false;
+    }
+    return true;
   }
   }
 
 
   template <typename T>
   template <typename T>
-  void read(T &value) {
-    read(reinterpret_cast<uint8_t *>(&value), sizeof(value));
+  bool readBytes(T &value) {
+    return readBytes(reinterpret_cast<uint8_t *>(&value), sizeof(value));
   }
   }
 
 
   template <typename T>
   template <typename T>
   T readInteger() {
   T readInteger() {
     T value;
     T value;
-    read(value);
+    readBytes(value);
     fixEndianess(value);
     fixEndianess(value);
     return value;
     return value;
   }
   }
 
 
   template <typename T>
   template <typename T>
-  typename EnableIf<sizeof(T) == 4, T>::type readFloat() {
+  bool readInteger(T &value) {
+    if (!readBytes(value)) return false;
+    fixEndianess(value);
+    return true;
+  }
+
+  template <typename T>
+  MsgPackError readInteger(JsonVariant &variant) {
+    T value;
+    if (!readInteger(value)) return MsgPackError::IncompleteInput;
+    variant = value;
+    return MsgPackError::Ok;
+  }
+
+  template <typename T>
+  typename EnableIf<sizeof(T) == 4, MsgPackError>::type readFloat(
+      JsonVariant &variant) {
     T value;
     T value;
-    read(value);
+    if (!readBytes(value)) return MsgPackError::IncompleteInput;
     fixEndianess(value);
     fixEndianess(value);
-    return value;
+    variant = value;
+    return MsgPackError::Ok;
   }
   }
 
 
   template <typename T>
   template <typename T>
-  typename EnableIf<sizeof(T) == 8, T>::type readDouble() {
+  typename EnableIf<sizeof(T) == 8, MsgPackError>::type readDouble(
+      JsonVariant &variant) {
     T value;
     T value;
-    read(value);
+    if (!readBytes(value)) return MsgPackError::IncompleteInput;
     fixEndianess(value);
     fixEndianess(value);
-    return value;
+    variant = value;
+    return MsgPackError::Ok;
   }
   }
 
 
   template <typename T>
   template <typename T>
-  typename EnableIf<sizeof(T) == 4, T>::type readDouble() {
+  typename EnableIf<sizeof(T) == 4, MsgPackError>::type readDouble(
+      JsonVariant &variant) {
     uint8_t i[8];  // input is 8 bytes
     uint8_t i[8];  // input is 8 bytes
     T value;       // output is 4 bytes
     T value;       // output is 4 bytes
     uint8_t *o = reinterpret_cast<uint8_t *>(&value);
     uint8_t *o = reinterpret_cast<uint8_t *>(&value);
-    read(i, 8);
+    if (!readBytes(i, 8)) return MsgPackError::IncompleteInput;
     doubleToFloat(i, o);
     doubleToFloat(i, o);
     fixEndianess(value);
     fixEndianess(value);
-    return value;
+    variant = value;
+    return MsgPackError::Ok;
+  }
+
+  template <typename T>
+  MsgPackError readString(JsonVariant &variant) {
+    T size;
+    if (!readInteger(size)) return MsgPackError::IncompleteInput;
+    return readString(variant, size);
   }
   }
 
 
   MsgPackError readString(JsonVariant &variant, size_t n) {
   MsgPackError readString(JsonVariant &variant, size_t n) {
     typename RemoveReference<TWriter>::type::String str = _writer.startString();
     typename RemoveReference<TWriter>::type::String str = _writer.startString();
-    for (; n; --n) str.append(static_cast<char>(readOne()));
+    for (; n; --n) {
+      uint8_t c;
+      if (!readBytes(c)) return MsgPackError::IncompleteInput;
+      str.append(static_cast<char>(c));
+    }
     const char *s = str.c_str();
     const char *s = str.c_str();
     if (s == NULL) return MsgPackError::NoMemory;
     if (s == NULL) return MsgPackError::NoMemory;
     variant = s;
     variant = s;
     return MsgPackError::Ok;
     return MsgPackError::Ok;
   }
   }
 
 
+  template <typename TSize>
+  MsgPackError readArray(JsonVariant &variant) {
+    TSize size;
+    if (!readInteger(size)) return MsgPackError::IncompleteInput;
+    return readArray(variant, size);
+  }
+
   MsgPackError readArray(JsonVariant &variant, size_t n) {
   MsgPackError readArray(JsonVariant &variant, size_t n) {
     JsonArray *array = new (_buffer) JsonArray(_buffer);
     JsonArray *array = new (_buffer) JsonArray(_buffer);
     if (!array) return MsgPackError::NoMemory;
     if (!array) return MsgPackError::NoMemory;
@@ -227,6 +263,13 @@ class MsgPackDeserializer {
     return MsgPackError::Ok;
     return MsgPackError::Ok;
   }
   }
 
 
+  template <typename TSize>
+  MsgPackError readObject(JsonVariant &variant) {
+    TSize size;
+    if (!readInteger(size)) return MsgPackError::IncompleteInput;
+    return readObject(variant, size);
+  }
+
   MsgPackError readObject(JsonVariant &variant, size_t n) {
   MsgPackError readObject(JsonVariant &variant, size_t n) {
     JsonObject *object = new (_buffer) JsonObject(_buffer);
     JsonObject *object = new (_buffer) JsonObject(_buffer);
     if (!object) return MsgPackError::NoMemory;
     if (!object) return MsgPackError::NoMemory;
@@ -258,37 +301,11 @@ class MsgPackDeserializer {
   uint8_t _nestingLimit;
   uint8_t _nestingLimit;
 };
 };
 
 
-template <typename TJsonBuffer, typename TString, typename Enable = void>
-struct MsgPackDeserializerBuilder {
-  typedef typename StringTraits<TString>::Reader InputReader;
-  typedef MsgPackDeserializer<InputReader, TJsonBuffer &> TParser;
-
-  static TParser makeMsgPackDeserializer(TJsonBuffer *buffer, TString &json,
-                                         uint8_t nestingLimit) {
-    return TParser(buffer, InputReader(json), *buffer, nestingLimit);
-  }
-};
-
-template <typename TJsonBuffer, typename TChar>
-struct MsgPackDeserializerBuilder<
-    TJsonBuffer, TChar *, typename EnableIf<!IsConst<TChar>::value>::type> {
-  typedef typename StringTraits<TChar *>::Reader TReader;
-  typedef StringWriter<TChar> TWriter;
-  typedef MsgPackDeserializer<TReader, TWriter> TParser;
-
-  static TParser makeMsgPackDeserializer(TJsonBuffer *buffer, TChar *json,
-                                         uint8_t nestingLimit) {
-    return TParser(buffer, TReader(json), TWriter(json), nestingLimit);
-  }
-};
-
-template <typename TJsonBuffer, typename TString>
-inline typename MsgPackDeserializerBuilder<TJsonBuffer, TString>::TParser
-makeMsgPackDeserializer(TJsonBuffer *buffer, TString &json,
-                        uint8_t nestingLimit) {
-  return MsgPackDeserializerBuilder<
-      TJsonBuffer, TString>::makeMsgPackDeserializer(buffer, json,
-                                                     nestingLimit);
+template <typename TJsonBuffer, typename TReader, typename TWriter>
+MsgPackDeserializer<TReader, TWriter> makeMsgPackDeserializer(
+    TJsonBuffer *buffer, TReader reader, TWriter writer, uint8_t nestingLimit) {
+  return MsgPackDeserializer<TReader, TWriter>(buffer, reader, writer,
+                                               nestingLimit);
 }
 }
 }  // namespace Internals
 }  // namespace Internals
 }  // namespace ArduinoJson
 }  // namespace ArduinoJson

+ 7 - 1
src/ArduinoJson/MsgPack/MsgPackError.hpp

@@ -4,11 +4,15 @@
 
 
 #pragma once
 #pragma once
 
 
+#if ARDUINOJSON_ENABLE_STD_STREAM
+#include <ostream>
+#endif
+
 namespace ArduinoJson {
 namespace ArduinoJson {
 
 
 class MsgPackError {
 class MsgPackError {
  public:
  public:
-  enum Code { Ok, NotSupported, NoMemory, TooDeep };
+  enum Code { Ok, NotSupported, NoMemory, TooDeep, IncompleteInput };
 
 
   MsgPackError() {}
   MsgPackError() {}
 
 
@@ -44,6 +48,8 @@ class MsgPackError {
         return "NoMemory";
         return "NoMemory";
       case TooDeep:
       case TooDeep:
         return "TooDeep";
         return "TooDeep";
+      case IncompleteInput:
+        return "IncompleteInput";
       default:
       default:
         return "???";
         return "???";
     }
     }

+ 41 - 0
src/ArduinoJson/Reading/ArduinoStreamReader.hpp

@@ -0,0 +1,41 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#if ARDUINOJSON_ENABLE_ARDUINO_STREAM
+
+#include <Stream.h>
+
+namespace ArduinoJson {
+namespace Internals {
+
+struct ArduinoStreamReader {
+  Stream& _stream;
+  char _current;
+  bool _ended;
+
+ public:
+  explicit ArduinoStreamReader(Stream& stream)
+      : _stream(stream), _current(0), _ended(false) {}
+
+  char read() {
+    // don't use _stream.read() as it ignores the timeout
+    char c = 0;
+    _ended = _stream.readBytes(&c, 1) == 0;
+    return c;
+  }
+
+  bool ended() const {
+    return _ended;
+  }
+};
+
+inline ArduinoStreamReader makeReader(Stream& input) {
+  return ArduinoStreamReader(input);
+}
+}  // namespace Internals
+}  // namespace ArduinoJson
+
+#endif

+ 64 - 0
src/ArduinoJson/Reading/CharPointerReader.hpp

@@ -0,0 +1,64 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+namespace ArduinoJson {
+namespace Internals {
+
+template <typename TChar>
+class UnsafeCharPointerReader {
+  const TChar* _ptr;
+
+ public:
+  explicit UnsafeCharPointerReader(const TChar* ptr)
+      : _ptr(ptr ? ptr : reinterpret_cast<const TChar*>("")) {}
+
+  char read() {
+    return static_cast<char>(*_ptr++);
+  }
+
+  bool ended() const {
+    // we cannot know
+    return false;
+  }
+};
+
+template <typename TChar>
+class SafeCharPointerReader {
+  const TChar* _ptr;
+  const TChar* _end;
+
+ public:
+  explicit SafeCharPointerReader(const TChar* ptr, size_t len)
+      : _ptr(ptr ? ptr : reinterpret_cast<const TChar*>("")),
+        _end(_ptr + len) {}
+
+  char read() {
+    return static_cast<char>(*_ptr++);
+  }
+
+  bool ended() const {
+    return _ptr == _end;
+  }
+};
+
+template <typename TChar>
+inline UnsafeCharPointerReader<TChar> makeReader(TChar* input) {
+  return UnsafeCharPointerReader<TChar>(input);
+}
+
+template <typename TChar>
+inline SafeCharPointerReader<TChar> makeReader(TChar* input, size_t n) {
+  return SafeCharPointerReader<TChar>(input, n);
+}
+
+#if ARDUINOJSON_ENABLE_ARDUINO_STRING
+inline SafeCharPointerReader<char> makeReader(const String& input) {
+  return SafeCharPointerReader<char>(input.c_str(), input.length());
+}
+#endif
+
+}  // namespace Internals
+}  // namespace ArduinoJson

+ 56 - 0
src/ArduinoJson/Reading/FlashStringReader.hpp

@@ -0,0 +1,56 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#if ARDUINOJSON_ENABLE_PROGMEM
+
+namespace ArduinoJson {
+namespace Internals {
+class UnsafeFlashStringReader {
+  const char* _ptr;
+
+ public:
+  explicit UnsafeFlashStringReader(const __FlashStringHelper* ptr)
+      : _ptr(reinterpret_cast<const char*>(ptr)) {}
+
+  char read() {
+    return pgm_read_byte_near(_ptr++);
+  }
+
+  bool ended() const {
+    // this reader cannot detect the end
+    return false;
+  }
+};
+
+class SafeFlashStringReader {
+  const char* _ptr;
+  const char* _end;
+
+ public:
+  explicit SafeFlashStringReader(const __FlashStringHelper* ptr, size_t size)
+      : _ptr(reinterpret_cast<const char*>(ptr)), _end(_ptr + size) {}
+
+  char read() {
+    return pgm_read_byte_near(_ptr++);
+  }
+
+  bool ended() const {
+    return _ptr == _end;
+  }
+};
+
+inline UnsafeFlashStringReader makeReader(const __FlashStringHelper* input) {
+  return UnsafeFlashStringReader(input);
+}
+
+inline SafeFlashStringReader makeReader(const __FlashStringHelper* input,
+                                        size_t size) {
+  return SafeFlashStringReader(input, size);
+}
+}  // namespace Internals
+}  // namespace ArduinoJson
+
+#endif

+ 34 - 0
src/ArduinoJson/Reading/IteratorReader.hpp

@@ -0,0 +1,34 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+namespace ArduinoJson {
+namespace Internals {
+
+template <typename TIterator>
+class IteratorReader {
+  TIterator _ptr, _end;
+
+ public:
+  explicit IteratorReader(TIterator begin, TIterator end)
+      : _ptr(begin), _end(end) {}
+
+  bool ended() const {
+    return _ptr == _end;
+  }
+
+  char read() {
+    return char(*_ptr++);
+  }
+};
+
+template <typename TInput>
+inline IteratorReader<typename TInput::const_iterator> makeReader(
+    const TInput& input) {
+  return IteratorReader<typename TInput::const_iterator>(input.begin(),
+                                                         input.end());
+}
+}  // namespace Internals
+}  // namespace ArduinoJson

+ 11 - 0
src/ArduinoJson/Reading/Reader.hpp

@@ -0,0 +1,11 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "./ArduinoStreamReader.hpp"
+#include "./CharPointerReader.hpp"
+#include "./FlashStringReader.hpp"
+#include "./IteratorReader.hpp"
+#include "./StdStreamReader.hpp"

+ 40 - 0
src/ArduinoJson/Reading/StdStreamReader.hpp

@@ -0,0 +1,40 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#if ARDUINOJSON_ENABLE_STD_STREAM
+
+#include <istream>
+
+namespace ArduinoJson {
+namespace Internals {
+
+class StdStreamReader {
+  std::istream& _stream;
+  char _current;
+
+ public:
+  explicit StdStreamReader(std::istream& stream)
+      : _stream(stream), _current(0) {}
+
+  bool ended() const {
+    return _stream.eof();
+  }
+
+  char read() {
+    return static_cast<char>(_stream.get());
+  }
+
+ private:
+  StdStreamReader& operator=(const StdStreamReader&);  // Visual Studio C4512
+};
+
+inline StdStreamReader makeReader(std::istream& input) {
+  return StdStreamReader(input);
+}
+}  // namespace Internals
+}  // namespace ArduinoJson
+
+#endif

+ 0 - 61
src/ArduinoJson/Strings/ArduinoStream.hpp

@@ -1,61 +0,0 @@
-// ArduinoJson - arduinojson.org
-// Copyright Benoit Blanchon 2014-2018
-// MIT License
-
-#pragma once
-
-#if ARDUINOJSON_ENABLE_ARDUINO_STREAM
-
-#include <Stream.h>
-
-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 <typename TStream>
-struct StringTraits<
-    TStream,
-    // match any type that is derived from Stream:
-    typename EnableIf<
-        IsBaseOf<Stream, typename RemoveReference<TStream>::type>::value>::type>
-    : ArduinoStreamTraits {};
-}
-}
-
-#endif

+ 0 - 20
src/ArduinoJson/Strings/CharPointer.hpp

@@ -9,26 +9,6 @@ namespace Internals {
 
 
 template <typename TChar>
 template <typename TChar>
 struct CharPointerTraits {
 struct CharPointerTraits {
-  class Reader {
-    const TChar* _ptr;
-
-   public:
-    Reader(const TChar* ptr)
-        : _ptr(ptr ? ptr : reinterpret_cast<const TChar*>("")) {}
-
-    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) {
   static bool equals(const TChar* str, const char* expected) {
     return strcmp(reinterpret_cast<const char*>(str), expected) == 0;
     return strcmp(reinterpret_cast<const char*>(str), expected) == 0;
   }
   }

+ 0 - 20
src/ArduinoJson/Strings/FlashString.hpp

@@ -10,26 +10,6 @@ namespace ArduinoJson {
 namespace Internals {
 namespace Internals {
 template <>
 template <>
 struct StringTraits<const __FlashStringHelper*, void> {
 struct StringTraits<const __FlashStringHelper*, void> {
-  class Reader {
-    const char* _ptr;
-
-   public:
-    Reader(const __FlashStringHelper* ptr)
-        : _ptr(reinterpret_cast<const char*>(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) {
   static bool equals(const __FlashStringHelper* str, const char* expected) {
     return strcmp_P(expected, (const char*)str) == 0;
     return strcmp_P(expected, (const char*)str) == 0;
   }
   }

+ 0 - 60
src/ArduinoJson/Strings/StdStream.hpp

@@ -1,60 +0,0 @@
-// ArduinoJson - arduinojson.org
-// Copyright Benoit Blanchon 2014-2018
-// MIT License
-
-#pragma once
-
-#if ARDUINOJSON_ENABLE_STD_STREAM
-
-#include <istream>
-
-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<char>(_stream.get());
-    }
-  };
-
-  static const bool has_append = false;
-  static const bool has_equals = false;
-};
-
-template <typename TStream>
-struct StringTraits<
-    TStream,
-    // match any type that is derived from std::istream:
-    typename EnableIf<IsBaseOf<
-        std::istream, typename RemoveReference<TStream>::type>::value>::type>
-    : StdStreamTraits {};
-}
-}
-
-#endif

+ 0 - 4
src/ArduinoJson/Strings/StdString.hpp

@@ -35,10 +35,6 @@ struct StdStringTraits {
     return !str.c_str();
     return !str.c_str();
   }
   }
 
 
-  struct Reader : CharPointerTraits<char>::Reader {
-    Reader(const TString& str) : CharPointerTraits<char>::Reader(str.c_str()) {}
-  };
-
   static bool equals(const TString& str, const char* expected) {
   static bool equals(const TString& str, const char* expected) {
     return 0 == strcmp(str.c_str(), expected);
     return 0 == strcmp(str.c_str(), expected);
   }
   }

+ 0 - 2
src/ArduinoJson/Strings/StringTraits.hpp

@@ -29,8 +29,6 @@ struct StringTraits<TString&, void> : StringTraits<TString> {};
 }
 }
 }
 }
 
 
-#include "ArduinoStream.hpp"
 #include "CharPointer.hpp"
 #include "CharPointer.hpp"
 #include "FlashString.hpp"
 #include "FlashString.hpp"
-#include "StdStream.hpp"
 #include "StdString.hpp"
 #include "StdString.hpp"

+ 27 - 0
src/ArduinoJson/Writing/JsonBufferWriter.hpp

@@ -0,0 +1,27 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "./StringWriter.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+template <typename TJsonBuffer>
+class JsonBufferWriter {
+ public:
+  JsonBufferWriter(TJsonBuffer& jb) : _jb(&jb) {}
+
+  typedef typename TJsonBuffer::String String;
+
+  String startString() {
+    return _jb->startString();
+  }
+
+ private:
+  TJsonBuffer* _jb;
+};
+}  // namespace Internals
+}  // namespace ArduinoJson

+ 0 - 0
src/ArduinoJson/Strings/StringWriter.hpp → src/ArduinoJson/Writing/StringWriter.hpp


+ 44 - 0
src/ArduinoJson/Writing/Writer.hpp

@@ -0,0 +1,44 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "./JsonBufferWriter.hpp"
+#include "./StringWriter.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+template <typename TJsonBuffer, typename TInput, typename Enable = void>
+struct Writer {
+  typedef JsonBufferWriter<TJsonBuffer> type;
+
+  static type create(TJsonBuffer& jb, TInput&) {
+    return type(jb);
+  }
+};
+
+template <typename TJsonBuffer, typename TChar>
+struct Writer<TJsonBuffer, TChar*,
+              typename EnableIf<!IsConst<TChar>::value>::type> {
+  typedef StringWriter<TChar> type;
+
+  static type create(TJsonBuffer&, TChar* input) {
+    return type(input);
+  }
+};
+
+template <typename TJsonBuffer, typename TInput>
+typename Writer<TJsonBuffer, TInput>::type makeWriter(TJsonBuffer& jb,
+                                                      TInput& input) {
+  return Writer<TJsonBuffer, TInput>::create(jb, input);
+}
+
+template <typename TJsonBuffer, typename TChar>
+typename Writer<TJsonBuffer, TChar*>::type makeWriter(TJsonBuffer& jb,
+                                                      TChar* input) {
+  return Writer<TJsonBuffer, TChar*>::create(jb, input);
+}
+}  // namespace Internals
+}  // namespace ArduinoJson

+ 32 - 13
src/ArduinoJson/deserializeJson.hpp

@@ -5,34 +5,53 @@
 #pragma once
 #pragma once
 
 
 #include "Json/Deserialization/JsonDeserializer.hpp"
 #include "Json/Deserialization/JsonDeserializer.hpp"
+#include "Reading/Reader.hpp"
+#include "Writing/Writer.hpp"
 
 
 namespace ArduinoJson {
 namespace ArduinoJson {
-// JsonError deserializeJson(TDocument& doc, TString json);
+// JsonError deserializeJson(TDocument& doc, TString input);
 // TDocument = DynamicJsonDocument, StaticJsonDocument
 // TDocument = DynamicJsonDocument, StaticJsonDocument
 // TString = const std::string&, const String&
 // TString = const std::string&, const String&
 template <typename TDocument, typename TString>
 template <typename TDocument, typename TString>
 typename Internals::EnableIf<!Internals::IsArray<TString>::value,
 typename Internals::EnableIf<!Internals::IsArray<TString>::value,
                              JsonError>::type
                              JsonError>::type
-deserializeJson(TDocument &doc, const TString &json) {
-  return Internals::makeParser(&doc.buffer(), json, doc.nestingLimit)
+deserializeJson(TDocument &doc, const TString &input) {
+  using namespace Internals;
+  return makeJsonDeserializer(&doc.buffer(), makeReader(input),
+                              makeWriter(doc.buffer(), input), doc.nestingLimit)
       .parse(doc.template to<JsonVariant>());
       .parse(doc.template to<JsonVariant>());
 }
 }
 //
 //
-// JsonError deserializeJson(TDocument& doc, TString json);
+// JsonError deserializeJson(TDocument& doc, TChar* input);
 // TDocument = DynamicJsonDocument, StaticJsonDocument
 // TDocument = DynamicJsonDocument, StaticJsonDocument
-// TString = const char*, const char[N], const FlashStringHelper*
-template <typename TDocument, typename TString>
-JsonError deserializeJson(TDocument &doc, TString *json) {
-  return Internals::makeParser(&doc.buffer(), json, doc.nestingLimit)
+// TChar* = char*, const char*, const FlashStringHelper*
+template <typename TDocument, typename TChar>
+JsonError deserializeJson(TDocument &doc, TChar *input) {
+  using namespace Internals;
+  return makeJsonDeserializer(&doc.buffer(), makeReader(input),
+                              makeWriter(doc.buffer(), input), doc.nestingLimit)
       .parse(doc.template to<JsonVariant>());
       .parse(doc.template to<JsonVariant>());
 }
 }
 //
 //
-// JsonError deserializeJson(TDocument& doc, TString json);
+// JsonError deserializeJson(TDocument& doc, TChar* input, size_t inputSize);
 // TDocument = DynamicJsonDocument, StaticJsonDocument
 // TDocument = DynamicJsonDocument, StaticJsonDocument
-// TString = std::istream&, Stream&
-template <typename TDocument, typename TString>
-JsonError deserializeJson(TDocument &doc, TString &json) {
-  return Internals::makeParser(&doc.buffer(), json, doc.nestingLimit)
+// TChar* = char*, const char*, const FlashStringHelper*
+template <typename TDocument, typename TChar>
+JsonError deserializeJson(TDocument &doc, TChar *input, size_t inputSize) {
+  using namespace Internals;
+  return makeJsonDeserializer(&doc.buffer(), makeReader(input, inputSize),
+                              makeWriter(doc.buffer(), input), doc.nestingLimit)
+      .parse(doc.template to<JsonVariant>());
+}
+//
+// JsonError deserializeJson(TDocument& doc, TStream input);
+// TDocument = DynamicJsonDocument, StaticJsonDocument
+// TStream = std::istream&, Stream&
+template <typename TDocument, typename TStream>
+JsonError deserializeJson(TDocument &doc, TStream &input) {
+  using namespace Internals;
+  return makeJsonDeserializer(&doc.buffer(), makeReader(input),
+                              makeWriter(doc.buffer(), input), doc.nestingLimit)
       .parse(doc.template to<JsonVariant>());
       .parse(doc.template to<JsonVariant>());
 }
 }
 }  // namespace ArduinoJson
 }  // namespace ArduinoJson

+ 41 - 19
src/ArduinoJson/deserializeMsgPack.hpp

@@ -5,37 +5,59 @@
 #pragma once
 #pragma once
 
 
 #include "MsgPack/MsgPackDeserializer.hpp"
 #include "MsgPack/MsgPackDeserializer.hpp"
+#include "Reading/Reader.hpp"
+#include "Writing/Writer.hpp"
 
 
 namespace ArduinoJson {
 namespace ArduinoJson {
-// MsgPackError deserializeMsgPack(TDocument& doc, TString json);
-// TDocument = DynamicJsonArray | StaticJsonArray
+// MsgPackError deserializeMsgPack(TDocument& doc, TString input);
+// TDocument = DynamicJsonDocument, StaticJsonDocument
 // TString = const std::string&, const String&
 // TString = const std::string&, const String&
 template <typename TDocument, typename TString>
 template <typename TDocument, typename TString>
 typename Internals::EnableIf<!Internals::IsArray<TString>::value,
 typename Internals::EnableIf<!Internals::IsArray<TString>::value,
                              MsgPackError>::type
                              MsgPackError>::type
-deserializeMsgPack(TDocument &doc, const TString &json) {
-  return Internals::makeMsgPackDeserializer(&doc.buffer(), json,
-                                            doc.nestingLimit)
+deserializeMsgPack(TDocument &doc, const TString &input) {
+  using namespace Internals;
+  return makeMsgPackDeserializer(&doc.buffer(), makeReader(input),
+                                 makeWriter(doc.buffer(), input),
+                                 doc.nestingLimit)
       .parse(doc.template to<JsonVariant>());
       .parse(doc.template to<JsonVariant>());
 }
 }
 //
 //
-// MsgPackError deserializeMsgPack(TDocument& doc, TString json);
-// TDocument = DynamicJsonArray | StaticJsonArray
-// TString = const char*, const char[N], const FlashStringHelper*
-template <typename TDocument, typename TString>
-MsgPackError deserializeMsgPack(TDocument &doc, TString *json) {
-  return Internals::makeMsgPackDeserializer(&doc.buffer(), json,
-                                            doc.nestingLimit)
+// MsgPackError deserializeMsgPack(TDocument& doc, TChar* input);
+// TDocument = DynamicJsonDocument, StaticJsonDocument
+// TChar* = char*, const char*, const FlashStringHelper*
+template <typename TDocument, typename TChar>
+MsgPackError deserializeMsgPack(TDocument &doc, TChar *input) {
+  using namespace Internals;
+  return makeMsgPackDeserializer(&doc.buffer(), makeReader(input),
+                                 makeWriter(doc.buffer(), input),
+                                 doc.nestingLimit)
       .parse(doc.template to<JsonVariant>());
       .parse(doc.template to<JsonVariant>());
 }
 }
 //
 //
-// MsgPackError deserializeMsgPack(TDocument& doc, TString json);
-// TDocument = DynamicJsonArray | StaticJsonArray
-// TString = std::istream&, Stream&
-template <typename TDocument, typename TString>
-MsgPackError deserializeMsgPack(TDocument &doc, TString &json) {
-  return Internals::makeMsgPackDeserializer(&doc.buffer(), json,
-                                            doc.nestingLimit)
+// MsgPackError deserializeMsgPack(TDocument& doc, TChar* input, size_t
+// inputSize);
+// TDocument = DynamicJsonDocument, StaticJsonDocument
+// TChar* = char*, const char*, const FlashStringHelper*
+template <typename TDocument, typename TChar>
+MsgPackError deserializeMsgPack(TDocument &doc, TChar *input,
+                                size_t inputSize) {
+  using namespace Internals;
+  return makeMsgPackDeserializer(&doc.buffer(), makeReader(input, inputSize),
+                                 makeWriter(doc.buffer(), input),
+                                 doc.nestingLimit)
+      .parse(doc.template to<JsonVariant>());
+}
+//
+// MsgPackError deserializeMsgPack(TDocument& doc, TStream input);
+// TDocument = DynamicJsonDocument, StaticJsonDocument
+// TStream = std::istream&, Stream&
+template <typename TDocument, typename TStream>
+MsgPackError deserializeMsgPack(TDocument &doc, TStream &input) {
+  using namespace Internals;
+  return makeMsgPackDeserializer(&doc.buffer(), makeReader(input),
+                                 makeWriter(doc.buffer(), input),
+                                 doc.nestingLimit)
       .parse(doc.template to<JsonVariant>());
       .parse(doc.template to<JsonVariant>());
 }
 }
 }  // namespace ArduinoJson
 }  // namespace ArduinoJson

+ 1 - 0
test/JsonArray/CMakeLists.txt

@@ -12,6 +12,7 @@ add_executable(JsonArrayTests
 	remove.cpp
 	remove.cpp
 	set.cpp
 	set.cpp
 	size.cpp
 	size.cpp
+	std_string.cpp
 	subscript.cpp
 	subscript.cpp
 )
 )
 
 

+ 39 - 0
test/JsonArray/std_string.cpp

@@ -0,0 +1,39 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+static void eraseString(std::string &str) {
+  char *p = const_cast<char *>(str.c_str());
+  while (*p) *p++ = '*';
+}
+
+TEST_CASE("std::string") {
+  DynamicJsonDocument doc;
+  JsonArray &array = doc.to<JsonArray>();
+
+  SECTION("add()") {
+    std::string value("hello");
+    array.add(value);
+    eraseString(value);
+    REQUIRE(std::string("hello") == array[0]);
+  }
+
+  SECTION("set()") {
+    std::string value("world");
+    array.add("hello");
+    array.set(0, value);
+    eraseString(value);
+    REQUIRE(std::string("world") == array[0]);
+  }
+
+  SECTION("operator[]") {
+    std::string value("world");
+    array.add("hello");
+    array[0] = value;
+    eraseString(value);
+    REQUIRE(std::string("world") == array[0]);
+  }
+}

+ 2 - 0
test/JsonDeserializer/CMakeLists.txt

@@ -10,6 +10,8 @@ add_executable(JsonDeserializerTests
 	deserializeJsonValue.cpp
 	deserializeJsonValue.cpp
 	JsonError.cpp
 	JsonError.cpp
 	nestingLimit.cpp
 	nestingLimit.cpp
+	std_istream.cpp
+	std_string.cpp
 )
 )
 
 
 target_link_libraries(JsonDeserializerTests catch)
 target_link_libraries(JsonDeserializerTests catch)

+ 2 - 0
test/JsonDeserializer/JsonError.cpp

@@ -25,6 +25,7 @@ TEST_CASE("JsonError") {
     TEST_STRINGIFICATION(TooDeep);
     TEST_STRINGIFICATION(TooDeep);
     TEST_STRINGIFICATION(NoMemory);
     TEST_STRINGIFICATION(NoMemory);
     TEST_STRINGIFICATION(InvalidInput);
     TEST_STRINGIFICATION(InvalidInput);
+    TEST_STRINGIFICATION(IncompleteInput);
   }
   }
 
 
   SECTION("as boolean") {
   SECTION("as boolean") {
@@ -32,6 +33,7 @@ TEST_CASE("JsonError") {
     TEST_BOOLIFICATION(TooDeep, true);
     TEST_BOOLIFICATION(TooDeep, true);
     TEST_BOOLIFICATION(NoMemory, true);
     TEST_BOOLIFICATION(NoMemory, true);
     TEST_BOOLIFICATION(InvalidInput, true);
     TEST_BOOLIFICATION(InvalidInput, true);
+    TEST_BOOLIFICATION(IncompleteInput, true);
   }
   }
 
 
   SECTION("ostream") {
   SECTION("ostream") {

+ 38 - 24
test/JsonDeserializer/deserializeJsonArray.cpp

@@ -164,13 +164,13 @@ TEST_CASE("deserialize JSON array") {
     SECTION("Closing single quotes missing") {
     SECTION("Closing single quotes missing") {
       JsonError err = deserializeJson(doc, "[\"]");
       JsonError err = deserializeJson(doc, "[\"]");
 
 
-      REQUIRE(err == JsonError::InvalidInput);
+      REQUIRE(err == JsonError::IncompleteInput);
     }
     }
 
 
     SECTION("Closing double quotes missing") {
     SECTION("Closing double quotes missing") {
       JsonError err = deserializeJson(doc, "[\']");
       JsonError err = deserializeJson(doc, "[\']");
 
 
-      REQUIRE(err == JsonError::InvalidInput);
+      REQUIRE(err == JsonError::IncompleteInput);
     }
     }
   }
   }
 
 
@@ -233,21 +233,21 @@ TEST_CASE("deserialize JSON array") {
 
 
     SECTION("/*/") {
     SECTION("/*/") {
       JsonError err = deserializeJson(doc, "[/*/\n]");
       JsonError err = deserializeJson(doc, "[/*/\n]");
-      REQUIRE(err == JsonError::InvalidInput);
+      REQUIRE(err == JsonError::IncompleteInput);
     }
     }
 
 
     SECTION("Unfinished comment") {
     SECTION("Unfinished comment") {
       JsonError err = deserializeJson(doc, "[/*COMMENT]");
       JsonError err = deserializeJson(doc, "[/*COMMENT]");
-      REQUIRE(err == JsonError::InvalidInput);
+      REQUIRE(err == JsonError::IncompleteInput);
     }
     }
 
 
     SECTION("Final slash missing") {
     SECTION("Final slash missing") {
       JsonError err = deserializeJson(doc, "[/*COMMENT*]");
       JsonError err = deserializeJson(doc, "[/*COMMENT*]");
-      REQUIRE(err == JsonError::InvalidInput);
+      REQUIRE(err == JsonError::IncompleteInput);
     }
     }
   }
   }
 
 
-  SECTION("Line comments") {
+  SECTION("Trailing comments") {
     SECTION("Before opening bracket") {
     SECTION("Before opening bracket") {
       JsonError err = deserializeJson(doc, "//COMMENT\n\t[\"hello\"]");
       JsonError err = deserializeJson(doc, "//COMMENT\n\t[\"hello\"]");
       JsonArray& arr = doc.as<JsonArray>();
       JsonArray& arr = doc.as<JsonArray>();
@@ -311,39 +311,53 @@ TEST_CASE("deserialize JSON array") {
 
 
     SECTION("End document with comment") {
     SECTION("End document with comment") {
       JsonError err = deserializeJson(doc, "[//COMMENT");
       JsonError err = deserializeJson(doc, "[//COMMENT");
-      REQUIRE(err == JsonError::InvalidInput);
+      REQUIRE(err == JsonError::IncompleteInput);
     }
     }
   }
   }
 
 
-  SECTION("Misc") {
-    SECTION("Garbage") {
-      JsonError err = deserializeJson(doc, "%*$£¤");
+  SECTION("Premature null-terminator") {
+    SECTION("After opening bracket") {
+      JsonError err = deserializeJson(doc, "[");
 
 
-      REQUIRE(err == JsonError::InvalidInput);
+      REQUIRE(err == JsonError::IncompleteInput);
     }
     }
 
 
-    SECTION("The opening bracket is missing") {
-      JsonError err = deserializeJson(doc, "]");
+    SECTION("After value") {
+      JsonError err = deserializeJson(doc, "[1");
 
 
-      REQUIRE(err == JsonError::InvalidInput);
+      REQUIRE(err == JsonError::IncompleteInput);
     }
     }
 
 
-    SECTION("The closing bracket is missing") {
-      JsonError err = deserializeJson(doc, "[");
+    SECTION("After comma") {
+      JsonError err = deserializeJson(doc, "[1,");
 
 
-      REQUIRE(err == JsonError::InvalidInput);
+      REQUIRE(err == JsonError::IncompleteInput);
     }
     }
+  }
 
 
-    SECTION("Escape sequences") {
-      JsonError err =
-          deserializeJson(doc, "[\"1\\\"2\\\\3\\/4\\b5\\f6\\n7\\r8\\t9\"]");
-      JsonArray& arr = doc.as<JsonArray>();
+  SECTION("Premature end of input") {
+    const char* input = "[1,2]";
 
 
-      REQUIRE(err == JsonError::Ok);
-      REQUIRE(1 == arr.size());
-      REQUIRE(arr[0] == "1\"2\\3/4\b5\f6\n7\r8\t9");
+    SECTION("After opening bracket") {
+      JsonError err = deserializeJson(doc, input, 1);
+
+      REQUIRE(err == JsonError::IncompleteInput);
+    }
+
+    SECTION("After value") {
+      JsonError err = deserializeJson(doc, input, 2);
+
+      REQUIRE(err == JsonError::IncompleteInput);
     }
     }
 
 
+    SECTION("After comma") {
+      JsonError err = deserializeJson(doc, input, 3);
+
+      REQUIRE(err == JsonError::IncompleteInput);
+    }
+  }
+
+  SECTION("Misc") {
     SECTION("Nested objects") {
     SECTION("Nested objects") {
       char jsonString[] =
       char jsonString[] =
           " [ { \"a\" : 1 , \"b\" : 2 } , { \"c\" : 3 , \"d\" : 4 } ] ";
           " [ { \"a\" : 1 , \"b\" : 2 } , { \"c\" : 3 , \"d\" : 4 } ] ";

+ 2 - 18
test/JsonDeserializer/deserializeJsonArrayStatic.cpp

@@ -21,7 +21,7 @@ TEST_CASE("deserialize JSON array with a StaticJsonDocument") {
 
 
     JsonError err = deserializeJson(doc, input);
     JsonError err = deserializeJson(doc, input);
 
 
-    REQUIRE(err != JsonError::Ok);
+    REQUIRE(err == JsonError::NoMemory);
   }
   }
 
 
   SECTION("BufferOfTheRightSizeForArrayWithOneValue") {
   SECTION("BufferOfTheRightSizeForArrayWithOneValue") {
@@ -39,7 +39,7 @@ TEST_CASE("deserialize JSON array with a StaticJsonDocument") {
 
 
     JsonError err = deserializeJson(doc, input);
     JsonError err = deserializeJson(doc, input);
 
 
-    REQUIRE(err != JsonError::Ok);
+    REQUIRE(err == JsonError::NoMemory);
   }
   }
 
 
   SECTION("BufferOfTheRightSizeForArrayWithNestedObject") {
   SECTION("BufferOfTheRightSizeForArrayWithNestedObject") {
@@ -51,22 +51,6 @@ TEST_CASE("deserialize JSON array with a StaticJsonDocument") {
     REQUIRE(err == JsonError::Ok);
     REQUIRE(err == JsonError::Ok);
   }
   }
 
 
-  SECTION("CharPtrNull") {
-    StaticJsonDocument<100> doc;
-
-    JsonError err = deserializeJson(doc, static_cast<char*>(0));
-
-    REQUIRE(err != JsonError::Ok);
-  }
-
-  SECTION("ConstCharPtrNull") {
-    StaticJsonDocument<100> doc;
-
-    JsonError err = deserializeJson(doc, static_cast<const char*>(0));
-
-    REQUIRE(err != JsonError::Ok);
-  }
-
   SECTION("CopyStringNotSpaces") {
   SECTION("CopyStringNotSpaces") {
     StaticJsonDocument<100> doc;
     StaticJsonDocument<100> doc;
 
 

+ 220 - 6
test/JsonDeserializer/deserializeJsonObject.cpp

@@ -212,19 +212,39 @@ TEST_CASE("deserialize JSON object") {
     }
     }
   }
   }
 
 
-  SECTION("Misc") {
-    SECTION("The opening brace is missing") {
-      JsonError err = deserializeJson(doc, "}");
+  SECTION("Premature null terminator") {
+    SECTION("After opening brace") {
+      JsonError err = deserializeJson(doc, "{");
 
 
-      REQUIRE(err == JsonError::InvalidInput);
+      REQUIRE(err == JsonError::IncompleteInput);
+    }
+
+    SECTION("After key") {
+      JsonError err = deserializeJson(doc, "{\"hello\"");
+
+      REQUIRE(err == JsonError::IncompleteInput);
     }
     }
 
 
-    SECTION("The closing brace is missing") {
+    SECTION("After colon") {
+      JsonError err = deserializeJson(doc, "{\"hello\":");
+
+      REQUIRE(err == JsonError::IncompleteInput);
+    }
+
+    SECTION("After value") {
       JsonError err = deserializeJson(doc, "{\"hello\":\"world\"");
       JsonError err = deserializeJson(doc, "{\"hello\":\"world\"");
 
 
-      REQUIRE(err == JsonError::InvalidInput);
+      REQUIRE(err == JsonError::IncompleteInput);
     }
     }
 
 
+    SECTION("After comma") {
+      JsonError err = deserializeJson(doc, "{\"hello\":\"world\",");
+
+      REQUIRE(err == JsonError::IncompleteInput);
+    }
+  }
+
+  SECTION("Misc") {
     SECTION("A quoted key without value") {
     SECTION("A quoted key without value") {
       JsonError err = deserializeJson(doc, "{\"key\"}");
       JsonError err = deserializeJson(doc, "{\"key\"}");
 
 
@@ -250,6 +270,200 @@ TEST_CASE("deserialize JSON object") {
     }
     }
   }
   }
 
 
+  SECTION("Block comments") {
+    SECTION("Before opening brace") {
+      JsonError err = deserializeJson(doc, "/*COMMENT*/ {\"hello\":\"world\"}");
+      JsonObject& obj = doc.as<JsonObject>();
+
+      REQUIRE(err == JsonError::Ok);
+      REQUIRE(obj["hello"] == "world");
+    }
+
+    SECTION("After opening brace") {
+      JsonError err = deserializeJson(doc, "{/*COMMENT*/\"hello\":\"world\"}");
+      JsonObject& obj = doc.as<JsonObject>();
+
+      REQUIRE(err == JsonError::Ok);
+      REQUIRE(obj["hello"] == "world");
+    }
+
+    SECTION("Before colon") {
+      JsonError err = deserializeJson(doc, "{\"hello\"/*COMMENT*/:\"world\"}");
+      JsonObject& obj = doc.as<JsonObject>();
+
+      REQUIRE(err == JsonError::Ok);
+      REQUIRE(obj["hello"] == "world");
+    }
+
+    SECTION("After colon") {
+      JsonError err = deserializeJson(doc, "{\"hello\":/*COMMENT*/\"world\"}");
+      JsonObject& obj = doc.as<JsonObject>();
+
+      REQUIRE(err == JsonError::Ok);
+      REQUIRE(obj["hello"] == "world");
+    }
+
+    SECTION("Before closing brace") {
+      JsonError err = deserializeJson(doc, "{\"hello\":\"world\"/*COMMENT*/}");
+      JsonObject& obj = doc.as<JsonObject>();
+
+      REQUIRE(err == JsonError::Ok);
+      REQUIRE(obj["hello"] == "world");
+    }
+
+    SECTION("After closing brace") {
+      JsonError err = deserializeJson(doc, "{\"hello\":\"world\"}/*COMMENT*/");
+      JsonObject& obj = doc.as<JsonObject>();
+
+      REQUIRE(err == JsonError::Ok);
+      REQUIRE(obj["hello"] == "world");
+    }
+
+    SECTION("Before comma") {
+      JsonError err = deserializeJson(
+          doc, "{\"hello\":\"world\"/*COMMENT*/,\"answer\":42}");
+      JsonObject& obj = doc.as<JsonObject>();
+
+      REQUIRE(err == JsonError::Ok);
+      REQUIRE(obj["hello"] == "world");
+      REQUIRE(obj["answer"] == 42);
+    }
+
+    SECTION("After comma") {
+      JsonError err = deserializeJson(
+          doc, "{\"hello\":\"world\",/*COMMENT*/\"answer\":42}");
+      JsonObject& obj = doc.as<JsonObject>();
+
+      REQUIRE(err == JsonError::Ok);
+      REQUIRE(obj["hello"] == "world");
+      REQUIRE(obj["answer"] == 42);
+    }
+  }
+
+  SECTION("Trailing comments") {
+    SECTION("Before opening brace") {
+      JsonError err = deserializeJson(doc, "//COMMENT\n {\"hello\":\"world\"}");
+      JsonObject& obj = doc.as<JsonObject>();
+
+      REQUIRE(err == JsonError::Ok);
+      REQUIRE(obj["hello"] == "world");
+    }
+
+    SECTION("After opening brace") {
+      JsonError err = deserializeJson(doc, "{//COMMENT\n\"hello\":\"world\"}");
+      JsonObject& obj = doc.as<JsonObject>();
+
+      REQUIRE(err == JsonError::Ok);
+      REQUIRE(obj["hello"] == "world");
+    }
+
+    SECTION("Before colon") {
+      JsonError err = deserializeJson(doc, "{\"hello\"//COMMENT\n:\"world\"}");
+      JsonObject& obj = doc.as<JsonObject>();
+
+      REQUIRE(err == JsonError::Ok);
+      REQUIRE(obj["hello"] == "world");
+    }
+
+    SECTION("After colon") {
+      JsonError err = deserializeJson(doc, "{\"hello\"://COMMENT\n\"world\"}");
+      JsonObject& obj = doc.as<JsonObject>();
+
+      REQUIRE(err == JsonError::Ok);
+      REQUIRE(obj["hello"] == "world");
+    }
+
+    SECTION("Before closing brace") {
+      JsonError err = deserializeJson(doc, "{\"hello\":\"world\"//COMMENT\n}");
+      JsonObject& obj = doc.as<JsonObject>();
+
+      REQUIRE(err == JsonError::Ok);
+      REQUIRE(obj["hello"] == "world");
+    }
+
+    SECTION("After closing brace") {
+      JsonError err = deserializeJson(doc, "{\"hello\":\"world\"}//COMMENT\n");
+      JsonObject& obj = doc.as<JsonObject>();
+
+      REQUIRE(err == JsonError::Ok);
+      REQUIRE(obj["hello"] == "world");
+    }
+
+    SECTION("Before comma") {
+      JsonError err = deserializeJson(
+          doc, "{\"hello\":\"world\"//COMMENT\n,\"answer\":42}");
+      JsonObject& obj = doc.as<JsonObject>();
+
+      REQUIRE(err == JsonError::Ok);
+      REQUIRE(obj["hello"] == "world");
+      REQUIRE(obj["answer"] == 42);
+    }
+
+    SECTION("After comma") {
+      JsonError err = deserializeJson(
+          doc, "{\"hello\":\"world\",//COMMENT\n\"answer\":42}");
+      JsonObject& obj = doc.as<JsonObject>();
+
+      REQUIRE(err == JsonError::Ok);
+      REQUIRE(obj["hello"] == "world");
+      REQUIRE(obj["answer"] == 42);
+    }
+  }
+
+  SECTION("Dangling slash") {
+    SECTION("Before opening brace") {
+      JsonError err = deserializeJson(doc, "/{\"hello\":\"world\"}");
+
+      REQUIRE(err == JsonError::InvalidInput);
+    }
+
+    SECTION("After opening brace") {
+      JsonError err = deserializeJson(doc, "{/\"hello\":\"world\"}");
+
+      REQUIRE(err == JsonError::InvalidInput);
+    }
+
+    SECTION("Before colon") {
+      JsonError err = deserializeJson(doc, "{\"hello\"/:\"world\"}");
+
+      REQUIRE(err == JsonError::InvalidInput);
+    }
+
+    SECTION("After colon") {
+      JsonError err = deserializeJson(doc, "{\"hello\":/\"world\"}");
+
+      REQUIRE(err == JsonError::InvalidInput);
+    }
+
+    SECTION("Before closing brace") {
+      JsonError err = deserializeJson(doc, "{\"hello\":\"world\"/}");
+
+      REQUIRE(err == JsonError::InvalidInput);
+    }
+
+    SECTION("After closing brace") {
+      JsonError err = deserializeJson(doc, "{\"hello\":\"world\"}/");
+      JsonObject& obj = doc.as<JsonObject>();
+
+      REQUIRE(err == JsonError::Ok);
+      REQUIRE(obj["hello"] == "world");
+    }
+
+    SECTION("Before comma") {
+      JsonError err =
+          deserializeJson(doc, "{\"hello\":\"world\"/,\"answer\":42}");
+
+      REQUIRE(err == JsonError::InvalidInput);
+    }
+
+    SECTION("After comma") {
+      JsonError err =
+          deserializeJson(doc, "{\"hello\":\"world\",/\"answer\":42}");
+
+      REQUIRE(err == JsonError::InvalidInput);
+    }
+  }
+
   SECTION("Should clear the JsonObject") {
   SECTION("Should clear the JsonObject") {
     deserializeJson(doc, "{\"hello\":\"world\"}");
     deserializeJson(doc, "{\"hello\":\"world\"}");
     deserializeJson(doc, "{}");
     deserializeJson(doc, "{}");

+ 2 - 18
test/JsonDeserializer/deserializeJsonObjectStatic.cpp

@@ -21,7 +21,7 @@ TEST_CASE("deserialize JSON object with StaticJsonDocument") {
 
 
     JsonError err = deserializeJson(doc, input);
     JsonError err = deserializeJson(doc, input);
 
 
-    REQUIRE(err != JsonError::Ok);
+    REQUIRE(err == JsonError::NoMemory);
   }
   }
 
 
   SECTION("BufferOfTheRightSizeForObjectWithOneValue") {
   SECTION("BufferOfTheRightSizeForObjectWithOneValue") {
@@ -39,7 +39,7 @@ TEST_CASE("deserialize JSON object with StaticJsonDocument") {
 
 
     JsonError err = deserializeJson(doc, input);
     JsonError err = deserializeJson(doc, input);
 
 
-    REQUIRE(err != JsonError::Ok);
+    REQUIRE(err == JsonError::NoMemory);
   }
   }
 
 
   SECTION("BufferOfTheRightSizeForObjectWithNestedObject") {
   SECTION("BufferOfTheRightSizeForObjectWithNestedObject") {
@@ -51,22 +51,6 @@ TEST_CASE("deserialize JSON object with StaticJsonDocument") {
     REQUIRE(err == JsonError::Ok);
     REQUIRE(err == JsonError::Ok);
   }
   }
 
 
-  SECTION("CharPtrNull") {
-    StaticJsonDocument<100> doc;
-
-    JsonError err = deserializeJson(doc, static_cast<char*>(0));
-
-    REQUIRE(err != JsonError::Ok);
-  }
-
-  SECTION("ConstCharPtrNull") {
-    StaticJsonDocument<100> doc;
-
-    JsonError err = deserializeJson(doc, static_cast<const char*>(0));
-
-    REQUIRE(err != JsonError::Ok);
-  }
-
   SECTION("Should clear the JsonObject") {
   SECTION("Should clear the JsonObject") {
     StaticJsonDocument<JSON_OBJECT_SIZE(1)> doc;
     StaticJsonDocument<JSON_OBJECT_SIZE(1)> doc;
     char input[] = "{\"hello\":\"world\"}";
     char input[] = "{\"hello\":\"world\"}";

+ 92 - 23
test/JsonDeserializer/deserializeJsonValue.cpp

@@ -10,18 +10,16 @@ using namespace Catch::Matchers;
 TEST_CASE("deserializeJson(DynamicJsonDocument&)") {
 TEST_CASE("deserializeJson(DynamicJsonDocument&)") {
   DynamicJsonDocument doc;
   DynamicJsonDocument doc;
 
 
-  SECTION("EmptyObject") {
-    JsonError err = deserializeJson(doc, "{}");
+  SECTION("null char*") {
+    JsonError err = deserializeJson(doc, static_cast<char*>(0));
 
 
-    REQUIRE(err == JsonError::Ok);
-    REQUIRE(doc.is<JsonObject>());
+    REQUIRE(err != JsonError::Ok);
   }
   }
 
 
-  SECTION("EmptyArray") {
-    JsonError err = deserializeJson(doc, "[]");
+  SECTION("null const char*") {
+    JsonError err = deserializeJson(doc, static_cast<const char*>(0));
 
 
-    REQUIRE(err == JsonError::Ok);
-    REQUIRE(doc.is<JsonArray>());
+    REQUIRE(err != JsonError::Ok);
   }
   }
 
 
   SECTION("Integer") {
   SECTION("Integer") {
@@ -58,6 +56,14 @@ TEST_CASE("deserializeJson(DynamicJsonDocument&)") {
     REQUIRE_THAT(doc.as<char*>(), Equals("hello world"));
     REQUIRE_THAT(doc.as<char*>(), Equals("hello world"));
   }
   }
 
 
+  SECTION("Escape sequences") {
+    JsonError err =
+        deserializeJson(doc, "\"1\\\"2\\\\3\\/4\\b5\\f6\\n7\\r8\\t9\"");
+
+    REQUIRE(err == JsonError::Ok);
+    REQUIRE(doc.as<std::string>() == "1\"2\\3/4\b5\f6\n7\r8\t9");
+  }
+
   SECTION("True") {
   SECTION("True") {
     JsonError err = deserializeJson(doc, "true");
     JsonError err = deserializeJson(doc, "true");
 
 
@@ -74,30 +80,93 @@ TEST_CASE("deserializeJson(DynamicJsonDocument&)") {
     REQUIRE(doc.as<bool>() == false);
     REQUIRE(doc.as<bool>() == false);
   }
   }
 
 
-  SECTION("OpenBrace") {
-    JsonError err = deserializeJson(doc, "{");
+  SECTION("Should clear the JsonVariant") {
+    deserializeJson(doc, "[1,2,3]");
+    deserializeJson(doc, "{}");
+
+    REQUIRE(doc.is<JsonObject>());
+    REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(0));
+  }
+
+  SECTION("Empty input") {
+    JsonError err = deserializeJson(doc, "");
 
 
-    REQUIRE(err != JsonError::Ok);
+    REQUIRE(err == JsonError::IncompleteInput);
   }
   }
 
 
-  SECTION("Incomplete string") {
-    JsonError err = deserializeJson(doc, "\"hello");
+  SECTION("Just a trailing comment") {
+    JsonError err = deserializeJson(doc, "// comment");
 
 
-    REQUIRE(err == JsonError::Ok);
-    REQUIRE(doc.is<char*>());
-    REQUIRE_THAT(doc.as<char*>(), Equals("hello"));
+    REQUIRE(err == JsonError::IncompleteInput);
+  }
+
+  SECTION("Just a block comment") {
+    JsonError err = deserializeJson(doc, "/*comment*/");
+
+    REQUIRE(err == JsonError::IncompleteInput);
+  }
+
+  SECTION("Just a slash") {
+    JsonError err = deserializeJson(doc, "/");
+
+    REQUIRE(err == JsonError::InvalidInput);
   }
   }
 
 
-  SECTION("Unterminated escape sequence") {
-    JsonError err = deserializeJson(doc, "\"\\\0\"");
+  SECTION("Garbage") {
+    JsonError err = deserializeJson(doc, "%*$£¤");
+
     REQUIRE(err == JsonError::InvalidInput);
     REQUIRE(err == JsonError::InvalidInput);
   }
   }
 
 
-  SECTION("Should clear the JsonVariant") {
-    deserializeJson(doc, "[1,2,3]");
-    deserializeJson(doc, "{}");
+  SECTION("Premature null-terminator") {
+    SECTION("In escape sequence") {
+      JsonError err = deserializeJson(doc, "\"\\");
 
 
-    REQUIRE(doc.is<JsonObject>());
-    REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(0));
+      REQUIRE(err == JsonError::IncompleteInput);
+    }
+
+    SECTION("In block comment") {
+      JsonError err = deserializeJson(doc, "/* comment");
+
+      REQUIRE(err == JsonError::IncompleteInput);
+    }
+
+    SECTION("In double quoted string") {
+      JsonError err = deserializeJson(doc, "\"hello");
+
+      REQUIRE(err == JsonError::IncompleteInput);
+    }
+
+    SECTION("In single quoted string") {
+      JsonError err = deserializeJson(doc, "'hello");
+
+      REQUIRE(err == JsonError::IncompleteInput);
+    }
+  }
+
+  SECTION("Premature end of input") {
+    SECTION("In escape sequence") {
+      JsonError err = deserializeJson(doc, "\"\\n\"", 2);
+
+      REQUIRE(err == JsonError::IncompleteInput);
+    }
+
+    SECTION("In block comment") {
+      JsonError err = deserializeJson(doc, "/* comment */", 10);
+
+      REQUIRE(err == JsonError::IncompleteInput);
+    }
+
+    SECTION("In double quoted string") {
+      JsonError err = deserializeJson(doc, "\"hello\"", 6);
+
+      REQUIRE(err == JsonError::IncompleteInput);
+    }
+
+    SECTION("In single quoted string") {
+      JsonError err = deserializeJson(doc, "'hello'", 6);
+
+      REQUIRE(err == JsonError::IncompleteInput);
+    }
   }
   }
 }
 }

+ 73 - 0
test/JsonDeserializer/std_istream.cpp

@@ -0,0 +1,73 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+#include <sstream>
+
+TEST_CASE("deserializeJson(std::istream&)") {
+  DynamicJsonDocument doc;
+
+  SECTION("array") {
+    std::istringstream json(" [ 42 /* comment */ ] ");
+
+    JsonError err = deserializeJson(doc, json);
+    JsonArray& arr = doc.as<JsonArray>();
+
+    REQUIRE(err == JsonError::Ok);
+    REQUIRE(1 == arr.size());
+    REQUIRE(42 == arr[0]);
+  }
+
+  SECTION("object") {
+    std::istringstream json(" { hello : world // comment\n }");
+
+    JsonError err = deserializeJson(doc, json);
+    JsonObject& obj = doc.as<JsonObject>();
+
+    REQUIRE(err == JsonError::Ok);
+    REQUIRE(1 == obj.size());
+    REQUIRE(std::string("world") == obj["hello"]);
+  }
+
+  SECTION("Should not read after the closing brace of an empty object") {
+    std::istringstream json("{}123");
+
+    deserializeJson(doc, json);
+
+    REQUIRE('1' == char(json.get()));
+  }
+
+  SECTION("Should not read after the closing brace") {
+    std::istringstream json("{\"hello\":\"world\"}123");
+
+    deserializeJson(doc, json);
+
+    REQUIRE('1' == char(json.get()));
+  }
+
+  SECTION("Should not read after the closing bracket of an empty array") {
+    std::istringstream json("[]123");
+
+    deserializeJson(doc, json);
+
+    REQUIRE('1' == char(json.get()));
+  }
+
+  SECTION("Should not read after the closing bracket") {
+    std::istringstream json("[\"hello\",\"world\"]123");
+
+    deserializeJson(doc, json);
+
+    REQUIRE('1' == char(json.get()));
+  }
+
+  SECTION("Should not read after the closing quote") {
+    std::istringstream json("\"hello\"123");
+
+    deserializeJson(doc, json);
+
+    REQUIRE('1' == char(json.get()));
+  }
+}

+ 35 - 0
test/JsonDeserializer/std_string.cpp

@@ -0,0 +1,35 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("deserializeJson(const std::string&)") {
+  DynamicJsonDocument doc;
+
+  SECTION("should accept const string") {
+    const std::string input("[42]");
+
+    JsonError err = deserializeJson(doc, input);
+
+    REQUIRE(err == JsonError::Ok);
+  }
+
+  SECTION("should accept temporary string") {
+    JsonError err = deserializeJson(doc, std::string("[42]"));
+
+    REQUIRE(err == JsonError::Ok);
+  }
+
+  SECTION("should duplicate content") {
+    std::string input("[\"hello\"]");
+
+    JsonError err = deserializeJson(doc, input);
+    input[2] = 'X';  // alter the string tomake sure we made a copy
+
+    JsonArray &array = doc.as<JsonArray>();
+    REQUIRE(err == JsonError::Ok);
+    REQUIRE(std::string("hello") == array[0]);
+  }
+}

+ 1 - 0
test/JsonObject/CMakeLists.txt

@@ -11,6 +11,7 @@ add_executable(JsonObjectTests
 	remove.cpp
 	remove.cpp
 	set.cpp
 	set.cpp
 	size.cpp
 	size.cpp
+	std_string.cpp
 	subscript.cpp
 	subscript.cpp
 )
 )
 
 

+ 173 - 0
test/JsonObject/std_string.cpp

@@ -0,0 +1,173 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+static void eraseString(std::string &str) {
+  char *p = const_cast<char *>(str.c_str());
+  while (*p) *p++ = '*';
+}
+
+TEST_CASE("std::string") {
+  DynamicJsonDocument doc;
+
+  SECTION("operator[]") {
+    char json[] = "{\"key\":\"value\"}";
+
+    deserializeJson(doc, json);
+    JsonObject &obj = doc.as<JsonObject>();
+
+    REQUIRE(std::string("value") == obj[std::string("key")]);
+  }
+
+  SECTION("operator[] const") {
+    char json[] = "{\"key\":\"value\"}";
+
+    deserializeJson(doc, json);
+    JsonObject &obj = doc.as<JsonObject>();
+
+    REQUIRE(std::string("value") == obj[std::string("key")]);
+  }
+
+  SECTION("set(key)") {
+    JsonObject &obj = doc.to<JsonObject>();
+    std::string key("hello");
+    obj.set(key, "world");
+    eraseString(key);
+    REQUIRE(std::string("world") == obj["hello"]);
+  }
+
+  SECTION("set(value)") {
+    JsonObject &obj = doc.to<JsonObject>();
+    std::string value("world");
+    obj.set("hello", value);
+    eraseString(value);
+    REQUIRE(std::string("world") == obj["hello"]);
+  }
+
+  SECTION("set(key,value)") {
+    JsonObject &obj = doc.to<JsonObject>();
+    std::string key("hello");
+    std::string value("world");
+    obj.set(key, value);
+    eraseString(key);
+    eraseString(value);
+    REQUIRE(std::string("world") == obj["hello"]);
+  }
+
+  SECTION("set(JsonArraySubscript)") {
+    JsonObject &obj = doc.to<JsonObject>();
+    DynamicJsonDocument doc2;
+    JsonArray &arr = doc2.to<JsonArray>();
+    arr.add("world");
+
+    obj.set(std::string("hello"), arr[0]);
+
+    REQUIRE(std::string("world") == obj["hello"]);
+  }
+
+  SECTION("set(JsonObjectSubscript)") {
+    JsonObject &obj = doc.to<JsonObject>();
+    DynamicJsonDocument doc2;
+    JsonObject &obj2 = doc2.to<JsonObject>();
+    obj2.set("x", "world");
+
+    obj.set(std::string("hello"), obj2["x"]);
+
+    REQUIRE(std::string("world") == obj["hello"]);
+  }
+
+  SECTION("get<T>()") {
+    char json[] = "{\"key\":\"value\"}";
+    deserializeJson(doc, json);
+    JsonObject &obj = doc.as<JsonObject>();
+
+    REQUIRE(std::string("value") == obj.get<const char *>(std::string("key")));
+  }
+
+  SECTION("is<T>()") {
+    char json[] = "{\"key\":\"value\"}";
+    deserializeJson(doc, json);
+    JsonObject &obj = doc.as<JsonObject>();
+
+    REQUIRE(true == obj.is<const char *>(std::string("key")));
+  }
+
+  SECTION("createNestedObject()") {
+    JsonObject &obj = doc.to<JsonObject>();
+    std::string key = "key";
+    char json[64];
+    obj.createNestedObject(key);
+    eraseString(key);
+    serializeJson(doc, json, sizeof(json));
+    REQUIRE(std::string("{\"key\":{}}") == json);
+  }
+
+  SECTION("createNestedArray()") {
+    JsonObject &obj = doc.to<JsonObject>();
+    std::string key = "key";
+    char json[64];
+    obj.createNestedArray(key);
+    eraseString(key);
+    serializeJson(doc, json, sizeof(json));
+    REQUIRE(std::string("{\"key\":[]}") == json);
+  }
+
+  SECTION("containsKey()") {
+    char json[] = "{\"key\":\"value\"}";
+    deserializeJson(doc, json);
+    JsonObject &obj = doc.as<JsonObject>();
+    REQUIRE(true == obj.containsKey(std::string("key")));
+  }
+
+  SECTION("remove()") {
+    JsonObject &obj = doc.to<JsonObject>();
+    obj["key"] = "value";
+
+    obj.remove(std::string("key"));
+
+    REQUIRE(0 == obj.size());
+  }
+
+  SECTION("operator[], set key") {
+    std::string key("hello");
+    JsonObject &obj = doc.to<JsonObject>();
+    obj[key] = "world";
+    eraseString(key);
+    REQUIRE(std::string("world") == obj["hello"]);
+  }
+
+  SECTION("operator[], set value") {
+    std::string value("world");
+    JsonObject &obj = doc.to<JsonObject>();
+    obj["hello"] = value;
+    eraseString(value);
+    REQUIRE(std::string("world") == obj["hello"]);
+  }
+
+  SECTION("memoryUsage() increases when adding a new key") {
+    std::string key1("hello"), key2("world");
+    JsonObject &obj = doc.to<JsonObject>();
+
+    obj[key1] = 1;
+    size_t sizeBefore = doc.memoryUsage();
+    obj[key2] = 2;
+    size_t sizeAfter = doc.memoryUsage();
+
+    REQUIRE(sizeAfter - sizeBefore >= key2.size());
+  }
+
+  SECTION("memoryUsage() remains when adding the same key") {
+    std::string key("hello");
+    JsonObject &obj = doc.to<JsonObject>();
+
+    obj[key] = 1;
+    size_t sizeBefore = doc.memoryUsage();
+    obj[key] = 2;
+    size_t sizeAfter = doc.memoryUsage();
+
+    REQUIRE(sizeBefore == sizeAfter);
+  }
+}

+ 2 - 0
test/JsonSerializer/CMakeLists.txt

@@ -8,6 +8,8 @@ add_executable(JsonSerializerTests
 	JsonObject.cpp
 	JsonObject.cpp
 	JsonObjectPretty.cpp
 	JsonObjectPretty.cpp
 	JsonVariant.cpp
 	JsonVariant.cpp
+	std_stream.cpp
+	std_string.cpp
 )
 )
 
 
 target_link_libraries(JsonSerializerTests catch)
 target_link_libraries(JsonSerializerTests catch)

+ 64 - 0
test/JsonSerializer/std_stream.cpp

@@ -0,0 +1,64 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+#include <sstream>
+
+TEST_CASE("operator<<(std::ostream)") {
+  DynamicJsonDocument doc;
+  std::ostringstream os;
+
+  SECTION("JsonVariant containing false") {
+    JsonVariant variant = false;
+
+    os << variant;
+
+    REQUIRE("false" == os.str());
+  }
+
+  SECTION("JsonVariant containing string") {
+    JsonVariant variant = "coucou";
+
+    os << variant;
+
+    REQUIRE("\"coucou\"" == os.str());
+  }
+
+  SECTION("JsonObject") {
+    JsonObject& object = doc.to<JsonObject>();
+    object["key"] = "value";
+
+    os << object;
+
+    REQUIRE("{\"key\":\"value\"}" == os.str());
+  }
+
+  SECTION("JsonObjectSubscript") {
+    JsonObject& object = doc.to<JsonObject>();
+    object["key"] = "value";
+
+    os << object["key"];
+
+    REQUIRE("\"value\"" == os.str());
+  }
+
+  SECTION("JsonArray") {
+    JsonArray& array = doc.to<JsonArray>();
+    array.add("value");
+
+    os << array;
+
+    REQUIRE("[\"value\"]" == os.str());
+  }
+
+  SECTION("JsonArraySubscript") {
+    JsonArray& array = doc.to<JsonArray>();
+    array.add("value");
+
+    os << array[0];
+
+    REQUIRE("\"value\"" == os.str());
+  }
+}

+ 47 - 0
test/JsonSerializer/std_string.cpp

@@ -0,0 +1,47 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("serialize JsonArray to std::string") {
+  DynamicJsonDocument doc;
+  JsonArray &array = doc.to<JsonArray>();
+  array.add(4);
+  array.add(2);
+
+  SECTION("serializeJson()") {
+    std::string json;
+    serializeJson(array, json);
+
+    REQUIRE(std::string("[4,2]") == json);
+  }
+
+  SECTION("serializeJsonPretty") {
+    std::string json;
+    serializeJsonPretty(array, json);
+
+    REQUIRE(std::string("[\r\n  4,\r\n  2\r\n]") == json);
+  }
+}
+
+TEST_CASE("serialize JsonObject to std::string") {
+  DynamicJsonDocument doc;
+  JsonObject &obj = doc.to<JsonObject>();
+  obj["key"] = "value";
+
+  SECTION("object") {
+    std::string json;
+    serializeJson(doc, json);
+
+    REQUIRE(std::string("{\"key\":\"value\"}") == json);
+  }
+
+  SECTION("serializeJsonPretty") {
+    std::string json;
+    serializeJsonPretty(doc, json);
+
+    REQUIRE(std::string("{\r\n  \"key\": \"value\"\r\n}") == json);
+  }
+}

+ 1 - 3
test/Misc/CMakeLists.txt

@@ -2,10 +2,8 @@
 # Copyright Benoit Blanchon 2014-2018
 # Copyright Benoit Blanchon 2014-2018
 # MIT License
 # MIT License
 
 
-add_executable(MiscTests 
+add_executable(MiscTests
 	FloatParts.cpp
 	FloatParts.cpp
-	std_stream.cpp
-	std_string.cpp
 	StringBuilder.cpp
 	StringBuilder.cpp
 	StringTraits.cpp
 	StringTraits.cpp
 	TypeTraits.cpp
 	TypeTraits.cpp

+ 0 - 88
test/Misc/std_stream.cpp

@@ -1,88 +0,0 @@
-// ArduinoJson - arduinojson.org
-// Copyright Benoit Blanchon 2014-2018
-// MIT License
-
-#include <ArduinoJson.h>
-#include <catch.hpp>
-#include <sstream>
-
-TEST_CASE("std::stream") {
-  SECTION("JsonVariantFalse") {
-    std::ostringstream os;
-    JsonVariant variant = false;
-    os << variant;
-    REQUIRE("false" == os.str());
-  }
-
-  SECTION("JsonVariantString") {
-    std::ostringstream os;
-    JsonVariant variant = "coucou";
-    os << variant;
-    REQUIRE("\"coucou\"" == os.str());
-  }
-
-  SECTION("JsonObject") {
-    std::ostringstream os;
-    DynamicJsonDocument doc;
-    JsonObject& object = doc.to<JsonObject>();
-    object["key"] = "value";
-    os << object;
-    REQUIRE("{\"key\":\"value\"}" == os.str());
-  }
-
-  SECTION("JsonObjectSubscript") {
-    std::ostringstream os;
-    DynamicJsonDocument doc;
-    JsonObject& object = doc.to<JsonObject>();
-    object["key"] = "value";
-    os << object["key"];
-    REQUIRE("\"value\"" == os.str());
-  }
-
-  SECTION("JsonArray") {
-    std::ostringstream os;
-    DynamicJsonDocument doc;
-    JsonArray& array = doc.to<JsonArray>();
-    array.add("value");
-    os << array;
-    REQUIRE("[\"value\"]" == os.str());
-  }
-
-  SECTION("JsonArraySubscript") {
-    std::ostringstream os;
-    DynamicJsonDocument doc;
-    JsonArray& array = doc.to<JsonArray>();
-    array.add("value");
-    os << array[0];
-    REQUIRE("\"value\"" == os.str());
-  }
-
-  SECTION("ParseArray") {
-    std::istringstream json(" [ 42 /* comment */ ] ");
-    DynamicJsonDocument doc;
-    JsonError err = deserializeJson(doc, json);
-    JsonArray& arr = doc.as<JsonArray>();
-
-    REQUIRE(err == JsonError::Ok);
-    REQUIRE(1 == arr.size());
-    REQUIRE(42 == arr[0]);
-  }
-
-  SECTION("ParseObject") {
-    std::istringstream json(" { hello : world // comment\n }");
-    DynamicJsonDocument doc;
-    JsonError err = deserializeJson(doc, json);
-    JsonObject& obj = doc.as<JsonObject>();
-
-    REQUIRE(err == JsonError::Ok);
-    REQUIRE(1 == obj.size());
-    REQUIRE(std::string("world") == obj["hello"]);
-  }
-
-  SECTION("ShouldNotReadPastTheEnd") {
-    std::istringstream json("{}123");
-    DynamicJsonDocument doc;
-    deserializeJson(doc, json);
-    REQUIRE('1' == json.get());
-  }
-}

+ 0 - 259
test/Misc/std_string.cpp

@@ -1,259 +0,0 @@
-// ArduinoJson - arduinojson.org
-// Copyright Benoit Blanchon 2014-2018
-// MIT License
-
-#include <ArduinoJson.h>
-#include <catch.hpp>
-
-static void eraseString(std::string &str) {
-  char *p = const_cast<char *>(str.c_str());
-  while (*p) *p++ = '*';
-}
-
-TEST_CASE("std::string") {
-  SECTION("deserializeJson duplicates content") {
-    std::string json("[\"hello\"]");
-
-    DynamicJsonDocument doc;
-    JsonError err = deserializeJson(doc, json);
-    eraseString(json);
-
-    JsonArray &array = doc.as<JsonArray>();
-    REQUIRE(err == JsonError::Ok);
-    REQUIRE(std::string("hello") == array[0]);
-  }
-
-  SECTION("JsonArray") {
-    DynamicJsonDocument doc;
-    JsonArray &array = doc.to<JsonArray>();
-
-    SECTION("add()") {
-      std::string value("hello");
-      array.add(value);
-      eraseString(value);
-      REQUIRE(std::string("hello") == array[0]);
-    }
-
-    SECTION("set()") {
-      std::string value("world");
-      array.add("hello");
-      array.set(0, value);
-      eraseString(value);
-      REQUIRE(std::string("world") == array[0]);
-    }
-
-    SECTION("operator[]") {
-      std::string value("world");
-      array.add("hello");
-      array[0] = value;
-      eraseString(value);
-      REQUIRE(std::string("world") == array[0]);
-    }
-
-    SECTION("serializeJson()") {
-      array.add(4);
-      array.add(2);
-      std::string json;
-      serializeJson(array, json);
-      REQUIRE(std::string("[4,2]") == json);
-    }
-
-    SECTION("serializeJsonPretty()") {
-      array.add(4);
-      array.add(2);
-      std::string json;
-      serializeJsonPretty(array, json);
-      REQUIRE(std::string("[\r\n  4,\r\n  2\r\n]") == json);
-    }
-  }
-
-  SECTION("JsonObject") {
-    DynamicJsonDocument doc;
-
-    SECTION("deserializeJson()") {
-      std::string json("{\"hello\":\"world\"}");
-
-      JsonError err = deserializeJson(doc, json);
-      JsonObject &obj = doc.as<JsonObject>();
-      eraseString(json);
-
-      REQUIRE(err == JsonError::Ok);
-      REQUIRE(std::string("world") == obj["hello"]);
-    }
-
-    SECTION("operator[]") {
-      char json[] = "{\"key\":\"value\"}";
-
-      deserializeJson(doc, json);
-      JsonObject &obj = doc.as<JsonObject>();
-
-      REQUIRE(std::string("value") == obj[std::string("key")]);
-    }
-
-    SECTION("operator[] const") {
-      char json[] = "{\"key\":\"value\"}";
-
-      deserializeJson(doc, json);
-      JsonObject &obj = doc.as<JsonObject>();
-
-      REQUIRE(std::string("value") == obj[std::string("key")]);
-    }
-
-    SECTION("set(key)") {
-      JsonObject &obj = doc.to<JsonObject>();
-      std::string key("hello");
-      obj.set(key, "world");
-      eraseString(key);
-      REQUIRE(std::string("world") == obj["hello"]);
-    }
-
-    SECTION("set(value)") {
-      JsonObject &obj = doc.to<JsonObject>();
-      std::string value("world");
-      obj.set("hello", value);
-      eraseString(value);
-      REQUIRE(std::string("world") == obj["hello"]);
-    }
-
-    SECTION("set(key,value)") {
-      JsonObject &obj = doc.to<JsonObject>();
-      std::string key("hello");
-      std::string value("world");
-      obj.set(key, value);
-      eraseString(key);
-      eraseString(value);
-      REQUIRE(std::string("world") == obj["hello"]);
-    }
-
-    SECTION("set(JsonArraySubscript)") {
-      JsonObject &obj = doc.to<JsonObject>();
-      DynamicJsonDocument doc2;
-      JsonArray &arr = doc2.to<JsonArray>();
-      arr.add("world");
-
-      obj.set(std::string("hello"), arr[0]);
-
-      REQUIRE(std::string("world") == obj["hello"]);
-    }
-
-    SECTION("set(JsonObjectSubscript)") {
-      JsonObject &obj = doc.to<JsonObject>();
-      DynamicJsonDocument doc2;
-      JsonObject &obj2 = doc2.to<JsonObject>();
-      obj2.set("x", "world");
-
-      obj.set(std::string("hello"), obj2["x"]);
-
-      REQUIRE(std::string("world") == obj["hello"]);
-    }
-
-    SECTION("get<T>()") {
-      char json[] = "{\"key\":\"value\"}";
-      deserializeJson(doc, json);
-      JsonObject &obj = doc.as<JsonObject>();
-
-      REQUIRE(std::string("value") ==
-              obj.get<const char *>(std::string("key")));
-    }
-
-    SECTION("is<T>()") {
-      char json[] = "{\"key\":\"value\"}";
-      deserializeJson(doc, json);
-      JsonObject &obj = doc.as<JsonObject>();
-
-      REQUIRE(true == obj.is<const char *>(std::string("key")));
-    }
-
-    SECTION("createNestedObject()") {
-      JsonObject &obj = doc.to<JsonObject>();
-      std::string key = "key";
-      char json[64];
-      obj.createNestedObject(key);
-      eraseString(key);
-      serializeJson(doc, json, sizeof(json));
-      REQUIRE(std::string("{\"key\":{}}") == json);
-    }
-
-    SECTION("createNestedArray()") {
-      JsonObject &obj = doc.to<JsonObject>();
-      std::string key = "key";
-      char json[64];
-      obj.createNestedArray(key);
-      eraseString(key);
-      serializeJson(doc, json, sizeof(json));
-      REQUIRE(std::string("{\"key\":[]}") == json);
-    }
-
-    SECTION("containsKey()") {
-      char json[] = "{\"key\":\"value\"}";
-      deserializeJson(doc, json);
-      JsonObject &obj = doc.as<JsonObject>();
-      REQUIRE(true == obj.containsKey(std::string("key")));
-    }
-
-    SECTION("remove()") {
-      JsonObject &obj = doc.to<JsonObject>();
-      obj["key"] = "value";
-
-      obj.remove(std::string("key"));
-
-      REQUIRE(0 == obj.size());
-    }
-
-    SECTION("operator[], set key") {
-      std::string key("hello");
-      JsonObject &obj = doc.to<JsonObject>();
-      obj[key] = "world";
-      eraseString(key);
-      REQUIRE(std::string("world") == obj["hello"]);
-    }
-
-    SECTION("operator[], set value") {
-      std::string value("world");
-      JsonObject &obj = doc.to<JsonObject>();
-      obj["hello"] = value;
-      eraseString(value);
-      REQUIRE(std::string("world") == obj["hello"]);
-    }
-
-    SECTION("serializeJson()") {
-      JsonObject &obj = doc.to<JsonObject>();
-      obj["key"] = "value";
-      std::string json;
-      serializeJson(doc, json);
-      REQUIRE(std::string("{\"key\":\"value\"}") == json);
-    }
-
-    SECTION("serializeJsonPretty()") {
-      JsonObject &obj = doc.to<JsonObject>();
-      obj["key"] = "value";
-      std::string json;
-      serializeJsonPretty(doc, json);
-      REQUIRE(std::string("{\r\n  \"key\": \"value\"\r\n}") == json);
-    }
-
-    SECTION("memoryUsage() increases when adding a new key") {
-      std::string key1("hello"), key2("world");
-      JsonObject &obj = doc.to<JsonObject>();
-
-      obj[key1] = 1;
-      size_t sizeBefore = doc.memoryUsage();
-      obj[key2] = 2;
-      size_t sizeAfter = doc.memoryUsage();
-
-      REQUIRE(sizeAfter - sizeBefore >= key2.size());
-    }
-
-    SECTION("memoryUsage() remains when adding the same key") {
-      std::string key("hello");
-      JsonObject &obj = doc.to<JsonObject>();
-
-      obj[key] = 1;
-      size_t sizeBefore = doc.memoryUsage();
-      obj[key] = 2;
-      size_t sizeAfter = doc.memoryUsage();
-
-      REQUIRE(sizeBefore == sizeAfter);
-    }
-  }
-}

+ 181 - 173
test/Misc/unsigned_char.cpp

@@ -9,259 +9,267 @@
 #define CONFLICTS_WITH_BUILTIN_OPERATOR
 #define CONFLICTS_WITH_BUILTIN_OPERATOR
 #endif
 #endif
 
 
-TEST_CASE("unsigned char string") {
-  SECTION("JsonBuffer::parseArray") {
-    unsigned char json[] = "[42]";
+TEST_CASE("unsigned char[]") {
+  SECTION("deserializeJson()") {
+    unsigned char input[] = "{\"a\":42}";
 
 
-    StaticJsonDocument<JSON_ARRAY_SIZE(1)> doc;
-    JsonError err = deserializeJson(doc, json);
+    StaticJsonDocument<JSON_OBJECT_SIZE(1)> doc;
+    JsonError err = deserializeJson(doc, input);
 
 
     REQUIRE(err == JsonError::Ok);
     REQUIRE(err == JsonError::Ok);
   }
   }
 
 
-  SECTION("JsonBuffer::parseObject") {
-    unsigned char json[] = "{\"a\":42}";
+  SECTION("deserializeMsgPack()") {
+    unsigned char input[] = "\xDE\x00\x01\xA5Hello\xA5world";
 
 
-    StaticJsonDocument<JSON_OBJECT_SIZE(1)> doc;
-    JsonError err = deserializeJson(doc, json);
+    StaticJsonDocument<JSON_OBJECT_SIZE(2)> doc;
+    MsgPackError err = deserializeMsgPack(doc, input);
 
 
-    REQUIRE(err == JsonError::Ok);
+    REQUIRE(err == MsgPackError::Ok);
   }
   }
 
 
-  SECTION("JsonVariant constructor") {
-    unsigned char value[] = "42";
+  SECTION("JsonVariant") {
+    SECTION("constructor") {
+      unsigned char value[] = "42";
 
 
-    JsonVariant variant(value);
+      JsonVariant variant(value);
 
 
-    REQUIRE(42 == variant.as<int>());
-  }
+      REQUIRE(42 == variant.as<int>());
+    }
 
 
-  SECTION("JsonVariant assignment operator") {
-    unsigned char value[] = "42";
+    SECTION("operator=") {
+      unsigned char value[] = "42";
 
 
-    JsonVariant variant(666);
-    variant = value;
+      JsonVariant variant(666);
+      variant = value;
 
 
-    REQUIRE(42 == variant.as<int>());
-  }
+      REQUIRE(42 == variant.as<int>());
+    }
 
 
 #ifndef CONFLICTS_WITH_BUILTIN_OPERATOR
 #ifndef CONFLICTS_WITH_BUILTIN_OPERATOR
-  SECTION("JsonVariant::operator[]") {
-    unsigned char key[] = "hello";
+    SECTION("operator[]") {
+      unsigned char key[] = "hello";
 
 
-    DynamicJsonDocument doc;
-    deserializeJson(doc, "{\"hello\":\"world\"}");
-    JsonVariant variant = doc.as<JsonVariant>();
+      DynamicJsonDocument doc;
+      deserializeJson(doc, "{\"hello\":\"world\"}");
+      JsonVariant variant = doc.as<JsonVariant>();
 
 
-    REQUIRE(std::string("world") == variant[key]);
-  }
+      REQUIRE(std::string("world") == variant[key]);
+    }
 #endif
 #endif
 
 
 #ifndef CONFLICTS_WITH_BUILTIN_OPERATOR
 #ifndef CONFLICTS_WITH_BUILTIN_OPERATOR
-  SECTION("JsonVariant::operator[] const") {
-    unsigned char key[] = "hello";
+    SECTION("operator[] const") {
+      unsigned char key[] = "hello";
 
 
-    DynamicJsonDocument doc;
-    deserializeJson(doc, "{\"hello\":\"world\"}");
-    const JsonVariant variant = doc.as<JsonVariant>();
+      DynamicJsonDocument doc;
+      deserializeJson(doc, "{\"hello\":\"world\"}");
+      const JsonVariant variant = doc.as<JsonVariant>();
 
 
-    REQUIRE(std::string("world") == variant[key]);
-  }
+      REQUIRE(std::string("world") == variant[key]);
+    }
 #endif
 #endif
 
 
-  SECTION("JsonVariant::operator==") {
-    unsigned char comparand[] = "hello";
+    SECTION("operator==") {
+      unsigned char comparand[] = "hello";
 
 
-    JsonVariant variant;
-    variant = "hello";
+      JsonVariant variant;
+      variant = "hello";
 
 
-    REQUIRE(comparand == variant);
-    REQUIRE(variant == comparand);
-    REQUIRE_FALSE(comparand != variant);
-    REQUIRE_FALSE(variant != comparand);
-  }
+      REQUIRE(comparand == variant);
+      REQUIRE(variant == comparand);
+      REQUIRE_FALSE(comparand != variant);
+      REQUIRE_FALSE(variant != comparand);
+    }
 
 
-  SECTION("JsonVariant::operator!=") {
-    unsigned char comparand[] = "hello";
+    SECTION("operator!=") {
+      unsigned char comparand[] = "hello";
 
 
-    JsonVariant variant;
-    variant = "world";
+      JsonVariant variant;
+      variant = "world";
 
 
-    REQUIRE(comparand != variant);
-    REQUIRE(variant != comparand);
-    REQUIRE_FALSE(comparand == variant);
-    REQUIRE_FALSE(variant == comparand);
+      REQUIRE(comparand != variant);
+      REQUIRE(variant != comparand);
+      REQUIRE_FALSE(comparand == variant);
+      REQUIRE_FALSE(variant == comparand);
+    }
   }
   }
 
 
+  SECTION("JsonObject") {
 #ifndef CONFLICTS_WITH_BUILTIN_OPERATOR
 #ifndef CONFLICTS_WITH_BUILTIN_OPERATOR
-  SECTION("JsonObject::operator[]") {
-    unsigned char key[] = "hello";
+    SECTION("operator[]") {
+      unsigned char key[] = "hello";
 
 
-    DynamicJsonDocument doc;
-    JsonObject& obj = doc.to<JsonObject>();
-    obj[key] = "world";
+      DynamicJsonDocument doc;
+      JsonObject& obj = doc.to<JsonObject>();
+      obj[key] = "world";
 
 
-    REQUIRE(std::string("world") == obj["hello"]);
-  }
-#endif
+      REQUIRE(std::string("world") == obj["hello"]);
+    }
 
 
-  SECTION("JsonObjectSubscript::operator=") {  // issue #416
-    unsigned char value[] = "world";
+    SECTION("JsonObject::operator[] const") {
+      unsigned char key[] = "hello";
 
 
-    DynamicJsonDocument doc;
-    JsonObject& obj = doc.to<JsonObject>();
-    obj["hello"] = value;
+      DynamicJsonDocument doc;
+      deserializeJson(doc, "{\"hello\":\"world\"}");
 
 
-    REQUIRE(std::string("world") == obj["hello"]);
-  }
+      JsonObject& obj = doc.as<JsonObject>();
+      REQUIRE(std::string("world") == obj[key]);
+    }
+#endif
 
 
-  SECTION("JsonObjectSubscript::set()") {
-    unsigned char value[] = "world";
+    SECTION("get()") {
+      unsigned char key[] = "hello";
 
 
-    DynamicJsonDocument doc;
-    JsonObject& obj = doc.to<JsonObject>();
-    obj["hello"].set(value);
+      DynamicJsonDocument doc;
+      deserializeJson(doc, "{\"hello\":\"world\"}");
+      JsonObject& obj = doc.as<JsonObject>();
+      REQUIRE(std::string("world") == obj.get<char*>(key));
+    }
 
 
-    REQUIRE(std::string("world") == obj["hello"]);
-  }
+    SECTION("set() key") {
+      unsigned char key[] = "hello";
 
 
-#ifndef CONFLICTS_WITH_BUILTIN_OPERATOR
-  SECTION("JsonObject::operator[] const") {
-    unsigned char key[] = "hello";
+      DynamicJsonDocument doc;
+      JsonObject& obj = doc.to<JsonObject>();
+      obj.set(key, "world");
 
 
-    DynamicJsonDocument doc;
-    deserializeJson(doc, "{\"hello\":\"world\"}");
+      REQUIRE(std::string("world") == obj["hello"]);
+    }
 
 
-    JsonObject& obj = doc.as<JsonObject>();
-    REQUIRE(std::string("world") == obj[key]);
-  }
-#endif
+    SECTION("set() value") {
+      unsigned char value[] = "world";
 
 
-  SECTION("JsonObject::get()") {
-    unsigned char key[] = "hello";
+      DynamicJsonDocument doc;
+      JsonObject& obj = doc.to<JsonObject>();
+      obj.set("hello", value);
 
 
-    DynamicJsonDocument doc;
-    deserializeJson(doc, "{\"hello\":\"world\"}");
-    JsonObject& obj = doc.as<JsonObject>();
-    REQUIRE(std::string("world") == obj.get<char*>(key));
-  }
+      REQUIRE(std::string("world") == obj["hello"]);
+    }
 
 
-  SECTION("JsonObject::set() key") {
-    unsigned char key[] = "hello";
+    SECTION("set() key&value") {
+      unsigned char key[] = "world";
 
 
-    DynamicJsonDocument doc;
-    JsonObject& obj = doc.to<JsonObject>();
-    obj.set(key, "world");
+      DynamicJsonDocument doc;
+      JsonObject& obj = doc.to<JsonObject>();
+      obj.set(key, key);
 
 
-    REQUIRE(std::string("world") == obj["hello"]);
-  }
+      REQUIRE(std::string("world") == obj["world"]);
+    }
 
 
-  SECTION("JsonObject::set() value") {
-    unsigned char value[] = "world";
+    SECTION("containsKey()") {
+      unsigned char key[] = "hello";
 
 
-    DynamicJsonDocument doc;
-    JsonObject& obj = doc.to<JsonObject>();
-    obj.set("hello", value);
+      DynamicJsonDocument doc;
+      deserializeJson(doc, "{\"hello\":\"world\"}");
+      JsonObject& obj = doc.as<JsonObject>();
+      REQUIRE(true == obj.containsKey(key));
+    }
 
 
-    REQUIRE(std::string("world") == obj["hello"]);
-  }
+    SECTION("remove()") {
+      unsigned char key[] = "hello";
 
 
-  SECTION("JsonObject::set key&value") {
-    unsigned char key[] = "world";
+      DynamicJsonDocument doc;
+      deserializeJson(doc, "{\"hello\":\"world\"}");
+      JsonObject& obj = doc.as<JsonObject>();
+      obj.remove(key);
 
 
-    DynamicJsonDocument doc;
-    JsonObject& obj = doc.to<JsonObject>();
-    obj.set(key, key);
+      REQUIRE(0 == obj.size());
+    }
 
 
-    REQUIRE(std::string("world") == obj["world"]);
-  }
+    SECTION("is()") {
+      unsigned char key[] = "hello";
 
 
-  SECTION("JsonObject::containsKey()") {
-    unsigned char key[] = "hello";
+      DynamicJsonDocument doc;
+      deserializeJson(doc, "{\"hello\":42}");
+      JsonObject& obj = doc.as<JsonObject>();
 
 
-    DynamicJsonDocument doc;
-    deserializeJson(doc, "{\"hello\":\"world\"}");
-    JsonObject& obj = doc.as<JsonObject>();
-    REQUIRE(true == obj.containsKey(key));
-  }
+      REQUIRE(true == obj.is<int>(key));
+    }
 
 
-  SECTION("JsonObject::remove()") {
-    unsigned char key[] = "hello";
+    SECTION("createNestedArray()") {
+      unsigned char key[] = "hello";
 
 
-    DynamicJsonDocument doc;
-    deserializeJson(doc, "{\"hello\":\"world\"}");
-    JsonObject& obj = doc.as<JsonObject>();
-    obj.remove(key);
+      DynamicJsonDocument doc;
+      JsonObject& obj = doc.to<JsonObject>();
+      obj.createNestedArray(key);
+    }
 
 
-    REQUIRE(0 == obj.size());
-  }
+    SECTION("createNestedObject()") {
+      unsigned char key[] = "hello";
 
 
-  SECTION("JsonObject::is()") {
-    unsigned char key[] = "hello";
+      DynamicJsonDocument doc;
+      JsonObject& obj = doc.to<JsonObject>();
+      obj.createNestedObject(key);
+    }
+  }
 
 
-    DynamicJsonDocument doc;
-    deserializeJson(doc, "{\"hello\":42}");
-    JsonObject& obj = doc.as<JsonObject>();
+  SECTION("JsonObjectSubscript") {
+    SECTION("operator=") {  // issue #416
+      unsigned char value[] = "world";
 
 
-    REQUIRE(true == obj.is<int>(key));
-  }
+      DynamicJsonDocument doc;
+      JsonObject& obj = doc.to<JsonObject>();
+      obj["hello"] = value;
 
 
-  SECTION("JsonObject::createNestedArray()") {
-    unsigned char key[] = "hello";
+      REQUIRE(std::string("world") == obj["hello"]);
+    }
 
 
-    DynamicJsonDocument doc;
-    JsonObject& obj = doc.to<JsonObject>();
-    obj.createNestedArray(key);
-  }
+    SECTION("set()") {
+      unsigned char value[] = "world";
 
 
-  SECTION("JsonObject::createNestedObject()") {
-    unsigned char key[] = "hello";
+      DynamicJsonDocument doc;
+      JsonObject& obj = doc.to<JsonObject>();
+      obj["hello"].set(value);
 
 
-    DynamicJsonDocument doc;
-    JsonObject& obj = doc.to<JsonObject>();
-    obj.createNestedObject(key);
+      REQUIRE(std::string("world") == obj["hello"]);
+    }
   }
   }
 
 
-  SECTION("JsonArray::add()") {
-    unsigned char value[] = "world";
+  SECTION("JsonArray") {
+    SECTION("add()") {
+      unsigned char value[] = "world";
 
 
-    DynamicJsonDocument doc;
-    JsonArray& arr = doc.to<JsonArray>();
-    arr.add(value);
+      DynamicJsonDocument doc;
+      JsonArray& arr = doc.to<JsonArray>();
+      arr.add(value);
 
 
-    REQUIRE(std::string("world") == arr[0]);
-  }
+      REQUIRE(std::string("world") == arr[0]);
+    }
 
 
-  SECTION("JsonArray::set()") {
-    unsigned char value[] = "world";
+    SECTION("set()") {
+      unsigned char value[] = "world";
 
 
-    DynamicJsonDocument doc;
-    JsonArray& arr = doc.to<JsonArray>();
-    arr.add("hello");
-    arr.set(0, value);
+      DynamicJsonDocument doc;
+      JsonArray& arr = doc.to<JsonArray>();
+      arr.add("hello");
+      arr.set(0, value);
 
 
-    REQUIRE(std::string("world") == arr[0]);
+      REQUIRE(std::string("world") == arr[0]);
+    }
   }
   }
 
 
-  SECTION("JsonArraySubscript::set()") {
-    unsigned char value[] = "world";
+  SECTION("JsonArraySubscript") {
+    SECTION("set()") {
+      unsigned char value[] = "world";
 
 
-    DynamicJsonDocument doc;
-    JsonArray& arr = doc.to<JsonArray>();
-    arr.add("hello");
-    arr[0].set(value);
+      DynamicJsonDocument doc;
+      JsonArray& arr = doc.to<JsonArray>();
+      arr.add("hello");
+      arr[0].set(value);
 
 
-    REQUIRE(std::string("world") == arr[0]);
-  }
+      REQUIRE(std::string("world") == arr[0]);
+    }
 
 
-  SECTION("JsonArraySubscript::operator=") {
-    unsigned char value[] = "world";
+    SECTION("operator=") {
+      unsigned char value[] = "world";
 
 
-    DynamicJsonDocument doc;
-    JsonArray& arr = doc.to<JsonArray>();
-    arr.add("hello");
-    arr[0] = value;
+      DynamicJsonDocument doc;
+      JsonArray& arr = doc.to<JsonArray>();
+      arr.add("hello");
+      arr[0] = value;
 
 
-    REQUIRE(std::string("world") == arr[0]);
+      REQUIRE(std::string("world") == arr[0]);
+    }
   }
   }
 }
 }

+ 252 - 253
test/Misc/vla.cpp

@@ -17,322 +17,321 @@
 #ifndef VLA_NOT_SUPPORTED
 #ifndef VLA_NOT_SUPPORTED
 
 
 TEST_CASE("Variable Length Array") {
 TEST_CASE("Variable Length Array") {
-  SECTION("ParseArray") {
-    int i = 8;
+  SECTION("deserializeJson()") {
+    int i = 9;
     char vla[i];
     char vla[i];
-    strcpy(vla, "[42]");
+    strcpy(vla, "{\"a\":42}");
 
 
-    StaticJsonDocument<JSON_ARRAY_SIZE(1)> doc;
+    StaticJsonDocument<JSON_OBJECT_SIZE(1)> doc;
     JsonError err = deserializeJson(doc, vla);
     JsonError err = deserializeJson(doc, vla);
 
 
     REQUIRE(err == JsonError::Ok);
     REQUIRE(err == JsonError::Ok);
   }
   }
 
 
-  SECTION("ParseObject") {
+  SECTION("deserializeMsgPack()") {
     int i = 16;
     int i = 16;
     char vla[i];
     char vla[i];
-    strcpy(vla, "{\"a\":42}");
+    memcpy(vla, "\xDE\x00\x01\xA5Hello\xA5world", 15);
 
 
     StaticJsonDocument<JSON_OBJECT_SIZE(1)> doc;
     StaticJsonDocument<JSON_OBJECT_SIZE(1)> doc;
-    JsonError error = deserializeJson(doc, vla);
+    MsgPackError err = deserializeMsgPack(doc, vla);
 
 
-    REQUIRE(error == JsonError::Ok);
+    REQUIRE(err == MsgPackError::Ok);
   }
   }
 
 
-  SECTION("Parse") {
-    int i = 16;
-    char vla[i];
-    strcpy(vla, "42");
+  SECTION("JsonVariant") {
+    SECTION("constructor") {
+      int i = 16;
+      char vla[i];
+      strcpy(vla, "42");
 
 
-    StaticJsonDocument<> variant;
-    deserializeJson(variant, vla);
+      JsonVariant variant(vla);
 
 
-    REQUIRE(42 == variant.as<int>());
-  }
+      REQUIRE(42 == variant.as<int>());
+    }
 
 
-  SECTION("JsonVariant_Constructor") {
-    int i = 16;
-    char vla[i];
-    strcpy(vla, "42");
-
-    JsonVariant variant(vla);
-
-    REQUIRE(42 == variant.as<int>());
-  }
+    SECTION("operator=") {
+      int i = 16;
+      char vla[i];
+      strcpy(vla, "42");
 
 
-  SECTION("JsonVariant_Assign") {
-    int i = 16;
-    char vla[i];
-    strcpy(vla, "42");
+      JsonVariant variant(666);
+      variant = vla;
 
 
-    JsonVariant variant(666);
-    variant = vla;
-
-    REQUIRE(42 == variant.as<int>());
-  }
+      REQUIRE(42 == variant.as<int>());
+    }
 
 
 #ifndef CONFLICTS_WITH_BUILTIN_OPERATOR
 #ifndef CONFLICTS_WITH_BUILTIN_OPERATOR
-  SECTION("JsonVariant_Subscript") {
-    int i = 16;
-    char vla[i];
-    strcpy(vla, "hello");
+    SECTION("operator[]") {
+      int i = 16;
+      char vla[i];
+      strcpy(vla, "hello");
 
 
-    DynamicJsonDocument doc;
-    deserializeJson(doc, "{\"hello\":\"world\"}");
-    JsonVariant variant = doc.as<JsonVariant>();
+      DynamicJsonDocument doc;
+      deserializeJson(doc, "{\"hello\":\"world\"}");
+      JsonVariant variant = doc.as<JsonVariant>();
 
 
-    REQUIRE(std::string("world") == variant[vla]);
-  }
+      REQUIRE(std::string("world") == variant[vla]);
+    }
 #endif
 #endif
 
 
 #ifndef CONFLICTS_WITH_BUILTIN_OPERATOR
 #ifndef CONFLICTS_WITH_BUILTIN_OPERATOR
-  SECTION("JsonVariant_Subscript_Const") {
-    int i = 16;
-    char vla[i];
-    strcpy(vla, "hello");
+    SECTION("operator[] const") {
+      int i = 16;
+      char vla[i];
+      strcpy(vla, "hello");
 
 
-    DynamicJsonDocument doc;
-    deserializeJson(doc, "{\"hello\":\"world\"}");
-    const JsonVariant variant = doc.as<JsonVariant>();
+      DynamicJsonDocument doc;
+      deserializeJson(doc, "{\"hello\":\"world\"}");
+      const JsonVariant variant = doc.as<JsonVariant>();
 
 
-    REQUIRE(std::string("world") == variant[vla]);
-  }
+      REQUIRE(std::string("world") == variant[vla]);
+    }
 #endif
 #endif
 
 
-  SECTION("JsonVariant_Equals") {
-    int i = 16;
-    char vla[i];
-    strcpy(vla, "hello");
-
-    JsonVariant variant;
-    variant = "hello";
-
-    REQUIRE((vla == variant));
-    REQUIRE((variant == vla));
-    REQUIRE_FALSE((vla != variant));
-    REQUIRE_FALSE((variant != vla));
-  }
-
-  SECTION("JsonVariant_Differs") {
-    int i = 16;
-    char vla[i];
-    strcpy(vla, "hello");
-
-    JsonVariant variant;
-    variant = "world";
-
-    REQUIRE((vla != variant));
-    REQUIRE((variant != vla));
-    REQUIRE_FALSE((vla == variant));
-    REQUIRE_FALSE((variant == vla));
+    SECTION("operator==") {
+      int i = 16;
+      char vla[i];
+      strcpy(vla, "hello");
+
+      JsonVariant variant;
+      variant = "hello";
+
+      REQUIRE((vla == variant));
+      REQUIRE((variant == vla));
+      REQUIRE_FALSE((vla != variant));
+      REQUIRE_FALSE((variant != vla));
+    }
+
+    SECTION("operator!=") {
+      int i = 16;
+      char vla[i];
+      strcpy(vla, "hello");
+
+      JsonVariant variant;
+      variant = "world";
+
+      REQUIRE((vla != variant));
+      REQUIRE((variant != vla));
+      REQUIRE_FALSE((vla == variant));
+      REQUIRE_FALSE((variant == vla));
+    }
   }
   }
 
 
+  SECTION("JsonObject") {
 #ifndef CONFLICTS_WITH_BUILTIN_OPERATOR
 #ifndef CONFLICTS_WITH_BUILTIN_OPERATOR
-  SECTION("JsonObject_Subscript") {
-    int i = 16;
-    char vla[i];
-    strcpy(vla, "hello");
+    SECTION("operator[]") {
+      int i = 16;
+      char vla[i];
+      strcpy(vla, "hello");
 
 
-    DynamicJsonDocument doc;
-    JsonObject& obj = doc.to<JsonObject>();
-    obj[vla] = "world";
+      DynamicJsonDocument doc;
+      JsonObject& obj = doc.to<JsonObject>();
+      obj[vla] = "world";
 
 
-    REQUIRE(std::string("world") == obj["hello"]);
-  }
+      REQUIRE(std::string("world") == obj["hello"]);
+    }
 #endif
 #endif
 
 
-  SECTION("JsonObject_Subscript_Assign") {  // issue #416
-    int i = 32;
-    char vla[i];
-    strcpy(vla, "world");
-
-    DynamicJsonDocument doc;
-    JsonObject& obj = doc.to<JsonObject>();
-    obj["hello"] = vla;
-
-    REQUIRE(std::string("world") == obj["hello"].as<char*>());
-  }
-
-  SECTION("JsonObject_Subscript_Set") {
-    int i = 32;
-    char vla[i];
-    strcpy(vla, "world");
-
-    DynamicJsonDocument doc;
-    JsonObject& obj = doc.to<JsonObject>();
-    obj["hello"].set(vla);
-
-    REQUIRE(std::string("world") == obj["hello"].as<char*>());
-  }
-
 #ifndef CONFLICTS_WITH_BUILTIN_OPERATOR
 #ifndef CONFLICTS_WITH_BUILTIN_OPERATOR
-  SECTION("JsonObject_Subscript_Const") {
-    int i = 16;
-    char vla[i];
-    strcpy(vla, "hello");
+    SECTION("operator[] const") {
+      int i = 16;
+      char vla[i];
+      strcpy(vla, "hello");
 
 
-    DynamicJsonDocument doc;
-    deserializeJson(doc, "{\"hello\":\"world\"}");
+      DynamicJsonDocument doc;
+      deserializeJson(doc, "{\"hello\":\"world\"}");
 
 
-    JsonObject& obj = doc.as<JsonObject>();
-    REQUIRE(std::string("world") == obj[vla]);
-  }
+      JsonObject& obj = doc.as<JsonObject>();
+      REQUIRE(std::string("world") == obj[vla]);
+    }
 #endif
 #endif
 
 
-  SECTION("JsonObject_Get") {
-    int i = 16;
-    char vla[i];
-    strcpy(vla, "hello");
-
-    DynamicJsonDocument doc;
-    deserializeJson(doc, "{\"hello\":\"world\"}");
-
-    JsonObject& obj = doc.as<JsonObject>();
-    REQUIRE(std::string("world") == obj.get<char*>(vla));
-  }
-
-  SECTION("JsonObject_Set_Key") {
-    int i = 16;
-    char vla[i];
-    strcpy(vla, "hello");
-
-    DynamicJsonDocument doc;
-    JsonObject& obj = doc.to<JsonObject>();
-    obj.set(vla, "world");
-
-    REQUIRE(std::string("world") == obj["hello"]);
-  }
-
-  SECTION("JsonObject_Set_Value") {
-    int i = 16;
-    char vla[i];
-    strcpy(vla, "world");
-
-    DynamicJsonDocument doc;
-    JsonObject& obj = doc.to<JsonObject>();
-    obj.set("hello", vla);
-
-    REQUIRE(std::string("world") == obj["hello"]);
-  }
-
-  SECTION("JsonObject_Set_KeyAndValue") {
-    int i = 16;
-    char vla[i];
-    strcpy(vla, "world");
-
-    DynamicJsonDocument doc;
-    JsonObject& obj = doc.to<JsonObject>();
-    obj.set(vla, vla);
-
-    REQUIRE(std::string("world") == obj["world"]);
-  }
-
-  SECTION("JsonObject_ContainsKey") {
-    int i = 16;
-    char vla[i];
-    strcpy(vla, "hello");
-
-    DynamicJsonDocument doc;
-    deserializeJson(doc, "{\"hello\":\"world\"}");
-
-    JsonObject& obj = doc.as<JsonObject>();
-    REQUIRE(true == obj.containsKey(vla));
+    SECTION("get()") {
+      int i = 16;
+      char vla[i];
+      strcpy(vla, "hello");
+
+      DynamicJsonDocument doc;
+      deserializeJson(doc, "{\"hello\":\"world\"}");
+
+      JsonObject& obj = doc.as<JsonObject>();
+      REQUIRE(std::string("world") == obj.get<char*>(vla));
+    }
+
+    SECTION("set() key") {
+      int i = 16;
+      char vla[i];
+      strcpy(vla, "hello");
+
+      DynamicJsonDocument doc;
+      JsonObject& obj = doc.to<JsonObject>();
+      obj.set(vla, "world");
+
+      REQUIRE(std::string("world") == obj["hello"]);
+    }
+
+    SECTION("set() value") {
+      int i = 16;
+      char vla[i];
+      strcpy(vla, "world");
+
+      DynamicJsonDocument doc;
+      JsonObject& obj = doc.to<JsonObject>();
+      obj.set("hello", vla);
+
+      REQUIRE(std::string("world") == obj["hello"]);
+    }
+
+    SECTION("set() key&value") {
+      int i = 16;
+      char vla[i];
+      strcpy(vla, "world");
+
+      DynamicJsonDocument doc;
+      JsonObject& obj = doc.to<JsonObject>();
+      obj.set(vla, vla);
+
+      REQUIRE(std::string("world") == obj["world"]);
+    }
+
+    SECTION("containsKey()") {
+      int i = 16;
+      char vla[i];
+      strcpy(vla, "hello");
+
+      DynamicJsonDocument doc;
+      deserializeJson(doc, "{\"hello\":\"world\"}");
+
+      JsonObject& obj = doc.as<JsonObject>();
+      REQUIRE(true == obj.containsKey(vla));
+    }
+
+    SECTION("remove()") {
+      int i = 16;
+      char vla[i];
+      strcpy(vla, "hello");
+
+      DynamicJsonDocument doc;
+      deserializeJson(doc, "{\"hello\":\"world\"}");
+      JsonObject& obj = doc.as<JsonObject>();
+      obj.remove(vla);
+
+      REQUIRE(0 == obj.size());
+    }
+
+    SECTION("is<T>()") {
+      int i = 16;
+      char vla[i];
+      strcpy(vla, "hello");
+
+      DynamicJsonDocument doc;
+      deserializeJson(doc, "{\"hello\":42}");
+      JsonObject& obj = doc.as<JsonObject>();
+
+      REQUIRE(true == obj.is<int>(vla));
+    }
+
+    SECTION("createNestedArray()") {
+      int i = 16;
+      char vla[i];
+      strcpy(vla, "hello");
+
+      DynamicJsonDocument doc;
+      JsonObject& obj = doc.to<JsonObject>();
+      obj.createNestedArray(vla);
+    }
+
+    SECTION("createNestedObject()") {
+      int i = 16;
+      char vla[i];
+      strcpy(vla, "hello");
+
+      DynamicJsonDocument doc;
+      JsonObject& obj = doc.to<JsonObject>();
+      obj.createNestedObject(vla);
+    }
   }
   }
 
 
-  SECTION("JsonObject_Remove") {
-    int i = 16;
-    char vla[i];
-    strcpy(vla, "hello");
+  SECTION("JsonObjectSubscript") {
+    SECTION("operator=") {  // issue #416
+      int i = 32;
+      char vla[i];
+      strcpy(vla, "world");
 
 
-    DynamicJsonDocument doc;
-    deserializeJson(doc, "{\"hello\":\"world\"}");
-    JsonObject& obj = doc.as<JsonObject>();
-    obj.remove(vla);
+      DynamicJsonDocument doc;
+      JsonObject& obj = doc.to<JsonObject>();
+      obj["hello"] = vla;
 
 
-    REQUIRE(0 == obj.size());
-  }
+      REQUIRE(std::string("world") == obj["hello"].as<char*>());
+    }
 
 
-  SECTION("JsonObject_Is") {
-    int i = 16;
-    char vla[i];
-    strcpy(vla, "hello");
+    SECTION("set()") {
+      int i = 32;
+      char vla[i];
+      strcpy(vla, "world");
 
 
-    DynamicJsonDocument doc;
-    deserializeJson(doc, "{\"hello\":42}");
-    JsonObject& obj = doc.as<JsonObject>();
+      DynamicJsonDocument doc;
+      JsonObject& obj = doc.to<JsonObject>();
+      obj["hello"].set(vla);
 
 
-    REQUIRE(true == obj.is<int>(vla));
+      REQUIRE(std::string("world") == obj["hello"].as<char*>());
+    }
   }
   }
 
 
-  SECTION("JsonObject_CreateNestedArray") {
-    int i = 16;
-    char vla[i];
-    strcpy(vla, "hello");
+  SECTION("JsonArray") {
+    SECTION("add()") {
+      int i = 16;
+      char vla[i];
+      strcpy(vla, "world");
 
 
-    DynamicJsonDocument doc;
-    JsonObject& obj = doc.to<JsonObject>();
-    obj.createNestedArray(vla);
-  }
+      DynamicJsonDocument doc;
+      JsonArray& arr = doc.to<JsonArray>();
+      arr.add(vla);
 
 
-  SECTION("JsonObject_CreateNestedObject") {
-    int i = 16;
-    char vla[i];
-    strcpy(vla, "hello");
+      REQUIRE(std::string("world") == arr[0]);
+    }
 
 
-    DynamicJsonDocument doc;
-    JsonObject& obj = doc.to<JsonObject>();
-    obj.createNestedObject(vla);
-  }
+    SECTION("set()") {
+      int i = 16;
+      char vla[i];
+      strcpy(vla, "world");
 
 
-  SECTION("JsonArray_Add") {
-    int i = 16;
-    char vla[i];
-    strcpy(vla, "world");
+      DynamicJsonDocument doc;
+      JsonArray& arr = doc.to<JsonArray>();
+      arr.add("hello");
+      arr.set(0, vla);
 
 
-    DynamicJsonDocument doc;
-    JsonArray& arr = doc.to<JsonArray>();
-    arr.add(vla);
-
-    REQUIRE(std::string("world") == arr[0]);
-  }
-
-  SECTION("JsonArray_Set") {
-    int i = 16;
-    char vla[i];
-    strcpy(vla, "world");
-
-    DynamicJsonDocument doc;
-    JsonArray& arr = doc.to<JsonArray>();
-    arr.add("hello");
-    arr.set(0, vla);
-
-    REQUIRE(std::string("world") == arr[0]);
+      REQUIRE(std::string("world") == arr[0]);
+    }
   }
   }
 
 
-  SECTION("JsonArraySubscript_Set") {
-    int i = 16;
-    char vla[i];
-    strcpy(vla, "world");
+  SECTION("JsonArraySubscript") {
+    SECTION("set()") {
+      int i = 16;
+      char vla[i];
+      strcpy(vla, "world");
 
 
-    DynamicJsonDocument doc;
-    JsonArray& arr = doc.to<JsonArray>();
-    arr.add("hello");
-    arr[0].set(vla);
+      DynamicJsonDocument doc;
+      JsonArray& arr = doc.to<JsonArray>();
+      arr.add("hello");
+      arr[0].set(vla);
 
 
-    REQUIRE(std::string("world") == arr[0]);
-  }
+      REQUIRE(std::string("world") == arr[0]);
+    }
 
 
-  SECTION("JsonArraySubscript_Assign") {
-    int i = 16;
-    char vla[i];
-    strcpy(vla, "world");
+    SECTION("operator=") {
+      int i = 16;
+      char vla[i];
+      strcpy(vla, "world");
 
 
-    DynamicJsonDocument doc;
-    JsonArray& arr = doc.to<JsonArray>();
-    arr.add("hello");
-    arr[0] = vla;
+      DynamicJsonDocument doc;
+      JsonArray& arr = doc.to<JsonArray>();
+      arr.add("hello");
+      arr[0] = vla;
 
 
-    REQUIRE(std::string("world") == arr[0]);
+      REQUIRE(std::string("world") == arr[0]);
+    }
   }
   }
 }
 }
 #endif
 #endif

+ 7 - 3
test/MsgPack/CMakeLists.txt

@@ -3,13 +3,17 @@
 # MIT License
 # MIT License
 
 
 add_executable(MsgPackTests
 add_executable(MsgPackTests
-	deserializationErrors.cpp
+	MsgPackError.cpp
 	deserializeArray.cpp
 	deserializeArray.cpp
 	deserializeObject.cpp
 	deserializeObject.cpp
-	deserializeVariant.cpp
 	deserializeStaticVariant.cpp
 	deserializeStaticVariant.cpp
+	deserializeVariant.cpp
 	doubleToFloat.cpp
 	doubleToFloat.cpp
-	MsgPackError.cpp
+	incompleteInput.cpp
+	nestingLimit.cpp
+	notSupported.cpp
+	std_string.cpp
+	std_istream.cpp
 )
 )
 
 
 target_link_libraries(MsgPackTests catch)
 target_link_libraries(MsgPackTests catch)

+ 2 - 0
test/MsgPack/MsgPackError.cpp

@@ -25,6 +25,7 @@ TEST_CASE("MsgPackError") {
     TEST_STRINGIFICATION(NotSupported);
     TEST_STRINGIFICATION(NotSupported);
     TEST_STRINGIFICATION(NoMemory);
     TEST_STRINGIFICATION(NoMemory);
     TEST_STRINGIFICATION(TooDeep);
     TEST_STRINGIFICATION(TooDeep);
+    TEST_STRINGIFICATION(IncompleteInput);
   }
   }
 
 
   SECTION("as boolean") {
   SECTION("as boolean") {
@@ -32,6 +33,7 @@ TEST_CASE("MsgPackError") {
     TEST_BOOLIFICATION(NotSupported, true);
     TEST_BOOLIFICATION(NotSupported, true);
     TEST_BOOLIFICATION(NoMemory, true);
     TEST_BOOLIFICATION(NoMemory, true);
     TEST_BOOLIFICATION(TooDeep, true);
     TEST_BOOLIFICATION(TooDeep, true);
+    TEST_BOOLIFICATION(IncompleteInput, true);
   }
   }
 
 
   SECTION("ostream") {
   SECTION("ostream") {

+ 0 - 59
test/MsgPack/deserializationErrors.cpp

@@ -1,59 +0,0 @@
-// ArduinoJson - arduinojson.org
-// Copyright Benoit Blanchon 2014-2018
-// MIT License
-
-#include <ArduinoJson.h>
-#include <catch.hpp>
-
-static void check(const char* input, MsgPackError expected,
-                  uint8_t nestingLimit = 10) {
-  DynamicJsonDocument doc;
-  doc.nestingLimit = nestingLimit;
-
-  MsgPackError error = deserializeMsgPack(doc, input);
-
-  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);       // [[]]
-  }
-}

+ 106 - 0
test/MsgPack/incompleteInput.cpp

@@ -0,0 +1,106 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+MsgPackError deserialize(const char* input, size_t len) {
+  DynamicJsonDocument doc;
+
+  return deserializeMsgPack(doc, input, len);
+}
+
+void checkAllSizes(const char* input, size_t len) {
+  REQUIRE(deserialize(input, len) == MsgPackError::Ok);
+
+  while (--len) {
+    REQUIRE(deserialize(input, len) == MsgPackError::IncompleteInput);
+  }
+}
+
+TEST_CASE("deserializeMsgPack() returns IncompleteInput") {
+  SECTION("empty input") {
+    checkAllSizes("\x00", 1);
+  }
+
+  SECTION("fixarray") {
+    checkAllSizes("\x91\x01", 2);
+  }
+
+  SECTION("array 16") {
+    checkAllSizes("\xDC\x00\x01\x01", 4);
+  }
+
+  SECTION("array 32") {
+    checkAllSizes("\xDD\x00\x00\x00\x01\x01", 6);
+  }
+
+  SECTION("fixmap") {
+    checkAllSizes("\x81\xA3one\x01", 6);
+  }
+
+  SECTION("map 16") {
+    checkAllSizes("\xDE\x00\x01\xA3one\x01", 8);
+  }
+
+  SECTION("map 32") {
+    checkAllSizes("\xDF\x00\x00\x00\x01\xA3one\x01", 10);
+  }
+
+  SECTION("uint 8") {
+    checkAllSizes("\xcc\x01", 2);
+  }
+
+  SECTION("uint 16") {
+    checkAllSizes("\xcd\x00\x01", 3);
+  }
+
+  SECTION("uint 32") {
+    checkAllSizes("\xCE\x00\x00\x00\x01", 5);
+  }
+
+  SECTION("uint 64") {
+    checkAllSizes("\xCF\x00\x00\x00\x00\x00\x00\x00\x00", 9);
+  }
+
+  SECTION("int 8") {
+    checkAllSizes("\xD0\x01", 2);
+  }
+
+  SECTION("int 16") {
+    checkAllSizes("\xD1\x00\x01", 3);
+  }
+
+  SECTION("int 32") {
+    checkAllSizes("\xD2\x00\x00\x00\x01", 5);
+  }
+
+  SECTION("int 64") {
+    checkAllSizes("\xD3\x00\x00\x00\x00\x00\x00\x00\x00", 9);
+  }
+
+  SECTION("float 32") {
+    checkAllSizes("\xCA\x40\x48\xF5\xC3", 5);
+  }
+
+  SECTION("float 64") {
+    checkAllSizes("\xCB\x40\x09\x21\xCA\xC0\x83\x12\x6F", 9);
+  }
+
+  SECTION("fixstr") {
+    checkAllSizes("\xABhello world", 12);
+  }
+
+  SECTION("str 8") {
+    checkAllSizes("\xd9\x05hello", 7);
+  }
+
+  SECTION("str 16") {
+    checkAllSizes("\xda\x00\x05hello", 8);
+  }
+
+  SECTION("str 32") {
+    checkAllSizes("\xdb\x00\x00\x00\x05hello", 10);
+  }
+}

+ 31 - 0
test/MsgPack/nestingLimit.cpp

@@ -0,0 +1,31 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+static void check(const char* input, MsgPackError expected, uint8_t limit) {
+  DynamicJsonDocument doc;
+  doc.nestingLimit = limit;
+
+  MsgPackError error = deserializeMsgPack(doc, input);
+
+  REQUIRE(error == expected);
+}
+
+TEST_CASE("Errors returned by deserializeMsgPack()") {
+  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);       // [[]]
+  }
+}

+ 73 - 0
test/MsgPack/notSupported.cpp

@@ -0,0 +1,73 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+static void checkNotSupported(const char* input) {
+  DynamicJsonDocument doc;
+
+  MsgPackError error = deserializeMsgPack(doc, input);
+
+  REQUIRE(error == MsgPackError::NotSupported);
+}
+
+TEST_CASE("deserializeMsgPack() return NotSupported") {
+  SECTION("bin 8") {
+    checkNotSupported("\xc4");
+  }
+
+  SECTION("bin 16") {
+    checkNotSupported("\xc5");
+  }
+
+  SECTION("bin 32") {
+    checkNotSupported("\xc6");
+  }
+
+  SECTION("ext 8") {
+    checkNotSupported("\xc7");
+  }
+
+  SECTION("ext 16") {
+    checkNotSupported("\xc8");
+  }
+
+  SECTION("ext 32") {
+    checkNotSupported("\xc9");
+  }
+
+  SECTION("fixext 1") {
+    checkNotSupported("\xd4");
+  }
+
+  SECTION("fixext 2") {
+    checkNotSupported("\xd5");
+  }
+
+  SECTION("fixext 4") {
+    checkNotSupported("\xd6");
+  }
+
+  SECTION("fixext 8") {
+    checkNotSupported("\xd7");
+  }
+
+  SECTION("fixext 16") {
+    checkNotSupported("\xd8");
+  }
+
+  SECTION("unsupported in array") {
+    checkNotSupported("\x91\xc4");
+  }
+
+  SECTION("unsupported in map") {
+    checkNotSupported("\x81\xc4\x00\xA1H");
+    checkNotSupported("\x81\xA1H\xc4\x00");
+  }
+
+  SECTION("integer as key") {
+    checkNotSupported("\x81\x01\xA1H");
+  }
+}

+ 29 - 0
test/MsgPack/std_istream.cpp

@@ -0,0 +1,29 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("deserializeMsgPack(std::istream&)") {
+  DynamicJsonDocument doc;
+
+  SECTION("should accept a zero in input") {
+    std::istringstream input(std::string("\x92\x00\x02", 3));
+
+    MsgPackError err = deserializeMsgPack(doc, input);
+
+    REQUIRE(err == MsgPackError::Ok);
+    JsonArray& arr = doc.as<JsonArray>();
+    REQUIRE(arr[0] == 0);
+    REQUIRE(arr[1] == 2);
+  }
+
+  SECTION("should detect incomplete input") {
+    std::istringstream input("\x92\x00\x02");
+
+    MsgPackError err = deserializeMsgPack(doc, input);
+
+    REQUIRE(err == MsgPackError::IncompleteInput);
+  }
+}

+ 44 - 0
test/MsgPack/std_string.cpp

@@ -0,0 +1,44 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("deserializeMsgPack(const std::string&)") {
+  DynamicJsonDocument doc;
+
+  SECTION("should accept const string") {
+    const std::string input("\x92\x01\x02");
+
+    MsgPackError err = deserializeMsgPack(doc, input);
+
+    REQUIRE(err == MsgPackError::Ok);
+  }
+
+  SECTION("should accept temporary string") {
+    MsgPackError err = deserializeMsgPack(doc, std::string("\x92\x01\x02"));
+
+    REQUIRE(err == MsgPackError::Ok);
+  }
+
+  SECTION("should duplicate content") {
+    std::string input("\x91\xA5hello");
+
+    MsgPackError err = deserializeMsgPack(doc, input);
+    input[2] = 'X';  // alter the string tomake sure we made a copy
+
+    JsonArray& array = doc.as<JsonArray>();
+    REQUIRE(err == MsgPackError::Ok);
+    REQUIRE(std::string("hello") == array[0]);
+  }
+
+  SECTION("should accept a zero in input") {
+    MsgPackError err = deserializeMsgPack(doc, std::string("\x92\x00\x02", 3));
+
+    REQUIRE(err == MsgPackError::Ok);
+    JsonArray& arr = doc.as<JsonArray>();
+    REQUIRE(arr[0] == 0);
+    REQUIRE(arr[1] == 2);
+  }
+}