/* * Copyright 2013-2020 Max Kellermann * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef STRING_VIEW_HXX #define STRING_VIEW_HXX #include "ConstBuffer.hxx" #include "StringAPI.hxx" #include "Compiler.h" #include #include #if __cplusplus >= 201703L && !GCC_OLDER_THAN(7,0) #include #endif template struct BasicStringView : ConstBuffer { using typename ConstBuffer::size_type; using typename ConstBuffer::value_type; using typename ConstBuffer::pointer; using typename ConstBuffer::const_pointer; using ConstBuffer::data; using ConstBuffer::size; BasicStringView() = default; explicit constexpr BasicStringView(ConstBuffer src) :ConstBuffer(src) {} explicit constexpr BasicStringView(ConstBuffer src) :ConstBuffer(ConstBuffer::FromVoid(src)) {} constexpr BasicStringView(pointer _data, size_type _size) noexcept :ConstBuffer(_data, _size) {} constexpr BasicStringView(pointer _begin, pointer _end) noexcept :ConstBuffer(_begin, _end - _begin) {} BasicStringView(pointer _data) noexcept :ConstBuffer(_data, _data != nullptr ? StringLength(_data) : 0) {} constexpr BasicStringView(std::nullptr_t n) noexcept :ConstBuffer(n) {} #if __cplusplus >= 201703L && !GCC_OLDER_THAN(7,0) constexpr BasicStringView(std::basic_string_view src) noexcept :ConstBuffer(src.data(), src.size()) {} constexpr operator std::basic_string_view() const noexcept { return {data, size}; } #endif using ConstBuffer::empty; using ConstBuffer::begin; using ConstBuffer::end; using ConstBuffer::front; using ConstBuffer::back; using ConstBuffer::pop_front; using ConstBuffer::pop_back; using ConstBuffer::skip_front; constexpr BasicStringView substr(size_type pos, size_type count) const noexcept { return {data + pos, count}; } constexpr BasicStringView substr(size_type pos) const noexcept { return {data + pos, size - pos}; } constexpr BasicStringView substr(const_pointer start) const noexcept { return {start, size_t(data + size - start)}; } gcc_pure pointer Find(value_type ch) const noexcept { return StringFind(data, ch, this->size); } gcc_pure pointer FindLast(value_type ch) const noexcept { return StringFindLast(data, ch, size); } /** * Split the string at the first occurrence of the given * character. If the character is not found, then the first * value is the whole string and the second value is nullptr. */ gcc_pure std::pair, BasicStringView> Split(value_type ch) const noexcept { const auto separator = Find(ch); if (separator == nullptr) return {*this, nullptr}; return {{begin(), separator}, {separator + 1, end()}}; } /** * Split the string at the last occurrence of the given * character. If the character is not found, then the first * value is the whole string and the second value is nullptr. */ gcc_pure std::pair, BasicStringView> SplitLast(value_type ch) const noexcept { const auto separator = FindLast(ch); if (separator == nullptr) return {*this, nullptr}; return {{begin(), separator}, {separator + 1, end()}}; } gcc_pure bool StartsWith(BasicStringView needle) const noexcept { return this->size >= needle.size && StringIsEqual(data, needle.data, needle.size); } gcc_pure bool EndsWith(BasicStringView needle) const noexcept { return this->size >= needle.size && StringIsEqual(data + this->size - needle.size, needle.data, needle.size); } gcc_pure bool StartsWith(value_type ch) const noexcept { return !empty() && front() == ch; } gcc_pure bool EndsWith(value_type ch) const noexcept { return !empty() && back() == ch; } gcc_pure int Compare(BasicStringView other) const noexcept { if (size < other.size) { int result = StringCompare(data, other.data, size); if (result == 0) result = -1; return result; } else if (size > other.size) { int result = StringCompare(data, other.data, other.size); if (result == 0) result = 1; return result; } else return StringCompare(data, other.data, size); } gcc_pure bool Equals(BasicStringView other) const noexcept { return this->size == other.size && StringIsEqual(data, other.data, this->size); } gcc_pure bool StartsWithIgnoreCase(BasicStringView needle) const noexcept { return this->size >= needle.size && StringIsEqualIgnoreCase(data, needle.data, needle.size); } gcc_pure bool EndsWithIgnoreCase(BasicStringView needle) const noexcept { return this->size >= needle.size && StringIsEqualIgnoreCase(data + this->size - needle.size, needle.data, needle.size); } gcc_pure bool EqualsIgnoreCase(BasicStringView other) const noexcept { return this->size == other.size && StringIsEqualIgnoreCase(data, other.data, this->size); } /** * Skip all whitespace at the beginning. */ void StripLeft() noexcept; /** * Skip all whitespace at the end. */ void StripRight() noexcept; void Strip() noexcept { StripLeft(); StripRight(); } bool SkipPrefix(BasicStringView needle) noexcept { bool match = StartsWith(needle); if (match) skip_front(needle.size); return match; } bool RemoveSuffix(BasicStringView needle) noexcept { bool match = EndsWith(needle); if (match) size -= needle.size; return match; } }; struct StringView : BasicStringView { using BasicStringView::BasicStringView; StringView() = default; constexpr StringView(BasicStringView src) noexcept :BasicStringView(src) {} }; #endif