255 lines · plain
1#include "string_util.h"2 3#include <array>4#ifdef BENCHMARK_STL_ANDROID_GNUSTL5#include <cerrno>6#endif7#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 rendered44 // 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-is50 const double simple_threshold = 0.01;51 52 if (val > big_threshold) {53 // Positive powers54 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 powers68 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 copy113 va_list args_cp;114 va_copy(args_cp, args);115 116 // TODO(ericwf): use std::array for first attempt to avoid one memory117 // allocation guess what the size might be118 std::array<char, 256> local_buff;119 120 // 2015-10-08: vsnprintf is used instead of snd::vsnprintf due to a limitation121 // in the android-ndk122 auto ret = vsnprintf(local_buff.data(), local_buff.size(), msg, args_cp);123 124 va_end(args_cp);125 126 // handle empty expansion127 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 overflow133 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 limitation136 // in the android-ndk137 vsnprintf(buff_ptr.get(), size, msg, args);138 return std::string(buff_ptr.get());139}140 141} // end namespace142 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_GNUSTL169/*170 * GNU STL in Android NDK lacks support for some C++11 functions, including171 * 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#endif253 254} // end namespace benchmark255