Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
string_template.cpp
Go to the documentation of this file.
1// Copyright 2024 The PDFium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7#include "core/fxcrt/string_template.h"
8
9#include <algorithm>
10#include <utility>
11
12#include "core/fxcrt/check.h"
13#include "core/fxcrt/check_op.h"
14#include "core/fxcrt/compiler_specific.h"
15#include "core/fxcrt/span.h"
16#include "core/fxcrt/span_util.h"
17
18namespace fxcrt {
19
20template <typename T>
21pdfium::span<T> StringTemplate<T>::GetBuffer(size_t nMinBufLength) {
22 if (!m_pData) {
23 if (nMinBufLength == 0) {
24 return pdfium::span<T>();
25 }
26 m_pData = StringData::Create(nMinBufLength);
27 m_pData->m_nDataLength = 0;
28 m_pData->m_String[0] = 0;
29 return m_pData->alloc_span();
30 }
31 if (m_pData->CanOperateInPlace(nMinBufLength)) {
32 return m_pData->alloc_span();
33 }
34 nMinBufLength = std::max(nMinBufLength, m_pData->m_nDataLength);
35 if (nMinBufLength == 0) {
36 return pdfium::span<T>();
37 }
38 RetainPtr<StringData> pNewData = StringData::Create(nMinBufLength);
39 pNewData->CopyContents(*m_pData);
40 pNewData->m_nDataLength = m_pData->m_nDataLength;
41 m_pData = std::move(pNewData);
42 return m_pData->alloc_span();
43}
44
45template <typename T>
46void StringTemplate<T>::ReleaseBuffer(size_t nNewLength) {
47 if (!m_pData) {
48 return;
49 }
50 nNewLength = std::min(nNewLength, m_pData->m_nAllocLength);
51 if (nNewLength == 0) {
52 clear();
53 return;
54 }
55 DCHECK_EQ(m_pData->m_nRefs, 1);
56 m_pData->m_nDataLength = nNewLength;
57 m_pData->capacity_span()[nNewLength] = 0;
58 if (m_pData->m_nAllocLength - nNewLength >= 32) {
59 // Over arbitrary threshold, so pay the price to relocate. Force copy to
60 // always occur by holding a second reference to the string.
61 StringTemplate preserve(*this);
62 ReallocBeforeWrite(nNewLength);
63 }
64}
65
66template <typename T>
67size_t StringTemplate<T>::Remove(T chRemove) {
68 size_t count = std::count(span().begin(), span().end(), chRemove);
69 if (count == 0) {
70 return 0;
71 }
72 ReallocBeforeWrite(m_pData->m_nDataLength);
73 auto src_span = m_pData->span();
74 auto dst_span = m_pData->span();
75 // Perform self-intersecting copy in forwards order.
76 while (!src_span.empty()) {
77 if (src_span[0] != chRemove) {
78 dst_span[0] = src_span[0];
79 dst_span = dst_span.subspan(1);
80 }
81 src_span = src_span.subspan(1);
82 }
83 m_pData->m_nDataLength -= count;
84 m_pData->capacity_span()[m_pData->m_nDataLength] = 0;
85 return count;
86}
87
88template <typename T>
89size_t StringTemplate<T>::Insert(size_t index, T ch) {
90 const size_t cur_length = GetLength();
91 if (!IsValidLength(index)) {
92 return cur_length;
93 }
94 const size_t new_length = cur_length + 1;
95 ReallocBeforeWrite(new_length);
96 fxcrt::spanmove(m_pData->capacity_span().subspan(index + 1),
97 m_pData->capacity_span().subspan(index, new_length - index));
98 m_pData->capacity_span()[index] = ch;
99 m_pData->m_nDataLength = new_length;
100 return new_length;
101}
102
103template <typename T>
104size_t StringTemplate<T>::Delete(size_t index, size_t count) {
105 if (!m_pData) {
106 return 0;
107 }
108 size_t old_length = m_pData->m_nDataLength;
109 if (count == 0 || index != std::clamp<size_t>(index, 0, old_length)) {
110 return old_length;
111 }
112 size_t removal_length = index + count;
113 if (removal_length > old_length) {
114 return old_length;
115 }
116 ReallocBeforeWrite(old_length);
117 // Include the NUL char not accounted for in lengths.
118 size_t chars_to_copy = old_length - removal_length + 1;
119 fxcrt::spanmove(
120 m_pData->capacity_span().subspan(index),
121 m_pData->capacity_span().subspan(removal_length, chars_to_copy));
122 m_pData->m_nDataLength = old_length - count;
123 return m_pData->m_nDataLength;
124}
125
126template <typename T>
127void StringTemplate<T>::SetAt(size_t index, T ch) {
128 DCHECK(IsValidIndex(index));
129 ReallocBeforeWrite(m_pData->m_nDataLength);
130 m_pData->span()[index] = ch;
131}
132
133template <typename T>
134std::optional<size_t> StringTemplate<T>::Find(T ch, size_t start) const {
135 return Find(StringView(ch), start);
136}
137
138template <typename T>
140 size_t start) const {
141 if (!m_pData) {
142 return std::nullopt;
143 }
144 if (!IsValidIndex(start)) {
145 return std::nullopt;
146 }
147 std::optional<size_t> result =
148 spanpos(m_pData->span().subspan(start), str.span());
149 if (!result.has_value()) {
150 return std::nullopt;
151 }
152 return start + result.value();
154
155template <typename T>
157 if (!m_pData) {
158 return std::nullopt;
160 size_t nLength = m_pData->m_nDataLength;
161 while (nLength--) {
162 if (m_pData->span()[nLength] == ch) {
163 return nLength;
164 }
165 }
166 return std::nullopt;
167}
168
169template <typename T>
171 if (!m_pData || oldstr.IsEmpty()) {
172 return 0;
173 }
174 size_t count = 0;
175 {
176 // Limit span lifetime.
177 pdfium::span<const T> search_span = m_pData->span();
178 while (true) {
179 std::optional<size_t> found = spanpos(search_span, oldstr.span());
180 if (!found.has_value()) {
181 break;
182 }
183 ++count;
184 search_span = search_span.subspan(found.value() + oldstr.GetLength());
185 }
186 }
187 if (count == 0) {
188 return 0;
189 }
190 size_t nNewLength = m_pData->m_nDataLength +
191 count * (newstr.GetLength() - oldstr.GetLength());
192 if (nNewLength == 0) {
193 clear();
194 return count;
195 }
196 RetainPtr<StringData> newstr_data = StringData::Create(nNewLength);
197 {
198 // Spans can't outlive StringData buffers.
199 pdfium::span<const T> search_span = m_pData->span();
200 pdfium::span<T> dest_span = newstr_data->span();
201 for (size_t i = 0; i < count; i++) {
202 size_t found = spanpos(search_span, oldstr.span()).value();
203 dest_span = spancpy(dest_span, search_span.first(found));
204 dest_span = spancpy(dest_span, newstr.span());
205 search_span = search_span.subspan(found + oldstr.GetLength());
206 }
207 dest_span = spancpy(dest_span, search_span);
208 CHECK(dest_span.empty());
209 }
210 m_pData = std::move(newstr_data);
211 return count;
212}
213
214template <typename T>
215void StringTemplate<T>::Trim(T ch) {
216 TrimFront(ch);
217 TrimBack(ch);
218}
219
220template <typename T>
221void StringTemplate<T>::TrimFront(T ch) {
222 TrimFront(StringView(ch));
223}
224
225template <typename T>
226void StringTemplate<T>::TrimBack(T ch) {
227 TrimBack(StringView(ch));
228}
229
230template <typename T>
231void StringTemplate<T>::Trim(StringView targets) {
232 TrimFront(targets);
233 TrimBack(targets);
234}
235
236template <typename T>
238 if (!m_pData || targets.IsEmpty()) {
239 return;
240 }
241
242 size_t len = GetLength();
243 if (len == 0) {
244 return;
245 }
246
247 size_t pos = 0;
248 while (pos < len) {
249 size_t i = 0;
250 while (i < targets.GetLength() &&
251 targets.CharAt(i) != m_pData->span()[pos]) {
252 i++;
253 }
254 if (i == targets.GetLength()) {
255 break;
256 }
257 pos++;
258 }
259 if (!pos) {
260 return;
261 }
262
263 ReallocBeforeWrite(len);
264 size_t nDataLength = len - pos;
265 // Move the terminating NUL as well.
266 fxcrt::spanmove(m_pData->capacity_span(),
267 m_pData->capacity_span().subspan(pos, nDataLength + 1));
268 m_pData->m_nDataLength = nDataLength;
269}
270
271template <typename T>
273 if (!m_pData || targets.IsEmpty()) {
274 return;
275 }
276
277 size_t pos = GetLength();
278 if (pos == 0) {
279 return;
280 }
281
282 while (pos) {
283 size_t i = 0;
284 while (i < targets.GetLength() &&
285 targets.CharAt(i) != m_pData->span()[pos - 1]) {
286 i++;
287 }
288 if (i == targets.GetLength()) {
289 break;
290 }
291 pos--;
292 }
293 if (pos < m_pData->m_nDataLength) {
294 ReallocBeforeWrite(m_pData->m_nDataLength);
295 m_pData->m_nDataLength = pos;
296 m_pData->capacity_span()[m_pData->m_nDataLength] = 0;
297 }
298}
299
300template <typename T>
301void StringTemplate<T>::ReallocBeforeWrite(size_t nNewLength) {
302 if (m_pData && m_pData->CanOperateInPlace(nNewLength)) {
303 return;
304 }
305 if (nNewLength == 0) {
306 clear();
307 return;
308 }
309 RetainPtr<StringData> pNewData = StringData::Create(nNewLength);
310 if (m_pData) {
311 size_t nCopyLength = std::min(m_pData->m_nDataLength, nNewLength);
312 // SAFETY: copy of no more than m_nDataLength bytes.
313 pNewData->CopyContents(
314 UNSAFE_BUFFERS(pdfium::make_span(m_pData->m_String, nCopyLength)));
315 pNewData->m_nDataLength = nCopyLength;
316 } else {
317 pNewData->m_nDataLength = 0;
318 }
319 pNewData->capacity_span()[pNewData->m_nDataLength] = 0;
320 m_pData = std::move(pNewData);
321}
322
323template <typename T>
324void StringTemplate<T>::AllocBeforeWrite(size_t nNewLength) {
325 if (m_pData && m_pData->CanOperateInPlace(nNewLength)) {
326 return;
327 }
328 if (nNewLength == 0) {
329 clear();
330 return;
331 }
332 m_pData = StringData::Create(nNewLength);
333}
334
335template <typename T>
336void StringTemplate<T>::AssignCopy(const T* pSrcData, size_t nSrcLen) {
337 AllocBeforeWrite(nSrcLen);
338 // SAFETY: AllocBeforeWrite() ensures `nSrcLen` bytes available.
339 m_pData->CopyContents(UNSAFE_BUFFERS(pdfium::make_span(pSrcData, nSrcLen)));
340 m_pData->m_nDataLength = nSrcLen;
341}
342
343template <typename T>
344void StringTemplate<T>::Concat(const T* pSrcData, size_t nSrcLen) {
345 if (!pSrcData || nSrcLen == 0) {
346 return;
347 }
348 // SAFETY: required from caller.
349 // TODO(tsepez): should be UNSAFE_BUFFER_USAGE or pass span.
350 auto src_span = UNSAFE_BUFFERS(pdfium::make_span(pSrcData, nSrcLen));
351 if (!m_pData) {
352 m_pData = StringData::Create(src_span);
353 return;
354 }
355 if (m_pData->CanOperateInPlace(m_pData->m_nDataLength + nSrcLen)) {
356 m_pData->CopyContentsAt(m_pData->m_nDataLength, src_span);
357 m_pData->m_nDataLength += nSrcLen;
358 return;
359 }
360 // Increase size by at least 50% to amortize repeated concatenations.
361 size_t nGrowLen = std::max(m_pData->m_nDataLength / 2, nSrcLen);
362 RetainPtr<StringData> pNewData =
363 StringData::Create(m_pData->m_nDataLength + nGrowLen);
364 pNewData->CopyContents(*m_pData);
365 pNewData->CopyContentsAt(m_pData->m_nDataLength, src_span);
366 pNewData->m_nDataLength = m_pData->m_nDataLength + nSrcLen;
367 m_pData = std::move(pNewData);
368}
369
370template <typename T>
372 if (m_pData && m_pData->CanOperateInPlace(0)) {
373 m_pData->m_nDataLength = 0;
374 return;
375 }
376 m_pData.Reset();
377}
378
379// Instantiate.
380template class StringTemplate<char>;
381template class StringTemplate<wchar_t>;
382
383} // namespace fxcrt
#define DCHECK
Definition check.h:33
#define DCHECK_EQ(x, y)
Definition check_op.h:17
pdfium::span< T > GetBuffer(size_t nMinBufLength)
size_t Delete(size_t index, size_t count=1)
void AllocBeforeWrite(size_t nNewLen)
size_t Replace(StringView oldstr, StringView newstr)
void ReleaseBuffer(size_t nNewLength)
StringDataTemplate< T > StringData
std::optional< size_t > ReverseFind(T ch) const
StringViewTemplate< T > StringView
void AssignCopy(const T *pSrcData, size_t nSrcLen)
RetainPtr< StringData > m_pData
std::optional< size_t > Find(StringView str, size_t start=0) const
void SetAt(size_t index, T ch)
void TrimBack(StringView targets)
void Trim(StringView targets)
void Concat(const T *pSrcData, size_t nSrcLen)
std::optional< size_t > Find(T ch, size_t start=0) const
void ReallocBeforeWrite(size_t nNewLen)
void TrimFront(StringView targets)
size_t Insert(size_t index, T ch)
#define UNSAFE_BUFFERS(...)
#define CHECK(cvref)