aboutsummaryrefslogtreecommitdiffstats
path: root/CommonLibs
diff options
context:
space:
mode:
Diffstat (limited to 'CommonLibs')
-rw-r--r--CommonLibs/BitVector.cpp28
-rw-r--r--CommonLibs/BitVector.h23
-rw-r--r--CommonLibs/Configuration.cpp872
-rw-r--r--CommonLibs/Configuration.h160
-rw-r--r--CommonLibs/ConfigurationTest.cpp52
-rw-r--r--CommonLibs/Interthread.h156
-rw-r--r--CommonLibs/InterthreadTest.cpp3
-rw-r--r--CommonLibs/LinkedLists.cpp16
-rw-r--r--CommonLibs/LinkedLists.h81
-rw-r--r--CommonLibs/LogTest.cpp3
-rw-r--r--CommonLibs/Logger.cpp98
-rw-r--r--CommonLibs/Logger.h41
-rw-r--r--CommonLibs/Makefile.am28
-rw-r--r--CommonLibs/MemoryLeak.h111
-rw-r--r--CommonLibs/ScalarTypes.h136
-rw-r--r--CommonLibs/Sockets.cpp9
-rw-r--r--CommonLibs/Threads.cpp5
-rw-r--r--CommonLibs/Threads.h8
-rw-r--r--CommonLibs/Timeval.h1
-rw-r--r--CommonLibs/URLEncode.cpp25
-rw-r--r--CommonLibs/URLEncodeTest.cpp17
-rw-r--r--CommonLibs/Utils.cpp211
-rw-r--r--CommonLibs/Utils.h148
-rw-r--r--CommonLibs/Vector.h13
-rw-r--r--CommonLibs/VectorTest.cpp4
25 files changed, 2034 insertions, 215 deletions
diff --git a/CommonLibs/BitVector.cpp b/CommonLibs/BitVector.cpp
index 54a3edc..7487834 100644
--- a/CommonLibs/BitVector.cpp
+++ b/CommonLibs/BitVector.cpp
@@ -29,6 +29,7 @@
#include "BitVector.h"
#include <iostream>
#include <stdio.h>
+#include <sstream>
using namespace std;
@@ -274,9 +275,6 @@ void BitVector::unmap(const unsigned *map, size_t mapSize, BitVector& dest) cons
-
-
-
ostream& operator<<(ostream& os, const BitVector& hv)
{
for (size_t i=0; i<hv.size(); i++) {
@@ -527,6 +525,22 @@ void SoftVector::decode(ViterbiR2O4 &decoder, BitVector& target) const
+// (pat) Added 6-22-2012
+float SoftVector::getEnergy(float *plow) const
+{
+ const SoftVector &vec = *this;
+ int len = vec.size();
+ float avg = 0; float low = 1;
+ for (int i = 0; i < len; i++) {
+ float bit = vec[i];
+ float energy = 2*((bit < 0.5) ? (0.5-bit) : (bit-0.5));
+ if (energy < low) low = energy;
+ avg += energy/len;
+ }
+ if (plow) { *plow = low; }
+ return avg;
+}
+
ostream& operator<<(ostream& os, const SoftVector& sv)
{
@@ -578,6 +592,14 @@ void BitVector::hex(ostream& os) const
os << std::dec;
}
+std::string BitVector::hexstr() const
+{
+ std::ostringstream ss;
+ hex(ss);
+ return ss.str();
+}
+
+
bool BitVector::unhex(const char* src)
{
// Assumes MSB-first packing.
diff --git a/CommonLibs/BitVector.h b/CommonLibs/BitVector.h
index 572e6b4..e244be7 100644
--- a/CommonLibs/BitVector.h
+++ b/CommonLibs/BitVector.h
@@ -314,6 +314,9 @@ class BitVector : public Vector<char> {
void fillFieldReversed(size_t writeIndex, uint64_t value, unsigned length);
void writeField(size_t& writeIndex, uint64_t value, unsigned length);
void writeFieldReversed(size_t& writeIndex, uint64_t value, unsigned length);
+ void write0(size_t& writeIndex) { writeField(writeIndex,0,1); }
+ void write1(size_t& writeIndex) { writeField(writeIndex,1,1); }
+
//@}
/** Sum of bits. */
@@ -333,11 +336,26 @@ class BitVector : public Vector<char> {
/** Make a hexdump string. */
void hex(std::ostream&) const;
+ std::string hexstr() const;
/** Unpack from a hexdump string.
* @returns true on success, false on error. */
bool unhex(const char*);
+ void set(BitVector other) // That's right. No ampersand.
+ {
+ clear();
+ mData=other.mData;
+ mStart=other.mStart;
+ mEnd=other.mEnd;
+ other.mData=NULL;
+ }
+
+ void settfb(int i, int j) const
+ {
+ mStart[i] = j;
+ }
+
};
@@ -412,6 +430,11 @@ class SoftVector: public Vector<float> {
/** Decode soft symbols with the GSM rate-1/2 Viterbi decoder. */
void decode(ViterbiR2O4 &decoder, BitVector& target) const;
+ // (pat) How good is the SoftVector in the sense of the bits being solid?
+ // Result of 1 is perfect and 0 means all the bits were 0.5
+ // If plow is non-NULL, also return the lowest energy bit.
+ float getEnergy(float *low=0) const;
+
/** Fill with "unknown" values. */
void unknown() { fill(0.5F); }
diff --git a/CommonLibs/Configuration.cpp b/CommonLibs/Configuration.cpp
index 5dcc277..bda6865 100644
--- a/CommonLibs/Configuration.cpp
+++ b/CommonLibs/Configuration.cpp
@@ -32,6 +32,12 @@
#include <iostream>
#include <string.h>
+#ifdef DEBUG_CONFIG
+#define debugLogEarly gLogEarly
+#else
+#define debugLogEarly
+#endif
+
using namespace std;
@@ -57,7 +63,7 @@ float ConfigurationRecord::floatNumber() const
}
-ConfigurationTable::ConfigurationTable(const char* filename, const char *wCmdName)
+ConfigurationTable::ConfigurationTable(const char* filename, const char *wCmdName, ConfigurationKeyMap wSchema)
{
gLogEarly(LOG_INFO, "opening configuration table from path %s", filename);
// Connect to the database.
@@ -78,35 +84,440 @@ ConfigurationTable::ConfigurationTable(const char* filename, const char *wCmdNam
if (!sqlite3_command(mDB,createConfigTable)) {
gLogEarly(LOG_EMERG, "cannot create configuration table in database at %s, error message: %s", filename, sqlite3_errmsg(mDB));
}
+
+ // Build CommonLibs schema
+ ConfigurationKey *tmp;
+ tmp = new ConfigurationKey("Log.Alarms.Max","20",
+ "alarms",
+ ConfigurationKey::CUSTOMER,
+ ConfigurationKey::VALRANGE,
+ "10:20",// educated guess
+ false,
+ "Maximum number of alarms to remember inside the application."
+ );
+ mSchema[tmp->getName()] = *tmp;
+ free(tmp);
+
+ tmp = new ConfigurationKey("Log.File","",
+ "",
+ ConfigurationKey::DEVELOPER,
+ ConfigurationKey::FILEPATH_OPT,// audited
+ "",
+ false,
+ "Path to use for textfile based logging. "
+ "By default, this feature is disabled. "
+ "To enable, specify an absolute path to the file you wish to use, eg: /tmp/my-debug.log. "
+ "To disable again, execute \"unconfig Log.File\"."
+ );
+ mSchema[tmp->getName()] = *tmp;
+ free(tmp);
+
+ tmp = new ConfigurationKey("Log.Level","NOTICE",
+ "",
+ ConfigurationKey::CUSTOMER,
+ ConfigurationKey::CHOICE,
+ "EMERG|EMERGENCY - report serious faults associated with service failure or hardware damage,"
+ "ALERT|ALERT - report likely service disruption caused by misconfiguration or poor connectivity,"
+ "CRIT|CRITICAL - report anomalous events that are likely to degrade service,"
+ "ERR|ERROR - report internal errors of the software that may result in degradation of service in unusual circumstances,"
+ "WARNING|WARNING - report anomalous events that may indicate a degradation of normal service,"
+ "NOTICE|NOTICE - report anomalous events that probably do not affect service but may be of interest to network operators,"
+ "INFO|INFORMATION - report normal events,"
+ "DEBUG|DEBUG - only for use by developers and will degrade system performance",
+ false,
+ "Default logging level when no other level is defined for a file."
+ );
+ mSchema[tmp->getName()] = *tmp;
+ free(tmp);
+
+ // Add application specific schema
+ mSchema.insert(wSchema.begin(), wSchema.end());
+
+ // Init the cross checking callback to something predictable
+ mCrossCheck = NULL;
}
+string ConfigurationTable::getDefaultSQL(const std::string& program, const std::string& version)
+{
+ stringstream ss;
+ ConfigurationKeyMap::iterator mp;
+
+ ss << "--" << endl;
+ ss << "-- This file was generated using: " << program << " --gensql" << endl;
+ ss << "-- binary version: " << version << endl;
+ ss << "--" << endl;
+ ss << "-- Future changes should not be put in this file directly but" << endl;
+ ss << "-- rather in the program's ConfigurationKey schema." << endl;
+ ss << "--" << endl;
+ ss << "PRAGMA foreign_keys=OFF;" << endl;
+ ss << "BEGIN TRANSACTION;" << endl;
+ ss << "CREATE TABLE CONFIG ( KEYSTRING TEXT UNIQUE NOT NULL, VALUESTRING TEXT, STATIC INTEGER DEFAULT 0, OPTIONAL INTEGER DEFAULT 0, COMMENTS TEXT DEFAULT '');" << endl;
+
+ mp = mSchema.begin();
+ while (mp != mSchema.end()) {
+ ss << "INSERT INTO \"CONFIG\" VALUES(";
+ // name
+ ss << "'" << mp->first << "',";
+ // default
+ ss << "'" << mp->second.getDefaultValue() << "',";
+ // static
+ if (mp->second.isStatic()) {
+ ss << "1";
+ } else {
+ ss << "0";
+ }
+ ss << ",";
+ // optional
+ ss << "0,";
+ // description
+ ss << "'";
+ if (mp->second.getType() == ConfigurationKey::BOOLEAN) {
+ ss << "1=enabled, 0=disabled - ";
+ }
+ ss << mp->second.getDescription();
+ if (mp->second.isStatic()) {
+ ss << " Static.";
+ }
+ ss << "'";
+ ss << ");" << endl;
+ mp++;
+ }
+
+ ss << "COMMIT;" << endl;
+ ss << endl;
+ return ss.str();
+}
-bool ConfigurationTable::defines(const string& key)
+string ConfigurationTable::getTeX(const std::string& program, const std::string& version)
{
- assert(mDB);
- ScopedLock lock(mLock);
+ stringstream ss;
+ ConfigurationKeyMap::iterator mp;
+
+ ss << "% START AUTO-GENERATED CONTENT" << endl;
+ ss << "% -- these sections were generated using: " << program << " --gentex" << endl;
+ ss << "% -- binary version: " << version << endl;
+
+ ss << "\\subsection{Customer Site Parameters}" << endl;
+ ss << "These parameters must be changed to fit your site." << endl;
+ ss << "\\begin{itemize}" << endl;
+ mp = mSchema.begin();
+ while (mp != mSchema.end()) {
+ if (mp->second.getVisibility() == ConfigurationKey::CUSTOMERSITE) {
+ ss << " \\item ";
+ // name
+ ss << mp->first << " -- ";
+ // description
+ ss << mp->second.getDescription();
+ ss << endl;
+ }
+ mp++;
+ }
+ ss << "\\end{itemize}" << endl;
+ ss << endl;
+
+ ss << "\\subsection{Customer Tuneable Parameters}" << endl;
+ ss << "These parameters can be changed to optimize your site." << endl;
+ ss << "\\begin{itemize}" << endl;
+ mp = mSchema.begin();
+ while (mp != mSchema.end()) {
+ if (mp->second.getVisibility() != ConfigurationKey::CUSTOMERSITE &&
+ (
+ mp->second.getVisibility() == ConfigurationKey::CUSTOMER ||
+ mp->second.getVisibility() == ConfigurationKey::CUSTOMERTUNE ||
+ mp->second.getVisibility() == ConfigurationKey::CUSTOMERWARN
+ )) {
+ ss << " \\item ";
+ // name
+ ss << mp->first << " -- ";
+ // description
+ ss << mp->second.getDescription();
+ ss << endl;
+ }
+ mp++;
+ }
+ ss << "\\end{itemize}" << endl;
+ ss << endl;
+
+ ss << "\\subsection{Developer/Factory Parameters}" << endl;
+ ss << "These parameters should only be changed by when developing new code." << endl;
+ ss << "\\begin{itemize}" << endl;
+ mp = mSchema.begin();
+ while (mp != mSchema.end()) {
+ if (mp->second.getVisibility() == ConfigurationKey::FACTORY ||
+ mp->second.getVisibility() == ConfigurationKey::DEVELOPER) {
+ ss << " \\item ";
+ // name
+ ss << mp->first << " -- ";
+ // description
+ ss << mp->second.getDescription();
+ ss << endl;
+ }
+ mp++;
+ }
+ ss << "\\end{itemize}" << endl;
+ ss << "% END AUTO-GENERATED CONTENT" << endl;
+ ss << endl;
- // Check the cache.
- checkCacheAge();
- ConfigurationMap::const_iterator where = mCache.find(key);
- if (where!=mCache.end()) return where->second.defined();
+ string tmp = Utils::replaceAll(ss.str(), "^", "\\^");
+ return Utils::replaceAll(tmp, "_", "\\_");
+}
- // Check the database.
- char *value = NULL;
- sqlite3_single_lookup(mDB,"CONFIG","KEYSTRING",key.c_str(),"VALUESTRING",value);
+bool ConfigurationTable::defines(const string& key)
+{
+ try {
+ ScopedLock lock(mLock);
+ return lookup(key).defined();
+ } catch (ConfigurationTableKeyNotFound) {
+ debugLogEarly(LOG_ALERT, "configuration parameter %s not found", key.c_str());
+ return false;
+ }
+}
- // Cache the result.
- if (value) {
- mCache[key] = ConfigurationRecord(value);
- free(value);
- return true;
+bool ConfigurationTable::keyDefinedInSchema(const std::string& name)
+{
+ return mSchema.find(name) == mSchema.end() ? false : true;
+}
+
+bool ConfigurationTable::isValidValue(const std::string& name, const std::string& val) {
+ bool ret = false;
+
+ ConfigurationKey key = mSchema[name];
+
+ switch (key.getType()) {
+ case ConfigurationKey::BOOLEAN: {
+ if (val == "1" || val == "0") {
+ ret = true;
+ }
+ break;
+ }
+
+ case ConfigurationKey::CHOICE_OPT: {
+ if (val.length() == 0) {
+ ret = true;
+ break;
+ }
+ }
+ case ConfigurationKey::CHOICE: {
+ int startPos = -1;
+ uint endPos = 0;
+
+ std::string tmp = key.getValidValues();
+
+ do {
+ startPos++;
+ if ((endPos = tmp.find('|', startPos)) != std::string::npos) {
+ if (val == tmp.substr(startPos, endPos-startPos)) {
+ ret = true;
+ break;
+ }
+ } else {
+ if (val == tmp.substr(startPos, tmp.find(',', startPos)-startPos)) {
+ ret = true;
+ break;
+ }
+ }
+
+ } while ((startPos = tmp.find(',', startPos)) != (int)std::string::npos);
+ break;
+ }
+
+ case ConfigurationKey::CIDR_OPT: {
+ if (val.length() == 0) {
+ ret = true;
+ break;
+ }
+ }
+ case ConfigurationKey::CIDR: {
+ uint delimiter;
+ std::string ip;
+ int cidr = -1;
+
+ delimiter = val.find('/');
+ if (delimiter != std::string::npos) {
+ ip = val.substr(0, delimiter);
+ std::stringstream(val.substr(delimiter+1)) >> cidr;
+ if (ConfigurationKey::isValidIP(ip) && 0 <= cidr && cidr <= 32) {
+ ret = true;
+ }
+ }
+ break;
+ }
+
+ case ConfigurationKey::FILEPATH_OPT: {
+ if (val.length() == 0) {
+ ret = true;
+ break;
+ }
+ }
+ case ConfigurationKey::FILEPATH: {
+ regex_t r;
+ const char* expression = "^[a-zA-Z0-9/_.-]+$";
+ int result = regcomp(&r, expression, REG_EXTENDED);
+ if (result) {
+ char msg[256];
+ regerror(result,&r,msg,255);
+ break;//abort();
+ }
+ if (regexec(&r, val.c_str(), 0, NULL, 0)==0) {
+ ret = true;
+ }
+ regfree(&r);
+ break;
+ }
+
+ case ConfigurationKey::IPADDRESS_OPT: {
+ if (val.length() == 0) {
+ ret = true;
+ break;
+ }
+ }
+ case ConfigurationKey::IPADDRESS: {
+ ret = ConfigurationKey::isValidIP(val);
+ break;
+ }
+
+ case ConfigurationKey::IPANDPORT: {
+ uint delimiter;
+ std::string ip;
+ int port = -1;
+
+ delimiter = val.find(':');
+ if (delimiter != std::string::npos) {
+ ip = val.substr(0, delimiter);
+ std::stringstream(val.substr(delimiter+1)) >> port;
+ if (ConfigurationKey::isValidIP(ip) && 1 <= port && port <= 65535) {
+ ret = true;
+ }
+ }
+ break;
+ }
+
+ case ConfigurationKey::MIPADDRESS_OPT: {
+ if (val.length() == 0) {
+ ret = true;
+ break;
+ }
+ }
+ case ConfigurationKey::MIPADDRESS: {
+ int startPos = -1;
+ uint endPos = 0;
+
+ do {
+ startPos++;
+ endPos = val.find(' ', startPos);
+ if (ConfigurationKey::isValidIP(val.substr(startPos, endPos-startPos))) {
+ ret = true;
+ } else {
+ ret = false;
+ break;
+ }
+
+ } while ((startPos = endPos) != (int)std::string::npos);
+ break;
+ }
+
+ case ConfigurationKey::PORT_OPT: {
+ if (val.length() == 0) {
+ ret = true;
+ break;
+ }
+ }
+ case ConfigurationKey::PORT: {
+ int intVal;
+
+ std::stringstream(val) >> intVal;
+
+ if (1 <= intVal && intVal <= 65535) {
+ ret = true;
+ }
+ break;
+ }
+
+ case ConfigurationKey::REGEX_OPT: {
+ if (val.length() == 0) {
+ ret = true;
+ break;
+ }
+ }
+ case ConfigurationKey::REGEX: {
+ regex_t r;
+ const char* expression = val.c_str();
+ int result = regcomp(&r, expression, REG_EXTENDED);
+ if (result) {
+ char msg[256];
+ regerror(result,&r,msg,255);
+ } else {
+ ret = true;
+ }
+ regfree(&r);
+ break;
+ }
+
+ case ConfigurationKey::STRING_OPT: {
+ if (val.length() == 0) {
+ ret = true;
+ break;
+ }
+ }
+ case ConfigurationKey::STRING: {
+ regex_t r;
+ const char* expression = key.getValidValues().c_str();
+ int result = regcomp(&r, expression, REG_EXTENDED);
+ if (result) {
+ char msg[256];
+ regerror(result,&r,msg,255);
+ break;//abort();
+ }
+ if (regexec(&r, val.c_str(), 0, NULL, 0)==0) {
+ ret = true;
+ }
+ regfree(&r);
+ break;
+ }
+
+ case ConfigurationKey::VALRANGE: {
+ regex_t r;
+ int result;
+ if (key.getValidValues().find('.') != std::string::npos) {
+ result = regcomp(&r, "^[0-9.-]+$", REG_EXTENDED);
+ } else {
+ result = regcomp(&r, "^[0-9-]+$", REG_EXTENDED);
+ }
+ if (result) {
+ char msg[256];
+ regerror(result,&r,msg,255);
+ break;//abort();
+ }
+ if (regexec(&r, val.c_str(), 0, NULL, 0)!=0) {
+ ret = false;
+ } else if (key.getValidValues().find('.') != std::string::npos) {
+ ret = ConfigurationKey::isInValRange<float>(key, val, false);
+ } else {
+ ret = ConfigurationKey::isInValRange<int>(key, val, true);
+ }
+
+ regfree(&r);
+ break;
+ }
}
-
- mCache[key] = ConfigurationRecord(false);
- return false;
+
+ return ret;
}
+ConfigurationKeyMap ConfigurationTable::getSimilarKeys(const std::string& snippet) {
+ ConfigurationKeyMap tmp;
+
+ ConfigurationKeyMap::const_iterator mp = mSchema.begin();
+ while (mp != mSchema.end()) {
+ if (mp->first.find(snippet) != std::string::npos) {
+ tmp[mp->first] = mp->second;
+ }
+ mp++;
+ }
+
+ return tmp;
+}
const ConfigurationRecord& ConfigurationTable::lookup(const string& key)
{
@@ -129,15 +540,18 @@ const ConfigurationRecord& ConfigurationTable::lookup(const string& key)
sqlite3_single_lookup(mDB,"CONFIG",
"KEYSTRING",key.c_str(),"VALUESTRING",value);
- // Nothing defined?
- if (!value) {
- // Cache the failure.
+ // value found, cache the result
+ if (value) {
+ mCache[key] = ConfigurationRecord(value);
+ // key definition found, cache the default
+ } else if (keyDefinedInSchema(key)) {
+ mCache[key] = ConfigurationRecord(mSchema[key].getDefaultValue());
+ // total miss, cache the error
+ } else {
mCache[key] = ConfigurationRecord(false);
throw ConfigurationTableKeyNotFound(key);
}
- // Cache the result.
- mCache[key] = ConfigurationRecord(value);
free(value);
// Leave mLock locked. The caller holds it still.
@@ -146,22 +560,13 @@ const ConfigurationRecord& ConfigurationTable::lookup(const string& key)
-bool ConfigurationTable::isStatic(const string& key) const
-{
- assert(mDB);
- unsigned stat;
- bool success = sqlite3_single_lookup(mDB,"CONFIG","KEYSTRING",key.c_str(),"STATIC",stat);
- if (success) return (bool)stat;
- return false;
-}
-
-bool ConfigurationTable::isRequired(const string& key) const
+bool ConfigurationTable::isStatic(const string& key)
{
- assert(mDB);
- unsigned optional;
- bool success = sqlite3_single_lookup(mDB,"CONFIG","KEYSTRING",key.c_str(),"OPTIONAL",optional);
- if (success) return !((bool)optional);
- return false;
+ if (keyDefinedInSchema(key)) {
+ return mSchema[key].isStatic();
+ } else {
+ return false;
+ }
}
@@ -175,30 +580,20 @@ string ConfigurationTable::getStr(const string& key)
return lookup(key).value();
} catch (ConfigurationTableKeyNotFound) {
// Raise an alert and re-throw the exception.
- gLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
+ debugLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
throw ConfigurationTableKeyNotFound(key);
}
}
-string ConfigurationTable::getStr(const string& key, const char* defaultValue)
-{
- try {
- ScopedLock lock(mLock);
- return lookup(key).value();
- } catch (ConfigurationTableKeyNotFound) {
- gLogEarly(LOG_NOTICE, "deinfing missing parameter %s with value %s", key.c_str(),defaultValue);
- set(key,defaultValue);
- return string(defaultValue);
- }
-}
-
bool ConfigurationTable::getBool(const string& key)
{
try {
return getNum(key) != 0;
} catch (ConfigurationTableKeyNotFound) {
- return false;
+ // Raise an alert and re-throw the exception.
+ debugLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
+ throw ConfigurationTableKeyNotFound(key);
}
}
@@ -211,32 +606,24 @@ long ConfigurationTable::getNum(const string& key)
return lookup(key).number();
} catch (ConfigurationTableKeyNotFound) {
// Raise an alert and re-throw the exception.
- gLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
+ debugLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
throw ConfigurationTableKeyNotFound(key);
}
}
-long ConfigurationTable::getNum(const string& key, long defaultValue)
+float ConfigurationTable::getFloat(const string& key)
{
try {
ScopedLock lock(mLock);
- return lookup(key).number();
+ return lookup(key).floatNumber();
} catch (ConfigurationTableKeyNotFound) {
- gLogEarly(LOG_NOTICE, "deinfing missing parameter %s with value %ld", key.c_str(),defaultValue);
- set(key,defaultValue);
- return defaultValue;
+ // Raise an alert and re-throw the exception.
+ debugLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
+ throw ConfigurationTableKeyNotFound(key);
}
}
-
-float ConfigurationTable::getFloat(const string& key)
-{
- // We need the lock because rec is a reference into the cache.
- ScopedLock lock(mLock);
- return lookup(key).floatNumber();
-}
-
std::vector<string> ConfigurationTable::getVectorOfStrings(const string& key)
{
// Look up the string.
@@ -247,7 +634,7 @@ std::vector<string> ConfigurationTable::getVectorOfStrings(const string& key)
line = strdup(rec.value().c_str());
} catch (ConfigurationTableKeyNotFound) {
// Raise an alert and re-throw the exception.
- gLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
+ debugLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
throw ConfigurationTableKeyNotFound(key);
}
@@ -268,17 +655,6 @@ std::vector<string> ConfigurationTable::getVectorOfStrings(const string& key)
}
-std::vector<string> ConfigurationTable::getVectorOfStrings(const string& key, const char* defaultValue){
- try {
- return getVectorOfStrings(key);
- } catch (ConfigurationTableKeyNotFound) {
- set(key,defaultValue);
- return getVectorOfStrings(key);
- }
-}
-
-
-
std::vector<unsigned> ConfigurationTable::getVector(const string& key)
{
// Look up the string.
@@ -289,7 +665,7 @@ std::vector<unsigned> ConfigurationTable::getVector(const string& key)
line = strdup(rec.value().c_str());
} catch (ConfigurationTableKeyNotFound) {
// Raise an alert and re-throw the exception.
- gLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
+ debugLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
throw ConfigurationTableKeyNotFound(key);
}
@@ -310,25 +686,9 @@ std::vector<unsigned> ConfigurationTable::getVector(const string& key)
}
-bool ConfigurationTable::unset(const string& key)
-{
- assert(mDB);
- if (!defines(key)) return true;
- if (isRequired(key)) return false;
-
- ScopedLock lock(mLock);
- // Clear the cache entry and the database.
- ConfigurationMap::iterator where = mCache.find(key);
- if (where!=mCache.end()) mCache.erase(where);
- // Don't delete it; just set VALUESTRING to NULL.
- string cmd = "UPDATE CONFIG SET VALUESTRING=NULL WHERE KEYSTRING=='"+key+"'";
- return sqlite3_command(mDB,cmd.c_str());
-}
-
bool ConfigurationTable::remove(const string& key)
{
assert(mDB);
- if (isRequired(key)) return false;
ScopedLock lock(mLock);
// Clear the cache entry and the database.
@@ -352,14 +712,43 @@ void ConfigurationTable::find(const string& pat, ostream& os) const
while (src==SQLITE_ROW) {
const char* value = (const char*)sqlite3_column_text(stmt,1);
os << sqlite3_column_text(stmt,0) << " ";
- if (value) os << value << endl;
- else os << "(null)" << endl;
+ int len = 0;
+ if (value) {
+ len = strlen(value);
+ }
+ if (len && value) os << value << endl;
+ else os << "(disabled)" << endl;
src = sqlite3_run_query(mDB,stmt);
}
sqlite3_finalize(stmt);
}
+ConfigurationRecordMap ConfigurationTable::getAllPairs() const
+{
+ ConfigurationRecordMap tmp;
+
+ // Prepare the statement.
+ string cmd = "SELECT KEYSTRING,VALUESTRING FROM CONFIG";
+ sqlite3_stmt *stmt;
+ if (sqlite3_prepare_statement(mDB,&stmt,cmd.c_str())) return tmp;
+ // Read the result.
+ int src = sqlite3_run_query(mDB,stmt);
+ while (src==SQLITE_ROW) {
+ const char* key = (const char*)sqlite3_column_text(stmt,0);
+ const char* value = (const char*)sqlite3_column_text(stmt,1);
+ if (key && value) {
+ tmp[string(key)] = ConfigurationRecord(value);
+ } else if (key && !value) {
+ tmp[string(key)] = ConfigurationRecord(false);
+ }
+ src = sqlite3_run_query(mDB,stmt);
+ }
+ sqlite3_finalize(stmt);
+
+ return tmp;
+}
+
bool ConfigurationTable::set(const string& key, const string& value)
{
assert(mDB);
@@ -428,6 +817,21 @@ void ConfigurationTable::setUpdateHook(void(*func)(void *,int ,char const *,char
}
+void ConfigurationTable::setCrossCheckHook(vector<string> (*wCrossCheck)(const string&))
+{
+ mCrossCheck = wCrossCheck;
+}
+
+
+vector<string> ConfigurationTable::crossCheck(const string& key) {
+ vector<string> ret;
+
+ if (mCrossCheck != NULL) {
+ ret = mCrossCheck(key);
+ }
+
+ return ret;
+}
void HashString::computeHash()
{
@@ -474,5 +878,277 @@ void SimpleKeyValue::addItems(const char* pairs_orig)
}
+bool ConfigurationKey::isValidIP(const std::string& ip) {
+ struct sockaddr_in sa;
+ return inet_pton(AF_INET, ip.c_str(), &(sa.sin_addr)) != 0;
+}
+
+
+void ConfigurationKey::getMinMaxStepping(const ConfigurationKey &key, std::string &min, std::string &max, std::string &stepping) {
+ uint delimiter;
+ int startPos;
+ uint endPos;
+
+ std::string tmp = key.getValidValues();
+ stepping = "1";
+
+ // grab steps if they're defined
+ startPos = tmp.find('(');
+ if (startPos != (int)std::string::npos) {
+ endPos = tmp.find(')');
+ stepping = tmp.substr(startPos+1, endPos-startPos-1);
+ tmp = tmp.substr(0, startPos);
+ }
+ startPos = 0;
+
+ delimiter = tmp.find(':', startPos);
+ min = tmp.substr(startPos, delimiter-startPos);
+ max = tmp.substr(delimiter+1, tmp.find(',', delimiter)-delimiter-1);
+}
+
+
+template<class T> bool ConfigurationKey::isInValRange(const ConfigurationKey &key, const std::string& val, const bool isInteger) {
+ bool ret = false;
+
+ T convVal;
+ T min;
+ T max;
+ T steps;
+ std::string strMin;
+ std::string strMax;
+ std::string strSteps;
+
+ std::stringstream(val) >> convVal;
+
+ ConfigurationKey::getMinMaxStepping(key, strMin, strMax, strSteps);
+ std::stringstream(strMin) >> min;
+ std::stringstream(strMax) >> max;
+ std::stringstream(strSteps) >> steps;
+
+ // TODO : only ranges checked, steps not enforced
+ if (isInteger) {
+ if (val.find('.') == std::string::npos && min <= convVal && convVal <= max) {
+ ret = true;
+ }
+ } else {
+ if (min <= convVal && convVal <= max) {
+ ret = true;
+ }
+ }
+
+ return ret;
+}
+
+const std::string ConfigurationKey::getARFCNsString() {
+ stringstream ss;
+ int i;
+ float downlink;
+ float uplink;
+
+ // 128:251 GSM850
+ downlink = 869.2;
+ uplink = 824.2;
+ for (i = 128; i <= 251; i++) {
+ ss << i << "|GSM850 #" << i << " : " << downlink << " MHz downlink / " << uplink << " MHz uplink,";
+ downlink += 0.2;
+ uplink += 0.2;
+ }
+
+ // 1:124 PGSM900
+ downlink = 935.2;
+ uplink = 890.2;
+ for (i = 1; i <= 124; i++) {
+ ss << i << "|PGSM900 #" << i << " : " << downlink << " MHz downlink / " << uplink << " MHz uplink,";
+ downlink += 0.2;
+ uplink += 0.2;
+ }
+
+ // 512:885 DCS1800
+ downlink = 1805.2;
+ uplink = 1710.2;
+ for (i = 512; i <= 885; i++) {
+ ss << i << "|DCS1800 #" << i << " : " << downlink << " MHz downlink / " << uplink << " MHz uplink,";
+ downlink += 0.2;
+ uplink += 0.2;
+ }
+
+ // 512:810 PCS1900
+ downlink = 1930.2;
+ uplink = 1850.2;
+ for (i = 512; i <= 810; i++) {
+ ss << i << "|PCS1900 #" << i << " : " << downlink << " MHz downlink / " << uplink << " MHz uplink,";
+ downlink += 0.2;
+ uplink += 0.2;
+ }
+
+ ss << endl;
+
+ return ss.str();
+}
+
+const std::string ConfigurationKey::visibilityLevelToString(const ConfigurationKey::VisibilityLevel& visibility) {
+ std::string ret = "UNKNOWN ERROR";
+
+ switch (visibility) {
+ case ConfigurationKey::CUSTOMER:
+ ret = "customer - can be freely changed by the customer without any detriment to their system";
+ break;
+ case ConfigurationKey::CUSTOMERSITE:
+ ret = "customer site - these values are different for each BTS and should not be left default";
+ break;
+ case ConfigurationKey::CUSTOMERTUNE:
+ ret = "customer tune - should only be changed to tune an installation to better suit the physical environment or MS usage pattern";
+ break;
+ case ConfigurationKey::CUSTOMERWARN:
+ ret = "customer warn - a warning will be presented and confirmation required before changing this sensitive setting";
+ break;
+ case ConfigurationKey::DEVELOPER:
+ ret = "developer - should only be changed by developers to debug/optimize the implementation";
+ break;
+ case ConfigurationKey::FACTORY:
+ ret = "factory - set once at the factory, should never be changed";
+ break;
+ }
+
+ return ret;
+}
+
+const std::string ConfigurationKey::typeToString(const ConfigurationKey::Type& type) {
+ std::string ret = "UNKNOWN ERROR";
+
+ switch (type) {
+ case BOOLEAN:
+ ret = "boolean";
+ break;
+ case CHOICE_OPT:
+ ret = "multiple choice (optional)";
+ break;
+ case CHOICE:
+ ret = "multiple choice";
+ break;
+ case CIDR_OPT:
+ ret = "CIDR notation (optional)";
+ break;
+ case CIDR:
+ ret = "CIDR notation";
+ break;
+ case FILEPATH_OPT:
+ ret = "file path (optional)";
+ break;
+ case FILEPATH:
+ ret = "file path";
+ break;
+ case IPADDRESS_OPT:
+ ret = "IP address (optional)";
+ break;
+ case IPADDRESS:
+ ret = "IP address";
+ break;
+ case IPANDPORT:
+ ret = "IP address and port";
+ break;
+ case MIPADDRESS_OPT:
+ ret = "space-separated list of IP addresses (optional)";
+ break;
+ case MIPADDRESS:
+ ret = "space-separated list of IP addresses";
+ break;
+ case PORT_OPT:
+ ret = "IP port (optional)";
+ break;
+ case PORT:
+ ret = "IP port";
+ break;
+ case REGEX_OPT:
+ ret = "regular expression (optional)";
+ break;
+ case REGEX:
+ ret = "regular expression";
+ break;
+ case STRING_OPT:
+ ret = "string (optional)";
+ break;
+ case STRING:
+ ret = "string";
+ break;
+ case VALRANGE:
+ ret = "value range";
+ break;
+ }
+
+ return ret;
+}
+
+void ConfigurationKey::printKey(const ConfigurationKey &key, const std::string& currentValue, ostream& os) {
+ os << key.getName() << " ";
+ if (!currentValue.length()) {
+ os << "(disabled)";
+ } else {
+ os << currentValue;
+ }
+ if (currentValue.compare(key.getDefaultValue()) == 0) {
+ os << " [default]";
+ }
+ os << endl;
+}
+
+void ConfigurationKey::printDescription(const ConfigurationKey &key, ostream& os) {
+ std::string tmp;
+
+ os << " - description: " << key.getDescription() << std::endl;
+ if (key.getUnits().length()) {
+ os << " - units: " << key.getUnits() << std::endl;
+ }
+ os << " - type: " << ConfigurationKey::typeToString(key.getType()) << std::endl;
+ if (key.getDefaultValue().length()) {
+ os << " - default value: " << key.getDefaultValue() << std::endl;
+ }
+ os << " - visibility level: " << ConfigurationKey::visibilityLevelToString(key.getVisibility()) << std::endl;
+ os << " - static: " << key.isStatic() << std::endl;
+
+ tmp = key.getValidValues();
+ if (key.getType() == ConfigurationKey::VALRANGE) {
+ int startPos = tmp.find('(');
+ uint delimiter = 0;
+ if (startPos != (int)std::string::npos) {
+ tmp = tmp.substr(0, startPos);
+ }
+ startPos = -1;
+
+ do {
+ startPos++;
+ delimiter = tmp.find(':', startPos);
+ os << " - valid values: " << "from " << tmp.substr(startPos, delimiter-startPos) << " to "
+ << tmp.substr(delimiter+1, tmp.find(',', delimiter)-delimiter-1) << std::endl;
+
+ } while ((startPos = tmp.find(',', startPos)) != (int)std::string::npos);
+
+ } else if (key.getType() == ConfigurationKey::CHOICE) {
+ int startPos = -1;
+ uint endPos = 0;
+
+ do {
+ startPos++;
+ if ((endPos = tmp.find('|', startPos)) != std::string::npos) {
+ os << " - valid values: " << tmp.substr(startPos, endPos-startPos);
+ os << " = " << tmp.substr(endPos+1, tmp.find(',', endPos)-endPos-1) << std::endl;
+ } else {
+ os << " - valid values: " << tmp.substr(startPos, tmp.find(',', startPos)-startPos) << std::endl;
+ }
+
+ } while ((startPos = tmp.find(',', startPos)) != (int)std::string::npos);
+
+ } else if (key.getType() == ConfigurationKey::BOOLEAN) {
+ os << " - valid values: 0 = disabled" << std::endl;
+ os << " - valid values: 1 = enabled" << std::endl;
+
+ } else if (key.getType() == ConfigurationKey::STRING) {
+ os << " - valid val regex: " << tmp << std::endl;
+
+ } else if (key.getValidValues().length()) {
+ os << " - raw valid values: " << tmp << std::endl;
+ }
+}
+
// vim: ts=4 sw=4
diff --git a/CommonLibs/Configuration.h b/CommonLibs/Configuration.h
index bc37a49..cd4838e 100644
--- a/CommonLibs/Configuration.h
+++ b/CommonLibs/Configuration.h
@@ -33,10 +33,14 @@
#include <assert.h>
#include <stdlib.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <regex.h>
#include <map>
#include <vector>
#include <string>
+#include <sstream>
#include <iostream>
#include <Threads.h>
@@ -165,8 +169,10 @@ class HashString : public std::string {
};
+typedef std::map<std::string, ConfigurationRecord> ConfigurationRecordMap;
typedef std::map<HashString, ConfigurationRecord> ConfigurationMap;
-
+class ConfigurationKey;
+typedef std::map<std::string, ConfigurationKey> ConfigurationKeyMap;
/**
A class for maintaining a configuration key-value table,
@@ -180,40 +186,44 @@ class ConfigurationTable {
sqlite3* mDB; ///< database connection
ConfigurationMap mCache; ///< cache of recently access configuration values
mutable Mutex mLock; ///< control for multithreaded access to the cache
+ std::vector<std::string> (*mCrossCheck)(const std::string&); ///< cross check callback pointer
public:
+ ConfigurationKeyMap mSchema;///< definition of configuration default values and validation logic
+
+ ConfigurationTable(const char* filename = ":memory:", const char *wCmdName = 0, ConfigurationKeyMap wSchema = ConfigurationKeyMap());
- ConfigurationTable(const char* filename = ":memory:", const char *wCmdName = 0);
+ /** Generate an up-to-date example sql file for new installs. */
+ std::string getDefaultSQL(const std::string& program, const std::string& version);
+
+ /** Generate an up-to-date TeX snippet. */
+ std::string getTeX(const std::string& program, const std::string& version);
/** Return true if the key is used in the table. */
bool defines(const std::string& key);
- /** Return true if this key is identified as static. */
- bool isStatic(const std::string& key) const;
+ /** Return true if the application's schema knows about this key. */
+ bool keyDefinedInSchema(const std::string& name);
- /** Return true if this key is identified as required (!optional). */
- bool isRequired(const std::string& key) const;
-
- /**
- Get a string parameter from the table.
- Throw ConfigurationTableKeyNotFound if not found.
- */
- std::string getStr(const std::string& key);
+ /** Return true if the provided value validates correctly against the defined schema. */
+ bool isValidValue(const std::string& name, const std::string& val);
+ /** Return true if the provided value validates correctly against the defined schema. */
+ bool isValidValue(const std::string& name, const int val) { std::stringstream ss; ss << val; return isValidValue(name, ss.str()); }
- /**
- Get a string parameter from the table.
- Define the parameter to the default value if not found.
- */
- std::string getStr(const std::string& key, const char* defaultValue);
+ /** Return a map of all similar keys in the defined schema. */
+ ConfigurationKeyMap getSimilarKeys(const std::string& snippet);
+ /** Return true if this key is identified as static. */
+ bool isStatic(const std::string& key);
/**
- Get a numeric parameter from the table.
+ Get a string parameter from the table.
Throw ConfigurationTableKeyNotFound if not found.
*/
- long getNum(const std::string& key);
+ std::string getStr(const std::string& key);
+
/**
Get a boolean from the table.
@@ -223,9 +233,9 @@ class ConfigurationTable {
/**
Get a numeric parameter from the table.
- Define the parameter to the default value if not found.
+ Throw ConfigurationTableKeyNotFound if not found.
*/
- long getNum(const std::string& key, long defaultValue);
+ long getNum(const std::string& key);
/**
Get a vector of strings from the table.
@@ -233,11 +243,6 @@ class ConfigurationTable {
std::vector<std::string> getVectorOfStrings(const std::string& key);
/**
- Get a vector of strings from the table, with a default value..
- */
- std::vector<std::string> getVectorOfStrings(const std::string& key, const char* defaultValue);
-
- /**
Get a float from the table.
Throw ConfigurationTableKeyNotFound if not found.
*/
@@ -262,14 +267,6 @@ class ConfigurationTable {
bool set(const std::string& key);
/**
- Set a corresponding value to NULL.
- Will not alter required values.
- @param key The key of the item to be nulled-out.
- @return true if anything was actually nulled-out.
- */
- bool unset(const std::string& key);
-
- /**
Remove an entry from the table.
Will not alter required values.
@param key The key of the item to be removed.
@@ -280,9 +277,18 @@ class ConfigurationTable {
/** Search the table, dumping to a stream. */
void find(const std::string& pattern, std::ostream&) const;
+ /** Return all key/value pairs stored in the ConfigurationTable */
+ ConfigurationRecordMap getAllPairs() const;
+
/** Define the callback to purge the cache whenever the database changes. */
void setUpdateHook(void(*)(void *,int ,char const *,char const *,sqlite3_int64));
+ /** Define the callback for cross checking. */
+ void setCrossCheckHook(std::vector<std::string> (*wCrossCheck)(const std::string&));
+
+ /** Execute the application specific value cross checking logic. */
+ std::vector<std::string> crossCheck(const std::string& key);
+
/** purege cache if it exceeds a certain age */
void checkCacheAge();
@@ -323,6 +329,92 @@ class SimpleKeyValue {
};
+class ConfigurationKey {
+
+ public:
+
+ enum VisibilityLevel
+ {
+ CUSTOMER,
+ CUSTOMERSITE,
+ CUSTOMERTUNE,
+ CUSTOMERWARN,
+ DEVELOPER,
+ FACTORY
+ };
+
+ enum Type
+ {
+ BOOLEAN,
+ CHOICE_OPT,
+ CHOICE,
+ CIDR_OPT,
+ CIDR,
+ FILEPATH_OPT,
+ FILEPATH,
+ IPADDRESS_OPT,
+ IPADDRESS,
+ IPANDPORT,
+ MIPADDRESS_OPT,
+ MIPADDRESS,
+ PORT_OPT,
+ PORT,
+ REGEX_OPT,
+ REGEX,
+ STRING_OPT,
+ STRING,
+ VALRANGE
+ };
+
+ private:
+
+ std::string mName;
+ std::string mDefaultValue;
+ std::string mUnits;
+ VisibilityLevel mVisibility;
+ Type mType;
+ std::string mValidValues;
+ bool mIsStatic;
+ std::string mDescription;
+
+
+ public:
+
+ ConfigurationKey(const std::string& wName, const std::string& wDefaultValue, const std::string& wUnits, const VisibilityLevel wVisibility, const Type wType, const std::string& wValidValues, bool wIsStatic, const std::string& wDescription):
+ mName(wName),
+ mDefaultValue(wDefaultValue),
+ mUnits(wUnits),
+ mVisibility(wVisibility),
+ mType(wType),
+ mValidValues(wValidValues),
+ mIsStatic(wIsStatic),
+ mDescription(wDescription)
+ { }
+
+ ConfigurationKey()
+ { }
+
+ const std::string& getName() const { return mName; }
+ const std::string& getDefaultValue() const { return mDefaultValue; }
+ void updateDefaultValue(const std::string& newValue) { mDefaultValue = newValue; }
+ void updateDefaultValue(const int newValue) { std::stringstream ss; ss << newValue; updateDefaultValue(ss.str()); }
+ const std::string& getUnits() const { return mUnits; }
+ const VisibilityLevel& getVisibility() const { return mVisibility; }
+ const Type& getType() const { return mType; }
+ const std::string& getValidValues() const { return mValidValues; }
+ bool isStatic() const { return mIsStatic; }
+ const std::string& getDescription() const { return mDescription; }
+
+ static bool isValidIP(const std::string& ip);
+ static void getMinMaxStepping(const ConfigurationKey &key, std::string &min, std::string &max, std::string &stepping);
+ template<class T> static bool isInValRange(const ConfigurationKey &key, const std::string& val, const bool isInteger);
+ static const std::string visibilityLevelToString(const VisibilityLevel& visibility);
+ static const std::string typeToString(const ConfigurationKey::Type& type);
+ static void printKey(const ConfigurationKey &key, const std::string& currentValue, std::ostream& os);
+ static void printDescription(const ConfigurationKey &key, std::ostream& os);
+ static const std::string getARFCNsString();
+};
+
#endif
diff --git a/CommonLibs/ConfigurationTest.cpp b/CommonLibs/ConfigurationTest.cpp
index 3a2045f..2fd43e9 100644
--- a/CommonLibs/ConfigurationTest.cpp
+++ b/CommonLibs/ConfigurationTest.cpp
@@ -32,7 +32,8 @@
using namespace std;
-ConfigurationTable gConfig("exampleconfig.db","test");
+ConfigurationKeyMap getConfigurationKeys();
+ConfigurationTable gConfig("exampleconfig.db","test", getConfigurationKeys());
void purgeConfig(void*,int,char const*, char const*, sqlite3_int64)
{
@@ -46,7 +47,7 @@ int main(int argc, char *argv[])
gConfig.setUpdateHook(purgeConfig);
- const char *keys[5] = {"key1", "key2", "key3", "key4", "key5"};
+ char *keys[5] = {"key1", "key2", "key3", "key4", "key5"};
for (int i=0; i<5; i++) {
gConfig.set(keys[i],i);
@@ -57,7 +58,6 @@ int main(int argc, char *argv[])
cout << "table[" << keys[i] << "]=" << gConfig.getNum(keys[i]) << endl;
}
- gConfig.unset("key1");
for (int i=0; i<5; i++) {
cout << "defined table[" << keys[i] << "]=" << gConfig.defines(keys[i]) << endl;
}
@@ -78,8 +78,8 @@ int main(int argc, char *argv[])
gConfig.set("booltest",0);
cout << "bool " << gConfig.getBool("booltest") << endl;
- gConfig.getStr("newstring","new string value");
- gConfig.getNum("numnumber",42);
+ gConfig.getStr("newstring");
+ gConfig.getNum("numnumber");
SimpleKeyValue pairs;
@@ -94,7 +94,6 @@ int main(int argc, char *argv[])
cout << "search fkey:" << endl;
gConfig.find("fkey",cout);
- gConfig.unset("fkey");
cout << "search fkey:" << endl;
gConfig.find("fkey",cout);
gConfig.remove("fkey");
@@ -107,3 +106,44 @@ int main(int argc, char *argv[])
cout << "ConfigurationTableKeyNotFound exception successfully caught." << endl;
}
}
+
+ConfigurationKeyMap getConfigurationKeys()
+{
+ ConfigurationKeyMap map;
+ ConfigurationKey *tmp;
+
+ tmp = new ConfigurationKey("booltest","0",
+ "",
+ ConfigurationKey::DEVELOPER,
+ ConfigurationKey::BOOLEAN,
+ "",
+ false,
+ ""
+ );
+ map[tmp->getName()] = *tmp;
+ free(tmp);
+
+ tmp = new ConfigurationKey("numnumber","42",
+ "",
+ ConfigurationKey::DEVELOPER,
+ ConfigurationKey::VALRANGE,
+ "0-100",
+ false,
+ ""
+ );
+ map[tmp->getName()] = *tmp;
+ free(tmp);
+
+ tmp = new ConfigurationKey("newstring","new string value",
+ "",
+ ConfigurationKey::DEVELOPER,
+ ConfigurationKey::STRING,
+ "",
+ false,
+ ""
+ );
+ map[tmp->getName()] = *tmp;
+ free(tmp);
+
+ return map;
+}
diff --git a/CommonLibs/Interthread.h b/CommonLibs/Interthread.h
index 023ac14..42e6f7f 100644
--- a/CommonLibs/Interthread.h
+++ b/CommonLibs/Interthread.h
@@ -42,15 +42,21 @@
/** Pointer FIFO for interthread operations. */
-template <class T> class InterthreadQueue {
+// (pat) The elements in the queue are type T*, and
+// the Fifo class implements the underlying queue.
+// The default is class PointerFIFO, which does not place any restrictions on the type of T,
+// and is implemented by allocating auxilliary structures for the queue,
+// or SingleLinkedList, which implements the queue using an internal pointer in type T,
+// which must implement the functional interface of class SingleLinkListNode,
+// namely: functions T*next() and void setNext(T*).
+template <class T, class Fifo=PointerFIFO> class InterthreadQueue {
protected:
- PointerFIFO mQ;
+ Fifo mQ;
mutable Mutex mLock;
mutable Signal mWriteSignal;
-
public:
/** Delete contents. */
@@ -78,6 +84,12 @@ template <class T> class InterthreadQueue {
return mQ.size();
}
+ size_t totalSize() const // pat added
+ {
+ ScopedLock lock(mLock);
+ return mQ.totalSize();
+ }
+
/**
Blocking read.
@return Pointer to object (will not be NULL).
@@ -93,6 +105,13 @@ template <class T> class InterthreadQueue {
return retVal;
}
+ /** Non-blocking peek at the first element; returns NULL if empty. */
+ T* front()
+ {
+ ScopedLock lock(mLock);
+ return (T*) mQ.front();
+ }
+
/**
Blocking read with a timeout.
@param timeout The read timeout in ms.
@@ -127,7 +146,132 @@ template <class T> class InterthreadQueue {
mWriteSignal.signal();
}
+ /** Non-block write to the front of the queue. */
+ void write_front(T* val) // pat added
+ {
+ ScopedLock lock(mLock);
+ mQ.push_front(val);
+ mWriteSignal.signal();
+ }
+};
+
+// (pat) Identical to above but with the threading problem fixed.
+template <class T, class Fifo=PointerFIFO> class InterthreadQueue2 {
+
+ protected:
+
+ Fifo mQ;
+ mutable Mutex mLock;
+ mutable Signal mWriteSignal;
+
+ public:
+
+ /** Delete contents. */
+ void clear()
+ {
+ ScopedLock lock(mLock);
+ while (mQ.size()>0) delete (T*)mQ.get();
+ }
+
+ /** Empty the queue, but don't delete. */
+ void flushNoDelete()
+ {
+ ScopedLock lock(mLock);
+ while (mQ.size()>0) mQ.get();
+ }
+
+
+ ~InterthreadQueue2()
+ { clear(); }
+
+ size_t size() const
+ {
+ ScopedLock lock(mLock);
+ return mQ.size();
+ }
+
+ size_t totalSize() const // pat added
+ {
+ ScopedLock lock(mLock);
+ return mQ.totalSize();
+ }
+
+ /**
+ Blocking read.
+ @return Pointer to object (will not be NULL).
+ */
+ T* read()
+ {
+ ScopedLock lock(mLock);
+ T* retVal = (T*)mQ.get();
+ while (retVal==NULL) {
+ mWriteSignal.wait(mLock);
+ retVal = (T*)mQ.get();
+ }
+ return retVal;
+ }
+
+ /** Non-blocking peek at the first element; returns NULL if empty. */
+ T* front()
+ {
+ ScopedLock lock(mLock);
+ return (T*) mQ.front();
+ }
+
+ /**
+ Blocking read with a timeout.
+ @param timeout The read timeout in ms.
+ @return Pointer to object or NULL on timeout.
+ */
+ T* read(unsigned timeout)
+ {
+ if (timeout==0) return readNoBlock();
+ Timeval waitTime(timeout);
+ ScopedLock lock(mLock);
+ while ((mQ.size()==0) && (!waitTime.passed()))
+ mWriteSignal.wait(mLock,waitTime.remaining());
+ T* retVal = (T*)mQ.get();
+ return retVal;
+ }
+
+ /**
+ Non-blocking read.
+ @return Pointer to object or NULL if FIFO is empty.
+ */
+ T* readNoBlock()
+ {
+ ScopedLock lock(mLock);
+ return (T*)mQ.get();
+ }
+
+ /** Non-blocking write. */
+ void write(T* val)
+ {
+ // (pat) The Mutex mLock must be released before signaling the mWriteSignal condition.
+ // This is an implicit requirement of pthread_cond_wait() called from signal().
+ // If you do not do that, the InterthreadQueue read() function cannot start
+ // because the mutex is still locked by the thread calling the write(),
+ // so the read() thread yields its immediate execution opportunity.
+ // This recurs (and the InterthreadQueue fills up with data)
+ // until the read thread's accumulated temporary priority causes it to
+ // get a second pre-emptive activation over the writing thread,
+ // resulting in bursts of activity by the read thread.
+ { ScopedLock lock(mLock);
+ mQ.put(val);
+ }
+ mWriteSignal.signal();
+ }
+
+ /** Non-block write to the front of the queue. */
+ void write_front(T* val) // pat added
+ {
+ // (pat) See comments above.
+ { ScopedLock lock(mLock);
+ mQ.push_front(val);
+ }
+ mWriteSignal.signal();
+ }
};
@@ -214,12 +358,17 @@ template <class T> class InterthreadQueueWithWait {
/** Non-blocking write. */
void write(T* val)
{
+ // (pat) 8-14: Taking out the threading problem fix temporarily for David to use in the field.
ScopedLock lock(mLock);
mQ.put(val);
mWriteSignal.signal();
}
/** Wait until the queue falls below a low water mark. */
+ // (pat) This function suffers from the same problem as documented
+ // at InterthreadQueue.write(), but I am not fixing it because I cannot test it.
+ // The caller of this function will eventually get to run, just not immediately
+ // after the mReadSignal condition is fulfilled.
void wait(size_t sz=0)
{
ScopedLock lock(mLock);
@@ -484,6 +633,7 @@ template <class T, class C = std::vector<T*>, class Cmp = PointerCompare<T> > cl
/** Non-blocking write. */
void write(T* val)
{
+ // (pat) 8-14: Taking out the threading problem fix temporarily for David to use in the field.
ScopedLock lock(mLock);
mQ.push(val);
mWriteSignal.signal();
diff --git a/CommonLibs/InterthreadTest.cpp b/CommonLibs/InterthreadTest.cpp
index 1712689..03445d9 100644
--- a/CommonLibs/InterthreadTest.cpp
+++ b/CommonLibs/InterthreadTest.cpp
@@ -82,7 +82,8 @@ void* mapReader(void*)
for (int i=0; i<20; i++) {
int *p = gMap.read(i);
COUT("map read " << *p);
- delete p;
+ // InterthreadMap will delete the pointers
+ // delete p;
}
return NULL;
}
diff --git a/CommonLibs/LinkedLists.cpp b/CommonLibs/LinkedLists.cpp
index ba0f0cc..35a8541 100644
--- a/CommonLibs/LinkedLists.cpp
+++ b/CommonLibs/LinkedLists.cpp
@@ -29,7 +29,17 @@
#include "LinkedLists.h"
-
+void PointerFIFO::push_front(void* val) // by pat
+{
+ // Pat added this routine for completeness, but never used or tested.
+ // The first person to use this routine should remove this assert.
+ ListNode *node = allocate();
+ node->data(val);
+ node->next(mHead);
+ mHead = node;
+ if (!mTail) mTail=node;
+ mSize++;
+}
void PointerFIFO::put(void* val)
{
@@ -58,7 +68,6 @@ void* PointerFIFO::get()
}
-
ListNode *PointerFIFO::allocate()
{
if (mFreeList==NULL) return new ListNode;
@@ -72,6 +81,3 @@ void PointerFIFO::release(ListNode* wNode)
wNode->next(mFreeList);
mFreeList = wNode;
}
-
-
-
diff --git a/CommonLibs/LinkedLists.h b/CommonLibs/LinkedLists.h
index 2cb83ee..31fb9c5 100644
--- a/CommonLibs/LinkedLists.h
+++ b/CommonLibs/LinkedLists.h
@@ -1,6 +1,8 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
+* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
+*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
@@ -28,6 +30,7 @@
#define LINKEDLISTS_H
#include <stdlib.h>
+#include <assert.h>
@@ -54,7 +57,7 @@ class ListNode {
/** A fast FIFO for pointer-based storage. */
class PointerFIFO {
- private:
+ protected:
ListNode* mHead; ///< points to next item out
ListNode* mTail; ///< points to last item in
@@ -69,9 +72,12 @@ class PointerFIFO {
{}
unsigned size() const { return mSize; }
+ unsigned totalSize() const { return 0; } // Not used in this version.
- /** Put an item into the FIFO. */
+ /** Put an item into the FIFO at the back of the queue. */
void put(void* val);
+ /** Push an item on the front of the FIFO. */
+ void push_front(void*val); // pat added.
/**
Take an item from the FIFO.
@@ -79,6 +85,9 @@ class PointerFIFO {
*/
void* get();
+ /** Peek at front item without removal. */
+ void *front() { return mHead ? mHead->data() : 0; } // pat added
+
private:
@@ -90,6 +99,74 @@ class PointerFIFO {
};
+// This is the default type for SingleLinkList Node element;
+// You can derive your class directly from this, but then you must add type casts
+// all over the place.
+class SingleLinkListNode
+{ public:
+ SingleLinkListNode *mNext;
+ SingleLinkListNode *next() {return mNext;}
+ void setNext(SingleLinkListNode *item) {mNext=item;}
+ SingleLinkListNode() : mNext(0) {}
+ virtual unsigned size() { return 0; }
+};
+
+// A single-linked lists of elements with internal pointers.
+// The methods must match those from SingleLinkListNode.
+// This class also assumes the Node has a size() method, and accumulates
+// the total size of elements in the list in totalSize().
+template<class Node=SingleLinkListNode>
+class SingleLinkList
+{
+ Node *mHead, *mTail;
+ unsigned mSize; // Number of elements in list.
+ unsigned mTotalSize; // Total of size() method of elements in list.
+
+ public:
+ SingleLinkList() : mHead(0), mTail(0), mSize(0), mTotalSize(0) {}
+ unsigned size() const { return mSize; }
+ unsigned totalSize() const { return mTotalSize; }
+
+ Node *pop_back() { assert(0); } // Not efficient with this type of list.
+
+ Node *pop_front()
+ {
+ if (!mHead) return NULL;
+ Node *result = mHead;
+ mHead = mHead->next();
+ if (mTail == result) { mTail = NULL; assert(mHead == NULL); }
+ result->setNext(NULL); // be neat
+ mSize--;
+ mTotalSize -= result->size();
+ return result;
+ }
+
+ void push_front(Node *item)
+ {
+ item->setNext(mHead);
+ mHead = item;
+ if (!mTail) { mTail = item; }
+ mSize++;
+ mTotalSize += item->size();
+ }
+
+ void push_back(Node *item)
+ {
+ item->setNext(NULL);
+ if (mTail) { mTail->setNext(item); }
+ mTail = item;
+ if (!mHead) mHead = item;
+ mSize++;
+ mTotalSize += item->size();
+ }
+ Node *front() { return mHead; }
+ Node *back() { return mTail; }
+
+ // Interface to InterthreadQueue so it can used SingleLinkList as the Fifo.
+ void put(void *val) { push_back((Node*)val); }
+ void *get() { return pop_front(); }
+};
+
diff --git a/CommonLibs/LogTest.cpp b/CommonLibs/LogTest.cpp
index e9f73b0..20959a0 100644
--- a/CommonLibs/LogTest.cpp
+++ b/CommonLibs/LogTest.cpp
@@ -37,7 +37,7 @@ void printAlarms()
{
std::ostream_iterator<std::string> output( std::cout, "\n" );
std::list<std::string> alarms = gGetLoggerAlarms();
- std::cout << "#alarms = " << alarms.size() << std::endl;
+ std::cout << "# alarms = " << alarms.size() << std::endl;
std::copy( alarms.begin(), alarms.end(), output );
}
@@ -55,7 +55,6 @@ int main(int argc, char *argv[])
LOG(DEBUG) << " testing the logger.";
std::cout << "\n\n\n";
std::cout << "testing Alarms\n";
- LOG(ALERT) << " testing the logger alarm.";
std::cout << "you should see three lines:" << std::endl;
printAlarms();
std::cout << "----------- generating 20 alarms ----------" << std::endl;
diff --git a/CommonLibs/Logger.cpp b/CommonLibs/Logger.cpp
index 36b7696..06e91f6 100644
--- a/CommonLibs/Logger.cpp
+++ b/CommonLibs/Logger.cpp
@@ -1,6 +1,7 @@
/*
* Copyright 2009, 2010 Free Software Foundation, Inc.
* Copyright 2010 Kestrel Signal Processing, Inc.
+* Copyright 2011, 2012 Range Networks, Inc.
*
*
* This software is distributed under the terms of the GNU Affero Public License.
@@ -32,6 +33,7 @@
#include "Configuration.h"
#include "Logger.h"
+#include "Threads.h" // pat added
using namespace std;
@@ -49,36 +51,71 @@ void addAlarm(const string&);
+// (pat) If Log messages are printed before the classes in this module are inited
+// (which happens when static classes have constructors that do work)
+// the OpenBTS just crashes.
+// Prevent that by setting sLoggerInited to true when this module is inited.
+static bool sLoggerInited = 0;
+static struct CheckLoggerInitStatus {
+ CheckLoggerInitStatus() { sLoggerInited = 1; }
+} sCheckloggerInitStatus;
+
+
/** Names of the logging levels. */
const char *levelNames[] = {
"EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG"
};
int numLevels = 8;
+bool gLogToConsole = 0;
+FILE *gLogToFile = NULL;
+Mutex gLogToLock;
-/** Given a string, return the corresponding level name. */
-int lookupLevel(const string& name)
+int levelStringToInt(const string& name)
{
// Reverse search, since the numerically larger levels are more common.
for (int i=numLevels-1; i>=0; i--) {
if (name == levelNames[i]) return i;
}
- // This should never be called with a bogus name.
- LOG(ERR) << "undefined logging level " << name << "defaulting to ERR";
- return LOG_ERR;
+
+ // Common substitutions.
+ if (name=="INFORMATION") return 6;
+ if (name=="WARN") return 4;
+ if (name=="ERROR") return 3;
+ if (name=="CRITICAL") return 2;
+ if (name=="EMERGENCY") return 0;
+
+ // Unknown level.
+ return -1;
+}
+
+/** Given a string, return the corresponding level name. */
+int lookupLevel(const string& key)
+{
+ string val = gConfig.getStr(key);
+ int level = levelStringToInt(val);
+
+ if (level == -1) {
+ string defaultLevel = gConfig.mSchema["Log.Level"].getDefaultValue();
+ level = levelStringToInt(defaultLevel);
+ _LOG(CRIT) << "undefined logging level (" << key << " = \"" << val << "\") defaulting to \"" << defaultLevel << ".\" Valid levels are: EMERG, ALERT, CRIT, ERR, WARNING, NOTICE, INFO or DEBUG";
+ gConfig.set(key, defaultLevel);
+ }
+
+ return level;
}
int getLoggingLevel(const char* filename)
{
// Default level?
- if (!filename) return lookupLevel(gConfig.getStr("Log.Level"));
+ if (!filename) return lookupLevel("Log.Level");
// This can afford to be inefficient since it is not called that often.
const string keyName = string("Log.Level.") + string(filename);
- if (gConfig.defines(keyName)) return lookupLevel(gConfig.getStr(keyName));
- return lookupLevel(gConfig.getStr("Log.Level"));
+ if (gConfig.defines(keyName)) return lookupLevel(keyName);
+ return lookupLevel("Log.Level");
}
@@ -113,6 +150,7 @@ int gGetLoggingLevel(const char* filename)
}
// Look it up in the config table and cache it.
// FIXME: Figure out why unlock and lock below fix the config table deadlock.
+ // (pat) Probably because getLoggingLevel may call LOG recursively via lookupLevel().
sLogCacheLock.unlock();
int level = getLoggingLevel(filename);
sLogCacheLock.lock();
@@ -155,12 +193,30 @@ Log::~Log()
// Anything at or above LOG_CRIT is an "alarm".
// Save alarms in the local list and echo them to stderr.
if (mPriority <= LOG_CRIT) {
- addAlarm(mStream.str().c_str());
+ if (sLoggerInited) addAlarm(mStream.str().c_str());
cerr << mStream.str() << endl;
}
// Current logging level was already checked by the macro.
// So just log.
syslog(mPriority, "%s", mStream.str().c_str());
+ // pat added for easy debugging.
+ if (gLogToConsole||gLogToFile) {
+ int mlen = mStream.str().size();
+ int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
+ gLogToLock.lock();
+ if (gLogToConsole) {
+ // The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
+ // so just use std::cout.
+ std::cout << mStream.str();
+ if (neednl) std::cout<<"\n";
+ }
+ if (gLogToFile) {
+ fputs(mStream.str().c_str(),gLogToFile);
+ if (neednl) {fputc('\n',gLogToFile);}
+ fflush(gLogToFile);
+ }
+ gLogToLock.unlock();
+ }
}
@@ -182,18 +238,26 @@ ostringstream& Log::get()
void gLogInit(const char* name, const char* level, int facility)
{
- // Set the level.
+ // Set the level if one has been specified.
if (level) {
gConfig.set("Log.Level",level);
- } else {
- if (!gConfig.defines("Log.Level")) {
- gConfig.set("Log.Level","WARNING");
- }
}
- // Define other logging parameters in the global config.
- if (!gConfig.defines("Log.Alarms.Max")) {
- gConfig.set("Log.Alarms.Max",DEFAULT_MAX_ALARMS);
+ // Pat added, tired of the syslog facility.
+ // Both the transceiver and OpenBTS use this same facility, but only OpenBTS/OpenNodeB may use this log file:
+ string str = gConfig.getStr("Log.File");
+ if (gLogToFile==0 && str.length() && 0==strncmp(gCmdName,"Open",4)) {
+ const char *fn = str.c_str();
+ if (fn && *fn && strlen(fn)>3) { // strlen because a garbage char is getting in sometimes.
+ gLogToFile = fopen(fn,"w"); // New log file each time we start.
+ if (gLogToFile) {
+ time_t now;
+ time(&now);
+ fprintf(gLogToFile,"Starting at %s",ctime(&now));
+ fflush(gLogToFile);
+ std::cout << "Logging to file: " << fn << "\n";
+ }
+ }
}
// Open the log connection.
diff --git a/CommonLibs/Logger.h b/CommonLibs/Logger.h
index 6b1c44d..58dfa22 100644
--- a/CommonLibs/Logger.h
+++ b/CommonLibs/Logger.h
@@ -23,6 +23,11 @@
*/
+// (pat) WARNING is stupidly defined in /usr/local/include/osipparser2/osip_const.h.
+// This must be outside the #ifndef LOGGER_H to fix it as long as Logger.h included after the above file.
+#ifdef WARNING
+#undef WARNING
+#endif
#ifndef LOGGER_H
#define LOGGER_H
@@ -34,21 +39,42 @@
#include <list>
#include <map>
#include <string>
-#include "Threads.h"
-
#define _LOG(level) \
Log(LOG_##level).get() << pthread_self() \
- << " " __FILE__ ":" << __LINE__ << ":" << __FUNCTION__ << ": "
+ << timestr() << " " __FILE__ ":" << __LINE__ << ":" << __FUNCTION__ << ": "
+
+#define IS_LOG_LEVEL(wLevel) (gGetLoggingLevel(__FILE__)>=LOG_##wLevel)
#ifdef NDEBUG
#define LOG(wLevel) \
- if (LOG_##wLevel!=LOG_DEBUG && gGetLoggingLevel(__FILE__)>=LOG_##wLevel) _LOG(wLevel)
+ if (LOG_##wLevel!=LOG_DEBUG && IS_LOG_LEVEL(wLevel)) _LOG(wLevel)
#else
#define LOG(wLevel) \
- if (gGetLoggingLevel(__FILE__)>=LOG_##wLevel) _LOG(wLevel)
+ if (IS_LOG_LEVEL(wLevel)) _LOG(wLevel)
#endif
+// pat: And for your edification here are the 'levels' as defined in syslog.h:
+// LOG_EMERG 0 system is unusable
+// LOG_ALERT 1 action must be taken immediately
+// LOG_CRIT 2 critical conditions
+// LOG_ERR 3 error conditions
+// LOG_WARNING 4 warning conditions
+// LOG_NOTICE 5 normal, but significant, condition
+// LOG_INFO 6 informational message
+// LOG_DEBUG 7 debug-level message
+
+// (pat) added - print out a var and its name.
+// Use like this: int descriptive_name; LOG(INFO)<<LOGVAR(descriptive_name);
+#define LOGVAR2(name,val) " " << name << "=" << (val)
+#define LOGVAR(var) (" " #var "=") << var
+#define LOGHEX(var) (" " #var "=0x") << hex << ((unsigned)var) << dec
+#define LOGHEX2(name,val) " " << name << "=0x" << hex << ((unsigned)(val)) << dec
+// These are kind of cheesy, but you can use for bitvector
+#define LOGBV2(name,val) " " << name << "=(" << val<<" size:"<<val.size()<<")"
+#define LOGBV(bv) LOGBV2(#bv,bv)
+#define LOGVARRANGE(name,cur,lo,hi) " "<<name <<"=("<<(cur) << " range:"<<(lo) << " to "<<(hi) <<")"
+
#define OBJLOG(wLevel) \
LOG(wLevel) << "obj: " << this << ' '
@@ -56,8 +82,8 @@
#define LOG_ASSERT(x) { if (!(x)) LOG(EMERG) << "assertion " #x " failed"; } assert(x);
-#define DEFAULT_MAX_ALARMS 10
-
+#include "Threads.h" // must be after defines above, if these files are to be allowed to use LOG()
+#include "Utils.h"
/**
A C++ stream-based thread-safe logger.
@@ -90,6 +116,7 @@ class Log {
std::ostringstream& get();
};
+extern bool gLogToConsole; // Pat added for easy debugging.
diff --git a/CommonLibs/Makefile.am b/CommonLibs/Makefile.am
index 624ba44..26a55ed 100644
--- a/CommonLibs/Makefile.am
+++ b/CommonLibs/Makefile.am
@@ -22,7 +22,11 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
-AM_CXXFLAGS = -Wall -ldl -O3 -g -lpthread
+AM_CXXFLAGS = -Wall -O3 -g -ldl -lpthread
+
+EXTRA_DIST = \
+ example.config \
+ README.common
noinst_LTLIBRARIES = libcommon.la
@@ -32,11 +36,12 @@ libcommon_la_SOURCES = \
Sockets.cpp \
Threads.cpp \
Timeval.cpp \
+ Reporting.cpp \
+ Logger.cpp \
Configuration.cpp \
sqlite3util.cpp \
- Logger.cpp \
URLEncode.cpp \
- Reporting.cpp
+ Utils.cpp
noinst_PROGRAMS = \
BitVectorTest \
@@ -47,9 +52,10 @@ noinst_PROGRAMS = \
VectorTest \
ConfigurationTest \
LogTest \
+ URLEncodeTest \
F16Test
-# ReportingTest
+# ReportingTest
noinst_HEADERS = \
BitVector.h \
@@ -60,15 +66,19 @@ noinst_HEADERS = \
Timeval.h \
Regexp.h \
Vector.h \
- URLEncode.h \
Configuration.h \
Reporting.h \
F16.h \
+ URLEncode.h \
+ Utils.h \
Logger.h \
sqlite3util.h
+URLEncodeTest_SOURCES = URLEncodeTest.cpp
+URLEncodeTest_LDADD = libcommon.la
+
BitVectorTest_SOURCES = BitVectorTest.cpp
-BitVectorTest_LDADD = libcommon.la
+BitVectorTest_LDADD = libcommon.la $(SQLITE_LA)
InterthreadTest_SOURCES = InterthreadTest.cpp
InterthreadTest_LDADD = libcommon.la
@@ -82,7 +92,7 @@ TimevalTest_SOURCES = TimevalTest.cpp
TimevalTest_LDADD = libcommon.la
VectorTest_SOURCES = VectorTest.cpp
-VectorTest_LDADD = libcommon.la
+VectorTest_LDADD = libcommon.la $(SQLITE_LA)
RegexpTest_SOURCES = RegexpTest.cpp
RegexpTest_LDADD = libcommon.la
@@ -90,8 +100,8 @@ RegexpTest_LDADD = libcommon.la
ConfigurationTest_SOURCES = ConfigurationTest.cpp
ConfigurationTest_LDADD = libcommon.la $(SQLITE_LA)
-#ReportingTest_SOURCES = ReportingTest.cpp
-#ReportingTest_LDADD = libcommon.la $(SQLITE_LA)
+# ReportingTest_SOURCES = ReportingTest.cpp
+# ReportingTest_LDADD = libcommon.la $(SQLITE_LA)
LogTest_SOURCES = LogTest.cpp
LogTest_LDADD = libcommon.la $(SQLITE_LA)
diff --git a/CommonLibs/MemoryLeak.h b/CommonLibs/MemoryLeak.h
new file mode 100644
index 0000000..4948534
--- /dev/null
+++ b/CommonLibs/MemoryLeak.h
@@ -0,0 +1,111 @@
+/*
+* Copyright 2011 Range Networks, Inc.
+* All Rights Reserved.
+*
+* This software is distributed under multiple licenses;
+* see the COPYING file in the main directory for licensing
+* information for this specific distribuion.
+*
+* This use of this software may be subject to additional restrictions.
+* See the LEGAL file in the main directory for details.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+*/
+#ifndef _MEMORYLEAK_
+#define _MEMORYLEAK_ 1
+#include <map>
+#include "ScalarTypes.h"
+#include "Logger.h"
+
+namespace Utils {
+
+struct MemStats {
+ // Enumerates the classes that are checked.
+ // Redundancies are ok, for example, we check BitVector and also
+ // several descendants of BitVector.
+ enum MemoryNames {
+ mZeroIsUnused,
+ mVector,
+ mVectorData,
+ mBitVector,
+ mByteVector,
+ mByteVectorData,
+ mRLCRawBlock,
+ mRLCUplinkDataBlock,
+ mRLCMessage,
+ mRLCMsgPacketDownlinkDummyControlBlock, // Redundant with RLCMessage
+ mTBF,
+ mLlcEngine,
+ mSgsnDownlinkMsg,
+ mRachInfo,
+ mPdpPdu,
+ mFECDispatchInfo,
+ mL3Frame,
+ msignalVector,
+ mSoftVector,
+ mScramblingCode,
+ mURlcDownSdu,
+ mURlcPdu,
+ // Must be last:
+ mMax,
+ };
+ int mMemTotal[mMax]; // In elements, not bytes.
+ int mMemNow[mMax];
+ const char *mMemName[mMax];
+ MemStats();
+ void memChkNew(MemoryNames memIndex, const char *id);
+ void memChkDel(MemoryNames memIndex, const char *id);
+ void text(std::ostream &os);
+ // We would prefer to use an unordered_map, but that requires special compile switches.
+ // What a super great language.
+ typedef std::map<std::string,Int_z> MemMapType;
+ MemMapType mMemMap;
+};
+extern struct MemStats gMemStats;
+extern int gMemLeakDebug;
+
+// This is a memory leak detector.
+// Use by putting RN_MEMCHKNEW and RN_MEMCHKDEL in class constructors/destructors,
+// or use the DEFINE_MEMORY_LEAK_DETECTOR class and add the defined class
+// as an ancestor to the class to be memory leak checked.
+
+struct MemLabel {
+ std::string mccKey;
+ virtual ~MemLabel() {
+ Int_z &tmp = Utils::gMemStats.mMemMap[mccKey]; tmp = tmp - 1;
+ }
+};
+
+#if RN_DISABLE_MEMORY_LEAK_TEST
+#define RN_MEMCHKNEW(type)
+#define RN_MEMCHKDEL(type)
+#define RN_MEMLOG(type,ptr)
+#define DEFINE_MEMORY_LEAK_DETECTOR_CLASS(subClass,checkerClass) \
+ struct checkerClass {};
+#else
+
+#define RN_MEMCHKNEW(type) { Utils::gMemStats.memChkNew(Utils::MemStats::m##type,#type); }
+#define RN_MEMCHKDEL(type) { Utils::gMemStats.memChkDel(Utils::MemStats::m##type,#type); }
+
+#define RN_MEMLOG(type,ptr) { \
+ static std::string key = format("%s_%s:%d",#type,__FILE__,__LINE__); \
+ (ptr)->/* MemCheck##type:: */ mccKey = key; \
+ Utils::gMemStats.mMemMap[key]++; \
+ }
+
+// TODO: The above assumes that checkclass is MemCheck ## subClass
+#define DEFINE_MEMORY_LEAK_DETECTOR_CLASS(subClass,checkerClass) \
+ struct checkerClass : public virtual Utils::MemLabel { \
+ checkerClass() { RN_MEMCHKNEW(subClass); } \
+ virtual ~checkerClass() { \
+ RN_MEMCHKDEL(subClass); \
+ } \
+ };
+
+#endif
+
+} // namespace Utils
+
+#endif
diff --git a/CommonLibs/ScalarTypes.h b/CommonLibs/ScalarTypes.h
new file mode 100644
index 0000000..077d889
--- /dev/null
+++ b/CommonLibs/ScalarTypes.h
@@ -0,0 +1,136 @@
+/*
+* Copyright 2011 Range Networks, Inc.
+* All Rights Reserved.
+*
+* This software is distributed under multiple licenses;
+* see the COPYING file in the main directory for licensing
+* information for this specific distribuion.
+*
+* This use of this software may be subject to additional restrictions.
+* See the LEGAL file in the main directory for details.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+*/
+
+#ifndef SCALARTYPES_H
+#define SCALARTYPES_H
+#include <iostream> // For size_t
+#include <stdint.h>
+//#include "GSMCommon.h" // Was included for Z100Timer
+
+// We dont bother to define *= /= etc.; you'll have to convert: a*=b; to: a=a*b;
+#define _INITIALIZED_SCALAR_BASE_FUNCS(Classname,Basetype,Init) \
+ Classname() : value(Init) {} \
+ Classname(Basetype wvalue) { value = wvalue; } /* Can set from basetype. */ \
+ operator Basetype(void) const { return value; } /* Converts from basetype. */ \
+ Basetype operator=(Basetype wvalue) { return value = wvalue; } \
+ Basetype* operator&() { return &value; }
+
+#define _INITIALIZED_SCALAR_ARITH_FUNCS(Basetype) \
+ Basetype operator++() { return ++value; } \
+ Basetype operator++(int) { return value++; } \
+ Basetype operator--() { return --value; } \
+ Basetype operator--(int) { return value--; } \
+ Basetype operator+=(Basetype wvalue) { return value = value + wvalue; } \
+ Basetype operator-=(Basetype wvalue) { return value = value - wvalue; }
+
+#define _INITIALIZED_SCALAR_FUNCS(Classname,Basetype,Init) \
+ _INITIALIZED_SCALAR_BASE_FUNCS(Classname,Basetype,Init) \
+ _INITIALIZED_SCALAR_ARITH_FUNCS(Basetype)
+
+
+#define _DECLARE_SCALAR_TYPE(Classname_i,Classname_z,Basetype) \
+ template <Basetype Init> \
+ struct Classname_i { \
+ Basetype value; \
+ _INITIALIZED_SCALAR_FUNCS(Classname_i,Basetype,Init) \
+ }; \
+ typedef Classname_i<0> Classname_z;
+
+
+// Usage:
+// Where 'classname' is one of the types listed below, then:
+// classname_z specifies a zero initialized type;
+// classname_i<value> initializes the type to the specified value.
+// We also define Float_z.
+_DECLARE_SCALAR_TYPE(Int_i, Int_z, int)
+_DECLARE_SCALAR_TYPE(Char_i, Char_z, signed char)
+_DECLARE_SCALAR_TYPE(Int16_i, Int16_z, int16_t)
+_DECLARE_SCALAR_TYPE(Int32_i, Int32_z, int32_t)
+_DECLARE_SCALAR_TYPE(UInt_i, UInt_z, unsigned)
+_DECLARE_SCALAR_TYPE(UChar_i, UChar_z, unsigned char)
+_DECLARE_SCALAR_TYPE(UInt16_i, UInt16_z, uint16_t)
+_DECLARE_SCALAR_TYPE(UInt32_i, UInt32_z, uint32_t)
+_DECLARE_SCALAR_TYPE(Size_t_i, Size_t_z, size_t)
+
+// Bool is special because it cannot accept some arithmetic funcs
+//_DECLARE_SCALAR_TYPE(Bool_i, Bool_z, bool)
+template <bool Init>
+struct Bool_i {
+ bool value;
+ _INITIALIZED_SCALAR_BASE_FUNCS(Bool_i,bool,Init)
+};
+typedef Bool_i<0> Bool_z;
+
+// float is special, because C++ does not permit the template initalization:
+struct Float_z {
+ float value;
+ _INITIALIZED_SCALAR_FUNCS(Float_z,float,0)
+};
+struct Double_z {
+ double value;
+ _INITIALIZED_SCALAR_FUNCS(Double_z,double,0)
+};
+
+
+class ItemWithValueAndWidth {
+ public:
+ virtual unsigned getValue() const = 0;
+ virtual unsigned getWidth() const = 0;
+};
+
+// A Range Networks Field with a specified width.
+// See RLCMessages.h for examples.
+template <int Width=32, unsigned Init=0>
+class Field_i : public ItemWithValueAndWidth
+{
+ public:
+ unsigned value;
+ _INITIALIZED_SCALAR_FUNCS(Field_i,unsigned,Init)
+ unsigned getWidth() const { return Width; }
+ unsigned getValue() const { return value; }
+};
+
+// Synonym for Field_i, but no way to do it.
+template <int Width, unsigned Init=0>
+class Field_z : public ItemWithValueAndWidth
+{
+ public:
+ unsigned value;
+ _INITIALIZED_SCALAR_FUNCS(Field_z,unsigned,Init)
+ unsigned getWidth() const { return Width; }
+ unsigned getValue() const { return value; }
+};
+
+// This is an uninitialized field.
+template <int Width=32, unsigned Init=0>
+class Field : public ItemWithValueAndWidth
+{
+ public:
+ unsigned value;
+ _INITIALIZED_SCALAR_FUNCS(Field,unsigned,Init)
+ unsigned getWidth() const { return Width; }
+ unsigned getValue() const { return value; }
+};
+
+
+// A Z100Timer with an initial value specified.
+//template <int Init>
+//class Z100Timer_i : public GSM::Z100Timer {
+// public:
+// Z100Timer_i() : GSM::Z100Timer(Init) {}
+//};
+
+#endif
diff --git a/CommonLibs/Sockets.cpp b/CommonLibs/Sockets.cpp
index dc110c8..dd7527c 100644
--- a/CommonLibs/Sockets.cpp
+++ b/CommonLibs/Sockets.cpp
@@ -41,6 +41,10 @@
#include <stdlib.h>
+
+
+
+
bool resolveAddress(struct sockaddr_in *address, const char *hostAndPort)
{
assert(address);
@@ -255,6 +259,11 @@ void UDPSocket::open(unsigned short localPort)
throw SocketError();
}
+ // pat added: This lets the socket be reused immediately, which is needed if OpenBTS crashes.
+ int on = 1;
+ setsockopt(mSocketFD, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+
// bind
struct sockaddr_in address;
size_t length = sizeof(address);
diff --git a/CommonLibs/Threads.cpp b/CommonLibs/Threads.cpp
index 7cc8b92..de6520b 100644
--- a/CommonLibs/Threads.cpp
+++ b/CommonLibs/Threads.cpp
@@ -107,8 +107,9 @@ void Thread::start(void *(*task)(void*), void *arg)
{
assert(mThread==((pthread_t)0));
bool res;
- res = pthread_attr_init(&mAttrib);
- assert(!res);
+ // (pat) Moved initialization to constructor to avoid crash in destructor.
+ //res = pthread_attr_init(&mAttrib);
+ //assert(!res);
res = pthread_attr_setstacksize(&mAttrib, mStackSize);
assert(!res);
res = pthread_create(&mThread, &mAttrib, task, arg);
diff --git a/CommonLibs/Threads.h b/CommonLibs/Threads.h
index eba8e89..a38a73a 100644
--- a/CommonLibs/Threads.h
+++ b/CommonLibs/Threads.h
@@ -155,12 +155,16 @@ class Thread {
public:
/** Create a thread in a non-running state. */
- Thread(size_t wStackSize = (65536*4)):mThread((pthread_t)0) { mStackSize=wStackSize;}
+ Thread(size_t wStackSize = (65536*4)):mThread((pthread_t)0) {
+ pthread_attr_init(&mAttrib); // (pat) moved this here.
+ mStackSize=wStackSize;
+ }
/**
Destroy the Thread.
It should be stopped and joined.
*/
+ // (pat) If the Thread is destroyed without being started, then mAttrib is undefined. Oops.
~Thread() { pthread_attr_destroy(&mAttrib); }
@@ -168,7 +172,7 @@ class Thread {
void start(void *(*task)(void*), void *arg);
/** Join a thread that will stop on its own. */
- void join() { int s = pthread_join(mThread,NULL); assert(!s); }
+ void join() { int s = pthread_join(mThread,NULL); assert(!s); mThread = 0; }
};
diff --git a/CommonLibs/Timeval.h b/CommonLibs/Timeval.h
index f528819..c497864 100644
--- a/CommonLibs/Timeval.h
+++ b/CommonLibs/Timeval.h
@@ -32,6 +32,7 @@
#include <unistd.h>
+
/** A wrapper on usleep to sleep for milliseconds. */
inline void msleep(long v) { usleep(v*1000); }
diff --git a/CommonLibs/URLEncode.cpp b/CommonLibs/URLEncode.cpp
index 025ef60..cdf38dd 100644
--- a/CommonLibs/URLEncode.cpp
+++ b/CommonLibs/URLEncode.cpp
@@ -1,27 +1,4 @@
-/*
-* Copyright 2011 Free Software Foundation, Inc.
-*
-*
-* This software is distributed under the terms of the GNU Affero Public License.
-* See the COPYING file in the main directory for details.
-*
-* This use of this software may be subject to additional restrictions.
-* See the LEGAL file in the main directory for details.
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-*/
+/* Copyright 2011, Range Networks, Inc. */
#include <URLEncode.h>
#include <string>
diff --git a/CommonLibs/URLEncodeTest.cpp b/CommonLibs/URLEncodeTest.cpp
new file mode 100644
index 0000000..dbc4630
--- /dev/null
+++ b/CommonLibs/URLEncodeTest.cpp
@@ -0,0 +1,17 @@
+
+#include "URLEncode.h"
+#include <string>
+#include <iostream>
+
+
+using namespace std;
+
+
+int main(int argc, char *argv[])
+{
+
+ string test = string("Testing: !@#$%^&*() " __DATE__ " " __TIME__);
+ cout << test << endl;
+ cout << URLEncode(test) << endl;
+}
+
diff --git a/CommonLibs/Utils.cpp b/CommonLibs/Utils.cpp
new file mode 100644
index 0000000..1da95fa
--- /dev/null
+++ b/CommonLibs/Utils.cpp
@@ -0,0 +1,211 @@
+/*
+* Copyright 2011 Range Networks, Inc.
+* All Rights Reserved.
+*
+* This software is distributed under multiple licenses;
+* see the COPYING file in the main directory for licensing
+* information for this specific distribuion.
+*
+* This use of this software may be subject to additional restrictions.
+* See the LEGAL file in the main directory for details.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+*/
+
+#include <unistd.h> // For usleep
+#include <sys/time.h> // For gettimeofday
+#include <stdio.h> // For vsnprintf
+#include <ostream> // For ostream
+#include <sstream> // For ostringstream
+#include <string.h> // For strcpy
+//#include "GSMCommon.h"
+#include "Utils.h"
+#include "MemoryLeak.h"
+
+namespace Utils {
+
+MemStats gMemStats;
+int gMemLeakDebug = 0;
+
+MemStats::MemStats()
+{
+ memset(mMemNow,0,sizeof(mMemNow));
+ memset(mMemTotal,0,sizeof(mMemTotal));
+ memset(mMemName,0,sizeof(mMemName));
+}
+
+void MemStats::text(std::ostream &os)
+{
+ os << "Structs current total:\n";
+ for (int i = 0; i < mMax; i++) {
+ os << "\t" << (mMemName[i] ? mMemName[i] : "unknown") << " " << mMemNow[i] << " " << mMemTotal[i] << "\n";
+ }
+}
+
+void MemStats::memChkNew(MemoryNames memIndex, const char *id)
+{
+ /*std::cout << "new " #type "\n";*/
+ mMemNow[memIndex]++;
+ mMemTotal[memIndex]++;
+ mMemName[memIndex] = id;
+}
+
+void MemStats::memChkDel(MemoryNames memIndex, const char *id)
+{
+ /*std::cout << "del " #type "\n";*/
+ mMemNow[memIndex]--;
+ if (mMemNow[memIndex] < 0) {
+ LOG(ERR) << "Memory underflow on type "<<id;
+ if (gMemLeakDebug) assert(0);
+ mMemNow[memIndex] += 100; // Prevent another message for a while.
+ }
+}
+
+std::ostream& operator<<(std::ostream& os, std::ostringstream& ss)
+{
+ return os << ss.str();
+}
+
+std::ostream &osprintf(std::ostream &os, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[300];
+ va_start(ap,fmt);
+ int n = vsnprintf(buf,300,fmt,ap);
+ va_end(ap);
+ if (n >= (300-4)) { strcpy(&buf[(300-4)],"..."); }
+ os << buf;
+ return os;
+}
+
+std::string format(const char *fmt, ...)
+{
+ va_list ap;
+ char buf[300];
+ va_start(ap,fmt);
+ int n = vsnprintf(buf,300,fmt,ap);
+ va_end(ap);
+ if (n >= (300-4)) { strcpy(&buf[(300-4)],"..."); }
+ return std::string(buf);
+}
+
+// Return time in seconds with high resolution.
+// Note: In the past I found this to be a surprisingly expensive system call in linux.
+double timef()
+{
+ struct timeval tv;
+ gettimeofday(&tv,NULL);
+ return tv.tv_usec / 1000000.0 + tv.tv_sec;
+}
+
+const std::string timestr()
+{
+ struct timeval tv;
+ struct tm tm;
+ gettimeofday(&tv,NULL);
+ localtime_r(&tv.tv_sec,&tm);
+ unsigned tenths = tv.tv_usec / 100000; // Rounding down is ok.
+ return format(" %02d:%02d:%02d.%1d",tm.tm_hour,tm.tm_min,tm.tm_sec,tenths);
+}
+
+// High resolution sleep for the specified time.
+// Return FALSE if time is already past.
+void sleepf(double howlong)
+{
+ if (howlong <= 0.00001) return; // Less than 10 usecs, forget it.
+ usleep((useconds_t) (1000000.0 * howlong));
+}
+
+//bool sleepuntil(double untilwhen)
+//{
+ //double now = timef();
+ //double howlong = untilwhen - now; // Fractional time in seconds.
+ // We are not worrying about overflow because all times should be in the near future.
+ //if (howlong <= 0.00001) return false; // Less than 10 usecs, forget it.
+ //sleepf(sleeptime);
+//}
+
+std::string Text2Str::str() const
+{
+ std::ostringstream ss;
+ text(ss);
+ return ss.str();
+}
+
+std::ostream& operator<<(std::ostream& os, const Text2Str *val)
+{
+ std::ostringstream ss;
+ if (val) {
+ val->text(ss);
+ os << ss.str();
+ } else {
+ os << "(null)";
+ }
+ return os;
+}
+
+// Greatest Common Denominator.
+// This is by Doug Brown.
+int gcd(int x, int y)
+{
+ if (x > y) {
+ return x % y == 0 ? y : gcd(y, x % y);
+ } else {
+ return y % x == 0 ? x : gcd(x, y % x);
+ }
+}
+
+
+// Split a C string into an argc,argv array in place; the input string is modified.
+// Returns argc, and places results in argv, up to maxargc elements.
+// The final argv receives the rest of the input string from maxargc on,
+// even if it contains additional splitchars.
+// The correct idiom for use is to make a copy of your string, like this:
+// char *copy = strcpy((char*)alloca(the_string.length()+1),the_string.c_str());
+// char *argv[2];
+// int argc = cstrSplit(copy,argv,2,NULL);
+// If you want to detect the error of too many arguments, add 1 to argv, like this:
+// char *argv[3];
+// int argc = cstrSplit(copy,argv,3,NULL);
+// if (argc == 3) { error("too many arguments"; }
+int cstrSplit(char *in, char **pargv,int maxargc, const char *splitchars)
+{
+ if (splitchars == NULL) { splitchars = " \t\r\n"; } // Default is any space.
+ int argc = 0;
+ while (argc < maxargc) {
+ while (*in && strchr(splitchars,*in)) {in++;} // scan past any splitchars
+ if (! *in) return argc; // return if finished.
+ pargv[argc++] = in; // save ptr to start of arg.
+ in = strpbrk(in,splitchars); // go to end of arg.
+ if (!in) return argc; // return if finished.
+ *in++ = 0; // zero terminate this arg.
+ }
+ return argc;
+}
+
+std::ostream& operator<<(std::ostream& os, const Statistic<int> &stat) { stat.text(os); return os; }
+std::ostream& operator<<(std::ostream& os, const Statistic<unsigned> &stat) { stat.text(os); return os; }
+std::ostream& operator<<(std::ostream& os, const Statistic<float> &stat) { stat.text(os); return os; }
+std::ostream& operator<<(std::ostream& os, const Statistic<double> &stat) { stat.text(os); return os; }
+
+std::string replaceAll(const std::string input, const std::string search, const std::string replace)
+{
+ std::string output = input;
+ int index = 0;
+
+ while (true) {
+ index = output.find(search, index);
+ if (index == std::string::npos) {
+ break;
+ }
+
+ output.replace(index, replace.length(), replace);
+ index += replace.length();
+ }
+
+ return output;
+}
+
+};
diff --git a/CommonLibs/Utils.h b/CommonLibs/Utils.h
new file mode 100644
index 0000000..0bc738e
--- /dev/null
+++ b/CommonLibs/Utils.h
@@ -0,0 +1,148 @@
+/*
+* Copyright 2011 Range Networks, Inc.
+* All Rights Reserved.
+*
+* This software is distributed under multiple licenses;
+* see the COPYING file in the main directory for licensing
+* information for this specific distribuion.
+*
+* This use of this software may be subject to additional restrictions.
+* See the LEGAL file in the main directory for details.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+*/
+
+#ifndef GPRSUTILS_H
+#define GPRSUTILS_H
+#include <stdint.h>
+#include <stdarg.h>
+#include <string>
+#include <string.h>
+#include <math.h> // for sqrtf
+#include "Logger.h"
+
+
+namespace Utils {
+
+extern double timef(); // high resolution time
+extern const std::string timestr(); // A timestamp to print in messages.
+extern void sleepf(double howlong); // high resolution sleep
+extern int gcd(int x, int y);
+
+// It is irritating to create a string just to interface to the brain-damaged
+// C++ stream class, but this is only used for debug messages.
+std::string format(const char *fmt, ...) __attribute__((format (printf,1,2)));
+
+int cstrSplit(char *in, char **pargv,int maxargc, const char *splitchars=NULL);
+
+// For classes with a text() function, provide a function to return a String,
+// and also a standard << stream function that takes a pointer to the object.
+// We dont provide the function that takes a reference to the object
+// because it is too highly overloaded and generally doesnt work.
+class Text2Str {
+ public:
+ virtual void text(std::ostream &os) const = 0;
+ std::string str() const;
+};
+std::ostream& operator<<(std::ostream& os, const Text2Str *val);
+
+#if 0
+// Generic Activity Timer. Lots of controls to make everybody happy.
+class ATimer {
+ double mStart;
+ //bool mActive;
+ double mLimitTime;
+ public:
+ ATimer() : mStart(0), mLimitTime(0) { }
+ ATimer(double wLimitTime) : mStart(0), mLimitTime(wLimitTime) { }
+ void start() { mStart=timef(); }
+ void stop() { mStart=0; }
+ bool active() { return !!mStart; }
+ double elapsed() { return timef() - mStart; }
+ bool expired() { return elapsed() > mLimitTime; }
+};
+#endif
+
+
+struct BitSet {
+ unsigned mBits;
+ void setBit(unsigned whichbit) { mBits |= 1<<whichbit; }
+ void clearBit(unsigned whichbit) { mBits &= ~(1<<whichbit); }
+ unsigned getBit(unsigned whichbit) const { return mBits & (1<<whichbit); }
+ bool isSet(unsigned whichbit) const { return mBits & (1<<whichbit); }
+ unsigned bits() const { return mBits; }
+ operator int(void) const { return mBits; }
+ BitSet() { mBits = 0; }
+};
+
+// Store current, min, max and compute running average and standard deviation.
+template<class Type> struct Statistic {
+ Type mCurrent, mMin, mMax; // min,max optional initialization so you can print before adding any values.
+ unsigned mCnt;
+ double mSum;
+ //double mSum2; // sum of squares.
+ // (Type) cast needed in case Type is an enum, stupid language.
+ Statistic() : mCurrent((Type)0), mMin((Type)0), mMax((Type)0), mCnt(0), mSum(0) /*,mSum2(0)*/ {}
+ // Set the current value and add a statisical point.
+ void addPoint(Type val) {
+ mCurrent = val;
+ if (mCnt == 0 || val < mMin) {mMin = val;}
+ if (mCnt == 0 || val > mMax) {mMax = val;}
+ mCnt++;
+ mSum += val;
+ //mSum2 += val * val;
+ }
+ Type getCurrent() const { // Return current value.
+ return mCnt ? mCurrent : 0;
+ }
+ double getAvg() const { // Return average.
+ return mCnt==0 ? 0 : mSum/mCnt;
+ };
+ //float getSD() const { // Return standard deviation. Use low precision square root function.
+ // return mCnt==0 ? 0 : sqrtf(mCnt * mSum2 - mSum*mSum) / mCnt;
+ //}
+
+ void text(std::ostream &os) const { // Print everything in parens.
+ os << "("<<mCurrent;
+ if (mMin != mMax) { // Not point in printing all this stuff if min == max.
+ os <<LOGVAR2("min",mMin)<<LOGVAR2("max",mMax)<<LOGVAR2("avg",getAvg());
+ if (mCnt <= 999999) {
+ os <<LOGVAR2("N",mCnt);
+ } else { // Shorten this up:
+ char buf[10], *ep;
+ sprintf(buf,"%.3g",round(mCnt));
+ if ((ep = strchr(buf,'e')) && ep[1] == '+') { strcpy(ep+1,ep+2); }
+ os << LOGVAR2("N",buf);
+ }
+ // os<<LOGVAR2("sd",getSD()) standard deviation not interesting
+ }
+ os << ")";
+ // " min="<<mMin <<" max="<<mMax <<format(" avg=%4g sd=%3g)",getAvg(),getSD());
+ }
+ // Not sure if this works:
+ //std::string statStr() const {
+ // return (std::string)mCurrent + " min=" + (std::string) mMin +" max="+(string)mMax+ format(" avg=%4g sd=%3g",getAvg(),getSD());
+ //}
+};
+
+// This I/O mechanism is so dumb:
+std::ostream& operator<<(std::ostream& os, const Statistic<int> &stat);
+std::ostream& operator<<(std::ostream& os, const Statistic<unsigned> &stat);
+std::ostream& operator<<(std::ostream& os, const Statistic<float> &stat);
+std::ostream& operator<<(std::ostream& os, const Statistic<double> &stat);
+
+
+// Yes, they botched and left this out:
+std::ostream& operator<<(std::ostream& os, std::ostringstream& ss);
+
+std::ostream &osprintf(std::ostream &os, const char *fmt, ...) __attribute__((format (printf,2,3)));
+
+std::string replaceAll(const std::string input, const std::string search, const std::string replace);
+
+}; // namespace
+
+using namespace Utils;
+
+#endif
diff --git a/CommonLibs/Vector.h b/CommonLibs/Vector.h
index 62cb6fb..38dc8d5 100644
--- a/CommonLibs/Vector.h
+++ b/CommonLibs/Vector.h
@@ -32,6 +32,10 @@
#include <string.h>
#include <iostream>
#include <assert.h>
+// We cant use Logger.h in this file...
+extern int gVectorDebug;
+#define BVDEBUG(msg) if (gVectorDebug) {std::cout << msg;}
+
/**
@@ -59,6 +63,14 @@ template <class T> class Vector {
public:
+ /****
+ char *inspect() {
+ static char buf[100];
+ sprintf(buf," mData=%p mStart=%p mEnd=%p ",mData,mStart,mEnd);
+ return buf;
+ }
+ ***/
+
/** Return the size of the Vector. */
size_t size() const
{
@@ -246,6 +258,7 @@ template <class T> class Vector {
T* begin() { return mStart; }
const T* end() const { return mEnd; }
T* end() { return mEnd; }
+ bool isOwner() { return !!mData; } // Do we own any memory ourselves?
//@}
diff --git a/CommonLibs/VectorTest.cpp b/CommonLibs/VectorTest.cpp
index 6958889..ad5c473 100644
--- a/CommonLibs/VectorTest.cpp
+++ b/CommonLibs/VectorTest.cpp
@@ -28,6 +28,10 @@
#include "Vector.h"
#include <iostream>
+// We must have a gConfig now to include Vector.
+#include "Configuration.h"
+ConfigurationTable gConfig;
+
using namespace std;
typedef Vector<int> TestVector;