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 15 namespace benchmark { 16 namespace { 17 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"; 24 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"); 30 31 static const int64_t kUnitsSize = arraysize(kBigSIUnits); 32 33 void ToExponentAndMantissa(double val, double thresh, int precision, 34 double one_k, std::string* mantissa, 35 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(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; 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 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* array = 96 (exponent > 0 ? (iec ? kBigIECUnits : kBigSIUnits) : kSmallSIUnits); 97 if (iec) 98 return array[index] + std::string("i"); 99 else 100 return std::string(1, array[index]); 101 } 102 103 std::string ToBinaryStringFullySpecified(double value, double threshold, 104 int precision, double one_k = 1024.0) { 105 std::string mantissa; 106 int64_t exponent; 107 ToExponentAndMantissa(value, threshold, precision, one_k, &mantissa, 108 &exponent); 109 return mantissa + ExponentToPrefix(exponent, false); 110 } 111 112 } // end namespace 113 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); 118 *str += ss.str(); 119 } 120 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); 126 } 127 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 130 va_list args_cp; 131 va_copy(args_cp, args); 132 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); 140 141 va_end(args_cp); 142 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()); 147 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()); 156 } 157 158 std::string StrFormat(const char* format, ...) { 159 va_list args; 160 va_start(args, format); 161 std::string tmp = StrFormatImp(format, args); 162 va_end(args); 163 return tmp; 164 } 165 166 std::vector<std::string> StrSplit(const std::string& str, char delim) { 167 if (str.empty()) return {}; 168 std::vector<std::string> ret; 169 size_t first = 0; 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)); 174 } 175 ret.push_back(str.substr(first)); 176 return ret; 177 } 178 179 #ifdef BENCHMARK_STL_ANDROID_GNUSTL 180 /* 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. 185 */ 186 unsigned long stoul(const std::string& str, size_t* pos, int base) { 187 /* Record previous errno */ 188 const int oldErrno = errno; 189 errno = 0; 190 191 const char* strStart = str.c_str(); 192 char* strEnd = const_cast<char*>(strStart); 193 const unsigned long result = strtoul(strStart, &strEnd, base); 194 195 const int strtoulErrno = errno; 196 /* Restore previous errno */ 197 errno = oldErrno; 198 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"); 205 } 206 if (pos != nullptr) { 207 *pos = static_cast<size_t>(strEnd - strStart); 208 } 209 return result; 210 } 211 212 int stoi(const std::string& str, size_t* pos, int base) { 213 /* Record previous errno */ 214 const int oldErrno = errno; 215 errno = 0; 216 217 const char* strStart = str.c_str(); 218 char* strEnd = const_cast<char*>(strStart); 219 const long result = strtol(strStart, &strEnd, base); 220 221 const int strtolErrno = errno; 222 /* Restore previous errno */ 223 errno = oldErrno; 224 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"); 231 } 232 if (pos != nullptr) { 233 *pos = static_cast<size_t>(strEnd - strStart); 234 } 235 return int(result); 236 } 237 238 double stod(const std::string& str, size_t* pos) { 239 /* Record previous errno */ 240 const int oldErrno = errno; 241 errno = 0; 242 243 const char* strStart = str.c_str(); 244 char* strEnd = const_cast<char*>(strStart); 245 const double result = strtod(strStart, &strEnd); 246 247 /* Restore previous errno */ 248 const int strtodErrno = errno; 249 errno = oldErrno; 250 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"); 257 } 258 if (pos != nullptr) { 259 *pos = static_cast<size_t>(strEnd - strStart); 260 } 261 return result; 262 } 263 #endif 264 265 } // end namespace benchmark 266