mirror of
				https://github.com/eledio-devices/thirdparty-ArduinoJson.git
				synced 2025-10-31 16:14:11 +01:00 
			
		
		
		
	Reduced memory consumption by not duplicating spaces and comments
This commit is contained in:
		| @@ -5,6 +5,7 @@ HEAD | |||||||
| ---- | ---- | ||||||
|  |  | ||||||
| * Added operator `==` to compare `JsonVariant` and strings (issue #402) | * Added operator `==` to compare `JsonVariant` and strings (issue #402) | ||||||
|  | * Reduced memory consumption by not duplicating spaces and comments | ||||||
|  |  | ||||||
| v5.7.3 | v5.7.3 | ||||||
| ------ | ------ | ||||||
|   | |||||||
| @@ -1,115 +0,0 @@ | |||||||
| // Copyright Benoit Blanchon 2014-2016 |  | ||||||
| // MIT License |  | ||||||
| // |  | ||||||
| // Arduino JSON library |  | ||||||
| // https://github.com/bblanchon/ArduinoJson |  | ||||||
| // If you like this project, please add a star! |  | ||||||
|  |  | ||||||
| #pragma once |  | ||||||
|  |  | ||||||
| #include "../JsonBuffer.hpp" |  | ||||||
|  |  | ||||||
| #include <stdlib.h> |  | ||||||
|  |  | ||||||
| #if defined(__clang__) |  | ||||||
| #pragma clang diagnostic push |  | ||||||
| #pragma clang diagnostic ignored "-Wnon-virtual-dtor" |  | ||||||
| #elif defined(__GNUC__) |  | ||||||
| #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) |  | ||||||
| #pragma GCC diagnostic push |  | ||||||
| #endif |  | ||||||
| #pragma GCC diagnostic ignored "-Wnon-virtual-dtor" |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| namespace ArduinoJson { |  | ||||||
| namespace Internals { |  | ||||||
| class DefaultAllocator { |  | ||||||
|  public: |  | ||||||
|   void* allocate(size_t size) { |  | ||||||
|     return malloc(size); |  | ||||||
|   } |  | ||||||
|   void deallocate(void* pointer) { |  | ||||||
|     free(pointer); |  | ||||||
|   } |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| template <typename TAllocator> |  | ||||||
| class BlockJsonBuffer : public JsonBuffer { |  | ||||||
|   struct Block; |  | ||||||
|   struct EmptyBlock { |  | ||||||
|     Block* next; |  | ||||||
|     size_t capacity; |  | ||||||
|     size_t size; |  | ||||||
|   }; |  | ||||||
|   struct Block : EmptyBlock { |  | ||||||
|     uint8_t data[1]; |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|  public: |  | ||||||
|   BlockJsonBuffer(size_t initialSize = 256) |  | ||||||
|       : _head(NULL), _nextBlockSize(initialSize) {} |  | ||||||
|  |  | ||||||
|   ~BlockJsonBuffer() { |  | ||||||
|     Block* currentBlock = _head; |  | ||||||
|  |  | ||||||
|     while (currentBlock != NULL) { |  | ||||||
|       Block* nextBlock = currentBlock->next; |  | ||||||
|       _allocator.deallocate(currentBlock); |  | ||||||
|       currentBlock = nextBlock; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   size_t size() const { |  | ||||||
|     size_t total = 0; |  | ||||||
|     for (const Block* b = _head; b; b = b->next) total += b->size; |  | ||||||
|     return total; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   virtual void* alloc(size_t bytes) { |  | ||||||
|     return canAllocInHead(bytes) ? allocInHead(bytes) : allocInNewBlock(bytes); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|  private: |  | ||||||
|   bool canAllocInHead(size_t bytes) const { |  | ||||||
|     return _head != NULL && _head->size + bytes <= _head->capacity; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   void* allocInHead(size_t bytes) { |  | ||||||
|     void* p = _head->data + _head->size; |  | ||||||
|     _head->size += round_size_up(bytes); |  | ||||||
|     return p; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   void* allocInNewBlock(size_t bytes) { |  | ||||||
|     size_t capacity = _nextBlockSize; |  | ||||||
|     if (bytes > capacity) capacity = bytes; |  | ||||||
|     if (!addNewBlock(capacity)) return NULL; |  | ||||||
|     _nextBlockSize *= 2; |  | ||||||
|     return allocInHead(bytes); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool addNewBlock(size_t capacity) { |  | ||||||
|     size_t bytes = sizeof(EmptyBlock) + capacity; |  | ||||||
|     Block* block = static_cast<Block*>(_allocator.allocate(bytes)); |  | ||||||
|     if (block == NULL) return false; |  | ||||||
|     block->capacity = capacity; |  | ||||||
|     block->size = 0; |  | ||||||
|     block->next = _head; |  | ||||||
|     _head = block; |  | ||||||
|     return true; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   TAllocator _allocator; |  | ||||||
|   Block* _head; |  | ||||||
|   size_t _nextBlockSize; |  | ||||||
| }; |  | ||||||
| } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #if defined(__clang__) |  | ||||||
| #pragma clang diagnostic pop |  | ||||||
| #elif defined(__GNUC__) |  | ||||||
| #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) |  | ||||||
| #pragma GCC diagnostic pop |  | ||||||
| #endif |  | ||||||
| #endif |  | ||||||
| @@ -30,6 +30,17 @@ template <typename TString> | |||||||
| struct StringFuncs<TString&> : StringFuncs<TString> {}; | struct StringFuncs<TString&> : StringFuncs<TString> {}; | ||||||
|  |  | ||||||
| struct CharPtrFuncs { | struct CharPtrFuncs { | ||||||
|  |   class Iterator { | ||||||
|  |     const char* _ptr; | ||||||
|  |  | ||||||
|  |    public: | ||||||
|  |     Iterator(const char* ptr) : _ptr(ptr ? ptr : "") {} | ||||||
|  |  | ||||||
|  |     char next() { | ||||||
|  |       return *_ptr++; | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|   static bool equals(const char* str, const char* expected) { |   static bool equals(const char* str, const char* expected) { | ||||||
|     return strcmp(str, expected) == 0; |     return strcmp(str, expected) == 0; | ||||||
|   } |   } | ||||||
| @@ -71,6 +82,10 @@ struct StdStringFuncs { | |||||||
|     return static_cast<char*>(dup); |     return static_cast<char*>(dup); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   struct Iterator : CharPtrFuncs::Iterator { | ||||||
|  |     Iterator(const TString& str) : CharPtrFuncs::Iterator(str.c_str()) {} | ||||||
|  |   }; | ||||||
|  |  | ||||||
|   static bool equals(const TString& str, const char* expected) { |   static bool equals(const TString& str, const char* expected) { | ||||||
|     return str == expected; |     return str == expected; | ||||||
|   } |   } | ||||||
| @@ -99,6 +114,18 @@ struct StringFuncs<std::string> : StdStringFuncs<std::string> {}; | |||||||
| #if ARDUINOJSON_ENABLE_PROGMEM | #if ARDUINOJSON_ENABLE_PROGMEM | ||||||
| template <> | template <> | ||||||
| struct StringFuncs<const __FlashStringHelper*> { | struct StringFuncs<const __FlashStringHelper*> { | ||||||
|  |   class Iterator { | ||||||
|  |     const char* _ptr; | ||||||
|  |  | ||||||
|  |    public: | ||||||
|  |     Iterator(const __FlashStringHelper* ptr) | ||||||
|  |         : _ptr(reinterpret_cast<const char*>(ptr)) {} | ||||||
|  |  | ||||||
|  |     char next() { | ||||||
|  |       return pgm_read_byte_near(_ptr++); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|   static bool equals(const __FlashStringHelper* str, const char* expected) { |   static bool equals(const __FlashStringHelper* str, const char* expected) { | ||||||
|     return strcmp_P(expected, (PGM_P)str) == 0; |     return strcmp_P(expected, (PGM_P)str) == 0; | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -12,51 +12,51 @@ namespace Internals { | |||||||
| template <typename TInput> | template <typename TInput> | ||||||
| void skipSpacesAndComments(TInput& input) { | void skipSpacesAndComments(TInput& input) { | ||||||
|   for (;;) { |   for (;;) { | ||||||
|     switch (input.peek()) { |     switch (input.current()) { | ||||||
|       // spaces |       // spaces | ||||||
|       case ' ': |       case ' ': | ||||||
|       case '\t': |       case '\t': | ||||||
|       case '\r': |       case '\r': | ||||||
|       case '\n': |       case '\n': | ||||||
|         input.skip(); |         input.move(); | ||||||
|         continue; |         continue; | ||||||
|  |  | ||||||
|       // comments |       // comments | ||||||
|       case '/': |       case '/': | ||||||
|         switch (input.peekNext()) { |         switch (input.next()) { | ||||||
|           // C-style block comment |           // C-style block comment | ||||||
|           case '*': |           case '*': | ||||||
|             input.skip();  // skip '/' |             input.move();  // skip '/' | ||||||
|             input.skip();  // skip '*' |             input.move();  // skip '*' | ||||||
|             for (;;) { |             for (;;) { | ||||||
|               switch (input.peek()) { |               switch (input.current()) { | ||||||
|                 case '\0': |                 case '\0': | ||||||
|                   return; |                   return; | ||||||
|                 case '*': |                 case '*': | ||||||
|                   input.skip();  // skip '*' |                   input.move();  // skip '*' | ||||||
|                   if (input.peek() == '/') { |                   if (input.current() == '/') { | ||||||
|                     input.skip();  // skip '/' |                     input.move();  // skip '/' | ||||||
|                     return; |                     return; | ||||||
|                   } |                   } | ||||||
|                   break; |                   break; | ||||||
|                 default: |                 default: | ||||||
|                   input.skip(); |                   input.move(); | ||||||
|               } |               } | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|           // C++-style line comment |           // C++-style line comment | ||||||
|           case '/': |           case '/': | ||||||
|             input.skip();  // skip '/' |             input.move();  // skip '/' | ||||||
|             for (;;) { |             for (;;) { | ||||||
|               switch (input.peek()) { |               switch (input.current()) { | ||||||
|                 case '\0': |                 case '\0': | ||||||
|                   return; |                   return; | ||||||
|                 case '\n': |                 case '\n': | ||||||
|                   input.skip(); |                   input.move(); | ||||||
|                   return; |                   return; | ||||||
|                 default: |                 default: | ||||||
|                   input.skip(); |                   input.move(); | ||||||
|               } |               } | ||||||
|             } |             } | ||||||
|             return; |             return; | ||||||
|   | |||||||
| @@ -18,12 +18,14 @@ namespace Internals { | |||||||
| // Parse JSON string to create JsonArrays and JsonObjects | // Parse JSON string to create JsonArrays and JsonObjects | ||||||
| // This internal class is not indended to be used directly. | // This internal class is not indended to be used directly. | ||||||
| // Instead, use JsonBuffer.parseArray() or .parseObject() | // Instead, use JsonBuffer.parseArray() or .parseObject() | ||||||
|  | template <typename TReader, typename TWriter> | ||||||
| class JsonParser { | class JsonParser { | ||||||
|  public: |  public: | ||||||
|   JsonParser(JsonBuffer *buffer, char *json, uint8_t nestingLimit) |   JsonParser(JsonBuffer *buffer, TReader reader, TWriter writer, | ||||||
|  |              uint8_t nestingLimit) | ||||||
|       : _buffer(buffer), |       : _buffer(buffer), | ||||||
|         _reader(json), |         _reader(reader), | ||||||
|         _writer(json), |         _writer(writer), | ||||||
|         _nestingLimit(nestingLimit) {} |         _nestingLimit(nestingLimit) {} | ||||||
|  |  | ||||||
|   JsonArray &parseArray(); |   JsonArray &parseArray(); | ||||||
| @@ -36,7 +38,9 @@ class JsonParser { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|  private: |  private: | ||||||
|   static bool eat(StringReader &, char charToSkip); |   JsonParser &operator=(const JsonParser &);  // non-copiable | ||||||
|  |  | ||||||
|  |   static bool eat(TReader &, char charToSkip); | ||||||
|   FORCE_INLINE bool eat(char charToSkip) { |   FORCE_INLINE bool eat(char charToSkip) { | ||||||
|     return eat(_reader, charToSkip); |     return eat(_reader, charToSkip); | ||||||
|   } |   } | ||||||
| @@ -63,9 +67,42 @@ class JsonParser { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   JsonBuffer *_buffer; |   JsonBuffer *_buffer; | ||||||
|   StringReader _reader; |   TReader _reader; | ||||||
|   StringWriter _writer; |   TWriter _writer; | ||||||
|   uint8_t _nestingLimit; |   uint8_t _nestingLimit; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | template <typename TJsonBuffer, typename TString> | ||||||
|  | struct JsonParserBuilder { | ||||||
|  |   typedef typename Internals::StringFuncs<TString>::Iterator InputIterator; | ||||||
|  |   typedef JsonParser<StringReader<InputIterator>, TJsonBuffer &> TParser; | ||||||
|  |  | ||||||
|  |   static TParser makeParser(TJsonBuffer *buffer, const TString &json, | ||||||
|  |                             uint8_t nestingLimit) { | ||||||
|  |     return TParser(buffer, InputIterator(json), *buffer, nestingLimit); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | template <typename TJsonBuffer> | ||||||
|  | struct JsonParserBuilder<TJsonBuffer, char *> { | ||||||
|  |   typedef typename Internals::StringFuncs<char *>::Iterator InputIterator; | ||||||
|  |   typedef JsonParser<StringReader<InputIterator>, StringWriter> TParser; | ||||||
|  |  | ||||||
|  |   static TParser makeParser(TJsonBuffer *buffer, char *json, | ||||||
|  |                             uint8_t nestingLimit) { | ||||||
|  |     return TParser(buffer, InputIterator(json), json, nestingLimit); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | template <typename TJsonBuffer, typename TChar, size_t N> | ||||||
|  | struct JsonParserBuilder<TJsonBuffer, TChar[N]> | ||||||
|  |     : JsonParserBuilder<TJsonBuffer, TChar *> {}; | ||||||
|  |  | ||||||
|  | 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); | ||||||
|  | } | ||||||
| } | } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -10,16 +10,19 @@ | |||||||
| #include "Comments.hpp" | #include "Comments.hpp" | ||||||
| #include "JsonParser.hpp" | #include "JsonParser.hpp" | ||||||
|  |  | ||||||
| inline bool ArduinoJson::Internals::JsonParser::eat(StringReader &reader, | template <typename TReader, typename TWriter> | ||||||
|                                                     char charToSkip) { | inline bool ArduinoJson::Internals::JsonParser<TReader, TWriter>::eat( | ||||||
|  |     TReader &reader, char charToSkip) { | ||||||
|   skipSpacesAndComments(reader); |   skipSpacesAndComments(reader); | ||||||
|   if (reader.peek() != charToSkip) return false; |   if (reader.current() != charToSkip) return false; | ||||||
|   reader.skip(); |   reader.move(); | ||||||
|   skipSpacesAndComments(reader); |   skipSpacesAndComments(reader); | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| inline bool ArduinoJson::Internals::JsonParser::parseAnythingTo( | template <typename TReader, typename TWriter> | ||||||
|  | inline bool | ||||||
|  | ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseAnythingTo( | ||||||
|     JsonVariant *destination) { |     JsonVariant *destination) { | ||||||
|   if (_nestingLimit == 0) return false; |   if (_nestingLimit == 0) return false; | ||||||
|   _nestingLimit--; |   _nestingLimit--; | ||||||
| @@ -28,11 +31,13 @@ inline bool ArduinoJson::Internals::JsonParser::parseAnythingTo( | |||||||
|   return success; |   return success; | ||||||
| } | } | ||||||
|  |  | ||||||
| inline bool ArduinoJson::Internals::JsonParser::parseAnythingToUnsafe( | template <typename TReader, typename TWriter> | ||||||
|  | inline bool | ||||||
|  | ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseAnythingToUnsafe( | ||||||
|     JsonVariant *destination) { |     JsonVariant *destination) { | ||||||
|   skipSpacesAndComments(_reader); |   skipSpacesAndComments(_reader); | ||||||
|  |  | ||||||
|   switch (_reader.peek()) { |   switch (_reader.current()) { | ||||||
|     case '[': |     case '[': | ||||||
|       return parseArrayTo(destination); |       return parseArrayTo(destination); | ||||||
|  |  | ||||||
| @@ -44,8 +49,9 @@ inline bool ArduinoJson::Internals::JsonParser::parseAnythingToUnsafe( | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | template <typename TReader, typename TWriter> | ||||||
| inline ArduinoJson::JsonArray & | inline ArduinoJson::JsonArray & | ||||||
| ArduinoJson::Internals::JsonParser::parseArray() { | ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseArray() { | ||||||
|   // Create an empty array |   // Create an empty array | ||||||
|   JsonArray &array = _buffer->createArray(); |   JsonArray &array = _buffer->createArray(); | ||||||
|  |  | ||||||
| @@ -76,7 +82,8 @@ ERROR_NO_MEMORY: | |||||||
|   return JsonArray::invalid(); |   return JsonArray::invalid(); | ||||||
| } | } | ||||||
|  |  | ||||||
| inline bool ArduinoJson::Internals::JsonParser::parseArrayTo( | template <typename TReader, typename TWriter> | ||||||
|  | inline bool ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseArrayTo( | ||||||
|     JsonVariant *destination) { |     JsonVariant *destination) { | ||||||
|   JsonArray &array = parseArray(); |   JsonArray &array = parseArray(); | ||||||
|   if (!array.success()) return false; |   if (!array.success()) return false; | ||||||
| @@ -85,8 +92,9 @@ inline bool ArduinoJson::Internals::JsonParser::parseArrayTo( | |||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | template <typename TReader, typename TWriter> | ||||||
| inline ArduinoJson::JsonObject & | inline ArduinoJson::JsonObject & | ||||||
| ArduinoJson::Internals::JsonParser::parseObject() { | ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseObject() { | ||||||
|   // Create an empty object |   // Create an empty object | ||||||
|   JsonObject &object = _buffer->createObject(); |   JsonObject &object = _buffer->createObject(); | ||||||
|  |  | ||||||
| @@ -124,7 +132,8 @@ ERROR_NO_MEMORY: | |||||||
|   return JsonObject::invalid(); |   return JsonObject::invalid(); | ||||||
| } | } | ||||||
|  |  | ||||||
| inline bool ArduinoJson::Internals::JsonParser::parseObjectTo( | template <typename TReader, typename TWriter> | ||||||
|  | inline bool ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseObjectTo( | ||||||
|     JsonVariant *destination) { |     JsonVariant *destination) { | ||||||
|   JsonObject &object = parseObject(); |   JsonObject &object = parseObject(); | ||||||
|   if (!object.success()) return false; |   if (!object.success()) return false; | ||||||
| @@ -133,46 +142,49 @@ inline bool ArduinoJson::Internals::JsonParser::parseObjectTo( | |||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| inline const char *ArduinoJson::Internals::JsonParser::parseString() { | template <typename TReader, typename TWriter> | ||||||
|   const char *str = _writer.startString(); | inline const char * | ||||||
|  | ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseString() { | ||||||
|  |   typename TypeTraits::RemoveReference<TWriter>::type::String str = | ||||||
|  |       _writer.startString(); | ||||||
|  |  | ||||||
|   char c = _reader.peek(); |   char c = _reader.current(); | ||||||
|  |  | ||||||
|   if (isQuote(c)) {  // quotes |   if (isQuote(c)) {  // quotes | ||||||
|     _reader.skip(); |     _reader.move(); | ||||||
|     char stopChar = c; |     char stopChar = c; | ||||||
|     for (;;) { |     for (;;) { | ||||||
|       c = _reader.peek(); |       c = _reader.current(); | ||||||
|       if (c == '\0') break; |       if (c == '\0') break; | ||||||
|       _reader.skip(); |       _reader.move(); | ||||||
|  |  | ||||||
|       if (c == stopChar) break; |       if (c == stopChar) break; | ||||||
|  |  | ||||||
|       if (c == '\\') { |       if (c == '\\') { | ||||||
|         // replace char |         // replace char | ||||||
|         c = Encoding::unescapeChar(_reader.peek()); |         c = Encoding::unescapeChar(_reader.current()); | ||||||
|         if (c == '\0') break; |         if (c == '\0') break; | ||||||
|         _reader.skip(); |         _reader.move(); | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       _writer.append(c); |       str.append(c); | ||||||
|     } |     } | ||||||
|   } else {  // no quotes |   } else {  // no quotes | ||||||
|     for (;;) { |     for (;;) { | ||||||
|       if (!isLetterOrNumber(c)) break; |       if (!isLetterOrNumber(c)) break; | ||||||
|       _reader.skip(); |       _reader.move(); | ||||||
|       _writer.append(c); |       str.append(c); | ||||||
|       c = _reader.peek(); |       c = _reader.current(); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   _writer.stopString(); |   return str.c_str(); | ||||||
|   return str; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| inline bool ArduinoJson::Internals::JsonParser::parseStringTo( | template <typename TReader, typename TWriter> | ||||||
|  | inline bool ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseStringTo( | ||||||
|     JsonVariant *destination) { |     JsonVariant *destination) { | ||||||
|   bool hasQuotes = isQuote(_reader.peek()); |   bool hasQuotes = isQuote(_reader.current()); | ||||||
|   const char *value = parseString(); |   const char *value = parseString(); | ||||||
|   if (value == NULL) return false; |   if (value == NULL) return false; | ||||||
|   if (hasQuotes) { |   if (hasQuotes) { | ||||||
|   | |||||||
| @@ -13,24 +13,29 @@ namespace Internals { | |||||||
| // Parse JSON string to create JsonArrays and JsonObjects | // Parse JSON string to create JsonArrays and JsonObjects | ||||||
| // This internal class is not indended to be used directly. | // This internal class is not indended to be used directly. | ||||||
| // Instead, use JsonBuffer.parseArray() or .parseObject() | // Instead, use JsonBuffer.parseArray() or .parseObject() | ||||||
|  | template <typename TIterator> | ||||||
| class StringReader { | class StringReader { | ||||||
|  |   TIterator _input; | ||||||
|  |   char _current, _next; | ||||||
|  |  | ||||||
|  public: |  public: | ||||||
|   StringReader(const char *input) : _ptr(input ? input : "") {} |   StringReader(const TIterator& input) : _input(input) { | ||||||
|  |     _current = _input.next(); | ||||||
|   void skip() { |     _next = _input.next(); | ||||||
|     _ptr++; |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   char peek() const { |   void move() { | ||||||
|     return _ptr[0]; |     _current = _next; | ||||||
|  |     _next = _input.next(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   char peekNext() const { |   char current() const { | ||||||
|     return _ptr[1]; |     return _current; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  private: |   char next() const { | ||||||
|   const char *_ptr; |     return _next; | ||||||
|  |   } | ||||||
| }; | }; | ||||||
| } | } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -10,27 +10,34 @@ | |||||||
| namespace ArduinoJson { | namespace ArduinoJson { | ||||||
| namespace Internals { | namespace Internals { | ||||||
|  |  | ||||||
| // Parse JSON string to create JsonArrays and JsonObjects |  | ||||||
| // This internal class is not indended to be used directly. |  | ||||||
| // Instead, use JsonBuffer.parseArray() or .parseObject() |  | ||||||
| class StringWriter { | class StringWriter { | ||||||
|  public: |  public: | ||||||
|   StringWriter(char *buffer) : _ptr(buffer) {} |   class String { | ||||||
|  |    public: | ||||||
|  |     String(char** ptr) : _writePtr(ptr), _startPtr(*ptr) {} | ||||||
|  |  | ||||||
|   const char *startString() { |     void append(char c) { | ||||||
|     return _ptr; |       *(*_writePtr)++ = c; | ||||||
|   } |     } | ||||||
|  |  | ||||||
|   void stopString() { |     const char* c_str() const { | ||||||
|     *_ptr++ = 0; |       *(*_writePtr)++ = 0; | ||||||
|   } |       return _startPtr; | ||||||
|  |     } | ||||||
|  |  | ||||||
|   void append(char c) { |    private: | ||||||
|     *_ptr++ = c; |     char** _writePtr; | ||||||
|  |     char* _startPtr; | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   StringWriter(char* buffer) : _ptr(buffer) {} | ||||||
|  |  | ||||||
|  |   String startString() { | ||||||
|  |     return String(&_ptr); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  private: |  private: | ||||||
|   char *_ptr; |   char* _ptr; | ||||||
| }; | }; | ||||||
| } | } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -7,12 +7,153 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "Data/BlockJsonBuffer.hpp" | #include "JsonBufferBase.hpp" | ||||||
|  |  | ||||||
|  | #include <stdlib.h> | ||||||
|  |  | ||||||
|  | #if defined(__clang__) | ||||||
|  | #pragma clang diagnostic push | ||||||
|  | #pragma clang diagnostic ignored "-Wnon-virtual-dtor" | ||||||
|  | #elif defined(__GNUC__) | ||||||
|  | #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) | ||||||
|  | #pragma GCC diagnostic push | ||||||
|  | #endif | ||||||
|  | #pragma GCC diagnostic ignored "-Wnon-virtual-dtor" | ||||||
|  | #endif | ||||||
|  |  | ||||||
| namespace ArduinoJson { | namespace ArduinoJson { | ||||||
|  | class DefaultAllocator { | ||||||
|  |  public: | ||||||
|  |   void* allocate(size_t size) { | ||||||
|  |     return malloc(size); | ||||||
|  |   } | ||||||
|  |   void deallocate(void* pointer) { | ||||||
|  |     free(pointer); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | template <typename TAllocator> | ||||||
|  | class DynamicJsonBufferBase | ||||||
|  |     : public JsonBufferBase<DynamicJsonBufferBase<TAllocator> > { | ||||||
|  |   struct Block; | ||||||
|  |   struct EmptyBlock { | ||||||
|  |     Block* next; | ||||||
|  |     size_t capacity; | ||||||
|  |     size_t size; | ||||||
|  |   }; | ||||||
|  |   struct Block : EmptyBlock { | ||||||
|  |     uint8_t data[1]; | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |  public: | ||||||
|  |   DynamicJsonBufferBase(size_t initialSize = 256) | ||||||
|  |       : _head(NULL), _nextBlockCapacity(initialSize) {} | ||||||
|  |  | ||||||
|  |   ~DynamicJsonBufferBase() { | ||||||
|  |     Block* currentBlock = _head; | ||||||
|  |  | ||||||
|  |     while (currentBlock != NULL) { | ||||||
|  |       Block* nextBlock = currentBlock->next; | ||||||
|  |       _allocator.deallocate(currentBlock); | ||||||
|  |       currentBlock = nextBlock; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   size_t size() const { | ||||||
|  |     size_t total = 0; | ||||||
|  |     for (const Block* b = _head; b; b = b->next) total += b->size; | ||||||
|  |     return total; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   virtual void* alloc(size_t bytes) { | ||||||
|  |     alignNextAlloc(); | ||||||
|  |     return canAllocInHead(bytes) ? allocInHead(bytes) : allocInNewBlock(bytes); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   class String { | ||||||
|  |    public: | ||||||
|  |     String(DynamicJsonBufferBase* parent) | ||||||
|  |         : _parent(parent), _start(NULL), _length(0) {} | ||||||
|  |  | ||||||
|  |     void append(char c) { | ||||||
|  |       if (_parent->canAllocInHead(1)) { | ||||||
|  |         char* end = static_cast<char*>(_parent->allocInHead(1)); | ||||||
|  |         *end = c; | ||||||
|  |         if (_length == 0) _start = end; | ||||||
|  |       } else { | ||||||
|  |         char* newStart = | ||||||
|  |             static_cast<char*>(_parent->allocInNewBlock(_length + 1)); | ||||||
|  |         if (_start && newStart) memcpy(newStart, _start, _length); | ||||||
|  |         newStart[_length] = c; | ||||||
|  |         _start = newStart; | ||||||
|  |       } | ||||||
|  |       _length++; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const char* c_str() { | ||||||
|  |       append(0); | ||||||
|  |       return _start; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |    private: | ||||||
|  |     DynamicJsonBufferBase* _parent; | ||||||
|  |     char* _start; | ||||||
|  |     int _length; | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   String startString() { | ||||||
|  |     return String(this); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   void alignNextAlloc() { | ||||||
|  |     if (_head) _head->size = this->round_size_up(_head->size); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   bool canAllocInHead(size_t bytes) const { | ||||||
|  |     return _head != NULL && _head->size + bytes <= _head->capacity; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void* allocInHead(size_t bytes) { | ||||||
|  |     void* p = _head->data + _head->size; | ||||||
|  |     _head->size += bytes; | ||||||
|  |     return p; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void* allocInNewBlock(size_t bytes) { | ||||||
|  |     size_t capacity = _nextBlockCapacity; | ||||||
|  |     if (bytes > capacity) capacity = bytes; | ||||||
|  |     if (!addNewBlock(capacity)) return NULL; | ||||||
|  |     _nextBlockCapacity *= 2; | ||||||
|  |     return allocInHead(bytes); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   bool addNewBlock(size_t capacity) { | ||||||
|  |     size_t bytes = sizeof(EmptyBlock) + capacity; | ||||||
|  |     Block* block = static_cast<Block*>(_allocator.allocate(bytes)); | ||||||
|  |     if (block == NULL) return false; | ||||||
|  |     block->capacity = capacity; | ||||||
|  |     block->size = 0; | ||||||
|  |     block->next = _head; | ||||||
|  |     _head = block; | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   TAllocator _allocator; | ||||||
|  |   Block* _head; | ||||||
|  |   size_t _nextBlockCapacity; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #if defined(__clang__) | ||||||
|  | #pragma clang diagnostic pop | ||||||
|  | #elif defined(__GNUC__) | ||||||
|  | #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) | ||||||
|  | #pragma GCC diagnostic pop | ||||||
|  | #endif | ||||||
|  | #endif | ||||||
|  |  | ||||||
| // Implements a JsonBuffer with dynamic memory allocation. | // Implements a JsonBuffer with dynamic memory allocation. | ||||||
| // You are strongly encouraged to consider using StaticJsonBuffer which is much | // You are strongly encouraged to consider using StaticJsonBuffer which is much | ||||||
| // more suitable for embedded systems. | // more suitable for embedded systems. | ||||||
| typedef Internals::BlockJsonBuffer<Internals::DefaultAllocator> | typedef DynamicJsonBufferBase<DefaultAllocator> DynamicJsonBuffer; | ||||||
|     DynamicJsonBuffer; |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -51,58 +51,6 @@ class JsonBuffer { | |||||||
|   // allocation fails. |   // allocation fails. | ||||||
|   JsonObject &createObject(); |   JsonObject &createObject(); | ||||||
|  |  | ||||||
|   // Allocates and populate a JsonArray from a JSON string. |  | ||||||
|   // |  | ||||||
|   // The First argument is a pointer to the JSON string, the memory must be |  | ||||||
|   // writable |  | ||||||
|   // because the parser will insert null-terminators and replace escaped chars. |  | ||||||
|   // |  | ||||||
|   // The second argument set the nesting limit |  | ||||||
|   // |  | ||||||
|   // Returns a reference to the new JsonObject or JsonObject::invalid() if the |  | ||||||
|   // allocation fails. |  | ||||||
|   JsonArray &parseArray( |  | ||||||
|       char *json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT); |  | ||||||
|  |  | ||||||
|   // With this overload, the JsonBuffer will make a copy of the string |  | ||||||
|   template <typename TString> |  | ||||||
|   JsonArray &parseArray(const TString &json, |  | ||||||
|                         uint8_t nesting = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { |  | ||||||
|     return parseArray(strdup(json), nesting); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // Allocates and populate a JsonObject from a JSON string. |  | ||||||
|   // |  | ||||||
|   // The First argument is a pointer to the JSON string, the memory must be |  | ||||||
|   // writable |  | ||||||
|   // because the parser will insert null-terminators and replace escaped chars. |  | ||||||
|   // |  | ||||||
|   // The second argument set the nesting limit |  | ||||||
|   // |  | ||||||
|   // Returns a reference to the new JsonObject or JsonObject::invalid() if the |  | ||||||
|   // allocation fails. |  | ||||||
|   JsonObject &parseObject( |  | ||||||
|       char *json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT); |  | ||||||
|  |  | ||||||
|   // With this overload, the JsonBuffer will make a copy of the string |  | ||||||
|   template <typename TString> |  | ||||||
|   JsonObject &parseObject(const TString &json, |  | ||||||
|                           uint8_t nesting = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { |  | ||||||
|     return parseObject(strdup(json), nesting); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // Generalized version of parseArray() and parseObject(), also works for |  | ||||||
|   // integral types. |  | ||||||
|   JsonVariant parse(char *json, |  | ||||||
|                     uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT); |  | ||||||
|  |  | ||||||
|   // With this overload, the JsonBuffer will make a copy of the string |  | ||||||
|   template <typename TString> |  | ||||||
|   JsonVariant parse(const TString &json, |  | ||||||
|                     uint8_t nesting = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { |  | ||||||
|     return parse(strdup(json), nesting); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // Duplicate a string |   // Duplicate a string | ||||||
|   template <typename TString> |   template <typename TString> | ||||||
|   char *strdup(const TString &src) { |   char *strdup(const TString &src) { | ||||||
| @@ -114,7 +62,7 @@ class JsonBuffer { | |||||||
|   virtual void *alloc(size_t size) = 0; |   virtual void *alloc(size_t size) = 0; | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   // Preserve aligment if nessary |   // Preserve aligment if necessary | ||||||
|   static FORCE_INLINE size_t round_size_up(size_t bytes) { |   static FORCE_INLINE size_t round_size_up(size_t bytes) { | ||||||
| #if ARDUINOJSON_ENABLE_ALIGNMENT | #if ARDUINOJSON_ENABLE_ALIGNMENT | ||||||
|     const size_t x = sizeof(void *) - 1; |     const size_t x = sizeof(void *) - 1; | ||||||
|   | |||||||
							
								
								
									
										97
									
								
								include/ArduinoJson/JsonBufferBase.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								include/ArduinoJson/JsonBufferBase.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,97 @@ | |||||||
|  | // Copyright Benoit Blanchon 2014-2016 | ||||||
|  | // MIT License | ||||||
|  | // | ||||||
|  | // Arduino JSON library | ||||||
|  | // https://github.com/bblanchon/ArduinoJson | ||||||
|  | // If you like this project, please add a star! | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "Deserialization/JsonParser.hpp" | ||||||
|  |  | ||||||
|  | #if defined(__clang__) | ||||||
|  | #pragma clang diagnostic push | ||||||
|  | #pragma clang diagnostic ignored "-Wnon-virtual-dtor" | ||||||
|  | #elif defined(__GNUC__) | ||||||
|  | #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) | ||||||
|  | #pragma GCC diagnostic push | ||||||
|  | #endif | ||||||
|  | #pragma GCC diagnostic ignored "-Wnon-virtual-dtor" | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | namespace ArduinoJson { | ||||||
|  | template <typename TDerived> | ||||||
|  | class JsonBufferBase : public JsonBuffer { | ||||||
|  |  public: | ||||||
|  |   // Allocates and populate a JsonArray from a JSON string. | ||||||
|  |   // | ||||||
|  |   // The First argument is a pointer to the JSON string, the memory must be | ||||||
|  |   // writable | ||||||
|  |   // because the parser will insert null-terminators and replace escaped chars. | ||||||
|  |   // | ||||||
|  |   // The second argument set the nesting limit | ||||||
|  |   // | ||||||
|  |   // Returns a reference to the new JsonObject or JsonObject::invalid() if the | ||||||
|  |   // allocation fails. | ||||||
|  |   // With this overload, the JsonBuffer will make a copy of the string | ||||||
|  |   template <typename TString> | ||||||
|  |   JsonArray &parseArray( | ||||||
|  |       const TString &json, | ||||||
|  |       uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { | ||||||
|  |     return Internals::makeParser(that(), json, nestingLimit).parseArray(); | ||||||
|  |   } | ||||||
|  |   template <typename TString> | ||||||
|  |   JsonArray &parseArray( | ||||||
|  |       TString &json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { | ||||||
|  |     return Internals::makeParser(that(), json, nestingLimit).parseArray(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Allocates and populate a JsonObject from a JSON string. | ||||||
|  |   // | ||||||
|  |   // The First argument is a pointer to the JSON string, the memory must be | ||||||
|  |   // writable | ||||||
|  |   // because the parser will insert null-terminators and replace escaped chars. | ||||||
|  |   // | ||||||
|  |   // The second argument set the nesting limit | ||||||
|  |   // | ||||||
|  |   // Returns a reference to the new JsonObject or JsonObject::invalid() if the | ||||||
|  |   // allocation fails. | ||||||
|  |   template <typename TString> | ||||||
|  |   JsonObject &parseObject( | ||||||
|  |       const TString &json, | ||||||
|  |       uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { | ||||||
|  |     return Internals::makeParser(that(), json, nestingLimit).parseObject(); | ||||||
|  |   } | ||||||
|  |   template <typename TString> | ||||||
|  |   JsonObject &parseObject( | ||||||
|  |       TString &json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { | ||||||
|  |     return Internals::makeParser(that(), json, nestingLimit).parseObject(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Generalized version of parseArray() and parseObject(), also works for | ||||||
|  |   // integral types. | ||||||
|  |   template <typename TString> | ||||||
|  |   JsonVariant parse(const TString &json, | ||||||
|  |                     uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { | ||||||
|  |     return Internals::makeParser(that(), json, nestingLimit).parseVariant(); | ||||||
|  |   } | ||||||
|  |   template <typename TString> | ||||||
|  |   JsonVariant parse(TString &json, | ||||||
|  |                     uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { | ||||||
|  |     return Internals::makeParser(that(), json, nestingLimit).parseVariant(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   TDerived *that() { | ||||||
|  |     return static_cast<TDerived *>(this); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #if defined(__clang__) | ||||||
|  | #pragma clang diagnostic pop | ||||||
|  | #elif defined(__GNUC__) | ||||||
|  | #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) | ||||||
|  | #pragma GCC diagnostic pop | ||||||
|  | #endif | ||||||
|  | #endif | ||||||
| @@ -18,21 +18,3 @@ inline ArduinoJson::JsonObject &ArduinoJson::JsonBuffer::createObject() { | |||||||
|   JsonObject *ptr = new (this) JsonObject(this); |   JsonObject *ptr = new (this) JsonObject(this); | ||||||
|   return ptr ? *ptr : JsonObject::invalid(); |   return ptr ? *ptr : JsonObject::invalid(); | ||||||
| } | } | ||||||
|  |  | ||||||
| inline ArduinoJson::JsonArray &ArduinoJson::JsonBuffer::parseArray( |  | ||||||
|     char *json, uint8_t nestingLimit) { |  | ||||||
|   Internals::JsonParser parser(this, json, nestingLimit); |  | ||||||
|   return parser.parseArray(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| inline ArduinoJson::JsonObject &ArduinoJson::JsonBuffer::parseObject( |  | ||||||
|     char *json, uint8_t nestingLimit) { |  | ||||||
|   Internals::JsonParser parser(this, json, nestingLimit); |  | ||||||
|   return parser.parseObject(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| inline ArduinoJson::JsonVariant ArduinoJson::JsonBuffer::parse( |  | ||||||
|     char *json, uint8_t nestingLimit) { |  | ||||||
|   Internals::JsonParser parser(this, json, nestingLimit); |  | ||||||
|   return parser.parseVariant(); |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "Internals/StringFuncs.hpp" | #include "Data/StringFuncs.hpp" | ||||||
| #include "JsonVariantBase.hpp" | #include "JsonVariantBase.hpp" | ||||||
| #include "TypeTraits/EnableIf.hpp" | #include "TypeTraits/EnableIf.hpp" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "JsonBuffer.hpp" | #include "JsonBufferBase.hpp" | ||||||
|  |  | ||||||
| #if defined(__clang__) | #if defined(__clang__) | ||||||
| #pragma clang diagnostic push | #pragma clang diagnostic push | ||||||
| @@ -21,32 +21,87 @@ | |||||||
|  |  | ||||||
| namespace ArduinoJson { | namespace ArduinoJson { | ||||||
|  |  | ||||||
| // Implements a JsonBuffer with fixed memory allocation. | class StaticJsonBufferBase : public JsonBufferBase<StaticJsonBufferBase> { | ||||||
| // The template paramenter CAPACITY specifies the capacity of the buffer in |  | ||||||
| // bytes. |  | ||||||
| template <size_t CAPACITY> |  | ||||||
| class StaticJsonBuffer : public JsonBuffer { |  | ||||||
|  public: |  public: | ||||||
|   explicit StaticJsonBuffer() : _size(0) {} |   class String { | ||||||
|  |    public: | ||||||
|  |     String(StaticJsonBufferBase* parent) : _parent(parent) { | ||||||
|  |       _start = parent->_buffer + parent->_size; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void append(char c) { | ||||||
|  |       if (_parent->canAlloc(1)) { | ||||||
|  |         char* last = static_cast<char*>(_parent->doAlloc(1)); | ||||||
|  |         *last = c; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const char* c_str() const { | ||||||
|  |       if (_parent->canAlloc(1)) { | ||||||
|  |         char* last = static_cast<char*>(_parent->doAlloc(1)); | ||||||
|  |         *last = '\0'; | ||||||
|  |         return _start; | ||||||
|  |       } else { | ||||||
|  |         return NULL; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |    private: | ||||||
|  |     StaticJsonBufferBase* _parent; | ||||||
|  |     char* _start; | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   StaticJsonBufferBase(char* buffer, size_t capa) | ||||||
|  |       : _buffer(buffer), _capacity(capa), _size(0) {} | ||||||
|  |  | ||||||
|   size_t capacity() const { |   size_t capacity() const { | ||||||
|     return CAPACITY; |     return _capacity; | ||||||
|   } |   } | ||||||
|   size_t size() const { |   size_t size() const { | ||||||
|     return _size; |     return _size; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   virtual void* alloc(size_t bytes) { |   virtual void* alloc(size_t bytes) { | ||||||
|     if (_size + bytes > CAPACITY) return NULL; |     alignNextAlloc(); | ||||||
|     void* p = &_buffer[_size]; |     if (!canAlloc(bytes)) return NULL; | ||||||
|     _size += round_size_up(bytes); |     return doAlloc(bytes); | ||||||
|     return p; |   } | ||||||
|  |  | ||||||
|  |   String startString() { | ||||||
|  |     return String(this); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  private: |  private: | ||||||
|   uint8_t _buffer[CAPACITY]; |   void alignNextAlloc() { | ||||||
|  |     _size = round_size_up(_size); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   bool canAlloc(size_t bytes) const { | ||||||
|  |     return _size + bytes <= _capacity; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void* doAlloc(size_t bytes) { | ||||||
|  |     void* p = &_buffer[_size]; | ||||||
|  |     _size += bytes; | ||||||
|  |     return p; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   char* _buffer; | ||||||
|  |   size_t _capacity; | ||||||
|   size_t _size; |   size_t _size; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | // Implements a JsonBuffer with fixed memory allocation. | ||||||
|  | // The template paramenter CAPACITY specifies the capacity of the buffer in | ||||||
|  | // bytes. | ||||||
|  | template <size_t CAPACITY> | ||||||
|  | class StaticJsonBuffer : public StaticJsonBufferBase { | ||||||
|  |  public: | ||||||
|  |   explicit StaticJsonBuffer() : StaticJsonBufferBase(_buffer, CAPACITY) {} | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   char _buffer[CAPACITY]; | ||||||
|  | }; | ||||||
| } | } | ||||||
|  |  | ||||||
| #if defined(__clang__) | #if defined(__clang__) | ||||||
|   | |||||||
| @@ -30,13 +30,19 @@ TEST_F(DynamicJsonBuffer_Basic_Tests, ReturnDifferentPointer) { | |||||||
|   ASSERT_NE(p1, p2); |   ASSERT_NE(p1, p2); | ||||||
| } | } | ||||||
|  |  | ||||||
| TEST_F(DynamicJsonBuffer_Basic_Tests, Alignment) { | static bool isAligned(void* ptr) { | ||||||
|   size_t mask = sizeof(void*) - 1; |   const size_t mask = sizeof(void*) - 1; | ||||||
|  |   size_t addr = reinterpret_cast<size_t>(ptr); | ||||||
|  |   return (addr & mask) == 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|   for (size_t size = 1; size <= sizeof(void*); size++) { | TEST_F(DynamicJsonBuffer_Basic_Tests, Alignment) { | ||||||
|     size_t addr = reinterpret_cast<size_t>(buffer.alloc(1)); |   // make room for tow but not three | ||||||
|     ASSERT_EQ(0, addr & mask); |   buffer = DynamicJsonBuffer(2 * sizeof(void*) + 1); | ||||||
|   } |  | ||||||
|  |   ASSERT_TRUE(isAligned(buffer.alloc(1)));  // this on is aligned by design | ||||||
|  |   ASSERT_TRUE(isAligned(buffer.alloc(1)));  // this one fits in the first block | ||||||
|  |   ASSERT_TRUE(isAligned(buffer.alloc(1)));  // this one requires a new block | ||||||
| } | } | ||||||
|  |  | ||||||
| TEST_F(DynamicJsonBuffer_Basic_Tests, strdup) { | TEST_F(DynamicJsonBuffer_Basic_Tests, strdup) { | ||||||
|   | |||||||
| @@ -5,18 +5,20 @@ | |||||||
| // https://github.com/bblanchon/ArduinoJson | // https://github.com/bblanchon/ArduinoJson | ||||||
| // If you like this project, please add a star! | // If you like this project, please add a star! | ||||||
|  |  | ||||||
| #include <gtest/gtest.h> |  | ||||||
| #include <ArduinoJson.h> | #include <ArduinoJson.h> | ||||||
|  | #include <gtest/gtest.h> | ||||||
|  |  | ||||||
| class NoMemoryAllocator { | class NoMemoryAllocator { | ||||||
|  public: |  public: | ||||||
|   void* allocate(size_t) { return NULL; } |   void* allocate(size_t) { | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|   void deallocate(void*) {} |   void deallocate(void*) {} | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class DynamicJsonBuffer_NoMemory_Tests : public ::testing::Test { | class DynamicJsonBuffer_NoMemory_Tests : public ::testing::Test { | ||||||
|  protected: |  protected: | ||||||
|   Internals::BlockJsonBuffer<NoMemoryAllocator> _jsonBuffer; |   DynamicJsonBufferBase<NoMemoryAllocator> _jsonBuffer; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| TEST_F(DynamicJsonBuffer_NoMemory_Tests, FixCodeCoverage) { | TEST_F(DynamicJsonBuffer_NoMemory_Tests, FixCodeCoverage) { | ||||||
|   | |||||||
							
								
								
									
										48
									
								
								test/DynamicJsonBuffer_String_Tests.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								test/DynamicJsonBuffer_String_Tests.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | |||||||
|  | // Copyright Benoit Blanchon 2014-2016 | ||||||
|  | // MIT License | ||||||
|  | // | ||||||
|  | // Arduino JSON library | ||||||
|  | // https://github.com/bblanchon/ArduinoJson | ||||||
|  | // If you like this project, please add a star! | ||||||
|  |  | ||||||
|  | #include <ArduinoJson.h> | ||||||
|  | #include <gtest/gtest.h> | ||||||
|  |  | ||||||
|  | TEST(DynamicJsonBuffer_String_Tests, WorksWhenBufferIsBigEnough) { | ||||||
|  |   DynamicJsonBuffer jsonBuffer(6); | ||||||
|  |  | ||||||
|  |   DynamicJsonBuffer::String str = jsonBuffer.startString(); | ||||||
|  |   str.append('h'); | ||||||
|  |   str.append('e'); | ||||||
|  |   str.append('l'); | ||||||
|  |   str.append('l'); | ||||||
|  |   str.append('o'); | ||||||
|  |  | ||||||
|  |   ASSERT_STREQ("hello", str.c_str()); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | TEST(DynamicJsonBuffer_String_Tests, GrowsWhenBufferIsTooSmall) { | ||||||
|  |   DynamicJsonBuffer jsonBuffer(5); | ||||||
|  |  | ||||||
|  |   DynamicJsonBuffer::String str = jsonBuffer.startString(); | ||||||
|  |   str.append('h'); | ||||||
|  |   str.append('e'); | ||||||
|  |   str.append('l'); | ||||||
|  |   str.append('l'); | ||||||
|  |   str.append('o'); | ||||||
|  |  | ||||||
|  |   ASSERT_STREQ("hello", str.c_str()); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | TEST(DynamicJsonBuffer_String_Tests, SizeIncreases) { | ||||||
|  |   DynamicJsonBuffer jsonBuffer(5); | ||||||
|  |  | ||||||
|  |   DynamicJsonBuffer::String str = jsonBuffer.startString(); | ||||||
|  |   ASSERT_EQ(0, jsonBuffer.size()); | ||||||
|  |  | ||||||
|  |   str.append('h'); | ||||||
|  |   ASSERT_EQ(1, jsonBuffer.size()); | ||||||
|  |  | ||||||
|  |   str.c_str(); | ||||||
|  |   ASSERT_EQ(2, jsonBuffer.size()); | ||||||
|  | } | ||||||
| @@ -5,12 +5,12 @@ | |||||||
| // https://github.com/bblanchon/ArduinoJson | // https://github.com/bblanchon/ArduinoJson | ||||||
| // If you like this project, please add a star! | // If you like this project, please add a star! | ||||||
|  |  | ||||||
| #include <gtest/gtest.h> |  | ||||||
| #include <ArduinoJson.h> | #include <ArduinoJson.h> | ||||||
|  | #include <gtest/gtest.h> | ||||||
|  |  | ||||||
| class StaticJsonBuffer_ParseArray_Tests : public testing::Test { | class StaticJsonBuffer_ParseArray_Tests : public testing::Test { | ||||||
|  protected: |  protected: | ||||||
|   void with(JsonBuffer& jsonBuffer) { |   void with(StaticJsonBufferBase& jsonBuffer) { | ||||||
|     _jsonBuffer = &jsonBuffer; |     _jsonBuffer = &jsonBuffer; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -27,7 +27,7 @@ class StaticJsonBuffer_ParseArray_Tests : public testing::Test { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|  private: |  private: | ||||||
|   JsonBuffer* _jsonBuffer; |   StaticJsonBufferBase* _jsonBuffer; | ||||||
|   char _jsonString[256]; |   char _jsonString[256]; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -86,3 +86,11 @@ TEST_F(StaticJsonBuffer_ParseArray_Tests, ConstCharPtrNull) { | |||||||
|                    .parseArray(static_cast<const char*>(0)) |                    .parseArray(static_cast<const char*>(0)) | ||||||
|                    .success()); |                    .success()); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | TEST_F(StaticJsonBuffer_ParseArray_Tests, CopyStringNotSpaces) { | ||||||
|  |   StaticJsonBuffer<100> jsonBuffer; | ||||||
|  |   jsonBuffer.parseArray("  [ \"1234567\" ] "); | ||||||
|  |   ASSERT_EQ(JSON_ARRAY_SIZE(1) + sizeof("1234567"), jsonBuffer.size()); | ||||||
|  |   // note we use a string of 8 bytes to be sure that the StaticJsonBuffer | ||||||
|  |   // will not insert bytes to enforce alignement | ||||||
|  | } | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ | |||||||
|  |  | ||||||
| class StaticJsonBuffer_ParseObject_Tests : public testing::Test { | class StaticJsonBuffer_ParseObject_Tests : public testing::Test { | ||||||
|  protected: |  protected: | ||||||
|   void with(JsonBuffer& jsonBuffer) { |   void with(StaticJsonBufferBase& jsonBuffer) { | ||||||
|     _jsonBuffer = &jsonBuffer; |     _jsonBuffer = &jsonBuffer; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -27,7 +27,7 @@ class StaticJsonBuffer_ParseObject_Tests : public testing::Test { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|  private: |  private: | ||||||
|   JsonBuffer* _jsonBuffer; |   StaticJsonBufferBase* _jsonBuffer; | ||||||
|   char _jsonString[256]; |   char _jsonString[256]; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										48
									
								
								test/StaticJsonBuffer_String_Tests.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								test/StaticJsonBuffer_String_Tests.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | |||||||
|  | // Copyright Benoit Blanchon 2014-2016 | ||||||
|  | // MIT License | ||||||
|  | // | ||||||
|  | // Arduino JSON library | ||||||
|  | // https://github.com/bblanchon/ArduinoJson | ||||||
|  | // If you like this project, please add a star! | ||||||
|  |  | ||||||
|  | #include <ArduinoJson.h> | ||||||
|  | #include <gtest/gtest.h> | ||||||
|  |  | ||||||
|  | TEST(StaticJsonBuffer_String_Tests, WorksWhenBufferIsBigEnough) { | ||||||
|  |   StaticJsonBuffer<6> jsonBuffer; | ||||||
|  |  | ||||||
|  |   StaticJsonBufferBase::String str = jsonBuffer.startString(); | ||||||
|  |   str.append('h'); | ||||||
|  |   str.append('e'); | ||||||
|  |   str.append('l'); | ||||||
|  |   str.append('l'); | ||||||
|  |   str.append('o'); | ||||||
|  |  | ||||||
|  |   ASSERT_STREQ("hello", str.c_str()); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | TEST(StaticJsonBuffer_String_Tests, ReturnsNullWhenTooSmall) { | ||||||
|  |   StaticJsonBuffer<5> jsonBuffer; | ||||||
|  |  | ||||||
|  |   StaticJsonBufferBase::String str = jsonBuffer.startString(); | ||||||
|  |   str.append('h'); | ||||||
|  |   str.append('e'); | ||||||
|  |   str.append('l'); | ||||||
|  |   str.append('l'); | ||||||
|  |   str.append('o'); | ||||||
|  |  | ||||||
|  |   ASSERT_EQ(NULL, str.c_str()); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | TEST(StaticJsonBuffer_String_Tests, SizeIncreases) { | ||||||
|  |   StaticJsonBuffer<5> jsonBuffer; | ||||||
|  |  | ||||||
|  |   StaticJsonBufferBase::String str = jsonBuffer.startString(); | ||||||
|  |   ASSERT_EQ(0, jsonBuffer.size()); | ||||||
|  |  | ||||||
|  |   str.append('h'); | ||||||
|  |   ASSERT_EQ(1, jsonBuffer.size()); | ||||||
|  |  | ||||||
|  |   str.c_str(); | ||||||
|  |   ASSERT_EQ(2, jsonBuffer.size()); | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user