| /* | |
| * 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/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], std::size(buf), "%.16g", | |
| static_cast<double>(value)); | |
| RTC_DCHECK_LE(len, std::ssize(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 |