bblanchon / ArduinoJson

📟 JSON library for Arduino and embedded C++. Simple and efficient.
https://arduinojson.org
MIT License
6.63k stars 1.1k forks source link

Maximum string length of 13 characters #2093

Closed DWilhelmLKKS closed 1 month ago

DWilhelmLKKS commented 1 month ago

Describe the issue I'm trying to read some texts out of my JSON file but when strings become longer than 13 characters I just get data junk.

Troubleshooter report Here is the report generated by the ArduinoJson Troubleshooter: Well, I'm out of ideas: you need to talk to a human.

Environment Here is the environment that I'm using':

Reproduction Here is a small snippet that demonstrate the problem.

JsonObject root = jsonDocument.as<JsonObject>();
uint16_t i = 0;

for (JsonPair pair : root) {
  JsonObject object = pair.value().as<JsonObject>();

  Remote remote;
  remote.link         = object[KEY_LINK].as<String>();
  remote.manufacturer = object[KEY_MANUFACTURER].as<String>();
  remote.serial       = String(pair.key().c_str());

  remotesList[i++] = remote;
  Serial.printf("%s / %s / %s\n", remotesList[i-1].link, remotesList[i-1].manufacturer, remotesList[i-1].serial);
}
{
  "9": {"manufacturer": "123456789", "link": "123456789"},
  "10": {"manufacturer": "1234567890", "link": "1234567890"},
  "11": {"manufacturer": "12345678901", "link": "12345678901"},
  "12": {"manufacturer": "123456789012", "link": "123456789012"},
  "13": {"manufacturer": "1234567890123", "link": "1234567890123"},
  "14": {"manufacturer": "12345678901234", "link": "12345678901234"},
  "15": {"manufacturer": "123456789012345", "link": "123456789012345"}
}

Program output
If relevant, include the program output.

Expected output:

123456789 / 123456789 / 9
1234567890 / 1234567890 / 10
12345678901 / 12345678901 / 11
123456789012 / 123456789012 / 12
1234567890123 / 1234567890123 / 13
12345678901234 / 12345678901234 / 14
123456789012345 / 123456789012345 / 15

Actual output:

123456789 / 123456789 / 9
1234567890 / 1234567890 / 10
12345678901 / 12345678901 / 11
123456789012 / 123456789012 / 12
1234567890123 / 1234567890123 / 13
��? / ��? / 14
@��? / @��? / 15

Printing the value of Serial.printf("%d\n", ArduinoJson::detail::StringNode::maxLength); returns 255 as expected.

DWilhelmLKKS commented 1 month ago

I found the solution! It had nothing to do with ArduinoJson. :) Using this code fixed it:

Serial.printf("%s / %s / %s\n", remotesList[i-1].link.c_str(), remotesList[i-1].manufacturer.c_str(), remotesList[i-1].serial.c_str());
bblanchon commented 1 month ago

Hi @DWilhelmLKKS,

I was about to tell you the same thing :-) Indeed, printf() is a legacy of the C language and knows nothing about String and classes in general. I don't know why they decided to integrate it into ESP32's core.

BTW, it was accidentally working with strings of 13 characters or less because of the short-string optimization (SSO) implemented in ESP32's String:

struct _ptr { 
    char *   buff;
    uint32_t cap;
    uint32_t len;
};
enum { SSOSIZE = sizeof(struct _ptr) + 4 - 1 };
struct _sso {
    char     buff[SSOSIZE];
    unsigned char len   : 7;
    unsigned char isSSO: 1;
};

This gives an SSOSIZE of 12 + 4 - 1 = 15, enough room for 14 characters and the terminator. So, in theory, 14-character strings should have worked too, but there is an off-by-one error in the length comparison that decides whether to use SSO or not:

if (maxStrLen < sizeof(sso.buff) - 1)

It should be <= instead of <; that's why SSO doesn't work with 14-character strings.

Best regard, Benoit