C/C++ API Reference
Loading...
Searching...
No Matches
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 <string_view>
21#include <type_traits>
22#include <utility>
23
24#include "pw_preprocessor/compiler.h"
25#include "pw_span/span.h"
26#include "pw_status/status.h"
27#include "pw_status/status_with_size.h"
28#include "pw_string/string.h"
29#include "pw_string/to_string.h"
30
31namespace pw {
32
34
87 public:
89 explicit constexpr StringBuilder(span<char> buffer)
90 : buffer_(buffer), size_(&inline_size_), inline_size_(0) {
91 NullTerminate();
92 }
93
94 explicit StringBuilder(span<std::byte> buffer)
96 {reinterpret_cast<char*>(buffer.data()), buffer.size_bytes()}) {}
97
98 explicit constexpr StringBuilder(InlineString<>& string)
99 : buffer_(string.data(), string.max_size() + 1),
100 size_(&string.length_),
101 inline_size_(0) {}
102
105 StringBuilder(const StringBuilder&) = delete;
106
107 StringBuilder& operator=(const StringBuilder&) = delete;
108
110 const char* data() const { return buffer_.data(); }
111
113 const char* c_str() const { return data(); }
114
118 std::string_view view() const { return std::string_view(data(), size()); }
119
122 operator std::string_view() const { return view(); }
123
127 return span(reinterpret_cast<const std::byte*>(buffer_.data()), size());
128 }
129
140 Status status() const { return static_cast<Status::Code>(status_); }
141
144 return StatusWithSize(status(), size());
145 }
146
148 Status last_status() const { return static_cast<Status::Code>(last_status_); }
149
151 bool ok() const { return status().ok(); }
152
154 bool empty() const { return size() == 0u; }
155
157 size_t size() const { return *size_; }
158
160 size_t max_size() const { return buffer_.empty() ? 0u : buffer_.size() - 1; }
161
163 void clear();
164
167 status_ = static_cast<unsigned char>(OkStatus().code());
168 last_status_ = static_cast<unsigned char>(OkStatus().code());
169 }
170
173 void push_back(char ch) { append(1, ch); }
174
177 void pop_back() PW_NO_SANITIZE("unsigned-integer-overflow") {
178 resize(size() - 1);
179 }
180
182 StringBuilder& append(size_t count, char ch);
183
190 StringBuilder& append(const char* str, size_t count);
191
199 StringBuilder& append(const char* str);
200
202 StringBuilder& append(std::string_view str);
203
207 StringBuilder& append(std::string_view str,
208 size_t pos,
209 size_t count = std::string_view::npos);
210
213 template <typename T>
214 StringBuilder& operator<<(const T& value) {
217 if constexpr (std::is_convertible_v<T, std::string_view>) {
218 append(value);
219 } else if constexpr (std::is_convertible_v<T, span<const std::byte>>) {
220 WriteBytes(value);
221 } else {
222 HandleStatusWithSize(ToString(value, buffer_.subspan(size())));
223 }
224 return *this;
225 }
226
229 return append(value ? "true" : "false");
230 }
231
232 StringBuilder& operator<<(char value) {
233 push_back(value);
234 return *this;
235 }
236
237 StringBuilder& operator<<(std::nullptr_t) {
238 return append(string::kNullPointerString);
239 }
240
241 StringBuilder& operator<<(Status status) { return *this << status.str(); }
242
253 PW_PRINTF_FORMAT(2, 3) StringBuilder& Format(const char* format, ...);
254
260 PW_PRINTF_FORMAT(2, 0)
261 StringBuilder& FormatVaList(const char* format, va_list args);
262
265 void resize(size_t new_size);
266
267 protected:
269 constexpr StringBuilder(span<char> buffer, const StringBuilder& other)
270 : buffer_(buffer),
271 size_(&inline_size_),
272 inline_size_(*other.size_),
273 status_(other.status_),
274 last_status_(other.last_status_) {}
275
276 void CopySizeAndStatus(const StringBuilder& other);
277
278 private:
280 static constexpr unsigned char StatusCode(Status status) {
281 return static_cast<unsigned char>(status.code());
282 }
283
284 void WriteBytes(span<const std::byte> data);
285
286 size_t ResizeAndTerminate(size_t chars_to_append);
287
288 void HandleStatusWithSize(StatusWithSize written);
289
290 constexpr void NullTerminate() {
291 if (!buffer_.empty()) {
292 buffer_[size()] = '\0';
293 }
294 }
295
296 void SetErrorStatus(Status status);
297
298 const span<char> buffer_;
299
300 InlineString<>::size_type* size_;
301
302 // Place the `inline_size_`, `status_`, and `last_status_` members together
303 // and use `unsigned char` for the status codes so these members can be
304 // packed into a single word.
305 InlineString<>::size_type inline_size_;
306 unsigned char status_ = StatusCode(OkStatus());
307 unsigned char last_status_ = StatusCode(OkStatus());
308};
309
310// StringBuffer declares a buffer along with a StringBuilder. StringBuffer
311// can be used as a statically allocated replacement for std::ostringstream or
312// std::string. For example:
313//
314// StringBuffer<32> str;
315// str << "The answer is " << number << "!"; // with number = 42
316// str.c_str(); // null terminated C string "The answer is 42."
317// str.view(); // std::string_view of "The answer is 42."
318//
319template <size_t kSizeBytes>
321 public:
322 StringBuffer() : StringBuilder(buffer_) {}
323
324 // StringBuffers of the same size may be copied and assigned into one another.
325 StringBuffer(const StringBuffer& other) : StringBuilder(buffer_, other) {
326 CopyContents(other);
327 }
328
329 // A smaller StringBuffer may be copied or assigned into a larger one.
330 template <size_t kOtherSizeBytes>
332 : StringBuilder(buffer_, other) {
333 static_assert(StringBuffer<kOtherSizeBytes>::max_size() <= max_size(),
334 "A StringBuffer cannot be copied into a smaller buffer");
335 CopyContents(other);
336 }
337
338 template <size_t kOtherSizeBytes>
339 StringBuffer& operator=(const StringBuffer<kOtherSizeBytes>& other) {
340 assign<kOtherSizeBytes>(other);
341 return *this;
342 }
343
344 StringBuffer& operator=(const StringBuffer& other) {
345 assign<kSizeBytes>(other);
346 return *this;
347 }
348
349 template <size_t kOtherSizeBytes>
350 StringBuffer& assign(const StringBuffer<kOtherSizeBytes>& other) {
351 static_assert(StringBuffer<kOtherSizeBytes>::max_size() <= max_size(),
352 "A StringBuffer cannot be copied into a smaller buffer");
353 CopySizeAndStatus(other);
354 CopyContents(other);
355 return *this;
356 }
357
359 StringBuffer(StringBuffer&& other) = delete;
360
363
364 // Returns the maximum length of the string, excluding the null terminator.
365 static constexpr size_t max_size() { return kSizeBytes - 1; }
366
367 // Returns a StringBuffer<kSizeBytes>& instead of a generic StringBuilder& for
368 // append calls and stream-style operations.
369 template <typename... Args>
370 StringBuffer& append(Args&&... args) {
371 StringBuilder::append(std::forward<Args>(args)...);
372 return *this;
373 }
374
375 template <typename T>
376 StringBuffer& operator<<(T&& value) {
377 static_cast<StringBuilder&>(*this) << std::forward<T>(value);
378 return *this;
379 }
380
381 private:
382 template <size_t kOtherSize>
383 void CopyContents(const StringBuffer<kOtherSize>& other) {
384 std::memcpy(buffer_, other.data(), other.size() + 1); // include the \0
385 }
386
387 static_assert(kSizeBytes >= 1u, "StringBuffers must be at least 1 byte long");
388 char buffer_[kSizeBytes];
389};
390
392
393namespace string_internal {
394
395// Internal code for determining the default size of StringBuffers created with
396// MakeString.
397//
398// StringBuffers created with MakeString default to at least 24 bytes. This is
399// large enough to fit the largest 64-bit integer (20 digits plus a \0), rounded
400// up to the nearest multiple of 4.
401inline constexpr size_t kDefaultMinimumStringBufferSize = 24;
402
403// By default, MakeString uses a buffer size large enough to fit all string
404// literal arguments. ArgLength uses this value as an estimate of the number of
405// characters needed to represent a non-string argument.
406inline constexpr size_t kDefaultArgumentSize = 4;
407
408// Returns a string literal's length or kDefaultArgumentSize for non-strings.
409template <typename T>
410constexpr size_t ArgLength() {
411 using Arg = std::remove_reference_t<T>;
412
413 // If the argument is an array of const char, assume it is a string literal.
414 if constexpr (std::is_array_v<Arg>) {
415 using Element = std::remove_reference_t<decltype(std::declval<Arg>()[0])>;
416
417 if constexpr (std::is_same_v<Element, const char>) {
418 return std::extent_v<Arg> > 0u ? std::extent_v<Arg> - 1 : size_t(0);
419 }
420 }
421
422 return kDefaultArgumentSize;
423}
424
425// This function returns the default string buffer size used by MakeString.
426template <typename... Args>
427constexpr size_t DefaultStringBufferSize() {
428 return std::max((size_t(1) + ... + ArgLength<Args>()),
429 kDefaultMinimumStringBufferSize);
430}
431
432// Internal version of MakeString with const reference arguments instead of
433// deduced types, which include the lengths of string literals. Having this
434// function can reduce code size.
435template <size_t kBufferSize, typename... Args>
436auto InitializeStringBuffer(const Args&... args) {
437 return (StringBuffer<kBufferSize>() << ... << args);
438}
439
440} // namespace string_internal
441
443
444// Makes a StringBuffer with a string version of a series of values. This is
445// useful for creating and initializing a StringBuffer or for conveniently
446// getting a null-terminated string. For example:
447//
448// LOG_INFO("The MAC address is %s", MakeString(mac_address).c_str());
449//
450// By default, the buffer size is 24 bytes, large enough to fit any 64-bit
451// integer. If string literal arguments are provided, the default size will be
452// large enough to fit them and a null terminator, plus 4 additional bytes for
453// each argument. To use a fixed buffer size, set the kBufferSize template
454// argument. For example:
455//
456// // Creates a default-size StringBuffer (10 + 10 + 4 + 1 + 1 = 26 bytes).
457// auto sb = MakeString("1234567890", "1234567890", number, "!");
458//
459// // Creates a 32-byte StringBuffer.
460// auto sb = MakeString<32>("1234567890", "1234567890", number, "!");
461//
462// Keep in mind that each argument to MakeString expands to a function call.
463// MakeString may increase code size more than an equivalent pw::string::Format
464// (or std::snprintf) call.
465template <size_t kBufferSize = 0u, typename... Args>
466auto MakeString(Args&&... args) {
467 constexpr size_t kSize =
468 kBufferSize == 0u ? string_internal::DefaultStringBufferSize<Args...>()
469 : kBufferSize;
470 return string_internal::InitializeStringBuffer<kSize>(args...);
471}
472
474
475} // namespace pw
Definition: status.h:120
constexpr Code code() const
Definition: status.h:341
constexpr bool ok() const
Definition: status.h:346
const char * str() const
Definition: status.h:433
Definition: status_with_size.h:51
Definition: string_builder.h:320
StringBuffer(StringBuffer &&other)=delete
StringBuffers are not movable: the underlying data must be copied.
StringBuffer & operator=(StringBuffer &&other)=delete
StringBuffers are not movable: the underlying data must be copied.
Definition: string_builder.h:86
bool empty() const
True if the string is empty.
Definition: string_builder.h:154
size_t size() const
Returns the current length of the string, excluding the null terminator.
Definition: string_builder.h:157
const char * c_str() const
Returns the contents of the string buffer. Always null-terminated.
Definition: string_builder.h:113
const char * data() const
Returns the contents of the string buffer. Always null-terminated.
Definition: string_builder.h:110
void push_back(char ch)
Definition: string_builder.h:173
StringBuilder & operator<<(const T &value)
Definition: string_builder.h:214
Status last_status() const
The status from the last operation. May be OK while status() is not OK.
Definition: string_builder.h:148
void clear_status()
Sets the statuses to OkStatus();.
Definition: string_builder.h:166
StringBuilder & Format(const char *format,...)
void resize(size_t new_size)
Status status() const
Definition: string_builder.h:140
span< const std::byte > as_bytes() const
Definition: string_builder.h:126
StringBuilder & append(size_t count, char ch)
Appends the provided character count times.
void clear()
Clears the string and resets its error state.
StatusWithSize status_with_size() const
Returns status() and size() as a StatusWithSize.
Definition: string_builder.h:143
size_t max_size() const
Returns the maximum length of the string, excluding the null terminator.
Definition: string_builder.h:160
StringBuilder & append(std::string_view str, size_t pos, size_t count=std::string_view::npos)
StringBuilder & FormatVaList(const char *format, va_list args)
void pop_back()
Definition: string_builder.h:177
StringBuilder & operator<<(bool value)
Provide a few additional operator<< overloads that reduce code size.
Definition: string_builder.h:228
constexpr StringBuilder(span< char > buffer)
Creates an empty pw::StringBuilder.
Definition: string_builder.h:89
bool ok() const
True if status() is OkStatus().
Definition: string_builder.h:151
StringBuilder & append(const char *str, size_t count)
StringBuilder(const StringBuilder &)=delete
StringBuilder & append(const char *str)
StringBuilder & append(std::string_view str)
Appends a std::string_view to the end of the StringBuilder.
std::string_view view() const
Definition: string_builder.h:118
Definition: span_impl.h:235
#define PW_PRINTF_FORMAT(format_index, parameter_index)
Definition: compiler.h:89
#define PW_NO_SANITIZE(check)
Definition: compiler.h:158
pw_Status
C API for status codes. In C++, use the pw::Status class instead.
Definition: status.h:40
constexpr Status OkStatus()
Definition: status.h:450
The Pigweed namespace.
Definition: alignment.h:27
pw::InlineBasicString and pw::InlineString are safer alternatives to std::basic_string and std::strin...