From f00dfd7bfe9f5b8e223148063bba46650e09d35e Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Wed, 21 Aug 2019 09:31:53 +0200 Subject: [PATCH] Added fallbacks strlen_P, strncmp_P, strcmp_P and memcpy_P (fixes #1073) --- CHANGELOG.md | 5 ++ .../Deserialization/FlashStringReader.hpp | 4 +- src/ArduinoJson/Polyfills/pgmspace.hpp | 62 +++++++++++++++++++ .../Strings/FlashStringAdapter.hpp | 2 + test/MixedConfiguration/enable_progmem_1.cpp | 34 ++++++++++ test/MixedConfiguration/progmem_emulation.hpp | 22 +------ 6 files changed, 107 insertions(+), 22 deletions(-) create mode 100644 src/ArduinoJson/Polyfills/pgmspace.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 8202be63..4f526859 100644 --- a/CHANGELOG.md +++ b/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) ------- diff --git a/src/ArduinoJson/Deserialization/FlashStringReader.hpp b/src/ArduinoJson/Deserialization/FlashStringReader.hpp index e8066ef6..051d06ec 100644 --- a/src/ArduinoJson/Deserialization/FlashStringReader.hpp +++ b/src/ArduinoJson/Deserialization/FlashStringReader.hpp @@ -15,7 +15,7 @@ class UnsafeFlashStringReader { : _ptr(reinterpret_cast(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; } diff --git a/src/ArduinoJson/Polyfills/pgmspace.hpp b/src/ArduinoJson/Polyfills/pgmspace.hpp new file mode 100644 index 00000000..4dea1a80 --- /dev/null +++ b/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(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(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(dst); + const char* s = src.address; + while (n-- > 0) { + *d++ = pgm_read_byte(s++); + } + return dst; +} +#endif diff --git a/src/ArduinoJson/Strings/FlashStringAdapter.hpp b/src/ArduinoJson/Strings/FlashStringAdapter.hpp index d1b094ad..6747cfa8 100644 --- a/src/ArduinoJson/Strings/FlashStringAdapter.hpp +++ b/src/ArduinoJson/Strings/FlashStringAdapter.hpp @@ -4,6 +4,8 @@ #pragma once +#include "../Polyfills/pgmspace.hpp" + namespace ARDUINOJSON_NAMESPACE { class FlashStringAdapter { diff --git a/test/MixedConfiguration/enable_progmem_1.cpp b/test/MixedConfiguration/enable_progmem_1.cpp index 187ec30a..a228d746 100644 --- a/test/MixedConfiguration/enable_progmem_1.cpp +++ b/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); +} diff --git a/test/MixedConfiguration/progmem_emulation.hpp b/test/MixedConfiguration/progmem_emulation.hpp index b0bc8b50..5b9e507f 100644 --- a/test/MixedConfiguration/progmem_emulation.hpp +++ b/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(s) + 42; } @@ -19,23 +16,8 @@ inline const void* convertFlashToPtr(const void* s) { } #define F(X) reinterpret_cast(convertPtrToFlash(X)) +#define FC(X) reinterpret_cast(convertPtrToFlash(X)) -inline uint8_t pgm_read_byte_near(const void* p) { +inline uint8_t pgm_read_byte(const void* p) { return *reinterpret_cast(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(convertFlashToPtr(b))); -} - -inline int strncmp_P(const char* a, const prog_char* b, size_t n) { - return strncmp(a, reinterpret_cast(convertFlashToPtr(b)), n); -} - -inline size_t strlen_P(const prog_char* s) { - return strlen(reinterpret_cast(convertFlashToPtr(s))); -}