Blender V4.3
BLI_string_ref.hh
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#pragma once
6
34#include <cstring>
35#include <string>
36#include <string_view>
37
38#include "BLI_span.hh"
39#include "BLI_utildefines.h"
40
41namespace blender {
42
43class StringRef;
44
50 protected:
51 const char *data_;
53
54 constexpr StringRefBase(const char *data, int64_t size);
55
56 public:
57 /* Similar to string_view::npos, but signed. */
58 static constexpr int64_t not_found = -1;
59
60 constexpr int64_t size() const;
61 constexpr bool is_empty() const;
62 constexpr const char *data() const;
63 constexpr operator Span<char>() const;
64
65 operator std::string() const;
66 constexpr operator std::string_view() const;
67
68 constexpr const char *begin() const;
69 constexpr const char *end() const;
70
71 constexpr IndexRange index_range() const;
72
73 void unsafe_copy(char *dst) const;
74 void copy(char *dst, int64_t dst_size) const;
75 template<size_t N> void copy(char (&dst)[N]) const;
76
77 constexpr bool startswith(StringRef prefix) const;
78 constexpr bool endswith(StringRef suffix) const;
79 constexpr StringRef substr(int64_t start, int64_t size) const;
80
81 constexpr const char &front() const;
82 constexpr const char &back() const;
83
88 constexpr int64_t find(char c, int64_t pos = 0) const;
89 constexpr int64_t find(StringRef str, int64_t pos = 0) const;
90 constexpr int64_t rfind(char c, int64_t pos = INT64_MAX) const;
91 constexpr int64_t rfind(StringRef str, int64_t pos = INT64_MAX) const;
92 constexpr int64_t find_first_of(StringRef chars, int64_t pos = 0) const;
93 constexpr int64_t find_first_of(char c, int64_t pos = 0) const;
94 constexpr int64_t find_last_of(StringRef chars, int64_t pos = INT64_MAX) const;
95 constexpr int64_t find_last_of(char c, int64_t pos = INT64_MAX) const;
96 constexpr int64_t find_first_not_of(StringRef chars, int64_t pos = 0) const;
97 constexpr int64_t find_first_not_of(char c, int64_t pos = 0) const;
98 constexpr int64_t find_last_not_of(StringRef chars, int64_t pos = INT64_MAX) const;
99 constexpr int64_t find_last_not_of(char c, int64_t pos = INT64_MAX) const;
100
101 constexpr StringRef trim() const;
102 constexpr StringRef trim(StringRef characters_to_remove) const;
103 constexpr StringRef trim(char character_to_remove) const;
104};
105
112
113 public:
114 constexpr StringRefNull();
115 constexpr StringRefNull(const char *str, int64_t size);
116 StringRefNull(const char *str);
117 StringRefNull(const std::string &str);
118
119 constexpr char operator[](int64_t index) const;
120 constexpr const char *c_str() const;
121};
122
128class StringRef : public StringRefBase {
129 public:
130 constexpr StringRef();
131 constexpr StringRef(StringRefNull other);
132 constexpr StringRef(const char *str);
133 constexpr StringRef(const char *str, int64_t length);
134 constexpr StringRef(const char *begin, const char *one_after_end);
135 constexpr StringRef(std::string_view view);
136 StringRef(const std::string &str);
137
138 constexpr StringRef drop_prefix(int64_t n) const;
139 constexpr StringRef drop_known_prefix(StringRef prefix) const;
140 constexpr StringRef drop_suffix(int64_t n) const;
141
142 constexpr char operator[](int64_t index) const;
143};
144
145/* -------------------------------------------------------------------- */
149constexpr StringRefBase::StringRefBase(const char *data, const int64_t size)
150 : data_(data), size_(size)
151{
152}
153
158{
159 return size_;
160}
161
162constexpr bool StringRefBase::is_empty() const
163{
164 return size_ == 0;
165}
166
170constexpr const char *StringRefBase::data() const
171{
172 return data_;
173}
174
175constexpr StringRefBase::operator Span<char>() const
176{
177 return Span<char>(data_, size_);
178}
179
184inline StringRefBase::operator std::string() const
185{
186 return std::string(data_, size_t(size_));
187}
188
189constexpr StringRefBase::operator std::string_view() const
190{
191 return std::string_view(data_, size_t(size_));
192}
193
194constexpr const char *StringRefBase::begin() const
195{
196 return data_;
197}
198
199constexpr const char *StringRefBase::end() const
200{
201 return data_ + size_;
202}
203
205{
206 return IndexRange(size_);
207}
208
214inline void StringRefBase::unsafe_copy(char *dst) const
215{
216 if (size_ > 0) {
217 memcpy(dst, data_, size_t(size_));
218 }
219 dst[size_] = '\0';
220}
221
226inline void StringRefBase::copy(char *dst, const int64_t dst_size) const
227{
228 if (size_ < dst_size) {
229 this->unsafe_copy(dst);
230 }
231 else {
232 BLI_assert(false);
233 dst[0] = '\0';
234 }
235}
236
241template<size_t N> inline void StringRefBase::copy(char (&dst)[N]) const
242{
243 this->copy(dst, N);
244}
245
249constexpr bool StringRefBase::startswith(StringRef prefix) const
250{
251 if (size_ < prefix.size_) {
252 return false;
253 }
254 for (int64_t i = 0; i < prefix.size_; i++) {
255 if (data_[i] != prefix.data_[i]) {
256 return false;
257 }
258 }
259 return true;
260}
261
265constexpr bool StringRefBase::endswith(StringRef suffix) const
266{
267 if (size_ < suffix.size_) {
268 return false;
269 }
270 const int64_t offset = size_ - suffix.size_;
271 for (int64_t i = 0; i < suffix.size_; i++) {
272 if (data_[offset + i] != suffix.data_[i]) {
273 return false;
274 }
275 }
276 return true;
277}
278
284 const int64_t max_size = INT64_MAX) const
285{
286 BLI_assert(max_size >= 0);
287 BLI_assert(start >= 0);
288 const int64_t substr_size = std::min(max_size, size_ - start);
289 return StringRef(data_ + start, substr_size);
290}
291
295constexpr const char &StringRefBase::front() const
296{
297 BLI_assert(size_ >= 1);
298 return data_[0];
299}
300
304constexpr const char &StringRefBase::back() const
305{
306 BLI_assert(size_ >= 1);
307 return data_[size_ - 1];
308}
309
310constexpr int64_t index_or_npos_to_int64(size_t index)
311{
312 /* The compiler will probably optimize this check away. */
313 if (index == std::string_view::npos) {
315 }
316 return int64_t(index);
317}
318
319constexpr int64_t StringRefBase::find(char c, int64_t pos) const
320{
321 BLI_assert(pos >= 0);
322 return index_or_npos_to_int64(std::string_view(*this).find(c, size_t(pos)));
323}
324
326{
327 BLI_assert(pos >= 0);
328 return index_or_npos_to_int64(std::string_view(*this).find(str, size_t(pos)));
329}
330
331constexpr int64_t StringRefBase::rfind(char c, int64_t pos) const
332{
333 BLI_assert(pos >= 0);
334 return index_or_npos_to_int64(std::string_view(*this).rfind(c, size_t(pos)));
335}
336
338{
339 BLI_assert(pos >= 0);
340 return index_or_npos_to_int64(std::string_view(*this).rfind(str, size_t(pos)));
341}
342
344{
345 BLI_assert(pos >= 0);
346 return index_or_npos_to_int64(std::string_view(*this).find_first_of(chars, size_t(pos)));
347}
348
350{
351 BLI_assert(pos >= 0);
352 return index_or_npos_to_int64(std::string_view(*this).find_first_of(c, size_t(pos)));
353}
354
356{
357 BLI_assert(pos >= 0);
358 return index_or_npos_to_int64(std::string_view(*this).find_last_of(chars, size_t(pos)));
359}
360
362{
363 BLI_assert(pos >= 0);
364 return index_or_npos_to_int64(std::string_view(*this).find_last_of(c, size_t(pos)));
365}
366
368{
369 BLI_assert(pos >= 0);
370 return index_or_npos_to_int64(std::string_view(*this).find_first_not_of(chars, size_t(pos)));
371}
372
374{
375 BLI_assert(pos >= 0);
376 return index_or_npos_to_int64(std::string_view(*this).find_first_not_of(c, size_t(pos)));
377}
378
380{
381 BLI_assert(pos >= 0);
382 return index_or_npos_to_int64(std::string_view(*this).find_last_not_of(chars, size_t(pos)));
383}
384
386{
387 BLI_assert(pos >= 0);
388 return index_or_npos_to_int64(std::string_view(*this).find_last_not_of(c, size_t(pos)));
389}
390
392{
393 return this->trim(" \t\r\n");
394}
395
399constexpr StringRef StringRefBase::trim(const char character_to_remove) const
400{
401 return this->trim(StringRef(&character_to_remove, 1));
402}
403
408constexpr StringRef StringRefBase::trim(StringRef characters_to_remove) const
409{
410 const int64_t find_front = this->find_first_not_of(characters_to_remove);
411 if (find_front == not_found) {
412 return StringRef();
413 }
414 const int64_t find_end = this->find_last_not_of(characters_to_remove);
415 /* `find_end` cannot be `not_found`, because that means the string is only
416 * `characters_to_remove`, in which case `find_front` would already have
417 * been `not_found`. */
418 BLI_assert_msg(find_end != not_found,
419 "forward search found characters-to-not-remove, but backward search did not");
420 const int64_t substr_len = find_end - find_front + 1;
421 return this->substr(find_front, substr_len);
422}
423
426/* -------------------------------------------------------------------- */
431
436constexpr StringRefNull::StringRefNull(const char *str, const int64_t size)
437 : StringRefBase(str, size)
438{
439 BLI_assert(int64_t(strlen(str)) == size);
440}
441
447{
448 BLI_assert(str != nullptr);
449 BLI_assert(data_[size_] == '\0');
450}
451
456inline StringRefNull::StringRefNull(const std::string &str)
457 : StringRefNull(str.c_str(), int64_t(str.size()))
458{
459}
460
464constexpr char StringRefNull::operator[](const int64_t index) const
465{
466 BLI_assert(index >= 0);
467 /* Use '<=' instead of just '<', so that the null character can be accessed as well. */
468 BLI_assert(index <= size_);
469 return data_[index];
470}
471
477constexpr const char *StringRefNull::c_str() const
478{
479 return data_;
480}
481
484/* -------------------------------------------------------------------- */
488constexpr StringRef::StringRef() : StringRefBase(nullptr, 0) {}
489
493constexpr StringRef::StringRef(StringRefNull other) : StringRefBase(other.data(), other.size()) {}
494
498constexpr StringRef::StringRef(const char *str)
499 : StringRefBase(str, str ? int64_t(std::char_traits<char>::length(str)) : 0)
500{
501}
502
503constexpr StringRef::StringRef(const char *str, const int64_t length) : StringRefBase(str, length)
504{
505}
506
512{
513 BLI_assert(n >= 0);
514 const int64_t clamped_n = std::min(n, size_);
515 const int64_t new_size = size_ - clamped_n;
516 return StringRef(data_ + clamped_n, new_size);
517}
518
524{
525 BLI_assert(this->startswith(prefix));
526 return this->drop_prefix(prefix.size());
527}
528
534{
535 BLI_assert(n >= 0);
536 const int64_t new_size = std::max<int64_t>(0, size_ - n);
537 return StringRef(data_, new_size);
538}
539
543constexpr char StringRef::operator[](int64_t index) const
544{
545 BLI_assert(index >= 0);
546 BLI_assert(index < size_);
547 return data_[index];
548}
549
554constexpr StringRef::StringRef(const char *begin, const char *one_after_end)
555 : StringRefBase(begin, int64_t(one_after_end - begin))
556{
557 BLI_assert(begin <= one_after_end);
558}
559
564inline StringRef::StringRef(const std::string &str)
565 : StringRefBase(str.data(), int64_t(str.size()))
566{
567}
568
569constexpr StringRef::StringRef(std::string_view view)
570 : StringRefBase(view.data(), int64_t(view.size()))
571{
572}
573
576/* -------------------------------------------------------------------- */
580std::ostream &operator<<(std::ostream &stream, StringRef ref);
581std::ostream &operator<<(std::ostream &stream, StringRefNull ref);
582
587inline std::string operator+(StringRef a, StringRef b)
588{
589 return std::string(a) + std::string(b);
590}
591
592/* This does not compare StringRef and std::string_view, because of ambiguous overloads. This is
593 * not a problem when std::string_view is only used at api boundaries. To compare a StringRef and a
594 * std::string_view, one should convert the std::string_view to StringRef (which is very cheap).
595 * Ideally, we only use StringRef in our code to avoid this problem altogether.
596 *
597 * NOTE: these functions are also suitable for StringRefNull comparisons, as these are
598 * implicitly converted to StringRef by the compiler. */
599constexpr bool operator==(StringRef a, StringRef b)
600{
601 return std::string_view(a) == std::string_view(b);
602}
603
604constexpr bool operator!=(StringRef a, StringRef b)
605{
606 return std::string_view(a) != std::string_view(b);
607}
608
609constexpr bool operator<(StringRef a, StringRef b)
610{
611 return std::string_view(a) < std::string_view(b);
612}
613
614constexpr bool operator>(StringRef a, StringRef b)
615{
616 return std::string_view(a) > std::string_view(b);
617}
618
619constexpr bool operator<=(StringRef a, StringRef b)
620{
621 return std::string_view(a) <= std::string_view(b);
622}
623
624constexpr bool operator>=(StringRef a, StringRef b)
625{
626 return std::string_view(a) >= std::string_view(b);
627}
628
631/* -------------------------------------------------------------------- */
638inline std::string_view format_as(StringRef str)
639{
640 return str;
641}
642
645} // namespace blender
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
constexpr const char & front() const
void unsafe_copy(char *dst) const
static constexpr int64_t not_found
constexpr int64_t rfind(char c, int64_t pos=INT64_MAX) const
constexpr int64_t find_last_of(StringRef chars, int64_t pos=INT64_MAX) const
void copy(char *dst, int64_t dst_size) const
constexpr int64_t find(char c, int64_t pos=0) const
constexpr const char * begin() const
constexpr const char * end() const
constexpr int64_t find_last_not_of(StringRef chars, int64_t pos=INT64_MAX) const
constexpr int64_t find_first_not_of(StringRef chars, int64_t pos=0) const
constexpr bool is_empty() const
constexpr StringRef substr(int64_t start, int64_t size) const
constexpr StringRefBase(const char *data, int64_t size)
constexpr bool startswith(StringRef prefix) const
constexpr bool endswith(StringRef suffix) const
constexpr int64_t find_first_of(StringRef chars, int64_t pos=0) const
constexpr IndexRange index_range() const
constexpr int64_t size() const
constexpr StringRef trim() const
constexpr const char * data() const
constexpr const char & back() const
constexpr const char * c_str() const
constexpr char operator[](int64_t index) const
constexpr StringRef drop_prefix(int64_t n) const
constexpr StringRef drop_suffix(int64_t n) const
constexpr char operator[](int64_t index) const
constexpr StringRef drop_known_prefix(StringRef prefix) const
local_group_size(16, 16) .push_constant(Type b
#define str(s)
#define N
constexpr bool operator!=(StringRef a, StringRef b)
constexpr int64_t index_or_npos_to_int64(size_t index)
constexpr bool operator==(StringRef a, StringRef b)
std::string_view format_as(StringRef str)
std::ostream & operator<<(std::ostream &stream, const eAlpha &space)
Definition BLI_color.cc:11
constexpr bool operator>=(StringRef a, StringRef b)
constexpr bool operator<(StringRef a, StringRef b)
constexpr bool operator<=(StringRef a, StringRef b)
constexpr bool operator>(StringRef a, StringRef b)
std::string operator+(StringRef a, StringRef b)
__int64 int64_t
Definition stdint.h:89
#define INT64_MAX
Definition stdint.h:139