LCOV - code coverage report
Current view: top level - asmjit - string.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage (other modules) Lines: 3 194 1.5 %
Date: 2026-11-05 10:19:06 Functions: 1 14 7.1 %

          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

Generated by: LCOV version 1.16