blob: 86a98bc3ba6a0281023f4dc48861a03f2804e64d [file] [log] [blame]
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import "dart:collection";
import "dart:typed_data";
import "indirect_table.dart";
import "list_overlap.dart";
/// Splits an indirect table with one large chunk into separate smaller chunks.
///
/// No new chunk is larger than the largest entry.
///
/// Preserves the entries, but they now point into the new chunks.
/// All chunks are distinct, and no chunk is a sub-list of another chunk.
void chunkifyTable(IndirectTable table) {
if (table.chunks.length != 1) {
throw ArgumentError("Single chunk table required");
}
var data = table.chunks[0];
var entries = table.entries.toList();
entries.sort((a, b) => b.length - a.length);
var uniqueChunks = <Uint8List>[];
var duplicateDetector =
HashMap<Uint8List, TableEntry>(equals: _equals, hashCode: _hash);
for (var entry in entries) {
var chunk = data.sublist(entry.start, entry.end);
var existingEntry = duplicateDetector[chunk];
if (existingEntry != null) {
entry.copyFrom(existingEntry);
} else {
// Check if chunk is a sublist of any existing chunk.
var chunkNum = 0;
var indexOf = 0;
for (; chunkNum < uniqueChunks.length; chunkNum++) {
var existingChunk = uniqueChunks[chunkNum];
if (existingChunk.length > chunk.length) {
var position = _indexOf(chunk, existingChunk);
if (position >= 0) {
indexOf = position;
break;
}
}
}
if (chunkNum == uniqueChunks.length) {
uniqueChunks.add(chunk);
}
entry.update(chunkNum, indexOf, entry.length);
duplicateDetector[chunk] = entry;
}
}
table.chunks = uniqueChunks;
}
int _indexOf(Uint8List short, Uint8List long) {
var length = short.length;
var range = long.length - length;
outer:
for (var i = 0; i < range; i++) {
for (var j = 0; j < short.length; j++) {
if (short[j] != long[i + j]) continue outer;
}
return i;
}
return -1;
}
/// Combines an indirect table with multiple chunks into only one chunk.
void combineChunkedTable(IndirectTable table) {
var overlapped = combineLists(table.chunks);
for (var entry in table.entries) {
var chunkEntry = overlapped.entries[entry.chunkNumber];
entry.update(0, entry.start + chunkEntry.start, entry.length);
}
table.chunks = [overlapped.chunks[0]];
}
/// Hash on a list.
int _hash(Uint8List list) {
var view = list.buffer.asUint32List();
var hash = 0;
for (var i = 0; i < view.length; i++) {
hash = (hash * 37 ^ view[i]) & 0xFFFFFFFF;
}
return hash;
}
/// Equality of lists of equal length.
bool _equals(Uint8List a, Uint8List b) {
assert(a.length == b.length);
assert(a.length % 8 == 0);
// Compare 32 bits at a time.
var aView = a.buffer.asInt64List();
var bView = b.buffer.asInt64List();
for (var i = 0; i < aView.length; i++) {
if (aView[i] != bView[i]) return false;
}
return true;
}