• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <string>  // for std::char_traits
17 #include <type_traits>
18 
19 #if PW_CXX_STANDARD_IS_SUPPORTED(17)  // std::string_view is a C++17 feature
20 #include <string_view>
21 #endif  // PW_CXX_STANDARD_IS_SUPPORTED(17)
22 
23 #include "pw_assert/assert.h"
24 #include "pw_polyfill/language_feature_macros.h"
25 
26 namespace pw {
27 namespace string_impl {
28 
29 // pw::InlineString<>::size_type is unsigned short so the capacity and current
30 // size fit into a single word.
31 using size_type = unsigned short;
32 
33 template <typename CharType, typename T>
34 using EnableIfNonArrayCharPointer = std::enable_if_t<
35     std::is_pointer<T>::value && !std::is_array<T>::value &&
36     std::is_same<CharType, std::remove_cv_t<std::remove_pointer_t<T>>>::value>;
37 
38 template <typename T>
39 using EnableIfInputIterator = std::enable_if_t<
40     std::is_convertible<typename std::iterator_traits<T>::iterator_category,
41                         std::input_iterator_tag>::value>;
42 
43 #if PW_CXX_STANDARD_IS_SUPPORTED(17)  // std::string_view is a C++17 feature
44 template <typename CharType, typename T>
45 using EnableIfStringViewLike = std::enable_if_t<
46     std::is_convertible<const T&, std::basic_string_view<CharType>>() &&
47     !std::is_convertible<const T&, const CharType*>()>;
48 
49 template <typename CharType, typename T>
50 using EnableIfStringViewLikeButNotStringView = std::enable_if_t<
51     !std::is_same<T, std::basic_string_view<CharType>>() &&
52     std::is_convertible<const T&, std::basic_string_view<CharType>>() &&
53     !std::is_convertible<const T&, const CharType*>()>;
54 #endif  // PW_CXX_STANDARD_IS_SUPPORTED(17)
55 
56 // Reserved capacity that is used to represent a generic-length
57 // pw::InlineString.
58 PW_INLINE_VARIABLE constexpr size_type kGeneric = size_type(-1);
59 
60 #if defined(__cpp_lib_constexpr_string) && __cpp_lib_constexpr_string >= 201907L
61 
62 // Like std::string and std::string_view, pw::InlineString uses std::char_traits
63 // for low-level operations.
64 using std::char_traits;
65 
66 #else
67 
68 // If constexpr std::char_traits is not available, provide a custom traits class
69 // with constexpr versions of the necessary functions. Defer to std::char_traits
70 // when possible.
71 template <typename T>
72 class char_traits : private std::char_traits<T> {
73  public:
assign(T & dest,const T & source)74   static constexpr void assign(T& dest, const T& source) noexcept {
75     dest = source;
76   }
77 
assign(T * dest,size_t count,T value)78   static constexpr T* assign(T* dest, size_t count, T value) {
79     for (size_t i = 0; i < count; ++i) {
80       dest[i] = value;
81     }
82     return dest;
83   }
84 
85   using std::char_traits<T>::eq;
86 
copy(T * dest,const T * source,size_t count)87   static constexpr T* copy(T* dest, const T* source, size_t count) {
88     for (size_type i = 0; i < count; ++i) {
89       char_traits<T>::assign(dest[i], source[i]);
90     }
91     return dest;
92   }
93 
94   using std::char_traits<T>::compare;
95 };
96 
97 #endif  // __cpp_lib_constexpr_string
98 
99 // Used in static_asserts to check that a C array fits in an InlineString.
NullTerminatedArrayFitsInString(size_t null_terminated_array_size,size_type capacity)100 constexpr bool NullTerminatedArrayFitsInString(
101     size_t null_terminated_array_size, size_type capacity) {
102   return null_terminated_array_size > 0u &&
103          null_terminated_array_size - 1 <= capacity &&
104          null_terminated_array_size - 1 < kGeneric;
105 }
106 
107 // Constexpr utility functions for pw::InlineString. These are NOT intended for
108 // general use. These mostly map directly to general purpose standard library
109 // utilities that are not constexpr until C++20.
110 
111 // Calculates the length of a C string up to the capacity. Returns capacity + 1
112 // if the string is longer than the capacity. This replaces
113 // std::char_traits<T>::length, which is unbounded. The string must contain at
114 // least one character.
115 template <typename T>
BoundedStringLength(const T * string,size_type capacity)116 constexpr size_type BoundedStringLength(const T* string, size_type capacity) {
117   size_type length = 0;
118   for (; length <= capacity; ++length) {
119     if (char_traits<T>::eq(string[length], T())) {
120       break;
121     }
122   }
123   return length;  // length is capacity + 1 if T() was not found.
124 }
125 
126 // As with std::string, InlineString treats literals and character arrays as
127 // null-terminated strings. ArrayStringLength checks that the array size fits
128 // within size_type and asserts if no null terminator was found in the array.
129 template <typename T>
ArrayStringLength(const T * array,size_type max_string_length,size_type capacity)130 constexpr size_type ArrayStringLength(const T* array,
131                                       size_type max_string_length,
132                                       size_type capacity) {
133   const size_type max_length = std::min(max_string_length, capacity);
134   const size_type length = BoundedStringLength(array, max_length);
135   PW_ASSERT(length <= max_string_length);  // The array is not null terminated
136   return length;
137 }
138 
139 template <typename T, size_t kCharArraySize>
ArrayStringLength(const T (& array)[kCharArraySize],size_type capacity)140 constexpr size_type ArrayStringLength(const T (&array)[kCharArraySize],
141                                       size_type capacity) {
142   static_assert(kCharArraySize > 0u, "C arrays cannot have a length of 0");
143   static_assert(kCharArraySize - 1 < kGeneric,
144                 "The size of this literal or character array is too large "
145                 "for pw::InlineString<>::size_type");
146   return ArrayStringLength(
147       array, static_cast<size_type>(kCharArraySize - 1), capacity);
148 }
149 
150 // Constexpr version of std::copy that returns the number of copied characters.
151 template <typename InputIterator, typename T>
IteratorCopyAndTerminate(InputIterator begin,InputIterator end,T * const string_begin,const T * const string_end)152 constexpr size_type IteratorCopyAndTerminate(InputIterator begin,
153                                              InputIterator end,
154                                              T* const string_begin,
155                                              const T* const string_end) {
156   T* current_position = string_begin;
157   for (InputIterator it = begin; it != end; ++it) {
158     PW_ASSERT(current_position != string_end);
159     char_traits<T>::assign(*current_position++, *it);
160   }
161   char_traits<T>::assign(*current_position, T());  // Null terminate
162   return static_cast<size_type>(current_position - string_begin);
163 }
164 
165 // Constexpr lexicographical comparison.
166 template <typename T>
Compare(const T * lhs,size_type lhs_size,const T * rhs,size_type rhs_size)167 constexpr int Compare(const T* lhs,
168                       size_type lhs_size,
169                       const T* rhs,
170                       size_type rhs_size) noexcept {
171   int result = char_traits<T>::compare(lhs, rhs, std::min(lhs_size, rhs_size));
172   if (result != 0 || lhs_size == rhs_size) {
173     return result;
174   }
175   return lhs_size < rhs_size ? -1 : 1;
176 }
177 
178 }  // namespace string_impl
179 }  // namespace pw
180