/* * Copyright 2020 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. */ #ifndef P2P_BASE_STUN_DICTIONARY_H_ #define P2P_BASE_STUN_DICTIONARY_H_ #include #include #include #include #include #include "api/rtc_error.h" #include "api/transport/stun.h" namespace cricket { // A StunDictionaryView is a dictionary of StunAttributes. // - the StunAttributes can be read using the |Get|-methods. // - the dictionary is updated by using the |ApplyDelta|-method. // // A StunDictionaryWriter is used to create |delta|s for the |ApplyDelta|-method // - It keeps track of which updates has been applied at StunDictionaryView. // - It optionally keeps a local StunDictionaryView contains modification made // `locally` // // A pair StunDictionaryView(A)/StunDictionaryWriter(B) are linked so that // modifications to B is transfered to A using the STUN_ATTR_GOOG_DELTA // (StunByteStringAttribute) and the modification is ack:ed using // STUN_ATTR_GOOG_DELTA_ACK (StunUInt64Attribute). // // Note: // 1) It is possible to update one StunDictionaryView from multiple writers, // but this only works of the different writers write disjoint keys (which // is not checked/enforced by these classes). // 2) The opposite, one writer updating multiple StunDictionaryView, is not // possible. class StunDictionaryView { public: // A reserved key used to transport the version number static constexpr uint16_t kVersionKey = 0xFFFF; // A magic number used when transporting deltas. static constexpr uint16_t kDeltaMagic = 0x7788; // The version number for the delta format. static constexpr uint16_t kDeltaVersion = 0x1; // Gets the desired attribute value, or NULL if no such attribute type exists. // The pointer returned is guaranteed to be valid until ApplyDelta is called. const StunAddressAttribute* GetAddress(int key) const; const StunUInt32Attribute* GetUInt32(int key) const; const StunUInt64Attribute* GetUInt64(int key) const; const StunByteStringAttribute* GetByteString(int key) const; const StunUInt16ListAttribute* GetUInt16List(int key) const; bool empty() const { return attrs_.empty(); } size_t size() const { return attrs_.size(); } int bytes_stored() const { return bytes_stored_; } void set_max_bytes_stored(int max_bytes_stored) { max_bytes_stored_ = max_bytes_stored; } // Apply a delta and return // a pair with // - StunUInt64Attribute to ack the |delta|. // - vector of keys that was modified. webrtc::RTCErrorOr< std::pair, std::vector>> ApplyDelta(const StunByteStringAttribute& delta); private: friend class StunDictionaryWriter; const StunAttribute* GetOrNull( int key, absl::optional = absl::nullopt) const; size_t GetLength(int key) const; static webrtc::RTCErrorOr< std::pair>>> ParseDelta(const StunByteStringAttribute& delta); std::map> attrs_; std::map version_per_key_; int max_bytes_stored_ = 16384; int bytes_stored_ = 0; }; class StunDictionaryWriter { public: StunDictionaryWriter() { dictionary_ = std::make_unique(); } explicit StunDictionaryWriter( std::unique_ptr dictionary) { dictionary_ = std::move(dictionary); } // A pending modification. template class Modification { public: ~Modification() { commit(); } T* operator->() { return attr_.get(); } void abort() { attr_ = nullptr; } void commit() { if (attr_) { writer_->Set(std::move(attr_)); } } private: friend class StunDictionaryWriter; Modification(StunDictionaryWriter* writer, std::unique_ptr attr) : writer_(writer), attr_(std::move(attr)) {} StunDictionaryWriter* writer_; std::unique_ptr attr_; Modification(const Modification&) = delete; // not copyable (but movable). Modification& operator=(Modification&) = delete; // not copyable (but movable). }; // Record a modification. Modification SetAddress(int key) { return Modification( this, StunAttribute::CreateAddress(key)); } Modification SetUInt32(int key) { return Modification(this, StunAttribute::CreateUInt32(key)); } Modification SetUInt64(int key) { return Modification(this, StunAttribute::CreateUInt64(key)); } Modification SetByteString(int key) { return Modification( this, StunAttribute::CreateByteString(key)); } Modification SetUInt16List(int key) { return Modification( this, StunAttribute::CreateUInt16ListAttribute(key)); } // Delete a key. void Delete(int key); // Check if a key has a pending change (i.e a change // that has not been acked). bool Pending(int key) const; // Return number of of pending modifications. int Pending() const; // Create an StunByteStringAttribute containing the pending (e.g not ack:ed) // modifications. std::unique_ptr CreateDelta(); // Apply an delta ack. void ApplyDeltaAck(const StunUInt64Attribute&); // Return pointer to (optional) StunDictionaryView. const StunDictionaryView* dictionary() { return dictionary_.get(); } const StunDictionaryView* operator->() { return dictionary_.get(); } // Disable writer, // i.e CreateDelta always return null, and no modifications are made. // This is called if remote peer does not support GOOG_DELTA. void Disable(); bool disabled() const { return disabled_; } private: void Set(std::unique_ptr attr); bool disabled_ = false; // version of modification. int64_t version_ = 1; // (optional) StunDictionaryView. std::unique_ptr dictionary_; // sorted list of changes that has not been yet been ack:ed. std::vector> pending_; // tombstones, i.e values that has been deleted but not yet acked. std::map> tombstones_; }; } // namespace cricket #endif // P2P_BASE_STUN_DICTIONARY_H_