string_view (11436B)
1 // -*- c++ -*- 2 #pragma once 3 4 #include <libshit/assert.hpp> 5 6 #include <algorithm> // std::min 7 #include <cstddef> 8 #include <iosfwd> 9 #include <iterator> 10 #include <limits> 11 #include <stdexcept> 12 #include <type_traits> 13 14 namespace std 15 { 16 17 template <typename Char, typename Traits> 18 class basic_string_view 19 { 20 public: 21 using traits_type = Traits; 22 using value_type = Char; 23 using pointer = Char*; 24 using const_pointer = const Char*; 25 using reference = Char&; 26 using const_reference = const Char&; 27 using const_iterator = const Char*; 28 using iterator = const_iterator; 29 using const_reverse_iterator = reverse_iterator<const_iterator>; 30 using reverse_iterator = const_reverse_iterator; 31 using size_type = size_t; 32 using difference_type = ptrdiff_t; 33 34 using unsigned_value_type = make_unsigned_t<Char>; 35 using unsigned_pointer = unsigned_value_type*; 36 using unsigned_const_pointer = const unsigned_value_type*; 37 using unsigned_reference = unsigned_value_type&; 38 using unsigned_const_reference = const unsigned_value_type&; 39 40 static constexpr const size_type npos = numeric_limits<size_type>::max(); 41 42 constexpr basic_string_view() noexcept : str{nullptr}, len{0} {} 43 44 constexpr basic_string_view(const_pointer str) noexcept 45 : str{str}, len{str ? Traits::length(str) : 0} {} 46 47 basic_string_view(unsigned_const_pointer str) noexcept 48 : basic_string_view{reinterpret_cast<const_pointer>(str)} {} 49 50 constexpr basic_string_view( 51 const_pointer str, size_type len) noexcept 52 : str{str}, len{len} 53 { LIBSHIT_ASSERT(str != nullptr || len == 0); } 54 55 constexpr basic_string_view( 56 unsigned_const_pointer str, size_type len) noexcept 57 : basic_string_view{reinterpret_cast<const_pointer>(str), len} {} 58 59 template <typename Allocator> 60 basic_string_view( 61 const basic_string<Char, Traits, Allocator>& str) noexcept 62 : str{str.c_str()}, len{str.size()} {} 63 64 template <size_t N> 65 constexpr basic_string_view(const Char (&str)[N]) noexcept 66 : str{str}, len{N-1} {} 67 68 constexpr const_iterator begin() const noexcept { return str; } 69 constexpr const_iterator cbegin() const noexcept { return str; } 70 constexpr const_iterator end() const noexcept { return str+len; } 71 constexpr const_iterator cend() const noexcept { return str+len; } 72 73 constexpr const_reverse_iterator rbegin() const noexcept 74 { return const_reverse_iterator{end()}; } 75 constexpr const_reverse_iterator crbegin() const noexcept 76 { return const_reverse_iterator{end()}; } 77 constexpr const_reverse_iterator rend() const noexcept 78 { return const_reverse_iterator{begin()}; } 79 constexpr const_reverse_iterator crend() const noexcept 80 { return const_reverse_iterator{begin()}; } 81 82 constexpr const_reference operator[](size_type i) const noexcept 83 { 84 LIBSHIT_ASSERT(i < len); 85 return str[i]; 86 } 87 constexpr const_reference at(size_type i) const 88 { 89 if (i >= len) 90 throw out_of_range("NonowningString"); 91 return str[i]; 92 } 93 94 // undefined if string is empty 95 constexpr const_reference front() const noexcept 96 { LIBSHIT_ASSERT(len); return str[0]; } 97 constexpr const_reference back() const noexcept 98 { LIBSHIT_ASSERT(len); return str[len-1]; } 99 100 // can return nullptr or pointer to '\0' in case of empty string 101 constexpr const_pointer data() const noexcept { return str; } 102 103 constexpr size_type size() const noexcept { return len; } 104 constexpr size_type length() const noexcept { return len; } 105 constexpr size_type max_size() const noexcept 106 { return numeric_limits<size_type>::max(); } 107 constexpr bool empty() const noexcept { return len == 0; } 108 109 constexpr void remove_prefix(size_type n) noexcept 110 { LIBSHIT_ASSERT(len >= n); str += n; len -= n; } 111 112 constexpr void remove_suffix(size_type n) noexcept 113 { LIBSHIT_ASSERT(len >= n); len -= n; } 114 115 constexpr void swap(basic_string_view& o) noexcept 116 { 117 swap(str, o.str); 118 swap(len, o.len); 119 } 120 121 // todo: does this solve the missing std::string ctor problem? 122 template <typename Allocator> 123 operator basic_string<Char, Traits, Allocator>() const 124 { return {str, len}; } 125 126 // copy, *find*: too lazy 127 constexpr basic_string_view substr( 128 size_type pos, size_type n = npos) const noexcept 129 { 130 LIBSHIT_ASSERT(pos <= len); 131 return {str+pos, min(n, len-pos)}; 132 } 133 134 // search functions 135 constexpr size_type find( 136 basic_string_view o, size_type pos = 0) const noexcept 137 { 138 if (pos >= len || pos + o.size() > len) return npos; 139 for (; pos <= len - o.len; ++pos) 140 if (!Traits::compare(str + pos, o.str, o.len)) 141 return pos; 142 return npos; 143 } 144 constexpr size_type find(Char ch, size_type pos = 0) const noexcept 145 { 146 for (; pos < len; ++pos) 147 if (Traits::eq(str[pos], ch)) return pos; 148 return npos; 149 } 150 constexpr size_type find(const Char* s, size_type pos, size_type count) const 151 { return find(basic_string_view(s, count), pos); } 152 constexpr size_type find(const Char* s, size_type pos = 0) const 153 { return find(basic_string_view(s), pos); } 154 155 156 constexpr size_type rfind( 157 basic_string_view o, size_type pos = npos) const noexcept 158 { 159 if (o.size() > len) return npos; 160 if (pos > len - o.len) pos = len - o.len; 161 while (true) 162 { 163 if (!Traits::compare(str + pos, o.str, o.len)) 164 return pos; 165 if (pos == 0) return npos; 166 --pos; 167 } 168 } 169 constexpr size_type rfind(Char ch, size_type pos = npos) const noexcept 170 { 171 if (pos > len-1) pos = len-1; 172 while (true) 173 { 174 if (Traits::eq(str[pos], ch)) return pos; 175 if (pos == 0) return npos; 176 --pos; 177 } 178 } 179 constexpr size_type rfind(const Char* s, size_type pos, size_type count) const 180 { return rfind(basic_string_view(s, count), pos); } 181 constexpr size_type rfind(const Char* s, size_type pos = npos) const 182 { return rfind(basic_string_view(s), pos); } 183 184 185 constexpr size_type find_first_of( 186 basic_string_view o, size_type pos = 0) const noexcept 187 { 188 if (pos >= len || o.len == 0) return npos; 189 for (; pos < len; ++pos) 190 for (size_t i = 0; i < o.len; ++i) 191 if (Traits::eq(str[pos], o.str[i])) 192 return pos; 193 return npos; 194 } 195 constexpr size_type find_first_of(Char ch, size_type pos = 0) const noexcept 196 { return find(ch, pos); } 197 constexpr size_type find_first_of(const Char* s, size_type pos, size_type count) const 198 { return find_first_of(basic_string_view(s, count), pos); } 199 constexpr size_type find_first_of(const Char* s, size_type pos = 0) const 200 { return find_first_of(basic_string_view(s), pos); } 201 202 203 constexpr size_type find_last_of( 204 basic_string_view o, size_type pos = npos) const noexcept 205 { 206 if (o.len == 0) return npos; 207 if (pos > len-1) pos = len-1; 208 while (true) 209 { 210 for (size_t i = 0; i < o.len; ++i) 211 if (Traits::eq(str[pos], o.str[i])) 212 return pos; 213 if (pos == 0) return npos; 214 --pos; 215 } 216 } 217 constexpr size_type find_last_of(Char ch, size_type pos = npos) const noexcept 218 { return rfind(ch, pos); } 219 constexpr size_type find_last_of(const Char* s, size_type pos, size_type count) const 220 { return find_last_of(basic_string_view(s, count), pos); } 221 constexpr size_type find_last_of(const Char* s, size_type pos = npos) const 222 { return find_last_of(basic_string_view(s), pos); } 223 224 225 constexpr size_type find_first_not_of( 226 basic_string_view o, size_type pos = 0) const noexcept 227 { 228 if (pos >= len || o.len == 0) return npos; 229 for (; pos < len; ++pos) 230 { 231 bool found = false; 232 for (size_t i = 0; i < o.len; ++i) 233 if (Traits::eq(str[pos], o.str[i])) 234 { found = true; break; } // can't goto in constexpr :'( 235 if (!found) return pos; 236 } 237 return npos; 238 } 239 constexpr size_type find_first_not_of(Char ch, size_type pos = 0) const noexcept 240 { 241 for (; pos < len; ++pos) 242 if (!Traits::eq(str[pos], ch)) return pos; 243 return npos; 244 } 245 constexpr size_type find_first_not_of(const Char* s, size_type pos, size_type count) const 246 { return find_first_not_of(basic_string_view(s, count), pos); } 247 constexpr size_type find_first_not_of(const Char* s, size_type pos = 0) const 248 { return find_first_not_of(basic_string_view(s), pos); } 249 250 251 constexpr size_type find_last_not_of( 252 basic_string_view o, size_type pos = npos) const noexcept 253 { 254 if (o.len == 0) return npos; 255 if (pos > len-1) pos = len-1; 256 while (true) 257 { 258 bool found = false; 259 for (size_t i = 0; i < o.len; ++i) 260 if (Traits::eq(str[pos], o.str[i])) 261 { found = true; break; } // can't goto in constexpr :'( 262 if (!found) return pos; 263 if (pos == 0) return npos; 264 --pos; 265 } 266 } 267 constexpr size_type find_last_not_of(Char ch, size_type pos = npos) const noexcept 268 { 269 if (pos > len-1) pos = len-1; 270 while (true) 271 { 272 if (!Traits::eq(str[pos], ch)) return pos; 273 if (pos == 0) return npos; 274 --pos; 275 } 276 } 277 constexpr size_type find_last_not_of(const Char* s, size_type pos, size_type count) const 278 { return find_last_not_of(basic_string_view(s, count), pos); } 279 constexpr size_type find_last_not_of(const Char* s, size_type pos = npos) const 280 { return find_last_not_of(basic_string_view(s), pos); } 281 282 283 constexpr int compare(basic_string_view o) const noexcept 284 { 285 auto cmp = Traits::compare(str, o.str, min(len, o.len)); 286 if (cmp == 0) cmp = (len == o.len ? 0 : (len < o.len ? -1 : 1)); 287 return cmp; 288 } 289 // too lazy for the other overloads 290 291 constexpr bool operator==(basic_string_view o) const noexcept 292 { 293 return len == o.len && 294 (str == o.str || Traits::compare(str, o.str, len) == 0); 295 } 296 constexpr bool operator!=(basic_string_view o) const noexcept 297 { return !(*this == o); } 298 299 #define X_GEN_OP(op) \ 300 constexpr bool operator op(basic_string_view o) const noexcept \ 301 { return compare(o) op 0; } 302 X_GEN_OP(<) X_GEN_OP(<=) X_GEN_OP(>) X_GEN_OP(>=) 303 #undef X_GEN_OP 304 305 private: 306 const_pointer str; 307 size_type len; 308 }; 309 310 #define X_GEN_OP(op) \ 311 template <typename Char, typename Traits, typename Allocator> \ 312 inline bool operator op( \ 313 const basic_string<Char, Traits, Allocator>& lhs, \ 314 basic_string_view<Char, Traits> rhs) \ 315 { \ 316 return basic_string_view<Char, Traits>{lhs} op rhs; \ 317 } 318 X_GEN_OP(==) 319 X_GEN_OP(!=) 320 X_GEN_OP(<) 321 X_GEN_OP(<=) 322 X_GEN_OP(>) 323 X_GEN_OP(>=) 324 #undef X_GEN_OP 325 326 using string_view = basic_string_view<char>; 327 using wstring_view = basic_string_view<wchar_t>; 328 using u16string_view = basic_string_view<char16_t>; 329 using u32string_view = basic_string_view<char32_t>; 330 331 template <typename T, typename U> 332 inline basic_ostream<T>& operator<<( 333 basic_ostream<T>& os, basic_string_view<T, U> s) 334 { 335 os.write(s.data(), s.size()); 336 return os; 337 } 338 339 }