mirror of
https://github.com/storytold/effectcraft.git
synced 2026-11-11 15:22:05 +00:00
address review: header bytes counted as JSON without serializing them
This commit is contained in:
+15
-1
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:?}");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user