You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
340 lines
11 KiB
C++
340 lines
11 KiB
C++
// -*- c++ -*-
|
|
#pragma once
|
|
|
|
#include <libshit/assert.hpp>
|
|
|
|
#include <algorithm> // std::min
|
|
#include <cstddef>
|
|
#include <iosfwd>
|
|
#include <iterator>
|
|
#include <limits>
|
|
#include <stdexcept>
|
|
#include <type_traits>
|
|
|
|
namespace std
|
|
{
|
|
|
|
template <typename Char, typename Traits>
|
|
class basic_string_view
|
|
{
|
|
public:
|
|
using traits_type = Traits;
|
|
using value_type = Char;
|
|
using pointer = Char*;
|
|
using const_pointer = const Char*;
|
|
using reference = Char&;
|
|
using const_reference = const Char&;
|
|
using const_iterator = const Char*;
|
|
using iterator = const_iterator;
|
|
using const_reverse_iterator = reverse_iterator<const_iterator>;
|
|
using reverse_iterator = const_reverse_iterator;
|
|
using size_type = size_t;
|
|
using difference_type = ptrdiff_t;
|
|
|
|
using unsigned_value_type = make_unsigned_t<Char>;
|
|
using unsigned_pointer = unsigned_value_type*;
|
|
using unsigned_const_pointer = const unsigned_value_type*;
|
|
using unsigned_reference = unsigned_value_type&;
|
|
using unsigned_const_reference = const unsigned_value_type&;
|
|
|
|
static constexpr const size_type npos = numeric_limits<size_type>::max();
|
|
|
|
constexpr basic_string_view() noexcept : str{nullptr}, len{0} {}
|
|
|
|
constexpr basic_string_view(const_pointer str) noexcept
|
|
: str{str}, len{str ? Traits::length(str) : 0} {}
|
|
|
|
basic_string_view(unsigned_const_pointer str) noexcept
|
|
: basic_string_view{reinterpret_cast<const_pointer>(str)} {}
|
|
|
|
constexpr basic_string_view(
|
|
const_pointer str, size_type len) noexcept
|
|
: str{str}, len{len}
|
|
{ LIBSHIT_ASSERT(str != nullptr || len == 0); }
|
|
|
|
constexpr basic_string_view(
|
|
unsigned_const_pointer str, size_type len) noexcept
|
|
: basic_string_view{reinterpret_cast<const_pointer>(str), len} {}
|
|
|
|
template <typename Allocator>
|
|
basic_string_view(
|
|
const basic_string<Char, Traits, Allocator>& str) noexcept
|
|
: str{str.c_str()}, len{str.size()} {}
|
|
|
|
template <size_t N>
|
|
constexpr basic_string_view(const Char (&str)[N]) noexcept
|
|
: str{str}, len{N-1} {}
|
|
|
|
constexpr const_iterator begin() const noexcept { return str; }
|
|
constexpr const_iterator cbegin() const noexcept { return str; }
|
|
constexpr const_iterator end() const noexcept { return str+len; }
|
|
constexpr const_iterator cend() const noexcept { return str+len; }
|
|
|
|
constexpr const_reverse_iterator rbegin() const noexcept
|
|
{ return const_reverse_iterator{end()}; }
|
|
constexpr const_reverse_iterator crbegin() const noexcept
|
|
{ return const_reverse_iterator{end()}; }
|
|
constexpr const_reverse_iterator rend() const noexcept
|
|
{ return const_reverse_iterator{begin()}; }
|
|
constexpr const_reverse_iterator crend() const noexcept
|
|
{ return const_reverse_iterator{begin()}; }
|
|
|
|
constexpr const_reference operator[](size_type i) const noexcept
|
|
{
|
|
LIBSHIT_ASSERT(i < len);
|
|
return str[i];
|
|
}
|
|
constexpr const_reference at(size_type i) const
|
|
{
|
|
if (i >= len)
|
|
throw out_of_range("NonowningString");
|
|
return str[i];
|
|
}
|
|
|
|
// undefined if string is empty
|
|
constexpr const_reference front() const noexcept
|
|
{ LIBSHIT_ASSERT(len); return str[0]; }
|
|
constexpr const_reference back() const noexcept
|
|
{ LIBSHIT_ASSERT(len); return str[len-1]; }
|
|
|
|
// can return nullptr or pointer to '\0' in case of empty string
|
|
constexpr const_pointer data() const noexcept { return str; }
|
|
|
|
constexpr size_type size() const noexcept { return len; }
|
|
constexpr size_type length() const noexcept { return len; }
|
|
constexpr size_type max_size() const noexcept
|
|
{ return numeric_limits<size_type>::max(); }
|
|
constexpr bool empty() const noexcept { return len == 0; }
|
|
|
|
constexpr void remove_prefix(size_type n) noexcept
|
|
{ LIBSHIT_ASSERT(len >= n); str += n; len -= n; }
|
|
|
|
constexpr void remove_suffix(size_type n) noexcept
|
|
{ LIBSHIT_ASSERT(len >= n); len -= n; }
|
|
|
|
constexpr void swap(basic_string_view& o) noexcept
|
|
{
|
|
swap(str, o.str);
|
|
swap(len, o.len);
|
|
}
|
|
|
|
// todo: does this solve the missing std::string ctor problem?
|
|
template <typename Allocator>
|
|
operator basic_string<Char, Traits, Allocator>() const
|
|
{ return {str, len}; }
|
|
|
|
// copy, *find*: too lazy
|
|
constexpr basic_string_view substr(
|
|
size_type pos, size_type n = npos) const noexcept
|
|
{
|
|
LIBSHIT_ASSERT(pos <= len);
|
|
return {str+pos, min(n, len-pos)};
|
|
}
|
|
|
|
// search functions
|
|
constexpr size_type find(
|
|
basic_string_view o, size_type pos = 0) const noexcept
|
|
{
|
|
if (pos >= len || pos + o.size() > len) return npos;
|
|
for (; pos <= len - o.len; ++pos)
|
|
if (!Traits::compare(str + pos, o.str, o.len))
|
|
return pos;
|
|
return npos;
|
|
}
|
|
constexpr size_type find(Char ch, size_type pos = 0) const noexcept
|
|
{
|
|
for (; pos < len; ++pos)
|
|
if (Traits::eq(str[pos], ch)) return pos;
|
|
return npos;
|
|
}
|
|
constexpr size_type find(const Char* s, size_type pos, size_type count) const
|
|
{ return find(basic_string_view(s, count), pos); }
|
|
constexpr size_type find(const Char* s, size_type pos = 0) const
|
|
{ return find(basic_string_view(s), pos); }
|
|
|
|
|
|
constexpr size_type rfind(
|
|
basic_string_view o, size_type pos = npos) const noexcept
|
|
{
|
|
if (o.size() > len) return npos;
|
|
if (pos > len - o.len) pos = len - o.len;
|
|
while (true)
|
|
{
|
|
if (!Traits::compare(str + pos, o.str, o.len))
|
|
return pos;
|
|
if (pos == 0) return npos;
|
|
--pos;
|
|
}
|
|
}
|
|
constexpr size_type rfind(Char ch, size_type pos = npos) const noexcept
|
|
{
|
|
if (pos > len-1) pos = len-1;
|
|
while (true)
|
|
{
|
|
if (Traits::eq(str[pos], ch)) return pos;
|
|
if (pos == 0) return npos;
|
|
--pos;
|
|
}
|
|
}
|
|
constexpr size_type rfind(const Char* s, size_type pos, size_type count) const
|
|
{ return rfind(basic_string_view(s, count), pos); }
|
|
constexpr size_type rfind(const Char* s, size_type pos = npos) const
|
|
{ return rfind(basic_string_view(s), pos); }
|
|
|
|
|
|
constexpr size_type find_first_of(
|
|
basic_string_view o, size_type pos = 0) const noexcept
|
|
{
|
|
if (pos >= len || o.len == 0) return npos;
|
|
for (; pos < len; ++pos)
|
|
for (size_t i = 0; i < o.len; ++i)
|
|
if (Traits::eq(str[pos], o.str[i]))
|
|
return pos;
|
|
return npos;
|
|
}
|
|
constexpr size_type find_first_of(Char ch, size_type pos = 0) const noexcept
|
|
{ return find(ch, pos); }
|
|
constexpr size_type find_first_of(const Char* s, size_type pos, size_type count) const
|
|
{ return find_first_of(basic_string_view(s, count), pos); }
|
|
constexpr size_type find_first_of(const Char* s, size_type pos = 0) const
|
|
{ return find_first_of(basic_string_view(s), pos); }
|
|
|
|
|
|
constexpr size_type find_last_of(
|
|
basic_string_view o, size_type pos = npos) const noexcept
|
|
{
|
|
if (o.len == 0) return npos;
|
|
if (pos > len-1) pos = len-1;
|
|
while (true)
|
|
{
|
|
for (size_t i = 0; i < o.len; ++i)
|
|
if (Traits::eq(str[pos], o.str[i]))
|
|
return pos;
|
|
if (pos == 0) return npos;
|
|
--pos;
|
|
}
|
|
}
|
|
constexpr size_type find_last_of(Char ch, size_type pos = npos) const noexcept
|
|
{ return rfind(ch, pos); }
|
|
constexpr size_type find_last_of(const Char* s, size_type pos, size_type count) const
|
|
{ return find_last_of(basic_string_view(s, count), pos); }
|
|
constexpr size_type find_last_of(const Char* s, size_type pos = npos) const
|
|
{ return find_last_of(basic_string_view(s), pos); }
|
|
|
|
|
|
constexpr size_type find_first_not_of(
|
|
basic_string_view o, size_type pos = 0) const noexcept
|
|
{
|
|
if (pos >= len || o.len == 0) return npos;
|
|
for (; pos < len; ++pos)
|
|
{
|
|
bool found = false;
|
|
for (size_t i = 0; i < o.len; ++i)
|
|
if (Traits::eq(str[pos], o.str[i]))
|
|
{ found = true; break; } // can't goto in constexpr :'(
|
|
if (!found) return pos;
|
|
}
|
|
return npos;
|
|
}
|
|
constexpr size_type find_first_not_of(Char ch, size_type pos = 0) const noexcept
|
|
{
|
|
for (; pos < len; ++pos)
|
|
if (!Traits::eq(str[pos], ch)) return pos;
|
|
return npos;
|
|
}
|
|
constexpr size_type find_first_not_of(const Char* s, size_type pos, size_type count) const
|
|
{ return find_first_not_of(basic_string_view(s, count), pos); }
|
|
constexpr size_type find_first_not_of(const Char* s, size_type pos = 0) const
|
|
{ return find_first_not_of(basic_string_view(s), pos); }
|
|
|
|
|
|
constexpr size_type find_last_not_of(
|
|
basic_string_view o, size_type pos = npos) const noexcept
|
|
{
|
|
if (o.len == 0) return npos;
|
|
if (pos > len-1) pos = len-1;
|
|
while (true)
|
|
{
|
|
bool found = false;
|
|
for (size_t i = 0; i < o.len; ++i)
|
|
if (Traits::eq(str[pos], o.str[i]))
|
|
{ found = true; break; } // can't goto in constexpr :'(
|
|
if (!found) return pos;
|
|
if (pos == 0) return npos;
|
|
--pos;
|
|
}
|
|
}
|
|
constexpr size_type find_last_not_of(Char ch, size_type pos = npos) const noexcept
|
|
{
|
|
if (pos > len-1) pos = len-1;
|
|
while (true)
|
|
{
|
|
if (!Traits::eq(str[pos], ch)) return pos;
|
|
if (pos == 0) return npos;
|
|
--pos;
|
|
}
|
|
}
|
|
constexpr size_type find_last_not_of(const Char* s, size_type pos, size_type count) const
|
|
{ return find_last_not_of(basic_string_view(s, count), pos); }
|
|
constexpr size_type find_last_not_of(const Char* s, size_type pos = npos) const
|
|
{ return find_last_not_of(basic_string_view(s), pos); }
|
|
|
|
|
|
constexpr int compare(basic_string_view o) const noexcept
|
|
{
|
|
auto cmp = Traits::compare(str, o.str, min(len, o.len));
|
|
if (cmp == 0) cmp = (len == o.len ? 0 : (len < o.len ? -1 : 1));
|
|
return cmp;
|
|
}
|
|
// too lazy for the other overloads
|
|
|
|
constexpr bool operator==(basic_string_view o) const noexcept
|
|
{
|
|
return len == o.len &&
|
|
(str == o.str || Traits::compare(str, o.str, len) == 0);
|
|
}
|
|
constexpr bool operator!=(basic_string_view o) const noexcept
|
|
{ return !(*this == o); }
|
|
|
|
#define X_GEN_OP(op) \
|
|
constexpr bool operator op(basic_string_view o) const noexcept \
|
|
{ return compare(o) op 0; }
|
|
X_GEN_OP(<) X_GEN_OP(<=) X_GEN_OP(>) X_GEN_OP(>=)
|
|
#undef X_GEN_OP
|
|
|
|
private:
|
|
const_pointer str;
|
|
size_type len;
|
|
};
|
|
|
|
#define X_GEN_OP(op) \
|
|
template <typename Char, typename Traits, typename Allocator> \
|
|
inline bool operator op( \
|
|
const basic_string<Char, Traits, Allocator>& lhs, \
|
|
basic_string_view<Char, Traits> rhs) \
|
|
{ \
|
|
return basic_string_view<Char, Traits>{lhs} op rhs; \
|
|
}
|
|
X_GEN_OP(==)
|
|
X_GEN_OP(!=)
|
|
X_GEN_OP(<)
|
|
X_GEN_OP(<=)
|
|
X_GEN_OP(>)
|
|
X_GEN_OP(>=)
|
|
#undef X_GEN_OP
|
|
|
|
using string_view = basic_string_view<char>;
|
|
using wstring_view = basic_string_view<wchar_t>;
|
|
using u16string_view = basic_string_view<char16_t>;
|
|
using u32string_view = basic_string_view<char32_t>;
|
|
|
|
template <typename T, typename U>
|
|
inline basic_ostream<T>& operator<<(
|
|
basic_ostream<T>& os, basic_string_view<T, U> s)
|
|
{
|
|
os.write(s.data(), s.size());
|
|
return os;
|
|
}
|
|
|
|
}
|