This commit is contained in:
Benoit Blanchon
2024-08-27 14:40:24 +02:00
parent c0bebe35f1
commit 7643dadaec
4 changed files with 156 additions and 24 deletions

View File

@ -13,7 +13,8 @@ using ArduinoJson::detail::sizeofObject;
enum ErrorCode { ERROR_01 = 1, ERROR_10 = 10 }; enum ErrorCode { ERROR_01 = 1, ERROR_10 = 10 };
TEST_CASE("JsonVariant::set() when there is enough memory") { TEST_CASE("JsonVariant::set() when there is enough memory") {
JsonDocument doc; SpyingAllocator spy;
JsonDocument doc(&spy);
JsonVariant variant = doc.to<JsonVariant>(); JsonVariant variant = doc.to<JsonVariant>();
SECTION("const char*") { SECTION("const char*") {
@ -25,6 +26,7 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
REQUIRE(result == true); REQUIRE(result == true);
REQUIRE(variant == "world"); // stores by pointer REQUIRE(variant == "world"); // stores by pointer
REQUIRE(spy.log() == AllocatorLog{});
} }
SECTION("(const char*)0") { SECTION("(const char*)0") {
@ -32,6 +34,7 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
REQUIRE(result == true); REQUIRE(result == true);
REQUIRE(variant.isNull()); REQUIRE(variant.isNull());
REQUIRE(spy.log() == AllocatorLog{});
} }
SECTION("char*") { SECTION("char*") {
@ -43,6 +46,9 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
REQUIRE(result == true); REQUIRE(result == true);
REQUIRE(variant == "hello"); // stores by copy REQUIRE(variant == "hello"); // stores by copy
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("hello")),
});
} }
SECTION("(char*)0") { SECTION("(char*)0") {
@ -50,6 +56,7 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
REQUIRE(result == true); REQUIRE(result == true);
REQUIRE(variant.isNull()); REQUIRE(variant.isNull());
REQUIRE(spy.log() == AllocatorLog{});
} }
SECTION("unsigned char*") { SECTION("unsigned char*") {
@ -61,6 +68,9 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
REQUIRE(result == true); REQUIRE(result == true);
REQUIRE(variant == "hello"); // stores by copy REQUIRE(variant == "hello"); // stores by copy
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("hello")),
});
} }
SECTION("signed char*") { SECTION("signed char*") {
@ -72,6 +82,9 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
REQUIRE(result == true); REQUIRE(result == true);
REQUIRE(variant == "hello"); // stores by copy REQUIRE(variant == "hello"); // stores by copy
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("hello")),
});
} }
#ifdef HAS_VARIABLE_LENGTH_ARRAY #ifdef HAS_VARIABLE_LENGTH_ARRAY
@ -85,6 +98,9 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
REQUIRE(result == true); REQUIRE(result == true);
REQUIRE(variant == "hello"); // stores by copy REQUIRE(variant == "hello"); // stores by copy
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("hello")),
});
} }
#endif #endif
@ -97,6 +113,9 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
REQUIRE(result == true); REQUIRE(result == true);
REQUIRE(variant == "hello"); // stores by copy REQUIRE(variant == "hello"); // stores by copy
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("hello")),
});
} }
SECTION("static JsonString") { SECTION("static JsonString") {
@ -108,6 +127,7 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
REQUIRE(result == true); REQUIRE(result == true);
REQUIRE(variant == "world"); // stores by pointer REQUIRE(variant == "world"); // stores by pointer
REQUIRE(spy.log() == AllocatorLog{});
} }
SECTION("non-static JsonString") { SECTION("non-static JsonString") {
@ -119,6 +139,9 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
REQUIRE(result == true); REQUIRE(result == true);
REQUIRE(variant == "hello"); // stores by copy REQUIRE(variant == "hello"); // stores by copy
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("hello")),
});
} }
SECTION("enum") { SECTION("enum") {
@ -129,6 +152,89 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
REQUIRE(result == true); REQUIRE(result == true);
REQUIRE(variant.is<int>() == true); REQUIRE(variant.is<int>() == true);
REQUIRE(variant.as<int>() == 10); REQUIRE(variant.as<int>() == 10);
REQUIRE(spy.log() == AllocatorLog{});
}
SECTION("float") {
bool result = variant.set(1.2f);
REQUIRE(result == true);
REQUIRE(variant.is<float>() == true);
REQUIRE(variant.as<float>() == 1.2f);
REQUIRE(spy.log() == AllocatorLog{});
}
SECTION("double") {
bool result = variant.set(1.2);
doc.shrinkToFit();
REQUIRE(result == true);
REQUIRE(variant.is<double>() == true);
REQUIRE(variant.as<double>() == 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<int32_t>() == true);
REQUIRE(variant.as<int32_t>() == 42);
REQUIRE(spy.log() == AllocatorLog{});
}
SECTION("int64_t") {
bool result = variant.set(int64_t(-2147483649));
doc.shrinkToFit();
REQUIRE(result == true);
REQUIRE(variant.is<int64_t>() == true);
REQUIRE(variant.as<int64_t>() == -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<uint32_t>() == true);
REQUIRE(variant.as<uint32_t>() == 42);
REQUIRE(spy.log() == AllocatorLog{});
}
SECTION("uint64_t") {
bool result = variant.set(uint64_t(4294967296));
doc.shrinkToFit();
REQUIRE(result == true);
REQUIRE(variant.is<uint64_t>() == true);
REQUIRE(variant.as<uint64_t>() == 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(result == false);
REQUIRE(v.isNull()); REQUIRE(v.isNull());
} }
}
TEST_CASE("JsonVariant::set(JsonDocument)") { SECTION("float") {
JsonDocument doc1; bool result = v.set(1.2f);
doc1["hello"] = "world";
JsonDocument doc2; REQUIRE(result == true);
JsonVariant v = doc2.to<JsonVariant>(); REQUIRE(v.is<float>());
}
// Should copy the doc SECTION("double") {
v.set(doc1); bool result = v.set(1.2);
doc1.clear();
std::string json; REQUIRE(result == false);
serializeJson(doc2, json); REQUIRE(v.isNull());
REQUIRE(json == "{\"hello\":\"world\"}"); }
} }
TEST_CASE("JsonVariant::set() releases the previous value") { 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()),
});
}
}

View File

@ -137,8 +137,7 @@ struct Converter<T, detail::enable_if_t<detail::is_floating_point<T>::value>>
return false; return false;
auto resources = getResourceManager(dst); auto resources = getResourceManager(dst);
data->clear(resources); data->clear(resources);
data->setFloat(src, resources); return data->setFloat(src, resources);
return true;
} }
static T fromJson(JsonVariantConst src) { static T fromJson(JsonVariantConst src) {

View File

@ -416,14 +416,15 @@ class VariantData {
} }
template <typename T> template <typename T>
enable_if_t<sizeof(T) == 4> setFloat(T value, ResourceManager*) { enable_if_t<sizeof(T) == 4, bool> setFloat(T value, ResourceManager*) {
ARDUINOJSON_ASSERT(type_ == VALUE_IS_NULL); // must call clear() first ARDUINOJSON_ASSERT(type_ == VALUE_IS_NULL); // must call clear() first
type_ = VALUE_IS_FLOAT; type_ = VALUE_IS_FLOAT;
content_.asFloat = value; content_.asFloat = value;
return true;
} }
template <typename T> template <typename T>
enable_if_t<sizeof(T) == 8> setFloat(T value, ResourceManager*); enable_if_t<sizeof(T) == 8, bool> setFloat(T value, ResourceManager*);
template <typename T> template <typename T>
enable_if_t<is_signed<T>::value> setInteger(T value, enable_if_t<is_signed<T>::value> setInteger(T value,

View File

@ -65,8 +65,8 @@ inline const VariantExtension* VariantData::getExtension(
#endif #endif
template <typename T> template <typename T>
enable_if_t<sizeof(T) == 8> VariantData::setFloat(T value, enable_if_t<sizeof(T) == 8, bool> VariantData::setFloat(
ResourceManager* resources) { T value, ResourceManager* resources) {
ARDUINOJSON_ASSERT(type_ == VALUE_IS_NULL); // must call clear() first ARDUINOJSON_ASSERT(type_ == VALUE_IS_NULL); // must call clear() first
(void)resources; // silence warning (void)resources; // silence warning
@ -78,16 +78,17 @@ enable_if_t<sizeof(T) == 8> VariantData::setFloat(T value,
content_.asFloat = valueAsFloat; content_.asFloat = valueAsFloat;
} else { } else {
auto extension = resources->allocExtension(); auto extension = resources->allocExtension();
if (extension) { if (!extension)
type_ = VALUE_IS_DOUBLE; return false;
content_.asSlotId = extension.id(); type_ = VALUE_IS_DOUBLE;
extension->asDouble = value; content_.asSlotId = extension.id();
} extension->asDouble = value;
} }
#else #else
type_ = VALUE_IS_FLOAT; type_ = VALUE_IS_FLOAT;
content_.asFloat = valueAsFloat; content_.asFloat = valueAsFloat;
#endif #endif
return true;
} }
template <typename T> template <typename T>