mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-12 08:52:32 +00:00
The base is FilmCraft's filmcraft-cfb (reader, writer, tests) at filmcraft 6d5269ed. Ported on top of it, each with a test: From GridCraft's and DeckCraft's readers: only sector shifts 9 and 12; a file that ends inside its last sector still reads; a chain loop shorter than the stream is an error instead of repeated data; a damaged mini stream no longer fails the open; links to missing directory entries are ignored; a stream size cap (Limits, default 1 GiB, and a per-call cap); a lenient directory scan (directory_names, has_entry_named) for files too damaged to open. From WordCraft's guard: a FAT sector listed twice is refused; spare FAT sectors and zero-padded DIFAT sectors are accepted. New: Limits::max_dir_entries, walk() and path() to list entries, has_stream, Writer::stream_at and write_streams, and only storages the root reaches are walked.
33 lines
1.7 KiB
Rust
33 lines
1.7 KiB
Rust
//! The writer scales: adding many children to one storage, and the duplicate-name rules that check keeps.
|
|
|
|
use craft_cfb::{Version, Writer};
|
|
|
|
/// Adding a child compared its name with every sibling (allocating twice per comparison), so n children
|
|
/// cost O(n^2): 20 000 children in one storage took about a minute. The duplicate check is a hash
|
|
/// lookup now. 40 000 plain names take milliseconds now and about 18 s before, so the limit separates them by orders of magnitude on any machine.
|
|
#[test]
|
|
fn adding_many_children_is_not_quadratic() {
|
|
let mut w = Writer::new(Version::V3);
|
|
let start = std::time::Instant::now();
|
|
for i in 0..40_000 {
|
|
w.stream(Writer::ROOT, &format!("{i}"), vec![1]).expect("unique name");
|
|
}
|
|
assert!(start.elapsed() < std::time::Duration::from_secs(3), "{:?}", start.elapsed());
|
|
}
|
|
|
|
/// The faster duplicate check still means what sibling comparison means: case-insensitive, also beyond ASCII,
|
|
/// per storage (the same name in another storage is fine), and a name that differs only in length is distinct.
|
|
#[test]
|
|
fn duplicate_names_are_still_rejected() {
|
|
let mut w = Writer::new(Version::V3);
|
|
let a = w.storage(Writer::ROOT, "Alpha", [0; 16]).unwrap();
|
|
assert!(w.stream(Writer::ROOT, "ALPHA", vec![]).is_err(), "case-insensitive");
|
|
assert!(w.stream(Writer::ROOT, "alpha", vec![]).is_err());
|
|
assert!(w.stream(Writer::ROOT, "Alph", vec![]).is_ok());
|
|
assert!(w.stream(Writer::ROOT, "Alpha2", vec![]).is_ok());
|
|
assert!(w.stream(a, "Alpha", vec![]).is_ok(), "same name in another storage");
|
|
assert!(w.stream(a, "Alpha", vec![]).is_err());
|
|
w.stream(Writer::ROOT, "Ünï", vec![]).unwrap();
|
|
assert!(w.stream(Writer::ROOT, "ÜNÏ", vec![]).is_err(), "non-ASCII case");
|
|
}
|