blob: b5a3f59f866aca742f6038e63cf2be3404c48e08 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef LIB_FIT_STRING_VIEW_H_
#define LIB_FIT_STRING_VIEW_H_
#include <cassert>
#include <cstdlib>
#include <ios>
#include <iterator>
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
namespace fit {
namespace internal {
// Constexpr filler for std::swap. No specialization for arrays.
template <typename T>
constexpr void constexpr_swap(T& a, T& b) noexcept {
T tmp = std::move(a);
a = std::move(b);
b = std::move(tmp);
}
// Constexpr filler for C++17 std::addressof.
template <typename T>
constexpr T* addressof(T& arg) {
return reinterpret_cast<T*>(&const_cast<char&>(reinterpret_cast<const volatile char&>(arg)));
}
// Filler for char_traits<CharT>::compare
template <typename CharT>
constexpr int compare(const CharT* s1, const CharT* s2, std::size_t count) {
for (std::size_t curr = 0; curr < count; ++curr) {
// Exit as soon as we find a different character.
if (std::char_traits<CharT>::lt(s1[curr], s2[curr])) {
return -1;
} else if (!std::char_traits<CharT>::eq(s1[curr], s2[curr])) {
return 1;
}
}
// If all characters within [s1, s1+count) and [s2, s2+count) are equal
// return 0.
return 0;
}
// Returns the distance from |begin| to first character in [|it|, |end|) that is equal to
// |needle.front()|.
// Returns |StringViewType::npos| if no such character is found.
//
// Complexity: O(|std::distance(it, end)|).
template <typename StringViewType, typename Iterator>
typename StringViewType::size_type find_char(Iterator it, Iterator begin, Iterator end,
StringViewType needle) {
// Look starting point.
while (it != end && !StringViewType::traits_type::eq(*it, needle.front())) {
++it;
}
if (it == end) {
return StringViewType::npos;
}
return static_cast<typename StringViewType::size_type>(std::distance(begin, it));
}
// Returns the distance from the first character starting from |begin| that matches
// any characters in |matchers|.
// Returns |StringViewType::npos| if no characters are within |matchers|.
//
// Complexity: O(|std::distance(begin, end)|*|matchers.length()|).
template <typename StringViewType, typename Iterator>
constexpr typename StringViewType::size_type find_first_of(Iterator begin, Iterator end,
StringViewType matchers) {
typename StringViewType::size_type curr = 0;
for (Iterator it = begin; it < end; ++it) {
for (const auto& matcher : matchers) {
if (StringViewType::traits_type::eq(*it, matcher)) {
return curr;
}
}
++curr;
}
return StringViewType::npos;
}
// Returns the distance from the first character starting from |begin| that does not match
// any characters in |matchers|.
// Returns |StringViewType::npos| if all characters are within |matchers|.
//
// Complexity: O(|std::distance(begin, end)|*|matchers.length()|).
template <typename StringViewType, typename Iterator>
constexpr typename StringViewType::size_type find_first_not_of(Iterator begin, Iterator end,
StringViewType matchers) {
typename StringViewType::size_type curr = 0;
for (Iterator it = begin; it < end; ++it) {
bool matched = false;
for (const auto& matcher : matchers) {
if (StringViewType::traits_type::eq(*it, matcher)) {
matched = true;
break;
}
}
if (!matched) {
return curr;
}
++curr;
}
return StringViewType::npos;
}
// Returns the starting point of |needle| within |haystack|.
// If no match is found, return |StringViewType::npos|.
//
// Complexity: O(|std::distance(begin, end)| * |needle.length()|)
template <typename StringViewType, typename Iterator>
constexpr typename StringViewType::size_type find(Iterator begin, Iterator end,
const StringViewType needle) {
// If the needle does not fit in the haystack, there is no possible match.
if (static_cast<typename StringViewType::size_type>(std::distance(begin, end)) <
needle.length()) {
return StringViewType::npos;
}
if (needle.empty()) {
return 0;
}
Iterator it = begin;
while (it < end) {
typename StringViewType::size_type offset = find_char(it, begin, end, needle);
// If no match discard.
if (offset == StringViewType::npos) {
return StringViewType::npos;
}
it = begin + offset;
if (internal::compare<typename StringViewType::value_type>(&(*it), needle.data(),
needle.size()) == 0) {
return std::distance(begin, it);
}
++it;
}
// We did not find the substring in the haystack.
return StringViewType::npos;
}
} // namespace internal
// Provides a view to a sequence of characters.
template <class CharT, class Traits = std::char_traits<CharT>>
class basic_string_view {
public:
using traits_type = Traits;
using value_type = CharT;
using pointer = CharT*;
using const_pointer = const CharT*;
using reference = CharT&;
using const_reference = const CharT&;
using iterator = const_pointer;
using const_iterator = iterator;
using reverse_iterator = std::reverse_iterator<const_iterator>;
using const_reverse_iterator = reverse_iterator;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
static constexpr size_type npos = static_cast<size_type>(-1);
constexpr basic_string_view() noexcept : data_(nullptr), length_(0) {}
constexpr basic_string_view(const CharT* s, size_type count) noexcept
: data_(s), length_(count) {}
constexpr basic_string_view(const CharT* s) noexcept : data_(s), length_(Traits::length(s)) {}
template <typename Allocator>
basic_string_view(const std::basic_string<CharT, Traits, Allocator>& s) noexcept
: data_(s.data()), length_(s.length()) {}
constexpr basic_string_view(const basic_string_view& other) noexcept = default;
basic_string_view(basic_string_view&&) noexcept = default;
constexpr basic_string_view& operator=(const basic_string_view& view) noexcept = default;
constexpr basic_string_view& operator=(basic_string_view&&) noexcept = default;
~basic_string_view() = default;
constexpr iterator begin() const { return data_; }
constexpr iterator end() const { return begin() + length_; }
constexpr const_iterator cbegin() const { return data_; }
constexpr const_iterator cend() const { return begin() + length_; }
constexpr reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
constexpr reverse_iterator rend() const { return const_reverse_iterator(begin()); }
constexpr const_reverse_iterator crbegin() const { return const_reverse_iterator(end()); }
constexpr const_reverse_iterator crend() const { return const_reverse_iterator(begin()); }
constexpr const_pointer data() const { return data_; }
constexpr size_type size() const { return length_; }
constexpr size_type length() const { return length_; }
constexpr size_type max_size() const { return std::numeric_limits<size_type>::max(); }
constexpr const_reference front() const { return this->operator[](0); }
constexpr const_reference back() const { return this->operator[](size() - 1); }
constexpr bool empty() const { return size() == 0; }
constexpr const_reference operator[](size_type pos) const { return *(data() + pos); }
constexpr const_reference at(size_type pos) const {
assert(pos < size());
return this->operator[](pos);
}
constexpr void remove_prefix(size_type n) {
data_ += n;
length_ -= n;
}
constexpr void remove_suffix(size_type n) { length_ -= n; }
constexpr void swap(basic_string_view& other) noexcept {
internal::constexpr_swap(data_, other.data_);
internal::constexpr_swap(length_, other.length_);
}
size_type copy(CharT* dest, size_type count, size_type pos = 0) const {
assert(pos < size());
Traits::copy(dest, data() + pos, calculate_length(pos, count));
return count;
}
constexpr basic_string_view substr(size_type pos = 0, size_type count = npos) const {
return basic_string_view(data() + pos, calculate_length(pos, count));
}
constexpr int compare(basic_string_view v) const {
const int result = internal::compare(data(), v.data(), std::min(size(), v.size()));
if (result == 0) {
return static_cast<int>(size() - v.size());
}
return result;
}
constexpr int compare(size_type pos1, size_type count1, basic_string_view v) const {
return substr(pos1, count1).compare(v);
}
constexpr int compare(size_type pos1, size_type count1, basic_string_view v, size_type pos2,
size_type count2) const {
return substr(pos1, count1).compare(v.substr(pos2, count2));
}
constexpr int compare(const CharT* s) const { return compare(basic_string_view(s)); }
constexpr int compare(size_type pos1, size_type count1, const CharT* s) const {
return substr(pos1, count1).compare(basic_string_view(s));
}
constexpr int compare(size_type pos1, size_type count1, const CharT* s, size_type count2) const {
return substr(pos1, count1).compare(basic_string_view(s, count2));
}
constexpr size_type find(basic_string_view v, size_type pos = 0) const noexcept {
auto tmp = internal::find(substr(pos).begin(), substr(pos).end(), v);
return (tmp == npos) ? npos : pos + tmp;
}
constexpr size_type find(CharT ch, size_type pos = 0) const {
return find(basic_string_view(internal::addressof(ch), 1), pos);
}
constexpr size_type find(const CharT* s, size_type pos, size_type count) const {
return find(basic_string_view(s, count), pos);
}
constexpr size_type find(const CharT* s, size_type pos) const {
return find(basic_string_view(s), pos);
}
constexpr size_type rfind(basic_string_view v, size_type pos = 0) const noexcept {
auto tmp = internal::find(substr(pos).rbegin(), substr(pos).rend(), v);
return (tmp == npos) ? npos : length() - 1 - tmp;
}
constexpr size_type rfind(CharT ch, size_type pos = 0) const {
return rfind(basic_string_view(internal::addressof(ch), 1), pos);
}
constexpr size_type rfind(const CharT* s, size_type pos, size_type count) const {
return rfind(basic_string_view(s, count), pos);
}
constexpr size_type rfind(const CharT* s, size_type pos) const {
return rfind(basic_string_view(s), pos);
}
constexpr size_type find_first_of(basic_string_view v, size_type pos = 0) const noexcept {
auto tmp = internal::find_first_of(substr(pos).begin(), substr(pos).end(), v);
return tmp == npos ? npos : pos + tmp;
}
constexpr size_type find_first_of(CharT c, size_type pos = 0) const noexcept {
return find_first_of(basic_string_view(internal::addressof(c), 1), pos);
}
constexpr size_type find_first_of(const CharT* 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 CharT* s, size_type pos = 0) const {
return find_first_of(basic_string_view(s), pos);
}
constexpr size_type find_last_of(basic_string_view v,
size_type pos = basic_string_view::npos) const noexcept {
const size_type fixed_length = (pos == npos) ? size() : pos + 1;
const size_type tmp = internal::find_first_of(substr(0, fixed_length).rbegin(),
substr(0, fixed_length).rend(), v);
return tmp == npos ? npos : fixed_length - 1 - tmp;
}
constexpr size_type find_last_of(CharT c, size_type pos = basic_string_view::npos) const
noexcept {
return find_last_of(basic_string_view(internal::addressof(c), 1), pos);
}
constexpr size_type find_last_of(const CharT* 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 CharT* s, size_type pos = basic_string_view::npos) const {
return find_last_of(basic_string_view(s), pos);
}
constexpr size_type find_first_not_of(basic_string_view v, size_type pos = 0) const noexcept {
const auto tmp = internal::find_first_not_of(substr(pos).begin(), substr(pos).end(), v);
return tmp == npos ? npos : pos + tmp;
}
constexpr size_type find_first_not_of(CharT c, size_type pos = 0) const noexcept {
return find_first_not_of(basic_string_view(internal::addressof(c), 1), pos);
}
constexpr size_type find_first_not_of(const CharT* 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 CharT* 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 v,
size_type pos = basic_string_view::npos) const noexcept {
const size_type fixed_length = (pos == npos) ? size() : pos + 1;
auto tmp = internal::find_first_not_of(substr(0, fixed_length).rbegin(),
substr(0, fixed_length).rend(), v);
return tmp == npos ? npos : fixed_length - 1 - tmp;
}
constexpr size_type find_last_not_of(CharT c, size_type pos = basic_string_view::npos) const
noexcept {
return find_last_not_of(basic_string_view(internal::addressof(c), 1), pos);
}
constexpr size_type find_last_not_of(const CharT* 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 CharT* s,
size_type pos = basic_string_view::npos) const {
return find_last_not_of(basic_string_view(s), pos);
}
private:
constexpr size_type calculate_length(size_type pos, size_type count) const {
if (count == npos) {
count = size();
}
return std::min(count, size() - pos);
}
const_pointer data_;
size_type length_;
};
// Operators and overloads to satisfy all conversions.
//
// Defined overloads are of the form:
// <basic_string_view, basic_string_view>
// <RawType, basic_string_view>
// <basic_string_view, RawType>
//
// When |RawType| is lhs: std::is_constructible<basic_string_view, RawType>::value must be true.
// When |RawType| is rhs: There must be an overload of basic_string_view::compare for |RawType|.
template <class CharT, class Traits>
constexpr bool operator==(fit::basic_string_view<CharT, Traits> lhs,
fit::basic_string_view<CharT, Traits> rhs) noexcept {
return lhs.compare(rhs) == 0;
}
template <class CharT, class Traits, class RawType>
constexpr bool operator==(RawType lhs, fit::basic_string_view<CharT, Traits> rhs) noexcept {
return fit::basic_string_view<CharT, Traits>(lhs).compare(rhs) == 0;
}
template <class CharT, class Traits, class RawType>
constexpr bool operator==(fit::basic_string_view<CharT, Traits> lhs, RawType rhs) noexcept {
return lhs.compare(rhs) == 0;
}
template <class CharT, class Traits>
constexpr bool operator!=(fit::basic_string_view<CharT, Traits> lhs,
fit::basic_string_view<CharT, Traits> rhs) noexcept {
return lhs.compare(rhs) != 0;
}
template <class CharT, class Traits, class RawType>
constexpr bool operator!=(RawType lhs, fit::basic_string_view<CharT, Traits> rhs) noexcept {
return fit::basic_string_view<CharT, Traits>(lhs).compare(rhs) != 0;
}
template <class CharT, class Traits, class RawType>
constexpr bool operator!=(fit::basic_string_view<CharT, Traits> lhs, RawType rhs) noexcept {
return lhs.compare(rhs) != 0;
}
template <class CharT, class Traits>
constexpr bool operator<(fit::basic_string_view<CharT, Traits> lhs,
fit::basic_string_view<CharT, Traits> rhs) noexcept {
return lhs.compare(rhs) < 0;
}
template <class CharT, class Traits, class RawType>
constexpr bool operator<(RawType lhs, fit::basic_string_view<CharT, Traits> rhs) noexcept {
return fit::basic_string_view<CharT, Traits>(lhs).compare(rhs) < 0;
}
template <class CharT, class Traits, class RawType>
constexpr bool operator<(fit::basic_string_view<CharT, Traits> lhs, RawType rhs) noexcept {
return lhs.compare(rhs) < 0;
}
template <class CharT, class Traits>
constexpr bool operator>(fit::basic_string_view<CharT, Traits> lhs,
fit::basic_string_view<CharT, Traits> rhs) noexcept {
return lhs.compare(rhs) > 0;
}
template <class CharT, class Traits, class RawType>
constexpr bool operator>(RawType lhs, fit::basic_string_view<CharT, Traits> rhs) noexcept {
return fit::basic_string_view<CharT, Traits>(lhs).compare(rhs) > 0;
}
template <class CharT, class Traits, class RawType>
constexpr bool operator>(fit::basic_string_view<CharT, Traits> lhs, RawType rhs) noexcept {
return lhs.compare(rhs) > 0;
}
template <class CharT, class Traits>
constexpr bool operator<=(fit::basic_string_view<CharT, Traits> lhs,
fit::basic_string_view<CharT, Traits> rhs) noexcept {
return lhs.compare(rhs) <= 0;
}
template <class CharT, class Traits, class RawType>
constexpr bool operator<=(RawType lhs, fit::basic_string_view<CharT, Traits> rhs) noexcept {
return fit::basic_string_view<CharT, Traits>(lhs).compare(rhs) <= 0;
}
template <class CharT, class Traits, class RawType>
constexpr bool operator<=(fit::basic_string_view<CharT, Traits> lhs, RawType rhs) noexcept {
return lhs.compare(rhs) <= 0;
}
template <class CharT, class Traits>
constexpr bool operator>=(fit::basic_string_view<CharT, Traits> lhs,
fit::basic_string_view<CharT, Traits> rhs) noexcept {
return lhs.compare(rhs) >= 0;
}
template <class CharT, class Traits, class RawType>
constexpr bool operator>=(RawType lhs, fit::basic_string_view<CharT, Traits> rhs) noexcept {
return fit::basic_string_view<CharT, Traits>(lhs).compare(rhs) >= 0;
}
template <class CharT, class Traits, class RawType>
constexpr bool operator>=(fit::basic_string_view<CharT, Traits> lhs, RawType rhs) noexcept {
return lhs.compare(rhs) >= 0;
}
// Specializations.
using string_view = fit::basic_string_view<char>;
// Constructs a string_view from ""_sv literal.
// Literals with no leading underscore are reserved for the standard library.
// https://en.cppreference.com/w/cpp/string/basic_string_view/operator%22%22sv
inline namespace literals {
inline namespace string_view_literals {
constexpr fit::string_view operator"" _sv(typename fit::string_view::const_pointer str,
typename fit::string_view::size_type len) noexcept {
return fit::string_view(str, len);
}
} // namespace string_view_literals
} // namespace literals
} // namespace fit
namespace std {
// Hash needs to match basic_string view hash of the same string, so we need to rely on compiler
// implementation.
// https://en.cppreference.com/w/cpp/string/basic_string_view/hash
template <class CharT>
struct hash<fit::basic_string_view<CharT, std::char_traits<CharT>>> {
std::size_t operator()(const fit::basic_string_view<CharT, std::char_traits<CharT>> val) const {
return __do_string_hash(val.data(), val.data() + val.length());
}
};
// Ouput stream specialization for fit::string_view.
//
// https://en.cppreference.com/w/cpp/string/basic_string_view/operator_ltlt
template <class CharT, class Traits>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
fit::basic_string_view<CharT, Traits> v) {
using size_type = typename fit::basic_string_view<CharT, Traits>::size_type;
const size_type fixed_length = std::min(static_cast<size_type>(os.width()), v.length());
const size_type fill_length =
(static_cast<size_type>(os.width()) > v.length()) ? os.width() - v.length() : 0;
auto fill_space = [](std::basic_ostream<CharT, Traits>& os, size_type fill_length) {
for (std::size_t i = 0; i < fill_length; ++i) {
os.put(os.fill());
}
};
bool fill_left = (os.flags() & std::ios_base::adjustfield) == std::ios_base::left;
if (!fill_left) {
fill_space(os, fill_length);
}
os.write(v.data(), fixed_length);
if (fill_left) {
fill_space(os, fill_length);
}
os.width(0);
return os;
}
} // namespace std
#endif