1 /*
2 * Copyright 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "common/strings.h"
18
19 #include <algorithm>
20 #include <charconv>
21 #include <cstdlib>
22 #include <functional>
23 #include <iomanip>
24 #include <iterator>
25 #include <sstream>
26 #include <system_error>
27
28 namespace {
29
30 struct IsSpace {
operator ()__anon2e3a88c30111::IsSpace31 bool operator()(std::string::value_type v) {
32 return isspace(static_cast<int>(v));
33 }
34 };
35
36 struct IsHexDigit {
operator ()__anon2e3a88c30111::IsHexDigit37 bool operator()(std::string::value_type v) {
38 return isxdigit(static_cast<int>(v));
39 }
40 };
41
42 } // namespace
43
44 namespace bluetooth {
45 namespace common {
46
ToHexString(const std::vector<uint8_t> & value)47 std::string ToHexString(const std::vector<uint8_t>& value) {
48 return ToHexString(value.begin(), value.end());
49 }
50
IsValidHexString(const std::string & str)51 bool IsValidHexString(const std::string& str) {
52 return std::find_if_not(str.begin(), str.end(), IsHexDigit{}) == str.end();
53 }
54
FromHexString(const std::string & str)55 std::optional<std::vector<uint8_t>> FromHexString(const std::string& str) {
56 if (str.size() % 2 != 0) {
57 LOG_INFO("str size is not divisible by 2, size is %zu", str.size());
58 return std::nullopt;
59 }
60 if (std::find_if_not(str.begin(), str.end(), IsHexDigit{}) != str.end()) {
61 LOG_INFO("value contains none hex digit");
62 return std::nullopt;
63 }
64 std::vector<uint8_t> value;
65 value.reserve(str.size() / 2);
66 for (size_t i = 0; i < str.size(); i += 2) {
67 uint8_t v = 0;
68 auto ret = std::from_chars(str.c_str() + i, str.c_str() + i + 2, v, 16);
69 if (std::make_error_code(ret.ec)) {
70 LOG_INFO("failed to parse hex char at index %zu", i);
71 return std::nullopt;
72 }
73 value.push_back(v);
74 }
75 return value;
76 }
77
StringTrim(std::string str)78 std::string StringTrim(std::string str) {
79 str.erase(str.begin(), std::find_if_not(str.begin(), str.end(), IsSpace{}));
80 str.erase(std::find_if_not(str.rbegin(), str.rend(), IsSpace{}).base(), str.end());
81 return str;
82 }
83
StringSplit(const std::string & str,const std::string & delim,size_t max_token)84 std::vector<std::string> StringSplit(const std::string& str, const std::string& delim, size_t max_token) {
85 ASSERT_LOG(!delim.empty(), "delim cannot be empty");
86 std::vector<std::string> tokens;
87 // Use std::string::find and std::string::substr to avoid copying str into a stringstream
88 std::string::size_type starting_index = 0;
89 auto index_of_delim = str.find(delim);
90 while ((max_token == 0 || tokens.size() < (max_token - 1)) && index_of_delim != std::string::npos) {
91 tokens.push_back(str.substr(starting_index, index_of_delim - starting_index));
92 starting_index = index_of_delim + delim.size();
93 index_of_delim = str.find(delim, starting_index);
94 }
95 // Append last item to the vector if there are anything left
96 if (starting_index < (str.size() + 1)) {
97 tokens.push_back(str.substr(starting_index));
98 }
99 return tokens;
100 }
101
StringJoin(const std::vector<std::string> & strings,const std::string & delim)102 std::string StringJoin(const std::vector<std::string>& strings, const std::string& delim) {
103 std::stringstream ss;
104 for (auto it = strings.begin(); it != strings.end(); it++) {
105 ss << *it;
106 if (std::next(it) != strings.end()) {
107 ss << delim;
108 }
109 }
110 return ss.str();
111 }
112
Int64FromString(const std::string & str)113 std::optional<int64_t> Int64FromString(const std::string& str) {
114 char* ptr = nullptr;
115 errno = 0;
116 int64_t value = std::strtoll(str.c_str(), &ptr, 10);
117 if (errno != 0) {
118 LOG_INFO("cannot parse string '%s' with error '%s'", str.c_str(), strerror(errno));
119 return std::nullopt;
120 }
121 if (ptr == str.c_str()) {
122 LOG_INFO("string '%s' is empty or has wrong format", str.c_str());
123 return std::nullopt;
124 }
125 if (ptr != (str.c_str() + str.size())) {
126 LOG_INFO("cannot parse whole string '%s'", str.c_str());
127 return std::nullopt;
128 }
129 return value;
130 }
131
ToString(int64_t value)132 std::string ToString(int64_t value) {
133 return std::to_string(value);
134 }
135
Uint64FromString(const std::string & str)136 std::optional<uint64_t> Uint64FromString(const std::string& str) {
137 if (str.find('-') != std::string::npos) {
138 LOG_INFO("string '%s' contains minus sign, this function is for unsigned", str.c_str());
139 return std::nullopt;
140 }
141 char* ptr = nullptr;
142 errno = 0;
143 uint64_t value = std::strtoull(str.c_str(), &ptr, 10);
144 if (errno != 0) {
145 LOG_INFO("cannot parse string '%s' with error '%s'", str.c_str(), strerror(errno));
146 return std::nullopt;
147 }
148 if (ptr == str.c_str()) {
149 LOG_INFO("string '%s' is empty or has wrong format", str.c_str());
150 return std::nullopt;
151 }
152 if (ptr != (str.c_str() + str.size())) {
153 LOG_INFO("cannot parse whole string '%s'", str.c_str());
154 return std::nullopt;
155 }
156 return value;
157 }
158
ToString(uint64_t value)159 std::string ToString(uint64_t value) {
160 return std::to_string(value);
161 }
162
BoolFromString(const std::string & str)163 std::optional<bool> BoolFromString(const std::string& str) {
164 if (str == "true") {
165 return true;
166 } else if (str == "false") {
167 return false;
168 } else {
169 LOG_INFO("string '%s' is neither true nor false", str.c_str());
170 return std::nullopt;
171 }
172 }
173
ToString(bool value)174 std::string ToString(bool value) {
175 return value ? "true" : "false";
176 }
177
178 } // namespace common
179 } // namespace bluetooth
180