/* * Copyright 2004 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 WEBRTC_BASE_BUFFER_H_ #define WEBRTC_BASE_BUFFER_H_ #include // std::swap (pre-C++11) #include #include #include // std::swap (C++11 and later) #include "webrtc/base/scoped_ptr.h" namespace rtc { namespace internal { // (Internal; please don't use outside this file.) ByteType::t is int if T // is uint8_t, int8_t, or char; otherwise, it's a compilation error. Use like // this: // // template ::t = 0> // void foo(T* x); // // to let foo be defined only for byte-sized integers. template struct ByteType { private: static int F(uint8_t*); static int F(int8_t*); static int F(char*); public: using t = decltype(F(static_cast(nullptr))); }; } // namespace internal // Basic buffer class, can be grown and shrunk dynamically. // Unlike std::string/vector, does not initialize data when expanding capacity. class Buffer final { public: Buffer(); // An empty buffer. Buffer(const Buffer& buf); // Copy size and contents of an existing buffer. Buffer(Buffer&& buf); // Move contents from an existing buffer. // Construct a buffer with the specified number of uninitialized bytes. explicit Buffer(size_t size); Buffer(size_t size, size_t capacity); // Construct a buffer and copy the specified number of bytes into it. The // source array may be (const) uint8_t*, int8_t*, or char*. template ::t = 0> Buffer(const T* data, size_t size) : Buffer(data, size, size) {} template ::t = 0> Buffer(const T* data, size_t size, size_t capacity) : Buffer(size, capacity) { std::memcpy(data_.get(), data, size); } // Construct a buffer from the contents of an array. template ::t = 0> Buffer(const T(&array)[N]) : Buffer(array, N) {} ~Buffer(); // Get a pointer to the data. Just .data() will give you a (const) uint8_t*, // but you may also use .data() and .data(). template ::t = 0> const T* data() const { assert(IsConsistent()); return reinterpret_cast(data_.get()); } template ::t = 0> T* data() { assert(IsConsistent()); return reinterpret_cast(data_.get()); } size_t size() const { assert(IsConsistent()); return size_; } size_t capacity() const { assert(IsConsistent()); return capacity_; } Buffer& operator=(const Buffer& buf) { if (&buf != this) SetData(buf.data(), buf.size()); return *this; } Buffer& operator=(Buffer&& buf) { assert(IsConsistent()); assert(buf.IsConsistent()); size_ = buf.size_; capacity_ = buf.capacity_; data_ = buf.data_.Pass(); buf.OnMovedFrom(); return *this; } bool operator==(const Buffer& buf) const { assert(IsConsistent()); return size_ == buf.size() && memcmp(data_.get(), buf.data(), size_) == 0; } bool operator!=(const Buffer& buf) const { return !(*this == buf); } // Replace the contents of the buffer. Accepts the same types as the // constructors. template ::t = 0> void SetData(const T* data, size_t size) { assert(IsConsistent()); size_ = 0; AppendData(data, size); } template ::t = 0> void SetData(const T(&array)[N]) { SetData(array, N); } void SetData(const Buffer& buf) { SetData(buf.data(), buf.size()); } // Append data to the buffer. Accepts the same types as the constructors. template ::t = 0> void AppendData(const T* data, size_t size) { assert(IsConsistent()); const size_t new_size = size_ + size; EnsureCapacity(new_size); std::memcpy(data_.get() + size_, data, size); size_ = new_size; assert(IsConsistent()); } template ::t = 0> void AppendData(const T(&array)[N]) { AppendData(array, N); } void AppendData(const Buffer& buf) { AppendData(buf.data(), buf.size()); } // Sets the size of the buffer. If the new size is smaller than the old, the // buffer contents will be kept but truncated; if the new size is greater, // the existing contents will be kept and the new space will be // uninitialized. void SetSize(size_t size) { EnsureCapacity(size); size_ = size; } // Ensure that the buffer size can be increased to at least capacity without // further reallocation. (Of course, this operation might need to reallocate // the buffer.) void EnsureCapacity(size_t capacity) { assert(IsConsistent()); if (capacity <= capacity_) return; scoped_ptr new_data(new uint8_t[capacity]); std::memcpy(new_data.get(), data_.get(), size_); data_ = new_data.Pass(); capacity_ = capacity; assert(IsConsistent()); } // We can't call std::move(b), so call b.Pass() instead to do the same job. Buffer&& Pass() { assert(IsConsistent()); return static_cast(*this); } // Resets the buffer to zero size and capacity. Works even if the buffer has // been moved from. void Clear() { data_.reset(); size_ = 0; capacity_ = 0; assert(IsConsistent()); } // Swaps two buffers. Also works for buffers that have been moved from. friend void swap(Buffer& a, Buffer& b) { using std::swap; swap(a.size_, b.size_); swap(a.capacity_, b.capacity_); swap(a.data_, b.data_); } private: // Precondition for all methods except Clear and the destructor. // Postcondition for all methods except move construction and move // assignment, which leave the moved-from object in a possibly inconsistent // state. bool IsConsistent() const { return (data_ || capacity_ == 0) && capacity_ >= size_; } // Called when *this has been moved from. Conceptually it's a no-op, but we // can mutate the state slightly to help subsequent sanity checks catch bugs. void OnMovedFrom() { #ifdef NDEBUG // Make *this consistent and empty. Shouldn't be necessary, but better safe // than sorry. size_ = 0; capacity_ = 0; #else // Ensure that *this is always inconsistent, to provoke bugs. size_ = 1; capacity_ = 0; #endif } size_t size_; size_t capacity_; scoped_ptr data_; }; } // namespace rtc #endif // WEBRTC_BASE_BUFFER_H_