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