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 16namespace benchmark { 17namespace { 18// kilo, Mega, Giga, Tera, Peta, Exa, Zetta, Yotta. 19const char* const kBigSIUnits[] = {"k", "M", "G", "T", "P", "E", "Z", "Y"}; 20// Kibi, Mebi, Gibi, Tebi, Pebi, Exbi, Zebi, Yobi. 21const char* const kBigIECUnits[] = {"Ki", "Mi", "Gi", "Ti", 22 "Pi", "Ei", "Zi", "Yi"}; 23// milli, micro, nano, pico, femto, atto, zepto, yocto. 24const char* const kSmallSIUnits[] = {"m", "u", "n", "p", "f", "a", "z", "y"}; 25 26// We require that all three arrays have the same size. 27static_assert(arraysize(kBigSIUnits) == arraysize(kBigIECUnits), 28 "SI and IEC unit arrays must be the same size"); 29static_assert(arraysize(kSmallSIUnits) == arraysize(kBigSIUnits), 30 "Small SI and Big SI unit arrays must be the same size"); 31 32static const int64_t kUnitsSize = arraysize(kBigSIUnits); 33 34void 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 = 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 89std::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 101std::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 111std::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 143std::string HumanReadableNumber(double n, Counter::OneK one_k) { 144 return ToBinaryStringFullySpecified(n, 1, one_k); 145} 146 147std::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 155std::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 */ 175unsigned 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 201int 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 227double 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