diff --git a/libs/utils/include/utils/CString.h b/libs/utils/include/utils/CString.h index bea466ae29..53a71d5a8e 100644 --- a/libs/utils/include/utils/CString.h +++ b/libs/utils/include/utils/CString.h @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -97,11 +98,7 @@ public: 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 @@ -110,8 +107,8 @@ public: 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(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(); } @@ -119,33 +116,89 @@ public: 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 + CString& replace(size_type const pos, + size_type const len, const StringLiteral& str) & noexcept { + return replace(pos, len, str, N - 1); + } - const_reference operator[](size_type pos) const noexcept { + 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 + CString&& replace(size_type const pos, + size_type const len, const StringLiteral& str) && noexcept { + return std::move(replace(pos, len, str)); + } + + CString&& replace(size_type const pos, size_type const len, const CString& str) && noexcept { + return std::move(replace(pos, len, str)); + } + + // insert + template + CString& insert(size_type const pos, const StringLiteral& str) & noexcept { + return replace(pos, 0, str); + } + + CString& insert(size_type const pos, const CString& str) & noexcept { + return replace(pos, 0, str); + } + + template + CString&& insert(size_type const pos, const StringLiteral& 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 + CString& append(const StringLiteral& str) & noexcept { + return insert(length(), str); + } + + CString& append(const CString& str) & noexcept { + return insert(length(), str); + } + + template + CString&& append(const StringLiteral& 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]; } @@ -171,7 +224,7 @@ public: } // 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; } @@ -185,6 +238,8 @@ public: }; 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 @@ -225,7 +280,7 @@ private: } }; -// 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 CString to_string(T value) noexcept; @@ -249,7 +304,7 @@ public: pointer c_str() noexcept { return mData; } private: - value_type mData[N] = {0}; + value_type mData[N] = {}; }; } // namespace utils diff --git a/libs/utils/src/CString.cpp b/libs/utils/src/CString.cpp index 3e540a22e8..519303ed03 100644 --- a/libs/utils/src/CString.cpp +++ b/libs/utils/src/CString.cpp @@ -20,27 +20,30 @@ #include #include +#include #include + 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(std::malloc(sizeof(Data) + length + 1)); + p->length = size_type(length); + mCStr = reinterpret_cast(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(std::malloc(sizeof(Data) + length + 1)); + p->length = size_type(length); + mCStr = reinterpret_cast(p + 1); std::fill_n(mCStr, length, 0); mCStr[length] = '\0'; } @@ -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(std::malloc(sizeof(Data) + newSize + 1)); assert(p); p->length = newSize; - value_type* newStr = (value_type*) (p + 1); + value_type* newStr = reinterpret_cast(p + 1); const value_type* beginning = mCStr; const value_type* replacementStart = mCStr + pos; @@ -84,7 +93,7 @@ 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 @@ -92,14 +101,14 @@ CString& CString::replace(size_type pos, size_type len, const CString& str) noex std::swap(mCStr, newStr); if (newStr) { - free((Data*) newStr - 1); + std::free(reinterpret_cast(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