Arene Base
Fundamental Utilities For Safety Critical C++
Loading...
Searching...
No Matches
null_terminated_string_view.hpp
Go to the documentation of this file.
1// Copyright 2024, Toyota Motor Corporation
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
5#ifndef INCLUDE_GUARD_ARENE_BASE_ARENE_BASE_STRINGS_NULL_TERMINATED_STRING_VIEW_HPP_
6#define INCLUDE_GUARD_ARENE_BASE_ARENE_BASE_STRINGS_NULL_TERMINATED_STRING_VIEW_HPP_
7
8// IWYU pragma: private
9// IWYU pragma: friend "(arene/base(?!/tests)|stdlib/include/stdlib_detail)/.*"
10
11// parasoft-begin-suppress AUTOSAR-A7_1_5-a-2 "Trailing return syntax permitted by A7-1-5 Permit #1 v1.0.0"
12
13// parasoft-begin-suppress AUTOSAR-A16_2_2-a-2 "Arene Base aggregate headers permitted by A16-2-2 Permit #1"
14#include "arene/base/compare/operators.hpp"
15#include "arene/base/compare/strong_ordering.hpp"
16#include "arene/base/compiler_support/attributes.hpp"
17#include "arene/base/constraints/constraints.hpp"
18#include "arene/base/contracts/contract.hpp"
19#include "arene/base/detail/raw_c_string.hpp"
20#include "arene/base/detail/wrapped_iterator.hpp" // IWYU pragma: keep
21#include "arene/base/iterator/advance.hpp"
22#include "arene/base/span/span.hpp"
23#include "arene/base/stdlib_choice/cstddef.hpp"
24#include "arene/base/stdlib_choice/enable_if.hpp"
25#include "arene/base/stdlib_choice/is_same.hpp"
26#include "arene/base/stdlib_choice/string.hpp"
27#include "arene/base/strings/detail/lexicographic_string_compare.hpp"
28#include "arene/base/strings/detail/string_length.hpp"
29#include "arene/base/strings/detail/string_view_ostream_mixin.hpp" // IWYU pragma: keep
30#include "arene/base/strings/string_view.hpp"
31// parasoft-end-suppress AUTOSAR-A16_2_2-a-2
32
33// parasoft-begin-suppress CERT_C-EXP37-a-3 "False positive: The rule does not mention naming all parameters"
34// parasoft-begin-suppress AUTOSAR-M2_10_1-a-2 "Similar names permitted by M2-10-1 Permit #1"
35
36namespace arene {
37namespace base {
38
39// Exception: AUTOSAR Rule M5-0-15 (required, implementation, automated)
40// Array indexing shall be the only form of pointer arithmetic.
41//
42// The purpose of this class is to abstract out the use of pointer arithmetic necessary to process null-terminated
43// strings and string literals. Pointer arithmetic is only used to adjust the pointer within the array containing the
44// string up until the null-terminator, as covered by M5-0-16 and M5-0-17, unless iterators are incremented unchecked.
45
46// parasoft-begin-suppress AUTOSAR-A12_1_5-a-2 "False positive: delegating constructors are used"
47/// @brief A read-only string-view that provides a view onto a null-terminated string.
48///
49/// This string type can be used to pass around null-terminated strings more safely and explicitly compared to passing
50/// a 'const char*', making it easier to query the length of the string, compare strings, compute sub-strings, etc.
51///
52/// This class just holds a reference to a string stored elsewhere. The null_terminated_string_view does not own the
53/// elements of the string.
54///
55/// You should prefer to use string_view over this class unless the extra storage of the size_t required by
56/// string_view is prohibitively expensive compared to the cost of repeatedly calculating the string length
57/// every time it is used.
58class null_terminated_string_view
59 : generic_ordering_from_three_way_compare<null_terminated_string_view>
60 , string_view_ostream_mixin<null_terminated_string_view> {
61 /// @brief simple tag passkey type to protect iterator construction.
62 class passkey {
63 public:
64 /// @brief explicit private ctor to prevent construction from {}
65 constexpr explicit passkey() = default;
66 };
67
68 /// @brief Internal type alias for the base class
69 using compare_base = generic_ordering_from_three_way_compare<null_terminated_string_view>;
70
71 // parasoft-begin-suppress AUTOSAR-A3_9_1-b-2 "Returning an int for compatibility with std::string_view"
72 /// @brief The type of comparisons that yield an integer rather than a @c strong_ordering
73 using integral_comparison_result = int;
74 // parasoft-end-suppress AUTOSAR-A3_9_1-b-2
75
76 public:
77 // parasoft-begin-suppress AUTOSAR-A12_1_1-a-2 "False positive: This constructor delegates to another, which does
78 // initialize the base class"
79 /// @brief construct an empty string-view.
80 constexpr null_terminated_string_view() noexcept
81 : null_terminated_string_view("") {}
82 // parasoft-end-suppress AUTOSAR-A12_1_1-a-2
83
84 /// @brief default copy constructor
85 constexpr null_terminated_string_view(null_terminated_string_view const&) = default;
86
87 /// @brief default copy assignment
88 constexpr auto operator=(null_terminated_string_view const&) -> null_terminated_string_view& = default;
89
90 /// @brief default move constructor
91 constexpr null_terminated_string_view(null_terminated_string_view&&) = default;
92
93 /// @brief default move assignment
94 constexpr auto operator=(null_terminated_string_view&&) -> null_terminated_string_view& = default;
95
96 /// @brief default destructor
97 ~null_terminated_string_view() = default;
98
99 /// @brief constructing from a nullptr literal is always an error.
100 explicit null_terminated_string_view(std::nullptr_t) = delete;
101
102 // parasoft-begin-suppress AUTOSAR-A7_1_3-a-2 "False Positive: const is on rhs"
103 /// @brief construct a view on an existing null-terminated string.
104 ///
105 /// @param string A pointer to the first character of the null-terminated
106 /// string. Caller must ensure that the string is properly null-terminated.
107 /// Passing a nullptr has undefined behaviour.
108 // NOLINTNEXTLINE(readability-avoid-const-params-in-decls)
109 constexpr explicit null_terminated_string_view(detail::raw_c_string const string) noexcept;
110 // parasoft-end-suppress AUTOSAR-A7_1_3-a-2
111
112 /// @brief Implicit construction from a std::string (which is required to be
113 /// null-terminated).
114 ///
115 /// The resulting string view is invalidated by any operation that modifies
116 /// the string.
117 ///
118 /// @param string The source string
119 ///
120 /// @note This constructor is a template rather than a non-template taking a
121 /// <c>const std::string&</c> to avoid the possibility of unknowingly
122 /// constructing a temporary @c std::string implicitly by passing something
123 /// that is convertible to @c std::string.
124 /// e.g. a @c std::initializer_list<char>
125 template <typename String, constraints<std::enable_if_t<std::is_same<String, std::string>::value>> = nullptr>
126 // NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions)
127 null_terminated_string_view(String const& string) noexcept
128 : compare_base{},
129 string_(string.c_str()) {}
130
131 /// @brief This type represents the sentinel of the range of characters.
132 ///
133 /// The return-type of @c end() method.
134 class sentinel {};
135
136 /// @brief The value type of this view
137 using value_type = detail::character;
138 /// @brief The pointer type of this view
139 using pointer = value_type*;
140 /// @brief The const pointer type of this view
141 using const_pointer = value_type const*;
142 /// @brief The type of a reference to an element in the view
143 using reference = value_type&;
144 /// @brief The type of a reference to an element in the view
145 using const_reference = value_type const&;
146 /// @brief The type of const iterator used by this view.
147 using const_iterator = ::arene::base::detail::wrapped_iterator<const_pointer, passkey>;
148 /// @brief The type of iterator used by this view.
149 using iterator = const_iterator;
150 /// @brief The type used for sizes in this view
151 using size_type = std::size_t;
152 /// @brief The type used for differences between iterators in this view
153 using difference_type = typename iterator::difference_type;
154
155 // parasoft-begin-suppress AUTOSAR-A13_5_5-b-2 "Comparison of iterator with sentinel"
156 // parasoft-begin-suppress AUTOSAR-A11_3_1-a-2 "False positive: comparison operators are permitted"
157 /// @brief Equality comparison for a null terminated string view's iterator and the end-of-string sentinel.
158 ///
159 /// @param iter The iterator to compare against the sentinel.
160 /// @return true if the iterator points to a @c '\0' character.
161 /// @return false Otherwise.
162 ARENE_NODISCARD friend constexpr auto operator==(iterator const iter, sentinel) noexcept -> bool {
163 return *iter.base() == '\0';
164 }
165 // parasoft-end-suppress AUTOSAR-A11_3_1-a-2
166 // parasoft-end-suppress AUTOSAR-A13_5_5-b-2
167
168 // parasoft-begin-suppress AUTOSAR-A13_5_5-b-2 "Comparison of iterator with sentinel"
169 // parasoft-begin-suppress AUTOSAR-A11_3_1-a-2 "False positive: comparison operators are permitted"
170 /// @brief Equality comparison for a null terminated string view's iterator and the end-of-string sentinel.
171 ///
172 /// @param iter The iterator to compare against the sentinel.
173 /// @return true if the iterator points to a @c '\0' character.
174 /// @return false Otherwise.
175 ARENE_NODISCARD friend constexpr auto operator!=(iterator const iter, sentinel) noexcept -> bool {
176 return !(iter == null_terminated_string_view::sentinel{});
177 }
178 // parasoft-end-suppress AUTOSAR-A11_3_1-a-2
179 // parasoft-end-suppress AUTOSAR-A13_5_5-b-2
180
181 /// @brief Get an iterator to the first element of the string.
182 /// @return const_iterator An iterator to the first character in the string.
183 ARENE_NODISCARD constexpr auto begin() const noexcept -> const_iterator { return const_iterator{passkey{}, string_}; }
184
185 /// @brief Get the sentinel (end-iterator) of this string.
186 /// @return sentinel The sentinel comparable to an iterator to mark the end of the string.
187 // If we make this a static method then we end up getting warnings at the usage site about calling a static member
188 // function using .end() syntax. So instead we rather keep this as a non-static member function.
189 // NOLINTNEXTLINE(readability-convert-member-functions-to-static)
190 ARENE_NODISCARD static constexpr auto end() noexcept -> sentinel { return {}; }
191
192 /// @brief Query if this string is the empty string.
193 /// @return true if the string is empty, false otherwise
194 ARENE_NODISCARD constexpr auto empty() const noexcept -> bool { return begin() == end(); }
195
196 /// @brief Query the length of the string.
197 ///
198 /// @return The number of 'char' elements in the null-terminate string, not including the null-terminator.
199 ///
200 /// @note This operation needs to traverse the string to determine the length and so may be expensive to call
201 /// repeatedly. If you need to call @c length() repeatedly then either cache the value or convert this object to a
202 /// @c string_view first and use that instead.
203 ARENE_NODISCARD constexpr auto length() const noexcept -> size_type { return detail::string_length(string_); }
204
205 /// @brief Query the length of the string.
206 ///
207 /// @return The number of 'char' elements in the null-terminate string, not including the null-terminator.
208 ///
209 /// @note This operation needs to traverse the string to determine the length and so may be expensive to call
210 /// repeatedly. If you need to call @c size() repeatedly then either cache the value or convert this object to a @c
211 /// string_view first and use that instead.
212 ARENE_NODISCARD constexpr auto size() const noexcept -> size_type { return length(); }
213
214 /// @brief Obtain a C-style null-terminated string pointer.
215 /// @return const_pointer A pointer to the first character in the string.
216 ARENE_NODISCARD constexpr auto c_str() const noexcept -> const_pointer { return string_; }
217
218 /// @brief Obtain a C-style null-terminated string pointer.
219 /// @return const_pointer A pointer to the first character in the string.
220 ARENE_NODISCARD constexpr auto data() const noexcept -> const_pointer { return string_; }
221
222 // parasoft-begin-suppress AUTOSAR-A13_5_2-a-2 "Implicit conversion to string_view is part of the API"
223 /// @brief Implicit conversion to string_view.
224 ///
225 /// @return A string_view that spans the characters of *this, minus the null-terminator.
226 // NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions)
227 constexpr operator string_view() const noexcept { return {data(), length()}; }
228 // parasoft-end-suppress AUTOSAR-A13_5_2-a-2
229
230 /// @brief Compute the sub-string starting at the specified offset within the string.
231 ///
232 /// @param offset The index of the first character that the returned substring should start at.
233 ///
234 /// @return null_terminated_string_view A view which starts at the character at index @c offset and continues to
235 /// the end of the string. If @c offset is larger than @c length() then returns the empty string view.
236 ///
237 /// @note This operation has complexity of O(min(offset, length())).
238 // parasoft-begin-suppress AUTOSAR-A15_4_5-a "False positive: There is no function that throws an excpetion of 'Any'
239 // type"
240 ARENE_NODISCARD constexpr auto substr(std::size_t offset) const noexcept -> null_terminated_string_view;
241 // parasoft-end-suppress AUTOSAR-A15_4_5-a
242
243 /// @brief Compute the sub-string starting at the specified offset and with the specified maximum length.
244 ///
245 /// @param offset The index of the first charcter of the substring. If this value is greater than @c length() then
246 /// returns the empty sub-string.
247 /// @param max_length The maximum length of the returned substring. The returned substring may be shorter than this
248 /// length if there are fewer than @c max_length elements after @c offset.
249 ///
250 /// @return string_view A substring referencing the elements of *this starting at index @c offset with a length at
251 /// most @c max_length.
252 ///
253 /// @note This operation has complexity of O(min(offset+max_length, length()))
254 // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
255 ARENE_NODISCARD constexpr auto substr(std::size_t offset, std::size_t max_length) const noexcept -> string_view;
256
257 /// @brief Compare this null-terminated string to another null-terminated string.
258 ///
259 /// @param other The other string to compare *this to.
260 ///
261 /// @return integral_comparison_result A negative value if @c *this lexicographically precedes @c other, a positive
262 /// value if @c other lexicographically precedes @c *this, or zero if the two strings are equal.
263 // parasoft-begin-suppress AUTOSAR-A7_1_3-a-2 "False Positive: const is on rhs"
264 constexpr auto compare(null_terminated_string_view const other) const noexcept -> integral_comparison_result {
265 return from_strong_ordering(three_way_compare(*this, other));
266 }
267 // parasoft-end-suppress AUTOSAR-A7_1_3-a-2
268
269 /// @brief Compare this null-terminated string to a string_view.
270 ///
271 /// @param other The other string to compare *this to.
272 ///
273 /// @return A negative value if @c *this lexicographically precedes @c other, a positive value if @c other
274 /// lexicographically precedes @c *this, or zero if the two strings are equal.
275 constexpr auto compare(string_view const& other) const noexcept -> integral_comparison_result {
276 return from_strong_ordering(three_way_compare(*this, other));
277 }
278
279 /// @brief Compare this null-terminated string to a std::string.
280 ///
281 /// @param other The other string to compare *this to.
282 ///
283 /// @return A negative value if @c *this lexicographically precedes @c other, a positive value if @c other
284 /// lexicographically precedes @c *this, or zero if the two strings are equal.
285 ///
286 /// @note This constructor is a template rather than a non-template taking a <c>const std::string&</c> to handle when
287 /// @c std::string is only declared, not defined.
288 template <typename String, constraints<std::enable_if_t<std::is_same<String, std::string>::value>> = nullptr>
289 auto compare(String const& other) const noexcept -> integral_comparison_result {
290 return compare(string_view(other));
291 }
292
293 /// @brief Compare two null-terminated strings
294 ///
295 /// @param lhs The first string to compare
296 /// @param rhs The second string to compare
297 ///
298 /// @return strong_ordering::less if @c lhs lexicographically precedes @c rhs
299 /// @return strong_ordering::greater if @c rhs lexicographically precedes @c lhs
300 /// @return strong_ordering::equal if the two strings are equal.
301 static constexpr auto three_way_compare(
302 null_terminated_string_view const lhs,
303 null_terminated_string_view const rhs
304 ) noexcept -> strong_ordering {
305 return detail::lexicographic_string_compare(lhs.c_str(), rhs.c_str());
306 }
307
308 /// @brief Compare two null-terminated strings
309 ///
310 /// @param lhs The first string to compare
311 /// @param rhs The second string to compare
312 ///
313 /// @return strong_ordering::less if @c lhs lexicographically precedes @c rhs
314 /// @return strong_ordering::greater if @c rhs lexicographically precedes @c lhs
315 /// @return strong_ordering::equal if the two strings are equal.
316 static constexpr auto
317 three_way_compare(null_terminated_string_view const lhs, detail::raw_c_string const rhs) noexcept -> strong_ordering {
318 return three_way_compare(lhs, null_terminated_string_view(rhs));
319 }
320
321 /// @brief Compare a null-terminated string to a @c std::string
322 ///
323 /// @param lhs The first string to compare
324 /// @param rhs The second string to compare
325 ///
326 /// @return strong_ordering::less if @c lhs lexicographically precedes @c rhs
327 /// @return strong_ordering::greater if @c rhs lexicographically precedes @c lhs
328 /// @return strong_ordering::equal if the two strings are equal.
329 ///
330 /// @note This constructor is a template rather than a non-template taking a <c>const std::string&</c> to handle when
331 /// @c std::string is only declared, not defined.
332 template <typename String, constraints<std::enable_if_t<std::is_same<String, std::string>::value>> = nullptr>
333 static auto three_way_compare(null_terminated_string_view const lhs, String const& rhs) noexcept -> strong_ordering {
334 return three_way_compare(lhs, string_view{rhs});
335 }
336
337 /// @brief Compare a null-terminated string to a @c string_view
338 ///
339 /// @param lhs The first string to compare
340 /// @param rhs The second string to compare
341 ///
342 /// @return strong_ordering::less if @c lhs lexicographically precedes @c rhs
343 /// @return strong_ordering::greater if @c rhs lexicographically precedes @c lhs
344 /// @return strong_ordering::equal if the two strings are equal.
345 static constexpr auto three_way_compare(null_terminated_string_view const lhs, string_view const rhs) noexcept
346 -> strong_ordering {
347 return opposite_ordering(detail::lexicographic_string_compare({rhs.data(), rhs.size()}, lhs.c_str()));
348 }
349
350 private:
351 /// @brief The pointer to the NUL-terminated string
352 detail::raw_c_string string_;
353};
354// parasoft-end-suppress AUTOSAR-A12_1_5-a-2 "False positive: delegating constructors are used"
355
356/// @brief Compute the sub-string starting at the specified offset within the string.
357///
358/// @param offset The index of the first character that the returned substring should start at.
359///
360/// @return null_terminated_string_view A view which starts at the character at index @c offset and continues to
361/// the end of the string. If @c offset is larger than @c length() then returns the empty string view.
362///
363/// @note This operation has complexity of O(min(offset, length())).
364// parasoft-begin-suppress AUTOSAR-A15_4_5-a "False positive: There is no function that throws an excpetion of 'Any'
365// type"
366ARENE_NODISCARD inline constexpr auto null_terminated_string_view::substr(std::size_t offset) const noexcept
367 -> null_terminated_string_view {
368 detail::raw_c_string start{string_};
369 while ((offset != 0U) && (*start != '\0')) {
370 --offset;
371 arene::base::advance(start, 1);
372 }
373
374 return null_terminated_string_view{start};
375}
376// parasoft-end-suppress AUTOSAR-A15_4_5-a
377
378// parasoft-begin-suppress AUTOSAR-A7_1_3-a-2 "False Positive: const is on rhs"
379/// @brief construct a view on an existing null-terminated string.
380///
381/// @param string A pointer to the first character of the null-terminated
382/// string. Caller must ensure that the string is properly null-terminated.
383/// Passing a nullptr has undefined behaviour.
384inline constexpr null_terminated_string_view::null_terminated_string_view(detail::raw_c_string const string) noexcept
385 : compare_base{},
386 string_(string) {
387 ARENE_PRECONDITION(string != nullptr);
388}
389// parasoft-end-suppress AUTOSAR-A7_1_3-a-2
390
391/// @brief Compute the sub-string starting at the specified offset and with the specified maximum length.
392///
393/// @param offset The index of the first charcter of the substring. If this value is greater than @c length() then
394/// returns the empty sub-string.
395/// @param max_length The maximum length of the returned substring. The returned substring may be shorter than this
396/// length if there are fewer than @c max_length elements after @c offset.
397///
398/// @return string_view A substring referencing the elements of *this starting at index @c offset with a length at
399/// most @c max_length.
400///
401/// @note This operation has complexity of O(min(offset+max_length, length()))
402// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
403ARENE_NODISCARD inline constexpr auto null_terminated_string_view::substr(std::size_t offset, std::size_t max_length)
404 const noexcept -> string_view {
405 auto first = begin();
406 while ((offset != 0U) && (first != end())) {
407 ++first;
408 --offset;
409 }
410
411 auto last = first;
412 while ((max_length != 0U) && (last != end())) {
413 ++last;
414 --max_length;
415 }
416
417 return {first.base(), static_cast<std::size_t>(last - first)};
418}
419
420namespace literals {
421
422// parasoft-begin-suppress AUTOSAR-A7_1_3-a "False positive: const is placed on the right hand side"
423/// @brief Construct a null-terminated string view from a string literal.
424///
425/// For example:
426/// @code
427/// using namespace literals;
428/// auto sv = "a null terminated string"_ntsv;
429/// @endcode
430///
431/// @param str Pointer to the string literal.
432/// @return The null_terminated_string_view referencing the string literal.
433constexpr auto operator""_ntsv(detail::raw_c_string const str, std::size_t /*size*/) noexcept
434 -> null_terminated_string_view {
435 return null_terminated_string_view{str};
436}
437// parasoft-end-suppress AUTOSAR-A7_1_3-a
438
439} // namespace literals
440
441} // namespace base
442} // namespace arene
443
444// parasoft-end-suppress AUTOSAR-M2_10_1-a-2
445
446#endif // INCLUDE_GUARD_ARENE_BASE_ARENE_BASE_STRINGS_NULL_TERMINATED_STRING_VIEW_HPP_
Definition array_exceptions_disabled.cpp:11
Copyright 2026, Toyota Motor Corporation.
Definition array_exceptions_disabled.cpp:10