From 40085609e2ee801dd498e386cfbb4356e159d99f Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Wed, 22 Jul 2020 20:38:09 +0200 Subject: [PATCH] Fixed `copyArray()` not working with `MemberProxy` and `ElementProxy` --- CHANGELOG.md | 2 +- extras/tests/JsonArray/copyArray.cpp | 176 +++++++++++++++++++-------- src/ArduinoJson/Array/Utilities.hpp | 121 ++++++++++++------ 3 files changed, 213 insertions(+), 86 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e948191b..28e8709d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ HEAD * Added comparisons (`>`, `>=`, `==`, `!=`, `<`, and `<=`) between `JsonVariant`s * Added string deduplication (issue #1303) -* Fixed `copyArray()` not working with `String` +* Fixed `copyArray()` not working with `String`, `ElementProxy`, and `MemberProxy` v6.15.2 (2020-05-15) ------- diff --git a/extras/tests/JsonArray/copyArray.cpp b/extras/tests/JsonArray/copyArray.cpp index a85647e9..ac655dd5 100644 --- a/extras/tests/JsonArray/copyArray.cpp +++ b/extras/tests/JsonArray/copyArray.cpp @@ -13,10 +13,10 @@ TEST_CASE("copyArray()") { int source[] = {1, 2, 3}; bool ok = copyArray(source, array); - REQUIRE(ok); + CHECK(ok); - serializeJson(array, json, sizeof(json)); - REQUIRE(std::string("[1,2,3]") == json); + serializeJson(array, json); + CHECK(std::string("[1,2,3]") == json); } SECTION("std::string[] -> JsonArray") { @@ -26,10 +26,10 @@ TEST_CASE("copyArray()") { std::string source[] = {"a", "b", "c"}; bool ok = copyArray(source, array); - REQUIRE(ok); + CHECK(ok); - serializeJson(array, json, sizeof(json)); - REQUIRE(std::string("[\"a\",\"b\",\"c\"]") == json); + serializeJson(array, json); + CHECK(std::string("[\"a\",\"b\",\"c\"]") == json); } SECTION("int[] -> JsonDocument") { @@ -38,10 +38,22 @@ TEST_CASE("copyArray()") { int source[] = {1, 2, 3}; bool ok = copyArray(source, doc); - REQUIRE(ok); + CHECK(ok); - serializeJson(doc, json, sizeof(json)); - REQUIRE(std::string("[1,2,3]") == json); + serializeJson(doc, json); + CHECK(std::string("[1,2,3]") == json); + } + + SECTION("int[] -> MemberProxy") { + DynamicJsonDocument doc(4096); + char json[32]; + int source[] = {1, 2, 3}; + + bool ok = copyArray(source, doc["data"]); + CHECK(ok); + + serializeJson(doc, json); + CHECK(std::string("{\"data\":[1,2,3]}") == json); } SECTION("int[] -> JsonArray, but not enough memory") { @@ -54,8 +66,8 @@ TEST_CASE("copyArray()") { bool ok = copyArray(source, array); REQUIRE_FALSE(ok); - serializeJson(array, json, sizeof(json)); - REQUIRE(std::string("[1,2]") == json); + serializeJson(array, json); + CHECK(std::string("[1,2]") == json); } SECTION("int[][] -> JsonArray") { @@ -65,10 +77,22 @@ TEST_CASE("copyArray()") { int source[][3] = {{1, 2, 3}, {4, 5, 6}}; bool ok = copyArray(source, array); - REQUIRE(ok); + CHECK(ok); - serializeJson(array, json, sizeof(json)); - REQUIRE(std::string("[[1,2,3],[4,5,6]]") == json); + serializeJson(array, json); + CHECK(std::string("[[1,2,3],[4,5,6]]") == json); + } + + SECTION("int[][] -> MemberProxy") { + DynamicJsonDocument doc(4096); + char json[32]; + int source[][3] = {{1, 2, 3}, {4, 5, 6}}; + + bool ok = copyArray(source, doc["data"]); + CHECK(ok); + + serializeJson(doc, json); + CHECK(std::string("{\"data\":[[1,2,3],[4,5,6]]}") == json); } SECTION("int[][] -> JsonDocument") { @@ -77,10 +101,10 @@ TEST_CASE("copyArray()") { int source[][3] = {{1, 2, 3}, {4, 5, 6}}; bool ok = copyArray(source, doc); - REQUIRE(ok); + CHECK(ok); - serializeJson(doc, json, sizeof(json)); - REQUIRE(std::string("[[1,2,3],[4,5,6]]") == json); + serializeJson(doc, json); + CHECK(std::string("[[1,2,3],[4,5,6]]") == json); } SECTION("int[][] -> JsonArray, but not enough memory") { @@ -97,53 +121,53 @@ TEST_CASE("copyArray()") { CAPTURE(doc.memoryUsage()); CHECK_FALSE(ok); - serializeJson(array, json, sizeof(json)); - REQUIRE(std::string("[[1,2,3],[4,5]]") == json); + serializeJson(array, json); + CHECK(std::string("[[1,2,3],[4,5]]") == json); } SECTION("JsonArray -> int[], with more space than needed") { DynamicJsonDocument doc(4096); char json[] = "[1,2,3]"; DeserializationError err = deserializeJson(doc, json); - REQUIRE(err == DeserializationError::Ok); + CHECK(err == DeserializationError::Ok); JsonArray array = doc.as(); int destination[4] = {0}; size_t result = copyArray(array, destination); - REQUIRE(3 == result); - REQUIRE(1 == destination[0]); - REQUIRE(2 == destination[1]); - REQUIRE(3 == destination[2]); - REQUIRE(0 == destination[3]); + CHECK(3 == result); + CHECK(1 == destination[0]); + CHECK(2 == destination[1]); + CHECK(3 == destination[2]); + CHECK(0 == destination[3]); } SECTION("JsonArray -> int[], without enough space") { DynamicJsonDocument doc(4096); char json[] = "[1,2,3]"; DeserializationError err = deserializeJson(doc, json); - REQUIRE(err == DeserializationError::Ok); + CHECK(err == DeserializationError::Ok); JsonArray array = doc.as(); int destination[2] = {0}; size_t result = copyArray(array, destination); - REQUIRE(2 == result); - REQUIRE(1 == destination[0]); - REQUIRE(2 == destination[1]); + CHECK(2 == result); + CHECK(1 == destination[0]); + CHECK(2 == destination[1]); } SECTION("JsonArray -> std::string[]") { DynamicJsonDocument doc(4096); char json[] = "[\"a\",\"b\",\"c\"]"; DeserializationError err = deserializeJson(doc, json); - REQUIRE(err == DeserializationError::Ok); + CHECK(err == DeserializationError::Ok); JsonArray array = doc.as(); std::string destination[4]; size_t result = copyArray(array, destination); - REQUIRE(3 == result); + CHECK(3 == result); CHECK("a" == destination[0]); CHECK("b" == destination[1]); CHECK("c" == destination[2]); @@ -154,16 +178,48 @@ TEST_CASE("copyArray()") { DynamicJsonDocument doc(4096); char json[] = "[1,2,3]"; DeserializationError err = deserializeJson(doc, json); - REQUIRE(err == DeserializationError::Ok); + CHECK(err == DeserializationError::Ok); int destination[4] = {0}; size_t result = copyArray(doc, destination); - REQUIRE(3 == result); - REQUIRE(1 == destination[0]); - REQUIRE(2 == destination[1]); - REQUIRE(3 == destination[2]); - REQUIRE(0 == destination[3]); + CHECK(3 == result); + CHECK(1 == destination[0]); + CHECK(2 == destination[1]); + CHECK(3 == destination[2]); + CHECK(0 == destination[3]); + } + + SECTION("MemberProxy -> int[]") { + DynamicJsonDocument doc(4096); + char json[] = "{\"data\":[1,2,3]}"; + DeserializationError err = deserializeJson(doc, json); + CHECK(err == DeserializationError::Ok); + + int destination[4] = {0}; + size_t result = copyArray(doc["data"], destination); + + CHECK(3 == result); + CHECK(1 == destination[0]); + CHECK(2 == destination[1]); + CHECK(3 == destination[2]); + CHECK(0 == destination[3]); + } + + SECTION("ElementProxy -> int[]") { + DynamicJsonDocument doc(4096); + char json[] = "[[1,2,3]]"; + DeserializationError err = deserializeJson(doc, json); + CHECK(err == DeserializationError::Ok); + + int destination[4] = {0}; + size_t result = copyArray(doc[0], destination); + + CHECK(3 == result); + CHECK(1 == destination[0]); + CHECK(2 == destination[1]); + CHECK(3 == destination[2]); + CHECK(0 == destination[3]); } SECTION("JsonArray -> int[][]") { @@ -171,18 +227,18 @@ TEST_CASE("copyArray()") { char json[] = "[[1,2],[3],[4]]"; DeserializationError err = deserializeJson(doc, json); - REQUIRE(err == DeserializationError::Ok); + CHECK(err == DeserializationError::Ok); JsonArray array = doc.as(); int destination[3][2] = {{0}}; copyArray(array, destination); - REQUIRE(1 == destination[0][0]); - REQUIRE(2 == destination[0][1]); - REQUIRE(3 == destination[1][0]); - REQUIRE(0 == destination[1][1]); - REQUIRE(4 == destination[2][0]); - REQUIRE(0 == destination[2][1]); + CHECK(1 == destination[0][0]); + CHECK(2 == destination[0][1]); + CHECK(3 == destination[1][0]); + CHECK(0 == destination[1][1]); + CHECK(4 == destination[2][0]); + CHECK(0 == destination[2][1]); } SECTION("JsonDocument -> int[][]") { @@ -190,16 +246,34 @@ TEST_CASE("copyArray()") { char json[] = "[[1,2],[3],[4]]"; DeserializationError err = deserializeJson(doc, json); - REQUIRE(err == DeserializationError::Ok); + CHECK(err == DeserializationError::Ok); int destination[3][2] = {{0}}; copyArray(doc, destination); - REQUIRE(1 == destination[0][0]); - REQUIRE(2 == destination[0][1]); - REQUIRE(3 == destination[1][0]); - REQUIRE(0 == destination[1][1]); - REQUIRE(4 == destination[2][0]); - REQUIRE(0 == destination[2][1]); + CHECK(1 == destination[0][0]); + CHECK(2 == destination[0][1]); + CHECK(3 == destination[1][0]); + CHECK(0 == destination[1][1]); + CHECK(4 == destination[2][0]); + CHECK(0 == destination[2][1]); + } + + SECTION("MemberProxy -> int[][]") { + DynamicJsonDocument doc(4096); + char json[] = "{\"data\":[[1,2],[3],[4]]}"; + + DeserializationError err = deserializeJson(doc, json); + CHECK(err == DeserializationError::Ok); + + int destination[3][2] = {{0}}; + copyArray(doc["data"], destination); + + CHECK(1 == destination[0][0]); + CHECK(2 == destination[0][1]); + CHECK(3 == destination[1][0]); + CHECK(0 == destination[1][1]); + CHECK(4 == destination[2][0]); + CHECK(0 == destination[2][1]); } } diff --git a/src/ArduinoJson/Array/Utilities.hpp b/src/ArduinoJson/Array/Utilities.hpp index 0a3477a7..182f89bd 100644 --- a/src/ArduinoJson/Array/Utilities.hpp +++ b/src/ArduinoJson/Array/Utilities.hpp @@ -10,8 +10,11 @@ namespace ARDUINOJSON_NAMESPACE { // Copy a 1D array to a JsonArray -template -inline bool copyArray(T (&src)[N], ArrayRef dst) { +template +inline typename enable_if::value && + !is_base_of::value, + bool>::type +copyArray(T (&src)[N], const TDestination& dst) { return copyArray(src, N, dst); } @@ -22,8 +25,11 @@ inline bool copyArray(T (&src)[N], JsonDocument& dst) { } // Copy a 1D array to a JsonArray -template -inline bool copyArray(T* src, size_t len, ArrayRef dst) { +template +inline typename enable_if::value && + !is_base_of::value, + bool>::type +copyArray(T* src, size_t len, const TDestination& dst) { bool ok = true; for (size_t i = 0; i < len; i++) { ok &= dst.add(src[i]); @@ -38,8 +44,10 @@ inline bool copyArray(T* src, size_t len, JsonDocument& dst) { } // Copy a 2D array to a JsonArray -template -inline bool copyArray(T (&src)[N1][N2], ArrayRef dst) { +template +inline typename enable_if::value, + bool>::type +copyArray(T (&src)[N1][N2], const TDestination& dst) { bool ok = true; for (size_t i = 0; i < N1; i++) { ArrayRef nestedArray = dst.createNestedArray(); @@ -56,42 +64,87 @@ inline bool copyArray(T (&src)[N1][N2], JsonDocument& dst) { return copyArray(src, dst.to()); } +template +class ArrayCopier1D { + public: + ArrayCopier1D(T* destination, size_t capacity) + : _destination(destination), _capacity(capacity), _size(0) {} + + void visitArray(const CollectionData& array) { + VariantSlot* slot = array.head(); + + while (slot != 0 && _size < _capacity) { + _destination[_size++] = variantAs(slot->data()); + slot = slot->next(); + } + } + void visitObject(const CollectionData&) {} + void visitFloat(Float) {} + void visitString(const char*) {} + void visitRawJson(const char*, size_t) {} + void visitNegativeInteger(UInt) {} + void visitPositiveInteger(UInt) {} + void visitBoolean(bool) {} + void visitNull() {} + + size_t result() const { + return _size; + } + + private: + T* _destination; + size_t _capacity; + size_t _size; +}; + +template +class ArrayCopier2D { + public: + ArrayCopier2D(T (*destination)[N1][N2]) : _destination(destination) {} + + void visitArray(const CollectionData& array) { + VariantSlot* slot = array.head(); + size_t n = 0; + while (slot != 0 && n < N1) { + ArrayCopier1D copier((*_destination)[n++], N2); + variantAccept(slot->data(), copier); + slot = slot->next(); + } + } + void visitObject(const CollectionData&) {} + void visitFloat(Float) {} + void visitString(const char*) {} + void visitRawJson(const char*, size_t) {} + void visitNegativeInteger(UInt) {} + void visitPositiveInteger(UInt) {} + void visitBoolean(bool) {} + void visitNull() {} + + private: + T (*_destination)[N1][N2]; + size_t _capacity1, _capacity2; +}; + // Copy a JsonArray to a 1D array -template -inline size_t copyArray(ArrayConstRef src, T (&dst)[N]) { +template +inline typename enable_if::value, size_t>::type copyArray( + const TSource& src, T (&dst)[N]) { return copyArray(src, dst, N); } -// Copy a JsonDocument to a 1D array -template -inline size_t copyArray(const JsonDocument& src, T (&dst)[N]) { - return copyArray(src.as(), dst, N); -} - // Copy a JsonArray to a 1D array -template -inline size_t copyArray(ArrayConstRef src, T* dst, size_t len) { - size_t i = 0; - for (ArrayConstRef::iterator it = src.begin(); it != src.end() && i < len; - ++it) - dst[i++] = it->as(); - return i; +template +inline size_t copyArray(const TSource& src, T* dst, size_t len) { + ArrayCopier1D copier(dst, len); + src.accept(copier); + return copier.result(); } // Copy a JsonArray to a 2D array -template -inline void copyArray(ArrayConstRef src, T (&dst)[N1][N2]) { - size_t i = 0; - for (ArrayConstRef::iterator it = src.begin(); it != src.end() && i < N1; - ++it) { - copyArray(it->as(), dst[i++]); - } -} - -// Copy a JsonDocument to a 2D array -template -inline void copyArray(const JsonDocument& src, T (&dst)[N1][N2]) { - copyArray(src.as(), dst); +template +inline void copyArray(const TSource& src, T (&dst)[N1][N2]) { + ArrayCopier2D copier(&dst); + src.accept(copier); } } // namespace ARDUINOJSON_NAMESPACE