C/C++ API Reference
Loading...
Searching...
No Matches
string_impl.h
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#pragma once
15
16#include <limits>
17#include <string> // for std::char_traits
18#include <string_view>
19#include <type_traits>
20
21#include "pw_assert/assert.h"
22
23namespace pw::string_impl {
24
25// pw::InlineString<>::size_type is unsigned short so the capacity and current
26// size fit into a single word.
27using size_type = unsigned short;
28
29// Reserved capacity that is used to represent a generic-length
30// pw::InlineString.
31inline constexpr size_t kGeneric = size_type(-1);
32
33template <typename T>
34inline constexpr bool kUseStdCharTraits =
35#if !defined(__cpp_lib_constexpr_string) || __cpp_lib_constexpr_string < 201907L
36 false &&
37#endif // __cpp_lib_constexpr_string
38 !std::is_same_v<T, std::byte>;
39
40// Provide a minimal custom traits class for use by std::byte and if
41// std::char_traits is not yet fully constexpr (__cpp_lib_constexpr_string).
42template <typename T, bool = kUseStdCharTraits<T>>
44 using char_type = T;
45 using int_type = unsigned int;
46
47 static constexpr void assign(T& dest, const T& source) noexcept {
48 dest = source;
49 }
50
51 static constexpr T* assign(T* dest, size_t count, T value) {
52 for (size_t i = 0; i < count; ++i) {
53 dest[i] = value;
54 }
55 return dest;
56 }
57
58 static constexpr bool eq(T lhs, T rhs) { return lhs == rhs; }
59
60 static constexpr T* move(T* dest, const T* source, size_t count) {
61 if (dest < source) {
62 char_traits<T>::copy(dest, source, count);
63 } else if (source < dest) {
64 for (size_t i = count; i != 0; --i) {
65 char_traits<T>::assign(dest[i - 1], source[i - 1]);
66 }
67 }
68 return dest;
69 }
70
71 static constexpr T* copy(T* dest, const T* source, size_t count) {
72 for (size_t i = 0; i < count; ++i) {
73 char_traits<T>::assign(dest[i], source[i]);
74 }
75 return dest;
76 }
77
78 static constexpr int compare(const T* lhs, const T* rhs, size_t count) {
79 for (size_t i = 0; i < count; ++i) {
80 if (lhs[i] < rhs[i]) {
81 return -1;
82 }
83 if (rhs[i] < lhs[i]) {
84 return 1;
85 }
86 }
87 return 0;
88 }
89};
90
91// Use std::char_traits for character types when it fully supports constexpr.
92template <typename T>
93struct char_traits<T, true> : public std::char_traits<T> {};
94
95// string_views for byte strings need to use Pigweed's custom char_traits since
96// std::char_traits<std::byte> is not defined. (Alternately, could specialize
97// std::char_traits, but this is simpler.) basic_string_view<byte> won't be
98// commonly used (byte spans are more common), but support it for completeness.
99template <typename T>
101 using type = std::basic_string_view<T>;
102};
103
104template <>
105struct StringViewType<std::byte> {
106 using type = std::basic_string_view<std::byte, char_traits<std::byte>>;
107};
108
109template <typename T>
110using View = typename StringViewType<T>::type;
111
112// Aliases for disabling overloads with SFINAE.
113template <typename CharType, typename T>
114using EnableIfNonArrayCharPointer = std::enable_if_t<
115 std::is_pointer<T>::value && !std::is_array<T>::value &&
116 std::is_same<CharType, std::remove_cv_t<std::remove_pointer_t<T>>>::value>;
117
118template <typename CharType, typename T>
119using EnableIfStringViewLike =
120 std::enable_if_t<std::is_convertible<const T&, View<CharType>>() &&
121 !std::is_convertible<const T&, const CharType*>()>;
122
123template <typename CharType, typename T>
124using EnableIfStringViewLikeButNotStringView =
125 std::enable_if_t<!std::is_same<T, View<CharType>>() &&
126 std::is_convertible<const T&, View<CharType>>() &&
127 !std::is_convertible<const T&, const CharType*>()>;
128
129// Used in static_asserts to check that a C array fits in an InlineString.
130constexpr bool NullTerminatedArrayFitsInString(
131 size_t null_terminated_array_size, size_t capacity) {
132 return null_terminated_array_size > 0u &&
133 null_terminated_array_size - 1 <= capacity &&
134 null_terminated_array_size - 1 < kGeneric;
135}
136
137// Used to safely convert various numeric types to `size_type`.
138template <typename T>
139constexpr size_type CheckedCastToSize(T num) {
140 static_assert(std::is_unsigned<T>::value,
141 "Attempted to convert signed value to string length, but only "
142 "unsigned types are allowed.");
143 PW_ASSERT(num < std::numeric_limits<size_type>::max());
144 return static_cast<size_type>(num);
145}
146
147// Constexpr utility functions for pw::InlineString. These are NOT intended for
148// general use. These mostly map directly to general purpose standard library
149// utilities that are not constexpr until C++20.
150
151// Calculates the length of a C string up to the capacity. Returns capacity + 1
152// if the string is longer than the capacity. This replaces
153// std::char_traits<T>::length, which is unbounded. The string must contain at
154// least one character.
155template <typename T>
156constexpr size_t BoundedStringLength(const T* string, size_t capacity) {
157 size_t length = 0;
158 for (; length <= capacity; ++length) {
159 if (char_traits<T>::eq(string[length], T())) {
160 break;
161 }
162 }
163 return length; // length is capacity + 1 if T() was not found.
164}
165
166// As with std::string, InlineString treats literals and character arrays as
167// null-terminated strings. ArrayStringLength checks that the array size fits
168// within size_t and asserts if no null terminator was found in the array.
169template <typename T>
170constexpr size_t ArrayStringLength(const T* array,
171 size_t max_string_length,
172 size_t capacity) {
173 const size_t max_length = std::min(max_string_length, capacity);
174 const size_t length = BoundedStringLength(array, max_length);
175 PW_ASSERT(length <= max_string_length); // The array is not null terminated
176 return length;
177}
178
179template <typename T, size_t kCharArraySize>
180constexpr size_t ArrayStringLength(const T (&array)[kCharArraySize],
181 size_t capacity) {
182 static_assert(kCharArraySize > 0u, "C arrays cannot have a length of 0");
183 static_assert(kCharArraySize - 1 < kGeneric,
184 "The size of this literal or character array is too large "
185 "for pw::InlineString<>::size_t");
186 return ArrayStringLength(
187 array, static_cast<size_t>(kCharArraySize - 1), capacity);
188}
189
190// Constexpr version of std::copy that returns the number of copied characters.
191// Does NOT null-terminate the string.
192template <typename InputIterator, typename T>
193constexpr size_t IteratorCopy(InputIterator begin,
194 InputIterator end,
195 T* const string_begin,
196 const T* const string_end) {
197 T* current_position = string_begin;
198
199 // If `InputIterator` is a `LegacyRandomAccessIterator`, the bounds check can
200 // be done up front, allowing the compiler more flexibility in optimizing the
201 // loop.
202 using category =
203 typename std::iterator_traits<InputIterator>::iterator_category;
204 if constexpr (std::is_same_v<category, std::random_access_iterator_tag>) {
205 PW_ASSERT(begin <= end);
206 PW_ASSERT(end - begin <= string_end - string_begin);
207 for (InputIterator it = begin; it != end; ++it) {
208 char_traits<T>::assign(*current_position++, *it);
209 }
210 } else {
211 for (InputIterator it = begin; it != end; ++it) {
212 PW_ASSERT(current_position != string_end);
213 char_traits<T>::assign(*current_position++, *it);
214 }
215 }
216 return static_cast<size_t>(current_position - string_begin);
217}
218
219// Constexpr lexicographical comparison.
220template <typename T>
221constexpr int Compare(const T* lhs,
222 size_t lhs_size,
223 const T* rhs,
224 size_t rhs_size) noexcept {
225 int result = char_traits<T>::compare(lhs, rhs, std::min(lhs_size, rhs_size));
226 if (result != 0 || lhs_size == rhs_size) {
227 return result;
228 }
229 return lhs_size < rhs_size ? -1 : 1;
230}
231
232} // namespace pw::string_impl
Definition: string_impl.h:100
Definition: string_impl.h:43