Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / third_party / pigweed / repo / pw_string / public / pw_string / string_builder.h
1 // Copyright 2019 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 <algorithm>
17 #include <cstdarg>
18 #include <cstddef>
19 #include <cstring>
20 #include <span>
21 #include <string_view>
22 #include <type_traits>
23 #include <utility>
24
25 #include "pw_preprocessor/compiler.h"
26 #include "pw_status/status.h"
27 #include "pw_status/status_with_size.h"
28 #include "pw_string/to_string.h"
29
30 namespace pw {
31
32 // StringBuilder facilitates building formatted strings in a fixed-size buffer.
33 // StringBuilders are always null terminated (unless they are constructed with
34 // an empty buffer) and never overflow. Status is tracked for each operation and
35 // an overall status is maintained, which reflects the most recent error.
36 //
37 // A StringBuilder does not own the buffer it writes to. It can be used to write
38 // strings to any buffer. The StringBuffer template class, defined below,
39 // allocates a buffer alongside a StringBuilder.
40 //
41 // StringBuilder supports C++-style << output, similar to std::ostringstream. It
42 // also supports std::string-like append functions and printf-style output.
43 //
44 // StringBuilder uses the ToString function to support arbitrary types. Defining
45 // a ToString template specialization overload in the pw namespace allows
46 // writing that type to a StringBuilder with <<.
47 //
48 // For example, the following ToString overload allows writing MyStatus objects
49 // to StringBuilders:
50 //
51 //   namespace pw {
52 //
53 //   template <>
54 //   StatusWithSize ToString<MyStatus>(MyStatus value, std::span<char> buffer) {
55 //     return CopyString(MyStatusString(value), buffer);
56 //   }
57 //
58 //   }  // namespace pw
59 //
60 // For complex types, it may be easier to override StringBuilder's << operator,
61 // similar to the standard library's std::ostream. For example:
62 //
63 //   namespace pw {
64 //
65 //   StringBuilder& operator<<(StringBuilder& sb, const MyType& value) {
66 //     return sb << "MyType(" << value.foo << ", " << value.bar << ')';
67 //   }
68 //
69 //   }  // namespace pw
70 //
71 // Alternately, complex types may use a StringBuilder in their ToString, but it
72 // is likely to be simpler to override StringBuilder's operator<<.
73 //
74 // StringBuilder is safe, flexible, and results in much smaller code size than
75 // using std::ostringstream. However, applications sensitive to code size should
76 // use StringBuilder with care.
77 //
78 // The fixed code size cost of StringBuilder is significant, though smaller than
79 // std::snprintf. Using StringBuilder's << and append methods exclusively in
80 // place of snprintf reduces code size, but snprintf may be difficult to avoid.
81 //
82 // The incremental code size cost of StringBuilder is comparable to snprintf if
83 // errors are handled. Each argument to StringBuilder's << expands to a function
84 // call, but one or two StringBuilder appends may have a smaller code size
85 // impact than a single snprintf call. See the size report for further analysis.
86 class StringBuilder {
87  public:
88   // Creates an empty StringBuilder.
89   constexpr StringBuilder(std::span<char> buffer) : buffer_(buffer), size_(0) {
90     NullTerminate();
91   }
92   StringBuilder(std::span<std::byte> buffer)
93       : StringBuilder(
94             {reinterpret_cast<char*>(buffer.data()), buffer.size_bytes()}) {}
95
96   // Disallow copy/assign to avoid confusion about where the string is actually
97   // stored. StringBuffers may be copied into one another.
98   StringBuilder(const StringBuilder&) = delete;
99
100   StringBuilder& operator=(const StringBuilder&) = delete;
101
102   // Returns the contents of the string buffer. Always null-terminated.
103   const char* data() const { return buffer_.data(); }
104   const char* c_str() const { return data(); }
105
106   // Returns a std::string_view of the contents of this StringBuilder. The
107   // std::string_view is invalidated if the StringBuilder contents change.
108   std::string_view view() const { return std::string_view(data(), size()); }
109
110   // Allow implicit conversions to std::string_view so StringBuilders can be
111   // passed into functions that take a std::string_view.
112   operator std::string_view() const { return view(); }
113
114   // Returns a std::span<const std::byte> representation of this StringBuffer.
115   std::span<const std::byte> as_bytes() const {
116     return std::span(reinterpret_cast<const std::byte*>(buffer_.data()), size_);
117   }
118
119   // Returns the StringBuilder's status, which reflects the most recent error
120   // that occurred while updating the string. After an update fails, the status
121   // remains non-OK until it is cleared with clear() or clear_status(). Returns:
122   //
123   //     OK if no errors have occurred
124   //     RESOURCE_EXHAUSTED if output to the StringBuilder was truncated
125   //     INVALID_ARGUMENT if printf-style formatting failed
126   //     OUT_OF_RANGE if an operation outside the buffer was attempted
127   //
128   Status status() const { return status_; }
129
130   // Returns status() and size() as a StatusWithSize.
131   StatusWithSize status_with_size() const {
132     return StatusWithSize(status_, size_);
133   }
134
135   // The status from the last operation. May be OK while status() is not OK.
136   Status last_status() const { return last_status_; }
137
138   // True if status() is OkStatus().
139   bool ok() const { return status_.ok(); }
140
141   // True if the string is empty.
142   bool empty() const { return size() == 0u; }
143
144   // Returns the current length of the string, excluding the null terminator.
145   size_t size() const { return size_; }
146
147   // Returns the maximum length of the string, excluding the null terminator.
148   size_t max_size() const { return buffer_.empty() ? 0u : buffer_.size() - 1; }
149
150   // Clears the string and resets its error state.
151   void clear();
152
153   // Sets the statuses to OkStatus();
154   void clear_status() {
155     status_ = OkStatus();
156     last_status_ = OkStatus();
157   }
158
159   // Appends a single character. Stets the status to RESOURCE_EXHAUSTED if the
160   // character cannot be added because the buffer is full.
161   void push_back(char ch) { append(1, ch); }
162
163   // Removes the last character. Sets the status to OUT_OF_RANGE if the buffer
164   // is empty (in which case the unsigned overflow is intentional).
165   void pop_back() PW_NO_SANITIZE("unsigned-integer-overflow") {
166     resize(size() - 1);
167   }
168
169   // Appends the provided character count times.
170   StringBuilder& append(size_t count, char ch);
171
172   // Appends count characters from str to the end of the StringBuilder. If count
173   // exceeds the remaining space in the StringBuffer, max_size() - size()
174   // characters are appended and the status is set to RESOURCE_EXHAUSTED.
175   //
176   // str is not considered null-terminated and may contain null characters.
177   StringBuilder& append(const char* str, size_t count);
178
179   // Appends characters from the null-terminated string to the end of the
180   // StringBuilder. If the string's length exceeds the remaining space in the
181   // buffer, max_size() - size() characters are copied and the status is set to
182   // RESOURCE_EXHAUSTED.
183   //
184   // This function uses string::Length instead of std::strlen to avoid unbounded
185   // reads if the string is not null terminated.
186   StringBuilder& append(const char* str);
187
188   // Appends a std::string_view to the end of the StringBuilder.
189   StringBuilder& append(const std::string_view& str);
190
191   // Appends a substring from the std::string_view to the StringBuilder. Copies
192   // up to count characters starting from pos to the end of the StringBuilder.
193   // If pos > str.size(), sets the status to OUT_OF_RANGE.
194   StringBuilder& append(const std::string_view& str,
195                         size_t pos,
196                         size_t count = std::string_view::npos);
197
198   // Appends to the end of the StringBuilder using the << operator. This enables
199   // C++ stream-style formatted to StringBuilders.
200   template <typename T>
201   StringBuilder& operator<<(const T& value) {
202     // For std::string_view-compatible types, use the append function, which
203     // gives smaller code size.
204     if constexpr (std::is_convertible_v<T, std::string_view>) {
205       append(value);
206     } else {
207       HandleStatusWithSize(ToString(value, buffer_.subspan(size_)));
208     }
209     return *this;
210   }
211
212   // Provide a few additional operator<< overloads that reduce code size.
213   StringBuilder& operator<<(bool value) {
214     return append(value ? "true" : "false");
215   }
216
217   StringBuilder& operator<<(char value) {
218     push_back(value);
219     return *this;
220   }
221
222   StringBuilder& operator<<(std::nullptr_t) {
223     return append(string::kNullPointerString);
224   }
225
226   StringBuilder& operator<<(Status status) { return *this << status.str(); }
227
228   // Appends a printf-style string to the end of the StringBuilder. If the
229   // formatted string does not fit, the results are truncated and the status is
230   // set to RESOURCE_EXHAUSTED.
231   //
232   // Internally, calls string::Format, which calls std::vsnprintf.
233   PW_PRINTF_FORMAT(2, 3) StringBuilder& Format(const char* format, ...);
234
235   // Appends a vsnprintf-style string with va_list arguments to the end of the
236   // StringBuilder. If the formatted string does not fit, the results are
237   // truncated and the status is set to RESOURCE_EXHAUSTED.
238   //
239   // Internally, calls string::Format, which calls std::vsnprintf.
240   StringBuilder& FormatVaList(const char* format, va_list args);
241
242   // Sets the StringBuilder's size. This function only truncates; if
243   // new_size > size(), it sets status to OUT_OF_RANGE and does nothing.
244   void resize(size_t new_size);
245
246  protected:
247   // Functions to support StringBuffer copies.
248   constexpr StringBuilder(std::span<char> buffer, const StringBuilder& other)
249       : buffer_(buffer),
250         size_(other.size_),
251         status_(other.status_),
252         last_status_(other.last_status_) {}
253
254   void CopySizeAndStatus(const StringBuilder& other);
255
256  private:
257   size_t ResizeAndTerminate(size_t chars_to_append);
258
259   void HandleStatusWithSize(StatusWithSize written);
260
261   constexpr void NullTerminate() {
262     if (!buffer_.empty()) {
263       buffer_[size_] = '\0';
264     }
265   }
266
267   void SetErrorStatus(Status status);
268
269   const std::span<char> buffer_;
270
271   size_t size_;
272   Status status_;
273   Status last_status_;
274 };
275
276 // StringBuffers declare a buffer along with a StringBuilder. StringBuffer can
277 // be used as a statically allocated replacement for std::ostringstream or
278 // std::string. For example:
279 //
280 //   StringBuffer<32> str;
281 //   str << "The answer is " << number << "!";  // with number = 42
282 //   str.c_str();  // null terminated C string "The answer is 42."
283 //   str.view();   // std::string_view of "The answer is 42."
284 //
285 template <size_t kSizeBytes>
286 class StringBuffer : public StringBuilder {
287  public:
288   StringBuffer() : StringBuilder(buffer_) {}
289
290   // StringBuffers of the same size may be copied and assigned into one another.
291   StringBuffer(const StringBuffer& other) : StringBuilder(buffer_, other) {
292     CopyContents(other);
293   }
294
295   // A smaller StringBuffer may be copied or assigned into a larger one.
296   template <size_t kOtherSizeBytes>
297   StringBuffer(const StringBuffer<kOtherSizeBytes>& other)
298       : StringBuilder(buffer_, other) {
299     static_assert(StringBuffer<kOtherSizeBytes>::max_size() <= max_size(),
300                   "A StringBuffer cannot be copied into a smaller buffer");
301     CopyContents(other);
302   }
303
304   template <size_t kOtherSizeBytes>
305   StringBuffer& operator=(const StringBuffer<kOtherSizeBytes>& other) {
306     assign<kOtherSizeBytes>(other);
307     return *this;
308   }
309
310   StringBuffer& operator=(const StringBuffer& other) {
311     assign<kSizeBytes>(other);
312     return *this;
313   }
314
315   template <size_t kOtherSizeBytes>
316   StringBuffer& assign(const StringBuffer<kOtherSizeBytes>& other) {
317     static_assert(StringBuffer<kOtherSizeBytes>::max_size() <= max_size(),
318                   "A StringBuffer cannot be copied into a smaller buffer");
319     CopySizeAndStatus(other);
320     CopyContents(other);
321     return *this;
322   }
323
324   // Returns the maximum length of the string, excluding the null terminator.
325   static constexpr size_t max_size() { return kSizeBytes - 1; }
326
327   // Returns a StringBuffer<kSizeBytes>& instead of a generic StringBuilder& for
328   // append calls and stream-style operations.
329   template <typename... Args>
330   StringBuffer& append(Args&&... args) {
331     StringBuilder::append(std::forward<Args>(args)...);
332     return *this;
333   }
334
335   template <typename T>
336   StringBuffer& operator<<(T&& value) {
337     static_cast<StringBuilder&>(*this) << std::forward<T>(value);
338     return *this;
339   }
340
341  private:
342   template <size_t kOtherSize>
343   void CopyContents(const StringBuffer<kOtherSize>& other) {
344     std::memcpy(buffer_, other.data(), other.size() + 1);  // include the \0
345   }
346
347   static_assert(kSizeBytes >= 1u, "StringBuffers must be at least 1 byte long");
348   char buffer_[kSizeBytes];
349 };
350
351 namespace string_internal {
352
353 // Internal code for determining the default size of StringBuffers created with
354 // MakeString.
355 //
356 // StringBuffers created with MakeString default to at least 24 bytes. This is
357 // large enough to fit the largest 64-bit integer (20 digits plus a \0), rounded
358 // up to the nearest multiple of 4.
359 inline constexpr size_t kDefaultMinimumStringBufferSize = 24;
360
361 // By default, MakeString uses a buffer size large enough to fit all string
362 // literal arguments. ArgLength uses this value as an estimate of the number of
363 // characters needed to represent a non-string argument.
364 inline constexpr size_t kDefaultArgumentSize = 4;
365
366 // Returns a string literal's length or kDefaultArgumentSize for non-strings.
367 template <typename T>
368 constexpr size_t ArgLength() {
369   using Arg = std::remove_reference_t<T>;
370
371   // If the argument is an array of const char, assume it is a string literal.
372   if constexpr (std::is_array_v<Arg>) {
373     using Element = std::remove_reference_t<decltype(std::declval<Arg>()[0])>;
374
375     if constexpr (std::is_same_v<Element, const char>) {
376       return std::extent_v<Arg> > 0u ? std::extent_v<Arg> - 1 : size_t(0);
377     }
378   }
379
380   return kDefaultArgumentSize;
381 }
382
383 // This function returns the default string buffer size used by MakeString.
384 template <typename... Args>
385 constexpr size_t DefaultStringBufferSize() {
386   return std::max((size_t(1) + ... + ArgLength<Args>()),
387                   kDefaultMinimumStringBufferSize);
388 }
389
390 // Internal version of MakeString with const reference arguments instead of
391 // deduced types, which include the lengths of string literals. Having this
392 // function can reduce code size.
393 template <size_t kBufferSize, typename... Args>
394 auto InitializeStringBuffer(const Args&... args) {
395   return (StringBuffer<kBufferSize>() << ... << args);
396 }
397
398 }  // namespace string_internal
399
400 // Makes a StringBuffer with a string version of a series of values. This is
401 // useful for creating and initializing a StringBuffer or for conveniently
402 // getting a null-terminated string. For example:
403 //
404 //     LOG_INFO("The MAC address is %s", MakeString(mac_address).c_str());
405 //
406 // By default, the buffer size is 24 bytes, large enough to fit any 64-bit
407 // integer. If string literal arguments are provided, the default size will be
408 // large enough to fit them and a null terminator, plus 4 additional bytes for
409 // each argument. To use a fixed buffer size, set the kBufferSize template
410 // argument. For example:
411 //
412 //   // Creates a default-size StringBuffer (10 + 10 + 4 + 1 + 1 = 26 bytes).
413 //   auto sb = MakeString("1234567890", "1234567890", number, "!");
414 //
415 //   // Creates a 32-byte StringBuffer.
416 //   auto sb = MakeString<32>("1234567890", "1234567890", number, "!");
417 //
418 // Keep in mind that each argument to MakeString expands to a function call.
419 // MakeString may increase code size more than an equivalent pw::string::Format
420 // (or std::snprintf) call.
421 template <size_t kBufferSize = 0u, typename... Args>
422 auto MakeString(Args&&... args) {
423   constexpr size_t kSize =
424       kBufferSize == 0u ? string_internal::DefaultStringBufferSize<Args...>()
425                         : kBufferSize;
426   return string_internal::InitializeStringBuffer<kSize>(args...);
427 }
428
429 }  // namespace pw