PackFileIndex
One traversal of a staging tree, plus the content digest RSPM derives from it.
Why the traversal is its own type
Files.walk(root).filter(Files::isRegularFile) looks free but is not: the walk already reads each directory's entries with their attributes, and then isRegularFile throws that away and issues a fresh stat per entry. On a 10k-file pack on NTFS that is 10k redundant syscalls. sortedRegularFiles uses walkFileTree, which hands the visitor the BasicFileAttributes the directory read already produced, so the type and the size come for free.
Why the digest is parallel
BedrockConversion hashes every staged byte to derive the Bedrock pack version, so an unchanged pack keeps its (uuid, version) cache key and clients don't redownload. That hash is inherently sequential — but the reads feeding it are not. sha256Hex prefetches file contents on a bounded pool and feeds them to a single MessageDigest in the same sorted order as before, so the resulting hex string is byte-for-byte what the old serial loop produced while the I/O overlaps.
Types
Functions
relativePath, 0x00, content, 0x00 for every file in order — identical to the serial digest loop it replaces, but with reads overlapped across a bounded pool.root, sorted by pack-relative path with / separators — the same ordering the previous Files.walk(...).sorted(...) calls used, so callers that depend on entry order (the archive writers, the content digest) are unaffected.relativePath, 0x00, content, 0x00 for every file, in order, into an existing digest.