Utils: Fix long small string

We used only 6 bit to save the short size but for SmallString with a size
over 64 it is not enough. So we have now to use a uint16 instead of a
uint8 if the size if over 64.

Change-Id: I53558e492b6cb40b739b23a8af83d192a2e11bd2
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
Marco Bubke
2017-01-31 12:18:08 +01:00
parent 729c535376
commit a0c69c517c
7 changed files with 61 additions and 19 deletions

View File

@@ -55,7 +55,7 @@ namespace Utils {
template <uint Size> template <uint Size>
class BasicSmallString; class BasicSmallString;
using SmallString = BasicSmallString<31>; using SmallString = BasicSmallString<31>;
using PathString = BasicSmallString<191>; using PathString = BasicSmallString<190>;
} }
namespace ClangBackEnd { namespace ClangBackEnd {

View File

@@ -65,6 +65,11 @@ 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;
static_assert(Size < 64
? sizeof(Internal::StringDataLayout<Size>) == Size + 1
: sizeof(Internal::StringDataLayout<Size>) == Size + 2,
"Size is wrong");
BasicSmallString() noexcept BasicSmallString() noexcept
: m_data(Internal::StringDataLayout<Size>()) : m_data(Internal::StringDataLayout<Size>())
{ {
@@ -498,7 +503,7 @@ public:
constexpr static constexpr static
size_type shortStringCapacity() noexcept size_type shortStringCapacity() noexcept
{ {
return BasicSmallStringLiteral<Size>::shortStringCapacity(); return Internal::StringDataLayout<Size>::shortStringCapacity();
} }
size_type optimalCapacity(const size_type size) size_type optimalCapacity(const size_type size)
@@ -897,6 +902,6 @@ std::vector<Type> clone(const std::vector<Type> &vector)
} }
using SmallString = BasicSmallString<31>; using SmallString = BasicSmallString<31>;
using PathString = BasicSmallString<191>; using PathString = BasicSmallString<190>;
} // namespace Utils } // namespace Utils

View File

@@ -28,6 +28,7 @@
#include <QtGlobal> #include <QtGlobal>
#include <cstdint> #include <cstdint>
#include <type_traits>
#ifdef Q_CC_MSVC #ifdef Q_CC_MSVC
# define ALIGNAS_16 # define ALIGNAS_16
@@ -41,7 +42,19 @@ namespace Internal {
using size_type = std::size_t; using size_type = std::size_t;
template <uint MaximumShortStringDataAreaSize> template<bool Bool>
struct block_type
{
using type = uint8_t;
};
template<>
struct block_type<false> {
using type = uint16_t;
};
template <uint MaximumShortStringDataAreaSize,
typename BlockType = typename block_type<(MaximumShortStringDataAreaSize < 64)>::type>
struct AllocatedLayout { struct AllocatedLayout {
struct Data { struct Data {
char *pointer; char *pointer;
@@ -49,12 +62,13 @@ struct AllocatedLayout {
size_type capacity; size_type capacity;
} data; } data;
char dummy[MaximumShortStringDataAreaSize - sizeof(Data)]; char dummy[MaximumShortStringDataAreaSize - sizeof(Data)];
std::uint8_t shortStringSize: 6; BlockType shortStringSize : (sizeof(BlockType) * 8) - 2;
std::uint8_t isReadOnlyReference : 1; BlockType isReadOnlyReference : 1;
std::uint8_t isReference : 1; BlockType isReference : 1;
}; };
template <uint MaximumShortStringDataAreaSize> template <uint MaximumShortStringDataAreaSize,
typename BlockType = typename block_type<(MaximumShortStringDataAreaSize < 64)>::type>
struct ReferenceLayout { struct ReferenceLayout {
struct Data { struct Data {
const char *pointer; const char *pointer;
@@ -62,23 +76,28 @@ struct ReferenceLayout {
size_type capacity; size_type capacity;
} data; } data;
char dummy[MaximumShortStringDataAreaSize - sizeof(Data)]; char dummy[MaximumShortStringDataAreaSize - sizeof(Data)];
std::uint8_t shortStringSize: 6; BlockType shortStringSize : (sizeof(BlockType) * 8) - 2;
std::uint8_t isReadOnlyReference : 1; BlockType isReadOnlyReference : 1;
std::uint8_t isReference : 1; BlockType isReference : 1;
}; };
template <uint MaximumShortStringDataAreaSize> template <uint MaximumShortStringDataAreaSize,
typename BlockType = typename block_type<(MaximumShortStringDataAreaSize < 64)>::type>
struct ShortStringLayout { struct ShortStringLayout {
char string[MaximumShortStringDataAreaSize]; char string[MaximumShortStringDataAreaSize];
std::uint8_t shortStringSize: 6; BlockType shortStringSize : (sizeof(BlockType) * 8) - 2;
std::uint8_t isReadOnlyReference : 1; BlockType isReadOnlyReference : 1;
std::uint8_t isReference : 1; BlockType isReference : 1;
}; };
template <uint MaximumShortStringDataAreaSize> 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 >= 15, "Size must be greater equal than 15 bytes!");
static_assert(((MaximumShortStringDataAreaSize + 1) % 16) == 0, "Size + 1 must be dividable by 16!"); static_assert(MaximumShortStringDataAreaSize < 64
? ((MaximumShortStringDataAreaSize + 1) % 16) == 0
: ((MaximumShortStringDataAreaSize + 2) % 16) == 0,
"Size + 1 must be dividable by 16 if under 64 and Size + 2 must be dividable by 16 if over 64!");
StringDataLayout() noexcept = default; StringDataLayout() noexcept = default;
constexpr StringDataLayout(const char *string, constexpr StringDataLayout(const char *string,
@@ -117,6 +136,14 @@ struct ALIGNAS_16 StringDataLayout {
#endif #endif
} }
constexpr static
size_type shortStringCapacity() noexcept
{
return MaximumShortStringDataAreaSize < 64
? MaximumShortStringDataAreaSize - 1
: MaximumShortStringDataAreaSize - 2;
}
union { union {
AllocatedLayout<MaximumShortStringDataAreaSize> allocated; AllocatedLayout<MaximumShortStringDataAreaSize> allocated;
ReferenceLayout<MaximumShortStringDataAreaSize> reference; ReferenceLayout<MaximumShortStringDataAreaSize> reference;

View File

@@ -89,7 +89,7 @@ public:
constexpr static constexpr static
size_type shortStringCapacity() noexcept size_type shortStringCapacity() noexcept
{ {
return sizeof(Internal::ShortStringLayout<Size>) - 2; return Internal::StringDataLayout<Size>::shortStringCapacity();
} }
bool isShortString() const noexcept bool isShortString() const noexcept

View File

@@ -154,5 +154,5 @@ private:
}; };
using SmallStringVector = BasicSmallStringVector<31>; using SmallStringVector = BasicSmallStringVector<31>;
using PathStringVector = BasicSmallStringVector<191>; using PathStringVector = BasicSmallStringVector<190>;
} // namespace Utils; } // namespace Utils;

View File

@@ -44,7 +44,7 @@ namespace Utils {
template <uint Size> template <uint Size>
class BasicSmallString; class BasicSmallString;
using SmallString = BasicSmallString<31>; using SmallString = BasicSmallString<31>;
using PathString = BasicSmallString<191>; using PathString = BasicSmallString<190>;
} }
namespace ClangBackEnd { namespace ClangBackEnd {

View File

@@ -33,6 +33,7 @@
using namespace ::testing; using namespace ::testing;
using Utils::PathString;
using Utils::SmallString; using Utils::SmallString;
using Utils::SmallStringLiteral; using Utils::SmallStringLiteral;
using Utils::SmallStringView; using Utils::SmallStringView;
@@ -445,6 +446,15 @@ TEST(SmallString, SizeShortSmallString)
ASSERT_THAT(size, 4); ASSERT_THAT(size, 4);
} }
TEST(SmallString, SizeShortPathString)
{
SmallString shortPath("very very very very very very very very very very very long path which fits in the short memory");
auto size = shortPath.size();
ASSERT_THAT(size, 95);
}
TEST(SmallString, SizeLongSmallString) TEST(SmallString, SizeLongSmallString)
{ {
auto longText = SmallString::fromUtf8("very very very very very very very very very very very long string"); auto longText = SmallString::fromUtf8("very very very very very very very very very very very long string");