blob: f007929a2943f053ffe4bc9a3349c5f708b71c69 [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.
Karl Wiberg94784372015-04-20 14:03:07 +020052 Buffer(Buffer&& buf); // Move contents from an existing buffer.
53
54 // Construct a buffer with the specified number of uninitialized bytes.
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +000055 explicit Buffer(size_t size);
Karl Wiberg94784372015-04-20 14:03:07 +020056 Buffer(size_t size, size_t capacity);
57
58 // Construct a buffer and copy the specified number of bytes into it. The
59 // source array may be (const) uint8_t*, int8_t*, or char*.
Karl Wibergc56ac1e2015-05-04 14:54:55 +020060 template <typename T, typename internal::ByteType<T>::t = 0>
Karl Wiberg94784372015-04-20 14:03:07 +020061 Buffer(const T* data, size_t size)
62 : Buffer(data, size, size) {}
ossub01c7812016-02-24 01:05:56 -080063
Karl Wibergc56ac1e2015-05-04 14:54:55 +020064 template <typename T, typename internal::ByteType<T>::t = 0>
Karl Wiberg94784372015-04-20 14:03:07 +020065 Buffer(const T* data, size_t size, size_t capacity)
66 : Buffer(size, capacity) {
67 std::memcpy(data_.get(), data, size);
68 }
69
70 // Construct a buffer from the contents of an array.
71 template <typename T, size_t N, typename internal::ByteType<T>::t = 0>
72 Buffer(const T(&array)[N])
73 : Buffer(array, N) {}
74
kwiberg@webrtc.org67186fe2015-03-09 22:21:53 +000075 ~Buffer();
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000076
Karl Wibergc56ac1e2015-05-04 14:54:55 +020077 // Get a pointer to the data. Just .data() will give you a (const) uint8_t*,
78 // but you may also use .data<int8_t>() and .data<char>().
79 template <typename T = uint8_t, typename internal::ByteType<T>::t = 0>
Karl Wiberg94784372015-04-20 14:03:07 +020080 const T* data() const {
kwiberge3d99222016-03-01 01:57:38 -080081 RTC_DCHECK(IsConsistent());
Karl Wiberg94784372015-04-20 14:03:07 +020082 return reinterpret_cast<T*>(data_.get());
83 }
ossub01c7812016-02-24 01:05:56 -080084
Karl Wibergc56ac1e2015-05-04 14:54:55 +020085 template <typename T = uint8_t, typename internal::ByteType<T>::t = 0>
Karl Wiberg94784372015-04-20 14:03:07 +020086 T* data() {
kwiberge3d99222016-03-01 01:57:38 -080087 RTC_DCHECK(IsConsistent());
Karl Wiberg94784372015-04-20 14:03:07 +020088 return reinterpret_cast<T*>(data_.get());
89 }
90
91 size_t size() const {
kwiberge3d99222016-03-01 01:57:38 -080092 RTC_DCHECK(IsConsistent());
Karl Wiberg94784372015-04-20 14:03:07 +020093 return size_;
94 }
ossub01c7812016-02-24 01:05:56 -080095
Karl Wiberg94784372015-04-20 14:03:07 +020096 size_t capacity() const {
kwiberge3d99222016-03-01 01:57:38 -080097 RTC_DCHECK(IsConsistent());
Karl Wiberg94784372015-04-20 14:03:07 +020098 return capacity_;
99 }
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000100
Karl Wiberg94784372015-04-20 14:03:07 +0200101 Buffer& operator=(Buffer&& buf) {
kwiberge3d99222016-03-01 01:57:38 -0800102 RTC_DCHECK(IsConsistent());
103 RTC_DCHECK(buf.IsConsistent());
Karl Wiberg94784372015-04-20 14:03:07 +0200104 size_ = buf.size_;
105 capacity_ = buf.capacity_;
kwiberg0eb15ed2015-12-17 03:04:15 -0800106 data_ = std::move(buf.data_);
Karl Wiberg94784372015-04-20 14:03:07 +0200107 buf.OnMovedFrom();
108 return *this;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000109 }
110
Karl Wiberg94784372015-04-20 14:03:07 +0200111 bool operator==(const Buffer& buf) const {
kwiberge3d99222016-03-01 01:57:38 -0800112 RTC_DCHECK(IsConsistent());
Karl Wiberg94784372015-04-20 14:03:07 +0200113 return size_ == buf.size() && memcmp(data_.get(), buf.data(), size_) == 0;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000114 }
Karl Wiberg94784372015-04-20 14:03:07 +0200115
116 bool operator!=(const Buffer& buf) const { return !(*this == buf); }
117
ossub9338ac2016-02-29 09:36:37 -0800118 uint8_t& operator[](size_t index) {
119 RTC_DCHECK_LT(index, size_);
120 return data()[index];
121 }
122
123 uint8_t operator[](size_t index) const {
124 RTC_DCHECK_LT(index, size_);
125 return data()[index];
126 }
127
ossub01c7812016-02-24 01:05:56 -0800128 // The SetData functions replace the contents of the buffer. They accept the
129 // same input types as the constructors.
Karl Wibergc56ac1e2015-05-04 14:54:55 +0200130 template <typename T, typename internal::ByteType<T>::t = 0>
Karl Wiberg94784372015-04-20 14:03:07 +0200131 void SetData(const T* data, size_t size) {
kwiberge3d99222016-03-01 01:57:38 -0800132 RTC_DCHECK(IsConsistent());
Karl Wiberg94784372015-04-20 14:03:07 +0200133 size_ = 0;
134 AppendData(data, size);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000135 }
ossub01c7812016-02-24 01:05:56 -0800136
Karl Wiberg94784372015-04-20 14:03:07 +0200137 template <typename T, size_t N, typename internal::ByteType<T>::t = 0>
138 void SetData(const T(&array)[N]) {
139 SetData(array, N);
140 }
ossub01c7812016-02-24 01:05:56 -0800141
Karl Wiberg94784372015-04-20 14:03:07 +0200142 void SetData(const Buffer& buf) { SetData(buf.data(), buf.size()); }
143
ossub01c7812016-02-24 01:05:56 -0800144 // Replace the data in the buffer with at most |max_bytes| of data, using the
145 // function |setter|, which should have the following signature:
146 // size_t setter(ArrayView<T> view)
147 // |setter| is given an appropriately typed ArrayView of the area in which to
148 // write the data (i.e. starting at the beginning of the buffer) and should
149 // return the number of bytes actually written. This number must be <=
150 // |max_bytes|.
151 template <typename T = uint8_t, typename F,
152 typename internal::ByteType<T>::t = 0>
153 size_t SetData(size_t max_bytes, F&& setter) {
154 RTC_DCHECK(IsConsistent());
155 size_ = 0;
156 return AppendData<T>(max_bytes, std::forward<F>(setter));
157 }
158
159 // The AppendData functions adds data to the end of the buffer. They accept
160 // the same input types as the constructors.
Karl Wibergc56ac1e2015-05-04 14:54:55 +0200161 template <typename T, typename internal::ByteType<T>::t = 0>
Karl Wiberg94784372015-04-20 14:03:07 +0200162 void AppendData(const T* data, size_t size) {
kwiberge3d99222016-03-01 01:57:38 -0800163 RTC_DCHECK(IsConsistent());
Karl Wiberg94784372015-04-20 14:03:07 +0200164 const size_t new_size = size_ + size;
165 EnsureCapacity(new_size);
166 std::memcpy(data_.get() + size_, data, size);
167 size_ = new_size;
kwiberge3d99222016-03-01 01:57:38 -0800168 RTC_DCHECK(IsConsistent());
Karl Wiberg94784372015-04-20 14:03:07 +0200169 }
ossub01c7812016-02-24 01:05:56 -0800170
Karl Wiberg94784372015-04-20 14:03:07 +0200171 template <typename T, size_t N, typename internal::ByteType<T>::t = 0>
172 void AppendData(const T(&array)[N]) {
173 AppendData(array, N);
174 }
ossub01c7812016-02-24 01:05:56 -0800175
Karl Wiberg94784372015-04-20 14:03:07 +0200176 void AppendData(const Buffer& buf) { AppendData(buf.data(), buf.size()); }
177
ossub01c7812016-02-24 01:05:56 -0800178 // Append at most |max_bytes| of data to the end of the buffer, using the
179 // function |setter|, which should have the following signature:
180 // size_t setter(ArrayView<T> view)
181 // |setter| is given an appropriately typed ArrayView of the area in which to
182 // write the data (i.e. starting at the former end of the buffer) and should
183 // return the number of bytes actually written. This number must be <=
184 // |max_bytes|.
185 template <typename T = uint8_t, typename F,
186 typename internal::ByteType<T>::t = 0>
187 size_t AppendData(size_t max_bytes, F&& setter) {
188 RTC_DCHECK(IsConsistent());
189 const size_t old_size = size_;
190 SetSize(old_size + max_bytes);
191 T *base_ptr = data<T>() + old_size;
192 size_t written_bytes =
193 setter(rtc::ArrayView<T>(base_ptr, max_bytes));
194
195 RTC_CHECK_LE(written_bytes, max_bytes);
196 size_ = old_size + written_bytes;
197 RTC_DCHECK(IsConsistent());
198 return written_bytes;
199 }
200
Karl Wiberg94784372015-04-20 14:03:07 +0200201 // Sets the size of the buffer. If the new size is smaller than the old, the
202 // buffer contents will be kept but truncated; if the new size is greater,
203 // the existing contents will be kept and the new space will be
204 // uninitialized.
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000205 void SetSize(size_t size) {
Karl Wiberg94784372015-04-20 14:03:07 +0200206 EnsureCapacity(size);
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000207 size_ = size;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000208 }
Karl Wiberg94784372015-04-20 14:03:07 +0200209
210 // Ensure that the buffer size can be increased to at least capacity without
211 // further reallocation. (Of course, this operation might need to reallocate
212 // the buffer.)
213 void EnsureCapacity(size_t capacity) {
kwiberge3d99222016-03-01 01:57:38 -0800214 RTC_DCHECK(IsConsistent());
Karl Wiberg94784372015-04-20 14:03:07 +0200215 if (capacity <= capacity_)
216 return;
kwiberg8fb35572016-02-11 13:36:43 -0800217 std::unique_ptr<uint8_t[]> new_data(new uint8_t[capacity]);
Karl Wiberg94784372015-04-20 14:03:07 +0200218 std::memcpy(new_data.get(), data_.get(), size_);
kwiberg0eb15ed2015-12-17 03:04:15 -0800219 data_ = std::move(new_data);
Karl Wiberg94784372015-04-20 14:03:07 +0200220 capacity_ = capacity;
kwiberge3d99222016-03-01 01:57:38 -0800221 RTC_DCHECK(IsConsistent());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000222 }
223
ossu728012e2016-02-19 02:38:32 -0800224 // Resets the buffer to zero size without altering capacity. Works even if the
225 // buffer has been moved from.
Karl Wiberg94784372015-04-20 14:03:07 +0200226 void Clear() {
Karl Wiberg94784372015-04-20 14:03:07 +0200227 size_ = 0;
kwiberge3d99222016-03-01 01:57:38 -0800228 RTC_DCHECK(IsConsistent());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000229 }
230
Karl Wiberg94784372015-04-20 14:03:07 +0200231 // Swaps two buffers. Also works for buffers that have been moved from.
232 friend void swap(Buffer& a, Buffer& b) {
233 using std::swap;
234 swap(a.size_, b.size_);
235 swap(a.capacity_, b.capacity_);
236 swap(a.data_, b.data_);
237 }
238
239 private:
240 // Precondition for all methods except Clear and the destructor.
241 // Postcondition for all methods except move construction and move
242 // assignment, which leave the moved-from object in a possibly inconsistent
243 // state.
244 bool IsConsistent() const {
245 return (data_ || capacity_ == 0) && capacity_ >= size_;
246 }
247
248 // Called when *this has been moved from. Conceptually it's a no-op, but we
249 // can mutate the state slightly to help subsequent sanity checks catch bugs.
250 void OnMovedFrom() {
251#ifdef NDEBUG
252 // Make *this consistent and empty. Shouldn't be necessary, but better safe
253 // than sorry.
254 size_ = 0;
255 capacity_ = 0;
256#else
257 // Ensure that *this is always inconsistent, to provoke bugs.
258 size_ = 1;
259 capacity_ = 0;
260#endif
261 }
262
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000263 size_t size_;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000264 size_t capacity_;
kwiberg8fb35572016-02-11 13:36:43 -0800265 std::unique_ptr<uint8_t[]> data_;
jbaucheec21bd2016-03-20 06:15:43 -0700266
267 RTC_DISALLOW_COPY_AND_ASSIGN(Buffer);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000268};
269
270} // namespace rtc
271
272#endif // WEBRTC_BASE_BUFFER_H_