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