From cf064500aaa0c0de7eafffd179250f9922bb03f8 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Sun, 7 Aug 2022 03:35:53 +0200 Subject: [PATCH] Utils: Improve memory layout code Actually, the layout is the same, but we move the control block. To prevent padding of the short string, we use pragma pack(1). To align the pointer again, we add some dummy data with the size of a pointer minus a control block because alignas is not working like expected on GCC. Change-Id: Ide86ace243dab5f487da63492ebac018da45098a Reviewed-by: Tim Jenssen --- src/libs/utils/smallstring.h | 76 +++++++++---------- src/libs/utils/smallstringlayout.h | 113 +++++++++++----------------- src/libs/utils/smallstringliteral.h | 12 +-- 3 files changed, 85 insertions(+), 116 deletions(-) diff --git a/src/libs/utils/smallstring.h b/src/libs/utils/smallstring.h index 60de005dbc8..0d8ba6ae706 100644 --- a/src/libs/utils/smallstring.h +++ b/src/libs/utils/smallstring.h @@ -63,7 +63,7 @@ namespace Utils { template -class BasicSmallString +class alignas(16) BasicSmallString { public: using const_iterator = Internal::SmallStringIterator; @@ -97,14 +97,14 @@ public: BasicSmallString(const char *string, size_type size, size_type capacity) { if (Q_LIKELY(capacity <= shortStringCapacity())) { - std::char_traits::copy(m_data.shortString.string, string, size); - m_data.shortString.string[size] = 0; - m_data.shortString.control.setShortStringSize(size); - m_data.shortString.control.setIsShortString(true); - m_data.shortString.control.setIsReadOnlyReference(false); + std::char_traits::copy(m_data.shortString, string, size); + m_data.shortString[size] = 0; + m_data.control.setShortStringSize(size); + m_data.control.setIsShortString(true); + m_data.control.setIsReadOnlyReference(false); } else { - m_data.allocated.data.pointer = Memory::allocate(capacity + 1); - std::char_traits::copy(m_data.allocated.data.pointer, string, size); + m_data.reference.pointer = Memory::allocate(capacity + 1); + std::char_traits::copy(m_data.reference.pointer, string, size); initializeLongString(size, capacity); } } @@ -167,7 +167,7 @@ public: ~BasicSmallString() noexcept { if (Q_UNLIKELY(hasAllocatedMemory())) - Memory::deallocate(m_data.allocated.data.pointer); + Memory::deallocate(m_data.reference.pointer); } BasicSmallString(const BasicSmallString &other) @@ -182,7 +182,7 @@ public: { if (Q_LIKELY(this != &other)) { if (Q_UNLIKELY(hasAllocatedMemory())) - Memory::deallocate(m_data.allocated.data.pointer); + Memory::deallocate(m_data.reference.pointer); if (Q_LIKELY(other.isShortString() || other.isReadOnlyReference())) m_data = other.m_data; @@ -203,7 +203,7 @@ public: { if (Q_LIKELY(this != &other)) { if (Q_UNLIKELY(hasAllocatedMemory())) - Memory::deallocate(m_data.allocated.data.pointer); + Memory::deallocate(m_data.reference.pointer); m_data = std::move(other.m_data); other.m_data.reset(); @@ -218,9 +218,9 @@ public: BasicSmallString clonedString(m_data); if (Q_UNLIKELY(hasAllocatedMemory())) - new (&clonedString) BasicSmallString{m_data.allocated.data.pointer, - m_data.allocated.data.size, - m_data.allocated.data.capacity}; + new (&clonedString) BasicSmallString{m_data.reference.pointer, + m_data.reference.size, + m_data.reference.capacity}; return clonedString; } @@ -274,11 +274,11 @@ public: { if (fitsNotInCapacity(newCapacity)) { if (Q_UNLIKELY(hasAllocatedMemory())) { - m_data.allocated.data.pointer = Memory::reallocate(m_data.allocated.data.pointer, - newCapacity + 1); - m_data.allocated.data.capacity = newCapacity; + m_data.reference.pointer = Memory::reallocate(m_data.reference.pointer, + newCapacity + 1); + m_data.reference.capacity = newCapacity; } else if (newCapacity <= shortStringCapacity()) { - new (this) BasicSmallString{m_data.allocated.data.pointer, m_data.allocated.data.size}; + new (this) BasicSmallString{m_data.reference.pointer, m_data.reference.size}; } else { const size_type oldSize = size(); newCapacity = std::max(newCapacity, oldSize); @@ -286,7 +286,7 @@ public: char *newData = Memory::allocate(newCapacity + 1); std::char_traits::copy(newData, oldData, oldSize); - m_data.allocated.data.pointer = newData; + m_data.reference.pointer = newData; initializeLongString(oldSize, newCapacity); } } @@ -307,12 +307,12 @@ public: char *data() noexcept { - return Q_LIKELY(isShortString()) ? m_data.shortString.string : m_data.allocated.data.pointer; + return Q_LIKELY(isShortString()) ? m_data.shortString : m_data.reference.pointer; } const char *data() const noexcept { - return Q_LIKELY(isShortString()) ? m_data.shortString.string : m_data.allocated.data.pointer; + return Q_LIKELY(isShortString()) ? m_data.shortString : m_data.reference.pointer; } const char *constData() const noexcept @@ -442,15 +442,15 @@ public: size_type size() const noexcept { if (!isShortString()) - return m_data.allocated.data.size; + return m_data.reference.size; - return m_data.shortString.control.shortStringSize(); + return m_data.control.shortStringSize(); } size_type capacity() const noexcept { if (!isShortString()) - return m_data.allocated.data.capacity; + return m_data.reference.capacity; return shortStringCapacity(); } @@ -603,11 +603,7 @@ public: return size; } - constexpr - size_type shortStringSize() const - { - return m_data.shortString.control.shortStringSize(); - } + constexpr size_type shortStringSize() const { return m_data.control.shortStringSize(); } static BasicSmallString join(std::initializer_list list) @@ -721,13 +717,13 @@ unittest_public: constexpr bool isShortString() const noexcept { - return m_data.shortString.control.isShortString(); + return m_data.control.isShortString(); } constexpr bool isReadOnlyReference() const noexcept { - return m_data.shortString.control.isReadOnlyReference(); + return m_data.control.isReadOnlyReference(); } constexpr @@ -739,7 +735,7 @@ unittest_public: bool fitsNotInCapacity(size_type capacity) const noexcept { return (isShortString() && capacity > shortStringCapacity()) - || (!isShortString() && capacity > m_data.allocated.data.capacity); + || (!isShortString() && capacity > m_data.reference.capacity); } static @@ -802,12 +798,12 @@ private: constexpr void initializeLongString(size_type size, size_type capacity) { - m_data.allocated.data.pointer[size] = 0; - m_data.allocated.data.size = size; - m_data.allocated.data.capacity = capacity; - m_data.shortString.control.setShortStringSize(0); - m_data.shortString.control.setIsReference(true); - m_data.shortString.control.setIsReadOnlyReference(false); + m_data.reference.pointer[size] = 0; + m_data.reference.size = size; + m_data.reference.capacity = capacity; + m_data.control.setShortStringSize(0); + m_data.control.setIsReference(true); + m_data.control.setIsReadOnlyReference(false); } char &at(size_type index) @@ -945,9 +941,9 @@ private: void setSize(size_type size) { if (isShortString()) - m_data.shortString.control.setShortStringSize(size); + m_data.control.setShortStringSize(size); else - m_data.allocated.data.size = size; + m_data.reference.size = size; } private: diff --git a/src/libs/utils/smallstringlayout.h b/src/libs/utils/smallstringlayout.h index 7d6190528ef..c1de97e109f 100644 --- a/src/libs/utils/smallstringlayout.h +++ b/src/libs/utils/smallstringlayout.h @@ -96,56 +96,17 @@ private: ControlType m_isReference : 1; }; -template -struct alignas(16) AllocatedLayout -{ - struct Data - { +struct ReferenceLayout +{ union { + const char *constPointer; char *pointer; - size_type size; - size_type capacity; }; - - ControlBlock control; - Data data; -}; - -template -struct alignas(16) ReferenceLayout -{ - constexpr ReferenceLayout() noexcept = default; - constexpr ReferenceLayout(const char *stringPointer, - size_type size, - size_type capacity) noexcept - : control(0, true, true), - data{stringPointer, size, capacity} - {} - - struct Data - { - const char *pointer; - size_type size; - size_type capacity; - }; - - ControlBlock control; - Data data; -}; - -template -struct alignas(16) ShortStringLayout -{ - constexpr ShortStringLayout( - typename ControlBlock::SizeType shortStringSize) noexcept - : control(shortStringSize, false, false) - {} - - ControlBlock control; - char string[MaximumShortStringDataAreaSize]; + size_type size; + size_type capacity; }; template -struct StringDataLayout +struct alignas(16) StringDataLayout { static_assert(MaximumShortStringDataAreaSize >= 15, "Size must be greater equal than 15 bytes!"); static_assert(MaximumShortStringDataAreaSize < 32, "Size must be less than 32 bytes!"); @@ -156,17 +117,19 @@ struct StringDataLayout StringDataLayout() noexcept { reset(); } constexpr StringDataLayout(const char *string, size_type size) noexcept - : reference(string, size, 0) + : control{0, true, true} + , reference{{string}, size, 0} {} template constexpr StringDataLayout(const char (&string)[Size]) noexcept { if constexpr (Size <= MaximumShortStringDataAreaSize) { - shortString = {Size - 1}; + control = {Size - 1, false, false}; for (size_type i = 0; i < Size; ++i) - shortString.string[i] = string[i]; + shortString[i] = string[i]; } else { + control = {0, true, true}; reference = {string, Size - 1, 0}; } } @@ -178,20 +141,27 @@ struct StringDataLayout constexpr void reset() { - shortString.control = ControlBlock(); - shortString.string[0] = '\0'; + control = ControlBlock(); + shortString[0] = '\0'; } +#pragma pack(push) +#pragma pack(1) + ControlBlock control; union { - AllocatedLayout allocated; - ReferenceLayout reference; - ShortStringLayout shortString; + char shortString[MaximumShortStringDataAreaSize]; + struct + { + char dummy[sizeof(void *) - sizeof(ControlBlock)]; + ReferenceLayout reference; + }; }; +#pragma pack(pop) }; template -struct StringDataLayout= 32>> +struct alignas(16) StringDataLayout= 32>> { static_assert(MaximumShortStringDataAreaSize > 31, "Size must be greater than 31 bytes!"); static_assert(MaximumShortStringDataAreaSize < 64 @@ -203,17 +173,19 @@ struct StringDataLayout constexpr StringDataLayout(const char (&string)[Size]) noexcept { if constexpr (Size <= MaximumShortStringDataAreaSize) { - shortString = {Size - 1}; + control = {Size - 1, false, false}; for (size_type i = 0; i < Size; ++i) - shortString.string[i] = string[i]; + shortString[i] = string[i]; } else { + control = {0, true, true}; reference = {string, Size - 1, 0}; } } @@ -223,11 +195,9 @@ struct StringDataLayout); - auto shortStringLayoutSize = other.shortString.control.stringSize() + controlBlockSize; - constexpr auto referenceLayoutSize = sizeof(ReferenceLayout); - std::memcpy(&shortString, - &other.shortString, - std::max(shortStringLayoutSize, referenceLayoutSize)); + auto shortStringLayoutSize = other.control.stringSize() + controlBlockSize; + constexpr auto referenceLayoutSize = sizeof(ReferenceLayout); + std::memcpy(this, &other, std::max(shortStringLayoutSize, referenceLayoutSize)); return *this; } @@ -239,15 +209,22 @@ struct StringDataLayout(); - shortString.string[0] = '\0'; + control = ControlBlock(); + shortString[0] = '\0'; } +#pragma pack(push) +#pragma pack(1) + ControlBlock control; union { - AllocatedLayout allocated; - ReferenceLayout reference; - ShortStringLayout shortString; + char shortString[MaximumShortStringDataAreaSize]; + struct + { + char dummy[sizeof(void *) - sizeof(ControlBlock)]; + ReferenceLayout reference; + }; }; +#pragma pack(pop) }; } // namespace Internal diff --git a/src/libs/utils/smallstringliteral.h b/src/libs/utils/smallstringliteral.h index cf0c4953fb8..c29a7cf8f85 100644 --- a/src/libs/utils/smallstringliteral.h +++ b/src/libs/utils/smallstringliteral.h @@ -58,12 +58,12 @@ public: const char *data() const noexcept { - return Q_LIKELY(isShortString()) ? m_data.shortString.string : m_data.allocated.data.pointer; + return Q_LIKELY(isShortString()) ? m_data.shortString : m_data.reference.constPointer; } size_type size() const noexcept { - return Q_LIKELY(isShortString()) ? m_data.shortString.control.shortStringSize() : m_data.allocated.data.size; + return Q_LIKELY(isShortString()) ? m_data.control.shortStringSize() : m_data.reference.size; } constexpr @@ -94,16 +94,12 @@ public: return Internal::StringDataLayout::shortStringCapacity(); } - constexpr - bool isShortString() const noexcept - { - return m_data.shortString.control.isShortString(); - } + constexpr bool isShortString() const noexcept { return m_data.control.isShortString(); } constexpr bool isReadOnlyReference() const noexcept { - return m_data.shortString.control.isReadOnlyReference(); + return m_data.control.isReadOnlyReference(); } constexpr