// 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 = []; var duplicateDetector = HashMap( 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; }