fix(format): one kiwi value per compressed canvas byte

One value per two bytes of the compressed canvas was too tight for a
public design system: a 39.5 MB canvas holding 29.7 million values got
a budget of 19.7 million (reported in #701). Real files hold 0.37 to 0.75
values per compressed byte; the budget is now one per byte, at least the
old 20 million and at most 128 million. A small file still gets main's
20 million, however far it unpacks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YQxjkNSUJhTkTfY74aNeki
This commit is contained in:
Andrii Volkov
2026-10-17 09:27:42 +03:00
co-authored by Claude Opus 5.5
parent 0efddc3894
commit 7dcd7fd05b
2 changed files with 10 additions and 7 deletions
+5 -5
View File
@@ -11,11 +11,11 @@ use std::fmt;
/// Deepest struct/message nesting decoded.
const MAX_DEPTH: usize = 64;
/// Most values decoded from one message (every scalar, array element and field counts): at least
/// [`MIN_VALUES`], and one per [`PACKED_BYTES_PER_VALUE`] bytes of the file's compressed canvas,
/// so a large real file decodes while a small one can't expand into a huge tree, however far its
/// chunks inflate.
/// [`MIN_VALUES`], and one per byte of the file's compressed canvas up to [`MAX_VALUES`], so a large
/// real file decodes while a small one can't expand into a huge tree, however far its chunks
/// inflate. Real files hold 0.37 to 0.75 values per compressed byte.
const MIN_VALUES: usize = 20_000_000;
const PACKED_BYTES_PER_VALUE: usize = 2;
const MAX_VALUES: usize = 128 << 20;
/// Most definitions in a schema, and fields in one definition.
const MAX_DEFS: usize = 10_000;
const MAX_FIELDS: usize = 10_000;
@@ -302,7 +302,7 @@ impl Schema {
/// Most values decoded from a file whose compressed canvas is `packed` bytes.
pub(crate) fn value_budget(packed: usize) -> usize {
MIN_VALUES.max(packed / PACKED_BYTES_PER_VALUE)
packed.clamp(MIN_VALUES, MAX_VALUES)
}
struct Decoder<'s, 'b> {
+5 -2
View File
@@ -629,8 +629,11 @@ fn large_files_get_a_larger_value_budget() {
// A real 400 000-layer file: an 83.5 MB canvas whose message unpacks to 334 MB and holds
// 30.5 million values was refused as "too many values".
assert!(kiwi::value_budget(83_491_232) > 30_514_899);
// Still bounded by the largest canvas read.
assert!(kiwi::value_budget(MAX_CANVAS as usize) <= 128 << 20);
// A public design system packs more densely: a 39.5 MB canvas holding 29.7 million values,
// which one value per two compressed bytes refused.
assert!(kiwi::value_budget(39_500_000) > 29_703_730);
// Never more than the ceiling, however large the canvas.
assert_eq!(kiwi::value_budget(MAX_CANVAS as usize), 128 << 20);
}
#[test]