Skip to content

Commit a2bcb9d

Browse files
committed
Everywhere: De-virtualize IGrowableBuffer destruction
- Avoid virtual call in the destructor used for finalization (mainly null-termination) - Avoid one more indirection to fetch the virtual table - Use the entire available capacity when asked This changes a little bit StringBuilder and SerializationJSON API.
1 parent 4421c8e commit a2bcb9d

File tree

37 files changed

+322
-398
lines changed

37 files changed

+322
-398
lines changed

Documentation/Libraries/Strings.md

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ Some relevant blog posts are:
4747

4848
# Definition
4949

50+
## StringBuilder
51+
@copydoc SC::StringBuilder
52+
5053
## StringView
5154
@copydoc SC::StringView
5255

@@ -104,21 +107,6 @@ Some relevant blog posts are:
104107
### StringViewTokenizer::countTokens
105108
@copydoc SC::StringViewTokenizer::countTokens
106109

107-
## StringBuilder
108-
@copydoc SC::StringBuilder
109-
110-
### StringBuilder::format
111-
@copydoc SC::StringBuilder::format
112-
113-
### StringBuilder::append
114-
@copydoc SC::StringBuilder::append
115-
116-
### StringBuilder::appendReplaceAll
117-
@copydoc SC::StringBuilder::appendReplaceAll
118-
119-
### StringBuilder::appendHex
120-
@copydoc SC::StringBuilder::appendHex
121-
122110
## StringIterator
123111
@copydoc SC::StringIterator
124112

Examples/SCExample/Examples/SerializationExample/SerializationExample.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,9 +174,8 @@ struct SC::SerializationExampleModel
174174

175175
Result saveToJSONFile(StringView jsonPath)
176176
{
177-
Buffer buffer;
178-
StringFormatOutput output(StringEncoding::Ascii, buffer);
179-
SC_TRY(SC::SerializationJson::write(modelState, output));
177+
Buffer buffer;
178+
SC_TRY(SC::SerializationJson::write(modelState, buffer));
180179
Span<const char> jsonSpan;
181180
SC_TRY(buffer.toSpanConst().sliceStartLength(0, buffer.size() - 1, jsonSpan));
182181
return FileSystem().writeString(jsonPath, {jsonSpan, false, StringEncoding::Ascii});

Libraries/Foundation/AlignedStorage.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,6 @@ struct AlignedStorage
6262
private:
6363
alignas(Alignment) char bytes[N] = {0};
6464
};
65-
// Allows using this type across Plugin boundaries
66-
SC_COMPILER_EXTERN template struct SC_COMPILER_EXPORT AlignedStorage<6 * sizeof(void*)>;
6765

6866
//! @}
6967
#if SC_COMPILER_GCC

Libraries/Foundation/Internal/IGrowableBuffer.h

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,47 +4,48 @@
44
#include "../../Foundation/PrimitiveTypes.h"
55
namespace SC
66
{
7-
/// @brief Virtual interface to abstract a linear binary buffer.
7+
/// @brief Interface abstracting a linear binary buffer.
88
/// It's used by File library to read data into a Buffer or String object without need to know their types.
99
/// This allows breaking the dependency between File and Strings / Memory libraries.
1010
struct SC_COMPILER_EXPORT IGrowableBuffer
1111
{
12+
using TryGrowFunc = bool (*)(IGrowableBuffer& growableBuffer, size_t newSize);
13+
1214
struct DirectAccess
1315
{
1416
size_t sizeInBytes = 0;
1517
size_t capacityInBytes = 0;
1618
void* data = nullptr;
1719
};
1820

19-
/// Note: derived classes should set their size == to match directAccess.sizeInBytes during destructor
20-
virtual ~IGrowableBuffer() = default;
21+
IGrowableBuffer(TryGrowFunc func) : ptrTryGrowTo(func) {}
2122

2223
[[nodiscard]] bool resizeWithoutInitializing(size_t newSize)
2324
{
24-
if (newSize < directAccess.capacityInBytes) // try to avoid a virtual call
25+
if (newSize <= directAccess.capacityInBytes) // try to avoid a virtual call
2526
{
2627
directAccess.sizeInBytes = newSize; // size on type erased buffer will be set by virtual destructor
2728
return true;
2829
}
29-
return tryGrowTo(newSize);
30+
return ptrTryGrowTo(*this, newSize);
3031
}
32+
3133
void clear() { directAccess.sizeInBytes = 0; }
3234
char* data() const { return static_cast<char*>(directAccess.data); }
3335
size_t size() const { return directAccess.sizeInBytes; }
34-
35-
DirectAccess getDirectAccess() const { return directAccess; }
36+
auto getDirectAccess() const { return directAccess; }
3637

3738
protected:
38-
/// @brief Try to grow the buffer to a new size, and update directAccess.
39-
/// @param newSize The desired new size for the buffer.
40-
/// @return `true` if the buffer was successfully grown, `false` otherwise.
41-
[[nodiscard]] virtual bool tryGrowTo(size_t newSize) = 0;
42-
39+
TryGrowFunc ptrTryGrowTo;
4340
DirectAccess directAccess;
4441
};
4542

4643
/// @brief Partial specialize GrowableBuffer deriving from IGrowableBuffer (see how String and Buffer do it)
4744
template <typename T>
48-
struct GrowableBuffer;
45+
struct GrowableBuffer
46+
{
47+
T& content;
48+
GrowableBuffer(T& content) : content(content) {}
49+
};
4950

5051
} // namespace SC

Libraries/Foundation/Internal/StringPath.inl

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,25 @@
22
// SPDX-License-Identifier: MIT
33
#include "../../Foundation/StringPath.h"
44

5-
SC::GrowableBuffer<SC::StringPath>::GrowableBuffer(StringPath& string) : sp(string)
5+
SC::GrowableBuffer<SC::StringPath>::GrowableBuffer(StringPath& string) noexcept
6+
: IGrowableBuffer(&GrowableBuffer::tryGrowTo), sp(string)
67
{
78
directAccess = {sp.view().sizeInBytes(), (StringPath::MaxPath - 1) * sizeof(native_char_t),
89
sp.writableSpan().data()};
910
}
1011

11-
SC::GrowableBuffer<SC::StringPath>::~GrowableBuffer()
12+
SC::GrowableBuffer<SC::StringPath>::~GrowableBuffer() noexcept { finalize(); }
13+
14+
void SC::GrowableBuffer<SC::StringPath>::finalize() noexcept
1215
{
1316
(void)sp.resize(directAccess.sizeInBytes / sizeof(native_char_t));
1417
}
1518

16-
bool SC::GrowableBuffer<SC::StringPath>::tryGrowTo(size_t newSize)
19+
bool SC::GrowableBuffer<SC::StringPath>::tryGrowTo(IGrowableBuffer& gb, size_t newSize) noexcept
1720
{
18-
const bool res = sp.resize(newSize / sizeof(native_char_t));
19-
directAccess.sizeInBytes = sp.view().sizeInBytes();
21+
GrowableBuffer& self = static_cast<GrowableBuffer&>(gb);
22+
const bool res = self.sp.resize(newSize / sizeof(native_char_t));
23+
self.directAccess.sizeInBytes = self.sp.view().sizeInBytes();
2024
return res;
2125
}
2226

Libraries/Foundation/StringPath.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,13 @@ struct SC_COMPILER_EXPORT StringPath
6363
};
6464

6565
template <>
66-
struct SC_COMPILER_EXPORT GrowableBuffer<StringPath> final : public IGrowableBuffer
66+
struct SC_COMPILER_EXPORT GrowableBuffer<StringPath> : public IGrowableBuffer
6767
{
6868
StringPath& sp;
69-
GrowableBuffer(StringPath& string);
70-
virtual ~GrowableBuffer() override;
71-
virtual bool tryGrowTo(size_t newSize) override;
72-
static auto getEncodingFor(const StringPath& sp) { return sp.getEncoding(); }
69+
GrowableBuffer(StringPath& string) noexcept;
70+
~GrowableBuffer() noexcept;
71+
static bool tryGrowTo(IGrowableBuffer& gb, size_t newSize) noexcept;
72+
static auto getEncodingFor(const StringPath& sp) noexcept { return sp.getEncoding(); }
73+
void finalize() noexcept;
7374
};
7475
} // namespace SC

Libraries/Memory/Buffer.h

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,29 +68,33 @@ struct SC_COMPILER_EXPORT Buffer;
6868

6969
// Enables File library from reading data from file descriptor into a Buffer
7070
template <>
71-
struct SC_COMPILER_EXPORT GrowableBuffer<Buffer> final : public IGrowableBuffer
71+
struct SC_COMPILER_EXPORT GrowableBuffer<Buffer> : public IGrowableBuffer
7272
{
7373
Buffer& buffer;
74-
GrowableBuffer(Buffer& buffer);
75-
~GrowableBuffer();
76-
virtual bool tryGrowTo(size_t newSize) override;
74+
GrowableBuffer(Buffer& buffer) noexcept;
75+
~GrowableBuffer() noexcept;
76+
static bool tryGrowTo(IGrowableBuffer&, size_t newSize) noexcept;
77+
void finalize() noexcept;
7778
};
7879

7980
template <int N>
80-
struct SC_COMPILER_EXPORT GrowableBuffer<SmallBuffer<N>> final : public IGrowableBuffer
81+
struct SC_COMPILER_EXPORT GrowableBuffer<SmallBuffer<N>> : public IGrowableBuffer
8182
{
8283
Buffer& buffer;
83-
GrowableBuffer(Buffer& buffer) : buffer(buffer)
84+
GrowableBuffer(Buffer& buffer) noexcept : IGrowableBuffer(&GrowableBuffer::tryGrowTo), buffer(buffer)
8485
{
8586
IGrowableBuffer::directAccess = {buffer.size(), buffer.capacity(), buffer.data()};
8687
}
87-
~GrowableBuffer() { (void)buffer.resizeWithoutInitializing(IGrowableBuffer::directAccess.sizeInBytes); }
88-
virtual bool tryGrowTo(size_t newSize) override
88+
~GrowableBuffer() noexcept { finalize(); }
89+
90+
static bool tryGrowTo(IGrowableBuffer& gb, size_t newSize) noexcept
8991
{
90-
const bool result = buffer.resizeWithoutInitializing(newSize);
91-
IGrowableBuffer::directAccess = {buffer.size(), buffer.capacity(), buffer.data()};
92+
GrowableBuffer& self = static_cast<GrowableBuffer&>(gb);
93+
const bool result = self.buffer.resizeWithoutInitializing(newSize);
94+
self.directAccess = {self.buffer.size(), self.buffer.capacity(), self.buffer.data()};
9295
return result;
9396
}
97+
void finalize() noexcept { (void)buffer.resizeWithoutInitializing(IGrowableBuffer::directAccess.sizeInBytes); }
9498
};
9599
//! @}
96100
} // namespace SC

Libraries/Memory/Internal/Buffer.inl

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,29 @@ template struct Segment<detail::SegmentBuffer>;
1111
template bool Segment<detail::SegmentBuffer>::assign<char>(Span<const char>) noexcept;
1212
template bool Segment<detail::SegmentBuffer>::append<char>(Span<const char>) noexcept;
1313

14-
GrowableBuffer<Buffer>::GrowableBuffer(Buffer& buffer) : buffer(buffer)
14+
GrowableBuffer<Buffer>::GrowableBuffer(Buffer& buffer) noexcept
15+
: IGrowableBuffer(&GrowableBuffer::tryGrowTo), buffer(buffer)
1516
{
1617
IGrowableBuffer::directAccess = {buffer.size(), buffer.capacity(), buffer.data()};
1718
}
1819

19-
GrowableBuffer<Buffer>::~GrowableBuffer()
20+
GrowableBuffer<Buffer>::~GrowableBuffer() noexcept { finalize(); }
21+
22+
void GrowableBuffer<Buffer>::finalize() noexcept
2023
{
2124
if (buffer.size() != IGrowableBuffer::directAccess.sizeInBytes)
2225
{
2326
(void)buffer.resizeWithoutInitializing(IGrowableBuffer::directAccess.sizeInBytes);
2427
}
2528
}
2629

27-
bool GrowableBuffer<Buffer>::tryGrowTo(size_t newSize)
30+
bool GrowableBuffer<Buffer>::tryGrowTo(IGrowableBuffer& gb, size_t newSize) noexcept
2831
{
32+
GrowableBuffer& self = static_cast<GrowableBuffer&>(gb);
2933
// ensure size is correct before trying to grow to avoid losing data
30-
(void)buffer.resizeWithoutInitializing(IGrowableBuffer::directAccess.sizeInBytes);
31-
const bool result = buffer.resizeWithoutInitializing(newSize);
32-
IGrowableBuffer::directAccess = {buffer.size(), buffer.capacity(), buffer.data()};
34+
(void)self.buffer.resizeWithoutInitializing(self.directAccess.sizeInBytes);
35+
const bool result = self.buffer.resizeWithoutInitializing(newSize);
36+
self.directAccess = {self.buffer.size(), self.buffer.capacity(), self.buffer.data()};
3337
return result;
3438
}
3539

Libraries/Memory/Internal/String.inl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,15 +78,17 @@ SC::StringSpan SC::String::view() const SC_LANGUAGE_LIFETIME_BOUND
7878
return StringSpan({items, isEmpty ? 0 : data.size() - StringEncodingGetSize(encoding)}, not isEmpty, encoding);
7979
}
8080

81-
SC::String::GrowableImplementation::GrowableImplementation(String& string, IGrowableBuffer::DirectAccess& da)
81+
SC::String::GrowableImplementation::GrowableImplementation(String& string, IGrowableBuffer::DirectAccess& da) noexcept
8282
: string(string), da(da)
8383
{
8484
const size_t numZeros = StringEncodingGetSize(string.getEncoding());
8585
da = {string.data.isEmpty() ? 0 : string.data.size() - numZeros,
8686
string.data.capacity() > numZeros ? string.data.capacity() - numZeros : 0, string.data.data()};
8787
}
8888

89-
SC::String::GrowableImplementation::~GrowableImplementation()
89+
SC::String::GrowableImplementation::~GrowableImplementation() noexcept { finalize(); }
90+
91+
void SC::String::GrowableImplementation::finalize() noexcept
9092
{
9193
const size_t numZeros = StringEncodingGetSize(string.getEncoding());
9294
if (da.sizeInBytes == 0)
@@ -103,7 +105,7 @@ SC::String::GrowableImplementation::~GrowableImplementation()
103105
}
104106
}
105107

106-
bool SC::String::GrowableImplementation::tryGrowTo(size_t newSize)
108+
bool SC::String::GrowableImplementation::tryGrowTo(size_t newSize) noexcept
107109
{
108110
// ensure size is correct before trying to grow to avoid losing data
109111
(void)string.data.resizeWithoutInitializing(da.sizeInBytes);

Libraries/Memory/String.h

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,10 @@ struct SC::String
156156

157157
IGrowableBuffer::DirectAccess& da;
158158

159-
GrowableImplementation(String& string, IGrowableBuffer::DirectAccess& da);
160-
~GrowableImplementation();
161-
bool tryGrowTo(size_t newSize);
159+
GrowableImplementation(String& string, IGrowableBuffer::DirectAccess& da) noexcept;
160+
~GrowableImplementation() noexcept;
161+
void finalize() noexcept;
162+
bool tryGrowTo(size_t newSize) noexcept;
162163
};
163164

164165
StringEncoding encoding;
@@ -208,20 +209,32 @@ SC_COMPILER_EXTERN template struct SC_COMPILER_EXPORT SmallString<1024 * sizeof(
208209

209210
// Enables File library from reading data from file descriptor into a String
210211
template <>
211-
struct SC_COMPILER_EXPORT GrowableBuffer<String> final : public IGrowableBuffer
212+
struct SC_COMPILER_EXPORT GrowableBuffer<String> : public IGrowableBuffer
212213
{
213214
String::GrowableImplementation gi;
214-
GrowableBuffer(String& string) : gi(string, IGrowableBuffer::directAccess) {}
215-
virtual bool tryGrowTo(size_t newSize) override { return gi.tryGrowTo(newSize); }
216-
static auto getEncodingFor(const String& str) { return str.getEncoding(); }
215+
GrowableBuffer(String& string)
216+
: IGrowableBuffer(&GrowableBuffer::tryGrowTo), gi(string, IGrowableBuffer::directAccess)
217+
{}
218+
static bool tryGrowTo(IGrowableBuffer& gb, size_t newSize) noexcept
219+
{
220+
return static_cast<GrowableBuffer&>(gb).gi.tryGrowTo(newSize);
221+
}
222+
static auto getEncodingFor(const String& str) noexcept { return str.getEncoding(); }
223+
void finalize() noexcept { gi.finalize(); }
217224
};
218225

219226
template <int N>
220-
struct SC_COMPILER_EXPORT GrowableBuffer<SmallString<N>> final : public IGrowableBuffer
227+
struct SC_COMPILER_EXPORT GrowableBuffer<SmallString<N>> : public IGrowableBuffer
221228
{
222229
String::GrowableImplementation gi;
223-
GrowableBuffer(String& string) : gi(string, IGrowableBuffer::directAccess) {}
224-
virtual bool tryGrowTo(size_t newSize) override { return gi.tryGrowTo(newSize); }
225-
static auto getEncodingFor(const SmallString<N>& str) { return str.getEncoding(); }
230+
GrowableBuffer(String& string)
231+
: IGrowableBuffer(&GrowableBuffer::tryGrowTo), gi(string, IGrowableBuffer::directAccess)
232+
{}
233+
static bool tryGrowTo(IGrowableBuffer& gb, size_t newSize) noexcept
234+
{
235+
return static_cast<GrowableBuffer&>(gb).gi.tryGrowTo(newSize);
236+
}
237+
static auto getEncodingFor(const SmallString<N>& str) noexcept { return str.getEncoding(); }
238+
void finalize() noexcept { gi.finalize(); }
226239
};
227240
} // namespace SC

0 commit comments

Comments
 (0)