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)` | ||||
| * Replaced `T JsonArray::get<T>(i)` with `JsonVariant JsonArray::get(i)` | ||||
| * 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) | ||||
| ----------- | ||||
|   | ||||
| @@ -11,6 +11,8 @@ if(${COVERAGE}) | ||||
| 	set(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage") | ||||
| endif() | ||||
|  | ||||
| add_definitions(-DARDUINOJSON_DEBUG) | ||||
|  | ||||
| include_directories(${CMAKE_CURRENT_LIST_DIR}/src) | ||||
| add_subdirectory(third-party/catch) | ||||
| add_subdirectory(test) | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| # CAUTION: this file is invoked by https://github.com/google/oss-fuzz | ||||
|  | ||||
| CXXFLAGS += -I../src | ||||
| CXXFLAGS += -I../src -DARDUINOJSON_DEBUG | ||||
|  | ||||
| all: \ | ||||
| 	$(OUT)/json_fuzzer \ | ||||
|   | ||||
| @@ -5,17 +5,34 @@ | ||||
| // This file is NOT use by Google's OSS fuzz | ||||
| // I only use it to reproduce the bugs found | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include <fstream> | ||||
| #include <stdint.h>  // size_t | ||||
| #include <stdio.h>   // fopen et al. | ||||
| #include <stdlib.h>  // exit | ||||
| #include <iostream> | ||||
| #include <string> | ||||
| #include <vector> | ||||
|  | ||||
| extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size); | ||||
|  | ||||
| std::string read(const char* path) { | ||||
|   std::ifstream file(path); | ||||
|   return std::string(std::istreambuf_iterator<char>(file), | ||||
|                      std::istreambuf_iterator<char>()); | ||||
| std::vector<uint8_t> read(const char* path) { | ||||
|   FILE* f = fopen(path, "rb"); | ||||
|   if (!f) { | ||||
|     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[]) { | ||||
| @@ -26,9 +43,8 @@ int main(int argc, const char* argv[]) { | ||||
|  | ||||
|   for (int i = 1; i < argc; i++) { | ||||
|     std::cout << "Loading " << argv[i] << std::endl; | ||||
|     std::string buffer = read(argv[i]); | ||||
|     LLVMFuzzerTestOneInput(reinterpret_cast<const uint8_t*>(buffer.data()), | ||||
|                            buffer.size()); | ||||
|     std::vector<uint8_t> buffer = read(argv[i]); | ||||
|     LLVMFuzzerTestOneInput(buffer.data(), buffer.size()); | ||||
|   } | ||||
|   return 0; | ||||
| } | ||||
|   | ||||
| @@ -130,16 +130,6 @@ | ||||
| #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 | ||||
| // CAUTION: cannot be more that 1e9 !!!! | ||||
| #ifndef ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD | ||||
|   | ||||
| @@ -5,7 +5,6 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "JsonVariantData.hpp" | ||||
| #include "Slot.hpp" | ||||
| #include "SlotFunctions.hpp" | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
| @@ -13,7 +12,7 @@ namespace ARDUINOJSON_NAMESPACE { | ||||
| inline JsonVariantData* arrayAdd(JsonArrayData* arr, MemoryPool* pool) { | ||||
|   if (!arr) return 0; | ||||
|  | ||||
|   Slot* slot = pool->allocSlot(); | ||||
|   VariantSlot* slot = pool->allocVariant(); | ||||
|   if (!slot) return 0; | ||||
|  | ||||
|   slot->next = 0; | ||||
| @@ -29,20 +28,22 @@ inline JsonVariantData* arrayAdd(JsonArrayData* arr, MemoryPool* pool) { | ||||
|     arr->tail = slot; | ||||
|   } | ||||
|  | ||||
|   slot->value.keyIsOwned = false; | ||||
|   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; | ||||
|   return slotAdvance(arr->head, 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; | ||||
| } | ||||
|  | ||||
| inline void arrayRemove(JsonArrayData* arr, Slot* slot, MemoryPool* pool) { | ||||
| inline void arrayRemove(JsonArrayData* arr, VariantSlot* slot, | ||||
|                         MemoryPool* pool) { | ||||
|   if (!arr || !slot) return; | ||||
|  | ||||
|   if (slot->prev) | ||||
| @@ -73,7 +74,7 @@ inline bool arrayCopy(JsonArrayData* dst, const JsonArrayData* src, | ||||
|                       MemoryPool* pool) { | ||||
|   if (!dst || !src) return false; | ||||
|   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; | ||||
|   } | ||||
|   return true; | ||||
| @@ -84,8 +85,8 @@ bool variantEquals(const JsonVariantData*, const JsonVariantData*); | ||||
| inline bool arrayEquals(const JsonArrayData* a1, const JsonArrayData* a2) { | ||||
|   if (a1 == a2) return true; | ||||
|   if (!a1 || !a2) return false; | ||||
|   Slot* s1 = a1->head; | ||||
|   Slot* s2 = a2->head; | ||||
|   VariantSlot* s1 = a1->head; | ||||
|   VariantSlot* s2 = a2->head; | ||||
|   for (;;) { | ||||
|     if (s1 == s2) return true; | ||||
|     if (!s1 || !s2) return false; | ||||
|   | ||||
| @@ -26,13 +26,13 @@ enum JsonVariantType { | ||||
| }; | ||||
|  | ||||
| struct JsonObjectData { | ||||
|   struct Slot *head; | ||||
|   struct Slot *tail; | ||||
|   struct VariantSlot *head; | ||||
|   struct VariantSlot *tail; | ||||
| }; | ||||
|  | ||||
| struct JsonArrayData { | ||||
|   struct Slot *head; | ||||
|   struct Slot *tail; | ||||
|   struct VariantSlot *head; | ||||
|   struct VariantSlot *tail; | ||||
| }; | ||||
|  | ||||
| struct RawData { | ||||
| @@ -48,6 +48,8 @@ union JsonVariantContent { | ||||
|   JsonArrayData asArray; | ||||
|   JsonObjectData asObject; | ||||
|   const char *asString; | ||||
|   struct StringSlot *asOwnedString; | ||||
|   struct StringSlot *asOwnedRaw; | ||||
|   struct { | ||||
|     const char *data; | ||||
|     size_t size; | ||||
| @@ -56,7 +58,7 @@ union JsonVariantContent { | ||||
|  | ||||
| // this struct must be a POD type to prevent error calling offsetof on clang | ||||
| struct JsonVariantData { | ||||
|   bool keyIsStatic : 1; | ||||
|   bool keyIsOwned : 1; | ||||
|   JsonVariantType type : 7; | ||||
|   JsonVariantContent content; | ||||
| }; | ||||
|   | ||||
| @@ -11,11 +11,11 @@ | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| template <typename TKey> | ||||
| inline Slot* objectFindSlot(const JsonObjectData* obj, TKey key) { | ||||
| inline VariantSlot* objectFindSlot(const JsonObjectData* obj, TKey key) { | ||||
|   if (!obj) return 0; | ||||
|   Slot* slot = obj->head; | ||||
|   VariantSlot* slot = obj->head; | ||||
|   while (slot) { | ||||
|     if (key.equals(slot->key)) break; | ||||
|     if (key.equals(slotGetKey(slot))) break; | ||||
|     slot = slot->next; | ||||
|   } | ||||
|   return slot; | ||||
| @@ -29,7 +29,7 @@ inline bool objectContainsKey(const JsonObjectData* obj, const TKey& key) { | ||||
| template <typename TKey> | ||||
| inline JsonVariantData* objectAdd(JsonObjectData* obj, TKey key, | ||||
|                                   MemoryPool* pool) { | ||||
|   Slot* slot = pool->allocSlot(); | ||||
|   VariantSlot* slot = pool->allocVariant(); | ||||
|   if (!slot) return 0; | ||||
|  | ||||
|   slot->next = 0; | ||||
| @@ -58,7 +58,7 @@ inline JsonVariantData* objectSet(JsonObjectData* obj, TKey key, | ||||
|   if (key.isNull()) return 0; | ||||
|  | ||||
|   // search a matching key | ||||
|   Slot* slot = objectFindSlot(obj, key); | ||||
|   VariantSlot* slot = objectFindSlot(obj, key); | ||||
|   if (slot) return &slot->value; | ||||
|  | ||||
|   return objectAdd(obj, key, pool); | ||||
| @@ -66,7 +66,7 @@ inline JsonVariantData* objectSet(JsonObjectData* obj, TKey key, | ||||
|  | ||||
| template <typename TKey> | ||||
| inline JsonVariantData* objectGet(const JsonObjectData* obj, TKey key) { | ||||
|   Slot* slot = objectFindSlot(obj, key); | ||||
|   VariantSlot* slot = objectFindSlot(obj, key); | ||||
|   return slot ? &slot->value : 0; | ||||
| } | ||||
|  | ||||
| @@ -76,7 +76,8 @@ inline void objectClear(JsonObjectData* obj) { | ||||
|   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 (!slot) return; | ||||
|   if (slot->prev) | ||||
| @@ -102,12 +103,12 @@ inline bool objectCopy(JsonObjectData* dst, const JsonObjectData* src, | ||||
|                        MemoryPool* pool) { | ||||
|   if (!dst || !src) return false; | ||||
|   objectClear(dst); | ||||
|   for (Slot* s = src->head; s; s = s->next) { | ||||
|   for (VariantSlot* s = src->head; s; s = s->next) { | ||||
|     JsonVariantData* var; | ||||
|     if (s->value.keyIsStatic) | ||||
|       var = objectAdd(dst, ZeroTerminatedRamStringConst(s->key), pool); | ||||
|     if (s->value.keyIsOwned) | ||||
|       var = objectAdd(dst, ZeroTerminatedRamString(s->ownedKey->value), pool); | ||||
|     else | ||||
|       var = objectAdd(dst, ZeroTerminatedRamString(s->key), pool); | ||||
|       var = objectAdd(dst, ZeroTerminatedRamStringConst(s->linkedKey), pool); | ||||
|     if (!variantCopy(var, &s->value, pool)) return false; | ||||
|   } | ||||
|   return true; | ||||
| @@ -117,9 +118,9 @@ inline bool objectEquals(const JsonObjectData* o1, const JsonObjectData* o2) { | ||||
|   if (o1 == o2) return true; | ||||
|   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* v2 = objectGet(o2, makeString(s->key)); | ||||
|     JsonVariantData* v2 = objectGet(o2, makeString(slotGetKey(s))); | ||||
|     if (!variantEquals(v1, v2)) return false; | ||||
|   } | ||||
|   return true; | ||||
|   | ||||
| @@ -7,65 +7,80 @@ | ||||
| #include "../Memory/MemoryPool.hpp" | ||||
| #include "../Strings/StringTypes.hpp" | ||||
| #include "JsonVariantData.hpp" | ||||
| #include "Slot.hpp" | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| template <typename TKey> | ||||
| inline bool slotSetKey(Slot* slot, TKey key, MemoryPool* pool) { | ||||
|   const char* dup = key.save(pool); | ||||
|   if (!dup) return false; | ||||
|   slot->key = dup; | ||||
|   slot->value.keyIsStatic = false; | ||||
| inline bool slotSetKey(VariantSlot* var, TKey key, MemoryPool* pool) { | ||||
|   StringSlot* slot = key.save(pool); | ||||
|   if (!slot) return false; | ||||
|   var->ownedKey = slot; | ||||
|   var->value.keyIsOwned = true; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| inline bool slotSetKey(Slot* slot, ZeroTerminatedRamStringConst key, | ||||
|                        MemoryPool* pool) { | ||||
|   slot->key = key.save(pool); | ||||
|   slot->value.keyIsStatic = true; | ||||
| inline bool slotSetKey(VariantSlot* var, ZeroTerminatedRamStringConst key, | ||||
|                        MemoryPool*) { | ||||
|   var->linkedKey = key.c_str(); | ||||
|   var->value.keyIsOwned = false; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| inline bool slotSetKey(Slot* slot, StringInMemoryPool key, MemoryPool* pool) { | ||||
|   slot->key = key.save(pool); | ||||
|   slot->value.keyIsStatic = false; | ||||
| inline bool slotSetKey(VariantSlot* var, StringInMemoryPool key, MemoryPool*) { | ||||
|   var->ownedKey = key.slot(); | ||||
|   var->value.keyIsOwned = true; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| inline const Slot* slotAdvance(const Slot* slot, size_t distance) { | ||||
|   while (distance && slot) { | ||||
|     slot = slot->next; | ||||
| inline const char* slotGetKey(const VariantSlot* var) { | ||||
|   return var->value.keyIsOwned ? var->ownedKey->value : var->linkedKey; | ||||
| } | ||||
|  | ||||
| inline const VariantSlot* slotAdvance(const VariantSlot* var, size_t distance) { | ||||
|   while (distance && var) { | ||||
|     var = var->next; | ||||
|     distance--; | ||||
|   } | ||||
|   return slot; | ||||
|   return var; | ||||
| } | ||||
|  | ||||
| inline Slot* slotAdvance(Slot* slot, size_t distance) { | ||||
|   while (distance && slot) { | ||||
|     slot = slot->next; | ||||
| inline VariantSlot* slotAdvance(VariantSlot* var, size_t distance) { | ||||
|   while (distance && var) { | ||||
|     var = var->next; | ||||
|     distance--; | ||||
|   } | ||||
|   return slot; | ||||
|   return var; | ||||
| } | ||||
|  | ||||
| inline size_t slotSize(const Slot* slot) { | ||||
| inline size_t slotSize(const VariantSlot* var) { | ||||
|   size_t n = 0; | ||||
|   while (slot) { | ||||
|   while (var) { | ||||
|     n++; | ||||
|     slot = slot->next; | ||||
|     var = var->next; | ||||
|   } | ||||
|   return n; | ||||
| } | ||||
|  | ||||
| inline void slotFree(Slot* slot, MemoryPool* pool) { | ||||
|   const JsonVariantData& v = slot->value; | ||||
|   if (v.type == JSON_ARRAY || v.type == JSON_OBJECT) { | ||||
|     for (Slot* s = v.content.asObject.head; s; s = s->next) { | ||||
|       slotFree(s, pool); | ||||
|     } | ||||
| inline void slotFree(VariantSlot* var, MemoryPool* pool) { | ||||
|   const JsonVariantData& v = var->value; | ||||
|  | ||||
|   switch (v.type) { | ||||
|     case JSON_ARRAY: | ||||
|     case JSON_OBJECT: | ||||
|       for (VariantSlot* s = v.content.asObject.head; s; s = s->next) { | ||||
|         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 | ||||
|   | ||||
| @@ -10,7 +10,6 @@ | ||||
| #include "ArrayFunctions.hpp" | ||||
| #include "JsonVariantData.hpp" | ||||
| #include "ObjectFunctions.hpp" | ||||
| #include "Slot.hpp" | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| @@ -24,8 +23,9 @@ inline T variantAsIntegral(const JsonVariantData* var) { | ||||
|     case JSON_NEGATIVE_INTEGER: | ||||
|       return T(~var->content.asInteger + 1); | ||||
|     case JSON_LINKED_STRING: | ||||
|     case JSON_OWNED_STRING: | ||||
|       return parseInteger<T>(var->content.asString); | ||||
|     case JSON_OWNED_STRING: | ||||
|       return parseInteger<T>(var->content.asOwnedString->value); | ||||
|     case JSON_FLOAT: | ||||
|       return T(var->content.asFloat); | ||||
|     default: | ||||
| @@ -48,8 +48,9 @@ inline T variantAsFloat(const JsonVariantData* var) { | ||||
|     case JSON_NEGATIVE_INTEGER: | ||||
|       return -static_cast<T>(var->content.asInteger); | ||||
|     case JSON_LINKED_STRING: | ||||
|     case JSON_OWNED_STRING: | ||||
|       return parseFloat<T>(var->content.asString); | ||||
|     case JSON_OWNED_STRING: | ||||
|       return parseFloat<T>(var->content.asOwnedString->value); | ||||
|     case JSON_FLOAT: | ||||
|       return static_cast<T>(var->content.asFloat); | ||||
|     default: | ||||
| @@ -59,11 +60,14 @@ inline T variantAsFloat(const JsonVariantData* var) { | ||||
|  | ||||
| inline const char* variantAsString(const JsonVariantData* var) { | ||||
|   if (!var) return 0; | ||||
|   if (var && | ||||
|       (var->type == JSON_LINKED_STRING || var->type == JSON_OWNED_STRING)) | ||||
|     return var->content.asString; | ||||
|   else | ||||
|     return 0; | ||||
|   switch (var->type) { | ||||
|     case JSON_LINKED_STRING: | ||||
|       return var->content.asString; | ||||
|     case JSON_OWNED_STRING: | ||||
|       return var->content.asOwnedString->value; | ||||
|     default: | ||||
|       return 0; | ||||
|   } | ||||
| } | ||||
|  | ||||
| inline JsonArrayData* variantAsArray(JsonVariantData* var) { | ||||
| @@ -141,11 +145,10 @@ template <typename T> | ||||
| inline bool variantSetOwnedRaw(JsonVariantData* var, SerializedValue<T> value, | ||||
|                                MemoryPool* pool) { | ||||
|   if (!var) return false; | ||||
|   const char* dup = makeString(value.data(), value.size()).save(pool); | ||||
|   if (dup) { | ||||
|   StringSlot* slot = makeString(value.data(), value.size()).save(pool); | ||||
|   if (slot) { | ||||
|     var->type = JSON_OWNED_RAW; | ||||
|     var->content.asRaw.data = dup; | ||||
|     var->content.asRaw.size = value.size(); | ||||
|     var->content.asOwnedRaw = slot; | ||||
|     return true; | ||||
|   } else { | ||||
|     var->type = JSON_NULL; | ||||
| @@ -156,10 +159,10 @@ inline bool variantSetOwnedRaw(JsonVariantData* var, SerializedValue<T> value, | ||||
| template <typename T> | ||||
| inline bool variantSetString(JsonVariantData* var, T value, MemoryPool* pool) { | ||||
|   if (!var) return false; | ||||
|   const char* dup = value.save(pool); | ||||
|   if (dup) { | ||||
|   StringSlot* slot = value.save(pool); | ||||
|   if (slot) { | ||||
|     var->type = JSON_OWNED_STRING; | ||||
|     var->content.asString = dup; | ||||
|     var->content.asOwnedString = slot; | ||||
|     return true; | ||||
|   } else { | ||||
|     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) { | ||||
|   if (!var) return false; | ||||
|   var->type = JSON_LINKED_STRING; | ||||
| @@ -174,11 +184,6 @@ inline bool variantSetString(JsonVariantData* var, const char* value) { | ||||
|   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) { | ||||
|   if (var) var->type = JSON_NULL; | ||||
| } | ||||
| @@ -212,15 +217,17 @@ inline bool variantCopy(JsonVariantData* dst, const JsonVariantData* src, | ||||
|     case JSON_OBJECT: | ||||
|       return objectCopy(variantToObject(dst), &src->content.asObject, pool); | ||||
|     case JSON_OWNED_STRING: | ||||
|       return variantSetString(dst, src->content.asString, pool); | ||||
|       return variantSetString( | ||||
|           dst, makeString(src->content.asOwnedString->value), pool); | ||||
|     case JSON_OWNED_RAW: | ||||
|       return variantSetOwnedRaw( | ||||
|           dst, | ||||
|           serialized(const_cast<char*>(src->content.asRaw.data), | ||||
|                      src->content.asRaw.size), | ||||
|           pool); | ||||
|       return variantSetOwnedRaw(dst, | ||||
|                                 serialized(src->content.asOwnedRaw->value, | ||||
|                                            src->content.asOwnedRaw->size), | ||||
|                                 pool); | ||||
|     default: | ||||
|       *dst = *src; | ||||
|       // caution: don't override keyIsOwned | ||||
|       dst->type = src->type; | ||||
|       dst->content = src->content; | ||||
|       return true; | ||||
|   } | ||||
| } | ||||
| @@ -260,11 +267,16 @@ inline bool variantEquals(const JsonVariantData* a, const JsonVariantData* b) { | ||||
|  | ||||
|   switch (a->type) { | ||||
|     case JSON_LINKED_RAW: | ||||
|     case JSON_OWNED_RAW: | ||||
|     case JSON_LINKED_STRING: | ||||
|     case JSON_OWNED_STRING: | ||||
|       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_POSITIVE_INTEGER: | ||||
|     case JSON_NEGATIVE_INTEGER: | ||||
|   | ||||
| @@ -18,6 +18,7 @@ template <typename TReader, typename TStringStorage> | ||||
| class JsonDeserializer { | ||||
|   typedef typename remove_reference<TStringStorage>::type::StringBuilder | ||||
|       StringBuilder; | ||||
|   typedef typename StringBuilder::StringType StringType; | ||||
|  | ||||
|  public: | ||||
|   JsonDeserializer(MemoryPool &memoryPool, TReader reader, | ||||
| @@ -124,7 +125,7 @@ class JsonDeserializer { | ||||
|     // Read each key value pair | ||||
|     for (;;) { | ||||
|       // Parse key | ||||
|       StringInMemoryPool key; | ||||
|       StringType key; | ||||
|       err = parseKey(key); | ||||
|       if (err) return err; | ||||
|  | ||||
| @@ -165,7 +166,7 @@ class JsonDeserializer { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   DeserializationError parseKey(StringInMemoryPool &key) { | ||||
|   DeserializationError parseKey(StringType &key) { | ||||
|     if (isQuote(current())) { | ||||
|       return parseQuotedString(key); | ||||
|     } else { | ||||
| @@ -174,15 +175,15 @@ class JsonDeserializer { | ||||
|   } | ||||
|  | ||||
|   DeserializationError parseStringValue(JsonVariant variant) { | ||||
|     StringInMemoryPool value; | ||||
|     StringType value; | ||||
|     DeserializationError err = parseQuotedString(value); | ||||
|     if (err) return err; | ||||
|     variant.set(value); | ||||
|     return DeserializationError::Ok; | ||||
|   } | ||||
|  | ||||
|   DeserializationError parseQuotedString(StringInMemoryPool &result) { | ||||
|     StringBuilder str = _stringStorage.startString(); | ||||
|   DeserializationError parseQuotedString(StringType &result) { | ||||
|     StringBuilder builder = _stringStorage.startString(); | ||||
|     const char stopChar = current(); | ||||
|  | ||||
|     move(); | ||||
| @@ -203,16 +204,16 @@ class JsonDeserializer { | ||||
|         move(); | ||||
|       } | ||||
|  | ||||
|       str.append(c); | ||||
|       builder.append(c); | ||||
|     } | ||||
|  | ||||
|     result = str.complete(); | ||||
|     result = builder.complete(); | ||||
|     if (result.isNull()) return DeserializationError::NoMemory; | ||||
|     return DeserializationError::Ok; | ||||
|   } | ||||
|  | ||||
|   DeserializationError parseNonQuotedString(StringInMemoryPool &result) { | ||||
|     StringBuilder str = _stringStorage.startString(); | ||||
|   DeserializationError parseNonQuotedString(StringType &result) { | ||||
|     StringBuilder builder = _stringStorage.startString(); | ||||
|  | ||||
|     char c = current(); | ||||
|     if (c == '\0') return DeserializationError::IncompleteInput; | ||||
| @@ -220,14 +221,14 @@ class JsonDeserializer { | ||||
|     if (canBeInNonQuotedString(c)) {  // no quotes | ||||
|       do { | ||||
|         move(); | ||||
|         str.append(c); | ||||
|         builder.append(c); | ||||
|         c = current(); | ||||
|       } while (canBeInNonQuotedString(c)); | ||||
|     } else { | ||||
|       return DeserializationError::InvalidInput; | ||||
|     } | ||||
|  | ||||
|     result = str.complete(); | ||||
|     result = builder.complete(); | ||||
|     if (result.isNull()) return DeserializationError::NoMemory; | ||||
|     return DeserializationError::Ok; | ||||
|   } | ||||
|   | ||||
| @@ -11,7 +11,7 @@ | ||||
| // Returns the size (in bytes) of an array with n elements. | ||||
| // Can be very handy to determine the size of a StaticMemoryPool. | ||||
| #define JSON_ARRAY_SIZE(NUMBER_OF_ELEMENTS) \ | ||||
|   ((NUMBER_OF_ELEMENTS) * sizeof(ARDUINOJSON_NAMESPACE::Slot)) | ||||
|   ((NUMBER_OF_ELEMENTS) * sizeof(ARDUINOJSON_NAMESPACE::VariantSlot)) | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
|   | ||||
| @@ -4,7 +4,6 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "Data/Slot.hpp" | ||||
| #include "Data/SlotFunctions.hpp" | ||||
| #include "JsonVariant.hpp" | ||||
|  | ||||
| @@ -30,7 +29,7 @@ class JsonVariantPtr { | ||||
| class JsonArrayIterator { | ||||
|  public: | ||||
|   JsonArrayIterator() : _slot(0) {} | ||||
|   explicit JsonArrayIterator(MemoryPool *memoryPool, Slot *slot) | ||||
|   explicit JsonArrayIterator(MemoryPool *memoryPool, VariantSlot *slot) | ||||
|       : _memoryPool(memoryPool), _slot(slot) {} | ||||
|  | ||||
|   JsonVariant operator*() const { | ||||
| @@ -58,13 +57,13 @@ class JsonArrayIterator { | ||||
|     return *this; | ||||
|   } | ||||
|  | ||||
|   Slot *internal() { | ||||
|   VariantSlot *internal() { | ||||
|     return _slot; | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   MemoryPool *_memoryPool; | ||||
|   Slot *_slot; | ||||
|   VariantSlot *_slot; | ||||
| }; | ||||
|  | ||||
| class JsonVariantConstPtr { | ||||
| @@ -86,7 +85,7 @@ class JsonVariantConstPtr { | ||||
| class JsonArrayConstIterator { | ||||
|  public: | ||||
|   JsonArrayConstIterator() : _slot(0) {} | ||||
|   explicit JsonArrayConstIterator(const Slot *slot) : _slot(slot) {} | ||||
|   explicit JsonArrayConstIterator(const VariantSlot *slot) : _slot(slot) {} | ||||
|  | ||||
|   JsonVariantConst operator*() const { | ||||
|     return JsonVariantConst(&_slot->value); | ||||
| @@ -113,11 +112,11 @@ class JsonArrayConstIterator { | ||||
|     return *this; | ||||
|   } | ||||
|  | ||||
|   const Slot *internal() { | ||||
|   const VariantSlot *internal() { | ||||
|     return _slot; | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   const Slot *_slot; | ||||
|   const VariantSlot *_slot; | ||||
| }; | ||||
| }  // namespace ARDUINOJSON_NAMESPACE | ||||
|   | ||||
| @@ -8,22 +8,18 @@ namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| class JsonKey { | ||||
|  public: | ||||
|   JsonKey(const Slot* slot) : _slot(slot) {} | ||||
|   JsonKey(const VariantSlot* slot) : _slot(slot) {} | ||||
|  | ||||
|   operator const char*() const { | ||||
|     return c_str(); | ||||
|   } | ||||
|  | ||||
|   const char* c_str() const { | ||||
|     return _slot ? _slot->key : 0; | ||||
|     return _slot ? slotGetKey(_slot) : 0; | ||||
|   } | ||||
|  | ||||
|   bool isNull() const { | ||||
|     return _slot == 0 || _slot->key == 0; | ||||
|   } | ||||
|  | ||||
|   bool isStatic() const { | ||||
|     return _slot ? _slot->value.keyIsStatic : true; | ||||
|     return _slot == 0 || _slot->linkedKey == 0; | ||||
|   } | ||||
|  | ||||
|   friend bool operator==(JsonKey lhs, const char* rhs) { | ||||
| @@ -32,6 +28,6 @@ class JsonKey { | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   const Slot* _slot; | ||||
|   const VariantSlot* _slot; | ||||
| }; | ||||
| }  // namespace ARDUINOJSON_NAMESPACE | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
| // Returns the size (in bytes) of an object with n elements. | ||||
| // Can be very handy to determine the size of a StaticMemoryPool. | ||||
| #define JSON_OBJECT_SIZE(NUMBER_OF_ELEMENTS) \ | ||||
|   ((NUMBER_OF_ELEMENTS) * sizeof(ARDUINOJSON_NAMESPACE::Slot)) | ||||
|   ((NUMBER_OF_ELEMENTS) * sizeof(ARDUINOJSON_NAMESPACE::VariantSlot)) | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| @@ -256,7 +256,11 @@ class JsonObject : public JsonObjectProxy<JsonObjectData>, public Visitable { | ||||
|     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); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -11,7 +11,8 @@ namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| class JsonPairPtr { | ||||
|  public: | ||||
|   JsonPairPtr(MemoryPool *memoryPool, Slot *slot) : _pair(memoryPool, slot) {} | ||||
|   JsonPairPtr(MemoryPool *memoryPool, VariantSlot *slot) | ||||
|       : _pair(memoryPool, slot) {} | ||||
|  | ||||
|   const JsonPair *operator->() const { | ||||
|     return &_pair; | ||||
| @@ -29,7 +30,7 @@ class JsonObjectIterator { | ||||
|  public: | ||||
|   JsonObjectIterator() : _slot(0) {} | ||||
|  | ||||
|   explicit JsonObjectIterator(MemoryPool *memoryPool, Slot *slot) | ||||
|   explicit JsonObjectIterator(MemoryPool *memoryPool, VariantSlot *slot) | ||||
|       : _memoryPool(memoryPool), _slot(slot) {} | ||||
|  | ||||
|   JsonPair operator*() const { | ||||
| @@ -57,18 +58,18 @@ class JsonObjectIterator { | ||||
|     return *this; | ||||
|   } | ||||
|  | ||||
|   Slot *internal() { | ||||
|   VariantSlot *internal() { | ||||
|     return _slot; | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   MemoryPool *_memoryPool; | ||||
|   Slot *_slot; | ||||
|   VariantSlot *_slot; | ||||
| }; | ||||
|  | ||||
| class JsonPairConstPtr { | ||||
|  public: | ||||
|   JsonPairConstPtr(const Slot *slot) : _pair(slot) {} | ||||
|   JsonPairConstPtr(const VariantSlot *slot) : _pair(slot) {} | ||||
|  | ||||
|   const JsonPairConst *operator->() const { | ||||
|     return &_pair; | ||||
| @@ -86,7 +87,7 @@ class JsonObjectConstIterator { | ||||
|  public: | ||||
|   JsonObjectConstIterator() : _slot(0) {} | ||||
|  | ||||
|   explicit JsonObjectConstIterator(const Slot *slot) : _slot(slot) {} | ||||
|   explicit JsonObjectConstIterator(const VariantSlot *slot) : _slot(slot) {} | ||||
|  | ||||
|   JsonPairConst operator*() const { | ||||
|     return JsonPairConst(_slot); | ||||
| @@ -113,11 +114,11 @@ class JsonObjectConstIterator { | ||||
|     return *this; | ||||
|   } | ||||
|  | ||||
|   const Slot *internal() { | ||||
|   const VariantSlot *internal() { | ||||
|     return _slot; | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   const Slot *_slot; | ||||
|   const VariantSlot *_slot; | ||||
| }; | ||||
| }  // namespace ARDUINOJSON_NAMESPACE | ||||
|   | ||||
| @@ -11,7 +11,7 @@ namespace ARDUINOJSON_NAMESPACE { | ||||
| // A key value pair for JsonObjectData. | ||||
| class JsonPair { | ||||
|  public: | ||||
|   JsonPair(MemoryPool* memoryPool, Slot* slot) : _key(slot) { | ||||
|   JsonPair(MemoryPool* memoryPool, VariantSlot* slot) : _key(slot) { | ||||
|     if (slot) { | ||||
|       _value = JsonVariant(memoryPool, &slot->value); | ||||
|     } | ||||
| @@ -32,7 +32,7 @@ class JsonPair { | ||||
|  | ||||
| class JsonPairConst { | ||||
|  public: | ||||
|   JsonPairConst(const Slot* slot) : _key(slot) { | ||||
|   JsonPairConst(const VariantSlot* slot) : _key(slot) { | ||||
|     if (slot) { | ||||
|       _value = JsonVariantConst(&slot->value); | ||||
|     } | ||||
|   | ||||
| @@ -204,9 +204,12 @@ class JsonVariant : public JsonVariantProxy<JsonVariantData>, | ||||
|     return variantSetString(_data, value); | ||||
|   } | ||||
|  | ||||
|   // set(const char*); | ||||
|   // for internal use only | ||||
|   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; | ||||
| @@ -323,15 +326,4 @@ class JsonVariantConst : public JsonVariantProxy<const JsonVariantData>, | ||||
|     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 | ||||
|   | ||||
| @@ -89,11 +89,16 @@ inline void JsonVariantConst::accept(Visitor& visitor) const { | ||||
|       return visitor.visitObject(JsonObjectConst(&_data->content.asObject)); | ||||
|  | ||||
|     case JSON_LINKED_STRING: | ||||
|     case JSON_OWNED_STRING: | ||||
|       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: | ||||
|       return visitor.visitRawJson(_data->content.asOwnedRaw->value, | ||||
|                                   _data->content.asOwnedRaw->size); | ||||
|  | ||||
|     case JSON_LINKED_RAW: | ||||
|       return visitor.visitRawJson(_data->content.asRaw.data, | ||||
|                                   _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 | ||||
|  | ||||
| #include "../Strings/StringInMemoryPool.hpp" | ||||
| #include "Alignment.hpp" | ||||
| #include "MemoryPool.hpp" | ||||
| #include "StringBuilder.hpp" | ||||
| #include "StaticMemoryPool.hpp" | ||||
|  | ||||
| #include <stdlib.h>  // malloc, free | ||||
|  | ||||
| @@ -33,18 +34,15 @@ class DefaultAllocator { | ||||
|  | ||||
| template <typename TAllocator> | ||||
| class DynamicMemoryPoolBase : public MemoryPool { | ||||
|   struct Block; | ||||
|   struct EmptyBlock { | ||||
|   class Block : public StaticMemoryPoolBase { | ||||
|    public: | ||||
|     Block(char* buf, size_t sz, Block* nxt) | ||||
|         : StaticMemoryPoolBase(buf, sz), next(nxt) {} | ||||
|     Block* next; | ||||
|     size_t capacity; | ||||
|     size_t size; | ||||
|   }; | ||||
|   struct Block : EmptyBlock { | ||||
|     char data[1]; | ||||
|   }; | ||||
|  | ||||
|  public: | ||||
|   enum { EmptyBlockSize = sizeof(EmptyBlock) }; | ||||
|   enum { EmptyBlockSize = sizeof(Block) }; | ||||
|  | ||||
|   DynamicMemoryPoolBase(size_t initialSize = ARDUINOJSON_DEFAULT_POOL_SIZE) | ||||
|       : _head(NULL), _nextBlockCapacity(initialSize) {} | ||||
| @@ -57,28 +55,82 @@ class DynamicMemoryPoolBase : public MemoryPool { | ||||
|     _nextBlockCapacity = capacity; | ||||
|   } | ||||
|  | ||||
|   // Gets the number of bytes occupied in the memoryPool | ||||
|   virtual size_t allocated_bytes() const { | ||||
|     size_t total = 0; | ||||
|     for (const Block* b = _head; b; b = b->next) total += b->size; | ||||
|     return total; | ||||
|   virtual size_t size() const { | ||||
|     size_t sum = 0; | ||||
|     for (Block* b = _head; b; b = b->next) { | ||||
|       sum += b->size(); | ||||
|     } | ||||
|     return sum; | ||||
|   } | ||||
|  | ||||
|   // Allocates the specified amount of bytes in the memoryPool | ||||
|   virtual char* alloc(size_t bytes) { | ||||
|     alignNextAlloc(); | ||||
|     return canAllocInHead(bytes) ? allocInHead(bytes) : allocInNewBlock(bytes); | ||||
|   virtual VariantSlot* allocVariant() { | ||||
|     for (Block* b = _head; b; b = b->next) { | ||||
|       VariantSlot* s = b->allocVariant(); | ||||
|       if (s) return s; | ||||
|     } | ||||
|  | ||||
|     if (!addNewBlock(sizeof(VariantSlot))) return 0; | ||||
|  | ||||
|     return _head->allocVariant(); | ||||
|   } | ||||
|  | ||||
|   virtual char* realloc(char* oldPtr, size_t oldSize, size_t newSize) { | ||||
|     size_t n = newSize - oldSize; | ||||
|     if (canAllocInHead(n)) { | ||||
|       allocInHead(n); | ||||
|       return oldPtr; | ||||
|     } else { | ||||
|       char* newPtr = allocInNewBlock(newSize); | ||||
|       if (oldPtr && newPtr) memcpy(newPtr, oldPtr, oldSize); | ||||
|       return newPtr; | ||||
|   virtual void freeVariant(VariantSlot* slot) { | ||||
|     for (Block* b = _head; b; b = b->next) { | ||||
|       if (b->owns(slot)) { | ||||
|         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() { | ||||
|     Block* currentBlock = _head; | ||||
|     while (currentBlock != NULL) { | ||||
|       _nextBlockCapacity = currentBlock->capacity; | ||||
|       _nextBlockCapacity = currentBlock->capacity(); | ||||
|       Block* nextBlock = currentBlock->next; | ||||
|       _allocator.deallocate(currentBlock); | ||||
|       currentBlock = nextBlock; | ||||
| @@ -95,40 +147,22 @@ class DynamicMemoryPoolBase : public MemoryPool { | ||||
|     _head = 0; | ||||
|   } | ||||
|  | ||||
|   StringBuilder startString() { | ||||
|     return StringBuilder(this); | ||||
|   size_t blockCount() const { | ||||
|     size_t sum = 0; | ||||
|     for (Block* b = _head; b; b = b->next) sum++; | ||||
|     return sum; | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   void alignNextAlloc() { | ||||
|     if (_head) _head->size = this->round_size_up(_head->size); | ||||
|   } | ||||
|  | ||||
|   bool canAllocInHead(size_t bytes) const { | ||||
|     return _head != NULL && _head->size + bytes <= _head->capacity; | ||||
|   } | ||||
|  | ||||
|   char* allocInHead(size_t bytes) { | ||||
|     char* p = _head->data + _head->size; | ||||
|     _head->size += bytes; | ||||
|     return p; | ||||
|   } | ||||
|  | ||||
|   char* allocInNewBlock(size_t bytes) { | ||||
|   bool addNewBlock(size_t minCapacity) { | ||||
|     size_t capacity = _nextBlockCapacity; | ||||
|     if (bytes > capacity) capacity = bytes; | ||||
|     if (!addNewBlock(capacity)) return NULL; | ||||
|     _nextBlockCapacity *= 2; | ||||
|     return allocInHead(bytes); | ||||
|   } | ||||
|  | ||||
|   bool addNewBlock(size_t capacity) { | ||||
|     size_t bytes = EmptyBlockSize + capacity; | ||||
|     Block* block = static_cast<Block*>(_allocator.allocate(bytes)); | ||||
|     if (block == NULL) return false; | ||||
|     block->capacity = capacity; | ||||
|     block->size = 0; | ||||
|     block->next = _head; | ||||
|     if (minCapacity > capacity) capacity = minCapacity; | ||||
|     capacity = addPadding(capacity); | ||||
|     size_t bytes = sizeof(Block) + capacity; | ||||
|     char* p = reinterpret_cast<char*>(_allocator.allocate(bytes)); | ||||
|     if (!p) return false; | ||||
|     Block* block = new (p) Block(p + sizeof(Block), capacity, _head); | ||||
|     _nextBlockCapacity = capacity * 2; | ||||
|     _head = block; | ||||
|     return true; | ||||
|   } | ||||
|   | ||||
| @@ -5,66 +5,30 @@ | ||||
| #pragma once | ||||
|  | ||||
| #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 "StringSlot.hpp" | ||||
| #include "VariantSlot.hpp" | ||||
|  | ||||
| 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 { | ||||
|  public: | ||||
|   // Allocates n bytes in the MemoryPool. | ||||
|   // Return a pointer to the allocated memory or NULL if allocation fails. | ||||
|   virtual char *alloc(size_t size) = 0; | ||||
|   virtual StringSlot *allocExpandableString() = 0; | ||||
|   virtual StringSlot *allocFrozenString(size_t) = 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() { | ||||
|     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; | ||||
|   } | ||||
|   virtual size_t size() const = 0; | ||||
|  | ||||
|  protected: | ||||
|   MemoryPool() : _freeSlots(0) {} | ||||
|  | ||||
|   // CAUTION: NO VIRTUAL DESTRUCTOR! | ||||
|   // If we add a virtual constructor the Arduino compiler will add malloc() | ||||
|   // and free() to the binary, adding 706 useless bytes. | ||||
|   ~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 | ||||
|   | ||||
							
								
								
									
										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 | ||||
|  | ||||
| #include "../Polyfills/assert.hpp" | ||||
| #include "../Polyfills/mpl/max.hpp" | ||||
| #include "../Strings/StringInMemoryPool.hpp" | ||||
| #include "Alignment.hpp" | ||||
| #include "MemoryPool.hpp" | ||||
| #include "StringBuilder.hpp" | ||||
| #include "SlotList.hpp" | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| // _begin                                _end | ||||
| // v                                        v | ||||
| // +-------------+--------------+-----------+ | ||||
| // | strings...  |   (free)     |  ...slots | | ||||
| // +-------------+--------------+-----------+ | ||||
| //               ^              ^ | ||||
| //             _left          _right | ||||
|  | ||||
| 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: | ||||
|   // Gets the capacity of the memoryPool in bytes | ||||
|   size_t capacity() const { | ||||
|     return _capacity; | ||||
|     return size_t(_end - _begin); | ||||
|   } | ||||
|  | ||||
|   // Allocates the specified amount of bytes in the memoryPool | ||||
|   virtual char* alloc(size_t bytes) { | ||||
|     alignNextAlloc(); | ||||
|     if (!canAlloc(bytes)) return NULL; | ||||
|     return doAlloc(bytes); | ||||
|   virtual size_t size() const { | ||||
|     return allocated_bytes() - _freeVariants.size() - _freeStrings.size(); | ||||
|   } | ||||
|  | ||||
|   virtual char* realloc(char* oldPtr, size_t oldSize, size_t newSize) { | ||||
|     size_t n = newSize - oldSize; | ||||
|     if (!canAlloc(n)) return NULL; | ||||
|     doAlloc(n); | ||||
|     return oldPtr; | ||||
|   virtual VariantSlot* allocVariant() { | ||||
|     VariantSlot* s = _freeVariants.pop(); | ||||
|     if (s) return s; | ||||
|     return s ? s : allocRight<VariantSlot>(); | ||||
|   } | ||||
|  | ||||
|   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() { | ||||
|     _size = 0; | ||||
|     _left = _begin; | ||||
|     _right = _end; | ||||
|     _freeVariants.clear(); | ||||
|     _freeStrings.clear(); | ||||
|     _usedString.clear(); | ||||
|   } | ||||
|  | ||||
|   StringBuilder startString() { | ||||
|     return StringBuilder(this); | ||||
|   bool canAlloc(size_t bytes) const { | ||||
|     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: | ||||
|   StaticMemoryPoolBase(char* memoryPool, size_t capa) | ||||
|       : _buffer(memoryPool), _capacity(capa), _size(0) {} | ||||
|   StaticMemoryPoolBase(char* buffer, size_t capa) | ||||
|       : _begin(buffer), | ||||
|         _left(buffer), | ||||
|         _right(buffer + capa), | ||||
|         _end(buffer + capa) {} | ||||
|  | ||||
|   ~StaticMemoryPoolBase() {} | ||||
|  | ||||
|   // Gets the current usage of the memoryPool in bytes | ||||
|   virtual size_t allocated_bytes() const { | ||||
|     return _size; | ||||
|   size_t allocated_bytes() const { | ||||
|     return size_t(_left - _begin + _end - _right); | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   void alignNextAlloc() { | ||||
|     _size = round_size_up(_size); | ||||
|   StringSlot* allocStringSlot() { | ||||
|     StringSlot* s = _freeStrings.pop(); | ||||
|     if (s) return s; | ||||
|     return allocRight<StringSlot>(); | ||||
|   } | ||||
|  | ||||
|   bool canAlloc(size_t bytes) const { | ||||
|     return _size + bytes <= _capacity; | ||||
|   void freeVariantSlot(VariantSlot* slot) { | ||||
|     _freeVariants.push(slot); | ||||
|   } | ||||
|  | ||||
|   char* doAlloc(size_t bytes) { | ||||
|     char* p = &_buffer[_size]; | ||||
|     _size += bytes; | ||||
|     return p; | ||||
|   void freeStringSlot(StringSlot* slot) { | ||||
|     _usedString.remove(slot); | ||||
|     _freeStrings.push(slot); | ||||
|   } | ||||
|  | ||||
|   char* _buffer; | ||||
|   size_t _capacity; | ||||
|   size_t _size; | ||||
| }; | ||||
|   void compactLeftSide(char* holeAddress, size_t holeSize) { | ||||
|     ARDUINOJSON_ASSERT(holeAddress >= _begin); | ||||
|     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__) | ||||
| #pragma clang diagnostic push | ||||
| @@ -88,7 +215,8 @@ class StaticMemoryPoolBase : public MemoryPool { | ||||
| // bytes. | ||||
| template <size_t CAPACITY> | ||||
| 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: | ||||
|   explicit StaticMemoryPool() | ||||
|   | ||||
| @@ -11,25 +11,43 @@ namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| class StringBuilder { | ||||
|  public: | ||||
|   explicit StringBuilder(MemoryPool* parent) | ||||
|       : _parent(parent), _start(0), _size(0) { | ||||
|     _start = _parent->alloc(1); | ||||
|   typedef StringInMemoryPool StringType; | ||||
|  | ||||
|   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) { | ||||
|     _start = _parent->realloc(_start, _size + 1, _size + 2); | ||||
|     if (_start) _start[_size++] = c; | ||||
|     if (!_slot) return; | ||||
|  | ||||
|     if (_size >= _slot->size) { | ||||
|       _slot = _parent->expandString(_slot); | ||||
|       if (!_slot) return; | ||||
|     } | ||||
|  | ||||
|     _slot->value[_size++] = c; | ||||
|   } | ||||
|  | ||||
|   StringInMemoryPool complete() { | ||||
|     if (_start) _start[_size] = 0; | ||||
|     return _start; | ||||
|   StringType complete() { | ||||
|     append('\0'); | ||||
|     if (_slot) { | ||||
|       _parent->freezeString(_slot, _size); | ||||
|     } | ||||
|     return _slot; | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   MemoryPool* _parent; | ||||
|   char* _start; | ||||
|   size_t _size; | ||||
|   StringSlot* _slot; | ||||
| }; | ||||
|  | ||||
| }  // 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 | ||||
| 
 | ||||
| #include "JsonVariantData.hpp" | ||||
| #include "../Data/JsonVariantData.hpp" | ||||
| 
 | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
| 
 | ||||
| struct Slot { | ||||
| struct VariantSlot { | ||||
|   JsonVariantData value; | ||||
|   struct Slot* next; | ||||
|   struct Slot* prev; | ||||
|   const char* key; | ||||
|   struct VariantSlot* next; | ||||
|   struct VariantSlot* prev; | ||||
|   union { | ||||
|     const char* linkedKey; | ||||
|     StringSlot* ownedKey; | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| }  // namespace ARDUINOJSON_NAMESPACE
 | ||||
| @@ -17,6 +17,7 @@ template <typename TReader, typename TStringStorage> | ||||
| class MsgPackDeserializer { | ||||
|   typedef typename remove_reference<TStringStorage>::type::StringBuilder | ||||
|       StringBuilder; | ||||
|   typedef typename StringBuilder::StringType StringType; | ||||
|  | ||||
|  public: | ||||
|   MsgPackDeserializer(MemoryPool &memoryPool, TReader reader, | ||||
| @@ -220,16 +221,29 @@ class MsgPackDeserializer { | ||||
|     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) { | ||||
|     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) { | ||||
|       uint8_t c; | ||||
|       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; | ||||
|     variant.set(s); | ||||
|     return DeserializationError::Ok; | ||||
|   } | ||||
|  | ||||
| @@ -278,12 +292,11 @@ class MsgPackDeserializer { | ||||
|     if (_nestingLimit == 0) return DeserializationError::TooDeep; | ||||
|     --_nestingLimit; | ||||
|     for (; n; --n) { | ||||
|       JsonVariantLocal key(_memoryPool); | ||||
|       DeserializationError err = parse(key); | ||||
|       StringType key; | ||||
|       DeserializationError err = parseKey(key); | ||||
|       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; | ||||
|  | ||||
|       err = parse(value); | ||||
| @@ -293,6 +306,27 @@ class MsgPackDeserializer { | ||||
|     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; | ||||
|   TReader _reader; | ||||
|   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: | ||||
|   class StringBuilder { | ||||
|    public: | ||||
|     typedef ZeroTerminatedRamStringConst StringType; | ||||
|  | ||||
|     StringBuilder(TChar** ptr) : _writePtr(ptr), _startPtr(*ptr) {} | ||||
|  | ||||
|     void append(char c) { | ||||
|       *(*_writePtr)++ = TChar(c); | ||||
|     } | ||||
|  | ||||
|     StringInMemoryPool complete() const { | ||||
|     StringType complete() const { | ||||
|       *(*_writePtr)++ = 0; | ||||
|       return reinterpret_cast<const char*>(_startPtr); | ||||
|     } | ||||
|   | ||||
| @@ -13,12 +13,12 @@ class ArduinoString { | ||||
|   ArduinoString(const ::String& str) : _str(&str) {} | ||||
|  | ||||
|   template <typename TMemoryPool> | ||||
|   const char* save(TMemoryPool* memoryPool) const { | ||||
|   StringSlot* save(TMemoryPool* memoryPool) const { | ||||
|     if (isNull()) return NULL; | ||||
|     size_t n = _str->length() + 1; | ||||
|     void* dup = memoryPool->alloc(n); | ||||
|     if (dup != NULL) memcpy(dup, _str->c_str(), n); | ||||
|     return static_cast<const char*>(dup); | ||||
|     StringSlot* slot = memoryPool->allocFrozenString(n); | ||||
|     if (slot) memcpy(slot->value, _str->c_str(), n); | ||||
|     return slot; | ||||
|   } | ||||
|  | ||||
|   bool isNull() const { | ||||
|   | ||||
| @@ -22,11 +22,11 @@ class FixedSizeFlashString { | ||||
|   } | ||||
|  | ||||
|   template <typename TMemoryPool> | ||||
|   const char* save(TMemoryPool* memoryPool) const { | ||||
|   StringSlot* save(TMemoryPool* memoryPool) const { | ||||
|     if (!_str) return NULL; | ||||
|     void* dup = memoryPool->alloc(_size); | ||||
|     if (dup != NULL) memcpy_P(dup, (const char*)_str, _size); | ||||
|     return static_cast<const char*>(dup); | ||||
|     StringSlot* slot = memoryPool->allocFrozenString(_size); | ||||
|     if (!slot) memcpy_P(slot->value, (const char*)_str, _size); | ||||
|     return slot; | ||||
|   } | ||||
|  | ||||
|   size_t size() const { | ||||
|   | ||||
| @@ -23,12 +23,11 @@ class FixedSizeRamString { | ||||
|   } | ||||
|  | ||||
|   template <typename TMemoryPool> | ||||
|   const char* save(TMemoryPool* memoryPool) const { | ||||
|   StringSlot* save(TMemoryPool* memoryPool) const { | ||||
|     if (!_str) return NULL; | ||||
|     void* dup = memoryPool->alloc(_size); | ||||
|     if (!dup) return NULL; | ||||
|     memcpy(dup, _str, _size); | ||||
|     return static_cast<const char*>(dup); | ||||
|     StringSlot* slot = memoryPool->allocFrozenString(_size); | ||||
|     if (slot) memcpy(slot->value, _str, _size); | ||||
|     return slot; | ||||
|   } | ||||
|  | ||||
|   size_t size() const { | ||||
|   | ||||
| @@ -13,11 +13,11 @@ class StlString { | ||||
|   StlString(const std::string& str) : _str(&str) {} | ||||
|  | ||||
|   template <typename TMemoryPool> | ||||
|   const char* save(TMemoryPool* memoryPool) const { | ||||
|   StringSlot* save(TMemoryPool* memoryPool) const { | ||||
|     size_t n = _str->length() + 1; | ||||
|     void* dup = memoryPool->alloc(n); | ||||
|     if (dup != NULL) memcpy(dup, _str->c_str(), n); | ||||
|     return static_cast<const char*>(dup); | ||||
|     StringSlot* slot = memoryPool->allocFrozenString(n); | ||||
|     if (slot) memcpy(slot->value, _str->c_str(), n); | ||||
|     return slot; | ||||
|   } | ||||
|  | ||||
|   bool isNull() const { | ||||
|   | ||||
| @@ -4,13 +4,45 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "ZeroTerminatedRamStringConst.hpp" | ||||
| #include <string.h> | ||||
| #include "../Memory/StringSlot.hpp" | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| class StringInMemoryPool : public ZeroTerminatedRamStringConst { | ||||
| class StringInMemoryPool { | ||||
|  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 | ||||
|   | ||||
| @@ -21,12 +21,12 @@ class ZeroTerminatedFlashString { | ||||
|   } | ||||
|  | ||||
|   template <typename TMemoryPool> | ||||
|   const char* save(TMemoryPool* memoryPool) const { | ||||
|   StringSlot* save(TMemoryPool* memoryPool) const { | ||||
|     if (!_str) return NULL; | ||||
|     size_t n = size() + 1;  // copy the terminator | ||||
|     void* dup = memoryPool->alloc(n); | ||||
|     if (dup != NULL) memcpy_P(dup, (const char*)_str, n); | ||||
|     return static_cast<const char*>(dup); | ||||
|     StringSlot* slot = memoryPool->allocFrozenString(n); | ||||
|     if (slot) memcpy_P(slot->value, reinterpret_cast<const char*>(_str), n); | ||||
|     return slot; | ||||
|   } | ||||
|  | ||||
|   size_t size() const { | ||||
|   | ||||
| @@ -14,13 +14,12 @@ class ZeroTerminatedRamString : public ZeroTerminatedRamStringConst { | ||||
|       : ZeroTerminatedRamStringConst(str) {} | ||||
|  | ||||
|   template <typename TMemoryPool> | ||||
|   const char* save(TMemoryPool* memoryPool) const { | ||||
|   StringSlot* save(TMemoryPool* memoryPool) const { | ||||
|     if (!_str) return NULL; | ||||
|     size_t n = size() + 1; | ||||
|     void* dup = memoryPool->alloc(n); | ||||
|     if (!dup) return NULL; | ||||
|     memcpy(dup, _str, n); | ||||
|     return static_cast<const char*>(dup); | ||||
|     StringSlot* slot = memoryPool->allocFrozenString(n); | ||||
|     if (slot) memcpy(slot->value, _str, n); | ||||
|     return slot; | ||||
|   } | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -11,7 +11,7 @@ namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| class ZeroTerminatedRamStringConst { | ||||
|  public: | ||||
|   ZeroTerminatedRamStringConst(const char* str) : _str(str) {} | ||||
|   ZeroTerminatedRamStringConst(const char* str = 0) : _str(str) {} | ||||
|  | ||||
|   bool equals(const char* expected) const { | ||||
|     const char* actual = _str; | ||||
| @@ -23,15 +23,19 @@ class ZeroTerminatedRamStringConst { | ||||
|     return !_str; | ||||
|   } | ||||
|  | ||||
|   template <typename TMemoryPool> | ||||
|   const char* save(TMemoryPool*) const { | ||||
|     return _str; | ||||
|   } | ||||
|   // template <typename TMemoryPool> | ||||
|   // const char* save(TMemoryPool*) const { | ||||
|   //   return _str; | ||||
|   // } | ||||
|  | ||||
|   size_t size() const { | ||||
|     return strlen(_str); | ||||
|   } | ||||
|  | ||||
|   const char* c_str() const { | ||||
|     return _str; | ||||
|   } | ||||
|  | ||||
|  protected: | ||||
|   const char* _str; | ||||
| }; | ||||
|   | ||||
| @@ -2,12 +2,14 @@ | ||||
| # Copyright Benoit Blanchon 2014-2018 | ||||
| # MIT License | ||||
|  | ||||
| add_executable(DynamicMemoryPoolTests  | ||||
| 	alloc.cpp | ||||
| 	allocSlot.cpp | ||||
| add_executable(DynamicMemoryPoolTests | ||||
| 	allocString.cpp | ||||
| 	allocVariant.cpp | ||||
| 	blocks.cpp | ||||
| 	clear.cpp | ||||
| 	no_memory.cpp | ||||
| 	size.cpp | ||||
| 	startString.cpp | ||||
| 	StringBuilder.cpp | ||||
| ) | ||||
|  | ||||
| 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 | ||||
| // MIT License | ||||
|  | ||||
| #include <ArduinoJson.h> | ||||
| #include <ArduinoJson/Memory/DynamicMemoryPool.hpp> | ||||
| #include <ArduinoJson/Memory/StringBuilder.hpp> | ||||
| #include <catch.hpp> | ||||
|  | ||||
| using namespace ARDUINOJSON_NAMESPACE; | ||||
| @@ -32,8 +33,8 @@ TEST_CASE("DynamicMemoryPool no memory") { | ||||
|   //   REQUIRE(err != DeserializationError::Ok); | ||||
|   // } | ||||
|  | ||||
|   SECTION("startString()") { | ||||
|     StringBuilder str = _memoryPool.startString(); | ||||
|   SECTION("StringBuilder returns null") { | ||||
|     StringBuilder str(&_memoryPool); | ||||
|     str.append('!'); | ||||
|     REQUIRE(str.complete().isNull()); | ||||
|   } | ||||
|   | ||||
| @@ -14,35 +14,43 @@ TEST_CASE("DynamicMemoryPool::size()") { | ||||
|     REQUIRE(0 == memoryPool.size()); | ||||
|   } | ||||
|  | ||||
|   SECTION("Increases after alloc()") { | ||||
|     memoryPool.alloc(1); | ||||
|     REQUIRE(1U <= memoryPool.size()); | ||||
|     memoryPool.alloc(1); | ||||
|     REQUIRE(2U <= memoryPool.size()); | ||||
|   SECTION("Increases after allocExpandableString()") { | ||||
|     StringSlot* a = memoryPool.allocExpandableString(); | ||||
|     memoryPool.freezeString(a, 1); | ||||
|     REQUIRE(memoryPool.size() == JSON_STRING_SIZE(1)); | ||||
|  | ||||
|     StringSlot* b = memoryPool.allocExpandableString(); | ||||
|     memoryPool.freezeString(b, 1); | ||||
|     REQUIRE(memoryPool.size() == 2 * JSON_STRING_SIZE(1)); | ||||
|   } | ||||
|  | ||||
|   SECTION("Goes back to 0 after clear()") { | ||||
|     memoryPool.alloc(1); | ||||
|     memoryPool.clear(); | ||||
|   SECTION("Increases after allocVariant()") { | ||||
|     memoryPool.allocVariant(); | ||||
|     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()); | ||||
|   } | ||||
|  | ||||
|   SECTION("Increases after allocSlot()") { | ||||
|     memoryPool.allocSlot(); | ||||
|     REQUIRE(sizeof(Slot) == memoryPool.size()); | ||||
|   SECTION("Decreases after freeString()") { | ||||
|     StringSlot* a = memoryPool.allocFrozenString(5); | ||||
|     StringSlot* b = memoryPool.allocFrozenString(6); | ||||
|  | ||||
|     memoryPool.allocSlot(); | ||||
|     REQUIRE(2 * sizeof(Slot) == memoryPool.size()); | ||||
|   } | ||||
|     memoryPool.freeString(b); | ||||
|     REQUIRE(memoryPool.size() == JSON_STRING_SIZE(5)); | ||||
|  | ||||
|   SECTION("Decreases after freeSlot()") { | ||||
|     Slot* s1 = memoryPool.allocSlot(); | ||||
|     Slot* s2 = memoryPool.allocSlot(); | ||||
|  | ||||
|     memoryPool.freeSlot(s1); | ||||
|     REQUIRE(sizeof(Slot) == memoryPool.size()); | ||||
|  | ||||
|     memoryPool.freeSlot(s2); | ||||
|     REQUIRE(0 == memoryPool.size()); | ||||
|     memoryPool.freeString(a); | ||||
|     REQUIRE(memoryPool.size() == 0); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -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*") { | ||||
|     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()); | ||||
|   } | ||||
|  | ||||
|   SECTION("should duplicate std::string") { | ||||
|     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()); | ||||
|   } | ||||
|  | ||||
| @@ -120,19 +120,19 @@ TEST_CASE("JsonArray::add()") { | ||||
|  | ||||
|   SECTION("should duplicate serialized(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()); | ||||
|   } | ||||
|  | ||||
|   SECTION("should duplicate 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()); | ||||
|   } | ||||
|  | ||||
|   SECTION("should duplicate serialized(std::string)") { | ||||
|     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()); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -105,13 +105,13 @@ TEST_CASE("JsonArray::operator[]") { | ||||
|  | ||||
|   SECTION("should duplicate char*") { | ||||
|     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()); | ||||
|   } | ||||
|  | ||||
|   SECTION("should duplicate std::string") { | ||||
|     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()); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -16,7 +16,7 @@ TEST_CASE("deserialize JSON array with a StaticJsonDocument") { | ||||
|   } | ||||
|  | ||||
|   SECTION("TooSmallBufferForArrayWithOneValue") { | ||||
|     StaticJsonDocument<JSON_ARRAY_SIZE(1) - 1> doc; | ||||
|     StaticJsonDocument<JSON_ARRAY_SIZE(0)> doc; | ||||
|     char input[] = "[1]"; | ||||
|  | ||||
|     DeserializationError err = deserializeJson(doc, input); | ||||
| @@ -34,7 +34,7 @@ TEST_CASE("deserialize JSON array with a StaticJsonDocument") { | ||||
|   } | ||||
|  | ||||
|   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[] = "[{}]"; | ||||
|  | ||||
|     DeserializationError err = deserializeJson(doc, input); | ||||
| @@ -56,7 +56,7 @@ TEST_CASE("deserialize JSON array with a StaticJsonDocument") { | ||||
|  | ||||
|     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 | ||||
|     // will not insert bytes to enforce alignement | ||||
|   } | ||||
|   | ||||
| @@ -16,7 +16,7 @@ TEST_CASE("deserialize JSON object with StaticJsonDocument") { | ||||
|   } | ||||
|  | ||||
|   SECTION("TooSmallBufferForObjectWithOneValue") { | ||||
|     StaticJsonDocument<JSON_OBJECT_SIZE(1) - 1> doc; | ||||
|     StaticJsonDocument<JSON_OBJECT_SIZE(0)> doc; | ||||
|     char input[] = "{\"a\":1}"; | ||||
|  | ||||
|     DeserializationError err = deserializeJson(doc, input); | ||||
| @@ -34,7 +34,7 @@ TEST_CASE("deserialize JSON object with StaticJsonDocument") { | ||||
|   } | ||||
|  | ||||
|   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\":[]}"; | ||||
|  | ||||
|     DeserializationError err = deserializeJson(doc, input); | ||||
|   | ||||
| @@ -94,35 +94,36 @@ TEST_CASE("DynamicJsonDocument") { | ||||
|   } | ||||
|  | ||||
|   SECTION("memoryUsage()") { | ||||
|     typedef ARDUINOJSON_NAMESPACE::Slot Slot; | ||||
|  | ||||
|     SECTION("Increases after adding value to array") { | ||||
|       JsonArray arr = doc.to<JsonArray>(); | ||||
|  | ||||
|       REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(0)); | ||||
|       arr.add(42); | ||||
|       REQUIRE(sizeof(Slot) == doc.memoryUsage()); | ||||
|       REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(1)); | ||||
|       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>(); | ||||
|       arr.add(42); | ||||
|       arr.add(43); | ||||
|  | ||||
|       REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(2)); | ||||
|       arr.remove(1); | ||||
|       REQUIRE(sizeof(Slot) == doc.memoryUsage()); | ||||
|       REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(1)); | ||||
|       arr.remove(0); | ||||
|       REQUIRE(0 == doc.memoryUsage()); | ||||
|       REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(0)); | ||||
|     } | ||||
|  | ||||
|     SECTION("Increases after adding value to object") { | ||||
|       JsonObject obj = doc.to<JsonObject>(); | ||||
|  | ||||
|       REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(0)); | ||||
|       obj["a"] = 1; | ||||
|       REQUIRE(sizeof(Slot) == doc.memoryUsage()); | ||||
|       REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(1)); | ||||
|       obj["b"] = 2; | ||||
|       REQUIRE(2 * sizeof(Slot) == doc.memoryUsage()); | ||||
|       REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2)); | ||||
|     } | ||||
|  | ||||
|     SECTION("Decreases after removing value from object") { | ||||
| @@ -130,10 +131,11 @@ TEST_CASE("DynamicJsonDocument") { | ||||
|       obj["a"] = 1; | ||||
|       obj["b"] = 2; | ||||
|  | ||||
|       REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2)); | ||||
|       obj.remove("a"); | ||||
|       REQUIRE(sizeof(Slot) == doc.memoryUsage()); | ||||
|       REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(1)); | ||||
|       obj.remove("b"); | ||||
|       REQUIRE(0 == doc.memoryUsage()); | ||||
|       REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(0)); | ||||
|     } | ||||
|  | ||||
|     SECTION("Decreases after removing nested object from array") { | ||||
| @@ -141,8 +143,9 @@ TEST_CASE("DynamicJsonDocument") { | ||||
|       JsonObject obj = arr.createNestedObject(); | ||||
|       obj["hello"] = "world"; | ||||
|  | ||||
|       REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(1)); | ||||
|       arr.remove(0); | ||||
|       REQUIRE(0 == doc.memoryUsage()); | ||||
|       REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(0)); | ||||
|     } | ||||
|  | ||||
|     SECTION("Decreases after removing nested array from object") { | ||||
| @@ -150,8 +153,9 @@ TEST_CASE("DynamicJsonDocument") { | ||||
|       JsonArray arr = obj.createNestedArray("hello"); | ||||
|       arr.add("world"); | ||||
|  | ||||
|       REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(1)); | ||||
|       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"); | ||||
|       serializeJson(obj, result); | ||||
|       REQUIRE("{\"b\":1,\"c\":2}" == result); | ||||
|       REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2)); | ||||
|     } | ||||
|  | ||||
|     SECTION("Remove middle") { | ||||
|       obj.remove("b"); | ||||
|       serializeJson(obj, result); | ||||
|       REQUIRE("{\"a\":0,\"c\":2}" == result); | ||||
|       REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2)); | ||||
|     } | ||||
|  | ||||
|     SECTION("Remove last") { | ||||
|       obj.remove("c"); | ||||
|       serializeJson(obj, 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") { | ||||
|     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()); | ||||
|   } | ||||
|  | ||||
|   SECTION("should duplicate char* key") { | ||||
|     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()); | ||||
|   } | ||||
|  | ||||
|   SECTION("should duplicate char* key&value") { | ||||
|     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()); | ||||
|   } | ||||
|  | ||||
|   SECTION("should duplicate std::string value") { | ||||
|     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()); | ||||
|   } | ||||
|  | ||||
|   SECTION("should duplicate std::string key") { | ||||
|     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()); | ||||
|   } | ||||
|  | ||||
|   SECTION("should duplicate std::string key&value") { | ||||
|     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()); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -108,6 +108,16 @@ TEST_CASE("JsonVariant::as()") { | ||||
|     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\")") { | ||||
|     variant.set("true"); | ||||
|  | ||||
|   | ||||
| @@ -216,7 +216,7 @@ TEST_CASE("JsonVariant comparisons") { | ||||
|   JsonVariant variant2 = doc2.to<JsonVariant>(); | ||||
|   JsonVariant variant3 = doc3.to<JsonVariant>(); | ||||
|  | ||||
|   SECTION("IntegerInVariant") { | ||||
|   SECTION("Variants containing integers") { | ||||
|     variant1.set(42); | ||||
|     variant2.set(42); | ||||
|     variant3.set(666); | ||||
| @@ -228,7 +228,7 @@ TEST_CASE("JsonVariant comparisons") { | ||||
|     REQUIRE_FALSE(variant1 == variant3); | ||||
|   } | ||||
|  | ||||
|   SECTION("StringInVariant") { | ||||
|   SECTION("Variants containing linked strings") { | ||||
|     variant1.set("0hello" + 1);  // make sure they have | ||||
|     variant2.set("1hello" + 1);  // different addresses | ||||
|     variant3.set("world"); | ||||
| @@ -240,7 +240,19 @@ TEST_CASE("JsonVariant comparisons") { | ||||
|     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); | ||||
|     variant2.set(42.0); | ||||
|     variant3.set(666.0); | ||||
|   | ||||
| @@ -13,22 +13,24 @@ TEST_CASE("JsonVariant::set(JsonVariant)") { | ||||
|  | ||||
|   SECTION("stores JsonArray by copy") { | ||||
|     JsonArray arr = doc2.to<JsonArray>(); | ||||
|     arr.add(42); | ||||
|     JsonObject obj = arr.createNestedObject(); | ||||
|     obj["hello"] = "world"; | ||||
|  | ||||
|     var1.set(arr); | ||||
|  | ||||
|     arr[0] = 666; | ||||
|     REQUIRE(var1.as<std::string>() == "[42]"); | ||||
|     REQUIRE(var1.as<std::string>() == "[{\"hello\":\"world\"}]"); | ||||
|   } | ||||
|  | ||||
|   SECTION("stores JsonObject by copy") { | ||||
|     JsonObject obj = doc2.to<JsonObject>(); | ||||
|     obj["value"] = 42; | ||||
|     JsonArray arr = obj.createNestedArray("value"); | ||||
|     arr.add(42); | ||||
|  | ||||
|     var1.set(obj); | ||||
|  | ||||
|     obj["value"] = 666; | ||||
|     REQUIRE(var1.as<std::string>() == "{\"value\":42}"); | ||||
|     REQUIRE(var1.as<std::string>() == "{\"value\":[42]}"); | ||||
|   } | ||||
|  | ||||
|   SECTION("stores const char* by reference") { | ||||
| @@ -45,20 +47,20 @@ TEST_CASE("JsonVariant::set(JsonVariant)") { | ||||
|     var1.set(str); | ||||
|     var2.set(var1); | ||||
|  | ||||
|     REQUIRE(doc1.memoryUsage() == 8); | ||||
|     REQUIRE(doc2.memoryUsage() == 8); | ||||
|     REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(8)); | ||||
|     REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(8)); | ||||
|   } | ||||
|  | ||||
|   SECTION("stores std::string by copy") { | ||||
|     var1.set(std::string("hello!!")); | ||||
|     var2.set(var1); | ||||
|  | ||||
|     REQUIRE(doc1.memoryUsage() == 8); | ||||
|     REQUIRE(doc2.memoryUsage() == 8); | ||||
|     REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(8)); | ||||
|     REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(8)); | ||||
|   } | ||||
|  | ||||
|   SECTION("stores Serialized<const char*> by reference") { | ||||
|     var1.set(serialized("hello!!", 8)); | ||||
|     var1.set(serialized("hello!!", JSON_STRING_SIZE(8))); | ||||
|     var2.set(var1); | ||||
|  | ||||
|     REQUIRE(doc1.memoryUsage() == 0); | ||||
| @@ -70,15 +72,15 @@ TEST_CASE("JsonVariant::set(JsonVariant)") { | ||||
|     var1.set(serialized(str, 8)); | ||||
|     var2.set(var1); | ||||
|  | ||||
|     REQUIRE(doc1.memoryUsage() == 8); | ||||
|     REQUIRE(doc2.memoryUsage() == 8); | ||||
|     REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(8)); | ||||
|     REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(8)); | ||||
|   } | ||||
|  | ||||
|   SECTION("stores Serialized<std::string> by copy") { | ||||
|     var1.set(serialized(std::string("hello!!!"))); | ||||
|     var2.set(var1); | ||||
|  | ||||
|     REQUIRE(doc1.memoryUsage() == 8); | ||||
|     REQUIRE(doc2.memoryUsage() == 8); | ||||
|     REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(8)); | ||||
|     REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(8)); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -5,17 +5,21 @@ | ||||
| #include <ArduinoJson.h> | ||||
| #include <catch.hpp> | ||||
|  | ||||
| static const size_t epsilon = sizeof(void*); | ||||
|  | ||||
| template <size_t Capacity> | ||||
| static void check(const char* input, DeserializationError expected) { | ||||
|   StaticJsonDocument<Capacity> variant; | ||||
|  | ||||
|   DeserializationError error = deserializeMsgPack(variant, input); | ||||
|  | ||||
|   CAPTURE(input); | ||||
|   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&)") { | ||||
|   SECTION("single values always fit") { | ||||
|     check<0>("\xc0", DeserializationError::Ok);                  // nil | ||||
| @@ -27,31 +31,39 @@ TEST_CASE("deserializeMsgPack(StaticJsonDocument&)") { | ||||
|   } | ||||
|  | ||||
|   SECTION("fixstr") { | ||||
|     check<0>("\xA0", DeserializationError::Ok); | ||||
|     check<0>("\xA1H", DeserializationError::NoMemory); | ||||
|     check<4>("\xA1H", DeserializationError::Ok); | ||||
|     check<4>("\xA5Hello", DeserializationError::NoMemory); | ||||
|     checkString<8>("\xA0", DeserializationError::Ok); | ||||
|     checkString<8>("\xA7ZZZZZZZ", DeserializationError::Ok); | ||||
|     checkString<8>("\xA8ZZZZZZZZ", DeserializationError::NoMemory); | ||||
|     checkString<16>("\xAFZZZZZZZZZZZZZZZ", DeserializationError::Ok); | ||||
|     checkString<16>("\xB0ZZZZZZZZZZZZZZZZ", DeserializationError::NoMemory); | ||||
|   } | ||||
|  | ||||
|   SECTION("str 8") { | ||||
|     check<0>("\xD9\x00", DeserializationError::Ok); | ||||
|     check<0>("\xD9\x01H", DeserializationError::NoMemory); | ||||
|     check<4>("\xD9\x01H", DeserializationError::Ok); | ||||
|     check<4>("\xD9\x05Hello", DeserializationError::NoMemory); | ||||
|     checkString<8>("\xD9\x00", DeserializationError::Ok); | ||||
|     checkString<8>("\xD9\x07ZZZZZZZ", DeserializationError::Ok); | ||||
|     checkString<8>("\xD9\x08ZZZZZZZZ", DeserializationError::NoMemory); | ||||
|     checkString<16>("\xD9\x0FZZZZZZZZZZZZZZZ", DeserializationError::Ok); | ||||
|     checkString<16>("\xD9\x10ZZZZZZZZZZZZZZZZ", DeserializationError::NoMemory); | ||||
|   } | ||||
|  | ||||
|   SECTION("str 16") { | ||||
|     check<0>("\xDA\x00\x00", DeserializationError::Ok); | ||||
|     check<0>("\xDA\x00\x01H", DeserializationError::NoMemory); | ||||
|     check<4>("\xDA\x00\x01H", DeserializationError::Ok); | ||||
|     check<4>("\xDA\x00\x05Hello", DeserializationError::NoMemory); | ||||
|     checkString<8>("\xDA\x00\x00", DeserializationError::Ok); | ||||
|     checkString<8>("\xDA\x00\x07ZZZZZZZ", DeserializationError::Ok); | ||||
|     checkString<8>("\xDA\x00\x08ZZZZZZZZ", DeserializationError::NoMemory); | ||||
|     checkString<16>("\xDA\x00\x0FZZZZZZZZZZZZZZZ", DeserializationError::Ok); | ||||
|     checkString<16>("\xDA\x00\x10ZZZZZZZZZZZZZZZZ", | ||||
|                     DeserializationError::NoMemory); | ||||
|   } | ||||
|  | ||||
|   SECTION("str 32") { | ||||
|     check<0>("\xDB\x00\x00\x00\x00", DeserializationError::Ok); | ||||
|     check<0>("\xDB\x00\x00\x00\x01H", DeserializationError::NoMemory); | ||||
|     check<4>("\xDB\x00\x00\x00\x01H", DeserializationError::Ok); | ||||
|     check<4>("\xDB\x00\x00\x00\x05Hello", DeserializationError::NoMemory); | ||||
|     checkString<8>("\xDB\x00\x00\x00\x00", DeserializationError::Ok); | ||||
|     checkString<8>("\xDB\x00\x00\x00\x07ZZZZZZZ", DeserializationError::Ok); | ||||
|     checkString<8>("\xDB\x00\x00\x00\x08ZZZZZZZZ", | ||||
|                    DeserializationError::NoMemory); | ||||
|     checkString<16>("\xDB\x00\x00\x00\x0FZZZZZZZZZZZZZZZ", | ||||
|                     DeserializationError::Ok); | ||||
|     checkString<16>("\xDB\x00\x00\x00\x10ZZZZZZZZZZZZZZZZ", | ||||
|                     DeserializationError::NoMemory); | ||||
|   } | ||||
|  | ||||
|   SECTION("fixarray") { | ||||
| @@ -89,14 +101,14 @@ TEST_CASE("deserializeMsgPack(StaticJsonDocument&)") { | ||||
|     SECTION("{H:1}") { | ||||
|       check<JSON_OBJECT_SIZE(0)>("\x81\xA1H\x01", | ||||
|                                  DeserializationError::NoMemory); | ||||
|       check<JSON_OBJECT_SIZE(1) + epsilon>("\x81\xA1H\x01", | ||||
|                                            DeserializationError::Ok); | ||||
|       check<JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(2)>( | ||||
|           "\x81\xA1H\x01", DeserializationError::Ok); | ||||
|     } | ||||
|     SECTION("{H:1,W:2}") { | ||||
|       check<JSON_OBJECT_SIZE(1) + epsilon>("\x82\xA1H\x01\xA1W\x02", | ||||
|                                            DeserializationError::NoMemory); | ||||
|       check<JSON_OBJECT_SIZE(2) + 2 * epsilon>("\x82\xA1H\x01\xA1W\x02", | ||||
|                                                DeserializationError::Ok); | ||||
|       check<JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(2)>( | ||||
|           "\x82\xA1H\x01\xA1W\x02", DeserializationError::NoMemory); | ||||
|       check<JSON_OBJECT_SIZE(2) + 2 * JSON_STRING_SIZE(2)>( | ||||
|           "\x82\xA1H\x01\xA1W\x02", DeserializationError::Ok); | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -107,14 +119,14 @@ TEST_CASE("deserializeMsgPack(StaticJsonDocument&)") { | ||||
|     SECTION("{H:1}") { | ||||
|       check<JSON_OBJECT_SIZE(0)>("\xDE\x00\x01\xA1H\x01", | ||||
|                                  DeserializationError::NoMemory); | ||||
|       check<JSON_OBJECT_SIZE(1) + epsilon>("\xDE\x00\x01\xA1H\x01", | ||||
|                                            DeserializationError::Ok); | ||||
|       check<JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(2)>( | ||||
|           "\xDE\x00\x01\xA1H\x01", DeserializationError::Ok); | ||||
|     } | ||||
|     SECTION("{H:1,W:2}") { | ||||
|       check<JSON_OBJECT_SIZE(1) + epsilon>("\xDE\x00\x02\xA1H\x01\xA1W\x02", | ||||
|                                            DeserializationError::NoMemory); | ||||
|       check<JSON_OBJECT_SIZE(2) + 2 * epsilon>("\xDE\x00\x02\xA1H\x01\xA1W\x02", | ||||
|                                                DeserializationError::Ok); | ||||
|       check<JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(2)>( | ||||
|           "\xDE\x00\x02\xA1H\x01\xA1W\x02", DeserializationError::NoMemory); | ||||
|       check<JSON_OBJECT_SIZE(2) + 2 * JSON_OBJECT_SIZE(1)>( | ||||
|           "\xDE\x00\x02\xA1H\x01\xA1W\x02", DeserializationError::Ok); | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -126,14 +138,14 @@ TEST_CASE("deserializeMsgPack(StaticJsonDocument&)") { | ||||
|     SECTION("{H:1}") { | ||||
|       check<JSON_OBJECT_SIZE(0)>("\xDF\x00\x00\x00\x01\xA1H\x01", | ||||
|                                  DeserializationError::NoMemory); | ||||
|       check<JSON_OBJECT_SIZE(1) + epsilon>("\xDF\x00\x00\x00\x01\xA1H\x01", | ||||
|                                            DeserializationError::Ok); | ||||
|       check<JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(2)>( | ||||
|           "\xDF\x00\x00\x00\x01\xA1H\x01", DeserializationError::Ok); | ||||
|     } | ||||
|     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", | ||||
|           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); | ||||
|     } | ||||
|   } | ||||
|   | ||||
| @@ -3,9 +3,11 @@ | ||||
| # MIT License | ||||
|  | ||||
| add_executable(StaticMemoryPoolTests  | ||||
| 	alloc.cpp | ||||
| 	allocVariant.cpp | ||||
| 	allocString.cpp | ||||
| 	clear.cpp | ||||
| 	size.cpp | ||||
| 	startString.cpp | ||||
| 	StringBuilder.cpp | ||||
| ) | ||||
|  | ||||
| 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; | ||||
|  | ||||
| TEST_CASE("StaticMemoryPool::size()") { | ||||
|   StaticMemoryPool<64> memoryPool; | ||||
|  | ||||
|   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") { | ||||
|     StaticMemoryPool<32> memoryPool; | ||||
|     REQUIRE(0 == memoryPool.size()); | ||||
|   } | ||||
|  | ||||
|   SECTION("Increases after alloc()") { | ||||
|     memoryPool.alloc(1); | ||||
|     REQUIRE(1U <= memoryPool.size()); | ||||
|     memoryPool.alloc(1); | ||||
|     REQUIRE(2U <= memoryPool.size()); | ||||
|   SECTION("Increases after allocFrozenString()") { | ||||
|     StaticMemoryPool<128> memoryPool; | ||||
|     memoryPool.allocFrozenString(0); | ||||
|     REQUIRE(memoryPool.size() == JSON_STRING_SIZE(0)); | ||||
|     memoryPool.allocFrozenString(0); | ||||
|     REQUIRE(memoryPool.size() == 2 * JSON_STRING_SIZE(0)); | ||||
|   } | ||||
|  | ||||
|   SECTION("Doesn't grow when memoryPool is full") { | ||||
|     memoryPool.alloc(64); | ||||
|     memoryPool.alloc(1); | ||||
|     REQUIRE(64 == memoryPool.size()); | ||||
|   SECTION("Decreases after freeVariant()") { | ||||
|     StaticMemoryPool<128> memoryPool; | ||||
|     VariantSlot* a = memoryPool.allocVariant(); | ||||
|     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") { | ||||
|     memoryPool.alloc(65); | ||||
|     REQUIRE(0 == memoryPool.size()); | ||||
|   SECTION("Decreases after calling freeString() in order") { | ||||
|     StaticMemoryPool<128> memoryPool; | ||||
|     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()") { | ||||
|     memoryPool.alloc(1); | ||||
|     memoryPool.clear(); | ||||
|     REQUIRE(0 == memoryPool.size()); | ||||
|   SECTION("Decreases after calling freeString() in reverse order") { | ||||
|     StaticMemoryPool<128> memoryPool; | ||||
|     StringSlot* a = memoryPool.allocFrozenString(5); | ||||
|     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