1 | // Copyright (C) 2021 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 QRINGBUFFER_P_H |
5 | #define QRINGBUFFER_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 for the convenience |
12 | // of a number of Qt sources files. This header file may change from |
13 | // version to version without notice, or even be removed. |
14 | // |
15 | // We mean it. |
16 | // |
17 | |
18 | #include <QtCore/private/qglobal_p.h> |
19 | #include <QtCore/qbytearray.h> |
20 | #include <QtCore/qlist.h> |
21 | |
22 | QT_BEGIN_NAMESPACE |
23 | |
24 | #ifndef QRINGBUFFER_CHUNKSIZE |
25 | #define QRINGBUFFER_CHUNKSIZE 4096 |
26 | #endif |
27 | |
28 | class QRingChunk |
29 | { |
30 | public: |
31 | // initialization and cleanup |
32 | QRingChunk() noexcept = default; |
33 | explicit inline QRingChunk(qsizetype alloc) : |
34 | chunk(alloc, Qt::Uninitialized), tailOffset(0) |
35 | { |
36 | } |
37 | explicit inline QRingChunk(const QByteArray &qba) noexcept : |
38 | chunk(qba), tailOffset(qba.size()) |
39 | { |
40 | } |
41 | explicit QRingChunk(QByteArray &&qba) noexcept : |
42 | chunk(std::move(qba)), tailOffset(chunk.size()) |
43 | { |
44 | } |
45 | |
46 | inline void swap(QRingChunk &other) noexcept |
47 | { |
48 | chunk.swap(other&: other.chunk); |
49 | qSwap(value1&: headOffset, value2&: other.headOffset); |
50 | qSwap(value1&: tailOffset, value2&: other.tailOffset); |
51 | } |
52 | |
53 | // allocating and sharing |
54 | void allocate(qsizetype alloc); |
55 | inline bool isShared() const |
56 | { |
57 | return !chunk.isDetached(); |
58 | } |
59 | Q_CORE_EXPORT void detach(); |
60 | QByteArray toByteArray() &&; |
61 | |
62 | // getters |
63 | inline qsizetype head() const |
64 | { |
65 | return headOffset; |
66 | } |
67 | inline qsizetype size() const |
68 | { |
69 | return tailOffset - headOffset; |
70 | } |
71 | inline qsizetype capacity() const |
72 | { |
73 | return chunk.size(); |
74 | } |
75 | inline qsizetype available() const |
76 | { |
77 | return chunk.size() - tailOffset; |
78 | } |
79 | inline const char *data() const |
80 | { |
81 | return chunk.constData() + headOffset; |
82 | } |
83 | inline char *data() |
84 | { |
85 | if (isShared()) |
86 | detach(); |
87 | return chunk.data() + headOffset; |
88 | } |
89 | |
90 | // array management |
91 | inline void advance(qsizetype offset) |
92 | { |
93 | Q_ASSERT(headOffset + offset >= 0); |
94 | Q_ASSERT(size() - offset > 0); |
95 | |
96 | headOffset += offset; |
97 | } |
98 | inline void grow(qsizetype offset) |
99 | { |
100 | Q_ASSERT(size() + offset > 0); |
101 | Q_ASSERT(head() + size() + offset <= capacity()); |
102 | |
103 | tailOffset += offset; |
104 | } |
105 | inline void assign(const QByteArray &qba) |
106 | { |
107 | chunk = qba; |
108 | headOffset = 0; |
109 | tailOffset = qba.size(); |
110 | } |
111 | void assign(QByteArray &&qba) |
112 | { |
113 | chunk = std::move(qba); |
114 | headOffset = 0; |
115 | tailOffset = chunk.size(); |
116 | } |
117 | inline void reset() |
118 | { |
119 | headOffset = tailOffset = 0; |
120 | } |
121 | inline void clear() |
122 | { |
123 | *this = {}; |
124 | } |
125 | |
126 | private: |
127 | QByteArray chunk; |
128 | qsizetype headOffset = 0; |
129 | qsizetype tailOffset = 0; |
130 | }; |
131 | Q_DECLARE_SHARED(QRingChunk) |
132 | |
133 | class QRingBuffer |
134 | { |
135 | Q_DISABLE_COPY(QRingBuffer) |
136 | public: |
137 | explicit inline QRingBuffer(int growth = QRINGBUFFER_CHUNKSIZE) : |
138 | bufferSize(0), basicBlockSize(growth) { } |
139 | |
140 | QRingBuffer(QRingBuffer &&) noexcept = default; |
141 | QRingBuffer &operator=(QRingBuffer &&) noexcept = default; |
142 | |
143 | inline void setChunkSize(int size) { |
144 | basicBlockSize = size; |
145 | } |
146 | |
147 | inline int chunkSize() const { |
148 | return basicBlockSize; |
149 | } |
150 | |
151 | inline qint64 nextDataBlockSize() const { |
152 | return bufferSize == 0 ? Q_INT64_C(0) : buffers.first().size(); |
153 | } |
154 | |
155 | inline const char *readPointer() const { |
156 | return bufferSize == 0 ? nullptr : buffers.first().data(); |
157 | } |
158 | |
159 | Q_CORE_EXPORT const char *readPointerAtPosition(qint64 pos, qint64 &length) const; |
160 | Q_CORE_EXPORT void free(qint64 bytes); |
161 | Q_CORE_EXPORT char *reserve(qint64 bytes); |
162 | Q_CORE_EXPORT char *reserveFront(qint64 bytes); |
163 | |
164 | inline void truncate(qint64 pos) { |
165 | Q_ASSERT(pos >= 0 && pos <= size()); |
166 | |
167 | chop(bytes: size() - pos); |
168 | } |
169 | |
170 | Q_CORE_EXPORT void chop(qint64 bytes); |
171 | |
172 | inline bool isEmpty() const { |
173 | return bufferSize == 0; |
174 | } |
175 | |
176 | inline int getChar() { |
177 | if (isEmpty()) |
178 | return -1; |
179 | char c = *readPointer(); |
180 | free(bytes: 1); |
181 | return int(uchar(c)); |
182 | } |
183 | |
184 | inline void putChar(char c) { |
185 | char *ptr = reserve(bytes: 1); |
186 | *ptr = c; |
187 | } |
188 | |
189 | void ungetChar(char c) |
190 | { |
191 | char *ptr = reserveFront(bytes: 1); |
192 | *ptr = c; |
193 | } |
194 | |
195 | |
196 | inline qint64 size() const { |
197 | return bufferSize; |
198 | } |
199 | |
200 | Q_CORE_EXPORT void clear(); |
201 | inline qint64 indexOf(char c) const { return indexOf(c, maxLength: size()); } |
202 | Q_CORE_EXPORT qint64 indexOf(char c, qint64 maxLength, qint64 pos = 0) const; |
203 | Q_CORE_EXPORT qint64 read(char *data, qint64 maxLength); |
204 | Q_CORE_EXPORT QByteArray read(); |
205 | Q_CORE_EXPORT qint64 peek(char *data, qint64 maxLength, qint64 pos = 0) const; |
206 | Q_CORE_EXPORT void append(const char *data, qint64 size); |
207 | Q_CORE_EXPORT void append(const QByteArray &qba); |
208 | Q_CORE_EXPORT void append(QByteArray &&qba); |
209 | |
210 | inline qint64 skip(qint64 length) { |
211 | qint64 bytesToSkip = qMin(a: length, b: bufferSize); |
212 | |
213 | free(bytes: bytesToSkip); |
214 | return bytesToSkip; |
215 | } |
216 | |
217 | Q_CORE_EXPORT qint64 readLine(char *data, qint64 maxLength); |
218 | |
219 | inline bool canReadLine() const { |
220 | return indexOf(c: '\n') >= 0; |
221 | } |
222 | |
223 | private: |
224 | QList<QRingChunk> buffers; |
225 | qint64 bufferSize; |
226 | int basicBlockSize; |
227 | }; |
228 | |
229 | Q_DECLARE_TYPEINFO(QRingBuffer, Q_RELOCATABLE_TYPE); |
230 | |
231 | QT_END_NAMESPACE |
232 | |
233 | #endif // QRINGBUFFER_P_H |
234 | |