Line data Source code
1 : #ifdef __PLUMED_HAS_ASMJIT
2 : // This file is part of AsmJit project <https://asmjit.com>
3 : //
4 : // See asmjit.h or LICENSE.md for license and copyright information
5 : // SPDX-License-Identifier: Zlib
6 :
7 : #include "api-build_p.h"
8 : #include "moved_string.h"
9 : #include "support.h"
10 :
11 : ASMJIT_BEGIN_NAMESPACE
12 :
13 : // String - Globals
14 : // ================
15 :
16 : static const char String_baseN[] = "0123456789ABCDEF";
17 :
18 : constexpr size_t kMinAllocSize = 64;
19 : constexpr size_t kMaxAllocSize = SIZE_MAX - Globals::kGrowThreshold;
20 :
21 : // String - Clear & Reset
22 : // ======================
23 :
24 164952 : Error String::reset() noexcept {
25 164952 : if (_type == kTypeLarge)
26 0 : ::free(_large.data);
27 :
28 : _resetInternal();
29 164952 : return kErrorOk;
30 : }
31 :
32 0 : Error String::clear() noexcept {
33 0 : if (isLargeOrExternal()) {
34 0 : _large.size = 0;
35 0 : _large.data[0] = '\0';
36 : }
37 : else {
38 0 : _raw.uptr[0] = 0;
39 : }
40 :
41 0 : return kErrorOk;
42 : }
43 :
44 : // String - Prepare
45 : // ================
46 :
47 0 : char* String::prepare(ModifyOp op, size_t size) noexcept {
48 : char* curData;
49 : size_t curSize;
50 : size_t curCapacity;
51 :
52 0 : if (isLargeOrExternal()) {
53 0 : curData = this->_large.data;
54 0 : curSize = this->_large.size;
55 0 : curCapacity = this->_large.capacity;
56 : }
57 : else {
58 0 : curData = this->_small.data;
59 0 : curSize = this->_small.type;
60 : curCapacity = kSSOCapacity;
61 : }
62 :
63 0 : if (op == ModifyOp::kAssign) {
64 0 : if (size > curCapacity) {
65 : // Prevent arithmetic overflow.
66 0 : if (ASMJIT_UNLIKELY(size >= kMaxAllocSize))
67 : return nullptr;
68 :
69 : size_t newCapacity = Support::alignUp<size_t>(size + 1, kMinAllocSize);
70 0 : char* newData = static_cast<char*>(::malloc(newCapacity));
71 :
72 0 : if (ASMJIT_UNLIKELY(!newData))
73 : return nullptr;
74 :
75 0 : if (_type == kTypeLarge)
76 0 : ::free(curData);
77 :
78 0 : _large.type = kTypeLarge;
79 0 : _large.size = size;
80 0 : _large.capacity = newCapacity - 1;
81 0 : _large.data = newData;
82 :
83 0 : newData[size] = '\0';
84 0 : return newData;
85 : }
86 : else {
87 : _setSize(size);
88 0 : curData[size] = '\0';
89 0 : return curData;
90 : }
91 : }
92 : else {
93 : // Prevent arithmetic overflow.
94 0 : if (ASMJIT_UNLIKELY(size >= kMaxAllocSize - curSize))
95 : return nullptr;
96 :
97 0 : size_t newSize = size + curSize;
98 0 : size_t newSizePlusOne = newSize + 1;
99 :
100 0 : if (newSizePlusOne > curCapacity) {
101 0 : size_t newCapacity = Support::max<size_t>(curCapacity + 1, kMinAllocSize);
102 :
103 0 : if (newCapacity < newSizePlusOne && newCapacity < Globals::kGrowThreshold)
104 : newCapacity = Support::alignUpPowerOf2(newCapacity);
105 :
106 0 : if (newCapacity < newSizePlusOne)
107 : newCapacity = Support::alignUp(newSizePlusOne, Globals::kGrowThreshold);
108 :
109 0 : if (ASMJIT_UNLIKELY(newCapacity < newSizePlusOne))
110 : return nullptr;
111 :
112 0 : char* newData = static_cast<char*>(::malloc(newCapacity));
113 0 : if (ASMJIT_UNLIKELY(!newData))
114 : return nullptr;
115 :
116 : memcpy(newData, curData, curSize);
117 :
118 0 : if (_type == kTypeLarge)
119 0 : ::free(curData);
120 :
121 0 : _large.type = kTypeLarge;
122 0 : _large.size = newSize;
123 0 : _large.capacity = newCapacity - 1;
124 0 : _large.data = newData;
125 :
126 0 : newData[newSize] = '\0';
127 0 : return newData + curSize;
128 : }
129 : else {
130 : _setSize(newSize);
131 0 : curData[newSize] = '\0';
132 0 : return curData + curSize;
133 : }
134 : }
135 : }
136 :
137 : // String - Assign
138 : // ===============
139 :
140 0 : Error String::assign(const char* data, size_t size) noexcept {
141 : char* dst = nullptr;
142 :
143 : // Null terminated string without `size` specified.
144 0 : if (size == SIZE_MAX)
145 0 : size = data ? strlen(data) : size_t(0);
146 :
147 0 : if (isLargeOrExternal()) {
148 0 : if (size <= _large.capacity) {
149 0 : dst = _large.data;
150 0 : _large.size = size;
151 : }
152 : else {
153 : size_t capacityPlusOne = Support::alignUp(size + 1, 32);
154 0 : if (ASMJIT_UNLIKELY(capacityPlusOne < size))
155 : return DebugUtils::errored(kErrorOutOfMemory);
156 :
157 0 : dst = static_cast<char*>(::malloc(capacityPlusOne));
158 0 : if (ASMJIT_UNLIKELY(!dst))
159 : return DebugUtils::errored(kErrorOutOfMemory);
160 :
161 0 : if (_type == kTypeLarge)
162 0 : ::free(_large.data);
163 :
164 0 : _large.type = kTypeLarge;
165 0 : _large.data = dst;
166 0 : _large.size = size;
167 0 : _large.capacity = capacityPlusOne - 1;
168 : }
169 : }
170 : else {
171 0 : if (size <= kSSOCapacity) {
172 : ASMJIT_ASSERT(size < 0xFFu);
173 :
174 0 : dst = _small.data;
175 0 : _small.type = uint8_t(size);
176 : }
177 : else {
178 0 : dst = static_cast<char*>(::malloc(size + 1));
179 0 : if (ASMJIT_UNLIKELY(!dst))
180 : return DebugUtils::errored(kErrorOutOfMemory);
181 :
182 0 : _large.type = kTypeLarge;
183 0 : _large.data = dst;
184 0 : _large.size = size;
185 0 : _large.capacity = size;
186 : }
187 : }
188 :
189 : // Optionally copy data from `data` and null-terminate.
190 0 : if (data && size) {
191 : // NOTE: It's better to use `memmove()`. If, for any reason, somebody uses
192 : // this function to substring the same string it would work as expected.
193 : ::memmove(dst, data, size);
194 : }
195 :
196 0 : dst[size] = '\0';
197 0 : return kErrorOk;
198 : }
199 :
200 : // String - Operations
201 : // ===================
202 :
203 0 : Error String::_opString(ModifyOp op, const char* str, size_t size) noexcept {
204 0 : if (size == SIZE_MAX)
205 0 : size = str ? strlen(str) : size_t(0);
206 :
207 0 : if (!size)
208 : return kErrorOk;
209 :
210 0 : char* p = prepare(op, size);
211 0 : if (!p)
212 : return DebugUtils::errored(kErrorOutOfMemory);
213 :
214 : memcpy(p, str, size);
215 0 : return kErrorOk;
216 : }
217 :
218 0 : Error String::_opChar(ModifyOp op, char c) noexcept {
219 0 : char* p = prepare(op, 1);
220 0 : if (!p)
221 : return DebugUtils::errored(kErrorOutOfMemory);
222 :
223 0 : *p = c;
224 0 : return kErrorOk;
225 : }
226 :
227 0 : Error String::_opChars(ModifyOp op, char c, size_t n) noexcept {
228 0 : if (!n)
229 : return kErrorOk;
230 :
231 0 : char* p = prepare(op, n);
232 0 : if (!p)
233 : return DebugUtils::errored(kErrorOutOfMemory);
234 :
235 0 : memset(p, c, n);
236 0 : return kErrorOk;
237 : }
238 :
239 0 : Error String::padEnd(size_t n, char c) noexcept {
240 : size_t size = this->size();
241 0 : return n > size ? appendChars(c, n - size) : kErrorOk;
242 : }
243 :
244 0 : Error String::_opNumber(ModifyOp op, uint64_t i, uint32_t base, size_t width, StringFormatFlags flags) noexcept {
245 0 : if (base == 0)
246 : base = 10;
247 :
248 : char buf[128];
249 : char* p = buf + ASMJIT_ARRAY_SIZE(buf);
250 :
251 : uint64_t orig = i;
252 : char sign = '\0';
253 :
254 : // Format Sign
255 : // -----------
256 :
257 0 : if (Support::test(flags, StringFormatFlags::kSigned) && int64_t(i) < 0) {
258 0 : i = uint64_t(-int64_t(i));
259 : sign = '-';
260 : }
261 0 : else if (Support::test(flags, StringFormatFlags::kShowSign)) {
262 : sign = '+';
263 : }
264 0 : else if (Support::test(flags, StringFormatFlags::kShowSpace)) {
265 : sign = ' ';
266 : }
267 :
268 : // Format Number
269 : // -------------
270 :
271 0 : switch (base) {
272 : case 2:
273 : case 8:
274 : case 16: {
275 : uint32_t shift = Support::ctz(base);
276 0 : uint32_t mask = base - 1;
277 :
278 : do {
279 0 : uint64_t d = i >> shift;
280 0 : size_t r = size_t(i & mask);
281 :
282 0 : *--p = String_baseN[r];
283 : i = d;
284 0 : } while (i);
285 :
286 : break;
287 : }
288 :
289 0 : case 10: {
290 : do {
291 0 : uint64_t d = i / 10;
292 0 : uint64_t r = i % 10;
293 :
294 0 : *--p = char(uint32_t('0') + uint32_t(r));
295 : i = d;
296 0 : } while (i);
297 :
298 : break;
299 : }
300 :
301 : default:
302 : return DebugUtils::errored(kErrorInvalidArgument);
303 : }
304 :
305 0 : size_t numberSize = (size_t)(buf + ASMJIT_ARRAY_SIZE(buf) - p);
306 :
307 : // Alternate Form
308 : // --------------
309 :
310 0 : if (Support::test(flags, StringFormatFlags::kAlternate)) {
311 0 : if (base == 8) {
312 0 : if (orig != 0)
313 0 : *--p = '0';
314 : }
315 0 : if (base == 16) {
316 0 : *--p = 'x';
317 0 : *--p = '0';
318 : }
319 : }
320 :
321 : // String Width
322 : // ------------
323 :
324 0 : if (sign != 0)
325 0 : *--p = sign;
326 :
327 0 : if (width > 256)
328 : width = 256;
329 :
330 0 : if (width <= numberSize)
331 : width = 0;
332 : else
333 0 : width -= numberSize;
334 :
335 : // Finalize
336 : // --------
337 :
338 0 : size_t prefixSize = (size_t)(buf + ASMJIT_ARRAY_SIZE(buf) - p) - numberSize;
339 0 : char* data = prepare(op, prefixSize + width + numberSize);
340 :
341 0 : if (!data)
342 : return DebugUtils::errored(kErrorOutOfMemory);
343 :
344 : memcpy(data, p, prefixSize);
345 0 : data += prefixSize;
346 :
347 : memset(data, '0', width);
348 0 : data += width;
349 :
350 0 : memcpy(data, p + prefixSize, numberSize);
351 0 : return kErrorOk;
352 : }
353 :
354 0 : Error String::_opHex(ModifyOp op, const void* data, size_t size, char separator) noexcept {
355 : char* dst;
356 : const uint8_t* src = static_cast<const uint8_t*>(data);
357 :
358 0 : if (!size)
359 : return kErrorOk;
360 :
361 0 : if (separator) {
362 0 : if (ASMJIT_UNLIKELY(size >= SIZE_MAX / 3))
363 : return DebugUtils::errored(kErrorOutOfMemory);
364 :
365 0 : dst = prepare(op, size * 3 - 1);
366 0 : if (ASMJIT_UNLIKELY(!dst))
367 : return DebugUtils::errored(kErrorOutOfMemory);
368 :
369 : size_t i = 0;
370 : for (;;) {
371 0 : dst[0] = String_baseN[(src[0] >> 4) & 0xF];
372 0 : dst[1] = String_baseN[(src[0] ) & 0xF];
373 0 : if (++i == size)
374 : break;
375 : // This makes sure that the separator is only put between two hexadecimal bytes.
376 0 : dst[2] = separator;
377 0 : dst += 3;
378 0 : src++;
379 : }
380 : }
381 : else {
382 0 : if (ASMJIT_UNLIKELY(size >= SIZE_MAX / 2))
383 : return DebugUtils::errored(kErrorOutOfMemory);
384 :
385 0 : dst = prepare(op, size * 2);
386 0 : if (ASMJIT_UNLIKELY(!dst))
387 : return DebugUtils::errored(kErrorOutOfMemory);
388 :
389 0 : for (size_t i = 0; i < size; i++, dst += 2, src++) {
390 0 : dst[0] = String_baseN[(src[0] >> 4) & 0xF];
391 0 : dst[1] = String_baseN[(src[0] ) & 0xF];
392 : }
393 : }
394 :
395 : return kErrorOk;
396 : }
397 :
398 0 : Error String::_opFormat(ModifyOp op, const char* fmt, ...) noexcept {
399 : Error err;
400 : va_list ap;
401 :
402 0 : va_start(ap, fmt);
403 0 : err = _opVFormat(op, fmt, ap);
404 0 : va_end(ap);
405 :
406 0 : return err;
407 : }
408 :
409 0 : Error String::_opVFormat(ModifyOp op, const char* fmt, va_list ap) noexcept {
410 0 : size_t startAt = (op == ModifyOp::kAssign) ? size_t(0) : size();
411 0 : size_t remainingCapacity = capacity() - startAt;
412 :
413 : char buf[1024];
414 : int fmtResult;
415 : size_t outputSize;
416 :
417 : va_list apCopy;
418 0 : va_copy(apCopy, ap);
419 :
420 0 : if (remainingCapacity >= 128) {
421 0 : fmtResult = vsnprintf(data() + startAt, remainingCapacity, fmt, ap);
422 0 : outputSize = size_t(fmtResult);
423 :
424 0 : if (ASMJIT_LIKELY(outputSize <= remainingCapacity)) {
425 0 : _setSize(startAt + outputSize);
426 0 : return kErrorOk;
427 : }
428 : }
429 : else {
430 : fmtResult = vsnprintf(buf, ASMJIT_ARRAY_SIZE(buf), fmt, ap);
431 0 : outputSize = size_t(fmtResult);
432 :
433 0 : if (ASMJIT_LIKELY(outputSize < ASMJIT_ARRAY_SIZE(buf)))
434 0 : return _opString(op, buf, outputSize);
435 : }
436 :
437 0 : if (ASMJIT_UNLIKELY(fmtResult < 0))
438 : return DebugUtils::errored(kErrorInvalidState);
439 :
440 0 : char* p = prepare(op, outputSize);
441 0 : if (ASMJIT_UNLIKELY(!p))
442 : return DebugUtils::errored(kErrorOutOfMemory);
443 :
444 0 : fmtResult = vsnprintf(p, outputSize + 1, fmt, apCopy);
445 : ASMJIT_ASSERT(size_t(fmtResult) == outputSize);
446 :
447 0 : return kErrorOk;
448 : }
449 :
450 0 : Error String::truncate(size_t newSize) noexcept {
451 0 : if (isLargeOrExternal()) {
452 0 : if (newSize < _large.size) {
453 0 : _large.data[newSize] = '\0';
454 0 : _large.size = newSize;
455 : }
456 : }
457 : else {
458 0 : if (newSize < _type) {
459 0 : _small.data[newSize] = '\0';
460 0 : _small.type = uint8_t(newSize);
461 : }
462 : }
463 :
464 0 : return kErrorOk;
465 : }
466 :
467 0 : bool String::eq(const char* other, size_t size) const noexcept {
468 : const char* aData = data();
469 : const char* bData = other;
470 :
471 : size_t aSize = this->size();
472 : size_t bSize = size;
473 :
474 0 : if (bSize == SIZE_MAX) {
475 : size_t i;
476 0 : for (i = 0; i < aSize; i++)
477 0 : if (aData[i] != bData[i] || bData[i] == 0)
478 : return false;
479 0 : return bData[i] == 0;
480 : }
481 : else {
482 0 : if (aSize != bSize)
483 : return false;
484 0 : return ::memcmp(aData, bData, aSize) == 0;
485 : }
486 : }
487 :
488 : // String - Tests
489 : // ==============
490 :
491 : #if defined(ASMJIT_TEST)
492 : UNIT(core_string) {
493 : String s;
494 :
495 : EXPECT(s.isLargeOrExternal() == false);
496 : EXPECT(s.isExternal() == false);
497 :
498 : EXPECT(s.assign('a') == kErrorOk);
499 : EXPECT(s.size() == 1);
500 : EXPECT(s.capacity() == String::kSSOCapacity);
501 : EXPECT(s.data()[0] == 'a');
502 : EXPECT(s.data()[1] == '\0');
503 : EXPECT(s.eq("a") == true);
504 : EXPECT(s.eq("a", 1) == true);
505 :
506 : EXPECT(s.assignChars('b', 4) == kErrorOk);
507 : EXPECT(s.size() == 4);
508 : EXPECT(s.capacity() == String::kSSOCapacity);
509 : EXPECT(s.data()[0] == 'b');
510 : EXPECT(s.data()[1] == 'b');
511 : EXPECT(s.data()[2] == 'b');
512 : EXPECT(s.data()[3] == 'b');
513 : EXPECT(s.data()[4] == '\0');
514 : EXPECT(s.eq("bbbb") == true);
515 : EXPECT(s.eq("bbbb", 4) == true);
516 :
517 : EXPECT(s.assign("abc") == kErrorOk);
518 : EXPECT(s.size() == 3);
519 : EXPECT(s.capacity() == String::kSSOCapacity);
520 : EXPECT(s.data()[0] == 'a');
521 : EXPECT(s.data()[1] == 'b');
522 : EXPECT(s.data()[2] == 'c');
523 : EXPECT(s.data()[3] == '\0');
524 : EXPECT(s.eq("abc") == true);
525 : EXPECT(s.eq("abc", 3) == true);
526 :
527 : const char* large = "Large string that will not fit into SSO buffer";
528 : EXPECT(s.assign(large) == kErrorOk);
529 : EXPECT(s.isLargeOrExternal() == true);
530 : EXPECT(s.size() == strlen(large));
531 : EXPECT(s.capacity() > String::kSSOCapacity);
532 : EXPECT(s.eq(large) == true);
533 : EXPECT(s.eq(large, strlen(large)) == true);
534 :
535 : const char* additional = " (additional content)";
536 : EXPECT(s.isLargeOrExternal() == true);
537 : EXPECT(s.append(additional) == kErrorOk);
538 : EXPECT(s.size() == strlen(large) + strlen(additional));
539 :
540 : EXPECT(s.clear() == kErrorOk);
541 : EXPECT(s.size() == 0);
542 : EXPECT(s.empty() == true);
543 : EXPECT(s.data()[0] == '\0');
544 : EXPECT(s.isLargeOrExternal() == true); // Clear should never release the memory.
545 :
546 : EXPECT(s.appendUInt(1234) == kErrorOk);
547 : EXPECT(s.eq("1234") == true);
548 :
549 : EXPECT(s.assignUInt(0xFFFF, 16, 0, StringFormatFlags::kAlternate) == kErrorOk);
550 : EXPECT(s.eq("0xFFFF"));
551 :
552 : StringTmp<64> sTmp;
553 : EXPECT(sTmp.isLargeOrExternal());
554 : EXPECT(sTmp.isExternal());
555 : EXPECT(sTmp.appendChars(' ', 1000) == kErrorOk);
556 : EXPECT(!sTmp.isExternal());
557 : }
558 : #endif
559 :
560 : ASMJIT_END_NAMESPACE
561 :
562 : #endif // __PLUMED_HAS_ASMJIT
|