1 | /* |
2 | This file is part of the KDE libraries |
3 | SPDX-FileCopyrightText: 2008 Jakub Stachowski <qbast@go2.pl> |
4 | |
5 | SPDX-License-Identifier: LGPL-2.0-or-later |
6 | */ |
7 | |
8 | #ifndef BUFFERFRAGMENT_H |
9 | #define BUFFERFRAGMENT_H |
10 | |
11 | #include "kconfigini_p.h" |
12 | |
13 | #define bf_isspace(str) ((str == ' ') || (str == '\t') || (str == '\r')) |
14 | |
15 | // This class provides wrapper around fragment of existing buffer (array of bytes). |
16 | // If underlying buffer gets deleted, all BufferFragment objects referencing it become invalid. |
17 | // Use toByteArray() to make deep copy of the buffer fragment. |
18 | // |
19 | // API is designed to subset of QByteArray methods with some changes: |
20 | // - trim() is like QByteArray.trimmed(), but it modifies current object |
21 | // - truncateLeft() provides way to cut off beginning of the buffer |
22 | // - split() works more like strtok_r than QByteArray.split() |
23 | // - truncateLeft() and mid() require position argument to be valid |
24 | |
25 | class KConfigIniBackend::BufferFragment |
26 | { |
27 | public: |
28 | BufferFragment() |
29 | : d(nullptr) |
30 | , len(0) |
31 | { |
32 | } |
33 | |
34 | BufferFragment(char *buf, int size) |
35 | : d(buf) |
36 | , len(size) |
37 | { |
38 | } |
39 | |
40 | int length() const |
41 | { |
42 | return len; |
43 | } |
44 | |
45 | char at(unsigned int i) const |
46 | { |
47 | Q_ASSERT(i < len); |
48 | return d[i]; |
49 | } |
50 | |
51 | void clear() |
52 | { |
53 | len = 0; |
54 | } |
55 | |
56 | const char *constData() const |
57 | { |
58 | return d; |
59 | } |
60 | |
61 | char *data() const |
62 | { |
63 | return d; |
64 | } |
65 | |
66 | void trim() |
67 | { |
68 | while (bf_isspace(*d) && len > 0) { |
69 | ++d; |
70 | --len; |
71 | } |
72 | while (len > 0 && bf_isspace(d[len - 1])) { |
73 | --len; |
74 | } |
75 | } |
76 | |
77 | // similar to strtok_r . On first call variable pointed by start should be set to 0. |
78 | // Each call will update *start to new starting position. |
79 | BufferFragment split(char c, unsigned int *start) |
80 | { |
81 | if (*start < len) { |
82 | int end = indexOf(c, from: *start); |
83 | if (end == -1) { |
84 | end = len; |
85 | } |
86 | BufferFragment line(d + (*start), end - (*start)); |
87 | *start = end + 1; |
88 | return line; |
89 | } |
90 | return BufferFragment(); |
91 | } |
92 | |
93 | bool isEmpty() const |
94 | { |
95 | return !len; |
96 | } |
97 | |
98 | BufferFragment left(unsigned int size) const |
99 | { |
100 | return BufferFragment(d, qMin(a: size, b: len)); |
101 | } |
102 | |
103 | void truncateLeft(unsigned int size) |
104 | { |
105 | Q_ASSERT(size <= len); |
106 | d += size; |
107 | len -= size; |
108 | } |
109 | |
110 | void truncate(unsigned int pos) |
111 | { |
112 | if (pos < len) { |
113 | len = pos; |
114 | } |
115 | } |
116 | |
117 | bool isNull() const |
118 | { |
119 | return !d; |
120 | } |
121 | |
122 | BufferFragment mid(unsigned int pos, int length = -1) const |
123 | { |
124 | Q_ASSERT(pos < len); |
125 | int size = length; |
126 | if (length == -1 || (pos + length) > len) { |
127 | size = len - pos; |
128 | } |
129 | return BufferFragment(d + pos, size); |
130 | } |
131 | |
132 | bool operator==(const QByteArray &other) const |
133 | { |
134 | return (other.size() == (int)len && memcmp(s1: d, s2: other.constData(), n: len) == 0); |
135 | } |
136 | |
137 | bool operator!=(const QByteArray &other) const |
138 | { |
139 | return (other.size() != (int)len || memcmp(s1: d, s2: other.constData(), n: len) != 0); |
140 | } |
141 | |
142 | bool operator==(const BufferFragment other) const |
143 | { |
144 | return other.len == len && !memcmp(s1: d, s2: other.d, n: len); |
145 | } |
146 | |
147 | int indexOf(char c, unsigned int from = 0) const |
148 | { |
149 | const char *cursor = d + from - 1; |
150 | const char *end = d + len; |
151 | while (++cursor < end) |
152 | if (*cursor == c) { |
153 | return cursor - d; |
154 | } |
155 | return -1; |
156 | } |
157 | |
158 | int lastIndexOf(char c) const |
159 | { |
160 | int from = len - 1; |
161 | while (from >= 0) |
162 | if (d[from] == c) { |
163 | return from; |
164 | } else { |
165 | --from; |
166 | } |
167 | return -1; |
168 | } |
169 | |
170 | QByteArray toByteArray() const |
171 | { |
172 | return QByteArray(d, len); |
173 | } |
174 | |
175 | // this is faster than toByteArray, but returned QByteArray becomes invalid |
176 | // when buffer for this BufferFragment disappears |
177 | QByteArray toVolatileByteArray() const |
178 | { |
179 | return QByteArray::fromRawData(data: d, size: len); |
180 | } |
181 | |
182 | private: |
183 | char *d; |
184 | unsigned int len; |
185 | }; |
186 | |
187 | size_t qHash(const KConfigIniBackend::BufferFragment fragment, size_t seed = 0) |
188 | { |
189 | const uchar *p = reinterpret_cast<const uchar *>(fragment.constData()); |
190 | const int len = fragment.length(); |
191 | |
192 | if (len == 0) { |
193 | return seed; |
194 | } |
195 | return qHashBits(p, size: len, seed); |
196 | } |
197 | |
198 | #endif |
199 | |