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