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 ()__anoncb2858340111::IsSpace33 bool operator()(std::string::value_type v) { return isspace(static_cast<int>(v)); }
34 };
35
36 struct IsHexDigit {
operator ()__anoncb2858340111::IsHexDigit37 bool operator()(std::string::value_type v) { return isxdigit(static_cast<int>(v)); }
38 };
39
40 } // namespace
41
42 namespace bluetooth {
43 namespace common {
44
ToHexString(const std::vector<uint8_t> & value)45 std::string ToHexString(const std::vector<uint8_t>& value) {
46 return ToHexString(value.begin(), value.end());
47 }
48
IsValidHexString(const std::string & str)49 bool IsValidHexString(const std::string& str) {
50 return std::find_if_not(str.begin(), str.end(), IsHexDigit{}) == str.end();
51 }
52
FromHexString(const std::string & str)53 std::optional<std::vector<uint8_t>> FromHexString(const std::string& str) {
54 if (str.size() % 2 != 0) {
55 log::info("str size is not divisible by 2, size is {}", str.size());
56 return std::nullopt;
57 }
58 if (std::find_if_not(str.begin(), str.end(), IsHexDigit{}) != str.end()) {
59 log::info("value contains none hex digit");
60 return std::nullopt;
61 }
62 std::vector<uint8_t> value;
63 value.reserve(str.size() / 2);
64 for (size_t i = 0; i < str.size(); i += 2) {
65 uint8_t v = 0;
66 auto ret = std::from_chars(str.c_str() + i, str.c_str() + i + 2, v, 16);
67 if (std::make_error_code(ret.ec)) {
68 log::info("failed to parse hex char at index {}", i);
69 return std::nullopt;
70 }
71 value.push_back(v);
72 }
73 return value;
74 }
75
StringTrim(std::string str)76 std::string StringTrim(std::string str) {
77 str.erase(str.begin(), std::find_if_not(str.begin(), str.end(), IsSpace{}));
78 str.erase(std::find_if_not(str.rbegin(), str.rend(), IsSpace{}).base(), str.end());
79 return str;
80 }
81
StringSplit(const std::string & str,const std::string & delim,size_t max_token)82 std::vector<std::string> StringSplit(const std::string& str, const std::string& delim,
83 size_t max_token) {
84 log::assert_that(!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)) &&
90 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 '{}' with error '{}'", str, strerror(errno));
119 return std::nullopt;
120 }
121 if (ptr == str.c_str()) {
122 log::info("string '{}' is empty or has wrong format", str);
123 return std::nullopt;
124 }
125 if (ptr != (str.c_str() + str.size())) {
126 log::info("cannot parse whole string '{}'", str);
127 return std::nullopt;
128 }
129 return value;
130 }
131
ToString(int64_t value)132 std::string ToString(int64_t value) { return std::to_string(value); }
133
Uint64FromString(const std::string & str)134 std::optional<uint64_t> Uint64FromString(const std::string& str) {
135 if (str.find('-') != std::string::npos) {
136 log::info("string '{}' contains minus sign, this function is for unsigned", str);
137 return std::nullopt;
138 }
139 char* ptr = nullptr;
140 errno = 0;
141 uint64_t value = std::strtoull(str.c_str(), &ptr, 10);
142 if (errno != 0) {
143 log::info("cannot parse string '{}' with error '{}'", str, strerror(errno));
144 return std::nullopt;
145 }
146 if (ptr == str.c_str()) {
147 log::info("string '{}' is empty or has wrong format", str);
148 return std::nullopt;
149 }
150 if (ptr != (str.c_str() + str.size())) {
151 log::info("cannot parse whole string '{}'", str);
152 return std::nullopt;
153 }
154 return value;
155 }
156
ToString(uint64_t value)157 std::string ToString(uint64_t value) { return std::to_string(value); }
158
BoolFromString(const std::string & str)159 std::optional<bool> BoolFromString(const std::string& str) {
160 if (str == "true") {
161 return true;
162 } else if (str == "false") {
163 return false;
164 } else {
165 log::info("string '{}' is neither true nor false", str);
166 return std::nullopt;
167 }
168 }
169
ToString(bool value)170 std::string ToString(bool value) { return value ? "true" : "false"; }
171
172 } // namespace common
173 } // namespace bluetooth
174