Support standard encoding for track names and namespaces. PiperOrigin-RevId: 869284787
diff --git a/quiche/quic/moqt/moqt_names.cc b/quiche/quic/moqt/moqt_names.cc index fa52970..4b135da 100644 --- a/quiche/quic/moqt/moqt_names.cc +++ b/quiche/quic/moqt/moqt_names.cc
@@ -11,6 +11,7 @@ #include "absl/status/status.h" #include "absl/status/statusor.h" +#include "absl/strings/ascii.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/strings/string_view.h" @@ -19,6 +20,41 @@ namespace moqt { +namespace { + +bool IsTrackNameSafeCharacter(char c) { + return absl::ascii_isalnum(c) || c == '_'; +} + +// Appends escaped version of a track name component into `output`. It is up to +// the caller to reserve() an appropriate amount of space in advance. The text +// format is defined in +// https://www.ietf.org/archive/id/draft-ietf-moq-transport-16.html#name-representing-namespace-and-amount +void EscapeTrackNameComponent(absl::string_view input, std::string& output) { + for (char c : input) { + if (IsTrackNameSafeCharacter(c)) { + output.push_back(c); + } else { + output.push_back('.'); + absl::StrAppend(&output, absl::Hex(c, absl::kZeroPad2)); + } + } +} + +// Similarly to the function above, the caller should call reserve() on `output` +// before calling. +void AppendEscapedTrackNameTuple(const MoqtStringTuple& tuple, + std::string& output) { + for (size_t i = 0; i < tuple.size(); ++i) { + EscapeTrackNameComponent(tuple[i], output); + if (i < (tuple.size() - 1)) { + output.push_back('-'); + } + } +} + +} // namespace + absl::StatusOr<TrackNamespace> TrackNamespace::Create(MoqtStringTuple tuple) { if (tuple.size() > kMaxNamespaceElements) { return absl::OutOfRangeError( @@ -56,8 +92,10 @@ } std::string TrackNamespace::ToString() const { - // TODO(vasilvv): switch to the standard encoding. - return absl::StrCat(tuple_); + std::string output; + output.reserve(3 * tuple_.TotalBytes() + tuple_.size()); + AppendEscapedTrackNameTuple(tuple_, output); + return output; } absl::StatusOr<FullTrackName> FullTrackName::Create(TrackNamespace ns, @@ -84,7 +122,7 @@ } FullTrackName::FullTrackName(absl::string_view ns, absl::string_view name) : namespace_(TrackNamespace({ns})), name_(name) { - if (ns.size() + name.size() > kMaxFullTrackNameSize) { + if (namespace_.total_length() + name.size() > kMaxFullTrackNameSize) { QUICHE_BUG(Moqt_full_track_name_too_large_02) << "Constructing a Full Track Name that is too large."; namespace_.Clear(); @@ -107,8 +145,13 @@ : namespace_(std::move(ns)), name_(std::move(name)) {} std::string FullTrackName::ToString() const { - // TODO(vasilvv): switch to the standard encoding. - return absl::StrCat(namespace_.ToString(), "::", name_); + std::string output; + output.reserve(3 * namespace_.total_length() + + namespace_.number_of_elements() + 3 * name_.size() + 2); + AppendEscapedTrackNameTuple(namespace_.tuple(), output); + output.append("--"); + EscapeTrackNameComponent(name_, output); + return output; } } // namespace moqt
diff --git a/quiche/quic/moqt/moqt_names.h b/quiche/quic/moqt/moqt_names.h index 7d91579..c447233 100644 --- a/quiche/quic/moqt/moqt_names.h +++ b/quiche/quic/moqt/moqt_names.h
@@ -18,7 +18,6 @@ #include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include "absl/types/span.h" -#include "quiche/common/quiche_status_utils.h" #include "quiche/common/quiche_string_tuple.h" namespace moqt { @@ -73,7 +72,10 @@ return result; } + // Encodes the string representation of MOQT track namespace in the format + // prescribed by the MOQT specification. std::string ToString() const; + // Returns the number of elements in the tuple. size_t number_of_elements() const { return tuple_.size(); } // Returns the sum of the lengths of all elements in the tuple. @@ -128,6 +130,8 @@ absl::string_view name() const ABSL_ATTRIBUTE_LIFETIME_BOUND { return name_; } size_t length() const { return namespace_.total_length() + name_.length(); } + // Encodes the string representation of MOQT full track name in the format + // prescribed by the MOQT specification. std::string ToString() const; auto operator<=>(const FullTrackName&) const = default;
diff --git a/quiche/quic/moqt/moqt_names_test.cc b/quiche/quic/moqt/moqt_names_test.cc index dd879cf..d10b986 100644 --- a/quiche/quic/moqt/moqt_names_test.cc +++ b/quiche/quic/moqt/moqt_names_test.cc
@@ -5,7 +5,6 @@ #include "quiche/quic/moqt/moqt_names.h" #include <utility> -#include <vector> #include "absl/hash/hash.h" #include "absl/status/status.h" @@ -65,15 +64,18 @@ TEST(MoqtNamesTest, TrackNamespaceToString) { TrackNamespace name1({"a", "b"}); - EXPECT_EQ(name1.ToString(), R"({"a", "b"})"); + EXPECT_EQ(name1.ToString(), "a-b"); - TrackNamespace name2({"\xff", "\x61"}); - EXPECT_EQ(name2.ToString(), R"({"\xff", "a"})"); + TrackNamespace name2({"\xff\x01", "\x61"}); + EXPECT_EQ(name2.ToString(), ".ff.01-a"); + + TrackNamespace name3({"a_b", "c_d?"}); + EXPECT_EQ(name3.ToString(), "a_b-c_d.3f"); } TEST(MoqtNamesTest, FullTrackNameToString) { - FullTrackName name1({"a", "b"}, "c"); - EXPECT_EQ(name1.ToString(), R"({"a", "b"}::c)"); + FullTrackName name1(TrackNamespace{"a", "b"}, "c"); + EXPECT_EQ(name1.ToString(), "a-b--c"); } TEST(MoqtNamesTest, TrackNamespaceSuffixes) {