#pragma once #include #include namespace Juliet { #define ConstString(str) { const_cast((str)), sizeof(str) - 1 } #define CStr(str) ((str).Data) #define InplaceString(name, size) \ char name##_[size]; \ MemSet(name##_, 0, sizeof(uint32)); \ String name = { name##_, 0 } // Everything is Little Endian enum class StringEncoding : uint8 { Unknown = 0, ASCII, LATIN1, UTF8, UTF16, UTF32, UCS2, UCS4, }; // Represents a UTF-8 String. // Not null terminated. struct String { char* Data; size_t Size; }; struct StringBuffer : String { size_t Capacity; }; constexpr uint32 kInvalidUTF8 = 0xFFFD; inline size_t StringLength(String str) { return str.Size; } inline size_t StringLength(const char* str) { size_t length = 0; if (str) { while (char ch = *str) { if ((ch & 0xC0) != 0x80) { ++length; } ++str; } } return length; } inline bool IsValid(String str) { return str.Size > 0 && str.Data != nullptr && *str.Data; } inline String WrapString(const char* str) { String result = {}; result.Data = const_cast(str); result.Size = StringLength(str); return result; } inline String FindChar(String str, char c) { String result = str; while (result.Size) { if (*result.Data != c) { ++result.Data; --result.Size; } else { return result; } } return {}; } inline bool ContainsChar(String str, char c) { return IsValid(FindChar(str, c)); } // Return: // - < 0 if str1 < str2 // - = 0 : Both strings are equals // - > 0 if str1 > str2 inline int32 StringCompare(String str1, String str2) { size_t len1 = StringLength(str1); size_t len2 = StringLength(str2); size_t minLen = Min(len1, len2); int32 result = MemCompare(CStr(str1), CStr(str2), minLen); if (result == 0) { if (len1 > len2) { return 1; } if (len1 < len2) { return -1; } return 0; } return result; } JULIET_API uint32 StepUTF8(String& inStr); JULIET_API String FindString(String strLeft, String strRight); // Case insensitive compare. Supports ASCII only // TODO: Support UNICODE extern JULIET_API int8 StringCompareCaseInsensitive(String str1, String str2); // Do not allocate anything, you must allocate your out buffer yourself // TODO: Version taking arena that can allocate // Do not take String type because we dont know the string encoding we are going from/to // src and dst will be casted based on the encoding. // size will correspond to the number of characters // Will convert \0 character if present. extern JULIET_API bool ConvertString(StringEncoding from, StringEncoding to, String src, StringBuffer& dst, bool nullTerminate); extern JULIET_API bool ConvertString(String from, String to, String src, StringBuffer& dst, bool nullTerminate); } // namespace Juliet #ifdef UNIT_TEST namespace Juliet::UnitTest { inline void TestFindChar() { String s1 = ConstString(""); String s2 = ConstString("abcdefabcdef"); String s3 = ConstString("11111111111111111111"); Assert(FindChar(s1, 'x').Data == nullptr); Assert(FindChar(s2, 'y').Data == nullptr); Assert(FindChar(s2, 'a').Data - s2.Data == 0); Assert(FindChar(s2, 'd').Data - s2.Data == 3); Assert(FindChar(s2, 'f').Data - s2.Data == 5); Assert(FindChar(s3, '1').Data - s3.Data == 0); } } // namespace Juliet::UnitTest #endif