1 #include "string_util.h"
4 #ifdef BENCHMARK_STL_ANDROID_GNUSTL
13 #include "arraysize.h"
14 #include "benchmark/benchmark.h"
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"};
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");
32 static const int64_t kUnitsSize
= arraysize(kBigSIUnits
);
34 void ToExponentAndMantissa(double val
, int precision
, double one_k
,
35 std::string
* mantissa
, int64_t* exponent
) {
36 std::stringstream mantissa_stream
;
39 mantissa_stream
<< "-";
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;
52 if (val
> big_threshold
) {
55 for (size_t i
= 0; i
< arraysize(kBigSIUnits
); ++i
) {
57 if (scaled
<= big_threshold
) {
58 mantissa_stream
<< scaled
;
60 *mantissa
= mantissa_stream
.str();
64 mantissa_stream
<< val
;
66 } else if (val
< small_threshold
) {
68 if (val
< simple_threshold
) {
70 for (size_t i
= 0; i
< arraysize(kSmallSIUnits
); ++i
) {
72 if (scaled
>= small_threshold
) {
73 mantissa_stream
<< scaled
;
74 *exponent
= -static_cast<int64_t>(i
+ 1);
75 *mantissa
= mantissa_stream
.str();
80 mantissa_stream
<< val
;
83 mantissa_stream
<< val
;
86 *mantissa
= mantissa_stream
.str();
89 std::string
ExponentToPrefix(int64_t exponent
, bool iec
) {
90 if (exponent
== 0) return "";
92 const int64_t index
= (exponent
> 0 ? exponent
- 1 : -exponent
- 1);
93 if (index
>= kUnitsSize
) return "";
95 const char* const* array
=
96 (exponent
> 0 ? (iec
? kBigIECUnits
: kBigSIUnits
) : kSmallSIUnits
);
98 return std::string(array
[index
]);
101 std::string
ToBinaryStringFullySpecified(double value
, int precision
,
102 Counter::OneK one_k
) {
103 std::string mantissa
;
105 ToExponentAndMantissa(value
, precision
,
106 one_k
== Counter::kIs1024
? 1024.0 : 1000.0, &mantissa
,
108 return mantissa
+ ExponentToPrefix(exponent
, one_k
== Counter::kIs1024
);
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
114 va_copy(args_cp
, args
);
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
;
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
);
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());
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());
143 std::string
HumanReadableNumber(double n
, Counter::OneK one_k
) {
144 return ToBinaryStringFullySpecified(n
, 1, one_k
);
147 std::string
StrFormat(const char* format
, ...) {
149 va_start(args
, format
);
150 std::string tmp
= StrFormatImp(format
, args
);
155 std::vector
<std::string
> StrSplit(const std::string
& str
, char delim
) {
156 if (str
.empty()) return {};
157 std::vector
<std::string
> ret
;
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
));
164 ret
.push_back(str
.substr(first
));
168 #ifdef BENCHMARK_STL_ANDROID_GNUSTL
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.
175 unsigned long stoul(const std::string
& str
, size_t* pos
, int base
) {
176 /* Record previous errno */
177 const int oldErrno
= errno
;
180 const char* strStart
= str
.c_str();
181 char* strEnd
= const_cast<char*>(strStart
);
182 const unsigned long result
= strtoul(strStart
, &strEnd
, base
);
184 const int strtoulErrno
= errno
;
185 /* Restore previous errno */
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");
195 if (pos
!= nullptr) {
196 *pos
= static_cast<size_t>(strEnd
- strStart
);
201 int stoi(const std::string
& str
, size_t* pos
, int base
) {
202 /* Record previous errno */
203 const int oldErrno
= errno
;
206 const char* strStart
= str
.c_str();
207 char* strEnd
= const_cast<char*>(strStart
);
208 const long result
= strtol(strStart
, &strEnd
, base
);
210 const int strtolErrno
= errno
;
211 /* Restore previous errno */
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");
221 if (pos
!= nullptr) {
222 *pos
= static_cast<size_t>(strEnd
- strStart
);
227 double stod(const std::string
& str
, size_t* pos
) {
228 /* Record previous errno */
229 const int oldErrno
= errno
;
232 const char* strStart
= str
.c_str();
233 char* strEnd
= const_cast<char*>(strStart
);
234 const double result
= strtod(strStart
, &strEnd
);
236 /* Restore previous errno */
237 const int strtodErrno
= errno
;
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");
247 if (pos
!= nullptr) {
248 *pos
= static_cast<size_t>(strEnd
- strStart
);
254 } // end namespace benchmark