| // 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 |