Index: include/llvm/ADT/StringLiteral.h =================================================================== --- /dev/null +++ include/llvm/ADT/StringLiteral.h @@ -0,0 +1,247 @@ +//===--- StringLiteral.h - Compile-time String Implementation ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_ADT_STRINGLITERAL_H +#define LLVM_ADT_STRINGLITERAL_H + +#include "llvm/ADT/StringRef.h" + +#include +#include + +namespace llvm { + +class StringLiteral { +private: + const char *const p_; + const std::size_t sz_; + +public: + template + /* implicit */ constexpr StringLiteral(const char (&a)[N]) + : p_(a), sz_(N - 1) {} + constexpr StringLiteral(const char *a, std::size_t N) : p_(a), sz_(N) {} + + static constexpr bool is_upper(char C) { return (C >= 'A' && C <= 'Z'); } + + static constexpr bool is_lower(char C) { return (C >= 'a' && C <= 'z'); } + + static constexpr char tolower(char C) { + return is_upper(C) ? (C - 'A' + 'a') : C; + } + + static constexpr char toupper(char C) { + return is_lower(C) ? (C - 'a' + 'A') : C; + } + + static constexpr bool is_digit(char c) { return (c >= '0' && c <= '9'); } + + static constexpr bool is_alpha(char c) { return is_upper(c) || is_lower(c); } + + static constexpr bool is_alphanumeric(char c) { + return is_alpha(c) || is_digit(c); + } + + static constexpr bool is_hexdigit(char c) { + return is_digit(c) || (tolower(c) >= 'a' && tolower(c) <= 'f'); + } + + static constexpr uint8_t as_digit(char c) { + return is_digit(c) ? (c - '0') : throw "Character is not a digit!"; + } + + static constexpr uint8_t as_hexdigit(char c) { + return is_hexdigit(c) + ? (is_digit(c) ? as_digit(c) : (10 + tolower(c) - 'a')) + : throw "Character is not a hex digit!"; + } + + constexpr std::size_t size() const { return sz_; } + constexpr bool empty() const { return sz_ == 0; } + + constexpr char front() const { + return (!empty()) ? p_[0] + : throw "Cannot call front() on an empty StringLiteral"; + } + + constexpr char back() const { + return (!empty()) ? p_[sz_ - 1] + : throw "Cannot call back() on an empty StringLiteral"; + } + + constexpr char operator[](std::size_t n) const { + return (n >= sz_) ? throw "Index out of range for operator[]" : p_[n]; + } + + constexpr bool operator==(const StringLiteral &Other) const { + return (size() == Other.size()) && + (empty() || + (front() == Other.front() && (drop_front() == Other.drop_front()))); + } + + constexpr bool operator!=(const StringLiteral &Other) const { + return !(*this == Other); + } + + constexpr bool equals_lower(const StringLiteral &Other) const { + return (empty() && Other.empty()) || + ((size() == Other.size()) && + (tolower(front()) == tolower(Other.front())) && + drop_front().equals_lower(Other.drop_front())); + } + + constexpr StringLiteral drop_front(std::size_t count = 1) const { + return (size() >= count) + ? StringLiteral(p_ + count, sz_ - count) + : throw "drop_front() cannot drop that many elements!"; + } + + constexpr StringLiteral drop_back(std::size_t count = 1) const { + return (size() >= count) + ? StringLiteral(p_, sz_ - count) + : throw "drop_back() cannot drop that many elements!"; + } + + constexpr StringLiteral take_front(std::size_t count = 1) const { + return (count == sz_) ? *this : drop_back(size() - count); + } + + constexpr StringLiteral take_back(std::size_t count = 1) const { + return (count == sz_) ? *this : drop_front(size() - count); + } + + constexpr bool startswith(const StringLiteral &Other) const { + return (size() >= Other.size()) && (take_front(Other.size()) == Other); + } + + constexpr bool endswith(const StringLiteral &Other) const { + return (size() >= Other.size()) && (take_back(Other.size()) == Other); + } + + constexpr bool startswith_lower(const StringLiteral &Other) const { + return (size() >= Other.size()) && + take_front(Other.size()).equals_lower(Other); + } + + constexpr bool endswith_lower(const StringLiteral &Other) const { + return (size() >= Other.size()) && + take_back(Other.size()).equals_lower(Other); + } + + constexpr bool contains(char c) const { + return !empty() && (front() == c || drop_front().contains(c)); + } + + constexpr bool contains(const StringLiteral &Other) const { + return startswith(Other) || (!empty() && drop_front().contains(Other)); + } + + constexpr bool contains_lower(char c) const { + return contains(toupper(c)) || contains(tolower(c)); + } + + constexpr bool contains_lower(const StringLiteral &Other) const { + return startswith_lower(Other) || + (!empty() && drop_front().contains_lower(Other)); + } + + constexpr std::size_t find_first_of(char c, std::size_t pos = 0) const { + return (pos < size() && drop_front(pos).contains(c)) + ? (drop_front(pos).front() == c + ? pos + : (pos + 1 + drop_front(pos + 1).find_first_of(c))) + : (std::size_t)-1; + } + + constexpr std::size_t find_first_not_of(char c, std::size_t pos = 0) const { + return (pos < size()) + ? ((!drop_front(pos).contains(c) + ? pos + : (drop_front(pos).front() != c + ? pos + : (pos + 1 + + drop_front(pos + 1).find_first_not_of(c))))) + : (std::size_t)-1; + } + + constexpr std::size_t find(const StringLiteral &Other) const { + return !contains(Other) + ? -1 + : (startswith(Other) ? 0 : 1 + drop_front().find(Other)); + } + + constexpr StringLiteral drop_until(char c) const { + return (empty() || !contains(c)) ? "" : drop_front(find_first_of(c)); + } + + constexpr StringLiteral drop_while(char c) const { + return (empty() || front() != c) ? *this : drop_front().drop_while(c); + } + + constexpr StringLiteral take_until(char c) const { + return (empty() || !contains(c)) ? *this : take_front(find_first_of(c)); + } + + constexpr StringLiteral take_while(char c) const { + return (empty() || front() != c) ? "" : take_front(find_first_not_of(c)); + } + + constexpr StringLiteral shrink(std::size_t amount = 1) const { + return (size() >= 2 * amount) + ? drop_front(amount).drop_back(amount) + : throw "Cannot shrink by the requested amount!"; + } + + constexpr StringLiteral substr(std::size_t pos, + std::size_t Length = -1) const { + return (Length == -1) ? drop_front(pos) + : drop_front(pos).take_front(Length); + } + + constexpr std::pair split(char C) const { + return !contains(C) + ? std::pair(*this, "") + : std::make_pair(take_until(C), drop_until(C).drop_front()); + } + + constexpr bool is_number() const { + return !empty() && is_digit(p_[0]) && + (sz_ == 1 || drop_front().is_number()); + } + + constexpr bool is_hex_number() const { + return !empty() && is_hexdigit(p_[0]) && + (sz_ == 1 || drop_front().is_hex_number()); + } + + constexpr std::size_t getAsUnsignedDecimal() const { + return is_number() + ? ((size() == 1) ? as_digit(front()) + : (as_digit(back()) + + 10 * drop_back().getAsUnsignedDecimal())) + : throw "Not a number!"; + } + + constexpr std::size_t getAsUnsignedHex() const { + return is_hex_number() + ? ((size() == 1) ? as_hexdigit(front()) + : (as_hexdigit(back()) + + 16 * drop_back().getAsUnsignedHex())) + : throw "Not a hex number!"; + } + + StringRef ref() const { return StringRef(p_, sz_); } + operator StringRef() const { return ref(); } + + std::string str() const { return ref().str(); } + operator std::string() const { return str(); } +}; +} + +#endif Index: unittests/ADT/CMakeLists.txt =================================================================== --- unittests/ADT/CMakeLists.txt +++ unittests/ADT/CMakeLists.txt @@ -53,6 +53,7 @@ SparseBitVectorTest.cpp SparseMultiSetTest.cpp SparseSetTest.cpp + StringLiteralTest.cpp StringMapTest.cpp StringRefTest.cpp TinyPtrVectorTest.cpp Index: unittests/ADT/StringLiteralTest.cpp =================================================================== --- /dev/null +++ unittests/ADT/StringLiteralTest.cpp @@ -0,0 +1,671 @@ +//===- llvm/unittest/ADT/StringLiteralTests.cpp - StringLiteral unit tests ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/StringLiteral.h" +#include "gtest/gtest.h" +using namespace llvm; + +TEST(StringLiteralTest, CharToUpperAndLower) { + static_assert(StringLiteral::toupper('A') == 'A', "toupper('A') != 'A'"); + static_assert(StringLiteral::toupper('a') == 'A', "toupper('a') != 'A'"); + static_assert(StringLiteral::toupper('Z') == 'Z', "toupper('Z') != 'Z'"); + static_assert(StringLiteral::toupper('z') == 'Z', "toupper('z') != 'Z'"); + static_assert(StringLiteral::toupper('.') == '.', "toupper('.') != '.'"); + + static_assert(StringLiteral::tolower('A') == 'a', "tolower('A') != 'a'"); + static_assert(StringLiteral::tolower('a') == 'a', "tolower('a') != 'a'"); + static_assert(StringLiteral::tolower('Z') == 'z', "tolower('Z') != 'z'"); + static_assert(StringLiteral::tolower('z') == 'z', "tolower('z') != 'z'"); + static_assert(StringLiteral::tolower('.') == '.', "tolower('.') != '.'"); +} + +TEST(StringLiteralTest, IsUpperLower) { + static_assert(StringLiteral::is_upper('A'), "A is not uppercase!"); + static_assert(StringLiteral::is_upper('Z'), "Z is not uppercase!"); + static_assert(!StringLiteral::is_upper('a'), "a is uppercase!"); + static_assert(!StringLiteral::is_upper('z'), "z is uppercase!"); + + static_assert(StringLiteral::is_lower('a'), "a is not uppercase!"); + static_assert(StringLiteral::is_lower('z'), "z is not uppercase!"); + static_assert(!StringLiteral::is_lower('A'), "A is uppercase!"); + static_assert(!StringLiteral::is_lower('Z'), "Z is uppercase!"); + + static_assert(!StringLiteral::is_upper('.'), ". is uppercase!"); + static_assert(!StringLiteral::is_lower('.'), ". is lowercase!"); +} + +TEST(StringLiteralTest, IsDigit) { + static_assert(StringLiteral::is_digit('0'), "0 is not a digit!"); + static_assert(StringLiteral::is_digit('1'), "1 is not a digit!"); + static_assert(StringLiteral::is_digit('2'), "2 is not a digit!"); + static_assert(StringLiteral::is_digit('3'), "3 is not a digit!"); + static_assert(StringLiteral::is_digit('4'), "4 is not a digit!"); + static_assert(StringLiteral::is_digit('5'), "5 is not a digit!"); + static_assert(StringLiteral::is_digit('6'), "6 is not a digit!"); + static_assert(StringLiteral::is_digit('7'), "7 is not a digit!"); + static_assert(StringLiteral::is_digit('8'), "8 is not a digit!"); + static_assert(StringLiteral::is_digit('9'), "9 is not a digit!"); + + static_assert(!StringLiteral::is_digit('a'), "a is a digit!"); +} + +TEST(StringLiteralTest, IsAlpha) { + static_assert(StringLiteral::is_alpha('a'), "a is not alphabetic!"); + static_assert(StringLiteral::is_alpha('z'), "z is not alphabetic!"); + static_assert(StringLiteral::is_alpha('A'), "A is not alphabetic!"); + static_assert(StringLiteral::is_alpha('Z'), "Z is not alphabetic!"); + + static_assert(!StringLiteral::is_alpha('0'), "0 is alphabetic!"); + static_assert(!StringLiteral::is_alpha('.'), ". is alphabetic!"); +} + +TEST(StringLiteralTest, IsAlphaNumeric) { + static_assert(StringLiteral::is_alphanumeric('a'), "a is not alphanumeric!"); + static_assert(StringLiteral::is_alphanumeric('z'), "z is not alphanumeric!"); + static_assert(StringLiteral::is_alphanumeric('A'), "A is not alphanumeric!"); + static_assert(StringLiteral::is_alphanumeric('Z'), "Z is not alphanumeric!"); + static_assert(StringLiteral::is_alphanumeric('0'), "0 is not alphanumeric!"); + static_assert(StringLiteral::is_alphanumeric('9'), "9 is not alphanumeric!"); + + static_assert(!StringLiteral::is_alphanumeric('.'), ". is alphanumeric!"); +} + +TEST(StringLiteralTest, IsHexDigit) { + static_assert(StringLiteral::is_hexdigit('0'), "0 is not a hex digit!"); + static_assert(StringLiteral::is_hexdigit('1'), "1 is not a hex digit!"); + static_assert(StringLiteral::is_hexdigit('2'), "2 is not a hex digit!"); + static_assert(StringLiteral::is_hexdigit('3'), "3 is not a hex digit!"); + static_assert(StringLiteral::is_hexdigit('4'), "4 is not a hex digit!"); + static_assert(StringLiteral::is_hexdigit('5'), "5 is not a hex digit!"); + static_assert(StringLiteral::is_hexdigit('6'), "6 is not a hex digit!"); + static_assert(StringLiteral::is_hexdigit('7'), "7 is not a hex digit!"); + static_assert(StringLiteral::is_hexdigit('8'), "8 is not a hex digit!"); + static_assert(StringLiteral::is_hexdigit('9'), "9 is not a hex digit!"); + static_assert(StringLiteral::is_hexdigit('A'), "A is not a hex digit!"); + static_assert(StringLiteral::is_hexdigit('B'), "B is not a hex digit!"); + static_assert(StringLiteral::is_hexdigit('C'), "C is not a hex digit!"); + static_assert(StringLiteral::is_hexdigit('D'), "D is not a hex digit!"); + static_assert(StringLiteral::is_hexdigit('E'), "E is not a hex digit!"); + static_assert(StringLiteral::is_hexdigit('F'), "F is not a hex digit!"); + static_assert(StringLiteral::is_hexdigit('a'), "a is not a hex digit!"); + static_assert(StringLiteral::is_hexdigit('b'), "b is not a hex digit!"); + static_assert(StringLiteral::is_hexdigit('c'), "c is not a hex digit!"); + static_assert(StringLiteral::is_hexdigit('d'), "d is not a hex digit!"); + static_assert(StringLiteral::is_hexdigit('e'), "e is not a hex digit!"); + static_assert(StringLiteral::is_hexdigit('f'), "f is not a hex digit!"); + + static_assert(!StringLiteral::is_hexdigit('g'), "g is a hex digit!"); + static_assert(!StringLiteral::is_hexdigit('G'), "G is a hex digit!"); + static_assert(!StringLiteral::is_hexdigit('.'), ". is a hex digit!"); +} + +TEST(StringLiteralTest, AsDigit) { + static_assert(0 == StringLiteral::as_digit('0'), "'0' is not equal to 0!"); + static_assert(1 == StringLiteral::as_digit('1'), "'1' is not equal to 1!"); + static_assert(2 == StringLiteral::as_digit('2'), "'2' is not equal to 2!"); + static_assert(3 == StringLiteral::as_digit('3'), "'3' is not equal to 3!"); + static_assert(4 == StringLiteral::as_digit('4'), "'4' is not equal to 4!"); + static_assert(5 == StringLiteral::as_digit('5'), "'5' is not equal to 5!"); + static_assert(6 == StringLiteral::as_digit('6'), "'6' is not equal to 6!"); + static_assert(7 == StringLiteral::as_digit('7'), "'7' is not equal to 7!"); + static_assert(8 == StringLiteral::as_digit('8'), "'8' is not equal to 8!"); + static_assert(9 == StringLiteral::as_digit('9'), "'9' is not equal to 9!"); +} + +TEST(StringLiteralTest, AsHexDigit) { + static_assert(0 == StringLiteral::as_hexdigit('0'), "0x0 is not equal to 0!"); + static_assert(1 == StringLiteral::as_hexdigit('1'), + "0x1 is not a hex digit!"); + static_assert(2 == StringLiteral::as_hexdigit('2'), + "0x2 is not a hex digit!"); + static_assert(3 == StringLiteral::as_hexdigit('3'), + "0x3 is not a hex digit!"); + static_assert(4 == StringLiteral::as_hexdigit('4'), + "0x4 is not a hex digit!"); + static_assert(5 == StringLiteral::as_hexdigit('5'), + "0x5 is not a hex digit!"); + static_assert(6 == StringLiteral::as_hexdigit('6'), + "0x6 is not a hex digit!"); + static_assert(7 == StringLiteral::as_hexdigit('7'), + "0x7 is not a hex digit!"); + static_assert(8 == StringLiteral::as_hexdigit('8'), + "0x8 is not a hex digit!"); + static_assert(9 == StringLiteral::as_hexdigit('9'), + "0x9 is not a hex digit!"); + static_assert(10 == StringLiteral::as_hexdigit('A'), + "0xA is not a hex digit!"); + static_assert(11 == StringLiteral::as_hexdigit('B'), + "0xB is not a hex digit!"); + static_assert(12 == StringLiteral::as_hexdigit('C'), + "0xC is not a hex digit!"); + static_assert(13 == StringLiteral::as_hexdigit('D'), + "0xD is not a hex digit!"); + static_assert(14 == StringLiteral::as_hexdigit('E'), + "0xE is not a hex digit!"); + static_assert(15 == StringLiteral::as_hexdigit('F'), + "0xF is not a hex digit!"); + static_assert(10 == StringLiteral::as_hexdigit('a'), + "0xa is not a hex digit!"); + static_assert(11 == StringLiteral::as_hexdigit('b'), + "0xb is not a hex digit!"); + static_assert(12 == StringLiteral::as_hexdigit('c'), + "0xc is not a hex digit!"); + static_assert(13 == StringLiteral::as_hexdigit('d'), + "0xd is not a hex digit!"); + static_assert(14 == StringLiteral::as_hexdigit('e'), + "0xe is not a hex digit!"); + static_assert(15 == StringLiteral::as_hexdigit('f'), + "0xf is not a hex digit!"); +} + +TEST(StringLiteralTest, Empty) { + constexpr StringLiteral EmptyString = ""; + constexpr StringLiteral OneChar = "A"; + constexpr StringLiteral ManyChars = "ABCDEFG"; + static_assert(EmptyString.empty(), "EmptyString should be empty!"); + static_assert(!OneChar.empty(), "EmptyString should be empty!"); + static_assert(!ManyChars.empty(), "EmptyString should be empty!"); +} + +TEST(StringLiteralTest, Size) { + constexpr StringLiteral EmptyString = ""; + constexpr StringLiteral OneChar = "A"; + constexpr StringLiteral ManyChars = "ABCDEFG"; + static_assert(EmptyString.size() == 0, "EmptyString.size() should be 0!"); + static_assert(OneChar.size() == 1, "OneChar.size() should be 1!"); + static_assert(ManyChars.size() == 7, "ManyChars.size() should be 7"); +} + +TEST(StringLiteralTest, FrontBack) { + constexpr StringLiteral EmptyString = ""; + constexpr StringLiteral OneChar = "A"; + constexpr StringLiteral ManyChars = "ABCDEFG"; + + // Fail to compile due to empty string. + // static_assert(EmptyString.front() == '\0', "Invalid result of front()!"); + // static_assert(EmptyString.back() == '\0', "Invalid result of back()!"); + static_assert(ManyChars.front() == 'A', "Invalid result of front()!"); + static_assert(ManyChars.back() == 'G', "Invalid result of back()!"); + static_assert(OneChar.front() == OneChar.back(), + "Invalid result of front()/back()!"); +} + +TEST(StringLiteralTest, OperatorBracket) { + constexpr StringLiteral EmptyString = ""; + constexpr StringLiteral ManyChars = "ABCDEFG"; + + // The following fails to compile because the index is out of bounds. + // static_assert(EmptyString[0] == '\0', "Invalid character returned from + // operator[]!"); + + static_assert(ManyChars[0] == 'A', + "Invalid character returned from operator[]!"); + static_assert(ManyChars[1] == 'B', + "Invalid character returned from operator[]!"); + static_assert(ManyChars[2] == 'C', + "Invalid character returned from operator[]!"); + static_assert(ManyChars[3] == 'D', + "Invalid character returned from operator[]!"); + static_assert(ManyChars[4] == 'E', + "Invalid character returned from operator[]!"); + static_assert(ManyChars[5] == 'F', + "Invalid character returned from operator[]!"); + static_assert(ManyChars[6] == 'G', + "Invalid character returned from operator[]!"); + + // The following fails to compile because the index is out of bounds. + // static_assert(ManyChars[7] == '\0', "Invalid character returned from + // operator[]!"); +} + +TEST(StringLiteralTest, OperatorEquals) { + constexpr StringLiteral Empty1 = ""; + constexpr StringLiteral Empty2 = ""; + constexpr StringLiteral Literal1 = "ABCDEFG"; + constexpr StringLiteral Literal2 = "ABCDEFG"; + + static_assert(Empty1 == Empty2, "Empty strings are not equal!"); + static_assert(Literal1 == Literal2, "Strings are not equal!"); + static_assert(Empty1 != Literal1, "Strings should be unequal!"); + + constexpr StringLiteral Literal3 = "ABCDE"; + constexpr StringLiteral Literal4 = Literal2.drop_back(2); + static_assert(Literal1 != Literal3, "Substrings are equal!"); + static_assert(Literal2 != Literal4, "Substrings are equal!"); + static_assert(Literal3 == Literal4, "Substrings are equal!"); + + constexpr StringLiteral Literal5 = "ABC"; + constexpr StringLiteral Literal6 = "ABCABC"; + static_assert(Literal6.drop_front(3) == Literal5, + "Substrings are not equal!"); +} + +TEST(StringLiteralTest, EqualsLower) { + constexpr StringLiteral Literal1 = "ABCDEFG"; + static_assert(Literal1.equals_lower("abcdefg"), + "Strings are not equal ignoring case!"); + static_assert(!Literal1.equals_lower("ABCDEFH"), + "Strings are equal ignoring case!"); + static_assert(!Literal1.equals_lower("ABCDEF"), + "Strings are equal ignoring case!"); + static_assert(!Literal1.equals_lower("ABCDEFGH"), + "Strings are equal ignoring case!"); +} + +TEST(StringLiteralTest, Drop) { + constexpr StringLiteral EmptyString = ""; + constexpr StringLiteral ManyChars = "ABCDEFG"; + + // Fails to compile due to dropping too many characters + // static_assert(EmptyString.drop_front() == "", "Dropping from an empty + // string!"); + // static_assert(ManyChars.drop_front(ManyChars.size()+1) == "", "Dropping too + // many characters!"); + + static_assert(EmptyString.drop_front(0) == EmptyString, + "Cannot drop 0 characters from an empty string!"); + static_assert(ManyChars.drop_front(ManyChars.size()) == "", + "Dropping entire string is not empty!"); + static_assert(ManyChars.drop_front().drop_front() == "CDEFG", + "Cannot chain calls to drop_front()!"); + + // Fails to compile due to dropping too many characters + // static_assert(EmptyString.drop_back() == "", "Dropping from an empty + // string!"); + // static_assert(ManyChars.drop_back(ManyChars.size()+1) == "", "Dropping too + // many characters!"); + + static_assert(EmptyString.drop_back(0) == EmptyString, + "Cannot drop 0 characters from an empty string!"); + static_assert(ManyChars.drop_back(ManyChars.size()) == "", + "Dropping entire string is not empty!"); + static_assert(ManyChars.drop_back().drop_back() == "ABCDE", + "Cannot chain calls to drop_back()!"); + static_assert(ManyChars.drop_back(2) == "ABCDE", + "Cannot chain calls to drop_back()!"); +} + +TEST(StringLiteralTest, Take) { + constexpr StringLiteral EmptyString = ""; + constexpr StringLiteral ManyChars = "ABCDEFG"; + + // Fails to compile due to dropping too many characters + // static_assert(EmptyString.take_front(1) == "", "Taking from an empty + // string!"); + // static_assert(ManyChars.take_front(ManyChars.size()+1) == "", "Dropping too + // many characters!"); + + static_assert(EmptyString.take_front(0) == EmptyString, + "Cannot take 0 characters from an empty string!"); + static_assert(ManyChars.take_front(ManyChars.size()) == ManyChars, + "Taking entire string is not the original string!"); + static_assert( + ManyChars.take_front() == "A", + "Taking one character from front is not equal to the first character!"); + static_assert(ManyChars.take_front(3) == "ABC", + "Taking multiple character from front is incorrect!"); + + // Fails to compile due to dropping too many characters + // static_assert(EmptyString.take_back() == "", "Taking from an empty + // string!"); + // static_assert(ManyChars.take_back(ManyChars.size()+1) == "", "Taking too + // many characters!"); + + static_assert(EmptyString.take_back(0) == EmptyString, + "Cannot take 0 characters from an empty string!"); + static_assert(ManyChars.take_back(ManyChars.size()) == ManyChars, + "Taking entire string is not the original string!"); + static_assert( + ManyChars.take_back() == "G", + "Taking one character from back is not equal to the last character!"); + static_assert(ManyChars.take_back(3) == "EFG", + "Taking multiple character from back is incorrect!"); +} + +TEST(StringLiteralTest, Contains) { + constexpr StringLiteral EmptyString = ""; + constexpr StringLiteral ManyChars = "ABCDEFG"; + + static_assert(!EmptyString.contains('A'), "Empty string contains something!"); + + static_assert(ManyChars.contains('A'), "ABCDEFG does not contain A"); + static_assert(ManyChars.contains('B'), "ABCDEFG does not contain B"); + static_assert(ManyChars.contains('C'), "ABCDEFG does not contain C"); + static_assert(ManyChars.contains('D'), "ABCDEFG does not contain D"); + static_assert(ManyChars.contains('E'), "ABCDEFG does not contain E"); + static_assert(ManyChars.contains('F'), "ABCDEFG does not contain F"); + static_assert(ManyChars.contains('G'), "ABCDEFG does not contain G"); + + static_assert(!ManyChars.contains('a'), "ABCDEFG contains a"); +} + +TEST(StringLiteralTest, ContainsLower) { + constexpr StringLiteral ManyChars = "ABCDEFG"; + + static_assert(ManyChars.contains_lower('A'), + "lower(ABCDEFG) does not contain A"); + static_assert(ManyChars.contains_lower('B'), + "lower(ABCDEFG) does not contain B"); + static_assert(ManyChars.contains_lower('C'), + "lower(ABCDEFG) does not contain C"); + static_assert(ManyChars.contains_lower('D'), + "lower(ABCDEFG) does not contain D"); + static_assert(ManyChars.contains_lower('E'), + "lower(ABCDEFG) does not contain E"); + static_assert(ManyChars.contains_lower('F'), + "lower(ABCDEFG) does not contain F"); + static_assert(ManyChars.contains_lower('G'), + "lower(ABCDEFG) does not contain G"); + + static_assert(ManyChars.contains_lower('a'), + "lower(ABCDEFG) does not contain a"); + static_assert(ManyChars.contains_lower('b'), + "lower(ABCDEFG) does not contain b"); + static_assert(ManyChars.contains_lower('c'), + "lower(ABCDEFG) does not contain c"); + static_assert(ManyChars.contains_lower('d'), + "lower(ABCDEFG) does not contain d"); + static_assert(ManyChars.contains_lower('e'), + "lower(ABCDEFG) does not contain e"); + static_assert(ManyChars.contains_lower('f'), + "lower(ABCDEFG) does not contain f"); + static_assert(ManyChars.contains_lower('g'), + "lower(ABCDEFG) does not contain g"); + + static_assert(!ManyChars.contains_lower('H'), "ABCDEFG contains H"); + static_assert(!ManyChars.contains_lower('h'), "ABCDEFG contains h"); + + static_assert(ManyChars.contains_lower("bCdE"), + "lower(ABCDEFG) does not contain lower(bCdE)"); +} + +TEST(StringLiteralTest, StartsWith) { + constexpr StringLiteral EmptyString = ""; + constexpr StringLiteral ManyChars = "AABBCCDDEEFFGG"; + + static_assert(EmptyString.startswith(EmptyString), + "String does not start with itself!"); + static_assert(ManyChars.startswith(ManyChars), + "String does not start with itself!"); + + static_assert(ManyChars.startswith("A"), + "String does not start with prefix!"); + static_assert(ManyChars.startswith("AABBCCDD"), + "String does not start with prefix!"); + + static_assert(!ManyChars.startswith("ABCD"), + "String starts with improper prefix!"); +} + +TEST(StringLiteralTest, EndsWith) { + constexpr StringLiteral EmptyString = ""; + constexpr StringLiteral ManyChars = "AABBCCDDEEFFGG"; + + static_assert(EmptyString.endswith(EmptyString), + "String does not end with itself!"); + static_assert(ManyChars.endswith(ManyChars), + "String does not end with itself!"); + + static_assert(ManyChars.endswith("G"), "String does not end with suffix!"); + static_assert(ManyChars.endswith("EEFFGG"), + "String does not end with suffix!"); + + static_assert(!ManyChars.endswith("DEFG"), + "String ends with improper suffix!"); +} + +TEST(StringLiteralTest, StartsWithLower) { + constexpr StringLiteral EmptyString = ""; + constexpr StringLiteral ManyChars = "AABBCCDDEEFFGG"; + constexpr StringLiteral ManyCharsLower = "aabbccddeeffgg"; + + static_assert(EmptyString.startswith_lower(EmptyString), + "String does not start with itself!"); + static_assert(ManyChars.startswith_lower(ManyCharsLower), + "String does not start with itself!"); + + static_assert(ManyChars.startswith_lower("a"), + "String does not start with prefix!"); + static_assert(ManyChars.startswith_lower("aabbccdd"), + "String does not start with prefix!"); + + static_assert(!ManyChars.startswith_lower("abcd"), + "String starts with improper prefix!"); + static_assert(!ManyChars.startswith_lower("abbccddeeffgg"), + "String starts with improper prefix!"); +} + +TEST(StringLiteralTest, EndsWithLower) { + constexpr StringLiteral EmptyString = ""; + constexpr StringLiteral ManyChars = "AABBCCDDEEFFGG"; + constexpr StringLiteral ManyCharsLower = "aabbccddeeffgg"; + + static_assert(EmptyString.endswith_lower(EmptyString), + "String does not end with itself!"); + static_assert(ManyChars.endswith_lower(ManyChars), + "String does not end with itself!"); + + static_assert(ManyChars.endswith_lower("g"), + "String does not end with suffix!"); + static_assert(ManyChars.endswith_lower("eeffgg"), + "String does not end with suffix!"); + + static_assert(!ManyChars.endswith_lower("defg"), + "String ends with improper suffix!"); + static_assert(!ManyChars.endswith_lower("aabbccddeeffg"), + "String ends with improper suffix!"); +} + +TEST(StringLiteralTest, FindFirstOf) { + constexpr StringLiteral EmptyString = ""; + constexpr StringLiteral ManyChars = "StringLiteralTest::FindFirstOf"; + + static_assert(EmptyString.find_first_of('A') == (std::size_t)-1, + "Letter A found in empty string!"); + static_assert(ManyChars.find_first_of(':') == 17, + ": Not found in StringLiteralTest::FindFirstOf"); + static_assert(ManyChars.find_first_of(' ') == (std::size_t)-1, + " found in StringLiteralTest::FindFirstOf"); + static_assert(ManyChars.find_first_of('f') == ManyChars.size() - 1, + "find_first_of could not find char at end!"); + static_assert(ManyChars.find_first_of('f', ManyChars.size() - 1) == + ManyChars.size() - 1, + "find_first_of could not find char at end with position!"); + static_assert(ManyChars.find_first_of(':', 18) == 18, + "find_first_of could not find : starting after the first one"); + static_assert( + ManyChars.find_first_of(':', 19) == -1, + "find_first_of found : even after starting beyond the last one"); +} + +TEST(StringLiteralTest, FindFirstNotOf) { + constexpr StringLiteral EmptyString = ""; + constexpr StringLiteral ManyChars = "AABBCCDDEEFFGG"; + + static_assert(EmptyString.find_first_not_of('A') == -1, + "find_first_not_of on an empty string should be -1"); + static_assert( + ManyChars.find_first_not_of('H') == 0, + "find_first_not_of where the character doesn't exist should be 0"); + static_assert( + ManyChars.find_first_not_of('A') == 2, + "find_first_not_of where the character does exist should skip it"); + static_assert( + ManyChars.find_first_not_of('A', 1) == 2, + "find_first_not_of where the character does exist should skip it"); + static_assert( + ManyChars.find_first_not_of('A', 2) == 2, + "find_first_not_of where the character does exist should skip it"); + static_assert(ManyChars.find_first_not_of('A', 3) == 3, + "find_first_not_of where the character does not exist should " + "return the first item"); + static_assert( + ManyChars.find_first_not_of('C', 4) == 6, + "find_first_not_of where the character does exist should skip it"); +} + +TEST(StringLiteralTest, Find) { + constexpr StringLiteral EmptyString = ""; + constexpr StringLiteral ManyChars = "AABBCCDDEEFFGG"; + + static_assert(EmptyString.find("") == 0, + "finding an empty string in any string should match at 0"); + static_assert(ManyChars.find("") == 0, + "finding an empty string in any string should match at 0"); + static_assert(ManyChars.find("ABC") == -1, + "finding a non-existant substring should return -1"); + static_assert(ManyChars.find("AAB") == 0, + "finding a match at the beginning should return 0"); + static_assert(ManyChars.find("CCDDEE") == 4, + "finding a match at the end should be correct"); +} + +TEST(StringLiteralTest, DropUntil) { + constexpr StringLiteral EmptyString = ""; + constexpr StringLiteral ManyChars = "ABCDEFG"; + + static_assert(EmptyString.drop_until('A') == "", + "drop_until from empty string should always be empty!"); + static_assert(ManyChars.drop_until('D') == "DEFG", + "drop_until from non-empty string is incorrect!"); + static_assert(ManyChars.drop_until('H') == "", + "drop_until where character is not found should be empty!"); +} + +TEST(StringLiteralTest, DropWhile) { + constexpr StringLiteral EmptyString = ""; + constexpr StringLiteral ManyChars = "AABBCCDDEEFFGG"; + + static_assert(EmptyString.drop_while('A') == "", + "drop_while from empty string should always be empty!"); + static_assert(ManyChars.drop_while('D') == ManyChars, + "drop_while where the first char doesn't match should be the " + "original string"); + + static_assert(ManyChars.drop_while('A') == "BBCCDDEEFFGG", + "drop_while where the first char matches should skip them"); +} + +TEST(StringLiteralTest, TakeWhile) { + constexpr StringLiteral EmptyString = ""; + constexpr StringLiteral ManyChars = "ABCDEFG"; + + static_assert(EmptyString.take_while('A') == "", + "take_while from empty string should always be empty!"); + static_assert(ManyChars.take_while('A') == "A", + "take_while from non-empty string is incorrect!"); + static_assert(ManyChars.take_while('B') == "", + "take_while where character is not found should be empty!"); +} + +TEST(StringLiteralTest, TakeUntil) { + constexpr StringLiteral EmptyString = ""; + constexpr StringLiteral ManyChars = "AABBCCDDEEFFGG"; + + constexpr std::size_t TakenCount = ManyChars.find_first_of('D'); + constexpr StringLiteral Taken = ManyChars.take_front(6); + static_assert(EmptyString.take_until('A') == "", + "take_until from empty string should always be empty!"); + static_assert(ManyChars.take_until('D') == "AABBCC", + "take_until where the char is in the middle should truncate"); + static_assert(ManyChars.take_until('A') == "", + "take_until where the first char matches return empty string"); +} + +TEST(StringLiteralTest, Shrink) { + constexpr StringLiteral EmptyString = ""; + constexpr StringLiteral ManyChars = "AABBCCDDEEFFGG"; + constexpr StringLiteral OddChars = "AABBCCD0DEEFFGG"; + + static_assert(EmptyString.shrink(0) == "", "Cannot shrink by 0!"); + static_assert(ManyChars.shrink(1) == "ABBCCDDEEFFG", "Cannot shrink by 1!"); + static_assert(ManyChars.shrink(7) == "", "Cannot shrink down to empty!"); + + static_assert(OddChars.shrink(7) == "0", + "Cannot shrink down to a single char!"); +} + +TEST(StringLiteralTest, Substr) { + constexpr StringLiteral EmptyString = ""; + constexpr StringLiteral ManyChars = "AABBCCDDEEFFGG"; + constexpr StringLiteral OddChars = "AABBCCD0DEEFFGG"; + + static_assert(EmptyString.substr(0) == "", + "Cannot take an empty substring of the empty string!"); + static_assert(ManyChars.substr(0) == ManyChars, + "Cannot take an empty substring of regular string!"); + static_assert(ManyChars.substr(0, 3) == "AAB", + "Cannot take an non-empty substring of regular string!"); + static_assert(ManyChars.substr(3, 4) == "BCCD", + "Cannot take substring from the middle!"); +} + +TEST(StringLiteralTest, Split) { + constexpr StringLiteral TestString = "Comma,Separated,List"; + constexpr StringLiteral Missing = "CommaSeparatedList"; + + constexpr auto TestResult = TestString.split(','); + static_assert(TestResult.first == "Comma", "Left of split is not correct!"); + static_assert(TestResult.second == "Separated,List", + "Right of split is not correct!"); + + constexpr auto MissingResult = Missing.split(','); + static_assert(MissingResult.first == "CommaSeparatedList", + "Left of split is not correct!"); + static_assert(MissingResult.second == "", "Right of split is not correct!"); +} + +TEST(StringLiteralTest, IsNumber) { + constexpr StringLiteral EmptyString = ""; + constexpr StringLiteral OneDigit = "0"; + constexpr StringLiteral ManyDigits = "123902"; + constexpr StringLiteral HexNumber = "12390A"; + + static_assert(!EmptyString.is_number(), "empty string is a number!"); + static_assert(OneDigit.is_number(), "0 is not a number!"); + static_assert(ManyDigits.is_number(), "123902 is not a number!"); + static_assert(!HexNumber.is_number(), "12390A is a number!"); +} + +TEST(StringLiteralTest, IsHexNumber) { + constexpr StringLiteral EmptyString = ""; + constexpr StringLiteral OneDigit = "0"; + constexpr StringLiteral ManyDigits = "123902"; + constexpr StringLiteral HexNumber = "12390A"; + constexpr StringLiteral ManyHexDigits = "DEADBEEF"; + + static_assert(!EmptyString.is_hex_number(), "empty string is a number!"); + static_assert(OneDigit.is_hex_number(), "0 is not a hex number!"); + static_assert(ManyDigits.is_hex_number(), "123902 is not a hex number!"); + static_assert(HexNumber.is_hex_number(), "12390A is not a hex number!"); + static_assert(ManyHexDigits.is_hex_number(), "DEADBEEF is not a hex number!"); +} + +TEST(StringLiteralTest, GetAsUnsigned) { + constexpr StringLiteral OneDigit = "0"; + constexpr StringLiteral ManyDigits = "123902"; + + static_assert(0 == OneDigit.getAsUnsignedDecimal(), + "0 is not converted to 0"); + static_assert(123902 == ManyDigits.getAsUnsignedDecimal(), + "123902 is not converted to 123902"); +} + +TEST(StringLiteralTest, GetAsUnsignedHex) { + constexpr StringLiteral OneDigit = "0"; + constexpr StringLiteral ManyDigits = "12390A"; + constexpr StringLiteral DeadBeef = "DEADBEEF"; + + constexpr std::size_t Result = ManyDigits.getAsUnsignedHex(); + static_assert(0 == OneDigit.getAsUnsignedHex(), "0 is not converted to 0"); + static_assert(0x12390A == ManyDigits.getAsUnsignedHex(), + "0x123902 is not converted to 123902"); + static_assert(0xDEADBEEF == DeadBeef.getAsUnsignedHex(), + "0xDEADBEEF is not converted to DEADBEEF"); +}