diff options
Diffstat (limited to '3rdparty/asmjit/src/asmjit/core/string.cpp')
-rw-r--r-- | 3rdparty/asmjit/src/asmjit/core/string.cpp | 559 |
1 files changed, 559 insertions, 0 deletions
diff --git a/3rdparty/asmjit/src/asmjit/core/string.cpp b/3rdparty/asmjit/src/asmjit/core/string.cpp new file mode 100644 index 00000000000..123e861983c --- /dev/null +++ b/3rdparty/asmjit/src/asmjit/core/string.cpp @@ -0,0 +1,559 @@ +// This file is part of AsmJit project <https://asmjit.com> +// +// See asmjit.h or LICENSE.md for license and copyright information +// SPDX-License-Identifier: Zlib + +#include "../core/api-build_p.h" +#include "../core/string.h" +#include "../core/support.h" + +ASMJIT_BEGIN_NAMESPACE + +// String - Globals +// ================ + +static const char String_baseN[] = "0123456789ABCDEF"; + +constexpr size_t kMinAllocSize = 64; +constexpr size_t kMaxAllocSize = SIZE_MAX - Globals::kGrowThreshold; + +// String - Clear & Reset +// ====================== + +Error String::reset() noexcept { + if (_type == kTypeLarge) + ::free(_large.data); + + _resetInternal(); + return kErrorOk; +} + +Error String::clear() noexcept { + if (isLargeOrExternal()) { + _large.size = 0; + _large.data[0] = '\0'; + } + else { + _raw.uptr[0] = 0; + } + + return kErrorOk; +} + +// String - Prepare +// ================ + +char* String::prepare(ModifyOp op, size_t size) noexcept { + char* curData; + size_t curSize; + size_t curCapacity; + + if (isLargeOrExternal()) { + curData = this->_large.data; + curSize = this->_large.size; + curCapacity = this->_large.capacity; + } + else { + curData = this->_small.data; + curSize = this->_small.type; + curCapacity = kSSOCapacity; + } + + if (op == ModifyOp::kAssign) { + if (size > curCapacity) { + // Prevent arithmetic overflow. + if (ASMJIT_UNLIKELY(size >= kMaxAllocSize)) + return nullptr; + + size_t newCapacity = Support::alignUp<size_t>(size + 1, kMinAllocSize); + char* newData = static_cast<char*>(::malloc(newCapacity)); + + if (ASMJIT_UNLIKELY(!newData)) + return nullptr; + + if (_type == kTypeLarge) + ::free(curData); + + _large.type = kTypeLarge; + _large.size = size; + _large.capacity = newCapacity - 1; + _large.data = newData; + + newData[size] = '\0'; + return newData; + } + else { + _setSize(size); + curData[size] = '\0'; + return curData; + } + } + else { + // Prevent arithmetic overflow. + if (ASMJIT_UNLIKELY(size >= kMaxAllocSize - curSize)) + return nullptr; + + size_t newSize = size + curSize; + size_t newSizePlusOne = newSize + 1; + + if (newSizePlusOne > curCapacity) { + size_t newCapacity = Support::max<size_t>(curCapacity + 1, kMinAllocSize); + + if (newCapacity < newSizePlusOne && newCapacity < Globals::kGrowThreshold) + newCapacity = Support::alignUpPowerOf2(newCapacity); + + if (newCapacity < newSizePlusOne) + newCapacity = Support::alignUp(newSizePlusOne, Globals::kGrowThreshold); + + if (ASMJIT_UNLIKELY(newCapacity < newSizePlusOne)) + return nullptr; + + char* newData = static_cast<char*>(::malloc(newCapacity)); + if (ASMJIT_UNLIKELY(!newData)) + return nullptr; + + memcpy(newData, curData, curSize); + + if (_type == kTypeLarge) + ::free(curData); + + _large.type = kTypeLarge; + _large.size = newSize; + _large.capacity = newCapacity - 1; + _large.data = newData; + + newData[newSize] = '\0'; + return newData + curSize; + } + else { + _setSize(newSize); + curData[newSize] = '\0'; + return curData + curSize; + } + } +} + +// String - Assign +// =============== + +Error String::assign(const char* data, size_t size) noexcept { + char* dst = nullptr; + + // Null terminated string without `size` specified. + if (size == SIZE_MAX) + size = data ? strlen(data) : size_t(0); + + if (isLargeOrExternal()) { + if (size <= _large.capacity) { + dst = _large.data; + _large.size = size; + } + else { + size_t capacityPlusOne = Support::alignUp(size + 1, 32); + if (ASMJIT_UNLIKELY(capacityPlusOne < size)) + return DebugUtils::errored(kErrorOutOfMemory); + + dst = static_cast<char*>(::malloc(capacityPlusOne)); + if (ASMJIT_UNLIKELY(!dst)) + return DebugUtils::errored(kErrorOutOfMemory); + + if (_type == kTypeLarge) + ::free(_large.data); + + _large.type = kTypeLarge; + _large.data = dst; + _large.size = size; + _large.capacity = capacityPlusOne - 1; + } + } + else { + if (size <= kSSOCapacity) { + ASMJIT_ASSERT(size < 0xFFu); + + dst = _small.data; + _small.type = uint8_t(size); + } + else { + dst = static_cast<char*>(::malloc(size + 1)); + if (ASMJIT_UNLIKELY(!dst)) + return DebugUtils::errored(kErrorOutOfMemory); + + _large.type = kTypeLarge; + _large.data = dst; + _large.size = size; + _large.capacity = size; + } + } + + // Optionally copy data from `data` and null-terminate. + if (data && size) { + // NOTE: It's better to use `memmove()`. If, for any reason, somebody uses + // this function to substring the same string it would work as expected. + ::memmove(dst, data, size); + } + + dst[size] = '\0'; + return kErrorOk; +} + +// String - Operations +// =================== + +Error String::_opString(ModifyOp op, const char* str, size_t size) noexcept { + if (size == SIZE_MAX) + size = str ? strlen(str) : size_t(0); + + if (!size) + return kErrorOk; + + char* p = prepare(op, size); + if (!p) + return DebugUtils::errored(kErrorOutOfMemory); + + memcpy(p, str, size); + return kErrorOk; +} + +Error String::_opChar(ModifyOp op, char c) noexcept { + char* p = prepare(op, 1); + if (!p) + return DebugUtils::errored(kErrorOutOfMemory); + + *p = c; + return kErrorOk; +} + +Error String::_opChars(ModifyOp op, char c, size_t n) noexcept { + if (!n) + return kErrorOk; + + char* p = prepare(op, n); + if (!p) + return DebugUtils::errored(kErrorOutOfMemory); + + memset(p, c, n); + return kErrorOk; +} + +Error String::padEnd(size_t n, char c) noexcept { + size_t size = this->size(); + return n > size ? appendChars(c, n - size) : kErrorOk; +} + +Error String::_opNumber(ModifyOp op, uint64_t i, uint32_t base, size_t width, StringFormatFlags flags) noexcept { + if (base == 0) + base = 10; + + char buf[128]; + char* p = buf + ASMJIT_ARRAY_SIZE(buf); + + uint64_t orig = i; + char sign = '\0'; + + // Format Sign + // ----------- + + if (Support::test(flags, StringFormatFlags::kSigned) && int64_t(i) < 0) { + i = uint64_t(-int64_t(i)); + sign = '-'; + } + else if (Support::test(flags, StringFormatFlags::kShowSign)) { + sign = '+'; + } + else if (Support::test(flags, StringFormatFlags::kShowSpace)) { + sign = ' '; + } + + // Format Number + // ------------- + + switch (base) { + case 2: + case 8: + case 16: { + uint32_t shift = Support::ctz(base); + uint32_t mask = base - 1; + + do { + uint64_t d = i >> shift; + size_t r = size_t(i & mask); + + *--p = String_baseN[r]; + i = d; + } while (i); + + break; + } + + case 10: { + do { + uint64_t d = i / 10; + uint64_t r = i % 10; + + *--p = char(uint32_t('0') + uint32_t(r)); + i = d; + } while (i); + + break; + } + + default: + return DebugUtils::errored(kErrorInvalidArgument); + } + + size_t numberSize = (size_t)(buf + ASMJIT_ARRAY_SIZE(buf) - p); + + // Alternate Form + // -------------- + + if (Support::test(flags, StringFormatFlags::kAlternate)) { + if (base == 8) { + if (orig != 0) + *--p = '0'; + } + if (base == 16) { + *--p = 'x'; + *--p = '0'; + } + } + + // String Width + // ------------ + + if (sign != 0) + *--p = sign; + + if (width > 256) + width = 256; + + if (width <= numberSize) + width = 0; + else + width -= numberSize; + + // Finalize + // -------- + + size_t prefixSize = (size_t)(buf + ASMJIT_ARRAY_SIZE(buf) - p) - numberSize; + char* data = prepare(op, prefixSize + width + numberSize); + + if (!data) + return DebugUtils::errored(kErrorOutOfMemory); + + memcpy(data, p, prefixSize); + data += prefixSize; + + memset(data, '0', width); + data += width; + + memcpy(data, p + prefixSize, numberSize); + return kErrorOk; +} + +Error String::_opHex(ModifyOp op, const void* data, size_t size, char separator) noexcept { + char* dst; + const uint8_t* src = static_cast<const uint8_t*>(data); + + if (!size) + return kErrorOk; + + if (separator) { + if (ASMJIT_UNLIKELY(size >= SIZE_MAX / 3)) + return DebugUtils::errored(kErrorOutOfMemory); + + dst = prepare(op, size * 3 - 1); + if (ASMJIT_UNLIKELY(!dst)) + return DebugUtils::errored(kErrorOutOfMemory); + + size_t i = 0; + for (;;) { + dst[0] = String_baseN[(src[0] >> 4) & 0xF]; + dst[1] = String_baseN[(src[0] ) & 0xF]; + if (++i == size) + break; + // This makes sure that the separator is only put between two hexadecimal bytes. + dst[2] = separator; + dst += 3; + src++; + } + } + else { + if (ASMJIT_UNLIKELY(size >= SIZE_MAX / 2)) + return DebugUtils::errored(kErrorOutOfMemory); + + dst = prepare(op, size * 2); + if (ASMJIT_UNLIKELY(!dst)) + return DebugUtils::errored(kErrorOutOfMemory); + + for (size_t i = 0; i < size; i++, dst += 2, src++) { + dst[0] = String_baseN[(src[0] >> 4) & 0xF]; + dst[1] = String_baseN[(src[0] ) & 0xF]; + } + } + + return kErrorOk; +} + +Error String::_opFormat(ModifyOp op, const char* fmt, ...) noexcept { + Error err; + va_list ap; + + va_start(ap, fmt); + err = _opVFormat(op, fmt, ap); + va_end(ap); + + return err; +} + +Error String::_opVFormat(ModifyOp op, const char* fmt, va_list ap) noexcept { + size_t startAt = (op == ModifyOp::kAssign) ? size_t(0) : size(); + size_t remainingCapacity = capacity() - startAt; + + char buf[1024]; + int fmtResult; + size_t outputSize; + + va_list apCopy; + va_copy(apCopy, ap); + + if (remainingCapacity >= 128) { + fmtResult = vsnprintf(data() + startAt, remainingCapacity, fmt, ap); + outputSize = size_t(fmtResult); + + if (ASMJIT_LIKELY(outputSize <= remainingCapacity)) { + _setSize(startAt + outputSize); + return kErrorOk; + } + } + else { + fmtResult = vsnprintf(buf, ASMJIT_ARRAY_SIZE(buf), fmt, ap); + outputSize = size_t(fmtResult); + + if (ASMJIT_LIKELY(outputSize < ASMJIT_ARRAY_SIZE(buf))) + return _opString(op, buf, outputSize); + } + + if (ASMJIT_UNLIKELY(fmtResult < 0)) + return DebugUtils::errored(kErrorInvalidState); + + char* p = prepare(op, outputSize); + if (ASMJIT_UNLIKELY(!p)) + return DebugUtils::errored(kErrorOutOfMemory); + + fmtResult = vsnprintf(p, outputSize + 1, fmt, apCopy); + ASMJIT_ASSERT(size_t(fmtResult) == outputSize); + + return kErrorOk; +} + +Error String::truncate(size_t newSize) noexcept { + if (isLargeOrExternal()) { + if (newSize < _large.size) { + _large.data[newSize] = '\0'; + _large.size = newSize; + } + } + else { + if (newSize < _type) { + _small.data[newSize] = '\0'; + _small.type = uint8_t(newSize); + } + } + + return kErrorOk; +} + +bool String::equals(const char* other, size_t size) const noexcept { + const char* aData = data(); + const char* bData = other; + + size_t aSize = this->size(); + size_t bSize = size; + + if (bSize == SIZE_MAX) { + size_t i; + for (i = 0; i < aSize; i++) + if (aData[i] != bData[i] || bData[i] == 0) + return false; + return bData[i] == 0; + } + else { + if (aSize != bSize) + return false; + return ::memcmp(aData, bData, aSize) == 0; + } +} + +// String - Tests +// ============== + +#if defined(ASMJIT_TEST) +UNIT(core_string) { + String s; + + EXPECT_FALSE(s.isLargeOrExternal()); + EXPECT_FALSE(s.isExternal()); + + EXPECT_EQ(s.assign('a'), kErrorOk); + EXPECT_EQ(s.size(), 1u); + EXPECT_EQ(s.capacity(), String::kSSOCapacity); + EXPECT_EQ(s.data()[0], 'a'); + EXPECT_EQ(s.data()[1], '\0'); + EXPECT_TRUE(s.equals("a")); + EXPECT_TRUE(s.equals("a", 1)); + + EXPECT_EQ(s.assignChars('b', 4), kErrorOk); + EXPECT_EQ(s.size(), 4u); + EXPECT_EQ(s.capacity(), String::kSSOCapacity); + EXPECT_EQ(s.data()[0], 'b'); + EXPECT_EQ(s.data()[1], 'b'); + EXPECT_EQ(s.data()[2], 'b'); + EXPECT_EQ(s.data()[3], 'b'); + EXPECT_EQ(s.data()[4], '\0'); + EXPECT_TRUE(s.equals("bbbb")); + EXPECT_TRUE(s.equals("bbbb", 4)); + + EXPECT_EQ(s.assign("abc"), kErrorOk); + EXPECT_EQ(s.size(), 3u); + EXPECT_EQ(s.capacity(), String::kSSOCapacity); + EXPECT_EQ(s.data()[0], 'a'); + EXPECT_EQ(s.data()[1], 'b'); + EXPECT_EQ(s.data()[2], 'c'); + EXPECT_EQ(s.data()[3], '\0'); + EXPECT_TRUE(s.equals("abc")); + EXPECT_TRUE(s.equals("abc", 3)); + + const char* large = "Large string that will not fit into SSO buffer"; + EXPECT_EQ(s.assign(large), kErrorOk); + EXPECT_TRUE(s.isLargeOrExternal()); + EXPECT_EQ(s.size(), strlen(large)); + EXPECT_GT(s.capacity(), String::kSSOCapacity); + EXPECT_TRUE(s.equals(large)); + EXPECT_TRUE(s.equals(large, strlen(large))); + + const char* additional = " (additional content)"; + EXPECT_TRUE(s.isLargeOrExternal()); + EXPECT_EQ(s.append(additional), kErrorOk); + EXPECT_EQ(s.size(), strlen(large) + strlen(additional)); + + EXPECT_EQ(s.clear(), kErrorOk); + EXPECT_EQ(s.size(), 0u); + EXPECT_TRUE(s.empty()); + EXPECT_EQ(s.data()[0], '\0'); + EXPECT_TRUE(s.isLargeOrExternal()); // Clear should never release the memory. + + EXPECT_EQ(s.appendUInt(1234), kErrorOk); + EXPECT_TRUE(s.equals("1234")); + + EXPECT_EQ(s.assignUInt(0xFFFF, 16, 0, StringFormatFlags::kAlternate), kErrorOk); + EXPECT_TRUE(s.equals("0xFFFF")); + + StringTmp<64> sTmp; + EXPECT_TRUE(sTmp.isLargeOrExternal()); + EXPECT_TRUE(sTmp.isExternal()); + EXPECT_EQ(sTmp.appendChars(' ', 1000), kErrorOk); + EXPECT_FALSE(sTmp.isExternal()); +} +#endif + +ASMJIT_END_NAMESPACE |