Skip to content

Commit 4813f4d

Browse files
committed
Don't rely on array size to get the length of string literals
See #2189
1 parent 5f96515 commit 4813f4d

File tree

15 files changed

+48
-73
lines changed

15 files changed

+48
-73
lines changed

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ HEAD
1111

1212
> ### BREAKING CHANGES
1313
>
14+
> #### `JsonString` constructor's boolean parameter
15+
>
1416
> Since version 7.3, you could pass a boolean to `JsonString`'s constructor to force the string to be stored by pointer.
1517
> This optimization has been removed, and you'll get a deprecation warning if you use it.
1618
> To fix the issue, you must remove the boolean argument from the constructor, or better yet, remove `JsonString` altogether.
@@ -20,6 +22,18 @@ HEAD
2022
> - doc["name"] = JsonString(name, true);
2123
> + doc["name"] = name;
2224
> ```
25+
>
26+
> #### NUL characters in string literals
27+
>
28+
> Since version 7.3, ArduinoJson supported NUL characters (`\0`) in string literals.
29+
> This feature has been removed as part of the storage policy change.
30+
>
31+
> If you do need to include NULs in your string, you must use a `JsonString` instead:
32+
>
33+
> ```diff
34+
> - doc["strings"] = "hello\0world"
35+
> + doc["strings"] = JsonString("hello\0world", 11)
36+
> ```
2337
2438
v7.4.2 (2025-06-20)
2539
------

extras/tests/JsonDocument/remove.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ TEST_CASE("JsonDocument::remove()") {
2222

2323
SECTION("string literal") {
2424
doc["a"] = 1;
25-
doc["a\0b"_s] = 2;
25+
doc["ab"_s] = 2;
2626
doc["b"] = 3;
2727

28-
doc.remove("a\0b");
28+
doc.remove("ab");
2929

3030
REQUIRE(doc.as<std::string>() == "{\"a\":1,\"b\":3}");
3131
}

extras/tests/JsonDocument/subscript.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ TEST_CASE("JsonDocument::operator[]") {
1414

1515
SECTION("object") {
1616
doc["abc"_s] = "ABC";
17-
doc["abc\0d"_s] = "ABCD";
17+
doc["abcd"_s] = "ABCD";
1818

1919
SECTION("const char*") {
2020
const char* key = "abc";
@@ -25,20 +25,20 @@ TEST_CASE("JsonDocument::operator[]") {
2525
SECTION("string literal") {
2626
REQUIRE(doc["abc"] == "ABC");
2727
REQUIRE(cdoc["abc"] == "ABC");
28-
REQUIRE(doc["abc\0d"] == "ABCD");
29-
REQUIRE(cdoc["abc\0d"] == "ABCD");
28+
REQUIRE(doc["abcd"] == "ABCD");
29+
REQUIRE(cdoc["abcd"] == "ABCD");
3030
}
3131

3232
SECTION("std::string") {
3333
REQUIRE(doc["abc"_s] == "ABC");
3434
REQUIRE(cdoc["abc"_s] == "ABC");
35-
REQUIRE(doc["abc\0d"_s] == "ABCD");
36-
REQUIRE(cdoc["abc\0d"_s] == "ABCD");
35+
REQUIRE(doc["abcd"_s] == "ABCD");
36+
REQUIRE(cdoc["abcd"_s] == "ABCD");
3737
}
3838

3939
SECTION("JsonVariant") {
4040
doc["key1"] = "abc";
41-
doc["key2"] = "abc\0d"_s;
41+
doc["key2"] = "abcd"_s;
4242
doc["key3"] = "foo";
4343

4444
CHECK(doc[doc["key1"]] == "ABC");

extras/tests/JsonVariant/set.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
1818
JsonVariant variant = doc.to<JsonVariant>();
1919

2020
SECTION("string literal") {
21-
bool result = variant.set("hello\0world");
21+
bool result = variant.set("hello world");
2222

2323
REQUIRE(result == true);
24-
REQUIRE(variant == "hello\0world"_s); // stores by copy
24+
REQUIRE(variant == "hello world"_s); // stores by copy
2525
REQUIRE(spy.log() == AllocatorLog{
2626
Allocate(sizeofString(11)),
2727
});

extras/tests/JsonVariantConst/subscript.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,12 @@ TEST_CASE("JsonVariantConst::operator[]") {
5252
JsonObject object = doc.to<JsonObject>();
5353
object["ab"_s] = "AB";
5454
object["abc"_s] = "ABC";
55-
object["abc\0d"_s] = "ABCD";
55+
object["abcd"_s] = "ABCD";
5656

5757
SECTION("string literal") {
5858
REQUIRE(var["ab"] == "AB"_s);
5959
REQUIRE(var["abc"] == "ABC"_s);
60-
REQUIRE(var["abc\0d"] == "ABCD"_s);
60+
REQUIRE(var["abcd"] == "ABCD"_s);
6161
REQUIRE(var["def"].isNull());
6262
REQUIRE(var[0].isNull());
6363
}
@@ -73,7 +73,7 @@ TEST_CASE("JsonVariantConst::operator[]") {
7373
SECTION("supports std::string") {
7474
REQUIRE(var["ab"_s] == "AB"_s);
7575
REQUIRE(var["abc"_s] == "ABC"_s);
76-
REQUIRE(var["abc\0d"_s] == "ABCD"_s);
76+
REQUIRE(var["abcd"_s] == "ABCD"_s);
7777
REQUIRE(var["def"_s].isNull());
7878
}
7979

@@ -91,7 +91,7 @@ TEST_CASE("JsonVariantConst::operator[]") {
9191
SECTION("supports JsonVariant") {
9292
object["key1"] = "ab";
9393
object["key2"] = "abc";
94-
object["key3"] = "abc\0d"_s;
94+
object["key3"] = "abcd"_s;
9595
object["key4"] = "foo";
9696

9797
REQUIRE(var[var["key1"]] == "AB"_s);

extras/tests/Misc/StringAdapters.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ TEST_CASE("adaptString()") {
2121
auto s = adaptString("bravo\0alpha");
2222

2323
CHECK(s.isNull() == false);
24-
CHECK(s.size() == 11);
24+
CHECK(s.size() == 5);
2525
}
2626

2727
SECTION("null const char*") {

src/ArduinoJson/Document/JsonDocument.hpp

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
145145

146146
// Replaces the root with the specified value.
147147
// https://arduinojson.org/v7/api/jsondocument/set/
148-
template <typename TChar,
149-
detail::enable_if_t<!detail::is_const<TChar>::value, int> = 0>
148+
template <typename TChar>
150149
bool set(TChar* src) {
151150
return to<JsonVariant>().set(src);
152151
}
@@ -218,9 +217,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
218217
// Gets or sets a root object's member.
219218
// https://arduinojson.org/v7/api/jsondocument/subscript/
220219
template <typename TChar,
221-
detail::enable_if_t<detail::IsString<TChar*>::value &&
222-
!detail::is_const<TChar>::value,
223-
int> = 0>
220+
detail::enable_if_t<detail::IsString<TChar*>::value, int> = 0>
224221
detail::MemberProxy<JsonDocument&, detail::AdaptedString<TChar*>> operator[](
225222
TChar* key) {
226223
return {*this, detail::adaptString(key)};
@@ -238,9 +235,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
238235
// Gets a root object's member.
239236
// https://arduinojson.org/v7/api/jsondocument/subscript/
240237
template <typename TChar,
241-
detail::enable_if_t<detail::IsString<TChar*>::value &&
242-
!detail::is_const<TChar>::value,
243-
int> = 0>
238+
detail::enable_if_t<detail::IsString<TChar*>::value, int> = 0>
244239
JsonVariantConst operator[](TChar* key) const {
245240
return JsonVariantConst(
246241
getVariantImpl().getMember(detail::adaptString(key)), &resources_);
@@ -299,8 +294,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
299294

300295
// Appends a value to the root array.
301296
// https://arduinojson.org/v7/api/jsondocument/add/
302-
template <typename TChar,
303-
detail::enable_if_t<!detail::is_const<TChar>::value, int> = 0>
297+
template <typename TChar>
304298
bool add(TChar* value) {
305299
return getOrCreateArray().add(value);
306300
}
@@ -316,9 +310,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
316310
// Removes a member of the root object.
317311
// https://arduinojson.org/v7/api/jsondocument/remove/
318312
template <typename TChar,
319-
detail::enable_if_t<detail::IsString<TChar*>::value &&
320-
!detail::is_const<TChar>::value,
321-
int> = 0>
313+
detail::enable_if_t<detail::IsString<TChar*>::value, int> = 0>
322314
void remove(TChar* key) {
323315
getVariantImpl().removeMember(detail::adaptString(key));
324316
}

src/ArduinoJson/Object/JsonObject.hpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,7 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
110110
// Gets or sets the member with specified key.
111111
// https://arduinojson.org/v7/api/jsonobject/subscript/
112112
template <typename TChar,
113-
detail::enable_if_t<detail::IsString<TChar*>::value &&
114-
!detail::is_const<TChar>::value,
115-
int> = 0>
113+
detail::enable_if_t<detail::IsString<TChar*>::value, int> = 0>
116114
detail::MemberProxy<JsonObject, detail::AdaptedString<TChar*>> operator[](
117115
TChar* key) const {
118116
return {*this, detail::adaptString(key)};
@@ -169,9 +167,7 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
169167
// DEPRECATED: use obj["key"].is<T>() instead
170168
// https://arduinojson.org/v7/api/jsonobject/containskey/
171169
template <typename TChar,
172-
detail::enable_if_t<detail::IsString<TChar*>::value &&
173-
!detail::is_const<TChar>::value,
174-
int> = 0>
170+
detail::enable_if_t<detail::IsString<TChar*>::value, int> = 0>
175171
ARDUINOJSON_DEPRECATED("use obj[\"key\"].is<T>() instead")
176172
bool containsKey(TChar* key) const {
177173
return impl_.getMember(detail::adaptString(key)) != 0;

src/ArduinoJson/Object/JsonObjectConst.hpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,7 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
106106
// Gets the member with specified key.
107107
// https://arduinojson.org/v7/api/jsonobjectconst/subscript/
108108
template <typename TChar,
109-
detail::enable_if_t<detail::IsString<TChar*>::value &&
110-
!detail::is_const<TChar>::value,
111-
int> = 0>
109+
detail::enable_if_t<detail::IsString<TChar*>::value, int> = 0>
112110
JsonVariantConst operator[](TChar* key) const {
113111
return JsonVariantConst(impl_.getMember(detail::adaptString(key)),
114112
impl_.resources());

src/ArduinoJson/Object/MemberProxy.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class MemberProxy
3939
return *this;
4040
}
4141

42-
template <typename T, enable_if_t<!is_const<T>::value, int> = 0>
42+
template <typename T>
4343
MemberProxy& operator=(T* src) {
4444
this->set(src);
4545
return *this;

0 commit comments

Comments
 (0)