mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-30 19:17:36 +00:00
483 lines
10 KiB
C++
483 lines
10 KiB
C++
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
|
|
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
|
|
#ifndef CTL_STRING_H_
|
|
#define CTL_STRING_H_
|
|
#include "reverse_iterator.h"
|
|
#include "string_view.h"
|
|
|
|
namespace ctl {
|
|
|
|
class string;
|
|
|
|
ctl::string strcat(ctl::string_view, ctl::string_view) noexcept __wur;
|
|
|
|
namespace __ {
|
|
|
|
constexpr size_t string_size = 3 * sizeof(size_t);
|
|
constexpr size_t sso_max = string_size - 1;
|
|
constexpr size_t big_mask = ~(1ull << (8ull * sizeof(size_t) - 1ull));
|
|
|
|
struct small_string
|
|
{
|
|
char buf[sso_max];
|
|
// interpretation is: size == sso_max - rem
|
|
unsigned char rem;
|
|
#if 0
|
|
size_t rem : 7;
|
|
size_t big : 1 /* = 0 */;
|
|
#endif
|
|
};
|
|
|
|
struct big_string
|
|
{
|
|
char* p;
|
|
size_t n;
|
|
// interpretation is: capacity == c & big_mask
|
|
size_t c;
|
|
#if 0
|
|
size_t c : sizeof(size_t) * 8 - 1;
|
|
size_t big : 1 /* = 1 */;
|
|
#endif
|
|
};
|
|
|
|
} // namespace __
|
|
|
|
class string
|
|
{
|
|
public:
|
|
using value_type = char;
|
|
using size_type = size_t;
|
|
using difference_type = ptrdiff_t;
|
|
using pointer = value_type*;
|
|
using const_pointer = const value_type*;
|
|
using reference = value_type&;
|
|
using const_reference = const value_type&;
|
|
using iterator = pointer;
|
|
using const_iterator = const_pointer;
|
|
using reverse_iterator = ctl::reverse_iterator<iterator>;
|
|
using const_reverse_iterator = ctl::reverse_iterator<const_iterator>;
|
|
|
|
static constexpr size_t npos = -1;
|
|
|
|
string() noexcept
|
|
{
|
|
__builtin_memset(blob, 0, sizeof(size_t) * 2);
|
|
// equivalent to set_small_size(0) but also zeroes memory
|
|
*(((size_t*)blob) + 2) = __::sso_max << (sizeof(size_t) - 1) * 8;
|
|
}
|
|
|
|
string(const ctl::string_view s) noexcept
|
|
{
|
|
if (s.n <= __::sso_max) {
|
|
__builtin_memcpy(blob, s.p, s.n);
|
|
__builtin_memset(blob + s.n, 0, __::sso_max - s.n);
|
|
set_small_size(s.n);
|
|
} else {
|
|
init_big(s);
|
|
}
|
|
}
|
|
|
|
explicit string(const size_t n, const char ch = 0) noexcept
|
|
{
|
|
if (n <= __::sso_max) {
|
|
__builtin_memset(blob, ch, n);
|
|
__builtin_memset(blob + n, 0, __::sso_max - n);
|
|
set_small_size(n);
|
|
} else {
|
|
init_big(n, ch);
|
|
}
|
|
}
|
|
|
|
string(const char* const p) noexcept
|
|
: string(ctl::string_view(p, __builtin_strlen(p)))
|
|
{
|
|
}
|
|
|
|
string(const string& r) noexcept
|
|
{
|
|
if (r.size() <= __::sso_max) {
|
|
__builtin_memcpy(blob, r.data(), __::string_size);
|
|
set_small_size(r.size());
|
|
} else {
|
|
init_big(r);
|
|
}
|
|
}
|
|
|
|
string(const char* const p, const size_t n) noexcept
|
|
: string(ctl::string_view(p, n))
|
|
{
|
|
}
|
|
|
|
~string() /* noexcept */
|
|
{
|
|
if (isbig())
|
|
destroy_big();
|
|
}
|
|
|
|
string& operator=(string) noexcept;
|
|
const char* c_str() const noexcept;
|
|
|
|
void pop_back() noexcept;
|
|
void grow(size_t) noexcept;
|
|
void reserve(size_t) noexcept;
|
|
void resize(size_t, char = 0) noexcept;
|
|
void append(char) noexcept;
|
|
void append(char, size_t) noexcept;
|
|
void append(unsigned long) noexcept;
|
|
void append(const void*, size_t) noexcept;
|
|
void append(const ctl::string_view&, size_t, size_t = npos) noexcept;
|
|
string& insert(size_t, ctl::string_view) noexcept;
|
|
string& erase(size_t = 0, size_t = npos) noexcept;
|
|
string substr(size_t = 0, size_t = npos) const noexcept;
|
|
string& replace(size_t, size_t, ctl::string_view) noexcept;
|
|
bool operator==(ctl::string_view) const noexcept;
|
|
bool operator!=(ctl::string_view) const noexcept;
|
|
bool contains(ctl::string_view) const noexcept;
|
|
bool ends_with(ctl::string_view) const noexcept;
|
|
bool starts_with(ctl::string_view) const noexcept;
|
|
size_t find(char, size_t = 0) const noexcept;
|
|
size_t find(ctl::string_view, size_t = 0) const noexcept;
|
|
size_t find_first_of(char, size_t = 0) const noexcept;
|
|
size_t find_first_of(ctl::string_view, size_t = 0) const noexcept;
|
|
size_t find_last_of(char, size_t = npos) const noexcept;
|
|
size_t find_last_of(ctl::string_view, size_t = npos) const noexcept;
|
|
|
|
void swap(string& s) noexcept
|
|
{
|
|
ctl::swap(blob, s.blob);
|
|
}
|
|
|
|
string(string&& s) noexcept
|
|
{
|
|
__builtin_memcpy(blob, s.blob, __::string_size);
|
|
s.set_small_size(0);
|
|
}
|
|
|
|
void clear() noexcept
|
|
{
|
|
if (isbig()) {
|
|
__b.n = 0;
|
|
} else {
|
|
set_small_size(0);
|
|
}
|
|
}
|
|
|
|
bool empty() const noexcept
|
|
{
|
|
return isbig() ? !__b.n : __s.rem >= __::sso_max;
|
|
}
|
|
|
|
__attribute__((__always_inline__)) char* data() noexcept
|
|
{
|
|
return isbig() ? __b.p : __s.buf;
|
|
}
|
|
|
|
__attribute__((__always_inline__)) const char* data() const noexcept
|
|
{
|
|
return isbig() ? __b.p : __s.buf;
|
|
}
|
|
|
|
__attribute__((__always_inline__)) size_t size() const noexcept
|
|
{
|
|
#if 0
|
|
if (!isbig() && __s.rem > __::sso_max)
|
|
__builtin_trap();
|
|
#endif
|
|
return isbig() ? __b.n : __::sso_max - __s.rem;
|
|
}
|
|
|
|
size_t length() const noexcept
|
|
{
|
|
return size();
|
|
}
|
|
|
|
size_t capacity() const noexcept
|
|
{
|
|
#if 0
|
|
if (isbig() && __b.c <= __::sso_max)
|
|
__builtin_trap();
|
|
#endif
|
|
return isbig() ? __::big_mask & __b.c : __::string_size;
|
|
}
|
|
|
|
iterator begin() noexcept
|
|
{
|
|
return data();
|
|
}
|
|
|
|
const_iterator begin() const noexcept
|
|
{
|
|
return data();
|
|
}
|
|
|
|
const_iterator cbegin() const noexcept
|
|
{
|
|
return data();
|
|
}
|
|
|
|
reverse_iterator rbegin() noexcept
|
|
{
|
|
return reverse_iterator(end());
|
|
}
|
|
|
|
const_reverse_iterator rbegin() const noexcept
|
|
{
|
|
return const_reverse_iterator(end());
|
|
}
|
|
|
|
const_reverse_iterator crbegin() const noexcept
|
|
{
|
|
return const_reverse_iterator(end());
|
|
}
|
|
|
|
iterator end() noexcept
|
|
{
|
|
return data() + size();
|
|
}
|
|
|
|
const_iterator end() const noexcept
|
|
{
|
|
return data() + size();
|
|
}
|
|
|
|
const_iterator cend() const noexcept
|
|
{
|
|
return data() + size();
|
|
}
|
|
|
|
reverse_iterator rend() noexcept
|
|
{
|
|
return reverse_iterator(begin());
|
|
}
|
|
|
|
const_reverse_iterator rend() const noexcept
|
|
{
|
|
return const_reverse_iterator(cbegin());
|
|
}
|
|
|
|
const_reverse_iterator crend() const noexcept
|
|
{
|
|
return const_reverse_iterator(cbegin());
|
|
}
|
|
|
|
char& front()
|
|
{
|
|
if (!size())
|
|
__builtin_trap();
|
|
return data()[0];
|
|
}
|
|
|
|
const char& front() const
|
|
{
|
|
if (!size())
|
|
__builtin_trap();
|
|
return data()[0];
|
|
}
|
|
|
|
char& back()
|
|
{
|
|
if (!size())
|
|
__builtin_trap();
|
|
return data()[size() - 1];
|
|
}
|
|
|
|
const char& back() const
|
|
{
|
|
if (!size())
|
|
__builtin_trap();
|
|
return data()[size() - 1];
|
|
}
|
|
|
|
char& operator[](size_t i) noexcept
|
|
{
|
|
if (i >= size())
|
|
__builtin_trap();
|
|
return data()[i];
|
|
}
|
|
|
|
const char& operator[](const size_t i) const noexcept
|
|
{
|
|
if (i >= size())
|
|
__builtin_trap();
|
|
return data()[i];
|
|
}
|
|
|
|
void push_back(const char ch) noexcept
|
|
{
|
|
append(ch);
|
|
}
|
|
|
|
void append(const ctl::string_view& s) noexcept
|
|
{
|
|
append(s.p, s.n);
|
|
}
|
|
|
|
operator ctl::string_view() const noexcept
|
|
{
|
|
return ctl::string_view(data(), size());
|
|
}
|
|
|
|
string& operator=(const char* s) noexcept
|
|
{
|
|
clear();
|
|
append(s);
|
|
return *this;
|
|
}
|
|
|
|
string& operator=(const ctl::string_view s) noexcept
|
|
{
|
|
clear();
|
|
append(s);
|
|
return *this;
|
|
}
|
|
|
|
string& operator+=(const char x) noexcept
|
|
{
|
|
append(x);
|
|
return *this;
|
|
}
|
|
|
|
string& operator+=(const char* s) noexcept
|
|
{
|
|
append(s);
|
|
return *this;
|
|
}
|
|
|
|
string& operator+=(const ctl::string s) noexcept
|
|
{
|
|
append(s);
|
|
return *this;
|
|
}
|
|
|
|
string& operator+=(const ctl::string_view s) noexcept
|
|
{
|
|
append(s);
|
|
return *this;
|
|
}
|
|
|
|
string operator+(const char c) const noexcept
|
|
{
|
|
char s[2] = { c };
|
|
return strcat(*this, s);
|
|
}
|
|
|
|
string operator+(const char* s) const noexcept
|
|
{
|
|
return strcat(*this, s);
|
|
}
|
|
|
|
string operator+(const string& s) const noexcept
|
|
{
|
|
return strcat(*this, s);
|
|
}
|
|
|
|
string operator+(const ctl::string_view s) const noexcept
|
|
{
|
|
return strcat(*this, s);
|
|
}
|
|
|
|
int compare(const ctl::string_view s) const noexcept
|
|
{
|
|
return strcmp(*this, s);
|
|
}
|
|
|
|
bool operator<(const ctl::string_view s) const noexcept
|
|
{
|
|
return compare(s) < 0;
|
|
}
|
|
|
|
bool operator<=(const ctl::string_view s) const noexcept
|
|
{
|
|
return compare(s) <= 0;
|
|
}
|
|
|
|
bool operator>(const ctl::string_view s) const noexcept
|
|
{
|
|
return compare(s) > 0;
|
|
}
|
|
|
|
bool operator>=(const ctl::string_view s) const noexcept
|
|
{
|
|
return compare(s) >= 0;
|
|
}
|
|
|
|
private:
|
|
void destroy_big() noexcept;
|
|
void init_big(const string&) noexcept;
|
|
void init_big(ctl::string_view) noexcept;
|
|
void init_big(size_t, char) noexcept;
|
|
|
|
__attribute__((__always_inline__)) bool isbig() const noexcept
|
|
{
|
|
return *(blob + __::sso_max) & 0x80;
|
|
}
|
|
|
|
void set_small_size(const size_t size) noexcept
|
|
{
|
|
if (size > __::sso_max)
|
|
__builtin_trap();
|
|
__s.rem = __::sso_max - size;
|
|
}
|
|
|
|
void set_big_string(char* const p, const size_t n, const size_t c2) noexcept
|
|
{
|
|
if (c2 > __::big_mask)
|
|
__builtin_trap();
|
|
__b.p = p;
|
|
__b.n = n;
|
|
__b.c = c2 | ~__::big_mask;
|
|
}
|
|
|
|
friend string strcat(ctl::string_view, ctl::string_view) noexcept;
|
|
|
|
union
|
|
{
|
|
__::big_string __b;
|
|
__::small_string __s;
|
|
char blob[__::string_size];
|
|
};
|
|
};
|
|
|
|
static_assert(sizeof(string) == __::string_size);
|
|
static_assert(sizeof(__::small_string) == __::string_size);
|
|
static_assert(sizeof(__::big_string) == __::string_size);
|
|
|
|
ctl::string
|
|
to_string(int) noexcept;
|
|
|
|
ctl::string
|
|
to_string(long) noexcept;
|
|
|
|
ctl::string
|
|
to_string(long long) noexcept;
|
|
|
|
ctl::string
|
|
to_string(unsigned) noexcept;
|
|
|
|
ctl::string
|
|
to_string(unsigned long) noexcept;
|
|
|
|
ctl::string
|
|
to_string(unsigned long long) noexcept;
|
|
|
|
ctl::string
|
|
to_string(float) noexcept;
|
|
|
|
ctl::string
|
|
to_string(double) noexcept;
|
|
|
|
ctl::string
|
|
to_string(long double) noexcept;
|
|
|
|
} // namespace ctl
|
|
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wliteral-suffix"
|
|
inline ctl::string
|
|
operator"" s(const char* s, size_t n)
|
|
{
|
|
return ctl::string(s, n);
|
|
}
|
|
#pragma GCC diagnostic pop
|
|
|
|
#endif // CTL_STRING_H_
|