Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright 2004 The WebRTC Project Authors. All rights reserved. | 2 * Copyright 2004 The WebRTC Project Authors. All rights reserved. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license | 4 * Use of this source code is governed by a BSD-style license |
| 5 * that can be found in the LICENSE file in the root of the source | 5 * that can be found in the LICENSE file in the root of the source |
| 6 * tree. An additional intellectual property rights grant can be found | 6 * tree. An additional intellectual property rights grant can be found |
| 7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
| 8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
| 9 */ | 9 */ |
| 10 | 10 |
| 11 #ifndef WEBRTC_BASE_BUFFER_H_ | 11 #ifndef WEBRTC_BASE_BUFFER_H_ |
| 12 #define WEBRTC_BASE_BUFFER_H_ | 12 #define WEBRTC_BASE_BUFFER_H_ |
| 13 | 13 |
| 14 #include <algorithm> // std::swap (pre-C++11) | 14 #include <algorithm> // std::swap (pre-C++11) |
| 15 #include <cassert> | 15 #include <cassert> |
| 16 #include <cstring> | 16 #include <cstring> |
| 17 #include <memory> | 17 #include <memory> |
| 18 #include <utility> // std::swap (C++11 and later) | 18 #include <utility> // std::swap (C++11 and later) |
| 19 | 19 |
| 20 #include "webrtc/base/constructormagic.h" | 20 #include "webrtc/base/constructormagic.h" |
| 21 #include "webrtc/base/atomicops.h" | |
| 21 #include "webrtc/base/deprecation.h" | 22 #include "webrtc/base/deprecation.h" |
| 22 | 23 |
| 23 namespace rtc { | 24 namespace rtc { |
| 24 | 25 |
| 25 namespace internal { | 26 namespace internal { |
| 26 | 27 |
| 27 // (Internal; please don't use outside this file.) ByteType<T>::t is int if T | 28 // (Internal; please don't use outside this file.) ByteType<T>::t is int if T |
| 28 // is uint8_t, int8_t, or char; otherwise, it's a compilation error. Use like | 29 // is uint8_t, int8_t, or char; otherwise, it's a compilation error. Use like |
| 29 // this: | 30 // this: |
| 30 // | 31 // |
| 31 // template <typename T, typename ByteType<T>::t = 0> | 32 // template <typename T, typename ByteType<T>::t = 0> |
| 32 // void foo(T* x); | 33 // void foo(T* x); |
| 33 // | 34 // |
| 34 // to let foo<T> be defined only for byte-sized integers. | 35 // to let foo<T> be defined only for byte-sized integers. |
| 35 template <typename T> | 36 template <typename T> |
| 36 struct ByteType { | 37 struct ByteType { |
| 37 private: | 38 private: |
| 38 static int F(uint8_t*); | 39 static int F(uint8_t*); |
| 39 static int F(int8_t*); | 40 static int F(int8_t*); |
| 40 static int F(char*); | 41 static int F(char*); |
| 41 | 42 |
| 42 public: | 43 public: |
| 43 using t = decltype(F(static_cast<T*>(nullptr))); | 44 using t = decltype(F(static_cast<T*>(nullptr))); |
| 44 }; | 45 }; |
| 45 | 46 |
| 46 } // namespace internal | 47 } // namespace internal |
| 47 | 48 |
| 49 // Data stored in a buffer. RefCounted and will be shared between buffers that | |
| 50 // are cloned to avoid unnecessary allocations / copies. | |
| 51 class BufferData { | |
|
tommi
2016/02/14 09:03:42
should this be in the 'internal' namespace? I'm as
joachim
2016/02/14 11:10:19
You are right, changed.
| |
| 52 public: | |
| 53 BufferData(); | |
| 54 | |
| 55 explicit BufferData(const BufferData& data); | |
| 56 | |
| 57 // Construct data with the specified number of uninitialized bytes. | |
| 58 explicit BufferData(size_t size); | |
| 59 BufferData(size_t size, size_t capacity); | |
| 60 | |
| 61 // Construct data and copy the specified number of bytes into it. | |
| 62 BufferData(const void* data, size_t size); | |
| 63 BufferData(const void* data, size_t size, size_t capacity); | |
| 64 | |
| 65 size_t size() const { | |
| 66 assert(IsConsistent()); | |
|
tommi
2016/02/14 09:03:42
use RTC_DCHECK instead of assert()
joachim
2016/02/14 11:10:18
Changed here and in other places.
| |
| 67 return size_; | |
| 68 } | |
| 69 size_t capacity() const { | |
| 70 assert(IsConsistent()); | |
| 71 return capacity_; | |
| 72 } | |
| 73 uint8_t* data() const { | |
| 74 assert(IsConsistent()); | |
| 75 return data_.get(); | |
| 76 } | |
| 77 | |
| 78 void SetData(const void* data, size_t size) { | |
| 79 assert(IsConsistent()); | |
| 80 size_ = 0; | |
| 81 AppendData(data, size); | |
| 82 } | |
| 83 | |
| 84 void AppendData(const void* data, size_t size) { | |
| 85 assert(IsConsistent()); | |
| 86 const size_t new_size = size_ + size; | |
| 87 EnsureCapacity(new_size); | |
| 88 std::memcpy(data_.get() + size_, data, size); | |
| 89 size_ = new_size; | |
| 90 assert(IsConsistent()); | |
| 91 } | |
| 92 | |
| 93 // Sets the size of the data. If the new size is smaller than the old, the | |
| 94 // data contents will be kept but truncated; if the new size is greater, | |
| 95 // the existing contents will be kept and the new space will be | |
| 96 // uninitialized. | |
| 97 void SetSize(size_t size) { | |
| 98 EnsureCapacity(size); | |
| 99 size_ = size; | |
| 100 } | |
| 101 | |
| 102 // Ensure that the data size can be increased to at least capacity without | |
| 103 // further reallocation. (Of course, this operation might need to reallocate | |
| 104 // the buffer.) | |
| 105 void EnsureCapacity(size_t capacity) { | |
| 106 assert(IsConsistent()); | |
| 107 if (capacity <= capacity_) { | |
| 108 return; | |
| 109 } | |
| 110 | |
| 111 std::unique_ptr<uint8_t[]> new_data(new uint8_t[capacity]); | |
| 112 std::memcpy(new_data.get(), data_.get(), size_); | |
| 113 data_ = std::move(new_data); | |
| 114 capacity_ = capacity; | |
| 115 assert(IsConsistent()); | |
| 116 } | |
| 117 | |
| 118 // Resets the data to zero size and capacity. | |
| 119 void Clear() { | |
| 120 data_.reset(); | |
| 121 size_ = 0; | |
| 122 capacity_ = 0; | |
| 123 assert(IsConsistent()); | |
| 124 } | |
| 125 | |
| 126 // Reimplement parts of RefCountedObject which is not in rtc_base_approved. | |
|
joachim
2016/02/13 16:08:46
Are there plans to move "refcount.h" into "rtc_bas
tommi
2016/02/14 09:03:42
Yes, that should be trivial.
Btw, does the refcoun
joachim
2016/02/14 11:10:18
Thanks for your CL to move it, please see my last
| |
| 127 | |
| 128 int AddRef() const { | |
| 129 return AtomicOps::Increment(&ref_count_); | |
| 130 } | |
| 131 | |
| 132 int Release() const { | |
| 133 int count = AtomicOps::Decrement(&ref_count_); | |
| 134 if (!count) { | |
| 135 delete this; | |
| 136 } | |
| 137 return count; | |
| 138 } | |
| 139 | |
| 140 // Returns true if the data is only used for a single Buffer. | |
| 141 bool HasOneRef() const { | |
| 142 return AtomicOps::AcquireLoad(&ref_count_) == 1; | |
| 143 } | |
| 144 | |
| 145 private: | |
| 146 friend class Buffer; | |
| 147 | |
| 148 ~BufferData(); | |
| 149 | |
| 150 // Precondition for all methods except Clear and the destructor. | |
| 151 // Postcondition for all methods except move construction and move | |
| 152 // assignment, which leave the moved-from object in a possibly inconsistent | |
| 153 // state. | |
| 154 bool IsConsistent() const { | |
| 155 return (data_ || capacity_ == 0) && capacity_ >= size_; | |
| 156 } | |
| 157 | |
| 158 #ifndef NDEBUG | |
| 159 // Called when *this has been moved from. Conceptually it's a no-op, but we | |
| 160 // can mutate the state slightly to help subsequent sanity checks catch bugs. | |
| 161 void OnMovedFrom() { | |
| 162 // Ensure that *this is always inconsistent, to provoke bugs. | |
| 163 size_ = 1; | |
| 164 capacity_ = 0; | |
| 165 } | |
| 166 #endif | |
| 167 | |
| 168 mutable volatile int ref_count_; | |
| 169 | |
| 170 size_t size_; | |
| 171 size_t capacity_; | |
| 172 std::unique_ptr<uint8_t[]> data_; | |
| 173 }; | |
| 174 | |
| 48 // Basic buffer class, can be grown and shrunk dynamically. | 175 // Basic buffer class, can be grown and shrunk dynamically. |
| 49 // Unlike std::string/vector, does not initialize data when expanding capacity. | 176 // Unlike std::string/vector, does not initialize data when expanding capacity. |
| 177 // The underlying memory is shared between copies of buffers and cloned if a | |
| 178 // shared buffer is being modified. | |
| 50 class Buffer { | 179 class Buffer { |
| 51 public: | 180 public: |
| 52 Buffer(); // An empty buffer. | 181 Buffer(); // An empty buffer. |
| 53 Buffer(const Buffer& buf); // Copy size and contents of an existing buffer. | 182 Buffer(const Buffer& buf); // Copy size and contents of an existing buffer. |
| 54 Buffer(Buffer&& buf); // Move contents from an existing buffer. | 183 Buffer(Buffer&& buf); // Move contents from an existing buffer. |
| 55 | 184 |
| 56 // Construct a buffer with the specified number of uninitialized bytes. | 185 // Construct a buffer with the specified number of uninitialized bytes. |
| 57 explicit Buffer(size_t size); | 186 explicit Buffer(size_t size); |
| 58 Buffer(size_t size, size_t capacity); | 187 Buffer(size_t size, size_t capacity); |
| 59 | 188 |
| 60 // Construct a buffer and copy the specified number of bytes into it. The | 189 // Construct a buffer and copy the specified number of bytes into it. The |
| 61 // source array may be (const) uint8_t*, int8_t*, or char*. | 190 // source array may be (const) uint8_t*, int8_t*, or char*. |
| 62 template <typename T, typename internal::ByteType<T>::t = 0> | 191 template <typename T, typename internal::ByteType<T>::t = 0> |
| 63 Buffer(const T* data, size_t size) | 192 Buffer(const T* data, size_t size) |
| 64 : Buffer(data, size, size) {} | 193 : Buffer(data, size, size) {} |
| 65 template <typename T, typename internal::ByteType<T>::t = 0> | 194 template <typename T, typename internal::ByteType<T>::t = 0> |
| 66 Buffer(const T* data, size_t size, size_t capacity) | 195 Buffer(const T* data, size_t size, size_t capacity) |
| 67 : Buffer(size, capacity) { | 196 : Buffer(size, capacity) { |
| 68 std::memcpy(data_.get(), data, size); | 197 std::memcpy(data_->data(), data, size); |
| 69 } | 198 } |
| 70 | 199 |
| 71 // Construct a buffer from the contents of an array. | 200 // Construct a buffer from the contents of an array. |
| 72 template <typename T, size_t N, typename internal::ByteType<T>::t = 0> | 201 template <typename T, size_t N, typename internal::ByteType<T>::t = 0> |
| 73 Buffer(const T(&array)[N]) | 202 Buffer(const T(&array)[N]) // NOLINT: runtime/explicit |
| 74 : Buffer(array, N) {} | 203 : Buffer(array, N) {} |
| 75 | 204 |
| 76 ~Buffer(); | 205 ~Buffer(); |
| 77 | 206 |
| 78 // Get a pointer to the data. Just .data() will give you a (const) uint8_t*, | 207 // Get a pointer to the data. Just .data() will give you a (const) uint8_t*, |
| 79 // but you may also use .data<int8_t>() and .data<char>(). | 208 // but you may also use .data<int8_t>() and .data<char>(). |
| 80 template <typename T = uint8_t, typename internal::ByteType<T>::t = 0> | 209 template <typename T = uint8_t, typename internal::ByteType<T>::t = 0> |
| 81 const T* data() const { | 210 const T* data() const { |
| 82 assert(IsConsistent()); | 211 assert(IsConsistent()); |
| 83 return reinterpret_cast<T*>(data_.get()); | 212 return reinterpret_cast<T*>(data_->data()); |
| 84 } | 213 } |
| 214 // Get writable pointer to the data. This will create a copy of the underlying | |
| 215 // data if it is shared with other buffers. | |
| 85 template <typename T = uint8_t, typename internal::ByteType<T>::t = 0> | 216 template <typename T = uint8_t, typename internal::ByteType<T>::t = 0> |
| 86 T* data() { | 217 T* data() { |
| 87 assert(IsConsistent()); | 218 CloneDataIfReferenced(); |
| 88 return reinterpret_cast<T*>(data_.get()); | 219 return reinterpret_cast<T*>(data_->data()); |
| 89 } | 220 } |
| 90 | 221 |
| 91 size_t size() const { | 222 size_t size() const { |
| 92 assert(IsConsistent()); | 223 assert(IsConsistent()); |
| 93 return size_; | 224 return data_->size(); |
| 94 } | 225 } |
| 95 size_t capacity() const { | 226 size_t capacity() const { |
| 96 assert(IsConsistent()); | 227 assert(IsConsistent()); |
| 97 return capacity_; | 228 return data_->capacity(); |
| 98 } | 229 } |
| 99 | 230 |
| 100 Buffer& operator=(const Buffer& buf) { | 231 Buffer& operator=(const Buffer& buf) { |
| 101 if (&buf != this) | 232 if (&buf != this) { |
| 102 SetData(buf.data(), buf.size()); | 233 SetData(buf); |
| 234 } | |
| 103 return *this; | 235 return *this; |
| 104 } | 236 } |
| 105 Buffer& operator=(Buffer&& buf) { | 237 Buffer& operator=(Buffer&& buf) { |
| 106 assert(IsConsistent()); | 238 if (&buf != this) { |
| 107 assert(buf.IsConsistent()); | 239 assert(IsConsistent()); |
| 108 size_ = buf.size_; | 240 assert(buf.IsConsistent()); |
| 109 capacity_ = buf.capacity_; | 241 data_->Release(); |
| 110 data_ = std::move(buf.data_); | 242 data_ = std::move(buf.data_); |
| 111 buf.OnMovedFrom(); | 243 buf.OnMovedFrom(); |
| 244 } | |
| 112 return *this; | 245 return *this; |
| 113 } | 246 } |
| 114 | 247 |
| 115 bool operator==(const Buffer& buf) const { | 248 bool operator==(const Buffer& buf) const { |
| 116 assert(IsConsistent()); | 249 return data_ == buf.data_ || |
| 117 return size_ == buf.size() && memcmp(data_.get(), buf.data(), size_) == 0; | 250 (size() == buf.size() && memcmp(data(), buf.data(), size()) == 0); |
| 118 } | 251 } |
| 119 | 252 |
| 120 bool operator!=(const Buffer& buf) const { return !(*this == buf); } | 253 bool operator!=(const Buffer& buf) const { return !(*this == buf); } |
| 121 | 254 |
| 122 // Replace the contents of the buffer. Accepts the same types as the | 255 // Replace the contents of the buffer. Accepts the same types as the |
| 123 // constructors. | 256 // constructors. |
| 124 template <typename T, typename internal::ByteType<T>::t = 0> | 257 template <typename T, typename internal::ByteType<T>::t = 0> |
| 125 void SetData(const T* data, size_t size) { | 258 void SetData(const T* data, size_t size) { |
| 126 assert(IsConsistent()); | 259 assert(IsConsistent()); |
| 127 size_ = 0; | 260 if (!data_->HasOneRef()) { |
| 128 AppendData(data, size); | 261 data_->Release(); |
| 262 data_ = new BufferData(data, size); | |
| 263 } else { | |
| 264 data_->SetData(data, size); | |
| 265 } | |
| 129 } | 266 } |
| 130 template <typename T, size_t N, typename internal::ByteType<T>::t = 0> | 267 template <typename T, size_t N, typename internal::ByteType<T>::t = 0> |
| 131 void SetData(const T(&array)[N]) { | 268 void SetData(const T(&array)[N]) { |
| 132 SetData(array, N); | 269 SetData(array, N); |
| 133 } | 270 } |
| 134 void SetData(const Buffer& buf) { SetData(buf.data(), buf.size()); } | 271 void SetData(const Buffer& buf) { |
| 272 if (&buf != this) { | |
| 273 assert(IsConsistent()); | |
| 274 assert(buf.IsConsistent()); | |
| 275 buf.data_->AddRef(); | |
| 276 data_->Release(); | |
| 277 data_ = buf.data_; | |
| 278 } | |
| 279 } | |
| 135 | 280 |
| 136 // Append data to the buffer. Accepts the same types as the constructors. | 281 // Append data to the buffer. Accepts the same types as the constructors. |
| 137 template <typename T, typename internal::ByteType<T>::t = 0> | 282 template <typename T, typename internal::ByteType<T>::t = 0> |
| 138 void AppendData(const T* data, size_t size) { | 283 void AppendData(const T* data, size_t size) { |
| 139 assert(IsConsistent()); | 284 CloneDataIfReferenced(); |
| 140 const size_t new_size = size_ + size; | 285 data_->AppendData(data, size); |
| 141 EnsureCapacity(new_size); | |
| 142 std::memcpy(data_.get() + size_, data, size); | |
| 143 size_ = new_size; | |
| 144 assert(IsConsistent()); | |
| 145 } | 286 } |
| 146 template <typename T, size_t N, typename internal::ByteType<T>::t = 0> | 287 template <typename T, size_t N, typename internal::ByteType<T>::t = 0> |
| 147 void AppendData(const T(&array)[N]) { | 288 void AppendData(const T(&array)[N]) { |
| 148 AppendData(array, N); | 289 AppendData(array, N); |
| 149 } | 290 } |
| 150 void AppendData(const Buffer& buf) { AppendData(buf.data(), buf.size()); } | 291 void AppendData(const Buffer& buf) { AppendData(buf.data(), buf.size()); } |
| 151 | 292 |
| 152 // Sets the size of the buffer. If the new size is smaller than the old, the | 293 // Sets the size of the buffer. If the new size is smaller than the old, the |
| 153 // buffer contents will be kept but truncated; if the new size is greater, | 294 // buffer contents will be kept but truncated; if the new size is greater, |
| 154 // the existing contents will be kept and the new space will be | 295 // the existing contents will be kept and the new space will be |
| 155 // uninitialized. | 296 // uninitialized. |
| 156 void SetSize(size_t size) { | 297 void SetSize(size_t size) { |
| 157 EnsureCapacity(size); | 298 CloneDataIfReferenced(); |
| 158 size_ = size; | 299 data_->SetSize(size); |
| 159 } | 300 } |
| 160 | 301 |
| 161 // Ensure that the buffer size can be increased to at least capacity without | 302 // Ensure that the buffer size can be increased to at least capacity without |
| 162 // further reallocation. (Of course, this operation might need to reallocate | 303 // further reallocation. (Of course, this operation might need to reallocate |
| 163 // the buffer.) | 304 // the buffer.) |
| 164 void EnsureCapacity(size_t capacity) { | 305 void EnsureCapacity(size_t capacity) { |
| 165 assert(IsConsistent()); | 306 assert(IsConsistent()); |
| 166 if (capacity <= capacity_) | 307 if (capacity <= data_->capacity()) |
| 167 return; | 308 return; |
| 168 std::unique_ptr<uint8_t[]> new_data(new uint8_t[capacity]); | 309 |
| 169 std::memcpy(new_data.get(), data_.get(), size_); | 310 CloneDataIfReferenced(); |
| 170 data_ = std::move(new_data); | 311 data_->EnsureCapacity(capacity); |
| 171 capacity_ = capacity; | |
| 172 assert(IsConsistent()); | |
| 173 } | 312 } |
| 174 | 313 |
| 175 // b.Pass() does the same thing as std::move(b). | 314 // b.Pass() does the same thing as std::move(b). |
| 176 // Deprecated; remove in March 2016 (bug 5373). | 315 // Deprecated; remove in March 2016 (bug 5373). |
| 177 RTC_DEPRECATED Buffer&& Pass() { return DEPRECATED_Pass(); } | 316 RTC_DEPRECATED Buffer&& Pass() { return DEPRECATED_Pass(); } |
| 178 Buffer&& DEPRECATED_Pass() { | 317 Buffer&& DEPRECATED_Pass() { |
| 179 assert(IsConsistent()); | 318 assert(IsConsistent()); |
| 180 return std::move(*this); | 319 return std::move(*this); |
| 181 } | 320 } |
| 182 | 321 |
| 183 // Resets the buffer to zero size and capacity. Works even if the buffer has | 322 // Resets the buffer to zero size and capacity. |
| 184 // been moved from. | |
| 185 void Clear() { | 323 void Clear() { |
| 186 data_.reset(); | 324 if (!data_->HasOneRef()) { |
| 187 size_ = 0; | 325 data_->Release(); |
| 188 capacity_ = 0; | 326 data_ = new BufferData(); |
| 327 } else { | |
| 328 data_->Clear(); | |
| 329 } | |
| 189 assert(IsConsistent()); | 330 assert(IsConsistent()); |
| 190 } | 331 } |
| 191 | 332 |
| 192 // Swaps two buffers. Also works for buffers that have been moved from. | 333 // Swaps two buffers. |
| 193 friend void swap(Buffer& a, Buffer& b) { | 334 friend void swap(Buffer& a, Buffer& b) { |
| 194 using std::swap; | 335 std::swap(a.data_, b.data_); |
| 195 swap(a.size_, b.size_); | |
| 196 swap(a.capacity_, b.capacity_); | |
| 197 swap(a.data_, b.data_); | |
| 198 } | 336 } |
| 199 | 337 |
| 200 private: | 338 private: |
| 201 // Precondition for all methods except Clear and the destructor. | 339 // Precondition for all methods except Clear and the destructor. |
| 202 // Postcondition for all methods except move construction and move | 340 // Postcondition for all methods except move construction and move |
| 203 // assignment, which leave the moved-from object in a possibly inconsistent | 341 // assignment, which leave the moved-from object in a possibly inconsistent |
| 204 // state. | 342 // state. |
| 205 bool IsConsistent() const { | 343 bool IsConsistent() const { |
| 206 return (data_ || capacity_ == 0) && capacity_ >= size_; | 344 return data_ != nullptr && data_->IsConsistent(); |
| 207 } | 345 } |
| 208 | 346 |
| 209 // Called when *this has been moved from. Conceptually it's a no-op, but we | 347 // Called when *this has been moved from. Conceptually it's a no-op, but we |
| 210 // can mutate the state slightly to help subsequent sanity checks catch bugs. | 348 // can mutate the state slightly to help subsequent sanity checks catch bugs. |
| 211 void OnMovedFrom() { | 349 void OnMovedFrom() { |
| 212 #ifdef NDEBUG | 350 data_ = new BufferData(); |
| 213 // Make *this consistent and empty. Shouldn't be necessary, but better safe | 351 #ifndef NDEBUG |
| 214 // than sorry. | |
| 215 size_ = 0; | |
| 216 capacity_ = 0; | |
| 217 #else | |
| 218 // Ensure that *this is always inconsistent, to provoke bugs. | 352 // Ensure that *this is always inconsistent, to provoke bugs. |
| 219 size_ = 1; | 353 data_->OnMovedFrom(); |
| 220 capacity_ = 0; | |
| 221 #endif | 354 #endif |
| 222 } | 355 } |
| 223 | 356 |
| 224 size_t size_; | 357 // Create a copy of the underlying data if it is referenced from other Buffer |
| 225 size_t capacity_; | 358 // objects. |
| 226 std::unique_ptr<uint8_t[]> data_; | 359 void CloneDataIfReferenced() { |
| 360 assert(IsConsistent()); | |
| 361 if (data_->HasOneRef()) { | |
| 362 return; | |
| 363 } | |
| 364 | |
| 365 BufferData* new_data = new BufferData(*data_); | |
| 366 data_->Release(); | |
| 367 data_ = new_data; | |
| 368 } | |
| 369 | |
| 370 BufferData* data_; | |
| 227 }; | 371 }; |
| 228 | 372 |
| 229 } // namespace rtc | 373 } // namespace rtc |
| 230 | 374 |
| 231 #endif // WEBRTC_BASE_BUFFER_H_ | 375 #endif // WEBRTC_BASE_BUFFER_H_ |
| OLD | NEW |