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