unpack static method
Future<int>
unpack({
- required RandomAccessFile source,
- required String outputDirPath,
- int chunkSize = 1 << 20,
Reads a pack from source and materializes each entry under
outputDirPath. Every path is re-validated against traversal. Returns the
entry count.
Implementation
static Future<int> unpack({
required RandomAccessFile source,
required String outputDirPath,
int chunkSize = 1 << 20,
}) async {
final buffer = Uint8List(chunkSize);
var count = 0;
while (true) {
final pathLenBytes = await _readExactlyOrNull(source, 4);
if (pathLenBytes == null) break; // clean EOF at an entry boundary
final pathLen = _readUint32(pathLenBytes);
if (pathLen <= 0 || pathLen > maxPathBytes) {
throw PqForgeException('Invalid pack entry path length: $pathLen');
}
final relativePath = _decodeUtf8(await _readExactly(source, pathLen));
_requireSafeRelativePath(relativePath);
final contentLen = _readUint64(await _readExactly(source, 8));
final output = File(_join(outputDirPath, relativePath));
await output.parent.create(recursive: true);
final sink = await output.open(mode: FileMode.write);
try {
var remaining = contentLen;
while (remaining > 0) {
final want = remaining < buffer.length ? remaining : buffer.length;
final n = await source.readInto(buffer, 0, want);
if (n <= 0) {
throw PqForgeException('Truncated pack content for $relativePath');
}
await sink.writeFrom(buffer, 0, n);
remaining -= n;
}
} finally {
await sink.close();
}
count++;
}
return count;
}