blob: 681348d91048a16c8122339c41a1315e18528a0a [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 <cstring>
kwiberg8fb35572016-02-11 13:36:43 -080015#include <memory>
ossub01c7812016-02-24 01:05:56 -080016#include <utility>
kwiberg36220ae2016-01-12 07:24:19 -080017
ossub01c7812016-02-24 01:05:56 -080018#include "webrtc/base/array_view.h"
19#include "webrtc/base/checks.h"
kwiberg8fb35572016-02-11 13:36:43 -080020#include "webrtc/base/constructormagic.h"
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000021
22namespace rtc {
23
Karl Wiberg94784372015-04-20 14:03:07 +020024namespace internal {
25
26// (Internal; please don't use outside this file.) ByteType<T>::t is int if T
27// is uint8_t, int8_t, or char; otherwise, it's a compilation error. Use like
28// this:
29//
30// template <typename T, typename ByteType<T>::t = 0>
31// void foo(T* x);
32//
33// to let foo<T> be defined only for byte-sized integers.
34template <typename T>
35struct ByteType {
36 private:
37 static int F(uint8_t*);
38 static int F(int8_t*);
39 static int F(char*);
40
41 public:
42 using t = decltype(F(static_cast<T*>(nullptr)));
43};
44
45} // namespace internal
46
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000047// Basic buffer class, can be grown and shrunk dynamically.
48// Unlike std::string/vector, does not initialize data when expanding capacity.
noahric27dfe202015-10-23 06:01:10 -070049class Buffer {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000050 public:
Karl Wiberg94784372015-04-20 14:03:07 +020051 Buffer(); // An empty buffer.
kjellander194e3bc2016-03-19 12:12:52 -070052 Buffer(const Buffer& buf); // Copy size and contents of an existing buffer.
Karl Wiberg94784372015-04-20 14:03:07 +020053 Buffer(Buffer&& buf); // Move contents from an existing buffer.
54
55 // Construct a buffer with the specified number of uninitialized bytes.
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +000056 explicit Buffer(size_t size);
Karl Wiberg94784372015-04-20 14:03:07 +020057 Buffer(size_t size, size_t capacity);
58
59 // Construct a buffer and copy the specified number of bytes into it. The
60 // source array may be (const) uint8_t*, int8_t*, or char*.
Karl Wibergc56ac1e2015-05-04 14:54:55 +020061 template <typename T, typename internal::ByteType<T>::t = 0>
Karl Wiberg94784372015-04-20 14:03:07 +020062 Buffer(const T* data, size_t size)
63 : Buffer(data, size, size) {}
ossub01c7812016-02-24 01:05:56 -080064
Karl Wibergc56ac1e2015-05-04 14:54:55 +020065 template <typename T, typename internal::ByteType<T>::t = 0>
Karl Wiberg94784372015-04-20 14:03:07 +020066 Buffer(const T* data, size_t size, size_t capacity)
67 : Buffer(size, capacity) {
68 std::memcpy(data_.get(), data, size);
69 }
70
71 // Construct a buffer from the contents of an array.
72 template <typename T, size_t N, typename internal::ByteType<T>::t = 0>
73 Buffer(const T(&array)[N])
74 : Buffer(array, N) {}
75
kwiberg@webrtc.org67186fe2015-03-09 22:21:53 +000076 ~Buffer();
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000077
Karl Wibergc56ac1e2015-05-04 14:54:55 +020078 // 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>().
80 template <typename T = uint8_t, typename internal::ByteType<T>::t = 0>
Karl Wiberg94784372015-04-20 14:03:07 +020081 const T* data() const {
kwiberge3d99222016-03-01 01:57:38 -080082 RTC_DCHECK(IsConsistent());
Karl Wiberg94784372015-04-20 14:03:07 +020083 return reinterpret_cast<T*>(data_.get());
84 }
ossub01c7812016-02-24 01:05:56 -080085
Karl Wibergc56ac1e2015-05-04 14:54:55 +020086 template <typename T = uint8_t, typename internal::ByteType<T>::t = 0>
Karl Wiberg94784372015-04-20 14:03:07 +020087 T* data() {
kwiberge3d99222016-03-01 01:57:38 -080088 RTC_DCHECK(IsConsistent());
Karl Wiberg94784372015-04-20 14:03:07 +020089 return reinterpret_cast<T*>(data_.get());
90 }
91
92 size_t size() const {
kwiberge3d99222016-03-01 01:57:38 -080093 RTC_DCHECK(IsConsistent());
Karl Wiberg94784372015-04-20 14:03:07 +020094 return size_;
95 }
ossub01c7812016-02-24 01:05:56 -080096
Karl Wiberg94784372015-04-20 14:03:07 +020097 size_t capacity() const {
kwiberge3d99222016-03-01 01:57:38 -080098 RTC_DCHECK(IsConsistent());
Karl Wiberg94784372015-04-20 14:03:07 +020099 return capacity_;
100 }
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000101
kjellander194e3bc2016-03-19 12:12:52 -0700102 Buffer& operator=(const Buffer& buf) {
103 if (&buf != this)
104 SetData(buf.data(), buf.size());
105 return *this;
106 }
107
Karl Wiberg94784372015-04-20 14:03:07 +0200108 Buffer& operator=(Buffer&& buf) {
kwiberge3d99222016-03-01 01:57:38 -0800109 RTC_DCHECK(IsConsistent());
110 RTC_DCHECK(buf.IsConsistent());
Karl Wiberg94784372015-04-20 14:03:07 +0200111 size_ = buf.size_;
112 capacity_ = buf.capacity_;
kwiberg0eb15ed2015-12-17 03:04:15 -0800113 data_ = std::move(buf.data_);
Karl Wiberg94784372015-04-20 14:03:07 +0200114 buf.OnMovedFrom();
115 return *this;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000116 }
117
Karl Wiberg94784372015-04-20 14:03:07 +0200118 bool operator==(const Buffer& buf) const {
kwiberge3d99222016-03-01 01:57:38 -0800119 RTC_DCHECK(IsConsistent());
Karl Wiberg94784372015-04-20 14:03:07 +0200120 return size_ == buf.size() && memcmp(data_.get(), buf.data(), size_) == 0;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000121 }
Karl Wiberg94784372015-04-20 14:03:07 +0200122
123 bool operator!=(const Buffer& buf) const { return !(*this == buf); }
124
ossub9338ac2016-02-29 09:36:37 -0800125 uint8_t& operator[](size_t index) {
126 RTC_DCHECK_LT(index, size_);
127 return data()[index];
128 }
129
130 uint8_t operator[](size_t index) const {
131 RTC_DCHECK_LT(index, size_);
132 return data()[index];
133 }
134
ossub01c7812016-02-24 01:05:56 -0800135 // The SetData functions replace the contents of the buffer. They accept the
136 // same input types as the constructors.
Karl Wibergc56ac1e2015-05-04 14:54:55 +0200137 template <typename T, typename internal::ByteType<T>::t = 0>
Karl Wiberg94784372015-04-20 14:03:07 +0200138 void SetData(const T* data, size_t size) {
kwiberge3d99222016-03-01 01:57:38 -0800139 RTC_DCHECK(IsConsistent());
Karl Wiberg94784372015-04-20 14:03:07 +0200140 size_ = 0;
141 AppendData(data, size);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000142 }
ossub01c7812016-02-24 01:05:56 -0800143
Karl Wiberg94784372015-04-20 14:03:07 +0200144 template <typename T, size_t N, typename internal::ByteType<T>::t = 0>
145 void SetData(const T(&array)[N]) {
146 SetData(array, N);
147 }
ossub01c7812016-02-24 01:05:56 -0800148
Karl Wiberg94784372015-04-20 14:03:07 +0200149 void SetData(const Buffer& buf) { SetData(buf.data(), buf.size()); }
150
ossub01c7812016-02-24 01:05:56 -0800151 // Replace the data in the buffer with at most |max_bytes| of data, using the
152 // function |setter|, which should have the following signature:
153 // size_t setter(ArrayView<T> view)
154 // |setter| is given an appropriately typed ArrayView of the area in which to
155 // write the data (i.e. starting at the beginning of the buffer) and should
156 // return the number of bytes actually written. This number must be <=
157 // |max_bytes|.
158 template <typename T = uint8_t, typename F,
159 typename internal::ByteType<T>::t = 0>
160 size_t SetData(size_t max_bytes, F&& setter) {
161 RTC_DCHECK(IsConsistent());
162 size_ = 0;
163 return AppendData<T>(max_bytes, std::forward<F>(setter));
164 }
165
166 // The AppendData functions adds data to the end of the buffer. They accept
167 // the same input types as the constructors.
Karl Wibergc56ac1e2015-05-04 14:54:55 +0200168 template <typename T, typename internal::ByteType<T>::t = 0>
Karl Wiberg94784372015-04-20 14:03:07 +0200169 void AppendData(const T* data, size_t size) {
kwiberge3d99222016-03-01 01:57:38 -0800170 RTC_DCHECK(IsConsistent());
Karl Wiberg94784372015-04-20 14:03:07 +0200171 const size_t new_size = size_ + size;
172 EnsureCapacity(new_size);
173 std::memcpy(data_.get() + size_, data, size);
174 size_ = new_size;
kwiberge3d99222016-03-01 01:57:38 -0800175 RTC_DCHECK(IsConsistent());
Karl Wiberg94784372015-04-20 14:03:07 +0200176 }
ossub01c7812016-02-24 01:05:56 -0800177
Karl Wiberg94784372015-04-20 14:03:07 +0200178 template <typename T, size_t N, typename internal::ByteType<T>::t = 0>
179 void AppendData(const T(&array)[N]) {
180 AppendData(array, N);
181 }
ossub01c7812016-02-24 01:05:56 -0800182
Karl Wiberg94784372015-04-20 14:03:07 +0200183 void AppendData(const Buffer& buf) { AppendData(buf.data(), buf.size()); }
184
ossub01c7812016-02-24 01:05:56 -0800185 // Append at most |max_bytes| of data to the end of the buffer, using the
186 // function |setter|, which should have the following signature:
187 // size_t setter(ArrayView<T> view)
188 // |setter| is given an appropriately typed ArrayView of the area in which to
189 // write the data (i.e. starting at the former end of the buffer) and should
190 // return the number of bytes actually written. This number must be <=
191 // |max_bytes|.
192 template <typename T = uint8_t, typename F,
193 typename internal::ByteType<T>::t = 0>
194 size_t AppendData(size_t max_bytes, F&& setter) {
195 RTC_DCHECK(IsConsistent());
196 const size_t old_size = size_;
197 SetSize(old_size + max_bytes);
198 T *base_ptr = data<T>() + old_size;
199 size_t written_bytes =
200 setter(rtc::ArrayView<T>(base_ptr, max_bytes));
201
202 RTC_CHECK_LE(written_bytes, max_bytes);
203 size_ = old_size + written_bytes;
204 RTC_DCHECK(IsConsistent());
205 return written_bytes;
206 }
207
Karl Wiberg94784372015-04-20 14:03:07 +0200208 // Sets the size of the buffer. If the new size is smaller than the old, the
209 // buffer contents will be kept but truncated; if the new size is greater,
210 // the existing contents will be kept and the new space will be
211 // uninitialized.
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000212 void SetSize(size_t size) {
Karl Wiberg94784372015-04-20 14:03:07 +0200213 EnsureCapacity(size);
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000214 size_ = size;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000215 }
Karl Wiberg94784372015-04-20 14:03:07 +0200216
217 // Ensure that the buffer size can be increased to at least capacity without
218 // further reallocation. (Of course, this operation might need to reallocate
219 // the buffer.)
220 void EnsureCapacity(size_t capacity) {
kwiberge3d99222016-03-01 01:57:38 -0800221 RTC_DCHECK(IsConsistent());
Karl Wiberg94784372015-04-20 14:03:07 +0200222 if (capacity <= capacity_)
223 return;
kwiberg8fb35572016-02-11 13:36:43 -0800224 std::unique_ptr<uint8_t[]> new_data(new uint8_t[capacity]);
Karl Wiberg94784372015-04-20 14:03:07 +0200225 std::memcpy(new_data.get(), data_.get(), size_);
kwiberg0eb15ed2015-12-17 03:04:15 -0800226 data_ = std::move(new_data);
Karl Wiberg94784372015-04-20 14:03:07 +0200227 capacity_ = capacity;
kwiberge3d99222016-03-01 01:57:38 -0800228 RTC_DCHECK(IsConsistent());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000229 }
230
ossu728012e2016-02-19 02:38:32 -0800231 // Resets the buffer to zero size without altering capacity. Works even if the
232 // buffer has been moved from.
Karl Wiberg94784372015-04-20 14:03:07 +0200233 void Clear() {
Karl Wiberg94784372015-04-20 14:03:07 +0200234 size_ = 0;
kwiberge3d99222016-03-01 01:57:38 -0800235 RTC_DCHECK(IsConsistent());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000236 }
237
Karl Wiberg94784372015-04-20 14:03:07 +0200238 // Swaps two buffers. Also works for buffers that have been moved from.
239 friend void swap(Buffer& a, Buffer& b) {
240 using std::swap;
241 swap(a.size_, b.size_);
242 swap(a.capacity_, b.capacity_);
243 swap(a.data_, b.data_);
244 }
245
246 private:
247 // Precondition for all methods except Clear and the destructor.
248 // Postcondition for all methods except move construction and move
249 // assignment, which leave the moved-from object in a possibly inconsistent
250 // state.
251 bool IsConsistent() const {
252 return (data_ || capacity_ == 0) && capacity_ >= size_;
253 }
254
255 // Called when *this has been moved from. Conceptually it's a no-op, but we
256 // can mutate the state slightly to help subsequent sanity checks catch bugs.
257 void OnMovedFrom() {
258#ifdef NDEBUG
259 // Make *this consistent and empty. Shouldn't be necessary, but better safe
260 // than sorry.
261 size_ = 0;
262 capacity_ = 0;
263#else
264 // Ensure that *this is always inconsistent, to provoke bugs.
265 size_ = 1;
266 capacity_ = 0;
267#endif
268 }
269
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000270 size_t size_;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000271 size_t capacity_;
kwiberg8fb35572016-02-11 13:36:43 -0800272 std::unique_ptr<uint8_t[]> data_;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000273};
274
275} // namespace rtc
276
277#endif // WEBRTC_BASE_BUFFER_H_