Skip to content

Commit 9bcb409

Browse files
committed
Fixed serializeJson(doc, String) when allocation fails (fixes #1572)
1 parent 3b10afd commit 9bcb409

File tree

4 files changed

+116
-58
lines changed

4 files changed

+116
-58
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ HEAD
66

77
* Fixed support for `volatile float` and `volatile double` (issue #1557)
88
* Fixed error `[Pe070]: incomplete type is not allowed` on IAR (issue #1560)
9+
* Fixed `serializeJson(doc, String)` when allocation fails (issue #1572)
910

1011
v6.18.0 (2021-05-05)
1112
-------

extras/tests/Helpers/api/String.h

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@
99
// Reproduces Arduino's String class
1010
class String {
1111
public:
12-
String() {}
13-
explicit String(const char* s) : _str(s) {}
12+
String() : _maxCapacity(1024) {}
13+
explicit String(const char* s) : _str(s), _maxCapacity(1024) {}
1414

15-
String& operator+=(const char* rhs) {
16-
_str += rhs;
17-
return *this;
15+
void limitCapacityTo(size_t maxCapacity) {
16+
_maxCapacity = maxCapacity;
17+
}
18+
19+
unsigned char concat(const char* s) {
20+
return concat(s, strlen(s));
1821
}
1922

2023
size_t length() const {
@@ -34,8 +37,18 @@ class String {
3437
return lhs;
3538
}
3639

40+
protected:
41+
// This function is protected in most Arduino cores
42+
unsigned char concat(const char* s, size_t n) {
43+
if (_str.size() + n > _maxCapacity)
44+
return 0;
45+
_str.append(s, n);
46+
return 1;
47+
}
48+
3749
private:
3850
std::string _str;
51+
size_t _maxCapacity;
3952
};
4053

4154
class StringSumHelper;

extras/tests/Misc/StringWriter.cpp

Lines changed: 89 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -11,95 +11,121 @@
1111
using namespace ARDUINOJSON_NAMESPACE;
1212

1313
template <typename StringWriter>
14-
static size_t print(StringWriter& sb, const char* s) {
15-
return sb.write(reinterpret_cast<const uint8_t*>(s), strlen(s));
14+
static size_t print(StringWriter& writer, const char* s) {
15+
return writer.write(reinterpret_cast<const uint8_t*>(s), strlen(s));
1616
}
1717

1818
template <typename StringWriter, typename String>
19-
void common_tests(StringWriter& sb, const String& output) {
19+
void common_tests(StringWriter& writer, const String& output) {
2020
SECTION("InitialState") {
2121
REQUIRE(std::string("") == output);
2222
}
2323

2424
SECTION("EmptyString") {
25-
REQUIRE(0 == print(sb, ""));
25+
REQUIRE(0 == print(writer, ""));
2626
REQUIRE(std::string("") == output);
2727
}
2828

2929
SECTION("OneString") {
30-
REQUIRE(4 == print(sb, "ABCD"));
30+
REQUIRE(4 == print(writer, "ABCD"));
3131
REQUIRE(std::string("ABCD") == output);
3232
}
3333

3434
SECTION("TwoStrings") {
35-
REQUIRE(4 == print(sb, "ABCD"));
36-
REQUIRE(4 == print(sb, "EFGH"));
35+
REQUIRE(4 == print(writer, "ABCD"));
36+
REQUIRE(4 == print(writer, "EFGH"));
3737
REQUIRE(std::string("ABCDEFGH") == output);
3838
}
3939
}
4040

4141
TEST_CASE("StaticStringWriter") {
4242
char output[20] = {0};
43-
StaticStringWriter sb(output, sizeof(output));
43+
StaticStringWriter writer(output, sizeof(output));
4444

45-
common_tests(sb, static_cast<const char*>(output));
45+
common_tests(writer, static_cast<const char*>(output));
4646

4747
SECTION("OverCapacity") {
48-
REQUIRE(20 == print(sb, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
49-
REQUIRE(0 == print(sb, "ABC"));
48+
REQUIRE(20 == print(writer, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
49+
REQUIRE(0 == print(writer, "ABC"));
5050
REQUIRE("ABCDEFGHIJKLMNOPQRST" == std::string(output, 20));
5151
}
5252
}
5353

5454
TEST_CASE("Writer<std::string>") {
5555
std::string output;
56-
Writer<std::string> sb(output);
57-
common_tests(sb, output);
56+
Writer<std::string> writer(output);
57+
common_tests(writer, output);
5858
}
5959

6060
TEST_CASE("Writer<String>") {
6161
::String output;
62-
Writer< ::String> sb(output);
63-
64-
common_tests(sb, output);
65-
66-
SECTION("Writes characters to temporary buffer") {
67-
// accumulate in buffer
68-
sb.write('a');
69-
sb.write('b');
70-
sb.write('c');
71-
REQUIRE(output == "");
72-
73-
// flush when full
74-
sb.write('d');
75-
REQUIRE(output == "abcd");
76-
77-
// flush on destruction
78-
sb.write('e');
79-
sb.~Writer();
80-
REQUIRE(output == "abcde");
62+
Writer< ::String> writer(output);
63+
64+
SECTION("write(char)") {
65+
SECTION("writes to temporary buffer") {
66+
// accumulate in buffer
67+
writer.write('a');
68+
writer.write('b');
69+
writer.write('c');
70+
writer.write('d');
71+
REQUIRE(output == "");
72+
73+
// flush when full
74+
writer.write('e');
75+
REQUIRE(output == "abcd");
76+
77+
// flush on destruction
78+
writer.write('f');
79+
writer.~Writer();
80+
REQUIRE(output == "abcdef");
81+
}
82+
83+
SECTION("returns 1 on success") {
84+
for (int i = 0; i < ARDUINOJSON_STRING_BUFFER_SIZE; i++) {
85+
REQUIRE(writer.write('x') == 1);
86+
}
87+
}
88+
89+
SECTION("returns 0 on error") {
90+
output.limitCapacityTo(1);
91+
92+
REQUIRE(writer.write('a') == 1);
93+
REQUIRE(writer.write('b') == 1);
94+
REQUIRE(writer.write('c') == 1);
95+
REQUIRE(writer.write('d') == 1);
96+
REQUIRE(writer.write('e') == 0);
97+
REQUIRE(writer.write('f') == 0);
98+
}
8199
}
82100

83-
SECTION("Writes strings to temporary buffer") {
84-
// accumulate in buffer
85-
print(sb, "abc");
86-
REQUIRE(output == "");
87-
88-
// flush when full, and continue to accumulate
89-
print(sb, "de");
90-
REQUIRE(output == "abcd");
91-
92-
// flush on destruction
93-
sb.~Writer();
94-
REQUIRE(output == "abcde");
101+
SECTION("write(char*, size_t)") {
102+
SECTION("empty string") {
103+
REQUIRE(0 == print(writer, ""));
104+
writer.flush();
105+
REQUIRE(output == "");
106+
}
107+
108+
SECTION("writes to temporary buffer") {
109+
// accumulate in buffer
110+
print(writer, "abc");
111+
REQUIRE(output == "");
112+
113+
// flush when full, and continue to accumulate
114+
print(writer, "de");
115+
REQUIRE(output == "abcd");
116+
117+
// flush on destruction
118+
writer.~Writer();
119+
REQUIRE(output == "abcde");
120+
}
95121
}
96122
}
97123

98124
TEST_CASE("Writer<custom_string>") {
99125
custom_string output;
100-
Writer<custom_string> sb(output);
126+
Writer<custom_string> writer(output);
101127

102-
REQUIRE(4 == print(sb, "ABCD"));
128+
REQUIRE(4 == print(writer, "ABCD"));
103129
REQUIRE("ABCD" == output);
104130
}
105131

@@ -116,3 +142,20 @@ TEST_CASE("IsWriteableString") {
116142
REQUIRE(IsWriteableString<std::basic_string<wchar_t> >::value == false);
117143
}
118144
}
145+
146+
TEST_CASE("serializeJson(doc, String)") {
147+
StaticJsonDocument<1024> doc;
148+
doc["hello"] = "world";
149+
::String output;
150+
151+
SECTION("sufficient capacity") {
152+
serializeJson(doc, output);
153+
REQUIRE(output == "{\"hello\":\"world\"}");
154+
}
155+
156+
SECTION("unsufficient capacity") { // issue #1561
157+
output.limitCapacityTo(10);
158+
serializeJson(doc, output);
159+
REQUIRE(output == "{\"hello\"");
160+
}
161+
}

src/ArduinoJson/Serialization/Writers/ArduinoStringWriter.hpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ class Writer< ::String, void> {
2222
}
2323

2424
size_t write(uint8_t c) {
25-
ARDUINOJSON_ASSERT(_size < bufferCapacity);
26-
_buffer[_size++] = static_cast<char>(c);
2725
if (_size + 1 >= bufferCapacity)
28-
flush();
26+
if (flush() != 0)
27+
return 0;
28+
_buffer[_size++] = static_cast<char>(c);
2929
return 1;
3030
}
3131

@@ -36,14 +36,15 @@ class Writer< ::String, void> {
3636
return n;
3737
}
3838

39-
private:
40-
void flush() {
39+
size_t flush() {
4140
ARDUINOJSON_ASSERT(_size < bufferCapacity);
4241
_buffer[_size] = 0;
43-
*_destination += _buffer;
44-
_size = 0;
42+
if (_destination->concat(_buffer))
43+
_size = 0;
44+
return _size;
4545
}
4646

47+
private:
4748
::String *_destination;
4849
char _buffer[bufferCapacity];
4950
size_t _size;

0 commit comments

Comments
 (0)