packStream static method

Stream<List<int>> packStream(
  1. List<PqPackEntry> entries, {
  2. int chunkSize = 1 << 20,
})

Emits entries as one sequential pack stream — the streaming counterpart of pack, for piping straight into an AEAD writer so the plaintext pack never exists on disk. Bounded memory: one chunk in flight at a time.

Implementation

static Stream<List<int>> packStream(
  List<PqPackEntry> entries, {
  int chunkSize = 1 << 20,
}) async* {
  for (final entry in entries) {
    _requireSafeRelativePath(entry.relativePath);
    final pathBytes = PqBytes.utf8Bytes(entry.relativePath);
    if (pathBytes.length > maxPathBytes) {
      throw PqForgeException(
        'Pack entry path too long: ${entry.relativePath}',
      );
    }
    final source = File(entry.sourcePath);
    final length = await source.length();
    yield (BytesBuilder(copy: false)
          ..add(PqBytes.uint32(pathBytes.length))
          ..add(pathBytes)
          ..add(PqBytes.uint64(length)))
        .toBytes();
    final reader = await source.open();
    try {
      var remaining = length;
      while (remaining > 0) {
        final want = remaining < chunkSize ? remaining : chunkSize;
        // Fresh buffer per chunk: the consumer may hold it across awaits.
        final chunk = Uint8List(want);
        var offset = 0;
        while (offset < want) {
          final n = await reader.readInto(chunk, offset, want);
          if (n <= 0) {
            throw PqForgeException(
              'Short read packing ${entry.relativePath}',
            );
          }
          offset += n;
        }
        remaining -= want;
        yield chunk;
      }
    } finally {
      await reader.close();
    }
  }
}