1///////////////////////////////////////////////////////////////////////////////
2//
3// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
4//
5// This code is licensed under the MIT License (MIT).
6//
7// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
12// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
13// THE SOFTWARE.
14//
15///////////////////////////////////////////////////////////////////////////////
16
17#pragma once
18
19#ifndef GSL_STRING_SPAN_H
20#define GSL_STRING_SPAN_H
21
22#include <gsl/gsl_assert>
23#include <gsl/gsl_util>
24#include <gsl/span>
25
26#include <cstdint>
27#include <cstring>
28#include <string>
29
30#ifdef _MSC_VER
31#pragma warning(push)
32
33// blanket turn off warnings from CppCoreCheck for now
34// so people aren't annoyed by them when running the tool.
35// more targeted suppressions will be added in a future update to the GSL
36#pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495)
37
38#if _MSC_VER < 1910
39#pragma push_macro("constexpr")
40#define constexpr /*constexpr*/
41
42#endif           // _MSC_VER < 1910
43#endif           // _MSC_VER
44
45// In order to test the library, we need it to throw exceptions that we can catch
46#ifdef GSL_THROW_ON_CONTRACT_VIOLATION
47#define GSL_NOEXCEPT /*noexcept*/
48#else
49#define GSL_NOEXCEPT noexcept
50#endif // GSL_THROW_ON_CONTRACT_VIOLATION
51
52namespace gsl
53{
54//
55// czstring and wzstring
56//
57// These are "tag" typedefs for C-style strings (i.e. null-terminated character arrays)
58// that allow static analysis to help find bugs.
59//
60// There are no additional features/semantics that we can find a way to add inside the
61// type system for these types that will not either incur significant runtime costs or
62// (sometimes needlessly) break existing programs when introduced.
63//
64
65template <typename CharT, std::ptrdiff_t Extent = dynamic_extent>
66using basic_zstring = CharT*;
67
68template <std::ptrdiff_t Extent = dynamic_extent>
69using czstring = basic_zstring<const char, Extent>;
70
71template <std::ptrdiff_t Extent = dynamic_extent>
72using cwzstring = basic_zstring<const wchar_t, Extent>;
73
74template <std::ptrdiff_t Extent = dynamic_extent>
75using zstring = basic_zstring<char, Extent>;
76
77template <std::ptrdiff_t Extent = dynamic_extent>
78using wzstring = basic_zstring<wchar_t, Extent>;
79
80namespace details
81{
82    inline std::ptrdiff_t string_length(const char* str, std::ptrdiff_t n)
83    {
84        if (str == nullptr || n <= 0) return 0;
85
86        span<const char> str_span{str, n};
87
88        std::ptrdiff_t len = 0;
89        while (len < n && str_span[len]) len++;
90
91        return len;
92    }
93
94    inline std::ptrdiff_t wstring_length(const wchar_t* str, std::ptrdiff_t n)
95    {
96        if (str == nullptr || n <= 0) return 0;
97
98        span<const wchar_t> str_span{str, n};
99
100        std::ptrdiff_t len = 0;
101        while (len < n && str_span[len]) len++;
102
103        return len;
104    }
105}
106
107//
108// ensure_sentinel()
109//
110// Provides a way to obtain an span from a contiguous sequence
111// that ends with a (non-inclusive) sentinel value.
112//
113// Will fail-fast if sentinel cannot be found before max elements are examined.
114//
115template <typename T, const T Sentinel>
116span<T, dynamic_extent> ensure_sentinel(T* seq, std::ptrdiff_t max = PTRDIFF_MAX)
117{
118    auto cur = seq;
119    while ((cur - seq) < max && *cur != Sentinel) ++cur;
120    Ensures(*cur == Sentinel);
121    return {seq, cur - seq};
122}
123
124//
125// ensure_z - creates a span for a czstring or cwzstring.
126// Will fail fast if a null-terminator cannot be found before
127// the limit of size_type.
128//
129template <typename T>
130inline span<T, dynamic_extent> ensure_z(T* const& sz, std::ptrdiff_t max = PTRDIFF_MAX)
131{
132    return ensure_sentinel<T, 0>(sz, max);
133}
134
135// TODO (neilmac) there is probably a better template-magic way to get the const and non-const
136// overloads to share an implementation
137inline span<char, dynamic_extent> ensure_z(char* const& sz, std::ptrdiff_t max)
138{
139    auto len = details::string_length(sz, max);
140    Ensures(sz[len] == 0);
141    return {sz, len};
142}
143
144inline span<const char, dynamic_extent> ensure_z(const char* const& sz, std::ptrdiff_t max)
145{
146    auto len = details::string_length(sz, max);
147    Ensures(sz[len] == 0);
148    return {sz, len};
149}
150
151inline span<wchar_t, dynamic_extent> ensure_z(wchar_t* const& sz, std::ptrdiff_t max)
152{
153    auto len = details::wstring_length(sz, max);
154    Ensures(sz[len] == 0);
155    return {sz, len};
156}
157
158inline span<const wchar_t, dynamic_extent> ensure_z(const wchar_t* const& sz, std::ptrdiff_t max)
159{
160    auto len = details::wstring_length(sz, max);
161    Ensures(sz[len] == 0);
162    return {sz, len};
163}
164
165template <typename T, std::size_t N>
166span<T, dynamic_extent> ensure_z(T (&sz)[N])
167{
168    return ensure_z(&sz[0], static_cast<std::ptrdiff_t>(N));
169}
170
171template <class Cont>
172span<typename std::remove_pointer<typename Cont::pointer>::type, dynamic_extent>
173ensure_z(Cont& cont)
174{
175    return ensure_z(cont.data(), static_cast<std::ptrdiff_t>(cont.length()));
176}
177
178template <typename CharT, std::ptrdiff_t>
179class basic_string_span;
180
181namespace details
182{
183    template <typename T>
184    struct is_basic_string_span_oracle : std::false_type
185    {
186    };
187
188    template <typename CharT, std::ptrdiff_t Extent>
189    struct is_basic_string_span_oracle<basic_string_span<CharT, Extent>> : std::true_type
190    {
191    };
192
193    template <typename T>
194    struct is_basic_string_span : is_basic_string_span_oracle<std::remove_cv_t<T>>
195    {
196    };
197
198    template <typename T>
199    struct length_func
200    {
201    };
202
203    template <>
204    struct length_func<char>
205    {
206        std::ptrdiff_t operator()(const char* const ptr, std::ptrdiff_t length) GSL_NOEXCEPT
207        {
208            return details::string_length(ptr, length);
209        }
210    };
211
212    template <>
213    struct length_func<wchar_t>
214    {
215        std::ptrdiff_t operator()(const wchar_t* const ptr, std::ptrdiff_t length) GSL_NOEXCEPT
216        {
217            return details::wstring_length(ptr, length);
218        }
219    };
220
221    template <>
222    struct length_func<const char>
223    {
224        std::ptrdiff_t operator()(const char* const ptr, std::ptrdiff_t length) GSL_NOEXCEPT
225        {
226            return details::string_length(ptr, length);
227        }
228    };
229
230    template <>
231    struct length_func<const wchar_t>
232    {
233        std::ptrdiff_t operator()(const wchar_t* const ptr, std::ptrdiff_t length) GSL_NOEXCEPT
234        {
235            return details::wstring_length(ptr, length);
236        }
237    };
238}
239
240//
241// string_span and relatives
242//
243template <typename CharT, std::ptrdiff_t Extent = dynamic_extent>
244class basic_string_span
245{
246public:
247    using element_type = CharT;
248    using pointer = std::add_pointer_t<element_type>;
249    using reference = std::add_lvalue_reference_t<element_type>;
250    using const_reference = std::add_lvalue_reference_t<std::add_const_t<element_type>>;
251    using impl_type = span<element_type, Extent>;
252
253    using index_type = typename impl_type::index_type;
254    using iterator = typename impl_type::iterator;
255    using const_iterator = typename impl_type::const_iterator;
256    using reverse_iterator = typename impl_type::reverse_iterator;
257    using const_reverse_iterator = typename impl_type::const_reverse_iterator;
258
259    // default (empty)
260    constexpr basic_string_span() GSL_NOEXCEPT = default;
261
262    // copy
263    constexpr basic_string_span(const basic_string_span& other) GSL_NOEXCEPT = default;
264
265// move
266    constexpr basic_string_span(basic_string_span&& other) GSL_NOEXCEPT = default;
267
268    // assign
269    constexpr basic_string_span& operator=(const basic_string_span& other) GSL_NOEXCEPT = default;
270
271// move assign
272    constexpr basic_string_span& operator=(basic_string_span&& other) GSL_NOEXCEPT = default;
273
274    // from nullptr
275    constexpr basic_string_span(std::nullptr_t ptr) GSL_NOEXCEPT : span_(ptr) {}
276
277    constexpr basic_string_span(pointer ptr, index_type length) : span_(ptr, length) {}
278    constexpr basic_string_span(pointer firstElem, pointer lastElem) : span_(firstElem, lastElem) {}
279
280    // From static arrays - if 0-terminated, remove 0 from the view
281    // All other containers allow 0s within the length, so we do not remove them
282    template <std::size_t N>
283    constexpr basic_string_span(element_type (&arr)[N]) : span_(remove_z(arr))
284    {
285    }
286
287    template <std::size_t N, class ArrayElementType = std::remove_const_t<element_type>>
288    constexpr basic_string_span(std::array<ArrayElementType, N>& arr) GSL_NOEXCEPT : span_(arr)
289    {
290    }
291
292    template <std::size_t N, class ArrayElementType = std::remove_const_t<element_type>>
293    constexpr basic_string_span(const std::array<ArrayElementType, N>& arr) GSL_NOEXCEPT
294        : span_(arr)
295    {
296    }
297
298    // Container signature should work for basic_string after C++17 version exists
299    template <class Traits, class Allocator>
300    constexpr basic_string_span(std::basic_string<element_type, Traits, Allocator>& str)
301        : span_(&str[0], narrow_cast<std::ptrdiff_t>(str.length()))
302    {
303    }
304
305    template <class Traits, class Allocator>
306    constexpr basic_string_span(const std::basic_string<element_type, Traits, Allocator>& str)
307        : span_(&str[0], str.length())
308    {
309    }
310
311    // from containers. Containers must have a pointer type and data() function signatures
312    template <class Container,
313              class = std::enable_if_t<
314                  !details::is_basic_string_span<Container>::value &&
315                  std::is_convertible<typename Container::pointer, pointer>::value &&
316                  std::is_convertible<typename Container::pointer,
317                                      decltype(std::declval<Container>().data())>::value>>
318    constexpr basic_string_span(Container& cont) : span_(cont)
319    {
320    }
321
322    template <class Container,
323              class = std::enable_if_t<
324                  !details::is_basic_string_span<Container>::value &&
325                  std::is_convertible<typename Container::pointer, pointer>::value &&
326                  std::is_convertible<typename Container::pointer,
327                                      decltype(std::declval<Container>().data())>::value>>
328    constexpr basic_string_span(const Container& cont) : span_(cont)
329    {
330    }
331
332    // from string_span
333    template <
334        class OtherValueType, std::ptrdiff_t OtherExtent,
335        class = std::enable_if_t<std::is_convertible<
336            typename basic_string_span<OtherValueType, OtherExtent>::impl_type, impl_type>::value>>
337    constexpr basic_string_span(basic_string_span<OtherValueType, OtherExtent> other)
338        : span_(other.data(), other.length())
339    {
340    }
341
342    template <index_type Count>
343    constexpr basic_string_span<element_type, Count> first() const
344    {
345        return {span_.template first<Count>()};
346    }
347
348    constexpr basic_string_span<element_type, dynamic_extent> first(index_type count) const
349    {
350        return {span_.first(count)};
351    }
352
353    template <index_type Count>
354    constexpr basic_string_span<element_type, Count> last() const
355    {
356        return {span_.template last<Count>()};
357    }
358
359    constexpr basic_string_span<element_type, dynamic_extent> last(index_type count) const
360    {
361        return {span_.last(count)};
362    }
363
364    template <index_type Offset, index_type Count>
365    constexpr basic_string_span<element_type, Count> subspan() const
366    {
367        return {span_.template subspan<Offset, Count>()};
368    }
369
370    constexpr basic_string_span<element_type, dynamic_extent>
371    subspan(index_type offset, index_type count = dynamic_extent) const
372    {
373        return {span_.subspan(offset, count)};
374    }
375
376    constexpr reference operator[](index_type idx) const { return span_[idx]; }
377    constexpr reference operator()(index_type idx) const { return span_[idx]; }
378
379    constexpr pointer data() const { return span_.data(); }
380
381    constexpr index_type length() const GSL_NOEXCEPT { return span_.size(); }
382    constexpr index_type size() const GSL_NOEXCEPT { return span_.size(); }
383    constexpr index_type size_bytes() const GSL_NOEXCEPT { return span_.size_bytes(); }
384    constexpr index_type length_bytes() const GSL_NOEXCEPT { return span_.length_bytes(); }
385    constexpr bool empty() const GSL_NOEXCEPT { return size() == 0; }
386
387    constexpr iterator begin() const GSL_NOEXCEPT { return span_.begin(); }
388    constexpr iterator end() const GSL_NOEXCEPT { return span_.end(); }
389
390    constexpr const_iterator cbegin() const GSL_NOEXCEPT { return span_.cbegin(); }
391    constexpr const_iterator cend() const GSL_NOEXCEPT { return span_.cend(); }
392
393    constexpr reverse_iterator rbegin() const GSL_NOEXCEPT { return span_.rbegin(); }
394    constexpr reverse_iterator rend() const GSL_NOEXCEPT { return span_.rend(); }
395
396    constexpr const_reverse_iterator crbegin() const GSL_NOEXCEPT { return span_.crbegin(); }
397    constexpr const_reverse_iterator crend() const GSL_NOEXCEPT { return span_.crend(); }
398
399private:
400    static impl_type remove_z(pointer const& sz, std::ptrdiff_t max)
401    {
402        return {sz, details::length_func<element_type>()(sz, max)};
403    }
404
405    template <std::size_t N>
406    static impl_type remove_z(element_type (&sz)[N])
407    {
408        return remove_z(&sz[0], narrow_cast<std::ptrdiff_t>(N));
409    }
410
411    impl_type span_;
412};
413
414template <std::ptrdiff_t Extent = dynamic_extent>
415using string_span = basic_string_span<char, Extent>;
416
417template <std::ptrdiff_t Extent = dynamic_extent>
418using cstring_span = basic_string_span<const char, Extent>;
419
420template <std::ptrdiff_t Extent = dynamic_extent>
421using wstring_span = basic_string_span<wchar_t, Extent>;
422
423template <std::ptrdiff_t Extent = dynamic_extent>
424using cwstring_span = basic_string_span<const wchar_t, Extent>;
425
426//
427// to_string() allow (explicit) conversions from string_span to string
428//
429
430template <typename CharT, std::ptrdiff_t Extent>
431std::basic_string<typename std::remove_const<CharT>::type>
432to_string(basic_string_span<CharT, Extent> view)
433{
434    return {view.data(), static_cast<std::size_t>(view.length())};
435}
436
437template <typename CharT, typename Traits = typename std::char_traits<CharT>,
438          typename Allocator = std::allocator<CharT>, typename gCharT, std::ptrdiff_t Extent>
439std::basic_string<CharT, Traits, Allocator> to_basic_string(basic_string_span<gCharT, Extent> view)
440{
441    return {view.data(), static_cast<std::size_t>(view.length())};
442}
443
444// zero-terminated string span, used to convert
445// zero-terminated spans to legacy strings
446template <typename CharT, std::ptrdiff_t Extent = dynamic_extent>
447class basic_zstring_span
448{
449public:
450    using value_type = CharT;
451    using const_value_type = std::add_const_t<CharT>;
452
453    using pointer = std::add_pointer_t<value_type>;
454    using const_pointer = std::add_pointer_t<const_value_type>;
455
456    using zstring_type = basic_zstring<value_type, Extent>;
457    using const_zstring_type = basic_zstring<const_value_type, Extent>;
458
459    using impl_type = span<value_type, Extent>;
460    using string_span_type = basic_string_span<value_type, Extent>;
461
462    constexpr basic_zstring_span(impl_type s) GSL_NOEXCEPT : span_(s)
463    {
464        // expects a zero-terminated span
465        Expects(s[s.size() - 1] == '\0');
466    }
467
468    // copy
469    constexpr basic_zstring_span(const basic_zstring_span& other) = default;
470
471// move
472    constexpr basic_zstring_span(basic_zstring_span&& other) = default;
473
474    // assign
475    constexpr basic_zstring_span& operator=(const basic_zstring_span& other) = default;
476
477// move assign
478    constexpr basic_zstring_span& operator=(basic_zstring_span&& other) = default;
479
480    constexpr bool empty() const GSL_NOEXCEPT { return span_.size() == 0; }
481
482    constexpr string_span_type as_string_span() const GSL_NOEXCEPT
483    {
484        auto sz = span_.size();
485        return span_.first(sz <= 0 ? 0 : sz - 1);
486    }
487
488    constexpr string_span_type ensure_z() const GSL_NOEXCEPT { return gsl::ensure_z(span_); }
489
490    constexpr const_zstring_type assume_z() const GSL_NOEXCEPT { return span_.data(); }
491
492private:
493    impl_type span_;
494};
495
496template <std::ptrdiff_t Max = dynamic_extent>
497using zstring_span = basic_zstring_span<char, Max>;
498
499template <std::ptrdiff_t Max = dynamic_extent>
500using wzstring_span = basic_zstring_span<wchar_t, Max>;
501
502template <std::ptrdiff_t Max = dynamic_extent>
503using czstring_span = basic_zstring_span<const char, Max>;
504
505template <std::ptrdiff_t Max = dynamic_extent>
506using cwzstring_span = basic_zstring_span<const wchar_t, Max>;
507
508// operator ==
509template <class CharT, std::ptrdiff_t Extent, class T,
510          class = std::enable_if_t<
511              details::is_basic_string_span<T>::value ||
512              std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>>>::value>>
513bool operator==(const gsl::basic_string_span<CharT, Extent>& one, const T& other) GSL_NOEXCEPT
514{
515    const gsl::basic_string_span<std::add_const_t<CharT>> tmp(other);
516    return std::equal(one.begin(), one.end(), tmp.begin(), tmp.end());
517}
518
519template <class CharT, std::ptrdiff_t Extent, class T,
520          class = std::enable_if_t<
521              !details::is_basic_string_span<T>::value &&
522              std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>>>::value>>
523bool operator==(const T& one, const gsl::basic_string_span<CharT, Extent>& other) GSL_NOEXCEPT
524{
525    gsl::basic_string_span<std::add_const_t<CharT>> tmp(one);
526    return std::equal(tmp.begin(), tmp.end(), other.begin(), other.end());
527}
528
529// operator !=
530template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
531          typename = std::enable_if_t<std::is_convertible<
532              T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>>
533bool operator!=(gsl::basic_string_span<CharT, Extent> one, const T& other) GSL_NOEXCEPT
534{
535    return !(one == other);
536}
537
538template <
539    typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
540    typename Dummy = std::enable_if_t<
541        std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value &&
542        !gsl::details::is_basic_string_span<T>::value>>
543bool operator!=(const T& one, gsl::basic_string_span<CharT, Extent> other) GSL_NOEXCEPT
544{
545    return !(one == other);
546}
547
548// operator<
549template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
550          typename = std::enable_if_t<std::is_convertible<
551              T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>>
552bool operator<(gsl::basic_string_span<CharT, Extent> one, const T& other) GSL_NOEXCEPT
553{
554    const gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(other);
555    return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end());
556}
557
558template <
559    typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
560    typename Dummy = std::enable_if_t<
561        std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value &&
562        !gsl::details::is_basic_string_span<T>::value>>
563bool operator<(const T& one, gsl::basic_string_span<CharT, Extent> other) GSL_NOEXCEPT
564{
565    gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(one);
566    return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end());
567}
568
569#ifndef _MSC_VER
570
571// VS treats temp and const containers as convertible to basic_string_span,
572// so the cases below are already covered by the previous operators
573
574template <
575    typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
576    typename DataType = typename T::value_type,
577    typename Dummy = std::enable_if_t<
578        !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
579        std::is_convertible<DataType*, CharT*>::value &&
580        std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
581                     DataType>::value>>
582bool operator<(gsl::basic_string_span<CharT, Extent> one, const T& other) GSL_NOEXCEPT
583{
584    gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(other);
585    return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end());
586}
587
588template <
589    typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
590    typename DataType = typename T::value_type,
591    typename Dummy = std::enable_if_t<
592        !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
593        std::is_convertible<DataType*, CharT*>::value &&
594        std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
595                     DataType>::value>>
596bool operator<(const T& one, gsl::basic_string_span<CharT, Extent> other) GSL_NOEXCEPT
597{
598    gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(one);
599    return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end());
600}
601#endif
602
603// operator <=
604template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
605          typename = std::enable_if_t<std::is_convertible<
606              T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>>
607bool operator<=(gsl::basic_string_span<CharT, Extent> one, const T& other) GSL_NOEXCEPT
608{
609    return !(other < one);
610}
611
612template <
613    typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
614    typename Dummy = std::enable_if_t<
615        std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value &&
616        !gsl::details::is_basic_string_span<T>::value>>
617bool operator<=(const T& one, gsl::basic_string_span<CharT, Extent> other) GSL_NOEXCEPT
618{
619    return !(other < one);
620}
621
622#ifndef _MSC_VER
623
624// VS treats temp and const containers as convertible to basic_string_span,
625// so the cases below are already covered by the previous operators
626
627template <
628    typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
629    typename DataType = typename T::value_type,
630    typename Dummy = std::enable_if_t<
631        !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
632        std::is_convertible<DataType*, CharT*>::value &&
633        std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
634                     DataType>::value>>
635bool operator<=(gsl::basic_string_span<CharT, Extent> one, const T& other) GSL_NOEXCEPT
636{
637    return !(other < one);
638}
639
640template <
641    typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
642    typename DataType = typename T::value_type,
643    typename Dummy = std::enable_if_t<
644        !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
645        std::is_convertible<DataType*, CharT*>::value &&
646        std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
647                     DataType>::value>>
648bool operator<=(const T& one, gsl::basic_string_span<CharT, Extent> other) GSL_NOEXCEPT
649{
650    return !(other < one);
651}
652#endif
653
654// operator>
655template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
656          typename = std::enable_if_t<std::is_convertible<
657              T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>>
658bool operator>(gsl::basic_string_span<CharT, Extent> one, const T& other) GSL_NOEXCEPT
659{
660    return other < one;
661}
662
663template <
664    typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
665    typename Dummy = std::enable_if_t<
666        std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value &&
667        !gsl::details::is_basic_string_span<T>::value>>
668bool operator>(const T& one, gsl::basic_string_span<CharT, Extent> other) GSL_NOEXCEPT
669{
670    return other < one;
671}
672
673#ifndef _MSC_VER
674
675// VS treats temp and const containers as convertible to basic_string_span,
676// so the cases below are already covered by the previous operators
677
678template <
679    typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
680    typename DataType = typename T::value_type,
681    typename Dummy = std::enable_if_t<
682        !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
683        std::is_convertible<DataType*, CharT*>::value &&
684        std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
685                     DataType>::value>>
686bool operator>(gsl::basic_string_span<CharT, Extent> one, const T& other) GSL_NOEXCEPT
687{
688    return other < one;
689}
690
691template <
692    typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
693    typename DataType = typename T::value_type,
694    typename Dummy = std::enable_if_t<
695        !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
696        std::is_convertible<DataType*, CharT*>::value &&
697        std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
698                     DataType>::value>>
699bool operator>(const T& one, gsl::basic_string_span<CharT, Extent> other) GSL_NOEXCEPT
700{
701    return other < one;
702}
703#endif
704
705// operator >=
706template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
707          typename = std::enable_if_t<std::is_convertible<
708              T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>>
709bool operator>=(gsl::basic_string_span<CharT, Extent> one, const T& other) GSL_NOEXCEPT
710{
711    return !(one < other);
712}
713
714template <
715    typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
716    typename Dummy = std::enable_if_t<
717        std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value &&
718        !gsl::details::is_basic_string_span<T>::value>>
719bool operator>=(const T& one, gsl::basic_string_span<CharT, Extent> other) GSL_NOEXCEPT
720{
721    return !(one < other);
722}
723
724#ifndef _MSC_VER
725
726// VS treats temp and const containers as convertible to basic_string_span,
727// so the cases below are already covered by the previous operators
728
729template <
730    typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
731    typename DataType = typename T::value_type,
732    typename Dummy = std::enable_if_t<
733        !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
734        std::is_convertible<DataType*, CharT*>::value &&
735        std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
736                     DataType>::value>>
737bool operator>=(gsl::basic_string_span<CharT, Extent> one, const T& other) GSL_NOEXCEPT
738{
739    return !(one < other);
740}
741
742template <
743    typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
744    typename DataType = typename T::value_type,
745    typename Dummy = std::enable_if_t<
746        !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
747        std::is_convertible<DataType*, CharT*>::value &&
748        std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
749                     DataType>::value>>
750bool operator>=(const T& one, gsl::basic_string_span<CharT, Extent> other) GSL_NOEXCEPT
751{
752    return !(one < other);
753}
754#endif
755} // namespace GSL
756
757#undef GSL_NOEXCEPT
758
759#ifdef _MSC_VER
760#pragma warning(pop)
761
762#if _MSC_VER < 1910
763#undef constexpr
764#pragma pop_macro("constexpr")
765
766#endif // _MSC_VER < 1910
767#endif // _MSC_VER
768
769#endif // GSL_STRING_SPAN_H
770