Bug 1587678 - Part 2: Use FixedLengthVector when the size is known at the allocation. r=Yoric

Differential Revision: https://phabricator.services.mozilla.com/D49564

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Tooru Fujisawa 2019-10-22 08:34:26 +00:00
Родитель 27072461a3
Коммит 9cfe0a749d
2 изменённых файлов: 97 добавлений и 103 удалений

Просмотреть файл

@ -6,9 +6,10 @@
#include "frontend/BinASTTokenReaderContext.h"
#include "mozilla/IntegerTypeTraits.h" // mozilla::MaxValue
#include "mozilla/Result.h" // MOZ_TRY*
#include "mozilla/ScopeExit.h" // mozilla::MakeScopeExit
#include "mozilla/IntegerTypeTraits.h" // mozilla::MaxValue
#include "mozilla/OperatorNewExtensions.h" // mozilla::KnownNotNull
#include "mozilla/Result.h" // MOZ_TRY*
#include "mozilla/ScopeExit.h" // mozilla::MakeScopeExit
#include <string.h> // memchr, memcmp, memmove
@ -726,13 +727,13 @@ class HuffmanPreludeReader {
// Read and add symbol.
BINJS_MOZ_TRY_DECL(
symbol, readSymbol<Entry>(entry, i)); // Symbol is read from disk.
MOZ_TRY(table.addSymbol(code, bitLength, symbol));
MOZ_TRY(table.addSymbol(i, code, bitLength, symbol));
// Prepare next code.
code = (code + 1) << (nextBitLength - bitLength);
}
MOZ_TRY(table.initComplete());
MOZ_TRY(table.initComplete(cx_));
auxStorageLength_.clear();
return Ok();
}
@ -818,13 +819,13 @@ class HuffmanPreludeReader {
BINJS_MOZ_TRY_DECL(symbol,
readSymbol<Entry>(entry, auxStorageLength_[i].index_));
MOZ_TRY(table.addSymbol(code, bitLength, symbol));
MOZ_TRY(table.addSymbol(i, code, bitLength, symbol));
// Prepare next code.
code = (code + 1) << (nextBitLength - bitLength);
}
MOZ_TRY(table.initComplete());
MOZ_TRY(table.initComplete(cx_));
auxStorageLength_.clear();
return Ok();
@ -1738,22 +1739,22 @@ const BinASTSymbol* GenericHuffmanTable::Iterator::operator->() const {
GenericHuffmanTable::GenericHuffmanTable(JSContext*)
: implementation_(HuffmanTableUnreachable{}) {}
JS::Result<Ok> GenericHuffmanTable::initComplete() {
JS::Result<Ok> GenericHuffmanTable::initComplete(JSContext* cx) {
return implementation_.match(
[](SingleEntryHuffmanTable& implementation) -> JS::Result<Ok> {
MOZ_CRASH("SingleEntryHuffmanTable shouldn't have multiple entries!");
},
[](TwoEntriesHuffmanTable& implementation) -> JS::Result<Ok> {
return implementation.initComplete();
[cx](TwoEntriesHuffmanTable& implementation) -> JS::Result<Ok> {
return implementation.initComplete(cx);
},
[](SingleLookupHuffmanTable& implementation) -> JS::Result<Ok> {
return implementation.initComplete();
[cx](SingleLookupHuffmanTable& implementation) -> JS::Result<Ok> {
return implementation.initComplete(cx);
},
[](TwoLookupsHuffmanTable& implementation) -> JS::Result<Ok> {
return implementation.initComplete();
[cx](TwoLookupsHuffmanTable& implementation) -> JS::Result<Ok> {
return implementation.initComplete(cx);
},
[](ThreeLookupsHuffmanTable& implementation) -> JS::Result<Ok> {
return implementation.initComplete();
[cx](ThreeLookupsHuffmanTable& implementation) -> JS::Result<Ok> {
return implementation.initComplete(cx);
},
[](HuffmanTableUnreachable&) -> JS::Result<Ok> {
MOZ_CRASH("GenericHuffmanTable is unitialized!");
@ -1868,36 +1869,37 @@ JS::Result<Ok> GenericHuffmanTable::initStart(JSContext* cx,
cx, numberOfSymbols, largestBitLength);
}
JS::Result<Ok> GenericHuffmanTable::addSymbol(uint32_t bits, uint8_t bitLength,
JS::Result<Ok> GenericHuffmanTable::addSymbol(size_t index, uint32_t bits,
uint8_t bitLength,
const BinASTSymbol& value) {
return implementation_.match(
[](SingleEntryHuffmanTable&) -> JS::Result<Ok> {
MOZ_CRASH("SingleEntryHuffmanTable shouldn't have multiple entries!");
return Ok();
},
[bits, bitLength,
[index, bits, bitLength,
value](TwoEntriesHuffmanTable&
implementation) mutable /* discard implicit const */
-> JS::Result<Ok> {
return implementation.addSymbol(bits, bitLength, value);
return implementation.addSymbol(index, bits, bitLength, value);
},
[bits, bitLength,
[index, bits, bitLength,
value](SingleLookupHuffmanTable&
implementation) mutable /* discard implicit const */
-> JS::Result<Ok> {
return implementation.addSymbol(bits, bitLength, value);
return implementation.addSymbol(index, bits, bitLength, value);
},
[bits, bitLength,
[index, bits, bitLength,
value](TwoLookupsHuffmanTable&
implementation) mutable /* discard implicit const */
-> JS::Result<Ok> {
return implementation.addSymbol(bits, bitLength, value);
return implementation.addSymbol(index, bits, bitLength, value);
},
[bits, bitLength,
[index, bits, bitLength,
value = value](ThreeLookupsHuffmanTable&
implementation) mutable /* discard implicit const */
-> JS::Result<Ok> {
return implementation.addSymbol(bits, bitLength, value);
return implementation.addSymbol(index, bits, bitLength, value);
},
[](HuffmanTableUnreachable&) -> JS::Result<Ok> {
MOZ_CRASH("GenericHuffmanTable is unitialized!");
@ -1999,39 +2001,32 @@ JS::Result<Ok> TwoEntriesHuffmanTable::initStart(JSContext* cx,
size_t numberOfSymbols,
uint8_t largestBitLength) {
// Make sure that we're initializing.
MOZ_ASSERT(length_ == 0);
MOZ_ASSERT(numberOfSymbols == 2);
MOZ_ASSERT(largestBitLength == 1);
return Ok();
}
JS::Result<Ok> TwoEntriesHuffmanTable::initComplete() {
MOZ_ASSERT(length_ == 2);
JS::Result<Ok> TwoEntriesHuffmanTable::initComplete(JSContext* cx) {
return Ok();
}
JS::Result<Ok> TwoEntriesHuffmanTable::addSymbol(uint32_t bits,
JS::Result<Ok> TwoEntriesHuffmanTable::addSymbol(size_t index, uint32_t bits,
uint8_t bitLength,
const BinASTSymbol& value) {
MOZ_ASSERT(length_ < 2);
// Symbols must be ranked by increasing bits length
MOZ_ASSERT_IF(length_ == 0, bits == 0);
MOZ_ASSERT_IF(length_ == 1, bits == 1);
MOZ_ASSERT_IF(index == 0, bits == 0);
MOZ_ASSERT_IF(index == 1, bits == 1);
// FIXME: Throw soft error instead of assert.
MOZ_ASSERT(bitLength == 1);
values_[length_] = value;
++length_;
values_[index] = value;
return Ok();
}
HuffmanLookupResult TwoEntriesHuffmanTable::lookup(HuffmanLookup key) const {
MOZ_ASSERT(length_ == 2);
// By invariant, bit lengths are 1.
const auto index = key.leadingBits(1);
MOZ_ASSERT(index < length_);
return HuffmanLookupResult::found(1, &values_[index]);
}
@ -2064,26 +2059,25 @@ JS::Result<Ok> SingleLookupHuffmanTable::initStart(JSContext* cx,
MOZ_ASSERT_IF(largestBitLength != 32,
(uint32_t(1) << largestBitLength) - 1 <=
mozilla::MaxValue<InternalIndex>::value);
MOZ_ASSERT(values_.empty()); // Make sure that we're initializing.
largestBitLength_ = largestBitLength;
if (MOZ_UNLIKELY(!values_.initCapacity(numberOfSymbols))) {
if (MOZ_UNLIKELY(!values_.allocateUninitialized(cx, numberOfSymbols))) {
return cx->alreadyReportedError();
}
const size_t saturatedLength = 1 << largestBitLength_;
if (MOZ_UNLIKELY(!saturated_.initCapacity(saturatedLength))) {
if (MOZ_UNLIKELY(!saturated_.allocateUninitialized(cx, saturatedLength))) {
return cx->alreadyReportedError();
}
// Enlarge `saturated_`, as we're going to fill it in random order.
for (size_t i = 0; i < saturatedLength; ++i) {
// Capacity reserved in this method.
saturated_.infallibleAppend(InternalIndex(-1));
saturated_[i] = InternalIndex(-1);
}
return Ok();
}
JS::Result<Ok> SingleLookupHuffmanTable::initComplete() {
JS::Result<Ok> SingleLookupHuffmanTable::initComplete(JSContext* cx) {
// Double-check that we've initialized properly.
MOZ_ASSERT(largestBitLength_ <= MAX_CODE_BIT_LENGTH);
@ -2116,18 +2110,16 @@ JS::Result<Ok> SingleLookupHuffmanTable::initComplete() {
return Ok();
}
JS::Result<Ok> SingleLookupHuffmanTable::addSymbol(uint32_t bits,
JS::Result<Ok> SingleLookupHuffmanTable::addSymbol(size_t index, uint32_t bits,
uint8_t bitLength,
const BinASTSymbol& value) {
MOZ_ASSERT_IF(largestBitLength_ != 0, bitLength != 0);
MOZ_ASSERT_IF(bitLength != 32 /* >> 32 is UB */, bits >> bitLength == 0);
MOZ_ASSERT(bitLength <= largestBitLength_);
const size_t index = values_.length();
// First add the value to `values_`.
// Memory was reserved in `init()`.
values_.infallibleEmplaceBack(bits, bitLength, value);
new (mozilla::KnownNotNull, &values_[index])
HuffmanEntry(bits, bitLength, value);
// Notation: in the following, unless otherwise specified, we consider
// values with `largestBitLength_` bits exactly.
@ -2216,13 +2208,12 @@ JS::Result<Ok> MultiLookupHuffmanTable<Subtable, PrefixBitLength>::initStart(
JSContext* cx, size_t numberOfSymbols, uint8_t largestBitLength) {
static_assert(PrefixBitLength < MAX_CODE_BIT_LENGTH,
"Invalid PrefixBitLength");
MOZ_ASSERT(values_.empty()); // Make sure that we're initializing.
MOZ_ASSERT(suffixTables_.empty());
largestBitLength_ = largestBitLength;
if (MOZ_UNLIKELY(!values_.initCapacity(numberOfSymbols))) {
if (MOZ_UNLIKELY(!values_.allocateUninitialized(cx, numberOfSymbols))) {
return cx->alreadyReportedError();
}
if (MOZ_UNLIKELY(!suffixTables_.initCapacity(1 << PrefixBitLength))) {
if (MOZ_UNLIKELY(
!suffixTables_.allocateUninitialized(cx, 1 << PrefixBitLength))) {
return cx->alreadyReportedError();
}
return Ok();
@ -2230,20 +2221,21 @@ JS::Result<Ok> MultiLookupHuffmanTable<Subtable, PrefixBitLength>::initStart(
template <typename Subtable, uint8_t PrefixBitLength>
JS::Result<Ok> MultiLookupHuffmanTable<Subtable, PrefixBitLength>::addSymbol(
uint32_t bits, uint8_t bitLength, const BinASTSymbol& value) {
size_t index, uint32_t bits, uint8_t bitLength, const BinASTSymbol& value) {
MOZ_ASSERT_IF(largestBitLength_ != 0, bitLength != 0);
MOZ_ASSERT(values_.empty() || values_.back().key().bitLength_ <= bitLength,
MOZ_ASSERT(index == 0 || values_[index - 1].key().bitLength_ <= bitLength,
"Symbols must be ranked by increasing bits length");
MOZ_ASSERT_IF(bitLength != 32 /* >> 32 is UB */, bits >> bitLength == 0);
values_.infallibleEmplaceBack(bits, bitLength, value);
new (mozilla::KnownNotNull, &values_[index])
HuffmanEntry(bits, bitLength, value);
return Ok();
}
template <typename Subtable, uint8_t PrefixBitLength>
JS::Result<Ok>
MultiLookupHuffmanTable<Subtable, PrefixBitLength>::initComplete() {
JS::Result<Ok> MultiLookupHuffmanTable<Subtable, PrefixBitLength>::initComplete(
JSContext* cx) {
// First, we need to collect the `largestBitLength_`
// and `numberofSymbols` for each subtable.
struct Bucket {
@ -2257,8 +2249,10 @@ MultiLookupHuffmanTable<Subtable, PrefixBitLength>::initComplete() {
}
}
};
Vector<Bucket> buckets{cx_};
BINJS_TRY(buckets.resize(1 << PrefixBitLength));
FixedLengthVector<Bucket> buckets;
if (!buckets.allocate(cx, 1 << PrefixBitLength)) {
return cx->alreadyReportedError();
}
Bucket shortKeysBucket;
for (const auto& entry : values_) {
@ -2283,31 +2277,44 @@ MultiLookupHuffmanTable<Subtable, PrefixBitLength>::initComplete() {
}
}
FixedLengthVector<size_t> suffixTablesIndices;
if (MOZ_UNLIKELY(!suffixTablesIndices.allocateUninitialized(
cx, suffixTables_.length()))) {
return cx->alreadyReportedError();
}
// We may now create the subtables.
size_t i = 0;
for (auto& bucket : buckets) {
Subtable sub(cx_);
new (mozilla::KnownNotNull, &suffixTables_[i]) Subtable(cx);
suffixTablesIndices[i] = 0;
if (bucket.numberOfSymbols_ != 0) {
// Often, a subtable will end up empty because all the prefixes end up
// in `shortKeys_`. In such a case, we want to avoid initializing the
// table.
MOZ_TRY(sub.initStart(cx_,
/* numberOfSymbols = */ bucket.numberOfSymbols_,
/* maxBitLength = */ bucket.largestBitLength_));
MOZ_TRY(suffixTables_[i].initStart(
cx,
/* numberOfSymbols = */ bucket.numberOfSymbols_,
/* maxBitLength = */ bucket.largestBitLength_));
}
BINJS_TRY(suffixTables_.append(std::move(sub)));
i++;
}
// Also, create the shortKeys_ fast lookup.
MOZ_TRY(shortKeys_.initStart(cx_, shortKeysBucket.numberOfSymbols_,
MOZ_TRY(shortKeys_.initStart(cx, shortKeysBucket.numberOfSymbols_,
shortKeysBucket.largestBitLength_));
// Now that all the subtables are created, let's dispatch the values
// among these tables.
size_t shortKeysIndex = 0;
for (size_t i = 0; i < values_.length(); ++i) {
const auto& entry = values_[i];
if (entry.key().bitLength_ <= SingleLookupHuffmanTable::MAX_BIT_LENGTH) {
// The key fits in `shortKeys_`, let's use this table.
MOZ_TRY(shortKeys_.addSymbol(entry.key().bits_, entry.key().bitLength_,
MOZ_TRY(shortKeys_.addSymbol(shortKeysIndex++, entry.key().bits_,
entry.key().bitLength_,
BinASTSymbol::fromSubtableIndex(i)));
continue;
}
@ -2321,20 +2328,21 @@ MultiLookupHuffmanTable<Subtable, PrefixBitLength>::initComplete() {
auto& sub = suffixTables_[index];
// We may now add a reference to `entry` into the sybtable.
MOZ_TRY(sub.addSymbol(split.suffix_.bits_, split.suffix_.bitLength_,
MOZ_TRY(sub.addSymbol(suffixTablesIndices[index]++, split.suffix_.bits_,
split.suffix_.bitLength_,
BinASTSymbol::fromSubtableIndex(i)));
}
}
// Finally, complete initialization of shortKeys_ and subtables.
MOZ_TRY(shortKeys_.initComplete());
MOZ_TRY(shortKeys_.initComplete(cx));
for (size_t i = 0; i < buckets.length(); ++i) {
if (buckets[i].numberOfSymbols_ == 0) {
// Again, we don't want to initialize empty subtables.
continue;
}
auto& sub = suffixTables_[i];
MOZ_TRY(sub.initComplete());
MOZ_TRY(sub.initComplete(cx));
}
return Ok();

Просмотреть файл

@ -17,6 +17,7 @@
#include <stddef.h> // size_t
#include <stdint.h> // uint8_t, uint32_t
#include "ds/FixedLengthVector.h" // FixedLengthVector
#include "frontend/BinASTRuntimeSupport.h" // CharSlice, BinASTVariant, BinASTKind, BinASTField, BinASTSourceMetadata
#include "frontend/BinASTToken.h"
#include "frontend/BinASTTokenReaderBase.h" // BinASTTokenReaderBase, SkippableSubTree
@ -300,7 +301,7 @@ class SingleEntryHuffmanTable {
// An implementation of Huffman Tables for two-entry table.
class TwoEntriesHuffmanTable {
public:
TwoEntriesHuffmanTable() : length_(0) {}
TwoEntriesHuffmanTable() {}
TwoEntriesHuffmanTable(TwoEntriesHuffmanTable&& other) noexcept = default;
// Initialize a Huffman table containing `numberOfSymbols`.
@ -310,10 +311,11 @@ class TwoEntriesHuffmanTable {
JS::Result<Ok> initStart(JSContext* cx, size_t numberOfSymbols,
uint8_t maxBitLength);
JS::Result<Ok> initComplete();
JS::Result<Ok> initComplete(JSContext* cx);
// Add a symbol to a value.
JS::Result<Ok> addSymbol(uint32_t bits, uint8_t bitLength,
// The symbol is the `index`-th item in this table.
JS::Result<Ok> addSymbol(size_t index, uint32_t bits, uint8_t bitLength,
const BinASTSymbol& value);
TwoEntriesHuffmanTable(TwoEntriesHuffmanTable&) = delete;
@ -343,31 +345,16 @@ class TwoEntriesHuffmanTable {
const BinASTSymbol* position_;
};
Iterator begin() const { return Iterator(std::begin(values_)); }
Iterator end() const {
MOZ_ASSERT(length_ == 2);
return Iterator(std::end(values_));
}
Iterator end() const { return Iterator(std::end(values_)); }
// The number of values in the table.
size_t length() const {
MOZ_ASSERT(length_ == 2);
return 2;
}
size_t length() const { return 2; }
private:
// A buffer for the values added to this table.
//
// Only the interval 0..length_ is considered
// initialized memory.
BinASTSymbol values_[2] = {BinASTSymbol::fromBool(false),
BinASTSymbol::fromBool(false)};
// The number of elements added to the table.
// Invariants:
// - during initialization, 0 <= length_ <= 2;
// - after initialization, length_ == 2;
uint8_t length_;
friend class HuffmanPreludeReader;
};
@ -466,9 +453,7 @@ class SingleLookupHuffmanTable {
explicit SingleLookupHuffmanTable(
JSContext* cx, Use use = Use::LeafOfMultiLookupHuffmanTable)
: values_(cx),
saturated_(cx),
largestBitLength_(-1)
: largestBitLength_(-1)
#ifdef DEBUG
,
use_(use)
@ -484,10 +469,11 @@ class SingleLookupHuffmanTable {
JS::Result<Ok> initStart(JSContext* cx, size_t numberOfSymbols,
uint8_t maxBitLength);
JS::Result<Ok> initComplete();
JS::Result<Ok> initComplete(JSContext* cx);
// Add a `(bit, bitLength) => value` mapping.
JS::Result<Ok> addSymbol(uint32_t bits, uint8_t bitLength,
// The symbol is the `index`-th item in this table.
JS::Result<Ok> addSymbol(size_t index, uint32_t bits, uint8_t bitLength,
const BinASTSymbol& value);
SingleLookupHuffmanTable() = delete;
@ -530,14 +516,14 @@ class SingleLookupHuffmanTable {
// Invariant (once `init*` has been called):
// - Length is the number of values inserted in the table.
// - for all i, `values_[i].bitLength_ <= largestBitLength_`.
Vector<HuffmanEntry> values_;
FixedLengthVector<HuffmanEntry> values_;
// The entries in this Huffman table, prepared for lookup.
//
// Invariant (once `init*` has been called):
// - Length is `1 << largestBitLength_`.
// - for all i, `saturated_[i] < values_.length()`
Vector<InternalIndex> saturated_;
FixedLengthVector<InternalIndex> saturated_;
// The maximal bitlength of a value in this table.
//
@ -696,8 +682,6 @@ class MultiLookupHuffmanTable {
explicit MultiLookupHuffmanTable(JSContext* cx)
: cx_(cx),
shortKeys_(cx, SingleLookupHuffmanTable::Use::ShortKeys),
values_(cx),
suffixTables_(cx),
largestBitLength_(-1) {}
MultiLookupHuffmanTable(MultiLookupHuffmanTable&& other) = default;
@ -708,10 +692,11 @@ class MultiLookupHuffmanTable {
JS::Result<Ok> initStart(JSContext* cx, size_t numberOfSymbols,
uint8_t largestBitLength);
JS::Result<Ok> initComplete();
JS::Result<Ok> initComplete(JSContext* cx);
// Add a `(bit, bitLength) => value` mapping.
JS::Result<Ok> addSymbol(uint32_t bits, uint8_t bitLength,
// The symbol is the `index`-th item in this table.
JS::Result<Ok> addSymbol(size_t index, uint32_t bits, uint8_t bitLength,
const BinASTSymbol& value);
MultiLookupHuffmanTable() = delete;
@ -769,7 +754,7 @@ class MultiLookupHuffmanTable {
//
// FIXME: In a ThreeLookupsHuffmanTable, we currently store each value
// three times. We could at least get down to twice.
Vector<HuffmanEntry> values_;
FixedLengthVector<HuffmanEntry> values_;
// A mapping from 0..2^prefixBitLen such that index `i`
// maps to a subtable that holds all values associated
@ -778,7 +763,7 @@ class MultiLookupHuffmanTable {
// Note that, to allow the use of smaller tables, keys
// inside the subtables have been stripped
// from the prefix `HuffmanKey(i, prefixBitLen)`.
Vector<Subtable> suffixTables_;
FixedLengthVector<Subtable> suffixTables_;
// The maximal bitlength of a value in this table.
//
@ -823,10 +808,11 @@ struct GenericHuffmanTable {
uint8_t maxBitLength);
// Add a `(bit, bitLength) => value` mapping.
JS::Result<Ok> addSymbol(uint32_t bits, uint8_t bitLength,
// The symbol is the `index`-th item in this table.
JS::Result<Ok> addSymbol(size_t index, uint32_t bits, uint8_t bitLength,
const BinASTSymbol& value);
JS::Result<Ok> initComplete();
JS::Result<Ok> initComplete(JSContext* cx);
// The number of values in the table.
size_t length() const;