Utils: Changeable SmallString stack size

The size of the small string optimization was hard coded. For some use
cases you want a bigger or smaller size. It is now configurable by a
template parameter. For that we changed the name to BasicSmallString and
added an alias of BasicSmallString<31> to SmallString.

Change-Id: I844b4420d260290307a6018bb6cc4cf3ba7bd449
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
Tim Jenssen
2016-11-01 17:15:17 +01:00
parent 601dc5fc07
commit b31c3e0dcb
4 changed files with 216 additions and 210 deletions

View File

@@ -63,7 +63,8 @@
namespace Utils { namespace Utils {
class SmallString template<uint Size>
class BasicSmallString
{ {
public: public:
using iterator = Internal::SmallStringIterator<std::random_access_iterator_tag, char>; using iterator = Internal::SmallStringIterator<std::random_access_iterator_tag, char>;
@@ -72,25 +73,25 @@ public:
using const_reverse_iterator = std::reverse_iterator<const_iterator>; using const_reverse_iterator = std::reverse_iterator<const_iterator>;
using size_type = std::size_t; using size_type = std::size_t;
SmallString() noexcept BasicSmallString() noexcept
: m_data(Internal::StringDataLayout()) : m_data(Internal::StringDataLayout<Size>())
{ {
} }
constexpr constexpr
SmallString(const SmallStringLiteral &stringReference) BasicSmallString(const BasicSmallStringLiteral<Size> &stringReference)
: m_data(stringReference.m_data) : m_data(stringReference.m_data)
{ {
} }
template<size_type Size> template<size_type ArraySize>
constexpr constexpr
SmallString(const char(&string)[Size]) BasicSmallString(const char(&string)[ArraySize])
: m_data(string) : m_data(string)
{ {
} }
SmallString(const char *string, size_type size, size_type capacity) BasicSmallString(const char *string, size_type size, size_type capacity)
{ {
if (Q_LIKELY(capacity <= shortStringCapacity())) { if (Q_LIKELY(capacity <= shortStringCapacity())) {
std::memcpy(m_data.shortString.string, string, size); std::memcpy(m_data.shortString.string, string, size);
@@ -110,62 +111,62 @@ public:
} }
} }
SmallString(const char *string, size_type size) BasicSmallString(const char *string, size_type size)
: SmallString(string, size, size) : BasicSmallString(string, size, size)
{ {
} }
template<typename Type, template<typename Type,
typename = typename std::enable_if<std::is_pointer<Type>::value>::type typename = typename std::enable_if<std::is_pointer<Type>::value>::type
> >
SmallString(Type characterPointer) BasicSmallString(Type characterPointer)
: SmallString(characterPointer, std::strlen(characterPointer)) : BasicSmallString(characterPointer, std::strlen(characterPointer))
{ {
static_assert(!std::is_array<Type>::value, "Input type is array and not char pointer!"); static_assert(!std::is_array<Type>::value, "Input type is array and not char pointer!");
} }
SmallString(const QString &qString) BasicSmallString(const QString &qString)
: SmallString(SmallString::fromQString(qString)) : BasicSmallString(BasicSmallString::fromQString(qString))
{} {}
template<typename Type, template<typename Type,
typename = typename std::enable_if< typename = typename std::enable_if<
std::is_same<typename std::decay<Type>::type, std::string>::value>::type> std::is_same<typename std::decay<Type>::type, std::string>::value>::type>
SmallString(Type &&string) BasicSmallString(Type &&string)
: SmallString(string.data(), string.size()) : BasicSmallString(string.data(), string.size())
{} {}
template<typename BeginIterator, template<typename BeginIterator,
typename EndIterator, typename EndIterator,
typename = typename std::enable_if<std::is_same<BeginIterator, EndIterator>::value>::type typename = typename std::enable_if<std::is_same<BeginIterator, EndIterator>::value>::type
> >
SmallString(BeginIterator begin, EndIterator end) BasicSmallString(BeginIterator begin, EndIterator end)
: SmallString(&(*begin), size_type(end - begin)) : BasicSmallString(&(*begin), size_type(end - begin))
{ {
} }
~SmallString() noexcept ~BasicSmallString() noexcept
{ {
if (Q_UNLIKELY(hasAllocatedMemory())) if (Q_UNLIKELY(hasAllocatedMemory()))
Memory::deallocate(m_data.allocated.data.pointer); Memory::deallocate(m_data.allocated.data.pointer);
} }
#if !defined(UNIT_TESTS) && !(defined(_MSC_VER) && _MSC_VER < 1900) #if !defined(UNIT_TESTS) && !(defined(_MSC_VER) && _MSC_VER < 1900)
SmallString(const SmallString &other) = delete; BasicSmallString(const BasicSmallString &other) = delete;
SmallString &operator=(const SmallString &other) = delete; BasicSmallString &operator=(const BasicSmallString &other) = delete;
#else #else
SmallString(const SmallString &string) BasicSmallString(const BasicSmallString &string)
{ {
if (string.isShortString() || string.isReadOnlyReference()) if (string.isShortString() || string.isReadOnlyReference())
m_data = string.m_data; m_data = string.m_data;
else else
new (this) SmallString{string.data(), string.size()}; new (this) BasicSmallString{string.data(), string.size()};
} }
SmallString &operator=(const SmallString &other) BasicSmallString &operator=(const BasicSmallString &other)
{ {
SmallString copy = other; BasicSmallString copy = other;
swap(*this, copy); swap(*this, copy);
@@ -173,31 +174,31 @@ public:
} }
#endif #endif
SmallString(SmallString &&other) noexcept BasicSmallString(BasicSmallString &&other) noexcept
{ {
m_data = other.m_data; m_data = other.m_data;
other.m_data = Internal::StringDataLayout(); other.m_data = Internal::StringDataLayout<Size>();
} }
SmallString &operator=(SmallString &&other) noexcept BasicSmallString &operator=(BasicSmallString &&other) noexcept
{ {
swap(*this, other); swap(*this, other);
return *this; return *this;
} }
SmallString clone() const BasicSmallString clone() const
{ {
SmallString clonedString(m_data); BasicSmallString clonedString(m_data);
if (Q_UNLIKELY(hasAllocatedMemory())) if (Q_UNLIKELY(hasAllocatedMemory()))
new (&clonedString) SmallString{m_data.allocated.data.pointer, m_data.allocated.data.size}; new (&clonedString) BasicSmallString{m_data.allocated.data.pointer, m_data.allocated.data.size};
return clonedString; return clonedString;
} }
friend friend
void swap(SmallString &first, SmallString &second) void swap(BasicSmallString &first, BasicSmallString &second)
{ {
using std::swap; using std::swap;
@@ -230,9 +231,9 @@ public:
} }
static static
SmallString fromUtf8(const char *characterPointer) BasicSmallString fromUtf8(const char *characterPointer)
{ {
return SmallString(characterPointer, std::strlen(characterPointer)); return BasicSmallString(characterPointer, std::strlen(characterPointer));
} }
void reserve(size_type newCapacity) void reserve(size_type newCapacity)
@@ -245,7 +246,7 @@ public:
} else { } else {
const size_type oldSize = size(); const size_type oldSize = size();
new (this) SmallString(data(), oldSize, newCapacity); new (this) BasicSmallString(data(), oldSize, newCapacity);
} }
} }
} }
@@ -259,8 +260,8 @@ public:
void clear() noexcept void clear() noexcept
{ {
this->~SmallString(); this->~BasicSmallString();
m_data = Internal::StringDataLayout(); m_data = Internal::StringDataLayout<Size>();
} }
char *data() noexcept char *data() noexcept
@@ -319,17 +320,17 @@ public:
} }
static static
SmallString fromQString(const QString &qString) BasicSmallString fromQString(const QString &qString)
{ {
const QByteArray &utf8ByteArray = qString.toUtf8(); const QByteArray &utf8ByteArray = qString.toUtf8();
return SmallString(utf8ByteArray.constData(), uint(utf8ByteArray.size())); return BasicSmallString(utf8ByteArray.constData(), uint(utf8ByteArray.size()));
} }
static static
SmallString fromQByteArray(const QByteArray &utf8ByteArray) BasicSmallString fromQByteArray(const QByteArray &utf8ByteArray)
{ {
return SmallString(utf8ByteArray.constData(), uint(utf8ByteArray.size())); return BasicSmallString(utf8ByteArray.constData(), uint(utf8ByteArray.size()));
} }
bool contains(SmallStringView subStringToSearch) const bool contains(SmallStringView subStringToSearch) const
@@ -426,7 +427,7 @@ public:
setSize(newSize); setSize(newSize);
} }
SmallString &operator+=(SmallStringView string) BasicSmallString &operator+=(SmallStringView string)
{ {
append(string); append(string);
@@ -464,7 +465,7 @@ public:
constexpr static constexpr static
size_type shortStringCapacity() noexcept size_type shortStringCapacity() noexcept
{ {
return SmallStringLiteral::shortStringCapacity(); return BasicSmallStringLiteral<Size>::shortStringCapacity();
} }
size_type optimalCapacity(const size_type size) size_type optimalCapacity(const size_type size)
@@ -481,16 +482,16 @@ public:
} }
static static
SmallString join(std::initializer_list<SmallString> list) BasicSmallString join(std::initializer_list<BasicSmallString> list)
{ {
size_type totalSize = 0; size_type totalSize = 0;
for (const SmallString &string : list) for (const BasicSmallString &string : list)
totalSize += string.size(); totalSize += string.size();
SmallString joinedString; BasicSmallString joinedString;
joinedString.reserve(totalSize); joinedString.reserve(totalSize);
for (const SmallString &string : list) for (const BasicSmallString &string : list)
joinedString.append(string); joinedString.append(string);
return joinedString; return joinedString;
@@ -537,8 +538,105 @@ UNIT_TEST_PUBLIC:
return cacheLineBlocks * cacheLineSize; return cacheLineBlocks * cacheLineSize;
} }
template<size_type ArraySize>
friend bool operator==(const BasicSmallString& first, const char(&second)[ArraySize]) noexcept
{
return first == SmallStringView(second);
}
template<size_type ArraySize>
friend bool operator==(const char(&first)[ArraySize], const BasicSmallString& second) noexcept
{
return second == first;
}
template<typename Type,
typename = typename std::enable_if<std::is_pointer<Type>::value>::type
>
friend bool operator==(const BasicSmallString& first, Type second) noexcept
{
return first == SmallStringView(second);
}
template<typename Type,
typename = typename std::enable_if<std::is_pointer<Type>::value>::type
>
friend bool operator==(Type first, const BasicSmallString& second) noexcept
{
return second == first;
}
friend bool operator==(const BasicSmallString& first, const BasicSmallString& second) noexcept
{
return first.size() == second.size()
&& std::memcmp(first.data(), second.data(), first.size()) == 0;
}
friend bool operator==(const BasicSmallString& first, const SmallStringView& second) noexcept
{
return first.size() == second.size()
&& std::memcmp(first.data(), second.data(), first.size()) == 0;
}
friend bool operator==(const SmallStringView& first, const BasicSmallString& second) noexcept
{
return second == first;
}
friend bool operator!=(const BasicSmallString& first, const SmallStringView& second) noexcept
{
return !(first == second);
}
friend bool operator!=(const SmallStringView& first, const BasicSmallString& second) noexcept
{
return second == first;
}
friend bool operator!=(const BasicSmallString& first, const BasicSmallString& second) noexcept
{
return !(first == second);
}
template<size_type ArraySize>
friend bool operator!=(const BasicSmallString& first, const char(&second)[ArraySize]) noexcept
{
return !(first == second);
}
template<size_type ArraySize>
friend bool operator!=(const char(&first)[ArraySize], const BasicSmallString& second) noexcept
{
return second != first;
}
template<typename Type,
typename = typename std::enable_if<std::is_pointer<Type>::value>::type
>
friend bool operator!=(const BasicSmallString& first, Type second) noexcept
{
return !(first == second);
}
template<typename Type,
typename = typename std::enable_if<std::is_pointer<Type>::value>::type
>
friend bool operator!=(Type first, const BasicSmallString& second) noexcept
{
return second != first;
}
friend bool operator<(const BasicSmallString& first, const BasicSmallString& second) noexcept
{
size_type minimalSize = std::min(first.size(), second.size());
const int comparison = std::memcmp(first.data(), second.data(), minimalSize + 1);
return comparison < 0;
}
private: private:
SmallString(Internal::StringDataLayout data) noexcept BasicSmallString(Internal::StringDataLayout<Size> data) noexcept
: m_data(data) : m_data(data)
{ {
} }
@@ -677,113 +775,9 @@ private:
} }
private: private:
Internal::StringDataLayout m_data; Internal::StringDataLayout<Size> m_data;
}; };
template<SmallString::size_type Size>
bool operator==(const SmallString& first, const char(&second)[Size]) noexcept
{
return first == SmallStringView(second);
}
template<SmallString::size_type Size>
bool operator==(const char(&first)[Size], const SmallString& second) noexcept
{
return second == first;
}
template<typename Type,
typename = typename std::enable_if<std::is_pointer<Type>::value>::type
>
bool operator==(const SmallString& first, Type second) noexcept
{
return first == SmallStringView(second);
}
template<typename Type,
typename = typename std::enable_if<std::is_pointer<Type>::value>::type
>
bool operator==(Type first, const SmallString& second) noexcept
{
return second == first;
}
inline
bool operator==(const SmallString& first, const SmallString& second) noexcept
{
return first.size() == second.size()
&& std::memcmp(first.data(), second.data(), first.size()) == 0;
}
inline
bool operator==(const SmallString& first, const SmallStringView& second) noexcept
{
return first.size() == second.size()
&& std::memcmp(first.data(), second.data(), first.size()) == 0;
}
inline
bool operator==(const SmallStringView& first, const SmallString& second) noexcept
{
return second == first;
}
inline
bool operator!=(const SmallString& first, const SmallStringView& second) noexcept
{
return !(first == second);
}
inline
bool operator!=(const SmallStringView& first, const SmallString& second) noexcept
{
return second == first;
}
inline
bool operator!=(const SmallString& first, const SmallString& second) noexcept
{
return !(first == second);
}
template<SmallString::size_type Size>
bool operator!=(const SmallString& first, const char(&second)[Size]) noexcept
{
return !(first == second);
}
template<SmallString::size_type Size>
bool operator!=(const char(&first)[Size], const SmallString& second) noexcept
{
return second != first;
}
template<typename Type,
typename = typename std::enable_if<std::is_pointer<Type>::value>::type
>
bool operator!=(const SmallString& first, Type second) noexcept
{
return !(first == second);
}
template<typename Type,
typename = typename std::enable_if<std::is_pointer<Type>::value>::type
>
bool operator!=(Type first, const SmallString& second) noexcept
{
return second != first;
}
inline
bool operator<(const SmallString& first, const SmallString& second) noexcept
{
SmallString::size_type minimalSize = std::min(first.size(), second.size());
const int comparison = std::memcmp(first.data(), second.data(), minimalSize + 1);
return comparison < 0;
}
template<typename Key, template<typename Key,
typename Value, typename Value,
typename Hash = std::hash<Key>, typename Hash = std::hash<Key>,
@@ -813,6 +807,8 @@ std::vector<Type> clone(const std::vector<Type> &vector)
return clonedVector; return clonedVector;
} }
using SmallString = BasicSmallString<31>;
} // namespace Utils } // namespace Utils
#pragma pop_macro("noexcept") #pragma pop_macro("noexcept")

View File

@@ -51,41 +51,44 @@ namespace Internal {
using size_type = std::size_t; using size_type = std::size_t;
static const int smallStringLayoutByteSize = 32; template <uint MaximumShortStringDataAreaSize>
static const int maximumShortStringDataAreaSize = smallStringLayoutByteSize - 1;
struct AllocatedLayout { struct AllocatedLayout {
struct Data { struct Data {
char *pointer; char *pointer;
size_type size; size_type size;
size_type capacity; size_type capacity;
} data; } data;
char dummy[maximumShortStringDataAreaSize - sizeof(Data)]; char dummy[MaximumShortStringDataAreaSize - sizeof(Data)];
std::uint8_t shortStringSize: 6; std::uint8_t shortStringSize: 6;
std::uint8_t isReadOnlyReference : 1; std::uint8_t isReadOnlyReference : 1;
std::uint8_t isReference : 1; std::uint8_t isReference : 1;
}; };
template <uint MaximumShortStringDataAreaSize>
struct ReferenceLayout { struct ReferenceLayout {
struct Data { struct Data {
const char *pointer; const char *pointer;
size_type size; size_type size;
size_type capacity; size_type capacity;
} data; } data;
char dummy[maximumShortStringDataAreaSize - sizeof(Data)]; char dummy[MaximumShortStringDataAreaSize - sizeof(Data)];
std::uint8_t shortStringSize: 6; std::uint8_t shortStringSize: 6;
std::uint8_t isReadOnlyReference : 1; std::uint8_t isReadOnlyReference : 1;
std::uint8_t isReference : 1; std::uint8_t isReference : 1;
}; };
template <uint MaximumShortStringDataAreaSize>
struct ShortStringLayout { struct ShortStringLayout {
char string[maximumShortStringDataAreaSize]; char string[MaximumShortStringDataAreaSize];
std::uint8_t shortStringSize: 6; std::uint8_t shortStringSize: 6;
std::uint8_t isReadOnlyReference : 1; std::uint8_t isReadOnlyReference : 1;
std::uint8_t isReference : 1; std::uint8_t isReference : 1;
}; };
template <uint MaximumShortStringDataAreaSize>
struct ALIGNAS_16 StringDataLayout { struct ALIGNAS_16 StringDataLayout {
static_assert( MaximumShortStringDataAreaSize >= 15, "Size must be greater equal than 15 bytes!");
static_assert(((MaximumShortStringDataAreaSize + 1) % 16) == 0, "Size + 1 must be dividable by 16!");
StringDataLayout() noexcept = default; StringDataLayout() noexcept = default;
constexpr StringDataLayout(const char *string, constexpr StringDataLayout(const char *string,
@@ -101,7 +104,7 @@ struct ALIGNAS_16 StringDataLayout {
#endif #endif
{ {
#if __cpp_constexpr >= 201304 #if __cpp_constexpr >= 201304
if (Size <= maximumShortStringDataAreaSize) { if (Size <= MaximumShortStringDataAreaSize) {
for (size_type i = 0; i < Size; ++i) for (size_type i = 0; i < Size; ++i)
shortString.string[i] = string[i]; shortString.string[i] = string[i];
@@ -125,9 +128,9 @@ struct ALIGNAS_16 StringDataLayout {
} }
union { union {
AllocatedLayout allocated; AllocatedLayout<MaximumShortStringDataAreaSize> allocated;
ReferenceLayout reference; ReferenceLayout<MaximumShortStringDataAreaSize> reference;
ShortStringLayout shortString = ShortStringLayout(); ShortStringLayout<MaximumShortStringDataAreaSize> shortString = ShortStringLayout<MaximumShortStringDataAreaSize>();
}; };
}; };

View File

@@ -41,27 +41,27 @@
namespace Utils { namespace Utils {
class SmallString; template <int Size>
class BasicSmallStringLiteral
class SmallStringLiteral
{ {
friend class SmallString; template<uint>
friend class BasicSmallString;
public: public:
using const_iterator = Internal::SmallStringIterator<std::random_access_iterator_tag, const char>; using const_iterator = Internal::SmallStringIterator<std::random_access_iterator_tag, const char>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>; using const_reverse_iterator = std::reverse_iterator<const_iterator>;
using size_type = std::size_t; using size_type = std::size_t;
template<size_type Size> template<size_type ArraySize>
constexpr constexpr
SmallStringLiteral(const char(&string)[Size]) noexcept BasicSmallStringLiteral(const char(&string)[ArraySize]) noexcept
: m_data(string) : m_data(string)
{ {
static_assert(Size >= 1, "Invalid string literal! Length is zero!"); static_assert(ArraySize >= 1, "Invalid string literal! Length is zero!");
} }
constexpr constexpr
SmallStringLiteral(const char *string, const size_type size) noexcept BasicSmallStringLiteral(const char *string, const size_type size) noexcept
: m_data(string, size) : m_data(string, size)
{ {
} }
@@ -99,7 +99,7 @@ public:
constexpr static constexpr static
size_type shortStringCapacity() noexcept size_type shortStringCapacity() noexcept
{ {
return sizeof(Internal::ShortStringLayout) - 2; return sizeof(Internal::ShortStringLayout<Size>) - 2;
} }
bool isShortString() const noexcept bool isShortString() const noexcept
@@ -118,14 +118,16 @@ public:
} }
private: private:
SmallStringLiteral(Internal::StringDataLayout data) noexcept BasicSmallStringLiteral(Internal::StringDataLayout<Size> data) noexcept
: m_data(data) : m_data(data)
{ {
} }
private: private:
Internal::StringDataLayout m_data; Internal::StringDataLayout<Size> m_data;
}; };
using SmallStringLiteral = BasicSmallStringLiteral<31>;
} // namespace Utils } // namespace Utils
#pragma pop_macro("noexcept") #pragma pop_macro("noexcept")

View File

@@ -40,100 +40,103 @@
namespace Utils { namespace Utils {
class SmallStringVector : public std::vector<Utils::SmallString> template<uint SmallStringSize>
class BasicSmallStringVector : public std::vector<BasicSmallString<SmallStringSize>>
{ {
using SmallString =BasicSmallString<SmallStringSize>;
using Base = std::vector<SmallString>;
public: public:
SmallStringVector() = default; BasicSmallStringVector() = default;
SmallStringVector(std::initializer_list<Utils::SmallString> list) BasicSmallStringVector(std::initializer_list<SmallString> list)
{ {
reserve(list.size()); Base::reserve(list.size());
for (auto &&entry : list) for (auto &&entry : list)
push_back(entry.clone()); Base::push_back(entry.clone());
} }
explicit SmallStringVector(const QStringList &stringList) explicit BasicSmallStringVector(const QStringList &stringList)
{ {
reserve(std::size_t(stringList.count())); std::vector<SmallString>::reserve(std::size_t(stringList.count()));
for (const QString &string : stringList) for (const QString &string : stringList)
push_back(Utils::SmallString::fromQString(string)); Base::push_back(SmallString::fromQString(string));
} }
explicit SmallStringVector(const std::vector<std::string> &stringVector) explicit BasicSmallStringVector(const std::vector<std::string> &stringVector)
{ {
reserve(std::size_t(stringVector.size())); Base::reserve(std::size_t(stringVector.size()));
for (const std::string &string : stringVector) for (const std::string &string : stringVector)
emplace_back(string); Base::emplace_back(string);
} }
#if !defined(UNIT_TESTS) && !(defined(_MSC_VER) && _MSC_VER < 1900) #if !defined(UNIT_TESTS) && !(defined(_MSC_VER) && _MSC_VER < 1900)
SmallStringVector(const SmallStringVector &) = delete; BasicSmallStringVector(const BasicSmallStringVector &) = delete;
SmallStringVector &operator=(const SmallStringVector &) = delete; BasicSmallStringVector &operator=(const BasicSmallStringVector &) = delete;
#else #else
SmallStringVector(const SmallStringVector &) = default; BasicSmallStringVector(const BasicSmallStringVector &) = default;
SmallStringVector &operator=(const SmallStringVector &) = default; BasicSmallStringVector &operator=(const BasicSmallStringVector &) = default;
#endif #endif
#if !(defined(_MSC_VER) && _MSC_VER < 1900) #if !(defined(_MSC_VER) && _MSC_VER < 1900)
SmallStringVector(SmallStringVector &&) noexcept = default; BasicSmallStringVector(BasicSmallStringVector &&) noexcept = default;
SmallStringVector &operator=(SmallStringVector &&) noexcept = default; BasicSmallStringVector &operator=(BasicSmallStringVector &&) noexcept = default;
#else #else
SmallStringVector(SmallStringVector &&other) BasicSmallStringVector(BasicSmallStringVector &&other)
: std::vector<Utils::SmallString>(std::move(other)) : Base(std::move(other))
{ {
} }
SmallStringVector &operator=(SmallStringVector &&other) BasicSmallStringVector &operator=(BasicSmallStringVector &&other)
{ {
std::vector<Utils::SmallString>(std::move(other)); Base(std::move(other));
return *this; return *this;
} }
#endif #endif
Utils::SmallString join(Utils::SmallString &&separator) const SmallString join(SmallString &&separator) const
{ {
Utils::SmallString joinedString; SmallString joinedString;
joinedString.reserve(totalByteSize() + separator.size() * std::size_t(size())); joinedString.reserve(totalByteSize() + separator.size() * std::size_t(Base::size()));
for (auto stringIterator = begin(); stringIterator != end(); ++stringIterator) { for (auto stringIterator = Base::begin(); stringIterator != Base::end(); ++stringIterator) {
joinedString.append(*stringIterator); joinedString.append(*stringIterator);
if (std::next(stringIterator) != end()) if (std::next(stringIterator) != Base::end())
joinedString.append(separator); joinedString.append(separator);
} }
return joinedString; return joinedString;
} }
bool contains(const Utils::SmallString &string) const noexcept bool contains(SmallStringView string) const noexcept
{ {
return std::find(cbegin(), cend(), string) != cend(); return std::find(Base::begin(), Base::end(), string) != Base::end();
} }
bool removeFast(Utils::SmallStringView valueToBeRemoved) bool removeFast(SmallStringView valueToBeRemoved)
{ {
auto position = std::remove(begin(), end(), valueToBeRemoved); auto position = std::remove(Base::begin(), Base::end(), valueToBeRemoved);
const bool hasEntry = position != end(); const bool hasEntry = position != Base::end();
erase(position, end()); erase(position, Base::end());
return hasEntry; return hasEntry;
} }
void append(Utils::SmallString &&string) void append(SmallString &&string)
{ {
push_back(std::move(string)); push_back(std::move(string));
} }
SmallStringVector clone() const BasicSmallStringVector clone() const
{ {
SmallStringVector clonedVector; BasicSmallStringVector clonedVector;
clonedVector.reserve(size()); clonedVector.reserve(Base::size());
for (auto &&entry : *this) for (auto &&entry : *this)
clonedVector.push_back(entry.clone()); clonedVector.push_back(entry.clone());
@@ -143,7 +146,7 @@ public:
operator std::vector<std::string>() const operator std::vector<std::string>() const
{ {
return std::vector<std::string>(begin(), end()); return std::vector<std::string>(Base::begin(), Base::end());
} }
private: private:
@@ -158,6 +161,8 @@ private:
} }
}; };
using SmallStringVector = BasicSmallStringVector<31>;
} // namespace Utils; } // namespace Utils;
#pragma pop_macro("noexcept") #pragma pop_macro("noexcept")