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