aboutsummaryrefslogtreecommitdiffstats
path: root/CommonLibs
diff options
context:
space:
mode:
authordburgess <dburgess@19bc5d8c-e614-43d4-8b26-e1612bc8e597>2011-10-07 02:40:51 +0000
committerdburgess <dburgess@19bc5d8c-e614-43d4-8b26-e1612bc8e597>2011-10-07 02:40:51 +0000
commit82c46ff7aeb793d70f11fcaf22b2813b7dc0f78b (patch)
tree25cf269a01eaf3ae2ca0cece6f1a22ca61308e6d /CommonLibs
parentcd8381743f1fe458f10e96020ad4626f4ae3d7bd (diff)
Putting the actual OpenBTS P2.8 source code into the public SVN branch.
git-svn-id: http://wush.net/svn/range/software/public/openbts/trunk@2242 19bc5d8c-e614-43d4-8b26-e1612bc8e597
Diffstat (limited to 'CommonLibs')
-rw-r--r--CommonLibs/BitVector.cpp603
-rw-r--r--CommonLibs/BitVector.h441
-rw-r--r--CommonLibs/BitVectorTest.cpp88
-rw-r--r--CommonLibs/Configuration.cpp339
-rw-r--r--CommonLibs/Configuration.h275
-rw-r--r--CommonLibs/ConfigurationTest.cpp69
-rw-r--r--CommonLibs/F16.h210
-rw-r--r--CommonLibs/F16Test.cpp55
-rw-r--r--CommonLibs/Interthread.h546
-rw-r--r--CommonLibs/InterthreadTest.cpp114
-rw-r--r--CommonLibs/LinkedLists.cpp77
-rw-r--r--CommonLibs/LinkedLists.h100
-rw-r--r--CommonLibs/LogTest.cpp70
-rw-r--r--CommonLibs/Logger.cpp197
-rw-r--r--CommonLibs/Logger.h99
-rw-r--r--CommonLibs/Makefile.am93
-rw-r--r--CommonLibs/Regexp.h64
-rw-r--r--CommonLibs/RegexpTest.cpp48
-rw-r--r--CommonLibs/Sockets.cpp302
-rw-r--r--CommonLibs/Sockets.h193
-rw-r--r--CommonLibs/SocketsTest.cpp103
-rw-r--r--CommonLibs/Threads.cpp120
-rw-r--r--CommonLibs/Threads.h176
-rw-r--r--CommonLibs/Timeval.cpp98
-rw-r--r--CommonLibs/Timeval.h104
-rw-r--r--CommonLibs/TimevalTest.cpp45
-rw-r--r--CommonLibs/URLEncode.cpp51
-rw-r--r--CommonLibs/URLEncode.h30
-rw-r--r--CommonLibs/Vector.h268
-rw-r--r--CommonLibs/VectorTest.cpp63
30 files changed, 5041 insertions, 0 deletions
diff --git a/CommonLibs/BitVector.cpp b/CommonLibs/BitVector.cpp
new file mode 100644
index 0000000..1d13dec
--- /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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+
+
+#include "BitVector.h"
+#include <iostream>
+#include <stdio.h>
+
+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<order; i++) sum ^= prod>>i;
+ return sum & 0x01;
+}
+
+
+
+
+
+
+BitVector::BitVector(const char *valString)
+ :Vector<char>(strlen(valString))
+{
+ uint32_t accum = 0;
+ for (size_t i=0; i<size(); i++) {
+ accum <<= 1;
+ if (valString[i]=='1') accum |= 0x01;
+ mStart[i] = accum;
+ }
+}
+
+
+
+
+
+uint64_t BitVector::peekField(size_t readIndex, unsigned length) const
+{
+ uint64_t accum = 0;
+ char *dp = mStart + readIndex;
+ assert(dp+length <= mEnd);
+ for (unsigned i=0; i<length; i++) {
+ accum = (accum<<1) | ((*dp++) & 0x01);
+ }
+ return accum;
+}
+
+
+
+
+uint64_t BitVector::peekFieldReversed(size_t readIndex, unsigned length) const
+{
+ uint64_t accum = 0;
+ char *dp = mStart + readIndex + length - 1;
+ assert(dp<mEnd);
+ for (int i=(length-1); 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<size(); i++) {
+ mStart[i] = ~mStart[i];
+ }
+}
+
+
+
+
+void BitVector::reverse8()
+{
+ assert(size()>=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 (dp<mEnd) gen.syndromeShift(*dp++);
+ return gen.state();
+}
+
+
+uint64_t BitVector::parity(Generator& gen) const
+{
+ gen.clear();
+ const char *dp = mStart;
+ while (dp<mEnd) gen.encoderShift(*dp++);
+ return gen.state();
+}
+
+
+void BitVector::encode(const ViterbiR2O4& coder, BitVector& target)
+{
+ size_t sz = size();
+ assert(sz*coder.iRate() == target.size());
+
+ // Build a "history" array where each element contains the full history.
+ uint32_t history[sz];
+ uint32_t accum = 0;
+ for (size_t i=0; i<sz; i++) {
+ accum = (accum<<1) | bit(i);
+ history[i] = accum;
+ }
+
+ // Look up histories in the pre-generated state table.
+ char *op = target.begin();
+ for (size_t i=0; i<sz; i++) {
+ unsigned index = coder.cMask() & history[i];
+ for (unsigned g=0; g<coder.iRate(); g++) {
+ *op++ = coder.stateTable(g,index);
+ }
+ }
+}
+
+
+
+unsigned BitVector::sum() const
+{
+ unsigned sum = 0;
+ for (size_t i=0; i<size(); i++) sum += mStart[i] & 0x01;
+ return sum;
+}
+
+
+
+
+void BitVector::map(const unsigned *map, size_t mapSize, BitVector& dest) const
+{
+ for (unsigned i=0; i<mapSize; i++) {
+ dest.mStart[i] = mStart[map[i]];
+ }
+}
+
+
+
+
+void BitVector::unmap(const unsigned *map, size_t mapSize, BitVector& dest) const
+{
+ for (unsigned i=0; i<mapSize; i++) {
+ dest.mStart[map[i]] = mStart[i];
+ }
+}
+
+
+
+
+
+
+
+
+
+
+ostream& operator<<(ostream& os, const BitVector& hv)
+{
+ for (size_t i=0; i<hv.size(); i++) {
+ if (hv.bit(i)) os << '1';
+ else os << '0';
+ }
+ return os;
+}
+
+
+
+
+ViterbiR2O4::ViterbiR2O4()
+{
+ assert(mDeferral < 32);
+ mCoeffs[0] = 0x019;
+ mCoeffs[1] = 0x01b;
+ computeStateTables(0);
+ computeStateTables(1);
+ computeGeneratorTable();
+}
+
+
+
+
+void ViterbiR2O4::initializeStates()
+{
+ for (unsigned i=0; i<mIStates; i++) clear(mSurvivors[i]);
+ for (unsigned i=0; i<mNumCands; i++) clear(mCandidates[i]);
+}
+
+
+
+void ViterbiR2O4::computeStateTables(unsigned g)
+{
+ assert(g<mIRate);
+ for (unsigned state=0; state<mIStates; state++) {
+ // 0 input
+ uint32_t inputVal = state<<1;
+ mStateTable[g][inputVal] = applyPoly(inputVal, mCoeffs[g], mOrder+1);
+ // 1 input
+ inputVal |= 1;
+ mStateTable[g][inputVal] = applyPoly(inputVal, mCoeffs[g], mOrder+1);
+ }
+}
+
+void ViterbiR2O4::computeGeneratorTable()
+{
+ for (unsigned index=0; index<mIStates*2; index++) {
+ mGeneratorTable[index] = (mStateTable[0][index]<<1) | mStateTable[1][index];
+ }
+}
+
+
+
+
+
+
+void ViterbiR2O4::branchCandidates()
+{
+ // Branch to generate new input states.
+ const vCand *sp = mSurvivors;
+ for (unsigned i=0; i<mNumCands; i+=2) {
+ // extend and suffix
+ const uint32_t iState0 = (sp->iState) << 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<mNumCands; i++) {
+ vCand& thisCand = mCandidates[i];
+ // We examine input bits 2 at a time for a rate 1/2 coder.
+ const unsigned mismatched = inSample ^ (thisCand.oState);
+ thisCand.cost += cTab[mismatched&0x01][1] + cTab[(mismatched>>1)&0x01][0];
+ }
+}
+
+
+void ViterbiR2O4::pruneCandidates()
+{
+ const vCand* c1 = mCandidates; // 0-prefix
+ const vCand* c2 = mCandidates + mIStates; // 1-prefix
+ for (unsigned i=0; i<mIStates; i++) {
+ if (c1[i].cost < c2[i].cost) mSurvivors[i] = c1[i];
+ else mSurvivors[i] = c2[i];
+ }
+}
+
+
+const ViterbiR2O4::vCand& ViterbiR2O4::minCost() const
+{
+ int minIndex = 0;
+ float minCost = mSurvivors[0].cost;
+ for (unsigned i=1; i<mIStates; i++) {
+ const float thisCost = mSurvivors[i].cost;
+ if (thisCost>=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; i<size(); i++) {
+ if (source.bit(i)) mStart[i]=1.0F;
+ else mStart[i]=0.0F;
+ }
+}
+
+
+BitVector SoftVector::sliced() const
+{
+ size_t sz = size();
+ BitVector newSig(sz);
+ for (size_t i=0; i<sz; i++) {
+ if (mStart[i]>0.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; i<sz; i++) {
+ accum = (accum<<1) | bits.bit(i);
+ history[i] = accum;
+ }
+ // Repeat last bit at the end.
+ for (size_t i=sz; i<ctsz; i++) {
+ accum = (accum<<1) | (accum & 0x01);
+ history[i] = accum;
+ }
+ }
+
+ // Precompute metric tables.
+ float matchCostTable[ctsz];
+ float mismatchCostTable[ctsz];
+ {
+ const float *dp = mStart;
+ for (size_t i=0; i<sz; i++) {
+ // pVal is the probability that a bit is correct.
+ // ipVal is the probability that a bit is incorrect.
+ float pVal = dp[i];
+ if (pVal>0.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<ctsz; i++) {
+ matchCostTable[i] = 0.5F;
+ mismatchCostTable[i] = 0.5F;
+ }
+ }
+
+ {
+ decoder.initializeStates();
+ // Each sample of history[] carries its history.
+ // So we only have to process every iRate-th sample.
+ const unsigned step = decoder.iRate();
+ // input pointer
+ const uint32_t *ip = history + step - 1;
+ // output pointers
+ char *op = target.begin();
+ const char *const opt = target.end();
+ // table pointers
+ const float* match = matchCostTable;
+ const float* mismatch = mismatchCostTable;
+ size_t oCount = 0;
+ while (op<opt) {
+ // Viterbi algorithm
+ assert(match-matchCostTable<sizeof(matchCostTable)/sizeof(matchCostTable[0])-1);
+ assert(mismatch-mismatchCostTable<sizeof(mismatchCostTable)/sizeof(mismatchCostTable[0])-1);
+ const ViterbiR2O4::vCand &minCost = decoder.step(*ip, match, mismatch);
+ ip += step;
+ match += step;
+ mismatch += step;
+ // output
+ if (oCount>=deferral) *op++ = (minCost.iState >> deferral)&0x01;
+ oCount++;
+ }
+ }
+}
+
+
+
+
+ostream& operator<<(ostream& os, const SoftVector& sv)
+{
+ for (size_t i=0; i<sv.size(); i++) {
+ if (sv[i]<0.25) os << "0";
+ else if (sv[i]>0.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; i<bytes; i++) {
+ targ[i] = peekField(i*8,8);
+ }
+ unsigned whole = bytes*8;
+ unsigned rem = size() - whole;
+ if (rem==0) return;
+ targ[bytes] = peekField(whole,rem) << (8-rem);
+}
+
+
+void BitVector::unpack(const unsigned char* src)
+{
+ // Assumes MSB-first packing.
+ unsigned bytes = size()/8;
+ for (unsigned i=0; i<bytes; i++) {
+ fillField(i*8,src[i],8);
+ }
+ unsigned whole = bytes*8;
+ unsigned rem = size() - whole;
+ if (rem==0) return;
+ fillField(whole,src[bytes],rem);
+}
+
+void BitVector::hex(ostream& os) const
+{
+ os << std::hex;
+ unsigned digits = size()/4;
+ size_t wp=0;
+ for (unsigned i=0; i<digits; i++) {
+ os << readField(wp,4);
+ }
+ os << std::dec;
+}
+
+bool BitVector::unhex(const char* src)
+{
+ // Assumes MSB-first packing.
+ unsigned int val;
+ unsigned digits = size()/4;
+ for (unsigned i=0; i<digits; i++) {
+ if (sscanf(src+i, "%1x", &val) < 1) {
+ return false;
+ }
+ fillField(i*4,val,4);
+ }
+ unsigned whole = digits*4;
+ unsigned rem = size() - whole;
+ if (rem>0) {
+ 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 0000000..572e6b4
--- /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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#ifndef FECVECTORS_H
+#define FECVECTORS_H
+
+#include "Vector.h"
+#include <stdint.h>
+
+
+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<<wLen)-1),
+ mLen(wLen),mLen_1(wLen-1)
+ { assert(wLen<64); }
+
+ void clear() { mState=0; }
+
+ /**@name Accessors */
+ //@{
+ uint64_t state() const { return mState & mMask; }
+ unsigned size() const { return mLen; }
+ //@}
+
+ /**
+ Calculate one bit of a syndrome.
+ This is in the .h for inlining.
+ */
+ void syndromeShift(unsigned inBit)
+ {
+ const unsigned fb = (mState>>(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<<mIRate)-1; ///< ouput mask, all iRate low bits set
+ static const unsigned mNumCands = mIStates*2; ///< number of candidates to generate during branching
+ static const unsigned mDeferral = 6*mOrder; ///< deferral to be used
+ //@}
+ //@}
+
+ /** Precomputed tables. */
+ //@{
+ uint32_t mCoeffs[mIRate]; ///< polynomial for each generator
+ uint32_t mStateTable[mIRate][2*mIStates]; ///< precomputed generator output tables
+ uint32_t mGeneratorTable[2*mIStates]; ///< precomputed coder output table
+ //@}
+
+ public:
+
+ /**
+ A candidate sequence in a Viterbi decoder.
+ The 32-bit state register can support a deferral of 6 with a 4th-order coder.
+ */
+ typedef struct candStruct {
+ uint32_t iState; ///< encoder input associated with this candidate
+ uint32_t oState; ///< encoder output associated with this candidate
+ float cost; ///< cost (metric value), float to support soft inputs
+ } vCand;
+
+ /** Clear a structure. */
+ void clear(vCand& v)
+ {
+ v.iState=0;
+ v.oState=0;
+ v.cost=0;
+ }
+
+
+ private:
+
+ /**@name Survivors and candidates. */
+ //@{
+ vCand mSurvivors[mIStates]; ///< current survivor pool
+ vCand mCandidates[2*mIStates]; ///< current candidate pool
+ //@}
+
+ public:
+
+ unsigned iRate() const { return mIRate; }
+ uint32_t cMask() const { return mCMask; }
+ uint32_t stateTable(unsigned g, unsigned i) const { return mStateTable[g][i]; }
+ unsigned deferral() const { return mDeferral; }
+
+
+ ViterbiR2O4();
+
+ /** Set all cost metrics to zero. */
+ void initializeStates();
+
+ /**
+ Full cycle of the Viterbi algorithm: branch, metrics, prune, select.
+ @return reference to minimum-cost candidate.
+ */
+ const vCand& step(uint32_t inSample, const float *probs, const float *iprobs);
+
+ private:
+
+ /** Branch survivors into new candidates. */
+ void branchCandidates();
+
+ /** Compute cost metrics for soft-inputs. */
+ void getSoftCostMetrics(uint32_t inSample, const float *probs, const float *iprobs);
+
+ /** Select survivors from the candidate set. */
+ void pruneCandidates();
+
+ /** Find the minimum cost survivor. */
+ const vCand& minCost() const;
+
+ /**
+ Precompute the state tables.
+ @param g Generator index 0..((1/rate)-1)
+ */
+ void computeStateTables(unsigned g);
+
+ /**
+ Precompute the generator outputs.
+ mCoeffs must be defined first.
+ */
+ void computeGeneratorTable();
+
+};
+
+
+
+
+class BitVector : public Vector<char> {
+
+
+ public:
+
+ /**@name Constructors. */
+ //@{
+
+ /**@name Casts of Vector constructors. */
+ //@{
+ BitVector(char* wData, char* wStart, char* wEnd)
+ :Vector<char>(wData,wStart,wEnd)
+ { }
+ BitVector(size_t len=0):Vector<char>(len) {}
+ BitVector(const Vector<char>& source):Vector<char>(source) {}
+ BitVector(Vector<char>& source):Vector<char>(source) {}
+ BitVector(const Vector<char>& source1, const Vector<char> source2):Vector<char>(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<mEnd);
+ return (*dp) & 0x01;
+ }
+
+ /**@name Casts and overrides of Vector operators. */
+ //@{
+ BitVector segment(size_t start, size_t span)
+ {
+ char* wStart = mStart + start;
+ char* wEnd = wStart + span;
+ assert(wEnd<=mEnd);
+ return BitVector(NULL,wStart,wEnd);
+ }
+
+ BitVector alias()
+ { return segment(0,size()); }
+
+ const BitVector segment(size_t start, size_t span) const
+ { return (BitVector)(Vector<char>::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<float> {
+
+ public:
+
+ /** Build a SoftVector of a given length. */
+ SoftVector(size_t wSize=0):Vector<float>(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<float>(wData,length)
+ {}
+
+ SoftVector(float* wData, float* wStart, float* wEnd)
+ :Vector<float>(wData,wStart,wEnd)
+ { }
+
+ /**
+ Casting from a Vector<float>.
+ Note that this is NOT pass-by-reference.
+ */
+ SoftVector(Vector<float> source)
+ :Vector<float>(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<float>::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(dp<mEnd);
+ return (*dp)>0.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 0000000..5e487ad
--- /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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+
+
+#include "BitVector.h"
+#include <iostream>
+#include <cstdlib>
+
+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<sv2.size()/4; i++) sv2[random()%sv2.size()]=0.5;
+ cout << sv2 << endl;
+ BitVector v3(v1.size());
+ sv2.decode(vCoder,v3);
+ cout << v3 << endl;
+
+ cout << v3.segment(3,4) << endl;
+
+ BitVector v4(v3.segment(0,4),v3.segment(8,4));
+ cout << v4 << endl;
+
+ BitVector v5("000011110000");
+ int r1 = v5.peekField(0,8);
+ int r2 = v5.peekField(4,4);
+ int r3 = v5.peekField(4,8);
+ cout << r1 << ' ' << r2 << ' ' << r3 << endl;
+ cout << v5 << endl;
+ v5.fillField(0,0xa,4);
+ int r4 = v5.peekField(0,8);
+ cout << v5 << endl;
+ cout << r4 << endl;
+
+ v5.reverse8();
+ cout << v5 << endl;
+
+ BitVector mC = "000000000000111100000000000001110000011100001101000011000000000000000111000011110000100100001010000010100000101000001010000010100000010000000000000000000000000000000000000000000000001100001111000000000000000000000000000000000000000000000000000010010000101000001010000010100000101000001010000001000000000000000000000000110000111100000000000001110000101000001100000001000000000000";
+ SoftVector mCS(mC);
+ BitVector mU(mC.size()/2);
+ mCS.decode(vCoder,mU);
+ cout << "c=" << mCS << endl;
+ cout << "u=" << mU << endl;
+
+
+ unsigned char ts[9] = "abcdefgh";
+ BitVector tp(70);
+ cout << "ts=" << ts << endl;
+ tp.unpack(ts);
+ cout << "tp=" << tp << endl;
+ tp.pack(ts);
+ cout << "ts=" << ts << endl;
+}
diff --git a/CommonLibs/Configuration.cpp b/CommonLibs/Configuration.cpp
new file mode 100644
index 0000000..3ad4f01
--- /dev/null
+++ b/CommonLibs/Configuration.cpp
@@ -0,0 +1,339 @@
+/*
+* Copyright 2008, 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#include "Configuration.h"
+#include <fstream>
+#include <iostream>
+#include <string.h>
+#include <syslog.h>
+
+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<unsigned> 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<unsigned> 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<size(); i++) {
+ mHash = mHash ^ (mHash >> 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 0000000..c9c4cf3
--- /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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#ifndef CONFIGURATION_H
+#define CONFIGURATION_H
+
+
+#include "sqlite3util.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include <map>
+#include <vector>
+#include <string>
+#include <iostream>
+
+#include <Threads.h>
+#include <stdint.h>
+
+
+/** 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<other.mHash;
+ }
+
+ bool operator>(const HashString& other)
+ {
+ return mHash<other.mHash;
+ }
+
+ uint64_t hash() const { return mHash; }
+
+};
+
+
+typedef std::map<HashString, ConfigurationRecord> 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<unsigned> 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 0000000..ef82601
--- /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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+
+#include "Configuration.h"
+#include <iostream>
+
+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<unsigned> vect = gConfig.getVector("key5");
+ cout << "vect length " << vect.size() << ": ";
+ for (unsigned i=0; i<vect.size(); i++) cout << " " << vect[i];
+ cout << endl;
+}
diff --git a/CommonLibs/F16.h b/CommonLibs/F16.h
new file mode 100644
index 0000000..aa292f0
--- /dev/null
+++ b/CommonLibs/F16.h
@@ -0,0 +1,210 @@
+/*
+* 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#ifndef F16_H
+#define F16_H
+
+#include <stdint.h>
+#include <ostream>
+
+
+
+/** 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<other.mV;
+ }
+
+ bool operator==(const F16& other) const
+ {
+ return mV==other.mV;
+ }
+
+ bool operator>(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 0000000..7f3c84d
--- /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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "F16.h"
+
+
+#include <iostream>
+
+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 0000000..023ac14
--- /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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#ifndef INTERTHREAD_H
+#define INTERTHREAD_H
+
+#include "Timeval.h"
+#include "Threads.h"
+#include "LinkedLists.h"
+#include <map>
+#include <vector>
+#include <queue>
+
+
+
+
+
+/**@defgroup Templates for interthread mechanisms. */
+//@{
+
+
+/** Pointer FIFO for interthread operations. */
+template <class T> 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 T> 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 K, class D > class InterthreadMap {
+
+protected:
+
+ typedef std::map<K,D*> 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 T> 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 T, class C = std::vector<T*>, class Cmp = PointerCompare<T> > class InterthreadPriorityQueue {
+
+ protected:
+
+ std::priority_queue<T*,C,Cmp> 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 0000000..1712689
--- /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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+
+#include "Threads.h"
+#include "Interthread.h"
+#include <iostream>
+
+using namespace std;
+
+
+InterthreadQueue<int> gQ;
+InterthreadMap<int,int> 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 0000000..ba0f0cc
--- /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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+
+
+#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 0000000..4ca27e6
--- /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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+
+#ifndef LINKEDLISTS_H
+#define LINKEDLISTS_H
+
+#include <stdlib.h>
+
+
+
+/** 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 0000000..e9f73b0
--- /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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include <iostream>
+#include <iterator>
+
+#include "Logger.h"
+#include "Configuration.h"
+
+ConfigurationTable gConfig;
+//ConfigurationTable gConfig("example.config");
+
+void printAlarms()
+{
+ std::ostream_iterator<std::string> output( std::cout, "\n" );
+ std::list<std::string> alarms = gGetLoggerAlarms();
+ std::cout << "#alarms = " << alarms.size() << std::endl;
+ std::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 0000000..57d2bff
--- /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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include <string.h>
+#include <cstdio>
+#include <fstream>
+#include <string>
+
+#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<string> 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<uint64_t,int> 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<uint64_t,int>::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<uint64_t,int>(key,level));
+ sLogCacheLock.unlock();
+ return level;
+}
+
+
+
+
+
+// copies the alarm list and returns it. list supposed to be small.
+list<string> gGetLoggerAlarms()
+{
+ alarmsLock.lock();
+ list<string> 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<string> > 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<numLevels);
+ mStream << levelNames[mPriority] << ' ';
+ return mStream;
+}
+
+
+
+void gLogInit(const char* name, const char* level, int facility)
+{
+ // Set the level.
+ if (level) {
+ gConfig.set("Log.Level",level);
+ } else {
+ if (!gConfig.defines("Log.Level")) {
+ gConfig.set("Log.Level","WARNING");
+ }
+ }
+
+ // Define other logging parameters in the global config.
+ if (!gConfig.defines("Log.Alarms.Max")) {
+ gConfig.set("Log.Alarms.Max",DEFAULT_MAX_ALARMS);
+ }
+
+ // Open the log connection.
+ openlog(name,0,facility);
+}
+
+
+
+
+// vim: ts=4 sw=4
diff --git a/CommonLibs/Logger.h b/CommonLibs/Logger.h
new file mode 100644
index 0000000..b9fd5ea
--- /dev/null
+++ b/CommonLibs/Logger.h
@@ -0,0 +1,99 @@
+/*
+* 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#ifndef LOGGER_H
+#define LOGGER_H
+
+#include <syslog.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sstream>
+#include <list>
+#include <map>
+#include <string>
+#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<std::string> 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 0000000..52c965c
--- /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 <http://www.gnu.org/licenses/>.
+#
+
+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 0000000..3ff1e97
--- /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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#ifndef REGEXPW_H
+#define REGEXPW_H
+
+#include <regex.h>
+#include <iostream>
+#include <stdlib.h>
+
+
+
+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 0000000..748be49
--- /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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+
+#include "Regexp.h"
+#include <iostream>
+
+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 0000000..f24b495
--- /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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <cstdio>
+#include <sys/select.h>
+
+#include "Threads.h"
+#include "Sockets.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+
+
+
+
+
+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 0000000..c79f79a
--- /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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#ifndef SOCKETS_H
+#define SOCKETS_H
+
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <errno.h>
+#include <list>
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+
+
+
+
+
+#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 "<host>:<port>". */
+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 0000000..9a4997b
--- /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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+
+
+#include "Sockets.h"
+#include "Threads.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+
+static const int gNumToSend = 10;
+
+
+void *testReaderIP(void *)
+{
+ UDPSocket readSocket(5934, "localhost", 5061);
+ readSocket.nonblocking();
+ int rc = 0;
+ while (rc<gNumToSend) {
+ char buf[MAX_UDP_LENGTH];
+ int count = readSocket.read(buf);
+ if (count>0) {
+ COUT("read: " << buf);
+ rc++;
+ } else {
+ sleep(2);
+ }
+ }
+ return NULL;
+}
+
+
+
+void *testReaderUnix(void *)
+{
+ UDDSocket readSocket("testDestination");
+ readSocket.nonblocking();
+ int rc = 0;
+ while (rc<gNumToSend) {
+ char buf[MAX_UDP_LENGTH];
+ int count = readSocket.read(buf);
+ if (count>0) {
+ 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<gNumToSend; i++) {
+ socket1.write("Hello IP land");
+ socket1U.write("Hello Unix domain");
+ sleep(1);
+ }
+
+ readerThreadIP.join();
+ readerThreadUnix.join();
+}
+
+// vim: ts=4 sw=4
diff --git a/CommonLibs/Threads.cpp b/CommonLibs/Threads.cpp
new file mode 100644
index 0000000..7cc8b92
--- /dev/null
+++ b/CommonLibs/Threads.cpp
@@ -0,0 +1,120 @@
+/*
+* 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+
+
+
+#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 0000000..c1cfc75
--- /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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#ifndef THREADS_H
+#define THREADS_H
+
+#include <pthread.h>
+#include <iostream>
+#include <assert.h>
+
+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 0000000..47eebc4
--- /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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+
+#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 0000000..c2a2617
--- /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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#ifndef TIMEVAL_H
+#define TIMEVAL_H
+
+#include <stdint.h>
+#include "sys/time.h"
+#include <iostream>
+
+
+
+/** 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 0000000..b4746f2
--- /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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+
+
+#include "Timeval.h"
+#include <iostream>
+
+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 0000000..870db33
--- /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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include <URLEncode.h>
+#include <string>
+#include <string.h>
+#include <ctype.h>
+
+using namespace std;
+
+//based on javascript encodeURIComponent()
+string URLEncode(const string &c)
+{
+ static const char *digits = "01234567890ABCDEF";
+ string retVal="";
+ for (int i=0; i<c.length(); i++)
+ {
+ const char ch = c[i];
+ if (isalnum(ch) || strchr("-_.!~'()",ch)) {
+ retVal += ch;
+ } else {
+ retVal += '%';
+ retVal += digits[(ch>>4) & 0x0f];
+ retVal += digits[ch & 0x0f];
+ }
+ }
+ return retVal;
+}
+
diff --git a/CommonLibs/URLEncode.h b/CommonLibs/URLEncode.h
new file mode 100644
index 0000000..558ced9
--- /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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+
+# include <string>
+
+std::string URLEncode(const std::string&);
diff --git a/CommonLibs/Vector.h b/CommonLibs/Vector.h
new file mode 100644
index 0000000..62cb6fb
--- /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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+
+
+#ifndef VECTOR_H
+#define VECTOR_H
+
+#include <string.h>
+#include <iostream>
+#include <assert.h>
+
+
+/**
+ 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 T> 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<T>& 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<T>& other)
+ :mData(other.mData),mStart(other.mStart),mEnd(other.mEnd)
+ { other.mData=NULL; }
+
+ /** Build a Vector by copying another. */
+ Vector(const Vector<T>& 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<T>& other1, const Vector<T>& 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<T>& other)
+ {
+ clear();
+ mData=other.mData;
+ mStart=other.mStart;
+ mEnd=other.mEnd;
+ other.mData=NULL;
+ }
+
+ /** Assign from another Vector, copying. */
+ void operator=(const Vector<T>& other) { clone(other); }
+
+ //@}
+
+
+ //@{
+
+ /** Return an alias to a segment of this Vector. */
+ Vector<T> segment(size_t start, size_t span)
+ {
+ T* wStart = mStart + start;
+ T* wEnd = wStart + span;
+ assert(wEnd<=mEnd);
+ return Vector<T>(NULL,wStart,wEnd);
+ }
+
+ /** Return an alias to a segment of this Vector. */
+ const Vector<T> segment(size_t start, size_t span) const
+ {
+ T* wStart = mStart + start;
+ T* wEnd = wStart + span;
+ assert(wEnd<=mEnd);
+ return Vector<T>(NULL,wStart,wEnd);
+ }
+
+ Vector<T> head(size_t span) { return segment(0,span); }
+ const Vector<T> head(size_t span) const { return segment(0,span); }
+ Vector<T> tail(size_t start) { return segment(start,size()-start); }
+ const Vector<T> 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<T>& 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<T>& other, size_t start=0) const { copyToSegment(other,start,size()); }
+
+ void copyTo(Vector<T>& 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<T>& 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<mEnd) *dp++=val;
+ }
+
+ void fill(const T& val, unsigned start, unsigned length)
+ {
+ T* dp=mStart+start;
+ T* end=dp+length;
+ assert(end<=mEnd);
+ while (dp<end) *dp++=val;
+ }
+
+
+ //@}
+
+
+ //@{
+
+ T& operator[](size_t index)
+ {
+ assert(mStart+index<mEnd);
+ return mStart[index];
+ }
+
+ const T& operator[](size_t index) const
+ {
+ assert(mStart+index<mEnd);
+ return mStart[index];
+ }
+
+ const T* begin() const { return mStart; }
+ T* begin() { return mStart; }
+ const T* end() const { return mEnd; }
+ T* end() { return mEnd; }
+ //@}
+
+
+};
+
+
+
+
+/** Basic print operator for Vector objects. */
+template <class T>
+std::ostream& operator<<(std::ostream& os, const Vector<T>& v)
+{
+ for (unsigned i=0; i<v.size(); i++) os << v[i] << " ";
+ return os;
+}
+
+
+
+#endif
+// vim: ts=4 sw=4
diff --git a/CommonLibs/VectorTest.cpp b/CommonLibs/VectorTest.cpp
new file mode 100644
index 0000000..6958889
--- /dev/null
+++ b/CommonLibs/VectorTest.cpp
@@ -0,0 +1,63 @@
+/*
+* 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+
+
+#include "Vector.h"
+#include <iostream>
+
+using namespace std;
+
+typedef Vector<int> 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;
+}