xref: /aosp_15_r20/external/google-benchmark/src/string_util.cc (revision dbb99499c3810fa1611fa2242a2fc446be01a57c)
1 #include "string_util.h"
2 
3 #include <array>
4 #ifdef BENCHMARK_STL_ANDROID_GNUSTL
5 #include <cerrno>
6 #endif
7 #include <cmath>
8 #include <cstdarg>
9 #include <cstdio>
10 #include <memory>
11 #include <sstream>
12 
13 #include "arraysize.h"
14 #include "benchmark/benchmark.h"
15 
16 namespace benchmark {
17 namespace {
18 // kilo, Mega, Giga, Tera, Peta, Exa, Zetta, Yotta.
19 const char* const kBigSIUnits[] = {"k", "M", "G", "T", "P", "E", "Z", "Y"};
20 // Kibi, Mebi, Gibi, Tebi, Pebi, Exbi, Zebi, Yobi.
21 const char* const kBigIECUnits[] = {"Ki", "Mi", "Gi", "Ti",
22                                     "Pi", "Ei", "Zi", "Yi"};
23 // milli, micro, nano, pico, femto, atto, zepto, yocto.
24 const char* const kSmallSIUnits[] = {"m", "u", "n", "p", "f", "a", "z", "y"};
25 
26 // We require that all three arrays have the same size.
27 static_assert(arraysize(kBigSIUnits) == arraysize(kBigIECUnits),
28               "SI and IEC unit arrays must be the same size");
29 static_assert(arraysize(kSmallSIUnits) == arraysize(kBigSIUnits),
30               "Small SI and Big SI unit arrays must be the same size");
31 
32 static const int64_t kUnitsSize = arraysize(kBigSIUnits);
33 
ToExponentAndMantissa(double val,int precision,double one_k,std::string * mantissa,int64_t * exponent)34 void ToExponentAndMantissa(double val, int precision, double one_k,
35                            std::string* mantissa, int64_t* exponent) {
36   std::stringstream mantissa_stream;
37 
38   if (val < 0) {
39     mantissa_stream << "-";
40     val = -val;
41   }
42 
43   // Adjust threshold so that it never excludes things which can't be rendered
44   // in 'precision' digits.
45   const double adjusted_threshold =
46       std::max(1.0, 1.0 / std::pow(10.0, precision));
47   const double big_threshold = (adjusted_threshold * one_k) - 1;
48   const double small_threshold = adjusted_threshold;
49   // Values in ]simple_threshold,small_threshold[ will be printed as-is
50   const double simple_threshold = 0.01;
51 
52   if (val > big_threshold) {
53     // Positive powers
54     double scaled = val;
55     for (size_t i = 0; i < arraysize(kBigSIUnits); ++i) {
56       scaled /= one_k;
57       if (scaled <= big_threshold) {
58         mantissa_stream << scaled;
59         *exponent = static_cast<int64_t>(i + 1);
60         *mantissa = mantissa_stream.str();
61         return;
62       }
63     }
64     mantissa_stream << val;
65     *exponent = 0;
66   } else if (val < small_threshold) {
67     // Negative powers
68     if (val < simple_threshold) {
69       double scaled = val;
70       for (size_t i = 0; i < arraysize(kSmallSIUnits); ++i) {
71         scaled *= one_k;
72         if (scaled >= small_threshold) {
73           mantissa_stream << scaled;
74           *exponent = -static_cast<int64_t>(i + 1);
75           *mantissa = mantissa_stream.str();
76           return;
77         }
78       }
79     }
80     mantissa_stream << val;
81     *exponent = 0;
82   } else {
83     mantissa_stream << val;
84     *exponent = 0;
85   }
86   *mantissa = mantissa_stream.str();
87 }
88 
ExponentToPrefix(int64_t exponent,bool iec)89 std::string ExponentToPrefix(int64_t exponent, bool iec) {
90   if (exponent == 0) return "";
91 
92   const int64_t index = (exponent > 0 ? exponent - 1 : -exponent - 1);
93   if (index >= kUnitsSize) return "";
94 
95   const char* const* array =
96       (exponent > 0 ? (iec ? kBigIECUnits : kBigSIUnits) : kSmallSIUnits);
97 
98   return std::string(array[index]);
99 }
100 
ToBinaryStringFullySpecified(double value,int precision,Counter::OneK one_k)101 std::string ToBinaryStringFullySpecified(double value, int precision,
102                                          Counter::OneK one_k) {
103   std::string mantissa;
104   int64_t exponent;
105   ToExponentAndMantissa(value, precision,
106                         one_k == Counter::kIs1024 ? 1024.0 : 1000.0, &mantissa,
107                         &exponent);
108   return mantissa + ExponentToPrefix(exponent, one_k == Counter::kIs1024);
109 }
110 
StrFormatImp(const char * msg,va_list args)111 std::string StrFormatImp(const char* msg, va_list args) {
112   // we might need a second shot at this, so pre-emptivly make a copy
113   va_list args_cp;
114   va_copy(args_cp, args);
115 
116   // TODO(ericwf): use std::array for first attempt to avoid one memory
117   // allocation guess what the size might be
118   std::array<char, 256> local_buff;
119 
120   // 2015-10-08: vsnprintf is used instead of snd::vsnprintf due to a limitation
121   // in the android-ndk
122   auto ret = vsnprintf(local_buff.data(), local_buff.size(), msg, args_cp);
123 
124   va_end(args_cp);
125 
126   // handle empty expansion
127   if (ret == 0) return std::string{};
128   if (static_cast<std::size_t>(ret) < local_buff.size())
129     return std::string(local_buff.data());
130 
131   // we did not provide a long enough buffer on our first attempt.
132   // add 1 to size to account for null-byte in size cast to prevent overflow
133   std::size_t size = static_cast<std::size_t>(ret) + 1;
134   auto buff_ptr = std::unique_ptr<char[]>(new char[size]);
135   // 2015-10-08: vsnprintf is used instead of snd::vsnprintf due to a limitation
136   // in the android-ndk
137   vsnprintf(buff_ptr.get(), size, msg, args);
138   return std::string(buff_ptr.get());
139 }
140 
141 }  // end namespace
142 
HumanReadableNumber(double n,Counter::OneK one_k)143 std::string HumanReadableNumber(double n, Counter::OneK one_k) {
144   return ToBinaryStringFullySpecified(n, 1, one_k);
145 }
146 
StrFormat(const char * format,...)147 std::string StrFormat(const char* format, ...) {
148   va_list args;
149   va_start(args, format);
150   std::string tmp = StrFormatImp(format, args);
151   va_end(args);
152   return tmp;
153 }
154 
StrSplit(const std::string & str,char delim)155 std::vector<std::string> StrSplit(const std::string& str, char delim) {
156   if (str.empty()) return {};
157   std::vector<std::string> ret;
158   size_t first = 0;
159   size_t next = str.find(delim);
160   for (; next != std::string::npos;
161        first = next + 1, next = str.find(delim, first)) {
162     ret.push_back(str.substr(first, next - first));
163   }
164   ret.push_back(str.substr(first));
165   return ret;
166 }
167 
168 #ifdef BENCHMARK_STL_ANDROID_GNUSTL
169 /*
170  * GNU STL in Android NDK lacks support for some C++11 functions, including
171  * stoul, stoi, stod. We reimplement them here using C functions strtoul,
172  * strtol, strtod. Note that reimplemented functions are in benchmark::
173  * namespace, not std:: namespace.
174  */
stoul(const std::string & str,size_t * pos,int base)175 unsigned long stoul(const std::string& str, size_t* pos, int base) {
176   /* Record previous errno */
177   const int oldErrno = errno;
178   errno = 0;
179 
180   const char* strStart = str.c_str();
181   char* strEnd = const_cast<char*>(strStart);
182   const unsigned long result = strtoul(strStart, &strEnd, base);
183 
184   const int strtoulErrno = errno;
185   /* Restore previous errno */
186   errno = oldErrno;
187 
188   /* Check for errors and return */
189   if (strtoulErrno == ERANGE) {
190     throw std::out_of_range("stoul failed: " + str +
191                             " is outside of range of unsigned long");
192   } else if (strEnd == strStart || strtoulErrno != 0) {
193     throw std::invalid_argument("stoul failed: " + str + " is not an integer");
194   }
195   if (pos != nullptr) {
196     *pos = static_cast<size_t>(strEnd - strStart);
197   }
198   return result;
199 }
200 
stoi(const std::string & str,size_t * pos,int base)201 int stoi(const std::string& str, size_t* pos, int base) {
202   /* Record previous errno */
203   const int oldErrno = errno;
204   errno = 0;
205 
206   const char* strStart = str.c_str();
207   char* strEnd = const_cast<char*>(strStart);
208   const long result = strtol(strStart, &strEnd, base);
209 
210   const int strtolErrno = errno;
211   /* Restore previous errno */
212   errno = oldErrno;
213 
214   /* Check for errors and return */
215   if (strtolErrno == ERANGE || long(int(result)) != result) {
216     throw std::out_of_range("stoul failed: " + str +
217                             " is outside of range of int");
218   } else if (strEnd == strStart || strtolErrno != 0) {
219     throw std::invalid_argument("stoul failed: " + str + " is not an integer");
220   }
221   if (pos != nullptr) {
222     *pos = static_cast<size_t>(strEnd - strStart);
223   }
224   return int(result);
225 }
226 
stod(const std::string & str,size_t * pos)227 double stod(const std::string& str, size_t* pos) {
228   /* Record previous errno */
229   const int oldErrno = errno;
230   errno = 0;
231 
232   const char* strStart = str.c_str();
233   char* strEnd = const_cast<char*>(strStart);
234   const double result = strtod(strStart, &strEnd);
235 
236   /* Restore previous errno */
237   const int strtodErrno = errno;
238   errno = oldErrno;
239 
240   /* Check for errors and return */
241   if (strtodErrno == ERANGE) {
242     throw std::out_of_range("stoul failed: " + str +
243                             " is outside of range of int");
244   } else if (strEnd == strStart || strtodErrno != 0) {
245     throw std::invalid_argument("stoul failed: " + str + " is not an integer");
246   }
247   if (pos != nullptr) {
248     *pos = static_cast<size_t>(strEnd - strStart);
249   }
250   return result;
251 }
252 #endif
253 
254 }  // end namespace benchmark
255