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

View File

@@ -51,41 +51,44 @@ namespace Internal {
using size_type = std::size_t;
static const int smallStringLayoutByteSize = 32;
static const int maximumShortStringDataAreaSize = smallStringLayoutByteSize - 1;
template <uint MaximumShortStringDataAreaSize>
struct AllocatedLayout {
struct Data {
char *pointer;
size_type size;
size_type capacity;
} data;
char dummy[maximumShortStringDataAreaSize - sizeof(Data)];
char dummy[MaximumShortStringDataAreaSize - sizeof(Data)];
std::uint8_t shortStringSize: 6;
std::uint8_t isReadOnlyReference : 1;
std::uint8_t isReference : 1;
};
template <uint MaximumShortStringDataAreaSize>
struct ReferenceLayout {
struct Data {
const char *pointer;
size_type size;
size_type capacity;
} data;
char dummy[maximumShortStringDataAreaSize - sizeof(Data)];
char dummy[MaximumShortStringDataAreaSize - sizeof(Data)];
std::uint8_t shortStringSize: 6;
std::uint8_t isReadOnlyReference : 1;
std::uint8_t isReference : 1;
};
template <uint MaximumShortStringDataAreaSize>
struct ShortStringLayout {
char string[maximumShortStringDataAreaSize];
char string[MaximumShortStringDataAreaSize];
std::uint8_t shortStringSize: 6;
std::uint8_t isReadOnlyReference : 1;
std::uint8_t isReference : 1;
};
template <uint MaximumShortStringDataAreaSize>
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;
constexpr StringDataLayout(const char *string,
@@ -101,7 +104,7 @@ struct ALIGNAS_16 StringDataLayout {
#endif
{
#if __cpp_constexpr >= 201304
if (Size <= maximumShortStringDataAreaSize) {
if (Size <= MaximumShortStringDataAreaSize) {
for (size_type i = 0; i < Size; ++i)
shortString.string[i] = string[i];
@@ -125,9 +128,9 @@ struct ALIGNAS_16 StringDataLayout {
}
union {
AllocatedLayout allocated;
ReferenceLayout reference;
ShortStringLayout shortString = ShortStringLayout();
AllocatedLayout<MaximumShortStringDataAreaSize> allocated;
ReferenceLayout<MaximumShortStringDataAreaSize> reference;
ShortStringLayout<MaximumShortStringDataAreaSize> shortString = ShortStringLayout<MaximumShortStringDataAreaSize>();
};
};

View File

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

View File

@@ -40,100 +40,103 @@
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:
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)
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)
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)
emplace_back(string);
Base::emplace_back(string);
}
#if !defined(UNIT_TESTS) && !(defined(_MSC_VER) && _MSC_VER < 1900)
SmallStringVector(const SmallStringVector &) = delete;
SmallStringVector &operator=(const SmallStringVector &) = delete;
BasicSmallStringVector(const BasicSmallStringVector &) = delete;
BasicSmallStringVector &operator=(const BasicSmallStringVector &) = delete;
#else
SmallStringVector(const SmallStringVector &) = default;
SmallStringVector &operator=(const SmallStringVector &) = default;
BasicSmallStringVector(const BasicSmallStringVector &) = default;
BasicSmallStringVector &operator=(const BasicSmallStringVector &) = default;
#endif
#if !(defined(_MSC_VER) && _MSC_VER < 1900)
SmallStringVector(SmallStringVector &&) noexcept = default;
SmallStringVector &operator=(SmallStringVector &&) noexcept = default;
BasicSmallStringVector(BasicSmallStringVector &&) noexcept = default;
BasicSmallStringVector &operator=(BasicSmallStringVector &&) noexcept = default;
#else
SmallStringVector(SmallStringVector &&other)
: std::vector<Utils::SmallString>(std::move(other))
BasicSmallStringVector(BasicSmallStringVector &&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;
}
#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);
if (std::next(stringIterator) != end())
if (std::next(stringIterator) != Base::end())
joinedString.append(separator);
}
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;
}
void append(Utils::SmallString &&string)
void append(SmallString &&string)
{
push_back(std::move(string));
}
SmallStringVector clone() const
BasicSmallStringVector clone() const
{
SmallStringVector clonedVector;
clonedVector.reserve(size());
BasicSmallStringVector clonedVector;
clonedVector.reserve(Base::size());
for (auto &&entry : *this)
clonedVector.push_back(entry.clone());
@@ -143,7 +146,7 @@ public:
operator std::vector<std::string>() const
{
return std::vector<std::string>(begin(), end());
return std::vector<std::string>(Base::begin(), Base::end());
}
private:
@@ -158,6 +161,8 @@ private:
}
};
using SmallStringVector = BasicSmallStringVector<31>;
} // namespace Utils;
#pragma pop_macro("noexcept")