| /* |
| * 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 <algorithm> // std::swap (pre-C++11) |
| #include <cassert> |
| #include <cstring> |
| #include <utility> // 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>::t is int if T |
| // is uint8_t, int8_t, or char; otherwise, it's a compilation error. Use like |
| // this: |
| // |
| // template <typename T, typename ByteType<T>::t = 0> |
| // void foo(T* x); |
| // |
| // to let foo<T> be defined only for byte-sized integers. |
| template <typename T> |
| struct ByteType { |
| private: |
| static int F(uint8_t*); |
| static int F(int8_t*); |
| static int F(char*); |
| |
| public: |
| using t = decltype(F(static_cast<T*>(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 <typename T, typename internal::ByteType<T>::t = 0> |
| Buffer(const T* data, size_t size) |
| : Buffer(data, size, size) {} |
| template <typename T, typename internal::ByteType<T>::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 <typename T, size_t N, typename internal::ByteType<T>::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<int8_t>() and .data<char>(). |
| template <typename T = uint8_t, typename internal::ByteType<T>::t = 0> |
| const T* data() const { |
| assert(IsConsistent()); |
| return reinterpret_cast<T*>(data_.get()); |
| } |
| template <typename T = uint8_t, typename internal::ByteType<T>::t = 0> |
| T* data() { |
| assert(IsConsistent()); |
| return reinterpret_cast<T*>(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 <typename T, typename internal::ByteType<T>::t = 0> |
| void SetData(const T* data, size_t size) { |
| assert(IsConsistent()); |
| size_ = 0; |
| AppendData(data, size); |
| } |
| template <typename T, size_t N, typename internal::ByteType<T>::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 <typename T, typename internal::ByteType<T>::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 <typename T, size_t N, typename internal::ByteType<T>::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<uint8_t[]> 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<Buffer&&>(*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<uint8_t[]> data_; |
| }; |
| |
| } // namespace rtc |
| |
| #endif // WEBRTC_BASE_BUFFER_H_ |