Skip to content

CString tidy cleanup and rvalue methods #8718

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 72 additions & 17 deletions libs/utils/include/utils/CString.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <utils/ostream.h>

#include <string_view>
#include <utility>

#include <assert.h>
#include <stddef.h>
Expand Down Expand Up @@ -97,11 +98,7 @@ class UTILS_PUBLIC CString {
return *this;
}

~CString() noexcept {
if (mData) {
free(mData - 1);
}
}
~CString() noexcept;

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

const_pointer c_str() const noexcept { return mCStr; }
pointer c_str() noexcept { return mCStr; }
const_pointer c_str() const noexcept { return const_cast<CString*>(this)->c_str(); }
const_pointer c_str_safe() const noexcept { return mData ? c_str() : ""; }
const_pointer data() const noexcept { return c_str(); }
pointer data() noexcept { return c_str(); }
size_type size() const noexcept { return mData ? mData[-1].length : 0; }
size_type length() const noexcept { return size(); }
bool empty() const noexcept { return size() == 0; }

iterator begin() noexcept { return mCStr; }
iterator begin() noexcept { return c_str(); }
iterator end() noexcept { return begin() + length(); }
const_iterator begin() const noexcept { return data(); }
const_iterator end() const noexcept { return begin() + length(); }
const_iterator cbegin() const noexcept { return begin(); }
const_iterator cend() const noexcept { return end(); }

CString& replace(size_type pos, size_type len, const CString& str) noexcept;
CString& insert(size_type pos, const CString& str) noexcept { return replace(pos, 0, str); }
CString& append(const CString& str) noexcept { return insert(length(), str); }
// replace
template<size_t N>
CString& replace(size_type const pos,
size_type const len, const StringLiteral<N>& str) & noexcept {
return replace(pos, len, str, N - 1);
}

CString& replace(size_type const pos, size_type const len, const CString& str) & noexcept {
return replace(pos, len, str.c_str_safe(), str.size());
}

template<size_t N>
CString&& replace(size_type const pos,
size_type const len, const StringLiteral<N>& str) && noexcept {
return std::move(replace(pos, len, str));
}

const_reference operator[](size_type pos) const noexcept {
CString&& replace(size_type const pos, size_type const len, const CString& str) && noexcept {
return std::move(replace(pos, len, str));
}

// insert
template<size_t N>
CString& insert(size_type const pos, const StringLiteral<N>& str) & noexcept {
return replace(pos, 0, str);
}

CString& insert(size_type const pos, const CString& str) & noexcept {
return replace(pos, 0, str);
}

template<size_t N>
CString&& insert(size_type const pos, const StringLiteral<N>& str) && noexcept {
return std::move(*this).replace(pos, 0, str);
}

CString&& insert(size_type const pos, const CString& str) && noexcept {
return std::move(*this).replace(pos, 0, str);
}

// append
template<size_t N>
CString& append(const StringLiteral<N>& str) & noexcept {
return insert(length(), str);
}

CString& append(const CString& str) & noexcept {
return insert(length(), str);
}

template<size_t N>
CString&& append(const StringLiteral<N>& str) && noexcept {
return std::move(*this).insert(length(), str);
}

CString&& append(const CString& str) && noexcept {
return std::move(*this).insert(length(), str);
}


const_reference operator[](size_type const pos) const noexcept {
assert(pos < size());
return begin()[pos];
}

reference operator[](size_type pos) noexcept {
reference operator[](size_type const pos) noexcept {
assert(pos < size());
return begin()[pos];
}

const_reference at(size_type pos) const noexcept {
const_reference at(size_type const pos) const noexcept {
assert(pos < size());
return begin()[pos];
}

reference at(size_type pos) noexcept {
reference at(size_type const pos) noexcept {
assert(pos < size());
return begin()[pos];
}
Expand All @@ -171,7 +224,7 @@ class UTILS_PUBLIC CString {
}

// placement new declared as "throw" to avoid the compiler's null-check
inline void* operator new(size_t, void* ptr) {
void* operator new(size_t, void* ptr) {
assert(ptr);
return ptr;
}
Expand All @@ -185,6 +238,8 @@ class UTILS_PUBLIC CString {
};

private:
CString& replace(size_type pos, size_type len, char const* str, size_t l) & noexcept;

#if !defined(NDEBUG)
friend io::ostream& operator<<(io::ostream& out, const CString& rhs);
#endif
Expand Down Expand Up @@ -225,7 +280,7 @@ class UTILS_PUBLIC CString {
}
};

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

private:
value_type mData[N] = {0};
value_type mData[N] = {};
};

} // namespace utils
Expand Down
41 changes: 25 additions & 16 deletions libs/utils/src/CString.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,30 @@
#include <utils/ostream.h>

#include <algorithm>
#include <cstdlib>
#include <memory>


namespace utils {

UTILS_NOINLINE
CString::CString(const char* cstr, size_t length) {
CString::CString(const char* cstr, size_t const length) {
if (length && cstr) {
Data* p = (Data*)malloc(sizeof(Data) + length + 1);
p->length = (size_type)length;
mCStr = (value_type*)(p + 1);

Data* const p = static_cast<Data*>(std::malloc(sizeof(Data) + length + 1));
p->length = size_type(length);
mCStr = reinterpret_cast<value_type*>(p + 1);
// we don't use memcpy here to avoid a call to libc, the generated code is pretty good.
std::uninitialized_copy_n(cstr, length, mCStr);
mCStr[length] = '\0';
}
}

CString::CString(size_t length) {
CString::CString(size_t const length) {
if (length) {
Data* p = (Data*)malloc(sizeof(Data) + length + 1);
p->length = (size_type)length;
mCStr = (value_type*)(p + 1);
Data* const p = static_cast<Data*>(std::malloc(sizeof(Data) + length + 1));
p->length = size_type(length);
mCStr = reinterpret_cast<value_type*>(p + 1);
std::fill_n(mCStr, length, 0);
mCStr[length] = '\0';
}
Expand All @@ -58,24 +61,30 @@ CString& CString::operator=(const CString& rhs) {
if (this != &rhs) {
auto *const p = mData ? mData - 1 : nullptr;
new(this) CString(rhs);
free(p);
std::free(p);
}
return *this;
}

CString& CString::replace(size_type pos, size_type len, const CString& str) noexcept {
CString::~CString() noexcept {
if (mData) {
std::free(mData - 1);
}
}

CString& CString::replace(size_type const pos, size_type len, char const* str, size_t const l) & noexcept {
assert(pos <= size());

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

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

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

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

value_type* ptr = newStr;
ptr = std::uninitialized_copy(beginning, replacementStart, ptr);
ptr = std::uninitialized_copy_n(str.c_str_safe(), str.length(), ptr);
ptr = std::uninitialized_copy_n(str, l, ptr);
ptr = std::uninitialized_copy(replacementEnd, end, ptr);

// null-terminator
*ptr = 0;

std::swap(mCStr, newStr);
if (newStr) {
free((Data*) newStr - 1);
std::free(reinterpret_cast<Data*>(newStr) - 1);
}

return *this;
}

#if !defined(NDEBUG)
io::ostream& operator<<(io::ostream& out, const utils::CString& rhs) {
io::ostream& operator<<(io::ostream& out, const CString& rhs) {
return out << rhs.c_str_safe();
}
#endif
Expand Down
Loading