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
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;
+}