mtl/include/mtl/string.hpp

281 lines
7.4 KiB
C++

#pragma once
#include <cstddef>
#include <cstring>
#include "utility.hpp"
#include "string_view.hpp"
namespace mtl {
/**
* \brief Generic string interface
*
* Interface for mtl::string and mtl::string_ext.
* Implements most functions from std::string, see std::string documentation
* for function descriptions. Some operations are unimplemented due to the
* lack of dynamic memory usage.
*/
class istring {
protected:
char* m_str;
size_t m_size;
size_t m_capacity;
public:
static constexpr const size_t npos = -1;
istring(char* _str, size_t _capacity, size_t _size);
operator string_view() const { return string_view(m_str, m_size); }
// Data functions
// If the new string does not fit, it will be truncated to the capacity
istring& assign(const istring& str);
istring& assign(const istring& str, size_t pos, size_t count = npos);
istring& assign(const string_view& str);
istring& assign(const string_view& str, size_t pos, size_t count = npos);
istring& assign(size_t count, char ch);
template <typename T>
istring& assign(T first, T last) {
m_size = 0;
T it = first;
while (it != last && m_size < m_capacity) {
m_str[m_size] = *it;
++m_size;
++it;
}
return *this;
}
istring& operator=(const istring& rhs);
istring& operator=(const string_view& rhs);
istring& operator=(char rhs);
// Element access functions
char& at(size_t i);
const char& at(size_t i) const;
char& operator[](size_t i);
const char& operator[](size_t i) const;
char& front();
const char& front() const;
char& back();
const char& back() const;
char* data();
const char* data() const;
const char* c_str() const;
// Capacity
bool empty() const;
size_t size() const;
size_t length() const;
size_t capacity() const;
// Modifiers
void clear();
/**
* \brief Insert a string at the given index
*
* Inserting a substring of the destination string is not supported.
*/
istring& insert(size_t index, const string_view& str);
istring& insert(size_t index, const string_view& str, size_t str_index, size_t count = npos);
istring& insert(size_t index, size_t count, char ch);
istring& erase(size_t index = 0, size_t count = npos);
istring& append(const string_view& str);
istring& append(const string_view& str, size_t str_index, size_t count = npos);
istring& append(size_t count, char ch);
istring& operator+=(char ch);
istring& operator+=(const string_view& str);
istring& replace(size_t pos, size_t count, const string_view& str);
istring& replace(size_t pos, size_t count, const string_view& str, size_t pos2, size_t count2 = npos);
istring& replace(size_t pos, size_t count, size_t count2, char ch);
/**
* \brief Copy the string to a buffer
*
* Copies the string to the buffer pointed to by dest. Will always
* copy the string from front to back, never in reverse. The strings
* must not overlap.
*/
size_t copy(char* dest, size_t count = npos, size_t pos = 0) const;
void resize(size_t count);
void resize(size_t count, char ch);
// Search
/**
* \brief Find the first occurence of the substring
*
* Uses a naive brute force search. Could be sped up by using the
* Boyer-Moore search algorithm.
*/
size_t find(const string_view& str, size_t pos = 0) const;
size_t find(char ch, size_t pos = 0) const;
size_t rfind(const string_view& str, size_t pos = npos) const;
size_t rfind(char ch, size_t pos = npos) const;
size_t find_first_of(const string_view& str, size_t pos = 0) const;
size_t find_first_of(char ch, size_t pos = 0) const;
size_t find_first_not_of(const string_view& str, size_t pos = 0) const;
size_t find_first_not_of(char ch, size_t pos = 0) const;
size_t find_last_of(const string_view& str, size_t pos = npos) const;
size_t find_last_of(char ch, size_t pos = npos) const;
size_t find_last_not_of(const string_view& str, size_t pos = npos) const;
size_t find_last_not_of(char ch, size_t pos = npos) const;
// Comparison
int32_t compare(const string_view& str) const;
int32_t compare(size_t pos, size_t count, const string_view& str) const;
int32_t compare(size_t pos1, size_t count1, const string_view& str, size_t pos2, size_t count2) const;
bool operator==(const istring& rhs) const;
bool operator!=(const istring& rhs) const;
bool operator==(const string_view& rhs) const;
bool operator!=(const string_view& rhs) const;
/**
* \brief Convert the signed integer to a string
*
* The value in str is overwritten.
*/
template <typename T, std::enable_if_t<std::is_signed_v<T>, bool> = true>
friend istring& to_string(T x, istring& str, bool hex = false) {
size_t num_char = 0;
#ifdef __ARM_ARCH_4T__
if (hex) {
num_char = mtl_itostrx(x, str.data());
} else {
num_char = mtl_itostr(x, str.data());
}
#else
itoa(x, str.data(), hex ? 16 : 10);
num_char = strlen(str.data());
#endif
str.m_size = num_char;
return str;
}
/**
* \brief Convert the unsigned integer to a string
*
* The value in str is overwritten.
*
* The template must check that the integer is not a boolean because
* std::is_unsigned counts booleans as unsigned integers.
*/
template <typename T, std::enable_if_t<std::is_unsigned_v<T>
&& !std::is_same_v<T, bool>, bool> = true>
friend istring& to_string(T x, istring& str, bool hex = false) {
size_t num_char = 0;
#ifdef __ARM_ARCH_4T__
if (hex) {
num_char = mtl_utostrx(x, str.data());
} else {
num_char = mtl_utostr(x, str.data());
}
#else
utoa(x, str.data(), hex ? 16 : 10);
num_char = strlen(str.data());
#endif
str.m_size = num_char;
return str;
}
/**
* \brief Convert the boolean to a string, using the provided buffer
*
* Writes the boolean as either "true" or "false". Depending on how
* this function is used, `x ? "true" : "false"` may be faster.
* Ex. mtl::string_stream does not use this function under the hood.
*
* The value in str is overwritten.
*
* This function needs to be templated otherwise non-boolean arguments
* could be implicitly converted to boolean.
*/
template <typename T, std::enable_if_t<std::is_same_v<T, bool>, bool> = true>
friend istring& to_string(T x, istring& str) {
str = x ? "true" : "false";
return str;
}
/**
* \brief Convert the address value to a string, using the provided buffer
*
* Always writes the address in hexidecimal format. Does not pad the
* value to the address width.
*
* The value in str is overwritten.
*
* The template must check that the pointer is not a character pointer
* otherwise C strings will be converted to their address string, not
* the C string.
*/
template <typename T, std::enable_if_t<std::is_pointer_v<T>
&& !std::is_same_v<T, char*>
&& !std::is_same_v<T, const char*>, bool> = true>
friend istring& to_string(T x, istring& str) {
size_t num_char = 0;
#ifdef __ARM_ARCH_4T__
num_char = mtl_utostrx(reinterpret_cast<uint32_t>(x), str.data());
#else
utoa(x, str.data(), 16);
num_char = strlen(str.data());
#endif
str.m_size = num_char;
return str;
}
};
template <size_t C>
class string : public istring {
private:
char m_buf[C + 1];
public:
string() : istring(m_buf, C, 0) {}
string(const string_view& str) : istring(m_buf, C, 0) {
m_size = str.length();
if (m_size > C) { m_size = m_capacity; }
mtl::memcpy(m_buf, str.c_str(), m_size);
m_str[m_size] = '\0';
}
using istring::operator=;
};
class string_ext : public istring {
public:
string_ext(char* buf, size_t buf_size);
string_ext(const char&) = delete;
using istring::operator=;
};
} // namespace ml