1 // Copyright 2017 The Fuchsia Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #pragma once 6 7 #include "string.h" 8 #include <stdarg.h> 9 10 #include <fbl/string.h> 11 #include <fbl/string_piece.h> 12 #include <zircon/assert.h> 13 #include <zircon/compiler.h> 14 15 namespace fbl { 16 namespace internal { 17 size_t StringBufferAppendPrintf(char* dest, size_t remaining, 18 const char* format, va_list ap); 19 } // namespace internal 20 21 // A fixed-size buffer for assembling a string. 22 // 23 // fbl::StringBuffer is designed to resemble std::string except that it 24 // does not allocate heap storage. 25 // 26 // The buffer is sized to hold up to N characters plus a null-terminator. 27 template <size_t N> 28 class StringBuffer final { 29 public: 30 // Creates an empty string buffer. StringBuffer()31 StringBuffer() 32 : length_(0u) { 33 data_[0] = 0; 34 } 35 36 // Releases the string buffer. 37 ~StringBuffer() = default; 38 39 // Returns a pointer to the null-terminated contents of the string. data()40 char* data() { return data_; } data()41 const char* data() const { return data_; } c_str()42 const char* c_str() const { return data_; } 43 44 // Returns the length of the string, excluding its null terminator. length()45 size_t length() const { return length_; } size()46 size_t size() const { return length_; } 47 48 // Returns the length of the string, excluding its null terminator. empty()49 bool empty() const { return length_ == 0u; } 50 51 // Returns the capacity of the buffer. capacity()52 constexpr size_t capacity() const { return N; } 53 54 // Character iterators, excluding the null terminator. begin()55 char* begin() { return data(); } begin()56 const char* begin() const { return data(); } cbegin()57 const char* cbegin() const { return data(); } end()58 char* end() { return data() + length(); } end()59 const char* end() const { return data() + length(); } cend()60 const char* cend() const { return data() + length(); } 61 62 // Gets a reference to the character at the specified index. 63 // Position must be greater than or equal to 0 and less than |length()|. 64 char& operator[](size_t pos) { return data_[pos]; } 65 const char& operator[](size_t pos) const { return data_[pos]; } 66 67 // Clears the string buffer. Clear()68 void Clear() { 69 length_ = 0u; 70 data_[0] = 0; 71 } 72 73 // Resizes the string buffer. 74 // If the current length is less than |count|, additional characters are appended 75 // with the value |ch|. 76 // If the current length is greater than |count|, the string is truncated. 77 // |length| must be less than or equal to |N|. 78 void Resize(size_t count, char ch = '\0') { 79 ZX_DEBUG_ASSERT(count <= N); 80 if (length_ < count) 81 memset(data_ + length_, ch, count - length_); 82 length_ = count; 83 data_[length_] = 0; 84 } 85 86 // Appends a single character. 87 // The result is truncated if the appended content does not fit completely. Append(char ch)88 StringBuffer& Append(char ch) { 89 if (length_ < N) { 90 data_[length_++] = ch; 91 data_[length_] = 0; 92 } 93 return *this; 94 } 95 96 // Appends content to the string buffer from a null-terminated C string. 97 // The result is truncated if the appended content does not fit completely. 98 // |data| must not be null. Append(const char * data)99 StringBuffer& Append(const char* data) { 100 Append(data, constexpr_strlen(data)); 101 return *this; 102 } 103 104 // Appends content to the string buffer from a character array of given length. 105 // The result is truncated if the appended content does not fit completely. 106 // |data| must not be null. Append(const char * data,size_t length)107 StringBuffer& Append(const char* data, size_t length) { 108 AppendInternal(data, length); 109 return *this; 110 } 111 112 // Appends content to the string buffer from a string piece. 113 // The result is truncated if the appended content does not fit completely. Append(const fbl::StringPiece & piece)114 StringBuffer& Append(const fbl::StringPiece& piece) { 115 AppendInternal(piece.data(), piece.length()); 116 return *this; 117 } 118 119 // Appends content to the string buffer from another string. 120 // The result is truncated if the appended content does not fit completely. Append(const fbl::String & other)121 StringBuffer& Append(const fbl::String& other) { 122 AppendInternal(other.data(), other.length()); 123 return *this; 124 } 125 126 // Appends |printf()|-like input. 127 // The result is truncated if the appended content does not fit completely. AppendPrintf(const char * format,...)128 StringBuffer& AppendPrintf(const char* format, ...) __PRINTFLIKE(2, 3) { 129 va_list ap; 130 va_start(ap, format); 131 AppendVPrintf(format, ap); 132 va_end(ap); 133 return *this; 134 } 135 136 // Appends |vprintf()|-like input using a |va_list|. 137 // The result is truncated if the appended content does not fit completely. AppendVPrintf(const char * format,va_list ap)138 StringBuffer& AppendVPrintf(const char* format, va_list ap) { 139 length_ += internal::StringBufferAppendPrintf( 140 data_ + length_, N - length_, format, ap); 141 return *this; 142 } 143 144 // Gets the buffer's contents as a string. ToString()145 fbl::String ToString() const { 146 return fbl::String(data(), length()); 147 } 148 149 // Gets the buffer's contents as a string piece. ToStringPiece()150 fbl::StringPiece ToStringPiece() const { 151 return fbl::StringPiece(data(), length()); 152 } 153 154 private: AppendInternal(const char * data,size_t length)155 void AppendInternal(const char* data, size_t length) { 156 size_t remaining = N - length_; 157 if (length > remaining) 158 length = remaining; 159 memcpy(data_ + length_, data, length); 160 length_ += length; 161 data_[length_] = 0; 162 } 163 164 size_t length_ = 0u; 165 char data_[N + 1u]; 166 }; 167 168 } // namespace fbl 169