/////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2015 Microsoft Corporation. All rights reserved. // // This code is licensed under the MIT License (MIT). // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // /////////////////////////////////////////////////////////////////////////////// #pragma once #ifndef GSL_STRING_SPAN_H #define GSL_STRING_SPAN_H #include "gsl_assert.h" #include "span.h" #include #ifdef _MSC_VER // No MSVC does constexpr fully yet #pragma push_macro("constexpr") #define constexpr /* nothing */ // VS 2013 workarounds #if _MSC_VER <= 1800 #define GSL_MSVC_HAS_TYPE_DEDUCTION_BUG // noexcept is not understood #ifndef GSL_THROW_ON_CONTRACT_VIOLATION #pragma push_macro("noexcept") #define noexcept /* nothing */ #endif #endif // _MSC_VER <= 1800 #endif // _MSC_VER // In order to test the library, we need it to throw exceptions that we can catch #ifdef GSL_THROW_ON_CONTRACT_VIOLATION #ifdef _MSC_VER #pragma push_macro("noexcept") #endif #define noexcept /* nothing */ #endif // GSL_THROW_ON_CONTRACT_VIOLATION namespace gsl { // // czstring and wzstring // // These are "tag" typedef's for C-style strings (i.e. null-terminated character arrays) // that allow static analysis to help find bugs. // // There are no additional features/semantics that we can find a way to add inside the // type system for these types that will not either incur significant runtime costs or // (sometimes needlessly) break existing programs when introduced. // template using czstring = const char*; template using cwzstring = const wchar_t*; template using zstring = char*; template using wzstring = wchar_t*; // // ensure_sentinel() // // Provides a way to obtain an span from a contiguous sequence // that ends with a (non-inclusive) sentinel value. // // Will fail-fast if sentinel cannot be found before max elements are examined. // template span ensure_sentinel(T* seq, std::ptrdiff_t max = PTRDIFF_MAX) { auto cur = seq; while ((cur - seq) < max && *cur != Sentinel) ++cur; Ensures(*cur == Sentinel); return{ seq, cur - seq }; } // // ensure_z - creates a string_span for a czstring or cwzstring. // Will fail fast if a null-terminator cannot be found before // the limit of size_type. // template inline span ensure_z(T* const & sz, std::ptrdiff_t max = PTRDIFF_MAX) { return ensure_sentinel(sz, max); } // TODO (neilmac) there is probably a better template-magic way to get the const and non-const overloads to share an implementation inline span ensure_z(char* const& sz, std::ptrdiff_t max) { auto len = strnlen(sz, max); Ensures(sz[len] == 0); return{ sz, static_cast(len) }; } inline span ensure_z(const char* const& sz, std::ptrdiff_t max) { auto len = strnlen(sz, max); Ensures(sz[len] == 0); return{ sz, static_cast(len) }; } inline span ensure_z(wchar_t* const& sz, std::ptrdiff_t max) { auto len = wcsnlen(sz, max); Ensures(sz[len] == 0); return{ sz, static_cast(len) }; } inline span ensure_z(const wchar_t* const& sz, std::ptrdiff_t max) { auto len = wcsnlen(sz, max); Ensures(sz[len] == 0); return{ sz, static_cast(len) }; } template span ensure_z(T(&sz)[N]) { return ensure_z(&sz[0], static_cast(N)); } template span::type, dynamic_range> ensure_z(Cont& cont) { return ensure_z(cont.data(), static_cast(cont.length())); } // TODO (neilmac) there is probably a better template-magic way to get the const and non-const overloads to share an implementation inline span remove_z(char* const& sz, std::ptrdiff_t max) { auto len = strnlen(sz, max); return{ sz, static_cast(len) }; } inline span remove_z(const char* const& sz, std::ptrdiff_t max) { auto len = strnlen(sz, max); return{ sz, static_cast(len) }; } inline span remove_z(wchar_t* const& sz, std::ptrdiff_t max) { auto len = wcsnlen(sz, max); return{ sz, static_cast(len) }; } inline span remove_z(const wchar_t* const& sz, std::ptrdiff_t max) { auto len = wcsnlen(sz, max); return{ sz, static_cast(len) }; } template span remove_z(T(&sz)[N]) { return remove_z(&sz[0], static_cast(N)); } template span::type, dynamic_range> remove_z(Cont& cont) { return remove_z(cont.data(), static_cast(cont.length())); } template class basic_string_span; namespace details { template struct is_basic_string_span_oracle : std::false_type {}; template struct is_basic_string_span_oracle> : std::true_type {}; template struct is_basic_string_span : is_basic_string_span_oracle> {}; } // // string_span and relatives // // Note that Extent is always single-dimension only // template class basic_string_span { using value_type = CharT; using const_value_type = std::add_const_t; using pointer = std::add_pointer_t; using reference = std::add_lvalue_reference_t; using const_reference = std::add_lvalue_reference_t; using bounds_type = static_bounds; using underlying_type = span; public: using size_type = ptrdiff_t; using iterator = typename underlying_type::iterator; using const_iterator = typename underlying_type::const_iterator; using reverse_iterator = typename underlying_type::reverse_iterator; using const_reverse_iterator = typename underlying_type::const_reverse_iterator; // empty constexpr basic_string_span() noexcept : real(nullptr) {} // copy constexpr basic_string_span(const basic_string_span& other) noexcept : real(other.real) {} // move constexpr basic_string_span(const basic_string_span&& other) noexcept : real(std::move(other.real)) {} // from nullptr and length constexpr basic_string_span(nullptr_t ptr, size_type length) noexcept : real(ptr, length) {} // For pointers and static arrays - if 0-terminated, remove 0 from the view // from c string constexpr basic_string_span(pointer& ptr) noexcept : real(ensure_z(ptr)) {} // from non-const pointer to const span template::value>> constexpr basic_string_span(std::remove_const_t*& ptr) noexcept : real(ensure_z(ptr)) {} // from raw data and length - remove 0 if needed constexpr basic_string_span(pointer ptr, size_type length) noexcept : real(remove_z(ptr, length)) {} // from static arrays and string literals template constexpr basic_string_span(value_type(&arr)[N]) noexcept : real(remove_z(arr)) {} // Those allow 0s in the middle, so we keep them constexpr basic_string_span(std::string& s) noexcept : real(&(s.at(0)), static_cast(s.length())) {} // from containers. It must have .size() and .data() function signatures template ::value && !details::is_basic_string_span::value && !(!std::is_const::value && std::is_const::value) // no converting const containers to non-const span && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> > constexpr basic_string_span(Cont& cont) : real(cont.data(), cont.size()) {} // from span template , typename Dummy = std::enable_if_t::value && std::is_convertible::value> > constexpr basic_string_span(const span& other) noexcept : real(other) {} // from string_span template , typename Dummy = std::enable_if_t::value && std::is_convertible::value> > constexpr basic_string_span(const basic_string_span& other) noexcept : real(other.data(), other.length()) {} // section on linear space template constexpr basic_string_span first() const noexcept { return{ real.first() }; } constexpr basic_string_span first(size_type count) const noexcept { return{ real.first(count); } } template constexpr basic_string_span last() const noexcept { return{ real.last() }; } constexpr basic_string_span last(size_type count) const noexcept { return{ real.last(count); } } template constexpr basic_string_span sub() const noexcept { return{ real.sub() }; } constexpr basic_string_span sub(size_type offset, size_type count = dynamic_range) const noexcept { return{ real.sub(offset, count) }; } constexpr const_reference operator[](size_type idx) const noexcept { return real[idx]; } constexpr reference operator[](size_type idx) noexcept { return real[idx]; } constexpr pointer data() const noexcept { return real.data(); } constexpr size_type length() const noexcept { return real.size(); } constexpr size_type size() const noexcept { return real.size(); } constexpr size_type used_length() const noexcept { return length(); } constexpr size_type bytes() const noexcept { return real.bytes(); } constexpr size_type used_bytes() const noexcept { return bytes(); } constexpr explicit operator bool() const noexcept { return real; } constexpr iterator begin() const noexcept { return real.begin(); } constexpr iterator end() const noexcept { return real.end(); } constexpr const_iterator cbegin() const noexcept { return real.cbegin(); } constexpr const_iterator cend() const noexcept { real.cend(); } constexpr reverse_iterator rbegin() const noexcept { return real.rbegin(); } constexpr reverse_iterator rend() const noexcept { return real.rend(); } constexpr const_reverse_iterator crbegin() const noexcept { return real.crbegin(); } constexpr const_reverse_iterator crend() const noexcept { return real.crend(); } private: span real; }; template using string_span = basic_string_span; template using cstring_span = basic_string_span; template using wstring_span = basic_string_span; template using cwstring_span = basic_string_span; // // to_string() allow (explicit) conversions from string_span to string // #ifndef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG template std::basic_string::type> to_string(basic_string_span view) { return{ view.data(), static_cast(view.length()) }; } #else inline std::string to_string(cstring_span<> view) { return{ view.data(), view.length() }; } inline std::string to_string(string_span<> view) { return{ view.data(), view.length() }; } inline std::wstring to_string(cwstring_span<> view) { return{ view.data(), view.length() }; } inline std::wstring to_string(wstring_span<> view) { return{ view.data(), view.length() }; } #endif template class basic_zstring_builder { public: using string_span_type = basic_string_span; using value_type = CharT; using pointer = CharT*; using size_type = typename string_span_type::size_type; using iterator = typename string_span_type::iterator; basic_zstring_builder(CharT* data, size_type length) : sv_(data, length) {} template basic_zstring_builder(CharT(&arr)[Size]) : sv_(arr) {} pointer data() const { return sv_.data(); } string_span_type view() const { return sv_; } size_type length() const { return sv_.length(); } pointer assume0() const { return data(); } string_span_type ensure_z() const { return gsl::ensure_z(sv_); } iterator begin() const { return sv_.begin(); } iterator end() const { return sv_.end(); } private: string_span_type sv_; }; template using zstring_builder = basic_zstring_builder; template using wzstring_builder = basic_zstring_builder; } constexpr bool operator==(const gsl::cstring_span<>& one, const gsl::cstring_span<>& other) noexcept { return std::equal(one.begin(), one.end(), other.begin(), other.end()); } constexpr bool operator==(const gsl::string_span<>& one, const gsl::string_span<>& other) noexcept { return std::equal(one.begin(), one.end(), other.begin(), other.end()); } // TODO: ca we make twmplate ops work? //template //constexpr bool operator==(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept //{ // return std::equal(one.begin(), one.end(), other.begin(), other.end()); //} /* template , std::remove_cv_t>::value>> constexpr bool operator==(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept { return std::equal(one.begin(), one.end(), other.begin(), other.end()); } template , std::remove_cv_t>::value>> constexpr bool operator!=(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept { return !(one == other); } template , std::remove_cv_t>::value>> constexpr bool operator<(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept { return std::lexicographical_compare(one.begin(), one.end(), other.begin(), other.end()); } template , std::remove_cv_t>::value>> constexpr bool operator<=(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept { return !(other < one); } template , std::remove_cv_t>::value>> constexpr bool operator>(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept { return other < one; } template , std::remove_cv_t>::value>> constexpr bool operator>=(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept { return !(one < other); } */ // VS 2013 workarounds #ifdef _MSC_VER #undef constexpr #pragma pop_macro("constexpr") #if _MSC_VER <= 1800 #pragma warning(pop) #ifndef GSL_THROW_ON_CONTRACT_VIOLATION #undef noexcept #pragma pop_macro("noexcept") #endif // GSL_THROW_ON_CONTRACT_VIOLATION #undef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG #endif // _MSC_VER <= 1800 #endif // _MSC_VER #if defined(GSL_THROW_ON_CONTRACT_VIOLATION) #undef noexcept #ifdef _MSC_VER #pragma pop_macro("noexcept") #endif #endif // GSL_THROW_ON_CONTRACT_VIOLATION #endif // GSL_STRING_SPAN_H