Просмотр исходного кода

Added fallbacks strlen_P, strncmp_P, strcmp_P and memcpy_P (fixes #1073)

Benoit Blanchon 6 лет назад
Родитель
Сommit
f00dfd7bfe

+ 5 - 0
CHANGELOG.md

@@ -1,6 +1,11 @@
 ArduinoJson: change log
 =======================
 
+HEAD
+----
+
+* Added fallback implementations of `strlen_P()`, `strncmp_P()`, `strcmp_P()`, and `memcpy_P()` (issue #1073)
+
 v6.11.4 (2019-08-12)
 -------
 

+ 2 - 2
src/ArduinoJson/Deserialization/FlashStringReader.hpp

@@ -15,7 +15,7 @@ class UnsafeFlashStringReader {
       : _ptr(reinterpret_cast<const char*>(ptr)) {}
 
   int read() {
-    return pgm_read_byte_near(_ptr++);
+    return pgm_read_byte(_ptr++);
   }
 };
 
@@ -29,7 +29,7 @@ class SafeFlashStringReader {
 
   int read() {
     if (_ptr < _end)
-      return pgm_read_byte_near(_ptr++);
+      return pgm_read_byte(_ptr++);
     else
       return -1;
   }

+ 62 - 0
src/ArduinoJson/Polyfills/pgmspace.hpp

@@ -0,0 +1,62 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2019
+// MIT License
+
+#pragma once
+
+namespace ARDUINOJSON_NAMESPACE {
+// Wraps a const char* so that the our functions are picked only if the
+// originals are missing
+struct pgm_p {
+  pgm_p(const char* p) : address(p) {}
+  const char* address;
+};
+}  // namespace ARDUINOJSON_NAMESPACE
+
+#ifndef strlen_P
+inline size_t strlen_P(ARDUINOJSON_NAMESPACE::pgm_p s) {
+  const char* p = s.address;
+  while (pgm_read_byte(p)) p++;
+  return size_t(p - s.address);
+}
+#endif
+
+#ifndef strncmp_P
+inline int strncmp_P(const char* a, ARDUINOJSON_NAMESPACE::pgm_p b, size_t n) {
+  const char* s1 = a;
+  const char* s2 = b.address;
+  while (n-- > 0) {
+    char c1 = *s1++;
+    char c2 = static_cast<char>(pgm_read_byte(s2++));
+    if (c1 < c2) return -1;
+    if (c1 > c2) return 1;
+    if (c1 == 0 /* and c2 as well */) return 0;
+  }
+  return 0;
+}
+#endif
+
+#ifndef strcmp_P
+inline int strcmp_P(const char* a, ARDUINOJSON_NAMESPACE::pgm_p b) {
+  const char* s1 = a;
+  const char* s2 = b.address;
+  for (;;) {
+    char c1 = *s1++;
+    char c2 = static_cast<char>(pgm_read_byte(s2++));
+    if (c1 < c2) return -1;
+    if (c1 > c2) return 1;
+    if (c1 == 0 /* and c2 as well */) return 0;
+  }
+}
+#endif
+
+#ifndef memcpy_P
+inline void* memcpy_P(void* dst, ARDUINOJSON_NAMESPACE::pgm_p src, size_t n) {
+  uint8_t* d = reinterpret_cast<uint8_t*>(dst);
+  const char* s = src.address;
+  while (n-- > 0) {
+    *d++ = pgm_read_byte(s++);
+  }
+  return dst;
+}
+#endif

+ 2 - 0
src/ArduinoJson/Strings/FlashStringAdapter.hpp

@@ -4,6 +4,8 @@
 
 #pragma once
 
+#include "../Polyfills/pgmspace.hpp"
+
 namespace ARDUINOJSON_NAMESPACE {
 
 class FlashStringAdapter {

+ 34 - 0
test/MixedConfiguration/enable_progmem_1.cpp

@@ -51,3 +51,37 @@ TEST_CASE("Flash strings") {
     REQUIRE(doc[0] == F("world"));
   }
 }
+
+TEST_CASE("strlen_P") {
+  CHECK(strlen_P(FC("")) == 0);
+  CHECK(strlen_P(FC("a")) == 1);
+  CHECK(strlen_P(FC("ac")) == 2);
+}
+
+TEST_CASE("strncmp_P") {
+  CHECK(strncmp_P("a", FC("b"), 0) == 0);
+  CHECK(strncmp_P("a", FC("b"), 1) == -1);
+  CHECK(strncmp_P("b", FC("a"), 1) == 1);
+  CHECK(strncmp_P("a", FC("a"), 0) == 0);
+  CHECK(strncmp_P("a", FC("b"), 2) == -1);
+  CHECK(strncmp_P("b", FC("a"), 2) == 1);
+  CHECK(strncmp_P("a", FC("a"), 2) == 0);
+}
+
+TEST_CASE("strcmp_P") {
+  CHECK(strcmp_P("a", FC("b")) == -1);
+  CHECK(strcmp_P("b", FC("a")) == 1);
+  CHECK(strcmp_P("a", FC("a")) == 0);
+  CHECK(strcmp_P("aa", FC("ab")) == -1);
+  CHECK(strcmp_P("ab", FC("aa")) == 1);
+  CHECK(strcmp_P("aa", FC("aa")) == 0);
+}
+
+TEST_CASE("memcpy_P") {
+  char dst[4];
+  CHECK(memcpy_P(dst, FC("ABC"), 4) == dst);
+  CHECK(dst[0] == 'A');
+  CHECK(dst[1] == 'B');
+  CHECK(dst[2] == 'C');
+  CHECK(dst[3] == 0);
+}

+ 2 - 20
test/MixedConfiguration/progmem_emulation.hpp

@@ -7,9 +7,6 @@
 
 class __FlashStringHelper;
 
-typedef char prog_char;
-typedef void prog_void;
-
 inline const void* convertPtrToFlash(const void* s) {
   return reinterpret_cast<const char*>(s) + 42;
 }
@@ -19,23 +16,8 @@ inline const void* convertFlashToPtr(const void* s) {
 }
 
 #define F(X) reinterpret_cast<const __FlashStringHelper*>(convertPtrToFlash(X))
+#define FC(X) reinterpret_cast<const char*>(convertPtrToFlash(X))
 
-inline uint8_t pgm_read_byte_near(const void* p) {
+inline uint8_t pgm_read_byte(const void* p) {
   return *reinterpret_cast<const uint8_t*>(convertFlashToPtr(p));
 }
-
-inline void* memcpy_P(void* a, const prog_void* b, size_t n) {
-  return memcpy(a, convertFlashToPtr(b), n);
-}
-
-inline int strcmp_P(const char* a, const prog_char* b) {
-  return strcmp(a, reinterpret_cast<const char*>(convertFlashToPtr(b)));
-}
-
-inline int strncmp_P(const char* a, const prog_char* b, size_t n) {
-  return strncmp(a, reinterpret_cast<const char*>(convertFlashToPtr(b)), n);
-}
-
-inline size_t strlen_P(const prog_char* s) {
-  return strlen(reinterpret_cast<const char*>(convertFlashToPtr(s)));
-}