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 <atomic>
8 #include <fbl/alloc_checker.h>
9 #include <fbl/string_piece.h>
10 #include <fbl/string_traits.h>
11 #include <initializer_list>
12 #include <type_traits>
13 #include <zircon/compiler.h>
14 
15 namespace fbl {
16 namespace tests {
17 struct StringTestHelper;
18 } // namespace tests
19 
20 // A string with immutable contents.
21 //
22 // fbl::String is designed to resemble std::string except that its content
23 // is immutable.  This makes it easy to share string buffers so that copying
24 // strings does not incur any allocation cost.
25 //
26 // Empty string objects do not incur any allocation.  Non-empty strings are
27 // stored on the heap.  Note that fbl::String does not have a null state
28 // distinct from the empty state.
29 //
30 // The content of a fbl::String object is always stored with a null terminator
31 // so that |c_str()| is fast.  However, be aware that the string may also contain
32 // embedded null characters (this is not checked by the implementation).
33 class String {
34 public:
35     // Creates an empty string.
36     // Does not allocate heap memory.
String()37     String() { InitWithEmpty(); }
38 
39     // Creates a copy of another string.
40     // Does not allocate heap memory.
String(const String & other)41     String(const String& other)
42         : data_(other.data_) {
43         AcquireRef(data_);
44     }
45 
46     // Move constructs from another string.
47     // The other string is set to empty.
48     // Does not allocate heap memory.
String(String && other)49     String(String&& other)
50         : data_(other.data_) {
51         other.InitWithEmpty();
52     }
53 
54     // Creates a string from the contents of a null-terminated C string.
55     // Allocates heap memory only if |data| is non-empty.
56     // |data| must not be null.
String(const char * data)57     String(const char* data) {
58         Init(data, constexpr_strlen(data));
59     }
60 
61     // Creates a string from the contents of a null-terminated C string.
62     // Allocates heap memory only if |data| is non-empty.
63     // |data| and |ac| must not be null.
String(const char * data,AllocChecker * ac)64     String(const char* data, AllocChecker* ac) {
65         Init(data, constexpr_strlen(data), ac);
66     }
67 
68     // Creates a string from the contents of a character array of given length.
69     // Allocates heap memory only if |length| is non-zero.
70     // |data| must not be null.
String(const char * data,size_t length)71     String(const char* data, size_t length) {
72         Init(data, length);
73     }
74 
75     // Creates a string from the contents of a character array of given length.
76     // Allocates heap memory only if |length| is non-zero.
77     // |data| and |ac| must not be null.
String(const char * data,size_t length,AllocChecker * ac)78     String(const char* data, size_t length, AllocChecker* ac) {
79         Init(data, length, ac);
80     }
81 
82     // Creates a string with |count| copies of |ch|.
83     // Allocates heap memory only if |count| is non-zero.
String(size_t count,char ch)84     String(size_t count, char ch) {
85         Init(count, ch);
86     }
87 
88     // Creates a string with |count| copies of |ch|.
89     // Allocates heap memory only if |count| is non-zero.
90     // |ac| must not be null.
String(size_t count,char ch,AllocChecker * ac)91     String(size_t count, char ch, AllocChecker* ac) {
92         Init(count, ch, ac);
93     }
94 
95     // Creates a string from the contents of a string piece.
96     // Allocates heap memory only if |piece.length()| is non-zero.
String(const StringPiece & piece)97     String(const StringPiece& piece)
98         : String(piece.data(), piece.length()) {}
99 
100     // Creates a string from the contents of a string piece.
101     // Allocates heap memory only if |piece.length()| is non-zero.
102     // |ac| must not be null.
String(const StringPiece & piece,AllocChecker * ac)103     String(const StringPiece& piece, AllocChecker* ac)
104         : String(piece.data(), piece.length(), ac) {}
105 
106     // Creates a string from a string-like object.
107     // Allocates heap memory only if the length of |value| is non-zero.
108     //
109     // Works with various string types including fbl::String, fbl::StringView,
110     // std::string, and std::string_view.
111     template <typename T, typename = typename std::enable_if<is_string_like<T>::value>::type>
String(const T & value)112     constexpr String(const T& value)
113         : String(GetStringData(value), GetStringLength(value)) {}
114 
115     // Destroys the string.
~String()116     ~String() { ReleaseRef(data_); }
117 
118     // Returns a pointer to the null-terminated contents of the string.
data()119     const char* data() const { return data_; }
c_str()120     const char* c_str() const { return data(); }
121 
122     // Returns the length of the string, excluding its null terminator.
length()123     size_t length() const { return *length_field_of(data_); }
size()124     size_t size() const { return length(); }
125 
126     // Returns true if the string's length is zero.
empty()127     bool empty() const { return length() == 0u; }
128 
129     // Character iterators, excluding the null terminator.
begin()130     const char* begin() const { return data(); }
cbegin()131     const char* cbegin() const { return data(); }
end()132     const char* end() const { return data() + length(); }
cend()133     const char* cend() const { return data() + length(); }
134 
135     // Gets the character at the specified index.
136     // Position must be greater than or equal to 0 and less than |length()|.
137     const char& operator[](size_t pos) const { return data()[pos]; }
138 
139     // Performs a lexicographical character by character comparison.
140     // Returns a negative value if |*this| comes before |other| in lexicographical order.
141     // Returns zero if the strings are equivalent.
142     // Returns a positive value if |*this| comes after |other| in lexicographical order.
143     int compare(const String& other) const;
144 
145     // Sets this string to empty.
146     // Does not allocate heap memory.
147     void clear();
148 
149     // Swaps the contents of this string with another string.
150     // Does not allocate heap memory.
151     void swap(String& other);
152 
153     // Assigns this string to a copy of another string.
154     // Does not allocate heap memory.
155     String& operator=(const String& other);
156 
157     // Move assigns from another string.
158     // The other string is set to empty.
159     // Does not allocate heap memory.
160     String& operator=(String&& other);
161 
162     // Assigns this string from the contents of a null-terminated C string.
163     // Allocates heap memory only if |data| is non-empty.
164     // |data| must not be null.
165     String& operator=(const char* data) {
166         Set(data);
167         return *this;
168     }
169 
170     // Assigns this string from the contents of a string piece.
171     // Allocates heap memory only if |piece.length()| is non-zero.
172     String& operator=(const StringPiece& piece) {
173         Set(piece);
174         return *this;
175     }
176 
177     // Assigns this string from the contents of a string-like object.
178     // Allocates heap memory only if the length of |value| is non-zero.
179     //
180     // Works with various string types including fbl::String, fbl::StringView,
181     // std::string, and std::string_view.
182     template <typename T, typename = typename std::enable_if<is_string_like<T>::value>::type>
183     String& operator=(const T& value) {
184         Set(GetStringData(value), GetStringLength(value));
185         return *this;
186     }
187 
188     // Assigns this string from the contents of a null-terminated C string.
189     // Allocates heap memory only if |data| is non-empty.
190     // |data| must not be null.
Set(const char * data)191     void Set(const char* data) {
192         Set(data, constexpr_strlen(data));
193     }
194 
195     // Assigns this string from the contents of a null-terminated C string.
196     // Allocates heap memory only if |data| is non-empty.
197     // |data| and |ac| must not be null.
Set(const char * data,AllocChecker * ac)198     void Set(const char* data, AllocChecker* ac) {
199         Set(data, constexpr_strlen(data), ac);
200     }
201 
202     // Assigns this string from the contents of a character array of given length.
203     // Allocates heap memory only if |length| is non-zero.
204     // |data| must not be null.
205     void Set(const char* data, size_t length);
206 
207     // Assigns this string from the contents of a character array of given length.
208     // Allocates heap memory only if |length| is non-zero.
209     // |data| and |ac| must not be null.
210     void Set(const char* data, size_t length, AllocChecker* ac);
211 
212     // Assigns this string with |count| copies of |ch|.
213     // Allocates heap memory only if |count| is non-zero.
Set(size_t count,char ch)214     void Set(size_t count, char ch) {
215         ReleaseRef(data_);
216         Init(count, ch);
217     }
218 
219     // Assigns this string with |count| copies of |ch|.
220     // Allocates heap memory only if |count| is non-zero.
221     // |ac| must not be null.
Set(size_t count,char ch,AllocChecker * ac)222     void Set(size_t count, char ch, AllocChecker* ac) {
223         ReleaseRef(data_);
224         Init(count, ch, ac);
225     }
226 
227     // Assigns this string from the contents of a string piece.
228     // Allocates heap memory only if |piece.length()| is non-zero.
Set(const StringPiece & piece)229     void Set(const StringPiece& piece) {
230         Set(piece.data(), piece.length());
231     }
232 
233     // Assigns this string from the contents of a string piece.
234     // Allocates heap memory only if |piece.length()| is non-zero.
235     // |ac| must not be null.
Set(const StringPiece & piece,AllocChecker * ac)236     void Set(const StringPiece& piece, AllocChecker* ac) {
237         Set(piece.data(), piece.length(), ac);
238     }
239 
240     // Creates a string piece backed by the string.
241     // The string piece does not take ownership of the data so the string
242     // must outlast the string piece.
ToStringPiece()243     StringPiece ToStringPiece() const {
244         return StringPiece(data(), length());
245     }
246 
247     // Concatenates the specified strings.
248     static String Concat(std::initializer_list<String> strings);
249 
250     // Concatenates the specified strings.
251     // |ac| must not be null.
252     static String Concat(std::initializer_list<String> strings,
253                          AllocChecker* ac);
254 
255 private:
256     friend struct fbl::tests::StringTestHelper;
257 
String(char * data,decltype (nullptr))258     explicit String(char* data, decltype(nullptr) /*overload disambiguation*/)
259         : data_(data) {}
260 
261     // A string buffer consists of a length followed by a reference count
262     // followed by a null-terminated string.  To make access faster, we offset
263     // the |data_| pointer to point at the first byte of the content instead of
264     // at the beginning of the string buffer itself.
265     static constexpr size_t kLengthFieldOffset = 0u;
266     static constexpr size_t kRefCountFieldOffset = sizeof(size_t);
267     static constexpr size_t kDataFieldOffset = sizeof(size_t) + sizeof(std::atomic_uint);
268 
length_field_of(char * data)269     static size_t* length_field_of(char* data) {
270         return reinterpret_cast<size_t*>(data - kDataFieldOffset + kLengthFieldOffset);
271     }
ref_count_field_of(char * data)272     static std::atomic_uint* ref_count_field_of(char* data) {
273         return reinterpret_cast<std::atomic_uint*>(data - kDataFieldOffset + kRefCountFieldOffset);
274     }
buffer_size(size_t length)275     static constexpr size_t buffer_size(size_t length) {
276         return kDataFieldOffset + length + 1u;
277     }
278 
279     // For use by test code only.
ref_count()280     unsigned int ref_count() const {
281         return ref_count_field_of(data_)->load(std::memory_order_relaxed);
282     }
283 
284     // Storage for an empty string.
285     struct EmptyBuffer {
286         size_t length{0u};
287         std::atomic_uint ref_count{1u};
288         char nul{0};
289     };
290     static_assert(offsetof(EmptyBuffer, length) == kLengthFieldOffset, "");
291     static_assert(offsetof(EmptyBuffer, ref_count) == kRefCountFieldOffset, "");
292     static_assert(offsetof(EmptyBuffer, nul) == kDataFieldOffset, "");
293 
294     static EmptyBuffer gEmpty;
295 
296     void Init(const char* data, size_t length);
297     void Init(const char* data, size_t length, AllocChecker* ac);
298     void Init(size_t count, char ch);
299     void Init(size_t count, char ch, AllocChecker* ac);
300     void InitWithEmpty();
301 
302     static char* AllocData(size_t length);
303     static char* AllocData(size_t length, AllocChecker* ac);
304     static char* InitData(void* buffer, size_t length);
305     static void AcquireRef(char* data);
306     static void ReleaseRef(char* data);
307 
308     char* data_;
309 };
310 
311 bool operator==(const String& lhs, const String& rhs);
312 
313 inline bool operator!=(const String& lhs, const String& rhs) {
314     return !(lhs == rhs);
315 }
316 
317 inline bool operator<(const String& lhs, const String& rhs) {
318     return lhs.compare(rhs) < 0;
319 }
320 
321 inline bool operator>(const String& lhs, const String& rhs) {
322     return lhs.compare(rhs) > 0;
323 }
324 
325 inline bool operator<=(const String& lhs, const String& rhs) {
326     return lhs.compare(rhs) <= 0;
327 }
328 
329 inline bool operator>=(const String& lhs, const String& rhs) {
330     return lhs.compare(rhs) >= 0;
331 }
332 
333 } // namespace fbl
334