1// Copyright 2022 The Pigweed Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); you may not 4// use this file except in compliance with the License. You may obtain a copy of 5// the License at 6// 7// https://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12// License for the specific language governing permissions and limitations under 13// the License. 14 15// This file contains the definitions of most pw::InlineBasicString functions. 16// The file is included inline in the fixed-capacity pw::InlineBasicString<T, 17// kCapacity> and generic-capacity pw::InlineBasicString<T> specialization 18// classes. 19// 20// These functions cannot simply be defined in the pw::InlineBasicString<T> base 21// class because: 22// 23// 1. Many functions return a *this reference. The functions should to return 24// a reference to the exact type they are called on rather than the 25// generic-capacity base class. 26// 2. Operations on the generic base class cannot be constexpr unless the 27// class is an InlineBasicString<T, 0>. The functions must be defined in 28// the fixed-capacity dervied classes to support constexpr operations. 29// 30// These functions have no logic and simply defer to shared, length-agnostic 31// implementations so they do not increase code size. 32 33PW_MODIFY_DIAGNOSTICS_PUSH(); 34PW_MODIFY_DIAGNOSTIC_GCC(ignored, "-Wtype-limits"); 35 36// Assignment functions 37 38constexpr InlineBasicString& assign(size_t count, T ch) { 39 return static_cast<InlineBasicString&>(Fill(data(), ch, count)); 40} 41 42// Checks capacity rather than current size. 43template <size_t kOtherCapacity> 44constexpr InlineBasicString& assign( 45 const InlineBasicString<T, kOtherCapacity>& other) { 46 static_assert( 47 kOtherCapacity == string_impl::kGeneric || kOtherCapacity <= kCapacity, 48 _PW_STRING_CAPACITY_TOO_SMALL_FOR_STRING); 49 return static_cast<InlineBasicString&>( 50 Copy(data(), other.data(), other.size())); 51} 52 53constexpr InlineBasicString& assign(const InlineBasicString& other, 54 size_t index, 55 size_t count = npos) { 56 return static_cast<InlineBasicString&>( 57 CopySubstr(data(), other.data(), other.size(), index, count)); 58} 59 60constexpr InlineBasicString& assign(const T* string, size_t count) { 61 return static_cast<InlineBasicString&>(Copy(data(), string, count)); 62} 63 64template <typename U, typename = string_impl::EnableIfNonArrayCharPointer<T, U>> 65constexpr InlineBasicString& assign(U c_string) { 66 return assign(c_string, 67 string_impl::BoundedStringLength(c_string, max_size())); 68} 69 70// Assignment from character array or string literal. For known-size strings, 71// the capacity is checked against the string/array size at compile time. 72template <size_t kCharArraySize> 73constexpr InlineBasicString& assign(const T (&array)[kCharArraySize]) { 74 static_assert( 75 string_impl::NullTerminatedArrayFitsInString(kCharArraySize, kCapacity), 76 _PW_STRING_CAPACITY_TOO_SMALL_FOR_ARRAY); 77 return static_cast<InlineBasicString&>( 78 Copy(data(), array, string_impl::ArrayStringLength(array, max_size()))); 79} 80 81template <typename InputIterator, 82 typename = string_impl::EnableIfInputIterator<InputIterator>> 83constexpr InlineBasicString& assign(InputIterator start, InputIterator finish) { 84 return static_cast<InlineBasicString&>(CopyIterator(data(), start, finish)); 85} 86 87constexpr InlineBasicString& assign(std::initializer_list<T> list) { 88 return assign(list.begin(), list.size()); 89} 90 91template <typename StringView, 92 typename = string_impl::EnableIfStringViewLike<T, StringView>> 93constexpr InlineBasicString& assign(const StringView& string) { 94 const string_impl::View<T> view = string; 95 PW_ASSERT(view.size() < npos); 96 return assign(view.data(), view.size()); 97} 98 99template <typename StringView, 100 typename = string_impl::EnableIfStringViewLike<T, StringView>> 101constexpr InlineBasicString& assign(const StringView& string, 102 size_t index, 103 size_t count = npos) { 104 const string_impl::View<T> view = string; 105 PW_ASSERT(view.size() < npos); 106 return static_cast<InlineBasicString&>( 107 CopySubstr(data(), view.data(), view.size(), index, count)); 108} 109 110constexpr InlineBasicString& assign(std::nullptr_t) = delete; 111 112// Element access 113 114constexpr reference at(size_t index) { 115 PW_ASSERT(index < length()); 116 return data()[index]; 117} 118constexpr const_reference at(size_t index) const { 119 PW_ASSERT(index < length()); 120 return data()[index]; 121} 122 123constexpr reference operator[](size_t index) { return data()[index]; } 124constexpr const_reference operator[](size_t index) const { 125 return data()[index]; 126} 127 128constexpr reference front() { return data()[0]; } 129constexpr const_reference front() const { return data()[0]; } 130 131constexpr reference back() { return data()[size() - 1]; } 132constexpr const_reference back() const { return data()[size() - 1]; } 133 134constexpr const_pointer c_str() const noexcept { return data(); } 135 136constexpr operator string_impl::View<T>() const noexcept { 137 return string_impl::View<T>(data(), size()); 138} 139 140// Iterators 141 142constexpr iterator begin() noexcept { return &data()[0]; } 143constexpr const_iterator begin() const noexcept { return &data()[0]; } 144constexpr const_iterator cbegin() const noexcept { return &data()[0]; } 145 146constexpr iterator end() noexcept { return &data()[size()]; } 147constexpr const_iterator end() const noexcept { return &data()[size()]; } 148constexpr const_iterator cend() const noexcept { return &data()[size()]; } 149 150constexpr reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } 151constexpr const_reverse_iterator rbegin() const noexcept { 152 return const_reverse_iterator(end()); 153} 154constexpr const_reverse_iterator crbegin() const noexcept { 155 return const_reverse_iterator(cend()); 156} 157 158constexpr reverse_iterator rend() noexcept { return reverse_iterator(begin()); } 159constexpr const_reverse_iterator rend() const noexcept { 160 return const_reverse_iterator(begin()); 161} 162constexpr const_reverse_iterator crend() const noexcept { 163 return const_reverse_iterator(cbegin()); 164} 165 166// Capacity 167 168[[nodiscard]] constexpr bool empty() const noexcept { return size() == 0u; } 169 170// The number of characters in the string. 171constexpr size_t length() const noexcept { return size(); } 172 173constexpr size_t capacity() const noexcept { return max_size(); } 174 175// Operations 176 177constexpr void clear() { SetSizeAndTerminate(data(), 0); } 178 179constexpr InlineBasicString& insert(size_t index, size_t count, T ch) { 180 MoveExtend(data(), index, index + count); 181 return static_cast<InlineBasicString&>(FillExtend(data(), index, ch, count)); 182} 183 184constexpr InlineBasicString& insert(size_t index, 185 const T* string, 186 size_t count) { 187 MoveExtend(data(), index, index + count); 188 return static_cast<InlineBasicString&>( 189 CopyExtend(data(), index, string, count)); 190} 191 192template <size_t kCharArraySize> 193constexpr InlineBasicString& insert(size_t index, 194 const T (&array)[kCharArraySize]) { 195 return insert( 196 index, array, string_impl::ArrayStringLength(array, max_size())); 197} 198 199template <typename U, typename = string_impl::EnableIfNonArrayCharPointer<T, U>> 200constexpr InlineBasicString& insert(size_t index, U c_string) { 201 return insert( 202 index, c_string, string_impl::BoundedStringLength(c_string, max_size())); 203} 204 205template <size_t kOtherCapacity> 206constexpr InlineBasicString& insert( 207 size_t index, const InlineBasicString<T, kOtherCapacity>& string) { 208 return insert(index, string.data(), string.size()); 209} 210 211template <size_t kOtherCapacity> 212constexpr InlineBasicString& insert( 213 size_t index, 214 const InlineBasicString<T, kOtherCapacity>& other, 215 size_t other_index, 216 size_t count = npos) { 217 PW_ASSERT(other_index <= other.size()); 218 if (count == npos || count > other.size() - other_index) { 219 count = other.size() - other_index; 220 } 221 PW_ASSERT(index <= index + count); 222 MoveExtend(data(), index, index + count); 223 return static_cast<InlineBasicString&>(CopyExtendSubstr( 224 data(), index, other.data(), other.size(), other_index, count)); 225} 226 227// Use a templated type rather than `const_iterator` to convince the compiler 228// to prefer `0` as an index rather than as a `nullptr`. 229template <typename U> 230constexpr iterator insert(const U* pos, size_t count, T ch) { 231 PW_ASSERT(cbegin() <= pos && pos <= cend()); 232 size_t index = static_cast<size_t>(pos - cbegin()); 233 PW_ASSERT(index <= index + count); 234 MoveExtend(data(), index, index + count); 235 FillExtend(data(), index, ch, count); 236 return begin() + index; 237} 238 239template <typename U> 240constexpr iterator insert(const U* pos, T ch) { 241 return insert(pos, 1, ch); 242} 243 244template <typename U, class InputIt> 245constexpr iterator insert(const U* pos, InputIt first, InputIt last) { 246 PW_ASSERT(cbegin() <= pos && pos <= cend()); 247 size_t index = static_cast<size_t>(pos - cbegin()); 248 size_t count = 0; 249 for (auto tmp = first; tmp != last; ++tmp) { 250 PW_ASSERT(count < max_size()); 251 ++count; 252 } 253 PW_ASSERT(index <= index + count); 254 MoveExtend(data(), index, index + count); 255 CopyIteratorExtend(data(), index, first, last); 256 return begin() + index; 257} 258 259template <typename U> 260constexpr iterator insert(const U* pos, std::initializer_list<T> list) { 261 return insert(pos, list.begin(), list.end()); 262} 263 264template <typename StringView, 265 typename = string_impl::EnableIfStringViewLike<T, StringView>> 266constexpr InlineBasicString& insert(size_t index, 267 const StringView& string, 268 size_t string_index, 269 size_t count = npos) { 270 const string_impl::View<T> view = string; 271 PW_ASSERT(view.size() < npos); 272 PW_ASSERT(string_index <= view.size()); 273 if (count == npos || count > view.size() - string_index) { 274 count = view.size() - string_index; 275 } 276 PW_ASSERT(index <= index + count); 277 MoveExtend(data(), index, index + count); 278 return static_cast<InlineBasicString&>(CopyExtendSubstr( 279 data(), index, view.data(), view.size(), string_index, count)); 280} 281 282template <typename StringView, 283 typename = string_impl::EnableIfStringViewLike<T, StringView>> 284constexpr InlineBasicString& insert(size_t index, const StringView& string) { 285 return insert(index, string, 0); 286} 287 288constexpr InlineBasicString& erase(size_t index = 0, size_t count = npos) { 289 PW_ASSERT(index <= size()); 290 size_t old_index = 291 index + std::min(count == npos ? size() : count, size() - index); 292 return static_cast<InlineBasicString&>(MoveExtend(data(), old_index, index)); 293} 294 295// Use a templated type rather than `const_iterator` to convince the compiler 296// to prefer `0` as an index rather than as a `nullptr`. 297template <typename U> 298constexpr iterator erase(const U* pos) { 299 PW_ASSERT(cbegin() <= pos && pos <= cend()); 300 size_t index = static_cast<size_t>(pos - cbegin()); 301 size_t old_index = index + 1; 302 if (old_index <= size()) { 303 MoveExtend(data(), old_index, index); 304 } 305 return old_index <= size() ? begin() + index : end(); 306} 307 308template <typename U> 309constexpr iterator erase(const U* first, const U* last) { 310 PW_ASSERT(cbegin() <= first && first <= cend()); 311 size_t old_index = 312 first == cend() ? size() : static_cast<size_t>(first - cbegin()); 313 size_t index = old_index; 314 for (auto tmp = first; tmp != last; ++tmp) { 315 PW_ASSERT(index < size()); 316 ++old_index; 317 } 318 MoveExtend(data(), old_index, index); 319 return old_index <= size() ? begin() + index : end(); 320} 321 322constexpr void push_back(value_type ch) { 323 static_assert(kCapacity != 0, 324 "Cannot add a character to pw::InlineString<0>"); 325 PushBack(data(), ch); 326} 327 328constexpr void pop_back() { 329 static_assert(kCapacity != 0, 330 "Cannot remove a character from pw::InlineString<0>"); 331 PopBack(data()); 332} 333 334constexpr InlineBasicString& append(size_t count, T ch) { 335 return static_cast<InlineBasicString&>(FillExtend(data(), size(), ch, count)); 336} 337 338template <size_t kOtherCapacity> 339constexpr InlineBasicString& append( 340 const InlineBasicString<T, kOtherCapacity>& string) { 341 static_assert( 342 kOtherCapacity == string_impl::kGeneric || kOtherCapacity <= kCapacity, 343 _PW_STRING_CAPACITY_TOO_SMALL_FOR_STRING); 344 return append(string.data(), string.size()); 345} 346 347template <size_t kOtherCapacity> 348constexpr InlineBasicString& append( 349 const InlineBasicString<T, kOtherCapacity>& other, 350 size_t index, 351 size_t count = npos) { 352 return static_cast<InlineBasicString&>(CopyExtendSubstr( 353 data(), size(), other.data(), other.size(), index, count)); 354} 355 356constexpr InlineBasicString& append(const T* string, size_t count) { 357 return static_cast<InlineBasicString&>( 358 CopyExtend(data(), size(), string, count)); 359} 360 361template <size_t kCharArraySize> 362constexpr InlineBasicString& append(const T (&array)[kCharArraySize]) { 363 static_assert( 364 string_impl::NullTerminatedArrayFitsInString(kCharArraySize, kCapacity), 365 _PW_STRING_CAPACITY_TOO_SMALL_FOR_ARRAY); 366 return append(array, string_impl::ArrayStringLength(array, max_size())); 367} 368 369template <typename U, typename = string_impl::EnableIfNonArrayCharPointer<T, U>> 370constexpr InlineBasicString& append(U c_string) { 371 return append(c_string, 372 string_impl::BoundedStringLength(c_string, max_size())); 373} 374 375template <typename InputIterator, 376 typename = string_impl::EnableIfInputIterator<InputIterator>> 377constexpr InlineBasicString& append(InputIterator first, InputIterator last) { 378 return static_cast<InlineBasicString&>( 379 CopyIteratorExtend(data(), size(), first, last)); 380} 381 382constexpr InlineBasicString& append(std::initializer_list<T> list) { 383 return append(list.begin(), list.size()); 384} 385 386template <typename StringView, 387 typename = string_impl::EnableIfStringViewLike<T, StringView>> 388constexpr InlineBasicString& append(const StringView& string) { 389 const string_impl::View<T> view = string; 390 PW_ASSERT(view.size() < npos); 391 return append(view.data(), view.size()); 392} 393 394template <typename StringView, 395 typename = string_impl::EnableIfStringViewLike<T, StringView>> 396constexpr InlineBasicString& append(const StringView& string, 397 size_t index, 398 size_t count = npos) { 399 const string_impl::View<T> view = string; 400 PW_ASSERT(view.size() < npos); 401 return static_cast<InlineBasicString&>( 402 CopyExtendSubstr(data(), size(), view.data(), view.size(), index, count)); 403} 404 405template <size_t kOtherCapacity> 406constexpr int compare( 407 const InlineBasicString<T, kOtherCapacity>& other) const noexcept { 408 return string_impl::Compare(data(), size(), other.data(), other.size()); 409} 410 411constexpr int compare(const T* other) const { 412 return string_impl::Compare( 413 data(), 414 size(), 415 other, 416 string_impl::BoundedStringLength(other, max_size())); 417} 418 419// TODO: b/239996007 - Implement other compare overloads. 420 421// TODO: b/239996007 - Implement other std::string functions: 422// 423// - starts_with 424// - ends_with 425// - replace 426// - substr 427// - copy 428 429constexpr void resize(size_t new_size) { resize(new_size, T()); } 430 431constexpr void resize(size_t new_size, T ch) { 432 return Resize(data(), new_size, ch); 433} 434 435// resize_and_overwrite() only takes the callable object since the underlying 436// buffer has a fixed size. 437template <typename Operation> 438constexpr void resize_and_overwrite(Operation operation) { 439 const auto new_size = 440 static_cast<size_t>(std::move(operation)(data(), max_size())); 441 PW_ASSERT(new_size <= max_size()); 442 SetSizeAndTerminate(data(), new_size); 443} 444 445// TODO: b/239996007 - Implement swap 446 447// Search 448 449// TODO: b/239996007 - Implement std::string search functions: 450// 451// - find 452// - rfind 453// - find_first_of 454// - find_first_not_of 455// - find_last_of 456// - find_last_not_of 457 458PW_MODIFY_DIAGNOSTICS_POP(); 459