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++

// -*- 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;
}
}