299 lines · plain
1// Copyright 2015 Google Inc. All rights reserved.2//3// Licensed under the Apache License, Version 2.0 (the "License");4// you may not use this file except in compliance with the License.5// You may obtain a copy of the License at6//7// http://www.apache.org/licenses/LICENSE-2.08//9// Unless required by applicable law or agreed to in writing, software10// distributed under the License is distributed on an "AS IS" BASIS,11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12// See the License for the specific language governing permissions and13// limitations under the License.14 15#include "commandlineflags.h"16 17#include <algorithm>18#include <cctype>19#include <cstdlib>20#include <cstring>21#include <iostream>22#include <limits>23#include <map>24#include <utility>25 26#include "../src/string_util.h"27 28namespace benchmark {29namespace {30 31// Parses 'str' for a 32-bit signed integer. If successful, writes32// the result to *value and returns true; otherwise leaves *value33// unchanged and returns false.34bool ParseInt32(const std::string& src_text, const char* str, int32_t* value) {35 // Parses the environment variable as a decimal integer.36 char* end = nullptr;37 const long long_value = strtol(str, &end, 10); // NOLINT38 39 // Has strtol() consumed all characters in the string?40 if (*end != '\0') {41 // No - an invalid character was encountered.42 std::cerr << src_text << " is expected to be a 32-bit integer, "43 << "but actually has value \"" << str << "\".\n";44 return false;45 }46 47 // Is the parsed value in the range of an Int32?48 const int32_t result = static_cast<int32_t>(long_value);49 if (long_value == std::numeric_limits<long>::max() ||50 long_value == std::numeric_limits<long>::min() ||51 // The parsed value overflows as a long. (strtol() returns52 // LONG_MAX or LONG_MIN when the input overflows.)53 result != long_value54 // The parsed value overflows as an Int32.55 ) {56 std::cerr << src_text << " is expected to be a 32-bit integer, "57 << "but actually has value \"" << str << "\", "58 << "which overflows.\n";59 return false;60 }61 62 *value = result;63 return true;64}65 66// Parses 'str' for a double. If successful, writes the result to *value and67// returns true; otherwise leaves *value unchanged and returns false.68bool ParseDouble(const std::string& src_text, const char* str, double* value) {69 // Parses the environment variable as a decimal integer.70 char* end = nullptr;71 const double double_value = strtod(str, &end); // NOLINT72 73 // Has strtol() consumed all characters in the string?74 if (*end != '\0') {75 // No - an invalid character was encountered.76 std::cerr << src_text << " is expected to be a double, "77 << "but actually has value \"" << str << "\".\n";78 return false;79 }80 81 *value = double_value;82 return true;83}84 85// Parses 'str' into KV pairs. If successful, writes the result to *value and86// returns true; otherwise leaves *value unchanged and returns false.87bool ParseKvPairs(const std::string& src_text, const char* str,88 std::map<std::string, std::string>* value) {89 std::map<std::string, std::string> kvs;90 for (const auto& kvpair : StrSplit(str, ',')) {91 const auto kv = StrSplit(kvpair, '=');92 if (kv.size() != 2) {93 std::cerr << src_text << " is expected to be a comma-separated list of "94 << "<key>=<value> strings, but actually has value \"" << str95 << "\".\n";96 return false;97 }98 if (!kvs.emplace(kv[0], kv[1]).second) {99 std::cerr << src_text << " is expected to contain unique keys but key \""100 << kv[0] << "\" was repeated.\n";101 return false;102 }103 }104 105 *value = kvs;106 return true;107}108 109// Returns the name of the environment variable corresponding to the110// given flag. For example, FlagToEnvVar("foo") will return111// "BENCHMARK_FOO" in the open-source version.112static std::string FlagToEnvVar(const char* flag) {113 const std::string flag_str(flag);114 115 std::string env_var;116 for (size_t i = 0; i != flag_str.length(); ++i)117 env_var += static_cast<char>(::toupper(flag_str.c_str()[i]));118 119 return env_var;120}121 122} // namespace123 124BENCHMARK_EXPORT125bool BoolFromEnv(const char* flag, bool default_val) {126 const std::string env_var = FlagToEnvVar(flag);127 const char* const value_str = getenv(env_var.c_str());128 return value_str == nullptr ? default_val : IsTruthyFlagValue(value_str);129}130 131BENCHMARK_EXPORT132int32_t Int32FromEnv(const char* flag, int32_t default_val) {133 const std::string env_var = FlagToEnvVar(flag);134 const char* const value_str = getenv(env_var.c_str());135 int32_t value = default_val;136 if (value_str == nullptr ||137 !ParseInt32(std::string("Environment variable ") + env_var, value_str,138 &value)) {139 return default_val;140 }141 return value;142}143 144BENCHMARK_EXPORT145double DoubleFromEnv(const char* flag, double default_val) {146 const std::string env_var = FlagToEnvVar(flag);147 const char* const value_str = getenv(env_var.c_str());148 double value = default_val;149 if (value_str == nullptr ||150 !ParseDouble(std::string("Environment variable ") + env_var, value_str,151 &value)) {152 return default_val;153 }154 return value;155}156 157BENCHMARK_EXPORT158const char* StringFromEnv(const char* flag, const char* default_val) {159 const std::string env_var = FlagToEnvVar(flag);160 const char* const value = getenv(env_var.c_str());161 return value == nullptr ? default_val : value;162}163 164BENCHMARK_EXPORT165std::map<std::string, std::string> KvPairsFromEnv(166 const char* flag, std::map<std::string, std::string> default_val) {167 const std::string env_var = FlagToEnvVar(flag);168 const char* const value_str = getenv(env_var.c_str());169 170 if (value_str == nullptr) return default_val;171 172 std::map<std::string, std::string> value;173 if (!ParseKvPairs("Environment variable " + env_var, value_str, &value)) {174 return default_val;175 }176 return value;177}178 179// Parses a string as a command line flag. The string should have180// the format "--flag=value". When def_optional is true, the "=value"181// part can be omitted.182//183// Returns the value of the flag, or nullptr if the parsing failed.184const char* ParseFlagValue(const char* str, const char* flag,185 bool def_optional) {186 // str and flag must not be nullptr.187 if (str == nullptr || flag == nullptr) return nullptr;188 189 // The flag must start with "--".190 const std::string flag_str = std::string("--") + std::string(flag);191 const size_t flag_len = flag_str.length();192 if (strncmp(str, flag_str.c_str(), flag_len) != 0) return nullptr;193 194 // Skips the flag name.195 const char* flag_end = str + flag_len;196 197 // When def_optional is true, it's OK to not have a "=value" part.198 if (def_optional && (flag_end[0] == '\0')) return flag_end;199 200 // If def_optional is true and there are more characters after the201 // flag name, or if def_optional is false, there must be a '=' after202 // the flag name.203 if (flag_end[0] != '=') return nullptr;204 205 // Returns the string after "=".206 return flag_end + 1;207}208 209BENCHMARK_EXPORT210bool ParseBoolFlag(const char* str, const char* flag, bool* value) {211 // Gets the value of the flag as a string.212 const char* const value_str = ParseFlagValue(str, flag, true);213 214 // Aborts if the parsing failed.215 if (value_str == nullptr) return false;216 217 // Converts the string value to a bool.218 *value = IsTruthyFlagValue(value_str);219 return true;220}221 222BENCHMARK_EXPORT223bool ParseInt32Flag(const char* str, const char* flag, int32_t* value) {224 // Gets the value of the flag as a string.225 const char* const value_str = ParseFlagValue(str, flag, false);226 227 // Aborts if the parsing failed.228 if (value_str == nullptr) return false;229 230 // Sets *value to the value of the flag.231 return ParseInt32(std::string("The value of flag --") + flag, value_str,232 value);233}234 235BENCHMARK_EXPORT236bool ParseDoubleFlag(const char* str, const char* flag, double* value) {237 // Gets the value of the flag as a string.238 const char* const value_str = ParseFlagValue(str, flag, false);239 240 // Aborts if the parsing failed.241 if (value_str == nullptr) return false;242 243 // Sets *value to the value of the flag.244 return ParseDouble(std::string("The value of flag --") + flag, value_str,245 value);246}247 248BENCHMARK_EXPORT249bool ParseStringFlag(const char* str, const char* flag, std::string* value) {250 // Gets the value of the flag as a string.251 const char* const value_str = ParseFlagValue(str, flag, false);252 253 // Aborts if the parsing failed.254 if (value_str == nullptr) return false;255 256 *value = value_str;257 return true;258}259 260BENCHMARK_EXPORT261bool ParseKeyValueFlag(const char* str, const char* flag,262 std::map<std::string, std::string>* value) {263 const char* const value_str = ParseFlagValue(str, flag, false);264 265 if (value_str == nullptr) return false;266 267 for (const auto& kvpair : StrSplit(value_str, ',')) {268 const auto kv = StrSplit(kvpair, '=');269 if (kv.size() != 2) return false;270 value->emplace(kv[0], kv[1]);271 }272 273 return true;274}275 276BENCHMARK_EXPORT277bool IsFlag(const char* str, const char* flag) {278 return (ParseFlagValue(str, flag, true) != nullptr);279}280 281BENCHMARK_EXPORT282bool IsTruthyFlagValue(const std::string& value) {283 if (value.size() == 1) {284 char v = value[0];285 return isalnum(v) &&286 !(v == '0' || v == 'f' || v == 'F' || v == 'n' || v == 'N');287 }288 if (!value.empty()) {289 std::string value_lower(value);290 std::transform(value_lower.begin(), value_lower.end(), value_lower.begin(),291 [](char c) { return static_cast<char>(::tolower(c)); });292 return !(value_lower == "false" || value_lower == "no" ||293 value_lower == "off");294 }295 return true;296}297 298} // end namespace benchmark299