blob: 8059666ba2bddd0607514f32304a9db9ab861fdb [file] [log] [blame] [edit]
// Copyright 2026 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "quiche/common/moq_varint.h"
#include <array>
#include <cstdint>
#include <limits>
#include <optional>
#include <string>
#include "absl/strings/escaping.h"
#include "absl/strings/string_view.h"
#include "quiche/common/platform/api/quiche_fuzztest.h"
#include "quiche/common/platform/api/quiche_test.h"
#include "quiche/common/quiche_data_reader.h"
#include "quiche/common/quiche_data_writer.h"
namespace quiche {
namespace {
using ::testing::ElementsAre;
using ReadFunction = std::optional<uint64_t> (*)(QuicheDataReader&);
template <ReadFunction reader>
std::optional<uint64_t> CompleteRead(absl::string_view hex_varint) {
std::string raw_data;
if (!absl::HexStringToBytes(hex_varint, &raw_data)) {
return std::nullopt;
}
QuicheDataReader view(raw_data);
std::optional<uint64_t> result = reader(view);
if (!view.IsDoneReading()) {
return std::nullopt;
}
return result;
}
TEST(MoqVarintTest, VarintParsing) {
EXPECT_EQ(CompleteRead<ReadMoqVarint>(""), std::nullopt);
EXPECT_EQ(CompleteRead<ReadMoqVarint>("00"), 0);
EXPECT_EQ(CompleteRead<ReadEbmlVarint>("80"), 0);
EXPECT_EQ(CompleteRead<ReadEbmlVarint>("000000000000000000"), std::nullopt);
// Examples from RFC 8794, Section 4.4.
EXPECT_EQ(CompleteRead<ReadEbmlVarint>("82"), 2);
EXPECT_EQ(CompleteRead<ReadEbmlVarint>("4002"), 2);
EXPECT_EQ(CompleteRead<ReadEbmlVarint>("200002"), 2);
EXPECT_EQ(CompleteRead<ReadEbmlVarint>("10000002"), 2);
// Examples from draft-ietf-moq-transport-17(bis), Section 1.4.1.
EXPECT_EQ(CompleteRead<ReadMoqVarint>("25"), 37);
EXPECT_EQ(CompleteRead<ReadMoqVarint>("8025"), 37);
EXPECT_EQ(CompleteRead<ReadMoqVarint>("bbbd"), 15293);
EXPECT_EQ(CompleteRead<ReadMoqVarint>("ed7f3e7d"), 226442877);
EXPECT_EQ(CompleteRead<ReadMoqVarint>("faa1a0e403d8"), 2893212287960);
EXPECT_EQ(CompleteRead<ReadMoqVarint>("fefa318fa8e3ca11"), 70423237261249041);
EXPECT_EQ(CompleteRead<ReadMoqVarint>("ffffffffffffffffff"),
18446744073709551615u);
// The forbidden MOQ varint length.
EXPECT_EQ(CompleteRead<ReadMoqVarint>("fcffffffffffff"), std::nullopt);
EXPECT_EQ(CompleteRead<ReadEbmlVarint>("02ffffffffffff"), 0xffffffffffff);
}
void FuzzMoqVarintParser(absl::string_view s) {
QuicheDataReader reader(s);
ReadMoqVarint(reader);
}
void FuzzEbmlVarintParser(absl::string_view s) {
QuicheDataReader reader(s);
ReadEbmlVarint(reader);
}
FUZZ_TEST(MoqVarintFuzzTest, FuzzMoqVarintParser);
FUZZ_TEST(MoqVarintFuzzTest, FuzzEbmlVarintParser);
using WriteFunction = bool (*)(QuicheDataWriter&, uint64_t);
template <WriteFunction writer>
std::optional<std::string> Write(uint64_t value) {
char buffer[9];
QuicheDataWriter output(sizeof(buffer), buffer);
bool success = writer(output, value);
if (!success) {
return std::nullopt;
}
return absl::BytesToHexString(
absl::string_view(buffer, sizeof(buffer) - output.remaining()));
}
TEST(MoqVarintTest, VarintSerialization) {
EXPECT_EQ(Write<WriteMoqVarint>(0), "00");
EXPECT_EQ(Write<WriteMoqVarint>(std::numeric_limits<uint64_t>::max()),
"ffffffffffffffffff");
EXPECT_EQ(Write<WriteMoqVarint>(UINT64_C(0xffffffffffffff00)),
"ffffffffffffffff00");
EXPECT_EQ(Write<WriteEbmlVarint>(0), "80");
EXPECT_EQ(Write<WriteEbmlVarint>(127), "407f");
EXPECT_EQ(Write<WriteEbmlVarint>(UINT64_C(1) << 56), std::nullopt);
EXPECT_EQ(Write<WriteEbmlVarint>(0xffffffffffff), "02ffffffffffff");
EXPECT_EQ(Write<WriteMoqVarint>(0xffffffffffff), "fe00ffffffffffff");
// Examples from draft-ietf-moq-transport-17(bis), Section 1.4.1.
EXPECT_EQ(Write<WriteMoqVarint>(37), "25");
EXPECT_EQ(Write<WriteMoqVarint>(15293), "bbbd");
EXPECT_EQ(Write<WriteMoqVarint>(226442877), "ed7f3e7d");
EXPECT_EQ(Write<WriteMoqVarint>(2893212287960), "faa1a0e403d8");
EXPECT_EQ(Write<WriteMoqVarint>(70423237261249041), "fefa318fa8e3ca11");
EXPECT_EQ(Write<WriteMoqVarint>(18446744073709551615u), "ffffffffffffffffff");
}
TEST(MoqVarintTest, WriteMoqVarintWithCustomLength) {
std::array<char, 9> buffer;
quiche::QuicheDataWriter writer(buffer.size(), buffer.data());
ASSERT_TRUE(WriteMoqVarintWithCustomLength(writer, 0xabcd, 9));
EXPECT_THAT(buffer, ElementsAre(0xff, 0, 0, 0, 0, 0, 0, 0xab, 0xcd));
}
TEST(MoqVarintTest, VarintSerializationInsufficientBuffer) {
const uint64_t kValue = (UINT64_C(1) << 56) - 1;
char buffer[9];
for (int size = 0; size < 8; ++size) {
QuicheDataWriter writer(size, buffer);
EXPECT_FALSE(WriteMoqVarint(writer, kValue)) << size;
EXPECT_FALSE(WriteEbmlVarint(writer, kValue)) << size;
}
}
void MoqRoundTrip(uint64_t value) {
std::optional<std::string> encoded = Write<WriteMoqVarint>(value);
ASSERT_TRUE(encoded.has_value());
EXPECT_EQ(encoded->size() / 2, GetMoqVarintLengthForValue(value));
std::optional<uint64_t> decoded = CompleteRead<ReadMoqVarint>(*encoded);
EXPECT_EQ(value, decoded) << *encoded;
}
void EbmlRoundTrip(uint64_t value) {
std::optional<std::string> encoded = Write<WriteEbmlVarint>(value);
if (!encoded.has_value()) {
return;
}
EXPECT_EQ(encoded->size() / 2, GetEbmlVarintLengthForValue(value));
std::optional<uint64_t> decoded = CompleteRead<ReadEbmlVarint>(*encoded);
EXPECT_EQ(value, decoded);
}
FUZZ_TEST(MoqVarintFuzzTest, MoqRoundTrip);
FUZZ_TEST(MoqVarintFuzzTest, EbmlRoundTrip);
} // namespace
} // namespace quiche