1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Copyright (C) 2015 Alex Trotsenko <alex1973tr@gmail.com> |
5 | ** Contact: https://www.qt.io/licensing/ |
6 | ** |
7 | ** This file is part of the QtCore module of the Qt Toolkit. |
8 | ** |
9 | ** $QT_BEGIN_LICENSE:LGPL$ |
10 | ** Commercial License Usage |
11 | ** Licensees holding valid commercial Qt licenses may use this file in |
12 | ** accordance with the commercial license agreement provided with the |
13 | ** Software or, alternatively, in accordance with the terms contained in |
14 | ** a written agreement between you and The Qt Company. For licensing terms |
15 | ** and conditions see https://www.qt.io/terms-conditions. For further |
16 | ** information use the contact form at https://www.qt.io/contact-us. |
17 | ** |
18 | ** GNU Lesser General Public License Usage |
19 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
20 | ** General Public License version 3 as published by the Free Software |
21 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
22 | ** packaging of this file. Please review the following information to |
23 | ** ensure the GNU Lesser General Public License version 3 requirements |
24 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
25 | ** |
26 | ** GNU General Public License Usage |
27 | ** Alternatively, this file may be used under the terms of the GNU |
28 | ** General Public License version 2.0 or (at your option) the GNU General |
29 | ** Public license version 3 or any later version approved by the KDE Free |
30 | ** Qt Foundation. The licenses are as published by the Free Software |
31 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
32 | ** included in the packaging of this file. Please review the following |
33 | ** information to ensure the GNU General Public License requirements will |
34 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
35 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
36 | ** |
37 | ** $QT_END_LICENSE$ |
38 | ** |
39 | ****************************************************************************/ |
40 | |
41 | #include "private/qringbuffer_p.h" |
42 | #include "private/qbytearray_p.h" |
43 | #include <string.h> |
44 | |
45 | QT_BEGIN_NAMESPACE |
46 | |
47 | void QRingChunk::allocate(int alloc) |
48 | { |
49 | Q_ASSERT(alloc > 0 && size() == 0); |
50 | |
51 | if (chunk.size() < alloc || isShared()) |
52 | chunk = QByteArray(alloc, Qt::Uninitialized); |
53 | } |
54 | |
55 | void QRingChunk::detach() |
56 | { |
57 | Q_ASSERT(isShared()); |
58 | |
59 | const int chunkSize = size(); |
60 | QByteArray x(chunkSize, Qt::Uninitialized); |
61 | ::memcpy(dest: x.data(), src: chunk.constData() + headOffset, n: chunkSize); |
62 | chunk = std::move(x); |
63 | headOffset = 0; |
64 | tailOffset = chunkSize; |
65 | } |
66 | |
67 | QByteArray QRingChunk::toByteArray() |
68 | { |
69 | if (headOffset != 0 || tailOffset != chunk.size()) { |
70 | if (isShared()) |
71 | return chunk.mid(index: headOffset, len: size()); |
72 | |
73 | if (headOffset != 0) { |
74 | char *ptr = chunk.data(); |
75 | ::memmove(dest: ptr, src: ptr + headOffset, n: size()); |
76 | tailOffset -= headOffset; |
77 | headOffset = 0; |
78 | } |
79 | |
80 | chunk.reserve(asize: 0); // avoid that resizing needlessly reallocates |
81 | chunk.resize(size: tailOffset); |
82 | } |
83 | |
84 | return chunk; |
85 | } |
86 | |
87 | /*! |
88 | \internal |
89 | |
90 | Access the bytes at a specified position the out-variable length will |
91 | contain the amount of bytes readable from there, e.g. the amount still |
92 | the same QByteArray |
93 | */ |
94 | const char *QRingBuffer::readPointerAtPosition(qint64 pos, qint64 &length) const |
95 | { |
96 | Q_ASSERT(pos >= 0); |
97 | |
98 | for (const QRingChunk &chunk : buffers) { |
99 | length = chunk.size(); |
100 | if (length > pos) { |
101 | length -= pos; |
102 | return chunk.data() + pos; |
103 | } |
104 | pos -= length; |
105 | } |
106 | |
107 | length = 0; |
108 | return nullptr; |
109 | } |
110 | |
111 | void QRingBuffer::free(qint64 bytes) |
112 | { |
113 | Q_ASSERT(bytes <= bufferSize); |
114 | |
115 | while (bytes > 0) { |
116 | const qint64 chunkSize = buffers.constFirst().size(); |
117 | |
118 | if (buffers.size() == 1 || chunkSize > bytes) { |
119 | QRingChunk &chunk = buffers.first(); |
120 | // keep a single block around if it does not exceed |
121 | // the basic block size, to avoid repeated allocations |
122 | // between uses of the buffer |
123 | if (bufferSize == bytes) { |
124 | if (chunk.capacity() <= basicBlockSize && !chunk.isShared()) { |
125 | chunk.reset(); |
126 | bufferSize = 0; |
127 | } else { |
128 | clear(); // try to minify/squeeze us |
129 | } |
130 | } else { |
131 | Q_ASSERT(bytes < MaxByteArraySize); |
132 | chunk.advance(offset: bytes); |
133 | bufferSize -= bytes; |
134 | } |
135 | return; |
136 | } |
137 | |
138 | bufferSize -= chunkSize; |
139 | bytes -= chunkSize; |
140 | buffers.removeFirst(); |
141 | } |
142 | } |
143 | |
144 | char *QRingBuffer::reserve(qint64 bytes) |
145 | { |
146 | Q_ASSERT(bytes > 0 && bytes < MaxByteArraySize); |
147 | |
148 | const int chunkSize = qMax(a: basicBlockSize, b: int(bytes)); |
149 | int tail = 0; |
150 | if (bufferSize == 0) { |
151 | if (buffers.isEmpty()) |
152 | buffers.append(t: QRingChunk(chunkSize)); |
153 | else |
154 | buffers.first().allocate(alloc: chunkSize); |
155 | } else { |
156 | const QRingChunk &chunk = buffers.constLast(); |
157 | // if need a new buffer |
158 | if (basicBlockSize == 0 || chunk.isShared() || bytes > chunk.available()) |
159 | buffers.append(t: QRingChunk(chunkSize)); |
160 | else |
161 | tail = chunk.size(); |
162 | } |
163 | |
164 | buffers.last().grow(offset: bytes); |
165 | bufferSize += bytes; |
166 | return buffers.last().data() + tail; |
167 | } |
168 | |
169 | /*! |
170 | \internal |
171 | |
172 | Allocate data at buffer head |
173 | */ |
174 | char *QRingBuffer::reserveFront(qint64 bytes) |
175 | { |
176 | Q_ASSERT(bytes > 0 && bytes < MaxByteArraySize); |
177 | |
178 | const int chunkSize = qMax(a: basicBlockSize, b: int(bytes)); |
179 | if (bufferSize == 0) { |
180 | if (buffers.isEmpty()) |
181 | buffers.prepend(t: QRingChunk(chunkSize)); |
182 | else |
183 | buffers.first().allocate(alloc: chunkSize); |
184 | buffers.first().grow(offset: chunkSize); |
185 | buffers.first().advance(offset: chunkSize - bytes); |
186 | } else { |
187 | const QRingChunk &chunk = buffers.constFirst(); |
188 | // if need a new buffer |
189 | if (basicBlockSize == 0 || chunk.isShared() || bytes > chunk.head()) { |
190 | buffers.prepend(t: QRingChunk(chunkSize)); |
191 | buffers.first().grow(offset: chunkSize); |
192 | buffers.first().advance(offset: chunkSize - bytes); |
193 | } else { |
194 | buffers.first().advance(offset: -bytes); |
195 | } |
196 | } |
197 | |
198 | bufferSize += bytes; |
199 | return buffers.first().data(); |
200 | } |
201 | |
202 | void QRingBuffer::chop(qint64 bytes) |
203 | { |
204 | Q_ASSERT(bytes <= bufferSize); |
205 | |
206 | while (bytes > 0) { |
207 | const qint64 chunkSize = buffers.constLast().size(); |
208 | |
209 | if (buffers.size() == 1 || chunkSize > bytes) { |
210 | QRingChunk &chunk = buffers.last(); |
211 | // keep a single block around if it does not exceed |
212 | // the basic block size, to avoid repeated allocations |
213 | // between uses of the buffer |
214 | if (bufferSize == bytes) { |
215 | if (chunk.capacity() <= basicBlockSize && !chunk.isShared()) { |
216 | chunk.reset(); |
217 | bufferSize = 0; |
218 | } else { |
219 | clear(); // try to minify/squeeze us |
220 | } |
221 | } else { |
222 | Q_ASSERT(bytes < MaxByteArraySize); |
223 | chunk.grow(offset: -bytes); |
224 | bufferSize -= bytes; |
225 | } |
226 | return; |
227 | } |
228 | |
229 | bufferSize -= chunkSize; |
230 | bytes -= chunkSize; |
231 | buffers.removeLast(); |
232 | } |
233 | } |
234 | |
235 | void QRingBuffer::clear() |
236 | { |
237 | if (buffers.isEmpty()) |
238 | return; |
239 | |
240 | buffers.erase(abegin: buffers.begin() + 1, aend: buffers.end()); |
241 | buffers.first().clear(); |
242 | bufferSize = 0; |
243 | } |
244 | |
245 | qint64 QRingBuffer::indexOf(char c, qint64 maxLength, qint64 pos) const |
246 | { |
247 | Q_ASSERT(maxLength >= 0 && pos >= 0); |
248 | |
249 | if (maxLength == 0) |
250 | return -1; |
251 | |
252 | qint64 index = -pos; |
253 | for (const QRingChunk &chunk : buffers) { |
254 | const qint64 nextBlockIndex = qMin(a: index + chunk.size(), b: maxLength); |
255 | |
256 | if (nextBlockIndex > 0) { |
257 | const char *ptr = chunk.data(); |
258 | if (index < 0) { |
259 | ptr -= index; |
260 | index = 0; |
261 | } |
262 | |
263 | const char *findPtr = reinterpret_cast<const char *>(memchr(s: ptr, c: c, |
264 | n: nextBlockIndex - index)); |
265 | if (findPtr) |
266 | return qint64(findPtr - ptr) + index + pos; |
267 | |
268 | if (nextBlockIndex == maxLength) |
269 | return -1; |
270 | } |
271 | index = nextBlockIndex; |
272 | } |
273 | return -1; |
274 | } |
275 | |
276 | qint64 QRingBuffer::read(char *data, qint64 maxLength) |
277 | { |
278 | const qint64 bytesToRead = qMin(a: size(), b: maxLength); |
279 | qint64 readSoFar = 0; |
280 | while (readSoFar < bytesToRead) { |
281 | const qint64 bytesToReadFromThisBlock = qMin(a: bytesToRead - readSoFar, |
282 | b: nextDataBlockSize()); |
283 | if (data) |
284 | memcpy(dest: data + readSoFar, src: readPointer(), n: bytesToReadFromThisBlock); |
285 | readSoFar += bytesToReadFromThisBlock; |
286 | free(bytes: bytesToReadFromThisBlock); |
287 | } |
288 | return readSoFar; |
289 | } |
290 | |
291 | /*! |
292 | \internal |
293 | |
294 | Read an unspecified amount (will read the first buffer) |
295 | */ |
296 | QByteArray QRingBuffer::read() |
297 | { |
298 | if (bufferSize == 0) |
299 | return QByteArray(); |
300 | |
301 | bufferSize -= buffers.constFirst().size(); |
302 | return buffers.takeFirst().toByteArray(); |
303 | } |
304 | |
305 | /*! |
306 | \internal |
307 | |
308 | Peek the bytes from a specified position |
309 | */ |
310 | qint64 QRingBuffer::peek(char *data, qint64 maxLength, qint64 pos) const |
311 | { |
312 | Q_ASSERT(maxLength >= 0 && pos >= 0); |
313 | |
314 | qint64 readSoFar = 0; |
315 | for (const QRingChunk &chunk : buffers) { |
316 | if (readSoFar == maxLength) |
317 | break; |
318 | |
319 | qint64 blockLength = chunk.size(); |
320 | if (pos < blockLength) { |
321 | blockLength = qMin(a: blockLength - pos, b: maxLength - readSoFar); |
322 | memcpy(dest: data + readSoFar, src: chunk.data() + pos, n: blockLength); |
323 | readSoFar += blockLength; |
324 | pos = 0; |
325 | } else { |
326 | pos -= blockLength; |
327 | } |
328 | } |
329 | |
330 | return readSoFar; |
331 | } |
332 | |
333 | /*! |
334 | \internal |
335 | |
336 | Append bytes from data to the end |
337 | */ |
338 | void QRingBuffer::append(const char *data, qint64 size) |
339 | { |
340 | Q_ASSERT(size >= 0); |
341 | |
342 | if (size == 0) |
343 | return; |
344 | |
345 | char *writePointer = reserve(bytes: size); |
346 | if (size == 1) |
347 | *writePointer = *data; |
348 | else |
349 | ::memcpy(dest: writePointer, src: data, n: size); |
350 | } |
351 | |
352 | /*! |
353 | \internal |
354 | |
355 | Append a new buffer to the end |
356 | */ |
357 | void QRingBuffer::append(const QByteArray &qba) |
358 | { |
359 | if (bufferSize != 0 || buffers.isEmpty()) |
360 | buffers.append(t: QRingChunk(qba)); |
361 | else |
362 | buffers.last().assign(qba); |
363 | bufferSize += qba.size(); |
364 | } |
365 | |
366 | qint64 QRingBuffer::readLine(char *data, qint64 maxLength) |
367 | { |
368 | Q_ASSERT(data != nullptr && maxLength > 1); |
369 | |
370 | --maxLength; |
371 | qint64 i = indexOf(c: '\n', maxLength); |
372 | i = read(data, maxLength: i >= 0 ? (i + 1) : maxLength); |
373 | |
374 | // Terminate it. |
375 | data[i] = '\0'; |
376 | return i; |
377 | } |
378 | |
379 | QT_END_NAMESPACE |
380 | |