| // Copyright 2013 The Flutter 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_utils.h" | |
| #include <array> | |
| #include <cctype> | |
| #include <codecvt> | |
| #include <locale> | |
| #include <regex> | |
| #include <sstream> | |
| #include "flutter/fml/string_conversion.h" | |
| #include "third_party/dart/runtime/third_party/double-conversion/src/double-conversion.h" | |
| #include "no_destructor.h" | |
| namespace base { | |
| using double_conversion::DoubleToStringConverter; | |
| using double_conversion::StringBuilder; | |
| namespace { | |
| constexpr char kExponentChar = 'e'; | |
| constexpr char kInfinitySymbol[] = "Infinity"; | |
| constexpr char kNaNSymbol[] = "NaN"; | |
| // The number of digits after the decimal we allow before switching to | |
| // exponential representation. | |
| constexpr int kDecimalInShortestLow = -6; | |
| // The number of digits before the decimal we allow before switching to | |
| // exponential representation. | |
| constexpr int kDecimalInShortestHigh = 12; | |
| constexpr int kConversionFlags = | |
| DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN; | |
| const DoubleToStringConverter& GetDoubleToStringConverter() { | |
| static DoubleToStringConverter converter( | |
| kConversionFlags, kInfinitySymbol, kNaNSymbol, kExponentChar, | |
| kDecimalInShortestLow, kDecimalInShortestHigh, 0, 0); | |
| return converter; | |
| } | |
| std::string NumberToStringImpl(double number, bool is_single_precision) { | |
| if (number == 0.0) { | |
| return "0"; | |
| } | |
| constexpr int kBufferSize = 128; | |
| std::array<char, kBufferSize> char_buffer; | |
| StringBuilder builder(char_buffer.data(), char_buffer.size()); | |
| if (is_single_precision) { | |
| GetDoubleToStringConverter().ToShortestSingle(static_cast<float>(number), | |
| &builder); | |
| } else { | |
| GetDoubleToStringConverter().ToShortest(number, &builder); | |
| } | |
| return std::string(char_buffer.data(), builder.position()); | |
| } | |
| } // namespace | |
| std::u16string ASCIIToUTF16(std::string src) { | |
| return std::u16string(src.begin(), src.end()); | |
| } | |
| std::u16string UTF8ToUTF16(std::string src) { | |
| return fml::Utf8ToUtf16(src); | |
| } | |
| std::string UTF16ToUTF8(std::u16string src) { | |
| return fml::Utf16ToUtf8(src); | |
| } | |
| std::u16string WideToUTF16(const std::wstring& src) { | |
| return std::u16string(src.begin(), src.end()); | |
| } | |
| std::wstring UTF16ToWide(const std::u16string& src) { | |
| return std::wstring(src.begin(), src.end()); | |
| } | |
| std::u16string NumberToString16(float number) { | |
| return ASCIIToUTF16(NumberToString(number)); | |
| } | |
| std::u16string NumberToString16(int32_t number) { | |
| return ASCIIToUTF16(NumberToString(number)); | |
| } | |
| std::u16string NumberToString16(unsigned int number) { | |
| return ASCIIToUTF16(NumberToString(number)); | |
| } | |
| std::u16string NumberToString16(double number) { | |
| return ASCIIToUTF16(NumberToString(number)); | |
| } | |
| std::string NumberToString(int32_t number) { | |
| return std::to_string(number); | |
| } | |
| std::string NumberToString(unsigned int number) { | |
| return std::to_string(number); | |
| } | |
| std::string NumberToString(float number) { | |
| return NumberToStringImpl(number, true); | |
| } | |
| std::string NumberToString(double number) { | |
| return NumberToStringImpl(number, false); | |
| } | |
| std::string JoinString(std::vector<std::string> tokens, std::string delimiter) { | |
| std::ostringstream imploded; | |
| for (size_t i = 0; i < tokens.size(); i++) { | |
| if (i == tokens.size() - 1) { | |
| imploded << tokens[i]; | |
| } else { | |
| imploded << tokens[i] << delimiter; | |
| } | |
| } | |
| return imploded.str(); | |
| } | |
| std::u16string JoinString(std::vector<std::u16string> tokens, | |
| std::u16string delimiter) { | |
| std::u16string result; | |
| for (size_t i = 0; i < tokens.size(); i++) { | |
| if (i == tokens.size() - 1) { | |
| result.append(tokens[i]); | |
| } else { | |
| result.append(tokens[i]); | |
| result.append(delimiter); | |
| } | |
| } | |
| return result; | |
| } | |
| void ReplaceChars(std::string in, | |
| std::string from, | |
| std::string to, | |
| std::string* out) { | |
| size_t pos = in.find(from); | |
| while (pos != std::string::npos) { | |
| in.replace(pos, from.size(), to); | |
| pos = in.find(from, pos + to.size()); | |
| } | |
| *out = in; | |
| } | |
| void ReplaceChars(std::u16string in, | |
| std::u16string from, | |
| std::u16string to, | |
| std::u16string* out) { | |
| size_t pos = in.find(from); | |
| while (pos != std::u16string::npos) { | |
| in.replace(pos, from.size(), to); | |
| pos = in.find(from, pos + to.size()); | |
| } | |
| *out = in; | |
| } | |
| const std::string& EmptyString() { | |
| static const base::NoDestructor<std::string> s; | |
| return *s; | |
| } | |
| std::string ToUpperASCII(std::string str) { | |
| std::string ret; | |
| ret.reserve(str.size()); | |
| for (size_t i = 0; i < str.size(); i++) | |
| ret.push_back(std::toupper(str[i])); | |
| return ret; | |
| } | |
| std::string ToLowerASCII(std::string str) { | |
| std::string ret; | |
| ret.reserve(str.size()); | |
| for (size_t i = 0; i < str.size(); i++) | |
| ret.push_back(std::tolower(str[i])); | |
| return ret; | |
| } | |
| bool LowerCaseEqualsASCII(std::string a, std::string b) { | |
| std::string lower_a = ToLowerASCII(a); | |
| return lower_a.compare(ToLowerASCII(b)) == 0; | |
| } | |
| bool ContainsOnlyChars(std::u16string str, char16_t ch) { | |
| return std::find_if(str.begin(), str.end(), | |
| [ch](char16_t c) { return c != ch; }) == str.end(); | |
| } | |
| } // namespace base |