diff --git a/CHANGELOG.md b/CHANGELOG.md index 75813fb8..fa04016c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,8 @@ HEAD * Removed `JsonObject::is(k)` and `JsonObject::set(k,v)` * Replaced `T JsonArray::get(i)` with `JsonVariant JsonArray::get(i)` * Replaced `T JsonObject::get(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) ----------- diff --git a/CMakeLists.txt b/CMakeLists.txt index 697703b1..8a3e5a5f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/fuzzing/Makefile b/fuzzing/Makefile index 0f2aaabf..ce4ede32 100644 --- a/fuzzing/Makefile +++ b/fuzzing/Makefile @@ -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 \ diff --git a/fuzzing/fuzzer_main.cpp b/fuzzing/fuzzer_main.cpp index 06ec63a5..a4a0f455 100644 --- a/fuzzing/fuzzer_main.cpp +++ b/fuzzing/fuzzer_main.cpp @@ -5,17 +5,34 @@ // This file is NOT use by Google's OSS fuzz // I only use it to reproduce the bugs found -#include -#include +#include // size_t +#include // fopen et al. +#include // exit #include -#include +#include 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(file), - std::istreambuf_iterator()); +std::vector 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 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(buffer.data()), - buffer.size()); + std::vector buffer = read(argv[i]); + LLVMFuzzerTestOneInput(buffer.data(), buffer.size()); } return 0; } diff --git a/src/ArduinoJson/Configuration.hpp b/src/ArduinoJson/Configuration.hpp index f8d2bd97..26211128 100644 --- a/src/ArduinoJson/Configuration.hpp +++ b/src/ArduinoJson/Configuration.hpp @@ -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 diff --git a/src/ArduinoJson/Data/ArrayFunctions.hpp b/src/ArduinoJson/Data/ArrayFunctions.hpp index 708265ba..52d29953 100644 --- a/src/ArduinoJson/Data/ArrayFunctions.hpp +++ b/src/ArduinoJson/Data/ArrayFunctions.hpp @@ -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; diff --git a/src/ArduinoJson/Data/JsonVariantData.hpp b/src/ArduinoJson/Data/JsonVariantData.hpp index d00854c4..c0ff52ed 100644 --- a/src/ArduinoJson/Data/JsonVariantData.hpp +++ b/src/ArduinoJson/Data/JsonVariantData.hpp @@ -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; }; diff --git a/src/ArduinoJson/Data/ObjectFunctions.hpp b/src/ArduinoJson/Data/ObjectFunctions.hpp index 30dc7338..5604f877 100644 --- a/src/ArduinoJson/Data/ObjectFunctions.hpp +++ b/src/ArduinoJson/Data/ObjectFunctions.hpp @@ -11,11 +11,11 @@ namespace ARDUINOJSON_NAMESPACE { template -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 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 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; diff --git a/src/ArduinoJson/Data/SlotFunctions.hpp b/src/ArduinoJson/Data/SlotFunctions.hpp index 283a2c60..058d236f 100644 --- a/src/ArduinoJson/Data/SlotFunctions.hpp +++ b/src/ArduinoJson/Data/SlotFunctions.hpp @@ -7,65 +7,80 @@ #include "../Memory/MemoryPool.hpp" #include "../Strings/StringTypes.hpp" #include "JsonVariantData.hpp" -#include "Slot.hpp" namespace ARDUINOJSON_NAMESPACE { template -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 diff --git a/src/ArduinoJson/Data/VariantFunctions.hpp b/src/ArduinoJson/Data/VariantFunctions.hpp index fba9abd9..0305b7a7 100644 --- a/src/ArduinoJson/Data/VariantFunctions.hpp +++ b/src/ArduinoJson/Data/VariantFunctions.hpp @@ -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(var->content.asString); + case JSON_OWNED_STRING: + return parseInteger(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(var->content.asInteger); case JSON_LINKED_STRING: - case JSON_OWNED_STRING: return parseFloat(var->content.asString); + case JSON_OWNED_STRING: + return parseFloat(var->content.asOwnedString->value); case JSON_FLOAT: return static_cast(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 inline bool variantSetOwnedRaw(JsonVariantData* var, SerializedValue 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 value, template 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(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(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: diff --git a/src/ArduinoJson/Json/JsonDeserializer.hpp b/src/ArduinoJson/Json/JsonDeserializer.hpp index d956ca56..d11bc162 100644 --- a/src/ArduinoJson/Json/JsonDeserializer.hpp +++ b/src/ArduinoJson/Json/JsonDeserializer.hpp @@ -18,6 +18,7 @@ template class JsonDeserializer { typedef typename remove_reference::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; } diff --git a/src/ArduinoJson/JsonArray.hpp b/src/ArduinoJson/JsonArray.hpp index e37ca723..23e8b948 100644 --- a/src/ArduinoJson/JsonArray.hpp +++ b/src/ArduinoJson/JsonArray.hpp @@ -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 { diff --git a/src/ArduinoJson/JsonArrayIterator.hpp b/src/ArduinoJson/JsonArrayIterator.hpp index 73fa285a..80ebb7ae 100644 --- a/src/ArduinoJson/JsonArrayIterator.hpp +++ b/src/ArduinoJson/JsonArrayIterator.hpp @@ -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 diff --git a/src/ArduinoJson/JsonKey.hpp b/src/ArduinoJson/JsonKey.hpp index 34c69dd6..fcb85b0b 100644 --- a/src/ArduinoJson/JsonKey.hpp +++ b/src/ArduinoJson/JsonKey.hpp @@ -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 diff --git a/src/ArduinoJson/JsonObject.hpp b/src/ArduinoJson/JsonObject.hpp index 68d61852..1e9fde69 100644 --- a/src/ArduinoJson/JsonObject.hpp +++ b/src/ArduinoJson/JsonObject.hpp @@ -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, 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); } diff --git a/src/ArduinoJson/JsonObjectIterator.hpp b/src/ArduinoJson/JsonObjectIterator.hpp index 1075d8c7..542005d4 100644 --- a/src/ArduinoJson/JsonObjectIterator.hpp +++ b/src/ArduinoJson/JsonObjectIterator.hpp @@ -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 diff --git a/src/ArduinoJson/JsonPair.hpp b/src/ArduinoJson/JsonPair.hpp index 67cc8e63..b9abc4ff 100644 --- a/src/ArduinoJson/JsonPair.hpp +++ b/src/ArduinoJson/JsonPair.hpp @@ -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); } diff --git a/src/ArduinoJson/JsonVariant.hpp b/src/ArduinoJson/JsonVariant.hpp index f96255bd..73b13840 100644 --- a/src/ArduinoJson/JsonVariant.hpp +++ b/src/ArduinoJson/JsonVariant.hpp @@ -204,9 +204,12 @@ class JsonVariant : public JsonVariantProxy, 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, 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 diff --git a/src/ArduinoJson/JsonVariantImpl.hpp b/src/ArduinoJson/JsonVariantImpl.hpp index 94aa9fb2..66e6fdc6 100644 --- a/src/ArduinoJson/JsonVariantImpl.hpp +++ b/src/ArduinoJson/JsonVariantImpl.hpp @@ -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); diff --git a/src/ArduinoJson/Memory/Alignment.hpp b/src/ArduinoJson/Memory/Alignment.hpp new file mode 100644 index 00000000..663d8eef --- /dev/null +++ b/src/ArduinoJson/Memory/Alignment.hpp @@ -0,0 +1,28 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include // size_t + +namespace ARDUINOJSON_NAMESPACE { + +inline bool isAligned(void *ptr) { + const size_t mask = sizeof(void *) - 1; + size_t addr = reinterpret_cast(ptr); + return (addr & mask) == 0; +} + +inline size_t addPadding(size_t bytes) { + const size_t mask = sizeof(void *) - 1; + return (bytes + mask) & ~mask; +} + +template +struct AddPadding { + static const size_t mask = sizeof(void *) - 1; + static const size_t value = (bytes + mask) & ~mask; +}; + +} // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Memory/DynamicMemoryPool.hpp b/src/ArduinoJson/Memory/DynamicMemoryPool.hpp index 194c9281..b4292cba 100644 --- a/src/ArduinoJson/Memory/DynamicMemoryPool.hpp +++ b/src/ArduinoJson/Memory/DynamicMemoryPool.hpp @@ -5,8 +5,9 @@ #pragma once #include "../Strings/StringInMemoryPool.hpp" +#include "Alignment.hpp" #include "MemoryPool.hpp" -#include "StringBuilder.hpp" +#include "StaticMemoryPool.hpp" #include // malloc, free @@ -33,18 +34,15 @@ class DefaultAllocator { template 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(_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(_allocator.allocate(bytes)); + if (!p) return false; + Block* block = new (p) Block(p + sizeof(Block), capacity, _head); + _nextBlockCapacity = capacity * 2; _head = block; return true; } diff --git a/src/ArduinoJson/Memory/MemoryPool.hpp b/src/ArduinoJson/Memory/MemoryPool.hpp index f0d07ee0..41b803db 100644 --- a/src/ArduinoJson/Memory/MemoryPool.hpp +++ b/src/ArduinoJson/Memory/MemoryPool.hpp @@ -5,66 +5,30 @@ #pragma once #include // for size_t -#include // for uint8_t -#include -#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(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 diff --git a/src/ArduinoJson/Memory/SlotList.hpp b/src/ArduinoJson/Memory/SlotList.hpp new file mode 100644 index 00000000..d8a780e4 --- /dev/null +++ b/src/ArduinoJson/Memory/SlotList.hpp @@ -0,0 +1,67 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include // for size_t + +namespace ARDUINOJSON_NAMESPACE { + +template +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(slot)); + } + + template + 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 diff --git a/src/ArduinoJson/Memory/StaticMemoryPool.hpp b/src/ArduinoJson/Memory/StaticMemoryPool.hpp index bbf3f8c9..a1a34d52 100644 --- a/src/ArduinoJson/Memory/StaticMemoryPool.hpp +++ b/src/ArduinoJson/Memory/StaticMemoryPool.hpp @@ -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(); + } + + 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 + T* allocRight() { + return reinterpret_cast(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(); } - 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 _freeVariants; + SlotList _freeStrings; + SlotList _usedString; +}; // namespace ARDUINOJSON_NAMESPACE #if defined(__clang__) #pragma clang diagnostic push @@ -88,7 +215,8 @@ class StaticMemoryPoolBase : public MemoryPool { // bytes. template class StaticMemoryPool : public StaticMemoryPoolBase { - static const size_t ACTUAL_CAPACITY = Max<1, CAPACITY>::value; + static const size_t ACTUAL_CAPACITY = + AddPadding::value>::value; public: explicit StaticMemoryPool() diff --git a/src/ArduinoJson/Memory/StringBuilder.hpp b/src/ArduinoJson/Memory/StringBuilder.hpp index f3016def..7a967913 100644 --- a/src/ArduinoJson/Memory/StringBuilder.hpp +++ b/src/ArduinoJson/Memory/StringBuilder.hpp @@ -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 diff --git a/src/ArduinoJson/Memory/StringSlot.hpp b/src/ArduinoJson/Memory/StringSlot.hpp new file mode 100644 index 00000000..b3e12c45 --- /dev/null +++ b/src/ArduinoJson/Memory/StringSlot.hpp @@ -0,0 +1,20 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include // 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 diff --git a/src/ArduinoJson/Data/Slot.hpp b/src/ArduinoJson/Memory/VariantSlot.hpp similarity index 52% rename from src/ArduinoJson/Data/Slot.hpp rename to src/ArduinoJson/Memory/VariantSlot.hpp index 7c8d113c..22fc84bd 100644 --- a/src/ArduinoJson/Data/Slot.hpp +++ b/src/ArduinoJson/Memory/VariantSlot.hpp @@ -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 diff --git a/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp b/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp index 92e87e29..1e18f23b 100644 --- a/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp +++ b/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp @@ -17,6 +17,7 @@ template class MsgPackDeserializer { typedef typename remove_reference::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 + 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(c)); + builder.append(static_cast(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()) return DeserializationError::NotSupported; - JsonVariant value = object.set(StringInMemoryPool(key.as())); + 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(key); + + case 0xda: + return readString(key); + + case 0xdb: + return readString(key); + + default: + return DeserializationError::NotSupported; + } + } + MemoryPool *_memoryPool; TReader _reader; TStringStorage _stringStorage; diff --git a/src/ArduinoJson/Polyfills/assert.hpp b/src/ArduinoJson/Polyfills/assert.hpp new file mode 100644 index 00000000..06d99fa1 --- /dev/null +++ b/src/ArduinoJson/Polyfills/assert.hpp @@ -0,0 +1,12 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#ifdef ARDUINOJSON_DEBUG +#include +#define ARDUINOJSON_ASSERT(X) assert(X) +#else +#define ARDUINOJSON_ASSERT(X) ((void)0) +#endif diff --git a/src/ArduinoJson/StringStorage/StringMover.hpp b/src/ArduinoJson/StringStorage/StringMover.hpp index 3abdaf34..5c180ab1 100644 --- a/src/ArduinoJson/StringStorage/StringMover.hpp +++ b/src/ArduinoJson/StringStorage/StringMover.hpp @@ -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(_startPtr); } diff --git a/src/ArduinoJson/Strings/ArduinoString.hpp b/src/ArduinoJson/Strings/ArduinoString.hpp index eccb2c96..23effe84 100644 --- a/src/ArduinoJson/Strings/ArduinoString.hpp +++ b/src/ArduinoJson/Strings/ArduinoString.hpp @@ -13,12 +13,12 @@ class ArduinoString { ArduinoString(const ::String& str) : _str(&str) {} template - 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(dup); + StringSlot* slot = memoryPool->allocFrozenString(n); + if (slot) memcpy(slot->value, _str->c_str(), n); + return slot; } bool isNull() const { diff --git a/src/ArduinoJson/Strings/FixedSizeFlashString.hpp b/src/ArduinoJson/Strings/FixedSizeFlashString.hpp index 41e6de1a..9c301669 100644 --- a/src/ArduinoJson/Strings/FixedSizeFlashString.hpp +++ b/src/ArduinoJson/Strings/FixedSizeFlashString.hpp @@ -22,11 +22,11 @@ class FixedSizeFlashString { } template - 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(dup); + StringSlot* slot = memoryPool->allocFrozenString(_size); + if (!slot) memcpy_P(slot->value, (const char*)_str, _size); + return slot; } size_t size() const { diff --git a/src/ArduinoJson/Strings/FixedSizeRamString.hpp b/src/ArduinoJson/Strings/FixedSizeRamString.hpp index 15066922..932b7b6c 100644 --- a/src/ArduinoJson/Strings/FixedSizeRamString.hpp +++ b/src/ArduinoJson/Strings/FixedSizeRamString.hpp @@ -23,12 +23,11 @@ class FixedSizeRamString { } template - 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(dup); + StringSlot* slot = memoryPool->allocFrozenString(_size); + if (slot) memcpy(slot->value, _str, _size); + return slot; } size_t size() const { diff --git a/src/ArduinoJson/Strings/StlString.hpp b/src/ArduinoJson/Strings/StlString.hpp index bdf9dcba..f51054f6 100644 --- a/src/ArduinoJson/Strings/StlString.hpp +++ b/src/ArduinoJson/Strings/StlString.hpp @@ -13,11 +13,11 @@ class StlString { StlString(const std::string& str) : _str(&str) {} template - 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(dup); + StringSlot* slot = memoryPool->allocFrozenString(n); + if (slot) memcpy(slot->value, _str->c_str(), n); + return slot; } bool isNull() const { diff --git a/src/ArduinoJson/Strings/StringInMemoryPool.hpp b/src/ArduinoJson/Strings/StringInMemoryPool.hpp index 5b1f9950..c9ab049e 100644 --- a/src/ArduinoJson/Strings/StringInMemoryPool.hpp +++ b/src/ArduinoJson/Strings/StringInMemoryPool.hpp @@ -4,13 +4,45 @@ #pragma once -#include "ZeroTerminatedRamStringConst.hpp" +#include +#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 + 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 diff --git a/src/ArduinoJson/Strings/ZeroTerminatedFlashString.hpp b/src/ArduinoJson/Strings/ZeroTerminatedFlashString.hpp index 5601d1be..c6018fda 100644 --- a/src/ArduinoJson/Strings/ZeroTerminatedFlashString.hpp +++ b/src/ArduinoJson/Strings/ZeroTerminatedFlashString.hpp @@ -21,12 +21,12 @@ class ZeroTerminatedFlashString { } template - 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(dup); + StringSlot* slot = memoryPool->allocFrozenString(n); + if (slot) memcpy_P(slot->value, reinterpret_cast(_str), n); + return slot; } size_t size() const { diff --git a/src/ArduinoJson/Strings/ZeroTerminatedRamString.hpp b/src/ArduinoJson/Strings/ZeroTerminatedRamString.hpp index 8c07e2f6..96874e8a 100644 --- a/src/ArduinoJson/Strings/ZeroTerminatedRamString.hpp +++ b/src/ArduinoJson/Strings/ZeroTerminatedRamString.hpp @@ -14,13 +14,12 @@ class ZeroTerminatedRamString : public ZeroTerminatedRamStringConst { : ZeroTerminatedRamStringConst(str) {} template - 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(dup); + StringSlot* slot = memoryPool->allocFrozenString(n); + if (slot) memcpy(slot->value, _str, n); + return slot; } }; diff --git a/src/ArduinoJson/Strings/ZeroTerminatedRamStringConst.hpp b/src/ArduinoJson/Strings/ZeroTerminatedRamStringConst.hpp index a0090153..c155566d 100644 --- a/src/ArduinoJson/Strings/ZeroTerminatedRamStringConst.hpp +++ b/src/ArduinoJson/Strings/ZeroTerminatedRamStringConst.hpp @@ -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 - const char* save(TMemoryPool*) const { - return _str; - } + // template + // 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; }; diff --git a/test/DynamicMemoryPool/CMakeLists.txt b/test/DynamicMemoryPool/CMakeLists.txt index 063b3a9c..e95b7bb4 100644 --- a/test/DynamicMemoryPool/CMakeLists.txt +++ b/test/DynamicMemoryPool/CMakeLists.txt @@ -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) diff --git a/test/DynamicMemoryPool/StringBuilder.cpp b/test/DynamicMemoryPool/StringBuilder.cpp new file mode 100644 index 00000000..12aaf159 --- /dev/null +++ b/test/DynamicMemoryPool/StringBuilder.cpp @@ -0,0 +1,41 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include +#include + +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()); + } +} diff --git a/test/DynamicMemoryPool/alloc.cpp b/test/DynamicMemoryPool/alloc.cpp deleted file mode 100644 index aa444d64..00000000 --- a/test/DynamicMemoryPool/alloc.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#include -#include -#include - -using namespace ARDUINOJSON_NAMESPACE; - -static bool isAligned(void* ptr) { - const size_t mask = sizeof(void*) - 1; - size_t addr = reinterpret_cast(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 memoryPool(1); - memoryPool.alloc(1); - memoryPool.alloc(1); - } - REQUIRE(allocatorLog.str() == "A1A2FF"); - } - - SECTION("Resets allocation size after clear()") { - allocatorLog.str(""); - { - DynamicMemoryPoolBase 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 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 - } -} diff --git a/test/DynamicMemoryPool/allocSlot.cpp b/test/DynamicMemoryPool/allocSlot.cpp deleted file mode 100644 index 15d48e57..00000000 --- a/test/DynamicMemoryPool/allocSlot.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#include -#include - -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); - } -} diff --git a/test/DynamicMemoryPool/allocString.cpp b/test/DynamicMemoryPool/allocString.cpp new file mode 100644 index 00000000..22d20710 --- /dev/null +++ b/test/DynamicMemoryPool/allocString.cpp @@ -0,0 +1,28 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include +#include + +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); + } +} diff --git a/test/DynamicMemoryPool/allocVariant.cpp b/test/DynamicMemoryPool/allocVariant.cpp new file mode 100644 index 00000000..70540b9f --- /dev/null +++ b/test/DynamicMemoryPool/allocVariant.cpp @@ -0,0 +1,41 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +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); + } +} diff --git a/test/DynamicMemoryPool/blocks.cpp b/test/DynamicMemoryPool/blocks.cpp new file mode 100644 index 00000000..d9fcb912 --- /dev/null +++ b/test/DynamicMemoryPool/blocks.cpp @@ -0,0 +1,69 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include +#include + +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 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 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 memoryPool(1); + memoryPool.allocString(42); + } + std::stringstream expected; + expected << "A" << JSON_STRING_SIZE(42) // block 1 + << "F"; + REQUIRE(allocatorLog.str() == expected.str()); + }*/ +} diff --git a/test/DynamicMemoryPool/clear.cpp b/test/DynamicMemoryPool/clear.cpp new file mode 100644 index 00000000..38e1b9a6 --- /dev/null +++ b/test/DynamicMemoryPool/clear.cpp @@ -0,0 +1,47 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +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); + } +} diff --git a/test/DynamicMemoryPool/no_memory.cpp b/test/DynamicMemoryPool/no_memory.cpp index c53d5b83..554b34df 100644 --- a/test/DynamicMemoryPool/no_memory.cpp +++ b/test/DynamicMemoryPool/no_memory.cpp @@ -2,7 +2,8 @@ // Copyright Benoit Blanchon 2014-2018 // MIT License -#include +#include +#include #include 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()); } diff --git a/test/DynamicMemoryPool/size.cpp b/test/DynamicMemoryPool/size.cpp index d6ef7abc..f3f69384 100644 --- a/test/DynamicMemoryPool/size.cpp +++ b/test/DynamicMemoryPool/size.cpp @@ -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); } } diff --git a/test/DynamicMemoryPool/startString.cpp b/test/DynamicMemoryPool/startString.cpp deleted file mode 100644 index 1b0964e8..00000000 --- a/test/DynamicMemoryPool/startString.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#include -#include - -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()); - } -} diff --git a/test/JsonArray/add.cpp b/test/JsonArray/add.cpp index 50fd77d4..7889c91d 100644 --- a/test/JsonArray/add.cpp +++ b/test/JsonArray/add.cpp @@ -102,13 +102,13 @@ TEST_CASE("JsonArray::add()") { SECTION("should duplicate char*") { array.add(const_cast("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("{}"))); - 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()); } } diff --git a/test/JsonArray/subscript.cpp b/test/JsonArray/subscript.cpp index a50fe958..a3b408af 100644 --- a/test/JsonArray/subscript.cpp +++ b/test/JsonArray/subscript.cpp @@ -105,13 +105,13 @@ TEST_CASE("JsonArray::operator[]") { SECTION("should duplicate char*") { array[0] = const_cast("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()); } diff --git a/test/JsonDeserializer/deserializeJsonArrayStatic.cpp b/test/JsonDeserializer/deserializeJsonArrayStatic.cpp index c9f31fad..36662ce3 100644 --- a/test/JsonDeserializer/deserializeJsonArrayStatic.cpp +++ b/test/JsonDeserializer/deserializeJsonArrayStatic.cpp @@ -16,7 +16,7 @@ TEST_CASE("deserialize JSON array with a StaticJsonDocument") { } SECTION("TooSmallBufferForArrayWithOneValue") { - StaticJsonDocument doc; + StaticJsonDocument doc; char input[] = "[1]"; DeserializationError err = deserializeJson(doc, input); @@ -34,7 +34,7 @@ TEST_CASE("deserialize JSON array with a StaticJsonDocument") { } SECTION("TooSmallBufferForArrayWithNestedObject") { - StaticJsonDocument doc; + StaticJsonDocument 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 } diff --git a/test/JsonDeserializer/deserializeJsonObjectStatic.cpp b/test/JsonDeserializer/deserializeJsonObjectStatic.cpp index 6a1d307a..f7cfa407 100644 --- a/test/JsonDeserializer/deserializeJsonObjectStatic.cpp +++ b/test/JsonDeserializer/deserializeJsonObjectStatic.cpp @@ -16,7 +16,7 @@ TEST_CASE("deserialize JSON object with StaticJsonDocument") { } SECTION("TooSmallBufferForObjectWithOneValue") { - StaticJsonDocument doc; + StaticJsonDocument doc; char input[] = "{\"a\":1}"; DeserializationError err = deserializeJson(doc, input); @@ -34,7 +34,7 @@ TEST_CASE("deserialize JSON object with StaticJsonDocument") { } SECTION("TooSmallBufferForObjectWithNestedObject") { - StaticJsonDocument doc; + StaticJsonDocument doc; char input[] = "{\"a\":[]}"; DeserializationError err = deserializeJson(doc, input); diff --git a/test/JsonDocument/DynamicJsonDocument.cpp b/test/JsonDocument/DynamicJsonDocument.cpp index 5700624a..e6a99240 100644 --- a/test/JsonDocument/DynamicJsonDocument.cpp +++ b/test/JsonDocument/DynamicJsonDocument.cpp @@ -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(); + 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(); 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(); + 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)); } } } diff --git a/test/JsonObject/remove.cpp b/test/JsonObject/remove.cpp index 3c83e7f2..3af304f9 100644 --- a/test/JsonObject/remove.cpp +++ b/test/JsonObject/remove.cpp @@ -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)); } } diff --git a/test/JsonObject/subscript.cpp b/test/JsonObject/subscript.cpp index 1b0753b3..f673a9ee 100644 --- a/test/JsonObject/subscript.cpp +++ b/test/JsonObject/subscript.cpp @@ -107,37 +107,37 @@ TEST_CASE("JsonObject::operator[]") { SECTION("should duplicate char* value") { obj["hello"] = const_cast("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("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("hello")] = const_cast("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()); } diff --git a/test/JsonVariant/as.cpp b/test/JsonVariant/as.cpp index 7c0a450f..16618782 100644 --- a/test/JsonVariant/as.cpp +++ b/test/JsonVariant/as.cpp @@ -108,6 +108,16 @@ TEST_CASE("JsonVariant::as()") { REQUIRE(variant.as() == std::string("hello")); } + SECTION("set(std::string(\"4.2\"))") { + variant.set(std::string("4.2")); + + REQUIRE(variant.as() == true); + REQUIRE(variant.as() == 4L); + REQUIRE(variant.as() == 4.2); + REQUIRE(variant.as() == std::string("4.2")); + REQUIRE(variant.as() == std::string("4.2")); + } + SECTION("set(\"true\")") { variant.set("true"); diff --git a/test/JsonVariant/compare.cpp b/test/JsonVariant/compare.cpp index 3d75ea0d..976a7ed1 100644 --- a/test/JsonVariant/compare.cpp +++ b/test/JsonVariant/compare.cpp @@ -216,7 +216,7 @@ TEST_CASE("JsonVariant comparisons") { JsonVariant variant2 = doc2.to(); JsonVariant variant3 = doc3.to(); - 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); diff --git a/test/JsonVariant/copy.cpp b/test/JsonVariant/copy.cpp index d81be221..b99b998d 100644 --- a/test/JsonVariant/copy.cpp +++ b/test/JsonVariant/copy.cpp @@ -13,22 +13,24 @@ TEST_CASE("JsonVariant::set(JsonVariant)") { SECTION("stores JsonArray by copy") { JsonArray arr = doc2.to(); - arr.add(42); + JsonObject obj = arr.createNestedObject(); + obj["hello"] = "world"; var1.set(arr); arr[0] = 666; - REQUIRE(var1.as() == "[42]"); + REQUIRE(var1.as() == "[{\"hello\":\"world\"}]"); } SECTION("stores JsonObject by copy") { JsonObject obj = doc2.to(); - obj["value"] = 42; + JsonArray arr = obj.createNestedArray("value"); + arr.add(42); var1.set(obj); obj["value"] = 666; - REQUIRE(var1.as() == "{\"value\":42}"); + REQUIRE(var1.as() == "{\"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 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 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)); } } diff --git a/test/MsgPackDeserializer/deserializeStaticVariant.cpp b/test/MsgPackDeserializer/deserializeStaticVariant.cpp index 5944f7c7..8210d96d 100644 --- a/test/MsgPackDeserializer/deserializeStaticVariant.cpp +++ b/test/MsgPackDeserializer/deserializeStaticVariant.cpp @@ -5,17 +5,21 @@ #include #include -static const size_t epsilon = sizeof(void*); - template static void check(const char* input, DeserializationError expected) { StaticJsonDocument variant; DeserializationError error = deserializeMsgPack(variant, input); + CAPTURE(input); REQUIRE(error == expected); } +template +static void checkString(const char* input, DeserializationError expected) { + check(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("\x81\xA1H\x01", DeserializationError::NoMemory); - check("\x81\xA1H\x01", - DeserializationError::Ok); + check( + "\x81\xA1H\x01", DeserializationError::Ok); } SECTION("{H:1,W:2}") { - check("\x82\xA1H\x01\xA1W\x02", - DeserializationError::NoMemory); - check("\x82\xA1H\x01\xA1W\x02", - DeserializationError::Ok); + check( + "\x82\xA1H\x01\xA1W\x02", DeserializationError::NoMemory); + check( + "\x82\xA1H\x01\xA1W\x02", DeserializationError::Ok); } } @@ -107,14 +119,14 @@ TEST_CASE("deserializeMsgPack(StaticJsonDocument&)") { SECTION("{H:1}") { check("\xDE\x00\x01\xA1H\x01", DeserializationError::NoMemory); - check("\xDE\x00\x01\xA1H\x01", - DeserializationError::Ok); + check( + "\xDE\x00\x01\xA1H\x01", DeserializationError::Ok); } SECTION("{H:1,W:2}") { - check("\xDE\x00\x02\xA1H\x01\xA1W\x02", - DeserializationError::NoMemory); - check("\xDE\x00\x02\xA1H\x01\xA1W\x02", - DeserializationError::Ok); + check( + "\xDE\x00\x02\xA1H\x01\xA1W\x02", DeserializationError::NoMemory); + check( + "\xDE\x00\x02\xA1H\x01\xA1W\x02", DeserializationError::Ok); } } @@ -126,14 +138,14 @@ TEST_CASE("deserializeMsgPack(StaticJsonDocument&)") { SECTION("{H:1}") { check("\xDF\x00\x00\x00\x01\xA1H\x01", DeserializationError::NoMemory); - check("\xDF\x00\x00\x00\x01\xA1H\x01", - DeserializationError::Ok); + check( + "\xDF\x00\x00\x00\x01\xA1H\x01", DeserializationError::Ok); } SECTION("{H:1,W:2}") { - check( + check( "\xDF\x00\x00\x00\x02\xA1H\x01\xA1W\x02", DeserializationError::NoMemory); - check( + check( "\xDF\x00\x00\x00\x02\xA1H\x01\xA1W\x02", DeserializationError::Ok); } } diff --git a/test/StaticMemoryPool/CMakeLists.txt b/test/StaticMemoryPool/CMakeLists.txt index 5d026dc9..9fa5f180 100644 --- a/test/StaticMemoryPool/CMakeLists.txt +++ b/test/StaticMemoryPool/CMakeLists.txt @@ -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) diff --git a/test/StaticMemoryPool/StringBuilder.cpp b/test/StaticMemoryPool/StringBuilder.cpp new file mode 100644 index 00000000..69c83614 --- /dev/null +++ b/test/StaticMemoryPool/StringBuilder.cpp @@ -0,0 +1,39 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include +#include + +using namespace ARDUINOJSON_NAMESPACE; + +TEST_CASE("StringBuilder") { + SECTION("WorksWhenBufferIsBigEnough") { + StaticMemoryPool 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 memoryPool; + + StringBuilder str(&memoryPool); + str.append('h'); + str.complete(); + + REQUIRE(JSON_STRING_SIZE(2) == memoryPool.size()); + } +} diff --git a/test/StaticMemoryPool/alloc.cpp b/test/StaticMemoryPool/alloc.cpp deleted file mode 100644 index 23500e22..00000000 --- a/test/StaticMemoryPool/alloc.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#include -#include - -using namespace ARDUINOJSON_NAMESPACE; - -static bool isAligned(void *ptr) { - const size_t mask = sizeof(void *) - 1; - size_t addr = reinterpret_cast(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); - } -} diff --git a/test/StaticMemoryPool/allocString.cpp b/test/StaticMemoryPool/allocString.cpp new file mode 100644 index 00000000..eb033e09 --- /dev/null +++ b/test/StaticMemoryPool/allocString.cpp @@ -0,0 +1,131 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +using namespace ARDUINOJSON_NAMESPACE; + +TEST_CASE("StaticMemoryPool::allocFrozenString()") { + const size_t poolCapacity = 64; + const size_t longestString = poolCapacity - sizeof(StringSlot); + StaticMemoryPool 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 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); + } +} diff --git a/test/StaticMemoryPool/allocVariant.cpp b/test/StaticMemoryPool/allocVariant.cpp new file mode 100644 index 00000000..1c93f575 --- /dev/null +++ b/test/StaticMemoryPool/allocVariant.cpp @@ -0,0 +1,38 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +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())); + } +} diff --git a/test/StaticMemoryPool/clear.cpp b/test/StaticMemoryPool/clear.cpp new file mode 100644 index 00000000..cfa4b7aa --- /dev/null +++ b/test/StaticMemoryPool/clear.cpp @@ -0,0 +1,81 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +using namespace ARDUINOJSON_NAMESPACE; + +static const size_t poolCapacity = 512; + +TEST_CASE("StaticMemoryPool::clear()") { + StaticMemoryPool 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")); + } +} diff --git a/test/StaticMemoryPool/size.cpp b/test/StaticMemoryPool/size.cpp index 411d432b..771a09be 100644 --- a/test/StaticMemoryPool/size.cpp +++ b/test/StaticMemoryPool/size.cpp @@ -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 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 memoryPool; + + for (size_t i = 0; i < variantCount; i++) memoryPool.allocVariant(); + REQUIRE(capacity == memoryPool.size()); + + memoryPool.allocVariant(); + + REQUIRE(capacity == memoryPool.size()); } } diff --git a/test/StaticMemoryPool/startString.cpp b/test/StaticMemoryPool/startString.cpp deleted file mode 100644 index 6eca182e..00000000 --- a/test/StaticMemoryPool/startString.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#include -#include - -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()); - } -}