aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/utils/SkRTConf.cpp
diff options
context:
space:
mode:
authorGravatar humper@google.com <humper@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-01-14 18:49:19 +0000
committerGravatar humper@google.com <humper@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-01-14 18:49:19 +0000
commit7af56bee17764a0c118c8856a035bb3d27766969 (patch)
tree403db31d9a78f6102707544e334361dd92ac99d7 /src/utils/SkRTConf.cpp
parent50dd41017ad121b5f40f063d813ba517668fcfbc (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.cpp325
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;
+}