text: /*
   *  Copyright 2014 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 "webrtc/api/statstypes.h"
  
  #include <string.h>
  
  #include "webrtc/rtc_base/checks.h"
  
  // TODO(tommi): Could we have a static map of value name -> expected type
  // and use this to RTC_DCHECK on correct usage (somewhat strongly typed values)?
  // Alternatively, we could define the names+type in a separate document and
  // generate strongly typed inline C++ code that forces the correct type to be
  // used for a given name at compile time.
  
  using rtc::RefCountedObject;
  
  namespace webrtc {
  namespace {
  
  // The id of StatsReport of type kStatsReportTypeBwe.
  const char kStatsReportVideoBweId[] = "bweforvideo";
  
  // NOTE: These names need to be consistent with an external
  // specification (W3C Stats Identifiers).
  const char* InternalTypeToString(StatsReport::StatsType type) {
    switch (type) {
      case StatsReport::kStatsReportTypeSession:
        return "googLibjingleSession";
      case StatsReport::kStatsReportTypeBwe:
        return "VideoBwe";
      case StatsReport::kStatsReportTypeRemoteSsrc:
        return "remoteSsrc";
      case StatsReport::kStatsReportTypeSsrc:
        return "ssrc";
      case StatsReport::kStatsReportTypeTrack:
        return "googTrack";
      case StatsReport::kStatsReportTypeIceLocalCandidate:
        return "localcandidate";
      case StatsReport::kStatsReportTypeIceRemoteCandidate:
        return "remotecandidate";
      case StatsReport::kStatsReportTypeTransport:
        return "transport";
      case StatsReport::kStatsReportTypeComponent:
        return "googComponent";
      case StatsReport::kStatsReportTypeCandidatePair:
        return "googCandidatePair";
      case StatsReport::kStatsReportTypeCertificate:
        return "googCertificate";
      case StatsReport::kStatsReportTypeDataChannel:
        return "datachannel";
    }
    RTC_NOTREACHED();
    return nullptr;
  }
  
  class BandwidthEstimationId : public StatsReport::IdBase {
   public:
    BandwidthEstimationId()
        : StatsReport::IdBase(StatsReport::kStatsReportTypeBwe) {}
    std::string ToString() const override { return kStatsReportVideoBweId; }
  };
  
  class TypedId : public StatsReport::IdBase {
   public:
    TypedId(StatsReport::StatsType type, const std::string& id)
        : StatsReport::IdBase(type), id_(id) {}
  
    bool Equals(const IdBase& other) const override {
      return IdBase::Equals(other) &&
             static_cast<const TypedId&>(other).id_ == id_;
    }
  
    std::string ToString() const override {
      return std::string(InternalTypeToString(type_)) + kSeparator + id_;
    }
  
   protected:
    const std::string id_;
  };
  
  class TypedIntId : public StatsReport::IdBase {
   public:
    TypedIntId(StatsReport::StatsType type, int id)
        : StatsReport::IdBase(type), id_(id) {}
  
    bool Equals(const IdBase& other) const override {
      return IdBase::Equals(other) &&
             static_cast<const TypedIntId&>(other).id_ == id_;
    }
  
    std::string ToString() const override {
      return std::string(InternalTypeToString(type_)) +
             kSeparator +
             rtc::ToString<int>(id_);
    }
  
   protected:
    const int id_;
  };
  
  class IdWithDirection : public TypedId {
   public:
    IdWithDirection(StatsReport::StatsType type, const std::string& id,
                    StatsReport::Direction direction)
        : TypedId(type, id), direction_(direction) {}
  
    bool Equals(const IdBase& other) const override {
      return TypedId::Equals(other) &&
             static_cast<const IdWithDirection&>(other).direction_ == direction_;
    }
  
    std::string ToString() const override {
      std::string ret(TypedId::ToString());
      ret += kSeparator;
      ret += direction_ == StatsReport::kSend ? "send" : "recv";
      return ret;
    }
  
   private:
    const StatsReport::Direction direction_;
  };
  
  class CandidateId : public TypedId {
   public:
    CandidateId(bool local, const std::string& id)
        : TypedId(local ?
                      StatsReport::kStatsReportTypeIceLocalCandidate :
                      StatsReport::kStatsReportTypeIceRemoteCandidate,
                  id) {
    }
  
    std::string ToString() const override {
      return "Cand-" + id_;
    }
  };
  
  class ComponentId : public StatsReport::IdBase {
   public:
    ComponentId(const std::string& content_name, int component)
        : ComponentId(StatsReport::kStatsReportTypeComponent, content_name,
              component) {}
  
    bool Equals(const IdBase& other) const override {
      return IdBase::Equals(other) &&
          static_cast<const ComponentId&>(other).component_ == component_ &&
          static_cast<const ComponentId&>(other).content_name_ == content_name_;
    }
  
    std::string ToString() const override {
      return ToString("Channel-");
    }
  
   protected:
    ComponentId(StatsReport::StatsType type, const std::string& content_name,
                int component)
        : IdBase(type),
          content_name_(content_name),
          component_(component) {}
  
    std::string ToString(const char* prefix) const {
      std::string ret(prefix);
      ret += content_name_;
      ret += '-';
      ret += rtc::ToString<>(component_);
      return ret;
    }
  
   private:
    const std::string content_name_;
    const int component_;
  };
  
  class CandidatePairId : public ComponentId {
   public:
    CandidatePairId(const std::string& content_name, int component, int index)
        : ComponentId(StatsReport::kStatsReportTypeCandidatePair, content_name,
              component),
          index_(index) {}
  
    bool Equals(const IdBase& other) const override {
      return ComponentId::Equals(other) &&
          static_cast<const CandidatePairId&>(other).index_ == index_;
    }
  
    std::string ToString() const override {
      std::string ret(ComponentId::ToString("Conn-"));
      ret += '-';
      ret += rtc::ToString<>(index_);
      return ret;
    }
  
   private:
    const int index_;
  };
  
  }  // namespace
  
  StatsReport::IdBase::IdBase(StatsType type) : type_(type) {}
  StatsReport::IdBase::~IdBase() {}
  
  StatsReport::StatsType StatsReport::IdBase::type() const { return type_; }
  
  bool StatsReport::IdBase::Equals(const IdBase& other) const {
    return other.type_ == type_;
  }
  
  StatsReport::Value::Value(StatsValueName name, int64_t value, Type int_type)
      : name(name), type_(int_type) {
    RTC_DCHECK(type_ == kInt || type_ == kInt64);
    type_ == kInt ? value_.int_ = static_cast<int>(value) : value_.int64_ = value;
  }
  
  StatsReport::Value::Value(StatsValueName name, float f)
      : name(name), type_(kFloat) {
    value_.float_ = f;
  }
  
  StatsReport::Value::Value(StatsValueName name, const std::string& value)
      : name(name), type_(kString) {
    value_.string_ = new std::string(value);
  }
  
  StatsReport::Value::Value(StatsValueName name, const char* value)
      : name(name), type_(kStaticString) {
    value_.static_string_ = value;
  }
  
  StatsReport::Value::Value(StatsValueName name, bool b)
      : name(name), type_(kBool) {
    value_.bool_ = b;
  }
  
  StatsReport::Value::Value(StatsValueName name, const Id& value)
      : name(name), type_(kId) {
    value_.id_ = new Id(value);
  }
  
  StatsReport::Value::~Value() {
    switch (type_) {
      case kInt:
      case kInt64:
      case kFloat:
      case kBool:
      case kStaticString:
        break;
      case kString:
        delete value_.string_;
        break;
      case kId:
        delete value_.id_;
        break;
    }
  }
  
  bool StatsReport::Value::Equals(const Value& other) const {
    if (name != other.name)
      return false;
  
    // There's a 1:1 relation between a name and a type, so we don't have to
    // check that.
    RTC_DCHECK_EQ(type_, other.type_);
  
    switch (type_) {
      case kInt:
        return value_.int_ == other.value_.int_;
      case kInt64:
        return value_.int64_ == other.value_.int64_;
      case kFloat:
        return value_.float_ == other.value_.float_;
      case kStaticString: {
  #if RTC_DCHECK_IS_ON
        if (value_.static_string_ != other.value_.static_string_) {
          RTC_DCHECK(strcmp(value_.static_string_, other.value_.static_string_) !=
                     0)
              << "Duplicate global?";
        }
  #endif
        return value_.static_string_ == other.value_.static_string_;
      }
      case kString:
        return *value_.string_ == *other.value_.string_;
      case kBool:
        return value_.bool_ == other.value_.bool_;
      case kId:
        return (*value_.id_)->Equals(*other.value_.id_);
    }
    RTC_NOTREACHED();
    return false;
  }
  
  bool StatsReport::Value::operator==(const std::string& value) const {
    return (type_ == kString && value_.string_->compare(value) == 0) ||
           (type_ == kStaticString && value.compare(value_.static_string_) == 0);
  }
  
  bool StatsReport::Value::operator==(const char* value) const {
    if (type_ == kString)
      return value_.string_->compare(value) == 0;
    if (type_ != kStaticString)
      return false;
  #if RTC_DCHECK_IS_ON
    if (value_.static_string_ != value)
      RTC_DCHECK(strcmp(value_.static_string_, value) != 0)
          << "Duplicate global?";
  #endif
    return value == value_.static_string_;
  }
  
  bool StatsReport::Value::operator==(int64_t value) const {
    return type_ == kInt ? value_.int_ == static_cast<int>(value) :
        (type_ == kInt64 ? value_.int64_ == value : false);
  }
  
  bool StatsReport::Value::operator==(bool value) const {
    return type_ == kBool && value_.bool_ == value;
  }
  
  bool StatsReport::Value::operator==(float value) const {
    return type_ == kFloat && value_.float_ == value;
  }
  
  bool StatsReport::Value::operator==(const Id& value) const {
    return type_ == kId && (*value_.id_)->Equals(value);
  }
  
  int StatsReport::Value::int_val() const {
    RTC_DCHECK(type_ == kInt);
    return value_.int_;
  }
  
  int64_t StatsReport::Value::int64_val() const {
    RTC_DCHECK(type_ == kInt64);
    return value_.int64_;
  }
  
  float StatsReport::Value::float_val() const {
    RTC_DCHECK(type_ == kFloat);
    return value_.float_;
  }
  
  const char* StatsReport::Value::static_string_val() const {
    RTC_DCHECK(type_ == kStaticString);
    return value_.static_string_;
  }
  
  const std::string& StatsReport::Value::string_val() const {
    RTC_DCHECK(type_ == kString);
    return *value_.string_;
  }
  
  bool StatsReport::Value::bool_val() const {
    RTC_DCHECK(type_ == kBool);
    return value_.bool_;
  }
  
  const char* StatsReport::Value::display_name() const {
    switch (name) {
      case kStatsValueNameAecDivergentFilterFraction:
        return "aecDivergentFilterFraction";
      case kStatsValueNameAudioOutputLevel:
        return "audioOutputLevel";
      case kStatsValueNameAudioInputLevel:
        return "audioInputLevel";
      case kStatsValueNameBytesSent:
        return "bytesSent";
      case kStatsValueNamePacketsSent:
        return "packetsSent";
      case kStatsValueNameBytesReceived:
        return "bytesReceived";
      case kStatsValueNameLabel:
        return "label";
      case kStatsValueNamePacketsReceived:
        return "packetsReceived";
      case kStatsValueNamePacketsLost:
        return "packetsLost";
      case kStatsValueNameProtocol:
        return "protocol";
      case kStatsValueNameTransportId:
        return "transportId";
      case kStatsValueNameSelectedCandidatePairId:
        return "selectedCandidatePairId";
      case kStatsValueNameSsrc:
        return "ssrc";
      case kStatsValueNameState:
        return "state";
      case kStatsValueNameDataChannelId:
        return "datachannelid";
      case kStatsValueNameFramesDecoded:
        return "framesDecoded";
      case kStatsValueNameFramesEncoded:
        return "framesEncoded";
      case kStatsValueNameCodecImplementationName:
        return "codecImplementationName";
      case kStatsValueNameMediaType:
        return "mediaType";
      case kStatsValueNameQpSum:
        return "qpSum";
      // 'goog' prefixed constants.
      case kStatsValueNameAccelerateRate:
        return "googAccelerateRate";
      case kStatsValueNameActiveConnection:
        return "googActiveConnection";
      case kStatsValueNameActualEncBitrate:
        return "googActualEncBitrate";
      case kStatsValueNameAvailableReceiveBandwidth:
        return "googAvailableReceiveBandwidth";
      case kStatsValueNameAvailableSendBandwidth:
        return "googAvailableSendBandwidth";
      case kStatsValueNameAvgEncodeMs:
        return "googAvgEncodeMs";
      case kStatsValueNameBucketDelay:
        return "googBucketDelay";
      case kStatsValueNameBandwidthLimitedResolution:
        return "googBandwidthLimitedResolution";
      // STUN ping related attributes.
      // TODO(zhihuang) Rename these stats to follow the standards.
      case kStatsValueNameSentPingRequestsTotal:
        return "requestsSent";
      case kStatsValueNameSentPingRequestsBeforeFirstResponse:
        return "consentRequestsSent";
      case kStatsValueNameSentPingResponses:
        return "responsesSent";
      case kStatsValueNameRecvPingRequests:
        return "requestsReceived";
      case kStatsValueNameRecvPingResponses:
        return "responsesReceived";
  
      // Candidate related attributes. Values are taken from
      // http://w3c.github.io/webrtc-stats/#rtcstatstype-enum*.
      case kStatsValueNameCandidateIPAddress:
        return "ipAddress";
      case kStatsValueNameCandidateNetworkType:
        return "networkType";
      case kStatsValueNameCandidatePortNumber:
        return "portNumber";
      case kStatsValueNameCandidatePriority:
        return "priority";
      case kStatsValueNameCandidateTransportType:
        return "transport";
      case kStatsValueNameCandidateType:
        return "candidateType";
  
      case kStatsValueNameChannelId:
        return "googChannelId";
      case kStatsValueNameCodecName:
        return "googCodecName";
      case kStatsValueNameComponent:
        return "googComponent";
      case kStatsValueNameContentName:
        return "googContentName";
      case kStatsValueNameCpuLimitedResolution:
        return "googCpuLimitedResolution";
      case kStatsValueNameDecodingCTSG:
        return "googDecodingCTSG";
      case kStatsValueNameDecodingCTN:
        return "googDecodingCTN";
      case kStatsValueNameDecodingMutedOutput:
        return "googDecodingMuted";
      case kStatsValueNameDecodingNormal:
        return "googDecodingNormal";
      case kStatsValueNameDecodingPLC:
        return "googDecodingPLC";
      case kStatsValueNameDecodingCNG:
        return "googDecodingCNG";
      case kStatsValueNameDecodingPLCCNG:
        return "googDecodingPLCCNG";
      case kStatsValueNameDer:
        return "googDerBase64";
      case kStatsValueNameDtlsCipher:
        return "dtlsCipher";
      case kStatsValueNameEchoCancellationQualityMin:
        return "googEchoCancellationQualityMin";
      case kStatsValueNameEchoDelayMedian:
        return "googEchoCancellationEchoDelayMedian";
      case kStatsValueNameEchoDelayStdDev:
        return "googEchoCancellationEchoDelayStdDev";
      case kStatsValueNameEchoReturnLoss:
        return "googEchoCancellationReturnLoss";
      case kStatsValueNameEchoReturnLossEnhancement:
        return "googEchoCancellationReturnLossEnhancement";
      case kStatsValueNameEncodeUsagePercent:
        return "googEncodeUsagePercent";
      case kStatsValueNameExpandRate:
        return "googExpandRate";
      case kStatsValueNameFingerprint:
        return "googFingerprint";
      case kStatsValueNameFingerprintAlgorithm:
        return "googFingerprintAlgorithm";
      case kStatsValueNameFirsReceived:
        return "googFirsReceived";
      case kStatsValueNameFirsSent:
        return "googFirsSent";
      case kStatsValueNameFrameHeightInput:
        return "googFrameHeightInput";
      case kStatsValueNameFrameHeightReceived:
        return "googFrameHeightReceived";
      case kStatsValueNameFrameHeightSent:
        return "googFrameHeightSent";
      case kStatsValueNameFrameRateReceived:
        return "googFrameRateReceived";
      case kStatsValueNameFrameRateDecoded:
        return "googFrameRateDecoded";
      case kStatsValueNameFrameRateOutput:
        return "googFrameRateOutput";
      case kStatsValueNameDecodeMs:
        return "googDecodeMs";
      case kStatsValueNameMaxDecodeMs:
        return "googMaxDecodeMs";
      case kStatsValueNameCurrentDelayMs:
        return "googCurrentDelayMs";
      case kStatsValueNameTargetDelayMs:
        return "googTargetDelayMs";
      case kStatsValueNameJitterBufferMs:
        return "googJitterBufferMs";
      case kStatsValueNameMinPlayoutDelayMs:
        return "googMinPlayoutDelayMs";
      case kStatsValueNameRenderDelayMs:
        return "googRenderDelayMs";
      case kStatsValueNameCaptureStartNtpTimeMs:
        return "googCaptureStartNtpTimeMs";
      case kStatsValueNameFrameRateInput:
        return "googFrameRateInput";
      case kStatsValueNameFrameRateSent:
        return "googFrameRateSent";
      case kStatsValueNameFrameWidthInput:
        return "googFrameWidthInput";
      case kStatsValueNameFrameWidthReceived:
        return "googFrameWidthReceived";
      case kStatsValueNameFrameWidthSent:
        return "googFrameWidthSent";
      case kStatsValueNameInitiator:
        return "googInitiator";
      case kStatsValueNameInterframeDelaySumMs:
        return "googInterframeDelaySum";
      case kStatsValueNameIssuerId:
        return "googIssuerId";
      case kStatsValueNameJitterReceived:
        return "googJitterReceived";
      case kStatsValueNameLocalAddress:
        return "googLocalAddress";
      case kStatsValueNameLocalCandidateId:
        return "localCandidateId";
      case kStatsValueNameLocalCandidateType:
        return "googLocalCandidateType";
      case kStatsValueNameLocalCertificateId:
        return "localCertificateId";
      case kStatsValueNameAdaptationChanges:
        return "googAdaptationChanges";
      case kStatsValueNameNacksReceived:
        return "googNacksReceived";
      case kStatsValueNameNacksSent:
        return "googNacksSent";
      case kStatsValueNamePreemptiveExpandRate:
        return "googPreemptiveExpandRate";
      case kStatsValueNamePlisReceived:
        return "googPlisReceived";
      case kStatsValueNamePlisSent:
        return "googPlisSent";
      case kStatsValueNamePreferredJitterBufferMs:
        return "googPreferredJitterBufferMs";
      case kStatsValueNameReceiving:
        return "googReadable";
      case kStatsValueNameRemoteAddress:
        return "googRemoteAddress";
      case kStatsValueNameRemoteCandidateId:
        return "remoteCandidateId";
      case kStatsValueNameRemoteCandidateType:
        return "googRemoteCandidateType";
      case kStatsValueNameRemoteCertificateId:
        return "remoteCertificateId";
      case kStatsValueNameResidualEchoLikelihood:
        return "googResidualEchoLikelihood";
      case kStatsValueNameResidualEchoLikelihoodRecentMax:
        return "googResidualEchoLikelihoodRecentMax";
      case kStatsValueNameRetransmitBitrate:
        return "googRetransmitBitrate";
      case kStatsValueNameRtt:
        return "googRtt";
      case kStatsValueNameSecondaryDecodedRate:
        return "googSecondaryDecodedRate";
      case kStatsValueNameSendPacketsDiscarded:
        return "packetsDiscardedOnSend";
      case kStatsValueNameSpeechExpandRate:
        return "googSpeechExpandRate";
      case kStatsValueNameSrtpCipher:
        return "srtpCipher";
      case kStatsValueNameTargetEncBitrate:
        return "googTargetEncBitrate";
      case kStatsValueNameTotalAudioEnergy:
        return "totalAudioEnergy";
      case kStatsValueNameTotalSamplesDuration:
        return "totalSamplesDuration";
      case kStatsValueNameTransmitBitrate:
        return "googTransmitBitrate";
      case kStatsValueNameTransportType:
        return "googTransportType";
      case kStatsValueNameTrackId:
        return "googTrackId";
      case kStatsValueNameTimingFrameInfo:
        return "googTimingFrameInfo";
      case kStatsValueNameTypingNoiseState:
        return "googTypingNoiseState";
      case kStatsValueNameWritable:
        return "googWritable";
    }
  
    return nullptr;
  }
  
  std::string StatsReport::Value::ToString() const {
    switch (type_) {
      case kInt:
        return rtc::ToString(value_.int_);
      case kInt64:
        return rtc::ToString(value_.int64_);
      case kFloat:
        return rtc::ToString(value_.float_);
      case kStaticString:
        return std::string(value_.static_string_);
      case kString:
        return *value_.string_;
      case kBool:
        return value_.bool_ ? "true" : "false";
      case kId:
        return (*value_.id_)->ToString();
    }
    RTC_NOTREACHED();
    return std::string();
  }
  
  StatsReport::StatsReport(const Id& id) : id_(id), timestamp_(0.0) {
    RTC_DCHECK(id_.get());
  }
  
  StatsReport::~StatsReport() = default;
  
  // static
  StatsReport::Id StatsReport::NewBandwidthEstimationId() {
    return Id(new RefCountedObject<BandwidthEstimationId>());
  }
  
  // static
  StatsReport::Id StatsReport::NewTypedId(StatsType type, const std::string& id) {
    return Id(new RefCountedObject<TypedId>(type, id));
  }
  
  // static
  StatsReport::Id StatsReport::NewTypedIntId(StatsType type, int id) {
    return Id(new RefCountedObject<TypedIntId>(type, id));
  }
  
  // static
  StatsReport::Id StatsReport::NewIdWithDirection(
      StatsType type, const std::string& id, StatsReport::Direction direction) {
    return Id(new RefCountedObject<IdWithDirection>(type, id, direction));
  }
  
  // static
  StatsReport::Id StatsReport::NewCandidateId(bool local, const std::string& id) {
    return Id(new RefCountedObject<CandidateId>(local, id));
  }
  
  // static
  StatsReport::Id StatsReport::NewComponentId(
      const std::string& content_name, int component) {
    return Id(new RefCountedObject<ComponentId>(content_name, component));
  }
  
  // static
  StatsReport::Id StatsReport::NewCandidatePairId(
      const std::string& content_name, int component, int index) {
    return Id(new RefCountedObject<CandidatePairId>(
        content_name, component, index));
  }
  
  const char* StatsReport::TypeToString() const {
    return InternalTypeToString(id_->type());
  }
  
  void StatsReport::AddString(StatsReport::StatsValueName name,
                              const std::string& value) {
    const Value* found = FindValue(name);
    if (!found || !(*found == value))
      values_[name] = ValuePtr(new Value(name, value));
  }
  
  void StatsReport::AddString(StatsReport::StatsValueName name,
                              const char* value) {
    const Value* found = FindValue(name);
    if (!found || !(*found == value))
      values_[name] = ValuePtr(new Value(name, value));
  }
  
  void StatsReport::AddInt64(StatsReport::StatsValueName name, int64_t value) {
    const Value* found = FindValue(name);
    if (!found || !(*found == value))
      values_[name] = ValuePtr(new Value(name, value, Value::kInt64));
  }
  
  void StatsReport::AddInt(StatsReport::StatsValueName name, int value) {
    const Value* found = FindValue(name);
    if (!found || !(*found == static_cast<int64_t>(value)))
      values_[name] = ValuePtr(new Value(name, value, Value::kInt));
  }
  
  void StatsReport::AddFloat(StatsReport::StatsValueName name, float value) {
    const Value* found = FindValue(name);
    if (!found || !(*found == value))
      values_[name] = ValuePtr(new Value(name, value));
  }
  
  void StatsReport::AddBoolean(StatsReport::StatsValueName name, bool value) {
    const Value* found = FindValue(name);
    if (!found || !(*found == value))
      values_[name] = ValuePtr(new Value(name, value));
  }
  
  void StatsReport::AddId(StatsReport::StatsValueName name,
                          const Id& value) {
    const Value* found = FindValue(name);
    if (!found || !(*found == value))
      values_[name] = ValuePtr(new Value(name, value));
  }
  
  const StatsReport::Value* StatsReport::FindValue(StatsValueName name) const {
    Values::const_iterator it = values_.find(name);
    return it == values_.end() ? nullptr : it->second.get();
  }
  
  StatsCollection::StatsCollection() {
  }
  
  StatsCollection::~StatsCollection() {
    RTC_DCHECK(thread_checker_.CalledOnValidThread());
    for (auto* r : list_)
      delete r;
  }
  
  StatsCollection::const_iterator StatsCollection::begin() const {
    RTC_DCHECK(thread_checker_.CalledOnValidThread());
    return list_.begin();
  }
  
  StatsCollection::const_iterator StatsCollection::end() const {
    RTC_DCHECK(thread_checker_.CalledOnValidThread());
    return list_.end();
  }
  
  size_t StatsCollection::size() const {
    RTC_DCHECK(thread_checker_.CalledOnValidThread());
    return list_.size();
  }
  
  StatsReport* StatsCollection::InsertNew(const StatsReport::Id& id) {
    RTC_DCHECK(thread_checker_.CalledOnValidThread());
    RTC_DCHECK(Find(id) == nullptr);
    StatsReport* report = new StatsReport(id);
    list_.push_back(report);
    return report;
  }
  
  StatsReport* StatsCollection::FindOrAddNew(const StatsReport::Id& id) {
    RTC_DCHECK(thread_checker_.CalledOnValidThread());
    StatsReport* ret = Find(id);
    return ret ? ret : InsertNew(id);
  }
  
  StatsReport* StatsCollection::ReplaceOrAddNew(const StatsReport::Id& id) {
    RTC_DCHECK(thread_checker_.CalledOnValidThread());
    RTC_DCHECK(id.get());
    Container::iterator it = std::find_if(list_.begin(), list_.end(),
        [&id](const StatsReport* r)->bool { return r->id()->Equals(id); });
    if (it != end()) {
      StatsReport* report = new StatsReport((*it)->id());
      delete *it;
      *it = report;
      return report;
    }
    return InsertNew(id);
  }
  
  // Looks for a report with the given |id|.  If one is not found, null
  // will be returned.
  StatsReport* StatsCollection::Find(const StatsReport::Id& id) {
    RTC_DCHECK(thread_checker_.CalledOnValidThread());
    Container::iterator it = std::find_if(list_.begin(), list_.end(),
        [&id](const StatsReport* r)->bool { return r->id()->Equals(id); });
    return it == list_.end() ? nullptr : *it;
  }
  
  }  // namespace webrtc
  