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#include <fbl/string.h> 6 7#include <string.h> 8 9#include <zircon/assert.h> 10 11#include <fbl/algorithm.h> 12#include <fbl/atomic.h> 13#include <fbl/new.h> 14 15namespace fbl { 16namespace { 17 18size_t SumLengths(const String* begin, const String* end, 19 const String** last_non_empty_string) { 20 size_t total_length = 0u; 21 for (const String* it = begin; it != end; it++) { 22 if (!it->empty()) { 23 *last_non_empty_string = it; 24 total_length += it->length(); 25 } 26 } 27 return total_length; 28} 29 30void Concat(char* data, const String* begin, const String* end) { 31 for (const String* it = begin; it != end; it++) { 32 memcpy(data, it->data(), it->length()); 33 data += it->length(); 34 } 35 *data = 0; 36} 37 38} // namespace 39 40String::EmptyBuffer String::gEmpty; 41 42void String::clear() { 43 ReleaseRef(data_); 44 InitWithEmpty(); 45} 46 47int String::compare(const String& other) const { 48 size_t len = min(length(), other.length()); 49 int retval = memcmp(data(), other.data(), len); 50 if (retval == 0) { 51 if (length() == other.length()) { 52 return 0; 53 } 54 return length() < other.length() ? -1 : 1; 55 } 56 return retval; 57} 58 59void String::swap(String& other) { 60 char* temp_data = data_; 61 data_ = other.data_; 62 other.data_ = temp_data; 63} 64 65String& String::operator=(const String& other) { 66 AcquireRef(other.data_); 67 ReleaseRef(data_); // release after acquire in case other == *this 68 data_ = other.data_; 69 return *this; 70} 71 72String& String::operator=(String&& other) { 73 ReleaseRef(data_); 74 data_ = other.data_; 75 other.InitWithEmpty(); 76 return *this; 77} 78 79void String::Set(const char* data, size_t length) { 80 char* temp_data = data_; 81 Init(data, length); 82 ReleaseRef(temp_data); // release after init in case data is within data_ 83} 84 85void String::Set(const char* data, size_t length, fbl::AllocChecker* ac) { 86 char* temp_data = data_; 87 Init(data, length, ac); 88 ReleaseRef(temp_data); // release after init in case data is within data_ 89} 90 91String String::Concat(initializer_list<String> strings) { 92 const String* last_non_empty_string = nullptr; 93 size_t total_length = SumLengths(strings.begin(), strings.end(), 94 &last_non_empty_string); 95 if (last_non_empty_string == nullptr) { 96 return String(); 97 } 98 if (total_length == last_non_empty_string->length()) { 99 return *last_non_empty_string; 100 } 101 102 char* data = AllocData(total_length); 103 104 fbl::Concat(data, strings.begin(), last_non_empty_string + 1); 105 return String(data, nullptr); 106} 107 108String String::Concat(initializer_list<String> strings, AllocChecker* ac) { 109 const String* last_non_empty_string = nullptr; 110 size_t total_length = SumLengths(strings.begin(), strings.end(), 111 &last_non_empty_string); 112 if (last_non_empty_string == nullptr) { 113 ac->arm(0u, true); 114 return String(); 115 } 116 if (total_length == last_non_empty_string->length()) { 117 ac->arm(0u, true); 118 return *last_non_empty_string; 119 } 120 121 char* data = AllocData(total_length, ac); 122 if (!data) { 123 return String(); 124 } 125 126 fbl::Concat(data, strings.begin(), last_non_empty_string + 1); 127 return String(data, nullptr); 128} 129 130void String::Init(const char* data, size_t length) { 131 if (length == 0u) { 132 InitWithEmpty(); 133 return; 134 } 135 136 data_ = AllocData(length); 137 memcpy(data_, data, length); 138 data_[length] = 0u; 139} 140 141void String::Init(const char* data, size_t length, AllocChecker* ac) { 142 if (length == 0u) { 143 ac->arm(0u, true); 144 InitWithEmpty(); 145 return; 146 } 147 148 data_ = AllocData(length, ac); 149 if (!data_) { 150 InitWithEmpty(); 151 return; 152 } 153 memcpy(data_, data, length); 154 data_[length] = 0u; 155} 156 157void String::Init(size_t count, char ch) { 158 if (count == 0u) { 159 InitWithEmpty(); 160 return; 161 } 162 163 data_ = AllocData(count); 164 memset(data_, ch, count); 165 data_[count] = 0u; 166} 167 168void String::Init(size_t count, char ch, AllocChecker* ac) { 169 if (count == 0u) { 170 ac->arm(0u, true); 171 InitWithEmpty(); 172 return; 173 } 174 175 data_ = AllocData(count, ac); 176 if (!data_) { 177 InitWithEmpty(); 178 return; 179 } 180 memset(data_, ch, count); 181 data_[count] = 0u; 182} 183 184void String::InitWithEmpty() { 185 gEmpty.ref_count.fetch_add(1u, memory_order_relaxed); 186 data_ = &gEmpty.nul; 187} 188 189char* String::AllocData(size_t length) { 190 void* buffer = operator new(buffer_size(length)); 191 return InitData(buffer, length); 192} 193 194char* String::AllocData(size_t length, AllocChecker* ac) { 195 void* buffer = operator new(buffer_size(length), ac); 196 if (!buffer) 197 return nullptr; 198 return InitData(buffer, length); 199} 200 201char* String::InitData(void* buffer, size_t length) { 202 char* data = static_cast<char*>(buffer) + kDataFieldOffset; 203 *length_field_of(data) = length; 204 new (ref_count_field_of(data)) atomic_uint(1u); 205 return data; 206} 207 208void String::AcquireRef(char* data) { 209 ref_count_field_of(data)->fetch_add(1u, memory_order_relaxed); 210} 211 212void String::ReleaseRef(char* data) { 213 unsigned int prior_count = ref_count_field_of(data)->fetch_sub(1u, memory_order_release); 214 ZX_DEBUG_ASSERT(prior_count != 0u); 215 if (prior_count == 1u) { 216 atomic_thread_fence(memory_order_acquire); 217 operator delete(data - kDataFieldOffset); 218 } 219} 220 221bool operator==(const String& lhs, const String& rhs) { 222 return lhs.length() == rhs.length() && 223 memcmp(lhs.data(), rhs.data(), lhs.length()) == 0; 224} 225 226} // namespace fbl 227