diff options
Diffstat (limited to 'source/core/slang-string.h')
| -rw-r--r-- | source/core/slang-string.h | 1514 |
1 files changed, 697 insertions, 817 deletions
diff --git a/source/core/slang-string.h b/source/core/slang-string.h index 6c0dec420..24b119383 100644 --- a/source/core/slang-string.h +++ b/source/core/slang-string.h @@ -1,989 +1,872 @@ #ifndef SLANG_CORE_STRING_H #define SLANG_CORE_STRING_H -#include <string.h> -#include <cstdlib> -#include <stdio.h> -#include <iostream> - -#include "slang-smart-pointer.h" #include "slang-common.h" #include "slang-hash.h" #include "slang-secure-crt.h" +#include "slang-smart-pointer.h" #include "slang-stable-hash.h" +#include <cstdlib> +#include <iostream> #include <new> +#include <stdio.h> +#include <string.h> #include <type_traits> namespace Slang { - class _EndLine - {}; - extern _EndLine EndLine; +class _EndLine +{ +}; +extern _EndLine EndLine; - // in-place reversion, works only for ascii string - inline void reverseInplaceAscii(char* buffer, int length) +// in-place reversion, works only for ascii string +inline void reverseInplaceAscii(char* buffer, int length) +{ + int i, j; + char c; + for (i = 0, j = length - 1; i < j; i++, j--) { - int i, j; - char c; - for (i = 0, j = length - 1; i<j; i++, j--) - { - c = buffer[i]; - buffer[i] = buffer[j]; - buffer[j] = c; - } + c = buffer[i]; + buffer[i] = buffer[j]; + buffer[j] = c; } - template<typename IntType> - inline int intToAscii(char* buffer, IntType val, int radix, int padTo = 0) +} +template<typename IntType> +inline int intToAscii(char* buffer, IntType val, int radix, int padTo = 0) +{ + static_assert(std::is_integral_v<IntType>); + + int i = 0; + IntType sign; + + sign = val; + if (sign < 0) { - static_assert(std::is_integral_v<IntType>); + val = (IntType)(0 - val); + } - int i = 0; - IntType sign; - - sign = val; - if (sign < 0) - { - val = (IntType)(0 - val); - } + do + { + int digit = (val % radix); + if (digit <= 9) + buffer[i++] = (char)(digit + '0'); + else + buffer[i++] = (char)(digit - 10 + 'A'); + } while ((val /= radix) > 0); - do - { - int digit = (val % radix); - if (digit <= 9) - buffer[i++] = (char)(digit + '0'); - else - buffer[i++] = (char)(digit - 10 + 'A'); - } while ((val /= radix) > 0); + SLANG_ASSERT(i >= 0); + while (i < padTo) + buffer[i++] = '0'; - SLANG_ASSERT(i >= 0); - while(i < padTo) - buffer[i++] = '0'; + if (sign < 0) + buffer[i++] = '-'; - if (sign < 0) - buffer[i++] = '-'; + // Put in normal character order + reverseInplaceAscii(buffer, i); - // Put in normal character order - reverseInplaceAscii(buffer, i); + buffer[i] = '\0'; + return i; +} - buffer[i] = '\0'; - return i; - } +SLANG_FORCE_INLINE bool isUtf8LeadingByte(char ch) +{ + return (((unsigned char)ch) & 0xC0) == 0xC0; +} + +SLANG_FORCE_INLINE bool isUtf8ContinuationByte(char ch) +{ + return (((unsigned char)ch) & 0xC0) == 0x80; +} - SLANG_FORCE_INLINE bool isUtf8LeadingByte(char ch) +/* A string slice that doesn't own the contained characters. +It is the responsibility of code using the type to keep the memory backing +the slice in scope. +A slice is generally *not* zero terminated. */ +struct SLANG_RT_API UnownedStringSlice +{ +public: + typedef UnownedStringSlice ThisType; + + // Type to indicate that a ctor is with a length to disabmiguate 0/nullptr + // causing ambiguity. + struct WithLength { - return (((unsigned char)ch) & 0xC0) == 0xC0; - } + }; - SLANG_FORCE_INLINE bool isUtf8ContinuationByte(char ch) + UnownedStringSlice() + : m_begin(nullptr), m_end(nullptr) { - return (((unsigned char)ch) & 0xC0) == 0x80; } - /* A string slice that doesn't own the contained characters. - It is the responsibility of code using the type to keep the memory backing - the slice in scope. - A slice is generally *not* zero terminated. */ - struct SLANG_RT_API UnownedStringSlice + explicit UnownedStringSlice(char const* a) + : m_begin(a), m_end(a ? a + strlen(a) : nullptr) { - public: - typedef UnownedStringSlice ThisType; - - // Type to indicate that a ctor is with a length to disabmiguate 0/nullptr - // causing ambiguity. - struct WithLength {}; - - UnownedStringSlice() - : m_begin(nullptr) - , m_end(nullptr) - {} - - explicit UnownedStringSlice(char const* a) : - m_begin(a), - m_end(a ? a + strlen(a) : nullptr) - {} - UnownedStringSlice(char const* b, char const* e) - : m_begin(b) - , m_end(e) - {} - UnownedStringSlice(char const* b, size_t len) - : m_begin(b) - , m_end(b + len) - {} - UnownedStringSlice(WithLength, char const* b, size_t len) - : m_begin(b) - , m_end(b + len) - {} + } + UnownedStringSlice(char const* b, char const* e) + : m_begin(b), m_end(e) + { + } + UnownedStringSlice(char const* b, size_t len) + : m_begin(b), m_end(b + len) + { + } + UnownedStringSlice(WithLength, char const* b, size_t len) + : m_begin(b), m_end(b + len) + { + } - SLANG_FORCE_INLINE char const* begin() const { return m_begin; } + SLANG_FORCE_INLINE char const* begin() const { return m_begin; } - SLANG_FORCE_INLINE char const* end() const { return m_end; } + SLANG_FORCE_INLINE char const* end() const { return m_end; } - /// True if slice is strictly contained in memory. - bool isMemoryContained(const UnownedStringSlice& slice) const - { - return slice.m_begin >= m_begin && slice.m_end <= m_end; - } - bool isMemoryContained(const char* pos) const - { - return pos >= m_begin && pos <= m_end; - } + /// True if slice is strictly contained in memory. + bool isMemoryContained(const UnownedStringSlice& slice) const + { + return slice.m_begin >= m_begin && slice.m_end <= m_end; + } + bool isMemoryContained(const char* pos) const { return pos >= m_begin && pos <= m_end; } - /// Get the length in *bytes* - Count getLength() const { return Index(m_end - m_begin); } + /// Get the length in *bytes* + Count getLength() const { return Index(m_end - m_begin); } - /// Finds first index of char 'c'. If not found returns -1. - Index indexOf(char c) const; - /// Find first index of slice. If not found returns -1 - Index indexOf(const UnownedStringSlice& slice) const; + /// Finds first index of char 'c'. If not found returns -1. + Index indexOf(char c) const; + /// Find first index of slice. If not found returns -1 + Index indexOf(const UnownedStringSlice& slice) const; - /// Returns a substring. idx is the start index, and len - /// is the amount of characters. - /// The returned length might be truncated, if len extends beyond slice. - UnownedStringSlice subString(Index idx, Index len) const; + /// Returns a substring. idx is the start index, and len + /// is the amount of characters. + /// The returned length might be truncated, if len extends beyond slice. + UnownedStringSlice subString(Index idx, Index len) const; - /// Return a head of the slice - everything up to the index - SLANG_FORCE_INLINE UnownedStringSlice head(Index idx) const { SLANG_ASSERT(idx >= 0 && idx <= getLength()); return UnownedStringSlice(m_begin, idx); } - /// Return a tail of the slice - everything from the index to the end of the slice - SLANG_FORCE_INLINE UnownedStringSlice tail(Index idx) const { SLANG_ASSERT(idx >= 0 && idx <= getLength()); return UnownedStringSlice(m_begin + idx, m_end); } + /// Return a head of the slice - everything up to the index + SLANG_FORCE_INLINE UnownedStringSlice head(Index idx) const + { + SLANG_ASSERT(idx >= 0 && idx <= getLength()); + return UnownedStringSlice(m_begin, idx); + } + /// Return a tail of the slice - everything from the index to the end of the slice + SLANG_FORCE_INLINE UnownedStringSlice tail(Index idx) const + { + SLANG_ASSERT(idx >= 0 && idx <= getLength()); + return UnownedStringSlice(m_begin + idx, m_end); + } - /// True if rhs and this are equal without having to take into account case - /// Note 'case' here is *not* locale specific - it is only A-Z and a-z - bool caseInsensitiveEquals(const ThisType& rhs) const; + /// True if rhs and this are equal without having to take into account case + /// Note 'case' here is *not* locale specific - it is only A-Z and a-z + bool caseInsensitiveEquals(const ThisType& rhs) const; - Index lastIndexOf(char c) const + Index lastIndexOf(char c) const + { + const Index size = Index(m_end - m_begin); + for (Index i = size - 1; i >= 0; --i) { - const Index size = Index(m_end - m_begin); - for (Index i = size - 1; i >= 0; --i) + if (m_begin[i] == c) { - if (m_begin[i] == c) - { - return i; - } + return i; } - return -1; } + return -1; + } - const char& operator[](Index i) const - { - assert(i >= 0 && i < Index(m_end - m_begin)); - return m_begin[i]; - } + const char& operator[](Index i) const + { + assert(i >= 0 && i < Index(m_end - m_begin)); + return m_begin[i]; + } - bool operator==(ThisType const& other) const; - bool operator!=(UnownedStringSlice const& other) const { return !(*this == other); } + bool operator==(ThisType const& other) const; + bool operator!=(UnownedStringSlice const& other) const { return !(*this == other); } - bool operator==(char const* str) const { return (*this) == UnownedStringSlice(str); } - bool operator!=(char const* str) const { return !(*this == str); } + bool operator==(char const* str) const { return (*this) == UnownedStringSlice(str); } + bool operator!=(char const* str) const { return !(*this == str); } - /// True if contents is a single char of c - SLANG_FORCE_INLINE bool isChar(char c) const { return getLength() == 1 && m_begin[0] == c; } + /// True if contents is a single char of c + SLANG_FORCE_INLINE bool isChar(char c) const { return getLength() == 1 && m_begin[0] == c; } - bool startsWithCaseInsensitive(UnownedStringSlice const& other) const; - bool startsWith(UnownedStringSlice const& other) const; - bool startsWith(char const* str) const; + bool startsWithCaseInsensitive(UnownedStringSlice const& other) const; + bool startsWith(UnownedStringSlice const& other) const; + bool startsWith(char const* str) const; - bool endsWithCaseInsensitive(UnownedStringSlice const& other) const; - bool endsWithCaseInsensitive(char const* str) const; + bool endsWithCaseInsensitive(UnownedStringSlice const& other) const; + bool endsWithCaseInsensitive(char const* str) const; - bool endsWith(UnownedStringSlice const& other) const; - bool endsWith(char const* str) const; + bool endsWith(UnownedStringSlice const& other) const; + bool endsWith(char const* str) const; - /// Trims any horizontal whitespace from the start and end and returns as a substring - UnownedStringSlice trim() const; - /// Trims any 'c' from the start or the end, and returns as a substring - UnownedStringSlice trim(char c) const; + /// Trims any horizontal whitespace from the start and end and returns as a substring + UnownedStringSlice trim() const; + /// Trims any 'c' from the start or the end, and returns as a substring + UnownedStringSlice trim(char c) const; - /// Trims any horizonatl whitespace from start and returns as a substring - UnownedStringSlice trimStart() const; + /// Trims any horizonatl whitespace from start and returns as a substring + UnownedStringSlice trimStart() const; - static constexpr bool kHasUniformHash = true; - HashCode64 getHashCode() const - { - return Slang::getHashCode(m_begin, size_t(m_end - m_begin)); - } + static constexpr bool kHasUniformHash = true; + HashCode64 getHashCode() const { return Slang::getHashCode(m_begin, size_t(m_end - m_begin)); } - template <size_t SIZE> - SLANG_FORCE_INLINE static UnownedStringSlice fromLiteral(const char (&in)[SIZE]) { return UnownedStringSlice(in, SIZE - 1); } + template<size_t SIZE> + SLANG_FORCE_INLINE static UnownedStringSlice fromLiteral(const char (&in)[SIZE]) + { + return UnownedStringSlice(in, SIZE - 1); + } - protected: +protected: + char const* m_begin; + char const* m_end; +}; - char const* m_begin; - char const* m_end; - }; +// A more convenient way to make slices from *string literals* +template<size_t SIZE> +SLANG_FORCE_INLINE UnownedStringSlice toSlice(const char (&in)[SIZE]) +{ + return UnownedStringSlice(in, SIZE - 1); +} - // A more convenient way to make slices from *string literals* - template <size_t SIZE> - SLANG_FORCE_INLINE UnownedStringSlice toSlice(const char (&in)[SIZE]) { return UnownedStringSlice(in, SIZE - 1); } - - /// Same as UnownedStringSlice, but must be zero terminated. - /// Zero termination is *not* included in the length. - struct SLANG_RT_API UnownedTerminatedStringSlice : public UnownedStringSlice - { - public: - typedef UnownedStringSlice Super; - typedef UnownedTerminatedStringSlice ThisType; - - /// We can turn into a regular zero terminated string - SLANG_FORCE_INLINE operator const char*() const { return m_begin; } - - /// Exists to match the equivalent function in String. - SLANG_FORCE_INLINE char const* getBuffer() const { return m_begin; } - - /// Construct from a literal directly. - template <size_t SIZE> - SLANG_FORCE_INLINE static ThisType fromLiteral(const char(&in)[SIZE]) { return ThisType(in, SIZE - 1); } - - /// Default constructor - UnownedTerminatedStringSlice():Super(Super::WithLength(), "", 0) {} - - /// Note, b cannot be null because if it were then the string would not be null terminated - UnownedTerminatedStringSlice(char const* b) - : Super(b, b + strlen(b)) - {} - UnownedTerminatedStringSlice(char const* b, size_t len) - : Super(b, len) - { - // b must be valid and it must be null terminated - SLANG_ASSERT(b && b[len] == 0); - } - }; +/// Same as UnownedStringSlice, but must be zero terminated. +/// Zero termination is *not* included in the length. +struct SLANG_RT_API UnownedTerminatedStringSlice : public UnownedStringSlice +{ +public: + typedef UnownedStringSlice Super; + typedef UnownedTerminatedStringSlice ThisType; + + /// We can turn into a regular zero terminated string + SLANG_FORCE_INLINE operator const char*() const { return m_begin; } - // A more convenient way to make terminated slices from *string literals* - template <size_t SIZE> - SLANG_FORCE_INLINE UnownedTerminatedStringSlice toTerminatedSlice(const char(&in)[SIZE]) { return UnownedTerminatedStringSlice(in, SIZE - 1); } + /// Exists to match the equivalent function in String. + SLANG_FORCE_INLINE char const* getBuffer() const { return m_begin; } - // A `StringRepresentation` provides the backing storage for - // all reference-counted string-related types. - class SLANG_RT_API StringRepresentation : public RefObject + /// Construct from a literal directly. + template<size_t SIZE> + SLANG_FORCE_INLINE static ThisType fromLiteral(const char (&in)[SIZE]) { - public: - Index length; - Index capacity; + return ThisType(in, SIZE - 1); + } - SLANG_FORCE_INLINE Index getLength() const - { - return length; - } + /// Default constructor + UnownedTerminatedStringSlice() + : Super(Super::WithLength(), "", 0) + { + } - SLANG_FORCE_INLINE char* getData() - { - return (char*) (this + 1); - } - SLANG_FORCE_INLINE const char* getData() const - { - return (const char*)(this + 1); - } + /// Note, b cannot be null because if it were then the string would not be null terminated + UnownedTerminatedStringSlice(char const* b) + : Super(b, b + strlen(b)) + { + } + UnownedTerminatedStringSlice(char const* b, size_t len) + : Super(b, len) + { + // b must be valid and it must be null terminated + SLANG_ASSERT(b && b[len] == 0); + } +}; - /// Set the contents to be the slice. Must be enough capacity to hold the slice. - void setContents(const UnownedStringSlice& slice); +// A more convenient way to make terminated slices from *string literals* +template<size_t SIZE> +SLANG_FORCE_INLINE UnownedTerminatedStringSlice toTerminatedSlice(const char (&in)[SIZE]) +{ + return UnownedTerminatedStringSlice(in, SIZE - 1); +} - static const char* getData(const StringRepresentation* stringRep) - { - return stringRep ? stringRep->getData() : ""; - } +// A `StringRepresentation` provides the backing storage for +// all reference-counted string-related types. +class SLANG_RT_API StringRepresentation : public RefObject +{ +public: + Index length; + Index capacity; - static UnownedStringSlice asSlice(const StringRepresentation* rep) - { - return rep ? UnownedStringSlice(rep->getData(), rep->getLength()) : UnownedStringSlice(); - } + SLANG_FORCE_INLINE Index getLength() const { return length; } - static bool equal(const StringRepresentation* a, const StringRepresentation* b) - { - return (a == b) || asSlice(a) == asSlice(b); - } + SLANG_FORCE_INLINE char* getData() { return (char*)(this + 1); } + SLANG_FORCE_INLINE const char* getData() const { return (const char*)(this + 1); } - static StringRepresentation* createWithCapacityAndLength(Index capacity, Index length) - { - SLANG_ASSERT(capacity >= length); - void* allocation = operator new(sizeof(StringRepresentation) + capacity + 1); - StringRepresentation* obj = new(allocation) StringRepresentation(); - obj->capacity = capacity; - obj->length = length; - obj->getData()[length] = 0; - return obj; - } + /// Set the contents to be the slice. Must be enough capacity to hold the slice. + void setContents(const UnownedStringSlice& slice); - static StringRepresentation* createWithCapacity(Index capacity) - { - return createWithCapacityAndLength(capacity, 0); - } + static const char* getData(const StringRepresentation* stringRep) + { + return stringRep ? stringRep->getData() : ""; + } - static StringRepresentation* createWithLength(Index length) - { - return createWithCapacityAndLength(length, length); - } + static UnownedStringSlice asSlice(const StringRepresentation* rep) + { + return rep ? UnownedStringSlice(rep->getData(), rep->getLength()) : UnownedStringSlice(); + } - /// Create a representation from the slice. If slice is empty will return nullptr. - static StringRepresentation* create(const UnownedStringSlice& slice); - /// Same as create, but representation will have refcount of 1 (if not nullptr) - static StringRepresentation* createWithReference(const UnownedStringSlice& slice); + static bool equal(const StringRepresentation* a, const StringRepresentation* b) + { + return (a == b) || asSlice(a) == asSlice(b); + } - StringRepresentation* cloneWithCapacity(Index newCapacity) - { - StringRepresentation* newObj = createWithCapacityAndLength(newCapacity, length); - memcpy(getData(), newObj->getData(), length + 1); - return newObj; - } + static StringRepresentation* createWithCapacityAndLength(Index capacity, Index length) + { + SLANG_ASSERT(capacity >= length); + void* allocation = operator new(sizeof(StringRepresentation) + capacity + 1); + StringRepresentation* obj = new (allocation) StringRepresentation(); + obj->capacity = capacity; + obj->length = length; + obj->getData()[length] = 0; + return obj; + } - StringRepresentation* clone() - { - return cloneWithCapacity(length); - } + static StringRepresentation* createWithCapacity(Index capacity) + { + return createWithCapacityAndLength(capacity, 0); + } - StringRepresentation* ensureCapacity(Index required) - { - if (capacity >= required) return this; + static StringRepresentation* createWithLength(Index length) + { + return createWithCapacityAndLength(length, length); + } - Index newCapacity = capacity; - if (!newCapacity) newCapacity = 16; // TODO: figure out good value for minimum capacity + /// Create a representation from the slice. If slice is empty will return nullptr. + static StringRepresentation* create(const UnownedStringSlice& slice); + /// Same as create, but representation will have refcount of 1 (if not nullptr) + static StringRepresentation* createWithReference(const UnownedStringSlice& slice); - while (newCapacity < required) - { - newCapacity = 2 * newCapacity; - } + StringRepresentation* cloneWithCapacity(Index newCapacity) + { + StringRepresentation* newObj = createWithCapacityAndLength(newCapacity, length); + memcpy(getData(), newObj->getData(), length + 1); + return newObj; + } - return cloneWithCapacity(newCapacity); - } + StringRepresentation* clone() { return cloneWithCapacity(length); } + + StringRepresentation* ensureCapacity(Index required) + { + if (capacity >= required) + return this; - /// Overload delete to silence ASAN new-delete-type-mismatch errors. - /// These occur because the allocation size of StringRepresentation - /// does not match deallocation size (due variable sized string payload). - void operator delete(void* p) + Index newCapacity = capacity; + if (!newCapacity) + newCapacity = 16; // TODO: figure out good value for minimum capacity + + while (newCapacity < required) { - StringRepresentation* str = (StringRepresentation*) p; - ::operator delete(str); - } - }; + newCapacity = 2 * newCapacity; + } - class String; + return cloneWithCapacity(newCapacity); + } - struct SLANG_RT_API StringSlice + /// Overload delete to silence ASAN new-delete-type-mismatch errors. + /// These occur because the allocation size of StringRepresentation + /// does not match deallocation size (due variable sized string payload). + void operator delete(void* p) { - public: - StringSlice(); + StringRepresentation* str = (StringRepresentation*)p; + ::operator delete(str); + } +}; - StringSlice(String const& str); +class String; - StringSlice(String const& str, UInt beginIndex, UInt endIndex); +struct SLANG_RT_API StringSlice +{ +public: + StringSlice(); - UInt getLength() const - { - return endIndex - beginIndex; - } + StringSlice(String const& str); - char const* begin() const - { - return representation ? representation->getData() + beginIndex : ""; - } + StringSlice(String const& str, UInt beginIndex, UInt endIndex); - char const* end() const - { - return begin() + getLength(); - } + UInt getLength() const { return endIndex - beginIndex; } - private: - RefPtr<StringRepresentation> representation; - UInt beginIndex; - UInt endIndex; + char const* begin() const + { + return representation ? representation->getData() + beginIndex : ""; + } - friend class String; + char const* end() const { return begin() + getLength(); } - StringSlice(RefPtr<StringRepresentation> const& representation, UInt beginIndex, UInt endIndex) - : representation(representation) - , beginIndex(beginIndex) - , endIndex(endIndex) - {} - }; +private: + RefPtr<StringRepresentation> representation; + UInt beginIndex; + UInt endIndex; - /// String as expected by underlying platform APIs - class SLANG_RT_API OSString - { - public: - /// Default - OSString(); - /// NOTE! This assumes that begin is a new wchar_t[] buffer, and it will - /// now be owned by the OSString - OSString(wchar_t* begin, wchar_t* end); - /// Move Ctor - OSString(OSString&& rhs): - m_begin(rhs.m_begin), - m_end(rhs.m_end) - { - rhs.m_begin = nullptr; - rhs.m_end = nullptr; - } - // Copy Ctor - OSString(const OSString& rhs) : - m_begin(nullptr), - m_end(nullptr) - { - set(rhs.m_begin, rhs.m_end); - } + friend class String; - /// = - void operator=(const OSString& rhs) { set(rhs.m_begin, rhs.m_end); } - void operator=(OSString&& rhs) - { - auto begin = m_begin; - auto end = m_end; - m_begin = rhs.m_begin; - m_end = rhs.m_end; - rhs.m_begin = begin; - rhs.m_end = end; - } + StringSlice(RefPtr<StringRepresentation> const& representation, UInt beginIndex, UInt endIndex) + : representation(representation), beginIndex(beginIndex), endIndex(endIndex) + { + } +}; - ~OSString() { _releaseBuffer(); } +/// String as expected by underlying platform APIs +class SLANG_RT_API OSString +{ +public: + /// Default + OSString(); + /// NOTE! This assumes that begin is a new wchar_t[] buffer, and it will + /// now be owned by the OSString + OSString(wchar_t* begin, wchar_t* end); + /// Move Ctor + OSString(OSString&& rhs) + : m_begin(rhs.m_begin), m_end(rhs.m_end) + { + rhs.m_begin = nullptr; + rhs.m_end = nullptr; + } + // Copy Ctor + OSString(const OSString& rhs) + : m_begin(nullptr), m_end(nullptr) + { + set(rhs.m_begin, rhs.m_end); + } - size_t getLength() const { return (m_end - m_begin); } - void set(const wchar_t* begin, const wchar_t* end); + /// = + void operator=(const OSString& rhs) { set(rhs.m_begin, rhs.m_end); } + void operator=(OSString&& rhs) + { + auto begin = m_begin; + auto end = m_end; + m_begin = rhs.m_begin; + m_end = rhs.m_end; + rhs.m_begin = begin; + rhs.m_end = end; + } - operator wchar_t const*() const - { - return begin(); - } + ~OSString() { _releaseBuffer(); } - wchar_t const* begin() const; - wchar_t const* end() const; + size_t getLength() const { return (m_end - m_begin); } + void set(const wchar_t* begin, const wchar_t* end); - private: + operator wchar_t const*() const { return begin(); } - void _releaseBuffer(); + wchar_t const* begin() const; + wchar_t const* end() const; - wchar_t* m_begin; ///< First character. This is a new wchar_t[] buffer - wchar_t* m_end; ///< Points to terminating 0 - }; +private: + void _releaseBuffer(); - /*! - @brief Represents a UTF-8 encoded string. - */ + wchar_t* m_begin; ///< First character. This is a new wchar_t[] buffer + wchar_t* m_end; ///< Points to terminating 0 +}; - class SLANG_RT_API String - { - friend struct StringSlice; - friend class StringBuilder; - private: +/*! +@brief Represents a UTF-8 encoded string. +*/ +class SLANG_RT_API String +{ + friend struct StringSlice; + friend class StringBuilder; - char* getData() const - { - return m_buffer ? m_buffer->getData() : (char*)""; - } +private: + char* getData() const { return m_buffer ? m_buffer->getData() : (char*)""; } - - void ensureUniqueStorageWithCapacity(Index capacity); - - RefPtr<StringRepresentation> m_buffer; - public: + void ensureUniqueStorageWithCapacity(Index capacity); - explicit String(StringRepresentation* buffer) - : m_buffer(buffer) - {} + RefPtr<StringRepresentation> m_buffer; - static String fromWString(const wchar_t* wstr); - static String fromWString(const wchar_t* wstr, const wchar_t* wend); - static String fromWChar(const wchar_t ch); - static String fromUnicodePoint(Char32 codePoint); +public: + explicit String(StringRepresentation* buffer) + : m_buffer(buffer) + { + } - String() - { - } + static String fromWString(const wchar_t* wstr); + static String fromWString(const wchar_t* wstr, const wchar_t* wend); + static String fromWChar(const wchar_t ch); + static String fromUnicodePoint(Char32 codePoint); - /// Returns a buffer which can hold at least count chars - char* prepareForAppend(Index count); - /// Append data written to buffer output via 'prepareForAppend' directly written 'inplace' - void appendInPlace(const char* chars, Index count); + String() {} - /// Get the internal string represenation - SLANG_FORCE_INLINE StringRepresentation* getStringRepresentation() const { return m_buffer; } + /// Returns a buffer which can hold at least count chars + char* prepareForAppend(Index count); + /// Append data written to buffer output via 'prepareForAppend' directly written 'inplace' + void appendInPlace(const char* chars, Index count); - /// Detach the representation (will leave string as empty). Rep ref count will remain unchanged. - SLANG_FORCE_INLINE StringRepresentation* detachStringRepresentation() { return m_buffer.detach(); } + /// Get the internal string represenation + SLANG_FORCE_INLINE StringRepresentation* getStringRepresentation() const { return m_buffer; } - const char* begin() const - { - return getData(); - } - const char* end() const - { - return getData() + getLength(); - } + /// Detach the representation (will leave string as empty). Rep ref count will remain unchanged. + SLANG_FORCE_INLINE StringRepresentation* detachStringRepresentation() + { + return m_buffer.detach(); + } - void append(int32_t value, int radix = 10); - void append(uint32_t value, int radix = 10); - void append(int64_t value, int radix = 10); - void append(uint64_t value, int radix = 10); - void append(float val, const char* format = "%g"); - void append(double val, const char* format = "%g"); + const char* begin() const { return getData(); } + const char* end() const { return getData() + getLength(); } - // Padded hex representations - void append(StableHashCode32 val); - void append(StableHashCode64 val); + void append(int32_t value, int radix = 10); + void append(uint32_t value, int radix = 10); + void append(int64_t value, int radix = 10); + void append(uint64_t value, int radix = 10); + void append(float val, const char* format = "%g"); + void append(double val, const char* format = "%g"); - void append(char const* str); - void append(char const* str, size_t len); - void append(const char* textBegin, char const* textEnd); - void append(char chr); - void append(String const& str); - void append(StringSlice const& slice); - void append(UnownedStringSlice const& slice); + // Padded hex representations + void append(StableHashCode32 val); + void append(StableHashCode64 val); - /// Append a character (to remove ambiguity with other integral types) - void appendChar(char chr); + void append(char const* str); + void append(char const* str, size_t len); + void append(const char* textBegin, char const* textEnd); + void append(char chr); + void append(String const& str); + void append(StringSlice const& slice); + void append(UnownedStringSlice const& slice); - /// Append the specified char count times - void appendRepeatedChar(char chr, Index count); + /// Append a character (to remove ambiguity with other integral types) + void appendChar(char chr); - String(const char* str) - { - append(str); + /// Append the specified char count times + void appendRepeatedChar(char chr, Index count); - } - String(const char* textBegin, char const* textEnd) - { - append(textBegin, textEnd); - } + String(const char* str) { append(str); } + String(const char* textBegin, char const* textEnd) { append(textBegin, textEnd); } - // Make all String ctors from a numeric explicit, to avoid unexpected/unnecessary conversions - explicit String(int32_t val, int radix = 10) - { - append(val, radix); - } - explicit String(uint32_t val, int radix = 10) - { - append(val, radix); - } - explicit String(int64_t val, int radix = 10) - { - append(val, radix); - } - explicit String(uint64_t val, int radix = 10) - { - append(val, radix); - } - explicit String(StableHashCode32 val) - { - append(val); - } - explicit String(StableHashCode64 val) - { - append(val); - } - explicit String(float val, const char* format = "%g") - { - append(val, format); - } - explicit String(double val, const char* format = "%g") - { - append(val, format); - } + // Make all String ctors from a numeric explicit, to avoid unexpected/unnecessary conversions + explicit String(int32_t val, int radix = 10) { append(val, radix); } + explicit String(uint32_t val, int radix = 10) { append(val, radix); } + explicit String(int64_t val, int radix = 10) { append(val, radix); } + explicit String(uint64_t val, int radix = 10) { append(val, radix); } + explicit String(StableHashCode32 val) { append(val); } + explicit String(StableHashCode64 val) { append(val); } + explicit String(float val, const char* format = "%g") { append(val, format); } + explicit String(double val, const char* format = "%g") { append(val, format); } - explicit String(char chr) - { - appendChar(chr); - } - String(String const& str) - { - m_buffer = str.m_buffer; - } - String(String&& other) - { - m_buffer = _Move(other.m_buffer); - } + explicit String(char chr) { appendChar(chr); } + String(String const& str) { m_buffer = str.m_buffer; } + String(String&& other) { m_buffer = _Move(other.m_buffer); } - String(StringSlice const& slice) - { - append(slice); - } + String(StringSlice const& slice) { append(slice); } - String(UnownedStringSlice const& slice) - { - append(slice); - } + String(UnownedStringSlice const& slice) { append(slice); } - ~String() - { - m_buffer.setNull(); - } + ~String() { m_buffer.setNull(); } - String& operator=(const String& str) - { - m_buffer = str.m_buffer; - return *this; - } - String& operator=(String&& other) - { - m_buffer = _Move(other.m_buffer); - return *this; - } - char operator[](Index id) const - { - SLANG_ASSERT(id >= 0 && id < getLength()); - // Silence a pedantic warning on GCC + String& operator=(const String& str) + { + m_buffer = str.m_buffer; + return *this; + } + String& operator=(String&& other) + { + m_buffer = _Move(other.m_buffer); + return *this; + } + char operator[](Index id) const + { + SLANG_ASSERT(id >= 0 && id < getLength()); + // Silence a pedantic warning on GCC #if __GNUC__ - if(id < 0) __builtin_unreachable(); + if (id < 0) + __builtin_unreachable(); #endif - return begin()[id]; - } + return begin()[id]; + } - Index getLength() const - { - return m_buffer ? m_buffer->getLength() : 0; - } - /// Make the length of the string the amount specified. Must be less than current size - void reduceLength(Index length); - - friend String operator+(const char*op1, const String & op2); - friend String operator+(const String & op1, const char * op2); - friend String operator+(const String & op1, const String & op2); - - StringSlice trimStart() const - { - if (!m_buffer) - return StringSlice(); - Index startIndex = 0; - const char*const data = getData(); - while (startIndex < getLength() && - (data[startIndex] == ' ' || data[startIndex] == '\t' || data[startIndex] == '\r' || data[startIndex] == '\n')) - startIndex++; - return StringSlice(m_buffer, startIndex, getLength()); - } + Index getLength() const { return m_buffer ? m_buffer->getLength() : 0; } + /// Make the length of the string the amount specified. Must be less than current size + void reduceLength(Index length); - StringSlice trimEnd() const - { - if (!m_buffer) - return StringSlice(); + friend String operator+(const char* op1, const String& op2); + friend String operator+(const String& op1, const char* op2); + friend String operator+(const String& op1, const String& op2); - Index endIndex = getLength(); - const char*const data = getData(); - while (endIndex > 0 && - (data[endIndex-1] == ' ' || data[endIndex-1] == '\t' || data[endIndex-1] == '\r' || data[endIndex-1] == '\n')) - endIndex--; + StringSlice trimStart() const + { + if (!m_buffer) + return StringSlice(); + Index startIndex = 0; + const char* const data = getData(); + while (startIndex < getLength() && (data[startIndex] == ' ' || data[startIndex] == '\t' || + data[startIndex] == '\r' || data[startIndex] == '\n')) + startIndex++; + return StringSlice(m_buffer, startIndex, getLength()); + } - return StringSlice(m_buffer, 0, endIndex); - } + StringSlice trimEnd() const + { + if (!m_buffer) + return StringSlice(); - StringSlice trim() const - { - if (!m_buffer) - return StringSlice(); - - Index startIndex = 0; - const char*const data = getData(); - while (startIndex < getLength() && - (data[startIndex] == ' ' || data[startIndex] == '\t' || data[startIndex] == '\r' || data[startIndex] == '\n')) - startIndex++; - Index endIndex = getLength(); - while (endIndex > startIndex && - (data[endIndex-1] == ' ' || data[endIndex-1] == '\t' || data[endIndex-1] == '\r' || data[endIndex-1] == '\n')) - endIndex--; - - return StringSlice(m_buffer, startIndex, endIndex); - } + Index endIndex = getLength(); + const char* const data = getData(); + while (endIndex > 0 && (data[endIndex - 1] == ' ' || data[endIndex - 1] == '\t' || + data[endIndex - 1] == '\r' || data[endIndex - 1] == '\n')) + endIndex--; - StringSlice subString(Index id, Index len) const - { - if (len == 0) - return StringSlice(); + return StringSlice(m_buffer, 0, endIndex); + } + + StringSlice trim() const + { + if (!m_buffer) + return StringSlice(); + + Index startIndex = 0; + const char* const data = getData(); + while (startIndex < getLength() && (data[startIndex] == ' ' || data[startIndex] == '\t' || + data[startIndex] == '\r' || data[startIndex] == '\n')) + startIndex++; + Index endIndex = getLength(); + while (endIndex > startIndex && (data[endIndex - 1] == ' ' || data[endIndex - 1] == '\t' || + data[endIndex - 1] == '\r' || data[endIndex - 1] == '\n')) + endIndex--; + + return StringSlice(m_buffer, startIndex, endIndex); + } + + StringSlice subString(Index id, Index len) const + { + if (len == 0) + return StringSlice(); - if (id + len > getLength()) - len = getLength() - id; + if (id + len > getLength()) + len = getLength() - id; #if _DEBUG - if (id < 0 || id >= getLength() || (id + len) > getLength()) - SLANG_ASSERT_FAILURE("SubString: index out of range."); - if (len < 0) - SLANG_ASSERT_FAILURE("SubString: length less than zero."); + if (id < 0 || id >= getLength() || (id + len) > getLength()) + SLANG_ASSERT_FAILURE("SubString: index out of range."); + if (len < 0) + SLANG_ASSERT_FAILURE("SubString: length less than zero."); #endif - return StringSlice(m_buffer, id, id + len); - } + return StringSlice(m_buffer, id, id + len); + } - char const* getBuffer() const - { - return getData(); - } + char const* getBuffer() const { return getData(); } - OSString toWString(Index* len = 0) const; + OSString toWString(Index* len = 0) const; - bool equals(const String& str, bool caseSensitive = true) + bool equals(const String& str, bool caseSensitive = true) + { + if (caseSensitive) + return (strcmp(begin(), str.begin()) == 0); + else { - if (caseSensitive) - return (strcmp(begin(), str.begin()) == 0); - else - { #ifdef _MSC_VER - return (_stricmp(begin(), str.begin()) == 0); + return (_stricmp(begin(), str.begin()) == 0); #else - return (strcasecmp(begin(), str.begin()) == 0); + return (strcasecmp(begin(), str.begin()) == 0); #endif - } - } - bool operator==(const char* strbuffer) const - { - return (strcmp(begin(), strbuffer) == 0); - } - - bool operator==(const String& str) const - { - return (strcmp(begin(), str.begin()) == 0); - } - bool operator!=(const char* strbuffer) const - { - return (strcmp(begin(), strbuffer) != 0); - } - bool operator!=(const String& str) const - { - return (strcmp(begin(), str.begin()) != 0); - } - bool operator>(const String& str) const - { - return (strcmp(begin(), str.begin()) > 0); - } - bool operator<(const String& str) const - { - return (strcmp(begin(), str.begin()) < 0); - } - bool operator>=(const String& str) const - { - return (strcmp(begin(), str.begin()) >= 0); - } - bool operator<=(const String& str) const - { - return (strcmp(begin(), str.begin()) <= 0); } + } + bool operator==(const char* strbuffer) const { return (strcmp(begin(), strbuffer) == 0); } - SLANG_FORCE_INLINE bool operator==(const UnownedStringSlice& slice) const { return getUnownedSlice() == slice; } - SLANG_FORCE_INLINE bool operator!=(const UnownedStringSlice& slice) const { return getUnownedSlice() != slice; } + bool operator==(const String& str) const { return (strcmp(begin(), str.begin()) == 0); } + bool operator!=(const char* strbuffer) const { return (strcmp(begin(), strbuffer) != 0); } + bool operator!=(const String& str) const { return (strcmp(begin(), str.begin()) != 0); } + bool operator>(const String& str) const { return (strcmp(begin(), str.begin()) > 0); } + bool operator<(const String& str) const { return (strcmp(begin(), str.begin()) < 0); } + bool operator>=(const String& str) const { return (strcmp(begin(), str.begin()) >= 0); } + bool operator<=(const String& str) const { return (strcmp(begin(), str.begin()) <= 0); } - String toUpper() const - { - String result; - for (auto c : *this) - { - char d = (c >= 'a' && c <= 'z') ? (c - ('a' - 'A')) : c; - result.append(d); - } - return result; - } + SLANG_FORCE_INLINE bool operator==(const UnownedStringSlice& slice) const + { + return getUnownedSlice() == slice; + } + SLANG_FORCE_INLINE bool operator!=(const UnownedStringSlice& slice) const + { + return getUnownedSlice() != slice; + } - String toLower() const + String toUpper() const + { + String result; + for (auto c : *this) { - String result; - for (auto c : *this) - { - char d = (c >= 'A' && c <= 'Z') ? (c - ('A' - 'a')) : c; - result.append(d); - } - return result; + char d = (c >= 'a' && c <= 'z') ? (c - ('a' - 'A')) : c; + result.append(d); } + return result; + } - Index indexOf(const char* str, Index id) const // String str + String toLower() const + { + String result; + for (auto c : *this) { - if (id >= getLength()) - return Index(-1); - auto findRs = strstr(begin() + id, str); - Index res = findRs ? findRs - begin() : Index(-1); - return res; + char d = (c >= 'A' && c <= 'Z') ? (c - ('A' - 'a')) : c; + result.append(d); } + return result; + } - Index indexOf(const String& str, Index id) const - { - return indexOf(str.begin(), id); - } + Index indexOf(const char* str, Index id) const // String str + { + if (id >= getLength()) + return Index(-1); + auto findRs = strstr(begin() + id, str); + Index res = findRs ? findRs - begin() : Index(-1); + return res; + } - Index indexOf(const char* str) const - { - return indexOf(str, 0); - } + Index indexOf(const String& str, Index id) const { return indexOf(str.begin(), id); } - Index indexOf(const String& str) const - { - return indexOf(str.begin(), 0); - } + Index indexOf(const char* str) const { return indexOf(str, 0); } - void swapWith(String& other) - { - m_buffer.swapWith(other.m_buffer); - } + Index indexOf(const String& str) const { return indexOf(str.begin(), 0); } - Index indexOf(char ch, Index id) const - { - const Index length = getLength(); - SLANG_ASSERT(id >= 0 && id <= length); + void swapWith(String& other) { m_buffer.swapWith(other.m_buffer); } - if (!m_buffer) - return Index(-1); + Index indexOf(char ch, Index id) const + { + const Index length = getLength(); + SLANG_ASSERT(id >= 0 && id <= length); - const char* data = getData(); - for (Index i = id; i < length; i++) - if (data[i] == ch) - return i; + if (!m_buffer) return Index(-1); - } - - Index indexOf(char ch) const - { - return indexOf(ch, 0); - } - Index lastIndexOf(char ch) const - { - const Index length = getLength(); - const char* data = getData(); + const char* data = getData(); + for (Index i = id; i < length; i++) + if (data[i] == ch) + return i; + return Index(-1); + } - for (Index i = length - 1; i >= 0; --i) - if (data[i] == ch) - return i; - return Index(-1); - } + Index indexOf(char ch) const { return indexOf(ch, 0); } - bool startsWith(const char* str) const - { - if (!m_buffer) - return false; - Index strLen = Index(::strlen(str)); - if (strLen > getLength()) - return false; + Index lastIndexOf(char ch) const + { + const Index length = getLength(); + const char* data = getData(); - const char*const data = getData(); + for (Index i = length - 1; i >= 0; --i) + if (data[i] == ch) + return i; + return Index(-1); + } - for (Index i = 0; i < strLen; i++) - if (str[i] != data[i]) - return false; - return true; - } + bool startsWith(const char* str) const + { + if (!m_buffer) + return false; + Index strLen = Index(::strlen(str)); + if (strLen > getLength()) + return false; - bool startsWith(const String& str) const - { - return startsWith(str.begin()); - } + const char* const data = getData(); - bool endsWith(char const* str) const // String str - { - if (!m_buffer) + for (Index i = 0; i < strLen; i++) + if (str[i] != data[i]) return false; + return true; + } + + bool startsWith(const String& str) const { return startsWith(str.begin()); } + + bool endsWith(char const* str) const // String str + { + if (!m_buffer) + return false; - const Index strLen = Index(::strlen(str)); - const Index len = getLength(); + const Index strLen = Index(::strlen(str)); + const Index len = getLength(); - if (strLen > len) + if (strLen > len) + return false; + const char* data = getData(); + for (Index i = strLen; i > 0; i--) + if (str[i - 1] != data[len - strLen + i - 1]) return false; - const char* data = getData(); - for (Index i = strLen; i > 0; i--) - if (str[i-1] != data[len - strLen + i-1]) - return false; - return true; - } + return true; + } - bool endsWith(const String& str) const - { - return endsWith(str.begin()); - } + bool endsWith(const String& str) const { return endsWith(str.begin()); } - bool contains(const char* str) const // String str - { - return m_buffer && indexOf(str) != Index(-1); - } + bool contains(const char* str) const // String str + { + return m_buffer && indexOf(str) != Index(-1); + } - bool contains(const String& str) const - { - return contains(str.begin()); - } + bool contains(const String& str) const { return contains(str.begin()); } - static constexpr bool kHasUniformHash = true; - HashCode64 getHashCode() const - { - return Slang::getHashCode(StringRepresentation::asSlice(m_buffer)); - } + static constexpr bool kHasUniformHash = true; + HashCode64 getHashCode() const + { + return Slang::getHashCode(StringRepresentation::asSlice(m_buffer)); + } - UnownedStringSlice getUnownedSlice() const - { - return StringRepresentation::asSlice(m_buffer); - } - }; + UnownedStringSlice getUnownedSlice() const { return StringRepresentation::asSlice(m_buffer); } +}; - class SLANG_RT_API StringBuilder : public String +class SLANG_RT_API StringBuilder : public String +{ +private: + enum { - private: - enum { InitialSize = 1024 }; - public: - typedef String Super; - using Super::append; + InitialSize = 1024 + }; - explicit StringBuilder(UInt bufferSize = InitialSize) - { - ensureUniqueStorageWithCapacity(bufferSize); - } +public: + typedef String Super; + using Super::append; - void ensureCapacity(UInt size) - { - ensureUniqueStorageWithCapacity(size); - } - StringBuilder& operator << (char ch) - { - appendChar(ch); - return *this; - } - StringBuilder& operator << (Int32 val) - { - append(val); - return *this; - } - StringBuilder& operator << (UInt32 val) - { - append(val); - return *this; - } - StringBuilder& operator << (Int64 val) - { - append(val); - return *this; - } - StringBuilder& operator << (UInt64 val) - { - append(val); - return *this; - } - StringBuilder& operator << (float val) - { - append(val); - return *this; - } - StringBuilder& operator << (double val) - { - append(val); - return *this; - } - StringBuilder& operator << (const char* str) - { - append(str, strlen(str)); - return *this; - } - StringBuilder& operator << (const String& str) - { - append(str); - return *this; - } - StringBuilder& operator << (UnownedStringSlice const& str) - { - append(str); - return *this; - } - StringBuilder& operator << (const _EndLine) - { - appendChar('\n'); - return *this; - } + explicit StringBuilder(UInt bufferSize = InitialSize) + { + ensureUniqueStorageWithCapacity(bufferSize); + } - String toString() - { - return *this; - } + void ensureCapacity(UInt size) { ensureUniqueStorageWithCapacity(size); } + StringBuilder& operator<<(char ch) + { + appendChar(ch); + return *this; + } + StringBuilder& operator<<(Int32 val) + { + append(val); + return *this; + } + StringBuilder& operator<<(UInt32 val) + { + append(val); + return *this; + } + StringBuilder& operator<<(Int64 val) + { + append(val); + return *this; + } + StringBuilder& operator<<(UInt64 val) + { + append(val); + return *this; + } + StringBuilder& operator<<(float val) + { + append(val); + return *this; + } + StringBuilder& operator<<(double val) + { + append(val); + return *this; + } + StringBuilder& operator<<(const char* str) + { + append(str, strlen(str)); + return *this; + } + StringBuilder& operator<<(const String& str) + { + append(str); + return *this; + } + StringBuilder& operator<<(UnownedStringSlice const& str) + { + append(str); + return *this; + } + StringBuilder& operator<<(const _EndLine) + { + appendChar('\n'); + return *this; + } - String produceString() - { - return *this; - } + String toString() { return *this; } + + String produceString() { return *this; } #if 0 void Remove(int id, int len) @@ -1000,20 +883,17 @@ namespace Slang length -= actualDelLength; } #endif - friend std::ostream& operator<< (std::ostream& stream, const String& s); + friend std::ostream& operator<<(std::ostream& stream, const String& s); - void clear() - { - m_buffer.setNull(); - } - }; + void clear() { m_buffer.setNull(); } +}; - int stringToInt(const String& str, int radix = 10); - unsigned int stringToUInt(const String& str, int radix = 10); - double stringToDouble(const String& str); - float stringToFloat(const String& str); -} +int stringToInt(const String& str, int radix = 10); +unsigned int stringToUInt(const String& str, int radix = 10); +double stringToDouble(const String& str); +float stringToFloat(const String& str); +} // namespace Slang -std::ostream& operator<< (std::ostream& stream, const Slang::String& s); +std::ostream& operator<<(std::ostream& stream, const Slang::String& s); #endif |
