tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 5917024326c57620dd4bc89493536431a5c338ba
parent fd3c32ffc06af01a6b6e773ac6a243d44265c87b
Author: Tim Huang <tihuang@mozilla.com>
Date:   Wed, 15 Oct 2025 12:18:39 +0000

Bug 1975192 - Implement the RiceDeltaDecoder for various lengths. r=dimi

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

Diffstat:
Mtoolkit/components/url-classifier/ProtocolParser.cpp | 205++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mtoolkit/components/url-classifier/ProtocolParser.h | 12++++++++++++
Mtoolkit/components/url-classifier/RiceDeltaDecoder.cpp | 228++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mtoolkit/components/url-classifier/RiceDeltaDecoder.h | 47+++++++++++++++++++++++++++++++++++++++++++++++
Mtoolkit/components/url-classifier/tests/gtest/TestRiceDeltaDecoder.cpp | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 595 insertions(+), 2 deletions(-)

diff --git a/toolkit/components/url-classifier/ProtocolParser.cpp b/toolkit/components/url-classifier/ProtocolParser.cpp @@ -1178,9 +1178,15 @@ nsresult ProtocolParserProtobufV5::ProcessOneResponse( rv = ProcessAddition4Bytes(*tuV4, aHashList.additions_four_bytes()); break; case v5::HashList::kAdditionsEightBytes: + rv = ProcessAddition8Bytes(*tuV4, aHashList.additions_eight_bytes()); + break; case v5::HashList::kAdditionsSixteenBytes: + rv = ProcessAddition16Bytes(*tuV4, aHashList.additions_sixteen_bytes()); + break; case v5::HashList::kAdditionsThirtyTwoBytes: - return NS_ERROR_NOT_IMPLEMENTED; + rv = + ProcessAddition32Bytes(*tuV4, aHashList.additions_thirty_two_bytes()); + break; case v5::HashList::COMPRESSED_ADDITIONS_NOT_SET: break; } @@ -1239,6 +1245,121 @@ static nsresult DoRiceDeltaDecode4Bytes( return NS_OK; } +static nsresult DoRiceDeltaDecode8Bytes( + const v5::RiceDeltaEncoded64Bit& aEncoding, nsTArray<uint64_t>& aDecoded) { + auto first_value = aEncoding.first_value(); + + PARSER_LOG(("* Encoding info for V5 8bytes encoding:")); + PARSER_LOG((" - First value: %" PRIu64, first_value)); + PARSER_LOG((" - Num of entries: %d", aEncoding.entries_count())); + PARSER_LOG((" - Rice parameter: %d", aEncoding.rice_parameter())); + + // Set up the input buffer. Note that the bits should be read + // from LSB to MSB so that we in-place reverse the bits before + // feeding to the decoder. + auto encoded = + const_cast<v5::RiceDeltaEncoded64Bit&>(aEncoding).mutable_encoded_data(); + RiceDeltaDecoder decoder((uint8_t*)encoded->c_str(), encoded->size()); + + // Setup the output buffer. The "first value" is included in + // the output buffer. + if (!aDecoded.SetLength(aEncoding.entries_count() + 1, mozilla::fallible)) { + NS_WARNING("Not enough memory to decode the RiceDelta input."); + return NS_ERROR_OUT_OF_MEMORY; + } + + // Decode! + bool rv = decoder.Decode64( + aEncoding.rice_parameter(), first_value, + aEncoding.entries_count(), // # of entries (first value not included). + &aDecoded[0]); + + NS_ENSURE_TRUE(rv, NS_ERROR_UC_PARSER_DECODE_FAILURE); + + return NS_OK; +} + +static nsresult DoRiceDeltaDecode16Bytes( + const v5::RiceDeltaEncoded128Bit& aEncoding, nsACString& aDecoded) { + auto first_value_hi = aEncoding.first_value_hi(); + auto first_value_lo = aEncoding.first_value_lo(); + + PARSER_LOG(("* Encoding info for V5 16bytes encoding:")); + PARSER_LOG((" - First value hi: %" PRIu64, first_value_hi)); + PARSER_LOG((" - First value lo: %" PRIu64, first_value_lo)); + PARSER_LOG((" - Num of entries: %d", aEncoding.entries_count())); + PARSER_LOG((" - Rice parameter: %d", aEncoding.rice_parameter())); + + // Set up the input buffer. Note that the bits should be read + // from LSB to MSB so that we in-place reverse the bits before + // feeding to the decoder. + auto encoded = + const_cast<v5::RiceDeltaEncoded128Bit&>(aEncoding).mutable_encoded_data(); + RiceDeltaDecoder decoder((uint8_t*)encoded->c_str(), encoded->size()); + + // Setup the output buffer. The "first value" is included in + // the output buffer. + if (!aDecoded.SetCapacity(aEncoding.entries_count() * 16, + mozilla::fallible)) { + NS_WARNING("Not enough memory to decode the RiceDelta input."); + return NS_ERROR_OUT_OF_MEMORY; + } + + // Decode! + bool rv = decoder.Decode128( + aEncoding.rice_parameter(), first_value_hi, first_value_lo, + aEncoding.entries_count(), // # of entries (first value not included). + aDecoded); + + NS_ENSURE_TRUE(rv, NS_ERROR_UC_PARSER_DECODE_FAILURE); + + return NS_OK; +} + +static nsresult DoRiceDeltaDecode32Bytes( + const v5::RiceDeltaEncoded256Bit& aEncoding, nsACString& aDecoded) { + auto first_value_first_part = aEncoding.first_value_first_part(); + auto first_value_second_part = aEncoding.first_value_second_part(); + auto first_value_third_part = aEncoding.first_value_third_part(); + auto first_value_fourth_part = aEncoding.first_value_fourth_part(); + + PARSER_LOG(("* Encoding info for V5 32bytes encoding:")); + PARSER_LOG((" - First value first part: %" PRIu64, first_value_first_part)); + PARSER_LOG( + (" - First value second part: %" PRIu64, first_value_second_part)); + PARSER_LOG((" - First value third part: %" PRIu64, first_value_third_part)); + PARSER_LOG( + (" - First value fourth part: %" PRIu64, first_value_fourth_part)); + PARSER_LOG((" - Num of entries: %d", aEncoding.entries_count())); + PARSER_LOG((" - Rice parameter: %d", aEncoding.rice_parameter())); + + // Set up the input buffer. Note that the bits should be read + // from LSB to MSB so that we in-place reverse the bits before + // feeding to the decoder. + auto encoded = + const_cast<v5::RiceDeltaEncoded256Bit&>(aEncoding).mutable_encoded_data(); + RiceDeltaDecoder decoder((uint8_t*)encoded->c_str(), encoded->size()); + + // Setup the output buffer. The "first value" is included in + // the output buffer. + if (!aDecoded.SetCapacity((aEncoding.entries_count() + 1) * 32, + mozilla::fallible)) { + NS_WARNING("Not enough memory to decode the RiceDelta input."); + return NS_ERROR_OUT_OF_MEMORY; + } + + // Decode! + bool rv = decoder.Decode256( + aEncoding.rice_parameter(), first_value_first_part, + first_value_second_part, first_value_third_part, first_value_fourth_part, + aEncoding.entries_count(), // # of entries (first value not included). + aDecoded); + + NS_ENSURE_TRUE(rv, NS_ERROR_UC_PARSER_DECODE_FAILURE); + + return NS_OK; +} + nsresult ProtocolParserProtobufV5::ProcessAddition4Bytes( TableUpdateV4& aTableUpdate, const v5::RiceDeltaEncoded32Bit& aAddition) { nsTArray<uint32_t> decoded; @@ -1260,6 +1381,7 @@ nsresult ProtocolParserProtobufV5::ProcessAddition4Bytes( // We need to swap the bytes to get the correct prefix. char p[4]; NativeEndian::copyAndSwapToBigEndian(p, &decoded[i], 1); + prefixes.Append(p, 4); } @@ -1267,6 +1389,87 @@ nsresult ProtocolParserProtobufV5::ProcessAddition4Bytes( return NS_OK; } +nsresult ProtocolParserProtobufV5::ProcessAddition8Bytes( + TableUpdateV4& aTableUpdate, const v5::RiceDeltaEncoded64Bit& aAddition) { + nsTArray<uint64_t> decoded; + + nsresult rv = DoRiceDeltaDecode8Bytes(aAddition, decoded); + if (NS_FAILED(rv)) { + PARSER_LOG(("Failed to parse encoded prefixes.")); + return rv; + } + + nsCString prefixes; + if (!prefixes.SetCapacity(decoded.Length() * 8, mozilla::fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + for (size_t i = 0; i < decoded.Length(); i++) { + char p[8]; + NativeEndian::copyAndSwapToBigEndian(p, &decoded[i], 1); + prefixes.Append(p, 8); + } + + aTableUpdate.NewPrefixes(8, prefixes); + return NS_OK; +} + +nsresult ProtocolParserProtobufV5::ProcessAddition16Bytes( + TableUpdateV4& aTableUpdate, const v5::RiceDeltaEncoded128Bit& aAddition) { + nsAutoCString decoded; + + nsresult rv = DoRiceDeltaDecode16Bytes(aAddition, decoded); + if (NS_FAILED(rv)) { + PARSER_LOG(("Failed to parse encoded prefixes.")); + return rv; + } + + nsCString prefixes; + if (!prefixes.SetCapacity(decoded.Length(), mozilla::fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + for (size_t i = 0; i < decoded.Length() / 16; i++) { + // Swap the bytes to the correct endianness, the data is 128-bits long. + char p[16]; + for (size_t j = 0; j < 16; j++) { + p[j] = decoded[i * 16 + (15 - j)]; + } + prefixes.Append(p, 16); + } + + aTableUpdate.NewPrefixes(16, prefixes); + return NS_OK; +} + +nsresult ProtocolParserProtobufV5::ProcessAddition32Bytes( + TableUpdateV4& aTableUpdate, const v5::RiceDeltaEncoded256Bit& aAddition) { + nsAutoCString decoded; + + nsresult rv = DoRiceDeltaDecode32Bytes(aAddition, decoded); + if (NS_FAILED(rv)) { + PARSER_LOG(("Failed to parse encoded prefixes.")); + return rv; + } + + nsCString prefixes; + if (!prefixes.SetCapacity(decoded.Length(), mozilla::fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + for (size_t i = 0; i < decoded.Length() / 32; i++) { + // Swap the bytes to the correct endianness, the data is 256-bits long. + char p[32]; + for (size_t j = 0; j < 32; j++) { + p[j] = decoded[i * 32 + (31 - j)]; + } + prefixes.Append(p, 32); + } + + aTableUpdate.NewPrefixes(32, prefixes); + return NS_OK; +} + nsresult ProtocolParserProtobufV5::ProcessRemoval( TableUpdateV4& aTableUpdate, const v5::RiceDeltaEncoded32Bit& aRemoval) { nsTArray<uint32_t> decoded; diff --git a/toolkit/components/url-classifier/ProtocolParser.h b/toolkit/components/url-classifier/ProtocolParser.h @@ -218,6 +218,18 @@ class ProtocolParserProtobufV5 final : public ProtocolParser { nsresult ProcessAddition4Bytes(TableUpdateV4& aTableUpdate, const v5::RiceDeltaEncoded32Bit& aAddition); + // Process the additions for a 8-byte encoded prefixes. + nsresult ProcessAddition8Bytes(TableUpdateV4& aTableUpdate, + const v5::RiceDeltaEncoded64Bit& aAddition); + + // Process the additions for a 16-byte encoded prefixes. + nsresult ProcessAddition16Bytes(TableUpdateV4& aTableUpdate, + const v5::RiceDeltaEncoded128Bit& aAddition); + + // Process the additions for a 32-byte encoded prefixes. + nsresult ProcessAddition32Bytes(TableUpdateV4& aTableUpdate, + const v5::RiceDeltaEncoded256Bit& aAddition); + // Process the removals for a encoded prefixes. nsresult ProcessRemoval(TableUpdateV4& aTableUpdate, const v5::RiceDeltaEncoded32Bit& aRemoval); diff --git a/toolkit/components/url-classifier/RiceDeltaDecoder.cpp b/toolkit/components/url-classifier/RiceDeltaDecoder.cpp @@ -4,6 +4,7 @@ #include "RiceDeltaDecoder.h" #include "mozilla/Logging.h" +#include "nsString.h" #include <limits> @@ -76,6 +77,62 @@ static void ReverseByte(uint8_t& b) { b = (b & 0xAA) >> 1 | (b & 0x55) << 1; } +// Template for multi-precision numbers. Supports 128-bit (2 uint64_t) and +// 256-bit (4 uint64_t) +template <size_t N> +struct Number { + static_assert( + N >= 2 && N <= 4, + "Number template only supports 128-bit (N=2) and 256-bit (N=4)"); + + Number() { + for (size_t i = 0; i < N; i++) { + mData[i] = 0; + } + } + + // Constructor that takes an array of values + explicit Number(const uint64_t (&values)[N]) { + for (size_t i = 0; i < N; i++) { + mData[i] = values[i]; + } + } + + const char* get() const { return reinterpret_cast<const char*>(mData); } + + Number operator+(const Number& aOther) const { + uint64_t result[N]; + uint64_t carry = 0; + + // Add from least significant to most significant, propagating carry + for (size_t i = 0; i < N; i++) { + uint64_t sum = mData[i] + aOther.mData[i] + carry; + result[i] = sum; + // Check for overflow: if the sum is less than either operand (when carry + // is 0) or if the sum is less than the sum without carry (when carry is + // 1) + carry = (sum < mData[i]) || (carry && sum < (mData[i] + aOther.mData[i])) + ? 1 + : 0; + } + + return Number(result); + } + + Number operator=(const Number& aOther) { + for (size_t i = 0; i < N; i++) { + mData[i] = aOther.mData[i]; + } + return *this; + } + + uint64_t mData[N]; +}; + +// Type aliases for convenience +using Number128 = Number<2>; +using Number256 = Number<4>; + namespace mozilla { namespace safebrowsing { @@ -120,13 +177,182 @@ bool RiceDeltaDecoder::Decode(uint32_t aRiceParameter, uint32_t aFirstValue, // Caculate N from q,r,k. uint32_t N = (q << k) + r; - // We start filling aDecodedData from [1]. + // We start filling aDecodedData. + aDecodedData[i + 1] = N + aDecodedData[i]; + } + + return true; +} + +bool RiceDeltaDecoder::Decode64(uint32_t aRiceParameter, uint64_t aFirstValue, + uint32_t aNumEntries, uint64_t* aDecodedData) { + // Reverse each byte before reading bits from the byte buffer. + for (size_t i = 0; i < mEncodedDataSize; i++) { + ReverseByte(mEncodedData[i]); + } + + BitBuffer bitBuffer(mEncodedData, mEncodedDataSize); + + // q = quotient + // r = remainder + // k = RICE parameter + const uint32_t k = aRiceParameter; + aDecodedData[0] = aFirstValue; + for (uint32_t i = 0; i < aNumEntries; i++) { + // Read the quotient of N. + uint32_t q; + if (!bitBuffer.ReadExponentialGolomb(&q)) { + LOG(("Encoded data underflow!")); + return false; + } + + // Read the remainder of N, one bit at a time. + uint64_t r = 0; + for (uint32_t j = 0; j < k; j++) { + uint32_t b = 0; + if (!bitBuffer.ReadBits(&b, 1)) { + // Insufficient bits. Just leave them as zeros. + break; + } + // Add the bit to the right position so that it's in Little Endian order. + r |= static_cast<uint64_t>(b) << j; + } + + // Calculate N from q,r,k. + uint64_t N = (static_cast<uint64_t>(q) << k) + r; + + // We start filling aDecodedData. aDecodedData[i + 1] = N + aDecodedData[i]; } return true; } +bool RiceDeltaDecoder::Decode128(uint32_t aRiceParameter, + uint64_t aFirstValueHigh, + uint64_t aFirstValueLow, uint32_t aNumEntries, + nsACString& aDecodedData) { + // Reverse each byte before reading bits from the byte buffer. + for (size_t i = 0; i < mEncodedDataSize; i++) { + ReverseByte(mEncodedData[i]); + } + + BitBuffer bitBuffer(mEncodedData, mEncodedDataSize); + + // q = quotient + // r = remainder + // k = RICE parameter + const uint32_t k = aRiceParameter; + Number128 firstValue({aFirstValueLow, aFirstValueHigh}); + + aDecodedData.Append(firstValue.get(), sizeof(firstValue)); + + Number128 previousValue = firstValue; + for (uint32_t i = 0; i < aNumEntries; i++) { + // Read the quotient of N. + uint32_t q; + if (!bitBuffer.ReadExponentialGolomb(&q)) { + LOG(("Encoded data underflow!")); + return false; + } + + // The rice parameter is guaranteed to be between 99 and 126. So, the + // quotient is guaranteed to be located at the first 4 bytes. + uint64_t r[2] = {0, 0}; + for (uint32_t j = 0; j < k; j++) { + uint32_t b = 0; + if (!bitBuffer.ReadBits(&b, 1)) { + // Insufficient bits. Just leave them as zeros. + break; + } + // Add the bit to the right position so that it's in Little Endian order. + r[j / 64] |= static_cast<uint64_t>(b) << (j % 64); + } + + // Calculate N from q,r,k. + uint64_t N[2] = {0, 0}; + N[0] = r[0]; + N[1] = (static_cast<uint64_t>(q) << (k - 64)) + r[1]; + + // Create delta N and add it to the previous value + Number128 deltaN(N); + Number128 result = previousValue + deltaN; + previousValue = result; + + // Append the result to the decoded data + aDecodedData.Append(result.get(), sizeof(result)); + } + + return true; +} + +bool RiceDeltaDecoder::Decode256(uint32_t aRiceParameter, + uint64_t aFirstValueOne, + uint64_t aFirstValueTwo, + uint64_t aFirstValueThree, + uint64_t aFirstValueFour, uint32_t aNumEntries, + nsACString& aDecodedData) { + // Reverse each byte before reading bits from the byte buffer. + for (size_t i = 0; i < mEncodedDataSize; i++) { + ReverseByte(mEncodedData[i]); + } + + BitBuffer bitBuffer(mEncodedData, mEncodedDataSize); + + // q = quotient + // r = remainder + // k = RICE parameter + const uint32_t k = aRiceParameter; + // The first value is in the Big Endian order. The value one contains the + // highest 64 bits, value two contains the second highest 64 bits, and so on. + Number256 firstValue( + {aFirstValueFour, aFirstValueThree, aFirstValueTwo, aFirstValueOne}); + + aDecodedData.Append(firstValue.get(), sizeof(firstValue)); + + Number256 previousValue = firstValue; + for (uint32_t i = 0; i < aNumEntries; i++) { + // Read the quotient of N. + uint32_t q; + if (!bitBuffer.ReadExponentialGolomb(&q)) { + LOG(("Encoded data underflow!")); + return false; + } + + // Read the remainder of N, one bit at a time. + uint64_t r[4] = {0, 0, 0, 0}; + + for (uint32_t j = 0; j < k; j++) { + uint32_t b = 0; + if (!bitBuffer.ReadBits(&b, 1)) { + // Insufficient bits. Just leave them as zeros. + break; + } + // Add the bit to the right position so that it's in Little Endian order. + r[j / 64] |= static_cast<uint64_t>(b) << (j % 64); + } + + // Calculate N from q,r,k. + // The rice parameter is guaranteed to be between 227 and 254. So, the + // quotient is guaranteed to be located at the highest 4 bytes. + uint64_t N[4] = {0, 0, 0, 0}; + N[0] = r[0]; + N[1] = r[1]; + N[2] = r[2]; + N[3] = (static_cast<uint64_t>(q) << (k - (64 * 3))) + r[3]; + + // Create delta N and add it to the previous value + Number256 deltaN(N); + Number256 result = previousValue + deltaN; + previousValue = result; + + // Append the result to the decoded data + aDecodedData.Append(result.get(), sizeof(result)); + } + + return true; +} + } // namespace safebrowsing } // namespace mozilla diff --git a/toolkit/components/url-classifier/RiceDeltaDecoder.h b/toolkit/components/url-classifier/RiceDeltaDecoder.h @@ -8,6 +8,8 @@ #include <cstddef> #include <cstdint> +#include "nsStringFwd.h" + namespace mozilla { namespace safebrowsing { @@ -29,6 +31,51 @@ class RiceDeltaDecoder { bool Decode(uint32_t aRiceParameter, uint32_t aFirstValue, uint32_t aNumEntries, uint32_t* aDecodedData); + // Decode the 64-bit encoded data. + // + // @param aFirstValue The first value of the encoded data. + // @param aNumEntries The number of values to be decoded, not including + // the first value. + // @param aDecodedData A pre-allocated output buffer. Note that + // aDecodedData[0] will be filled with |aFirstValue| + // and the buffer length (in byte) should be + // ((aNumEntries + 1) * sizeof(uint64_t)). + bool Decode64(uint32_t aRiceParameter, uint64_t aFirstValue, + uint32_t aNumEntries, uint64_t* aDecodedData); + + // Decode the 128-bit encoded data. + // + // @param aFirstValueHigh The high 64 bits of the first value of the encoded + // data. + // @param aFirstValueLow The low 64 bits of the first value of the encoded + // data. + // @param aNumEntries The number of values to be decoded, not including + // the first value. + // @param aDecodedData A pre-allocated output buffer. Note that + // the first 16 bytes will be filled with |aFirstValue| + // and the buffer length (in byte) should be + // ((aNumEntries + 1) * 16). + bool Decode128(uint32_t aRiceParameter, uint64_t aFirstValueHigh, + uint64_t aFirstValueLow, uint32_t aNumEntries, + nsACString& aDecodedData); + + // Decode the 256-bit encoded data. + // + // @param aFirstValueOne The first high 64 bits value of the encoded data. + // @param aFirstValueTwo The second high 64 bits value of the encoded data. + // @param aFirstValueThree The third high 64 bits value of the encoded data. + // @param aFirstValueFour The fourth high 64 bits value of the encoded data. + // @param aNumEntries The number of values to be decoded, not including + // the first value. + // @param aDecodedData A pre-allocated output buffer. Note that + // the first 32 byteswill be filled with |aFirstValue| + // and the buffer length (in byte) should be + // ((aNumEntries + 1) * 32). + bool Decode256(uint32_t aRiceParameter, uint64_t aFirstValueOne, + uint64_t aFirstValueTwo, uint64_t aFirstValueThree, + uint64_t aFirstValueFour, uint32_t aNumEntries, + nsACString& aDecodedData); + private: uint8_t* mEncodedData; size_t mEncodedDataSize; diff --git a/toolkit/components/url-classifier/tests/gtest/TestRiceDeltaDecoder.cpp b/toolkit/components/url-classifier/tests/gtest/TestRiceDeltaDecoder.cpp @@ -16,6 +16,24 @@ struct TestingData { uint32_t mRiceParameter; }; +struct TestingData64 { + std::vector<uint64_t> mExpectedDecoded; + std::vector<uint8_t> mEncoded; + uint32_t mRiceParameter; +}; + +struct TestingData128 { + nsCString mExpectedDecoded; + std::vector<uint8_t> mEncoded; + uint32_t mRiceParameter; +}; + +struct TestingData256 { + nsCString mExpectedDecoded; + std::vector<uint8_t> mEncoded; + uint32_t mRiceParameter; +}; + } // namespace static bool runOneTest(TestingData& aData) { @@ -32,6 +50,65 @@ static bool runOneTest(TestingData& aData) { return rv && decoded == aData.mExpectedDecoded; } +static bool runOneTest64(TestingData64& aData) { + RiceDeltaDecoder decoder(&aData.mEncoded[0], aData.mEncoded.size()); + + std::vector<uint64_t> decoded(aData.mExpectedDecoded.size()); + + uint64_t firstValue = + reinterpret_cast<uint64_t*>(&aData.mExpectedDecoded[0])[0]; + bool rv = decoder.Decode64( + aData.mRiceParameter, firstValue, + decoded.size() - 1, // # of entries (first value not included). + &decoded[0]); + + return rv && decoded == aData.mExpectedDecoded; +} + +static bool runOneTest128(TestingData128& aData) { + RiceDeltaDecoder decoder(&aData.mEncoded[0], aData.mEncoded.size()); + + nsAutoCString decoded; + decoded.SetCapacity(aData.mExpectedDecoded.Length()); + + uint64_t firstValueHigh = + reinterpret_cast<const uint64_t*>(aData.mExpectedDecoded.get())[1]; + uint64_t firstValueLow = + reinterpret_cast<const uint64_t*>(aData.mExpectedDecoded.get())[0]; + + bool rv = + decoder.Decode128(aData.mRiceParameter, firstValueHigh, firstValueLow, + (aData.mExpectedDecoded.Length() / 16) - + 1, // # of entries (first value not included). + decoded); + + return rv && decoded == aData.mExpectedDecoded; +} + +static bool runOneTest256(TestingData256& aData) { + RiceDeltaDecoder decoder(&aData.mEncoded[0], aData.mEncoded.size()); + + nsAutoCString decoded; + decoded.SetCapacity(aData.mExpectedDecoded.Length()); + + uint64_t firstValueOne = + reinterpret_cast<const uint64_t*>(aData.mExpectedDecoded.get())[3]; + uint64_t firstValueTwo = + reinterpret_cast<const uint64_t*>(aData.mExpectedDecoded.get())[2]; + uint64_t firstValueThree = + reinterpret_cast<const uint64_t*>(aData.mExpectedDecoded.get())[1]; + uint64_t firstValueFour = + reinterpret_cast<const uint64_t*>(aData.mExpectedDecoded.get())[0]; + bool rv = + decoder.Decode256(aData.mRiceParameter, firstValueOne, firstValueTwo, + firstValueThree, firstValueFour, + (aData.mExpectedDecoded.Length() / 32) - + 1, // # of entries (first value not included). + decoded); + + return rv && decoded == aData.mExpectedDecoded; +} + TEST(UrlClassifierRiceDeltaDecoder, SingleEncodedValue) { TestingData td = {{99}, {99}, 0}; @@ -39,6 +116,34 @@ TEST(UrlClassifierRiceDeltaDecoder, SingleEncodedValue) ASSERT_TRUE(runOneTest(td)); } +TEST(UrlClassifierRiceDeltaDecoder, SingleEncodedValue64) +{ + TestingData64 td = {{99}, {99}, 0}; + ASSERT_TRUE(runOneTest64(td)); +} + +TEST(UrlClassifierRiceDeltaDecoder, SingleEncodedValue128) +{ + TestingData128 td = { + nsLiteralCString( + "\x63\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"), + {99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + 0}; + ASSERT_TRUE(runOneTest128(td)); +} + +TEST(UrlClassifierRiceDeltaDecoder, SingleEncodedValue256) +{ + TestingData256 td = { + nsLiteralCString( + "\x63\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"), + {99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + 0}; + ASSERT_TRUE(runOneTest256(td)); +} + // In this batch of tests, the encoded data would be like // what we originally receive from the network. See comment // in |runOneTest| for more detail.