From 7dcd7fd05bfa820bf6adb0fe4e607f1e9ee5c89f Mon Sep 17 00:00:00 2001 From: Andrii Volkov <60794004+volkovskey@users.noreply.github.com> Date: Sat, 17 Oct 2026 09:27:42 +0300 Subject: [PATCH] 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 Claude-Session: https://claude.ai/code/session_01YQxjkNSUJhTkTfY74aNeki --- crates/format/src/fig/kiwi.rs | 10 +++++----- crates/format/src/fig/tests.rs | 7 +++++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/crates/format/src/fig/kiwi.rs b/crates/format/src/fig/kiwi.rs index 46a9daac..6496062b 100644 --- a/crates/format/src/fig/kiwi.rs +++ b/crates/format/src/fig/kiwi.rs @@ -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> { diff --git a/crates/format/src/fig/tests.rs b/crates/format/src/fig/tests.rs index 574f860c..c2fae249 100644 --- a/crates/format/src/fig/tests.rs +++ b/crates/format/src/fig/tests.rs @@ -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]