commit 45bb87a424e6a59804afc42a59b59cb41de306dd
parent 17c6bdb0c8cf7534561316bce0326aecab9e202e
Author: Tim Huang <tihuang@mozilla.com>
Date: Wed, 15 Oct 2025 07:55:29 +0000
Bug 1975192 - Implement the RiceDeltaDecoder for various lengths. r=dimi
Differential Revision: https://phabricator.services.mozilla.com/D268504
Diffstat:
5 files changed, 594 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
@@ -76,6 +76,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 +176,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.