1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #ifndef QBYTEDATA_P_H |
5 | #define QBYTEDATA_P_H |
6 | |
7 | // |
8 | // W A R N I N G |
9 | // ------------- |
10 | // |
11 | // This file is not part of the Qt API. It exists purely as an |
12 | // implementation detail. This header file may change from version to |
13 | // version without notice, or even be removed. |
14 | // |
15 | // We mean it. |
16 | // |
17 | |
18 | #include <QtCore/private/qglobal_p.h> |
19 | #include <qbytearray.h> |
20 | #include <QtCore/qlist.h> |
21 | |
22 | #include <climits> |
23 | |
24 | QT_BEGIN_NAMESPACE |
25 | |
26 | // this class handles a list of QByteArrays. It is a variant of QRingBuffer |
27 | // that avoid malloc/realloc/memcpy. |
28 | class QByteDataBuffer |
29 | { |
30 | private: |
31 | QList<QByteArray> buffers; |
32 | qint64 bufferCompleteSize = 0; |
33 | qint64 firstPos = 0; |
34 | public: |
35 | static inline void popFront(QByteArray &ba, qint64 n) |
36 | { |
37 | ba = QByteArray(ba.constData() + n, ba.size() - n); |
38 | } |
39 | |
40 | inline void squeezeFirst() |
41 | { |
42 | if (!buffers.isEmpty() && firstPos > 0) { |
43 | popFront(ba&: buffers.first(), n: firstPos); |
44 | firstPos = 0; |
45 | } |
46 | } |
47 | |
48 | inline void append(const QByteDataBuffer& other) |
49 | { |
50 | if (other.isEmpty()) |
51 | return; |
52 | |
53 | buffers.append(l: other.buffers); |
54 | bufferCompleteSize += other.byteAmount(); |
55 | |
56 | if (other.firstPos > 0) |
57 | popFront(ba&: buffers[bufferCount() - other.bufferCount()], n: other.firstPos); |
58 | } |
59 | |
60 | inline void append(QByteDataBuffer &&other) |
61 | { |
62 | if (other.isEmpty()) |
63 | return; |
64 | |
65 | auto otherBufferCount = other.bufferCount(); |
66 | auto otherByteAmount = other.byteAmount(); |
67 | buffers.append(other: std::move(other.buffers)); |
68 | bufferCompleteSize += otherByteAmount; |
69 | |
70 | if (other.firstPos > 0) |
71 | popFront(ba&: buffers[bufferCount() - otherBufferCount], n: other.firstPos); |
72 | } |
73 | |
74 | inline void append(const QByteArray& bd) |
75 | { |
76 | append(bd: QByteArray(bd)); |
77 | } |
78 | |
79 | inline void append(QByteArray &&bd) |
80 | { |
81 | if (bd.isEmpty()) |
82 | return; |
83 | |
84 | bufferCompleteSize += bd.size(); |
85 | buffers.append(t: std::move(bd)); |
86 | } |
87 | |
88 | inline void prepend(const QByteArray& bd) |
89 | { |
90 | prepend(bd: QByteArray(bd)); |
91 | } |
92 | |
93 | inline void prepend(QByteArray &&bd) |
94 | { |
95 | if (bd.isEmpty()) |
96 | return; |
97 | |
98 | squeezeFirst(); |
99 | |
100 | bufferCompleteSize += bd.size(); |
101 | buffers.prepend(t: std::move(bd)); |
102 | } |
103 | |
104 | // return the first QByteData. User of this function has to free() its .data! |
105 | // preferably use this function to read data. |
106 | inline QByteArray read() |
107 | { |
108 | Q_ASSERT(!isEmpty()); |
109 | squeezeFirst(); |
110 | bufferCompleteSize -= buffers.first().size(); |
111 | return buffers.takeFirst(); |
112 | } |
113 | |
114 | // return everything. User of this function has to free() its .data! |
115 | // avoid to use this, it might malloc and memcpy. |
116 | inline QByteArray readAll() |
117 | { |
118 | return read(amount: byteAmount()); |
119 | } |
120 | |
121 | // return amount. User of this function has to free() its .data! |
122 | // avoid to use this, it might malloc and memcpy. |
123 | inline QByteArray read(qint64 amount) |
124 | { |
125 | amount = qMin(a: byteAmount(), b: amount); |
126 | if constexpr (sizeof(qsizetype) == sizeof(int)) { // 32-bit |
127 | // While we cannot overall have more than INT_MAX memory allocated, |
128 | // the QByteArrays we hold may be shared copies of each other, |
129 | // causing byteAmount() to exceed INT_MAX. |
130 | if (amount > INT_MAX) |
131 | qBadAlloc(); // what resize() would do if it saw past the truncation |
132 | } |
133 | QByteArray byteData; |
134 | byteData.resize(size: qsizetype(amount)); |
135 | read(dst: byteData.data(), amount: byteData.size()); |
136 | return byteData; |
137 | } |
138 | |
139 | // return amount bytes. User of this function has to free() its .data! |
140 | // avoid to use this, it will memcpy. |
141 | qint64 read(char* dst, qint64 amount) |
142 | { |
143 | amount = qMin(a: amount, b: byteAmount()); |
144 | qint64 originalAmount = amount; |
145 | char *writeDst = dst; |
146 | |
147 | while (amount > 0) { |
148 | const QByteArray &first = buffers.first(); |
149 | qint64 firstSize = first.size() - firstPos; |
150 | if (amount >= firstSize) { |
151 | // take it completely |
152 | bufferCompleteSize -= firstSize; |
153 | amount -= firstSize; |
154 | memcpy(dest: writeDst, src: first.constData() + firstPos, n: firstSize); |
155 | writeDst += firstSize; |
156 | firstPos = 0; |
157 | buffers.takeFirst(); |
158 | } else { |
159 | // take a part of it & it is the last one to take |
160 | bufferCompleteSize -= amount; |
161 | memcpy(dest: writeDst, src: first.constData() + firstPos, n: amount); |
162 | firstPos += amount; |
163 | amount = 0; |
164 | } |
165 | } |
166 | |
167 | return originalAmount; |
168 | } |
169 | |
170 | /*! |
171 | \internal |
172 | Returns a view into the first QByteArray contained inside, |
173 | ignoring any already read data. Call advanceReadPointer() |
174 | to advance the view forward. When a QByteArray is exhausted |
175 | the view returned by this function will view into another |
176 | QByteArray if any. Returns a default constructed view if |
177 | no data is available. |
178 | |
179 | \sa advanceReadPointer |
180 | */ |
181 | QByteArrayView readPointer() const |
182 | { |
183 | if (isEmpty()) |
184 | return {}; |
185 | return { buffers.first().constData() + qsizetype(firstPos), |
186 | buffers.first().size() - qsizetype(firstPos) }; |
187 | } |
188 | |
189 | /*! |
190 | \internal |
191 | Advances the read pointer by \a distance. |
192 | |
193 | \sa readPointer |
194 | */ |
195 | void advanceReadPointer(qint64 distance) |
196 | { |
197 | qint64 newPos = firstPos + distance; |
198 | if (isEmpty()) { |
199 | newPos = 0; |
200 | } else if (auto size = buffers.first().size(); newPos >= size) { |
201 | while (newPos >= size) { |
202 | bufferCompleteSize -= (size - firstPos); |
203 | newPos -= size; |
204 | buffers.pop_front(); |
205 | if (isEmpty()) { |
206 | size = 0; |
207 | newPos = 0; |
208 | break; |
209 | } |
210 | size = buffers.front().size(); |
211 | } |
212 | bufferCompleteSize -= newPos; |
213 | } else { |
214 | bufferCompleteSize -= newPos - firstPos; |
215 | } |
216 | firstPos = newPos; |
217 | } |
218 | |
219 | inline char getChar() |
220 | { |
221 | Q_ASSERT_X(!isEmpty(), "QByteDataBuffer::getChar" , |
222 | "Cannot read a char from an empty buffer!" ); |
223 | char c; |
224 | read(dst: &c, amount: 1); |
225 | return c; |
226 | } |
227 | |
228 | inline void clear() |
229 | { |
230 | buffers.clear(); |
231 | bufferCompleteSize = 0; |
232 | firstPos = 0; |
233 | } |
234 | |
235 | // The byte count of all QByteArrays |
236 | inline qint64 byteAmount() const |
237 | { |
238 | return bufferCompleteSize; |
239 | } |
240 | |
241 | // the number of QByteArrays |
242 | qsizetype bufferCount() const |
243 | { |
244 | return buffers.size(); |
245 | } |
246 | |
247 | inline bool isEmpty() const |
248 | { |
249 | return byteAmount() == 0; |
250 | } |
251 | |
252 | inline qint64 sizeNextBlock() const |
253 | { |
254 | if (buffers.isEmpty()) |
255 | return 0; |
256 | else |
257 | return buffers.first().size() - firstPos; |
258 | } |
259 | |
260 | QByteArray &operator[](qsizetype i) |
261 | { |
262 | if (i == 0) |
263 | squeezeFirst(); |
264 | |
265 | return buffers[i]; |
266 | } |
267 | |
268 | inline bool canReadLine() const { |
269 | qsizetype i = 0; |
270 | if (i < buffers.size()) { |
271 | if (buffers.at(i).indexOf(c: '\n', from: firstPos) != -1) |
272 | return true; |
273 | ++i; |
274 | |
275 | for (; i < buffers.size(); i++) |
276 | if (buffers.at(i).contains(c: '\n')) |
277 | return true; |
278 | } |
279 | return false; |
280 | } |
281 | }; |
282 | |
283 | QT_END_NAMESPACE |
284 | |
285 | #endif // QBYTEDATA_P_H |
286 | |