Since #2425 checks Agile encryption's dataIntegrity, every workbook Excel
encrypts fails with "the decrypted data failed its integrity check". The
reader keyed the HMAC with the decrypted encryptedHmacKey cut to the salt
size (16 bytes), as [MS-OFFCRYPTO] 2.3.4.14 describes it, but Excel's key
is as long as the hash: 64 bytes for SHA-512. The check now accepts a key
of either length, so spec-literal writers still open and a modified
package still fails.
The test encryptor writes the integrity key as Excel does, so every
encrypted fixture now has Excel's layout; a new test covers both key
lengths, a key of neither length, and tampering with each.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Honor CARGO_BUILD_TARGET in FreeBSD packaging for aarch64 release builds
Cross-built FreeBSD aarch64 archives were not possible: package.sh took the
arch from uname -m (the x86_64 VM host), built for the host, read binaries
from target/release, and ran the aarch64 gridcraft-cli to print its version.
When CARGO_BUILD_TARGET is set, build with --target, read from
target/<triple>/release, name the archive by the target's arch (aarch64 for
aarch64-unknown-freebsd, x86_64 otherwise), and skip running the binary.
Unset behavior is unchanged.
* Reject unsupported CARGO_BUILD_TARGET in FreeBSD packaging
Only x86_64-unknown-freebsd and aarch64-unknown-freebsd are valid; any other
value now fails before the build or packaging.
The pre-signing check matched any folder named Program Files, so it rejected
a valid MSI when Windows resolves Program Files elsewhere, accepted the 64-bit
root for an x86 package and accepted a different app name. Build the expected
folder from the package architecture's resolved root and the policy name.
Saving a large workbook spent about half its time building worksheet XML,
mostly in small per-cell allocations. The writer now appends to the part
directly:
- Cell references, row numbers, style ids, shared string indexes and
numbers are appended without temporary strings (push_a1, push_u64,
push_num), and text is escaped in place (push_esc, with a fast path for
plain ASCII).
- sheetData walks the cell store in its own row-major order instead of
copying every address into a BTreeSet and a per-row BTreeMap and looking
each cell up again. Cells outside the store (pictures, spill values,
table headers) still go through the sorted set.
- Formulas with nothing the file spells differently (no LET/LAMBDA, no
_xlfn function, no spill, @, trim operator or structured reference) are
printed from the shared expression instead of being cloned and rebuilt
twice by the file rewrite.
- The A1 printer in gridcraft-formula appends references to the output
instead of formatting each anchor into its own string.
The output is byte-for-byte unchanged.
write_perf, 200,000 rows (2.0M cells, 14.2 MB package), release, Ryzen
16 threads / Linux, 36 interleaved runs each:
before: median 2083.9 ms, min 1615.7 ms
after: median 1501.3 ms, min 1192.3 ms (-28.0 % median, -26.2 % min)
What remains is mostly deflate (zlib-rs, level 6), about 70 % of the save.
Tests pin the exact sheetData of a workbook covering row settings between
cell rows, spill values, every value kind, number spellings, escaping and
formulas with and without a file spelling (generated with the previous
writer), and check each appending helper and the formula fast path against
the code they replace.
The formula fast path is guarded by a test that runs both paths over every
function in the gridcraft-functions registry (a new dev-dependency, allowed
by the layer rules), in 15 surrounding shapes covering every operator and
rewrite, so a function or rewrite added later without updating the trigger
list fails it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Real msiexec runs (per-user and all-users, silent and wizard) found two defects
that the metadata fixtures cannot see:
- "Only for me" published ALLUSERS=2, so FindRelatedProducts treated the run as
per-machine and skipped the existing per-user copy. An interactive upgrade
left two registered products and uninstall left files behind. The page now
leaves ALLUSERS unset for a per-user choice.
- Windows Installer resolves Program Files once, for the context it started in,
so choosing "Everyone" in the wizard installed into the user profile with a
machine-wide registration. The wizard now sets the new-install folder itself
after the choice (Program Files, Program Files (x86) for 32-bit, or the
per-user Programs folder). Silent installs are unchanged.
check-install-policy.ps1 now fails if either regresses.
* fix(ui-egui): dropdown menus regrow while open
Use craft_ui's ComboBox and combo::Menu so a menu that shrank (a filter with
no matches) grows back when the filter is cleared (egui#5225). Repin
craft-libs to one revision.
* chore(deps): craft-libs 60d60d48 (dropdown fixes)
* chore(deps): craft-libs 6af63481 (dropdown fixes)
* chore(deps): craft-libs 4d7b82f7 (dropdown fixes)
* chore(deps): craft-libs 92d392cf (#387 merged)
`cargo run --release -p gridcraft-xlsx --example write_perf -- [rows] [runs] [out.xlsx]`
builds a sales-style workbook (order ids, dates, categories, numbers, two
formula columns with cached values, booleans, sparse notes, a summary sheet)
and times write_xlsx, printing each run and the median / minimum. An optional
output path keeps the last package for byte comparisons between builds.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>