1 // Copyright 2024 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_STRINGS_CSTRING_VIEW_H_ 6 #define BASE_STRINGS_CSTRING_VIEW_H_ 7 8 #include <algorithm> 9 #include <concepts> 10 #include <cstddef> 11 #include <iosfwd> 12 #include <string> 13 #include <string_view> 14 15 #include "base/check.h" 16 #include "base/compiler_specific.h" 17 #include "base/containers/checked_iterators.h" 18 #include "base/memory/raw_ptr_exclusion.h" 19 #include "base/numerics/safe_conversions.h" 20 #include "build/build_config.h" 21 22 namespace base { 23 24 // A CString is a NUL-terminated character array, which is the C programming 25 // language representation of a string. This class (and its aliases below) 26 // provides a non-owning and bounds-safe view of a CString, and can replace all 27 // use of native pointers (such as `const char*`) for this purpose in C++ code. 28 // 29 // The basic_cstring_view class is followed by aliases for the various char 30 // types: 31 // * cstring_view provides a view of a `const char*`. 32 // * u16cstring_view provides a view of a `const char16_t*`. 33 // * u32cstring_view provides a view of a `const char32_t*`. 34 // * wcstring_view provides a view of a `const wchar_t*`. 35 template <class Char> 36 class basic_cstring_view final { 37 static_assert(!std::is_const_v<Char>); 38 static_assert(!std::is_reference_v<Char>); 39 40 public: 41 using value_type = Char; 42 using pointer = Char*; 43 using const_pointer = const Char*; 44 using reference = Char&; 45 using const_reference = const Char&; 46 using iterator = CheckedContiguousIterator<const Char>; 47 using const_iterator = CheckedContiguousIterator<const Char>; 48 using reverse_iterator = std::reverse_iterator<iterator>; 49 using const_reverse_iterator = std::reverse_iterator<iterator>; 50 using size_type = size_t; 51 using difference_type = ptrdiff_t; 52 53 // The `npos` constant represents a non-existent position in the cstring view. 54 constexpr static auto npos = static_cast<size_t>(-1); 55 56 // Constructs an empty cstring view, which points to an empty string with a 57 // terminating NUL. basic_cstring_view()58 constexpr basic_cstring_view() noexcept : ptr_(kEmpty), len_(0u) {} 59 60 // cstring views are trivially copyable, moveable, and destructible. 61 62 // Constructs a cstring view that points at the contents of a string literal. 63 // 64 // Example: 65 // ``` 66 // const char kLiteral[] = "hello world"; 67 // auto s = base::cstring_view(kLiteral); 68 // CHECK(s == "hello world"); 69 // auto s2 = base::cstring_view("this works too"); 70 // CHECK(s == "this works too"); 71 // ``` 72 template <int&..., size_t M> 73 // NOLINTNEXTLINE(google-explicit-constructor) basic_cstring_view(const Char (& lit LIFETIME_BOUND)[M])74 constexpr basic_cstring_view(const Char (&lit LIFETIME_BOUND)[M]) noexcept 75 ENABLE_IF_ATTR(lit[M - 1u] == Char{0}, "requires string literal as input") 76 : ptr_(lit), len_(M - 1u) { 77 // For non-clang compilers. On clang, the function is not even callable 78 // without this being known to pass at compile time. 79 // 80 // SAFETY: lit is an array of size M, so M-1 is in bounds. 81 DCHECK_EQ(UNSAFE_BUFFERS(lit[M - 1u]), Char{0}); 82 } 83 84 // Constructs a cstring view from a std::string (or other std::basic_string 85 // type). The string parameter must outlive the cstring view, including that 86 // it must not be moved-from or destroyed. 87 // 88 // This conversion is implicit, which matches the conversion from std::string 89 // to std::string_view (through string's `operator string_view()`). 90 // 91 // # Interaction with SSO 92 // std::string stores its contents inline when they fit (which is an 93 // implementation defined length), instead of in a heap-allocated buffer. This 94 // is referred to as the Small String Optimization. This means that moving or 95 // destring a std::string will invalidate a cstring view and leave it with 96 // dangling pointers. This differs from the behaviour of std::vector and span, 97 // since pointers into a std::vector remain valid after moving the std::vector 98 // and destroying the original. 99 // 100 // # Preventing implicit temporaries 101 // Because std::string can be implicitly constructed, the string constructor 102 // may unintentionally be called with a temporary `std::string` when called 103 // with values that convert to `std::string`. We prevent this templating this 104 // constructor and requiring the incoming type to actually be a `std::string` 105 // (or other `std::basic_string`). This also improves compiler errors, 106 // compared to deleting a string&& overload, when passed an array that does 107 // not match the `ENABLE_IF_ATTR` constructor condition by not sending it to a 108 // deleted overload receiving `std::string`. 109 template <std::same_as<std::basic_string<Char>> String> 110 // NOLINTNEXTLINE(google-explicit-constructor) basic_cstring_view(const String & s LIFETIME_BOUND)111 constexpr basic_cstring_view(const String& s LIFETIME_BOUND) noexcept 112 : ptr_(s.c_str()), len_(s.size()) {} 113 114 // Unsafe construction from a pointer and length. Prefer to construct cstring 115 // view from a string literal, std::string, or another cstring view. 116 // 117 // # Safety 118 // The `ptr` and `len` pair indicate a valid NUL-terminated string: 119 // * The `ptr` must not be null, and must point to a NUL-terminated string. 120 // * The `len` must be valid such that `ptr + len` gives a pointer to the 121 // terminating NUL and is in the same allocation as `ptr`. basic_cstring_view(const Char * ptr LIFETIME_BOUND,size_t len)122 UNSAFE_BUFFER_USAGE explicit constexpr basic_cstring_view(const Char* ptr 123 LIFETIME_BOUND, 124 size_t len) 125 : ptr_(ptr), len_(len) { 126 // This method is marked UNSAFE_BUFFER_USAGE so we are trusting the caller 127 // to do things right, and expecting strong scrutiny at the call site, but 128 // we perform a debug check to help catch mistakes regardless. 129 // 130 // SAFETY: `ptr` points to `len` many chars and then a NUL, according to the 131 // caller of this method. So then `len` index will be in bounds and return 132 // the NUL. 133 DCHECK_EQ(UNSAFE_BUFFERS(ptr[len]), Char{0}); 134 } 135 136 // Returns a pointer to the NUL-terminated string, for passing to C-style APIs 137 // that require `const char*` (or whatever the `Char` type is). 138 // 139 // This is never null. c_str()140 PURE_FUNCTION constexpr const Char* c_str() const noexcept { return ptr_; } 141 142 // Returns a pointer to underlying buffer. To get a string pointer, use 143 // `c_str()`. 144 // 145 // Pair with `size()` to construct a bounded non-NUL-terminated view, such as 146 // by `base::span`. This is never null. data()147 PURE_FUNCTION constexpr const Char* data() const noexcept { return ptr_; } 148 149 // Returns the number of characters in the string, not including the 150 // terminating NUL. size()151 PURE_FUNCTION constexpr size_t size() const noexcept { return len_; } 152 // An alias for `size()`, returning the number of characters in the string. length()153 PURE_FUNCTION constexpr size_t length() const noexcept { return len_; } 154 155 // Returns whether the cstring view is for an empty string. When empty, it is 156 // pointing to a cstring that contains only a NUL character. empty()157 PURE_FUNCTION constexpr bool empty() const noexcept { return len_ == 0u; } 158 159 // Returns the maximum number of characters that can be represented inside the 160 // cstring view for character type `Char`. 161 // 162 // This is the number of `Char` objects that can fit inside an addressable 163 // byte array. Since the number of bytes allowed is fixed, the number returned 164 // is smaller when the `Char` is a larger type. max_size()165 PURE_FUNCTION constexpr size_t max_size() const noexcept { 166 return static_cast<size_t>(-1) / sizeof(Char); 167 } 168 169 // Returns the number of bytes in the string, not including the terminating 170 // NUL. To include the NUL, add `sizeof(Char)` where `Char` is the character 171 // type of the cstring view (accessible as the `value_type` alias). size_bytes()172 PURE_FUNCTION constexpr size_t size_bytes() const noexcept { 173 return len_ * sizeof(Char); 174 } 175 176 // Produces an iterator over the cstring view, excluding the terminating NUL. begin()177 PURE_FUNCTION constexpr const iterator begin() const noexcept { 178 // SAFETY: `ptr_ + len_` for a cstring view always gives a pointer in 179 // the same allocation as `ptr_` based on the precondition of 180 // the type. 181 return UNSAFE_BUFFERS(iterator(ptr_, ptr_ + len_)); 182 } 183 // Produces an iterator over the cstring view, excluding the terminating NUL. end()184 PURE_FUNCTION constexpr const iterator end() const noexcept { 185 // SAFETY: `ptr_ + len_` for a cstring view always gives a pointer in 186 // the same allocation as `ptr_` based on the precondition of 187 // the type. 188 return UNSAFE_BUFFERS(iterator(ptr_, ptr_ + len_, ptr_ + len_)); 189 } 190 // Produces an iterator over the cstring view, excluding the terminating NUL. cbegin()191 PURE_FUNCTION constexpr const const_iterator cbegin() const noexcept { 192 return begin(); 193 } 194 // Produces an iterator over the cstring view, excluding the terminating NUL. cend()195 PURE_FUNCTION constexpr const const_iterator cend() const noexcept { 196 return end(); 197 } 198 199 // Produces a reverse iterator over the cstring view, excluding the 200 // terminating NUL. rbegin()201 PURE_FUNCTION constexpr const reverse_iterator rbegin() const noexcept { 202 return std::reverse_iterator(end()); 203 } 204 // Produces a reverse iterator over the cstring view, excluding the 205 // terminating NUL. rend()206 PURE_FUNCTION constexpr const reverse_iterator rend() const noexcept { 207 return std::reverse_iterator(begin()); 208 } 209 // Produces a reverse iterator over the cstring view, excluding the 210 // terminating NUL. rcbegin()211 PURE_FUNCTION constexpr const const_reverse_iterator rcbegin() 212 const noexcept { 213 return std::reverse_iterator(cend()); 214 } 215 // Produces a reverse iterator over the cstring view, excluding the 216 // terminating NUL. rcend()217 PURE_FUNCTION constexpr const const_reverse_iterator rcend() const noexcept { 218 return std::reverse_iterator(cbegin()); 219 } 220 221 // Returns the character at offset `idx`. 222 // 223 // This can be used to access any character in the ctring, as well as the NUL 224 // terminator. 225 // 226 // # Checks 227 // The function CHECKs that the `idx` is inside the cstring (including at its 228 // NUL terminator) and will terminate otherwise. 229 PURE_FUNCTION constexpr const Char& operator[](size_t idx) const noexcept { 230 CHECK_LE(idx, len_); 231 // SAFETY: `ptr_` points `len_` many elements plus a NUL terminator, and 232 // `idx <= len_`, so `idx` is in range for `ptr_`. 233 return UNSAFE_BUFFERS(ptr_[idx]); 234 } 235 236 // A named function that performs the same as `operator[]`. at(size_t idx)237 PURE_FUNCTION constexpr const Char& at(size_t idx) const noexcept { 238 return (*this)[idx]; 239 } 240 241 // Returns the first character in the cstring view. 242 // 243 // # Checks 244 // The function CHECKs that the string is non-empty, and will terminate 245 // otherwise. front()246 PURE_FUNCTION constexpr const Char& front() const noexcept { 247 CHECK(len_); 248 // Since `len_ > 0`, 0 is a valid offset into the string contents. 249 return UNSAFE_BUFFERS(ptr_[0u]); 250 } 251 252 // Returns the last (non-NUL) character in the cstring view. 253 // 254 // # Checks 255 // The function CHECKs that the string is non-empty, and will terminate 256 // otherwise. back()257 PURE_FUNCTION constexpr const Char& back() const noexcept { 258 CHECK(len_); 259 // Since `len_ > 0`, `len - 1` will not underflow. There are `len_` many 260 // chars in the string before a NUL, so `len_ - 1` is in range of the string 261 // contents. 262 return UNSAFE_BUFFERS(ptr_[len_ - 1u]); 263 } 264 265 // Modifies the cstring view in place, moving the front ahead by `n` 266 // characters. 267 // 268 // # Checks 269 // The function CHECKs that `n <= size()`, and will terminate otherwise. remove_prefix(size_t n)270 constexpr void remove_prefix(size_t n) noexcept { 271 CHECK_LE(n, len_); 272 // SAFETY: Since `n <= len_`, the pointer at offset `n` is inside the string 273 // (or at the terminating NUL) and the `len_ - n` value will not underflow. 274 // Thus the resulting pointer is still a NUL- terminated string of length 275 // `len_ - n`. 276 ptr_ = UNSAFE_BUFFERS(ptr_ + n); 277 len_ = len_ - n; 278 } 279 280 // No `remove_suffix()` method exists as it would remove the terminating NUL 281 // character. Convert to a `std::string_view` (either by construction or with 282 // a `substr(0u)` call) to construct arbitrary substrings that are not 283 // NUL-terminated. 284 void remove_suffix(size_t n) = delete; 285 286 // Modifies the cstring view in place, swapping its contents with another view 287 // of the same type. swap(basic_cstring_view & other)288 constexpr void swap(basic_cstring_view& other) noexcept { 289 std::swap(ptr_, other.ptr_); 290 std::swap(len_, other.len_); 291 } 292 293 // Returns a string view of the subrange starting as `pos` and including 294 // `count` characters. If `count` is not specified, or exceeds the length of 295 // the string after `pos`, the subrange returned will include all characters 296 // up to the terminating NUL. 297 // 298 // # Checks 299 // The function CHECKs that `pos` is in range for the string (or at the 300 // terminating NULL), and will terminate otherwise. 301 PURE_FUNCTION constexpr std::basic_string_view<Char> substr( 302 size_t pos, 303 size_t count = npos) const noexcept { 304 // Ensure `ptr_ + pos` is valid. and `len_ - pos` does not underflow. 305 CHECK_LE(pos, len_); 306 // SAFETY: We require that: 307 // * `ptr_ + pos` is a pointer in the string. 308 // * `pos + count <= len_` so that resulting substring's end is in range. 309 // 310 // The first follows directly from the CHECK above that `pos <= len_`. The 311 // second follows from clamping `count` to at most `len_ - pos`. 312 return UNSAFE_BUFFERS( 313 std::basic_string_view<Char>(ptr_ + pos, std::min(count, len_ - pos))); 314 } 315 316 // Returns whether the cstring view starts with the given `prefix`. Will 317 // always return false if `prefix` is larger than the current cstring view. starts_with(std::basic_string_view<Char> prefix)318 constexpr bool starts_with( 319 std::basic_string_view<Char> prefix) const noexcept { 320 return std::basic_string_view<Char>(*this).starts_with(prefix); 321 } 322 323 // Returns whether the cstring view starts with the given `character`. starts_with(Char character)324 constexpr bool starts_with(Char character) const noexcept { 325 return std::basic_string_view<Char>(*this).starts_with(character); 326 } 327 328 // Returns whether the cstring view ends with the given `suffix`. Will 329 // always return false if `suffix` is larger than the current cstring view. ends_with(std::basic_string_view<Char> suffix)330 constexpr bool ends_with(std::basic_string_view<Char> suffix) const noexcept { 331 return std::basic_string_view<Char>(*this).ends_with(suffix); 332 } 333 334 // Returns whether the cstring view starts with the given `character`. ends_with(Char character)335 constexpr bool ends_with(Char character) const noexcept { 336 return std::basic_string_view<Char>(*this).ends_with(character); 337 } 338 339 // Returns the first position in the cstring view at which `search` is found, 340 // starting from the offset `pos`. If `pos` is not specified, the entire 341 // cstring view is searched. Returns `npos` if `search` is not found or if 342 // `pos` is out of range. 343 constexpr size_t find(std::basic_string_view<Char> search, 344 size_t pos = 0u) const noexcept { 345 return std::basic_string_view<Char>(*this).find(search, pos); 346 } 347 constexpr size_t find(Char search, size_t pos = 0u) const noexcept { 348 return std::basic_string_view<Char>(*this).find(search, pos); 349 } 350 351 // Returns the last position in the cstring view at which `search` is found, 352 // starting from the offset `pos`. If `pos` is not specified or is out of 353 // range, the entire cstring view is searched. Returns `npos` if `search` is 354 // not found. 355 constexpr size_t rfind(std::basic_string_view<Char> search, 356 size_t pos = npos) const noexcept { 357 return std::basic_string_view<Char>(*this).rfind(search, pos); 358 } 359 constexpr size_t rfind(Char search, size_t pos = npos) const noexcept { 360 return std::basic_string_view<Char>(*this).rfind(search, pos); 361 } 362 363 // Returns the first position in the cstring view at any character in the 364 // `search` is found, starting from the offset `pos`. If `pos` is not 365 // specified, the entire cstring view is searched. Returns `npos` if `search` 366 // is not found or if `pos` is out of range. 367 constexpr size_t find_first_of(std::basic_string_view<Char> search, 368 size_t pos = 0u) const noexcept { 369 return std::basic_string_view<Char>(*this).find_first_of(search, pos); 370 } 371 constexpr size_t find_first_of(Char search, size_t pos = 0u) const noexcept { 372 return std::basic_string_view<Char>(*this).find_first_of(search, pos); 373 } 374 375 // Returns the last position in the cstring view at any character in the 376 // `search` is found, starting from the offset `pos`. If `pos` is not 377 // specified or is out of range, the entire cstring view is searched. Returns 378 // `npos` if `search` is not found. 379 constexpr size_t find_last_of(std::basic_string_view<Char> search, 380 size_t pos = npos) const noexcept { 381 return std::basic_string_view<Char>(*this).find_last_of(search, pos); 382 } 383 constexpr size_t find_last_of(Char search, size_t pos = npos) const noexcept { 384 return std::basic_string_view<Char>(*this).find_last_of(search, pos); 385 } 386 387 // Returns the first position in the cstring view that is not equal to any 388 // character in the `search`, starting from the offset `pos`. If `pos` is not 389 // specified, the entire cstring view is searched. Returns `npos` if every 390 // character is part of `search` or if `pos` is out of range. 391 constexpr size_t find_first_not_of(std::basic_string_view<Char> search, 392 size_t pos = 0u) const noexcept { 393 return std::basic_string_view<Char>(*this).find_first_not_of(search, pos); 394 } 395 constexpr size_t find_first_not_of(Char search, 396 size_t pos = 0u) const noexcept { 397 return std::basic_string_view<Char>(*this).find_first_not_of(search, pos); 398 } 399 400 // Returns the last position in the cstring view that is not equal to any 401 // character in the `search`, starting from the offset `pos`. If `pos` is not 402 // specified or is out of range, the entire cstring view is searched. Returns 403 // `npos` if every character is part of `search`. 404 constexpr size_t find_last_not_of(std::basic_string_view<Char> search, 405 size_t pos = npos) const noexcept { 406 return std::basic_string_view<Char>(*this).find_last_not_of(search, pos); 407 } 408 constexpr size_t find_last_not_of(Char search, 409 size_t pos = npos) const noexcept { 410 return std::basic_string_view<Char>(*this).find_last_not_of(search, pos); 411 } 412 413 // Compare two cstring views for equality, comparing the string contents. 414 friend constexpr bool operator==(basic_cstring_view l, basic_cstring_view r) { 415 return std::ranges::equal(l, r); 416 } 417 418 // Return an ordering between two cstring views, comparing the string 419 // contents. 420 // 421 // cstring views are weakly ordered, since string views pointing into 422 // different strings can compare as equal. 423 friend constexpr std::weak_ordering operator<=>(basic_cstring_view l, 424 basic_cstring_view r) { 425 return std::lexicographical_compare_three_way(l.begin(), l.end(), r.begin(), 426 r.end()); 427 } 428 429 // Implicitly converts from cstring_view to a non-NUL-terminated 430 // std::string_view. The std::string_view type implicitly constructs from 431 // `const char*` and cstring view is meant to replace the latter, so this acts 432 // like an implicit constructor on `std::string_view` for cstring views. 433 // 434 // This operator also avoids a requirement on having overloads for both 435 // std::string_view and cstring_view. Such overloads are ambiguous because 436 // both can construct from a character array. 437 // 438 // NOLINTNEXTLINE(google-explicit-constructor) 439 constexpr operator std::basic_string_view<Char>() const noexcept { 440 // SAFETY: The cstring view provides that `ptr_ + len_` to be valid. 441 return UNSAFE_BUFFERS(std::basic_string_view<Char>(ptr_, len_)); 442 } 443 444 // Converts from cstring_view to std::string. This allocates a new string 445 // backing and copies into it. 446 // 447 // The std::string type implicitly constructs from `const char*` however it 448 // does not implicitly construct from std::string_view. This type sits between 449 // these two, and opts towards making heap allocations explicit by requiring 450 // an explicit conversion. 451 constexpr explicit operator std::basic_string<Char>() const noexcept { 452 // SAFETY: The cstring view provides that `ptr_ + len_` to be valid. 453 return UNSAFE_BUFFERS(std::basic_string<Char>(ptr_, len_)); 454 } 455 456 // Concatenate a std::string with a cstring_view to produce another 457 // std::string. 458 // 459 // These act like overloads on `std::string` that work for concatenating 460 // `std::string` and `const char*`. 461 // 462 // The rvalue overloads allow `std::string` to reuse existing capacity, by 463 // calling through to the rvalue overloads on `std::string`. 464 template <class Traits, class Alloc> 465 friend constexpr std::basic_string<Char, Traits, Alloc> operator+( 466 basic_cstring_view lhs, 467 const std::basic_string<Char, Traits, Alloc>& rhs) { 468 return lhs.c_str() + rhs; 469 } 470 template <class Traits, class Alloc> 471 friend constexpr std::basic_string<Char, Traits, Alloc> operator+( 472 basic_cstring_view lhs, 473 std::basic_string<Char, Traits, Alloc>&& rhs) { 474 return lhs.c_str() + std::move(rhs); 475 } 476 template <class Traits, class Alloc> 477 friend constexpr std::basic_string<Char, Traits, Alloc> operator+( 478 const std::basic_string<Char, Traits, Alloc>& lhs, 479 basic_cstring_view rhs) { 480 return lhs + rhs.c_str(); 481 } 482 template <class Traits, class Alloc> 483 friend constexpr std::basic_string<Char, Traits, Alloc> operator+( 484 std::basic_string<Char, Traits, Alloc>&& lhs, 485 basic_cstring_view rhs) { 486 return std::move(lhs) + rhs.c_str(); 487 } 488 489 private: 490 // An empty string literal for the `Char` type. 491 static constexpr Char kEmpty[] = {Char{0}}; 492 493 // An always-valid pointer (never null) to a NUL-terminated string. 494 // 495 // RAW_PTR_EXCLUSION: cstring_view is typically used on the stack as a local 496 // variable/function parameter, so no raw_ptr is used here. 497 RAW_PTR_EXCLUSION const Char* ptr_; 498 // The number of characters between `ptr_` and the NUL terminator. 499 // 500 // SAFETY: `ptr_ + len_` is always valid since `len_` must not exceed the 501 // number of characters in the allocation, or it would no longer indicate the 502 // position of the NUL terminator in the string allocation. 503 size_t len_; 504 }; 505 506 // cstring_view provides a view of a NUL-terminated string. It is a replacement 507 // for all use of `const char*`, in order to provide bounds checks and prevent 508 // unsafe pointer usage (otherwise prevented by `-Wunsafe-buffer-usage`). 509 // 510 // See basic_cstring_view for more. 511 using cstring_view = basic_cstring_view<char>; 512 513 // u16cstring_view provides a view of a NUL-terminated string. It is a 514 // replacement for all use of `const char16_t*`, in order to provide bounds 515 // checks and prevent unsafe pointer usage (otherwise prevented by 516 // `-Wunsafe-buffer-usage`). 517 // 518 // See basic_cstring_view for more. 519 using u16cstring_view = basic_cstring_view<char16_t>; 520 521 // u32cstring_view provides a view of a NUL-terminated string. It is a 522 // replacement for all use of `const char32_t*`, in order to provide bounds 523 // checks and prevent unsafe pointer usage (otherwise prevented by 524 // `-Wunsafe-buffer-usage`). 525 // 526 // See basic_cstring_view for more. 527 using u32cstring_view = basic_cstring_view<char32_t>; 528 529 #if BUILDFLAG(IS_WIN) 530 // wcstring_view provides a view of a NUL-terminated string. It is a 531 // replacement for all use of `const wchar_t*`, in order to provide bounds 532 // checks and prevent unsafe pointer usage (otherwise prevented by 533 // `-Wunsafe-buffer-usage`). 534 // 535 // See basic_cstring_view for more. 536 using wcstring_view = basic_cstring_view<wchar_t>; 537 #endif 538 539 // Writes the contents of the cstring view to the stream. 540 template <class Char, class Traits> 541 std::basic_ostream<Char, Traits>& operator<<( 542 std::basic_ostream<Char, Traits>& os, 543 basic_cstring_view<Char> view) { 544 return os << std::basic_string_view<Char>(view); 545 } 546 547 } // namespace base 548 549 template <class Char> 550 struct std::hash<base::basic_cstring_view<Char>> { 551 size_t operator()(const base::basic_cstring_view<Char>& t) const noexcept { 552 return std::hash<std::basic_string_view<Char>>()(t); 553 } 554 }; 555 556 template <class Char> 557 inline constexpr bool 558 std::ranges::enable_borrowed_range<base::basic_cstring_view<Char>> = true; 559 560 template <class Char> 561 inline constexpr bool std::ranges::enable_view<base::basic_cstring_view<Char>> = 562 true; 563 564 #endif // BASE_STRINGS_CSTRING_VIEW_H_ 565