Skip to content

Commit 5a5168a

Browse files
committed
CString tidy cleanup and rvalue methods
We add rvalue version of append/insert/replace so that calling those on a temporary yields to a temporary. Also add string literal versions of those so that we can append/insert/replace a literal without allocation, e.g.: `CString foo = CString{ bar }.append("baz");` This will not end-up creating a temporary CString for "baz".
1 parent e85dfe7 commit 5a5168a

File tree

2 files changed

+97
-33
lines changed

2 files changed

+97
-33
lines changed

libs/utils/include/utils/CString.h

Lines changed: 72 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <utils/ostream.h>
2424

2525
#include <string_view>
26+
#include <utility>
2627

2728
#include <assert.h>
2829
#include <stddef.h>
@@ -97,11 +98,7 @@ class UTILS_PUBLIC CString {
9798
return *this;
9899
}
99100

100-
~CString() noexcept {
101-
if (mData) {
102-
free(mData - 1);
103-
}
104-
}
101+
~CString() noexcept;
105102

106103
void swap(CString& other) noexcept {
107104
// don't use std::swap(), we don't want an STL dependency in this file
@@ -110,42 +107,98 @@ class UTILS_PUBLIC CString {
110107
other.mCStr = temp;
111108
}
112109

113-
const_pointer c_str() const noexcept { return mCStr; }
114110
pointer c_str() noexcept { return mCStr; }
111+
const_pointer c_str() const noexcept { return const_cast<CString*>(this)->c_str(); }
115112
const_pointer c_str_safe() const noexcept { return mData ? c_str() : ""; }
116113
const_pointer data() const noexcept { return c_str(); }
117114
pointer data() noexcept { return c_str(); }
118115
size_type size() const noexcept { return mData ? mData[-1].length : 0; }
119116
size_type length() const noexcept { return size(); }
120117
bool empty() const noexcept { return size() == 0; }
121118

122-
iterator begin() noexcept { return mCStr; }
119+
iterator begin() noexcept { return c_str(); }
123120
iterator end() noexcept { return begin() + length(); }
124121
const_iterator begin() const noexcept { return data(); }
125122
const_iterator end() const noexcept { return begin() + length(); }
126123
const_iterator cbegin() const noexcept { return begin(); }
127124
const_iterator cend() const noexcept { return end(); }
128125

129-
CString& replace(size_type pos, size_type len, const CString& str) noexcept;
130-
CString& insert(size_type pos, const CString& str) noexcept { return replace(pos, 0, str); }
131-
CString& append(const CString& str) noexcept { return insert(length(), str); }
126+
// replace
127+
template<size_t N>
128+
CString& replace(size_type const pos,
129+
size_type const len, const StringLiteral<N>& str) & noexcept {
130+
return replace(pos, len, str, N - 1);
131+
}
132+
133+
CString& replace(size_type const pos, size_type const len, const CString& str) & noexcept {
134+
return replace(pos, len, str.c_str_safe(), str.size());
135+
}
136+
137+
template<size_t N>
138+
CString&& replace(size_type const pos,
139+
size_type const len, const StringLiteral<N>& str) && noexcept {
140+
return std::move(replace(pos, len, str));
141+
}
132142

133-
const_reference operator[](size_type pos) const noexcept {
143+
CString&& replace(size_type const pos, size_type const len, const CString& str) && noexcept {
144+
return std::move(replace(pos, len, str));
145+
}
146+
147+
// insert
148+
template<size_t N>
149+
CString& insert(size_type const pos, const StringLiteral<N>& str) & noexcept {
150+
return replace(pos, 0, str);
151+
}
152+
153+
CString& insert(size_type const pos, const CString& str) & noexcept {
154+
return replace(pos, 0, str);
155+
}
156+
157+
template<size_t N>
158+
CString&& insert(size_type const pos, const StringLiteral<N>& str) && noexcept {
159+
return std::move(*this).replace(pos, 0, str);
160+
}
161+
162+
CString&& insert(size_type const pos, const CString& str) && noexcept {
163+
return std::move(*this).replace(pos, 0, str);
164+
}
165+
166+
// append
167+
template<size_t N>
168+
CString& append(const StringLiteral<N>& str) & noexcept {
169+
return insert(length(), str);
170+
}
171+
172+
CString& append(const CString& str) & noexcept {
173+
return insert(length(), str);
174+
}
175+
176+
template<size_t N>
177+
CString&& append(const StringLiteral<N>& str) && noexcept {
178+
return std::move(*this).insert(length(), str);
179+
}
180+
181+
CString&& append(const CString& str) && noexcept {
182+
return std::move(*this).insert(length(), str);
183+
}
184+
185+
186+
const_reference operator[](size_type const pos) const noexcept {
134187
assert(pos < size());
135188
return begin()[pos];
136189
}
137190

138-
reference operator[](size_type pos) noexcept {
191+
reference operator[](size_type const pos) noexcept {
139192
assert(pos < size());
140193
return begin()[pos];
141194
}
142195

143-
const_reference at(size_type pos) const noexcept {
196+
const_reference at(size_type const pos) const noexcept {
144197
assert(pos < size());
145198
return begin()[pos];
146199
}
147200

148-
reference at(size_type pos) noexcept {
201+
reference at(size_type const pos) noexcept {
149202
assert(pos < size());
150203
return begin()[pos];
151204
}
@@ -171,7 +224,7 @@ class UTILS_PUBLIC CString {
171224
}
172225

173226
// placement new declared as "throw" to avoid the compiler's null-check
174-
inline void* operator new(size_t, void* ptr) {
227+
void* operator new(size_t, void* ptr) {
175228
assert(ptr);
176229
return ptr;
177230
}
@@ -185,6 +238,8 @@ class UTILS_PUBLIC CString {
185238
};
186239

187240
private:
241+
CString& replace(size_type pos, size_type len, char const* str, size_t l) & noexcept;
242+
188243
#if !defined(NDEBUG)
189244
friend io::ostream& operator<<(io::ostream& out, const CString& rhs);
190245
#endif
@@ -225,7 +280,7 @@ class UTILS_PUBLIC CString {
225280
}
226281
};
227282

228-
// implement this for your type for automatic conversion to CString. Failing to do so leads
283+
// Implement this for your type for automatic conversion to CString. Failing to do so leads
229284
// to a compile-time failure.
230285
template<typename T>
231286
CString to_string(T value) noexcept;
@@ -249,7 +304,7 @@ class UTILS_PUBLIC FixedSizeString {
249304
pointer c_str() noexcept { return mData; }
250305

251306
private:
252-
value_type mData[N] = {0};
307+
value_type mData[N] = {};
253308
};
254309

255310
} // namespace utils

libs/utils/src/CString.cpp

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,30 @@
2020
#include <utils/ostream.h>
2121

2222
#include <algorithm>
23+
#include <cstdlib>
2324
#include <memory>
2425

26+
2527
namespace utils {
2628

2729
UTILS_NOINLINE
28-
CString::CString(const char* cstr, size_t length) {
30+
CString::CString(const char* cstr, size_t const length) {
2931
if (length && cstr) {
30-
Data* p = (Data*)malloc(sizeof(Data) + length + 1);
31-
p->length = (size_type)length;
32-
mCStr = (value_type*)(p + 1);
32+
33+
Data* const p = static_cast<Data*>(std::malloc(sizeof(Data) + length + 1));
34+
p->length = size_type(length);
35+
mCStr = reinterpret_cast<value_type*>(p + 1);
3336
// we don't use memcpy here to avoid a call to libc, the generated code is pretty good.
3437
std::uninitialized_copy_n(cstr, length, mCStr);
3538
mCStr[length] = '\0';
3639
}
3740
}
3841

39-
CString::CString(size_t length) {
42+
CString::CString(size_t const length) {
4043
if (length) {
41-
Data* p = (Data*)malloc(sizeof(Data) + length + 1);
42-
p->length = (size_type)length;
43-
mCStr = (value_type*)(p + 1);
44+
Data* const p = static_cast<Data*>(std::malloc(sizeof(Data) + length + 1));
45+
p->length = size_type(length);
46+
mCStr = reinterpret_cast<value_type*>(p + 1);
4447
std::fill_n(mCStr, length, 0);
4548
mCStr[length] = '\0';
4649
}
@@ -58,24 +61,30 @@ CString& CString::operator=(const CString& rhs) {
5861
if (this != &rhs) {
5962
auto *const p = mData ? mData - 1 : nullptr;
6063
new(this) CString(rhs);
61-
free(p);
64+
std::free(p);
6265
}
6366
return *this;
6467
}
6568

66-
CString& CString::replace(size_type pos, size_type len, const CString& str) noexcept {
69+
CString::~CString() noexcept {
70+
if (mData) {
71+
std::free(mData - 1);
72+
}
73+
}
74+
75+
CString& CString::replace(size_type const pos, size_type len, char const* str, size_t const l) & noexcept {
6776
assert(pos <= size());
6877

6978
len = std::min(len, size() - pos);
7079

7180
// The new size of the string, after the replacement.
72-
const size_type newSize = size() - len + str.size();
81+
const size_type newSize = size() - len + l;
7382

7483
// Allocate enough memory to hold the new string.
75-
Data* p = (Data*) malloc(sizeof(Data) + newSize + 1);
84+
Data* const p = static_cast<Data*>(std::malloc(sizeof(Data) + newSize + 1));
7685
assert(p);
7786
p->length = newSize;
78-
value_type* newStr = (value_type*) (p + 1);
87+
value_type* newStr = reinterpret_cast<value_type*>(p + 1);
7988

8089
const value_type* beginning = mCStr;
8190
const value_type* replacementStart = mCStr + pos;
@@ -84,22 +93,22 @@ CString& CString::replace(size_type pos, size_type len, const CString& str) noex
8493

8594
value_type* ptr = newStr;
8695
ptr = std::uninitialized_copy(beginning, replacementStart, ptr);
87-
ptr = std::uninitialized_copy_n(str.c_str_safe(), str.length(), ptr);
96+
ptr = std::uninitialized_copy_n(str, l, ptr);
8897
ptr = std::uninitialized_copy(replacementEnd, end, ptr);
8998

9099
// null-terminator
91100
*ptr = 0;
92101

93102
std::swap(mCStr, newStr);
94103
if (newStr) {
95-
free((Data*) newStr - 1);
104+
std::free(reinterpret_cast<Data*>(newStr) - 1);
96105
}
97106

98107
return *this;
99108
}
100109

101110
#if !defined(NDEBUG)
102-
io::ostream& operator<<(io::ostream& out, const utils::CString& rhs) {
111+
io::ostream& operator<<(io::ostream& out, const CString& rhs) {
103112
return out << rhs.c_str_safe();
104113
}
105114
#endif

0 commit comments

Comments
 (0)