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
This commit is contained in:
parent
cd8381743f
commit
82c46ff7ae
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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&);
|
|
@ -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
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue