blob: 5880f50c9444cc845c72d65a712423ff88bbd728 [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*.
59 template <typename T, typename internal::ByteType<T>::t = 0>
60 Buffer(const T* data, size_t size)
61 : Buffer(data, size, size) {}
62 template <typename T, typename internal::ByteType<T>::t = 0>
63 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 Wiberg94784372015-04-20 14:03:07 +020075 // Get a pointer to the data. Just .data() will give you a (const) char*,
76 // but you may also use .data<int8_t>() and .data<uint8_t>().
77 // TODO(kwiberg): Change default to uint8_t
78 template <typename T = char, typename internal::ByteType<T>::t = 0>
79 const T* data() const {
80 assert(IsConsistent());
81 return reinterpret_cast<T*>(data_.get());
82 }
83 template <typename T = char, typename internal::ByteType<T>::t = 0>
84 T* data() {
85 assert(IsConsistent());
86 return reinterpret_cast<T*>(data_.get());
87 }
88
89 size_t size() const {
90 assert(IsConsistent());
91 return size_;
92 }
93 size_t capacity() const {
94 assert(IsConsistent());
95 return capacity_;
96 }
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000097
98 Buffer& operator=(const Buffer& buf) {
Karl Wiberg94784372015-04-20 14:03:07 +020099 if (&buf != this)
100 SetData(buf.data(), buf.size());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000101 return *this;
102 }
Karl Wiberg94784372015-04-20 14:03:07 +0200103 Buffer& operator=(Buffer&& buf) {
104 assert(IsConsistent());
105 assert(buf.IsConsistent());
106 size_ = buf.size_;
107 capacity_ = buf.capacity_;
108 data_ = buf.data_.Pass();
109 buf.OnMovedFrom();
110 return *this;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000111 }
112
Karl Wiberg94784372015-04-20 14:03:07 +0200113 bool operator==(const Buffer& buf) const {
114 assert(IsConsistent());
115 return size_ == buf.size() && memcmp(data_.get(), buf.data(), size_) == 0;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000116 }
Karl Wiberg94784372015-04-20 14:03:07 +0200117
118 bool operator!=(const Buffer& buf) const { return !(*this == buf); }
119
120 // Replace the contents of the buffer. Accepts the same types as the
121 // constructors.
122 template <typename T, typename internal::ByteType<T>::t = 0>
123 void SetData(const T* data, size_t size) {
124 assert(IsConsistent());
125 size_ = 0;
126 AppendData(data, size);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000127 }
Karl Wiberg94784372015-04-20 14:03:07 +0200128 template <typename T, size_t N, typename internal::ByteType<T>::t = 0>
129 void SetData(const T(&array)[N]) {
130 SetData(array, N);
131 }
132 void SetData(const Buffer& buf) { SetData(buf.data(), buf.size()); }
133
134 // Append data to the buffer. Accepts the same types as the constructors.
135 template <typename T, typename internal::ByteType<T>::t = 0>
136 void AppendData(const T* data, size_t size) {
137 assert(IsConsistent());
138 const size_t new_size = size_ + size;
139 EnsureCapacity(new_size);
140 std::memcpy(data_.get() + size_, data, size);
141 size_ = new_size;
142 assert(IsConsistent());
143 }
144 template <typename T, size_t N, typename internal::ByteType<T>::t = 0>
145 void AppendData(const T(&array)[N]) {
146 AppendData(array, N);
147 }
148 void AppendData(const Buffer& buf) { AppendData(buf.data(), buf.size()); }
149
150 // Sets the size of the buffer. If the new size is smaller than the old, the
151 // buffer contents will be kept but truncated; if the new size is greater,
152 // the existing contents will be kept and the new space will be
153 // uninitialized.
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000154 void SetSize(size_t size) {
Karl Wiberg94784372015-04-20 14:03:07 +0200155 EnsureCapacity(size);
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000156 size_ = size;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000157 }
Karl Wiberg94784372015-04-20 14:03:07 +0200158
159 // Ensure that the buffer size can be increased to at least capacity without
160 // further reallocation. (Of course, this operation might need to reallocate
161 // the buffer.)
162 void EnsureCapacity(size_t capacity) {
163 assert(IsConsistent());
164 if (capacity <= capacity_)
165 return;
166 scoped_ptr<uint8_t[]> new_data(new uint8_t[capacity]);
167 std::memcpy(new_data.get(), data_.get(), size_);
168 data_ = new_data.Pass();
169 capacity_ = capacity;
170 assert(IsConsistent());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000171 }
172
Karl Wiberg94784372015-04-20 14:03:07 +0200173 // We can't call std::move(b), so call b.Pass() instead to do the same job.
174 Buffer&& Pass() {
175 assert(IsConsistent());
176 return static_cast<Buffer&&>(*this);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000177 }
178
Karl Wiberg94784372015-04-20 14:03:07 +0200179 // Resets the buffer to zero size and capacity. Works even if the buffer has
180 // been moved from.
181 void Clear() {
182 data_.reset();
183 size_ = 0;
184 capacity_ = 0;
185 assert(IsConsistent());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000186 }
187
Karl Wiberg94784372015-04-20 14:03:07 +0200188 // Swaps two buffers. Also works for buffers that have been moved from.
189 friend void swap(Buffer& a, Buffer& b) {
190 using std::swap;
191 swap(a.size_, b.size_);
192 swap(a.capacity_, b.capacity_);
193 swap(a.data_, b.data_);
194 }
195
196 private:
197 // Precondition for all methods except Clear and the destructor.
198 // Postcondition for all methods except move construction and move
199 // assignment, which leave the moved-from object in a possibly inconsistent
200 // state.
201 bool IsConsistent() const {
202 return (data_ || capacity_ == 0) && capacity_ >= size_;
203 }
204
205 // Called when *this has been moved from. Conceptually it's a no-op, but we
206 // can mutate the state slightly to help subsequent sanity checks catch bugs.
207 void OnMovedFrom() {
208#ifdef NDEBUG
209 // Make *this consistent and empty. Shouldn't be necessary, but better safe
210 // than sorry.
211 size_ = 0;
212 capacity_ = 0;
213#else
214 // Ensure that *this is always inconsistent, to provoke bugs.
215 size_ = 1;
216 capacity_ = 0;
217#endif
218 }
219
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000220 size_t size_;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000221 size_t capacity_;
Karl Wiberg94784372015-04-20 14:03:07 +0200222 scoped_ptr<uint8_t[]> data_;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000223};
224
225} // namespace rtc
226
227#endif // WEBRTC_BASE_BUFFER_H_