diff options
author | humper@google.com <humper@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-01-14 18:49:19 +0000 |
---|---|---|
committer | humper@google.com <humper@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-01-14 18:49:19 +0000 |
commit | 7af56bee17764a0c118c8856a035bb3d27766969 (patch) | |
tree | 403db31d9a78f6102707544e334361dd92ac99d7 /src/utils/SkRTConf.cpp | |
parent | 50dd41017ad121b5f40f063d813ba517668fcfbc (diff) |
Runtime configuration system for skia. This will allow developers to control settings at launch time without relying on compile-time flags or recompilation. It can be used to turn features on and off, as well as to control numeric quantities to 'tune' algorithms. Once I make sure it's working across all platforms I'll send out a quick tutorial on its use.
Review URL: https://codereview.appspot.com/7098051
git-svn-id: http://skia.googlecode.com/svn/trunk@7158 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src/utils/SkRTConf.cpp')
-rw-r--r-- | src/utils/SkRTConf.cpp | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/src/utils/SkRTConf.cpp b/src/utils/SkRTConf.cpp new file mode 100644 index 0000000000..fcc51c33c3 --- /dev/null +++ b/src/utils/SkRTConf.cpp @@ -0,0 +1,325 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkRTConf.h" +#include "SkOSFile.h" + +SkRTConfRegistry::SkRTConfRegistry(): fConfs(100) { + + SkFILE *fp = sk_fopen(configFileLocation(), kRead_SkFILE_Flag); + + if (!fp) { + return; + } + + char *line = NULL; + size_t n = 0; + + while (!sk_feof(fp)) { + if (line) { + sk_free(line); + } + line = NULL; + + if (sk_getline(&line, &n, fp) == -1) break; + + char *commentptr = strchr(line, '#'); + if (commentptr == line) { + continue; + } + if (NULL != commentptr) { + char *tmp = (char *) sk_malloc_throw(commentptr-line+1); + strncpy(tmp, line, commentptr-line); + sk_free(line); + line = tmp; + } + + char sep[] = " \t"; + + char *keyptr = strtok(line, sep); + if (!keyptr) { + continue; + } + + char *valptr = strtok(NULL, sep); + if (!valptr) { + continue; + } + + SkString* key = new SkString(keyptr); + SkString* val = new SkString(valptr); + + fConfigFileKeys.append(1, &key); + fConfigFileValues.append(1, &val); + } + sk_fclose(fp); +} + +const char *SkRTConfRegistry::configFileLocation() const { + return "skia.conf"; // for now -- should probably do something fancier like home directories or whatever. +} + +// dump all known runtime config options to the file with their default values. +// to trigger this, make a config file of zero size. +void SkRTConfRegistry::possiblyDumpFile() const { + const char *path = configFileLocation(); + SkFILE *fp = sk_fopen(path, kRead_SkFILE_Flag); + if (!fp) { + return; + } + size_t configFileSize = sk_fgetsize(fp); + if (configFileSize == 0) { + printAll(path); + } + sk_fclose(fp); +} + +// Run through every provided configuration option and print a warning if the user hasn't +// declared a correponding configuration object somewhere. +void SkRTConfRegistry::validate() const { + for (int i = 0 ; i < fConfigFileKeys.count() ; i++) { + if (fConfs.find(fConfigFileKeys[i]->c_str()) == -1) { + SkDebugf("WARNING: You have config value %s in your configuration file, but I've never heard of that.\n", fConfigFileKeys[i]->c_str()); + } + } +} + +void SkRTConfRegistry::printAll(const char *fname) const { + SkWStream *o; + + if (NULL != fname) { + o = new SkFILEWStream(fname); + } else { + o = new SkDebugWStream(); + } + + ConfMap::Iter iter(fConfs); + SkTDArray<SkRTConfBase *> *confArray; + + while (iter.next(&confArray)) { + if (confArray->getAt(0)->isDefault()) { + o->writeText("# "); + } + confArray->getAt(0)->print(o); + o->newline(); + } + + delete o; +} + +void SkRTConfRegistry::printNonDefault(const char *fname) const { + SkWStream *o; + + if (NULL != fname) { + o = new SkFILEWStream(fname); + } else { + o = new SkDebugWStream(); + } + ConfMap::Iter iter(fConfs); + SkTDArray<SkRTConfBase *> *confArray; + + while (iter.next(&confArray)) { + if (!confArray->getAt(0)->isDefault()) { + confArray->getAt(0)->print(o); + o->newline(); + } + } + + delete o; +} + +// register a configuration variable after its value has been set by the parser. +// we maintain a vector of these things instead of just a single one because the +// user might set the value after initialization time and we need to have +// all the pointers lying around, not just one. +void SkRTConfRegistry::registerConf(SkRTConfBase *conf) { + SkTDArray<SkRTConfBase *> *confArray; + if (fConfs.find(conf->getName(), &confArray)) { + if (!conf->equals(confArray->getAt(0))) { + SkDebugf("WARNING: Skia config \"%s\" was registered more than once in incompatible ways.\n", conf->getName()); + } else { + confArray->append(1, &conf); + } + } else { + confArray = new SkTDArray<SkRTConfBase *>; + confArray->append(1, &conf); + fConfs.set(conf->getName(),confArray); + } +} + +template <typename T> T doParse(const char *s, bool *success ) { + SkDebugf("WARNING: Invoked non-specialized doParse function...\n"); + if (success) { + *success = false; + } + return (T) 0; +} + +template<> bool doParse<bool>(const char *s, bool *success) { + if (success) { + *success = true; + } + if (!strcmp(s,"1") || !strcmp(s,"true")) { + return true; + } + if (!strcmp(s,"0") || !strcmp(s,"false")) { + return false; + } + if (success) { + *success = false; + } + return false; +} + +template<> const char * doParse<const char *>(const char * s, bool *success) { + if (success) { + *success = true; + } + return s; +} + +template<> int doParse<int>(const char * s, bool *success) { + return atoi(s); +} + +template<> unsigned int doParse<unsigned int>(const char * s, bool *success) { + return (unsigned int) atoi(s); +} + +template<> float doParse<float>(const char * s, bool *success) { + return (float) atof(s); +} + +template<> double doParse<double>(const char * s, bool *success) { + return atof(s); +} + +static inline void str_replace(char *s, char search, char replace) { + for (char *ptr = s ; *ptr ; ptr++) { + if (*ptr == search) { + *ptr = replace; + } + } +} + +template<typename T> bool SkRTConfRegistry::parse(const char *name, T* value) { + SkString *str = NULL; + + for (int i = 0 ; i < fConfigFileKeys.count() ; i++) { + if (fConfigFileKeys[i]->equals(name)) { + str = fConfigFileValues[i]; + } + } + + SkString environment_variable("skia."); + environment_variable.append(name); + + const char *environment_value = getenv(environment_variable.c_str()); + if (environment_value) { + str->set(environment_value); + } else { + // apparently my shell doesn't let me have environment variables that + // have periods in them, so also let the user substitute underscores. + SkString underscore_environment_variable("skia_"); + char *underscore_name = SkStrDup(name); + str_replace(underscore_name,'.','_'); + underscore_environment_variable.append(underscore_name); + sk_free(underscore_name); + environment_value = getenv(underscore_environment_variable.c_str()); + if (environment_value) { + str->set(environment_value); + } + } + + if (!str) { + return false; + } + + bool success; + T new_value = doParse<T>(str->c_str(),&success); + if (success) { + *value = new_value; + } else { + SkDebugf("WARNING: Couldn't parse value \'%s\' for variable \'%s\'\n", str->c_str(), name); + } + return success; +} + +// need to explicitly instantiate the parsing function for every config type we might have... + +template bool SkRTConfRegistry::parse(const char *name, bool *value); +template bool SkRTConfRegistry::parse(const char *name, int *value); +template bool SkRTConfRegistry::parse(const char *name, unsigned int *value); +template bool SkRTConfRegistry::parse(const char *name, float *value); +template bool SkRTConfRegistry::parse(const char *name, double *value); +template bool SkRTConfRegistry::parse(const char *name, const char **value); + +template <typename T> void SkRTConfRegistry::set(const char *name, T value) { + + SkTDArray<SkRTConfBase *> *confArray; + if (!fConfs.find(name, &confArray)) { + SkDebugf("WARNING: Attempting to set configuration value \"%s\", but I've never heard of that.\n", name); + return; + } + + for (SkRTConfBase **confBase = confArray->begin(); confBase != confArray->end(); confBase++) { + + SkRTConf<bool> *concrete = dynamic_cast<SkRTConf<bool> *>(*confBase); + + if (concrete) { + concrete->set(value); + } + } +} + +template void SkRTConfRegistry::set(const char *name, bool value); +template void SkRTConfRegistry::set(const char *name, int value); +template void SkRTConfRegistry::set(const char *name, unsigned int value); +template void SkRTConfRegistry::set(const char *name, float value); +template void SkRTConfRegistry::set(const char *name, double value); +template void SkRTConfRegistry::set(const char *name, char * value); + +template<> void SkRTConf<bool>::doPrint(char *s) const { + char tmp[30]; + sprintf(tmp, "%s # [%s]", fValue ? "true" : "false", fDefault ? "true" : "false"); + sprintf(s, "%-30.30s", tmp); +} + +template<> void SkRTConf<int>::doPrint(char *s) const { + char tmp[30]; + sprintf(tmp, "%d # [%d]", fValue, fDefault); + sprintf(s, "%-30.30s", tmp); +} + +template<> void SkRTConf<unsigned int>::doPrint(char *s) const { + char tmp[30]; + sprintf(tmp, "%u # [%u]", fValue, fDefault); + sprintf(s, "%-30.30s", tmp); +} + +template<> void SkRTConf<float>::doPrint(char *s) const { + char tmp[30]; + sprintf(tmp, "%6.6f # [%6.6f]", fValue, fDefault); + sprintf(s, "%-30.30s", tmp); +} + +template<> void SkRTConf<double>::doPrint(char *s) const { + char tmp[30]; + sprintf(tmp, "%6.6f # [%6.6f]", fValue, fDefault); + sprintf(s, "%-30.30s", tmp); +} + +template<> void SkRTConf<const char *>::doPrint(char *s) const { + char tmp[30]; + sprintf(tmp, "%s # [%s]", fValue, fDefault); + sprintf(s, "%-30.30s", tmp); +} + +SkRTConfRegistry &skRTConfRegistry() { + static SkRTConfRegistry r; + return r; +} |