| /* | 
 |  *  Copyright 2024 The WebRTC Project Authors. All rights reserved. | 
 |  * | 
 |  *  Use of this source code is governed by a BSD-style license | 
 |  *  that can be found in the LICENSE file in the root of the source | 
 |  *  tree. An additional intellectual property rights grant can be found | 
 |  *  in the file PATENTS.  All contributing project authors may | 
 |  *  be found in the AUTHORS file in the root of the source tree. | 
 |  */ | 
 |  | 
 | #include "api/stats/attribute.h" | 
 |  | 
 | #include <cstdint> | 
 | #include <cstdio> | 
 | #include <map> | 
 | #include <optional> | 
 | #include <string> | 
 | #include <type_traits> | 
 | #include <variant> | 
 | #include <vector> | 
 |  | 
 | #include "rtc_base/arraysize.h" | 
 | #include "rtc_base/checks.h" | 
 | #include "rtc_base/string_encode.h" | 
 | #include "rtc_base/strings/string_builder.h" | 
 |  | 
 | namespace webrtc { | 
 |  | 
 | namespace { | 
 |  | 
 | struct VisitIsSequence { | 
 |   // Any type of vector is a sequence. | 
 |   template <typename T> | 
 |   bool operator()(const std::optional<std::vector<T>>* attribute) { | 
 |     return true; | 
 |   } | 
 |   // Any other type is not. | 
 |   template <typename T> | 
 |   bool operator()(const std::optional<T>* attribute) { | 
 |     return false; | 
 |   } | 
 | }; | 
 |  | 
 | // Converts the attribute to string in a JSON-compatible way. | 
 | struct VisitToString { | 
 |   template <typename T, | 
 |             typename std::enable_if_t< | 
 |                 std::is_same_v<T, int32_t> || std::is_same_v<T, uint32_t> || | 
 |                     std::is_same_v<T, bool> || std::is_same_v<T, std::string>, | 
 |                 bool> = true> | 
 |   std::string ValueToString(const T& value) { | 
 |     if constexpr (std::is_same_v<T, bool>) { | 
 |       return BoolToString(value); | 
 |     } | 
 |     return absl::StrCat(value); | 
 |   } | 
 |  | 
 |   // Convert 64-bit integers to doubles before converting to string because JSON | 
 |   // represents all numbers as floating points with ~15 digits of precision. | 
 |   template <typename T, | 
 |             typename std::enable_if_t<std::is_same_v<T, int64_t> || | 
 |                                           std::is_same_v<T, uint64_t> || | 
 |                                           std::is_same_v<T, double>, | 
 |                                       bool> = true> | 
 |   std::string ValueToString(const T& value) { | 
 |     char buf[32]; | 
 |     const int len = std::snprintf(&buf[0], arraysize(buf), "%.16g", | 
 |                                   static_cast<double>(value)); | 
 |     RTC_DCHECK_LE(len, arraysize(buf)); | 
 |     return std::string(&buf[0], len); | 
 |   } | 
 |  | 
 |   // Vector attributes. | 
 |   template <typename T> | 
 |   std::string operator()(const std::optional<std::vector<T>>* attribute) { | 
 |     StringBuilder sb; | 
 |     sb << "["; | 
 |     const char* separator = ""; | 
 |     constexpr bool element_is_string = std::is_same<T, std::string>::value; | 
 |     for (const T& element : attribute->value()) { | 
 |       sb << separator; | 
 |       if (element_is_string) { | 
 |         sb << "\""; | 
 |       } | 
 |       sb << ValueToString(element); | 
 |       if (element_is_string) { | 
 |         sb << "\""; | 
 |       } | 
 |       separator = ","; | 
 |     } | 
 |     sb << "]"; | 
 |     return sb.Release(); | 
 |   } | 
 |   // Map attributes. | 
 |   template <typename T> | 
 |   std::string operator()( | 
 |       const std::optional<std::map<std::string, T>>* attribute) { | 
 |     StringBuilder sb; | 
 |     sb << "{"; | 
 |     const char* separator = ""; | 
 |     constexpr bool element_is_string = std::is_same<T, std::string>::value; | 
 |     for (const auto& pair : attribute->value()) { | 
 |       sb << separator; | 
 |       sb << "\"" << pair.first << "\":"; | 
 |       if (element_is_string) { | 
 |         sb << "\""; | 
 |       } | 
 |       sb << ValueToString(pair.second); | 
 |       if (element_is_string) { | 
 |         sb << "\""; | 
 |       } | 
 |       separator = ","; | 
 |     } | 
 |     sb << "}"; | 
 |     return sb.Release(); | 
 |   } | 
 |   // Simple attributes. | 
 |   template <typename T> | 
 |   std::string operator()(const std::optional<T>* attribute) { | 
 |     return ValueToString(attribute->value()); | 
 |   } | 
 | }; | 
 |  | 
 | struct VisitIsEqual { | 
 |   template <typename T> | 
 |   bool operator()(const std::optional<T>* attribute) { | 
 |     if (!other.holds_alternative<T>()) { | 
 |       return false; | 
 |     } | 
 |     return *attribute == other.as_optional<T>(); | 
 |   } | 
 |  | 
 |   const Attribute& other; | 
 | }; | 
 |  | 
 | }  // namespace | 
 |  | 
 | const char* Attribute::name() const { | 
 |   return name_; | 
 | } | 
 |  | 
 | const Attribute::StatVariant& Attribute::as_variant() const { | 
 |   return attribute_; | 
 | } | 
 |  | 
 | bool Attribute::has_value() const { | 
 |   return std::visit([](const auto* attr) { return attr->has_value(); }, | 
 |                     attribute_); | 
 | } | 
 |  | 
 | bool Attribute::is_sequence() const { | 
 |   return std::visit(VisitIsSequence(), attribute_); | 
 | } | 
 |  | 
 | bool Attribute::is_string() const { | 
 |   return std::holds_alternative<const std::optional<std::string>*>(attribute_); | 
 | } | 
 |  | 
 | std::string Attribute::ToString() const { | 
 |   if (!has_value()) { | 
 |     return "null"; | 
 |   } | 
 |   return std::visit(VisitToString(), attribute_); | 
 | } | 
 |  | 
 | bool Attribute::operator==(const Attribute& other) const { | 
 |   return std::visit(VisitIsEqual{.other = other}, attribute_); | 
 | } | 
 |  | 
 | bool Attribute::operator!=(const Attribute& other) const { | 
 |   return !(*this == other); | 
 | } | 
 |  | 
 | AttributeInit::AttributeInit(const char* name, | 
 |                              const Attribute::StatVariant& variant) | 
 |     : name(name), variant(variant) {} | 
 |  | 
 | }  // namespace webrtc |