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 ()__anond35c0fe80111::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 ()__anond35c0fe80111::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_DEBUG("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_DEBUG("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_DEBUG("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_DEBUG("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_DEBUG("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_DEBUG("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_DEBUG("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_DEBUG("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_DEBUG("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_DEBUG("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_DEBUG("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