Pārlūkot izejas kodu

Fixed `copyArray()` not working with `MemberProxy` and `ElementProxy`

Benoit Blanchon 5 gadi atpakaļ
vecāks
revīzija
40085609e2
3 mainītis faili ar 213 papildinājumiem un 86 dzēšanām
  1. 1 1
      CHANGELOG.md
  2. 125 51
      extras/tests/JsonArray/copyArray.cpp
  3. 87 34
      src/ArduinoJson/Array/Utilities.hpp

+ 1 - 1
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)
 -------

+ 125 - 51
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);
+    CHECK(std::string("[[1,2,3],[4,5,6]]") == json);
+  }
 
-    serializeJson(array, json, sizeof(json));
-    REQUIRE(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<JsonArray>();
 
     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<JsonArray>();
 
     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<JsonArray>();
 
     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<JsonArray>();
 
     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]);
   }
 }

+ 87 - 34
src/ArduinoJson/Array/Utilities.hpp

@@ -10,8 +10,11 @@
 namespace ARDUINOJSON_NAMESPACE {
 
 // Copy a 1D array to a JsonArray
-template <typename T, size_t N>
-inline bool copyArray(T (&src)[N], ArrayRef dst) {
+template <typename T, size_t N, typename TDestination>
+inline typename enable_if<!is_array<T>::value &&
+                              !is_base_of<JsonDocument, TDestination>::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 <typename T>
-inline bool copyArray(T* src, size_t len, ArrayRef dst) {
+template <typename T, typename TDestination>
+inline typename enable_if<!is_array<T>::value &&
+                              !is_base_of<JsonDocument, TDestination>::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 <typename T, size_t N1, size_t N2>
-inline bool copyArray(T (&src)[N1][N2], ArrayRef dst) {
+template <typename T, size_t N1, size_t N2, typename TDestination>
+inline typename enable_if<!is_base_of<JsonDocument, TDestination>::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<ArrayRef>());
 }
 
+template <typename T>
+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<T>(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 <typename T, size_t N1, size_t N2>
+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<T> 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 <typename T, size_t N>
-inline size_t copyArray(ArrayConstRef src, T (&dst)[N]) {
+template <typename TSource, typename T, size_t N>
+inline typename enable_if<!is_array<T>::value, size_t>::type copyArray(
+    const TSource& src, T (&dst)[N]) {
   return copyArray(src, dst, N);
 }
 
-// Copy a JsonDocument to a 1D array
-template <typename T, size_t N>
-inline size_t copyArray(const JsonDocument& src, T (&dst)[N]) {
-  return copyArray(src.as<ArrayConstRef>(), dst, N);
-}
-
 // Copy a JsonArray to a 1D array
-template <typename T>
-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<T>();
-  return i;
+template <typename TSource, typename T>
+inline size_t copyArray(const TSource& src, T* dst, size_t len) {
+  ArrayCopier1D<T> copier(dst, len);
+  src.accept(copier);
+  return copier.result();
 }
 
 // Copy a JsonArray to a 2D array
-template <typename T, size_t N1, size_t N2>
-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<ArrayConstRef>(), dst[i++]);
-  }
-}
-
-// Copy a JsonDocument to a 2D array
-template <typename T, size_t N1, size_t N2>
-inline void copyArray(const JsonDocument& src, T (&dst)[N1][N2]) {
-  copyArray(src.as<ArrayConstRef>(), dst);
+template <typename TSource, typename T, size_t N1, size_t N2>
+inline void copyArray(const TSource& src, T (&dst)[N1][N2]) {
+  ArrayCopier2D<T, N1, N2> copier(&dst);
+  src.accept(copier);
 }
 
 }  // namespace ARDUINOJSON_NAMESPACE