blob: 8959b40d8822bcc815ba7cccabb06242bda4f91c [file] [log] [blame]
henrike@webrtc.orgf0488722014-05-13 18:00:26 +00001/*
2 * Copyright 2004 The WebRTC Project Authors. All rights reserved.
3 *
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
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#ifndef WEBRTC_BASE_BUFFER_H_
12#define WEBRTC_BASE_BUFFER_H_
13
Karl Wiberg94784372015-04-20 14:03:07 +020014#include <algorithm> // std::swap (pre-C++11)
15#include <cassert>
16#include <cstring>
17#include <utility> // std::swap (C++11 and later)
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000018#include "webrtc/base/scoped_ptr.h"
19
20namespace rtc {
21
Karl Wiberg94784372015-04-20 14:03:07 +020022namespace internal {
23
24// (Internal; please don't use outside this file.) ByteType<T>::t is int if T
25// is uint8_t, int8_t, or char; otherwise, it's a compilation error. Use like
26// this:
27//
28// template <typename T, typename ByteType<T>::t = 0>
29// void foo(T* x);
30//
31// to let foo<T> be defined only for byte-sized integers.
32template <typename T>
33struct ByteType {
34 private:
35 static int F(uint8_t*);
36 static int F(int8_t*);
37 static int F(char*);
38
39 public:
40 using t = decltype(F(static_cast<T*>(nullptr)));
41};
42
Karl Wibergcbf09272015-04-30 16:00:56 +020043// Deprecated: Accept void* in addition to the byte-sized types.
44// TODO(kwiberg): Remove once Chromium doesn't need this anymore.
45template <typename T>
46struct ByteTypeOrVoid {
47 private:
48 static int F(uint8_t*);
49 static int F(int8_t*);
50 static int F(char*);
51 static int F(void*);
52
53 public:
54 using t = decltype(F(static_cast<T*>(nullptr)));
55};
56
Karl Wiberg94784372015-04-20 14:03:07 +020057} // namespace internal
58
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000059// Basic buffer class, can be grown and shrunk dynamically.
60// Unlike std::string/vector, does not initialize data when expanding capacity.
Karl Wiberg94784372015-04-20 14:03:07 +020061class Buffer final {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000062 public:
Karl Wiberg94784372015-04-20 14:03:07 +020063 Buffer(); // An empty buffer.
64 Buffer(const Buffer& buf); // Copy size and contents of an existing buffer.
65 Buffer(Buffer&& buf); // Move contents from an existing buffer.
66
67 // Construct a buffer with the specified number of uninitialized bytes.
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +000068 explicit Buffer(size_t size);
Karl Wiberg94784372015-04-20 14:03:07 +020069 Buffer(size_t size, size_t capacity);
70
71 // Construct a buffer and copy the specified number of bytes into it. The
72 // source array may be (const) uint8_t*, int8_t*, or char*.
Karl Wibergcbf09272015-04-30 16:00:56 +020073 template <typename T, typename internal::ByteTypeOrVoid<T>::t = 0>
Karl Wiberg94784372015-04-20 14:03:07 +020074 Buffer(const T* data, size_t size)
75 : Buffer(data, size, size) {}
Karl Wibergcbf09272015-04-30 16:00:56 +020076 template <typename T, typename internal::ByteTypeOrVoid<T>::t = 0>
Karl Wiberg94784372015-04-20 14:03:07 +020077 Buffer(const T* data, size_t size, size_t capacity)
78 : Buffer(size, capacity) {
79 std::memcpy(data_.get(), data, size);
80 }
81
82 // Construct a buffer from the contents of an array.
83 template <typename T, size_t N, typename internal::ByteType<T>::t = 0>
84 Buffer(const T(&array)[N])
85 : Buffer(array, N) {}
86
kwiberg@webrtc.org67186fe2015-03-09 22:21:53 +000087 ~Buffer();
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000088
Karl Wibergcbf09272015-04-30 16:00:56 +020089 // Get a pointer to the data. Just .data() will give you a (const) char*,
90 // but you may also use .data<int8_t>() and .data<uint8_t>().
91 // TODO(kwiberg): Change default to uint8_t
92 template <typename T = char, typename internal::ByteType<T>::t = 0>
Karl Wiberg94784372015-04-20 14:03:07 +020093 const T* data() const {
94 assert(IsConsistent());
95 return reinterpret_cast<T*>(data_.get());
96 }
Karl Wibergcbf09272015-04-30 16:00:56 +020097 template <typename T = char, typename internal::ByteType<T>::t = 0>
Karl Wiberg94784372015-04-20 14:03:07 +020098 T* data() {
99 assert(IsConsistent());
100 return reinterpret_cast<T*>(data_.get());
101 }
102
103 size_t size() const {
104 assert(IsConsistent());
105 return size_;
106 }
107 size_t capacity() const {
108 assert(IsConsistent());
109 return capacity_;
110 }
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000111
112 Buffer& operator=(const Buffer& buf) {
Karl Wiberg94784372015-04-20 14:03:07 +0200113 if (&buf != this)
114 SetData(buf.data(), buf.size());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000115 return *this;
116 }
Karl Wiberg94784372015-04-20 14:03:07 +0200117 Buffer& operator=(Buffer&& buf) {
118 assert(IsConsistent());
119 assert(buf.IsConsistent());
120 size_ = buf.size_;
121 capacity_ = buf.capacity_;
122 data_ = buf.data_.Pass();
123 buf.OnMovedFrom();
124 return *this;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000125 }
126
Karl Wiberg94784372015-04-20 14:03:07 +0200127 bool operator==(const Buffer& buf) const {
128 assert(IsConsistent());
129 return size_ == buf.size() && memcmp(data_.get(), buf.data(), size_) == 0;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000130 }
Karl Wiberg94784372015-04-20 14:03:07 +0200131
132 bool operator!=(const Buffer& buf) const { return !(*this == buf); }
133
134 // Replace the contents of the buffer. Accepts the same types as the
135 // constructors.
Karl Wibergcbf09272015-04-30 16:00:56 +0200136 template <typename T, typename internal::ByteTypeOrVoid<T>::t = 0>
Karl Wiberg94784372015-04-20 14:03:07 +0200137 void SetData(const T* data, size_t size) {
138 assert(IsConsistent());
139 size_ = 0;
140 AppendData(data, size);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000141 }
Karl Wiberg94784372015-04-20 14:03:07 +0200142 template <typename T, size_t N, typename internal::ByteType<T>::t = 0>
143 void SetData(const T(&array)[N]) {
144 SetData(array, N);
145 }
146 void SetData(const Buffer& buf) { SetData(buf.data(), buf.size()); }
147
148 // Append data to the buffer. Accepts the same types as the constructors.
Karl Wibergcbf09272015-04-30 16:00:56 +0200149 template <typename T, typename internal::ByteTypeOrVoid<T>::t = 0>
Karl Wiberg94784372015-04-20 14:03:07 +0200150 void AppendData(const T* data, size_t size) {
151 assert(IsConsistent());
152 const size_t new_size = size_ + size;
153 EnsureCapacity(new_size);
154 std::memcpy(data_.get() + size_, data, size);
155 size_ = new_size;
156 assert(IsConsistent());
157 }
158 template <typename T, size_t N, typename internal::ByteType<T>::t = 0>
159 void AppendData(const T(&array)[N]) {
160 AppendData(array, N);
161 }
162 void AppendData(const Buffer& buf) { AppendData(buf.data(), buf.size()); }
163
164 // Sets the size of the buffer. If the new size is smaller than the old, the
165 // buffer contents will be kept but truncated; if the new size is greater,
166 // the existing contents will be kept and the new space will be
167 // uninitialized.
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000168 void SetSize(size_t size) {
Karl Wiberg94784372015-04-20 14:03:07 +0200169 EnsureCapacity(size);
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000170 size_ = size;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000171 }
Karl Wiberg94784372015-04-20 14:03:07 +0200172
173 // Ensure that the buffer size can be increased to at least capacity without
174 // further reallocation. (Of course, this operation might need to reallocate
175 // the buffer.)
176 void EnsureCapacity(size_t capacity) {
177 assert(IsConsistent());
178 if (capacity <= capacity_)
179 return;
180 scoped_ptr<uint8_t[]> new_data(new uint8_t[capacity]);
181 std::memcpy(new_data.get(), data_.get(), size_);
182 data_ = new_data.Pass();
183 capacity_ = capacity;
184 assert(IsConsistent());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000185 }
186
Karl Wiberg94784372015-04-20 14:03:07 +0200187 // We can't call std::move(b), so call b.Pass() instead to do the same job.
188 Buffer&& Pass() {
189 assert(IsConsistent());
190 return static_cast<Buffer&&>(*this);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000191 }
192
Karl Wiberg94784372015-04-20 14:03:07 +0200193 // Resets the buffer to zero size and capacity. Works even if the buffer has
194 // been moved from.
195 void Clear() {
196 data_.reset();
197 size_ = 0;
198 capacity_ = 0;
199 assert(IsConsistent());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000200 }
201
Karl Wiberg94784372015-04-20 14:03:07 +0200202 // Swaps two buffers. Also works for buffers that have been moved from.
203 friend void swap(Buffer& a, Buffer& b) {
204 using std::swap;
205 swap(a.size_, b.size_);
206 swap(a.capacity_, b.capacity_);
207 swap(a.data_, b.data_);
208 }
209
210 private:
211 // Precondition for all methods except Clear and the destructor.
212 // Postcondition for all methods except move construction and move
213 // assignment, which leave the moved-from object in a possibly inconsistent
214 // state.
215 bool IsConsistent() const {
216 return (data_ || capacity_ == 0) && capacity_ >= size_;
217 }
218
219 // Called when *this has been moved from. Conceptually it's a no-op, but we
220 // can mutate the state slightly to help subsequent sanity checks catch bugs.
221 void OnMovedFrom() {
222#ifdef NDEBUG
223 // Make *this consistent and empty. Shouldn't be necessary, but better safe
224 // than sorry.
225 size_ = 0;
226 capacity_ = 0;
227#else
228 // Ensure that *this is always inconsistent, to provoke bugs.
229 size_ = 1;
230 capacity_ = 0;
231#endif
232 }
233
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000234 size_t size_;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000235 size_t capacity_;
Karl Wiberg94784372015-04-20 14:03:07 +0200236 scoped_ptr<uint8_t[]> data_;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000237};
238
239} // namespace rtc
240
241#endif // WEBRTC_BASE_BUFFER_H_