blob: 03d4a36cb96f3051574f54e386be5e5afcfe0c52 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <string.h>
#include <zircon/assert.h>
#include <atomic>
#include <new>
#include <fbl/algorithm.h>
#include <fbl/string.h>
namespace fbl {
namespace {
size_t SumLengths(const String* begin, const String* end, const String** last_non_empty_string) {
size_t total_length = 0U;
for (const String* it = begin; it != end; it++) {
if (!it->empty()) {
*last_non_empty_string = it;
total_length += it->length();
}
}
return total_length;
}
void Concat(char* data, const String* begin, const String* end) {
for (const String* it = begin; it != end; it++) {
memcpy(data, it->data(), it->length());
data += it->length();
}
*data = 0;
}
} // namespace
String::EmptyBuffer String::gEmpty;
void String::clear() {
ReleaseRef(data_);
InitWithEmpty();
}
int String::compare(const String& other) const {
size_t len = std::min(length(), other.length());
int retval = memcmp(data(), other.data(), len);
if (retval == 0) {
if (length() == other.length()) {
return 0;
}
return length() < other.length() ? -1 : 1;
}
return retval;
}
void String::swap(String& other) {
char* temp_data = data_;
data_ = other.data_;
other.data_ = temp_data;
}
String& String::operator=(const String& other) {
AcquireRef(other.data_);
ReleaseRef(data_); // release after acquire in case other == *this
data_ = other.data_;
return *this;
}
String& String::operator=(String&& other) noexcept {
ReleaseRef(data_);
data_ = other.data_;
other.InitWithEmpty();
return *this;
}
void String::Set(const char* data, size_t length) {
char* temp_data = data_;
Init(data, length);
ReleaseRef(temp_data); // release after init in case data is within data_
}
String String::Concat(std::initializer_list<String> strings) {
const String* last_non_empty_string = nullptr;
size_t total_length = SumLengths(strings.begin(), strings.end(), &last_non_empty_string);
if (last_non_empty_string == nullptr) {
return String();
}
if (total_length == last_non_empty_string->length()) {
return *last_non_empty_string;
}
char* data = AllocData(total_length);
fbl::Concat(data, strings.begin(), last_non_empty_string + 1);
return String(data, nullptr);
}
void String::Init(const char* data, size_t length) {
if (length == 0U) {
InitWithEmpty();
return;
}
data_ = AllocData(length);
memcpy(data_, data, length);
data_[length] = 0U;
}
void String::Init(size_t count, char ch) {
if (count == 0U) {
InitWithEmpty();
return;
}
data_ = AllocData(count);
memset(data_, ch, count);
data_[count] = 0U;
}
void String::InitWithEmpty() {
gEmpty.ref_count.fetch_add(1U, std::memory_order_relaxed);
data_ = &gEmpty.nul;
}
char* String::AllocData(size_t length) {
void* buffer = operator new(buffer_size(length));
return InitData(buffer, length);
}
char* String::InitData(void* buffer, size_t length) {
char* data = static_cast<char*>(buffer) + kDataFieldOffset;
*length_field_of(data) = length;
new (ref_count_field_of(data)) std::atomic_uint(1U);
return data;
}
void String::AcquireRef(char* data) {
ref_count_field_of(data)->fetch_add(1U, std::memory_order_relaxed);
}
void String::ReleaseRef(char* data) {
unsigned int prior_count = ref_count_field_of(data)->fetch_sub(1U, std::memory_order_release);
ZX_DEBUG_ASSERT(prior_count != 0U);
if (prior_count == 1U) {
atomic_thread_fence(std::memory_order_acquire);
operator delete(data - kDataFieldOffset);
}
}
bool operator==(const String& lhs, const String& rhs) {
return lhs.length() == rhs.length() && memcmp(lhs.data(), rhs.data(), lhs.length()) == 0;
}
} // namespace fbl