Line data Source code
1 : /*
2 : * Copyright 2016 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_COPYONWRITEBUFFER_H_
12 : #define WEBRTC_BASE_COPYONWRITEBUFFER_H_
13 :
14 : #include <algorithm>
15 : #include <utility>
16 :
17 : #include "webrtc/base/buffer.h"
18 : #include "webrtc/base/checks.h"
19 : #include "webrtc/base/refcount.h"
20 : #include "webrtc/base/scoped_ref_ptr.h"
21 :
22 : namespace rtc {
23 :
24 0 : class CopyOnWriteBuffer {
25 : public:
26 : // An empty buffer.
27 : CopyOnWriteBuffer();
28 : // Copy size and contents of an existing buffer.
29 : CopyOnWriteBuffer(const CopyOnWriteBuffer& buf);
30 : // Move contents from an existing buffer.
31 : CopyOnWriteBuffer(CopyOnWriteBuffer&& buf);
32 :
33 : // Construct a buffer with the specified number of uninitialized bytes.
34 : explicit CopyOnWriteBuffer(size_t size);
35 : CopyOnWriteBuffer(size_t size, size_t capacity);
36 :
37 : // Construct a buffer and copy the specified number of bytes into it. The
38 : // source array may be (const) uint8_t*, int8_t*, or char*.
39 : template <typename T,
40 : typename std::enable_if<
41 : internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
42 : CopyOnWriteBuffer(const T* data, size_t size)
43 : : CopyOnWriteBuffer(data, size, size) {}
44 : template <typename T,
45 : typename std::enable_if<
46 : internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
47 : CopyOnWriteBuffer(const T* data, size_t size, size_t capacity)
48 : : CopyOnWriteBuffer(size, capacity) {
49 : if (buffer_) {
50 : std::memcpy(buffer_->data(), data, size);
51 : }
52 : }
53 :
54 : // Construct a buffer from the contents of an array.
55 : template <typename T,
56 : size_t N,
57 : typename std::enable_if<
58 : internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
59 : CopyOnWriteBuffer(const T (&array)[N]) // NOLINT: runtime/explicit
60 : : CopyOnWriteBuffer(array, N) {}
61 :
62 : ~CopyOnWriteBuffer();
63 :
64 : // Get a pointer to the data. Just .data() will give you a (const) uint8_t*,
65 : // but you may also use .data<int8_t>() and .data<char>().
66 : template <typename T = uint8_t,
67 : typename std::enable_if<
68 : internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
69 : const T* data() const {
70 : return cdata<T>();
71 : }
72 :
73 : // Get writable pointer to the data. This will create a copy of the underlying
74 : // data if it is shared with other buffers.
75 : template <typename T = uint8_t,
76 : typename std::enable_if<
77 : internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
78 0 : T* data() {
79 0 : RTC_DCHECK(IsConsistent());
80 0 : if (!buffer_) {
81 0 : return nullptr;
82 : }
83 0 : CloneDataIfReferenced(buffer_->capacity());
84 0 : return buffer_->data<T>();
85 : }
86 :
87 : // Get const pointer to the data. This will not create a copy of the
88 : // underlying data if it is shared with other buffers.
89 : template <typename T = uint8_t,
90 : typename std::enable_if<
91 : internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
92 0 : const T* cdata() const {
93 0 : RTC_DCHECK(IsConsistent());
94 0 : if (!buffer_) {
95 0 : return nullptr;
96 : }
97 0 : return buffer_->data<T>();
98 : }
99 :
100 0 : size_t size() const {
101 0 : RTC_DCHECK(IsConsistent());
102 0 : return buffer_ ? buffer_->size() : 0;
103 : }
104 :
105 0 : size_t capacity() const {
106 0 : RTC_DCHECK(IsConsistent());
107 0 : return buffer_ ? buffer_->capacity() : 0;
108 : }
109 :
110 : CopyOnWriteBuffer& operator=(const CopyOnWriteBuffer& buf) {
111 : RTC_DCHECK(IsConsistent());
112 : RTC_DCHECK(buf.IsConsistent());
113 : if (&buf != this) {
114 : buffer_ = buf.buffer_;
115 : }
116 : return *this;
117 : }
118 :
119 0 : CopyOnWriteBuffer& operator=(CopyOnWriteBuffer&& buf) {
120 0 : RTC_DCHECK(IsConsistent());
121 0 : RTC_DCHECK(buf.IsConsistent());
122 0 : buffer_ = std::move(buf.buffer_);
123 0 : return *this;
124 : }
125 :
126 : bool operator==(const CopyOnWriteBuffer& buf) const;
127 :
128 : bool operator!=(const CopyOnWriteBuffer& buf) const {
129 : return !(*this == buf);
130 : }
131 :
132 : uint8_t& operator[](size_t index) {
133 : RTC_DCHECK_LT(index, size());
134 : return data()[index];
135 : }
136 :
137 : uint8_t operator[](size_t index) const {
138 : RTC_DCHECK_LT(index, size());
139 : return cdata()[index];
140 : }
141 :
142 : // Replace the contents of the buffer. Accepts the same types as the
143 : // constructors.
144 : template <typename T,
145 : typename std::enable_if<
146 : internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
147 0 : void SetData(const T* data, size_t size) {
148 0 : RTC_DCHECK(IsConsistent());
149 0 : if (!buffer_) {
150 0 : buffer_ = size > 0 ? new RefCountedObject<Buffer>(data, size) : nullptr;
151 0 : } else if (!buffer_->HasOneRef()) {
152 0 : buffer_ = new RefCountedObject<Buffer>(data, size, buffer_->capacity());
153 : } else {
154 0 : buffer_->SetData(data, size);
155 : }
156 0 : RTC_DCHECK(IsConsistent());
157 0 : }
158 :
159 : template <typename T,
160 : size_t N,
161 : typename std::enable_if<
162 : internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
163 : void SetData(const T (&array)[N]) {
164 : SetData(array, N);
165 : }
166 :
167 : void SetData(const CopyOnWriteBuffer& buf) {
168 : RTC_DCHECK(IsConsistent());
169 : RTC_DCHECK(buf.IsConsistent());
170 : if (&buf != this) {
171 : buffer_ = buf.buffer_;
172 : }
173 : }
174 :
175 : // Append data to the buffer. Accepts the same types as the constructors.
176 : template <typename T,
177 : typename std::enable_if<
178 : internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
179 : void AppendData(const T* data, size_t size) {
180 : RTC_DCHECK(IsConsistent());
181 : if (!buffer_) {
182 : buffer_ = new RefCountedObject<Buffer>(data, size);
183 : RTC_DCHECK(IsConsistent());
184 : return;
185 : }
186 :
187 : CloneDataIfReferenced(std::max(buffer_->capacity(),
188 : buffer_->size() + size));
189 : buffer_->AppendData(data, size);
190 : RTC_DCHECK(IsConsistent());
191 : }
192 :
193 : template <typename T,
194 : size_t N,
195 : typename std::enable_if<
196 : internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
197 : void AppendData(const T (&array)[N]) {
198 : AppendData(array, N);
199 : }
200 :
201 : void AppendData(const CopyOnWriteBuffer& buf) {
202 : AppendData(buf.data(), buf.size());
203 : }
204 :
205 : // Sets the size of the buffer. If the new size is smaller than the old, the
206 : // buffer contents will be kept but truncated; if the new size is greater,
207 : // the existing contents will be kept and the new space will be
208 : // uninitialized.
209 : void SetSize(size_t size);
210 :
211 : // Ensure that the buffer size can be increased to at least capacity without
212 : // further reallocation. (Of course, this operation might need to reallocate
213 : // the buffer.)
214 : void EnsureCapacity(size_t capacity);
215 :
216 : // Resets the buffer to zero size without altering capacity. Works even if the
217 : // buffer has been moved from.
218 : void Clear();
219 :
220 : // Swaps two buffers.
221 : friend void swap(CopyOnWriteBuffer& a, CopyOnWriteBuffer& b) {
222 : std::swap(a.buffer_, b.buffer_);
223 : }
224 :
225 : private:
226 : // Create a copy of the underlying data if it is referenced from other Buffer
227 : // objects.
228 : void CloneDataIfReferenced(size_t new_capacity);
229 :
230 : // Pre- and postcondition of all methods.
231 0 : bool IsConsistent() const {
232 0 : return (!buffer_ || buffer_->capacity() > 0);
233 : }
234 :
235 : // buffer_ is either null, or points to an rtc::Buffer with capacity > 0.
236 : scoped_refptr<RefCountedObject<Buffer>> buffer_;
237 : };
238 :
239 : } // namespace rtc
240 :
241 : #endif // WEBRTC_BASE_COPYONWRITEBUFFER_H_
|