blob: 8071701d5a148b0262f192d9fb015d494acb1356 [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
43} // namespace internal
44
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000045// Basic buffer class, can be grown and shrunk dynamically.
46// Unlike std::string/vector, does not initialize data when expanding capacity.
Karl Wiberg94784372015-04-20 14:03:07 +020047class Buffer final {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000048 public:
Karl Wiberg94784372015-04-20 14:03:07 +020049 Buffer(); // An empty buffer.
50 Buffer(const Buffer& buf); // Copy size and contents of an existing buffer.
51 Buffer(Buffer&& buf); // Move contents from an existing buffer.
52
53 // Construct a buffer with the specified number of uninitialized bytes.
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +000054 explicit Buffer(size_t size);
Karl Wiberg94784372015-04-20 14:03:07 +020055 Buffer(size_t size, size_t capacity);
56
57 // Construct a buffer and copy the specified number of bytes into it. The
58 // source array may be (const) uint8_t*, int8_t*, or char*.
Karl Wibergc56ac1e2015-05-04 14:54:55 +020059 template <typename T, typename internal::ByteType<T>::t = 0>
Karl Wiberg94784372015-04-20 14:03:07 +020060 Buffer(const T* data, size_t size)
61 : Buffer(data, size, size) {}
Karl Wibergc56ac1e2015-05-04 14:54:55 +020062 template <typename T, typename internal::ByteType<T>::t = 0>
Karl Wiberg94784372015-04-20 14:03:07 +020063 Buffer(const T* data, size_t size, size_t capacity)
64 : Buffer(size, capacity) {
65 std::memcpy(data_.get(), data, size);
66 }
67
68 // Construct a buffer from the contents of an array.
69 template <typename T, size_t N, typename internal::ByteType<T>::t = 0>
70 Buffer(const T(&array)[N])
71 : Buffer(array, N) {}
72
kwiberg@webrtc.org67186fe2015-03-09 22:21:53 +000073 ~Buffer();
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000074
Karl Wibergc56ac1e2015-05-04 14:54:55 +020075 // Get a pointer to the data. Just .data() will give you a (const) uint8_t*,
76 // but you may also use .data<int8_t>() and .data<char>().
77 template <typename T = uint8_t, typename internal::ByteType<T>::t = 0>
Karl Wiberg94784372015-04-20 14:03:07 +020078 const T* data() const {
79 assert(IsConsistent());
80 return reinterpret_cast<T*>(data_.get());
81 }
Karl Wibergc56ac1e2015-05-04 14:54:55 +020082 template <typename T = uint8_t, typename internal::ByteType<T>::t = 0>
Karl Wiberg94784372015-04-20 14:03:07 +020083 T* data() {
84 assert(IsConsistent());
85 return reinterpret_cast<T*>(data_.get());
86 }
87
88 size_t size() const {
89 assert(IsConsistent());
90 return size_;
91 }
92 size_t capacity() const {
93 assert(IsConsistent());
94 return capacity_;
95 }
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000096
97 Buffer& operator=(const Buffer& buf) {
Karl Wiberg94784372015-04-20 14:03:07 +020098 if (&buf != this)
99 SetData(buf.data(), buf.size());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000100 return *this;
101 }
Karl Wiberg94784372015-04-20 14:03:07 +0200102 Buffer& operator=(Buffer&& buf) {
103 assert(IsConsistent());
104 assert(buf.IsConsistent());
105 size_ = buf.size_;
106 capacity_ = buf.capacity_;
107 data_ = buf.data_.Pass();
108 buf.OnMovedFrom();
109 return *this;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000110 }
111
Karl Wiberg94784372015-04-20 14:03:07 +0200112 bool operator==(const Buffer& buf) const {
113 assert(IsConsistent());
114 return size_ == buf.size() && memcmp(data_.get(), buf.data(), size_) == 0;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000115 }
Karl Wiberg94784372015-04-20 14:03:07 +0200116
117 bool operator!=(const Buffer& buf) const { return !(*this == buf); }
118
119 // Replace the contents of the buffer. Accepts the same types as the
120 // constructors.
Karl Wibergc56ac1e2015-05-04 14:54:55 +0200121 template <typename T, typename internal::ByteType<T>::t = 0>
Karl Wiberg94784372015-04-20 14:03:07 +0200122 void SetData(const T* data, size_t size) {
123 assert(IsConsistent());
124 size_ = 0;
125 AppendData(data, size);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000126 }
Karl Wiberg94784372015-04-20 14:03:07 +0200127 template <typename T, size_t N, typename internal::ByteType<T>::t = 0>
128 void SetData(const T(&array)[N]) {
129 SetData(array, N);
130 }
131 void SetData(const Buffer& buf) { SetData(buf.data(), buf.size()); }
132
133 // Append data to the buffer. Accepts the same types as the constructors.
Karl Wibergc56ac1e2015-05-04 14:54:55 +0200134 template <typename T, typename internal::ByteType<T>::t = 0>
Karl Wiberg94784372015-04-20 14:03:07 +0200135 void AppendData(const T* data, size_t size) {
136 assert(IsConsistent());
137 const size_t new_size = size_ + size;
138 EnsureCapacity(new_size);
139 std::memcpy(data_.get() + size_, data, size);
140 size_ = new_size;
141 assert(IsConsistent());
142 }
143 template <typename T, size_t N, typename internal::ByteType<T>::t = 0>
144 void AppendData(const T(&array)[N]) {
145 AppendData(array, N);
146 }
147 void AppendData(const Buffer& buf) { AppendData(buf.data(), buf.size()); }
148
149 // Sets the size of the buffer. If the new size is smaller than the old, the
150 // buffer contents will be kept but truncated; if the new size is greater,
151 // the existing contents will be kept and the new space will be
152 // uninitialized.
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000153 void SetSize(size_t size) {
Karl Wiberg94784372015-04-20 14:03:07 +0200154 EnsureCapacity(size);
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000155 size_ = size;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000156 }
Karl Wiberg94784372015-04-20 14:03:07 +0200157
158 // Ensure that the buffer size can be increased to at least capacity without
159 // further reallocation. (Of course, this operation might need to reallocate
160 // the buffer.)
161 void EnsureCapacity(size_t capacity) {
162 assert(IsConsistent());
163 if (capacity <= capacity_)
164 return;
165 scoped_ptr<uint8_t[]> new_data(new uint8_t[capacity]);
166 std::memcpy(new_data.get(), data_.get(), size_);
167 data_ = new_data.Pass();
168 capacity_ = capacity;
169 assert(IsConsistent());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000170 }
171
Karl Wiberg94784372015-04-20 14:03:07 +0200172 // We can't call std::move(b), so call b.Pass() instead to do the same job.
173 Buffer&& Pass() {
174 assert(IsConsistent());
175 return static_cast<Buffer&&>(*this);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000176 }
177
Karl Wiberg94784372015-04-20 14:03:07 +0200178 // Resets the buffer to zero size and capacity. Works even if the buffer has
179 // been moved from.
180 void Clear() {
181 data_.reset();
182 size_ = 0;
183 capacity_ = 0;
184 assert(IsConsistent());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000185 }
186
Karl Wiberg94784372015-04-20 14:03:07 +0200187 // Swaps two buffers. Also works for buffers that have been moved from.
188 friend void swap(Buffer& a, Buffer& b) {
189 using std::swap;
190 swap(a.size_, b.size_);
191 swap(a.capacity_, b.capacity_);
192 swap(a.data_, b.data_);
193 }
194
195 private:
196 // Precondition for all methods except Clear and the destructor.
197 // Postcondition for all methods except move construction and move
198 // assignment, which leave the moved-from object in a possibly inconsistent
199 // state.
200 bool IsConsistent() const {
201 return (data_ || capacity_ == 0) && capacity_ >= size_;
202 }
203
204 // Called when *this has been moved from. Conceptually it's a no-op, but we
205 // can mutate the state slightly to help subsequent sanity checks catch bugs.
206 void OnMovedFrom() {
207#ifdef NDEBUG
208 // Make *this consistent and empty. Shouldn't be necessary, but better safe
209 // than sorry.
210 size_ = 0;
211 capacity_ = 0;
212#else
213 // Ensure that *this is always inconsistent, to provoke bugs.
214 size_ = 1;
215 capacity_ = 0;
216#endif
217 }
218
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000219 size_t size_;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000220 size_t capacity_;
Karl Wiberg94784372015-04-20 14:03:07 +0200221 scoped_ptr<uint8_t[]> data_;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000222};
223
224} // namespace rtc
225
226#endif // WEBRTC_BASE_BUFFER_H_