Backed out 5 changesets (bug 1579862) for causing build bustages CLOSED TREE

Backed out changeset ebb8ad9cd82e (bug 1579862)
Backed out changeset bbddaf060c98 (bug 1579862)
Backed out changeset e288a597f967 (bug 1579862)
Backed out changeset 6ce3c1949f8b (bug 1579862)
Backed out changeset 04c6be2f7cfa (bug 1579862)
This commit is contained in:
Ciure Andrei 2019-09-13 09:06:19 +03:00
Родитель 40f368c95e
Коммит 86c41a5975
2 изменённых файлов: 101 добавлений и 972 удалений

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

@ -43,30 +43,12 @@ const uint32_t MAGIC_FORMAT_VERSION = 2;
// Hardcoded in the format.
const uint8_t MAX_CODE_BIT_LENGTH = 20;
// The maximal number of bits needed to represent bit lengths.
const uint8_t MAX_BIT_LENGTH_BIT_LENGTH = 5;
static_assert(1 << (MAX_BIT_LENGTH_BIT_LENGTH - 1) < MAX_CODE_BIT_LENGTH,
"MAX_BIT_LENGTH_BIT_LENGTH - 1 bits MUST be insufficient to "
"store MAX_CODE_BIT_LENGTH");
static_assert(
1 << MAX_BIT_LENGTH_BIT_LENGTH >= MAX_CODE_BIT_LENGTH,
"MAX_BIT_LENGTH bits MUST be sufficient to store MAX_CODE_BIT_LENGTH");
// The maximal length of a Huffman prefix.
//
// This is distinct from MAX_CODE_BIT_LENGTH as we commonly load
// more bites than necessary, just to be on the safe side.
const uint8_t MAX_PREFIX_BIT_LENGTH = 32;
// Maximal bit length acceptable in a `HuffmanTableSaturated`.
//
// As `HuffmanTableSaturated` require O(2 ^ max bit len) space, we
// cannot afford to use them for all tables. Whenever the max bit
// length in the table is <= MAX_BIT_LENGTH_IN_SATURATED_TABLE,
// we use a `HuffmanTableSaturated`. Otherwise, we fall back to
// a slower but less memory-hungry solution.
const uint8_t MAX_BIT_LENGTH_IN_SATURATED_TABLE = 10;
// The length of the bit buffer, in bits.
const uint8_t BIT_BUFFER_SIZE = 64;
@ -585,10 +567,7 @@ class HuffmanPreludeReader {
BINJS_MOZ_TRY_DECL(
symbol, readSymbol<Entry>(entry, /* First and only value */ 0));
MOZ_TRY(table.initWithSingleValue(cx_, std::move(symbol)));
#ifdef DEBUG
table.selfCheck();
#endif // DEBUG
MOZ_TRY(table.impl.initWithSingleValue(cx_, std::move(symbol)));
return Ok();
}
@ -617,17 +596,12 @@ class HuffmanPreludeReader {
auxStorageLength.empty()); // We must have cleaned it up properly.
BINJS_TRY(auxStorageLength.reserve(numberOfSymbols + 1));
uint8_t maxBitLength = 0;
// First read and store the bit lengths for all symbols.
for (size_t i = 0; i < numberOfSymbols; ++i) {
BINJS_MOZ_TRY_DECL(bitLength, reader.readByte<Compression::No>());
if (bitLength == 0) {
return raiseInvalidTableData(entry.identity);
}
if (bitLength > maxBitLength) {
maxBitLength = bitLength;
}
BINJS_TRY(auxStorageLength.append(BitLengthAndIndex(bitLength, i)));
}
// Append a terminator.
@ -636,7 +610,7 @@ class HuffmanPreludeReader {
// Now read the symbols and assign bits.
uint32_t code = 0;
MOZ_TRY(table.init(cx_, numberOfSymbols, maxBitLength));
MOZ_TRY(table.impl.init(cx_, numberOfSymbols));
for (size_t i = 0; i < numberOfSymbols; ++i) {
const auto bitLength = auxStorageLength[i].bitLength;
@ -653,15 +627,12 @@ 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, std::move(symbol)));
MOZ_TRY(table.impl.addSymbol(code, bitLength, std::move(symbol)));
// Prepare next code.
code = (code + 1) << (nextBitLength - bitLength);
}
#ifdef DEBUG
table.selfCheck();
#endif // DEBUG
auxStorageLength.clear();
return Ok();
}
@ -682,21 +653,16 @@ class HuffmanPreludeReader {
// - Lengths MAY be 0.
// - Lengths are not sorted on disk.
uint8_t maxBitLength = 0;
// First read the length for all symbols, only store non-0 lengths.
for (size_t i = 0; i < numberOfSymbols; ++i) {
BINJS_MOZ_TRY_DECL(bitLength, reader.readByte<Compression::No>());
if (bitLength > MAX_CODE_BIT_LENGTH) {
MOZ_CRASH("FIXME: Implement error");
}
if (bitLength > 0) {
BINJS_TRY(auxStorageLength.append(BitLengthAndIndex(bitLength, i)));
if (bitLength > maxBitLength) {
maxBitLength = bitLength;
}
}
}
// Append a terminator.
BINJS_TRY(auxStorageLength.append(
BitLengthAndIndex(MAX_CODE_BIT_LENGTH, numberOfSymbols)));
// Sort by length then webidl order (which is also the index).
std::sort(auxStorageLength.begin(), auxStorageLength.end(),
@ -713,14 +679,13 @@ class HuffmanPreludeReader {
// In case of equal bit length, compare by symbol value.
return entry.lessThan(a.index, b.index);
});
// Append a terminator.
BINJS_TRY(
auxStorageLength.emplaceBack(MAX_CODE_BIT_LENGTH, numberOfSymbols));
MOZ_ASSERT(
auxStorageLength[auxStorageLength.length() - 1].bitLength ==
MAX_CODE_BIT_LENGTH); // Guaranteed to exist as we have a terminator.
// Now read the symbols and assign bits.
uint32_t code = 0;
MOZ_TRY(table.init(cx_, auxStorageLength.length() - 1, maxBitLength));
MOZ_TRY(table.impl.init(cx_, auxStorageLength.length() - 1));
for (size_t i = 0; i < auxStorageLength.length() - 1; ++i) {
const auto bitLength = auxStorageLength[i].bitLength;
@ -737,16 +702,12 @@ class HuffmanPreludeReader {
entry,
auxStorageLength[i].index)); // Symbol is read from memory.
MOZ_TRY(table.addSymbol(code, bitLength, std::move(symbol)));
MOZ_TRY(table.impl.addSymbol(code, bitLength, std::move(symbol)));
// Prepare next code.
code = (code + 1) << (nextBitLength - bitLength);
}
#ifdef DEBUG
table.selfCheck();
#endif // DEBUG
auxStorageLength.clear();
return Ok();
}
@ -756,7 +717,7 @@ class HuffmanPreludeReader {
template <typename Entry>
MOZ_MUST_USE JS::Result<Ok> readTable(Entry entry) {
auto& table = dictionary.tableForField(entry.identity);
return readTable<HuffmanTableValue, Entry>(table, entry);
return readTable<HuffmanTable, Entry>(table, entry);
}
// Two-arguments version: pass table explicitly. Generally called from single-
@ -857,14 +818,12 @@ class HuffmanPreludeReader {
// a. Determine if the array type is always empty
// b. If so, stop
auto& lengthTable = table.as<HuffmanTableExplicitSymbolsListLength>();
bool empty = true;
for (auto iter : lengthTable) {
if (*iter > 0) {
empty = false;
break;
}
uint32_t length = 0;
for (const auto& iter : lengthTable.impl) {
length += iter.value;
}
if (empty) {
if (length == 0) {
return Ok();
}
@ -898,11 +857,7 @@ class HuffmanPreludeReader {
if (table.is<HuffmanTableUnreachable>()) {
// Effectively, an `Interface` is a sum with a single entry.
HuffmanTableIndexedSymbolsSum sum(cx_);
MOZ_TRY(sum.initWithSingleValue(cx_, BinASTKind(interface.kind)));
#ifdef DEBUG
sum.selfCheck();
#endif // DEBUG
MOZ_TRY(sum.impl.initWithSingleValue(cx_, BinASTKind(interface.kind)));
table = {mozilla::VariantType<HuffmanTableIndexedSymbolsSum>{},
std::move(sum)};
@ -1002,10 +957,10 @@ class HuffmanPreludeReader {
}
const auto& tableRef = table.as<HuffmanTableIndexedSymbolsSum>();
for (auto iter : tableRef) {
for (const auto& kind : tableRef.impl) {
MOZ_TRY(owner.pushValue(
entry.identity,
{mozilla::VariantType<Interface>(), entry.identity, *iter}));
{mozilla::VariantType<Interface>(), entry.identity, kind.value}));
}
return Ok();
}
@ -1027,10 +982,10 @@ class HuffmanPreludeReader {
}
const auto& tableRef = table.as<HuffmanTableIndexedSymbolsSum>();
for (auto iter : tableRef) {
for (const auto& kind : tableRef.impl) {
MOZ_TRY(owner.pushValue(
entry.identity,
{mozilla::VariantType<Interface>(), entry.identity, *iter}));
{mozilla::VariantType<Interface>(), entry.identity, kind.value}));
}
return Ok();
}
@ -1388,7 +1343,7 @@ struct TagReader {
JS::Result<BinASTKind> operator()(
const HuffmanTableIndexedSymbolsSum& specialized) {
// We're entering either a single interface or a sum.
const auto lookup = specialized.lookup(bits);
const auto lookup = specialized.impl.lookup(bits);
bitBuffer.advanceBitBuffer<Compression::No>(lookup.key.bitLength);
if (!lookup.value) {
return owner.raiseInvalidValue(context);
@ -1398,7 +1353,7 @@ struct TagReader {
JS::Result<BinASTKind> operator()(
const HuffmanTableIndexedSymbolsMaybeInterface& specialized) {
// We're entering an optional interface.
const auto lookup = specialized.lookup(bits);
const auto lookup = specialized.impl.lookup(bits);
bitBuffer.advanceBitBuffer<Compression::No>(lookup.key.bitLength);
if (!lookup.value) {
return owner.raiseInvalidValue(context);
@ -1435,7 +1390,7 @@ BinASTTokenReaderContext::readFieldFromTable(const Context& context) {
return raiseNotInPrelude(context);
}
BINJS_MOZ_TRY_DECL(bits, bitBuffer.getHuffmanLookup<Compression::No>(*this));
const auto lookup = table.as<Table>().lookup(bits);
const auto lookup = table.as<Table>().impl.lookup(bits);
bitBuffer.advanceBitBuffer<Compression::No>(lookup.key.bitLength);
if (!lookup.value) {
@ -1533,7 +1488,7 @@ JS::Result<Ok> BinASTTokenReaderContext::enterList(uint32_t& items,
BINJS_MOZ_TRY_DECL(bits, bitBuffer.getHuffmanLookup<Compression::No>(*this));
const auto& tableForLookup =
table.as<HuffmanTableExplicitSymbolsListLength>();
const auto lookup = tableForLookup.lookup(bits);
const auto lookup = tableForLookup.impl.lookup(bits);
bitBuffer.advanceBitBuffer<Compression::No>(lookup.key.bitLength);
if (!lookup.value) {
return raiseInvalidValue(context);
@ -1616,229 +1571,9 @@ JS::Result<Ok> BinASTTokenReaderContext::AutoTaggedTuple::done() {
return Ok();
}
HuffmanKey::HuffmanKey(const uint32_t bits, const uint8_t bitLength)
: bits(bits), bitLength(bitLength) {
MOZ_ASSERT(bitLength <= MAX_PREFIX_BIT_LENGTH);
MOZ_ASSERT_IF(bitLength != 32 /* >> 32 is UB */, bits >> bitLength == 0);
}
FlatHuffmanKey::FlatHuffmanKey(HuffmanKey key)
: representation((key.bitLength << MAX_CODE_BIT_LENGTH) | key.bits) {
static_assert(MAX_CODE_BIT_LENGTH + MAX_BIT_LENGTH_BIT_LENGTH <= 32,
"32 bits MUST be sufficient to store bits and bitLength");
MOZ_ASSERT(key.bits >> MAX_CODE_BIT_LENGTH == 0);
MOZ_ASSERT(key.bitLength >> MAX_BIT_LENGTH_BIT_LENGTH == 0);
}
FlatHuffmanKey::FlatHuffmanKey(const HuffmanKey* key)
: representation((key->bitLength << MAX_CODE_BIT_LENGTH) | key->bits) {
static_assert(MAX_CODE_BIT_LENGTH + MAX_BIT_LENGTH_BIT_LENGTH <= 32,
"32 bits MUST be sufficient to store bits and bitLength");
MOZ_ASSERT(key->bits >> MAX_CODE_BIT_LENGTH == 0);
MOZ_ASSERT(key->bitLength >> MAX_BIT_LENGTH_BIT_LENGTH == 0);
}
// ---- Implementation of Huffman Tables
template <typename T>
HuffmanTableImplementationGeneric<T>::Iterator::Iterator(
typename HuffmanTableImplementationSaturated<T>::Iterator&& iterator)
: implementation(std::move(iterator)) {}
template <typename T>
HuffmanTableImplementationGeneric<T>::Iterator::Iterator(
typename HuffmanTableImplementationMap<T>::Iterator&& iterator)
: implementation(std::move(iterator)) {}
template <typename T>
void HuffmanTableImplementationGeneric<T>::Iterator::operator++() {
implementation.match(
[](typename HuffmanTableImplementationSaturated<T>::Iterator& iterator) {
iterator.operator++();
},
[](typename HuffmanTableImplementationMap<T>::Iterator& iterator) {
iterator.operator++();
});
}
template <typename T>
bool HuffmanTableImplementationGeneric<T>::Iterator::operator==(
const HuffmanTableImplementationGeneric<T>::Iterator& other) const {
return implementation.match(
[other](const typename HuffmanTableImplementationSaturated<T>::Iterator&
iterator) {
return iterator ==
other.implementation.template as<
typename HuffmanTableImplementationSaturated<T>::Iterator>();
},
[other](
const typename HuffmanTableImplementationMap<T>::Iterator& iterator) {
return iterator ==
other.implementation.template as<
typename HuffmanTableImplementationMap<T>::Iterator>();
});
}
template <typename T>
bool HuffmanTableImplementationGeneric<T>::Iterator::operator!=(
const HuffmanTableImplementationGeneric<T>::Iterator& other) const {
return implementation.match(
[other](const typename HuffmanTableImplementationSaturated<T>::Iterator&
iterator) {
return iterator !=
other.implementation.template as<
typename HuffmanTableImplementationSaturated<T>::Iterator>();
},
[other](
const typename HuffmanTableImplementationMap<T>::Iterator& iterator) {
return iterator !=
other.implementation.template as<
typename HuffmanTableImplementationMap<T>::Iterator>();
});
}
template <typename T>
const T* HuffmanTableImplementationGeneric<T>::Iterator::operator*() const {
return implementation.match(
[](const typename HuffmanTableImplementationSaturated<T>::Iterator&
iterator) { return iterator.operator*(); },
[](const typename HuffmanTableImplementationMap<T>::Iterator& iterator) {
return iterator.operator*();
});
}
template <typename T>
HuffmanTableImplementationGeneric<T>::HuffmanTableImplementationGeneric(
JSContext*)
: implementation(HuffmanTableUnreachable{}) {}
#ifdef DEBUG
template <typename T>
void HuffmanTableImplementationGeneric<T>::selfCheck() {
this->implementation.match(
[](HuffmanTableImplementationSaturated<T>& implementation) {
implementation.selfCheck();
},
[](HuffmanTableImplementationMap<T>& implementation) {
implementation.selfCheck();
},
[](HuffmanTableUnreachable&) {
MOZ_CRASH("HuffmanTableImplementationGeneric is unitialized!");
});
}
#endif // DEBUG
template <typename T>
typename HuffmanTableImplementationGeneric<T>::Iterator
HuffmanTableImplementationGeneric<T>::begin() const {
return this->implementation.match(
[](const HuffmanTableImplementationSaturated<T>& implementation)
-> HuffmanTableImplementationGeneric<T>::Iterator {
return implementation.begin();
},
[](const HuffmanTableImplementationMap<T>& implementation)
-> HuffmanTableImplementationGeneric<T>::Iterator {
return implementation.begin();
},
[](const HuffmanTableUnreachable&)
-> HuffmanTableImplementationGeneric<T>::Iterator {
MOZ_CRASH("HuffmanTableImplementationGeneric is unitialized!");
});
}
template <typename T>
typename HuffmanTableImplementationGeneric<T>::Iterator
HuffmanTableImplementationGeneric<T>::end() const {
return this->implementation.match(
[](const HuffmanTableImplementationSaturated<T>& implementation)
-> HuffmanTableImplementationGeneric<T>::Iterator {
return implementation.end();
},
[](const HuffmanTableImplementationMap<T>& implementation)
-> HuffmanTableImplementationGeneric<T>::Iterator {
return implementation.end();
},
[](const HuffmanTableUnreachable&)
-> HuffmanTableImplementationGeneric<T>::Iterator {
MOZ_CRASH("HuffmanTableImplementationGeneric is unitialized!");
});
}
template <typename T>
JS::Result<Ok> HuffmanTableImplementationGeneric<T>::initWithSingleValue(
JSContext* cx, T&& value) {
// Only one value: use HuffmanImplementationSaturated
MOZ_ASSERT(this->implementation.template is<
HuffmanTableUnreachable>()); // Make sure that we're initializing.
this->implementation = {
mozilla::VariantType<HuffmanTableImplementationSaturated<T>>{}, cx};
MOZ_TRY(
this->implementation.template as<HuffmanTableImplementationSaturated<T>>()
.initWithSingleValue(cx, std::move(value)));
return Ok();
}
template <typename T>
JS::Result<Ok> HuffmanTableImplementationGeneric<T>::init(
JSContext* cx, size_t numberOfSymbols, uint8_t maxBitLength) {
MOZ_ASSERT(this->implementation.template is<
HuffmanTableUnreachable>()); // Make sure that we're initializing.
if (maxBitLength > MAX_BIT_LENGTH_IN_SATURATED_TABLE ||
numberOfSymbols > 256) {
this->implementation = {
mozilla::VariantType<HuffmanTableImplementationMap<T>>{}, cx};
MOZ_TRY(this->implementation.template as<HuffmanTableImplementationMap<T>>()
.init(cx, numberOfSymbols, maxBitLength));
} else {
this->implementation = {
mozilla::VariantType<HuffmanTableImplementationSaturated<T>>{}, cx};
MOZ_TRY(this->implementation
.template as<HuffmanTableImplementationSaturated<T>>()
.init(cx, numberOfSymbols, maxBitLength));
}
return Ok();
}
template <typename T>
JS::Result<Ok> HuffmanTableImplementationGeneric<T>::addSymbol(
uint32_t bits, uint8_t bitLength, T&& value) {
return this->implementation.match(
[bits, bitLength, value = std::move(value)](
HuffmanTableImplementationSaturated<T>&
implementation) mutable /* discard implicit const */
-> JS::Result<Ok> {
return implementation.addSymbol(bits, bitLength, std::move(value));
},
[bits, bitLength, value = std::move(value)](
HuffmanTableImplementationMap<T>&
implementation) mutable /* discard implicit const */
-> JS::Result<Ok> {
return implementation.addSymbol(bits, bitLength, std::move(value));
},
[](HuffmanTableUnreachable&) -> JS::Result<Ok> {
MOZ_CRASH("HuffmanTableImplementationGeneric is unitialized!");
return Ok();
});
}
template <typename T>
HuffmanEntry<const T*> HuffmanTableImplementationGeneric<T>::lookup(
HuffmanLookup lookup) const {
return this->implementation.match(
[lookup](const HuffmanTableImplementationSaturated<T>& implementation)
-> HuffmanEntry<const T*> { return implementation.lookup(lookup); },
[lookup](const HuffmanTableImplementationMap<T>& implementation)
-> HuffmanEntry<const T*> { return implementation.lookup(lookup); },
[](const HuffmanTableUnreachable&) -> HuffmanEntry<const T*> {
MOZ_CRASH("HuffmanTableImplementationGeneric is unitialized!");
});
}
template <typename T, int N>
JS::Result<Ok> HuffmanTableImplementationNaive<T, N>::initWithSingleValue(
JSContext* cx, T&& value) {
JS::Result<Ok> HuffmanTableImpl<T, N>::initWithSingleValue(JSContext* cx,
T&& value) {
MOZ_ASSERT(values.empty()); // Make sure that we're initializing.
if (!values.append(HuffmanEntry<T>(0, 0, std::move(value)))) {
return cx->alreadyReportedError();
@ -1847,8 +1582,8 @@ JS::Result<Ok> HuffmanTableImplementationNaive<T, N>::initWithSingleValue(
}
template <typename T, int N>
JS::Result<Ok> HuffmanTableImplementationNaive<T, N>::init(
JSContext* cx, size_t numberOfSymbols, uint8_t) {
JS::Result<Ok> HuffmanTableImpl<T, N>::init(JSContext* cx,
size_t numberOfSymbols) {
MOZ_ASSERT(values.empty()); // Make sure that we're initializing.
if (!values.initCapacity(numberOfSymbols)) {
return cx->alreadyReportedError();
@ -1856,16 +1591,9 @@ JS::Result<Ok> HuffmanTableImplementationNaive<T, N>::init(
return Ok();
}
#ifdef DEBUG
template <typename T, int N>
void HuffmanTableImplementationNaive<T, N>::selfCheck() {
// Nothing to check
}
#endif // DEBUG
template <typename T, int N>
JS::Result<Ok> HuffmanTableImplementationNaive<T, N>::addSymbol(
uint32_t bits, uint8_t bitLength, T&& value) {
JS::Result<Ok> HuffmanTableImpl<T, N>::addSymbol(uint32_t bits,
uint8_t bitLength, T&& value) {
MOZ_ASSERT(bitLength != 0,
"Adding a symbol with a bitLength of 0 doesn't make sense.");
MOZ_ASSERT(values.empty() || values.back().key.bitLength <= bitLength,
@ -1879,8 +1607,7 @@ JS::Result<Ok> HuffmanTableImplementationNaive<T, N>::addSymbol(
}
template <typename T, int N>
HuffmanEntry<const T*> HuffmanTableImplementationNaive<T, N>::lookup(
HuffmanLookup key) const {
HuffmanEntry<const T*> HuffmanTableImpl<T, N>::lookup(HuffmanLookup key) const {
// This current implementation is O(length) and designed mostly for testing.
// Future versions will presumably adapt the underlying data structure to
// provide bounded-time lookup.
@ -1898,233 +1625,9 @@ HuffmanEntry<const T*> HuffmanTableImplementationNaive<T, N>::lookup(
}
}
// Error: no entry found.
return HuffmanEntry<const T*>(0, 0, nullptr);
}
template <typename T>
JS::Result<Ok> HuffmanTableImplementationMap<T>::initWithSingleValue(
JSContext* cx, T&& value) {
MOZ_ASSERT(values.empty()); // Make sure that we're initializing.
const HuffmanKey key(0, 0);
if (!values.put(FlatHuffmanKey(key), std::move(value)) || !keys.append(key)) {
ReportOutOfMemory(cx);
return cx->alreadyReportedError();
}
return Ok();
}
template <typename T>
JS::Result<Ok> HuffmanTableImplementationMap<T>::init(JSContext* cx,
size_t numberOfSymbols,
uint8_t) {
MOZ_ASSERT(values.empty()); // Make sure that we're initializing.
if (!values.reserve(numberOfSymbols) || !keys.reserve(numberOfSymbols)) {
ReportOutOfMemory(cx);
return cx->alreadyReportedError();
}
return Ok();
}
#ifdef DEBUG
template <typename T>
void HuffmanTableImplementationMap<T>::selfCheck() {
// Check that there is a bijection between `keys` and `values`.
// 1. Injection.
for (const auto& key : keys) {
if (!values.has(FlatHuffmanKey(key))) {
MOZ_CRASH();
}
}
// 2. Cardinality.
MOZ_ASSERT(values.count() == keys.length());
}
#endif // DEBUG
template <typename T>
JS::Result<Ok> HuffmanTableImplementationMap<T>::addSymbol(uint32_t bits,
uint8_t bitLength,
T&& value) {
MOZ_ASSERT(bitLength != 0,
"Adding a symbol with a bitLength of 0 doesn't make sense.");
MOZ_ASSERT_IF(bitLength != 32 /* >> 32 is UB */, bits >> bitLength == 0);
const HuffmanKey key(bits, bitLength);
const FlatHuffmanKey flat(key);
values.putNewInfallible(
flat, std::move(value)); // Memory was reserved in `init()`.
keys.infallibleAppend(std::move(key));
return Ok();
}
template <typename T>
HuffmanEntry<const T*> HuffmanTableImplementationMap<T>::lookup(
HuffmanLookup lookup) const {
for (auto bitLength = 0; bitLength < MAX_CODE_BIT_LENGTH; ++bitLength) {
const uint32_t bits = lookup.leadingBits(bitLength);
const HuffmanKey key(bits, bitLength);
const FlatHuffmanKey flat(key);
if (auto ptr = values.lookup(key)) {
// Entry found.
return HuffmanEntry<const T*>(bits, bitLength, &ptr->value());
}
}
// Error: no entry found.
return HuffmanEntry<const T*>(0, 0, nullptr);
}
template <typename T>
HuffmanTableImplementationSaturated<T>::Iterator::Iterator(
const HuffmanEntry<T>* position)
: position(position) {}
template <typename T>
void HuffmanTableImplementationSaturated<T>::Iterator::operator++() {
position++;
}
template <typename T>
const T* HuffmanTableImplementationSaturated<T>::Iterator::operator*() const {
return &position->value;
}
template <typename T>
bool HuffmanTableImplementationSaturated<T>::Iterator::operator==(
const Iterator& other) const {
return position == other.position;
}
template <typename T>
bool HuffmanTableImplementationSaturated<T>::Iterator::operator!=(
const Iterator& other) const {
return position != other.position;
}
template <typename T>
JS::Result<Ok> HuffmanTableImplementationSaturated<T>::initWithSingleValue(
JSContext* cx, T&& value) {
MOZ_ASSERT(values.empty()); // Make sure that we're initializing.
if (!values.emplaceBack(0, 0, std::move(value))) {
return cx->alreadyReportedError();
}
if (!saturated.emplaceBack(0)) {
return cx->alreadyReportedError();
}
this->maxBitLength = 0;
return Ok();
}
template <typename T>
JS::Result<Ok> HuffmanTableImplementationSaturated<T>::init(
JSContext* cx, size_t numberOfSymbols, uint8_t maxBitLength) {
MOZ_ASSERT(maxBitLength <= MAX_BIT_LENGTH_IN_SATURATED_TABLE);
MOZ_ASSERT(values.empty()); // Make sure that we're initializing.
this->maxBitLength = maxBitLength;
if (!values.initCapacity(numberOfSymbols)) {
return cx->alreadyReportedError();
}
const size_t saturatedLength = 1 << maxBitLength;
if (!saturated.initCapacity(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(uint8_t(-1));
}
return Ok();
}
#ifdef DEBUG
template <typename T>
void HuffmanTableImplementationSaturated<T>::selfCheck() {
// Double-check that we've initialized properly.
MOZ_ASSERT(this->maxBitLength <= MAX_CODE_BIT_LENGTH);
bool foundMaxBitLength = false;
for (size_t i = 0; i < saturated.length(); ++i) {
// Check that all indices have been properly initialized.
// Note: this is an explicit `for(;;)` loop instead of
// a `for(:)` range loop, as knowing `i` should simplify
// debugging.
const uint8_t index = saturated[i];
MOZ_ASSERT(index < values.length());
if (values[index].key.bitLength == maxBitLength) {
foundMaxBitLength = true;
}
}
MOZ_ASSERT(foundMaxBitLength);
}
#endif // DEBUG
template <typename T>
JS::Result<Ok> HuffmanTableImplementationSaturated<T>::addSymbol(
uint32_t bits, uint8_t bitLength, T&& value) {
MOZ_ASSERT(bitLength != 0,
"Adding a symbol with a bitLength of 0 doesn't make sense.");
MOZ_ASSERT(values.empty() || values.back().key.bitLength <= bitLength,
"Symbols must be ranked by increasing bits length");
MOZ_ASSERT_IF(bitLength != 32 /* >> 32 is UB */, bits >> bitLength == 0);
MOZ_ASSERT(bitLength <= maxBitLength);
const size_t index = values.length();
// First add the value to `values`.
if (!values.emplaceBack(bits, bitLength, std::move(value))) {
// Memory was reserved in `init()`.
MOZ_CRASH();
}
// Notation: in the following, unless otherwise specified, we consider
// values with `maxBitLength` bits exactly.
//
// When we perform lookup, we will extract `maxBitLength` bits from the key
// into a value `0bB...B`. We have a match for `value` if and only if
// `0bB...B` may be decomposed into `0bC...CX...X` such that
// - `0bC...C` is `bitLength` bits long;
// - `0bC...C == bits`.
//
// To perform a fast lookup, we precompute all possible values of `0bB...B`
// for which this condition is true. That's all the values of segment
// `[0bC...C0...0, 0bC...C1...1]`.
const uint8_t padding = maxBitLength - bitLength;
// `begin` holds `0bC...C0...0` above
const size_t begin = bits << padding;
// `length holds `0bC...C1...1` - `0bC...C0...0`
const size_t length =
((padding != 0) ? size_t(-1) >> (8 * sizeof(size_t) - padding) : 0) + 1;
for (size_t i = begin; i < begin + length; ++i) {
saturated[i] = index;
}
return Ok();
}
template <typename T>
HuffmanEntry<const T*> HuffmanTableImplementationSaturated<T>::lookup(
HuffmanLookup key) const {
// Take the `maxBitLength` highest weight bits of `key`.
// In the documentation of `addSymbol`, this is
// `0bB...B`.
const uint32_t bits = key.leadingBits(maxBitLength);
// Invariants: `saturated.length() == 1 << maxBitLength`
// and `bits <= 1 << maxBitLength`.
const size_t index = saturated[bits];
// Invariants: `saturated[i] < values.length()`.
const auto& entry = values[index];
return HuffmanEntry<const T*>(entry.key.bits, entry.key.bitLength,
&entry.value);
}
// -----
// The number of possible interfaces in each sum, indexed by
// `static_cast<size_t>(BinASTSum)`.
const size_t SUM_LIMITS[]{
@ -2233,10 +1736,7 @@ MOZ_MUST_USE JS::Result<Ok> HuffmanPreludeReader::readSingleValueTable<Boolean>(
return raiseInvalidTableData(entry.identity);
}
MOZ_TRY(table.initWithSingleValue(cx_, indexByte != 0));
#ifdef DEBUG
table.selfCheck();
#endif // DEBUG
MOZ_TRY(table.impl.initWithSingleValue(cx_, indexByte != 0));
return Ok();
}
@ -2271,11 +1771,8 @@ HuffmanPreludeReader::readSingleValueTable<MaybeInterface>(
return raiseInvalidTableData(entry.identity);
}
MOZ_TRY(table.initWithSingleValue(
MOZ_TRY(table.impl.initWithSingleValue(
cx_, indexByte == 0 ? BinASTKind::_Null : entry.kind));
#ifdef DEBUG
table.selfCheck();
#endif // DEBUG
return Ok();
}
@ -2307,10 +1804,7 @@ MOZ_MUST_USE JS::Result<Ok> HuffmanPreludeReader::readSingleValueTable<Sum>(
return raiseInvalidTableData(sum.identity);
}
MOZ_TRY(table.initWithSingleValue(cx_, sum.interfaceAt(index)));
#ifdef DEBUG
table.selfCheck();
#endif // DEBUG
MOZ_TRY(table.impl.initWithSingleValue(cx_, sum.interfaceAt(index)));
return Ok();
}
@ -2344,10 +1838,7 @@ HuffmanPreludeReader::readSingleValueTable<MaybeSum>(
return raiseInvalidTableData(sum.identity);
}
MOZ_TRY(table.initWithSingleValue(cx_, sum.interfaceAt(index)));
#ifdef DEBUG
table.selfCheck();
#endif // DEBUG
MOZ_TRY(table.impl.initWithSingleValue(cx_, sum.interfaceAt(index)));
return Ok();
}
@ -2391,12 +1882,9 @@ MOZ_MUST_USE JS::Result<Ok> HuffmanPreludeReader::readSingleValueTable<Number>(
BINJS_MOZ_TRY_DECL(value, readSymbol(number, 0 /* ignored */));
// Note: The `std::move` is useless for performance, but necessary to keep
// a consistent API.
MOZ_TRY(table.initWithSingleValue(
MOZ_TRY(table.impl.initWithSingleValue(
cx_,
/* NOLINT(performance-move-const-arg) */ std::move(value)));
#ifdef DEBUG
table.selfCheck();
#endif // DEBUG
return Ok();
}
@ -2433,10 +1921,7 @@ MOZ_MUST_USE JS::Result<Ok> HuffmanPreludeReader::readSingleValueTable<List>(
if (length > MAX_LIST_LENGTH) {
return raiseInvalidTableData(list.identity);
}
MOZ_TRY(table.initWithSingleValue(cx_, std::move(length)));
#ifdef DEBUG
table.selfCheck();
#endif // DEBUG
MOZ_TRY(table.impl.initWithSingleValue(cx_, std::move(length)));
return Ok();
}
@ -2477,12 +1962,9 @@ MOZ_MUST_USE JS::Result<Ok> HuffmanPreludeReader::readSingleValueTable<String>(
// Note: The `std::move` is useless for performance, but necessary to keep
// a consistent API.
JSAtom* value = reader.metadata_->getAtom(index);
MOZ_TRY(table.initWithSingleValue(
MOZ_TRY(table.impl.initWithSingleValue(
cx_,
/* NOLINT(performance-move-const-arg) */ std::move(value)));
#ifdef DEBUG
table.selfCheck();
#endif // DEBUG
return Ok();
}
@ -2529,12 +2011,9 @@ HuffmanPreludeReader::readSingleValueTable<MaybeString>(
JSAtom* symbol = index == 0 ? nullptr : reader.metadata_->getAtom(index - 1);
// Note: The `std::move` is useless for performance, but necessary to keep
// a consistent API.
MOZ_TRY(table.initWithSingleValue(
MOZ_TRY(table.impl.initWithSingleValue(
cx_,
/* NOLINT(performance-move-const-arg) */ std::move(symbol)));
#ifdef DEBUG
table.selfCheck();
#endif // DEBUG
return Ok();
}
@ -2568,12 +2047,9 @@ HuffmanPreludeReader::readSingleValueTable<StringEnum>(
BinASTVariant symbol = entry.variantAt(index);
// Note: The `std::move` is useless for performance, but necessary to keep
// a consistent API.
MOZ_TRY(table.initWithSingleValue(
MOZ_TRY(table.impl.initWithSingleValue(
cx_,
/* NOLINT(performance-move-const-arg) */ std::move(symbol)));
#ifdef DEBUG
table.selfCheck();
#endif // DEBUG
return Ok();
}
@ -2606,12 +2082,9 @@ HuffmanPreludeReader::readSingleValueTable<UnsignedLong>(
BINJS_MOZ_TRY_DECL(index, reader.readUnpackedLong());
// Note: The `std::move` is useless for performance, but necessary to keep
// a consistent API.
MOZ_TRY(table.initWithSingleValue(
MOZ_TRY(table.impl.initWithSingleValue(
cx_,
/* NOLINT(performance-move-const-arg) */ std::move(index)));
#ifdef DEBUG
table.selfCheck();
#endif // DEBUG
return Ok();
}
@ -2621,7 +2094,7 @@ HuffmanDictionary::HuffmanDictionary(JSContext* cx)
listLengths(BINAST_PARAM_NUMBER_OF_LIST_TYPES(
mozilla::AsVariant(HuffmanTableUnreachable()))) {}
HuffmanTableValue& HuffmanDictionary::tableForField(
HuffmanTable& HuffmanDictionary::tableForField(
NormalizedInterfaceAndField index) {
return fields[static_cast<size_t>(index.identity)];
}

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

@ -109,7 +109,11 @@ struct HuffmanKey {
// For instance, if the Huffman code is `0100`,
// - `bits = 0b0000_0000__0000_0000__0000_0000__0000_0100`;
// - `bitLength = 4`.
HuffmanKey(const uint32_t bits, const uint8_t bitLength);
HuffmanKey(const uint32_t bits, const uint8_t bitLength)
: bits(bits), bitLength(bitLength) {
MOZ_ASSERT(bitLength <= 32);
MOZ_ASSERT_IF(bitLength != 32 /* >> 32 is UB */, bits >> bitLength == 0);
}
// The buffer holding the bits.
//
@ -128,33 +132,6 @@ struct HuffmanKey {
const uint8_t bitLength;
};
// A Huffman key represented as a single `uint32_t`.
struct FlatHuffmanKey {
FlatHuffmanKey(HuffmanKey key);
FlatHuffmanKey(const HuffmanKey* key);
// 0b0000000L_LLLLCCCC_CCCCCCCC_CCCCCCCC
// Where:
// - `LLLLL` store `key.bitLength`
// - `CCCC_CCCCCCCC_CCCCCCCC` store `key.bits`
//
// While `key.bits` is nominally 32 bits, it is in fact
// `MAX_CODE_BIT_LENGTH` bits, padded with 0s in the
// highest bits.
const uint32_t representation;
// -- Implementing HashPolicy
using Lookup = FlatHuffmanKey;
using Key = Lookup;
static HashNumber hash(const Lookup& lookup) {
return mozilla::DefaultHasher<uint32_t>::hash(lookup.representation);
}
static bool match(const Key& key, const Lookup& lookup) {
return mozilla::DefaultHasher<uint32_t>::match(key.representation,
lookup.representation);
}
};
// An entry in a Huffman table.
template <typename T>
struct HuffmanEntry {
@ -166,7 +143,7 @@ struct HuffmanEntry {
const T value;
};
// The default inline buffer length for instances of HuffmanTableValue.
// The default inline buffer length for instances of HuffmanTable.
// Specific type (e.g. booleans) will override this to provide something
// more suited to their type.
const size_t HUFFMAN_TABLE_DEFAULT_INLINE_BUFFER_LENGTH = 8;
@ -178,14 +155,11 @@ enum class Nullable {
NonNull,
};
// An implementation of Huffman Tables as a vector, with `O(entries)`
// lookup. Performance-wise, this implementation only makes sense for
// very short tables.
template <typename T, int N = HUFFMAN_TABLE_DEFAULT_INLINE_BUFFER_LENGTH>
class HuffmanTableImplementationNaive {
class HuffmanTableImpl {
public:
explicit HuffmanTableImplementationNaive(JSContext* cx) : values(cx) {}
HuffmanTableImplementationNaive(HuffmanTableImplementationNaive&& other)
explicit HuffmanTableImpl(JSContext* cx) : values(cx) {}
HuffmanTableImpl(HuffmanTableImpl&& other) noexcept
: values(std::move(other.values)) {}
// Initialize a Huffman table containing a single value.
@ -193,27 +167,22 @@ class HuffmanTableImplementationNaive {
// Initialize a Huffman table containing `numberOfSymbols`.
// Symbols must be added with `addSymbol`.
JS::Result<Ok> init(JSContext* cx, size_t numberOfSymbols,
uint8_t largestBitLength);
JS::Result<Ok> init(JSContext* cx, size_t numberOfSymbols);
// Add a symbol to a value.
JS::Result<Ok> addSymbol(uint32_t bits, uint8_t bits_length, T&& value);
HuffmanTableImplementationNaive() = delete;
HuffmanTableImplementationNaive(HuffmanTableImplementationNaive&) = delete;
#ifdef DEBUG
void selfCheck();
#endif // DEBUG
HuffmanTableImpl() = delete;
HuffmanTableImpl(HuffmanTableImpl&) = delete;
// Lookup a value in the table.
//
// Return an entry with a value of `nullptr` if the value is not in the table.
//
// The lookup may advance `key` by `[0, key.bitLength]` bits. Typically, in a
// table with a single instance, or if the value is not in the table, it
// will advance by 0 bits. The caller is responsible for advancing its
// bitstream by `result.key.bitLength` bits.
// The lookup may consume `[0, key.bitLength]` bits of `key`. Typically, in a
// table with a single instance, or if the value is not in the table, 0 bits
// will be consumed. The caller is responsible for advancing its bitstream by
// `result.key.bitLength` bits.
HuffmanEntry<const T*> lookup(HuffmanLookup key) const;
// The number of values in the table.
@ -231,403 +200,91 @@ class HuffmanTableImplementationNaive {
friend class HuffmanPreludeReader;
};
// An implementation of Huffman Tables as a hash map. Space-Efficient,
// faster than HuffmanTableImplementationNaive for large tables but not terribly
// fast, either.
//
// Complexity:
//
// - We assume that hashing is sufficient to guarantee `O(1)` lookups
// inside the hashmap.
// - On a well-formed file, all lookups are successful and a Huffman
// lookup will take exactly `bitLen` Hashmap lookups. This makes it
// `O(MAX_CODE_BIT_LENGTH)` worst case. This also makes it
// `O(ln(N))` in the best case (perfectly balanced Huffman table)
// and `O(N)` in the worst case (perfectly linear Huffman table),
// where `N` is the number of entries.
// - On an invalid file, the number of lookups is also bounded by
// `MAX_CODE_BIT_LENGTH`.
template <typename T>
class HuffmanTableImplementationMap {
public:
explicit HuffmanTableImplementationMap(JSContext* cx)
: values(cx), keys(cx) {}
HuffmanTableImplementationMap(HuffmanTableImplementationMap&& other) noexcept
: values(std::move(other.values)), keys(std::move(other.keys)) {}
// Initialize a Huffman table containing a single value.
JS::Result<Ok> initWithSingleValue(JSContext* cx, T&& value);
// Initialize a Huffman table containing `numberOfSymbols`.
// Symbols must be added with `addSymbol`.
JS::Result<Ok> init(JSContext* cx, size_t numberOfSymbols,
uint8_t largestBitLength);
// Add a `(bit, bits_length) => value` mapping.
JS::Result<Ok> addSymbol(uint32_t bits, uint8_t bits_length, T&& value);
#ifdef DEBUG
void selfCheck();
#endif // DEBUG
HuffmanTableImplementationMap() = delete;
HuffmanTableImplementationMap(HuffmanTableImplementationMap&) = delete;
// Lookup a value in the table.
//
// Return an entry with a value of `nullptr` if the value is not in the table.
//
// The lookup may advance `key` by `[0, key.bitLength]` bits. Typically, in a
// table with a single instance, or if the value is not in the table, it
// will advance by 0 bits. The caller is responsible for advancing its
// bitstream by `result.key.bitLength` bits.
HuffmanEntry<const T*> lookup(HuffmanLookup key) const;
// The number of values in the table.
size_t length() const { return values.length(); }
// Iterating in the order of insertion.
struct Iterator {
Iterator(const js::HashMap<FlatHuffmanKey, T, FlatHuffmanKey>& values,
const HuffmanKey* position)
: values(values), position(position) {}
void operator++() { ++position; }
const T* operator*() const {
const FlatHuffmanKey key(position);
if (const auto ptr = values.lookup(key)) {
return &ptr->value();
}
MOZ_CRASH();
}
bool operator==(const Iterator& other) const {
MOZ_ASSERT(&values == &other.values);
return position == other.position;
}
bool operator!=(const Iterator& other) const {
MOZ_ASSERT(&values == &other.values);
return position != other.position;
}
private:
const js::HashMap<FlatHuffmanKey, T, FlatHuffmanKey>& values;
const HuffmanKey* position;
};
Iterator begin() const { return Iterator(values, keys.begin()); }
Iterator end() const { return Iterator(values, keys.end()); }
private:
// The entries in this Huffman table, prepared for lookup.
js::HashMap<FlatHuffmanKey, T, FlatHuffmanKey> values;
// The entries in this Huffman Table, sorted in the order of insertion.
Vector<HuffmanKey> keys;
friend class HuffmanPreludeReader;
};
// An implementation of Huffman Tables as a vector designed to allow
// constant-time lookups at the expense of high space complexity.
//
// # Time complexity
//
// Lookups take constant time, which essentially consists in two
// simple vector lookups.
//
// # Space complexity
//
// After initialization, a `HuffmanTableImplementationSaturated`
// requires O(2 ^ max bit length in the table) space:
//
// - A vector `values` containing one entry per symbol.
// - A vector `saturated` containing exactly 2 ^ (max bit length in the
// table) entries, which we use to map any combination of `maxBitLength`
// bits onto the only `HuffmanEntry` that may be reached by a prefix
// of these `maxBitLength` bits. See below for more details.
//
// # Algorithm
//
// Consider the following Huffman table
//
// Symbol | Binary Code | Int value of Code | Bit Length
// ------ | ------------ | ----------------- | ----------
// A | 11000 | 24 | 5
// B | 11001 | 25 | 5
// C | 1101 | 13 | 4
// D | 100 | 4 | 3
// E | 101 | 5 | 3
// F | 111 | 7 | 3
// G | 00 | 0 | 2
// H | 01 | 1 | 2
//
// By definition of a Huffman Table, the Binary Codes represent
// paths in a Huffman Tree. Consequently, padding these codes
// to the end would not change the result.
//
// Symbol | Binary Code | Int value of Code | Bit Length
// ------ | ------------ | ----------------- | ----------
// A | 11000 | 24 | 5
// B | 11001 | 25 | 5
// C | 1101? | [26...27] | 4
// D | 100?? | [16...19] | 3
// E | 101?? | [20..23] | 3
// F | 111?? | [28..31] | 3
// G | 00??? | [0...7] | 2
// H | 01??? | [8...15] | 2
//
// Row "Int value of Code" now contains all possible values
// that may be expressed in 5 bits. By using these values
// as array indices, we may therefore represent the
// Huffman table as an array:
//
// Index | Symbol | Bit Length
// --------- | ---------- | -------------
// [0...7] | G | 2
// [8...15] | H | 2
// [16...19] | D | 3
// [20...23] | E | 3
// 24 | A | 5
// 25 | B | 5
// [26...27] | C | 4
// [28...31] | F | 3
//
// By using the next 5 bits in the bit buffer, we may, in
// a single lookup, determine the symbol and the bit length.
//
// In the current implementation, to save some space, we have
// two distinct arrays, one (`values`) with a single instance of each
// symbols bit length, and one (`saturated`) with indices into that
// array.
template <typename T>
class HuffmanTableImplementationSaturated {
public:
explicit HuffmanTableImplementationSaturated(JSContext* cx)
: values(cx), saturated(cx), maxBitLength(-1) {}
HuffmanTableImplementationSaturated(
HuffmanTableImplementationSaturated&& other) = default;
// Initialize a Huffman table containing a single value.
JS::Result<Ok> initWithSingleValue(JSContext* cx, T&& value);
// Initialize a Huffman table containing `numberOfSymbols`.
// Symbols must be added with `addSymbol`.
JS::Result<Ok> init(JSContext* cx, size_t numberOfSymbols,
uint8_t largestBitLength);
#ifdef DEBUG
void selfCheck();
#endif // DEBUG
// Add a `(bit, bits_length) => value` mapping.
JS::Result<Ok> addSymbol(uint32_t bits, uint8_t bits_length, T&& value);
HuffmanTableImplementationSaturated() = delete;
HuffmanTableImplementationSaturated(HuffmanTableImplementationSaturated&) =
delete;
// Lookup a value in the table.
//
// Return an entry with a value of `nullptr` if the value is not in the table.
//
// The lookup may advance `key` by `[0, key.bitLength]` bits. Typically, in a
// table with a single instance, or if the value is not in the table, it
// will advance by 0 bits. The caller is responsible for advancing its
// bitstream by `result.key.bitLength` bits.
HuffmanEntry<const T*> lookup(HuffmanLookup key) const;
// The number of values in the table.
size_t length() const { return values.length(); }
// Iterating in the order of insertion.
struct Iterator {
explicit Iterator(const HuffmanEntry<T>* position);
void operator++();
const T* operator*() const;
bool operator==(const Iterator& other) const;
bool operator!=(const Iterator& other) const;
private:
const HuffmanEntry<T>* position;
};
Iterator begin() const { return Iterator(values.begin()); }
Iterator end() const { return Iterator(values.end()); }
private:
// The entries in this Huffman Table, sorted in the order of insertion.
//
// Invariant (once `init*` has been called):
// - Length is the number of values inserted in the table.
// - for all i, `values[i].bitLength <= maxBitLength`.
Vector<HuffmanEntry<T>> values;
// The entries in this Huffman table, prepared for lookup.
// The `size_t` argument is an index into `values`.
//
// Invariant (once `init*` has been called):
// - Length is `1 << maxBitLength`.
// - for all i, `saturated[i] < values.length()`
Vector<uint8_t> saturated;
// The maximal bitlength of a value in this table.
//
// Invariant (once `init*` has been called):
// - `maxBitLength <= MAX_CODE_BIT_LENGTH`
uint8_t maxBitLength;
friend class HuffmanPreludeReader;
};
// An empty Huffman table. Attempting to get a value from this table is a syntax
// error. This is the default value for `HuffmanTableValue` and represents all
// states that may not be reached.
// error. This is the default value for `HuffmanTable` and represents all states
// that may not be reached.
//
// Part of variants `HuffmanTableValue`, `HuffmanTableListLength` and
// `HuffmanTableImplementationGeneric::implementation`.
// Part of variants `HuffmanTable` and `HuffmanTableListLength`.
struct HuffmanTableUnreachable {};
// Generic implementation of Huffman tables.
//
//
template <typename T>
struct HuffmanTableImplementationGeneric {
HuffmanTableImplementationGeneric(JSContext* cx);
HuffmanTableImplementationGeneric() = delete;
// Initialize a Huffman table containing a single value.
JS::Result<Ok> initWithSingleValue(JSContext* cx, T&& value);
// Initialize a Huffman table containing `numberOfSymbols`.
// Symbols must be added with `addSymbol`.
JS::Result<Ok> init(JSContext* cx, size_t numberOfSymbols,
uint8_t largestBitLength);
#ifdef DEBUG
void selfCheck();
#endif // DEBUG
// Add a `(bit, bits_length) => value` mapping.
JS::Result<Ok> addSymbol(uint32_t bits, uint8_t bits_length, T&& value);
// The number of values in the table.
size_t length() const;
struct Iterator {
Iterator(typename HuffmanTableImplementationSaturated<T>::Iterator&&);
Iterator(typename HuffmanTableImplementationMap<T>::Iterator&&);
void operator++();
const T* operator*() const;
bool operator==(const Iterator& other) const;
bool operator!=(const Iterator& other) const;
private:
mozilla::Variant<typename HuffmanTableImplementationSaturated<T>::Iterator,
typename HuffmanTableImplementationMap<T>::Iterator>
implementation;
};
// Iterating in the order of insertion.
Iterator begin() const;
Iterator end() const;
// Lookup a value in the table.
//
// Return an entry with a value of `nullptr` if the value is not in the table.
//
// The lookup may advance `key` by `[0, key.bitLength]` bits. Typically, in a
// table with a single instance, or if the value is not in the table, it
// will advance by 0 bits. The caller is responsible for advancing its
// bitstream by `result.key.bitLength` bits.
HuffmanEntry<const T*> lookup(HuffmanLookup key) const;
private:
mozilla::Variant<HuffmanTableImplementationSaturated<T>,
HuffmanTableImplementationMap<T>, HuffmanTableUnreachable>
implementation;
};
// While reading the Huffman prelude, whenever we first encounter a
// `HuffmanTableUnreachable`, we replace it with a `HuffmanTableInitializing`
// to mark that we should not attempt to read/initialize it again.
//
// Attempting to get a value from this table is an internal error.
//
// Part of variants `HuffmanTableValue` and `HuffmanTableListLength`.
// Part of variants `HuffmanTable` and `HuffmanTableListLength`.
struct HuffmanTableInitializing {};
// These classes are all parts of variant `HuffmanTableValue`.
// --- Explicit instantiations of `HuffmanTableImpl`.
// These classes are all parts of variant `HuffmanTable`.
struct HuffmanTableExplicitSymbolsF64
: HuffmanTableImplementationGeneric<double> {
struct HuffmanTableExplicitSymbolsF64 {
using Contents = double;
explicit HuffmanTableExplicitSymbolsF64(JSContext* cx)
: HuffmanTableImplementationGeneric(cx) {}
HuffmanTableImpl<double> impl;
explicit HuffmanTableExplicitSymbolsF64(JSContext* cx) : impl(cx) {}
};
struct HuffmanTableExplicitSymbolsU32
: HuffmanTableImplementationGeneric<uint32_t> {
struct HuffmanTableExplicitSymbolsU32 {
using Contents = uint32_t;
explicit HuffmanTableExplicitSymbolsU32(JSContext* cx)
: HuffmanTableImplementationGeneric(cx) {}
HuffmanTableImpl<uint32_t> impl;
explicit HuffmanTableExplicitSymbolsU32(JSContext* cx) : impl(cx) {}
};
struct HuffmanTableIndexedSymbolsSum
: HuffmanTableImplementationGeneric<BinASTKind> {
struct HuffmanTableIndexedSymbolsSum {
using Contents = BinASTKind;
explicit HuffmanTableIndexedSymbolsSum(JSContext* cx)
: HuffmanTableImplementationGeneric(cx) {}
HuffmanTableImpl<BinASTKind> impl;
explicit HuffmanTableIndexedSymbolsSum(JSContext* cx) : impl(cx) {}
};
struct HuffmanTableIndexedSymbolsBool
: HuffmanTableImplementationNaive<bool, 2> {
struct HuffmanTableIndexedSymbolsBool {
using Contents = bool;
explicit HuffmanTableIndexedSymbolsBool(JSContext* cx)
: HuffmanTableImplementationNaive(cx) {}
HuffmanTableImpl<bool, 2> impl;
explicit HuffmanTableIndexedSymbolsBool(JSContext* cx) : impl(cx) {}
};
// A Huffman table that may only ever contain two values:
// `BinASTKind::_Null` and another `BinASTKind`.
struct HuffmanTableIndexedSymbolsMaybeInterface
: HuffmanTableImplementationNaive<BinASTKind, 2> {
struct HuffmanTableIndexedSymbolsMaybeInterface {
using Contents = BinASTKind;
explicit HuffmanTableIndexedSymbolsMaybeInterface(JSContext* cx)
: HuffmanTableImplementationNaive(cx) {}
HuffmanTableImpl<BinASTKind, 2> impl;
explicit HuffmanTableIndexedSymbolsMaybeInterface(JSContext* cx) : impl(cx) {}
// `true` if this table only contains values for `null`.
bool isAlwaysNull() const {
MOZ_ASSERT(length() > 0);
MOZ_ASSERT(impl.length() > 0);
// By definition, we have either 1 or 2 values.
// By definition, if we have 2 values, one of them is not null.
if (length() != 1) {
if (impl.length() != 1) {
return false;
}
// Otherwise, check the single value.
return begin()->value == BinASTKind::_Null;
return impl.begin()->value == BinASTKind::_Null;
}
};
struct HuffmanTableIndexedSymbolsStringEnum
: HuffmanTableImplementationGeneric<BinASTVariant> {
struct HuffmanTableIndexedSymbolsStringEnum {
using Contents = BinASTVariant;
explicit HuffmanTableIndexedSymbolsStringEnum(JSContext* cx)
: HuffmanTableImplementationGeneric(cx) {}
HuffmanTableImpl<BinASTVariant> impl;
explicit HuffmanTableIndexedSymbolsStringEnum(JSContext* cx) : impl(cx) {}
};
struct HuffmanTableIndexedSymbolsLiteralString
: HuffmanTableImplementationGeneric<JSAtom*> {
struct HuffmanTableIndexedSymbolsLiteralString {
using Contents = JSAtom*;
explicit HuffmanTableIndexedSymbolsLiteralString(JSContext* cx)
: HuffmanTableImplementationGeneric(cx) {}
HuffmanTableImpl<JSAtom*> impl;
explicit HuffmanTableIndexedSymbolsLiteralString(JSContext* cx) : impl(cx) {}
};
struct HuffmanTableIndexedSymbolsOptionalLiteralString
: HuffmanTableImplementationGeneric<JSAtom*> {
struct HuffmanTableIndexedSymbolsOptionalLiteralString {
using Contents = JSAtom*;
HuffmanTableImpl<JSAtom*> impl;
explicit HuffmanTableIndexedSymbolsOptionalLiteralString(JSContext* cx)
: HuffmanTableImplementationGeneric(cx) {}
: impl(cx) {}
};
// A single Huffman table, used for values.
using HuffmanTableValue = mozilla::Variant<
// A single Huffman table.
using HuffmanTable = mozilla::Variant<
HuffmanTableUnreachable, // Default value.
HuffmanTableInitializing, HuffmanTableExplicitSymbolsF64,
HuffmanTableExplicitSymbolsU32, HuffmanTableIndexedSymbolsSum,
@ -636,11 +293,10 @@ using HuffmanTableValue = mozilla::Variant<
HuffmanTableIndexedSymbolsLiteralString,
HuffmanTableIndexedSymbolsOptionalLiteralString>;
struct HuffmanTableExplicitSymbolsListLength
: HuffmanTableImplementationGeneric<uint32_t> {
struct HuffmanTableExplicitSymbolsListLength {
using Contents = uint32_t;
explicit HuffmanTableExplicitSymbolsListLength(JSContext* cx)
: HuffmanTableImplementationGeneric(cx) {}
HuffmanTableImpl<uint32_t> impl;
explicit HuffmanTableExplicitSymbolsListLength(JSContext* cx) : impl(cx) {}
};
// A single Huffman table, specialized for list lengths.
@ -658,7 +314,7 @@ class HuffmanDictionary {
public:
explicit HuffmanDictionary(JSContext* cx);
HuffmanTableValue& tableForField(NormalizedInterfaceAndField index);
HuffmanTable& tableForField(NormalizedInterfaceAndField index);
HuffmanTableListLength& tableForListLength(BinASTList list);
private:
@ -669,7 +325,7 @@ class HuffmanDictionary {
//
// The mapping from `(Interface, Field) -> index` is extracted statically from
// the webidl specs.
mozilla::Array<HuffmanTableValue, BINAST_INTERFACE_AND_FIELD_LIMIT> fields;
mozilla::Array<HuffmanTable, BINAST_INTERFACE_AND_FIELD_LIMIT> fields;
// Huffman tables for list lengths. Some tables may be
// `HuffmanTableUnreacheable` if they represent lists that actually do not
@ -982,7 +638,7 @@ class MOZ_STACK_CLASS BinASTTokenReaderContext : public BinASTTokenReaderBase {
private:
// A mapping string index => BinASTVariant as extracted from the [STRINGS]
// section of the file. Populated lazily.
js::HashMap<FlatHuffmanKey, BinASTVariant, DefaultHasher<uint32_t>,
js::HashMap<uint32_t, BinASTVariant, DefaultHasher<uint32_t>,
SystemAllocPolicy>
variantsTable_;