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