Raw File
slang-string.h
#ifndef FUNDAMENTAL_LIB_STRING_H
#define FUNDAMENTAL_LIB_STRING_H

#include <string.h>
#include <cstdlib>
#include <stdio.h>

#include "smart-pointer.h"
#include "common.h"
#include "hash.h"
#include "secure-crt.h"

#include <new>

namespace Slang
{
	class _EndLine
	{};
	extern _EndLine EndLine;

	// in-place reversion, works only for ascii string
	inline void ReverseInternalAscii(char * buffer, int length)
	{
		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;
		}
	}
	template<typename IntType>
	inline int IntToAscii(char * buffer, IntType val, int radix)
	{
		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);
		if (sign < 0)
			buffer[i++] = '-';
		buffer[i] = '\0';
		return i;
	}

	inline bool IsUtf8LeadingByte(char ch)
	{
		return (((unsigned char)ch) & 0xC0) == 0xC0;
	}

	inline bool IsUtf8ContinuationByte(char ch)
	{
		return (((unsigned char)ch) & 0xC0) == 0x80;
	}

    struct UnownedStringSlice
    {
    public:
        UnownedStringSlice()
            : beginData(nullptr)
            , endData(nullptr)
        {}

        explicit UnownedStringSlice(char const* a) :
            beginData(a),
            endData(a + strlen(a))
        {}
        UnownedStringSlice(char const* b, char const* e)
            : beginData(b)
            , endData(e)
        {}
        UnownedStringSlice(char const* b, size_t len)
            : beginData(b)
            , endData(b + len)
        {}

        char const* begin() const
        {
            return beginData;
        }

        char const* end() const
        {
            return endData;
        }

        UInt size() const
        {
            return endData - beginData;
        }

        int indexOf(char c) const
        {
            const int size = int(endData - beginData);
            for (int i = 0; i < size; ++i)
            {
                if (beginData[i] == c)
                {
                    return i;
                }
            }
            return -1;
        }

        const char& operator[](UInt i) const
        {
            assert(i < UInt(endData - beginData));
            return beginData[i];
        }

        bool operator==(UnownedStringSlice const& other) const
        {
            return size() == other.size()
                && memcmp(begin(), other.begin(), size()) == 0;
        }

        bool operator==(char const* str) const
        {
            return (*this) == UnownedStringSlice(str, str + strlen(str));
        }

        bool operator!=(UnownedStringSlice const& other) const
        {
            return !(*this == other);
        }

        bool startsWith(UnownedStringSlice const& other) const;
        bool startsWith(char const* str) const;

        bool endsWith(UnownedStringSlice const& other) const;
        bool endsWith(char const* str) const;

        int GetHashCode() const
        {
            return Slang::GetHashCode(beginData, size_t(endData - beginData)); 
        }

        template <size_t SIZE> 
        SLANG_FORCE_INLINE static UnownedStringSlice fromLiteral(const char (&in)[SIZE]) { return UnownedStringSlice(in, SIZE - 1); }

    private:
        char const* beginData;
        char const* endData;
    };

    // A `StringRepresentation` provides the backing storage for
    // all reference-counted string-related types.
    class StringRepresentation : public RefObject
    {
    public:
        UInt length;
        UInt capacity;

        SLANG_FORCE_INLINE UInt getLength() const
        {
            return length;
        }

        SLANG_FORCE_INLINE char* getData()
        {
            return (char*) (this + 1);
        }
        SLANG_FORCE_INLINE const char* getData() const
        {
            return (const char*)(this + 1);
        }

        static const char* getData(const StringRepresentation* stringRep)
        {
            return stringRep ? stringRep->getData() : "";
        }

        static UnownedStringSlice asSlice(const StringRepresentation* rep)
        {
            return rep ? UnownedStringSlice(rep->getData(), rep->getLength()) : UnownedStringSlice();
        }

        static bool equal(const StringRepresentation* a, const StringRepresentation* b)
        {
            return (a == b) || asSlice(a) == asSlice(b);
        }

        static StringRepresentation* createWithCapacityAndLength(UInt capacity, UInt 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;
        }

        static StringRepresentation* createWithCapacity(UInt capacity)
        {
            return createWithCapacityAndLength(capacity, 0);
        }

        static StringRepresentation* createWithLength(UInt length)
        {
            return createWithCapacityAndLength(length, length);
        }

        StringRepresentation* cloneWithCapacity(UInt newCapacity)
        {
            StringRepresentation* newObj = createWithCapacityAndLength(newCapacity, length);
            memcpy(getData(), newObj->getData(), length + 1);
            return newObj;
        }

        StringRepresentation* clone()
        {
            return cloneWithCapacity(length);
        }

        StringRepresentation* ensureCapacity(UInt required)
        {
            if (capacity >= required) return this;

            UInt newCapacity = capacity;
            if (!newCapacity) newCapacity = 16; // TODO: figure out good value for minimum capacity

            while (newCapacity < required)
            {
                newCapacity = 2 * newCapacity;
            }

            return cloneWithCapacity(newCapacity);
        }
    };

    class String;



    struct UnownedTerminatedStringSlice : public UnownedStringSlice
    {
    public:
        UnownedTerminatedStringSlice(char const* b)
            : UnownedStringSlice(b, b + strlen(b))
        {}
    };

    struct StringSlice
    {
    public:
        StringSlice();

        StringSlice(String const& str);

        StringSlice(String const& str, UInt beginIndex, UInt endIndex);

        UInt getLength() const
        {
            return endIndex - beginIndex;
        }

        char const* begin() const
        {
            return representation ? representation->getData() + beginIndex : "";
        }

        char const* end() const
        {
            return begin() + getLength();
        }

    private:
        RefPtr<StringRepresentation> representation;
        UInt beginIndex;
        UInt endIndex;

        friend class String;

        StringSlice(RefPtr<StringRepresentation> const& representation, UInt beginIndex, UInt endIndex)
            : representation(representation)
            , beginIndex(beginIndex)
            , endIndex(endIndex)
        {}
    };

    /// String as expected by underlying platform APIs
    class OSString
    {
    public:
        OSString();
        OSString(wchar_t* begin, wchar_t* end);
        ~OSString();

        operator wchar_t const*() const
        {
            return begin();
        }

        wchar_t const* begin() const;
        wchar_t const* end() const;

    private:
        wchar_t* beginData;
        wchar_t* endData;
    };

	/*!
	@brief Represents a UTF-8 encoded string.
	*/

	class String
	{
        friend struct StringSlice;
		friend class StringBuilder;
	private:


        char* getData() const
        {
            return buffer ? buffer->getData() : (char*)"";
        }

        UInt getLength() const
        {
            return buffer ? buffer->getLength() : 0;
        }

        void ensureUniqueStorageWithCapacity(UInt capacity);
     
        RefPtr<StringRepresentation> buffer;

    public:

        explicit String(StringRepresentation* buffer)
            : buffer(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(unsigned int codePoint);
		String()
		{
		}

            /// Returns a buffer which can hold at least count chars
        char* prepareForAppend(UInt count);
            /// Append data written to buffer output via 'prepareForAppend' directly written 'inplace'
        void appendInPlace(const char* chars, UInt count);

        SLANG_FORCE_INLINE StringRepresentation* getStringRepresentation() const { return buffer; }

		const char * begin() const
		{
			return getData();
		}
		const char * end() const
		{
			return getData() + getLength();
		}

        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(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);

		String(int32_t val, int radix = 10)
		{
            append(val, radix);
		}
		String(uint32_t val, int radix = 10)
		{
            append(val, radix);
		}
		String(int64_t val, int radix = 10)
		{
            append(val, radix);
		}
		String(uint64_t val, int radix = 10)
		{
            append(val, radix);
		}
		String(float val, const char * format = "%g")
		{
            append(val, format);
		}
		String(double val, const char * format = "%g")
		{
            append(val, format);
		}
		String(const char * str)
		{
            append(str);
#if 0
			if (str)
			{
                buffer = StringRepresentation::createWithLength(strlen(str));
				memcpy(buffer.Ptr(), str, getLength() + 1);
			}
#endif
		}
        String(const char* textBegin, char const* textEnd)
		{
            append(textBegin, textEnd);
#if 0
			if (textBegin != textEnd)
			{
                buffer = StringRepresentation::createWithLength(textEnd - textBegin);
				memcpy(buffer.Ptr(), textBegin, getLength());
                buffer->getData()[getLength()] = 0;
			}
#endif
		}
		String(char chr)
		{
            append(chr);
#if 0
			if (chr)
			{
                buffer = StringRepresentation::createWithLength(1);
                buffer->getData()[0] = chr;
                buffer->getData()[1] = 0;
			}
#endif
		}
		String(String const& str)
		{
            buffer = str.buffer;
#if 0
			this->operator=(str);
#endif
		}
		String(String&& other)
		{
            buffer = _Move(other.buffer);
		}

        String(StringSlice const& slice)
        {
            append(slice);
        }

        String(UnownedStringSlice const& slice)
        {
            append(slice);
        }

		~String()
		{
            buffer = 0;
		}

		String & operator=(const String & str)
		{
            buffer = str.buffer;
			return *this;
		}
		String & operator=(String&& other)
		{
            buffer = _Move(other.buffer);
            return *this;
		}
		char operator[](UInt id) const
		{
#if _DEBUG
			if (id < 0 || id >= getLength())
				throw "Operator[]: index out of range.";
#endif
			return begin()[id];
		}

		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 (!buffer)
				return StringSlice();
			UInt startIndex = 0;
			while (startIndex < getLength() &&
				(getData()[startIndex] == ' ' || getData()[startIndex] == '\t' || getData()[startIndex] == '\r' || getData()[startIndex] == '\n'))
				startIndex++;
            return StringSlice(buffer, startIndex, getLength());
		}

		StringSlice TrimEnd() const
		{
			if (!buffer)
				return StringSlice();

			UInt endIndex = getLength();
			while (endIndex > 0 &&
				(getData()[endIndex-1] == ' ' || getData()[endIndex-1] == '\t' || getData()[endIndex-1] == '\r' || getData()[endIndex-1] == '\n'))
				endIndex--;

            return StringSlice(buffer, 0, endIndex);
		}

		StringSlice Trim() const
		{
			if (!buffer)
				return StringSlice();

			UInt startIndex = 0;
			while (startIndex < getLength() &&
				(getData()[startIndex] == ' ' || getData()[startIndex] == '\t'))
				startIndex++;
			UInt endIndex = getLength();
			while (endIndex > startIndex &&
				(getData()[endIndex-1] == ' ' || getData()[endIndex-1] == '\t'))
				endIndex--;

            return StringSlice(buffer, startIndex, endIndex);
		}

		StringSlice SubString(UInt id, UInt len) const
		{
			if (len == 0)
				return StringSlice();

            if (id + len > getLength())
				len = getLength() - id;
#if _DEBUG
			if (id < 0 || id >= getLength() || (id + len) > getLength())
				throw "SubString: index out of range.";
			if (len < 0)
				throw "SubString: length less than zero.";
#endif
            return StringSlice(buffer, id, id + len);
		}

		char const* Buffer() const
		{
            return getData();
		}

        OSString ToWString(UInt* len = 0) const;

		bool Equals(const String & str, bool caseSensitive = true)
		{
			if (caseSensitive)
				return (strcmp(begin(), str.begin()) == 0);
			else
			{
#ifdef _MSC_VER
				return (_stricmp(begin(), str.begin()) == 0);
#else
				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);
		}

		String ToUpper() const
		{
            String result;
            for (auto c : *this)
            {
                char d = (c >= 'a' && c <= 'z') ? (c - ('a' - 'A')) : c;
                result.append(d);
            }
            return result;
		}

		String ToLower() const
		{
            String result;
            for (auto c : *this)
            {
                char d = (c >= 'A' && c <= 'Z') ? (c - ('A' - 'a')) : c;
                result.append(d);
            }
            return result;
		}

		UInt Length() const
		{
			return getLength();
		}

		UInt IndexOf(const char * str, UInt id) const // String str
		{
			if (id >= getLength())
				return UInt(-1);
			auto findRs = strstr(begin() + id, str);
			UInt res = findRs ? findRs - begin() : -1;
			return res;
		}

		UInt IndexOf(const String & str, UInt id) const
		{
			return IndexOf(str.begin(), id);
		}

		UInt IndexOf(const char * str) const
		{
			return IndexOf(str, 0);
		}

		UInt IndexOf(const String & str) const
		{
			return IndexOf(str.begin(), 0);
		}

		UInt IndexOf(char ch, UInt id) const
		{
#if _DEBUG
			if (id < 0 || id >= getLength())
				throw "SubString: index out of range.";
#endif
			if (!buffer)
				return UInt(-1);
			for (UInt i = id; i < getLength(); i++)
				if (getData()[i] == ch)
					return i;
			return UInt(-1);
		}

		UInt IndexOf(char ch) const
		{
			return IndexOf(ch, 0);
		}

		UInt LastIndexOf(char ch) const
		{
			for (UInt i = getLength(); i > 0; i--)
				if (getData()[i-1] == ch)
					return i-1;
			return UInt(-1);
		}

		bool StartsWith(const char * str) const // String str
		{
			if (!buffer)
				return false;
			UInt strLen = strlen(str);
			if (strLen > getLength())
				return false;
			for (UInt i = 0; i < strLen; i++)
				if (str[i] != getData()[i])
					return false;
			return true;
		}

		bool StartsWith(const String & str) const
		{
			return StartsWith(str.begin());
		}

		bool EndsWith(char const * str)  const // String str
		{
			if (!buffer)
				return false;
			UInt strLen = strlen(str);
			if (strLen > getLength())
				return false;
			for (UInt i = strLen; i > 0; i--)
				if (str[i-1] != getData()[getLength() - strLen + i-1])
					return false;
			return true;
		}

		bool EndsWith(const String & str) const
		{
			return EndsWith(str.begin());
		}

		bool Contains(const char * str) const // String str
		{
			if (!buffer)
				return false;
			return (IndexOf(str) != UInt(-1)) ? true : false;
		}

		bool Contains(const String & str) const
		{
			return Contains(str.begin());
		}

		int GetHashCode() const
		{
			return Slang::GetHashCode((const char*)begin());
		}

        UnownedStringSlice getUnownedSlice() const
        {
            return StringRepresentation::asSlice(buffer);
        }
	};

	class StringBuilder : public String
	{
	private:
        enum { InitialSize = 1024 };
	public:
		explicit StringBuilder(UInt bufferSize = InitialSize)
		{
            ensureUniqueStorageWithCapacity(bufferSize);
		}
		void EnsureCapacity(UInt size)
		{
            ensureUniqueStorageWithCapacity(size);
		}
		StringBuilder & operator << (char ch)
		{
			Append(&ch, 1);
			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)
		{
			Append('\n');
			return *this;
		}
		void Append(char ch)
		{
			Append(&ch, 1);
		}
		void Append(float val)
		{
			char buf[128];
			sprintf_s(buf, 128, "%g", val);
			int len = (int)strnlen_s(buf, 128);
			Append(buf, len);
		}
		void Append(double val)
		{
			char buf[128];
			sprintf_s(buf, 128, "%g", val);
			int len = (int)strnlen_s(buf, 128);
			Append(buf, len);
		}
		void Append(Int32 value, int radix = 10)
		{
			char vBuffer[33];
			int len = IntToAscii(vBuffer, value, radix);
			ReverseInternalAscii(vBuffer, len);
			Append(vBuffer);
		}
		void Append(UInt32 value, int radix = 10)
		{
			char vBuffer[33];
			int len = IntToAscii(vBuffer, value, radix);
			ReverseInternalAscii(vBuffer, len);
			Append(vBuffer);
		}
		void Append(Int64 value, int radix = 10)
		{
			char vBuffer[65];
			int len = IntToAscii(vBuffer, value, radix);
			ReverseInternalAscii(vBuffer, len);
			Append(vBuffer);
		}
		void Append(UInt64 value, int radix = 10)
		{
			char vBuffer[65];
			int len = IntToAscii(vBuffer, value, radix);
			ReverseInternalAscii(vBuffer, len);
			Append(vBuffer);
		}
		void Append(const String & str)
		{
			Append(str.Buffer(), str.Length());
		}
		void Append(const char * str)
		{
			Append(str, strlen(str));
		}
		void Append(const char * str, UInt strLen)
		{
            append(str, str + strLen);
		}

#if 0
		int Capacity()
		{
			return bufferSize;
		}

		char * Buffer()
		{
			return buffer;
		}

		int Length()
		{
			return length;
		}
#endif

		String ToString()
		{
            return *this;
		}

		String ProduceString()
		{
            return *this;
		}

#if 0
		String GetSubString(int start, int count)
		{
			String rs;
			rs.buffer = new char[count + 1];
			rs.length = count;
			strncpy_s(rs.buffer.Ptr(), count + 1, buffer + start, count);
			rs.buffer[count] = 0;
			return rs;
		}
#endif

#if 0
		void Remove(int id, int len)
		{
#if _DEBUG
			if (id >= length || id < 0)
				throw "Remove: Index out of range.";
			if (len < 0)
				throw "Remove: remove length smaller than zero.";
#endif
			int actualDelLength = ((id + len) >= length) ? (length - id) : len;
			for (int i = id + actualDelLength; i <= length; i++)
				buffer[i - actualDelLength] = buffer[i];
			length -= actualDelLength;
		}
#endif

		void Clear()
		{
            buffer = 0;
		}
	};

	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);
}

#endif
back to top