Support for map of string keys to uint64_t / double values in RTCStats

Bug: webrtc:10685
Change-Id: I047d784bd20c3fca8b96391653f90fd8803140d8
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/219141
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Kári Helgason <kthelgason@webrtc.org>
Reviewed-by: Xavier Lepaul‎ <xalep@webrtc.org>
Commit-Queue: Henrik Boström <hbos@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#34121}
This commit is contained in:
Byoungchan Lee 2021-05-22 08:41:02 +09:00 committed by WebRTC LUCI CQ
parent cbeff55a6a
commit 0a52ede821
9 changed files with 160 additions and 7 deletions

View File

@ -14,6 +14,7 @@
#include <stddef.h>
#include <stdint.h>
#include <map>
#include <memory>
#include <string>
#include <utility>
@ -237,6 +238,9 @@ class RTCStatsMemberInterface {
kSequenceUint64, // std::vector<uint64_t>
kSequenceDouble, // std::vector<double>
kSequenceString, // std::vector<std::string>
kMapStringUint64, // std::map<std::string, uint64_t>
kMapStringDouble, // std::map<std::string, double>
};
virtual ~RTCStatsMemberInterface() {}
@ -363,6 +367,13 @@ class RTCStatsMember : public RTCStatsMemberInterface {
T value_;
};
namespace rtc_stats_internal {
typedef std::map<std::string, uint64_t> MapStringUint64;
typedef std::map<std::string, double> MapStringDouble;
} // namespace rtc_stats_internal
#define WEBRTC_DECLARE_RTCSTATSMEMBER(T) \
template <> \
RTC_EXPORT RTCStatsMemberInterface::Type RTCStatsMember<T>::StaticType(); \
@ -391,6 +402,8 @@ WEBRTC_DECLARE_RTCSTATSMEMBER(std::vector<int64_t>);
WEBRTC_DECLARE_RTCSTATSMEMBER(std::vector<uint64_t>);
WEBRTC_DECLARE_RTCSTATSMEMBER(std::vector<double>);
WEBRTC_DECLARE_RTCSTATSMEMBER(std::vector<std::string>);
WEBRTC_DECLARE_RTCSTATSMEMBER(rtc_stats_internal::MapStringUint64);
WEBRTC_DECLARE_RTCSTATSMEMBER(rtc_stats_internal::MapStringDouble);
// Using inheritance just so that it's obvious from the member's declaration
// whether it's standardized or not.
@ -455,6 +468,10 @@ extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT)
RTCNonStandardStatsMember<std::vector<double>>;
extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT)
RTCNonStandardStatsMember<std::vector<std::string>>;
extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT)
RTCNonStandardStatsMember<std::map<std::string, uint64_t>>;
extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT)
RTCNonStandardStatsMember<std::map<std::string, double>>;
} // namespace webrtc

View File

@ -62,6 +62,7 @@ public class RTCStats {
* - Double
* - String
* - The array form of any of the above (e.g., Integer[])
* - Map of String keys to BigInteger / Double values
*/
public Map<String, Object> getMembers() {
return members;

View File

@ -94,6 +94,23 @@ ScopedJavaLocalRef<jobject> MemberToJava(
case RTCStatsMemberInterface::kSequenceString:
return NativeToJavaStringArray(
env, *member.cast_to<RTCStatsMember<std::vector<std::string>>>());
case RTCStatsMemberInterface::kMapStringUint64:
return NativeToJavaMap(
env,
*member.cast_to<RTCStatsMember<std::map<std::string, uint64_t>>>(),
[](JNIEnv* env, const auto& entry) {
return std::make_pair(NativeToJavaString(env, entry.first),
NativeToJavaBigInteger(env, entry.second));
});
case RTCStatsMemberInterface::kMapStringDouble:
return NativeToJavaMap(
env, *member.cast_to<RTCStatsMember<std::map<std::string, double>>>(),
[](JNIEnv* env, const auto& entry) {
return std::make_pair(NativeToJavaString(env, entry.first),
NativeToJavaDouble(env, entry.second));
});
}
RTC_NOTREACHED();
return nullptr;

View File

@ -44,8 +44,8 @@ RTC_OBJC_EXPORT
@property(nonatomic, readonly) NSString *type;
/** The keys and values of the subreport, e.g. "totalFramesDuration = 5.551".
The values are either NSNumbers or NSStrings, or NSArrays encapsulating NSNumbers
or NSStrings. */
The values are either NSNumbers or NSStrings or NSArrays encapsulating NSNumbers
or NSStrings, or NSDictionary of NSString keys to NSNumber values. */
@property(nonatomic, readonly) NSDictionary<NSString *, NSObject *> *values;
- (instancetype)init NS_UNAVAILABLE;

View File

@ -16,7 +16,7 @@
namespace webrtc {
/** Converts a single value to a suitable NSNumber, NSString or NSArray containing NSNumbers
or NSStrings.*/
or NSStrings, or NSDictionary of NSString keys to NSNumber values.*/
NSObject *ValueFromStatsMember(const RTCStatsMemberInterface *member) {
if (member->is_defined()) {
switch (member->type()) {
@ -91,6 +91,26 @@ NSObject *ValueFromStatsMember(const RTCStatsMemberInterface *member) {
}
return [array copy];
}
case RTCStatsMemberInterface::kMapStringUint64: {
std::map<std::string, uint64_t> map =
*member->cast_to<RTCStatsMember<std::map<std::string, uint64_t>>>();
NSMutableDictionary<NSString *, NSNumber *> *dictionary =
[NSMutableDictionary dictionaryWithCapacity:map.size()];
for (const auto &item : map) {
dictionary[[NSString stringForStdString:item.first]] = @(item.second);
}
return [dictionary copy];
}
case RTCStatsMemberInterface::kMapStringDouble: {
std::map<std::string, double> map =
*member->cast_to<RTCStatsMember<std::map<std::string, double>>>();
NSMutableDictionary<NSString *, NSNumber *> *dictionary =
[NSMutableDictionary dictionaryWithCapacity:map.size()];
for (const auto &item : map) {
dictionary[[NSString stringForStdString:item.first]] = @(item.second);
}
return [dictionary copy];
}
default:
RTC_NOTREACHED();
}

View File

@ -64,6 +64,20 @@ std::string VectorOfStringsToString(const std::vector<T>& strings) {
return sb.Release();
}
template <typename T>
std::string MapToString(const std::map<std::string, T>& map) {
rtc::StringBuilder sb;
sb << "{";
const char* separator = "";
for (const auto& element : map) {
sb << separator << rtc::ToString(element.first) << ":"
<< rtc::ToString(element.second);
separator = ",";
}
sb << "}";
return sb.Release();
}
template <typename T>
std::string ToStringAsDouble(const T value) {
// JSON represents numbers as floating point numbers with about 15 decimal
@ -88,6 +102,20 @@ std::string VectorToStringAsDouble(const std::vector<T>& vector) {
return sb.Release();
}
template <typename T>
std::string MapToStringAsDouble(const std::map<std::string, T>& map) {
rtc::StringBuilder sb;
sb << "{";
const char* separator = "";
for (const auto& element : map) {
sb << separator << "\"" << rtc::ToString(element.first)
<< "\":" << ToStringAsDouble(element.second);
separator = ",";
}
sb << "}";
return sb.Release();
}
} // namespace
bool RTCStats::operator==(const RTCStats& other) const {
@ -248,6 +276,18 @@ WEBRTC_DEFINE_RTCSTATSMEMBER(std::vector<std::string>,
false,
VectorOfStringsToString(value_),
VectorOfStringsToString(value_));
WEBRTC_DEFINE_RTCSTATSMEMBER(rtc_stats_internal::MapStringUint64,
kMapStringUint64,
false,
false,
MapToString(value_),
MapToStringAsDouble(value_));
WEBRTC_DEFINE_RTCSTATSMEMBER(rtc_stats_internal::MapStringDouble,
kMapStringDouble,
false,
false,
MapToString(value_),
MapToStringAsDouble(value_));
template class RTC_EXPORT_TEMPLATE_DEFINE(RTC_EXPORT)
RTCNonStandardStatsMember<bool>;

View File

@ -71,7 +71,7 @@ TEST(RTCStatsTest, RTCStatsAndMembers) {
EXPECT_EQ(stats.id(), "testId");
EXPECT_EQ(stats.timestamp_us(), static_cast<int64_t>(42));
std::vector<const RTCStatsMemberInterface*> members = stats.Members();
EXPECT_EQ(members.size(), static_cast<size_t>(14));
EXPECT_EQ(members.size(), static_cast<size_t>(16));
for (const RTCStatsMemberInterface* member : members) {
EXPECT_FALSE(member->is_defined());
}
@ -98,6 +98,9 @@ TEST(RTCStatsTest, RTCStatsAndMembers) {
std::vector<std::string> sequence_string;
sequence_string.push_back(std::string("six"));
std::map<std::string, uint64_t> map_string_uint64{{"seven", 8}};
std::map<std::string, double> map_string_double{{"nine", 10.0}};
stats.m_sequence_bool = sequence_bool;
stats.m_sequence_int32 = sequence_int32;
stats.m_sequence_uint32 = sequence_uint32;
@ -106,6 +109,8 @@ TEST(RTCStatsTest, RTCStatsAndMembers) {
stats.m_sequence_uint64 = sequence_uint64;
stats.m_sequence_double = sequence_double;
stats.m_sequence_string = sequence_string;
stats.m_map_string_uint64 = map_string_uint64;
stats.m_map_string_double = map_string_double;
for (const RTCStatsMemberInterface* member : members) {
EXPECT_TRUE(member->is_defined());
}
@ -123,6 +128,8 @@ TEST(RTCStatsTest, RTCStatsAndMembers) {
EXPECT_EQ(*stats.m_sequence_uint64, sequence_uint64);
EXPECT_EQ(*stats.m_sequence_double, sequence_double);
EXPECT_EQ(*stats.m_sequence_string, sequence_string);
EXPECT_EQ(*stats.m_map_string_uint64, map_string_uint64);
EXPECT_EQ(*stats.m_map_string_double, map_string_double);
int32_t numbers[] = {4, 8, 15, 16, 23, 42};
std::vector<int32_t> numbers_sequence(&numbers[0], &numbers[6]);
@ -152,6 +159,8 @@ TEST(RTCStatsTest, EqualityOperator) {
stats_with_all_values.m_sequence_uint64 = std::vector<uint64_t>();
stats_with_all_values.m_sequence_double = std::vector<double>();
stats_with_all_values.m_sequence_string = std::vector<std::string>();
stats_with_all_values.m_map_string_uint64 = std::map<std::string, uint64_t>();
stats_with_all_values.m_map_string_double = std::map<std::string, double>();
EXPECT_NE(stats_with_all_values, empty_stats);
EXPECT_EQ(stats_with_all_values, stats_with_all_values);
EXPECT_NE(stats_with_all_values.m_int32, stats_with_all_values.m_uint32);
@ -180,6 +189,8 @@ TEST(RTCStatsTest, EqualityOperator) {
one_member_different[11].m_sequence_uint64->push_back(321);
one_member_different[12].m_sequence_double->push_back(321.0);
one_member_different[13].m_sequence_string->push_back("321");
(*one_member_different[13].m_map_string_uint64)["321"] = 321;
(*one_member_different[13].m_map_string_double)["321"] = 321.0;
for (size_t i = 0; i < 14; ++i) {
EXPECT_NE(stats_with_all_values, one_member_different[i]);
}
@ -238,6 +249,11 @@ TEST(RTCStatsTest, RTCStatsPrintsValidJson) {
std::vector<std::string> sequence_string;
sequence_string.push_back(std::string("four"));
std::map<std::string, uint64_t> map_string_uint64{
{"long", static_cast<uint64_t>(1234567890123456499L)}};
std::map<std::string, double> map_string_double{
{"three", 123.4567890123456499}, {"thirteen", 123.4567890123456499}};
RTCTestStats stats(id, timestamp);
stats.m_bool = m_bool;
stats.m_int32 = m_int32;
@ -249,6 +265,8 @@ TEST(RTCStatsTest, RTCStatsPrintsValidJson) {
stats.m_sequence_int64 = sequence_int64;
stats.m_sequence_double = sequence_double;
stats.m_sequence_string = sequence_string;
stats.m_map_string_uint64 = map_string_uint64;
stats.m_map_string_double = map_string_double;
Json::Value json_output;
EXPECT_TRUE(Json::Reader().parse(stats.ToJson(), json_output));
@ -278,6 +296,16 @@ TEST(RTCStatsTest, RTCStatsPrintsValidJson) {
rtc::GetValueFromJsonObject(json_output, "mSequenceString", &json_array));
EXPECT_TRUE(rtc::JsonArrayToStringVector(json_array, &sequence_string));
Json::Value json_map;
EXPECT_TRUE(
rtc::GetValueFromJsonObject(json_output, "mMapStringDouble", &json_map));
for (const auto& entry : map_string_double) {
double double_output = 0.0;
EXPECT_TRUE(
rtc::GetDoubleFromJsonObject(json_map, entry.first, &double_output));
EXPECT_NEAR(double_output, entry.second, GetExpectedError(entry.second));
}
EXPECT_EQ(id, stats.id());
EXPECT_EQ(timestamp, stats.timestamp_us());
EXPECT_EQ(m_bool, *stats.m_bool);
@ -286,6 +314,7 @@ TEST(RTCStatsTest, RTCStatsPrintsValidJson) {
EXPECT_EQ(sequence_bool, *stats.m_sequence_bool);
EXPECT_EQ(sequence_int32, *stats.m_sequence_int32);
EXPECT_EQ(sequence_string, *stats.m_sequence_string);
EXPECT_EQ(map_string_double, *stats.m_map_string_double);
EXPECT_NEAR(m_double, *stats.m_double, GetExpectedError(*stats.m_double));
@ -295,6 +324,13 @@ TEST(RTCStatsTest, RTCStatsPrintsValidJson) {
GetExpectedError(stats.m_sequence_double->at(i)));
}
EXPECT_EQ(map_string_double.size(), stats.m_map_string_double->size());
for (const auto& entry : map_string_double) {
auto it = stats.m_map_string_double->find(entry.first);
EXPECT_NE(it, stats.m_map_string_double->end());
EXPECT_NEAR(entry.second, it->second, GetExpectedError(it->second));
}
// We read mInt64 as double since JSON stores all numbers as doubles, so there
// is not enough precision to represent large numbers.
double m_int64_as_double;
@ -320,6 +356,19 @@ TEST(RTCStatsTest, RTCStatsPrintsValidJson) {
GetExpectedError(stats_value_as_double));
}
// Similarly, read Uint64 as double
EXPECT_TRUE(
rtc::GetValueFromJsonObject(json_output, "mMapStringUint64", &json_map));
for (const auto& entry : map_string_uint64) {
const double stats_value_as_double =
static_cast<double>((*stats.m_map_string_uint64)[entry.first]);
double double_output = 0.0;
EXPECT_TRUE(
rtc::GetDoubleFromJsonObject(json_map, entry.first, &double_output));
EXPECT_NEAR(double_output, stats_value_as_double,
GetExpectedError(stats_value_as_double));
}
// Neither stats.m_uint32 nor stats.m_uint64 are defined, so "mUint64" and
// "mUint32" should not be part of the generated JSON object.
int m_uint32;

View File

@ -30,7 +30,9 @@ WEBRTC_RTCSTATS_IMPL(RTCTestStats,
&m_sequence_int64,
&m_sequence_uint64,
&m_sequence_double,
&m_sequence_string)
&m_sequence_string,
&m_map_string_uint64,
&m_map_string_double)
RTCTestStats::RTCTestStats(const std::string& id, int64_t timestamp_us)
: RTCStats(id, timestamp_us),
@ -47,7 +49,9 @@ RTCTestStats::RTCTestStats(const std::string& id, int64_t timestamp_us)
m_sequence_int64("mSequenceInt64"),
m_sequence_uint64("mSequenceUint64"),
m_sequence_double("mSequenceDouble"),
m_sequence_string("mSequenceString") {}
m_sequence_string("mSequenceString"),
m_map_string_uint64("mMapStringUint64"),
m_map_string_double("mMapStringDouble") {}
RTCTestStats::RTCTestStats(const RTCTestStats& other)
: RTCStats(other.id(), other.timestamp_us()),
@ -64,7 +68,9 @@ RTCTestStats::RTCTestStats(const RTCTestStats& other)
m_sequence_int64(other.m_sequence_int64),
m_sequence_uint64(other.m_sequence_uint64),
m_sequence_double(other.m_sequence_double),
m_sequence_string(other.m_sequence_string) {}
m_sequence_string(other.m_sequence_string),
m_map_string_uint64(other.m_map_string_uint64),
m_map_string_double(other.m_map_string_double) {}
RTCTestStats::~RTCTestStats() {}

View File

@ -12,6 +12,7 @@
#define STATS_TEST_RTC_TEST_STATS_H_
#include <cstdint>
#include <map>
#include <string>
#include <vector>
@ -42,6 +43,8 @@ class RTC_EXPORT RTCTestStats : public RTCStats {
RTCStatsMember<std::vector<uint64_t>> m_sequence_uint64;
RTCStatsMember<std::vector<double>> m_sequence_double;
RTCStatsMember<std::vector<std::string>> m_sequence_string;
RTCStatsMember<std::map<std::string, uint64_t>> m_map_string_uint64;
RTCStatsMember<std::map<std::string, double>> m_map_string_double;
};
} // namespace webrtc