diff --git a/extras/tests/JsonVariant/set.cpp b/extras/tests/JsonVariant/set.cpp index e47c2b4a..bf4c951e 100644 --- a/extras/tests/JsonVariant/set.cpp +++ b/extras/tests/JsonVariant/set.cpp @@ -13,7 +13,8 @@ using ArduinoJson::detail::sizeofObject; enum ErrorCode { ERROR_01 = 1, ERROR_10 = 10 }; TEST_CASE("JsonVariant::set() when there is enough memory") { - JsonDocument doc; + SpyingAllocator spy; + JsonDocument doc(&spy); JsonVariant variant = doc.to(); SECTION("const char*") { @@ -25,6 +26,7 @@ TEST_CASE("JsonVariant::set() when there is enough memory") { REQUIRE(result == true); REQUIRE(variant == "world"); // stores by pointer + REQUIRE(spy.log() == AllocatorLog{}); } SECTION("(const char*)0") { @@ -32,6 +34,7 @@ TEST_CASE("JsonVariant::set() when there is enough memory") { REQUIRE(result == true); REQUIRE(variant.isNull()); + REQUIRE(spy.log() == AllocatorLog{}); } SECTION("char*") { @@ -43,6 +46,9 @@ TEST_CASE("JsonVariant::set() when there is enough memory") { REQUIRE(result == true); REQUIRE(variant == "hello"); // stores by copy + REQUIRE(spy.log() == AllocatorLog{ + Allocate(sizeofString("hello")), + }); } SECTION("(char*)0") { @@ -50,6 +56,7 @@ TEST_CASE("JsonVariant::set() when there is enough memory") { REQUIRE(result == true); REQUIRE(variant.isNull()); + REQUIRE(spy.log() == AllocatorLog{}); } SECTION("unsigned char*") { @@ -61,6 +68,9 @@ TEST_CASE("JsonVariant::set() when there is enough memory") { REQUIRE(result == true); REQUIRE(variant == "hello"); // stores by copy + REQUIRE(spy.log() == AllocatorLog{ + Allocate(sizeofString("hello")), + }); } SECTION("signed char*") { @@ -72,6 +82,9 @@ TEST_CASE("JsonVariant::set() when there is enough memory") { REQUIRE(result == true); REQUIRE(variant == "hello"); // stores by copy + REQUIRE(spy.log() == AllocatorLog{ + Allocate(sizeofString("hello")), + }); } #ifdef HAS_VARIABLE_LENGTH_ARRAY @@ -85,6 +98,9 @@ TEST_CASE("JsonVariant::set() when there is enough memory") { REQUIRE(result == true); REQUIRE(variant == "hello"); // stores by copy + REQUIRE(spy.log() == AllocatorLog{ + Allocate(sizeofString("hello")), + }); } #endif @@ -97,6 +113,9 @@ TEST_CASE("JsonVariant::set() when there is enough memory") { REQUIRE(result == true); REQUIRE(variant == "hello"); // stores by copy + REQUIRE(spy.log() == AllocatorLog{ + Allocate(sizeofString("hello")), + }); } SECTION("static JsonString") { @@ -108,6 +127,7 @@ TEST_CASE("JsonVariant::set() when there is enough memory") { REQUIRE(result == true); REQUIRE(variant == "world"); // stores by pointer + REQUIRE(spy.log() == AllocatorLog{}); } SECTION("non-static JsonString") { @@ -119,6 +139,9 @@ TEST_CASE("JsonVariant::set() when there is enough memory") { REQUIRE(result == true); REQUIRE(variant == "hello"); // stores by copy + REQUIRE(spy.log() == AllocatorLog{ + Allocate(sizeofString("hello")), + }); } SECTION("enum") { @@ -129,6 +152,89 @@ TEST_CASE("JsonVariant::set() when there is enough memory") { REQUIRE(result == true); REQUIRE(variant.is() == true); REQUIRE(variant.as() == 10); + REQUIRE(spy.log() == AllocatorLog{}); + } + + SECTION("float") { + bool result = variant.set(1.2f); + + REQUIRE(result == true); + REQUIRE(variant.is() == true); + REQUIRE(variant.as() == 1.2f); + REQUIRE(spy.log() == AllocatorLog{}); + } + + SECTION("double") { + bool result = variant.set(1.2); + doc.shrinkToFit(); + + REQUIRE(result == true); + REQUIRE(variant.is() == true); + REQUIRE(variant.as() == 1.2); + REQUIRE(spy.log() == + AllocatorLog{ + Allocate(sizeofPool()), + Reallocate(sizeofPool(), sizeofPool(1)), // one extension slot + }); + } + + SECTION("int32_t") { + bool result = variant.set(int32_t(42)); + + REQUIRE(result == true); + REQUIRE(variant.is() == true); + REQUIRE(variant.as() == 42); + REQUIRE(spy.log() == AllocatorLog{}); + } + + SECTION("int64_t") { + bool result = variant.set(int64_t(-2147483649)); + doc.shrinkToFit(); + + REQUIRE(result == true); + REQUIRE(variant.is() == true); + REQUIRE(variant.as() == -2147483649); + REQUIRE(spy.log() == + AllocatorLog{ + Allocate(sizeofPool()), + Reallocate(sizeofPool(), sizeofPool(1)), // one extension slot + }); + } + + SECTION("uint32_t") { + bool result = variant.set(uint32_t(42)); + + REQUIRE(result == true); + REQUIRE(variant.is() == true); + REQUIRE(variant.as() == 42); + REQUIRE(spy.log() == AllocatorLog{}); + } + + SECTION("uint64_t") { + bool result = variant.set(uint64_t(4294967296)); + doc.shrinkToFit(); + + REQUIRE(result == true); + REQUIRE(variant.is() == true); + REQUIRE(variant.as() == 4294967296); + REQUIRE(spy.log() == + AllocatorLog{ + Allocate(sizeofPool()), + Reallocate(sizeofPool(), sizeofPool(1)), // one extension slot + }); + } + + SECTION("JsonDocument") { + JsonDocument doc1; + doc1["hello"] = "world"; + + // Should copy the doc + variant.set(doc1); + doc1.clear(); + + std::string json; + serializeJson(doc, json); + REQUIRE(json == "{\"hello\":\"world\"}"); } } @@ -158,22 +264,20 @@ TEST_CASE("JsonVariant::set() with not enough memory") { REQUIRE(result == false); REQUIRE(v.isNull()); } -} -TEST_CASE("JsonVariant::set(JsonDocument)") { - JsonDocument doc1; - doc1["hello"] = "world"; + SECTION("float") { + bool result = v.set(1.2f); - JsonDocument doc2; - JsonVariant v = doc2.to(); + REQUIRE(result == true); + REQUIRE(v.is()); + } - // Should copy the doc - v.set(doc1); - doc1.clear(); + SECTION("double") { + bool result = v.set(1.2); - std::string json; - serializeJson(doc2, json); - REQUIRE(json == "{\"hello\":\"world\"}"); + REQUIRE(result == false); + REQUIRE(v.isNull()); + } } TEST_CASE("JsonVariant::set() releases the previous value") { @@ -220,3 +324,30 @@ TEST_CASE("JsonVariant::set() releases the previous value") { }); } } + +TEST_CASE("Extension slots") { + SpyingAllocator spy; + JsonDocument doc(&spy); + + SECTION("double requires two slot") { + int i = ARDUINOJSON_POOL_CAPACITY - 1; + + // make sure the pool is full + doc[i] = 1; + REQUIRE(spy.log() == AllocatorLog{ + Allocate(sizeofPool()), + }); + + // replace with a float => no allocation + spy.clearLog(); + doc[i] = 1.2f; + REQUIRE(spy.log() == AllocatorLog{}); + + // replace with a double => new pool needed + spy.clearLog(); + doc[i] = 1.2; + REQUIRE(spy.log() == AllocatorLog{ + Allocate(sizeofPool()), + }); + } +} diff --git a/src/ArduinoJson/Variant/ConverterImpl.hpp b/src/ArduinoJson/Variant/ConverterImpl.hpp index a0f8e8fd..5d520acd 100644 --- a/src/ArduinoJson/Variant/ConverterImpl.hpp +++ b/src/ArduinoJson/Variant/ConverterImpl.hpp @@ -137,8 +137,7 @@ struct Converter::value>> return false; auto resources = getResourceManager(dst); data->clear(resources); - data->setFloat(src, resources); - return true; + return data->setFloat(src, resources); } static T fromJson(JsonVariantConst src) { diff --git a/src/ArduinoJson/Variant/VariantData.hpp b/src/ArduinoJson/Variant/VariantData.hpp index 752499be..af49745a 100644 --- a/src/ArduinoJson/Variant/VariantData.hpp +++ b/src/ArduinoJson/Variant/VariantData.hpp @@ -416,14 +416,15 @@ class VariantData { } template - enable_if_t setFloat(T value, ResourceManager*) { + enable_if_t setFloat(T value, ResourceManager*) { ARDUINOJSON_ASSERT(type_ == VALUE_IS_NULL); // must call clear() first type_ = VALUE_IS_FLOAT; content_.asFloat = value; + return true; } template - enable_if_t setFloat(T value, ResourceManager*); + enable_if_t setFloat(T value, ResourceManager*); template enable_if_t::value> setInteger(T value, diff --git a/src/ArduinoJson/Variant/VariantImpl.hpp b/src/ArduinoJson/Variant/VariantImpl.hpp index 0882862f..49dd32e1 100644 --- a/src/ArduinoJson/Variant/VariantImpl.hpp +++ b/src/ArduinoJson/Variant/VariantImpl.hpp @@ -65,8 +65,8 @@ inline const VariantExtension* VariantData::getExtension( #endif template -enable_if_t VariantData::setFloat(T value, - ResourceManager* resources) { +enable_if_t VariantData::setFloat( + T value, ResourceManager* resources) { ARDUINOJSON_ASSERT(type_ == VALUE_IS_NULL); // must call clear() first (void)resources; // silence warning @@ -78,16 +78,17 @@ enable_if_t VariantData::setFloat(T value, content_.asFloat = valueAsFloat; } else { auto extension = resources->allocExtension(); - if (extension) { - type_ = VALUE_IS_DOUBLE; - content_.asSlotId = extension.id(); - extension->asDouble = value; - } + if (!extension) + return false; + type_ = VALUE_IS_DOUBLE; + content_.asSlotId = extension.id(); + extension->asDouble = value; } #else type_ = VALUE_IS_FLOAT; content_.asFloat = valueAsFloat; #endif + return true; } template