serializeMsgPack(doc, p, n) doesn't add terminator anymore (fixes #1545)

This commit is contained in:
Benoit Blanchon
2021-04-29 20:59:15 +02:00
parent 337864618c
commit 2be528a3fa
11 changed files with 47 additions and 20 deletions

View File

@ -16,6 +16,8 @@ HEAD
* Simplified `JsonVariant::as<T>()` to always return `T` (see below) * Simplified `JsonVariant::as<T>()` to always return `T` (see below)
* Updated folders list in `.mbedignore` (PR #1515 by @AGlass0fMilk) * Updated folders list in `.mbedignore` (PR #1515 by @AGlass0fMilk)
* Fixed member-call-on-null-pointer in `getMember()` when array is empty * Fixed member-call-on-null-pointer in `getMember()` when array is empty
* `serializeMsgPack(doc, buffer, size)` doesn't add null-terminator anymore (issue #1545)
* `serializeJson(doc, buffer, size)` adds null-terminator only if there is enough room
> ### BREAKING CHANGES > ### BREAKING CHANGES
> >

View File

@ -8,12 +8,15 @@
static void checkObject(const JsonObject obj, const std::string &expected) { static void checkObject(const JsonObject obj, const std::string &expected) {
char actual[256]; char actual[256];
memset(actual, '!', sizeof(actual));
size_t actualLen = serializeJson(obj, actual); size_t actualLen = serializeJson(obj, actual);
size_t measuredLen = measureJson(obj); size_t measuredLen = measureJson(obj);
REQUIRE(expected == actual);
REQUIRE(expected.size() == actualLen);
REQUIRE(expected.size() == measuredLen); REQUIRE(expected.size() == measuredLen);
REQUIRE(expected.size() == actualLen);
REQUIRE(actual[actualLen] == 0); // serializeJson() adds a null terminator
REQUIRE(expected == actual);
} }
TEST_CASE("serializeJson(JsonObject)") { TEST_CASE("serializeJson(JsonObject)") {

View File

@ -39,15 +39,15 @@ void common_tests(StringWriter& sb, const String& output) {
} }
TEST_CASE("StaticStringWriter") { TEST_CASE("StaticStringWriter") {
char output[20]; char output[20] = {0};
StaticStringWriter sb(output, sizeof(output)); StaticStringWriter sb(output, sizeof(output));
common_tests(sb, static_cast<const char*>(output)); common_tests(sb, static_cast<const char*>(output));
SECTION("OverCapacity") { SECTION("OverCapacity") {
REQUIRE(19 == print(sb, "ABCDEFGHIJKLMNOPQRSTUVWXYZ")); REQUIRE(20 == print(sb, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
REQUIRE(0 == print(sb, "ABC")); REQUIRE(0 == print(sb, "ABC"));
REQUIRE(std::string("ABCDEFGHIJKLMNOPQRS") == output); REQUIRE("ABCDEFGHIJKLMNOPQRST" == std::string(output, 20));
} }
} }

View File

@ -29,19 +29,31 @@ TEST_CASE("serialize MsgPack to various destination types") {
REQUIRE(expected_length == len); REQUIRE(expected_length == len);
} */ } */
SECTION("char[]") { SECTION("char[] larger than needed") {
char result[64]; char result[64];
memset(result, 42, sizeof(result));
size_t len = serializeMsgPack(object, result); size_t len = serializeMsgPack(object, result);
REQUIRE(std::string(expected_result) == result);
REQUIRE(expected_length == len); REQUIRE(expected_length == len);
REQUIRE(std::string(expected_result, len) == std::string(result, len));
REQUIRE(result[len] == 42);
}
SECTION("char[] of the right size") { // #1545
char result[13];
size_t len = serializeMsgPack(object, result);
REQUIRE(expected_length == len);
REQUIRE(std::string(expected_result, len) == std::string(result, len));
} }
SECTION("char*") { SECTION("char*") {
char result[64]; char result[64];
memset(result, 42, sizeof(result));
size_t len = serializeMsgPack(object, result, 64); size_t len = serializeMsgPack(object, result, 64);
REQUIRE(std::string(expected_result) == result);
REQUIRE(expected_length == len); REQUIRE(expected_length == len);
REQUIRE(std::string(expected_result, len) == std::string(result, len));
REQUIRE(result[len] == 42);
} }
} }

View File

@ -13,7 +13,7 @@ using namespace ARDUINOJSON_NAMESPACE;
template <typename T> template <typename T>
void checkWriteInteger(T value, std::string expected) { void checkWriteInteger(T value, std::string expected) {
char output[1024]; char output[64] = {0};
StaticStringWriter sb(output, sizeof(output)); StaticStringWriter sb(output, sizeof(output));
TextFormatter<StaticStringWriter> writer(sb); TextFormatter<StaticStringWriter> writer(sb);
writer.writeInteger<T>(value); writer.writeInteger<T>(value);

View File

@ -10,7 +10,7 @@
using namespace ARDUINOJSON_NAMESPACE; using namespace ARDUINOJSON_NAMESPACE;
void check(const char* input, std::string expected) { void check(const char* input, std::string expected) {
char output[1024]; char output[64] = {0};
StaticStringWriter sb(output, sizeof(output)); StaticStringWriter sb(output, sizeof(output));
TextFormatter<StaticStringWriter> writer(sb); TextFormatter<StaticStringWriter> writer(sb);
writer.writeString(input); writer.writeString(input);

View File

@ -14,6 +14,8 @@ namespace ARDUINOJSON_NAMESPACE {
template <typename TWriter> template <typename TWriter>
class JsonSerializer : public Visitor<size_t> { class JsonSerializer : public Visitor<size_t> {
public: public:
static const bool producesText = true;
JsonSerializer(TWriter writer) : _formatter(writer) {} JsonSerializer(TWriter writer) : _formatter(writer) {}
FORCE_INLINE size_t visitArray(const CollectionData &array) { FORCE_INLINE size_t visitArray(const CollectionData &array) {

View File

@ -16,7 +16,7 @@ class PrettyJsonSerializer : public JsonSerializer<TWriter> {
typedef JsonSerializer<TWriter> base; typedef JsonSerializer<TWriter> base;
public: public:
PrettyJsonSerializer(TWriter &writer) : base(writer), _nesting(0) {} PrettyJsonSerializer(TWriter writer) : base(writer), _nesting(0) {}
size_t visitArray(const CollectionData &array) { size_t visitArray(const CollectionData &array) {
VariantSlot *slot = array.head(); VariantSlot *slot = array.head();

View File

@ -17,6 +17,8 @@ namespace ARDUINOJSON_NAMESPACE {
template <typename TWriter> template <typename TWriter>
class MsgPackSerializer : public Visitor<size_t> { class MsgPackSerializer : public Visitor<size_t> {
public: public:
static const bool producesText = false;
MsgPackSerializer(TWriter writer) : _writer(writer) {} MsgPackSerializer(TWriter writer) : _writer(writer) {}
template <typename T> template <typename T>

View File

@ -8,18 +8,14 @@
namespace ARDUINOJSON_NAMESPACE { namespace ARDUINOJSON_NAMESPACE {
// A Print implementation that allows to write in a char[]
class StaticStringWriter { class StaticStringWriter {
public: public:
StaticStringWriter(char *buf, size_t size) : end(buf + size - 1), p(buf) { StaticStringWriter(char *buf, size_t size) : end(buf + size), p(buf) {}
*p = '\0';
}
size_t write(uint8_t c) { size_t write(uint8_t c) {
if (p >= end) if (p >= end)
return 0; return 0;
*p++ = static_cast<char>(c); *p++ = static_cast<char>(c);
*p = '\0';
return 1; return 1;
} }
@ -29,7 +25,6 @@ class StaticStringWriter {
*p++ = static_cast<char>(*s++); *p++ = static_cast<char>(*s++);
n--; n--;
} }
*p = '\0';
return size_t(p - begin); return size_t(p - begin);
} }

View File

@ -23,11 +23,23 @@ size_t serialize(const TSource &source, TDestination &destination) {
} }
template <template <typename> class TSerializer, typename TSource> template <template <typename> class TSerializer, typename TSource>
size_t serialize(const TSource &source, void *buffer, size_t bufferSize) { typename enable_if<!TSerializer<StaticStringWriter>::producesText, size_t>::type
serialize(const TSource &source, void *buffer, size_t bufferSize) {
StaticStringWriter writer(reinterpret_cast<char *>(buffer), bufferSize); StaticStringWriter writer(reinterpret_cast<char *>(buffer), bufferSize);
return doSerialize<TSerializer>(source, writer); return doSerialize<TSerializer>(source, writer);
} }
template <template <typename> class TSerializer, typename TSource>
typename enable_if<TSerializer<StaticStringWriter>::producesText, size_t>::type
serialize(const TSource &source, void *buffer, size_t bufferSize) {
StaticStringWriter writer(reinterpret_cast<char *>(buffer), bufferSize);
size_t n = doSerialize<TSerializer>(source, writer);
// add null-terminator for text output (not counted in the size)
if (n < bufferSize)
reinterpret_cast<char *>(buffer)[n] = 0;
return n;
}
template <template <typename> class TSerializer, typename TSource, template <template <typename> class TSerializer, typename TSource,
typename TChar, size_t N> typename TChar, size_t N>
#if defined _MSC_VER && _MSC_VER < 1900 #if defined _MSC_VER && _MSC_VER < 1900
@ -36,8 +48,7 @@ typename enable_if<sizeof(remove_reference<TChar>::type) == 1, size_t>::type
typename enable_if<sizeof(TChar) == 1, size_t>::type typename enable_if<sizeof(TChar) == 1, size_t>::type
#endif #endif
serialize(const TSource &source, TChar (&buffer)[N]) { serialize(const TSource &source, TChar (&buffer)[N]) {
StaticStringWriter writer(reinterpret_cast<char *>(buffer), N); return serialize<TSerializer>(source, buffer, N);
return doSerialize<TSerializer>(source, writer);
} }
} // namespace ARDUINOJSON_NAMESPACE } // namespace ARDUINOJSON_NAMESPACE