Server ID object for QUIC-LB. Wraps an array of uint8_t with validation to make sure the length is correct. This will be used by other classes.

PiperOrigin-RevId: 434513538
diff --git a/quic/load_balancer/load_balancer_server_id.cc b/quic/load_balancer/load_balancer_server_id.cc
new file mode 100644
index 0000000..12f1135
--- /dev/null
+++ b/quic/load_balancer/load_balancer_server_id.cc
@@ -0,0 +1,45 @@
+// Copyright (c) 2022 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 "quic/load_balancer/load_balancer_server_id.h"
+
+#include "absl/strings/escaping.h"
+#include "absl/types/span.h"
+#include "quic/core/quic_types.h"
+#include "quic/platform/api/quic_bug_tracker.h"
+
+namespace quic {
+
+namespace {
+
+// Helper to allow setting the const array during initialization.
+std::array<uint8_t, kLoadBalancerMaxServerIdLen> MakeArray(
+    const absl::Span<const uint8_t> data, const uint8_t length) {
+  std::array<uint8_t, kLoadBalancerMaxServerIdLen> array;
+  memcpy(array.data(), data.data(), length);
+  return array;
+}
+
+}  // namespace
+
+absl::optional<LoadBalancerServerId> LoadBalancerServerId::Create(
+    const absl::Span<const uint8_t> data) {
+  if (data.length() == 0 || data.length() > kLoadBalancerMaxServerIdLen) {
+    QUIC_BUG(quic_bug_433312504_01)
+        << "Attempted to create LoadBalancerServerId with length "
+        << data.length();
+    return absl::optional<LoadBalancerServerId>();
+  }
+  return LoadBalancerServerId(data);
+}
+
+std::string LoadBalancerServerId::ToString() const {
+  return absl::BytesToHexString(
+      absl::string_view(reinterpret_cast<const char*>(data_.data()), length_));
+}
+
+LoadBalancerServerId::LoadBalancerServerId(const absl::Span<const uint8_t> data)
+    : data_(MakeArray(data, data.length())), length_(data.length()) {}
+
+}  // namespace quic
diff --git a/quic/load_balancer/load_balancer_server_id.h b/quic/load_balancer/load_balancer_server_id.h
new file mode 100644
index 0000000..e729437
--- /dev/null
+++ b/quic/load_balancer/load_balancer_server_id.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2022 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.
+
+#ifndef QUICHE_QUIC_LOAD_BALANCER_SERVER_ID_H_
+#define QUICHE_QUIC_LOAD_BALANCER_SERVER_ID_H_
+
+#include <array>
+
+#include "quic/core/quic_types.h"
+#include "quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// The maximum number of bytes in a LoadBalancerServerId.
+inline constexpr uint8_t kLoadBalancerMaxServerIdLen = 15;
+
+// LoadBalancerServerId is the globally understood identifier for a given pool
+// member. It is unique to any given QUIC-LB configuration. See
+// draft-ietf-quic-load-balancers-12.
+// Note: this has nothing to do with QuicServerID. It's an unfortunate collision
+// between an internal term for the destination identifiers for a particular
+// deployment (QuicServerID) and the object of a load balancing decision
+// (LoadBalancerServerId).
+class QUIC_EXPORT_PRIVATE LoadBalancerServerId {
+ public:
+  // Copies all the bytes from |data| into a new LoadBalancerServerId.
+  static absl::optional<LoadBalancerServerId> Create(
+      const absl::Span<const uint8_t> data);
+
+  // Server IDs are opaque bytes, but defining these operators allows us to sort
+  // them into a tree and define ranges.
+  bool operator<(const LoadBalancerServerId& other) const {
+    return data() < other.data();
+  }
+  bool operator==(const LoadBalancerServerId& other) const {
+    return data() == other.data();
+  }
+
+  // Hash function to allow use as a key in unordered maps.
+  template <typename H>
+  friend H AbslHashValue(H h, const LoadBalancerServerId& server_id) {
+    return H::combine_contiguous(std::move(h), server_id.data().data(),
+                                 server_id.length());
+  }
+
+  absl::Span<const uint8_t> data() const {
+    return absl::MakeConstSpan(data_.data(), length_);
+  }
+  inline uint8_t length() const { return length_; }
+
+  // Returns the server ID in hex format.
+  std::string ToString() const;
+
+ private:
+  // The constructor is private because it can't validate the input.
+  LoadBalancerServerId(const absl::Span<const uint8_t> data);
+
+  const std::array<uint8_t, kLoadBalancerMaxServerIdLen> data_;
+  const uint8_t length_;
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_LOAD_BALANCER_SERVER_ID_H_
diff --git a/quic/load_balancer/load_balancer_server_id_test.cc b/quic/load_balancer/load_balancer_server_id_test.cc
new file mode 100644
index 0000000..8160830
--- /dev/null
+++ b/quic/load_balancer/load_balancer_server_id_test.cc
@@ -0,0 +1,111 @@
+// Copyright (c) 2022 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 "quic/load_balancer/load_balancer_server_id.h"
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Weverything"
+
+#include "absl/hash/hash_testing.h"
+
+#pragma clang diagnostic pop
+
+#include "quic/platform/api/quic_expect_bug.h"
+#include "quic/platform/api/quic_test.h"
+#include "quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+
+namespace test {
+
+namespace {
+
+class LoadBalancerServerIdTest : public QuicTest {};
+
+constexpr uint8_t kRawServerId[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+                                    0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
+                                    0x0c, 0x0d, 0x0e, 0x0f};
+
+TEST_F(LoadBalancerServerIdTest, CreateReturnsNullIfTooLong) {
+  EXPECT_QUIC_BUG(EXPECT_FALSE(LoadBalancerServerId::Create(
+                                   absl::Span<const uint8_t>(kRawServerId, 16))
+                                   .has_value()),
+                  "Attempted to create LoadBalancerServerId with length 16");
+  EXPECT_QUIC_BUG(
+      EXPECT_FALSE(LoadBalancerServerId::Create(absl::Span<const uint8_t>())
+                       .has_value()),
+      "Attempted to create LoadBalancerServerId with length 0");
+}
+
+TEST_F(LoadBalancerServerIdTest, CompareIdenticalExceptLength) {
+  auto server_id =
+      LoadBalancerServerId::Create(absl::Span<const uint8_t>(kRawServerId, 15));
+  ASSERT_TRUE(server_id.has_value());
+  EXPECT_EQ(server_id->length(), 15);
+  auto shorter_server_id =
+      LoadBalancerServerId::Create(absl::Span<const uint8_t>(kRawServerId, 5));
+  ASSERT_TRUE(shorter_server_id.has_value());
+  EXPECT_EQ(shorter_server_id->length(), 5);
+  // Shorter comes before longer if all bits match
+  EXPECT_TRUE(shorter_server_id < server_id);
+  EXPECT_FALSE(server_id < shorter_server_id);
+  // Different lengths are never equal.
+  EXPECT_FALSE(shorter_server_id == server_id);
+}
+
+TEST_F(LoadBalancerServerIdTest, AccessorFunctions) {
+  auto server_id =
+      LoadBalancerServerId::Create(absl::Span<const uint8_t>(kRawServerId, 5));
+  EXPECT_TRUE(server_id.has_value());
+  EXPECT_EQ(server_id->length(), 5);
+  EXPECT_EQ(memcmp(server_id->data().data(), kRawServerId, 5), 0);
+  EXPECT_EQ(server_id->ToString(), "0001020304");
+}
+
+TEST_F(LoadBalancerServerIdTest, CompareDifferentServerIds) {
+  auto server_id =
+      LoadBalancerServerId::Create(absl::Span<const uint8_t>(kRawServerId, 5));
+  ASSERT_TRUE(server_id.has_value());
+  auto reverse = LoadBalancerServerId::Create({0x0f, 0x0e, 0x0d, 0x0c, 0x0b});
+  ASSERT_TRUE(reverse.has_value());
+  EXPECT_TRUE(server_id < reverse);
+  auto long_server_id =
+      LoadBalancerServerId::Create(absl::Span<const uint8_t>(kRawServerId, 15));
+  EXPECT_TRUE(long_server_id < reverse);
+}
+
+TEST_F(LoadBalancerServerIdTest, EqualityOperators) {
+  auto server_id =
+      LoadBalancerServerId::Create(absl::Span<const uint8_t>(kRawServerId, 15));
+  ASSERT_TRUE(server_id.has_value());
+  auto shorter_server_id =
+      LoadBalancerServerId::Create(absl::Span<const uint8_t>(kRawServerId, 5));
+  ASSERT_TRUE(shorter_server_id.has_value());
+  EXPECT_FALSE(server_id == shorter_server_id);
+  auto server_id2 = server_id;
+  EXPECT_TRUE(server_id == server_id2);
+}
+
+TEST_F(LoadBalancerServerIdTest, SupportsHash) {
+  auto server_id =
+      LoadBalancerServerId::Create(absl::Span<const uint8_t>(kRawServerId, 15));
+  ASSERT_TRUE(server_id.has_value());
+  auto shorter_server_id =
+      LoadBalancerServerId::Create(absl::Span<const uint8_t>(kRawServerId, 5));
+  ASSERT_TRUE(shorter_server_id.has_value());
+  auto different_server_id =
+      LoadBalancerServerId::Create({0x0f, 0x0e, 0x0d, 0x0c, 0x0b});
+  ASSERT_TRUE(different_server_id.has_value());
+  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({
+      *server_id,
+      *shorter_server_id,
+      *different_server_id,
+  }));
+}
+
+}  // namespace
+
+}  // namespace test
+
+}  // namespace quic