Appends are ATOMIC: a field is emitted only if the WHOLE serialized form * fits (with PD_ESC_SPACE bytes reserved for the closing '}' + NUL). Cutting a * field mid-value produced unterminated strings/arrays — malformed properties * JSON that aborts every json_extract()-based consumer downstream (seen on the * Linux kernel: 50-param functions truncated at the 2 KB cap). Dropping an * oversized
| 173 | * Linux kernel: 50-param functions truncated at the 2 KB cap). Dropping an |
| 174 | * oversized optional field whole keeps the JSON valid. */ |
| 175 | static void append_json_string(char *buf, size_t bufsize, size_t *pos, const char *key, |
| 176 | const char *val) { |
| 177 | if (!val || val[0] == '\0') { |
| 178 | return; |
| 179 | } |
| 180 | /* ,"key":"<escaped>" — comma + 2 key quotes + colon + 2 value quotes */ |
| 181 | size_t required = strlen(key) + def_json_escaped_len(val) + PD_JSON_FIELD_OVERHEAD; |
| 182 | if (*pos + required + PD_ESC_SPACE > bufsize) { |
| 183 | return; /* whole field would not fit — skip it atomically */ |
| 184 | } |
| 185 | size_t p = *pos; |
| 186 | int w = snprintf(buf + p, bufsize - p, ",\"%s\":\"", key); |
| 187 | if (w <= 0 || (size_t)w >= bufsize - p) { |
| 188 | return; |
| 189 | } |
| 190 | p += (size_t)w; |
| 191 | for (const char *s = val; *s && p < bufsize - PD_ESC_MARGIN; s++) { |
| 192 | p += (size_t)def_json_escape_char(buf + p, bufsize - p - PD_ESC_SPACE, *s); |
| 193 | } |
| 194 | if (p < bufsize - SKIP_ONE) { |
| 195 | buf[p++] = '"'; |
| 196 | } |
| 197 | buf[p] = '\0'; |
| 198 | *pos = p; |
| 199 | } |
| 200 | |
| 201 | /* Append a JSON array of strings: ,"key":["a","b","c"]. Atomic like |
| 202 | * append_json_string: emitted only if the whole array fits. */ |
no test coverage detected