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 <new>
8 #include <atomic>
9 #include <string.h>
10
11 #include <zircon/assert.h>
12 #include <fbl/algorithm.h>
13
14 namespace fbl {
15 namespace {
16
SumLengths(const String * begin,const String * end,const String ** last_non_empty_string)17 size_t SumLengths(const String* begin, const String* end,
18 const String** last_non_empty_string) {
19 size_t total_length = 0u;
20 for (const String* it = begin; it != end; it++) {
21 if (!it->empty()) {
22 *last_non_empty_string = it;
23 total_length += it->length();
24 }
25 }
26 return total_length;
27 }
28
Concat(char * data,const String * begin,const String * end)29 void Concat(char* data, const String* begin, const String* end) {
30 for (const String* it = begin; it != end; it++) {
31 memcpy(data, it->data(), it->length());
32 data += it->length();
33 }
34 *data = 0;
35 }
36
37 } // namespace
38
39 String::EmptyBuffer String::gEmpty;
40
clear()41 void String::clear() {
42 ReleaseRef(data_);
43 InitWithEmpty();
44 }
45
compare(const String & other) const46 int String::compare(const String& other) const {
47 size_t len = min(length(), other.length());
48 int retval = memcmp(data(), other.data(), len);
49 if (retval == 0) {
50 if (length() == other.length()) {
51 return 0;
52 }
53 return length() < other.length() ? -1 : 1;
54 }
55 return retval;
56 }
57
swap(String & other)58 void String::swap(String& other) {
59 char* temp_data = data_;
60 data_ = other.data_;
61 other.data_ = temp_data;
62 }
63
operator =(const String & other)64 String& String::operator=(const String& other) {
65 AcquireRef(other.data_);
66 ReleaseRef(data_); // release after acquire in case other == *this
67 data_ = other.data_;
68 return *this;
69 }
70
operator =(String && other)71 String& String::operator=(String&& other) {
72 ReleaseRef(data_);
73 data_ = other.data_;
74 other.InitWithEmpty();
75 return *this;
76 }
77
Set(const char * data,size_t length)78 void String::Set(const char* data, size_t length) {
79 char* temp_data = data_;
80 Init(data, length);
81 ReleaseRef(temp_data); // release after init in case data is within data_
82 }
83
Set(const char * data,size_t length,fbl::AllocChecker * ac)84 void String::Set(const char* data, size_t length, fbl::AllocChecker* ac) {
85 char* temp_data = data_;
86 Init(data, length, ac);
87 ReleaseRef(temp_data); // release after init in case data is within data_
88 }
89
Concat(std::initializer_list<String> strings)90 String String::Concat(std::initializer_list<String> strings) {
91 const String* last_non_empty_string = nullptr;
92 size_t total_length = SumLengths(strings.begin(), strings.end(),
93 &last_non_empty_string);
94 if (last_non_empty_string == nullptr) {
95 return String();
96 }
97 if (total_length == last_non_empty_string->length()) {
98 return *last_non_empty_string;
99 }
100
101 char* data = AllocData(total_length);
102
103 fbl::Concat(data, strings.begin(), last_non_empty_string + 1);
104 return String(data, nullptr);
105 }
106
Concat(std::initializer_list<String> strings,AllocChecker * ac)107 String String::Concat(std::initializer_list<String> strings,
108 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
Init(const char * data,size_t length)130 void 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
Init(const char * data,size_t length,AllocChecker * ac)141 void 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
Init(size_t count,char ch)157 void 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
Init(size_t count,char ch,AllocChecker * ac)168 void 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
InitWithEmpty()184 void String::InitWithEmpty() {
185 gEmpty.ref_count.fetch_add(1u, std::memory_order_relaxed);
186 data_ = &gEmpty.nul;
187 }
188
AllocData(size_t length)189 char* String::AllocData(size_t length) {
190 void* buffer = operator new(buffer_size(length));
191 return InitData(buffer, length);
192 }
193
AllocData(size_t length,AllocChecker * ac)194 char* 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
InitData(void * buffer,size_t length)201 char* 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)) std::atomic_uint(1u);
205 return data;
206 }
207
AcquireRef(char * data)208 void String::AcquireRef(char* data) {
209 ref_count_field_of(data)->fetch_add(1u, std::memory_order_relaxed);
210 }
211
ReleaseRef(char * data)212 void String::ReleaseRef(char* data) {
213 unsigned int prior_count = ref_count_field_of(data)->fetch_sub(1u, std::memory_order_release);
214 ZX_DEBUG_ASSERT(prior_count != 0u);
215 if (prior_count == 1u) {
216 atomic_thread_fence(std::memory_order_acquire);
217 operator delete(data - kDataFieldOffset);
218 }
219 }
220
operator ==(const String & lhs,const String & rhs)221 bool 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