mirror of
				https://github.com/eledio-devices/thirdparty-ArduinoJson.git
				synced 2025-10-31 16:14:11 +01:00 
			
		
		
		
	JsonArray::remove() and JsonObject::remove() now release the memory of strings
This commit is contained in:
		| @@ -8,7 +8,8 @@ HEAD | |||||||
| * Removed `JsonObject::is<T>(k)` and `JsonObject::set(k,v)` | * Removed `JsonObject::is<T>(k)` and `JsonObject::set(k,v)` | ||||||
| * Replaced `T JsonArray::get<T>(i)` with `JsonVariant JsonArray::get(i)` | * Replaced `T JsonArray::get<T>(i)` with `JsonVariant JsonArray::get(i)` | ||||||
| * Replaced `T JsonObject::get<T>(k)` with `JsonVariant JsonObject::get(k)` | * Replaced `T JsonObject::get<T>(k)` with `JsonVariant JsonObject::get(k)` | ||||||
| * `JsonArray::remove()` and `JsonObject::remove()` now release the memory of the variant | * `JsonArray::remove()` and `JsonObject::remove()` now release the memory | ||||||
|  | * Added `JSON_STRING_SIZE()` | ||||||
|  |  | ||||||
| v6.5.0-beta (2018-10-13) | v6.5.0-beta (2018-10-13) | ||||||
| ----------- | ----------- | ||||||
|   | |||||||
| @@ -11,6 +11,8 @@ if(${COVERAGE}) | |||||||
| 	set(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage") | 	set(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage") | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
|  | add_definitions(-DARDUINOJSON_DEBUG) | ||||||
|  |  | ||||||
| include_directories(${CMAKE_CURRENT_LIST_DIR}/src) | include_directories(${CMAKE_CURRENT_LIST_DIR}/src) | ||||||
| add_subdirectory(third-party/catch) | add_subdirectory(third-party/catch) | ||||||
| add_subdirectory(test) | add_subdirectory(test) | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| # CAUTION: this file is invoked by https://github.com/google/oss-fuzz | # CAUTION: this file is invoked by https://github.com/google/oss-fuzz | ||||||
|  |  | ||||||
| CXXFLAGS += -I../src | CXXFLAGS += -I../src -DARDUINOJSON_DEBUG | ||||||
|  |  | ||||||
| all: \ | all: \ | ||||||
| 	$(OUT)/json_fuzzer \ | 	$(OUT)/json_fuzzer \ | ||||||
|   | |||||||
| @@ -5,17 +5,34 @@ | |||||||
| // This file is NOT use by Google's OSS fuzz | // This file is NOT use by Google's OSS fuzz | ||||||
| // I only use it to reproduce the bugs found | // I only use it to reproduce the bugs found | ||||||
|  |  | ||||||
| #include <stdint.h> | #include <stdint.h>  // size_t | ||||||
| #include <fstream> | #include <stdio.h>   // fopen et al. | ||||||
|  | #include <stdlib.h>  // exit | ||||||
| #include <iostream> | #include <iostream> | ||||||
| #include <string> | #include <vector> | ||||||
|  |  | ||||||
| extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size); | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size); | ||||||
|  |  | ||||||
| std::string read(const char* path) { | std::vector<uint8_t> read(const char* path) { | ||||||
|   std::ifstream file(path); |   FILE* f = fopen(path, "rb"); | ||||||
|   return std::string(std::istreambuf_iterator<char>(file), |   if (!f) { | ||||||
|                      std::istreambuf_iterator<char>()); |     std::cerr << "Failed to open " << path << std::endl; | ||||||
|  |     exit(1); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   fseek(f, 0, SEEK_END); | ||||||
|  |   size_t size = ftell(f); | ||||||
|  |   fseek(f, 0, SEEK_SET); | ||||||
|  |  | ||||||
|  |   std::vector<uint8_t> buffer(size); | ||||||
|  |   if (fread(buffer.data(), 1, size, f) != size) { | ||||||
|  |     fclose(f); | ||||||
|  |     std::cerr << "Failed to read " << path << std::endl; | ||||||
|  |     exit(1); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   fclose(f); | ||||||
|  |   return buffer; | ||||||
| } | } | ||||||
|  |  | ||||||
| int main(int argc, const char* argv[]) { | int main(int argc, const char* argv[]) { | ||||||
| @@ -26,9 +43,8 @@ int main(int argc, const char* argv[]) { | |||||||
|  |  | ||||||
|   for (int i = 1; i < argc; i++) { |   for (int i = 1; i < argc; i++) { | ||||||
|     std::cout << "Loading " << argv[i] << std::endl; |     std::cout << "Loading " << argv[i] << std::endl; | ||||||
|     std::string buffer = read(argv[i]); |     std::vector<uint8_t> buffer = read(argv[i]); | ||||||
|     LLVMFuzzerTestOneInput(reinterpret_cast<const uint8_t*>(buffer.data()), |     LLVMFuzzerTestOneInput(buffer.data(), buffer.size()); | ||||||
|                            buffer.size()); |  | ||||||
|   } |   } | ||||||
|   return 0; |   return 0; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -130,16 +130,6 @@ | |||||||
| #endif | #endif | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifndef ARDUINOJSON_ENABLE_ALIGNMENT |  | ||||||
| #ifdef ARDUINO_ARCH_AVR |  | ||||||
| // alignment isn't needed for 8-bit AVR |  | ||||||
| #define ARDUINOJSON_ENABLE_ALIGNMENT 0 |  | ||||||
| #else |  | ||||||
| // but most processors need pointers to be align on word size |  | ||||||
| #define ARDUINOJSON_ENABLE_ALIGNMENT 1 |  | ||||||
| #endif |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| // Control the exponentiation threshold for big numbers | // Control the exponentiation threshold for big numbers | ||||||
| // CAUTION: cannot be more that 1e9 !!!! | // CAUTION: cannot be more that 1e9 !!!! | ||||||
| #ifndef ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD | #ifndef ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD | ||||||
|   | |||||||
| @@ -5,7 +5,6 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "JsonVariantData.hpp" | #include "JsonVariantData.hpp" | ||||||
| #include "Slot.hpp" |  | ||||||
| #include "SlotFunctions.hpp" | #include "SlotFunctions.hpp" | ||||||
|  |  | ||||||
| namespace ARDUINOJSON_NAMESPACE { | namespace ARDUINOJSON_NAMESPACE { | ||||||
| @@ -13,7 +12,7 @@ namespace ARDUINOJSON_NAMESPACE { | |||||||
| inline JsonVariantData* arrayAdd(JsonArrayData* arr, MemoryPool* pool) { | inline JsonVariantData* arrayAdd(JsonArrayData* arr, MemoryPool* pool) { | ||||||
|   if (!arr) return 0; |   if (!arr) return 0; | ||||||
|  |  | ||||||
|   Slot* slot = pool->allocSlot(); |   VariantSlot* slot = pool->allocVariant(); | ||||||
|   if (!slot) return 0; |   if (!slot) return 0; | ||||||
|  |  | ||||||
|   slot->next = 0; |   slot->next = 0; | ||||||
| @@ -29,20 +28,22 @@ inline JsonVariantData* arrayAdd(JsonArrayData* arr, MemoryPool* pool) { | |||||||
|     arr->tail = slot; |     arr->tail = slot; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   slot->value.keyIsOwned = false; | ||||||
|   return &slot->value; |   return &slot->value; | ||||||
| } | } | ||||||
|  |  | ||||||
| inline Slot* arrayGetSlot(const JsonArrayData* arr, size_t index) { | inline VariantSlot* arrayGetSlot(const JsonArrayData* arr, size_t index) { | ||||||
|   if (!arr) return 0; |   if (!arr) return 0; | ||||||
|   return slotAdvance(arr->head, index); |   return slotAdvance(arr->head, index); | ||||||
| } | } | ||||||
|  |  | ||||||
| inline JsonVariantData* arrayGet(const JsonArrayData* arr, size_t index) { | inline JsonVariantData* arrayGet(const JsonArrayData* arr, size_t index) { | ||||||
|   Slot* slot = arrayGetSlot(arr, index); |   VariantSlot* slot = arrayGetSlot(arr, index); | ||||||
|   return slot ? &slot->value : 0; |   return slot ? &slot->value : 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| inline void arrayRemove(JsonArrayData* arr, Slot* slot, MemoryPool* pool) { | inline void arrayRemove(JsonArrayData* arr, VariantSlot* slot, | ||||||
|  |                         MemoryPool* pool) { | ||||||
|   if (!arr || !slot) return; |   if (!arr || !slot) return; | ||||||
|  |  | ||||||
|   if (slot->prev) |   if (slot->prev) | ||||||
| @@ -73,7 +74,7 @@ inline bool arrayCopy(JsonArrayData* dst, const JsonArrayData* src, | |||||||
|                       MemoryPool* pool) { |                       MemoryPool* pool) { | ||||||
|   if (!dst || !src) return false; |   if (!dst || !src) return false; | ||||||
|   arrayClear(dst); |   arrayClear(dst); | ||||||
|   for (Slot* s = src->head; s; s = s->next) { |   for (VariantSlot* s = src->head; s; s = s->next) { | ||||||
|     if (!variantCopy(arrayAdd(dst, pool), &s->value, pool)) return false; |     if (!variantCopy(arrayAdd(dst, pool), &s->value, pool)) return false; | ||||||
|   } |   } | ||||||
|   return true; |   return true; | ||||||
| @@ -84,8 +85,8 @@ bool variantEquals(const JsonVariantData*, const JsonVariantData*); | |||||||
| inline bool arrayEquals(const JsonArrayData* a1, const JsonArrayData* a2) { | inline bool arrayEquals(const JsonArrayData* a1, const JsonArrayData* a2) { | ||||||
|   if (a1 == a2) return true; |   if (a1 == a2) return true; | ||||||
|   if (!a1 || !a2) return false; |   if (!a1 || !a2) return false; | ||||||
|   Slot* s1 = a1->head; |   VariantSlot* s1 = a1->head; | ||||||
|   Slot* s2 = a2->head; |   VariantSlot* s2 = a2->head; | ||||||
|   for (;;) { |   for (;;) { | ||||||
|     if (s1 == s2) return true; |     if (s1 == s2) return true; | ||||||
|     if (!s1 || !s2) return false; |     if (!s1 || !s2) return false; | ||||||
|   | |||||||
| @@ -26,13 +26,13 @@ enum JsonVariantType { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| struct JsonObjectData { | struct JsonObjectData { | ||||||
|   struct Slot *head; |   struct VariantSlot *head; | ||||||
|   struct Slot *tail; |   struct VariantSlot *tail; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct JsonArrayData { | struct JsonArrayData { | ||||||
|   struct Slot *head; |   struct VariantSlot *head; | ||||||
|   struct Slot *tail; |   struct VariantSlot *tail; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct RawData { | struct RawData { | ||||||
| @@ -48,6 +48,8 @@ union JsonVariantContent { | |||||||
|   JsonArrayData asArray; |   JsonArrayData asArray; | ||||||
|   JsonObjectData asObject; |   JsonObjectData asObject; | ||||||
|   const char *asString; |   const char *asString; | ||||||
|  |   struct StringSlot *asOwnedString; | ||||||
|  |   struct StringSlot *asOwnedRaw; | ||||||
|   struct { |   struct { | ||||||
|     const char *data; |     const char *data; | ||||||
|     size_t size; |     size_t size; | ||||||
| @@ -56,7 +58,7 @@ union JsonVariantContent { | |||||||
|  |  | ||||||
| // this struct must be a POD type to prevent error calling offsetof on clang | // this struct must be a POD type to prevent error calling offsetof on clang | ||||||
| struct JsonVariantData { | struct JsonVariantData { | ||||||
|   bool keyIsStatic : 1; |   bool keyIsOwned : 1; | ||||||
|   JsonVariantType type : 7; |   JsonVariantType type : 7; | ||||||
|   JsonVariantContent content; |   JsonVariantContent content; | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -11,11 +11,11 @@ | |||||||
| namespace ARDUINOJSON_NAMESPACE { | namespace ARDUINOJSON_NAMESPACE { | ||||||
|  |  | ||||||
| template <typename TKey> | template <typename TKey> | ||||||
| inline Slot* objectFindSlot(const JsonObjectData* obj, TKey key) { | inline VariantSlot* objectFindSlot(const JsonObjectData* obj, TKey key) { | ||||||
|   if (!obj) return 0; |   if (!obj) return 0; | ||||||
|   Slot* slot = obj->head; |   VariantSlot* slot = obj->head; | ||||||
|   while (slot) { |   while (slot) { | ||||||
|     if (key.equals(slot->key)) break; |     if (key.equals(slotGetKey(slot))) break; | ||||||
|     slot = slot->next; |     slot = slot->next; | ||||||
|   } |   } | ||||||
|   return slot; |   return slot; | ||||||
| @@ -29,7 +29,7 @@ inline bool objectContainsKey(const JsonObjectData* obj, const TKey& key) { | |||||||
| template <typename TKey> | template <typename TKey> | ||||||
| inline JsonVariantData* objectAdd(JsonObjectData* obj, TKey key, | inline JsonVariantData* objectAdd(JsonObjectData* obj, TKey key, | ||||||
|                                   MemoryPool* pool) { |                                   MemoryPool* pool) { | ||||||
|   Slot* slot = pool->allocSlot(); |   VariantSlot* slot = pool->allocVariant(); | ||||||
|   if (!slot) return 0; |   if (!slot) return 0; | ||||||
|  |  | ||||||
|   slot->next = 0; |   slot->next = 0; | ||||||
| @@ -58,7 +58,7 @@ inline JsonVariantData* objectSet(JsonObjectData* obj, TKey key, | |||||||
|   if (key.isNull()) return 0; |   if (key.isNull()) return 0; | ||||||
|  |  | ||||||
|   // search a matching key |   // search a matching key | ||||||
|   Slot* slot = objectFindSlot(obj, key); |   VariantSlot* slot = objectFindSlot(obj, key); | ||||||
|   if (slot) return &slot->value; |   if (slot) return &slot->value; | ||||||
|  |  | ||||||
|   return objectAdd(obj, key, pool); |   return objectAdd(obj, key, pool); | ||||||
| @@ -66,7 +66,7 @@ inline JsonVariantData* objectSet(JsonObjectData* obj, TKey key, | |||||||
|  |  | ||||||
| template <typename TKey> | template <typename TKey> | ||||||
| inline JsonVariantData* objectGet(const JsonObjectData* obj, TKey key) { | inline JsonVariantData* objectGet(const JsonObjectData* obj, TKey key) { | ||||||
|   Slot* slot = objectFindSlot(obj, key); |   VariantSlot* slot = objectFindSlot(obj, key); | ||||||
|   return slot ? &slot->value : 0; |   return slot ? &slot->value : 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -76,7 +76,8 @@ inline void objectClear(JsonObjectData* obj) { | |||||||
|   obj->tail = 0; |   obj->tail = 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| inline void objectRemove(JsonObjectData* obj, Slot* slot, MemoryPool* pool) { | inline void objectRemove(JsonObjectData* obj, VariantSlot* slot, | ||||||
|  |                          MemoryPool* pool) { | ||||||
|   if (!obj) return; |   if (!obj) return; | ||||||
|   if (!slot) return; |   if (!slot) return; | ||||||
|   if (slot->prev) |   if (slot->prev) | ||||||
| @@ -102,12 +103,12 @@ inline bool objectCopy(JsonObjectData* dst, const JsonObjectData* src, | |||||||
|                        MemoryPool* pool) { |                        MemoryPool* pool) { | ||||||
|   if (!dst || !src) return false; |   if (!dst || !src) return false; | ||||||
|   objectClear(dst); |   objectClear(dst); | ||||||
|   for (Slot* s = src->head; s; s = s->next) { |   for (VariantSlot* s = src->head; s; s = s->next) { | ||||||
|     JsonVariantData* var; |     JsonVariantData* var; | ||||||
|     if (s->value.keyIsStatic) |     if (s->value.keyIsOwned) | ||||||
|       var = objectAdd(dst, ZeroTerminatedRamStringConst(s->key), pool); |       var = objectAdd(dst, ZeroTerminatedRamString(s->ownedKey->value), pool); | ||||||
|     else |     else | ||||||
|       var = objectAdd(dst, ZeroTerminatedRamString(s->key), pool); |       var = objectAdd(dst, ZeroTerminatedRamStringConst(s->linkedKey), pool); | ||||||
|     if (!variantCopy(var, &s->value, pool)) return false; |     if (!variantCopy(var, &s->value, pool)) return false; | ||||||
|   } |   } | ||||||
|   return true; |   return true; | ||||||
| @@ -117,9 +118,9 @@ inline bool objectEquals(const JsonObjectData* o1, const JsonObjectData* o2) { | |||||||
|   if (o1 == o2) return true; |   if (o1 == o2) return true; | ||||||
|   if (!o1 || !o2) return false; |   if (!o1 || !o2) return false; | ||||||
|  |  | ||||||
|   for (Slot* s = o1->head; s; s = s->next) { |   for (VariantSlot* s = o1->head; s; s = s->next) { | ||||||
|     JsonVariantData* v1 = &s->value; |     JsonVariantData* v1 = &s->value; | ||||||
|     JsonVariantData* v2 = objectGet(o2, makeString(s->key)); |     JsonVariantData* v2 = objectGet(o2, makeString(slotGetKey(s))); | ||||||
|     if (!variantEquals(v1, v2)) return false; |     if (!variantEquals(v1, v2)) return false; | ||||||
|   } |   } | ||||||
|   return true; |   return true; | ||||||
|   | |||||||
| @@ -7,65 +7,80 @@ | |||||||
| #include "../Memory/MemoryPool.hpp" | #include "../Memory/MemoryPool.hpp" | ||||||
| #include "../Strings/StringTypes.hpp" | #include "../Strings/StringTypes.hpp" | ||||||
| #include "JsonVariantData.hpp" | #include "JsonVariantData.hpp" | ||||||
| #include "Slot.hpp" |  | ||||||
|  |  | ||||||
| namespace ARDUINOJSON_NAMESPACE { | namespace ARDUINOJSON_NAMESPACE { | ||||||
|  |  | ||||||
| template <typename TKey> | template <typename TKey> | ||||||
| inline bool slotSetKey(Slot* slot, TKey key, MemoryPool* pool) { | inline bool slotSetKey(VariantSlot* var, TKey key, MemoryPool* pool) { | ||||||
|   const char* dup = key.save(pool); |   StringSlot* slot = key.save(pool); | ||||||
|   if (!dup) return false; |   if (!slot) return false; | ||||||
|   slot->key = dup; |   var->ownedKey = slot; | ||||||
|   slot->value.keyIsStatic = false; |   var->value.keyIsOwned = true; | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| inline bool slotSetKey(Slot* slot, ZeroTerminatedRamStringConst key, | inline bool slotSetKey(VariantSlot* var, ZeroTerminatedRamStringConst key, | ||||||
|                        MemoryPool* pool) { |                        MemoryPool*) { | ||||||
|   slot->key = key.save(pool); |   var->linkedKey = key.c_str(); | ||||||
|   slot->value.keyIsStatic = true; |   var->value.keyIsOwned = false; | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| inline bool slotSetKey(Slot* slot, StringInMemoryPool key, MemoryPool* pool) { | inline bool slotSetKey(VariantSlot* var, StringInMemoryPool key, MemoryPool*) { | ||||||
|   slot->key = key.save(pool); |   var->ownedKey = key.slot(); | ||||||
|   slot->value.keyIsStatic = false; |   var->value.keyIsOwned = true; | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| inline const Slot* slotAdvance(const Slot* slot, size_t distance) { | inline const char* slotGetKey(const VariantSlot* var) { | ||||||
|   while (distance && slot) { |   return var->value.keyIsOwned ? var->ownedKey->value : var->linkedKey; | ||||||
|     slot = slot->next; | } | ||||||
|  |  | ||||||
|  | inline const VariantSlot* slotAdvance(const VariantSlot* var, size_t distance) { | ||||||
|  |   while (distance && var) { | ||||||
|  |     var = var->next; | ||||||
|     distance--; |     distance--; | ||||||
|   } |   } | ||||||
|   return slot; |   return var; | ||||||
| } | } | ||||||
|  |  | ||||||
| inline Slot* slotAdvance(Slot* slot, size_t distance) { | inline VariantSlot* slotAdvance(VariantSlot* var, size_t distance) { | ||||||
|   while (distance && slot) { |   while (distance && var) { | ||||||
|     slot = slot->next; |     var = var->next; | ||||||
|     distance--; |     distance--; | ||||||
|   } |   } | ||||||
|   return slot; |   return var; | ||||||
| } | } | ||||||
|  |  | ||||||
| inline size_t slotSize(const Slot* slot) { | inline size_t slotSize(const VariantSlot* var) { | ||||||
|   size_t n = 0; |   size_t n = 0; | ||||||
|   while (slot) { |   while (var) { | ||||||
|     n++; |     n++; | ||||||
|     slot = slot->next; |     var = var->next; | ||||||
|   } |   } | ||||||
|   return n; |   return n; | ||||||
| } | } | ||||||
|  |  | ||||||
| inline void slotFree(Slot* slot, MemoryPool* pool) { | inline void slotFree(VariantSlot* var, MemoryPool* pool) { | ||||||
|   const JsonVariantData& v = slot->value; |   const JsonVariantData& v = var->value; | ||||||
|   if (v.type == JSON_ARRAY || v.type == JSON_OBJECT) { |  | ||||||
|     for (Slot* s = v.content.asObject.head; s; s = s->next) { |   switch (v.type) { | ||||||
|  |     case JSON_ARRAY: | ||||||
|  |     case JSON_OBJECT: | ||||||
|  |       for (VariantSlot* s = v.content.asObject.head; s; s = s->next) { | ||||||
|         slotFree(s, pool); |         slotFree(s, pool); | ||||||
|       } |       } | ||||||
|  |       break; | ||||||
|  |     case JSON_OWNED_STRING: | ||||||
|  |     case JSON_OWNED_RAW: | ||||||
|  |       pool->freeString(v.content.asOwnedString); | ||||||
|  |       break; | ||||||
|  |     default: | ||||||
|  |       break; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   pool->freeSlot(slot); |   if (v.keyIsOwned) pool->freeString(var->ownedKey); | ||||||
|  |  | ||||||
|  |   pool->freeVariant(var); | ||||||
| } | } | ||||||
| }  // namespace ARDUINOJSON_NAMESPACE | }  // namespace ARDUINOJSON_NAMESPACE | ||||||
|   | |||||||
| @@ -10,7 +10,6 @@ | |||||||
| #include "ArrayFunctions.hpp" | #include "ArrayFunctions.hpp" | ||||||
| #include "JsonVariantData.hpp" | #include "JsonVariantData.hpp" | ||||||
| #include "ObjectFunctions.hpp" | #include "ObjectFunctions.hpp" | ||||||
| #include "Slot.hpp" |  | ||||||
|  |  | ||||||
| namespace ARDUINOJSON_NAMESPACE { | namespace ARDUINOJSON_NAMESPACE { | ||||||
|  |  | ||||||
| @@ -24,8 +23,9 @@ inline T variantAsIntegral(const JsonVariantData* var) { | |||||||
|     case JSON_NEGATIVE_INTEGER: |     case JSON_NEGATIVE_INTEGER: | ||||||
|       return T(~var->content.asInteger + 1); |       return T(~var->content.asInteger + 1); | ||||||
|     case JSON_LINKED_STRING: |     case JSON_LINKED_STRING: | ||||||
|     case JSON_OWNED_STRING: |  | ||||||
|       return parseInteger<T>(var->content.asString); |       return parseInteger<T>(var->content.asString); | ||||||
|  |     case JSON_OWNED_STRING: | ||||||
|  |       return parseInteger<T>(var->content.asOwnedString->value); | ||||||
|     case JSON_FLOAT: |     case JSON_FLOAT: | ||||||
|       return T(var->content.asFloat); |       return T(var->content.asFloat); | ||||||
|     default: |     default: | ||||||
| @@ -48,8 +48,9 @@ inline T variantAsFloat(const JsonVariantData* var) { | |||||||
|     case JSON_NEGATIVE_INTEGER: |     case JSON_NEGATIVE_INTEGER: | ||||||
|       return -static_cast<T>(var->content.asInteger); |       return -static_cast<T>(var->content.asInteger); | ||||||
|     case JSON_LINKED_STRING: |     case JSON_LINKED_STRING: | ||||||
|     case JSON_OWNED_STRING: |  | ||||||
|       return parseFloat<T>(var->content.asString); |       return parseFloat<T>(var->content.asString); | ||||||
|  |     case JSON_OWNED_STRING: | ||||||
|  |       return parseFloat<T>(var->content.asOwnedString->value); | ||||||
|     case JSON_FLOAT: |     case JSON_FLOAT: | ||||||
|       return static_cast<T>(var->content.asFloat); |       return static_cast<T>(var->content.asFloat); | ||||||
|     default: |     default: | ||||||
| @@ -59,12 +60,15 @@ inline T variantAsFloat(const JsonVariantData* var) { | |||||||
|  |  | ||||||
| inline const char* variantAsString(const JsonVariantData* var) { | inline const char* variantAsString(const JsonVariantData* var) { | ||||||
|   if (!var) return 0; |   if (!var) return 0; | ||||||
|   if (var && |   switch (var->type) { | ||||||
|       (var->type == JSON_LINKED_STRING || var->type == JSON_OWNED_STRING)) |     case JSON_LINKED_STRING: | ||||||
|       return var->content.asString; |       return var->content.asString; | ||||||
|   else |     case JSON_OWNED_STRING: | ||||||
|  |       return var->content.asOwnedString->value; | ||||||
|  |     default: | ||||||
|       return 0; |       return 0; | ||||||
|   } |   } | ||||||
|  | } | ||||||
|  |  | ||||||
| inline JsonArrayData* variantAsArray(JsonVariantData* var) { | inline JsonArrayData* variantAsArray(JsonVariantData* var) { | ||||||
|   if (var && var->type == JSON_ARRAY) |   if (var && var->type == JSON_ARRAY) | ||||||
| @@ -141,11 +145,10 @@ template <typename T> | |||||||
| inline bool variantSetOwnedRaw(JsonVariantData* var, SerializedValue<T> value, | inline bool variantSetOwnedRaw(JsonVariantData* var, SerializedValue<T> value, | ||||||
|                                MemoryPool* pool) { |                                MemoryPool* pool) { | ||||||
|   if (!var) return false; |   if (!var) return false; | ||||||
|   const char* dup = makeString(value.data(), value.size()).save(pool); |   StringSlot* slot = makeString(value.data(), value.size()).save(pool); | ||||||
|   if (dup) { |   if (slot) { | ||||||
|     var->type = JSON_OWNED_RAW; |     var->type = JSON_OWNED_RAW; | ||||||
|     var->content.asRaw.data = dup; |     var->content.asOwnedRaw = slot; | ||||||
|     var->content.asRaw.size = value.size(); |  | ||||||
|     return true; |     return true; | ||||||
|   } else { |   } else { | ||||||
|     var->type = JSON_NULL; |     var->type = JSON_NULL; | ||||||
| @@ -156,10 +159,10 @@ inline bool variantSetOwnedRaw(JsonVariantData* var, SerializedValue<T> value, | |||||||
| template <typename T> | template <typename T> | ||||||
| inline bool variantSetString(JsonVariantData* var, T value, MemoryPool* pool) { | inline bool variantSetString(JsonVariantData* var, T value, MemoryPool* pool) { | ||||||
|   if (!var) return false; |   if (!var) return false; | ||||||
|   const char* dup = value.save(pool); |   StringSlot* slot = value.save(pool); | ||||||
|   if (dup) { |   if (slot) { | ||||||
|     var->type = JSON_OWNED_STRING; |     var->type = JSON_OWNED_STRING; | ||||||
|     var->content.asString = dup; |     var->content.asOwnedString = slot; | ||||||
|     return true; |     return true; | ||||||
|   } else { |   } else { | ||||||
|     var->type = JSON_NULL; |     var->type = JSON_NULL; | ||||||
| @@ -167,6 +170,13 @@ inline bool variantSetString(JsonVariantData* var, T value, MemoryPool* pool) { | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | inline bool variantSetOwnedString(JsonVariantData* var, StringSlot* slot) { | ||||||
|  |   if (!var) return false; | ||||||
|  |   var->type = JSON_OWNED_STRING; | ||||||
|  |   var->content.asOwnedString = slot; | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  |  | ||||||
| inline bool variantSetString(JsonVariantData* var, const char* value) { | inline bool variantSetString(JsonVariantData* var, const char* value) { | ||||||
|   if (!var) return false; |   if (!var) return false; | ||||||
|   var->type = JSON_LINKED_STRING; |   var->type = JSON_LINKED_STRING; | ||||||
| @@ -174,11 +184,6 @@ inline bool variantSetString(JsonVariantData* var, const char* value) { | |||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| inline bool variantSetString(JsonVariantData* var, const char* value, |  | ||||||
|                              MemoryPool* pool) { |  | ||||||
|   return variantSetString(var, makeString(const_cast<char*>(value)), pool); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| inline void variantSetNull(JsonVariantData* var) { | inline void variantSetNull(JsonVariantData* var) { | ||||||
|   if (var) var->type = JSON_NULL; |   if (var) var->type = JSON_NULL; | ||||||
| } | } | ||||||
| @@ -212,15 +217,17 @@ inline bool variantCopy(JsonVariantData* dst, const JsonVariantData* src, | |||||||
|     case JSON_OBJECT: |     case JSON_OBJECT: | ||||||
|       return objectCopy(variantToObject(dst), &src->content.asObject, pool); |       return objectCopy(variantToObject(dst), &src->content.asObject, pool); | ||||||
|     case JSON_OWNED_STRING: |     case JSON_OWNED_STRING: | ||||||
|       return variantSetString(dst, src->content.asString, pool); |       return variantSetString( | ||||||
|  |           dst, makeString(src->content.asOwnedString->value), pool); | ||||||
|     case JSON_OWNED_RAW: |     case JSON_OWNED_RAW: | ||||||
|       return variantSetOwnedRaw( |       return variantSetOwnedRaw(dst, | ||||||
|           dst, |                                 serialized(src->content.asOwnedRaw->value, | ||||||
|           serialized(const_cast<char*>(src->content.asRaw.data), |                                            src->content.asOwnedRaw->size), | ||||||
|                      src->content.asRaw.size), |  | ||||||
|                                 pool); |                                 pool); | ||||||
|     default: |     default: | ||||||
|       *dst = *src; |       // caution: don't override keyIsOwned | ||||||
|  |       dst->type = src->type; | ||||||
|  |       dst->content = src->content; | ||||||
|       return true; |       return true; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @@ -260,11 +267,16 @@ inline bool variantEquals(const JsonVariantData* a, const JsonVariantData* b) { | |||||||
|  |  | ||||||
|   switch (a->type) { |   switch (a->type) { | ||||||
|     case JSON_LINKED_RAW: |     case JSON_LINKED_RAW: | ||||||
|     case JSON_OWNED_RAW: |  | ||||||
|     case JSON_LINKED_STRING: |     case JSON_LINKED_STRING: | ||||||
|     case JSON_OWNED_STRING: |  | ||||||
|       return !strcmp(a->content.asString, b->content.asString); |       return !strcmp(a->content.asString, b->content.asString); | ||||||
|  |  | ||||||
|  |     case JSON_OWNED_RAW: | ||||||
|  |     case JSON_OWNED_STRING: | ||||||
|  |       return a->content.asOwnedString->size == b->content.asOwnedString->size && | ||||||
|  |              !memcmp(a->content.asOwnedString->value, | ||||||
|  |                      b->content.asOwnedString->value, | ||||||
|  |                      a->content.asOwnedString->size); | ||||||
|  |  | ||||||
|     case JSON_BOOLEAN: |     case JSON_BOOLEAN: | ||||||
|     case JSON_POSITIVE_INTEGER: |     case JSON_POSITIVE_INTEGER: | ||||||
|     case JSON_NEGATIVE_INTEGER: |     case JSON_NEGATIVE_INTEGER: | ||||||
|   | |||||||
| @@ -18,6 +18,7 @@ template <typename TReader, typename TStringStorage> | |||||||
| class JsonDeserializer { | class JsonDeserializer { | ||||||
|   typedef typename remove_reference<TStringStorage>::type::StringBuilder |   typedef typename remove_reference<TStringStorage>::type::StringBuilder | ||||||
|       StringBuilder; |       StringBuilder; | ||||||
|  |   typedef typename StringBuilder::StringType StringType; | ||||||
|  |  | ||||||
|  public: |  public: | ||||||
|   JsonDeserializer(MemoryPool &memoryPool, TReader reader, |   JsonDeserializer(MemoryPool &memoryPool, TReader reader, | ||||||
| @@ -124,7 +125,7 @@ class JsonDeserializer { | |||||||
|     // Read each key value pair |     // Read each key value pair | ||||||
|     for (;;) { |     for (;;) { | ||||||
|       // Parse key |       // Parse key | ||||||
|       StringInMemoryPool key; |       StringType key; | ||||||
|       err = parseKey(key); |       err = parseKey(key); | ||||||
|       if (err) return err; |       if (err) return err; | ||||||
|  |  | ||||||
| @@ -165,7 +166,7 @@ class JsonDeserializer { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   DeserializationError parseKey(StringInMemoryPool &key) { |   DeserializationError parseKey(StringType &key) { | ||||||
|     if (isQuote(current())) { |     if (isQuote(current())) { | ||||||
|       return parseQuotedString(key); |       return parseQuotedString(key); | ||||||
|     } else { |     } else { | ||||||
| @@ -174,15 +175,15 @@ class JsonDeserializer { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   DeserializationError parseStringValue(JsonVariant variant) { |   DeserializationError parseStringValue(JsonVariant variant) { | ||||||
|     StringInMemoryPool value; |     StringType value; | ||||||
|     DeserializationError err = parseQuotedString(value); |     DeserializationError err = parseQuotedString(value); | ||||||
|     if (err) return err; |     if (err) return err; | ||||||
|     variant.set(value); |     variant.set(value); | ||||||
|     return DeserializationError::Ok; |     return DeserializationError::Ok; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   DeserializationError parseQuotedString(StringInMemoryPool &result) { |   DeserializationError parseQuotedString(StringType &result) { | ||||||
|     StringBuilder str = _stringStorage.startString(); |     StringBuilder builder = _stringStorage.startString(); | ||||||
|     const char stopChar = current(); |     const char stopChar = current(); | ||||||
|  |  | ||||||
|     move(); |     move(); | ||||||
| @@ -203,16 +204,16 @@ class JsonDeserializer { | |||||||
|         move(); |         move(); | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       str.append(c); |       builder.append(c); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     result = str.complete(); |     result = builder.complete(); | ||||||
|     if (result.isNull()) return DeserializationError::NoMemory; |     if (result.isNull()) return DeserializationError::NoMemory; | ||||||
|     return DeserializationError::Ok; |     return DeserializationError::Ok; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   DeserializationError parseNonQuotedString(StringInMemoryPool &result) { |   DeserializationError parseNonQuotedString(StringType &result) { | ||||||
|     StringBuilder str = _stringStorage.startString(); |     StringBuilder builder = _stringStorage.startString(); | ||||||
|  |  | ||||||
|     char c = current(); |     char c = current(); | ||||||
|     if (c == '\0') return DeserializationError::IncompleteInput; |     if (c == '\0') return DeserializationError::IncompleteInput; | ||||||
| @@ -220,14 +221,14 @@ class JsonDeserializer { | |||||||
|     if (canBeInNonQuotedString(c)) {  // no quotes |     if (canBeInNonQuotedString(c)) {  // no quotes | ||||||
|       do { |       do { | ||||||
|         move(); |         move(); | ||||||
|         str.append(c); |         builder.append(c); | ||||||
|         c = current(); |         c = current(); | ||||||
|       } while (canBeInNonQuotedString(c)); |       } while (canBeInNonQuotedString(c)); | ||||||
|     } else { |     } else { | ||||||
|       return DeserializationError::InvalidInput; |       return DeserializationError::InvalidInput; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     result = str.complete(); |     result = builder.complete(); | ||||||
|     if (result.isNull()) return DeserializationError::NoMemory; |     if (result.isNull()) return DeserializationError::NoMemory; | ||||||
|     return DeserializationError::Ok; |     return DeserializationError::Ok; | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ | |||||||
| // Returns the size (in bytes) of an array with n elements. | // Returns the size (in bytes) of an array with n elements. | ||||||
| // Can be very handy to determine the size of a StaticMemoryPool. | // Can be very handy to determine the size of a StaticMemoryPool. | ||||||
| #define JSON_ARRAY_SIZE(NUMBER_OF_ELEMENTS) \ | #define JSON_ARRAY_SIZE(NUMBER_OF_ELEMENTS) \ | ||||||
|   ((NUMBER_OF_ELEMENTS) * sizeof(ARDUINOJSON_NAMESPACE::Slot)) |   ((NUMBER_OF_ELEMENTS) * sizeof(ARDUINOJSON_NAMESPACE::VariantSlot)) | ||||||
|  |  | ||||||
| namespace ARDUINOJSON_NAMESPACE { | namespace ARDUINOJSON_NAMESPACE { | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,7 +4,6 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "Data/Slot.hpp" |  | ||||||
| #include "Data/SlotFunctions.hpp" | #include "Data/SlotFunctions.hpp" | ||||||
| #include "JsonVariant.hpp" | #include "JsonVariant.hpp" | ||||||
|  |  | ||||||
| @@ -30,7 +29,7 @@ class JsonVariantPtr { | |||||||
| class JsonArrayIterator { | class JsonArrayIterator { | ||||||
|  public: |  public: | ||||||
|   JsonArrayIterator() : _slot(0) {} |   JsonArrayIterator() : _slot(0) {} | ||||||
|   explicit JsonArrayIterator(MemoryPool *memoryPool, Slot *slot) |   explicit JsonArrayIterator(MemoryPool *memoryPool, VariantSlot *slot) | ||||||
|       : _memoryPool(memoryPool), _slot(slot) {} |       : _memoryPool(memoryPool), _slot(slot) {} | ||||||
|  |  | ||||||
|   JsonVariant operator*() const { |   JsonVariant operator*() const { | ||||||
| @@ -58,13 +57,13 @@ class JsonArrayIterator { | |||||||
|     return *this; |     return *this; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   Slot *internal() { |   VariantSlot *internal() { | ||||||
|     return _slot; |     return _slot; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  private: |  private: | ||||||
|   MemoryPool *_memoryPool; |   MemoryPool *_memoryPool; | ||||||
|   Slot *_slot; |   VariantSlot *_slot; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class JsonVariantConstPtr { | class JsonVariantConstPtr { | ||||||
| @@ -86,7 +85,7 @@ class JsonVariantConstPtr { | |||||||
| class JsonArrayConstIterator { | class JsonArrayConstIterator { | ||||||
|  public: |  public: | ||||||
|   JsonArrayConstIterator() : _slot(0) {} |   JsonArrayConstIterator() : _slot(0) {} | ||||||
|   explicit JsonArrayConstIterator(const Slot *slot) : _slot(slot) {} |   explicit JsonArrayConstIterator(const VariantSlot *slot) : _slot(slot) {} | ||||||
|  |  | ||||||
|   JsonVariantConst operator*() const { |   JsonVariantConst operator*() const { | ||||||
|     return JsonVariantConst(&_slot->value); |     return JsonVariantConst(&_slot->value); | ||||||
| @@ -113,11 +112,11 @@ class JsonArrayConstIterator { | |||||||
|     return *this; |     return *this; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const Slot *internal() { |   const VariantSlot *internal() { | ||||||
|     return _slot; |     return _slot; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  private: |  private: | ||||||
|   const Slot *_slot; |   const VariantSlot *_slot; | ||||||
| }; | }; | ||||||
| }  // namespace ARDUINOJSON_NAMESPACE | }  // namespace ARDUINOJSON_NAMESPACE | ||||||
|   | |||||||
| @@ -8,22 +8,18 @@ namespace ARDUINOJSON_NAMESPACE { | |||||||
|  |  | ||||||
| class JsonKey { | class JsonKey { | ||||||
|  public: |  public: | ||||||
|   JsonKey(const Slot* slot) : _slot(slot) {} |   JsonKey(const VariantSlot* slot) : _slot(slot) {} | ||||||
|  |  | ||||||
|   operator const char*() const { |   operator const char*() const { | ||||||
|     return c_str(); |     return c_str(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const char* c_str() const { |   const char* c_str() const { | ||||||
|     return _slot ? _slot->key : 0; |     return _slot ? slotGetKey(_slot) : 0; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool isNull() const { |   bool isNull() const { | ||||||
|     return _slot == 0 || _slot->key == 0; |     return _slot == 0 || _slot->linkedKey == 0; | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool isStatic() const { |  | ||||||
|     return _slot ? _slot->value.keyIsStatic : true; |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   friend bool operator==(JsonKey lhs, const char* rhs) { |   friend bool operator==(JsonKey lhs, const char* rhs) { | ||||||
| @@ -32,6 +28,6 @@ class JsonKey { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|  private: |  private: | ||||||
|   const Slot* _slot; |   const VariantSlot* _slot; | ||||||
| }; | }; | ||||||
| }  // namespace ARDUINOJSON_NAMESPACE | }  // namespace ARDUINOJSON_NAMESPACE | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ | |||||||
| // Returns the size (in bytes) of an object with n elements. | // Returns the size (in bytes) of an object with n elements. | ||||||
| // Can be very handy to determine the size of a StaticMemoryPool. | // Can be very handy to determine the size of a StaticMemoryPool. | ||||||
| #define JSON_OBJECT_SIZE(NUMBER_OF_ELEMENTS) \ | #define JSON_OBJECT_SIZE(NUMBER_OF_ELEMENTS) \ | ||||||
|   ((NUMBER_OF_ELEMENTS) * sizeof(ARDUINOJSON_NAMESPACE::Slot)) |   ((NUMBER_OF_ELEMENTS) * sizeof(ARDUINOJSON_NAMESPACE::VariantSlot)) | ||||||
|  |  | ||||||
| namespace ARDUINOJSON_NAMESPACE { | namespace ARDUINOJSON_NAMESPACE { | ||||||
|  |  | ||||||
| @@ -256,7 +256,11 @@ class JsonObject : public JsonObjectProxy<JsonObjectData>, public Visitable { | |||||||
|     return set_impl(makeString(key)); |     return set_impl(makeString(key)); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   FORCE_INLINE JsonVariant set(const StringInMemoryPool& key) const { |   FORCE_INLINE JsonVariant set(StringInMemoryPool key) const { | ||||||
|  |     return set_impl(key); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   FORCE_INLINE JsonVariant set(ZeroTerminatedRamStringConst key) const { | ||||||
|     return set_impl(key); |     return set_impl(key); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -11,7 +11,8 @@ namespace ARDUINOJSON_NAMESPACE { | |||||||
|  |  | ||||||
| class JsonPairPtr { | class JsonPairPtr { | ||||||
|  public: |  public: | ||||||
|   JsonPairPtr(MemoryPool *memoryPool, Slot *slot) : _pair(memoryPool, slot) {} |   JsonPairPtr(MemoryPool *memoryPool, VariantSlot *slot) | ||||||
|  |       : _pair(memoryPool, slot) {} | ||||||
|  |  | ||||||
|   const JsonPair *operator->() const { |   const JsonPair *operator->() const { | ||||||
|     return &_pair; |     return &_pair; | ||||||
| @@ -29,7 +30,7 @@ class JsonObjectIterator { | |||||||
|  public: |  public: | ||||||
|   JsonObjectIterator() : _slot(0) {} |   JsonObjectIterator() : _slot(0) {} | ||||||
|  |  | ||||||
|   explicit JsonObjectIterator(MemoryPool *memoryPool, Slot *slot) |   explicit JsonObjectIterator(MemoryPool *memoryPool, VariantSlot *slot) | ||||||
|       : _memoryPool(memoryPool), _slot(slot) {} |       : _memoryPool(memoryPool), _slot(slot) {} | ||||||
|  |  | ||||||
|   JsonPair operator*() const { |   JsonPair operator*() const { | ||||||
| @@ -57,18 +58,18 @@ class JsonObjectIterator { | |||||||
|     return *this; |     return *this; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   Slot *internal() { |   VariantSlot *internal() { | ||||||
|     return _slot; |     return _slot; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  private: |  private: | ||||||
|   MemoryPool *_memoryPool; |   MemoryPool *_memoryPool; | ||||||
|   Slot *_slot; |   VariantSlot *_slot; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class JsonPairConstPtr { | class JsonPairConstPtr { | ||||||
|  public: |  public: | ||||||
|   JsonPairConstPtr(const Slot *slot) : _pair(slot) {} |   JsonPairConstPtr(const VariantSlot *slot) : _pair(slot) {} | ||||||
|  |  | ||||||
|   const JsonPairConst *operator->() const { |   const JsonPairConst *operator->() const { | ||||||
|     return &_pair; |     return &_pair; | ||||||
| @@ -86,7 +87,7 @@ class JsonObjectConstIterator { | |||||||
|  public: |  public: | ||||||
|   JsonObjectConstIterator() : _slot(0) {} |   JsonObjectConstIterator() : _slot(0) {} | ||||||
|  |  | ||||||
|   explicit JsonObjectConstIterator(const Slot *slot) : _slot(slot) {} |   explicit JsonObjectConstIterator(const VariantSlot *slot) : _slot(slot) {} | ||||||
|  |  | ||||||
|   JsonPairConst operator*() const { |   JsonPairConst operator*() const { | ||||||
|     return JsonPairConst(_slot); |     return JsonPairConst(_slot); | ||||||
| @@ -113,11 +114,11 @@ class JsonObjectConstIterator { | |||||||
|     return *this; |     return *this; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const Slot *internal() { |   const VariantSlot *internal() { | ||||||
|     return _slot; |     return _slot; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  private: |  private: | ||||||
|   const Slot *_slot; |   const VariantSlot *_slot; | ||||||
| }; | }; | ||||||
| }  // namespace ARDUINOJSON_NAMESPACE | }  // namespace ARDUINOJSON_NAMESPACE | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ namespace ARDUINOJSON_NAMESPACE { | |||||||
| // A key value pair for JsonObjectData. | // A key value pair for JsonObjectData. | ||||||
| class JsonPair { | class JsonPair { | ||||||
|  public: |  public: | ||||||
|   JsonPair(MemoryPool* memoryPool, Slot* slot) : _key(slot) { |   JsonPair(MemoryPool* memoryPool, VariantSlot* slot) : _key(slot) { | ||||||
|     if (slot) { |     if (slot) { | ||||||
|       _value = JsonVariant(memoryPool, &slot->value); |       _value = JsonVariant(memoryPool, &slot->value); | ||||||
|     } |     } | ||||||
| @@ -32,7 +32,7 @@ class JsonPair { | |||||||
|  |  | ||||||
| class JsonPairConst { | class JsonPairConst { | ||||||
|  public: |  public: | ||||||
|   JsonPairConst(const Slot* slot) : _key(slot) { |   JsonPairConst(const VariantSlot* slot) : _key(slot) { | ||||||
|     if (slot) { |     if (slot) { | ||||||
|       _value = JsonVariantConst(&slot->value); |       _value = JsonVariantConst(&slot->value); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -204,9 +204,12 @@ class JsonVariant : public JsonVariantProxy<JsonVariantData>, | |||||||
|     return variantSetString(_data, value); |     return variantSetString(_data, value); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // set(const char*); |   // for internal use only | ||||||
|   FORCE_INLINE bool set(StringInMemoryPool value) const { |   FORCE_INLINE bool set(StringInMemoryPool value) const { | ||||||
|     return variantSetString(_data, value, _memoryPool); |     return variantSetOwnedString(_data, value.slot()); | ||||||
|  |   } | ||||||
|  |   FORCE_INLINE bool set(ZeroTerminatedRamStringConst value) const { | ||||||
|  |     return variantSetString(_data, value.c_str()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool set(JsonVariantConst value) const; |   bool set(JsonVariantConst value) const; | ||||||
| @@ -323,15 +326,4 @@ class JsonVariantConst : public JsonVariantProxy<const JsonVariantData>, | |||||||
|     return JsonVariantConst(objectGet(variantAsObject(_data), makeString(key))); |     return JsonVariantConst(objectGet(variantAsObject(_data), makeString(key))); | ||||||
|   } |   } | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class JsonVariantLocal : public JsonVariant { |  | ||||||
|  public: |  | ||||||
|   explicit JsonVariantLocal(MemoryPool *memoryPool) |  | ||||||
|       : JsonVariant(memoryPool, &_localData) { |  | ||||||
|     _localData.type = JSON_NULL; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|  private: |  | ||||||
|   JsonVariantData _localData; |  | ||||||
| }; |  | ||||||
| }  // namespace ARDUINOJSON_NAMESPACE | }  // namespace ARDUINOJSON_NAMESPACE | ||||||
|   | |||||||
| @@ -89,11 +89,16 @@ inline void JsonVariantConst::accept(Visitor& visitor) const { | |||||||
|       return visitor.visitObject(JsonObjectConst(&_data->content.asObject)); |       return visitor.visitObject(JsonObjectConst(&_data->content.asObject)); | ||||||
|  |  | ||||||
|     case JSON_LINKED_STRING: |     case JSON_LINKED_STRING: | ||||||
|     case JSON_OWNED_STRING: |  | ||||||
|       return visitor.visitString(_data->content.asString); |       return visitor.visitString(_data->content.asString); | ||||||
|  |  | ||||||
|     case JSON_LINKED_RAW: |     case JSON_OWNED_STRING: | ||||||
|  |       return visitor.visitString(_data->content.asOwnedString->value); | ||||||
|  |  | ||||||
|     case JSON_OWNED_RAW: |     case JSON_OWNED_RAW: | ||||||
|  |       return visitor.visitRawJson(_data->content.asOwnedRaw->value, | ||||||
|  |                                   _data->content.asOwnedRaw->size); | ||||||
|  |  | ||||||
|  |     case JSON_LINKED_RAW: | ||||||
|       return visitor.visitRawJson(_data->content.asRaw.data, |       return visitor.visitRawJson(_data->content.asRaw.data, | ||||||
|                                   _data->content.asRaw.size); |                                   _data->content.asRaw.size); | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										28
									
								
								src/ArduinoJson/Memory/Alignment.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/ArduinoJson/Memory/Alignment.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | |||||||
|  | // ArduinoJson - arduinojson.org | ||||||
|  | // Copyright Benoit Blanchon 2014-2018 | ||||||
|  | // MIT License | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <stddef.h>  // size_t | ||||||
|  |  | ||||||
|  | namespace ARDUINOJSON_NAMESPACE { | ||||||
|  |  | ||||||
|  | inline bool isAligned(void *ptr) { | ||||||
|  |   const size_t mask = sizeof(void *) - 1; | ||||||
|  |   size_t addr = reinterpret_cast<size_t>(ptr); | ||||||
|  |   return (addr & mask) == 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | inline size_t addPadding(size_t bytes) { | ||||||
|  |   const size_t mask = sizeof(void *) - 1; | ||||||
|  |   return (bytes + mask) & ~mask; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <size_t bytes> | ||||||
|  | struct AddPadding { | ||||||
|  |   static const size_t mask = sizeof(void *) - 1; | ||||||
|  |   static const size_t value = (bytes + mask) & ~mask; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ARDUINOJSON_NAMESPACE | ||||||
| @@ -5,8 +5,9 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "../Strings/StringInMemoryPool.hpp" | #include "../Strings/StringInMemoryPool.hpp" | ||||||
|  | #include "Alignment.hpp" | ||||||
| #include "MemoryPool.hpp" | #include "MemoryPool.hpp" | ||||||
| #include "StringBuilder.hpp" | #include "StaticMemoryPool.hpp" | ||||||
|  |  | ||||||
| #include <stdlib.h>  // malloc, free | #include <stdlib.h>  // malloc, free | ||||||
|  |  | ||||||
| @@ -33,18 +34,15 @@ class DefaultAllocator { | |||||||
|  |  | ||||||
| template <typename TAllocator> | template <typename TAllocator> | ||||||
| class DynamicMemoryPoolBase : public MemoryPool { | class DynamicMemoryPoolBase : public MemoryPool { | ||||||
|   struct Block; |   class Block : public StaticMemoryPoolBase { | ||||||
|   struct EmptyBlock { |    public: | ||||||
|  |     Block(char* buf, size_t sz, Block* nxt) | ||||||
|  |         : StaticMemoryPoolBase(buf, sz), next(nxt) {} | ||||||
|     Block* next; |     Block* next; | ||||||
|     size_t capacity; |  | ||||||
|     size_t size; |  | ||||||
|   }; |  | ||||||
|   struct Block : EmptyBlock { |  | ||||||
|     char data[1]; |  | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|  public: |  public: | ||||||
|   enum { EmptyBlockSize = sizeof(EmptyBlock) }; |   enum { EmptyBlockSize = sizeof(Block) }; | ||||||
|  |  | ||||||
|   DynamicMemoryPoolBase(size_t initialSize = ARDUINOJSON_DEFAULT_POOL_SIZE) |   DynamicMemoryPoolBase(size_t initialSize = ARDUINOJSON_DEFAULT_POOL_SIZE) | ||||||
|       : _head(NULL), _nextBlockCapacity(initialSize) {} |       : _head(NULL), _nextBlockCapacity(initialSize) {} | ||||||
| @@ -57,28 +55,82 @@ class DynamicMemoryPoolBase : public MemoryPool { | |||||||
|     _nextBlockCapacity = capacity; |     _nextBlockCapacity = capacity; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Gets the number of bytes occupied in the memoryPool |   virtual size_t size() const { | ||||||
|   virtual size_t allocated_bytes() const { |     size_t sum = 0; | ||||||
|     size_t total = 0; |     for (Block* b = _head; b; b = b->next) { | ||||||
|     for (const Block* b = _head; b; b = b->next) total += b->size; |       sum += b->size(); | ||||||
|     return total; |     } | ||||||
|  |     return sum; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Allocates the specified amount of bytes in the memoryPool |   virtual VariantSlot* allocVariant() { | ||||||
|   virtual char* alloc(size_t bytes) { |     for (Block* b = _head; b; b = b->next) { | ||||||
|     alignNextAlloc(); |       VariantSlot* s = b->allocVariant(); | ||||||
|     return canAllocInHead(bytes) ? allocInHead(bytes) : allocInNewBlock(bytes); |       if (s) return s; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   virtual char* realloc(char* oldPtr, size_t oldSize, size_t newSize) { |     if (!addNewBlock(sizeof(VariantSlot))) return 0; | ||||||
|     size_t n = newSize - oldSize; |  | ||||||
|     if (canAllocInHead(n)) { |     return _head->allocVariant(); | ||||||
|       allocInHead(n); |   } | ||||||
|       return oldPtr; |  | ||||||
|     } else { |   virtual void freeVariant(VariantSlot* slot) { | ||||||
|       char* newPtr = allocInNewBlock(newSize); |     for (Block* b = _head; b; b = b->next) { | ||||||
|       if (oldPtr && newPtr) memcpy(newPtr, oldPtr, oldSize); |       if (b->owns(slot)) { | ||||||
|       return newPtr; |         b->freeVariant(slot); | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   virtual void freeString(StringSlot* slot) { | ||||||
|  |     for (Block* b = _head; b; b = b->next) { | ||||||
|  |       if (b->owns(slot)) { | ||||||
|  |         b->freeString(slot); | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   virtual StringSlot* allocFrozenString(size_t n) { | ||||||
|  |     for (Block* b = _head; b; b = b->next) { | ||||||
|  |       StringSlot* s = b->allocFrozenString(n); | ||||||
|  |       if (s) return s; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!addNewBlock(sizeof(StringSlot) + n)) return 0; | ||||||
|  |  | ||||||
|  |     return _head->allocFrozenString(n); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   virtual StringSlot* allocExpandableString() { | ||||||
|  |     for (Block* b = _head; b; b = b->next) { | ||||||
|  |       StringSlot* s = b->allocExpandableString(); | ||||||
|  |       if (s) return s; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!addNewBlock(sizeof(StringSlot))) return 0; | ||||||
|  |  | ||||||
|  |     return _head->allocExpandableString(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   virtual StringSlot* expandString(StringSlot* oldSlot) { | ||||||
|  |     if (!addNewBlock(sizeof(StringSlot) + oldSlot->size)) return 0; | ||||||
|  |  | ||||||
|  |     StringSlot* newSlot = _head->allocExpandableString(); | ||||||
|  |  | ||||||
|  |     ARDUINOJSON_ASSERT(newSlot->size > oldSlot->size); | ||||||
|  |     memcpy(newSlot->value, oldSlot->value, oldSlot->size); | ||||||
|  |     freeString(oldSlot); | ||||||
|  |  | ||||||
|  |     return newSlot; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   virtual void freezeString(StringSlot* slot, size_t newSize) { | ||||||
|  |     for (Block* b = _head; b; b = b->next) { | ||||||
|  |       if (b->owns(slot)) { | ||||||
|  |         b->freezeString(slot, newSize); | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -87,7 +139,7 @@ class DynamicMemoryPoolBase : public MemoryPool { | |||||||
|   void clear() { |   void clear() { | ||||||
|     Block* currentBlock = _head; |     Block* currentBlock = _head; | ||||||
|     while (currentBlock != NULL) { |     while (currentBlock != NULL) { | ||||||
|       _nextBlockCapacity = currentBlock->capacity; |       _nextBlockCapacity = currentBlock->capacity(); | ||||||
|       Block* nextBlock = currentBlock->next; |       Block* nextBlock = currentBlock->next; | ||||||
|       _allocator.deallocate(currentBlock); |       _allocator.deallocate(currentBlock); | ||||||
|       currentBlock = nextBlock; |       currentBlock = nextBlock; | ||||||
| @@ -95,40 +147,22 @@ class DynamicMemoryPoolBase : public MemoryPool { | |||||||
|     _head = 0; |     _head = 0; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   StringBuilder startString() { |   size_t blockCount() const { | ||||||
|     return StringBuilder(this); |     size_t sum = 0; | ||||||
|  |     for (Block* b = _head; b; b = b->next) sum++; | ||||||
|  |     return sum; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  private: |  private: | ||||||
|   void alignNextAlloc() { |   bool addNewBlock(size_t minCapacity) { | ||||||
|     if (_head) _head->size = this->round_size_up(_head->size); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool canAllocInHead(size_t bytes) const { |  | ||||||
|     return _head != NULL && _head->size + bytes <= _head->capacity; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   char* allocInHead(size_t bytes) { |  | ||||||
|     char* p = _head->data + _head->size; |  | ||||||
|     _head->size += bytes; |  | ||||||
|     return p; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   char* allocInNewBlock(size_t bytes) { |  | ||||||
|     size_t capacity = _nextBlockCapacity; |     size_t capacity = _nextBlockCapacity; | ||||||
|     if (bytes > capacity) capacity = bytes; |     if (minCapacity > capacity) capacity = minCapacity; | ||||||
|     if (!addNewBlock(capacity)) return NULL; |     capacity = addPadding(capacity); | ||||||
|     _nextBlockCapacity *= 2; |     size_t bytes = sizeof(Block) + capacity; | ||||||
|     return allocInHead(bytes); |     char* p = reinterpret_cast<char*>(_allocator.allocate(bytes)); | ||||||
|   } |     if (!p) return false; | ||||||
|  |     Block* block = new (p) Block(p + sizeof(Block), capacity, _head); | ||||||
|   bool addNewBlock(size_t capacity) { |     _nextBlockCapacity = capacity * 2; | ||||||
|     size_t bytes = EmptyBlockSize + 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; |     _head = block; | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -5,66 +5,30 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <stddef.h>  // for size_t | #include <stddef.h>  // for size_t | ||||||
| #include <stdint.h>  // for uint8_t |  | ||||||
| #include <string.h> |  | ||||||
|  |  | ||||||
| #include "../Configuration.hpp" |  | ||||||
| #include "../Data/Slot.hpp" |  | ||||||
| #include "../Polyfills/attributes.hpp" | #include "../Polyfills/attributes.hpp" | ||||||
|  | #include "StringSlot.hpp" | ||||||
|  | #include "VariantSlot.hpp" | ||||||
|  |  | ||||||
| namespace ARDUINOJSON_NAMESPACE { | namespace ARDUINOJSON_NAMESPACE { | ||||||
| // Handle the memory management (done in derived classes) and calls the parser. |  | ||||||
| // This abstract class is implemented by StaticMemoryPool which implements a |  | ||||||
| // fixed memory allocation. |  | ||||||
| class MemoryPool { | class MemoryPool { | ||||||
|  public: |  public: | ||||||
|   // Allocates n bytes in the MemoryPool. |   virtual StringSlot *allocExpandableString() = 0; | ||||||
|   // Return a pointer to the allocated memory or NULL if allocation fails. |   virtual StringSlot *allocFrozenString(size_t) = 0; | ||||||
|   virtual char *alloc(size_t size) = 0; |   virtual StringSlot *expandString(StringSlot *) = 0; | ||||||
|  |   virtual void freezeString(StringSlot *, size_t) = 0; | ||||||
|  |   virtual void freeString(StringSlot *) = 0; | ||||||
|  |  | ||||||
|   virtual char *realloc(char *oldPtr, size_t oldSize, size_t newSize) = 0; |   virtual VariantSlot *allocVariant() = 0; | ||||||
|  |   virtual void freeVariant(VariantSlot *) = 0; | ||||||
|  |  | ||||||
|   Slot *allocSlot() { |   virtual size_t size() const = 0; | ||||||
|     if (_freeSlots) { |  | ||||||
|       Slot *s = _freeSlots; |  | ||||||
|       _freeSlots = s->next; |  | ||||||
|       return s; |  | ||||||
|     } |  | ||||||
|     return reinterpret_cast<Slot *>(alloc(sizeof(Slot))); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   void freeSlot(Slot *slot) { |  | ||||||
|     slot->next = _freeSlots; |  | ||||||
|     _freeSlots = slot; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   size_t size() const { |  | ||||||
|     size_t result = allocated_bytes(); |  | ||||||
|     for (Slot *s = _freeSlots; s; s = s->next) result -= sizeof(Slot); |  | ||||||
|     return result; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   MemoryPool() : _freeSlots(0) {} |  | ||||||
|  |  | ||||||
|   // CAUTION: NO VIRTUAL DESTRUCTOR! |   // CAUTION: NO VIRTUAL DESTRUCTOR! | ||||||
|   // If we add a virtual constructor the Arduino compiler will add malloc() |   // If we add a virtual constructor the Arduino compiler will add malloc() | ||||||
|   // and free() to the binary, adding 706 useless bytes. |   // and free() to the binary, adding 706 useless bytes. | ||||||
|   ~MemoryPool() {} |   ~MemoryPool() {} | ||||||
|  |  | ||||||
|   virtual size_t allocated_bytes() const = 0; |  | ||||||
|  |  | ||||||
|   // Preserve aligment if necessary |  | ||||||
|   static FORCE_INLINE size_t round_size_up(size_t bytes) { |  | ||||||
| #if ARDUINOJSON_ENABLE_ALIGNMENT |  | ||||||
|     const size_t x = sizeof(void *) - 1; |  | ||||||
|     return (bytes + x) & ~x; |  | ||||||
| #else |  | ||||||
|     return bytes; |  | ||||||
| #endif |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|  private: |  | ||||||
|   Slot *_freeSlots; |  | ||||||
| }; | }; | ||||||
| }  // namespace ARDUINOJSON_NAMESPACE | }  // namespace ARDUINOJSON_NAMESPACE | ||||||
|   | |||||||
							
								
								
									
										67
									
								
								src/ArduinoJson/Memory/SlotList.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/ArduinoJson/Memory/SlotList.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | |||||||
|  | // ArduinoJson - arduinojson.org | ||||||
|  | // Copyright Benoit Blanchon 2014-2018 | ||||||
|  | // MIT License | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <stddef.h>  // for size_t | ||||||
|  |  | ||||||
|  | namespace ARDUINOJSON_NAMESPACE { | ||||||
|  |  | ||||||
|  | template <typename TSlot> | ||||||
|  | class SlotList { | ||||||
|  |  public: | ||||||
|  |   SlotList() : _head(0) {} | ||||||
|  |  | ||||||
|  |   TSlot *pop() { | ||||||
|  |     TSlot *slot = _head; | ||||||
|  |     if (slot) _head = slot->next; | ||||||
|  |     return slot; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void push(TSlot *slot) { | ||||||
|  |     slot->next = _head; | ||||||
|  |     _head = slot; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   bool remove(const TSlot *slot) { | ||||||
|  |     if (_head == slot) { | ||||||
|  |       _head = slot->next; | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (TSlot *s = _head; s; s = s->next) { | ||||||
|  |       if (s->next == slot) { | ||||||
|  |         s->next = slot->next; | ||||||
|  |         return true; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   bool remove(const void *slot) { | ||||||
|  |     return remove(reinterpret_cast<const TSlot *>(slot)); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   template <typename Functor> | ||||||
|  |   void forEach(const Functor &f) { | ||||||
|  |     for (TSlot *s = _head; s; s = s->next) { | ||||||
|  |       f(s); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   size_t size() const { | ||||||
|  |     size_t sum = 0; | ||||||
|  |     for (TSlot *s = _head; s; s = s->next) sum += sizeof(TSlot); | ||||||
|  |     return sum; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void clear() { | ||||||
|  |     _head = 0; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   TSlot *_head; | ||||||
|  | }; | ||||||
|  | }  // namespace ARDUINOJSON_NAMESPACE | ||||||
| @@ -4,74 +4,201 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
|  | #include "../Polyfills/assert.hpp" | ||||||
| #include "../Polyfills/mpl/max.hpp" | #include "../Polyfills/mpl/max.hpp" | ||||||
| #include "../Strings/StringInMemoryPool.hpp" | #include "../Strings/StringInMemoryPool.hpp" | ||||||
|  | #include "Alignment.hpp" | ||||||
| #include "MemoryPool.hpp" | #include "MemoryPool.hpp" | ||||||
| #include "StringBuilder.hpp" | #include "SlotList.hpp" | ||||||
|  |  | ||||||
| namespace ARDUINOJSON_NAMESPACE { | namespace ARDUINOJSON_NAMESPACE { | ||||||
|  |  | ||||||
|  | // _begin                                _end | ||||||
|  | // v                                        v | ||||||
|  | // +-------------+--------------+-----------+ | ||||||
|  | // | strings...  |   (free)     |  ...slots | | ||||||
|  | // +-------------+--------------+-----------+ | ||||||
|  | //               ^              ^ | ||||||
|  | //             _left          _right | ||||||
|  |  | ||||||
| class StaticMemoryPoolBase : public MemoryPool { | class StaticMemoryPoolBase : public MemoryPool { | ||||||
|  |   class UpdateStringSlotAddress { | ||||||
|  |    public: | ||||||
|  |     UpdateStringSlotAddress(const char* address, size_t offset) | ||||||
|  |         : _address(address), _offset(offset) {} | ||||||
|  |  | ||||||
|  |     void operator()(StringSlot* slot) const { | ||||||
|  |       ARDUINOJSON_ASSERT(slot != NULL); | ||||||
|  |       if (slot->value > _address) slot->value -= _offset; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |    private: | ||||||
|  |     const char* _address; | ||||||
|  |     size_t _offset; | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  public: |  public: | ||||||
|   // Gets the capacity of the memoryPool in bytes |   // Gets the capacity of the memoryPool in bytes | ||||||
|   size_t capacity() const { |   size_t capacity() const { | ||||||
|     return _capacity; |     return size_t(_end - _begin); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Allocates the specified amount of bytes in the memoryPool |   virtual size_t size() const { | ||||||
|   virtual char* alloc(size_t bytes) { |     return allocated_bytes() - _freeVariants.size() - _freeStrings.size(); | ||||||
|     alignNextAlloc(); |  | ||||||
|     if (!canAlloc(bytes)) return NULL; |  | ||||||
|     return doAlloc(bytes); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   virtual char* realloc(char* oldPtr, size_t oldSize, size_t newSize) { |   virtual VariantSlot* allocVariant() { | ||||||
|     size_t n = newSize - oldSize; |     VariantSlot* s = _freeVariants.pop(); | ||||||
|     if (!canAlloc(n)) return NULL; |     if (s) return s; | ||||||
|     doAlloc(n); |     return s ? s : allocRight<VariantSlot>(); | ||||||
|     return oldPtr; |   } | ||||||
|  |  | ||||||
|  |   virtual void freeVariant(VariantSlot* slot) { | ||||||
|  |     freeVariantSlot(slot); | ||||||
|  |     compactRightSide(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   virtual void freeString(StringSlot* slot) { | ||||||
|  |     freeStringSlot(slot); | ||||||
|  |     compactLeftSide(slot->value, slot->size); | ||||||
|  |     compactRightSide(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   virtual StringSlot* allocFrozenString(size_t n) { | ||||||
|  |     StringSlot* s = allocStringSlot(); | ||||||
|  |     if (!s) return 0; | ||||||
|  |     if (!canAlloc(n)) return 0; | ||||||
|  |  | ||||||
|  |     s->value = _left; | ||||||
|  |     s->size = n; | ||||||
|  |     _left += n; | ||||||
|  |     _usedString.push(s); | ||||||
|  |     checkInvariants(); | ||||||
|  |  | ||||||
|  |     return s; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   virtual StringSlot* allocExpandableString() { | ||||||
|  |     StringSlot* s = allocStringSlot(); | ||||||
|  |     if (!s) return 0; | ||||||
|  |  | ||||||
|  |     s->value = _left; | ||||||
|  |     s->size = size_t(_right - _left); | ||||||
|  |     _usedString.push(s); | ||||||
|  |     _left = _right; | ||||||
|  |  | ||||||
|  |     checkInvariants(); | ||||||
|  |     return s; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   virtual StringSlot* expandString(StringSlot*) { | ||||||
|  |     return 0; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   virtual void freezeString(StringSlot* slot, size_t newSize) { | ||||||
|  |     _left -= (slot->size - newSize); | ||||||
|  |     slot->size = newSize; | ||||||
|  |     checkInvariants(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Resets the memoryPool. |  | ||||||
|   // USE WITH CAUTION: this invalidates all previously allocated data |  | ||||||
|   void clear() { |   void clear() { | ||||||
|     _size = 0; |     _left = _begin; | ||||||
|  |     _right = _end; | ||||||
|  |     _freeVariants.clear(); | ||||||
|  |     _freeStrings.clear(); | ||||||
|  |     _usedString.clear(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   StringBuilder startString() { |   bool canAlloc(size_t bytes) const { | ||||||
|     return StringBuilder(this); |     return _left + bytes <= _right; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   bool owns(void* p) const { | ||||||
|  |     return _begin <= p && p < _end; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   template <typename T> | ||||||
|  |   T* allocRight() { | ||||||
|  |     return reinterpret_cast<T*>(allocRight(sizeof(T))); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   char* allocRight(size_t bytes) { | ||||||
|  |     if (!canAlloc(bytes)) return 0; | ||||||
|  |     _right -= bytes; | ||||||
|  |     return _right; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Workaround for missing placement new | ||||||
|  |   void* operator new(size_t, void* p) { | ||||||
|  |     return p; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   StaticMemoryPoolBase(char* memoryPool, size_t capa) |   StaticMemoryPoolBase(char* buffer, size_t capa) | ||||||
|       : _buffer(memoryPool), _capacity(capa), _size(0) {} |       : _begin(buffer), | ||||||
|  |         _left(buffer), | ||||||
|  |         _right(buffer + capa), | ||||||
|  |         _end(buffer + capa) {} | ||||||
|  |  | ||||||
|   ~StaticMemoryPoolBase() {} |   ~StaticMemoryPoolBase() {} | ||||||
|  |  | ||||||
|   // Gets the current usage of the memoryPool in bytes |   // Gets the current usage of the memoryPool in bytes | ||||||
|   virtual size_t allocated_bytes() const { |   size_t allocated_bytes() const { | ||||||
|     return _size; |     return size_t(_left - _begin + _end - _right); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  private: |  private: | ||||||
|   void alignNextAlloc() { |   StringSlot* allocStringSlot() { | ||||||
|     _size = round_size_up(_size); |     StringSlot* s = _freeStrings.pop(); | ||||||
|  |     if (s) return s; | ||||||
|  |     return allocRight<StringSlot>(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool canAlloc(size_t bytes) const { |   void freeVariantSlot(VariantSlot* slot) { | ||||||
|     return _size + bytes <= _capacity; |     _freeVariants.push(slot); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   char* doAlloc(size_t bytes) { |   void freeStringSlot(StringSlot* slot) { | ||||||
|     char* p = &_buffer[_size]; |     _usedString.remove(slot); | ||||||
|     _size += bytes; |     _freeStrings.push(slot); | ||||||
|     return p; |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   char* _buffer; |   void compactLeftSide(char* holeAddress, size_t holeSize) { | ||||||
|   size_t _capacity; |     ARDUINOJSON_ASSERT(holeAddress >= _begin); | ||||||
|   size_t _size; |     ARDUINOJSON_ASSERT(holeAddress + holeSize <= _left); | ||||||
| }; |     char* holeEnd = holeAddress + holeSize; | ||||||
|  |     memmove(holeAddress,               // where the hole begun | ||||||
|  |             holeEnd,                   // where the hole ended | ||||||
|  |             size_t(_left - holeEnd));  // everything after the hole | ||||||
|  |     _left -= holeSize; | ||||||
|  |     _usedString.forEach(UpdateStringSlotAddress(holeAddress, holeSize)); | ||||||
|  |     checkInvariants(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void compactRightSide() { | ||||||
|  |   loop: | ||||||
|  |     if (_freeStrings.remove(_right)) { | ||||||
|  |       _right += sizeof(StringSlot); | ||||||
|  |       goto loop; | ||||||
|  |     } | ||||||
|  |     if (_freeVariants.remove(_right)) { | ||||||
|  |       _right += sizeof(VariantSlot); | ||||||
|  |       goto loop; | ||||||
|  |     } | ||||||
|  |     checkInvariants(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void checkInvariants() { | ||||||
|  |     ARDUINOJSON_ASSERT(_begin <= _left); | ||||||
|  |     ARDUINOJSON_ASSERT(_left <= _right); | ||||||
|  |     ARDUINOJSON_ASSERT(_right <= _end); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   char *_begin, *_left, *_right, *_end; | ||||||
|  |   SlotList<VariantSlot> _freeVariants; | ||||||
|  |   SlotList<StringSlot> _freeStrings; | ||||||
|  |   SlotList<StringSlot> _usedString; | ||||||
|  | };  // namespace ARDUINOJSON_NAMESPACE | ||||||
|  |  | ||||||
| #if defined(__clang__) | #if defined(__clang__) | ||||||
| #pragma clang diagnostic push | #pragma clang diagnostic push | ||||||
| @@ -88,7 +215,8 @@ class StaticMemoryPoolBase : public MemoryPool { | |||||||
| // bytes. | // bytes. | ||||||
| template <size_t CAPACITY> | template <size_t CAPACITY> | ||||||
| class StaticMemoryPool : public StaticMemoryPoolBase { | class StaticMemoryPool : public StaticMemoryPoolBase { | ||||||
|   static const size_t ACTUAL_CAPACITY = Max<1, CAPACITY>::value; |   static const size_t ACTUAL_CAPACITY = | ||||||
|  |       AddPadding<Max<1, CAPACITY>::value>::value; | ||||||
|  |  | ||||||
|  public: |  public: | ||||||
|   explicit StaticMemoryPool() |   explicit StaticMemoryPool() | ||||||
|   | |||||||
| @@ -11,25 +11,43 @@ namespace ARDUINOJSON_NAMESPACE { | |||||||
|  |  | ||||||
| class StringBuilder { | class StringBuilder { | ||||||
|  public: |  public: | ||||||
|   explicit StringBuilder(MemoryPool* parent) |   typedef StringInMemoryPool StringType; | ||||||
|       : _parent(parent), _start(0), _size(0) { |  | ||||||
|     _start = _parent->alloc(1); |   explicit StringBuilder(MemoryPool* parent) : _parent(parent), _size(0) { | ||||||
|  |     _slot = _parent->allocExpandableString(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void append(const char* s) { | ||||||
|  |     while (*s) append(*s++); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void append(const char* s, size_t n) { | ||||||
|  |     while (n-- > 0) append(*s++); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   void append(char c) { |   void append(char c) { | ||||||
|     _start = _parent->realloc(_start, _size + 1, _size + 2); |     if (!_slot) return; | ||||||
|     if (_start) _start[_size++] = c; |  | ||||||
|  |     if (_size >= _slot->size) { | ||||||
|  |       _slot = _parent->expandString(_slot); | ||||||
|  |       if (!_slot) return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   StringInMemoryPool complete() { |     _slot->value[_size++] = c; | ||||||
|     if (_start) _start[_size] = 0; |   } | ||||||
|     return _start; |  | ||||||
|  |   StringType complete() { | ||||||
|  |     append('\0'); | ||||||
|  |     if (_slot) { | ||||||
|  |       _parent->freezeString(_slot, _size); | ||||||
|  |     } | ||||||
|  |     return _slot; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  private: |  private: | ||||||
|   MemoryPool* _parent; |   MemoryPool* _parent; | ||||||
|   char* _start; |  | ||||||
|   size_t _size; |   size_t _size; | ||||||
|  |   StringSlot* _slot; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| }  // namespace ARDUINOJSON_NAMESPACE | }  // namespace ARDUINOJSON_NAMESPACE | ||||||
|   | |||||||
							
								
								
									
										20
									
								
								src/ArduinoJson/Memory/StringSlot.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/ArduinoJson/Memory/StringSlot.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | // ArduinoJson - arduinojson.org | ||||||
|  | // Copyright Benoit Blanchon 2014-2018 | ||||||
|  | // MIT License | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <stddef.h>  // for size_t | ||||||
|  | #include "../Configuration.hpp" | ||||||
|  |  | ||||||
|  | #define JSON_STRING_SIZE(SIZE) \ | ||||||
|  |   (sizeof(ARDUINOJSON_NAMESPACE::StringSlot) + (SIZE)) | ||||||
|  |  | ||||||
|  | namespace ARDUINOJSON_NAMESPACE { | ||||||
|  |  | ||||||
|  | struct StringSlot { | ||||||
|  |   char *value; | ||||||
|  |   size_t size; | ||||||
|  |   struct StringSlot *next; | ||||||
|  | }; | ||||||
|  | }  // namespace ARDUINOJSON_NAMESPACE | ||||||
| @@ -4,15 +4,18 @@ | |||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "JsonVariantData.hpp" | #include "../Data/JsonVariantData.hpp" | ||||||
| 
 | 
 | ||||||
| namespace ARDUINOJSON_NAMESPACE { | namespace ARDUINOJSON_NAMESPACE { | ||||||
| 
 | 
 | ||||||
| struct Slot { | struct VariantSlot { | ||||||
|   JsonVariantData value; |   JsonVariantData value; | ||||||
|   struct Slot* next; |   struct VariantSlot* next; | ||||||
|   struct Slot* prev; |   struct VariantSlot* prev; | ||||||
|   const char* key; |   union { | ||||||
|  |     const char* linkedKey; | ||||||
|  |     StringSlot* ownedKey; | ||||||
|  |   }; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| }  // namespace ARDUINOJSON_NAMESPACE
 | }  // namespace ARDUINOJSON_NAMESPACE
 | ||||||
| @@ -17,6 +17,7 @@ template <typename TReader, typename TStringStorage> | |||||||
| class MsgPackDeserializer { | class MsgPackDeserializer { | ||||||
|   typedef typename remove_reference<TStringStorage>::type::StringBuilder |   typedef typename remove_reference<TStringStorage>::type::StringBuilder | ||||||
|       StringBuilder; |       StringBuilder; | ||||||
|  |   typedef typename StringBuilder::StringType StringType; | ||||||
|  |  | ||||||
|  public: |  public: | ||||||
|   MsgPackDeserializer(MemoryPool &memoryPool, TReader reader, |   MsgPackDeserializer(MemoryPool &memoryPool, TReader reader, | ||||||
| @@ -220,16 +221,29 @@ class MsgPackDeserializer { | |||||||
|     return readString(variant, size); |     return readString(variant, size); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   template <typename T> | ||||||
|  |   DeserializationError readString(StringType &str) { | ||||||
|  |     T size; | ||||||
|  |     if (!readInteger(size)) return DeserializationError::IncompleteInput; | ||||||
|  |     return readString(str, size); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   DeserializationError readString(JsonVariant variant, size_t n) { |   DeserializationError readString(JsonVariant variant, size_t n) { | ||||||
|     StringBuilder str = _stringStorage.startString(); |     StringType s; | ||||||
|  |     DeserializationError err = readString(s, n); | ||||||
|  |     if (!err) variant.set(s); | ||||||
|  |     return err; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   DeserializationError readString(StringType &s, size_t n) { | ||||||
|  |     StringBuilder builder = _stringStorage.startString(); | ||||||
|     for (; n; --n) { |     for (; n; --n) { | ||||||
|       uint8_t c; |       uint8_t c; | ||||||
|       if (!readBytes(c)) return DeserializationError::IncompleteInput; |       if (!readBytes(c)) return DeserializationError::IncompleteInput; | ||||||
|       str.append(static_cast<char>(c)); |       builder.append(static_cast<char>(c)); | ||||||
|     } |     } | ||||||
|     StringInMemoryPool s = str.complete(); |     s = builder.complete(); | ||||||
|     if (s.isNull()) return DeserializationError::NoMemory; |     if (s.isNull()) return DeserializationError::NoMemory; | ||||||
|     variant.set(s); |  | ||||||
|     return DeserializationError::Ok; |     return DeserializationError::Ok; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -278,12 +292,11 @@ class MsgPackDeserializer { | |||||||
|     if (_nestingLimit == 0) return DeserializationError::TooDeep; |     if (_nestingLimit == 0) return DeserializationError::TooDeep; | ||||||
|     --_nestingLimit; |     --_nestingLimit; | ||||||
|     for (; n; --n) { |     for (; n; --n) { | ||||||
|       JsonVariantLocal key(_memoryPool); |       StringType key; | ||||||
|       DeserializationError err = parse(key); |       DeserializationError err = parseKey(key); | ||||||
|       if (err) return err; |       if (err) return err; | ||||||
|       if (!key.is<char *>()) return DeserializationError::NotSupported; |  | ||||||
|  |  | ||||||
|       JsonVariant value = object.set(StringInMemoryPool(key.as<char *>())); |       JsonVariant value = object.set(key); | ||||||
|       if (value.isInvalid()) return DeserializationError::NoMemory; |       if (value.isInvalid()) return DeserializationError::NoMemory; | ||||||
|  |  | ||||||
|       err = parse(value); |       err = parse(value); | ||||||
| @@ -293,6 +306,27 @@ class MsgPackDeserializer { | |||||||
|     return DeserializationError::Ok; |     return DeserializationError::Ok; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   DeserializationError parseKey(StringType &key) { | ||||||
|  |     uint8_t code; | ||||||
|  |     if (!readByte(code)) return DeserializationError::IncompleteInput; | ||||||
|  |  | ||||||
|  |     if ((code & 0xe0) == 0xa0) return readString(key, code & 0x1f); | ||||||
|  |  | ||||||
|  |     switch (code) { | ||||||
|  |       case 0xd9: | ||||||
|  |         return readString<uint8_t>(key); | ||||||
|  |  | ||||||
|  |       case 0xda: | ||||||
|  |         return readString<uint16_t>(key); | ||||||
|  |  | ||||||
|  |       case 0xdb: | ||||||
|  |         return readString<uint32_t>(key); | ||||||
|  |  | ||||||
|  |       default: | ||||||
|  |         return DeserializationError::NotSupported; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|   MemoryPool *_memoryPool; |   MemoryPool *_memoryPool; | ||||||
|   TReader _reader; |   TReader _reader; | ||||||
|   TStringStorage _stringStorage; |   TStringStorage _stringStorage; | ||||||
|   | |||||||
							
								
								
									
										12
									
								
								src/ArduinoJson/Polyfills/assert.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/ArduinoJson/Polyfills/assert.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | // ArduinoJson - arduinojson.org | ||||||
|  | // Copyright Benoit Blanchon 2014-2018 | ||||||
|  | // MIT License | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #ifdef ARDUINOJSON_DEBUG | ||||||
|  | #include <assert.h> | ||||||
|  | #define ARDUINOJSON_ASSERT(X) assert(X) | ||||||
|  | #else | ||||||
|  | #define ARDUINOJSON_ASSERT(X) ((void)0) | ||||||
|  | #endif | ||||||
| @@ -11,13 +11,15 @@ class StringMover { | |||||||
|  public: |  public: | ||||||
|   class StringBuilder { |   class StringBuilder { | ||||||
|    public: |    public: | ||||||
|  |     typedef ZeroTerminatedRamStringConst StringType; | ||||||
|  |  | ||||||
|     StringBuilder(TChar** ptr) : _writePtr(ptr), _startPtr(*ptr) {} |     StringBuilder(TChar** ptr) : _writePtr(ptr), _startPtr(*ptr) {} | ||||||
|  |  | ||||||
|     void append(char c) { |     void append(char c) { | ||||||
|       *(*_writePtr)++ = TChar(c); |       *(*_writePtr)++ = TChar(c); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     StringInMemoryPool complete() const { |     StringType complete() const { | ||||||
|       *(*_writePtr)++ = 0; |       *(*_writePtr)++ = 0; | ||||||
|       return reinterpret_cast<const char*>(_startPtr); |       return reinterpret_cast<const char*>(_startPtr); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -13,12 +13,12 @@ class ArduinoString { | |||||||
|   ArduinoString(const ::String& str) : _str(&str) {} |   ArduinoString(const ::String& str) : _str(&str) {} | ||||||
|  |  | ||||||
|   template <typename TMemoryPool> |   template <typename TMemoryPool> | ||||||
|   const char* save(TMemoryPool* memoryPool) const { |   StringSlot* save(TMemoryPool* memoryPool) const { | ||||||
|     if (isNull()) return NULL; |     if (isNull()) return NULL; | ||||||
|     size_t n = _str->length() + 1; |     size_t n = _str->length() + 1; | ||||||
|     void* dup = memoryPool->alloc(n); |     StringSlot* slot = memoryPool->allocFrozenString(n); | ||||||
|     if (dup != NULL) memcpy(dup, _str->c_str(), n); |     if (slot) memcpy(slot->value, _str->c_str(), n); | ||||||
|     return static_cast<const char*>(dup); |     return slot; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool isNull() const { |   bool isNull() const { | ||||||
|   | |||||||
| @@ -22,11 +22,11 @@ class FixedSizeFlashString { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   template <typename TMemoryPool> |   template <typename TMemoryPool> | ||||||
|   const char* save(TMemoryPool* memoryPool) const { |   StringSlot* save(TMemoryPool* memoryPool) const { | ||||||
|     if (!_str) return NULL; |     if (!_str) return NULL; | ||||||
|     void* dup = memoryPool->alloc(_size); |     StringSlot* slot = memoryPool->allocFrozenString(_size); | ||||||
|     if (dup != NULL) memcpy_P(dup, (const char*)_str, _size); |     if (!slot) memcpy_P(slot->value, (const char*)_str, _size); | ||||||
|     return static_cast<const char*>(dup); |     return slot; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   size_t size() const { |   size_t size() const { | ||||||
|   | |||||||
| @@ -23,12 +23,11 @@ class FixedSizeRamString { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   template <typename TMemoryPool> |   template <typename TMemoryPool> | ||||||
|   const char* save(TMemoryPool* memoryPool) const { |   StringSlot* save(TMemoryPool* memoryPool) const { | ||||||
|     if (!_str) return NULL; |     if (!_str) return NULL; | ||||||
|     void* dup = memoryPool->alloc(_size); |     StringSlot* slot = memoryPool->allocFrozenString(_size); | ||||||
|     if (!dup) return NULL; |     if (slot) memcpy(slot->value, _str, _size); | ||||||
|     memcpy(dup, _str, _size); |     return slot; | ||||||
|     return static_cast<const char*>(dup); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   size_t size() const { |   size_t size() const { | ||||||
|   | |||||||
| @@ -13,11 +13,11 @@ class StlString { | |||||||
|   StlString(const std::string& str) : _str(&str) {} |   StlString(const std::string& str) : _str(&str) {} | ||||||
|  |  | ||||||
|   template <typename TMemoryPool> |   template <typename TMemoryPool> | ||||||
|   const char* save(TMemoryPool* memoryPool) const { |   StringSlot* save(TMemoryPool* memoryPool) const { | ||||||
|     size_t n = _str->length() + 1; |     size_t n = _str->length() + 1; | ||||||
|     void* dup = memoryPool->alloc(n); |     StringSlot* slot = memoryPool->allocFrozenString(n); | ||||||
|     if (dup != NULL) memcpy(dup, _str->c_str(), n); |     if (slot) memcpy(slot->value, _str->c_str(), n); | ||||||
|     return static_cast<const char*>(dup); |     return slot; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool isNull() const { |   bool isNull() const { | ||||||
|   | |||||||
| @@ -4,13 +4,45 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "ZeroTerminatedRamStringConst.hpp" | #include <string.h> | ||||||
|  | #include "../Memory/StringSlot.hpp" | ||||||
|  |  | ||||||
| namespace ARDUINOJSON_NAMESPACE { | namespace ARDUINOJSON_NAMESPACE { | ||||||
|  |  | ||||||
| class StringInMemoryPool : public ZeroTerminatedRamStringConst { | class StringInMemoryPool { | ||||||
|  public: |  public: | ||||||
|   StringInMemoryPool(const char* str = 0) : ZeroTerminatedRamStringConst(str) {} |   StringInMemoryPool(StringSlot* s = 0) : _slot(s) {} | ||||||
|  |  | ||||||
|  |   bool equals(const char* expected) const { | ||||||
|  |     if (!_slot) return expected == 0; | ||||||
|  |     const char* actual = _slot->value; | ||||||
|  |     if (actual == expected) return true; | ||||||
|  |     return strcmp(actual, expected) == 0; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   bool isNull() const { | ||||||
|  |     return !_slot; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   template <typename TMemoryPool> | ||||||
|  |   StringSlot* save(TMemoryPool*) const { | ||||||
|  |     return _slot; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   size_t size() const { | ||||||
|  |     return _slot->size; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   StringSlot* slot() const { | ||||||
|  |     return _slot; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   const char* c_str() const { | ||||||
|  |     return _slot->value; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   StringSlot* _slot; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| }  // namespace ARDUINOJSON_NAMESPACE | }  // namespace ARDUINOJSON_NAMESPACE | ||||||
|   | |||||||
| @@ -21,12 +21,12 @@ class ZeroTerminatedFlashString { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   template <typename TMemoryPool> |   template <typename TMemoryPool> | ||||||
|   const char* save(TMemoryPool* memoryPool) const { |   StringSlot* save(TMemoryPool* memoryPool) const { | ||||||
|     if (!_str) return NULL; |     if (!_str) return NULL; | ||||||
|     size_t n = size() + 1;  // copy the terminator |     size_t n = size() + 1;  // copy the terminator | ||||||
|     void* dup = memoryPool->alloc(n); |     StringSlot* slot = memoryPool->allocFrozenString(n); | ||||||
|     if (dup != NULL) memcpy_P(dup, (const char*)_str, n); |     if (slot) memcpy_P(slot->value, reinterpret_cast<const char*>(_str), n); | ||||||
|     return static_cast<const char*>(dup); |     return slot; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   size_t size() const { |   size_t size() const { | ||||||
|   | |||||||
| @@ -14,13 +14,12 @@ class ZeroTerminatedRamString : public ZeroTerminatedRamStringConst { | |||||||
|       : ZeroTerminatedRamStringConst(str) {} |       : ZeroTerminatedRamStringConst(str) {} | ||||||
|  |  | ||||||
|   template <typename TMemoryPool> |   template <typename TMemoryPool> | ||||||
|   const char* save(TMemoryPool* memoryPool) const { |   StringSlot* save(TMemoryPool* memoryPool) const { | ||||||
|     if (!_str) return NULL; |     if (!_str) return NULL; | ||||||
|     size_t n = size() + 1; |     size_t n = size() + 1; | ||||||
|     void* dup = memoryPool->alloc(n); |     StringSlot* slot = memoryPool->allocFrozenString(n); | ||||||
|     if (!dup) return NULL; |     if (slot) memcpy(slot->value, _str, n); | ||||||
|     memcpy(dup, _str, n); |     return slot; | ||||||
|     return static_cast<const char*>(dup); |  | ||||||
|   } |   } | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ namespace ARDUINOJSON_NAMESPACE { | |||||||
|  |  | ||||||
| class ZeroTerminatedRamStringConst { | class ZeroTerminatedRamStringConst { | ||||||
|  public: |  public: | ||||||
|   ZeroTerminatedRamStringConst(const char* str) : _str(str) {} |   ZeroTerminatedRamStringConst(const char* str = 0) : _str(str) {} | ||||||
|  |  | ||||||
|   bool equals(const char* expected) const { |   bool equals(const char* expected) const { | ||||||
|     const char* actual = _str; |     const char* actual = _str; | ||||||
| @@ -23,15 +23,19 @@ class ZeroTerminatedRamStringConst { | |||||||
|     return !_str; |     return !_str; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   template <typename TMemoryPool> |   // template <typename TMemoryPool> | ||||||
|   const char* save(TMemoryPool*) const { |   // const char* save(TMemoryPool*) const { | ||||||
|     return _str; |   //   return _str; | ||||||
|   } |   // } | ||||||
|  |  | ||||||
|   size_t size() const { |   size_t size() const { | ||||||
|     return strlen(_str); |     return strlen(_str); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   const char* c_str() const { | ||||||
|  |     return _str; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   const char* _str; |   const char* _str; | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -3,11 +3,13 @@ | |||||||
| # MIT License | # MIT License | ||||||
|  |  | ||||||
| add_executable(DynamicMemoryPoolTests | add_executable(DynamicMemoryPoolTests | ||||||
| 	alloc.cpp | 	allocString.cpp | ||||||
| 	allocSlot.cpp | 	allocVariant.cpp | ||||||
|  | 	blocks.cpp | ||||||
|  | 	clear.cpp | ||||||
| 	no_memory.cpp | 	no_memory.cpp | ||||||
| 	size.cpp | 	size.cpp | ||||||
| 	startString.cpp | 	StringBuilder.cpp | ||||||
| ) | ) | ||||||
|  |  | ||||||
| target_link_libraries(DynamicMemoryPoolTests catch) | target_link_libraries(DynamicMemoryPoolTests catch) | ||||||
|   | |||||||
							
								
								
									
										41
									
								
								test/DynamicMemoryPool/StringBuilder.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								test/DynamicMemoryPool/StringBuilder.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | |||||||
|  | // ArduinoJson - arduinojson.org | ||||||
|  | // Copyright Benoit Blanchon 2014-2018 | ||||||
|  | // MIT License | ||||||
|  |  | ||||||
|  | #include <ArduinoJson/Memory/DynamicMemoryPool.hpp> | ||||||
|  | #include <ArduinoJson/Memory/StringBuilder.hpp> | ||||||
|  | #include <catch.hpp> | ||||||
|  |  | ||||||
|  | using namespace ARDUINOJSON_NAMESPACE; | ||||||
|  |  | ||||||
|  | TEST_CASE("DynamicMemoryPool::startString()") { | ||||||
|  |   SECTION("WorksWhenBufferIsBigEnough") { | ||||||
|  |     DynamicMemoryPool memoryPool(JSON_STRING_SIZE(8)); | ||||||
|  |  | ||||||
|  |     StringBuilder str(&memoryPool); | ||||||
|  |     str.append("abcdefg"); | ||||||
|  |  | ||||||
|  |     REQUIRE(memoryPool.blockCount() == 1); | ||||||
|  |     REQUIRE(str.complete().equals("abcdefg")); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("GrowsWhenBufferIsTooSmall") { | ||||||
|  |     DynamicMemoryPool memoryPool(JSON_STRING_SIZE(8)); | ||||||
|  |  | ||||||
|  |     StringBuilder str(&memoryPool); | ||||||
|  |     str.append("abcdefghABC"); | ||||||
|  |  | ||||||
|  |     REQUIRE(memoryPool.blockCount() == 2); | ||||||
|  |     REQUIRE(str.complete().equals("abcdefghABC")); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("SizeIncreases") { | ||||||
|  |     DynamicMemoryPool memoryPool(JSON_STRING_SIZE(5)); | ||||||
|  |  | ||||||
|  |     StringBuilder str(&memoryPool); | ||||||
|  |     str.append('h'); | ||||||
|  |     str.complete(); | ||||||
|  |  | ||||||
|  |     REQUIRE(JSON_STRING_SIZE(2) == memoryPool.size()); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -1,77 +0,0 @@ | |||||||
| // ArduinoJson - arduinojson.org |  | ||||||
| // Copyright Benoit Blanchon 2014-2018 |  | ||||||
| // MIT License |  | ||||||
|  |  | ||||||
| #include <ArduinoJson.h> |  | ||||||
| #include <catch.hpp> |  | ||||||
| #include <sstream> |  | ||||||
|  |  | ||||||
| using namespace ARDUINOJSON_NAMESPACE; |  | ||||||
|  |  | ||||||
| static bool isAligned(void* ptr) { |  | ||||||
|   const size_t mask = sizeof(void*) - 1; |  | ||||||
|   size_t addr = reinterpret_cast<size_t>(ptr); |  | ||||||
|   return (addr & mask) == 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::stringstream allocatorLog; |  | ||||||
|  |  | ||||||
| struct SpyingAllocator : DefaultAllocator { |  | ||||||
|   void* allocate(size_t n) { |  | ||||||
|     allocatorLog << "A" << (n - DynamicMemoryPool::EmptyBlockSize); |  | ||||||
|     return DefaultAllocator::allocate(n); |  | ||||||
|   } |  | ||||||
|   void deallocate(void* p) { |  | ||||||
|     allocatorLog << "F"; |  | ||||||
|     return DefaultAllocator::deallocate(p); |  | ||||||
|   } |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| TEST_CASE("DynamicMemoryPool::alloc()") { |  | ||||||
|   SECTION("Returns different pointers") { |  | ||||||
|     DynamicMemoryPool memoryPool; |  | ||||||
|     void* p1 = memoryPool.alloc(1); |  | ||||||
|     void* p2 = memoryPool.alloc(2); |  | ||||||
|     REQUIRE(p1 != p2); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("Doubles allocation size when full") { |  | ||||||
|     allocatorLog.str(""); |  | ||||||
|     { |  | ||||||
|       DynamicMemoryPoolBase<SpyingAllocator> memoryPool(1); |  | ||||||
|       memoryPool.alloc(1); |  | ||||||
|       memoryPool.alloc(1); |  | ||||||
|     } |  | ||||||
|     REQUIRE(allocatorLog.str() == "A1A2FF"); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("Resets allocation size after clear()") { |  | ||||||
|     allocatorLog.str(""); |  | ||||||
|     { |  | ||||||
|       DynamicMemoryPoolBase<SpyingAllocator> memoryPool(1); |  | ||||||
|       memoryPool.alloc(1); |  | ||||||
|       memoryPool.alloc(1); |  | ||||||
|       memoryPool.clear(); |  | ||||||
|       memoryPool.alloc(1); |  | ||||||
|     } |  | ||||||
|     REQUIRE(allocatorLog.str() == "A1A2FFA1F"); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("Makes a big allocation when needed") { |  | ||||||
|     allocatorLog.str(""); |  | ||||||
|     { |  | ||||||
|       DynamicMemoryPoolBase<SpyingAllocator> memoryPool(1); |  | ||||||
|       memoryPool.alloc(42); |  | ||||||
|     } |  | ||||||
|     REQUIRE(allocatorLog.str() == "A42F"); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("Alignment") { |  | ||||||
|     // make room for two but not three |  | ||||||
|     DynamicMemoryPool tinyBuf(2 * sizeof(void*) + 1); |  | ||||||
|  |  | ||||||
|     REQUIRE(isAligned(tinyBuf.alloc(1)));  // this on is aligned by design |  | ||||||
|     REQUIRE(isAligned(tinyBuf.alloc(1)));  // this one fits in the first block |  | ||||||
|     REQUIRE(isAligned(tinyBuf.alloc(1)));  // this one requires a new block |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @@ -1,27 +0,0 @@ | |||||||
| // ArduinoJson - arduinojson.org |  | ||||||
| // Copyright Benoit Blanchon 2014-2018 |  | ||||||
| // MIT License |  | ||||||
|  |  | ||||||
| #include <ArduinoJson/Memory/DynamicMemoryPool.hpp> |  | ||||||
| #include <catch.hpp> |  | ||||||
|  |  | ||||||
| using namespace ARDUINOJSON_NAMESPACE; |  | ||||||
|  |  | ||||||
| TEST_CASE("DynamicMemoryPool::allocSlot()") { |  | ||||||
|   DynamicMemoryPool memoryPool; |  | ||||||
|  |  | ||||||
|   SECTION("Returns different pointer") { |  | ||||||
|     Slot* s1 = memoryPool.allocSlot(); |  | ||||||
|     Slot* s2 = memoryPool.allocSlot(); |  | ||||||
|  |  | ||||||
|     REQUIRE(s1 != s2); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("Returns same pointer after freeSlot()") { |  | ||||||
|     Slot* s1 = memoryPool.allocSlot(); |  | ||||||
|     memoryPool.freeSlot(s1); |  | ||||||
|     Slot* s2 = memoryPool.allocSlot(); |  | ||||||
|  |  | ||||||
|     REQUIRE(s1 == s2); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
							
								
								
									
										28
									
								
								test/DynamicMemoryPool/allocString.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								test/DynamicMemoryPool/allocString.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | |||||||
|  | // ArduinoJson - arduinojson.org | ||||||
|  | // Copyright Benoit Blanchon 2014-2018 | ||||||
|  | // MIT License | ||||||
|  |  | ||||||
|  | #include <ArduinoJson/Memory/DynamicMemoryPool.hpp> | ||||||
|  | #include <catch.hpp> | ||||||
|  | #include <sstream> | ||||||
|  |  | ||||||
|  | using namespace ARDUINOJSON_NAMESPACE; | ||||||
|  |  | ||||||
|  | TEST_CASE("DynamicMemoryPool::allocFrozenString()") { | ||||||
|  |   DynamicMemoryPool pool; | ||||||
|  |  | ||||||
|  |   SECTION("Returns different pointers") { | ||||||
|  |     StringSlot* a = pool.allocFrozenString(1); | ||||||
|  |     StringSlot* b = pool.allocFrozenString(2); | ||||||
|  |     REQUIRE(a != b); | ||||||
|  |     REQUIRE(a->value != b->value); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("Returns same slot after freeString") { | ||||||
|  |     StringSlot* a = pool.allocFrozenString(1); | ||||||
|  |     pool.freeString(a); | ||||||
|  |     StringSlot* b = pool.allocFrozenString(2); | ||||||
|  |     REQUIRE(a == b); | ||||||
|  |     REQUIRE(a->value == b->value); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										41
									
								
								test/DynamicMemoryPool/allocVariant.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								test/DynamicMemoryPool/allocVariant.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | |||||||
|  | // ArduinoJson - arduinojson.org | ||||||
|  | // Copyright Benoit Blanchon 2014-2018 | ||||||
|  | // MIT License | ||||||
|  |  | ||||||
|  | #include <ArduinoJson/Memory/DynamicMemoryPool.hpp> | ||||||
|  | #include <catch.hpp> | ||||||
|  |  | ||||||
|  | using namespace ARDUINOJSON_NAMESPACE; | ||||||
|  |  | ||||||
|  | TEST_CASE("DynamicMemoryPool::allocVariant()") { | ||||||
|  |   DynamicMemoryPool memoryPool; | ||||||
|  |  | ||||||
|  |   SECTION("Returns different pointer") { | ||||||
|  |     VariantSlot* s1 = memoryPool.allocVariant(); | ||||||
|  |     VariantSlot* s2 = memoryPool.allocVariant(); | ||||||
|  |  | ||||||
|  |     REQUIRE(s1 != s2); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("Returns same pointer after freeSlot()") { | ||||||
|  |     VariantSlot* s1 = memoryPool.allocVariant(); | ||||||
|  |     memoryPool.freeVariant(s1); | ||||||
|  |     VariantSlot* s2 = memoryPool.allocVariant(); | ||||||
|  |  | ||||||
|  |     REQUIRE(s1 == s2); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("Returns aligned pointers") { | ||||||
|  |     // make room for two but not three | ||||||
|  |     // pass an uneven capacity | ||||||
|  |     DynamicMemoryPool pool(2 * sizeof(VariantSlot) + 1); | ||||||
|  |  | ||||||
|  |     REQUIRE(isAligned(pool.allocVariant())); | ||||||
|  |     REQUIRE(isAligned(pool.allocVariant())); | ||||||
|  |     REQUIRE(pool.blockCount() == 1); | ||||||
|  |  | ||||||
|  |     REQUIRE(isAligned(pool.allocVariant())); | ||||||
|  |     REQUIRE(isAligned(pool.allocVariant())); | ||||||
|  |     REQUIRE(pool.blockCount() == 2); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										69
									
								
								test/DynamicMemoryPool/blocks.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								test/DynamicMemoryPool/blocks.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | |||||||
|  | // ArduinoJson - arduinojson.org | ||||||
|  | // Copyright Benoit Blanchon 2014-2018 | ||||||
|  | // MIT License | ||||||
|  |  | ||||||
|  | #include <ArduinoJson/Memory/DynamicMemoryPool.hpp> | ||||||
|  | #include <catch.hpp> | ||||||
|  | #include <sstream> | ||||||
|  |  | ||||||
|  | using namespace ARDUINOJSON_NAMESPACE; | ||||||
|  |  | ||||||
|  | std::stringstream allocatorLog; | ||||||
|  |  | ||||||
|  | struct SpyingAllocator : DefaultAllocator { | ||||||
|  |   void* allocate(size_t n) { | ||||||
|  |     allocatorLog << "A" << (n - DynamicMemoryPool::EmptyBlockSize); | ||||||
|  |     return DefaultAllocator::allocate(n); | ||||||
|  |   } | ||||||
|  |   void deallocate(void* p) { | ||||||
|  |     allocatorLog << "F"; | ||||||
|  |     return DefaultAllocator::deallocate(p); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | TEST_CASE("DynamicMemoryPool blocks") { | ||||||
|  |   SECTION("Doubles allocation size when full") { | ||||||
|  |     allocatorLog.str(""); | ||||||
|  |     { | ||||||
|  |       DynamicMemoryPoolBase<SpyingAllocator> memoryPool(sizeof(VariantSlot)); | ||||||
|  |       memoryPool.allocVariant(); | ||||||
|  |       memoryPool.allocVariant(); | ||||||
|  |     } | ||||||
|  |     std::stringstream expected; | ||||||
|  |     expected << "A" << sizeof(VariantSlot)      // block 1 | ||||||
|  |              << "A" << 2 * sizeof(VariantSlot)  // block 2, twice bigger | ||||||
|  |              << "FF"; | ||||||
|  |  | ||||||
|  |     REQUIRE(allocatorLog.str() == expected.str()); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("Resets allocation size after clear()") { | ||||||
|  |     allocatorLog.str(""); | ||||||
|  |     { | ||||||
|  |       DynamicMemoryPoolBase<SpyingAllocator> memoryPool(sizeof(VariantSlot)); | ||||||
|  |       memoryPool.allocVariant(); | ||||||
|  |       memoryPool.allocVariant(); | ||||||
|  |       memoryPool.clear(); | ||||||
|  |       memoryPool.allocVariant(); | ||||||
|  |     } | ||||||
|  |     std::stringstream expected; | ||||||
|  |     expected << "A" << sizeof(VariantSlot)      // block 1 | ||||||
|  |              << "A" << 2 * sizeof(VariantSlot)  // block 2, twice bigger | ||||||
|  |              << "FF"                            // clear | ||||||
|  |              << "A" << sizeof(VariantSlot)      // block 1 | ||||||
|  |              << "F"; | ||||||
|  |     REQUIRE(allocatorLog.str() == expected.str()); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /* SECTION("Alloc big block for large string") { | ||||||
|  |      allocatorLog.str(""); | ||||||
|  |      { | ||||||
|  |        DynamicMemoryPoolBase<SpyingAllocator> memoryPool(1); | ||||||
|  |        memoryPool.allocString(42); | ||||||
|  |      } | ||||||
|  |      std::stringstream expected; | ||||||
|  |      expected << "A" << JSON_STRING_SIZE(42)  // block 1 | ||||||
|  |               << "F"; | ||||||
|  |      REQUIRE(allocatorLog.str() == expected.str()); | ||||||
|  |    }*/ | ||||||
|  | } | ||||||
							
								
								
									
										47
									
								
								test/DynamicMemoryPool/clear.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								test/DynamicMemoryPool/clear.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | |||||||
|  | // ArduinoJson - arduinojson.org | ||||||
|  | // Copyright Benoit Blanchon 2014-2018 | ||||||
|  | // MIT License | ||||||
|  |  | ||||||
|  | #include <ArduinoJson/Memory/DynamicMemoryPool.hpp> | ||||||
|  | #include <catch.hpp> | ||||||
|  |  | ||||||
|  | using namespace ARDUINOJSON_NAMESPACE; | ||||||
|  |  | ||||||
|  | TEST_CASE("StaticMemoryPool::clear()") { | ||||||
|  |   DynamicMemoryPool memoryPool; | ||||||
|  |  | ||||||
|  |   SECTION("Discards allocated variants") { | ||||||
|  |     memoryPool.allocVariant(); | ||||||
|  |     REQUIRE(memoryPool.size() > 0); | ||||||
|  |  | ||||||
|  |     memoryPool.clear(); | ||||||
|  |     CHECK(memoryPool.size() == 0); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("Discards allocated strings") { | ||||||
|  |     memoryPool.allocFrozenString(10); | ||||||
|  |     REQUIRE(memoryPool.size() > 0); | ||||||
|  |  | ||||||
|  |     memoryPool.clear(); | ||||||
|  |  | ||||||
|  |     CHECK(memoryPool.size() == 0); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("Purges variant cache") { | ||||||
|  |     memoryPool.freeVariant(memoryPool.allocVariant()); | ||||||
|  |     REQUIRE(memoryPool.size() == 0); | ||||||
|  |  | ||||||
|  |     memoryPool.clear(); | ||||||
|  |  | ||||||
|  |     CHECK(memoryPool.size() == 0); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("Purges string cache") { | ||||||
|  |     memoryPool.freeString(memoryPool.allocFrozenString(10)); | ||||||
|  |     // REQUIRE(memoryPool.size() == 0); | ||||||
|  |  | ||||||
|  |     memoryPool.clear(); | ||||||
|  |  | ||||||
|  |     CHECK(memoryPool.size() == 0); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -2,7 +2,8 @@ | |||||||
| // Copyright Benoit Blanchon 2014-2018 | // Copyright Benoit Blanchon 2014-2018 | ||||||
| // MIT License | // MIT License | ||||||
|  |  | ||||||
| #include <ArduinoJson.h> | #include <ArduinoJson/Memory/DynamicMemoryPool.hpp> | ||||||
|  | #include <ArduinoJson/Memory/StringBuilder.hpp> | ||||||
| #include <catch.hpp> | #include <catch.hpp> | ||||||
|  |  | ||||||
| using namespace ARDUINOJSON_NAMESPACE; | using namespace ARDUINOJSON_NAMESPACE; | ||||||
| @@ -32,8 +33,8 @@ TEST_CASE("DynamicMemoryPool no memory") { | |||||||
|   //   REQUIRE(err != DeserializationError::Ok); |   //   REQUIRE(err != DeserializationError::Ok); | ||||||
|   // } |   // } | ||||||
|  |  | ||||||
|   SECTION("startString()") { |   SECTION("StringBuilder returns null") { | ||||||
|     StringBuilder str = _memoryPool.startString(); |     StringBuilder str(&_memoryPool); | ||||||
|     str.append('!'); |     str.append('!'); | ||||||
|     REQUIRE(str.complete().isNull()); |     REQUIRE(str.complete().isNull()); | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -14,35 +14,43 @@ TEST_CASE("DynamicMemoryPool::size()") { | |||||||
|     REQUIRE(0 == memoryPool.size()); |     REQUIRE(0 == memoryPool.size()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("Increases after alloc()") { |   SECTION("Increases after allocExpandableString()") { | ||||||
|     memoryPool.alloc(1); |     StringSlot* a = memoryPool.allocExpandableString(); | ||||||
|     REQUIRE(1U <= memoryPool.size()); |     memoryPool.freezeString(a, 1); | ||||||
|     memoryPool.alloc(1); |     REQUIRE(memoryPool.size() == JSON_STRING_SIZE(1)); | ||||||
|     REQUIRE(2U <= memoryPool.size()); |  | ||||||
|  |     StringSlot* b = memoryPool.allocExpandableString(); | ||||||
|  |     memoryPool.freezeString(b, 1); | ||||||
|  |     REQUIRE(memoryPool.size() == 2 * JSON_STRING_SIZE(1)); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("Goes back to 0 after clear()") { |   SECTION("Increases after allocVariant()") { | ||||||
|     memoryPool.alloc(1); |     memoryPool.allocVariant(); | ||||||
|     memoryPool.clear(); |     REQUIRE(sizeof(VariantSlot) == memoryPool.size()); | ||||||
|  |  | ||||||
|  |     memoryPool.allocVariant(); | ||||||
|  |     REQUIRE(2 * sizeof(VariantSlot) == memoryPool.size()); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("Decreases after freeVariant()") { | ||||||
|  |     VariantSlot* a = memoryPool.allocVariant(); | ||||||
|  |     VariantSlot* b = memoryPool.allocVariant(); | ||||||
|  |  | ||||||
|  |     memoryPool.freeVariant(b); | ||||||
|  |     REQUIRE(sizeof(VariantSlot) == memoryPool.size()); | ||||||
|  |  | ||||||
|  |     memoryPool.freeVariant(a); | ||||||
|     REQUIRE(0 == memoryPool.size()); |     REQUIRE(0 == memoryPool.size()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("Increases after allocSlot()") { |   SECTION("Decreases after freeString()") { | ||||||
|     memoryPool.allocSlot(); |     StringSlot* a = memoryPool.allocFrozenString(5); | ||||||
|     REQUIRE(sizeof(Slot) == memoryPool.size()); |     StringSlot* b = memoryPool.allocFrozenString(6); | ||||||
|  |  | ||||||
|     memoryPool.allocSlot(); |     memoryPool.freeString(b); | ||||||
|     REQUIRE(2 * sizeof(Slot) == memoryPool.size()); |     REQUIRE(memoryPool.size() == JSON_STRING_SIZE(5)); | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("Decreases after freeSlot()") { |     memoryPool.freeString(a); | ||||||
|     Slot* s1 = memoryPool.allocSlot(); |     REQUIRE(memoryPool.size() == 0); | ||||||
|     Slot* s2 = memoryPool.allocSlot(); |  | ||||||
|  |  | ||||||
|     memoryPool.freeSlot(s1); |  | ||||||
|     REQUIRE(sizeof(Slot) == memoryPool.size()); |  | ||||||
|  |  | ||||||
|     memoryPool.freeSlot(s2); |  | ||||||
|     REQUIRE(0 == memoryPool.size()); |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,46 +0,0 @@ | |||||||
| // ArduinoJson - arduinojson.org |  | ||||||
| // Copyright Benoit Blanchon 2014-2018 |  | ||||||
| // MIT License |  | ||||||
|  |  | ||||||
| #include <ArduinoJson/Memory/DynamicMemoryPool.hpp> |  | ||||||
| #include <catch.hpp> |  | ||||||
|  |  | ||||||
| using namespace ARDUINOJSON_NAMESPACE; |  | ||||||
|  |  | ||||||
| TEST_CASE("DynamicMemoryPool::startString()") { |  | ||||||
|   SECTION("WorksWhenBufferIsBigEnough") { |  | ||||||
|     DynamicMemoryPool memoryPool(6); |  | ||||||
|  |  | ||||||
|     StringBuilder str = memoryPool.startString(); |  | ||||||
|     str.append('h'); |  | ||||||
|     str.append('e'); |  | ||||||
|     str.append('l'); |  | ||||||
|     str.append('l'); |  | ||||||
|     str.append('o'); |  | ||||||
|  |  | ||||||
|     REQUIRE(str.complete().equals("hello")); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("GrowsWhenBufferIsTooSmall") { |  | ||||||
|     DynamicMemoryPool memoryPool(5); |  | ||||||
|  |  | ||||||
|     StringBuilder str = memoryPool.startString(); |  | ||||||
|     str.append('h'); |  | ||||||
|     str.append('e'); |  | ||||||
|     str.append('l'); |  | ||||||
|     str.append('l'); |  | ||||||
|     str.append('o'); |  | ||||||
|  |  | ||||||
|     REQUIRE(str.complete().equals("hello")); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("SizeIncreases") { |  | ||||||
|     DynamicMemoryPool memoryPool(5); |  | ||||||
|  |  | ||||||
|     StringBuilder str = memoryPool.startString(); |  | ||||||
|     str.append('h'); |  | ||||||
|     str.complete(); |  | ||||||
|  |  | ||||||
|     REQUIRE(2 == memoryPool.size()); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @@ -102,13 +102,13 @@ TEST_CASE("JsonArray::add()") { | |||||||
|  |  | ||||||
|   SECTION("should duplicate char*") { |   SECTION("should duplicate char*") { | ||||||
|     array.add(const_cast<char*>("world")); |     array.add(const_cast<char*>("world")); | ||||||
|     const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6; |     const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(6); | ||||||
|     REQUIRE(expectedSize == doc.memoryUsage()); |     REQUIRE(expectedSize == doc.memoryUsage()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("should duplicate std::string") { |   SECTION("should duplicate std::string") { | ||||||
|     array.add(std::string("world")); |     array.add(std::string("world")); | ||||||
|     const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6; |     const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(6); | ||||||
|     REQUIRE(expectedSize == doc.memoryUsage()); |     REQUIRE(expectedSize == doc.memoryUsage()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -120,19 +120,19 @@ TEST_CASE("JsonArray::add()") { | |||||||
|  |  | ||||||
|   SECTION("should duplicate serialized(char*)") { |   SECTION("should duplicate serialized(char*)") { | ||||||
|     array.add(serialized(const_cast<char*>("{}"))); |     array.add(serialized(const_cast<char*>("{}"))); | ||||||
|     const size_t expectedSize = JSON_ARRAY_SIZE(1) + 2; |     const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(2); | ||||||
|     REQUIRE(expectedSize == doc.memoryUsage()); |     REQUIRE(expectedSize == doc.memoryUsage()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("should duplicate serialized(std::string)") { |   SECTION("should duplicate serialized(std::string)") { | ||||||
|     array.add(serialized(std::string("{}"))); |     array.add(serialized(std::string("{}"))); | ||||||
|     const size_t expectedSize = JSON_ARRAY_SIZE(1) + 2; |     const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(2); | ||||||
|     REQUIRE(expectedSize == doc.memoryUsage()); |     REQUIRE(expectedSize == doc.memoryUsage()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("should duplicate serialized(std::string)") { |   SECTION("should duplicate serialized(std::string)") { | ||||||
|     array.add(serialized(std::string("\0XX", 3))); |     array.add(serialized(std::string("\0XX", 3))); | ||||||
|     const size_t expectedSize = JSON_ARRAY_SIZE(1) + 3; |     const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(3); | ||||||
|     REQUIRE(expectedSize == doc.memoryUsage()); |     REQUIRE(expectedSize == doc.memoryUsage()); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -105,13 +105,13 @@ TEST_CASE("JsonArray::operator[]") { | |||||||
|  |  | ||||||
|   SECTION("should duplicate char*") { |   SECTION("should duplicate char*") { | ||||||
|     array[0] = const_cast<char*>("world"); |     array[0] = const_cast<char*>("world"); | ||||||
|     const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6; |     const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(6); | ||||||
|     REQUIRE(expectedSize == doc.memoryUsage()); |     REQUIRE(expectedSize == doc.memoryUsage()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("should duplicate std::string") { |   SECTION("should duplicate std::string") { | ||||||
|     array[0] = std::string("world"); |     array[0] = std::string("world"); | ||||||
|     const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6; |     const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(6); | ||||||
|     REQUIRE(expectedSize == doc.memoryUsage()); |     REQUIRE(expectedSize == doc.memoryUsage()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ TEST_CASE("deserialize JSON array with a StaticJsonDocument") { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("TooSmallBufferForArrayWithOneValue") { |   SECTION("TooSmallBufferForArrayWithOneValue") { | ||||||
|     StaticJsonDocument<JSON_ARRAY_SIZE(1) - 1> doc; |     StaticJsonDocument<JSON_ARRAY_SIZE(0)> doc; | ||||||
|     char input[] = "[1]"; |     char input[] = "[1]"; | ||||||
|  |  | ||||||
|     DeserializationError err = deserializeJson(doc, input); |     DeserializationError err = deserializeJson(doc, input); | ||||||
| @@ -34,7 +34,7 @@ TEST_CASE("deserialize JSON array with a StaticJsonDocument") { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("TooSmallBufferForArrayWithNestedObject") { |   SECTION("TooSmallBufferForArrayWithNestedObject") { | ||||||
|     StaticJsonDocument<JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(0) - 1> doc; |     StaticJsonDocument<JSON_ARRAY_SIZE(0) + JSON_OBJECT_SIZE(0)> doc; | ||||||
|     char input[] = "[{}]"; |     char input[] = "[{}]"; | ||||||
|  |  | ||||||
|     DeserializationError err = deserializeJson(doc, input); |     DeserializationError err = deserializeJson(doc, input); | ||||||
| @@ -56,7 +56,7 @@ TEST_CASE("deserialize JSON array with a StaticJsonDocument") { | |||||||
|  |  | ||||||
|     deserializeJson(doc, "  [ \"1234567\" ] "); |     deserializeJson(doc, "  [ \"1234567\" ] "); | ||||||
|  |  | ||||||
|     REQUIRE(JSON_ARRAY_SIZE(1) + sizeof("1234567") == doc.memoryUsage()); |     REQUIRE(JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(8) == doc.memoryUsage()); | ||||||
|     // note: we use a string of 8 bytes to be sure that the StaticMemoryPool |     // note: we use a string of 8 bytes to be sure that the StaticMemoryPool | ||||||
|     // will not insert bytes to enforce alignement |     // will not insert bytes to enforce alignement | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ TEST_CASE("deserialize JSON object with StaticJsonDocument") { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("TooSmallBufferForObjectWithOneValue") { |   SECTION("TooSmallBufferForObjectWithOneValue") { | ||||||
|     StaticJsonDocument<JSON_OBJECT_SIZE(1) - 1> doc; |     StaticJsonDocument<JSON_OBJECT_SIZE(0)> doc; | ||||||
|     char input[] = "{\"a\":1}"; |     char input[] = "{\"a\":1}"; | ||||||
|  |  | ||||||
|     DeserializationError err = deserializeJson(doc, input); |     DeserializationError err = deserializeJson(doc, input); | ||||||
| @@ -34,7 +34,7 @@ TEST_CASE("deserialize JSON object with StaticJsonDocument") { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("TooSmallBufferForObjectWithNestedObject") { |   SECTION("TooSmallBufferForObjectWithNestedObject") { | ||||||
|     StaticJsonDocument<JSON_OBJECT_SIZE(1) + JSON_ARRAY_SIZE(0) - 1> doc; |     StaticJsonDocument<JSON_OBJECT_SIZE(0) + JSON_ARRAY_SIZE(0)> doc; | ||||||
|     char input[] = "{\"a\":[]}"; |     char input[] = "{\"a\":[]}"; | ||||||
|  |  | ||||||
|     DeserializationError err = deserializeJson(doc, input); |     DeserializationError err = deserializeJson(doc, input); | ||||||
|   | |||||||
| @@ -94,35 +94,36 @@ TEST_CASE("DynamicJsonDocument") { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("memoryUsage()") { |   SECTION("memoryUsage()") { | ||||||
|     typedef ARDUINOJSON_NAMESPACE::Slot Slot; |  | ||||||
|  |  | ||||||
|     SECTION("Increases after adding value to array") { |     SECTION("Increases after adding value to array") { | ||||||
|       JsonArray arr = doc.to<JsonArray>(); |       JsonArray arr = doc.to<JsonArray>(); | ||||||
|  |  | ||||||
|  |       REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(0)); | ||||||
|       arr.add(42); |       arr.add(42); | ||||||
|       REQUIRE(sizeof(Slot) == doc.memoryUsage()); |       REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(1)); | ||||||
|       arr.add(43); |       arr.add(43); | ||||||
|       REQUIRE(2 * sizeof(Slot) == doc.memoryUsage()); |       REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(2)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     SECTION("Decreases after remove value from array") { |     SECTION("Decreases after removing value from array") { | ||||||
|       JsonArray arr = doc.to<JsonArray>(); |       JsonArray arr = doc.to<JsonArray>(); | ||||||
|       arr.add(42); |       arr.add(42); | ||||||
|       arr.add(43); |       arr.add(43); | ||||||
|  |  | ||||||
|  |       REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(2)); | ||||||
|       arr.remove(1); |       arr.remove(1); | ||||||
|       REQUIRE(sizeof(Slot) == doc.memoryUsage()); |       REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(1)); | ||||||
|       arr.remove(0); |       arr.remove(0); | ||||||
|       REQUIRE(0 == doc.memoryUsage()); |       REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(0)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     SECTION("Increases after adding value to object") { |     SECTION("Increases after adding value to object") { | ||||||
|       JsonObject obj = doc.to<JsonObject>(); |       JsonObject obj = doc.to<JsonObject>(); | ||||||
|  |  | ||||||
|  |       REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(0)); | ||||||
|       obj["a"] = 1; |       obj["a"] = 1; | ||||||
|       REQUIRE(sizeof(Slot) == doc.memoryUsage()); |       REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(1)); | ||||||
|       obj["b"] = 2; |       obj["b"] = 2; | ||||||
|       REQUIRE(2 * sizeof(Slot) == doc.memoryUsage()); |       REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     SECTION("Decreases after removing value from object") { |     SECTION("Decreases after removing value from object") { | ||||||
| @@ -130,10 +131,11 @@ TEST_CASE("DynamicJsonDocument") { | |||||||
|       obj["a"] = 1; |       obj["a"] = 1; | ||||||
|       obj["b"] = 2; |       obj["b"] = 2; | ||||||
|  |  | ||||||
|  |       REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2)); | ||||||
|       obj.remove("a"); |       obj.remove("a"); | ||||||
|       REQUIRE(sizeof(Slot) == doc.memoryUsage()); |       REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(1)); | ||||||
|       obj.remove("b"); |       obj.remove("b"); | ||||||
|       REQUIRE(0 == doc.memoryUsage()); |       REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(0)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     SECTION("Decreases after removing nested object from array") { |     SECTION("Decreases after removing nested object from array") { | ||||||
| @@ -141,8 +143,9 @@ TEST_CASE("DynamicJsonDocument") { | |||||||
|       JsonObject obj = arr.createNestedObject(); |       JsonObject obj = arr.createNestedObject(); | ||||||
|       obj["hello"] = "world"; |       obj["hello"] = "world"; | ||||||
|  |  | ||||||
|  |       REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(1)); | ||||||
|       arr.remove(0); |       arr.remove(0); | ||||||
|       REQUIRE(0 == doc.memoryUsage()); |       REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(0)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     SECTION("Decreases after removing nested array from object") { |     SECTION("Decreases after removing nested array from object") { | ||||||
| @@ -150,8 +153,9 @@ TEST_CASE("DynamicJsonDocument") { | |||||||
|       JsonArray arr = obj.createNestedArray("hello"); |       JsonArray arr = obj.createNestedArray("hello"); | ||||||
|       arr.add("world"); |       arr.add("world"); | ||||||
|  |  | ||||||
|  |       REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(1)); | ||||||
|       obj.remove("hello"); |       obj.remove("hello"); | ||||||
|       REQUIRE(0 == doc.memoryUsage()); |       REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(0)); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -19,18 +19,47 @@ TEST_CASE("JsonObject::remove()") { | |||||||
|       obj.remove("a"); |       obj.remove("a"); | ||||||
|       serializeJson(obj, result); |       serializeJson(obj, result); | ||||||
|       REQUIRE("{\"b\":1,\"c\":2}" == result); |       REQUIRE("{\"b\":1,\"c\":2}" == result); | ||||||
|  |       REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     SECTION("Remove middle") { |     SECTION("Remove middle") { | ||||||
|       obj.remove("b"); |       obj.remove("b"); | ||||||
|       serializeJson(obj, result); |       serializeJson(obj, result); | ||||||
|       REQUIRE("{\"a\":0,\"c\":2}" == result); |       REQUIRE("{\"a\":0,\"c\":2}" == result); | ||||||
|  |       REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     SECTION("Remove last") { |     SECTION("Remove last") { | ||||||
|       obj.remove("c"); |       obj.remove("c"); | ||||||
|       serializeJson(obj, result); |       serializeJson(obj, result); | ||||||
|       REQUIRE("{\"a\":0,\"b\":1}" == result); |       REQUIRE("{\"a\":0,\"b\":1}" == result); | ||||||
|  |       REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     SECTION("Release value string memory") { | ||||||
|  |       obj["c"] = std::string("Copy me!"); | ||||||
|  |       REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(3) + JSON_STRING_SIZE(9)); | ||||||
|  |  | ||||||
|  |       obj.remove("c"); | ||||||
|  |       REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     SECTION("Release key string memory") { | ||||||
|  |       obj[std::string("Copy me!")] = 42; | ||||||
|  |       REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(4) + JSON_STRING_SIZE(9)); | ||||||
|  |  | ||||||
|  |       obj.remove("Copy me!"); | ||||||
|  |  | ||||||
|  |       REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(3)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     SECTION("Release raw string memory") { | ||||||
|  |       obj["c"] = serialized(std::string("Copy me!")); | ||||||
|  |       REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(3) + JSON_STRING_SIZE(8)); | ||||||
|  |  | ||||||
|  |       obj.remove("c"); | ||||||
|  |  | ||||||
|  |       REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2)); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -107,37 +107,37 @@ TEST_CASE("JsonObject::operator[]") { | |||||||
|  |  | ||||||
|   SECTION("should duplicate char* value") { |   SECTION("should duplicate char* value") { | ||||||
|     obj["hello"] = const_cast<char*>("world"); |     obj["hello"] = const_cast<char*>("world"); | ||||||
|     const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6; |     const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(6); | ||||||
|     REQUIRE(expectedSize == doc.memoryUsage()); |     REQUIRE(expectedSize == doc.memoryUsage()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("should duplicate char* key") { |   SECTION("should duplicate char* key") { | ||||||
|     obj[const_cast<char*>("hello")] = "world"; |     obj[const_cast<char*>("hello")] = "world"; | ||||||
|     const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6; |     const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(6); | ||||||
|     REQUIRE(expectedSize == doc.memoryUsage()); |     REQUIRE(expectedSize == doc.memoryUsage()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("should duplicate char* key&value") { |   SECTION("should duplicate char* key&value") { | ||||||
|     obj[const_cast<char*>("hello")] = const_cast<char*>("world"); |     obj[const_cast<char*>("hello")] = const_cast<char*>("world"); | ||||||
|     const size_t expectedSize = JSON_OBJECT_SIZE(1) + 12; |     const size_t expectedSize = JSON_OBJECT_SIZE(1) + 2 * JSON_STRING_SIZE(6); | ||||||
|     REQUIRE(expectedSize <= doc.memoryUsage()); |     REQUIRE(expectedSize <= doc.memoryUsage()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("should duplicate std::string value") { |   SECTION("should duplicate std::string value") { | ||||||
|     obj["hello"] = std::string("world"); |     obj["hello"] = std::string("world"); | ||||||
|     const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6; |     const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(6); | ||||||
|     REQUIRE(expectedSize == doc.memoryUsage()); |     REQUIRE(expectedSize == doc.memoryUsage()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("should duplicate std::string key") { |   SECTION("should duplicate std::string key") { | ||||||
|     obj[std::string("hello")] = "world"; |     obj[std::string("hello")] = "world"; | ||||||
|     const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6; |     const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(6); | ||||||
|     REQUIRE(expectedSize == doc.memoryUsage()); |     REQUIRE(expectedSize == doc.memoryUsage()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("should duplicate std::string key&value") { |   SECTION("should duplicate std::string key&value") { | ||||||
|     obj[std::string("hello")] = std::string("world"); |     obj[std::string("hello")] = std::string("world"); | ||||||
|     const size_t expectedSize = JSON_OBJECT_SIZE(1) + 12; |     const size_t expectedSize = JSON_OBJECT_SIZE(1) + 2 * JSON_STRING_SIZE(6); | ||||||
|     REQUIRE(expectedSize <= doc.memoryUsage()); |     REQUIRE(expectedSize <= doc.memoryUsage()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -108,6 +108,16 @@ TEST_CASE("JsonVariant::as()") { | |||||||
|     REQUIRE(variant.as<std::string>() == std::string("hello")); |     REQUIRE(variant.as<std::string>() == std::string("hello")); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   SECTION("set(std::string(\"4.2\"))") { | ||||||
|  |     variant.set(std::string("4.2")); | ||||||
|  |  | ||||||
|  |     REQUIRE(variant.as<bool>() == true); | ||||||
|  |     REQUIRE(variant.as<long>() == 4L); | ||||||
|  |     REQUIRE(variant.as<double>() == 4.2); | ||||||
|  |     REQUIRE(variant.as<char*>() == std::string("4.2")); | ||||||
|  |     REQUIRE(variant.as<std::string>() == std::string("4.2")); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   SECTION("set(\"true\")") { |   SECTION("set(\"true\")") { | ||||||
|     variant.set("true"); |     variant.set("true"); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -216,7 +216,7 @@ TEST_CASE("JsonVariant comparisons") { | |||||||
|   JsonVariant variant2 = doc2.to<JsonVariant>(); |   JsonVariant variant2 = doc2.to<JsonVariant>(); | ||||||
|   JsonVariant variant3 = doc3.to<JsonVariant>(); |   JsonVariant variant3 = doc3.to<JsonVariant>(); | ||||||
|  |  | ||||||
|   SECTION("IntegerInVariant") { |   SECTION("Variants containing integers") { | ||||||
|     variant1.set(42); |     variant1.set(42); | ||||||
|     variant2.set(42); |     variant2.set(42); | ||||||
|     variant3.set(666); |     variant3.set(666); | ||||||
| @@ -228,7 +228,7 @@ TEST_CASE("JsonVariant comparisons") { | |||||||
|     REQUIRE_FALSE(variant1 == variant3); |     REQUIRE_FALSE(variant1 == variant3); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("StringInVariant") { |   SECTION("Variants containing linked strings") { | ||||||
|     variant1.set("0hello" + 1);  // make sure they have |     variant1.set("0hello" + 1);  // make sure they have | ||||||
|     variant2.set("1hello" + 1);  // different addresses |     variant2.set("1hello" + 1);  // different addresses | ||||||
|     variant3.set("world"); |     variant3.set("world"); | ||||||
| @@ -240,7 +240,19 @@ TEST_CASE("JsonVariant comparisons") { | |||||||
|     REQUIRE_FALSE(variant1 == variant3); |     REQUIRE_FALSE(variant1 == variant3); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("DoubleInVariant") { |   SECTION("Variants containing owned strings") { | ||||||
|  |     variant1.set(std::string("hello")); | ||||||
|  |     variant2.set(std::string("hello")); | ||||||
|  |     variant3.set(std::string("world")); | ||||||
|  |  | ||||||
|  |     REQUIRE(variant1 == variant2); | ||||||
|  |     REQUIRE_FALSE(variant1 != variant2); | ||||||
|  |  | ||||||
|  |     REQUIRE(variant1 != variant3); | ||||||
|  |     REQUIRE_FALSE(variant1 == variant3); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("Variants containing double") { | ||||||
|     variant1.set(42.0); |     variant1.set(42.0); | ||||||
|     variant2.set(42.0); |     variant2.set(42.0); | ||||||
|     variant3.set(666.0); |     variant3.set(666.0); | ||||||
|   | |||||||
| @@ -13,22 +13,24 @@ TEST_CASE("JsonVariant::set(JsonVariant)") { | |||||||
|  |  | ||||||
|   SECTION("stores JsonArray by copy") { |   SECTION("stores JsonArray by copy") { | ||||||
|     JsonArray arr = doc2.to<JsonArray>(); |     JsonArray arr = doc2.to<JsonArray>(); | ||||||
|     arr.add(42); |     JsonObject obj = arr.createNestedObject(); | ||||||
|  |     obj["hello"] = "world"; | ||||||
|  |  | ||||||
|     var1.set(arr); |     var1.set(arr); | ||||||
|  |  | ||||||
|     arr[0] = 666; |     arr[0] = 666; | ||||||
|     REQUIRE(var1.as<std::string>() == "[42]"); |     REQUIRE(var1.as<std::string>() == "[{\"hello\":\"world\"}]"); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("stores JsonObject by copy") { |   SECTION("stores JsonObject by copy") { | ||||||
|     JsonObject obj = doc2.to<JsonObject>(); |     JsonObject obj = doc2.to<JsonObject>(); | ||||||
|     obj["value"] = 42; |     JsonArray arr = obj.createNestedArray("value"); | ||||||
|  |     arr.add(42); | ||||||
|  |  | ||||||
|     var1.set(obj); |     var1.set(obj); | ||||||
|  |  | ||||||
|     obj["value"] = 666; |     obj["value"] = 666; | ||||||
|     REQUIRE(var1.as<std::string>() == "{\"value\":42}"); |     REQUIRE(var1.as<std::string>() == "{\"value\":[42]}"); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("stores const char* by reference") { |   SECTION("stores const char* by reference") { | ||||||
| @@ -45,20 +47,20 @@ TEST_CASE("JsonVariant::set(JsonVariant)") { | |||||||
|     var1.set(str); |     var1.set(str); | ||||||
|     var2.set(var1); |     var2.set(var1); | ||||||
|  |  | ||||||
|     REQUIRE(doc1.memoryUsage() == 8); |     REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(8)); | ||||||
|     REQUIRE(doc2.memoryUsage() == 8); |     REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(8)); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("stores std::string by copy") { |   SECTION("stores std::string by copy") { | ||||||
|     var1.set(std::string("hello!!")); |     var1.set(std::string("hello!!")); | ||||||
|     var2.set(var1); |     var2.set(var1); | ||||||
|  |  | ||||||
|     REQUIRE(doc1.memoryUsage() == 8); |     REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(8)); | ||||||
|     REQUIRE(doc2.memoryUsage() == 8); |     REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(8)); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("stores Serialized<const char*> by reference") { |   SECTION("stores Serialized<const char*> by reference") { | ||||||
|     var1.set(serialized("hello!!", 8)); |     var1.set(serialized("hello!!", JSON_STRING_SIZE(8))); | ||||||
|     var2.set(var1); |     var2.set(var1); | ||||||
|  |  | ||||||
|     REQUIRE(doc1.memoryUsage() == 0); |     REQUIRE(doc1.memoryUsage() == 0); | ||||||
| @@ -70,15 +72,15 @@ TEST_CASE("JsonVariant::set(JsonVariant)") { | |||||||
|     var1.set(serialized(str, 8)); |     var1.set(serialized(str, 8)); | ||||||
|     var2.set(var1); |     var2.set(var1); | ||||||
|  |  | ||||||
|     REQUIRE(doc1.memoryUsage() == 8); |     REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(8)); | ||||||
|     REQUIRE(doc2.memoryUsage() == 8); |     REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(8)); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("stores Serialized<std::string> by copy") { |   SECTION("stores Serialized<std::string> by copy") { | ||||||
|     var1.set(serialized(std::string("hello!!!"))); |     var1.set(serialized(std::string("hello!!!"))); | ||||||
|     var2.set(var1); |     var2.set(var1); | ||||||
|  |  | ||||||
|     REQUIRE(doc1.memoryUsage() == 8); |     REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(8)); | ||||||
|     REQUIRE(doc2.memoryUsage() == 8); |     REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(8)); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -5,17 +5,21 @@ | |||||||
| #include <ArduinoJson.h> | #include <ArduinoJson.h> | ||||||
| #include <catch.hpp> | #include <catch.hpp> | ||||||
|  |  | ||||||
| static const size_t epsilon = sizeof(void*); |  | ||||||
|  |  | ||||||
| template <size_t Capacity> | template <size_t Capacity> | ||||||
| static void check(const char* input, DeserializationError expected) { | static void check(const char* input, DeserializationError expected) { | ||||||
|   StaticJsonDocument<Capacity> variant; |   StaticJsonDocument<Capacity> variant; | ||||||
|  |  | ||||||
|   DeserializationError error = deserializeMsgPack(variant, input); |   DeserializationError error = deserializeMsgPack(variant, input); | ||||||
|  |  | ||||||
|  |   CAPTURE(input); | ||||||
|   REQUIRE(error == expected); |   REQUIRE(error == expected); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | template <size_t Size> | ||||||
|  | static void checkString(const char* input, DeserializationError expected) { | ||||||
|  |   check<JSON_STRING_SIZE(Size)>(input, expected); | ||||||
|  | } | ||||||
|  |  | ||||||
| TEST_CASE("deserializeMsgPack(StaticJsonDocument&)") { | TEST_CASE("deserializeMsgPack(StaticJsonDocument&)") { | ||||||
|   SECTION("single values always fit") { |   SECTION("single values always fit") { | ||||||
|     check<0>("\xc0", DeserializationError::Ok);                  // nil |     check<0>("\xc0", DeserializationError::Ok);                  // nil | ||||||
| @@ -27,31 +31,39 @@ TEST_CASE("deserializeMsgPack(StaticJsonDocument&)") { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("fixstr") { |   SECTION("fixstr") { | ||||||
|     check<0>("\xA0", DeserializationError::Ok); |     checkString<8>("\xA0", DeserializationError::Ok); | ||||||
|     check<0>("\xA1H", DeserializationError::NoMemory); |     checkString<8>("\xA7ZZZZZZZ", DeserializationError::Ok); | ||||||
|     check<4>("\xA1H", DeserializationError::Ok); |     checkString<8>("\xA8ZZZZZZZZ", DeserializationError::NoMemory); | ||||||
|     check<4>("\xA5Hello", DeserializationError::NoMemory); |     checkString<16>("\xAFZZZZZZZZZZZZZZZ", DeserializationError::Ok); | ||||||
|  |     checkString<16>("\xB0ZZZZZZZZZZZZZZZZ", DeserializationError::NoMemory); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("str 8") { |   SECTION("str 8") { | ||||||
|     check<0>("\xD9\x00", DeserializationError::Ok); |     checkString<8>("\xD9\x00", DeserializationError::Ok); | ||||||
|     check<0>("\xD9\x01H", DeserializationError::NoMemory); |     checkString<8>("\xD9\x07ZZZZZZZ", DeserializationError::Ok); | ||||||
|     check<4>("\xD9\x01H", DeserializationError::Ok); |     checkString<8>("\xD9\x08ZZZZZZZZ", DeserializationError::NoMemory); | ||||||
|     check<4>("\xD9\x05Hello", DeserializationError::NoMemory); |     checkString<16>("\xD9\x0FZZZZZZZZZZZZZZZ", DeserializationError::Ok); | ||||||
|  |     checkString<16>("\xD9\x10ZZZZZZZZZZZZZZZZ", DeserializationError::NoMemory); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("str 16") { |   SECTION("str 16") { | ||||||
|     check<0>("\xDA\x00\x00", DeserializationError::Ok); |     checkString<8>("\xDA\x00\x00", DeserializationError::Ok); | ||||||
|     check<0>("\xDA\x00\x01H", DeserializationError::NoMemory); |     checkString<8>("\xDA\x00\x07ZZZZZZZ", DeserializationError::Ok); | ||||||
|     check<4>("\xDA\x00\x01H", DeserializationError::Ok); |     checkString<8>("\xDA\x00\x08ZZZZZZZZ", DeserializationError::NoMemory); | ||||||
|     check<4>("\xDA\x00\x05Hello", DeserializationError::NoMemory); |     checkString<16>("\xDA\x00\x0FZZZZZZZZZZZZZZZ", DeserializationError::Ok); | ||||||
|  |     checkString<16>("\xDA\x00\x10ZZZZZZZZZZZZZZZZ", | ||||||
|  |                     DeserializationError::NoMemory); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("str 32") { |   SECTION("str 32") { | ||||||
|     check<0>("\xDB\x00\x00\x00\x00", DeserializationError::Ok); |     checkString<8>("\xDB\x00\x00\x00\x00", DeserializationError::Ok); | ||||||
|     check<0>("\xDB\x00\x00\x00\x01H", DeserializationError::NoMemory); |     checkString<8>("\xDB\x00\x00\x00\x07ZZZZZZZ", DeserializationError::Ok); | ||||||
|     check<4>("\xDB\x00\x00\x00\x01H", DeserializationError::Ok); |     checkString<8>("\xDB\x00\x00\x00\x08ZZZZZZZZ", | ||||||
|     check<4>("\xDB\x00\x00\x00\x05Hello", DeserializationError::NoMemory); |                    DeserializationError::NoMemory); | ||||||
|  |     checkString<16>("\xDB\x00\x00\x00\x0FZZZZZZZZZZZZZZZ", | ||||||
|  |                     DeserializationError::Ok); | ||||||
|  |     checkString<16>("\xDB\x00\x00\x00\x10ZZZZZZZZZZZZZZZZ", | ||||||
|  |                     DeserializationError::NoMemory); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("fixarray") { |   SECTION("fixarray") { | ||||||
| @@ -89,14 +101,14 @@ TEST_CASE("deserializeMsgPack(StaticJsonDocument&)") { | |||||||
|     SECTION("{H:1}") { |     SECTION("{H:1}") { | ||||||
|       check<JSON_OBJECT_SIZE(0)>("\x81\xA1H\x01", |       check<JSON_OBJECT_SIZE(0)>("\x81\xA1H\x01", | ||||||
|                                  DeserializationError::NoMemory); |                                  DeserializationError::NoMemory); | ||||||
|       check<JSON_OBJECT_SIZE(1) + epsilon>("\x81\xA1H\x01", |       check<JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(2)>( | ||||||
|                                            DeserializationError::Ok); |           "\x81\xA1H\x01", DeserializationError::Ok); | ||||||
|     } |     } | ||||||
|     SECTION("{H:1,W:2}") { |     SECTION("{H:1,W:2}") { | ||||||
|       check<JSON_OBJECT_SIZE(1) + epsilon>("\x82\xA1H\x01\xA1W\x02", |       check<JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(2)>( | ||||||
|                                            DeserializationError::NoMemory); |           "\x82\xA1H\x01\xA1W\x02", DeserializationError::NoMemory); | ||||||
|       check<JSON_OBJECT_SIZE(2) + 2 * epsilon>("\x82\xA1H\x01\xA1W\x02", |       check<JSON_OBJECT_SIZE(2) + 2 * JSON_STRING_SIZE(2)>( | ||||||
|                                                DeserializationError::Ok); |           "\x82\xA1H\x01\xA1W\x02", DeserializationError::Ok); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -107,14 +119,14 @@ TEST_CASE("deserializeMsgPack(StaticJsonDocument&)") { | |||||||
|     SECTION("{H:1}") { |     SECTION("{H:1}") { | ||||||
|       check<JSON_OBJECT_SIZE(0)>("\xDE\x00\x01\xA1H\x01", |       check<JSON_OBJECT_SIZE(0)>("\xDE\x00\x01\xA1H\x01", | ||||||
|                                  DeserializationError::NoMemory); |                                  DeserializationError::NoMemory); | ||||||
|       check<JSON_OBJECT_SIZE(1) + epsilon>("\xDE\x00\x01\xA1H\x01", |       check<JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(2)>( | ||||||
|                                            DeserializationError::Ok); |           "\xDE\x00\x01\xA1H\x01", DeserializationError::Ok); | ||||||
|     } |     } | ||||||
|     SECTION("{H:1,W:2}") { |     SECTION("{H:1,W:2}") { | ||||||
|       check<JSON_OBJECT_SIZE(1) + epsilon>("\xDE\x00\x02\xA1H\x01\xA1W\x02", |       check<JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(2)>( | ||||||
|                                            DeserializationError::NoMemory); |           "\xDE\x00\x02\xA1H\x01\xA1W\x02", DeserializationError::NoMemory); | ||||||
|       check<JSON_OBJECT_SIZE(2) + 2 * epsilon>("\xDE\x00\x02\xA1H\x01\xA1W\x02", |       check<JSON_OBJECT_SIZE(2) + 2 * JSON_OBJECT_SIZE(1)>( | ||||||
|                                                DeserializationError::Ok); |           "\xDE\x00\x02\xA1H\x01\xA1W\x02", DeserializationError::Ok); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -126,14 +138,14 @@ TEST_CASE("deserializeMsgPack(StaticJsonDocument&)") { | |||||||
|     SECTION("{H:1}") { |     SECTION("{H:1}") { | ||||||
|       check<JSON_OBJECT_SIZE(0)>("\xDF\x00\x00\x00\x01\xA1H\x01", |       check<JSON_OBJECT_SIZE(0)>("\xDF\x00\x00\x00\x01\xA1H\x01", | ||||||
|                                  DeserializationError::NoMemory); |                                  DeserializationError::NoMemory); | ||||||
|       check<JSON_OBJECT_SIZE(1) + epsilon>("\xDF\x00\x00\x00\x01\xA1H\x01", |       check<JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(2)>( | ||||||
|                                            DeserializationError::Ok); |           "\xDF\x00\x00\x00\x01\xA1H\x01", DeserializationError::Ok); | ||||||
|     } |     } | ||||||
|     SECTION("{H:1,W:2}") { |     SECTION("{H:1,W:2}") { | ||||||
|       check<JSON_OBJECT_SIZE(1) + epsilon>( |       check<JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(2)>( | ||||||
|           "\xDF\x00\x00\x00\x02\xA1H\x01\xA1W\x02", |           "\xDF\x00\x00\x00\x02\xA1H\x01\xA1W\x02", | ||||||
|           DeserializationError::NoMemory); |           DeserializationError::NoMemory); | ||||||
|       check<JSON_OBJECT_SIZE(2) + 2 * epsilon>( |       check<JSON_OBJECT_SIZE(2) + 2 * JSON_OBJECT_SIZE(1)>( | ||||||
|           "\xDF\x00\x00\x00\x02\xA1H\x01\xA1W\x02", DeserializationError::Ok); |           "\xDF\x00\x00\x00\x02\xA1H\x01\xA1W\x02", DeserializationError::Ok); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -3,9 +3,11 @@ | |||||||
| # MIT License | # MIT License | ||||||
|  |  | ||||||
| add_executable(StaticMemoryPoolTests  | add_executable(StaticMemoryPoolTests  | ||||||
| 	alloc.cpp | 	allocVariant.cpp | ||||||
|  | 	allocString.cpp | ||||||
|  | 	clear.cpp | ||||||
| 	size.cpp | 	size.cpp | ||||||
| 	startString.cpp | 	StringBuilder.cpp | ||||||
| ) | ) | ||||||
|  |  | ||||||
| target_link_libraries(StaticMemoryPoolTests catch) | target_link_libraries(StaticMemoryPoolTests catch) | ||||||
|   | |||||||
							
								
								
									
										39
									
								
								test/StaticMemoryPool/StringBuilder.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								test/StaticMemoryPool/StringBuilder.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | |||||||
|  | // ArduinoJson - arduinojson.org | ||||||
|  | // Copyright Benoit Blanchon 2014-2018 | ||||||
|  | // MIT License | ||||||
|  |  | ||||||
|  | #include <ArduinoJson/Memory/StaticMemoryPool.hpp> | ||||||
|  | #include <ArduinoJson/Memory/StringBuilder.hpp> | ||||||
|  | #include <catch.hpp> | ||||||
|  |  | ||||||
|  | using namespace ARDUINOJSON_NAMESPACE; | ||||||
|  |  | ||||||
|  | TEST_CASE("StringBuilder") { | ||||||
|  |   SECTION("WorksWhenBufferIsBigEnough") { | ||||||
|  |     StaticMemoryPool<JSON_STRING_SIZE(6)> memoryPool; | ||||||
|  |  | ||||||
|  |     StringBuilder str(&memoryPool); | ||||||
|  |     str.append("hello"); | ||||||
|  |  | ||||||
|  |     REQUIRE(str.complete().equals("hello")); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("ReturnsNullWhenTooSmall") { | ||||||
|  |     StaticMemoryPool<1> memoryPool; | ||||||
|  |  | ||||||
|  |     StringBuilder str(&memoryPool); | ||||||
|  |     str.append("hello!!!"); | ||||||
|  |  | ||||||
|  |     REQUIRE(str.complete().isNull()); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("Increases size of memory pool") { | ||||||
|  |     StaticMemoryPool<JSON_STRING_SIZE(6)> memoryPool; | ||||||
|  |  | ||||||
|  |     StringBuilder str(&memoryPool); | ||||||
|  |     str.append('h'); | ||||||
|  |     str.complete(); | ||||||
|  |  | ||||||
|  |     REQUIRE(JSON_STRING_SIZE(2) == memoryPool.size()); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -1,54 +0,0 @@ | |||||||
| // ArduinoJson - arduinojson.org |  | ||||||
| // Copyright Benoit Blanchon 2014-2018 |  | ||||||
| // MIT License |  | ||||||
|  |  | ||||||
| #include <ArduinoJson/Memory/StaticMemoryPool.hpp> |  | ||||||
| #include <catch.hpp> |  | ||||||
|  |  | ||||||
| using namespace ARDUINOJSON_NAMESPACE; |  | ||||||
|  |  | ||||||
| static bool isAligned(void *ptr) { |  | ||||||
|   const size_t mask = sizeof(void *) - 1; |  | ||||||
|   size_t addr = reinterpret_cast<size_t>(ptr); |  | ||||||
|   return (addr & mask) == 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| TEST_CASE("StaticMemoryPool::alloc()") { |  | ||||||
|   StaticMemoryPool<64> memoryPool; |  | ||||||
|  |  | ||||||
|   SECTION("Returns different addresses") { |  | ||||||
|     void *p1 = memoryPool.alloc(1); |  | ||||||
|     void *p2 = memoryPool.alloc(1); |  | ||||||
|     REQUIRE(p1 != p2); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("Returns non-NULL when using full capacity") { |  | ||||||
|     void *p = memoryPool.alloc(64); |  | ||||||
|     REQUIRE(0 != p); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("Returns NULL when full") { |  | ||||||
|     memoryPool.alloc(64); |  | ||||||
|     void *p = memoryPool.alloc(1); |  | ||||||
|     REQUIRE(0 == p); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("Returns NULL when memoryPool is too small") { |  | ||||||
|     void *p = memoryPool.alloc(65); |  | ||||||
|     REQUIRE(0 == p); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("Returns aligned pointers") { |  | ||||||
|     for (size_t size = 1; size <= sizeof(void *); size++) { |  | ||||||
|       void *p = memoryPool.alloc(1); |  | ||||||
|       REQUIRE(isAligned(p)); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("Returns same address after clear()") { |  | ||||||
|     void *p1 = memoryPool.alloc(1); |  | ||||||
|     memoryPool.clear(); |  | ||||||
|     void *p2 = memoryPool.alloc(1); |  | ||||||
|     REQUIRE(p1 == p2); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
							
								
								
									
										131
									
								
								test/StaticMemoryPool/allocString.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								test/StaticMemoryPool/allocString.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,131 @@ | |||||||
|  | // ArduinoJson - arduinojson.org | ||||||
|  | // Copyright Benoit Blanchon 2014-2018 | ||||||
|  | // MIT License | ||||||
|  |  | ||||||
|  | #include <ArduinoJson/Memory/StaticMemoryPool.hpp> | ||||||
|  | #include <catch.hpp> | ||||||
|  |  | ||||||
|  | using namespace ARDUINOJSON_NAMESPACE; | ||||||
|  |  | ||||||
|  | TEST_CASE("StaticMemoryPool::allocFrozenString()") { | ||||||
|  |   const size_t poolCapacity = 64; | ||||||
|  |   const size_t longestString = poolCapacity - sizeof(StringSlot); | ||||||
|  |   StaticMemoryPool<poolCapacity> pool; | ||||||
|  |  | ||||||
|  |   SECTION("Returns different addresses") { | ||||||
|  |     StringSlot *a = pool.allocFrozenString(1); | ||||||
|  |     StringSlot *b = pool.allocFrozenString(1); | ||||||
|  |     REQUIRE(a != b); | ||||||
|  |     REQUIRE(a->value != b->value); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("Returns a StringSlot of the right size") { | ||||||
|  |     StringSlot *s = pool.allocFrozenString(12); | ||||||
|  |     REQUIRE(s->size == 12); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("Returns NULL when full") { | ||||||
|  |     pool.allocFrozenString(longestString); | ||||||
|  |     void *p = pool.allocFrozenString(1); | ||||||
|  |     REQUIRE(0 == p); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("Returns NULL when pool is too small") { | ||||||
|  |     void *p = pool.allocFrozenString(longestString + 1); | ||||||
|  |     REQUIRE(0 == p); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("Returns aligned pointers") { | ||||||
|  |     REQUIRE(isAligned(pool.allocFrozenString(1))); | ||||||
|  |     REQUIRE(isAligned(pool.allocFrozenString(1))); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("Returns same address after clear()") { | ||||||
|  |     StringSlot *a = pool.allocFrozenString(1); | ||||||
|  |     pool.clear(); | ||||||
|  |     StringSlot *b = pool.allocFrozenString(1); | ||||||
|  |  | ||||||
|  |     REQUIRE(a == b); | ||||||
|  |     REQUIRE(a->value == b->value); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("Returns same address after freeString()") { | ||||||
|  |     StringSlot *a = pool.allocFrozenString(1); | ||||||
|  |     pool.freeString(a); | ||||||
|  |     StringSlot *b = pool.allocFrozenString(1); | ||||||
|  |  | ||||||
|  |     REQUIRE(a == b); | ||||||
|  |     REQUIRE(a->value == b->value); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("Can use full capacity when fresh") { | ||||||
|  |     StringSlot *a = pool.allocFrozenString(longestString); | ||||||
|  |  | ||||||
|  |     REQUIRE(a != 0); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("Can use full capacity after clear") { | ||||||
|  |     pool.allocFrozenString(longestString); | ||||||
|  |     pool.clear(); | ||||||
|  |  | ||||||
|  |     StringSlot *a = pool.allocFrozenString(longestString); | ||||||
|  |  | ||||||
|  |     REQUIRE(a != 0); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | TEST_CASE("StaticMemoryPool::freeString()") { | ||||||
|  |   const size_t poolCapacity = 512; | ||||||
|  |   const size_t longestString = poolCapacity - sizeof(StringSlot); | ||||||
|  |   StaticMemoryPool<poolCapacity> pool; | ||||||
|  |  | ||||||
|  |   static const size_t testStringSize = | ||||||
|  |       (poolCapacity - sizeof(StringSlot) * 4 - sizeof(VariantSlot) * 4) / 4; | ||||||
|  |  | ||||||
|  |   SECTION("Restores full capacity") { | ||||||
|  |     StringSlot *strings[4]; | ||||||
|  |     VariantSlot *variants[4]; | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < 4; i++) { | ||||||
|  |       strings[i] = pool.allocFrozenString(testStringSize); | ||||||
|  |       REQUIRE(strings[i] != 0); | ||||||
|  |       variants[i] = pool.allocVariant(); | ||||||
|  |       REQUIRE(variants[i] != 0); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // In random order | ||||||
|  |     pool.freeString(strings[2]); | ||||||
|  |     pool.freeVariant(variants[3]); | ||||||
|  |     pool.freeVariant(variants[0]); | ||||||
|  |     pool.freeString(strings[0]); | ||||||
|  |     pool.freeVariant(variants[1]); | ||||||
|  |     pool.freeString(strings[1]); | ||||||
|  |     pool.freeVariant(variants[2]); | ||||||
|  |     pool.freeString(strings[3]); | ||||||
|  |  | ||||||
|  |     StringSlot *b = pool.allocFrozenString(longestString); | ||||||
|  |  | ||||||
|  |     REQUIRE(b != 0); | ||||||
|  |     REQUIRE(b->size == longestString); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("Move strings") { | ||||||
|  |     StringSlot *a = pool.allocFrozenString(6); | ||||||
|  |     strcpy(a->value, "hello"); | ||||||
|  |  | ||||||
|  |     StringSlot *b = pool.allocFrozenString(7); | ||||||
|  |     strcpy(b->value, "world!"); | ||||||
|  |     pool.freeString(a); | ||||||
|  |  | ||||||
|  |     REQUIRE(b->size == 7); | ||||||
|  |     REQUIRE(b->value == std::string("world!")); | ||||||
|  |     REQUIRE(a->value == b->value); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("Accepts non-frozen string") { | ||||||
|  |     StringSlot *a = pool.allocExpandableString(); | ||||||
|  |     pool.freeString(a); | ||||||
|  |  | ||||||
|  |     REQUIRE(pool.size() == 0); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										38
									
								
								test/StaticMemoryPool/allocVariant.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								test/StaticMemoryPool/allocVariant.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | // ArduinoJson - arduinojson.org | ||||||
|  | // Copyright Benoit Blanchon 2014-2018 | ||||||
|  | // MIT License | ||||||
|  |  | ||||||
|  | #include <ArduinoJson/Memory/StaticMemoryPool.hpp> | ||||||
|  | #include <catch.hpp> | ||||||
|  |  | ||||||
|  | using namespace ARDUINOJSON_NAMESPACE; | ||||||
|  |  | ||||||
|  | TEST_CASE("StaticMemoryPool::allocVariant()") { | ||||||
|  |   StaticMemoryPool<128> memoryPool; | ||||||
|  |  | ||||||
|  |   SECTION("Returns different pointer") { | ||||||
|  |     VariantSlot* s1 = memoryPool.allocVariant(); | ||||||
|  |     REQUIRE(s1 != 0); | ||||||
|  |     VariantSlot* s2 = memoryPool.allocVariant(); | ||||||
|  |     REQUIRE(s2 != 0); | ||||||
|  |  | ||||||
|  |     REQUIRE(s1 != s2); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("Returns same pointer after freeSlot()") { | ||||||
|  |     VariantSlot* s1 = memoryPool.allocVariant(); | ||||||
|  |     memoryPool.freeVariant(s1); | ||||||
|  |     VariantSlot* s2 = memoryPool.allocVariant(); | ||||||
|  |  | ||||||
|  |     REQUIRE(s1 == s2); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("Returns aligned pointers") { | ||||||
|  |     // make room for two | ||||||
|  |     // pass an uneven capacity | ||||||
|  |     StaticMemoryPool<2 * sizeof(VariantSlot) + 1> pool; | ||||||
|  |  | ||||||
|  |     REQUIRE(isAligned(pool.allocVariant())); | ||||||
|  |     REQUIRE(isAligned(pool.allocVariant())); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										81
									
								
								test/StaticMemoryPool/clear.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								test/StaticMemoryPool/clear.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | |||||||
|  | // ArduinoJson - arduinojson.org | ||||||
|  | // Copyright Benoit Blanchon 2014-2018 | ||||||
|  | // MIT License | ||||||
|  |  | ||||||
|  | #include <ArduinoJson/Memory/StaticMemoryPool.hpp> | ||||||
|  | #include <catch.hpp> | ||||||
|  |  | ||||||
|  | using namespace ARDUINOJSON_NAMESPACE; | ||||||
|  |  | ||||||
|  | static const size_t poolCapacity = 512; | ||||||
|  |  | ||||||
|  | TEST_CASE("StaticMemoryPool::clear()") { | ||||||
|  |   StaticMemoryPool<poolCapacity> memoryPool; | ||||||
|  |  | ||||||
|  |   SECTION("Discards allocated variants") { | ||||||
|  |     memoryPool.allocVariant(); | ||||||
|  |  | ||||||
|  |     memoryPool.clear(); | ||||||
|  |     REQUIRE(memoryPool.size() == 0); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("Discards allocated strings") { | ||||||
|  |     memoryPool.allocFrozenString(10); | ||||||
|  |  | ||||||
|  |     memoryPool.clear(); | ||||||
|  |  | ||||||
|  |     REQUIRE(memoryPool.size() == 0); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("Purges variant cache") { | ||||||
|  |     VariantSlot* a = memoryPool.allocVariant(); | ||||||
|  |     REQUIRE(a != 0); | ||||||
|  |     VariantSlot* b = memoryPool.allocVariant(); | ||||||
|  |     REQUIRE(b != 0); | ||||||
|  |  | ||||||
|  |     // place slot a in the pool of free slots | ||||||
|  |     memoryPool.freeVariant(a); | ||||||
|  |     memoryPool.clear(); | ||||||
|  |  | ||||||
|  |     REQUIRE(memoryPool.size() == 0); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("Purges string cache") { | ||||||
|  |     StringSlot* a = memoryPool.allocFrozenString(10); | ||||||
|  |     REQUIRE(a != 0); | ||||||
|  |     StringSlot* b = memoryPool.allocFrozenString(10); | ||||||
|  |     REQUIRE(b != 0); | ||||||
|  |  | ||||||
|  |     // place slot a in the pool of free slots | ||||||
|  |     memoryPool.freeString(a); | ||||||
|  |     memoryPool.clear(); | ||||||
|  |  | ||||||
|  |     REQUIRE(memoryPool.size() == 0); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("Purges list of string") { | ||||||
|  |     StringSlot* a = memoryPool.allocFrozenString(6); | ||||||
|  |     REQUIRE(a != 0); | ||||||
|  |     strcpy(a->value, "hello"); | ||||||
|  |  | ||||||
|  |     StringSlot* b = memoryPool.allocFrozenString(6); | ||||||
|  |     REQUIRE(b != 0); | ||||||
|  |     strcpy(b->value, "world"); | ||||||
|  |  | ||||||
|  |     memoryPool.clear();  // ACT! | ||||||
|  |  | ||||||
|  |     StringSlot* c = memoryPool.allocFrozenString(2); | ||||||
|  |     REQUIRE(c != 0); | ||||||
|  |     strcpy(c->value, "H"); | ||||||
|  |  | ||||||
|  |     StringSlot* d = memoryPool.allocFrozenString(2); | ||||||
|  |     REQUIRE(d != 0); | ||||||
|  |     strcpy(d->value, "W"); | ||||||
|  |  | ||||||
|  |     // if the memory pool keeps pointer to the old strings | ||||||
|  |     // it will try to compact the strings | ||||||
|  |     memoryPool.freeString(c); | ||||||
|  |  | ||||||
|  |     REQUIRE(d->value == std::string("W")); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -8,37 +8,72 @@ | |||||||
| using namespace ARDUINOJSON_NAMESPACE; | using namespace ARDUINOJSON_NAMESPACE; | ||||||
|  |  | ||||||
| TEST_CASE("StaticMemoryPool::size()") { | TEST_CASE("StaticMemoryPool::size()") { | ||||||
|   StaticMemoryPool<64> memoryPool; |  | ||||||
|  |  | ||||||
|   SECTION("Capacity equals template parameter") { |   SECTION("Capacity equals template parameter") { | ||||||
|     REQUIRE(64 == memoryPool.capacity()); |     const size_t capacity = 64; | ||||||
|  |     StaticMemoryPool<capacity> memoryPool; | ||||||
|  |     REQUIRE(capacity == memoryPool.capacity()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("Initial size is 0") { |   SECTION("Initial size is 0") { | ||||||
|  |     StaticMemoryPool<32> memoryPool; | ||||||
|     REQUIRE(0 == memoryPool.size()); |     REQUIRE(0 == memoryPool.size()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("Increases after alloc()") { |   SECTION("Increases after allocFrozenString()") { | ||||||
|     memoryPool.alloc(1); |     StaticMemoryPool<128> memoryPool; | ||||||
|     REQUIRE(1U <= memoryPool.size()); |     memoryPool.allocFrozenString(0); | ||||||
|     memoryPool.alloc(1); |     REQUIRE(memoryPool.size() == JSON_STRING_SIZE(0)); | ||||||
|     REQUIRE(2U <= memoryPool.size()); |     memoryPool.allocFrozenString(0); | ||||||
|  |     REQUIRE(memoryPool.size() == 2 * JSON_STRING_SIZE(0)); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("Doesn't grow when memoryPool is full") { |   SECTION("Decreases after freeVariant()") { | ||||||
|     memoryPool.alloc(64); |     StaticMemoryPool<128> memoryPool; | ||||||
|     memoryPool.alloc(1); |     VariantSlot* a = memoryPool.allocVariant(); | ||||||
|     REQUIRE(64 == memoryPool.size()); |     VariantSlot* b = memoryPool.allocVariant(); | ||||||
|  |  | ||||||
|  |     memoryPool.freeVariant(b); | ||||||
|  |     REQUIRE(memoryPool.size() == sizeof(VariantSlot)); | ||||||
|  |     memoryPool.freeVariant(a); | ||||||
|  |     REQUIRE(memoryPool.size() == 0); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("Does't grow when memoryPool is too small for alloc") { |   SECTION("Decreases after calling freeString() in order") { | ||||||
|     memoryPool.alloc(65); |     StaticMemoryPool<128> memoryPool; | ||||||
|     REQUIRE(0 == memoryPool.size()); |     StringSlot* a = memoryPool.allocFrozenString(5); | ||||||
|  |     REQUIRE(a != 0); | ||||||
|  |     StringSlot* b = memoryPool.allocFrozenString(6); | ||||||
|  |     REQUIRE(b != 0); | ||||||
|  |  | ||||||
|  |     memoryPool.freeString(b); | ||||||
|  |     REQUIRE(memoryPool.size() == JSON_STRING_SIZE(5)); | ||||||
|  |     memoryPool.freeString(a); | ||||||
|  |     REQUIRE(memoryPool.size() == 0); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   SECTION("Goes back to zero after clear()") { |   SECTION("Decreases after calling freeString() in reverse order") { | ||||||
|     memoryPool.alloc(1); |     StaticMemoryPool<128> memoryPool; | ||||||
|     memoryPool.clear(); |     StringSlot* a = memoryPool.allocFrozenString(5); | ||||||
|     REQUIRE(0 == memoryPool.size()); |     REQUIRE(a != 0); | ||||||
|  |     StringSlot* b = memoryPool.allocFrozenString(6); | ||||||
|  |     REQUIRE(b != 0); | ||||||
|  |  | ||||||
|  |     memoryPool.freeString(a); | ||||||
|  |     REQUIRE(memoryPool.size() == JSON_STRING_SIZE(6)); | ||||||
|  |     memoryPool.freeString(b); | ||||||
|  |     REQUIRE(memoryPool.size() == 0); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SECTION("Doesn't grow when memory pool is full") { | ||||||
|  |     const size_t variantCount = 4; | ||||||
|  |     const size_t capacity = variantCount * sizeof(VariantSlot); | ||||||
|  |     StaticMemoryPool<capacity> memoryPool; | ||||||
|  |  | ||||||
|  |     for (size_t i = 0; i < variantCount; i++) memoryPool.allocVariant(); | ||||||
|  |     REQUIRE(capacity == memoryPool.size()); | ||||||
|  |  | ||||||
|  |     memoryPool.allocVariant(); | ||||||
|  |  | ||||||
|  |     REQUIRE(capacity == memoryPool.size()); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,46 +0,0 @@ | |||||||
| // ArduinoJson - arduinojson.org |  | ||||||
| // Copyright Benoit Blanchon 2014-2018 |  | ||||||
| // MIT License |  | ||||||
|  |  | ||||||
| #include <ArduinoJson.h> |  | ||||||
| #include <catch.hpp> |  | ||||||
|  |  | ||||||
| using namespace ARDUINOJSON_NAMESPACE; |  | ||||||
|  |  | ||||||
| TEST_CASE("StaticMemoryPool::startString()") { |  | ||||||
|   SECTION("WorksWhenBufferIsBigEnough") { |  | ||||||
|     StaticMemoryPool<6> memoryPool; |  | ||||||
|  |  | ||||||
|     StringBuilder str = memoryPool.startString(); |  | ||||||
|     str.append('h'); |  | ||||||
|     str.append('e'); |  | ||||||
|     str.append('l'); |  | ||||||
|     str.append('l'); |  | ||||||
|     str.append('o'); |  | ||||||
|  |  | ||||||
|     REQUIRE(str.complete().equals("hello")); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("ReturnsNullWhenTooSmall") { |  | ||||||
|     StaticMemoryPool<5> memoryPool; |  | ||||||
|  |  | ||||||
|     StringBuilder str = memoryPool.startString(); |  | ||||||
|     str.append('h'); |  | ||||||
|     str.append('e'); |  | ||||||
|     str.append('l'); |  | ||||||
|     str.append('l'); |  | ||||||
|     str.append('o'); |  | ||||||
|  |  | ||||||
|     REQUIRE(str.complete().isNull()); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SECTION("SizeIncreases") { |  | ||||||
|     StaticMemoryPool<5> memoryPool; |  | ||||||
|  |  | ||||||
|     StringBuilder str = memoryPool.startString(); |  | ||||||
|     str.append('h'); |  | ||||||
|     str.complete(); |  | ||||||
|  |  | ||||||
|     REQUIRE(2 == memoryPool.size()); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
		Reference in New Issue
	
	Block a user