1 #include "string_util.h"
4 #ifdef BENCHMARK_STL_ANDROID_GNUSTL
13 #include "arraysize.h"
18 // kilo, Mega, Giga, Tera, Peta, Exa, Zetta, Yotta.
19 const char kBigSIUnits
[] = "kMGTPEZY";
20 // Kibi, Mebi, Gibi, Tebi, Pebi, Exbi, Zebi, Yobi.
21 const char kBigIECUnits
[] = "KMGTPEZY";
22 // milli, micro, nano, pico, femto, atto, zepto, yocto.
23 const char kSmallSIUnits
[] = "munpfazy";
25 // We require that all three arrays have the same size.
26 static_assert(arraysize(kBigSIUnits
) == arraysize(kBigIECUnits
),
27 "SI and IEC unit arrays must be the same size");
28 static_assert(arraysize(kSmallSIUnits
) == arraysize(kBigSIUnits
),
29 "Small SI and Big SI unit arrays must be the same size");
31 static const int64_t kUnitsSize
= arraysize(kBigSIUnits
);
33 void ToExponentAndMantissa(double val
, double thresh
, int precision
,
34 double one_k
, std::string
* mantissa
,
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(thresh
, 1.0 / std::pow(10.0, precision
));
47 const double big_threshold
= adjusted_threshold
* one_k
;
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 "";
96 (exponent
> 0 ? (iec
? kBigIECUnits
: kBigSIUnits
) : kSmallSIUnits
);
98 return array
[index
] + std::string("i");
100 return std::string(1, array
[index
]);
103 std::string
ToBinaryStringFullySpecified(double value
, double threshold
,
104 int precision
, double one_k
= 1024.0) {
105 std::string mantissa
;
107 ToExponentAndMantissa(value
, threshold
, precision
, one_k
, &mantissa
,
109 return mantissa
+ ExponentToPrefix(exponent
, false);
114 void AppendHumanReadable(int n
, std::string
* str
) {
115 std::stringstream ss
;
116 // Round down to the nearest SI prefix.
117 ss
<< ToBinaryStringFullySpecified(n
, 1.0, 0);
121 std::string
HumanReadableNumber(double n
, double one_k
) {
122 // 1.1 means that figures up to 1.1k should be shown with the next unit down;
123 // this softens edge effects.
124 // 1 means that we should show one decimal place of precision.
125 return ToBinaryStringFullySpecified(n
, 1.1, 1, one_k
);
128 std::string
StrFormatImp(const char* msg
, va_list args
) {
129 // we might need a second shot at this, so pre-emptivly make a copy
131 va_copy(args_cp
, args
);
133 // TODO(ericwf): use std::array for first attempt to avoid one memory
134 // allocation guess what the size might be
135 std::array
<char, 256> local_buff
;
136 std::size_t size
= local_buff
.size();
137 // 2015-10-08: vsnprintf is used instead of snd::vsnprintf due to a limitation
138 // in the android-ndk
139 auto ret
= vsnprintf(local_buff
.data(), size
, msg
, args_cp
);
143 // handle empty expansion
144 if (ret
== 0) return std::string
{};
145 if (static_cast<std::size_t>(ret
) < size
)
146 return std::string(local_buff
.data());
148 // we did not provide a long enough buffer on our first attempt.
149 // add 1 to size to account for null-byte in size cast to prevent overflow
150 size
= static_cast<std::size_t>(ret
) + 1;
151 auto buff_ptr
= std::unique_ptr
<char[]>(new char[size
]);
152 // 2015-10-08: vsnprintf is used instead of snd::vsnprintf due to a limitation
153 // in the android-ndk
154 vsnprintf(buff_ptr
.get(), size
, msg
, args
);
155 return std::string(buff_ptr
.get());
158 std::string
StrFormat(const char* format
, ...) {
160 va_start(args
, format
);
161 std::string tmp
= StrFormatImp(format
, args
);
166 std::vector
<std::string
> StrSplit(const std::string
& str
, char delim
) {
167 if (str
.empty()) return {};
168 std::vector
<std::string
> ret
;
170 size_t next
= str
.find(delim
);
171 for (; next
!= std::string::npos
;
172 first
= next
+ 1, next
= str
.find(delim
, first
)) {
173 ret
.push_back(str
.substr(first
, next
- first
));
175 ret
.push_back(str
.substr(first
));
179 #ifdef BENCHMARK_STL_ANDROID_GNUSTL
181 * GNU STL in Android NDK lacks support for some C++11 functions, including
182 * stoul, stoi, stod. We reimplement them here using C functions strtoul,
183 * strtol, strtod. Note that reimplemented functions are in benchmark::
184 * namespace, not std:: namespace.
186 unsigned long stoul(const std::string
& str
, size_t* pos
, int base
) {
187 /* Record previous errno */
188 const int oldErrno
= errno
;
191 const char* strStart
= str
.c_str();
192 char* strEnd
= const_cast<char*>(strStart
);
193 const unsigned long result
= strtoul(strStart
, &strEnd
, base
);
195 const int strtoulErrno
= errno
;
196 /* Restore previous errno */
199 /* Check for errors and return */
200 if (strtoulErrno
== ERANGE
) {
201 throw std::out_of_range("stoul failed: " + str
+
202 " is outside of range of unsigned long");
203 } else if (strEnd
== strStart
|| strtoulErrno
!= 0) {
204 throw std::invalid_argument("stoul failed: " + str
+ " is not an integer");
206 if (pos
!= nullptr) {
207 *pos
= static_cast<size_t>(strEnd
- strStart
);
212 int stoi(const std::string
& str
, size_t* pos
, int base
) {
213 /* Record previous errno */
214 const int oldErrno
= errno
;
217 const char* strStart
= str
.c_str();
218 char* strEnd
= const_cast<char*>(strStart
);
219 const long result
= strtol(strStart
, &strEnd
, base
);
221 const int strtolErrno
= errno
;
222 /* Restore previous errno */
225 /* Check for errors and return */
226 if (strtolErrno
== ERANGE
|| long(int(result
)) != result
) {
227 throw std::out_of_range("stoul failed: " + str
+
228 " is outside of range of int");
229 } else if (strEnd
== strStart
|| strtolErrno
!= 0) {
230 throw std::invalid_argument("stoul failed: " + str
+ " is not an integer");
232 if (pos
!= nullptr) {
233 *pos
= static_cast<size_t>(strEnd
- strStart
);
238 double stod(const std::string
& str
, size_t* pos
) {
239 /* Record previous errno */
240 const int oldErrno
= errno
;
243 const char* strStart
= str
.c_str();
244 char* strEnd
= const_cast<char*>(strStart
);
245 const double result
= strtod(strStart
, &strEnd
);
247 /* Restore previous errno */
248 const int strtodErrno
= errno
;
251 /* Check for errors and return */
252 if (strtodErrno
== ERANGE
) {
253 throw std::out_of_range("stoul failed: " + str
+
254 " is outside of range of int");
255 } else if (strEnd
== strStart
|| strtodErrno
!= 0) {
256 throw std::invalid_argument("stoul failed: " + str
+ " is not an integer");
258 if (pos
!= nullptr) {
259 *pos
= static_cast<size_t>(strEnd
- strStart
);
265 } // end namespace benchmark