1/*
2 * libjingle
3 * Copyright 2014 Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 *  1. Redistributions of source code must retain the above copyright notice,
9 *     this list of conditions and the following disclaimer.
10 *  2. Redistributions in binary form must reproduce the above copyright notice,
11 *     this list of conditions and the following disclaimer in the documentation
12 *     and/or other materials provided with the distribution.
13 *  3. The name of the author may not be used to endorse or promote products
14 *     derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "talk/app/webrtc/statstypes.h"
29
30#include <string.h>
31
32#include "webrtc/base/checks.h"
33
34// TODO(tommi): Could we have a static map of value name -> expected type
35// and use this to RTC_DCHECK on correct usage (somewhat strongly typed values)?
36// Alternatively, we could define the names+type in a separate document and
37// generate strongly typed inline C++ code that forces the correct type to be
38// used for a given name at compile time.
39
40using rtc::RefCountedObject;
41
42namespace webrtc {
43namespace {
44
45// The id of StatsReport of type kStatsReportTypeBwe.
46const char kStatsReportVideoBweId[] = "bweforvideo";
47
48// NOTE: These names need to be consistent with an external
49// specification (W3C Stats Identifiers).
50const char* InternalTypeToString(StatsReport::StatsType type) {
51  switch (type) {
52    case StatsReport::kStatsReportTypeSession:
53      return "googLibjingleSession";
54    case StatsReport::kStatsReportTypeBwe:
55      return "VideoBwe";
56    case StatsReport::kStatsReportTypeRemoteSsrc:
57      return "remoteSsrc";
58    case StatsReport::kStatsReportTypeSsrc:
59      return "ssrc";
60    case StatsReport::kStatsReportTypeTrack:
61      return "googTrack";
62    case StatsReport::kStatsReportTypeIceLocalCandidate:
63      return "localcandidate";
64    case StatsReport::kStatsReportTypeIceRemoteCandidate:
65      return "remotecandidate";
66    case StatsReport::kStatsReportTypeTransport:
67      return "transport";
68    case StatsReport::kStatsReportTypeComponent:
69      return "googComponent";
70    case StatsReport::kStatsReportTypeCandidatePair:
71      return "googCandidatePair";
72    case StatsReport::kStatsReportTypeCertificate:
73      return "googCertificate";
74    case StatsReport::kStatsReportTypeDataChannel:
75      return "datachannel";
76  }
77  RTC_DCHECK(false);
78  return nullptr;
79}
80
81class BandwidthEstimationId : public StatsReport::IdBase {
82 public:
83  BandwidthEstimationId()
84      : StatsReport::IdBase(StatsReport::kStatsReportTypeBwe) {}
85  std::string ToString() const override { return kStatsReportVideoBweId; }
86};
87
88class TypedId : public StatsReport::IdBase {
89 public:
90  TypedId(StatsReport::StatsType type, const std::string& id)
91      : StatsReport::IdBase(type), id_(id) {}
92
93  bool Equals(const IdBase& other) const override {
94    return IdBase::Equals(other) &&
95           static_cast<const TypedId&>(other).id_ == id_;
96  }
97
98  std::string ToString() const override {
99    return std::string(InternalTypeToString(type_)) + kSeparator + id_;
100  }
101
102 protected:
103  const std::string id_;
104};
105
106class TypedIntId : public StatsReport::IdBase {
107 public:
108  TypedIntId(StatsReport::StatsType type, int id)
109      : StatsReport::IdBase(type), id_(id) {}
110
111  bool Equals(const IdBase& other) const override {
112    return IdBase::Equals(other) &&
113           static_cast<const TypedIntId&>(other).id_ == id_;
114  }
115
116  std::string ToString() const override {
117    return std::string(InternalTypeToString(type_)) +
118           kSeparator +
119           rtc::ToString<int>(id_);
120  }
121
122 protected:
123  const int id_;
124};
125
126class IdWithDirection : public TypedId {
127 public:
128  IdWithDirection(StatsReport::StatsType type, const std::string& id,
129                  StatsReport::Direction direction)
130      : TypedId(type, id), direction_(direction) {}
131
132  bool Equals(const IdBase& other) const override {
133    return TypedId::Equals(other) &&
134           static_cast<const IdWithDirection&>(other).direction_ == direction_;
135  }
136
137  std::string ToString() const override {
138    std::string ret(TypedId::ToString());
139    ret += kSeparator;
140    ret += direction_ == StatsReport::kSend ? "send" : "recv";
141    return ret;
142  }
143
144 private:
145  const StatsReport::Direction direction_;
146};
147
148class CandidateId : public TypedId {
149 public:
150  CandidateId(bool local, const std::string& id)
151      : TypedId(local ?
152                    StatsReport::kStatsReportTypeIceLocalCandidate :
153                    StatsReport::kStatsReportTypeIceRemoteCandidate,
154                id) {
155  }
156
157  std::string ToString() const override {
158    return "Cand-" + id_;
159  }
160};
161
162class ComponentId : public StatsReport::IdBase {
163 public:
164  ComponentId(const std::string& content_name, int component)
165      : ComponentId(StatsReport::kStatsReportTypeComponent, content_name,
166            component) {}
167
168  bool Equals(const IdBase& other) const override {
169    return IdBase::Equals(other) &&
170        static_cast<const ComponentId&>(other).component_ == component_ &&
171        static_cast<const ComponentId&>(other).content_name_ == content_name_;
172  }
173
174  std::string ToString() const override {
175    return ToString("Channel-");
176  }
177
178 protected:
179  ComponentId(StatsReport::StatsType type, const std::string& content_name,
180              int component)
181      : IdBase(type),
182        content_name_(content_name),
183        component_(component) {}
184
185  std::string ToString(const char* prefix) const {
186    std::string ret(prefix);
187    ret += content_name_;
188    ret += '-';
189    ret += rtc::ToString<>(component_);
190    return ret;
191  }
192
193 private:
194  const std::string content_name_;
195  const int component_;
196};
197
198class CandidatePairId : public ComponentId {
199 public:
200  CandidatePairId(const std::string& content_name, int component, int index)
201      : ComponentId(StatsReport::kStatsReportTypeCandidatePair, content_name,
202            component),
203        index_(index) {}
204
205  bool Equals(const IdBase& other) const override {
206    return ComponentId::Equals(other) &&
207        static_cast<const CandidatePairId&>(other).index_ == index_;
208  }
209
210  std::string ToString() const override {
211    std::string ret(ComponentId::ToString("Conn-"));
212    ret += '-';
213    ret += rtc::ToString<>(index_);
214    return ret;
215  }
216
217 private:
218  const int index_;
219};
220
221}  // namespace
222
223StatsReport::IdBase::IdBase(StatsType type) : type_(type) {}
224StatsReport::IdBase::~IdBase() {}
225
226StatsReport::StatsType StatsReport::IdBase::type() const { return type_; }
227
228bool StatsReport::IdBase::Equals(const IdBase& other) const {
229  return other.type_ == type_;
230}
231
232StatsReport::Value::Value(StatsValueName name, int64_t value, Type int_type)
233    : name(name), type_(int_type) {
234  RTC_DCHECK(type_ == kInt || type_ == kInt64);
235  type_ == kInt ? value_.int_ = static_cast<int>(value) : value_.int64_ = value;
236}
237
238StatsReport::Value::Value(StatsValueName name, float f)
239    : name(name), type_(kFloat) {
240  value_.float_ = f;
241}
242
243StatsReport::Value::Value(StatsValueName name, const std::string& value)
244    : name(name), type_(kString) {
245  value_.string_ = new std::string(value);
246}
247
248StatsReport::Value::Value(StatsValueName name, const char* value)
249    : name(name), type_(kStaticString) {
250  value_.static_string_ = value;
251}
252
253StatsReport::Value::Value(StatsValueName name, bool b)
254    : name(name), type_(kBool) {
255  value_.bool_ = b;
256}
257
258StatsReport::Value::Value(StatsValueName name, const Id& value)
259    : name(name), type_(kId) {
260  value_.id_ = new Id(value);
261}
262
263StatsReport::Value::~Value() {
264  switch (type_) {
265    case kInt:
266    case kInt64:
267    case kFloat:
268    case kBool:
269    case kStaticString:
270      break;
271    case kString:
272      delete value_.string_;
273      break;
274    case kId:
275      delete value_.id_;
276      break;
277  }
278}
279
280bool StatsReport::Value::Equals(const Value& other) const {
281  if (name != other.name)
282    return false;
283
284  // There's a 1:1 relation between a name and a type, so we don't have to
285  // check that.
286  RTC_DCHECK_EQ(type_, other.type_);
287
288  switch (type_) {
289    case kInt:
290      return value_.int_ == other.value_.int_;
291    case kInt64:
292      return value_.int64_ == other.value_.int64_;
293    case kFloat:
294      return value_.float_ == other.value_.float_;
295    case kStaticString: {
296#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON))
297      if (value_.static_string_ != other.value_.static_string_) {
298        RTC_DCHECK(strcmp(value_.static_string_, other.value_.static_string_) !=
299                   0)
300            << "Duplicate global?";
301      }
302#endif
303      return value_.static_string_ == other.value_.static_string_;
304    }
305    case kString:
306      return *value_.string_ == *other.value_.string_;
307    case kBool:
308      return value_.bool_ == other.value_.bool_;
309    case kId:
310      return (*value_.id_)->Equals(*other.value_.id_);
311  }
312  RTC_NOTREACHED();
313  return false;
314}
315
316bool StatsReport::Value::operator==(const std::string& value) const {
317  return (type_ == kString && value_.string_->compare(value) == 0) ||
318         (type_ == kStaticString && value.compare(value_.static_string_) == 0);
319}
320
321bool StatsReport::Value::operator==(const char* value) const {
322  if (type_ == kString)
323    return value_.string_->compare(value) == 0;
324  if (type_ != kStaticString)
325    return false;
326#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON))
327  if (value_.static_string_ != value)
328    RTC_DCHECK(strcmp(value_.static_string_, value) != 0)
329        << "Duplicate global?";
330#endif
331  return value == value_.static_string_;
332}
333
334bool StatsReport::Value::operator==(int64_t value) const {
335  return type_ == kInt ? value_.int_ == static_cast<int>(value) :
336      (type_ == kInt64 ? value_.int64_ == value : false);
337}
338
339bool StatsReport::Value::operator==(bool value) const {
340  return type_ == kBool && value_.bool_ == value;
341}
342
343bool StatsReport::Value::operator==(float value) const {
344  return type_ == kFloat && value_.float_ == value;
345}
346
347bool StatsReport::Value::operator==(const Id& value) const {
348  return type_ == kId && (*value_.id_)->Equals(value);
349}
350
351int StatsReport::Value::int_val() const {
352  RTC_DCHECK(type_ == kInt);
353  return value_.int_;
354}
355
356int64_t StatsReport::Value::int64_val() const {
357  RTC_DCHECK(type_ == kInt64);
358  return value_.int64_;
359}
360
361float StatsReport::Value::float_val() const {
362  RTC_DCHECK(type_ == kFloat);
363  return value_.float_;
364}
365
366const char* StatsReport::Value::static_string_val() const {
367  RTC_DCHECK(type_ == kStaticString);
368  return value_.static_string_;
369}
370
371const std::string& StatsReport::Value::string_val() const {
372  RTC_DCHECK(type_ == kString);
373  return *value_.string_;
374}
375
376bool StatsReport::Value::bool_val() const {
377  RTC_DCHECK(type_ == kBool);
378  return value_.bool_;
379}
380
381const char* StatsReport::Value::display_name() const {
382  switch (name) {
383    case kStatsValueNameAudioOutputLevel:
384      return "audioOutputLevel";
385    case kStatsValueNameAudioInputLevel:
386      return "audioInputLevel";
387    case kStatsValueNameBytesSent:
388      return "bytesSent";
389    case kStatsValueNamePacketsSent:
390      return "packetsSent";
391    case kStatsValueNameBytesReceived:
392      return "bytesReceived";
393    case kStatsValueNameLabel:
394      return "label";
395    case kStatsValueNamePacketsReceived:
396      return "packetsReceived";
397    case kStatsValueNamePacketsLost:
398      return "packetsLost";
399    case kStatsValueNameProtocol:
400      return "protocol";
401    case kStatsValueNameTransportId:
402      return "transportId";
403    case kStatsValueNameSelectedCandidatePairId:
404      return "selectedCandidatePairId";
405    case kStatsValueNameSsrc:
406      return "ssrc";
407    case kStatsValueNameState:
408      return "state";
409    case kStatsValueNameDataChannelId:
410      return "datachannelid";
411    case kStatsValueNameCodecImplementationName:
412      return "codecImplementationName";
413
414    // 'goog' prefixed constants.
415    case kStatsValueNameAccelerateRate:
416      return "googAccelerateRate";
417    case kStatsValueNameActiveConnection:
418      return "googActiveConnection";
419    case kStatsValueNameActualEncBitrate:
420      return "googActualEncBitrate";
421    case kStatsValueNameAvailableReceiveBandwidth:
422      return "googAvailableReceiveBandwidth";
423    case kStatsValueNameAvailableSendBandwidth:
424      return "googAvailableSendBandwidth";
425    case kStatsValueNameAvgEncodeMs:
426      return "googAvgEncodeMs";
427    case kStatsValueNameBucketDelay:
428      return "googBucketDelay";
429    case kStatsValueNameBandwidthLimitedResolution:
430      return "googBandwidthLimitedResolution";
431
432    // Candidate related attributes. Values are taken from
433    // http://w3c.github.io/webrtc-stats/#rtcstatstype-enum*.
434    case kStatsValueNameCandidateIPAddress:
435      return "ipAddress";
436    case kStatsValueNameCandidateNetworkType:
437      return "networkType";
438    case kStatsValueNameCandidatePortNumber:
439      return "portNumber";
440    case kStatsValueNameCandidatePriority:
441      return "priority";
442    case kStatsValueNameCandidateTransportType:
443      return "transport";
444    case kStatsValueNameCandidateType:
445      return "candidateType";
446
447    case kStatsValueNameChannelId:
448      return "googChannelId";
449    case kStatsValueNameCodecName:
450      return "googCodecName";
451    case kStatsValueNameComponent:
452      return "googComponent";
453    case kStatsValueNameContentName:
454      return "googContentName";
455    case kStatsValueNameCpuLimitedResolution:
456      return "googCpuLimitedResolution";
457    case kStatsValueNameDecodingCTSG:
458      return "googDecodingCTSG";
459    case kStatsValueNameDecodingCTN:
460      return "googDecodingCTN";
461    case kStatsValueNameDecodingNormal:
462      return "googDecodingNormal";
463    case kStatsValueNameDecodingPLC:
464      return "googDecodingPLC";
465    case kStatsValueNameDecodingCNG:
466      return "googDecodingCNG";
467    case kStatsValueNameDecodingPLCCNG:
468      return "googDecodingPLCCNG";
469    case kStatsValueNameDer:
470      return "googDerBase64";
471    case kStatsValueNameDtlsCipher:
472      return "dtlsCipher";
473    case kStatsValueNameEchoCancellationQualityMin:
474      return "googEchoCancellationQualityMin";
475    case kStatsValueNameEchoDelayMedian:
476      return "googEchoCancellationEchoDelayMedian";
477    case kStatsValueNameEchoDelayStdDev:
478      return "googEchoCancellationEchoDelayStdDev";
479    case kStatsValueNameEchoReturnLoss:
480      return "googEchoCancellationReturnLoss";
481    case kStatsValueNameEchoReturnLossEnhancement:
482      return "googEchoCancellationReturnLossEnhancement";
483    case kStatsValueNameEncodeUsagePercent:
484      return "googEncodeUsagePercent";
485    case kStatsValueNameExpandRate:
486      return "googExpandRate";
487    case kStatsValueNameFingerprint:
488      return "googFingerprint";
489    case kStatsValueNameFingerprintAlgorithm:
490      return "googFingerprintAlgorithm";
491    case kStatsValueNameFirsReceived:
492      return "googFirsReceived";
493    case kStatsValueNameFirsSent:
494      return "googFirsSent";
495    case kStatsValueNameFrameHeightInput:
496      return "googFrameHeightInput";
497    case kStatsValueNameFrameHeightReceived:
498      return "googFrameHeightReceived";
499    case kStatsValueNameFrameHeightSent:
500      return "googFrameHeightSent";
501    case kStatsValueNameFrameRateReceived:
502      return "googFrameRateReceived";
503    case kStatsValueNameFrameRateDecoded:
504      return "googFrameRateDecoded";
505    case kStatsValueNameFrameRateOutput:
506      return "googFrameRateOutput";
507    case kStatsValueNameDecodeMs:
508      return "googDecodeMs";
509    case kStatsValueNameMaxDecodeMs:
510      return "googMaxDecodeMs";
511    case kStatsValueNameCurrentDelayMs:
512      return "googCurrentDelayMs";
513    case kStatsValueNameTargetDelayMs:
514      return "googTargetDelayMs";
515    case kStatsValueNameJitterBufferMs:
516      return "googJitterBufferMs";
517    case kStatsValueNameMinPlayoutDelayMs:
518      return "googMinPlayoutDelayMs";
519    case kStatsValueNameRenderDelayMs:
520      return "googRenderDelayMs";
521    case kStatsValueNameCaptureStartNtpTimeMs:
522      return "googCaptureStartNtpTimeMs";
523    case kStatsValueNameFrameRateInput:
524      return "googFrameRateInput";
525    case kStatsValueNameFrameRateSent:
526      return "googFrameRateSent";
527    case kStatsValueNameFrameWidthInput:
528      return "googFrameWidthInput";
529    case kStatsValueNameFrameWidthReceived:
530      return "googFrameWidthReceived";
531    case kStatsValueNameFrameWidthSent:
532      return "googFrameWidthSent";
533    case kStatsValueNameInitiator:
534      return "googInitiator";
535    case kStatsValueNameIssuerId:
536      return "googIssuerId";
537    case kStatsValueNameJitterReceived:
538      return "googJitterReceived";
539    case kStatsValueNameLocalAddress:
540      return "googLocalAddress";
541    case kStatsValueNameLocalCandidateId:
542      return "localCandidateId";
543    case kStatsValueNameLocalCandidateType:
544      return "googLocalCandidateType";
545    case kStatsValueNameLocalCertificateId:
546      return "localCertificateId";
547    case kStatsValueNameAdaptationChanges:
548      return "googAdaptationChanges";
549    case kStatsValueNameNacksReceived:
550      return "googNacksReceived";
551    case kStatsValueNameNacksSent:
552      return "googNacksSent";
553    case kStatsValueNamePreemptiveExpandRate:
554      return "googPreemptiveExpandRate";
555    case kStatsValueNamePlisReceived:
556      return "googPlisReceived";
557    case kStatsValueNamePlisSent:
558      return "googPlisSent";
559    case kStatsValueNamePreferredJitterBufferMs:
560      return "googPreferredJitterBufferMs";
561    case kStatsValueNameReceiving:
562      return "googReadable";
563    case kStatsValueNameRemoteAddress:
564      return "googRemoteAddress";
565    case kStatsValueNameRemoteCandidateId:
566      return "remoteCandidateId";
567    case kStatsValueNameRemoteCandidateType:
568      return "googRemoteCandidateType";
569    case kStatsValueNameRemoteCertificateId:
570      return "remoteCertificateId";
571    case kStatsValueNameRetransmitBitrate:
572      return "googRetransmitBitrate";
573    case kStatsValueNameRtt:
574      return "googRtt";
575    case kStatsValueNameSecondaryDecodedRate:
576      return "googSecondaryDecodedRate";
577    case kStatsValueNameSendPacketsDiscarded:
578      return "packetsDiscardedOnSend";
579    case kStatsValueNameSpeechExpandRate:
580      return "googSpeechExpandRate";
581    case kStatsValueNameSrtpCipher:
582      return "srtpCipher";
583    case kStatsValueNameTargetEncBitrate:
584      return "googTargetEncBitrate";
585    case kStatsValueNameTransmitBitrate:
586      return "googTransmitBitrate";
587    case kStatsValueNameTransportType:
588      return "googTransportType";
589    case kStatsValueNameTrackId:
590      return "googTrackId";
591    case kStatsValueNameTypingNoiseState:
592      return "googTypingNoiseState";
593    case kStatsValueNameViewLimitedResolution:
594      return "googViewLimitedResolution";
595    case kStatsValueNameWritable:
596      return "googWritable";
597  }
598
599  return nullptr;
600}
601
602std::string StatsReport::Value::ToString() const {
603  switch (type_) {
604    case kInt:
605      return rtc::ToString(value_.int_);
606    case kInt64:
607      return rtc::ToString(value_.int64_);
608    case kFloat:
609      return rtc::ToString(value_.float_);
610    case kStaticString:
611      return std::string(value_.static_string_);
612    case kString:
613      return *value_.string_;
614    case kBool:
615      return value_.bool_ ? "true" : "false";
616    case kId:
617      return (*value_.id_)->ToString();
618  }
619  RTC_NOTREACHED();
620  return std::string();
621}
622
623StatsReport::StatsReport(const Id& id) : id_(id), timestamp_(0.0) {
624  RTC_DCHECK(id_.get());
625}
626
627// static
628StatsReport::Id StatsReport::NewBandwidthEstimationId() {
629  return Id(new RefCountedObject<BandwidthEstimationId>());
630}
631
632// static
633StatsReport::Id StatsReport::NewTypedId(StatsType type, const std::string& id) {
634  return Id(new RefCountedObject<TypedId>(type, id));
635}
636
637// static
638StatsReport::Id StatsReport::NewTypedIntId(StatsType type, int id) {
639  return Id(new RefCountedObject<TypedIntId>(type, id));
640}
641
642// static
643StatsReport::Id StatsReport::NewIdWithDirection(
644    StatsType type, const std::string& id, StatsReport::Direction direction) {
645  return Id(new RefCountedObject<IdWithDirection>(type, id, direction));
646}
647
648// static
649StatsReport::Id StatsReport::NewCandidateId(bool local, const std::string& id) {
650  return Id(new RefCountedObject<CandidateId>(local, id));
651}
652
653// static
654StatsReport::Id StatsReport::NewComponentId(
655    const std::string& content_name, int component) {
656  return Id(new RefCountedObject<ComponentId>(content_name, component));
657}
658
659// static
660StatsReport::Id StatsReport::NewCandidatePairId(
661    const std::string& content_name, int component, int index) {
662  return Id(new RefCountedObject<CandidatePairId>(
663      content_name, component, index));
664}
665
666const char* StatsReport::TypeToString() const {
667  return InternalTypeToString(id_->type());
668}
669
670void StatsReport::AddString(StatsReport::StatsValueName name,
671                            const std::string& value) {
672  const Value* found = FindValue(name);
673  if (!found || !(*found == value))
674    values_[name] = ValuePtr(new Value(name, value));
675}
676
677void StatsReport::AddString(StatsReport::StatsValueName name,
678                            const char* value) {
679  const Value* found = FindValue(name);
680  if (!found || !(*found == value))
681    values_[name] = ValuePtr(new Value(name, value));
682}
683
684void StatsReport::AddInt64(StatsReport::StatsValueName name, int64_t value) {
685  const Value* found = FindValue(name);
686  if (!found || !(*found == value))
687    values_[name] = ValuePtr(new Value(name, value, Value::kInt64));
688}
689
690void StatsReport::AddInt(StatsReport::StatsValueName name, int value) {
691  const Value* found = FindValue(name);
692  if (!found || !(*found == static_cast<int64_t>(value)))
693    values_[name] = ValuePtr(new Value(name, value, Value::kInt));
694}
695
696void StatsReport::AddFloat(StatsReport::StatsValueName name, float value) {
697  const Value* found = FindValue(name);
698  if (!found || !(*found == value))
699    values_[name] = ValuePtr(new Value(name, value));
700}
701
702void StatsReport::AddBoolean(StatsReport::StatsValueName name, bool value) {
703  const Value* found = FindValue(name);
704  if (!found || !(*found == value))
705    values_[name] = ValuePtr(new Value(name, value));
706}
707
708void StatsReport::AddId(StatsReport::StatsValueName name,
709                        const Id& value) {
710  const Value* found = FindValue(name);
711  if (!found || !(*found == value))
712    values_[name] = ValuePtr(new Value(name, value));
713}
714
715const StatsReport::Value* StatsReport::FindValue(StatsValueName name) const {
716  Values::const_iterator it = values_.find(name);
717  return it == values_.end() ? nullptr : it->second.get();
718}
719
720StatsCollection::StatsCollection() {
721}
722
723StatsCollection::~StatsCollection() {
724  RTC_DCHECK(thread_checker_.CalledOnValidThread());
725  for (auto* r : list_)
726    delete r;
727}
728
729StatsCollection::const_iterator StatsCollection::begin() const {
730  RTC_DCHECK(thread_checker_.CalledOnValidThread());
731  return list_.begin();
732}
733
734StatsCollection::const_iterator StatsCollection::end() const {
735  RTC_DCHECK(thread_checker_.CalledOnValidThread());
736  return list_.end();
737}
738
739size_t StatsCollection::size() const {
740  RTC_DCHECK(thread_checker_.CalledOnValidThread());
741  return list_.size();
742}
743
744StatsReport* StatsCollection::InsertNew(const StatsReport::Id& id) {
745  RTC_DCHECK(thread_checker_.CalledOnValidThread());
746  RTC_DCHECK(Find(id) == nullptr);
747  StatsReport* report = new StatsReport(id);
748  list_.push_back(report);
749  return report;
750}
751
752StatsReport* StatsCollection::FindOrAddNew(const StatsReport::Id& id) {
753  RTC_DCHECK(thread_checker_.CalledOnValidThread());
754  StatsReport* ret = Find(id);
755  return ret ? ret : InsertNew(id);
756}
757
758StatsReport* StatsCollection::ReplaceOrAddNew(const StatsReport::Id& id) {
759  RTC_DCHECK(thread_checker_.CalledOnValidThread());
760  RTC_DCHECK(id.get());
761  Container::iterator it = std::find_if(list_.begin(), list_.end(),
762      [&id](const StatsReport* r)->bool { return r->id()->Equals(id); });
763  if (it != end()) {
764    StatsReport* report = new StatsReport((*it)->id());
765    delete *it;
766    *it = report;
767    return report;
768  }
769  return InsertNew(id);
770}
771
772// Looks for a report with the given |id|.  If one is not found, NULL
773// will be returned.
774StatsReport* StatsCollection::Find(const StatsReport::Id& id) {
775  RTC_DCHECK(thread_checker_.CalledOnValidThread());
776  Container::iterator it = std::find_if(list_.begin(), list_.end(),
777      [&id](const StatsReport* r)->bool { return r->id()->Equals(id); });
778  return it == list_.end() ? nullptr : *it;
779}
780
781}  // namespace webrtc
782