Truncate ASE swatch names that overflow the u16 length field

Found while testing crates/color/src/ase.rs:131-132

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
NetterSilver
2026-10-19 12:47:19 +02:00
co-authored by Claude Haiku 5.5
parent 443ea4640c
commit 352abea9fc
+23 -2
View File
@@ -128,8 +128,14 @@ pub fn write(swatches: &[Swatch]) -> Vec<u8> {
continue;
}
let mut b = Vec::new();
let units: Vec<u16> = s.name.encode_utf16().chain(std::iter::once(0)).collect();
b.extend((units.len() as u16).to_be_bytes());
// The length field is a u16 that counts the terminating NUL: truncate longer names.
let mut units: Vec<u16> = s.name.encode_utf16().take(usize::from(u16::MAX) - 1).collect();
// Do not leave half of a surrogate pair at the cut.
if units.last().is_some_and(|u| (0xD800..0xDC00).contains(u)) {
units.pop();
}
units.push(0);
b.extend(u16::try_from(units.len()).unwrap_or(u16::MAX).to_be_bytes());
for u in units {
b.extend(u.to_be_bytes());
}
@@ -233,6 +239,21 @@ mod tests {
assert_eq!(read(&write(&sw[1..])).map(|w| w.len()), Ok(MAX_COLORS), "up to the limit");
}
#[test]
fn over_long_name_is_truncated_to_fit() {
let sw = vec![Swatch {
name: "x".repeat(70_000),
value: SwatchValue::Color { color: Color::Gray { k: 0.5 }, color_type: ColorType::Process },
locked: false,
named: true,
hidden: false,
}];
let back = read(&write(&sw)).unwrap();
assert_eq!(back.len(), 1, "the swatch must survive the round trip");
assert_eq!(back[0].name, "x".repeat(usize::from(u16::MAX) - 1));
assert_eq!(back[0].value, sw[0].value);
}
#[test]
fn out_of_range_components_are_clamped() {
let mut f = Vec::new();