blob: 8071701d5a148b0262f192d9fb015d494acb1356 [file] [log] [blame]
/*
* 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_