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