1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtCore module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#ifndef QBYTEDATA_P_H
41#define QBYTEDATA_P_H
42
43//
44// W A R N I N G
45// -------------
46//
47// This file is not part of the Qt API. It exists purely as an
48// implementation detail. This header file may change from version to
49// version without notice, or even be removed.
50//
51// We mean it.
52//
53
54#include <QtCore/private/qglobal_p.h>
55#include <qbytearray.h>
56#include <QtCore/qlist.h>
57
58QT_BEGIN_NAMESPACE
59
60// this class handles a list of QByteArrays. It is a variant of QRingBuffer
61// that avoid malloc/realloc/memcpy.
62class QByteDataBuffer
63{
64private:
65 QList<QByteArray> buffers;
66 qint64 bufferCompleteSize = 0;
67 qint64 firstPos = 0;
68public:
69 static inline void popFront(QByteArray &ba, qint64 n)
70 {
71 ba = QByteArray(ba.constData() + n, ba.size() - n);
72 }
73
74 inline void squeezeFirst()
75 {
76 if (!buffers.isEmpty() && firstPos > 0) {
77 popFront(buffers.first(), firstPos);
78 firstPos = 0;
79 }
80 }
81
82 inline void append(const QByteDataBuffer& other)
83 {
84 if (other.isEmpty())
85 return;
86
87 buffers.append(other.buffers);
88 bufferCompleteSize += other.byteAmount();
89
90 if (other.firstPos > 0)
91 popFront(buffers[bufferCount() - other.bufferCount()], other.firstPos);
92 }
93
94 inline void append(QByteDataBuffer &&other)
95 {
96 if (other.isEmpty())
97 return;
98
99 auto otherBufferCount = other.bufferCount();
100 auto otherByteAmount = other.byteAmount();
101 buffers.append(std::move(other.buffers));
102 bufferCompleteSize += otherByteAmount;
103
104 if (other.firstPos > 0)
105 popFront(buffers[bufferCount() - otherBufferCount], other.firstPos);
106 }
107
108 inline void append(const QByteArray& bd)
109 {
110 append(QByteArray(bd));
111 }
112
113 inline void append(QByteArray &&bd)
114 {
115 if (bd.isEmpty())
116 return;
117
118 bufferCompleteSize += bd.size();
119 buffers.append(std::move(bd));
120 }
121
122 inline void prepend(const QByteArray& bd)
123 {
124 prepend(QByteArray(bd));
125 }
126
127 inline void prepend(QByteArray &&bd)
128 {
129 if (bd.isEmpty())
130 return;
131
132 squeezeFirst();
133
134 bufferCompleteSize += bd.size();
135 buffers.prepend(std::move(bd));
136 }
137
138 // return the first QByteData. User of this function has to free() its .data!
139 // preferably use this function to read data.
140 inline QByteArray read()
141 {
142 squeezeFirst();
143 bufferCompleteSize -= buffers.first().size();
144 return buffers.takeFirst();
145 }
146
147 // return everything. User of this function has to free() its .data!
148 // avoid to use this, it might malloc and memcpy.
149 inline QByteArray readAll()
150 {
151 return read(byteAmount());
152 }
153
154 // return amount. User of this function has to free() its .data!
155 // avoid to use this, it might malloc and memcpy.
156 inline QByteArray read(qint64 amount)
157 {
158 amount = qMin(byteAmount(), amount);
159 QByteArray byteData;
160 byteData.resize(amount);
161 read(byteData.data(), byteData.size());
162 return byteData;
163 }
164
165 // return amount bytes. User of this function has to free() its .data!
166 // avoid to use this, it will memcpy.
167 qint64 read(char* dst, qint64 amount)
168 {
169 amount = qMin(amount, byteAmount());
170 qint64 originalAmount = amount;
171 char *writeDst = dst;
172
173 while (amount > 0) {
174 const QByteArray &first = buffers.first();
175 qint64 firstSize = first.size() - firstPos;
176 if (amount >= firstSize) {
177 // take it completely
178 bufferCompleteSize -= firstSize;
179 amount -= firstSize;
180 memcpy(writeDst, first.constData() + firstPos, firstSize);
181 writeDst += firstSize;
182 firstPos = 0;
183 buffers.takeFirst();
184 } else {
185 // take a part of it & it is the last one to take
186 bufferCompleteSize -= amount;
187 memcpy(writeDst, first.constData() + firstPos, amount);
188 firstPos += amount;
189 amount = 0;
190 }
191 }
192
193 return originalAmount;
194 }
195
196 /*!
197 \internal
198 Returns a view into the first QByteArray contained inside,
199 ignoring any already read data. Call advanceReadPointer()
200 to advance the view forward. When a QByteArray is exhausted
201 the view returned by this function will view into another
202 QByteArray if any. Returns a default constructed view if
203 no data is available.
204
205 \sa advanceReadPointer
206 */
207 QByteArrayView readPointer() const
208 {
209 if (isEmpty())
210 return {};
211 return { buffers.first().constData() + qsizetype(firstPos),
212 buffers.first().size() - qsizetype(firstPos) };
213 }
214
215 /*!
216 \internal
217 Advances the read pointer by \a distance.
218
219 \sa readPointer
220 */
221 void advanceReadPointer(qint64 distance)
222 {
223 qint64 newPos = firstPos + distance;
224 if (isEmpty()) {
225 newPos = 0;
226 } else if (auto size = buffers.first().size(); newPos >= size) {
227 while (newPos >= size) {
228 bufferCompleteSize -= (size - firstPos);
229 newPos -= size;
230 buffers.pop_front();
231 if (isEmpty()) {
232 size = 0;
233 newPos = 0;
234 break;
235 }
236 size = buffers.front().size();
237 }
238 bufferCompleteSize -= newPos;
239 } else {
240 bufferCompleteSize -= newPos - firstPos;
241 }
242 firstPos = newPos;
243 }
244
245 inline char getChar()
246 {
247 char c;
248 read(&c, 1);
249 return c;
250 }
251
252 inline void clear()
253 {
254 buffers.clear();
255 bufferCompleteSize = 0;
256 firstPos = 0;
257 }
258
259 // The byte count of all QByteArrays
260 inline qint64 byteAmount() const
261 {
262 return bufferCompleteSize;
263 }
264
265 // the number of QByteArrays
266 inline int bufferCount() const
267 {
268 return buffers.length();
269 }
270
271 inline bool isEmpty() const
272 {
273 return byteAmount() == 0;
274 }
275
276 inline qint64 sizeNextBlock() const
277 {
278 if(buffers.isEmpty())
279 return 0;
280 else
281 return buffers.first().size() - firstPos;
282 }
283
284 inline QByteArray& operator[](int i)
285 {
286 if (i == 0)
287 squeezeFirst();
288
289 return buffers[i];
290 }
291
292 inline bool canReadLine() const {
293 int i = 0;
294 if (i < buffers.length()) {
295 if (buffers.at(i).indexOf('\n', firstPos) != -1)
296 return true;
297 ++i;
298
299 for (; i < buffers.length(); i++)
300 if (buffers.at(i).contains('\n'))
301 return true;
302 }
303 return false;
304 }
305};
306
307QT_END_NAMESPACE
308
309#endif // QBYTEDATA_P_H
310