mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-14 19:26:56 -04:00
#1172: Pathfinder Outline 19x, SVG export 1.5x and open 1.2x faster, same output
At 50,000 paths unless noted, against main:
- Pathfinder Outline (300 paths): 145 → 7.5 ms. Each segment was tested against every junction
of the arrangement, and each chain against every chain kept so far. Now a segment tests only
the junctions inside its control box (junctions are sorted by x; a point within reach of a
curve is within reach of that box), with the same nearest-point test. Coincident chains are
found through a grid on their midpoints, taking the first match in the same order a scan does.
- export SVG: 267 → 176 ms. Path data is written straight into one string (`push_num`, the
same digits and trimming as `fmt_num`) instead of a string per number, point and segment.
- open .vectorcraft: 239 → 195 ms. The JSON was parsed once only to find where it ends:
- A file with no NUL has no data after it.
- Otherwise the marker (which holds the first NUL, as JSON can't) is taken when it directly
follows the JSON's last byte, as the writer puts it.
- Anything else takes the old parse.
`perf --deep` prints output hashes of an SVG export and a save → open → save round trip; main
and this branch print the same ones. pathfinder_pinned (Outline included) passes, and so do the
pathops, format and svg suites (345 tests).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VmMmc1J8XeNLxhRihWv9cc
This commit is contained in:
1 parent
4c5809302d
commit
7652439b40
5 files changed
+91
-18
No files matched your search
@@ -145,6 +145,19 @@ fn preview_mime(bytes: &[u8]) -> &'static str {
|
||||
/// The JSON of a file and the data after it (v4), split at [`BLOB_MAGIC`] after the first JSON
|
||||
/// value.
|
||||
fn split_tail(text: &[u8]) -> (&[u8], &[u8]) {
|
||||
// JSON holds no raw NUL, so the marker (which has one) starts at the first NUL, if the file
|
||||
// has one. The writer puts it right after the JSON's last byte; found there, it is where the
|
||||
// first JSON value ends, without parsing the JSON twice. Anything else takes the parse.
|
||||
let Some(nul) = text.iter().position(|&b| b == 0) else {
|
||||
// No NUL, so no marker: the parse below would find none either.
|
||||
return (text, &[]);
|
||||
};
|
||||
if let Some(at) = nul.checked_sub(1)
|
||||
&& text.get(at..).is_some_and(|r| r.starts_with(BLOB_MAGIC))
|
||||
&& at.checked_sub(1).and_then(|k| text.get(k)).is_some_and(|b| matches!(b, b'}' | b']' | b'"'))
|
||||
{
|
||||
return (text.get(..at).unwrap_or(text), text.get(at + BLOB_MAGIC.len()..).unwrap_or_default());
|
||||
}
|
||||
let mut values = serde_json::Deserializer::from_slice(text).into_iter::<serde::de::IgnoredAny>();
|
||||
if values.next().is_some_and(|v| v.is_ok()) {
|
||||
let end = values.byte_offset();
|
||||
|
||||
Reference in new issue
Block a user