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