dvWriteAssetPack function

Uint8List dvWriteAssetPack(
  1. List<DVAssetPackEntry> entries
)

entries as a pack.

Throws ArgumentError for a path that is empty, absolute, has a backslash or a .. segment, or is carried twice.

Implementation

Uint8List dvWriteAssetPack(List<DVAssetPackEntry> entries) {
  final BytesBuilder data = BytesBuilder(copy: false);
  // Stored bytes already written, by what they are: a file carried twice,
  // under two paths, is written once.
  final Map<String, int> written = <String, int>{};
  final List<List<Object>> rows = <List<Object>>[];
  final Set<String> seen = <String>{};
  final List<DVAssetPackEntry> sorted = List<DVAssetPackEntry>.of(entries)
    ..sort((DVAssetPackEntry a, DVAssetPackEntry b) => a.path.compareTo(b.path));
  for (final DVAssetPackEntry entry in sorted) {
    final String path = entry.path;
    if (path.isEmpty ||
        path.startsWith('/') ||
        path.contains(r'\') ||
        path.split('/').any((String s) => s == '..' || s.isEmpty)) {
      throw ArgumentError.value(path, 'path', 'not a path inside the site');
    }
    if (!seen.add(path)) {
      throw ArgumentError.value(path, 'path', 'carried twice');
    }
    final String hash = sha256.convert(entry.bytes).toString().substring(0, 32);
    final Uint8List? encoded = entry.stored;
    final bool keepEncoded = encoded != null &&
        entry.encoding != DVAssetEncoding.identity &&
        encoded.length < entry.bytes.length;
    final Uint8List stored = keepEncoded ? encoded : entry.bytes;
    final DVAssetEncoding encoding = keepEncoded ? entry.encoding : DVAssetEncoding.identity;
    final String key = '$hash/${encoding.index}/${stored.length}';
    final int offset = written.putIfAbsent(key, () {
      final int at = data.length;
      data.add(stored);
      return at;
    });
    rows.add(<Object>[
      path,
      offset,
      stored.length,
      entry.bytes.length,
      encoding.index,
      hash,
      entry.protected ? _protectedFlag : 0,
    ]);
  }
  final String build = sha256
      .convert(utf8.encode(jsonEncode(<Object>[
        for (final List<Object> row in rows) <Object>[row[0], row[4], row[5], row[6]],
      ])))
      .toString()
      .substring(0, 16);
  final Uint8List index = utf8.encode(jsonEncode(<String, Object>{
    'build': build,
    'entries': rows,
  }));
  final Uint8List head = Uint8List(12);
  head.setRange(0, 8, ascii.encode(_magic));
  ByteData.sublistView(head).setUint32(8, index.length, Endian.little);
  return (BytesBuilder(copy: false)
        ..add(head)
        ..add(index)
        ..add(data.takeBytes()))
      .takeBytes();
}