| // Copyright 2023 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 <efi/string/string.h> |
| #include <src/lib/utf_conversion/utf_conversion.h> |
| |
| namespace efi { |
| |
| namespace { |
| |
| // Creates a duplicate fbl::Vector(). |
| template <typename T> |
| fbl::Vector<T> Copy(const fbl::Vector<T>& source) { |
| fbl::Vector<T> copy; |
| copy.resize(source.size()); |
| std::copy(source.begin(), source.end(), copy.begin()); |
| return copy; |
| } |
| |
| zx_status_t ConvertHelper(const char* source, size_t source_length, char16_t* dest, |
| size_t* dest_length) { |
| return utf8_to_utf16(reinterpret_cast<const uint8_t*>(source), source_length, |
| reinterpret_cast<uint16_t*>(dest), dest_length); |
| } |
| |
| zx_status_t ConvertHelper(const char16_t* source, size_t source_length, char* dest, |
| size_t* dest_length) { |
| return utf16_to_utf8(reinterpret_cast<const uint16_t*>(source), source_length, |
| reinterpret_cast<uint8_t*>(dest), dest_length); |
| } |
| |
| enum class Term { kOmit, kAdd }; |
| |
| // Converts between UTF-8 and UTF-16 while allocating an appropriately-sized vector. |
| // |
| // If |term| is set to kAdd, this allocates an additional terminator character when resizing the |
| // destination vector so the caller doesn't need to reallocate again. |
| template <typename From, typename To> |
| bool Convert(const From* source, size_t source_length, fbl::Vector<To>& dest, |
| Term term = Term::kOmit) { |
| // Dry-run convert to get required length for dest buffer. |
| size_t dest_length = 0; |
| zx_status_t res = ConvertHelper(source, source_length, nullptr, &dest_length); |
| if (res != ZX_OK) { |
| return false; |
| } |
| |
| // Resize the dest buffer and convert again. |
| if (term == Term::kOmit) { |
| dest.resize(dest_length); |
| } else { |
| dest.resize(dest_length + 1); |
| dest[dest_length] = 0; |
| } |
| res = ConvertHelper(source, source_length, dest.begin(), &dest_length); |
| if (res != ZX_OK) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Wraps Convert(), but on failure, resets both vectors to empty. |
| template <typename From, typename To> |
| void ConvertOrErase(fbl::Vector<From>& source, fbl::Vector<To>& dest) { |
| if (!Convert(source.data(), source.size(), dest)) { |
| source.reset(); |
| dest.reset(); |
| } |
| } |
| |
| } // namespace |
| |
| String::String(std::string_view contents) { |
| // Copy locally first so we can append the null terminator. |
| utf8_.resize(contents.size() + 1); |
| std::copy(contents.begin(), contents.end(), utf8_.begin()); |
| utf8_[contents.size()] = '\0'; |
| |
| ConvertOrErase(utf8_, ucs2_); |
| } |
| |
| String::String(std::u16string_view contents) { |
| ucs2_.resize(contents.size() + 1); |
| std::copy(contents.begin(), contents.end(), ucs2_.begin()); |
| ucs2_[contents.size()] = u'\0'; |
| |
| ConvertOrErase(ucs2_, utf8_); |
| } |
| |
| String::String(fbl::Vector<char>&& contents) : utf8_(std::move(contents)) { |
| ConvertOrErase(utf8_, ucs2_); |
| } |
| |
| String::String(fbl::Vector<char16_t>&& contents) : ucs2_(std::move(contents)) { |
| ConvertOrErase(ucs2_, utf8_); |
| } |
| |
| String::String(const String& other) : utf8_(Copy(other.utf8_)), ucs2_(Copy(other.ucs2_)) {} |
| |
| String& String::operator=(const String& other) { |
| utf8_ = Copy(other.utf8_); |
| ucs2_ = Copy(other.ucs2_); |
| return *this; |
| } |
| |
| fbl::Vector<char> String::ToUtf8(std::u16string_view contents) { |
| fbl::Vector<char> result; |
| Convert(contents.data(), contents.size(), result, Term::kAdd); |
| return result; |
| } |
| |
| fbl::Vector<char16_t> String::ToUtf16(std::string_view contents) { |
| fbl::Vector<char16_t> result; |
| Convert(contents.data(), contents.size(), result, Term::kAdd); |
| return result; |
| } |
| |
| std::tuple<fbl::Vector<char>, fbl::Vector<char16_t>> String::TakeData() { |
| auto ret = std::make_tuple(std::move(utf8_), std::move(ucs2_)); |
| return ret; |
| } |
| |
| const char* String::c_str() const { return utf8_.data(); } |
| |
| } // namespace efi |