diff --git a/CommonLibs/BitVector.cpp b/CommonLibs/BitVector.cpp new file mode 100644 index 00000000..1d13dec9 --- /dev/null +++ b/CommonLibs/BitVector.cpp @@ -0,0 +1,603 @@ +/* +* Copyright 2008, 2009 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 . + +*/ + + + + +#include "BitVector.h" +#include +#include + +using namespace std; + + +/** + Apply a Galois polymonial to a binary seqeunce. + @param val The input sequence. + @param poly The polynomial. + @param order The order of the polynomial. + @return Single-bit result. +*/ +unsigned applyPoly(uint64_t val, uint64_t poly, unsigned order) +{ + uint64_t prod = val & poly; + unsigned sum = prod; + for (unsigned i=1; i>i; + return sum & 0x01; +} + + + + + + +BitVector::BitVector(const char *valString) + :Vector(strlen(valString)) +{ + uint32_t accum = 0; + for (size_t i=0; i=0; i--) { + accum = (accum<<1) | ((*dp--) & 0x01); + } + return accum; +} + + + + +uint64_t BitVector::readField(size_t& readIndex, unsigned length) const +{ + const uint64_t retVal = peekField(readIndex,length); + readIndex += length; + return retVal; +} + + +uint64_t BitVector::readFieldReversed(size_t& readIndex, unsigned length) const +{ + const uint64_t retVal = peekFieldReversed(readIndex,length); + readIndex += length; + return retVal; +} + + + + + +void BitVector::fillField(size_t writeIndex, uint64_t value, unsigned length) +{ + char *dpBase = mStart + writeIndex; + char *dp = dpBase + length - 1; + assert(dp < mEnd); + while (dp>=dpBase) { + *dp-- = value & 0x01; + value >>= 1; + } +} + + +void BitVector::fillFieldReversed(size_t writeIndex, uint64_t value, unsigned length) +{ + char *dp = mStart + writeIndex; + char *dpEnd = dp + length - 1; + assert(dpEnd < mEnd); + while (dp<=dpEnd) { + *dp++ = value & 0x01; + value >>= 1; + } +} + + + + +void BitVector::writeField(size_t& writeIndex, uint64_t value, unsigned length) +{ + fillField(writeIndex,value,length); + writeIndex += length; +} + + +void BitVector::writeFieldReversed(size_t& writeIndex, uint64_t value, unsigned length) +{ + fillFieldReversed(writeIndex,value,length); + writeIndex += length; +} + + +void BitVector::invert() +{ + for (size_t i=0; i=8); + + char tmp0 = mStart[0]; + mStart[0] = mStart[7]; + mStart[7] = tmp0; + + char tmp1 = mStart[1]; + mStart[1] = mStart[6]; + mStart[6] = tmp1; + + char tmp2 = mStart[2]; + mStart[2] = mStart[5]; + mStart[5] = tmp2; + + char tmp3 = mStart[3]; + mStart[3] = mStart[4]; + mStart[4] = tmp3; +} + + + +void BitVector::LSB8MSB() +{ + if (size()<8) return; + size_t size8 = 8*(size()/8); + size_t iTop = size8 - 8; + for (size_t i=0; i<=iTop; i+=8) segment(i,8).reverse8(); +} + + + +uint64_t BitVector::syndrome(Generator& gen) const +{ + gen.clear(); + const char *dp = mStart; + while (dpiState) << 1; // input state for 0 + const uint32_t iState1 = iState0 | 0x01; // input state for 1 + const uint32_t oStateShifted = (sp->oState) << mIRate; // shifted output + const float cost = sp->cost; + sp++; + // 0 input extension + mCandidates[i].cost = cost; + mCandidates[i].oState = oStateShifted | mGeneratorTable[iState0 & mCMask]; + mCandidates[i].iState = iState0; + // 1 input extension + mCandidates[i+1].cost = cost; + mCandidates[i+1].oState = oStateShifted | mGeneratorTable[iState1 & mCMask]; + mCandidates[i+1].iState = iState1; + } +} + + +void ViterbiR2O4::getSoftCostMetrics(const uint32_t inSample, const float *matchCost, const float *mismatchCost) +{ + const float *cTab[2] = {matchCost,mismatchCost}; + for (unsigned i=0; i>1)&0x01][0]; + } +} + + +void ViterbiR2O4::pruneCandidates() +{ + const vCand* c1 = mCandidates; // 0-prefix + const vCand* c2 = mCandidates + mIStates; // 1-prefix + for (unsigned i=0; i=minCost) continue; + minCost = thisCost; + minIndex=i; + } + return mSurvivors[minIndex]; +} + + +const ViterbiR2O4::vCand& ViterbiR2O4::step(uint32_t inSample, const float *probs, const float *iprobs) +{ + branchCandidates(); + getSoftCostMetrics(inSample,probs,iprobs); + pruneCandidates(); + return minCost(); +} + + +uint64_t Parity::syndrome(const BitVector& receivedCodeword) +{ + return receivedCodeword.syndrome(*this); +} + + +void Parity::writeParityWord(const BitVector& data, BitVector& parityTarget, bool invert) +{ + uint64_t pWord = data.parity(*this); + if (invert) pWord = ~pWord; + parityTarget.fillField(0,pWord,size()); +} + + + + + + + + + +SoftVector::SoftVector(const BitVector& source) +{ + resize(source.size()); + for (size_t i=0; i0.5F) newSig[i]=1; + else newSig[i] = 0; + } + return newSig; +} + + + +void SoftVector::decode(ViterbiR2O4 &decoder, BitVector& target) const +{ + const size_t sz = size(); + const unsigned deferral = decoder.deferral(); + const size_t ctsz = sz + deferral*decoder.iRate(); + assert(sz <= decoder.iRate()*target.size()); + + // Build a "history" array where each element contains the full history. + uint32_t history[ctsz]; + { + BitVector bits = sliced(); + uint32_t accum = 0; + for (size_t i=0; i0.5F) pVal = 1.0F-pVal; + float ipVal = 1.0F-pVal; + // This is a cheap approximation to an ideal cost function. + if (pVal<0.01F) pVal = 0.01; + if (ipVal<0.01F) ipVal = 0.01; + matchCostTable[i] = 0.25F/ipVal; + mismatchCostTable[i] = 0.25F/pVal; + } + + // pad end of table with unknowns + for (size_t i=sz; i=deferral) *op++ = (minCost.iState >> deferral)&0x01; + oCount++; + } + } +} + + + + +ostream& operator<<(ostream& os, const SoftVector& sv) +{ + for (size_t i=0; i0.75) os << "1"; + else os << "-"; + } + return os; +} + + + +void BitVector::pack(unsigned char* targ) const +{ + // Assumes MSB-first packing. + unsigned bytes = size()/8; + for (unsigned i=0; i0) { + if (sscanf(src+digits, "%1x", &val) < 1) { + return false; + } + fillField(whole,val,rem); + } + return true; +} + +// vim: ts=4 sw=4 diff --git a/CommonLibs/BitVector.h b/CommonLibs/BitVector.h new file mode 100644 index 00000000..572e6b47 --- /dev/null +++ b/CommonLibs/BitVector.h @@ -0,0 +1,441 @@ +/* +* Copyright 2008, 2009 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 . + +*/ + + +#ifndef FECVECTORS_H +#define FECVECTORS_H + +#include "Vector.h" +#include + + +class BitVector; +class SoftVector; + + + +/** Shift-register (LFSR) generator. */ +class Generator { + + private: + + uint64_t mCoeff; ///< polynomial coefficients. LSB is zero exponent. + uint64_t mState; ///< shift register state. LSB is most recent. + uint64_t mMask; ///< mask for reading state + unsigned mLen; ///< number of bits used in shift register + unsigned mLen_1; ///< mLen - 1 + + public: + + Generator(uint64_t wCoeff, unsigned wLen) + :mCoeff(wCoeff),mState(0), + mMask((1ULL<>(mLen_1)) & 0x01; + mState = (mState<<1) ^ (inBit & 0x01); + if (fb) mState ^= mCoeff; + } + + /** + Update the generator state by one cycle. + This is in the .h for inlining. + */ + void encoderShift(unsigned inBit) + { + const unsigned fb = ((mState>>(mLen_1)) ^ inBit) & 0x01; + mState <<= 1; + if (fb) mState ^= mCoeff; + } + + +}; + + + + +/** Parity (CRC-type) generator and checker based on a Generator. */ +class Parity : public Generator { + + protected: + + unsigned mCodewordSize; + + public: + + Parity(uint64_t wCoefficients, unsigned wParitySize, unsigned wCodewordSize) + :Generator(wCoefficients, wParitySize), + mCodewordSize(wCodewordSize) + { } + + /** Compute the parity word and write it into the target segment. */ + void writeParityWord(const BitVector& data, BitVector& parityWordTarget, bool invert=true); + + /** Compute the syndrome of a received sequence. */ + uint64_t syndrome(const BitVector& receivedCodeword); +}; + + + + +/** + Class to represent convolutional coders/decoders of rate 1/2, memory length 4. + This is the "workhorse" coder for most GSM channels. +*/ +class ViterbiR2O4 { + + private: + /**name Lots of precomputed elements so the compiler can optimize like hell. */ + //@{ + /**@name Core values. */ + //@{ + static const unsigned mIRate = 2; ///< reciprocal of rate + static const unsigned mOrder = 4; ///< memory length of generators + //@} + /**@name Derived values. */ + //@{ + static const unsigned mIStates = 0x01 << mOrder; ///< number of states, number of survivors + static const uint32_t mSMask = mIStates-1; ///< survivor mask + static const uint32_t mCMask = (mSMask<<1) | 0x01; ///< candidate mask + static const uint32_t mOMask = (0x01< { + + + public: + + /**@name Constructors. */ + //@{ + + /**@name Casts of Vector constructors. */ + //@{ + BitVector(char* wData, char* wStart, char* wEnd) + :Vector(wData,wStart,wEnd) + { } + BitVector(size_t len=0):Vector(len) {} + BitVector(const Vector& source):Vector(source) {} + BitVector(Vector& source):Vector(source) {} + BitVector(const Vector& source1, const Vector source2):Vector(source1,source2) {} + //@} + + /** Construct from a string of "0" and "1". */ + BitVector(const char* valString); + //@} + + /** Index a single bit. */ + bool bit(size_t index) const + { + // We put this code in .h for fast inlining. + const char *dp = mStart+index; + assert(dp::segment(start,span)); } + + BitVector head(size_t span) { return segment(0,span); } + const BitVector head(size_t span) const { return segment(0,span); } + BitVector tail(size_t start) { return segment(start,size()-start); } + const BitVector tail(size_t start) const { return segment(start,size()-start); } + //@} + + + void zero() { fill(0); } + + /**@name FEC operations. */ + //@{ + /** Calculate the syndrome of the vector with the given Generator. */ + uint64_t syndrome(Generator& gen) const; + /** Calculate the parity word for the vector with the given Generator. */ + uint64_t parity(Generator& gen) const; + /** Encode the signal with the GSM rate 1/2 convolutional encoder. */ + void encode(const ViterbiR2O4& encoder, BitVector& target); + //@} + + + /** Invert 0<->1. */ + void invert(); + + /**@name Byte-wise operations. */ + //@{ + /** Reverse an 8-bit vector. */ + void reverse8(); + /** Reverse groups of 8 within the vector (byte reversal). */ + void LSB8MSB(); + //@} + + /**@name Serialization and deserialization. */ + //@{ + uint64_t peekField(size_t readIndex, unsigned length) const; + uint64_t peekFieldReversed(size_t readIndex, unsigned length) const; + uint64_t readField(size_t& readIndex, unsigned length) const; + uint64_t readFieldReversed(size_t& readIndex, unsigned length) const; + void fillField(size_t writeIndex, uint64_t value, unsigned length); + 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); + //@} + + /** Sum of bits. */ + unsigned sum() const; + + /** Reorder bits, dest[i] = this[map[i]]. */ + void map(const unsigned *map, size_t mapSize, BitVector& dest) const; + + /** Reorder bits, dest[map[i]] = this[i]. */ + void unmap(const unsigned *map, size_t mapSize, BitVector& dest) const; + + /** Pack into a char array. */ + void pack(unsigned char*) const; + + /** Unpack from a char array. */ + void unpack(const unsigned char*); + + /** Make a hexdump string. */ + void hex(std::ostream&) const; + + /** Unpack from a hexdump string. + * @returns true on success, false on error. */ + bool unhex(const char*); + +}; + + + +std::ostream& operator<<(std::ostream&, const BitVector&); + + + + + + +/** + The SoftVector class is used to represent a soft-decision signal. + Values 0..1 represent probabilities that a bit is "true". + */ +class SoftVector: public Vector { + + public: + + /** Build a SoftVector of a given length. */ + SoftVector(size_t wSize=0):Vector(wSize) {} + + /** Construct a SoftVector from a C string of "0", "1", and "X". */ + SoftVector(const char* valString); + + /** Construct a SoftVector from a BitVector. */ + SoftVector(const BitVector& source); + + /** + Wrap a SoftVector around a block of floats. + The block will be delete[]ed upon desctuction. + */ + SoftVector(float *wData, unsigned length) + :Vector(wData,length) + {} + + SoftVector(float* wData, float* wStart, float* wEnd) + :Vector(wData,wStart,wEnd) + { } + + /** + Casting from a Vector. + Note that this is NOT pass-by-reference. + */ + SoftVector(Vector source) + :Vector(source) + {} + + + /**@name Casts and overrides of Vector operators. */ + //@{ + SoftVector segment(size_t start, size_t span) + { + float* wStart = mStart + start; + float* wEnd = wStart + span; + assert(wEnd<=mEnd); + return SoftVector(NULL,wStart,wEnd); + } + + SoftVector alias() + { return segment(0,size()); } + + const SoftVector segment(size_t start, size_t span) const + { return (SoftVector)(Vector::segment(start,span)); } + + SoftVector head(size_t span) { return segment(0,span); } + const SoftVector head(size_t span) const { return segment(0,span); } + SoftVector tail(size_t start) { return segment(start,size()-start); } + const SoftVector tail(size_t start) const { return segment(start,size()-start); } + //@} + + /** Decode soft symbols with the GSM rate-1/2 Viterbi decoder. */ + void decode(ViterbiR2O4 &decoder, BitVector& target) const; + + /** Fill with "unknown" values. */ + void unknown() { fill(0.5F); } + + /** Return a hard bit value from a given index by slicing. */ + bool bit(size_t index) const + { + const float *dp = mStart+index; + assert(dp0.5F; + } + + /** Slice the whole signal into bits. */ + BitVector sliced() const; + +}; + + + +std::ostream& operator<<(std::ostream&, const SoftVector&); + + + + + + +#endif +// vim: ts=4 sw=4 diff --git a/CommonLibs/BitVectorTest.cpp b/CommonLibs/BitVectorTest.cpp new file mode 100644 index 00000000..5e487ad0 --- /dev/null +++ b/CommonLibs/BitVectorTest.cpp @@ -0,0 +1,88 @@ +/* +* Copyright 2008 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 . + +*/ + + + + +#include "BitVector.h" +#include +#include + +using namespace std; + + +int main(int argc, char *argv[]) +{ + BitVector v1("0000111100111100101011110000"); + cout << v1 << endl; + v1.LSB8MSB(); + cout << v1 << endl; + ViterbiR2O4 vCoder; + BitVector v2(v1.size()*2); + v1.encode(vCoder,v2); + cout << v2 << endl; + SoftVector sv2(v2); + cout << sv2 << endl; + for (unsigned i=0; i +#include +#include +#include + +using namespace std; + + +static const char* createConfigTable = { + "CREATE TABLE IF NOT EXISTS CONFIG (" + "KEYSTRING TEXT UNIQUE NOT NULL, " + "VALUESTRING TEXT, " + "STATIC INTEGER DEFAULT 0, " + "OPTIONAL INTEGER DEFAULT 0, " + "COMMENTS TEXT DEFAULT ''" + ")" +}; + + +ConfigurationTable::ConfigurationTable(const char* filename) +{ + // Connect to the database. + int rc = sqlite3_open(filename,&mDB); + if (rc) { + cerr << "Cannot open configuration database: " << sqlite3_errmsg(mDB); + sqlite3_close(mDB); + mDB = NULL; + return; + } + // Create the table, if needed. + if (!sqlite3_command(mDB,createConfigTable)) { + cerr << "Cannot create configuration table:" << sqlite3_errmsg(mDB); + } +} + + + +bool ConfigurationTable::defines(const string& key) +{ + assert(mDB); + ScopedLock lock(mLock); + + // Check the cache. + checkCacheAge(); + ConfigurationMap::const_iterator where = mCache.find(key); + if (where!=mCache.end()) return where->second.defined(); + + // Check the database. + char *value = NULL; + sqlite3_single_lookup(mDB,"CONFIG","KEYSTRING",key.c_str(),"VALUESTRING",value); + + // Cache the result. + if (value) { + mCache[key] = ConfigurationRecord(value); + free(value); + return true; + } + + mCache[key] = ConfigurationRecord(false); + return false; +} + + +const ConfigurationRecord& ConfigurationTable::lookup(const string& key) +{ + assert(mDB); + checkCacheAge(); + // We assume the caller holds mLock. + // So it is OK to return a reference into the cache. + + // Check the cache. + // This is cheap. + ConfigurationMap::const_iterator where = mCache.find(key); + if (where!=mCache.end()) { + if (where->second.defined()) return where->second; + // Unlock the mutex before throwing the exception. + mLock.unlock(); + syslog(LOG_ALERT, "configuration key %s not found", key.c_str()); + throw ConfigurationTableKeyNotFound(key); + } + + // Check the database. + // This is more expensive. + char *value = NULL; + sqlite3_single_lookup(mDB,"CONFIG", + "KEYSTRING",key.c_str(),"VALUESTRING",value); + + // Nothing defined? + if (!value) { + // Cache the failure. + mCache[key] = ConfigurationRecord(false); + // Unlock the mutex before throwing the exception. + mLock.unlock(); + throw ConfigurationTableKeyNotFound(key); + } + + // Cache the result. + mCache[key] = ConfigurationRecord(value); + free(value); + + // Leave mLock locked. The caller holds it still. + return mCache[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 +{ + assert(mDB); + unsigned optional; + bool success = sqlite3_single_lookup(mDB,"CONFIG","KEYSTRING",key.c_str(),"OPTIONAL",optional); + if (success) return !((bool)optional); + return false; +} + + + + +string ConfigurationTable::getStr(const string& key) +{ + // We need the lock because rec is a reference into the cache. + ScopedLock lock(mLock); + return lookup(key).value(); +} + +string ConfigurationTable::getStr(const string& key, const char* defaultValue) +{ + try { + return getStr(key); + } catch (ConfigurationTableKeyNotFound) { + set(key,defaultValue); + return string(defaultValue); + } +} + + +long ConfigurationTable::getNum(const string& key) +{ + // We need the lock because rec is a reference into the cache. + ScopedLock lock(mLock); + return lookup(key).number(); +} + + +long ConfigurationTable::getNum(const string& key, long defaultValue) +{ + try { + return getNum(key); + } catch (ConfigurationTableKeyNotFound) { + set(key,defaultValue); + return defaultValue; + } +} + + + +std::vector ConfigurationTable::getVector(const string& key) +{ + // Look up the string. + mLock.lock(); + const ConfigurationRecord& rec = lookup(key); + char* line = strdup(rec.value().c_str()); + mLock.unlock(); + // Parse the string. + std::vector retVal; + char *lp=line; + while (lp) { + // Watch for multiple or trailing spaces. + while (*lp==' ') lp++; + if (*lp=='\0') break; + retVal.push_back(strtol(lp,NULL,0)); + strsep(&lp," "); + } + free(line); + return retVal; +} + + +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()); +} + + +void ConfigurationTable::find(const string& pat, ostream& os) const +{ + // Prepare the statement. + string cmd = "SELECT KEYSTRING,VALUESTRING FROM CONFIG WHERE KEYSTRING LIKE \"%" + pat + "%\""; + sqlite3_stmt *stmt; + if (sqlite3_prepare_statement(mDB,&stmt,cmd.c_str())) return; + // Read the result. + int src = sqlite3_run_query(mDB,stmt); + 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; + src = sqlite3_run_query(mDB,stmt); + } + sqlite3_finalize(stmt); +} + + +bool ConfigurationTable::set(const string& key, const string& value) +{ + assert(mDB); + ScopedLock lock(mLock); + // Is it there already? + char * oldValue = NULL; + bool exists = sqlite3_single_lookup(mDB,"CONFIG","KEYSTRING",key.c_str(),"VALUESTRING",oldValue); + // Update or insert as appropriate. + string cmd; + if (exists) cmd = "UPDATE CONFIG SET VALUESTRING=\""+value+"\" WHERE KEYSTRING==\""+key+"\""; + else cmd = "INSERT INTO CONFIG (KEYSTRING,VALUESTRING,OPTIONAL) VALUES (\"" + key + "\",\"" + value + "\",1)"; + bool success = sqlite3_command(mDB,cmd.c_str()); + // Cache the result. + if (success) mCache[key] = ConfigurationRecord(value); + return success; +} + +bool ConfigurationTable::set(const string& key, long value) +{ + char buffer[30]; + sprintf(buffer,"%ld",value); + return set(key,buffer); +} + + +bool ConfigurationTable::set(const string& key) +{ + assert(mDB); + ScopedLock lock(mLock); + string cmd = "INSERT INTO CONFIG (KEYSTRING) VALUES (\"" + key + "\")"; + bool success = sqlite3_command(mDB,cmd.c_str()); + if (success) mCache[key] = ConfigurationRecord(true); + return success; +} + + +void ConfigurationTable::checkCacheAge() +{ + // mLock is set by caller + static time_t timeOfLastPurge = 0; + time_t now = time(NULL); + // purge every 3 seconds + // purge period cannot be configuration parameter + if (now - timeOfLastPurge < 3) return; + timeOfLastPurge = now; + // this is purge() without the lock + ConfigurationMap::iterator mp = mCache.begin(); + while (mp != mCache.end()) { + ConfigurationMap::iterator prev = mp; + mp++; + mCache.erase(prev); + } +} + + +void ConfigurationTable::purge() +{ + ScopedLock lock(mLock); + ConfigurationMap::iterator mp = mCache.begin(); + while (mp != mCache.end()) { + ConfigurationMap::iterator prev = mp; + mp++; + mCache.erase(prev); + } +} + + +void ConfigurationTable::setUpdateHook(void(*func)(void *,int ,char const *,char const *,sqlite3_int64)) +{ + assert(mDB); + sqlite3_update_hook(mDB,func,NULL); +} + + + +void HashString::computeHash() +{ + // FIXME -- Someone needs to review this hash function. + const char* cstr = c_str(); + mHash = 0; + for (unsigned i=0; i> 32); + mHash = mHash*127 + cstr[i]; + } +} + + + +// vim: ts=4 sw=4 diff --git a/CommonLibs/Configuration.h b/CommonLibs/Configuration.h new file mode 100644 index 00000000..c9c4cf3c --- /dev/null +++ b/CommonLibs/Configuration.h @@ -0,0 +1,275 @@ +/* +* Copyright 2009, 2010 Free Software Foundation, Inc. +* Copyright 2010 Kestrel Signal Processing, 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 . + +*/ + + +#ifndef CONFIGURATION_H +#define CONFIGURATION_H + + +#include "sqlite3util.h" + +#include +#include + +#include +#include +#include +#include + +#include +#include + + +/** A class for configuration file errors. */ +class ConfigurationTableError {}; + +/** An exception thrown when a given config key isn't found. */ +class ConfigurationTableKeyNotFound : public ConfigurationTableError { + + private: + + std::string mKey; + + public: + + ConfigurationTableKeyNotFound(const std::string& wKey) + :mKey(wKey) + { std::cerr << "cannot find configuration value " << mKey << std::endl; } + + const std::string& key() const { return mKey; } + +}; + + +class ConfigurationRecord { + + private: + + std::string mValue; + long mNumber; + bool mDefined; + + public: + + ConfigurationRecord(bool wDefined=true): + mDefined(wDefined) + { } + + ConfigurationRecord(const std::string& wValue): + mValue(wValue), + mNumber(strtol(wValue.c_str(),NULL,0)), + mDefined(true) + { } + + ConfigurationRecord(const char* wValue): + mValue(std::string(wValue)), + mNumber(strtol(wValue,NULL,0)), + mDefined(true) + { } + + + const std::string& value() const { return mValue; } + long number() const { return mNumber; } + bool defined() const { return mDefined; } + +}; + + +/** A string class that uses a hash function for comparison. */ +class HashString : public std::string { + + + protected: + + uint64_t mHash; + + void computeHash(); + + + public: + + HashString(const char* src) + :std::string(src) + { + computeHash(); + } + + HashString(const std::string& src) + :std::string(src) + { + computeHash(); + } + + HashString() + { + mHash=0; + } + + HashString& operator=(std::string& src) + { + std::string::operator=(src); + computeHash(); + return *this; + } + + HashString& operator=(const char* src) + { + std::string::operator=(src); + computeHash(); + return *this; + } + + bool operator==(const HashString& other) + { + return mHash==other.mHash; + } + + bool operator<(const HashString& other) + { + return mHash(const HashString& other) + { + return mHash ConfigurationMap; + + +/** + A class for maintaining a configuration key-value table, + based on sqlite3 and a local map-based cache. + Thread-safe, too. +*/ +class ConfigurationTable { + + private: + + sqlite3* mDB; ///< database connection + ConfigurationMap mCache; ///< cache of recently access configuration values + mutable Mutex mLock; ///< control for multithreaded access to the cache + + public: + + + ConfigurationTable(const char* filename = ":memory:"); + + /** 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 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); + + + /** + 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); + + + /** + Get a numeric parameter from the table. + Throw ConfigurationTableKeyNotFound if not found. + */ + long getNum(const std::string& key); + + /** + Get a numeric parameter from the table. + Define the parameter to the default value if not found. + */ + long getNum(const std::string& key, long defaultValue); + + /** + Get a numeric vector from the table. + */ + std::vector getVector(const std::string& key); + + /** Get length of a vector */ + unsigned getVectorLength(const std::string &key) + { return getVector(key).size(); } + + /** Set or change a value in the table. */ + bool set(const std::string& key, const std::string& value); + + /** Set or change a value in the table. */ + bool set(const std::string& key, long value); + + /** Create an entry in the table, no value though. */ + bool set(const std::string& key); + + /** + Remove a key from the table. + Will not remove static or required values. + @param key The key of the item to be deleted. + @return true if anything was actually removed. + */ + bool unset(const std::string& key); + + /** Search the table, dumping to a stream. */ + void find(const std::string& pattern, std::ostream&) const; + + /** Define the callback to purge the cache whenever the database changes. */ + void setUpdateHook(void(*)(void *,int ,char const *,char const *,sqlite3_int64)); + + /** purege cache if it exceeds a certain age */ + void checkCacheAge(); + + /** Delete all records from the cache. */ + void purge(); + + + private: + + /** + Attempt to lookup a record, cache if needed. + Throw ConfigurationTableKeyNotFound if not found. + Caller should hold mLock because the returned reference points into the cache. + */ + const ConfigurationRecord& lookup(const std::string& key); + +}; + + + +#endif + + +// vim: ts=4 sw=4 diff --git a/CommonLibs/ConfigurationTest.cpp b/CommonLibs/ConfigurationTest.cpp new file mode 100644 index 00000000..ef82601e --- /dev/null +++ b/CommonLibs/ConfigurationTest.cpp @@ -0,0 +1,69 @@ +/* +* Copyright 2009, 2010 Free Software Foundation, Inc. +* Copyright 2010 Kestrel Signal Processing, 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 . + +*/ + + + +#include "Configuration.h" +#include + +using namespace std; + +ConfigurationTable gConfig("exampleconfig.db"); + +void purgeConfig(void*,int,char const*, char const*, sqlite3_int64) +{ + //cout << "update hook" << endl; + gConfig.purge(); +} + + +int main(int argc, char *argv[]) +{ + + gConfig.setUpdateHook(purgeConfig); + + char *keys[5] = {"key1", "key2", "key3", "key4", "key5"}; + + for (int i=0; i<5; i++) { + gConfig.set(keys[i],i); + } + + for (int i=0; i<5; i++) { + cout << "table[" << keys[i] << "]=" << gConfig.getStr(keys[i]) << endl; + 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; + } + + gConfig.set("key5","100 200 300 400"); + std::vector vect = gConfig.getVector("key5"); + cout << "vect length " << vect.size() << ": "; + for (unsigned i=0; i. + +*/ + + +#ifndef F16_H +#define F16_H + +#include +#include + + + +/** Round a float to the appropriate F16 value. */ +inline int32_t _f16_round(float f) +{ + if (f>0.0F) return (int32_t)(f+0.5F); + if (f<0.0F) return (int32_t)(f-0.5F); + return 0; +} + + + +/** A class for F15.16 fixed point arithmetic with saturation. */ +class F16 { + + + private: + + int32_t mV; + + + public: + + F16() {} + + F16(int i) { mV = i<<16; } + F16(float f) { mV = _f16_round(f*65536.0F); } + F16(double f) { mV = _f16_round((float)f*65536.0F); } + + int32_t& raw() { return mV; } + const int32_t& raw() const { return mV; } + + float f() const { return mV/65536.0F; } + + //operator float() const { return mV/65536.0F; } + //operator int() const { return mV>>16; } + + F16 operator=(float f) + { + mV = _f16_round(f*65536.0F); + return *this; + } + + F16 operator=(int i) + { + mV = i<<16; + return *this; + } + + F16 operator=(const F16& other) + { + mV = other.mV; + return mV; + } + + F16 operator+(const F16& other) const + { + F16 retVal; + retVal.mV = mV + other.mV; + return retVal; + } + + F16& operator+=(const F16& other) + { + mV += other.mV; + return *this; + } + + F16 operator-(const F16& other) const + { + F16 retVal; + retVal.mV = mV - other.mV; + return retVal; + } + + F16& operator-=(const F16& other) + { + mV -= other.mV; + return *this; + } + + F16 operator*(const F16& other) const + { + F16 retVal; + int64_t p = (int64_t)mV * (int64_t)other.mV; + retVal.mV = p>>16; + return retVal; + } + + F16& operator*=(const F16& other) + { + int64_t p = (int64_t)mV * (int64_t)other.mV; + mV = p>>16; + return *this; + } + + F16 operator*(float f) const + { + F16 retVal; + retVal.mV = mV * f; + return retVal; + } + + F16& operator*=(float f) + { + mV *= f; + return *this; + } + + F16 operator/(const F16& other) const + { + F16 retVal; + int64_t pV = (int64_t)mV << 16; + retVal.mV = pV / other.mV; + return retVal; + } + + F16& operator/=(const F16& other) + { + int64_t pV = (int64_t)mV << 16; + mV = pV / other.mV; + return *this; + } + + F16 operator/(float f) const + { + F16 retVal; + retVal.mV = mV / f; + return retVal; + } + + F16& operator/=(float f) + { + mV /= f; + return *this; + } + + bool operator>(const F16& other) const + { + return mV>other.mV; + } + + bool operator<(const F16& other) const + { + return mV(float f) const + { + return (mV/65536.0F) > f; + } + + bool operator<(float f) const + { + return (mV/65536.0F) < f; + } + + bool operator==(float f) const + { + return (mV/65536.0F) == f; + } + +}; + + + +inline std::ostream& operator<<(std::ostream& os, const F16& v) +{ + os << v.f(); + return os; +} + +#endif + diff --git a/CommonLibs/F16Test.cpp b/CommonLibs/F16Test.cpp new file mode 100644 index 00000000..7f3c84d3 --- /dev/null +++ b/CommonLibs/F16Test.cpp @@ -0,0 +1,55 @@ +/* +* Copyright 2009 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 . + +*/ + +#include "F16.h" + + +#include + +using namespace std; + +int main(int argc, char **argv) +{ + + F16 a = 2.5; + F16 b = 1.5; + F16 c = 2.5 * 1.5; + F16 d = c + a; + F16 e = 10; + cout << a << ' ' << b << ' ' << c << ' ' << d << ' ' << e << endl; + + a *= 3; + b *= 0.3; + c *= e; + cout << a << ' ' << b << ' ' << c << ' ' << d << endl; + + a /= 3; + b /= 0.3; + c = d * 0.05; + cout << a << ' ' << b << ' ' << c << ' ' << d << endl; + + F16 f = a/d; + cout << f << ' ' << f+0.5 << endl; +} diff --git a/CommonLibs/Interthread.h b/CommonLibs/Interthread.h new file mode 100644 index 00000000..023ac148 --- /dev/null +++ b/CommonLibs/Interthread.h @@ -0,0 +1,546 @@ +/* +* Copyright 2008, 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 . + +*/ + + +#ifndef INTERTHREAD_H +#define INTERTHREAD_H + +#include "Timeval.h" +#include "Threads.h" +#include "LinkedLists.h" +#include +#include +#include + + + + + +/**@defgroup Templates for interthread mechanisms. */ +//@{ + + +/** Pointer FIFO for interthread operations. */ +template class InterthreadQueue { + + protected: + + PointerFIFO 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(); + } + + + ~InterthreadQueue() + { clear(); } + + + size_t size() const + { + ScopedLock lock(mLock); + return mQ.size(); + } + + /** + 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; + } + + /** + 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) + { + ScopedLock lock(mLock); + mQ.put(val); + mWriteSignal.signal(); + } + + +}; + + + +/** Pointer FIFO for interthread operations. */ +template class InterthreadQueueWithWait { + + protected: + + PointerFIFO mQ; + mutable Mutex mLock; + mutable Signal mWriteSignal; + mutable Signal mReadSignal; + + virtual void freeElement(T* element) const { delete element; }; + + public: + + /** Delete contents. */ + void clear() + { + ScopedLock lock(mLock); + while (mQ.size()>0) freeElement((T*)mQ.get()); + mReadSignal.signal(); + } + + + + virtual ~InterthreadQueueWithWait() + { clear(); } + + + size_t size() const + { + ScopedLock lock(mLock); + return mQ.size(); + } + + /** + 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(); + } + mReadSignal.signal(); + return retVal; + } + + /** + 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(); + if (retVal!=NULL) mReadSignal.signal(); + return retVal; + } + + /** + Non-blocking read. + @return Pointer to object or NULL if FIFO is empty. + */ + T* readNoBlock() + { + ScopedLock lock(mLock); + T* retVal = (T*)mQ.get(); + if (retVal!=NULL) mReadSignal.signal(); + return retVal; + } + + /** Non-blocking write. */ + void write(T* val) + { + ScopedLock lock(mLock); + mQ.put(val); + mWriteSignal.signal(); + } + + /** Wait until the queue falls below a low water mark. */ + void wait(size_t sz=0) + { + ScopedLock lock(mLock); + while (mQ.size()>sz) mReadSignal.wait(mLock); + } + +}; + + + + + +/** Thread-safe map of pointers to class D, keyed by class K. */ +template class InterthreadMap { + +protected: + + typedef std::map Map; + Map mMap; + mutable Mutex mLock; + Signal mWriteSignal; + +public: + + void clear() + { + // Delete everything in the map. + ScopedLock lock(mLock); + typename Map::iterator iter = mMap.begin(); + while (iter != mMap.end()) { + delete iter->second; + ++iter; + } + mMap.clear(); + } + + ~InterthreadMap() { clear(); } + + /** + Non-blocking write. + @param key The index to write to. + @param wData Pointer to data, not to be deleted until removed from the map. + */ + void write(const K &key, D * wData) + { + ScopedLock lock(mLock); + typename Map::iterator iter = mMap.find(key); + if (iter!=mMap.end()) { + delete iter->second; + iter->second = wData; + } else { + mMap[key] = wData; + } + mWriteSignal.broadcast(); + } + + /** + Non-blocking read with element removal. + @param key Key to read from. + @return Pointer at key or NULL if key not found, to be deleted by caller. + */ + D* getNoBlock(const K& key) + { + ScopedLock lock(mLock); + typename Map::iterator iter = mMap.find(key); + if (iter==mMap.end()) return NULL; + D* retVal = iter->second; + mMap.erase(iter); + return retVal; + } + + /** + Blocking read with a timeout and element removal. + @param key The key to read from. + @param timeout The blocking timeout in ms. + @return Pointer at key or NULL on timeout, to be deleted by caller. + */ + D* get(const K &key, unsigned timeout) + { + if (timeout==0) return getNoBlock(key); + Timeval waitTime(timeout); + ScopedLock lock(mLock); + typename Map::iterator iter = mMap.find(key); + while ((iter==mMap.end()) && (!waitTime.passed())) { + mWriteSignal.wait(mLock,waitTime.remaining()); + iter = mMap.find(key); + } + if (iter==mMap.end()) return NULL; + D* retVal = iter->second; + mMap.erase(iter); + return retVal; + } + + /** + Blocking read with and element removal. + @param key The key to read from. + @return Pointer at key, to be deleted by caller. + */ + D* get(const K &key) + { + ScopedLock lock(mLock); + typename Map::iterator iter = mMap.find(key); + while (iter==mMap.end()) { + mWriteSignal.wait(mLock); + iter = mMap.find(key); + } + D* retVal = iter->second; + mMap.erase(iter); + return retVal; + } + + + /** + Remove an entry and delete it. + @param key The key of the entry to delete. + @return True if it was actually found and deleted. + */ + bool remove(const K &key ) + { + D* val = getNoBlock(key); + if (!val) return false; + delete val; + return true; + } + + + /** + Non-blocking read. + @param key Key to read from. + @return Pointer at key or NULL if key not found. + */ + D* readNoBlock(const K& key) const + { + D* retVal=NULL; + ScopedLock lock(mLock); + typename Map::const_iterator iter = mMap.find(key); + if (iter!=mMap.end()) retVal = iter->second; + return retVal; + } + + /** + Blocking read with a timeout. + @param key The key to read from. + @param timeout The blocking timeout in ms. + @return Pointer at key or NULL on timeout. + */ + D* read(const K &key, unsigned timeout) const + { + if (timeout==0) return readNoBlock(key); + ScopedLock lock(mLock); + Timeval waitTime(timeout); + typename Map::const_iterator iter = mMap.find(key); + while ((iter==mMap.end()) && (!waitTime.passed())) { + mWriteSignal.wait(mLock,waitTime.remaining()); + iter = mMap.find(key); + } + if (iter==mMap.end()) return NULL; + D* retVal = iter->second; + return retVal; + } + + /** + Blocking read. + @param key The key to read from. + @return Pointer at key. + */ + D* read(const K &key) const + { + ScopedLock lock(mLock); + typename Map::const_iterator iter = mMap.find(key); + while (iter==mMap.end()) { + mWriteSignal.wait(mLock); + iter = mMap.find(key); + } + D* retVal = iter->second; + return retVal; + } + +}; + + + + + + + +/** This class is used to provide pointer-based comparison in priority_queues. */ +template class PointerCompare { + + public: + + /** Compare the objects pointed to, not the pointers themselves. */ + bool operator()(const T *v1, const T *v2) + { return (*v1)>(*v2); } + +}; + + + +/** + Priority queue for interthread operations. + Passes pointers to objects. +*/ +template , class Cmp = PointerCompare > class InterthreadPriorityQueue { + + protected: + + std::priority_queue mQ; + mutable Mutex mLock; + mutable Signal mWriteSignal; + + public: + + + /** Clear the FIFO. */ + void clear() + { + ScopedLock lock(mLock); + while (mQ.size()>0) { + T* ptr = mQ.top(); + mQ.pop(); + delete ptr; + } + } + + + ~InterthreadPriorityQueue() + { + clear(); + } + + size_t size() const + { + ScopedLock lock(mLock); + return mQ.size(); + } + + + /** Non-blocking read. */ + T* readNoBlock() + { + ScopedLock lock(mLock); + T* retVal = NULL; + if (mQ.size()!=0) { + retVal = mQ.top(); + mQ.pop(); + } + return retVal; + } + + /** Blocking read. */ + T* read() + { + ScopedLock lock(mLock); + T* retVal; + while (mQ.size()==0) mWriteSignal.wait(mLock); + retVal = mQ.top(); + mQ.pop(); + return retVal; + } + + /** Non-blocking write. */ + void write(T* val) + { + ScopedLock lock(mLock); + mQ.push(val); + mWriteSignal.signal(); + } + +}; + + + + + +class Semaphore { + + private: + + bool mFlag; + Signal mSignal; + mutable Mutex mLock; + + public: + + Semaphore() + :mFlag(false) + { } + + void post() + { + ScopedLock lock(mLock); + mFlag=true; + mSignal.signal(); + } + + void get() + { + ScopedLock lock(mLock); + while (!mFlag) mSignal.wait(mLock); + mFlag=false; + } + + bool semtry() + { + ScopedLock lock(mLock); + bool retVal = mFlag; + mFlag = false; + return retVal; + } + +}; + + + + + +//@} + + + + +#endif +// vim: ts=4 sw=4 diff --git a/CommonLibs/InterthreadTest.cpp b/CommonLibs/InterthreadTest.cpp new file mode 100644 index 00000000..17126891 --- /dev/null +++ b/CommonLibs/InterthreadTest.cpp @@ -0,0 +1,114 @@ +/* +* Copyright 2008 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 . + +*/ + + + +#include "Threads.h" +#include "Interthread.h" +#include + +using namespace std; + + +InterthreadQueue gQ; +InterthreadMap gMap; + +void* qWriter(void*) +{ + int *p; + for (int i=0; i<20; i++) { + p = new int; + *p = i; + COUT("queue write " << *p); + gQ.write(p); + if (random()%2) sleep(1); + } + p = new int; + *p = -1; + gQ.write(p); + return NULL; +} + +void* qReader(void*) +{ + bool done = false; + while (!done) { + int *p = gQ.read(); + COUT("queue read " << *p); + if (*p<0) done=true; + delete p; + } + return NULL; +} + + +void* mapWriter(void*) +{ + int *p; + for (int i=0; i<20; i++) { + p = new int; + *p = i; + COUT("map write " << *p); + gMap.write(i,p); + if (random()%2) sleep(1); + } + return NULL; +} + +void* mapReader(void*) +{ + for (int i=0; i<20; i++) { + int *p = gMap.read(i); + COUT("map read " << *p); + delete p; + } + return NULL; +} + + + + + + +int main(int argc, char *argv[]) +{ + Thread qReaderThread; + qReaderThread.start(qReader,NULL); + Thread mapReaderThread; + mapReaderThread.start(mapReader,NULL); + + Thread qWriterThread; + qWriterThread.start(qWriter,NULL); + Thread mapWriterThread; + mapWriterThread.start(mapWriter,NULL); + + qReaderThread.join(); + qWriterThread.join(); + mapReaderThread.join(); + mapWriterThread.join(); +} + + +// vim: ts=4 sw=4 diff --git a/CommonLibs/LinkedLists.cpp b/CommonLibs/LinkedLists.cpp new file mode 100644 index 00000000..ba0f0ccb --- /dev/null +++ b/CommonLibs/LinkedLists.cpp @@ -0,0 +1,77 @@ +/* +* Copyright 2008 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 . + +*/ + + + + +#include "LinkedLists.h" + + + + +void PointerFIFO::put(void* val) +{ + ListNode *node = allocate(); + node->data(val); + node->next(NULL); + if (mTail!=NULL) mTail->next(node); + mTail=node; + if (mHead==NULL) mHead=node; + mSize++; +} + +/** Take an item from the FIFO. */ +void* PointerFIFO::get() +{ + // empty list? + if (mHead==NULL) return NULL; + // normal case + ListNode* next = mHead->next(); + void* retVal = mHead->data(); + release(mHead); + mHead = next; + if (next==NULL) mTail=NULL; + mSize--; + return retVal; +} + + + +ListNode *PointerFIFO::allocate() +{ + if (mFreeList==NULL) return new ListNode; + ListNode* retVal = mFreeList; + mFreeList = mFreeList->next(); + return retVal; +} + +void PointerFIFO::release(ListNode* wNode) +{ + wNode->next(mFreeList); + mFreeList = wNode; +} + + + diff --git a/CommonLibs/LinkedLists.h b/CommonLibs/LinkedLists.h new file mode 100644 index 00000000..4ca27e6e --- /dev/null +++ b/CommonLibs/LinkedLists.h @@ -0,0 +1,100 @@ +/* +* 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. +* +* 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 . + +*/ + + + +#ifndef LINKEDLISTS_H +#define LINKEDLISTS_H + +#include + + + +/** This node class is used to build singly-linked lists. */ +class ListNode { + + private: + + ListNode* mNext; + void* mData; + + public: + + ListNode* next() { return mNext; } + void next(ListNode* wNext) { mNext=wNext; } + + void* data() { return mData; } + void data(void* wData) { mData=wData; } +}; + + + + +/** A fast FIFO for pointer-based storage. */ +class PointerFIFO { + + private: + + ListNode* mHead; ///< points to next item out + ListNode* mTail; ///< points to last item in + ListNode* mFreeList; ///< pool of previously-allocated nodes + unsigned mSize; ///< number of items in the FIFO + + public: + + PointerFIFO() + :mHead(NULL),mTail(NULL),mFreeList(NULL), + mSize(0) + {} + + unsigned size() const { return mSize; } + + /** Put an item into the FIFO. */ + void put(void* val); + + /** + Take an item from the FIFO. + Returns NULL for empty list. + */ + void* get(); + + + private: + + /** Allocate a new node to extend the FIFO. */ + ListNode *allocate(); + + /** Release a node to the free pool after removal from the FIFO. */ + void release(ListNode* wNode); + +}; + + + + + +#endif +// vim: ts=4 sw=4 diff --git a/CommonLibs/LogTest.cpp b/CommonLibs/LogTest.cpp new file mode 100644 index 00000000..e9f73b0e --- /dev/null +++ b/CommonLibs/LogTest.cpp @@ -0,0 +1,70 @@ +/* +* Copyright 2009 Free Software Foundation, Inc. +* Copyright 2010 Kestrel Signal Processing, 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 . + +*/ + +#include +#include + +#include "Logger.h" +#include "Configuration.h" + +ConfigurationTable gConfig; +//ConfigurationTable gConfig("example.config"); + +void printAlarms() +{ + std::ostream_iterator output( std::cout, "\n" ); + std::list alarms = gGetLoggerAlarms(); + std::cout << "#alarms = " << alarms.size() << std::endl; + std::copy( alarms.begin(), alarms.end(), output ); +} + +int main(int argc, char *argv[]) +{ + gLogInit("LogTest","NOTICE",LOG_LOCAL7); + + LOG(EMERG) << " testing the logger."; + LOG(ALERT) << " testing the logger."; + LOG(CRIT) << " testing the logger."; + LOG(ERR) << " testing the logger."; + LOG(WARNING) << " testing the logger."; + LOG(NOTICE) << " testing the logger."; + LOG(INFO) << " testing the logger."; + 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; + for (int i = 0 ; i < 20 ; ++i) { + LOG(ALERT) << i; + } + std::cout << "you should see ten lines with the numbers 10..19:" << std::endl; + printAlarms(); +} + + + diff --git a/CommonLibs/Logger.cpp b/CommonLibs/Logger.cpp new file mode 100644 index 00000000..57d2bff5 --- /dev/null +++ b/CommonLibs/Logger.cpp @@ -0,0 +1,197 @@ +/* +* Copyright 2009, 2010 Free Software Foundation, Inc. +* Copyright 2010 Kestrel Signal Processing, 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 . + +*/ + +#include +#include +#include +#include + +#include "Configuration.h" +#include "Logger.h" + + +using namespace std; + +// Reference to a global config table, used all over the system. +extern ConfigurationTable gConfig; + + +/**@ The global alarms table. */ +//@{ +Mutex alarmsLock; +list alarmsList; +void addAlarm(const string&); +//@} + + + + +/** Names of the logging levels. */ +const char *levelNames[] = { + "EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG" +}; +int numLevels = 8; + + +/** Given a string, return the corresponding level name. */ +int lookupLevel(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; +} + + +int getLoggingLevel(const char* filename) +{ + // Default level? + if (!filename) return lookupLevel(gConfig.getStr("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")); +} + + + +int gGetLoggingLevel(const char* filename) +{ + // This is called a lot and needs to be efficient. + + static Mutex sLogCacheLock; + static map sLogCache; + static unsigned sCacheCount; + static const unsigned sCacheRefreshCount = 1000; + + if (filename==NULL) return gGetLoggingLevel(""); + + HashString hs(filename); + uint64_t key = hs.hash(); + + sLogCacheLock.lock(); + // Time for a cache flush? + if (sCacheCount>sCacheRefreshCount) { + sLogCache.clear(); + sCacheCount=0; + } + // Is it cached already? + map::const_iterator where = sLogCache.find(key); + sCacheCount++; + if (where!=sLogCache.end()) { + int retVal = where->second; + sLogCacheLock.unlock(); + return retVal; + } + // Look it up in the config table and cache it. + // FIXME: Figure out why unlock and lock below fix the config table deadlock. + sLogCacheLock.unlock(); + int level = getLoggingLevel(filename); + sLogCacheLock.lock(); + sLogCache.insert(pair(key,level)); + sLogCacheLock.unlock(); + return level; +} + + + + + +// copies the alarm list and returns it. list supposed to be small. +list gGetLoggerAlarms() +{ + alarmsLock.lock(); + list ret; + // excuse the "complexity", but to use std::copy with a list you need + // an insert_iterator - copy technically overwrites, doesn't insert. + insert_iterator< list > ii(ret, ret.begin()); + copy(alarmsList.begin(), alarmsList.end(), ii); + alarmsLock.unlock(); + return ret; +} + +/** Add an alarm to the alarm list. */ +void addAlarm(const string& s) +{ + alarmsLock.lock(); + alarmsList.push_back(s); + unsigned maxAlarms = gConfig.getNum("Log.Alarms.Max"); + while (alarmsList.size() > maxAlarms) alarmsList.pop_front(); + alarmsLock.unlock(); +} + + +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()); + cerr << mStream.str() << endl; + } + // Current logging level was already checked by the macro. + // So just log. + syslog(mPriority, "%s", mStream.str().c_str()); +} + + +ostringstream& Log::get() +{ + assert(mPriority. + +*/ + + +#ifndef LOGGER_H +#define LOGGER_H + +#include +#include +#include +#include +#include +#include +#include +#include "Threads.h" + + +#define _LOG(level) \ + Log(LOG_##level).get() << pthread_self() \ + << " " __FILE__ ":" << __LINE__ << ":" << __FUNCTION__ << ": " +#define LOG(wLevel) \ + if (gGetLoggingLevel(__FILE__)>=LOG_##wLevel) _LOG(wLevel) +#define OBJLOG(wLevel) \ + if (gGetLoggingLevel(__FILE__)>=LOG_##wLevel) _LOG(wLevel) << "obj: " << this << ' ' + +#define LOG_ASSERT(x) { if (!(x)) LOG(EMERG) << "assertion " #x " failed"; } assert(x); + + +#define DEFAULT_MAX_ALARMS 10 + + +/** + A C++ stream-based thread-safe logger. + Derived from Dr. Dobb's Sept. 2007 issue. + Updated to use syslog. + This object is NOT the global logger; + every log record is an object of this class. +*/ +class Log { + + public: + + protected: + + std::ostringstream mStream; ///< This is where we buffer up the log entry. + int mPriority; ///< Priority of current repot. + + public: + + Log(int wPriority) + :mPriority(wPriority) + { } + + // Most of the work is in the desctructor. + /** The destructor actually generates the log entry. */ + ~Log(); + + std::ostringstream& get(); +}; + + + +std::list gGetLoggerAlarms(); ///< Get a copy of the recent alarm list. + + +/**@ Global control and initialization of the logging system. */ +//@{ +/** Initialize the global logging system. */ +void gLogInit(const char* name, const char* level=NULL, int facility=LOG_USER); +/** Get the logging level associated with a given file. */ +int gGetLoggingLevel(const char *filename=NULL); +//@} + + +#endif + +// vim: ts=4 sw=4 diff --git a/CommonLibs/Makefile.am b/CommonLibs/Makefile.am new file mode 100644 index 00000000..52c965cb --- /dev/null +++ b/CommonLibs/Makefile.am @@ -0,0 +1,93 @@ +# +# Copyright 2008, 2009 Free Software Foundation, Inc. +# +# This software is distributed under the terms of the GNU Public License. +# See the COPYING 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 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +include $(top_srcdir)/Makefile.common + +AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) +AM_CXXFLAGS = -Wall -ldl -O3 -g -lpthread + +noinst_LTLIBRARIES = libcommon.la + +libcommon_la_SOURCES = \ + BitVector.cpp \ + LinkedLists.cpp \ + Sockets.cpp \ + Threads.cpp \ + Timeval.cpp \ + Logger.cpp \ + URLEncode.cpp \ + Configuration.cpp + +noinst_PROGRAMS = \ + BitVectorTest \ + InterthreadTest \ + SocketsTest \ + TimevalTest \ + RegexpTest \ + VectorTest \ + ConfigurationTest \ + LogTest \ + F16Test + +noinst_HEADERS = \ + BitVector.h \ + Interthread.h \ + LinkedLists.h \ + Sockets.h \ + Threads.h \ + Timeval.h \ + Regexp.h \ + Vector.h \ + URLEncode.h \ + Configuration.h \ + F16.h \ + Logger.h + +BitVectorTest_SOURCES = BitVectorTest.cpp +BitVectorTest_LDADD = libcommon.la + +InterthreadTest_SOURCES = InterthreadTest.cpp +InterthreadTest_LDADD = libcommon.la +InterthreadTest_LDFLAGS = -lpthread + +SocketsTest_SOURCES = SocketsTest.cpp +SocketsTest_LDADD = libcommon.la +SocketsTest_LDFLAGS = -lpthread + +TimevalTest_SOURCES = TimevalTest.cpp +TimevalTest_LDADD = libcommon.la + +VectorTest_SOURCES = VectorTest.cpp +VectorTest_LDADD = libcommon.la + +RegexpTest_SOURCES = RegexpTest.cpp +RegexpTest_LDADD = libcommon.la + +ConfigurationTest_SOURCES = ConfigurationTest.cpp +ConfigurationTest_LDADD = libcommon.la $(SQLITE_LA) + +LogTest_SOURCES = LogTest.cpp +LogTest_LDADD = libcommon.la $(SQLITE_LA) + +F16Test_SOURCES = F16Test.cpp + +MOSTLYCLEANFILES += testSource testDestination + + diff --git a/CommonLibs/Regexp.h b/CommonLibs/Regexp.h new file mode 100644 index 00000000..3ff1e974 --- /dev/null +++ b/CommonLibs/Regexp.h @@ -0,0 +1,64 @@ +/* +* Copyright 2008 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 . + +*/ + + +#ifndef REGEXPW_H +#define REGEXPW_H + +#include +#include +#include + + + +class Regexp { + + private: + + regex_t mRegex; + + + public: + + Regexp(const char* regexp, int flags=REG_EXTENDED) + { + int result = regcomp(&mRegex, regexp, flags); + if (result) { + char msg[256]; + regerror(result,&mRegex,msg,255); + std::cerr << "Regexp compilation of " << regexp << " failed: " << msg << std::endl; + abort(); + } + } + + ~Regexp() + { regfree(&mRegex); } + + bool match(const char *text, int flags=0) const + { return regexec(&mRegex, text, 0, NULL, flags)==0; } + +}; + + +#endif diff --git a/CommonLibs/RegexpTest.cpp b/CommonLibs/RegexpTest.cpp new file mode 100644 index 00000000..748be49a --- /dev/null +++ b/CommonLibs/RegexpTest.cpp @@ -0,0 +1,48 @@ +/* +* Copyright 2008 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 . + +*/ + + + +#include "Regexp.h" +#include + +using namespace std; + + +int main(int argc, char *argv[]) +{ + + Regexp email("^[[:graph:]]+@[[:graph:]]+ "); + Regexp simple("^dburgess@"); + + const char text1[] = "dburgess@jcis.net test message"; + const char text2[] = "no address text message"; + + cout << email.match(text1) << " " << text1 << endl; + cout << email.match(text2) << " " << text2 << endl; + + cout << simple.match(text1) << " " << text1 << endl; + cout << simple.match(text2) << " " << text2 << endl; +} diff --git a/CommonLibs/Sockets.cpp b/CommonLibs/Sockets.cpp new file mode 100644 index 00000000..f24b4957 --- /dev/null +++ b/CommonLibs/Sockets.cpp @@ -0,0 +1,302 @@ +/* +* Copyright 2008, 2010 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 . + +*/ + + + +#include +#include +#include +#include + +#include "Threads.h" +#include "Sockets.h" +#include +#include +#include + +#include +#include + + + + + + +bool resolveAddress(struct sockaddr_in *address, const char *hostAndPort) +{ + assert(address); + assert(hostAndPort); + char *copy = strdup(hostAndPort); + char *colon = strchr(copy,':'); + if (!colon) return false; + *colon = '\0'; + char *host = copy; + unsigned port = strtol(colon+1,NULL,10); + bool retVal = resolveAddress(address,host,port); + free(copy); + return retVal; +} + +bool resolveAddress(struct sockaddr_in *address, const char *host, unsigned short port) +{ + assert(address); + assert(host); + // FIXME -- Need to ignore leading/trailing spaces in hostname. + struct hostent *hp = gethostbyname(host); + if (hp==NULL) { + CERR("WARNING -- gethostbyname() failed for " << host << ", " << hstrerror(h_errno)); + return false; + } + address->sin_family = AF_INET; + bcopy(hp->h_addr, &(address->sin_addr), hp->h_length); + address->sin_port = htons(port); + return true; +} + + + +DatagramSocket::DatagramSocket() +{ + bzero(mDestination,sizeof(mDestination)); +} + + + + + +void DatagramSocket::nonblocking() +{ + fcntl(mSocketFD,F_SETFL,O_NONBLOCK); +} + +void DatagramSocket::blocking() +{ + fcntl(mSocketFD,F_SETFL,0); +} + +void DatagramSocket::close() +{ + ::close(mSocketFD); +} + + +DatagramSocket::~DatagramSocket() +{ + close(); +} + + + + + +int DatagramSocket::write( const char * message, size_t length ) +{ + assert(length<=MAX_UDP_LENGTH); + int retVal = sendto(mSocketFD, message, length, 0, + (struct sockaddr *)mDestination, addressSize()); + if (retVal == -1 ) perror("DatagramSocket::write() failed"); + return retVal; +} + +int DatagramSocket::writeBack( const char * message, size_t length ) +{ + assert(length<=MAX_UDP_LENGTH); + int retVal = sendto(mSocketFD, message, length, 0, + (struct sockaddr *)mSource, addressSize()); + if (retVal == -1 ) perror("DatagramSocket::write() failed"); + return retVal; +} + + + +int DatagramSocket::write( const char * message) +{ + size_t length=strlen(message)+1; + return write(message,length); +} + +int DatagramSocket::writeBack( const char * message) +{ + size_t length=strlen(message)+1; + return writeBack(message,length); +} + + + +int DatagramSocket::send(const struct sockaddr* dest, const char * message, size_t length ) +{ + assert(length<=MAX_UDP_LENGTH); + int retVal = sendto(mSocketFD, message, length, 0, dest, addressSize()); + if (retVal == -1 ) perror("DatagramSocket::send() failed"); + return retVal; +} + +int DatagramSocket::send(const struct sockaddr* dest, const char * message) +{ + size_t length=strlen(message)+1; + return send(dest,message,length); +} + + + + + +int DatagramSocket::read(char* buffer) +{ + socklen_t temp_len = sizeof(mSource); + int length = recvfrom(mSocketFD, (void*)buffer, MAX_UDP_LENGTH, 0, + (struct sockaddr*)&mSource,&temp_len); + if ((length==-1) && (errno!=EAGAIN)) { + perror("DatagramSocket::read() failed"); + throw SocketError(); + } + return length; +} + + +int DatagramSocket::read(char* buffer, unsigned timeout) +{ + fd_set fds; + FD_SET(mSocketFD,&fds); + struct timeval tv; + tv.tv_sec = timeout/1000; + tv.tv_usec = (timeout%1000)*1000; + int sel = select(mSocketFD+1,&fds,NULL,NULL,&tv); + if (sel<0) { + perror("DatagramSocket::read() select() failed"); + throw SocketError(); + } + if (sel==0) return -1; + return read(buffer); +} + + + + + + +UDPSocket::UDPSocket(unsigned short wSrcPort) + :DatagramSocket() +{ + open(wSrcPort); +} + + +UDPSocket::UDPSocket(unsigned short wSrcPort, + const char * wDestIP, unsigned short wDestPort ) + :DatagramSocket() +{ + open(wSrcPort); + destination(wDestPort, wDestIP); +} + + + +void UDPSocket::destination( unsigned short wDestPort, const char * wDestIP ) +{ + resolveAddress((sockaddr_in*)mDestination, wDestIP, wDestPort ); +} + + +void UDPSocket::open(unsigned short localPort) +{ + // create + mSocketFD = socket(AF_INET,SOCK_DGRAM,0); + if (mSocketFD<0) { + perror("socket() failed"); + throw SocketError(); + } + + // bind + struct sockaddr_in address; + size_t length = sizeof(address); + bzero(&address,length); + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons(localPort); + if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) { + perror("bind() failed"); + throw SocketError(); + } +} + + + +unsigned short UDPSocket::port() const +{ + struct sockaddr_in name; + socklen_t nameSize = sizeof(name); + int retVal = getsockname(mSocketFD, (struct sockaddr*)&name, &nameSize); + if (retVal==-1) throw SocketError(); + return ntohs(name.sin_port); +} + + + + + +UDDSocket::UDDSocket(const char* localPath, const char* remotePath) + :DatagramSocket() +{ + if (localPath!=NULL) open(localPath); + if (remotePath!=NULL) destination(remotePath); +} + + + +void UDDSocket::open(const char* localPath) +{ + // create + mSocketFD = socket(AF_UNIX,SOCK_DGRAM,0); + if (mSocketFD<0) { + perror("socket() failed"); + throw SocketError(); + } + + // bind + struct sockaddr_un address; + size_t length = sizeof(address); + bzero(&address,length); + address.sun_family = AF_UNIX; + strcpy(address.sun_path,localPath); + unlink(localPath); + if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) { + perror("bind() failed"); + throw SocketError(); + } +} + + + +void UDDSocket::destination(const char* remotePath) +{ + struct sockaddr_un* unAddr = (struct sockaddr_un*)mDestination; + strcpy(unAddr->sun_path,remotePath); +} + + + + +// vim:ts=4:sw=4 diff --git a/CommonLibs/Sockets.h b/CommonLibs/Sockets.h new file mode 100644 index 00000000..c79f79aa --- /dev/null +++ b/CommonLibs/Sockets.h @@ -0,0 +1,193 @@ +/* +* Copyright 2008, 2010 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 . + +*/ + + +#ifndef SOCKETS_H +#define SOCKETS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + + + +#define MAX_UDP_LENGTH 1500 + +/** A function to resolve IP host names. */ +bool resolveAddress(struct sockaddr_in *address, const char *host, unsigned short port); + +/** Resolve an address of the form ":". */ +bool resolveAddress(struct sockaddr_in *address, const char *hostAndPort); + +/** An exception to throw when a critical socket operation fails. */ +class SocketError {}; +#define SOCKET_ERROR {throw SocketError(); } + +/** Abstract class for connectionless sockets. */ +class DatagramSocket { + +protected: + + int mSocketFD; ///< underlying file descriptor + char mDestination[256]; ///< address to which packets are sent + char mSource[256]; ///< return address of most recent received packet + +public: + + /** An almost-does-nothing constructor. */ + DatagramSocket(); + + virtual ~DatagramSocket(); + + /** Return the address structure size for this socket type. */ + virtual size_t addressSize() const = 0; + + /** + Send a binary packet. + @param buffer The data bytes to send to mDestination. + @param length Number of bytes to send, or strlen(buffer) if defaulted to -1. + @return number of bytes written, or -1 on error. + */ + int write( const char * buffer, size_t length); + + /** + Send a C-style string packet. + @param buffer The data bytes to send to mDestination. + @return number of bytes written, or -1 on error. + */ + int write( const char * buffer); + + /** + Send a binary packet. + @param buffer The data bytes to send to mSource. + @param length Number of bytes to send, or strlen(buffer) if defaulted to -1. + @return number of bytes written, or -1 on error. + */ + int writeBack(const char * buffer, size_t length); + + /** + Send a C-style string packet. + @param buffer The data bytes to send to mSource. + @return number of bytes written, or -1 on error. + */ + int writeBack(const char * buffer); + + + /** + Receive a packet. + @param buffer A char[MAX_UDP_LENGTH] procured by the caller. + @return The number of bytes received or -1 on non-blocking pass. + */ + int read(char* buffer); + + /** + Receive a packet with a timeout. + @param buffer A char[MAX_UDP_LENGTH] procured by the caller. + @param maximum wait time in milliseconds + @return The number of bytes received or -1 on timeout. + */ + int read(char* buffer, unsigned timeout); + + + /** Send a packet to a given destination, other than the default. */ + int send(const struct sockaddr *dest, const char * buffer, size_t length); + + /** Send a C-style string to a given destination, other than the default. */ + int send(const struct sockaddr *dest, const char * buffer); + + /** Make the socket non-blocking. */ + void nonblocking(); + + /** Make the socket blocking (the default). */ + void blocking(); + + /** Close the socket. */ + void close(); + +}; + + + +/** UDP/IP User Datagram Socket */ +class UDPSocket : public DatagramSocket { + +public: + + /** Open a USP socket with an OS-assigned port and no default destination. */ + UDPSocket( unsigned short localPort=0); + + /** Given a full specification, open the socket and set the dest address. */ + UDPSocket( unsigned short localPort, + const char * remoteIP, unsigned short remotePort); + + /** Set the destination port. */ + void destination( unsigned short wDestPort, const char * wDestIP ); + + /** Return the actual port number in use. */ + unsigned short port() const; + + /** Open and bind the UDP socket to a local port. */ + void open(unsigned short localPort=0); + + /** Give the return address of the most recently received packet. */ + const struct sockaddr_in* source() const { return (const struct sockaddr_in*)mSource; } + + size_t addressSize() const { return sizeof(struct sockaddr_in); } + +}; + + +/** Unix Domain Datagram Socket */ +class UDDSocket : public DatagramSocket { + +public: + + UDDSocket(const char* localPath=NULL, const char* remotePath=NULL); + + void destination(const char* remotePath); + + void open(const char* localPath); + + /** Give the return address of the most recently received packet. */ + const struct sockaddr_un* source() const { return (const struct sockaddr_un*)mSource; } + + size_t addressSize() const { return sizeof(struct sockaddr_un); } + +}; + + +#endif + + + +// vim:ts=4:sw=4 diff --git a/CommonLibs/SocketsTest.cpp b/CommonLibs/SocketsTest.cpp new file mode 100644 index 00000000..9a4997bc --- /dev/null +++ b/CommonLibs/SocketsTest.cpp @@ -0,0 +1,103 @@ +/* +* Copyright 2008 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 . + +*/ + + + + +#include "Sockets.h" +#include "Threads.h" +#include +#include + + +static const int gNumToSend = 10; + + +void *testReaderIP(void *) +{ + UDPSocket readSocket(5934, "localhost", 5061); + readSocket.nonblocking(); + int rc = 0; + while (rc0) { + COUT("read: " << buf); + rc++; + } else { + sleep(2); + } + } + return NULL; +} + + + +void *testReaderUnix(void *) +{ + UDDSocket readSocket("testDestination"); + readSocket.nonblocking(); + int rc = 0; + while (rc0) { + COUT("read: " << buf); + rc++; + } else { + sleep(2); + } + } + return NULL; +} + + +int main(int argc, char * argv[] ) +{ + + Thread readerThreadIP; + readerThreadIP.start(testReaderIP,NULL); + Thread readerThreadUnix; + readerThreadUnix.start(testReaderUnix,NULL); + + UDPSocket socket1(5061, "127.0.0.1",5934); + UDDSocket socket1U("testSource","testDestination"); + + COUT("socket1: " << socket1.port()); + + // give the readers time to open + sleep(1); + + for (int i=0; i. + +*/ + + + + + +#include "Threads.h" +#include "Timeval.h" + + +using namespace std; + + + + +Mutex gStreamLock; ///< Global lock to control access to cout and cerr. + +void lockCout() +{ + gStreamLock.lock(); + Timeval entryTime; + cout << entryTime << " " << pthread_self() << ": "; +} + + +void unlockCout() +{ + cout << dec << endl << flush; + gStreamLock.unlock(); +} + + +void lockCerr() +{ + gStreamLock.lock(); + Timeval entryTime; + cerr << entryTime << " " << pthread_self() << ": "; +} + +void unlockCerr() +{ + cerr << dec << endl << flush; + gStreamLock.unlock(); +} + + + + + + + +Mutex::Mutex() +{ + bool res; + res = pthread_mutexattr_init(&mAttribs); + assert(!res); + res = pthread_mutexattr_settype(&mAttribs,PTHREAD_MUTEX_RECURSIVE); + assert(!res); + res = pthread_mutex_init(&mMutex,&mAttribs); + assert(!res); +} + + +Mutex::~Mutex() +{ + pthread_mutex_destroy(&mMutex); + bool res = pthread_mutexattr_destroy(&mAttribs); + assert(!res); +} + + + + +/** Block for the signal up to the cancellation timeout. */ +void Signal::wait(Mutex& wMutex, unsigned timeout) const +{ + Timeval then(timeout); + struct timespec waitTime = then.timespec(); + pthread_cond_timedwait(&mSignal,&wMutex.mMutex,&waitTime); +} + + +void Thread::start(void *(*task)(void*), void *arg) +{ + assert(mThread==((pthread_t)0)); + bool res; + res = pthread_attr_init(&mAttrib); + assert(!res); + res = pthread_attr_setstacksize(&mAttrib, mStackSize); + assert(!res); + res = pthread_create(&mThread, &mAttrib, task, arg); + assert(!res); +} + + + +// vim: ts=4 sw=4 diff --git a/CommonLibs/Threads.h b/CommonLibs/Threads.h new file mode 100644 index 00000000..c1cfc750 --- /dev/null +++ b/CommonLibs/Threads.h @@ -0,0 +1,176 @@ +/* +* Copyright 2008, 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 . + +*/ + + +#ifndef THREADS_H +#define THREADS_H + +#include +#include +#include + +class Mutex; + + +/**@name Multithreaded access for standard streams. */ +//@{ + +/**@name Functions for gStreamLock. */ +//@{ +extern Mutex gStreamLock; ///< global lock for cout and cerr +void lockCerr(); ///< call prior to writing cerr +void unlockCerr(); ///< call after writing cerr +void lockCout(); ///< call prior to writing cout +void unlockCout(); ///< call after writing cout +//@} + +/**@name Macros for standard messages. */ +//@{ +#define COUT(text) { lockCout(); std::cout << text; unlockCout(); } +#define CERR(text) { lockCerr(); std::cerr << __FILE__ << ":" << __LINE__ << ": " << text; unlockCerr(); } +#ifdef NDEBUG +#define DCOUT(text) {} +#define OBJDCOUT(text) {} +#else +#define DCOUT(text) { COUT(__FILE__ << ":" << __LINE__ << " " << text); } +#define OBJDCOUT(text) { DCOUT(this << " " << text); } +#endif +//@} +//@} + + + +/**@defgroup C++ wrappers for pthread mechanisms. */ +//@{ + +/** A class for recursive mutexes based on pthread_mutex. */ +class Mutex { + + private: + + pthread_mutex_t mMutex; + pthread_mutexattr_t mAttribs; + + public: + + Mutex(); + + ~Mutex(); + + void lock() { pthread_mutex_lock(&mMutex); } + + void unlock() { pthread_mutex_unlock(&mMutex); } + + friend class Signal; + +}; + + +class ScopedLock { + + private: + Mutex& mMutex; + + public: + ScopedLock(Mutex& wMutex) :mMutex(wMutex) { mMutex.lock(); } + ~ScopedLock() { mMutex.unlock(); } + +}; + + + + +/** A C++ interthread signal based on pthread condition variables. */ +class Signal { + + private: + + mutable pthread_cond_t mSignal; + + public: + + Signal() { int s = pthread_cond_init(&mSignal,NULL); assert(!s); } + + ~Signal() { pthread_cond_destroy(&mSignal); } + + /** + Block for the signal up to the cancellation timeout. + Under Linux, spurious returns are possible. + */ + void wait(Mutex& wMutex, unsigned timeout) const; + + /** + Block for the signal. + Under Linux, spurious returns are possible. + */ + void wait(Mutex& wMutex) const + { pthread_cond_wait(&mSignal,&wMutex.mMutex); } + + void signal() { pthread_cond_signal(&mSignal); } + + void broadcast() { pthread_cond_broadcast(&mSignal); } + +}; + + + +#define START_THREAD(thread,function,argument) \ + thread.start((void *(*)(void*))function, (void*)argument); + +/** A C++ wrapper for pthread threads. */ +class Thread { + + private: + + pthread_t mThread; + pthread_attr_t mAttrib; + // FIXME -- Can this be reduced now? + size_t mStackSize; + + + public: + + /** Create a thread in a non-running state. */ + Thread(size_t wStackSize = (65536*4)):mThread((pthread_t)0) { mStackSize=wStackSize;} + + /** + Destroy the Thread. + It should be stopped and joined. + */ + ~Thread() { pthread_attr_destroy(&mAttrib); } + + + /** Start the thread on a task. */ + 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); } + +}; + + + + +#endif +// vim: ts=4 sw=4 diff --git a/CommonLibs/Timeval.cpp b/CommonLibs/Timeval.cpp new file mode 100644 index 00000000..47eebc4a --- /dev/null +++ b/CommonLibs/Timeval.cpp @@ -0,0 +1,98 @@ +/* +* Copyright 2008 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 . + +*/ + + + +#include "Timeval.h" + +using namespace std; + +void Timeval::future(unsigned offset) +{ + now(); + unsigned sec = offset/1000; + unsigned msec = offset%1000; + mTimeval.tv_usec += msec*1000; + mTimeval.tv_sec += sec; + if (mTimeval.tv_usec>1000000) { + mTimeval.tv_usec -= 1000000; + mTimeval.tv_sec += 1; + } +} + + +struct timespec Timeval::timespec() const +{ + struct timespec retVal; + retVal.tv_sec = mTimeval.tv_sec; + retVal.tv_nsec = 1000 * (long)mTimeval.tv_usec; + return retVal; +} + + +bool Timeval::passed() const +{ + Timeval nowTime; + if (nowTime.mTimeval.tv_sec < mTimeval.tv_sec) return false; + if (nowTime.mTimeval.tv_sec > mTimeval.tv_sec) return true; + if (nowTime.mTimeval.tv_usec > mTimeval.tv_usec) return true; + return false; +} + +double Timeval::seconds() const +{ + return ((double)mTimeval.tv_sec) + 1e-6*((double)mTimeval.tv_usec); +} + + + +long Timeval::delta(const Timeval& other) const +{ + // 2^31 milliseconds is just over 4 years. + long deltaS = other.sec() - sec(); + long deltaUs = other.usec() - usec(); + return 1000*deltaS + deltaUs/1000; +} + + + + +ostream& operator<<(ostream& os, const Timeval& tv) +{ + os.setf( ios::fixed, ios::floatfield ); + os << tv.seconds(); + return os; +} + + +ostream& operator<<(ostream& os, const struct timespec& ts) +{ + os << ts.tv_sec << "," << ts.tv_nsec; + return os; +} + + + +// vim: ts=4 sw=4 diff --git a/CommonLibs/Timeval.h b/CommonLibs/Timeval.h new file mode 100644 index 00000000..c2a2617e --- /dev/null +++ b/CommonLibs/Timeval.h @@ -0,0 +1,104 @@ +/* +* Copyright 2008 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 . + +*/ + + +#ifndef TIMEVAL_H +#define TIMEVAL_H + +#include +#include "sys/time.h" +#include + + + +/** A wrapper on usleep to sleep for milliseconds. */ +inline void msleep(long v) { usleep(v*1000); } + + +/** A C++ wrapper for struct timeval. */ +class Timeval { + + private: + + struct timeval mTimeval; + + public: + + /** Set the value to gettimeofday. */ + void now() { gettimeofday(&mTimeval,NULL); } + + /** Set the value to gettimeofday plus an offset. */ + void future(unsigned ms); + + //@{ + Timeval(unsigned sec, unsigned usec) + { + mTimeval.tv_sec = sec; + mTimeval.tv_usec = usec; + } + + Timeval(const struct timeval& wTimeval) + :mTimeval(wTimeval) + {} + + /** + Create a Timeval offset into the future. + @param offset milliseconds + */ + Timeval(unsigned offset=0) { future(offset); } + //@} + + /** Convert to a struct timespec. */ + struct timespec timespec() const; + + /** Return total seconds. */ + double seconds() const; + + uint32_t sec() const { return mTimeval.tv_sec; } + uint32_t usec() const { return mTimeval.tv_usec; } + + /** Return differnce from other (other-self), in ms. */ + long delta(const Timeval& other) const; + + /** Elapsed time in ms. */ + long elapsed() const { return delta(Timeval()); } + + /** Remaining time in ms. */ + long remaining() const { return -elapsed(); } + + /** Return true if the time has passed, as per gettimeofday. */ + bool passed() const; + + /** Add a given number of minutes to the time. */ + void addMinutes(unsigned minutes) { mTimeval.tv_sec += minutes*60; } + +}; + +std::ostream& operator<<(std::ostream& os, const Timeval&); + +std::ostream& operator<<(std::ostream& os, const struct timespec&); + + +#endif +// vim: ts=4 sw=4 diff --git a/CommonLibs/TimevalTest.cpp b/CommonLibs/TimevalTest.cpp new file mode 100644 index 00000000..b4746f20 --- /dev/null +++ b/CommonLibs/TimevalTest.cpp @@ -0,0 +1,45 @@ +/* +* Copyright 2008 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 . + +*/ + + + + +#include "Timeval.h" +#include + +using namespace std; + +int main(int argc, char *argv[]) +{ + + Timeval then(10000); + cout << then.elapsed() << endl; + + while (!then.passed()) { + cout << "now: " << Timeval() << " then: " << then << " remaining: " << then.remaining() << endl; + usleep(500000); + } + cout << "now: " << Timeval() << " then: " << then << " remaining: " << then.remaining() << endl; +} diff --git a/CommonLibs/URLEncode.cpp b/CommonLibs/URLEncode.cpp new file mode 100644 index 00000000..870db332 --- /dev/null +++ b/CommonLibs/URLEncode.cpp @@ -0,0 +1,51 @@ +/* +* 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 . + +*/ + +#include +#include +#include +#include + +using namespace std; + +//based on javascript encodeURIComponent() +string URLEncode(const string &c) +{ + static const char *digits = "01234567890ABCDEF"; + string retVal=""; + for (int i=0; i>4) & 0x0f]; + retVal += digits[ch & 0x0f]; + } + } + return retVal; +} + diff --git a/CommonLibs/URLEncode.h b/CommonLibs/URLEncode.h new file mode 100644 index 00000000..558ced9b --- /dev/null +++ b/CommonLibs/URLEncode.h @@ -0,0 +1,30 @@ +/* +* 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 . + +*/ + + + +# include + +std::string URLEncode(const std::string&); diff --git a/CommonLibs/Vector.h b/CommonLibs/Vector.h new file mode 100644 index 00000000..62cb6fb8 --- /dev/null +++ b/CommonLibs/Vector.h @@ -0,0 +1,268 @@ +/**@file Simplified Vector template with aliases. */ +/* +* Copyright 2008 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 . + +*/ + + + + +#ifndef VECTOR_H +#define VECTOR_H + +#include +#include +#include + + +/** + A simplified Vector template with aliases. + Unlike std::vector, this class does not support dynamic resizing. + Unlike std::vector, this class does support "aliases" and subvectors. +*/ +template class Vector { + + // TODO -- Replace memcpy calls with for-loops. + + public: + + /**@name Iterator types. */ + //@{ + typedef T* iterator; + typedef const T* const_iterator; + //@} + + protected: + + T* mData; ///< allocated data block, if any + T* mStart; ///< start of useful data + T* mEnd; ///< end of useful data + 1 + + public: + + /** Return the size of the Vector. */ + size_t size() const + { + assert(mStart>=mData); + assert(mEnd>=mStart); + return mEnd - mStart; + } + + /** Return size in bytes. */ + size_t bytes() const { return size()*sizeof(T); } + + /** Change the size of the Vector, discarding content. */ + void resize(size_t newSize) + { + if (mData!=NULL) delete[] mData; + if (newSize==0) mData=NULL; + else mData = new T[newSize]; + mStart = mData; + mEnd = mStart + newSize; + } + + /** Release memory and clear pointers. */ + void clear() { resize(0); } + + + /** Copy data from another vector. */ + void clone(const Vector& other) + { + resize(other.size()); + memcpy(mData,other.mStart,other.bytes()); + } + + + + + //@{ + + /** Build an empty Vector of a given size. */ + Vector(size_t wSize=0):mData(NULL) { resize(wSize); } + + /** Build a Vector by shifting the data block. */ + Vector(Vector& other) + :mData(other.mData),mStart(other.mStart),mEnd(other.mEnd) + { other.mData=NULL; } + + /** Build a Vector by copying another. */ + Vector(const Vector& other):mData(NULL) { clone(other); } + + /** Build a Vector with explicit values. */ + Vector(T* wData, T* wStart, T* wEnd) + :mData(wData),mStart(wStart),mEnd(wEnd) + { } + + /** Build a vector from an existing block, NOT to be deleted upon destruction. */ + Vector(T* wStart, size_t span) + :mData(NULL),mStart(wStart),mEnd(wStart+span) + { } + + /** Build a Vector by concatenation. */ + Vector(const Vector& other1, const Vector& other2) + :mData(NULL) + { + resize(other1.size()+other2.size()); + memcpy(mStart, other1.mStart, other1.bytes()); + memcpy(mStart+other1.size(), other2.mStart, other2.bytes()); + } + + //@} + + /** Destroy a Vector, deleting held memory. */ + ~Vector() { clear(); } + + + + + //@{ + + /** Assign from another Vector, shifting ownership. */ + void operator=(Vector& other) + { + clear(); + mData=other.mData; + mStart=other.mStart; + mEnd=other.mEnd; + other.mData=NULL; + } + + /** Assign from another Vector, copying. */ + void operator=(const Vector& other) { clone(other); } + + //@} + + + //@{ + + /** Return an alias to a segment of this Vector. */ + Vector segment(size_t start, size_t span) + { + T* wStart = mStart + start; + T* wEnd = wStart + span; + assert(wEnd<=mEnd); + return Vector(NULL,wStart,wEnd); + } + + /** Return an alias to a segment of this Vector. */ + const Vector segment(size_t start, size_t span) const + { + T* wStart = mStart + start; + T* wEnd = wStart + span; + assert(wEnd<=mEnd); + return Vector(NULL,wStart,wEnd); + } + + Vector head(size_t span) { return segment(0,span); } + const Vector head(size_t span) const { return segment(0,span); } + Vector tail(size_t start) { return segment(start,size()-start); } + const Vector tail(size_t start) const { return segment(start,size()-start); } + + /** + Copy part of this Vector to a segment of another Vector. + @param other The other vector. + @param start The start point in the other vector. + @param span The number of elements to copy. + */ + void copyToSegment(Vector& other, size_t start, size_t span) const + { + T* base = other.mStart + start; + assert(base+span<=other.mEnd); + assert(mStart+span<=mEnd); + memcpy(base,mStart,span*sizeof(T)); + } + + /** Copy all of this Vector to a segment of another Vector. */ + void copyToSegment(Vector& other, size_t start=0) const { copyToSegment(other,start,size()); } + + void copyTo(Vector& other) const { copyToSegment(other,0,size()); } + + /** + Copy a segment of this vector into another. + @param other The other vector (to copt into starting at 0.) + @param start The start point in this vector. + @param span The number of elements to copy. + */ + void segmentCopyTo(Vector& other, size_t start, size_t span) const + { + const T* base = mStart + start; + assert(base+span<=mEnd); + assert(other.mStart+span<=other.mEnd); + memcpy(other.mStart,base,span*sizeof(T)); + } + + void fill(const T& val) + { + T* dp=mStart; + while (dp +std::ostream& operator<<(std::ostream& os, const Vector& v) +{ + for (unsigned i=0; i. + +*/ + + + +#include "Vector.h" +#include + +using namespace std; + +typedef Vector TestVector; + +int main(int argc, char *argv[]) +{ + TestVector test1(5); + for (int i=0; i<5; i++) test1[i]=i; + TestVector test2(5); + for (int i=0; i<5; i++) test2[i]=10+i; + + cout << test1 << endl; + cout << test2 << endl; + + { + TestVector testC(test1,test2); + cout << testC << endl; + cout << testC.head(3) << endl; + cout << testC.tail(3) << endl; + testC.fill(8); + cout << testC << endl; + test1.copyToSegment(testC,3); + cout << testC << endl; + + TestVector testD(testC.segment(4,3)); + cout << testD << endl; + testD.fill(9); + cout << testC << endl; + cout << testD << endl; + } + + return 0; +}