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 <charconv>
20 #include <cstdlib>
21 #include <functional>
22 #include <iomanip>
23 #include <iterator>
24 #include <sstream>
25 #include <system_error>
26
27 namespace {
28
29 struct IsSpace : std::unary_function<std::string::value_type, bool> {
operator ()__anon9aeabb0f0111::IsSpace30 bool operator()(std::string::value_type v) {
31 return isspace(static_cast<int>(v));
32 }
33 };
34
35 struct IsHexDigit : std::unary_function<std::string::value_type, bool> {
operator ()__anon9aeabb0f0111::IsHexDigit36 bool operator()(std::string::value_type v) {
37 return isxdigit(static_cast<int>(v));
38 }
39 };
40
41 } // namespace
42
43 namespace bluetooth {
44 namespace common {
45
ToHexString(const std::vector<uint8_t> & value)46 std::string ToHexString(const std::vector<uint8_t>& value) {
47 return ToHexString(value.begin(), value.end());
48 }
49
IsValidHexString(const std::string & str)50 bool IsValidHexString(const std::string& str) {
51 return std::find_if_not(str.begin(), str.end(), IsHexDigit{}) == str.end();
52 }
53
FromHexString(const std::string & str)54 std::optional<std::vector<uint8_t>> FromHexString(const std::string& str) {
55 if (str.size() % 2 != 0) {
56 LOG_INFO("str size is not divisible by 2, size is %zu", str.size());
57 return std::nullopt;
58 }
59 if (std::find_if_not(str.begin(), str.end(), IsHexDigit{}) != str.end()) {
60 LOG_INFO("value contains none hex digit");
61 return std::nullopt;
62 }
63 std::vector<uint8_t> value;
64 value.reserve(str.size() / 2);
65 for (size_t i = 0; i < str.size(); i += 2) {
66 uint8_t v = 0;
67 auto ret = std::from_chars(str.c_str() + i, str.c_str() + i + 2, v, 16);
68 if (std::make_error_code(ret.ec)) {
69 LOG_INFO("failed to parse hex char at index %zu", i);
70 return std::nullopt;
71 }
72 value.push_back(v);
73 }
74 return value;
75 }
76
StringTrim(std::string str)77 std::string StringTrim(std::string str) {
78 str.erase(str.begin(), std::find_if_not(str.begin(), str.end(), IsSpace{}));
79 str.erase(std::find_if_not(str.rbegin(), str.rend(), IsSpace{}).base(), str.end());
80 return str;
81 }
82
StringSplit(const std::string & str,const std::string & delim,size_t max_token)83 std::vector<std::string> StringSplit(const std::string& str, const std::string& delim, size_t max_token) {
84 ASSERT_LOG(!delim.empty(), "delim cannot be empty");
85 std::vector<std::string> tokens;
86 // Use std::string::find and std::string::substr to avoid copying str into a stringstream
87 std::string::size_type starting_index = 0;
88 auto index_of_delim = str.find(delim);
89 while ((max_token == 0 || tokens.size() < (max_token - 1)) && index_of_delim != std::string::npos) {
90 tokens.push_back(str.substr(starting_index, index_of_delim - starting_index));
91 starting_index = index_of_delim + delim.size();
92 index_of_delim = str.find(delim, starting_index);
93 }
94 // Append last item to the vector if there are anything left
95 if (starting_index < (str.size() + 1)) {
96 tokens.push_back(str.substr(starting_index));
97 }
98 return tokens;
99 }
100
StringJoin(const std::vector<std::string> & strings,const std::string & delim)101 std::string StringJoin(const std::vector<std::string>& strings, const std::string& delim) {
102 std::stringstream ss;
103 for (auto it = strings.begin(); it != strings.end(); it++) {
104 ss << *it;
105 if (std::next(it) != strings.end()) {
106 ss << delim;
107 }
108 }
109 return ss.str();
110 }
111
Int64FromString(const std::string & str)112 std::optional<int64_t> Int64FromString(const std::string& str) {
113 char* ptr = nullptr;
114 errno = 0;
115 int64_t value = std::strtoll(str.c_str(), &ptr, 10);
116 if (errno != 0) {
117 LOG_INFO("cannot parse string '%s' with error '%s'", str.c_str(), strerror(errno));
118 return std::nullopt;
119 }
120 if (ptr == str.c_str()) {
121 LOG_INFO("string '%s' is empty or has wrong format", str.c_str());
122 return std::nullopt;
123 }
124 if (ptr != (str.c_str() + str.size())) {
125 LOG_INFO("cannot parse whole string '%s'", str.c_str());
126 return std::nullopt;
127 }
128 return value;
129 }
130
ToString(int64_t value)131 std::string ToString(int64_t value) {
132 return std::to_string(value);
133 }
134
Uint64FromString(const std::string & str)135 std::optional<uint64_t> Uint64FromString(const std::string& str) {
136 if (str.find('-') != std::string::npos) {
137 LOG_INFO("string '%s' contains minus sign, this function is for unsigned", str.c_str());
138 return std::nullopt;
139 }
140 char* ptr = nullptr;
141 errno = 0;
142 uint64_t value = std::strtoull(str.c_str(), &ptr, 10);
143 if (errno != 0) {
144 LOG_INFO("cannot parse string '%s' with error '%s'", str.c_str(), strerror(errno));
145 return std::nullopt;
146 }
147 if (ptr == str.c_str()) {
148 LOG_INFO("string '%s' is empty or has wrong format", str.c_str());
149 return std::nullopt;
150 }
151 if (ptr != (str.c_str() + str.size())) {
152 LOG_INFO("cannot parse whole string '%s'", str.c_str());
153 return std::nullopt;
154 }
155 return value;
156 }
157
ToString(uint64_t value)158 std::string ToString(uint64_t value) {
159 return std::to_string(value);
160 }
161
BoolFromString(const std::string & str)162 std::optional<bool> BoolFromString(const std::string& str) {
163 if (str == "true") {
164 return true;
165 } else if (str == "false") {
166 return false;
167 } else {
168 LOG_INFO("string '%s' is neither true nor false", str.c_str());
169 return std::nullopt;
170 }
171 }
172
ToString(bool value)173 std::string ToString(bool value) {
174 return value ? "true" : "false";
175 }
176
177 } // namespace common
178 } // namespace bluetooth