address review: header bytes counted as JSON without serializing them

This commit is contained in:
Dylan M. Taylor
2026-11-11 12:42:58 +00:00
parent 4d019ab753
commit e5bbb57c5a
2 changed files with 22 additions and 1 deletions
+15 -1
View File
@@ -123,6 +123,20 @@ fn cell_json(c: &str) -> serde_json::Value {
/// Bytes of header names the row objects of delimited data may repeat in all.
const MAX_DATA_JSON_BYTES: usize = 64 << 20;
/// The bytes `s` takes as a JSON string (quotes and `serde_json`'s escapes), counted without
/// building it: a header of control characters must not be serialized in full to be refused.
pub(crate) fn json_string_len(s: &str) -> usize {
let body: usize = s
.chars()
.map(|c| match c {
'"' | '\\' | '\n' | '\r' | '\t' | '\u{8}' | '\u{c}' => 2,
c if u32::from(c) < 0x20 => 6,
c => c.len_utf8(),
})
.fold(0, usize::saturating_add);
body.saturating_add(2)
}
/// The data of a data footage item as `{data, rows, header}` JSON (see [`Req::FootageData`]).
pub fn footage_data_json(f: &effectcraft_project::Footage) -> Option<String> {
let text = f.data.as_deref()?;
@@ -141,7 +155,7 @@ pub fn footage_data_json(f: &effectcraft_project::Footage) -> Option<String> {
// Every row object repeats every distinct header name (a repeated name keeps its last
// column), as serialized: a long header over many rows is far more than the text itself.
let distinct: std::collections::HashSet<&String> = header.iter().collect();
let keys: usize = distinct.iter().map(|k| serde_json::to_string(k).map_or(k.len() * 6 + 2, |j| j.len()) + 2).sum();
let keys: usize = distinct.iter().map(|k| json_string_len(k) + 2).fold(0, usize::saturating_add);
if keys.saturating_mul(body.len()) > MAX_DATA_JSON_BYTES {
return None;
}
+7
View File
@@ -2134,3 +2134,10 @@ fn delimited_data_estimate_counts_escaped_and_distinct_headers() {
text.push_str(&"1\n".repeat(140_000));
assert!(crate::host::footage_data_json(&data(text)).is_some());
}
#[test]
fn json_string_length_matches_the_serialized_one() {
for s in ["", "plain", "a\"b\\c", "tab\tnl\ncr\rbs\u{8}ff\u{c}", "\u{1}\u{1f}\u{7f}", "é界😀", &"\u{1}".repeat(1000)] {
assert_eq!(crate::host::json_string_len(s), serde_json::to_string(s).unwrap().len(), "{s:?}");
}
}