dvWriteAssetPack function
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();
}