Added Files:
atomicint.cpp atomicint.il counter.cpp kncircularmapping.cpp
knconsistenthash.cpp kncrashhandler.cpp kncrypt.cpp
knerror.cpp knhash.cpp knlog.cpp knmarkuputil.cpp knmemory.cpp
knmutex.cpp knnetwork.cpp knnewdelete.cpp knpattern.cpp
knperfstatistics.cpp knregex.cpp knrwlock.cpp
knrwlogginglock.cpp knset.cpp knsleep.cpp knstring.cpp
knstringtokenizer.cpp knsysutility_unix.cpp
knsysutility_win32.cpp kntclerrors.cpp kntclfile.cpp
kntclinterp.cpp kntemplateuri.cpp kntime.cpp knurl.cpp
mtrand.cpp randomnumber.cpp shahash.cpp uuidgenerator.cpp
Log Message:
Added this library to the AOLServer.com repository. It contains a number of C/C++ utilities, some of which integrate into AOLServer, and many of which standalone. A portion of the library is required for the knregistration module, which will be checked in next.
I believe this compiles fine in "native" AOLServer, but it has not been tested recently.
--- NEW FILE: atomicint.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/atomicint.h"
#ifndef WIN32
#include <unistd.h>
#endif
namespace
{
#ifdef WIN32
int getNumberOfProcessors()
{
SYSTEM_INFO si;
memset(&si, 0, sizeof(si));
GetSystemInfo(&si);
return si.dwNumberOfProcessors;
}
#else
int getNumberOfProcessors()
{
return sysconf(_SC_NPROCESSORS_ONLN);
}
#endif /* WIN32 */
/*
* Assume that the number of processors remains static during process
* lifetime. This may be dicey if the superuser is using/abusing tools
* such as Solaris's psradm.
*/
const int gNumberOfProcessors = getNumberOfProcessors();
#ifdef WIN32
#pragma warning(disable : 4035)
/*
* Return with the result in EAX.
*/
inline int atomicAdd(int *pcnt, int cnt)
{
if (gNumberOfProcessors == 1)
{
__asm
{
mov ecx, pcnt
mov eax, cnt
xadd dword ptr [ecx], eax
add eax, cnt
}
}
else
{
__asm
{
mov ecx, pcnt
mov eax, cnt
lock xadd dword ptr [ecx], eax
add eax, cnt
}
}
}
#pragma warning(default : 4035)
inline bool atomicCompareAndSwap(int *pcnt, int oval, int val)
{
unsigned char c;
if (gNumberOfProcessors == 1)
{
__asm
{
mov ecx, pcnt
mov eax, oval
mov edx, val
cmpxchg dword ptr [ecx], edx
sete c
}
}
else
{
__asm
{
mov ecx, pcnt
mov eax, oval
mov edx, val
lock cmpxchg dword ptr [ecx], edx
sete c
}
}
return c != 0;
}
inline bool atomicDecrementAndTest(int *pcnt)
{
unsigned char c;
if (gNumberOfProcessors == 1)
{
__asm
{
mov ecx, pcnt
dec dword ptr [ecx]
sete c
}
}
else
{
__asm
{
mov ecx, pcnt
lock dec dword ptr [ecx]
sete c
}
}
return c != 0;
}
inline bool atomicDoubleCompareAndSwap(int64_t *pcnt, int64_t oval, int64_t val)
{
unsigned char c;
if (gNumberOfProcessors == 1)
{
__asm
{
mov esi, pcnt
mov edx, dword ptr [oval + 4]
mov eax, dword ptr [oval]
mov ecx, dword ptr [val + 4]
mov ebx, dword ptr [val]
cmpxchg8b qword ptr [esi]
sete c
}
}
else
{
__asm
{
mov esi, pcnt
mov edx, dword ptr [oval + 4]
mov eax, dword ptr [oval]
mov ecx, dword ptr [val + 4]
mov ebx, dword ptr [val]
lock cmpxchg8b qword ptr [esi]
sete c
}
}
return c != 0;
}
inline void atomicIncrement(int *pcnt)
{
if (gNumberOfProcessors == 1)
{
__asm
{
mov ecx, pcnt
inc dword ptr [ecx]
}
}
else
{
__asm
{
mov ecx, pcnt
lock inc dword ptr [ecx]
}
}
}
inline void atomicReadBarrier()
{
/*
* A read barrier is only necessary on a multiprocessor box.
*/
if (gNumberOfProcessors != 1)
{
__asm
{
lock add dword ptr [esp], 0
}
}
}
inline void atomicWriteBarrier()
{
/*
* Intel doesn't reorder writes.
*/
}
#elif __linux
inline int atomicAdd(int *pcnt, int cnt)
{
int rc;
if (gNumberOfProcessors == 1)
{
__asm__ __volatile__(
"xaddl %0,%2"
: "=r" (rc)
: "0" (cnt), "m" (*pcnt)
: "memory");
}
else
{
__asm__ __volatile__(
"lock xaddl %0,%2"
: "=r" (rc)
: "0" (cnt), "m" (*pcnt)
: "memory");
}
return rc + cnt;
}
inline bool atomicCompareAndSwap(int *pcnt, int oval, int val)
{
unsigned char c;
if (gNumberOfProcessors == 1)
{
__asm__ __volatile__(
"cmpxchgl %2,%1; sete %0"
: "=q" (c), "=m" (*pcnt)
: "r" (val), "1" (*pcnt), "a" (oval));
}
else
{
__asm__ __volatile__(
"lock cmpxchgl %2,%1; sete %0"
: "=q" (c), "=m" (*pcnt)
: "r" (val), "1" (*pcnt), "a" (oval));
}
return c != 0;
}
inline bool atomicDecrementAndTest(int *pcnt)
{
unsigned char c;
if (gNumberOfProcessors == 1)
{
__asm__ __volatile__(
"decl %1; sete %0"
: "=q" (c)
: "m" (*pcnt)
: "memory");
}
else
{
__asm__ __volatile__(
"lock decl %1; sete %0"
: "=q" (c)
: "m" (*pcnt)
: "memory" );
}
return c != 0;
}
inline bool atomicDoubleCompareAndSwap(int64_t *pcnt, int64_t oval, int64_t val)
{
unsigned char c;
if (gNumberOfProcessors == 1)
{
__asm__ __volatile__(
"cmpxchg8b %1; sete %0"
: "=q" (c), "=m" (*pcnt)
: "1" (*pcnt),
"A" (oval),
"c" (static_cast<int>(val >> 32)),
"b" (static_cast<int>(val)));
}
else
{
__asm__ __volatile__(
"lock cmpxchg8b %1; sete %0"
: "=q" (c), "=m" (*pcnt)
: "1" (*pcnt),
"A" (oval),
"c" (static_cast<int>(val >> 32)),
"b" (static_cast<int>(val)));
}
return c != 0;
}
inline void atomicIncrement(int *pcnt)
{
if (gNumberOfProcessors == 1)
{
__asm__ __volatile__(
"incl %0"
: "+m" (*pcnt)
);
}
else
{
__asm__ __volatile__(
"lock incl %0"
: "+m" (*pcnt)
);
}
}
inline void atomicReadBarrier()
{
if (gNumberOfProcessors == 1)
{
__asm__ __volatile__(
""
:
:
: "memory");
}
else
{
int x = 0, y = 1;
__asm__ __volatile__(
"xchgl %0,%1"
: "=r" (x)
: "m" (y), "0" (x)
: "memory");
}
}
inline void atomicWriteBarrier()
{
__asm__ __volatile__(
""
:
:
: "memory");
}
#elif __sun
extern "C" int atomicAdd(int *, int);
extern "C" bool atomicCompareAndSwap(int *, int, int);
extern "C" bool atomicDoubleCompareAndSwap(int64_t *, int64_t *, int64_t *);
extern "C" void atomicReadBarrier();
extern "C" void atomicWriteBarrier();
inline bool atomicDecrementAndTest(int *pcnt)
{
return atomicAdd(pcnt, -1) == 0;
}
inline bool atomicDoubleCompareAndSwap(int64_t *pcnt, int64_t oval, int64_t val)
{
return atomicDoubleCompareAndSwap(pcnt, &oval, &val);
}
inline void atomicIncrement(int *pcnt)
{
atomicAdd(pcnt, 1);
}
#endif /* WIN32 */
} //namespace
int AtomicInt::operator+=(int cnt)
{
return atomicAdd(&m_cnt, cnt);
}
int AtomicInt::operator-=(int cnt)
{
return atomicAdd(&m_cnt, -cnt);
}
int AtomicInt::operator++()
{
return atomicAdd(&m_cnt, 1);
}
int AtomicInt::operator++(int)
{
return atomicAdd(&m_cnt, 1) - 1;
}
int AtomicInt::operator--()
{
return atomicAdd(&m_cnt, -1);
}
int AtomicInt::operator--(int)
{
return atomicAdd(&m_cnt, -1) + 1;
}
bool AtomicInt::decrementAndTest()
{
return atomicDecrementAndTest(&m_cnt);
}
void AtomicInt::increment()
{
atomicIncrement(&m_cnt);
}
--- NEW FILE: atomicint.il ---
.inline atomicAdd,8
1:
ld [%o0],%o2
add %o2,%o1,%o3
cas [%o0],%o2,%o3
cmp %o2,%o3
bne,pn %icc,1b
nop
add %o2,%o1,%o0
.end
.inline atomicCompareAndSwap,12
cas [%o0],%o1,%o2
xor %o0,%o0,%o0
cmp %o1,%o2
bne,pn %icc,2f
nop
add %o0,1,%o0
2:
.end
.inline atomicDoubleCompareAndSwap,12
ldx [%o1],%o3
ldx [%o2],%o4
casx [%o0],%o3,%o4
xor %o0,%o0,%o0
cmp %o3,%o4
bne,pn %xcc,3f
nop
add %o0,1,%o0
3:
.end
.inline atomicReadBarrier,0
membar #LoadLoad
.end
.inline atomicWriteBarrier,0
membar #StoreStore
.end
--- NEW FILE: counter.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/counter.h"
Counter::Counter(int c) : count(c)
{
}
Counter::~Counter()
{
}
--- NEW FILE: kncircularmapping.cpp ---
#include <math.h>
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include <sstream>
#include <algorithm>
#include "knexportlibraryknutilmodule.h"
#include "knutil/knerror.h"
#include "knutil/knlog.h"
#include "knutil/kncircularmapping.h"
//--------------------------------------------------------------------------
// Bucket Class
//--------------------------------------------------------------------------
class Bucket
{
public:
Bucket(const std::string& bucketName, double weight) : m_name(bucketName), m_weight(weight)
{
}
std::string m_name;
double m_weight;
std::vector<KnCircularMapping::PointT> m_replicaPoints;
};
bool BucketComp(const Bucket* lhs, const Bucket* rhs)
{
return lhs->m_name < rhs->m_name;
}
bool insertSorted(std::vector<Bucket*>& v, Bucket* value)
{
std::pair<std::vector<Bucket*>::iterator, std::vector<Bucket*>::iterator> bounds;
bounds = equal_range(v.begin(), v.end(), value, BucketComp);
if(bounds.first == bounds.second)
{
if(bounds.first == v.end())
{
v.push_back(value);
return true;
}
v.insert(bounds.second, value);
return true;
}
return false;
}
//--------------------------------------------------------------------------
// ReplicaPoint Class
//--------------------------------------------------------------------------
class ReplicaPoint
{
public:
ReplicaPoint(KnCircularMapping::PointT& point) : m_point(point)
{
}
~ReplicaPoint()
{
}
Bucket* getFirstBucket()
{
if(!m_replicas.size())
{
KnLog(KnLogError, "KnConsistentHash: replica point with no replicas.");
return NULL;
}
Bucket* bucket = *m_replicas.begin();
return bucket;
}
bool addReplica(Bucket* bucket) // keep them sorted , return true if conflict
{
if(!m_replicas.size())
{
m_replicas.push_back(bucket);
return false;
}
bool result = insertSorted(m_replicas, bucket);
if(!result)
{
KnLog(KnLogError, "KnConsistentHash: Bucket with same ID already present at replica");
}
KnLog(KnLogWarning, "KnConsistentHash: conflict when adding bucket %s replica at point %u.", bucket->m_name.c_str(), m_point);
return true;
}
KnCircularMapping::PointT m_point;
std::vector<Bucket*> m_replicas; // should be just one or there are conflicts
};
//--------------------------------------------------------------------------
// KnCircularMapping
//--------------------------------------------------------------------------
KnCircularMapping::KnCircularMapping()
{
m_totalWeight = 0;
}
KnCircularMapping::~KnCircularMapping()
{
removeAllBuckets();
}
bool KnCircularMapping::addBucket(const char* bucketName, std::vector<PointT>& replicas, double weight)
{
if (weight <= 0)
return false;
if (m_buckets.find(bucketName) != m_buckets.end())
return false;
Bucket* bucket = new Bucket(bucketName, weight);
m_buckets[bucketName] = bucket;
ReplicaPoint* rp;
bucket->m_replicaPoints = replicas;
for (int i = 0; i < replicas.size(); i++)
{
rp = findOrAddReplicaPoint(replicas[i]);
rp->addReplica(bucket);
}
m_totalWeight += weight;
return true;
}
bool KnCircularMapping::removeBucket(const char* bucketName)
{
BucketMapIterator it = m_buckets.find(bucketName);
if (it == m_buckets.end())
return false;
Bucket* bucket = it->second;
m_buckets.erase(it);
double weight = bucket->m_weight;
m_totalWeight -= weight;
for (int i = 0; i < bucket->m_replicaPoints.size(); i++)
{
if(!removeReplicaPoint(bucket->m_replicaPoints[i], bucket))
{
KnLog(KnLogError, "KnConsistentHash: could not remove replica point %u for bucket %s.", bucket->m_replicaPoints[i] ,bucket->m_name.c_str());
}
}
SAFE_DELETE(bucket);
return true;
}
ReplicaPoint* KnCircularMapping::findOrAddReplicaPoint(PointT point)
{
ReplicaMapRange bounds;
ReplicaPoint* result;
bounds = m_replicaMap.equal_range(point);
if(bounds.first == bounds.second)
{
// not found
result = new ReplicaPoint(point);
if(bounds.first == m_replicaMap.end())
{
m_replicaMap[point] = result;
return result;
}
m_replicaMap.insert(bounds.second, std::pair<const PointT, ReplicaPoint*>(point, result));
return result;
}
return (*bounds.first).second;
}
ReplicaPoint* KnCircularMapping::findReplicaPoint(PointT point)
{
ReplicaMapIterator it = m_replicaMap.find(point);
if(it == m_replicaMap.end())
return NULL;
return it->second;
}
bool KnCircularMapping::removeReplicaPoint(PointT point, Bucket* bucket)
{
ReplicaMapRange bounds;
bounds = m_replicaMap.equal_range(point);
if(bounds.first == bounds.second)
return false;
ReplicaPoint* rp = (*bounds.first).second;
std::vector<Bucket*>& replicas = rp->m_replicas;
std::vector<Bucket*>::iterator it = find(replicas.begin(), replicas.end(), bucket);
if(it == replicas.end())
return false;
replicas.erase(it);
if(!replicas.size())
{
m_replicaMap.erase(bounds.first);
SAFE_DELETE(rp);
}
return true;
}
Bucket* KnCircularMapping::findBucketForPoint(PointT point)
{
if (m_replicaMap.empty() || m_buckets.empty())
return NULL;
if (m_buckets.size() == 1)
return (*m_replicaMap.begin()).second->getFirstBucket();
ReplicaMapRange bounds;
bounds = m_replicaMap.equal_range(point);
if(bounds.first == bounds.second)
{
// not found
if(bounds.first == m_replicaMap.end())
{
// point is after all replicas => assign to first replica
return (*m_replicaMap.begin()).second->getFirstBucket();
}
// assign to greater replica
return (*bounds.second).second->getFirstBucket();
}
// assign to this replica as it has the same point value
return (*bounds.first).second->getFirstBucket();
}
void KnCircularMapping::removeAllBuckets()
{
ReplicaMapIterator it;
for(it = m_replicaMap.begin(); it != m_replicaMap.end(); it++)
SAFE_DELETE(it->second);
m_replicaMap.clear();
BucketMapIterator bmi = m_buckets.begin();
for(; bmi != m_buckets.end(); bmi++)
SAFE_DELETE(bmi->second);
m_buckets.clear();
}
double KnCircularMapping::getWeight(const std::string& bucketName)
{
BucketMapIterator it = m_buckets.find(bucketName.c_str());
if(it != m_buckets.end())
return it->second->m_weight;
return -1;
}
double KnCircularMapping::getTotalWeight()
{
return m_totalWeight;
}
bool KnCircularMapping::existsBucket(const std::string& bucketName)
{
return m_buckets.find(bucketName.c_str()) != m_buckets.end();
}
std::string KnCircularMapping::assignPointToBucket(PointT point)
{
Bucket* bucket = findBucketForPoint(point);
if(!bucket)
return "";
return bucket->m_name;
}
int KnCircularMapping::bucketCount()
{
return m_buckets.size();
}
--- NEW FILE: knconsistenthash.cpp ---
#include <math.h>
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include <sstream>
#include <algorithm>
#include <vector>
#include "knexportlibraryknutilmodule.h"
#include "knutil/knerror.h"
#include "knutil/knlog.h"
#include "knutil/knhash.h"
#include "knutil/mtrand.h"
#include "knutil/knconsistenthash.h"
//--------------------------------------------------------------------------
// KnConsistentHash
//--------------------------------------------------------------------------
KnConsistentHash::KnConsistentHash(int replicasPerBucket, KnCircularMapping::PointT pointInterval)
{
m_replicasPerBucket = replicasPerBucket;
m_pointInterval = pointInterval;
}
KnConsistentHash::~KnConsistentHash()
{
}
KnConsistentHash::IDHash KnConsistentHash::computeHash(const char* identifier)
{
return hash32(identifier, strlen(identifier), 1492);
}
KnCircularMapping::PointT KnConsistentHash::computePointForResource(const char* identifier)
{
MTRand_int32 randGen( computeHash(identifier) );
return randGen() % m_pointInterval;
}
bool KnConsistentHash::addBucket(const char* bucketName, double weight)
{
return addBucketUnsafe(bucketName, computeHash(bucketName), weight);
}
bool KnConsistentHash::addBucket(const char* bucketName, IDHash bucketHash, double weight)
{
KnRWLock::KnWriteLock wlock(m_bucketLock);
return addBucketUnsafe(bucketName, bucketHash, weight);
}
bool KnConsistentHash::addBucketUnsafe(const char* bucketName, IDHash bucketHash, double weight)
{
if (weight < 0.000001)
return m_mapping.removeBucket(bucketName);
if(m_mapping.existsBucket(bucketName))
{
// Bucket already exists => remove before adding
m_mapping.removeBucket(bucketName);
}
MTRand_int32 randGen(bucketHash);
KnCircularMapping::PointT point;
std::vector<KnCircularMapping::PointT> replicas;
int replicaCount = floor(weight * m_replicasPerBucket);
replicas.reserve(replicaCount);
for (int i = 0; i < replicaCount; i++)
{
point = randGen() % m_pointInterval;
replicas.push_back(point);
}
return m_mapping.addBucket(bucketName, replicas, weight);
}
bool KnConsistentHash::removeBucket(const char* bucketName)
{
KnRWLock::KnWriteLock wlock(m_bucketLock);
return m_mapping.removeBucket(bucketName);
}
void KnConsistentHash::removeAllBuckets()
{
KnRWLock::KnWriteLock wlock(m_bucketLock);
m_mapping.removeAllBuckets();
}
std::string KnConsistentHash::assignPointToBucket(KnCircularMapping::PointT point)
{
KnRWLock::KnReadLock rlock(m_bucketLock);
return m_mapping.assignPointToBucket(point);
}
std::string KnConsistentHash::assignResourceToBucket(const char* identifier)
{
KnRWLock::KnReadLock rlock(m_bucketLock);
return m_mapping.assignPointToBucket( computePointForResource(identifier) );
}
int KnConsistentHash::bucketCount()
{
return m_mapping.bucketCount();
}
bool KnConsistentHash::addBucketsFromSet(const KnSet& bucketSet)
{
if(bucketSet.size() == 0)
return true;
KnRWLock::KnWriteLock wlock(m_bucketLock);
return addBucketsFromSetUnsafe(bucketSet);
}
bool KnConsistentHash::setBucketsFromSet(const KnSet& bucketSet)
{
if(bucketSet.size() == 0)
return true;
KnRWLock::KnWriteLock wlock(m_bucketLock);
m_mapping.removeAllBuckets();
return addBucketsFromSetUnsafe(bucketSet);
}
bool KnConsistentHash::addBucketsFromSetUnsafe(const KnSet& bucketSet)
{
if(bucketSet.size() == 0)
return true;
for (size_t ix = 0; ix < bucketSet.size(); ++ix)
{
KnSetElem entry = bucketSet[ix];
KnString bucketName = entry.m_key;
if(entry.m_value == "")
{
// A delete bucket request
m_mapping.removeBucket(bucketName.c_str());
continue;
}
double weight = entry.m_value.toDouble();
addBucketUnsafe(bucketName.c_str(), computeHash(bucketName.c_str()), weight);
}
return true;
}
double KnConsistentHash::getTotalWeight()
{
return m_mapping.getTotalWeight();
}
--- NEW FILE: kncrashhandler.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/kncrashhandler.h"
#include "knutil/knlog.h"
#include "knutil/knmemory.h"
#include "knutil/knmutex.h"
#include "string"
namespace {
KnMutex gCrashHandlerMutex;
std::string gDumpLocation; // For occasions when we change it.
bool gDumpAtAll = true; // Avoid denial of service by running out of disk (Customer Support called for the default to be false for this reason).
}
#ifdef WIN32
#define _MSJDBG_H
#include "BugslayerUtil.h"
namespace {
// From http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/base/minidumpwritedump.asp:
// DumpType
// [in] The type of information to be generated. This parameter can be
// one or more of the values from the MINIDUMP_TYPE enumeration.
unsigned int gMiniDumpType = static_cast<unsigned int>(MiniDumpNormal);
char* gMiniDumpFile = "log/liveserver.dmp";
}
LONG __stdcall KnCrashHandler( EXCEPTION_POINTERS * pExPtrs )
{
// Count on the BugSlayer's OutputDebugString (annoying) if
// we're out of stack; if we're a service, we'll have hung
// before getting here. Unfortunately, it's not as rare for us as
// for many other programs, since we're multithreaded (and have therefore
// declared a stack size), but it's rare enough to have worked out OK for a while.
// When we get hangs or crashes on Windows with no stack trace or minidump, then
// we're probably looking at exactly that case. Try increasing the stack size
// in knrouter.tcl.
if ( EXCEPTION_STACK_OVERFLOW != pExPtrs->ExceptionRecord->ExceptionCode )
{
// None of the BugSlayer routines are thread-safe. OTOH, it's hard
// to bring myself to use another mutex at this point, since we deadlock
// so often for mutex problems. Doing it anyway.
KnMutex::KnLock lock(gCrashHandlerMutex);
KnLog(KnLogBug,GetFaultReason(pExPtrs));
KnLog(KnLogNotice,GetRegisterString(pExPtrs));
// Want the ns_fatal call to have all the info about minidump so it's easy
// to see in the Monitor Alarms view.
char *miniDumpIntro = "No .dmp generated:";
char *miniDumpExtra = "";
char *miniDumpFile = gDumpLocation.length() ? const_cast<char*>(gDumpLocation.c_str()) : gMiniDumpFile;
if (!gDumpAtAll)
{
miniDumpExtra = "disabled. See Configuration: Memory in the KSA";
}
else if (IsMiniDumpFunctionAvailable())
{
BSUMDRET problems = CreateCurrentProcessMiniDump(
static_cast<MINIDUMP_TYPE>(gMiniDumpType),
miniDumpFile,
GetCurrentThreadId(),
pExPtrs);
switch (problems)
{
case eDUMP_SUCCEEDED:
miniDumpIntro = "See minidump file";
miniDumpExtra = miniDumpFile;
break;
case eDBGHELP_NOT_FOUND:
miniDumpExtra = "dbghelp.dll not found.";
break;
case eDBGHELP_MISSING_EXPORTS:
miniDumpExtra = "dbghelp.dll missing functionality.";
break;
case eBAD_PARAM:
miniDumpExtra = "Bad parameter.";
break;
case eOPEN_DUMP_FAILED:
miniDumpIntro = "Could not open minidump file";
miniDumpExtra = miniDumpFile;
// No guidance from Mr. Robbins on why in this case.
break;
case eMINIDUMPWRITEDUMP_FAILED:
KnLog(KnLogNotice,"Couldn't write dump; GetLastError returns %d",GetLastError());
miniDumpExtra = "Dump writing failed.";
break;
case eDEATH_ERROR:
miniDumpExtra = "Minidump thread could not be started.";
break;
case eINVALID_ERROR:
miniDumpExtra = "Bad bugslayer code";
break;
}
}
else
{
miniDumpExtra = "Minidump facility not available.";
}
KnLog(KnLogNotice,"Stack trace:");
DWORD dwOpts = GSTSO_SRCLINE | GSTSO_SYMBOL | GSTSO_PARAMS;
for (
LPCSTR pStackMessage =GetFirstStackTraceString( dwOpts, pExPtrs ) ;
0 != pStackMessage ;
pStackMessage = GetNextStackTraceString( dwOpts, pExPtrs )
)
{
KnLog(KnLogNotice,pStackMessage);
}
KnMemory::logStatistics( KnLogNotice ) ;
KnLog(KnLogNotice,"knownow: Memory available: %lu",KnMemory::getMemoryAvail());
Ns_Fatal("Exception shuts down server immediately. %s %s", miniDumpIntro, miniDumpExtra);
}
return EXCEPTION_EXECUTE_HANDLER ;
};
// This routine should be called from DLLMAIN, but so far, we're doing
// OK just installing it from nskn. Since we're not limiting ourselves
// to just one dll, it works pretty nicely, without requiring changes in
// AOLServer. If we ever integrate any DLL that sets its own in this way,
// though, we will run into some conflicts, at which point we'll have to
// BE_NICE_TO_OUR_MODULE_WRITERS and get this called from each of our .dlls.
//
// See Robbins; I'm using "Debugging Applications" (the VS 6 - era book)
//
// Separated KnSetCrashHandler from KnSetCrashInfo for two reasons (one
// very minor)
//
// (1) didn't want to change an existing API [very minor, it's only called
// in one place]
//
// (2) want it called *before* we try to do anything across dll boundaries
// from nskn.
//
// OTOH, since it's going to be set before we even _can_ look at the defauls
// from the nskn ns_section, we've set up the defaults here so we do get a
// stack trace dump during that short time. That shouldn't be enough data
// to cause a serious denial of service -- and is a huge win for debugging. /*
bool KnSetCrashHandler()
{
if (! SetCrashHandlerFilter( KnCrashHandler ) ) {
KnLog(KnLogError,"Could not set crash handler.\n");
return false ;
}
#if BE_NICE_TO_OUR_MODULE_WRITERS
/* Get the handle for this DLL or module */
HMODULE hMyModule = GetModuleHandle(NULL) ;
if (! AddCrashHandlerLimitModule( hMyModule )) {
KnLog(KnLogError,"Could not limit crash handling\n");
return false ;
}
#endif
return true;
}
#else
#ifdef __sun
#else
#include "google/coredumper.h"
#endif
namespace {
char* gCoreFile = "log/liveserver.core";
}
bool KnDumpCore()
{
if (gDumpAtAll)
{
const char *coreFile = gDumpLocation.length() ? gDumpLocation.c_str() : gCoreFile;
#ifdef __sun
KnLog(KnLogNotice,"Core dumping currently not supported on Solaris.");
#else
return 0 == WriteCoreDump(coreFile);
#endif
}
return true;
}
bool KnSetCrashHandler()
{
return true;
}
#endif
bool KnSetCrashInfo(const char* dumpLocation, const char* dumpLevel)
{
if (dumpLocation != NULL)
gDumpLocation = dumpLocation;
std::string level(dumpLevel);
if (level[0] == '0')
{
gDumpAtAll = false;
}
#ifdef WIN32
else if (level[0] == '1')
{
gDumpAtAll = true;
gMiniDumpType = MiniDumpNormal; // Just the stacks, ma'am
}
else if (level[0] == '2')
{
gDumpAtAll = true;
gMiniDumpType = MiniDumpWithDataSegs;
}
else if (level[0] == '3')
{
gDumpAtAll = true;
gMiniDumpType = MiniDumpWithFullMemory | MiniDumpWithHandleData ; // Hope this also contains Full Data
}
else
{
KnLog(KnLogNotice,"Unrecognized dump level '%s'; will set to 'just the stacks' level",level.c_str());
gDumpAtAll = true;
gMiniDumpType = MiniDumpNormal;
}
#else
else if (level[0] == '1')
{
gDumpAtAll = true;
}
else
{
KnLog(KnLogNotice,"Unrecognized dump level '%s'; setting dump level to 'true'",level.c_str());
gDumpAtAll = true;
}
#endif
return true;
}
// We wrote our own so we always get crash dumps the way we want 'em
// (and get the assertion in the log)
void KnAssert(char *exp, char *file, unsigned int line)
{
KnLog(KnLogBug,"%s @ %s:%u", exp, file, line);
// I wanted something reasonably portable without working too hard, so here's the best I could do on short
// notice. All attempts do it with kill (and any portable fault) were
// caught by nsd and didn't give core dumps.
char *p = 0;
++p;
strcpy(p,"assertion failed");
}
--- NEW FILE: kncrypt.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/kncrypt.h"
KnError KnCrypt(KnString &buf, const KnString &key, const KnString &salt)
{
char tmp[NS_ENCRYPT_BUFSIZE];
/*
* Copy the raw byte string into a KnString.
*/
buf = Ns_Encrypt(const_cast<char*>(key.c_str()), const_cast<char*>(salt.c_str()), tmp);
return KnErrorSuccess;
}
--- NEW FILE: knerror.cpp ---
/* Copyright 2004-2005 KnowNow, Inc */
/**
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knexportlibraryknutilmodule.h"
#include "knutil/knerror.h"
#include "knutil/knmutex.h"
#include "knutil/knstring.h"
#include "knutil/knlog.h"
#include "knutil/simpletraits.h"
#include "knutil/simplemap.h"
namespace {
class KnErrorSingleton {
public:
static KnErrorSingleton& instance();
const KnString* get(KnError errEnum)
{
unsigned int err = static_cast<unsigned int>(errEnum);
const KnString *val = m_map.get(err);
if (0 == val) {
err &= 0xFF00;
val = m_map.get(err);
}
if (0 == val) {
KnString *unknown = new KnString("Unregistered error ");
unknown->printf("%u",err);
m_map.put(err,unknown);
val = m_map.get(err);
}
if (0 == val) {
Ns_Fatal("knerror: Can't map error %d",err);
}
return val;
}
private:
// Not implemented
KnErrorSingleton(const KnErrorSingleton &);
KnErrorSingleton &operator=(const KnErrorSingleton &);
// Implemented
KnErrorSingleton() ;
SimpleMap<unsigned int,const KnString *> m_map;
static KnErrorSingleton *m_instance ;
static KnMutex m_lock;
};
KnErrorSingleton* KnErrorSingleton::m_instance = 0 ;
KnMutex KnErrorSingleton::m_lock ;
KnErrorSingleton&
KnErrorSingleton::instance( void )
{
if ( 0 == m_instance )
{
KnMutex::KnLock guard(m_lock) ;
if ( 0 == m_instance )
{
m_instance = new KnErrorSingleton ;
if (0 == m_instance)
{
KnLog(KnLogFatal,"knerror: Can't allocate instance");
}
}
}
return *m_instance ;
}
KnErrorSingleton::KnErrorSingleton()
{
m_map.put(KnErrorSuccess, &KnStringConstants::success);
m_map.put(KnErrorUnauthorized, &KnStringConstants::unauthorized);
m_map.put(KnErrorForbidden, &KnStringConstants::forbidden);
m_map.put(KnErrorNotFound, &KnStringConstants::notspacefound);
m_map.put(KnErrorFailure, &KnStringConstants::failed);
/*
m_map.put(KnErrorNotFound_Event, new KnString("event not found"));
m_map.put(KnErrorNotFound_Route, new KnString("route not found"));
m_map.put(KnErrorNotFound_Topic, new KnString("topic not found"));
m_map.put(KnErrorNotFound_Journal, new KnString("journal not found"));
m_map.put(KnErrorNotFound_Parent, new KnString("parent topic not found"));
m_map.put(KnErrorNotFound_SubTopic, new KnString("subtopic not found"));
m_map.put(KnErrorNotFound_User, new KnString("user not found"));
m_map.put(KnErrorNotFound_Group, new KnString("group not found"));
*/
}
} // Namespace
const KnString& KnError_AsString( const KnError err )
{
return *KnErrorSingleton::instance().get(err);
}
const int KnError_AsHTTP(const KnError err) {
return err >> 8;
}
#if 0
KnError KnError_Register(unsigned int err, KnString string)
{
if (err > 65500 || err < 256) {
return KnError_OutOfRange;
}
return KnErrorSingleton::instance().put(err,string);
}
#endif
--- NEW FILE: knhash.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/knhash.h"
#define ind32(value, bits) \
(static_cast<uint32_t>(value) << (bits))
#define ind64(value, bits) \
(static_cast<uint64_t>(value) << (bits))
#define mix32(a, b, c) \
{ \
a -= b; a -= c; a ^= (c >> 13); \
b -= c; b -= a; b ^= (a << 8); \
c -= a; c -= b; c ^= (b >> 13); \
a -= b; a -= c; a ^= (c >> 12); \
b -= c; b -= a; b ^= (a << 16); \
c -= a; c -= b; c ^= (b >> 5); \
a -= b; a -= c; a ^= (c >> 3); \
b -= c; b -= a; b ^= (a << 10); \
c -= a; c -= b; c ^= (b >> 15); \
}
#define mix64(a, b, c) \
{ \
a -= b; a -= c; a ^= (c >> 43); \
b -= c; b -= a; b ^= (a << 9); \
c -= a; c -= b; c ^= (b >> 8); \
a -= b; a -= c; a ^= (c >> 38); \
b -= c; b -= a; b ^= (a << 23); \
c -= a; c -= b; c ^= (b >> 5); \
a -= b; a -= c; a ^= (c >> 35); \
b -= c; b -= a; b ^= (a << 49); \
c -= a; c -= b; c ^= (b >> 11); \
a -= b; a -= c; a ^= (c >> 12); \
b -= c; b -= a; b ^= (a << 18); \
c -= a; c -= b; c ^= (b >> 22); \
}
extern "C"
{
uint32_t hash32(const void *buf, uint32_t len, uint32_t seed)
{
const uint8_t *p = static_cast<const uint8_t *>(buf);
uint32_t a = 0x9E3779B9;
uint32_t b = 0x9E3779B9;
uint32_t c = seed;
uint32_t d = len;
for (; d >= 12; d -= 12, p += 12)
{
a += ind32(p[ 0], 0) + ind32(p[ 1], 8) + ind32(p[ 2], 16) + ind32(p[ 3], 24);
b += ind32(p[ 4], 0) + ind32(p[ 5], 8) + ind32(p[ 6], 16) + ind32(p[ 7], 24);
c += ind32(p[ 8], 0) + ind32(p[ 9], 8) + ind32(p[10], 16) + ind32(p[11], 24);
mix32(a, b, c);
}
c += len;
/*
* Duff's Device lives! The first byte of c is reserved for the length.
*/
switch (d)
{
case 11: c += ind32(p[10], 24);
case 10: c += ind32(p[ 9], 16);
case 9: c += ind32(p[ 8], 8);
case 8: b += ind32(p[ 7], 24);
case 7: b += ind32(p[ 6], 16);
case 6: b += ind32(p[ 5], 8);
case 5: b += ind32(p[ 4], 0);
case 4: a += ind32(p[ 3], 24);
case 3: a += ind32(p[ 2], 16);
case 2: a += ind32(p[ 1], 8);
case 1: a += ind32(p[ 0], 0);
}
mix32(a, b, c);
return c;
}
uint64_t hash64(const void *buf, uint64_t len, uint64_t seed)
{
const uint8_t *p = static_cast<const uint8_t *>(buf);
uint64_t a = seed;
uint64_t b = seed;
uint64_t c = 0x9E3779B97F4A7C13ULL;
uint64_t d = len;
for (; d >= 24; d -= 24, p += 24)
{
a += ind64(p[ 0], 0) + ind64(p[ 1], 8) + ind64(p[ 2], 16) + ind64(p[ 3], 24) +
ind64(p[ 4], 32) + ind64(p[ 5], 40) + ind64(p[ 6], 48) + ind64(p[ 7], 56);
b += ind64(p[ 8], 0) + ind64(p[ 9], 8) + ind64(p[10], 16) + ind64(p[11], 24) +
ind64(p[12], 32) + ind64(p[13], 40) + ind64(p[14], 48) + ind64(p[15], 56);
c += ind64(p[16], 0) + ind64(p[17], 8) + ind64(p[18], 16) + ind64(p[19], 24) +
ind64(p[20], 32) + ind64(p[21], 40) + ind64(p[22], 48) + ind64(p[23], 56);
mix64(a, b, c);
}
c += len;
/*
* Duff's Device lives! The first byte of c is reserved for the length.
*/
switch (d)
{
case 23: c += ind64(p[22], 56);
case 22: c += ind64(p[21], 48);
case 21: c += ind64(p[20], 40);
case 20: c += ind64(p[19], 32);
case 19: c += ind64(p[18], 24);
case 18: c += ind64(p[17], 16);
case 17: c += ind64(p[16], 8);
case 16: b += ind64(p[15], 56);
case 15: b += ind64(p[14], 48);
case 14: b += ind64(p[13], 40);
case 13: b += ind64(p[12], 32);
case 12: b += ind64(p[11], 24);
case 11: b += ind64(p[10], 16);
case 10: b += ind64(p[ 9], 8);
case 9: b += ind64(p[ 8], 0);
case 8: a += ind64(p[ 7], 56);
case 7: a += ind64(p[ 6], 48);
case 6: a += ind64(p[ 5], 40);
case 5: a += ind64(p[ 4], 32);
case 4: a += ind64(p[ 3], 24);
case 3: a += ind64(p[ 2], 16);
case 2: a += ind64(p[ 1], 8);
case 1: a += ind64(p[ 0], 0);
}
mix64(a, b, c);
return c;
}
} // extern "C"
--- NEW FILE: knlog.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/kntypes.h"
#include "knutil/knlog.h"
#include <cstdarg>
KnError KnLog(KnLogSeverity severity, const KnString &msg)
{
/*
* Reuse existing interface.
*/
return KnLog(severity, "%.*s", (int)msg.length(), msg.c_str());
}
KnError KnLog(KnLogSeverity severity, const char *fmt, ...)
{
va_list ap;
/*
* Grab the arguments off the stack.
*/
va_start(ap, fmt);
/*
* Note the C-style cast for converting between unrelated enums.
*/
Ns_ServerLog(Ns_LogSeverity(severity), const_cast<char*>(fmt), &ap);
/*
* Restore the stack.
*/
va_end(ap);
return KnErrorSuccess;
}
--- NEW FILE: knmarkuputil.cpp ---
//////////////////////////////////////////////////////
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
// knmarkuputil.cpp
//
// (c)2002-2006, KnowNow, Inc. All Rights Reserved.
#include "knexportlibraryknutilmodule.h"
#include "knutil/knmarkuputil.h"
#include "knutil/knlog.h"
template class EXPORT_LIBRARY_KNUTIL SimpleMap<char*, char*>;
KnMarkupUtil::KnMarkupUtil()
{
buildHtlmEntity();
buildHtlmTags();
}
KnMarkupUtil::~KnMarkupUtil()
{
}
void KnMarkupUtil::buildHtlmEntity()
{
// http://www.w3schools.com/tags/ref_entities.asp
// http://www.utoronto.ca/webdocs/HTMLdocs/NewHTML/iso_table.html
entityMap.put("#32"," ");
entityMap.put("#0x0020"," ");
entityMap.put("nbsp"," ");
entityMap.put("#33","!");
entityMap.put("#0x0021","!");
entityMap.put("#34","\"");
entityMap.put("#0x0022","\"");
entityMap.put("quot","\"");
entityMap.put("#35","#");
entityMap.put("#0x0023","#");
entityMap.put("#36","$");
entityMap.put("#0x0024","$");
entityMap.put("#37","%");
entityMap.put("#0x0025","%");
entityMap.put("#38","&");
entityMap.put("#0x0026","&");
entityMap.put("amp","&");
entityMap.put("#39","'");
entityMap.put("#0x0027","'");
entityMap.put("#40","(");
entityMap.put("#0x0028","(");
entityMap.put("#41",")");
entityMap.put("#0x0029",")");
entityMap.put("#42","*");
entityMap.put("#0x002a","*");
entityMap.put("#43","+");
entityMap.put("#0x002b","+");
entityMap.put("#44",",");
entityMap.put("#0x002c",",");
entityMap.put("#45","-");
entityMap.put("#0x002d","-");
entityMap.put("#46",".");
entityMap.put("#0x002e",".");
entityMap.put("#47","/");
entityMap.put("#0x002f","/");
entityMap.put("#48","0");
entityMap.put("#0x0030","0");
entityMap.put("#49","1");
entityMap.put("#0x0031","1");
entityMap.put("#50","2");
entityMap.put("#0x0032","2");
entityMap.put("#51","3");
entityMap.put("#0x0033","3");
entityMap.put("#52","4");
entityMap.put("#0x0034","4");
entityMap.put("#53","5");
entityMap.put("#0x0035","5");
entityMap.put("#54","6");
entityMap.put("#0x0036","6");
entityMap.put("#55","7");
entityMap.put("#0x0037","7");
entityMap.put("#56","8");
entityMap.put("#0x0038","8");
entityMap.put("#57","9");
entityMap.put("#0x0039","9");
entityMap.put("#58",":");
entityMap.put("#0x003a",":");
entityMap.put("#59",";");
entityMap.put("#0x003b",";");
entityMap.put("#60","<");
entityMap.put("#0x003c","<");
entityMap.put("lt","<");
entityMap.put("#61","=");
entityMap.put("#0x003d","=");
entityMap.put("#62",">");
entityMap.put("#0x003e",">");
entityMap.put("gt",">");
entityMap.put("#63","?");
entityMap.put("#0x003f","?");
entityMap.put("#64","@");
entityMap.put("#0x0040","@");
entityMap.put("#65","A");
entityMap.put("#0x0041","A");
entityMap.put("#66","B");
entityMap.put("#0x0042","B");
entityMap.put("#67","C");
entityMap.put("#0x0043","C");
entityMap.put("#68","D");
entityMap.put("#0x0044","D");
entityMap.put("#69","E");
entityMap.put("#0x0045","E");
entityMap.put("#70","F");
entityMap.put("#0x0046","F");
entityMap.put("#71","G");
entityMap.put("#0x0047","G");
entityMap.put("#72","H");
entityMap.put("#0x0048","H");
entityMap.put("#73","I");
entityMap.put("#0x0049","I");
entityMap.put("#74","J");
entityMap.put("#0x004a","J");
entityMap.put("#75","K");
entityMap.put("#0x004b","K");
entityMap.put("#76","L");
entityMap.put("#0x004c","L");
entityMap.put("#77","M");
entityMap.put("#0x004d","M");
entityMap.put("#78","N");
entityMap.put("#0x004e","N");
entityMap.put("#79","O");
entityMap.put("#0x004f","O");
entityMap.put("#80","P");
entityMap.put("#0x0050","P");
entityMap.put("#81","Q");
entityMap.put("#0x0051","Q");
entityMap.put("#82","R");
entityMap.put("#0x0052","R");
entityMap.put("#83","S");
entityMap.put("#0x0053","S");
entityMap.put("#84","T");
entityMap.put("#0x0054","T");
entityMap.put("#85","U");
entityMap.put("#0x0055","U");
entityMap.put("#86","V");
entityMap.put("#0x0056","V");
entityMap.put("#87","W");
entityMap.put("#0x0057","W");
entityMap.put("#88","X");
entityMap.put("#0x0058","X");
entityMap.put("#89","Y");
entityMap.put("#0x0059","Y");
entityMap.put("#90","Z");
entityMap.put("#0x005a","Z");
entityMap.put("#91","[");
entityMap.put("#0x005b","[");
entityMap.put("#92","\\");
entityMap.put("#0x005c","\\");
entityMap.put("#93","]");
entityMap.put("#0x005d","]");
entityMap.put("#94","^");
entityMap.put("#0x005e","^");
entityMap.put("#95","_");
entityMap.put("#0x005e","_");
entityMap.put("#96","`");
entityMap.put("#0x0060","`");
entityMap.put("#97","a");
entityMap.put("#0x0061","a");
entityMap.put("#98","b");
entityMap.put("#0x0062","b");
entityMap.put("#99","c");
entityMap.put("#0x0063","c");
entityMap.put("#100","d");
entityMap.put("#0x0064","d");
entityMap.put("#101","e");
entityMap.put("#0x0065","e");
entityMap.put("#102","f");
entityMap.put("#0x0066","f");
entityMap.put("#103","g");
entityMap.put("#0x0067","g");
entityMap.put("#104","h");
entityMap.put("#0x0068","h");
entityMap.put("#105","i");
entityMap.put("#0x0069","i");
entityMap.put("#106","j");
entityMap.put("#0x006a","j");
entityMap.put("#107","k");
entityMap.put("#0x006b","k");
entityMap.put("#108","l");
entityMap.put("#0x006c","l");
entityMap.put("#109","m");
entityMap.put("#0x006d","m");
entityMap.put("#110","n");
entityMap.put("#0x006e","n");
entityMap.put("#111","o");
entityMap.put("#0x006f","o");
entityMap.put("#112","p");
entityMap.put("#0x0070","p");
entityMap.put("#113","q");
entityMap.put("#0x0071","q");
entityMap.put("#114","r");
entityMap.put("#0x0072","r");
entityMap.put("#115","s");
entityMap.put("#0x0073","s");
entityMap.put("#116","t");
entityMap.put("#0x0074","t");
entityMap.put("#117","u");
entityMap.put("#0x0075","u");
entityMap.put("#118","v");
entityMap.put("#0x0076","v");
entityMap.put("#119","w");
entityMap.put("#0x0077","w");
entityMap.put("#120","x");
entityMap.put("#0x0078","x");
entityMap.put("#121","y");
entityMap.put("#0x0079","y");
entityMap.put("#122","z");
entityMap.put("#0x007a","z");
entityMap.put("#123","{");
entityMap.put("#0x007b","{");
entityMap.put("#124","|");
entityMap.put("#0x007c","|");
entityMap.put("#125","}");
entityMap.put("#0x007d","}");
entityMap.put("#126","~");
entityMap.put("#0x007e","~");
// ---------------------------- END ASCII -----------------------------
entityMap.put("#160"," ");
entityMap.put("#0x00a0"," ");
entityMap.put("nbsp"," ");
entityMap.put("#162","¢");
entityMap.put("#0x00a2","¢");
entityMap.put("cent","¢");
entityMap.put("#163","£");
entityMap.put("#0x00a3","£");
entityMap.put("pound","£");
entityMap.put("#164","¤");
entityMap.put("#0x00a4","¤");
entityMap.put("curren","¤");
entityMap.put("#165","Â¥");
entityMap.put("#0x00a5","Â¥");
entityMap.put("yen","Â¥");
entityMap.put("#171","«");
entityMap.put("#0x00ab","«");
entityMap.put("laquo","«");
entityMap.put("#187","»");
entityMap.put("#0x00bb","»");
entityMap.put("raquo","»");
}
void KnMarkupUtil::buildHtlmTags()
{
tagMap.put("br","\n"); // HTML <br>
tagMap.put("br/","\n"); // XHTML <br/>
}
void KnMarkupUtil::stripTags(const KnString& source, KnString& result)
{
std::string temp;
for (const char * p = source.c_str(); *p != 0; p++)
{
if (*p == '<')
for (p++; *p != 0 && *p != '>'; p++);
else
temp.append(p,1);
}
result = temp.c_str();
}
bool KnMarkupUtil::expandTagsAndStrip(const KnString& source, KnString& result)
{
std::string temp;
bool ok = true;
for (const char * p = source.c_str(); *p != 0; p++)
{
if (*p == '<')
{
std::string key;
for (p++; *p != 0 && *p != '>'; p++)
{
key.append(p,1);
}
if(*p == '>') // we found a valid entity, search it in the tag map
{
char* value = tagMap.get((char *const)key.c_str());
if(value != NULL)
{
temp.append(value, 1);
}
else
{
// just ignore it, strip it
/*
ok = false;
temp->append("<",1);
temp->append(key.c_str(), key.length());
temp->append(">",1);
//KnLog(KnLogDev,"Tag:'%s' not recognized, it will not expand", key.c_str());
*/
}
}
else // we got \0
{
if (key.length() > 0)
{
temp.append("<",1);
temp.append(key.c_str(), key.length());
KnLog(KnLogDebug,"Tag:'%s' not '>' terminated, it will not expand", key.c_str());
}
result = temp.c_str();
return false;
}
}
else
{
temp.append(p,1);
}
}
result = temp.c_str();
return ok;
}
bool KnMarkupUtil::expandHtmlEntities(const KnString& source, KnString& result)
{
std::string temp;
bool ok = true;
for (const char * p = source.c_str(); *p != 0; p++)
{
if (*p == '&')
{
std::string key;
for (p++; *p != 0 && *p != ';'; p++)
{
key.append(p,1);
}
if(*p == ';') // we found a valid entity, search it in the entity map
{
toLowerCase(key);
char* value = entityMap.get((char *const)key.c_str());
if(value != NULL)
{
temp.append(value, 1);
}
else
{
ok = false;
temp.append("&",1);
temp.append(key.c_str(), key.length());
temp.append(";",1);
KnLog(KnLogDebug,"Entity:'%s' not recognized, it will not expand", key.c_str());
}
}
else // we got \0
{
if(key.length() > 0)
{
temp.append("&",1);
temp.append(key.c_str(), key.length());
KnLog(KnLogDebug,"Entity:'%s' not ';' terminated, it will not expand", key.c_str());
}
result = temp.c_str();
return false;
}
}
else
{
temp.append(p,1);
}
}
result = temp.c_str();
return ok;
}
bool KnMarkupUtil::expandTagsAndEntities(const KnString& source, KnString& result)
{
bool ok1, ok2;
KnString temp;
ok1 = expandTagsAndStrip(source, temp);
ok2 = expandHtmlEntities(temp, result);
return ok1 && ok2;
}
bool KnMarkupUtil::stripTagsAndExpandEntities(const KnString& source, KnString& result)
{
bool ok;
KnString temp;
stripTags(source, temp);
ok = expandHtmlEntities(temp, result);
return ok;
}
/*
bool KnMarkupUtil::renderXmlHtml(const KnString* source, KnString* result)
{
XALAN_USING_STD(istrstream)
XALAN_USING_STD(ostrstream)
int theResult = -1;
KnString theXSLTSource("<?xml version=\"1.0\" encoding=\"utf-8\"?> \
<!DOCTYPE xsl:stylesheet [<!ENTITY nbsp \" \">]> \
<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\"> \
<xsl:output method=\"text\"> \
</xsl:output> \
</xsl:stylesheet>");
KnString theXMLSource("<strip>");
theXMLSource.append(source->c_str(), source->length());
theXMLSource.append("</strip>", 8);
XalanTransformer theXalanTransformer;
istrstream theXMLStream(theXMLSource.c_str(), theXMLSource.length());
istrstream theXSLStream(theXSLTSource.c_str(), theXSLTSource.length());
ostrstream theOutputStream;
XALAN_USING_XALAN(XSLTInputSource)
XSLTInputSource xmlSource(theXMLStream);
XSLTInputSource xslSource(theXSLStream);
// Do the transform.
theResult = theXalanTransformer.transform(xmlSource, xslSource, theOutputStream);
if (theResult != 0)
{
KnLog(KnLogError, "StreamTransform Error: %s\n" ,theXalanTransformer.getLastError());
return false;
}
else
{
KnString strout(theOutputStream.str(), theOutputStream.pcount());
KnString* temp = &strout;
stripTags(temp, result);
return true;
}
}
*/
--- NEW FILE: knmemory.cpp ---
/*
/**
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
* Copyright (c) 2003 KnowNow, Inc., All Rights Reserved
*/
#include "knexportlibraryknutilmodule.h"
#include "knutil/kntypes.h"
#include "knutil/atomicint.h"
#include "knutil/knmemory.h"
#ifdef WIN32
#include <winbase.h>
#else
#include <sys/resource.h>
#include <unistd.h>
#endif
/**
* File containing C++ wrappers for the AOLServer allocation & free
* routines.
*
* Also has a very elementary leak detector: it counts allocations
* rather than using a boolean to determine whether to print its trace
* message. Because it should be atomic, this
* will slow down the memory allocation time of the router. Let's
* hope that's not where our performance bottlenecks are.
*
* If it is, we'll wrap this in some preprocessor magic.
*/
EXPORT_LIBRARY_KNUTIL AtomicInt nAllocs = 0;
void KnMemory::increment() { ++nAllocs; }
void KnMemory::decrement() { --nAllocs; }
size_t
KnMemory::getBalance()
{
return nAllocs ;
}
void
KnMemory::logStatistics( KnLogSeverity severity )
{
KnLog(severity,"Allocations that were not deleted: %lu",
static_cast<unsigned long>(nAllocs));
}
size_t
KnMemory::getMemoryAvail()
{
size_t nAvail = 1234567890; // Suspicious value
#ifdef WIN32
// Assumes all of the system is available to us.
MEMORYSTATUS status ;
GlobalMemoryStatus (&status);
nAvail = status.dwAvailVirtual;
#elif defined(__linux)
// Two approaches could be used. We could try the whole system
// approach like Windows (querying /proc/meminfo), but because the
// Linux 2.6 vm system keeps the vmsize as close to the physical
// resources as possible, this doesn't work [shows the memory as always
// over-used].
//
// Instead, we need to use /proc/<pid>/stat and try to work with what
// it returns.
static bool bProcStatWorks = true ;
pid_t pid = getpid();
KnString statFile;
statFile.printf("/proc/%d/stat",pid);
if (bProcStatWorks)
{
FILE *pFile = fopen(statFile.c_str(),"r");
if (!pFile)
{
KnLog(KnLogNotice,"KnMemory: Can't determine memory available: Can't read /proc/meminfo");
bProcStatWorks = false ;
}
else
{
char procStatBuf[ BUFSIZ ] ;
if (NULL != fgets(procStatBuf,BUFSIZ,pFile))
{
unsigned long vsize ;
unsigned long rlim ;
int fields =
sscanf(
procStatBuf,
"%*d "
"(%*[^)]) " // Spaces are allowed in commands
"%*c "
"%*d "
"%*d "
"%*d "
"%*d "
"%*d "
"%*u " // "%*lu "
"%*u " // "%*lu "
"%*u " // "%*lu "
"%*u " // "%*lu "
"%*u " // "%*lu "
"%*u " // "%*lu "
"%*u " // "%*lu "
"%*d " // "%*ld "
"%*d " // "%*ld "
"%*d " // "%*ld "
"%*d " // "%*ld "
"%*d " // "%*ld "
"%*d " // "%*ld "
"%*u " // "%*lu "
"%lu "
"%*d " // "%*ld "
"%lu "
"%*u " // "%*lu "
"%*u " // "%*lu "
"%*u " // "%*lu "
"%*u " // "%*lu "
"%*u " // "%*lu "
"%*u " // "%*lu "
"%*u " // "%*lu "
"%*u " // "%*lu "
"%*u " // "%*lu "
"%*u " // "%*lu "
"%*u " // "%*lu "
"%*u " // "%*lu "
"%*d "
"%*d"
// ,pid
// ,comm
// ,state
// ,ppid
// ,pgrp
// ,session
// ,tty_nr
// ,tpgid
// ,flags
// ,minflt
// ,cminflt
// ,majflt
// ,cmajflt
// ,utime
// ,stime
// ,cutime
// ,cstime
// ,priority
// ,nice
// ,0
// ,itrealvalue
// ,starttime
,&vsize
// ,rss
,&rlim
// ,startcode
// ,endcode
// ,startstack
// ,kstkesp
// ,kstkeip
// ,signal
// ,blocked
// ,sigignore
// ,sigcatch
// ,wchan
// ,nswap
// ,cnswap
// ,exit_signal
// ,processor
);
if (2 == fields)
{
// I just don't believe that we really work above the signed/unsigned
// line yet, so I'm going forcing the rlim down to that limit
// artificially. If it turns out we give specious or ugly
// warnings and run just fine at 3G of a 32-bit space, remove
// this if.
if (rlim > (ULONG_MAX / 2))
{
rlim = ULONG_MAX / 2;
}
nAvail = rlim - vsize;
}
else
{
bProcStatWorks = false;
KnLog(KnLogNotice,"KnMemory: Can't determine memory available: sscanf of line from '%s' returned %d fields from '%s'",
statFile.c_str(),
fields,
procStatBuf
);
}
}
else
{
bProcStatWorks = false;
KnLog(KnLogNotice,"KnMemory: Can't determine memory available: no information in '%s'",statFile.c_str());
}
fclose(pFile);
}
}
#else
// It'd be nice if getrusage actually returned something
// comparable to RLIMIT_DATA. It doesn't; I though ru_idrss might
// work, but (a) no OS we support sets that correctly anyway, and
// (b) it's measured in pages, not bytes, so things get too complicated
// too fast. Let's hope the old assumption that heap is "last" in
// address space works.
struct rlimit rlim ;
if (0 == getrlimit( RLIMIT_DATA, &rlim ) )
{
if (rlim.rlim_cur == RLIM_INFINITY)
{
static bool bWarned = false ;
if (!bWarned)
{
KnLog(KnLogNotice,"getMemoryAvail: RLIMIT_DATA is infinity?!");
bWarned = true ;
}
}
// c_cast<unsigned long>(void*)
nAvail = rlim.rlim_cur - (rlim_t)(sbrk(0));
}
#endif
return nAvail ;
}
--- NEW FILE: knmutex.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/knmutex.h"
/*
* Need C cast to convert from void ** to Ns_Mutex *.
*/
KnMutex::KnMutex() :
m_impl(0)
{
Ns_MutexInit((Ns_Mutex *)&m_impl);
}
KnMutex::~KnMutex()
{
Ns_MutexDestroy((Ns_Mutex *)&m_impl);
}
bool KnMutex::lock()
{
Ns_MutexLock((Ns_Mutex *)&m_impl);
return true;
}
bool KnMutex::trylock()
{
return Ns_MutexTryLock((Ns_Mutex *)&m_impl) == NS_OK;
}
bool KnMutex::unlock()
{
Ns_MutexUnlock((Ns_Mutex *)&m_impl);
return true;
}
KnMutex::KnLock::KnLock(KnMutex &mutex, bool blocking) :
m_mutex(mutex),
m_owner(blocking ? m_mutex.lock() : m_mutex.trylock())
{
}
KnMutex::KnLock::~KnLock()
{
if (m_owner)
{
m_mutex.unlock();
}
}
bool KnMutex::KnLock::owner() const
{
return m_owner;
}
--- NEW FILE: knnetwork.cpp ---
/* Copyright 2008 KnowNow, Inc */
/**
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
[...1505 lines suppressed...]
KnError
KnNetwork::formatTopicLocation(
KnString& location,
const KnString& urlSpace
) {
return KnNetworkImpl::instance()->formatTopicLocation(location,urlSpace);
}
KnError
KnNetwork::formatNonNetworkRelativeRef(KnString& location,size_t inputPfxLen)
{
return KnNetworkImpl::instance()->formatNonNetworkRelativeRef(location,inputPfxLen);
}
KnError
KnNetwork::formatTopicRelativeRef(KnString& location,size_t inputPfxLen)
{
return KnNetworkImpl::instance()->formatTopicRelativeRef(location,inputPfxLen);
}
--- NEW FILE: knnewdelete.cpp ---
#define KNMODULE knutil
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knexportlibraryknutilmodule.h"
#include "knutil/knnewdelete.h"
--- NEW FILE: knpattern.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/knpattern.h"
// ---------------------------------------------------------
// KnMatcher
// ---------------------------------------------------------
KnMatcher::KnMatcher(const char* string)
{
m_obj = Tcl_NewStringObj((char*)string, strlen(string));
}
KnMatcher::~KnMatcher()
{
Tcl_DecrRefCount(m_obj);
}
Tcl_Obj* KnMatcher::getTclObj()
{
return m_obj;
}
// ---------------------------------------------------------
// KnPattern
// ---------------------------------------------------------
KnPattern::KnPattern()
{
}
KnPattern::KnPattern( const KnString& name )
{
m_name = name;
}
bool
KnPattern::setRegexPattern(const KnString& pattern)
{
return setRegexPattern(pattern.c_str());
}
bool
KnPattern::setRegexPattern(const char* pattern)
{
m_regexPattern = pattern;
return m_regex.setRegexPattern(pattern);
}
const KnString&
KnPattern::getName() const
{
return m_name;
}
const KnString&
KnPattern::getRegexPattern() const
{
return m_regexPattern;
}
const KnRegEx&
KnPattern::getRegexMatcher() const
{
return m_regex;
}
bool
KnPattern::isValidRegex() const
{
return m_regex.isValid();
}
bool
KnPattern::match(const char* string) const
{
return m_regex.match(string);
}
bool
KnPattern::match(KnMatcher& matchObj) const
{
return m_regex.match(matchObj.getTclObj());
}
int KnPattern::getSubCount() const
{
return 1; //TODO determinte the number of subexpressions
}
bool KnPattern::matchSubs(const char *str, KnSet& result)
{
bool ok = m_regex.matchSubs(str);
if(!ok)
return false;
char* mstart;
int mlen;
KnString name;
std::string matched;
int subsCount = getSubCount();
for(int i = 0; i < subsCount; i++)
{
m_regex.getSubRange(0, mstart, mlen);
matched = "";
if(mlen)
matched.append(mstart,mlen);
name.printf("%d",i);
result.put(name, matched.c_str());
}
return true;
}
// ---------------------------------------------------------
// KnUrlPattern
// ---------------------------------------------------------
namespace {
const KnString AnyPart("[^ ]*");
const KnString AnyUrlComponent("[^/ ]*");
const KnString AndSubTree("(?:/[^ ]*){0,1}");
const KnString Anywhere("(?:(?:/[^ ]*/)|/)");
const KnString SubTreeOnly("/[^ ]*");
static const char hexdigits[22] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A',
'B', 'C', 'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f'
};
};
KnUrlPattern::KnUrlPattern( const KnString& name)
: KnPattern(name),
m_filecards(0),
m_pathcards(0),
m_leaders(0),
m_trailers(0),
m_subtopics(0)
{
}
KnUrlPattern::KnUrlPattern( const KnString& name, const KnString& urlPattern)
: KnPattern(name),
m_filecards(0),
m_pathcards(0),
m_leaders(0),
m_trailers(0),
m_subtopics(0)
{
makePathRegex(m_regexPattern,urlPattern);
setRegexPattern(m_regexPattern.c_str());
}
// Regex building routines
// -----------------------
// We convert DOS glob expressions (on non-URL parts) and WS-Topics
// FullTopicExpressions (sans alternation) into a regular expression.
// Escape characters that aren't part of the regexes we're building
bool
KnUrlPattern::escape(KnString& putItHere, char aChar)
{
switch(aChar)
{
case '\\':
case '*':
case '{':
case '}':
case '[':
case ']':
case '(':
case ')':
case '.':
case '^':
case '$':
case '?':
putItHere += "\\";
break;
default:
break;
}
putItHere.append(&aChar,1);
return true;
}
// For parts of the pattern that can't take '**', treat it as a pair of '*'
// (which is identical to one *)
//
// Doesn't count against leaders & trailers.
bool
KnUrlPattern::makeNoPathRegex(KnString& putItHere, const KnString& part)
{
bool bOK = true ;
bool bEscape = false ;
int ix = 0;
for (; ix < (int)part.length() ; ++ix)
{
if (part[ix] == '\\')
{
bEscape = true ;
continue ;
}
else if (!bEscape && part[ix] == '*')
{
putItHere += AnyPart;
m_filecards++;
}
else
{
bOK = escape(putItHere,part[ix]); // Not sure this is right
}
bEscape = false ;
}
if (ix == 0)
{
putItHere += AnyPart ;
m_filecards++;
}
#if 0
//KNPDBG(KnLog(KnLogDev,"knperm: Escaping %s is %s",part.c_str(),putItHere.c_str()));
#endif
return true ;
}
// Pattern taken from WS-Topics, Draft 1.2 of 6/17/2004, lines 446 - 477
// Only oddity is '//.'; that document specifies that this at the end of
// a string means AndSubtree. So we go out of our way to delete the . if
// it's at the end of the string.
// Getting the counts right takes an embedded state machine. Idea is:
// foreach component
// if (no wildcards yet) { ++leaders, ++trailers }
// else if (component has a wildcard) {++<wildcardtype>, trailers = 0}
// else { ++trailers }
// we recognize a component when we reach a '/' other than at the
// beginning of the parse, and at the end of the parse, presuming
// the last thing was not a /.
bool
KnUrlPattern::makePathRegex(KnString& putItHere, const KnString& part)
{
enum { normal, oneslash, twoslash, withdot } state = normal ;
bool bOK = true ;
KnUrlPattern::ComponentCounter counter(*this) ;
int ix = 0 ;
bool subTopicCounted = false;
for (; ix < (int)part.length() ; ++ix)
{
switch (state)
{
default: // Just to make compilers happy
case normal:
switch(part[ix])
{
case '/':
if (ix != 0)
{
// ends a component, so count it.
counter.countComponent() ;
}
state = oneslash;
subTopicCounted = false;
break ;
case '*':
counter.countFileCard() ;
putItHere += AnyUrlComponent ;
break ;
default:
if (!subTopicCounted)
{
counter.countSubTopics();
subTopicCounted = true;
}
bOK = escape(putItHere,part[ix]);
}
break ;
case oneslash:
switch(part[ix])
{
case '/':
// It's definitely a wildcard, now to recognize it.
counter.countPathCard() ;
state = twoslash;
break;
case '*':
// This is the case foo/*..., so we're in a component
// and want a filecard. And we're back in normal state.
//
// So what happens with foo/*//. ? Should mean there
// must be one complete component; I think it does.
counter.countFileCard() ;
putItHere += "/";
putItHere += AnyUrlComponent ;
state = normal ;
break ;
default:
putItHere += "/" ;
bOK = escape(putItHere,part[ix]);
state = normal ;
break ;
}
break ;
case twoslash:
switch(part[ix])
{
case '.':
putItHere += AndSubTree ;
state = withdot;
break;
case '*':
putItHere += SubTreeOnly ;
counter.countComponent() ;
state = normal ;
break ;
default:
putItHere += Anywhere;
bOK = escape(putItHere,part[ix]);
counter.countComponent() ;
state = normal ;
break ;
}
break ;
case withdot:
// Well, we're still in the string, so include the .
putItHere += ".";
bOK = escape(putItHere,part[ix]);
// Didn't know whether to count even the first slash till now.
counter.countComponent() ;
state = normal;
break ;
}
}
switch (state)
{
case withdot: // Trash the dot; we already output the pattern.
case normal:
counter.countComponent();
break;
case oneslash:
putItHere += "/";
break;
case twoslash:
putItHere += AndSubTree ;
break ;
}
if (ix == 0)
{
putItHere += AndSubTree ;
counter.countPathCard() ;
}
#if 0
//KNPDBG(KnLog(KnLogDev,"knperm: Escaping %s is %s",part.c_str(),putItHere.c_str()));
#endif
return true;
}
// 1. Number of subtopics (more first)
// 2. Number of wildcards (fewest first)
// 3. Number of file wildcards vs. path wildcards (files beat paths)
// 4. Number of trailing non-path-wildcard components (more are less)
// 5. Number of leading non-path-wildcard components (more are less)
// 6. Alphabetic order of the pattern name.
// 7. Alphabetic order of the components (the regex)
bool
KnUrlPattern::lessSpecificThan(const KnUrlPattern& rhs) const
{
//KnLog(KnLogDev,"knperm: Comparing %s to %s (%s <? %s)",m_name.c_str(), rhs.m_name.c_str(), m_regexPattern.c_str(), rhs.m_regexPattern.c_str());
// more are less
int less = (rhs.m_subtopics - m_subtopics);
if (less == 0) {
less = (m_filecards + m_pathcards)-(rhs.m_filecards + rhs.m_pathcards); // fewer are less
if (less == 0)
{
less = rhs.m_filecards - m_filecards; // more are less
if (less == 0)
{
less = rhs.m_trailers - m_trailers; // more are less
if (less == 0)
{
less = rhs.m_leaders - m_leaders; // more are less
if (less == 0)
{
if (m_name == rhs.m_name)
{
less = (m_regexPattern < rhs.m_regexPattern) ? -1 : +1;
//KNPDBG(KnLog(KnLogDev,"knperm: Regex alphabetic decided %d", less));
}
else
{
less = (m_name < rhs.m_name) ? -1 : +1;
//KNPDBG(KnLog(KnLogDev,"knperm: Pattern name alphabetics decided %d", less));
}
}
else
{
//KNPDBG(KnLog(KnLogDev,"knperm: Comparison on leaders decided %d",less));
}
}
else
{
//KNPDBG(KnLog(KnLogDev,"knperm: Comparison on trailers decided %d",less));
}
}
else
{
///KNPDBG(KnLog(KnLogDev,"knperm: Comparison on filecards decided %d",less));
}
}
else
{
//KNPDBG(KnLog(KnLogDev,"knperm: Comparison on total wildcards decided %d",less));
}
}
else
{
//KNPDBG(KnLog(KnLogDev,"knperm: Comparison on total subtopics decided %d",less));
}
return less < 0;
}
--- NEW FILE: knperfstatistics.cpp ---
/* Copyright 2004 KnowNow, Inc., Sunnyvale CA 94089 USA. All Rights Reserved */
/**
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knexportlibraryknutilmodule.h"
#include "knutil/knset.h"
#include "knutil/knstring.h"
#include "knutil/knperfstatistics.h"
// ---------------------------------------------------------------
// KnPerfStatistic
// ---------------------------------------------------------------
KnPerfStatistic *KnPerfStatistic::allStatistics_[ALLSTATISTICSMAX];
int KnPerfStatistic::allStatisticsCount_ = 0;
KnString KnPerfStatistic::systemName_;
namespace {
// This rolls over every 24.27 259 259 ... days; it's the largest
// decimal milliseconds that will work nicely in 2^31 bits. We tried
// just relying on roll-over truncating and staying positive, but that
// didn't work. I suspect the better idea is to do everything unsigned,
// but this solution stays inside the file (no change in API). We should change the
// clients to use perftimer instead of long in case we need to move to, e.g.,
// usec timings.
inline perftimer perftimerNormalize( perftimer secs, perftimer msecs ) {
return ( (secs & 0x001FFFFF) * 1000) + msecs ;
};
inline perftimer perftimerNow() {
perftimer perftime;
#ifdef WIN32
_timeb timerData;
_ftime(&timerData);
perftime = perftimerNormalize(timerData.time,timerData.millitm);
#else
struct timeval timerData;
gettimeofday(&timerData,0);
timerData.tv_usec /= 1000; // milliseconds
perftime = perftimerNormalize(timerData.tv_sec,timerData.tv_usec);
#endif
return perftime;
}
inline perftimer perftimerRoll(perftimer value) {
// Presume our compilers are bright enough to figure this out
if (value < 0)
{
value += 0x7FFFFFFF;
++value;
}
return value;
}
}
int KnPerfStatistic::gCategoryPerformance = 60000;
int KnPerfStatistic::gCategoryPerformanceInSeconds = 1200000;
bool KnPerfStatistic::gLogAssertPerformanceOutOfScale = false;
bool KnPerfStatistic::gStatisticsRoll = false;
int KnPerfStatistic::gMaxBackupStatistics = 100;
int KnPerfStatistic::gClampStatisticsRoll = (1000 * 60 * 60 * 24 * 7); // 1 week
KnPerfStatistic::KnPerfStatistic(const KnString & name,
const KnString& displayName,
KnCategoryType cat) :
knId_(name),
displayName_(displayName),
value_(0),
category_(cat),
total_(0L),
min_(LONG_MAX),
max_(0L),
unit_(0),
histogram_(0),
start_(0L)
{
switch(cat)
{
case KnCategory_Performance:
histogram_ = new Histogram(0, gCategoryPerformance); // Allow up to a minute without asserting
insert(this);
break;
case KnCategory_PerformanceInSeconds:
histogram_ = new Histogram(0, gCategoryPerformanceInSeconds); // Easier to explain 20 minutes than 1000 seconds
insert(this);
break;
default:
KN_ASSERT(!"knperfstatistic: Unregistered statistics category. See KnPerfStatistic::KnPerfStatistic");
break;
}
}
void
KnPerfStatistic::configure(int a, int b)
{
gCategoryPerformance = a;
gCategoryPerformanceInSeconds = b;
}
void
KnPerfStatistic::configureStatisticsRoll(bool roll, int backups, int clamp)
{
gStatisticsRoll = roll;
gMaxBackupStatistics = backups;
gClampStatisticsRoll = clamp * 1000; // clamp is in seconds
}
void
KnPerfStatistic::setAssertPerformanceOutOfScale(bool value)
{
gLogAssertPerformanceOutOfScale = value;
}
bool
KnPerfStatistic::getAssertPerformanceOutOfScale()
{
return gLogAssertPerformanceOutOfScale;
}
KnPerfStatistic::~KnPerfStatistic()
{
if (histogram_)
delete histogram_;
histogram_ = 0;
}
KnError
KnPerfStatistic::incr( perftimer amount )
{
KnLog(KnLogDebug,"knstatistics: Adjusting %s by %ld",knId_.c_str(),amount);
value_ += amount;
if (value_ > max_)
max_ = value_;
if (value_ < min_)
min_ = value_;
return KnErrorSuccess ;
}
KnError
KnPerfStatistic::set(perftimer amount)
{
KnLog(KnLogDebug,"knstatistics: Adjusting %s to %ld",knId_.c_str(),amount);
value_ = amount;
if (value_ > max_)
max_ = value_;
if (value_ < min_)
min_ = value_;
return KnErrorSuccess;
}
perftimer
KnPerfStatistic::begin()
{
start_ = perftimerNow();
#ifdef PERFSTATISTICS_DEBUG
KnLog(KnLogDebug,"knstatistics: start timer %s @ %ld",knId_.c_str(),start_);
#endif
return start_ ;
}
static perftimer nextTimeInterval = 1000 * 60 * 1;// one minute to start
static perftimer nextTimeToDump = 0;
#define MAX_TIMEINTERVAL (1000 * 60 * 60 * 24 * 7) // 1 week
// each time we write the stats, take perftimerer to write out the next one
#define MULTIPLY_TIMEINTERVAL 2
// This is going to screw up every 24.xx days at present. Need to adjust
// the diff correctly on rollover, and to set up the next dump interval to
// work as it passes. The assertion should stay in, since it's part of
// debugging whether the *client* is keeping its state properly across threads.
perftimer
KnPerfStatistic::end(const perftimer start)
{
perftimer end_ = perftimerNow();
#ifdef PERFSTATISTICS_DEBUG
KnLog(KnLogDebug,"ReC: end timer %s @ %ld (start_ %ld, start %ld)",
id_.c_str(),
end_,
start_,
start
);
#endif
perftimer diff = perftimerRoll((start == 0) ? end_ - start_ : end_ - start);
KN_ASSERT(diff >= 0 && "Time must not go backward");
++value_;
total_ += diff;
if (diff < min_)
min_ = diff;
if (diff > max_)
max_ = diff;
histogram_->addInt(diff);
if (nextTimeToDump == 0)
nextTimeToDump = end_ + nextTimeInterval;
else if (nextTimeToDump <= end_)
{
nextTimeInterval = nextTimeInterval * MULTIPLY_TIMEINTERVAL;
if (nextTimeInterval > gClampStatisticsRoll)
nextTimeInterval = gClampStatisticsRoll;
nextTimeToDump = perftimerRoll(end_ + nextTimeInterval);
writeToFile();
}
return diff;
}
int KnPerfStatistic::find(KnString name)
{
int lo=0;
int hi=allStatisticsCount_;
while (lo < hi) {
const int mi = (lo + hi) / 2;
if (name <= allStatistics_[mi]->displayName_)
hi = mi;
else
lo = mi+1;
}
return lo;
}
void KnPerfStatistic::insert(KnPerfStatistic *statistic)
{
statistic->id_ = allStatisticsCount_;
if (allStatisticsCount_ >= ALLSTATISTICSMAX)
KnLog(KnLogError,"KnPerfStatistic::insert: ALLSTATISTICSMAX needs to be increased");
else
{
int i = find(statistic->displayName_);
for (int j = allStatisticsCount_; j > i; --j)
allStatistics_[j] = allStatistics_[j-1];
allStatistics_[i] = statistic;
allStatisticsCount_++;
}
}
void KnPerfStatistic::setSystemName(KnString systemName)
{
systemName_ = systemName;
}
void KnPerfStatistic::writeToFile(FILE *fp)
{
fprintf(fp, "<system>");
fprintf(fp, "<servername>");
fprintf(fp, "%s", systemName_.c_str());
fprintf(fp, "</servername>");
fprintf(fp, "<currenttime>");
char timeBuf[100];
Ns_LogTime(timeBuf);
fprintf(fp, "%s", timeBuf);
fprintf(fp, "</currenttime>\n");
fprintf(fp, "<performances>");
for (int i = 0; i < allStatisticsCount_; ++i)
{
fprintf(fp,
"<performance>\n"
"<name>%s</name>"
"<count>%d</count>"
"<min>%ld</min>"
"<max>%ld</max>"
"<total>" NS_INT_64_FORMAT_STRING "</total>"
"<avg>%ld</avg>"
"<percentile-10>%d</percentile-10>"
"<percentile-90>%d</percentile-90>"
,
allStatistics_[i]->displayName_.c_str(),
(int)allStatistics_[i]->value_,
allStatistics_[i]->min_,
allStatistics_[i]->max_,
allStatistics_[i]->total_,
(long)(allStatistics_[i]->total_/((int)allStatistics_[i]->value_>0?(int)allStatistics_[i]->value_:1)),
allStatistics_[i]->histogram_->percentile(10),
allStatistics_[i]->histogram_->percentile(90));
#ifdef dumpHistogram
allStatistics_[i]->histogram_->writeToFile(fp);
#endif
fprintf(fp, "</performance>\n");
}
fprintf(fp, "</performances></system>");
}
void KnPerfStatistic::writeToFile()
{
const char * defaultLogFile = "statistics.xml";
char* statisticsFile;
FILE *fd;
Ns_DString ds;
// get the statistics file path
Ns_DStringInit(&ds);
Ns_HomePath(&ds, "log", defaultLogFile, NULL);
statisticsFile = Ns_DStringExport(&ds);
// write the file
fd = fopen(statisticsFile, "w");
if (fd == NULL)
Ns_Log(Error, "log: failed to open statistics file '%s': '%s'", statisticsFile, strerror(errno));
else
{
writeToFile(fd);
fclose(fd);
}
// roll the file if so
if(gStatisticsRoll)
{
int status = NS_ERROR;
Ns_Log( Notice, "log: scheduled statistic roll started" );
if (statisticsFile != NULL)
{
if (access(statisticsFile, F_OK) == 0)
{
#ifdef TRUNCATE_DONT_ROLL
char * statisticFile000 = ns_malloc(strlen(statisticsFile) + 5);
sprintf(statisticFile000, "%s.000", statisticsFile);
#ifdef WIN32
CopyFile(statisticsFile,statisticFile000,0);
#endif
ns_free(statisticFile000);
status = Ns_RollFileCommon(statisticsFile, gMaxBackupStatistics,1);
#else
status = Ns_RollFile(statisticsFile, gMaxBackupStatistics);
#endif
#ifdef TRUNCATE_DONT_ROLL
status = truncate(statisticsFile,0);
if (status != 0)
fprintf(stderr,"unexpected error truncating statistic %d",status);
#endif
}
else
{
Ns_Log( Notice, "log: no file '%s' to roll (%s)",
statisticsFile, strerror(errno));
}
}
if (NS_OK == status)
{
Ns_Log(Notice, "log: statistic rolled successfully");
}
else
{
Ns_Log(Error, "log: statistic roll failed");
}
}
ns_free(statisticsFile);
}
Histogram::Histogram(const int low,const int high): low_(low), range_(high-low)
{
for (int i = 0; i < RESOLUTION; ++i)
frequency_[i] = 0;
};
void Histogram::addInt(const int value, const long weight)
{
int outOfRange = 0;
{
KnMutex::KnLock knlock( mutex_ );
int bucket = (value-low_)*RESOLUTION/range_;
if (bucket < 0)
{
bucket = 0;
outOfRange = -1;
}
if (bucket >= RESOLUTION)
{
bucket = RESOLUTION-1;
outOfRange = +1;
}
frequency_[bucket] += weight;
}
if (0 != outOfRange)
{
if (outOfRange < 0)
{
KnPerfStatistic::writeToFile();
KnLog(KnLogDebug,"Performance measurement out of range for category (below minimum)");
if(KnPerfStatistic::getAssertPerformanceOutOfScale())
KN_ASSERT_PERFORMANCE(!"Performance measurement out of range for category (below minimum)");
}
else
{
KnPerfStatistic::writeToFile();
KnLog(KnLogDebug,"Performance measurement out of range for category (above maximum)");
if(KnPerfStatistic::getAssertPerformanceOutOfScale())
KN_ASSERT_PERFORMANCE(!"Performance measurement out of range for category (above maximum)");
}
}
}
void Histogram::reset()
{
KnMutex::KnLock knlock( mutex_ );
for (int i = 0; i < RESOLUTION; i++)
frequency_[i] = 0;
}
void Histogram::setRange(const int low, const int high)
{
reset();
KnMutex::KnLock knlock( mutex_ ) ;
low_= low;
range_ = high - low;
}
int Histogram::percentile(const int percent)
{
KnMutex::KnLock knlock( mutex_ ) ;
perftimer total=0;
int i;
for (i = 0; i < RESOLUTION; i++)
total += frequency_[i];
total = total * percent / 100;
for (i = 0; i < RESOLUTION; i++)
if ((total -= frequency_[i]) < 0)
break;
return low_ + i * range_/RESOLUTION;
}
void Histogram::toString(KnString &result) const
{
result.printf("\n<histogram><low>%u</low><range>%u</range><high>%u</high><values>",low_,range_,low_ + range_);
// find largest value to normalize display
// display bars as text lines
bool inGap = false;
for (int i = 0; i < RESOLUTION; i++)
{
// print three dots for ranges with no data
if (frequency_[i] == 0)
{
if (inGap)
continue;
else
{
//result += "\n...";
inGap = true;
continue;
}
}
inGap = false;
int x1 = low_ + (range_ / RESOLUTION) * i;
int x2 = low_ + (range_ / RESOLUTION) * (i + 1);
KnString tmp;
tmp.printf("\n<value><id>%d</id><low>%d</low><high>%d</high><cnt>%ld</cnt></value>",i,x1,x2,frequency_[i]);
result += tmp;
}
result += "\n</values></histogram>";
}
void Histogram::writeToFile(FILE *fp) const
{
fprintf(fp, "\n<histogram><low>%u</low><range>%u</range><high>%u</high><values>",low_,range_,low_ + range_);
// find largest value to normalize display
// display bars as text lines
bool inGap = false;
for (int i = 0; i < RESOLUTION; i++)
{
// print three dots for ranges with no data
if (frequency_[i] == 0)
{
if (inGap)
continue;
else
{
//result += "\n...";
inGap = true;
continue;
}
}
inGap = false;
int x1 = low_ + (range_ / RESOLUTION) * i;
int x2 = low_ + (range_ / RESOLUTION) * (i + 1);
fprintf(fp, "\n<value><id>%d</id><low>%d</low><high>%d</high><cnt>%ld</cnt></value>",i,x1,x2,frequency_[i]);
}
fprintf(fp, "\n</values></histogram>");
}
--- NEW FILE: knregex.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/knregex.h"
#include "knutil/knlog.h"
KnRegEx::KnRegEx() :
m_regex(NULL),
m_patObj(NULL)
{
}
KnRegEx::KnRegEx(const KnString &expr, bool caseSensitive) :
m_regex(NULL),
m_patObj(NULL)
{
setRegexPattern(expr.c_str(), caseSensitive);
}
KnRegEx::KnRegEx(const char *expr, bool caseSensitive) :
m_regex(NULL),
m_patObj(NULL)
{
setRegexPattern(expr,caseSensitive);
}
void KnRegEx::clear()
{
if(m_patObj)
Tcl_DecrRefCount(m_patObj);
}
bool KnRegEx::setRegexPattern(const KnString& expr, bool caseSensitive)
{
return setRegexPattern(expr.c_str(), caseSensitive);
}
bool KnRegEx::setRegexPattern(const char *expr, bool caseSensitive)
{
clear();
constructRegEx(expr, caseSensitive);
if(m_regex == NULL)
{
KnLog(KnLogDebug, "KnRegEx: Failed to compile Tcl_RegExp");
return false;
}
return true;
}
KnRegEx::~KnRegEx()
{
clear();
}
void KnRegEx::constructRegEx(const char* regex, bool caseSensitive)
{
m_patObj = Tcl_NewStringObj((char*)regex, strlen(regex));
int cflags = TCL_REG_ADVANCED;
if (caseSensitive == false)
cflags |= TCL_REG_NOCASE;
m_regex = Tcl_GetRegExpFromObj(NULL, m_patObj, cflags);
}
bool KnRegEx::isValid() const
{
return m_regex != NULL;
}
bool KnRegEx::match(const KnString &str) const
{
return match(str.c_str());
}
bool KnRegEx::match(Tcl_Obj* matchObj) const
{
if(!isValid())
return false;
int retCode = Tcl_RegExpExecObj(NULL, m_regex, matchObj, 0, 0, 0);
return retCode == 1;
}
bool KnRegEx::match(const char *str) const
{
if(!isValid())
return false;
Tcl_Obj* objMatchPtr = Tcl_NewStringObj((char*)str, strlen(str));
int retCode = Tcl_RegExpExecObj(NULL, m_regex, objMatchPtr, 0, 0, 0);
Tcl_DecrRefCount(objMatchPtr);
return retCode == 1;
}
bool KnRegEx::matchSubs(const char *str)
{
if(!isValid())
return false;
return Tcl_RegExpExec(NULL, m_regex, (char*)str, (char*)str) == 1;
}
bool KnRegEx::find(const char *str, char*& matchStart, int& matchLen)
{
if(!isValid())
return false;
int len = strlen(str);
Tcl_Obj* objMatchPtr = Tcl_NewStringObj((char*)str, len);
Tcl_RegExpExecObj(NULL, m_regex, objMatchPtr, 0, 1, TCL_REG_NOTBOL|TCL_REG_NOTEOL);
Tcl_DecrRefCount(objMatchPtr);
if(!getSubRange( 0, matchStart, matchLen))
return false;
return true;
}
bool KnRegEx::matchPrefix(const char *str, int& matchLen)
{
if(!isValid())
return false;
char* matchStart = NULL;
int len = strlen(str);
std::string toMatch;
// add a ^ at the beginning if not found
if((len != 0) || (str[0] != '^'))
{
toMatch += '^';
toMatch += str;
}else
toMatch = str;
Tcl_Obj* objMatchPtr = Tcl_NewStringObj((char*)toMatch.c_str(), toMatch.length());
int retCode = Tcl_RegExpExecObj(NULL, m_regex, objMatchPtr, 0, 1, TCL_REG_NOTEOL);
Tcl_DecrRefCount(objMatchPtr);
if(!getSubRange( 0, matchStart, matchLen))
return false;
return retCode == 1;
}
bool KnRegEx::matchSuffix(const char *str, int& matchLen)
{
if(!isValid())
return false;
char* matchStart = NULL;
int len = strlen(str);
std::string toMatch;
// add a $ at the end if not found
if((len != 0) || (str[len-1] != '$'))
{
toMatch += str;
toMatch += '$';
}else
toMatch = str;
Tcl_Obj* objMatchPtr = Tcl_NewStringObj((char*)toMatch.c_str(), toMatch.length());
int retCode = Tcl_RegExpExecObj(NULL, m_regex, objMatchPtr, 0, 1, TCL_REG_NOTBOL);
Tcl_DecrRefCount(objMatchPtr);
if(!getSubRange( 0, matchStart, matchLen))
return false;
return retCode == 1;
}
bool KnRegEx::getSubRange( int subIndex, char*& matchStart, int& matchLen) const
{
char* startPtr;
char* endPtr;
Tcl_RegExpRange(m_regex, subIndex, &startPtr, &endPtr);
if((startPtr == NULL)||(endPtr == NULL))
{
matchStart = NULL;
matchLen = 0;
return false;
}
matchStart = startPtr;
matchLen = endPtr - startPtr;
return true;
}
--- NEW FILE: knrwlock.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/knrwlock.h"
#include "knutil/knperfstatistics.h"
#include "nsthread.h"
PERFORMANCE_STATISTICS_DECLARE_WITH_CATEGORY(gReadLockTime,KnPerfStatistic::KnCategory_PerformanceInSeconds);
PERFORMANCE_STATISTICS_DECLARE_WITH_CATEGORY(gWriteLockTime,KnPerfStatistic::KnCategory_PerformanceInSeconds);
/*
* Need C cast to convert from void ** to Ns_RWLock *.
*/
KnRWLock::KnRWLock() :
m_impl(0)
{
Ns_RWLockInit((Ns_RWLock *)&m_impl);
}
KnRWLock::~KnRWLock()
{
Ns_RWLockDestroy((Ns_RWLock *)&m_impl);
}
bool KnRWLock::lock(bool write)
{
(write) ? Ns_RWLockWrLock((Ns_RWLock *)&m_impl) :
Ns_RWLockRdLock((Ns_RWLock *)&m_impl);
return true;
}
bool KnRWLock::unlock()
{
Ns_RWLockUnlock((Ns_RWLock *)&m_impl);
return true;
}
KnRWLock::KnReadLock::KnReadLock(KnRWLock &lock) :
m_lock(lock)
{
perftimer readlock_time = PERFORMANCE_STATISTICS_BEGIN(gReadLockTime);
m_lock.lock();
PERFORMANCE_STATISTICS_END_WITH_TIME(gReadLockTime, readlock_time);
}
KnRWLock::KnReadLock::~KnReadLock()
{
m_lock.unlock();
}
KnRWLock::KnWriteLock::KnWriteLock(KnRWLock &lock) :
m_lock(lock)
{
perftimer writelock_time = PERFORMANCE_STATISTICS_BEGIN(gWriteLockTime);
m_lock.lock(true);
PERFORMANCE_STATISTICS_END_WITH_TIME(gWriteLockTime, writelock_time);
}
KnRWLock::KnWriteLock::~KnWriteLock()
{
m_lock.unlock();
}
--- NEW FILE: knrwlogginglock.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/knrwlogginglock.h"
struct KnRWLoggingLock::KnLoggingLockImpl {
Ns_RWLock m_nslock ;
KnString m_name;
bool m_locking;
bool m_logging;
KnLogSeverity m_level;
KnLoggingLockImpl(
const char *name,
bool locking,
bool logging,
KnLogSeverity level
) :
m_nslock(0),
m_name(name),
m_locking(locking),
m_logging(logging),
m_level(level)
{
Ns_RWLockInit(&m_nslock);
if (m_name.length() == 0)
{
m_name.printf("%p",this);
}
if (m_logging)
{
KnLog(m_level,"KnRWLock: Created lock '%s'",m_name.c_str());
}
};
~KnLoggingLockImpl()
{
if (m_logging)
{
KnLog(m_level,"KnRWLock: Destroyed lock '%s'",m_name.c_str());
}
Ns_RWLockDestroy(&m_nslock);
};
bool lock(bool write)
{
if (m_locking)
{
if (m_logging)
KnLog(m_level,"KnRWLock: Acquiring '%s' for %s", m_name.c_str(),
write ? "write" : "read");
(write) ? Ns_RWLockWrLock(&m_nslock) :
Ns_RWLockRdLock(&m_nslock);
if (m_logging)
KnLog(m_level,"KnRWLock: Acquired '%s' for %s", m_name.c_str(),
write ? "write" : "read");
}
else if (m_logging)
{
KnLog(m_level,"KnRWLock: Ignoring %s lock of %s",write ? "write" : "read",
m_name.c_str());
}
return true;
};
bool unlock(bool write)
{
if (m_locking)
{
if (m_logging)
KnLog(m_level,"KnRWLock: Releasing '%s' for %s", m_name.c_str(),
write ? "write" : "read");
Ns_RWLockUnlock(&m_nslock);
if (m_logging)
KnLog(m_level,"KnRWLock: Released '%s' for %s", m_name.c_str(),
write ? "write" : "read");
}
else if (m_logging)
{
KnLog(m_level,"KnRWLock: Ignoring %s unlock of %s",write?"write":"read",
m_name.c_str());
}
return true;
};
};
#ifdef WIN32
#pragma warning ( disable : 4710 ) // VC 6 won't inline the constructor call
#endif
KnRWLoggingLock::KnRWLoggingLock(
const char *name, bool locking, bool logging, KnLogSeverity level) :
m_impl( 0 )
{
m_impl = new KnRWLoggingLock::KnLoggingLockImpl( name, locking, logging, level );
}
KnRWLoggingLock::~KnRWLoggingLock()
{
delete m_impl ;
m_impl = 0;
}
bool KnRWLoggingLock::setLogging(bool to)
{
bool from = m_impl->m_logging ;
m_impl->m_logging = to ;
return from ;
}
bool KnRWLoggingLock::setLocking(bool to)
{
bool from = m_impl->m_locking ;
m_impl->m_locking = to ;
return from ;
}
bool KnRWLoggingLock::setLevel(KnLogSeverity level)
{
m_impl->m_level = level ;
return true ;
}
bool KnRWLoggingLock::lock(bool write)
{
return m_impl->lock(write);
}
bool KnRWLoggingLock::unlock(bool write)
{
return m_impl->unlock(write);
}
KnRWLoggingLock::KnReadLock::KnReadLock(KnRWLoggingLock &lock) :
m_lock(lock)
{
m_lock.lock();
}
KnRWLoggingLock::KnReadLock::~KnReadLock()
{
m_lock.unlock();
}
KnRWLoggingLock::KnWriteLock::KnWriteLock(KnRWLoggingLock &lock) :
m_lock(lock)
{
m_lock.lock(true);
}
KnRWLoggingLock::KnWriteLock::~KnWriteLock()
{
m_lock.unlock(true);
}
--- NEW FILE: knset.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/knset.h"
template union EXPORT_LIBRARY_KNUTIL SimpleBufferSlow<KnSetElem *>;
template class EXPORT_LIBRARY_KNUTIL SimpleVector<KnSetElem *>;
template class EXPORT_LIBRARY_KNUTIL SimpleSyncVector<KnSetElem *>;
template class EXPORT_LIBRARY_KNUTIL KnSetT<SimpleVectorSetElem>; //KnSet;
template class EXPORT_LIBRARY_KNUTIL KnSetT<SimpleSyncVectorSetElem>; //KnSyncSet;
void KnSetAssign(KnSyncSet& setSync, const KnSet& set)
{
setSync.clear();
for (size_t i = 0; i < set.size(); ++i)
{
setSync.put(set[i].m_key, set[i].m_value);
}
}
void KnSetAssign(KnSet& set, const KnSyncSet& setSync)
{
set.clear();
for (size_t i = 0; i < setSync.size(); ++i)
{
set.put(setSync[i].m_key, setSync[i].m_value);
}
}
--- NEW FILE: knsleep.cpp ---
/**
* Copyright (c) 2001 KnowNow, Inc. All rights reserved.
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knexportlibraryknutilmodule.h"
#include "knutil/knsleep.h"
#ifdef WIN32
#include <windows.h>
#else
#include <sys/time.h>
#include <unistd.h>
#endif
void KnSleep(long milliseconds) {
#ifdef WIN32
Sleep(milliseconds);
#else
if (milliseconds > 1000) {
sleep(milliseconds / 1000);
}
else
{
usleep(milliseconds * 1000);
}
#endif
}
--- NEW FILE: knstring.cpp ---
#include "string.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
[...1426 lines suppressed...]
break;
}
if(s)
ns_free(s);
}
void truncateFromRight( std::string& str, int len )
{
if (len > 0)
{
len = str.length() - len;
if (len < 0)
len = 0;
str.resize(len);
}
}
--- NEW FILE: knstringtokenizer.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/knstringtokenizer.h"
KnStringTokenizer::KnStringTokenizer(const KnString &str, bool skip) :
m_str(str),
m_delim(" \f\n\r\t\v"),
m_pos(0),
m_skip(skip)
{
}
KnStringTokenizer::KnStringTokenizer(const KnString &str,
const KnString &delim,
bool skip) :
m_str(str),
m_delim(delim),
m_pos(0),
m_skip(skip)
{
}
KnStringTokenizer::KnStringTokenizer(const KnString &str,
const char *delim,
bool skip) :
m_str(str),
m_delim(delim),
m_pos(0),
m_skip(skip)
{
}
KnStringTokenizer::KnStringTokenizer(const KnStringTokenizer &tk) :
m_str(tk.m_str),
m_delim(tk.m_delim),
m_pos(tk.m_pos),
m_skip(tk.m_skip)
{
}
KnStringTokenizer::~KnStringTokenizer()
{
}
bool KnStringTokenizer::next(KnString &token)
{
token = "";
if (m_pos >= m_str.length())
return false;
if (m_delim.indexOf(m_str[m_pos]) != -1)
{
++m_pos;
if (!m_skip)
{
token.copy(m_str.c_str() + (m_pos - 1), 1);
return true;
}
for (;;)
{
if (m_pos >= m_str.length())
return false;
if (m_delim.indexOf(m_str[m_pos]) == -1)
break;
++m_pos;
}
}
int pos = m_pos;
for (;;)
{
++m_pos;
if (m_pos >= m_str.length() || m_delim.indexOf(m_str[m_pos]) != -1)
break;
}
token.copy(m_str.c_str() + pos, m_pos - pos);
return true;
}
bool KnStringTokenizer::next(KnString &token, const KnString &delim)
{
m_delim = delim;
return next(token);
}
bool KnStringTokenizer::next(KnString &token, const char *delim)
{
m_delim = delim;
return next(token);
}
--- NEW FILE: knsysutility_unix.cpp ---
/**
/**
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
* Copyright 2000 - 2004 KnowNow, Inc., Sunnyvale, CA. All rights reserved.
*/
#include "knexportlibraryknutilmodule.h"
#include "knutil/knset.h"
#include "knutil/knsysutility.h"
#ifndef _WINDOWS
#include <stdlib.h>
#define HAVE_SETENV
bool KnSetEnvVar(const char *envvar, const char *value)
{
#if defined(HAVE_SETENV)
if (0 > setenv(envvar, value, 1))
return false;
return true;
#elif defined(HAVE_PUTENV)
size_t elen = strlen(envvar);
size_t vlen = strlen(value);
char *env = malloc(elen + vlen + 2);
char *p = env + elen;
memcpy(env, envvar, elen);
*p++ = '=';
memcpy(p, value, vlen);
p[vlen] = '\0';
if (0 > putenv(env))
return false;
return true;
#else
return false;
#endif
}
#endif
--- NEW FILE: knsysutility_win32.cpp ---
/**
/**
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
* Copyright 2000 - 2004 KnowNow, Inc., Sunnyvale, CA. All rights reserved.
*/
#include "knexportlibraryknutilmodule.h"
#include "knutil/knset.h"
#include "knutil/knsysutility.h"
#include "knkillprocess_win32.h"
#include <process.h>
namespace
{
void quoteArgs( KnString &args )
{
if ( args.length() <= 0 )
return;
std::string tmp;
std::string dargs;
int from = 0;
int delim = args.indexOf( ',' );
for (;;) /* MS compiler warning-free way to write while(true) */
{
if ( delim == -1 )
delim = args.length();
dargs += "\"";
tmp.assign( args.c_str() + from, delim - from );
dargs += tmp;
dargs += "\"";
if ( delim == args.length() )
{
break;
}
dargs += " ";
from = delim + 1;
delim = args.indexOf( ',', from );
}
args = dargs.c_str();
} // quoteArgs
} // namespace
int
KnProcessStart( const KnString &exec, KnString *args,
Ns_Set *env, int *pid,
bool restart, bool nullio )
{
bool ok ;
// executable name has to be provided
if ( 0 == exec.length() )
return -1 ;
KnString dir = exec ;
dir.truncateFromRight( dir.length() - dir.lastIndexOf( '/' ) - 1 );
STARTUPINFO si ;
PROCESS_INFORMATION pi ;
/*
* Setup STARTUPINFO with stdin, stdout, and stderr.
*/
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
if (nullio)
{
si.dwFlags = STARTF_USESTDHANDLES;
FILE *fnul = fopen("NUL", "w");
si.hStdOutput = (HANDLE) _get_osfhandle(_fileno(fnul));
si.hStdError = (HANDLE) _get_osfhandle(_fileno(fnul));
}
if ( !args && !env )
{
// 0 != syntax suppresses compiler warning about int->bool performance
ok = (0 != CreateProcess( exec.c_str(), NULL, NULL, NULL,
true, 0,
NULL, dir.c_str(),
&si, &pi ));
}
else
{
// place quotes around the arguments in case the full path names include
// spaces.
if ( args && !restart )
{
quoteArgs( *args );
}
std::string cmdline;
cmdline += "\"";
cmdline += exec.c_str();
cmdline += "\" ";
cmdline += (*args).c_str();
KnSet restore;
if ( env )
{
for ( int i = 0; i < Ns_SetSize( env ); ++i )
{
char buffer[2050] ;
int result;
result = GetEnvironmentVariable( Ns_SetKey( env, i ), (LPSTR)&buffer, sizeof(buffer) );
if (result != 0 && result < sizeof(buffer))
restore.put( Ns_SetKey( env, i ), buffer );
else
restore.put( Ns_SetKey( env, i ), NULL );
if ( !SetEnvironmentVariable( Ns_SetKey( env, i ),
Ns_SetValue( env, i )))
{
KnLog( KnLogWarning, "KnProcessStart: could not set "
"environment variable '%s' to '%s'",
Ns_SetKey( env, i ), Ns_SetValue( env, i ) ) ;
}
}
}
char *strcmdline = static_cast<char *>(ns_malloc(cmdline.length() + 1));
memcpy(strcmdline, cmdline.c_str(), cmdline.length()+1);
// 0 != syntax suppresses compiler warning about int->bool performance
ok = (0 != CreateProcess( NULL,
strcmdline,
NULL, NULL,
true, 0,
NULL,
dir.c_str(), &si, &pi )) ;
ns_free(strcmdline);
// Now restore the envs for the parent process
if ( env )
{
for ( size_t i = 0 ; i < restore.size() ; ++i )
{
if ( !SetEnvironmentVariable( restore[i].m_key.c_str(),
restore[i].m_value.c_str() ))
KnLog( KnLogWarning, "KnProcessStart: could not restore "
"environment variable '%s'", restore[i].m_key.c_str()) ;
}
}
}
CloseHandle(si.hStdInput);
CloseHandle(si.hStdOutput);
if ( !ok )
{
KnLog( KnLogDebug, "KnProcessStart failed to start process '%s':%u",
exec.c_str(), GetLastError() ) ;
return -1 ;
}
CloseHandle(pi.hThread);
KnLog( KnLogDebug, "KnProcessStart started process '%s' with pid %d",
exec.c_str(), pi.dwProcessId ) ;
if (pid)
*pid = pi.dwProcessId ;
return ( int) pi.hProcess ;
}
bool
KnProcessStop( const int phandle, int *exit )
{
if ( !TerminateProcess( (HANDLE)phandle, (DWORD)-1 ) )
{
KnLog( KnLogDebug, "KnProcessStop: failed to stop process: %u",
GetLastError() ) ;
return false ;
}
if (exit)
*exit = (int)-1 ;
return true ;
}
bool
KnProcessStop( const KnString &exec, int *exit )
{
CKillProcess helper;
KnString exe( exec.c_str()
+ exec.lastIndexOf( '/' ) + 1 ) ;
if ( !exe.has( "." ) )
{
exe += ".exe" ;
}
if ( !helper.KillProcess( exe.c_str(), exit ) )
{
KnLog( KnLogError, "KnProcessStop: failed to stop process '%s': %u",
exe.c_str(), GetLastError() ) ;
return false ;
}
if (exit)
*exit = (int)-1 ;
return true ;
}
void*
KnProcessFind( const KnString &procname )
{
DWORD dwId;
CKillProcess helper;
//KnString name( procname.c_str() + procname.lastIndexOf( '/' ) ) ;
return helper.FindProcess( procname.c_str(), dwId );
}
bool KnSetEnvVar(const char *envvar, const char *value )
{
if (!SetEnvironmentVariableA(envvar, value))
return false;
return true;
}
--- NEW FILE: kntclerrors.cpp ---
/**
* Copyright (c) 2005 KnowNow, Inc. All rights reserved.
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knexportlibraryknutilmodule.h"
#include "knutil/knstring.h"
#include "knutil/knerror.h"
#include "knutil/kntclerrors.h"
/*
* This is all about putting that "500" in front of the message. We try
* to be consistent about returning a HTTP-style code when throwing errors.
*
* However, the rest of TCL is not so nice about it, so any TCL "catch" which
* expects to return information to clients should recognize whether a
* code is present and prepend one if not.
*
* Note that this version of WrongNumArgs does *not* do any dictionary lookup,
* unless Tcl_GetString has more magic in it than I think it does. Certainly,
* the Tcl_WrongNumArgs source has special code for that lookup that requires
* knowledge of Tcl interpreter internals.
*/
int
KnTcl_WrongNumArgs(Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], const char *message) {
int ix;
std::string output(KNTCL_ERROR_WRONG_NUM_ARGS " Wrong # args: should be \"");
for (ix = 0; ix < objc; ++ix) {
if (ix > 0) {
output += " ";
}
output += Tcl_GetString(objv[ix]);
}
if (ix > 0) {
output += " ";
}
output += message ;
output += "\"";
Tcl_AppendResult(interp,output.c_str(),0);
return TCL_ERROR;
}
int /* Tcl_Error */
KnTcl_BadArg(Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], const char *message) {
int ix;
std::string output(KNTCL_ERROR_BAD_ARG " Invalid argument to ");
if (objc > 0) {
output += "\"";
for (ix = 0; ix < objc; ++ix) {
if (ix > 0) {
output += " ";
}
output += Tcl_GetString(objv[ix]);
}
output += "\"";
}
output += ": ";
output += message ;
Tcl_AppendResult(interp,output.c_str(),0);
return TCL_ERROR;
}
int
KnTcl_Error(Tcl_Interp *interp, KnError status)
{
KnString output;
output.printf("%d %s",
KnError_AsHTTP( status ),
KnError_AsString(status).c_str());
Tcl_Obj *value = Tcl_NewStringObj(const_cast<char*>(output.c_str()),output.length());
Tcl_SetObjResult(interp,value);
return (KnErrorSuccess == status) ? TCL_OK : TCL_ERROR ;
}
--- NEW FILE: kntclfile.cpp ---
/* Copyright 2004 KnowNow, Inc., Sunnyvale CA 94089 USA All Rights Reserved */
/**
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knexportlibraryknutilmodule.h"
#include "knutil/kntclfile.h"
#include "knutil/knlog.h"
#include <sys/stat.h>
#ifndef S_ISREG
#define S_ISREG(m) ((m)&_S_IFREG)
#endif
// This belongs in a filter-writing general location in KnowNow
// For security purposes, we may want to move from using Ns_Tcl allocation
// and deallocation routines to directly using the Tcl routines,
// since the Ns_ routines add commands to the namespace
// that we may not want exposed.
KnTclFile::KnTclFile(KnString &file, KnTclInterp* interp) :
interp_(interp),
filename_(file),
valid_(true),
destroy(false)
{
// Initialize an interpreter so we can run the given file
// to read the file as pure Unicode.
struct stat statBuf;
if (stat(filename_.c_str(), &statBuf) != 0)
{
KnLog(KnLogWarning, "kntclfile: failed to access '%s': '%s'",
filename_.c_str(), strerror(errno));
valid_ = false;
}
else if (!S_ISREG(statBuf.st_mode))
{
KnLog(KnLogWarning, "kntclfile: failed to access '%s': not an ordinary filename_.c_str()",
filename_.c_str());
valid_ = false;
}
else if (access(filename_.c_str(), R_OK) != 0)
{
KnLog(KnLogWarning, "kntclfile: failed to access '%s': '%s'",
filename_.c_str(), strerror(errno));
valid_ = false;
}
else if (!interp_)
{
interp_ = new KnTclInterp((const char*)NULL);
destroy = true;
valid_ = (interp_ != 0);
}
}
KnError
KnTclFile::run(KnString& file)
{
if (!valid_)
{
KnLog(KnLogBug, "kntclfile: Attempt to run invalid file");
return KnErrorFailure ;
}
if (!interp_)
{
KnLog(KnLogBug, "kntclfile: Attempt to run unallocated interpreter");
return KnErrorFailure ;
}
if (Ns_CheckStack() == NS_BREAK) {
KnLog(KnLogError,
"KnTclFile: stack grown too large; not running '%s'",
filename_.c_str());
return KnErrorFailure;
}
return interp_->evalFile(filename_, file);
}
KnTclFile::~KnTclFile()
{
if (destroy)
delete interp_;
}
bool
KnTclFile::isValid()
{
return valid_;
}
--- NEW FILE: kntclinterp.cpp ---
// (c) Copyright 2004 KnowNow, Inc
/**
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
//
// Provides a class that wraps the Tcl_Interp structure in a way that allows
// us to know when we should call Ns_TclDeAllocateInterp();
#include "knexportlibraryknutilmodule.h"
#include "knutil/kntclinterp.h"
// Implements reference counting in TCL variables in order to
// make sure we don't de-allocate a TclInterp that's still in use.
// Can't use C++ easily, since we're passed the Tcl_Interp* as an input
// parameter in some cases.
//
// We could potentially use a dictionary in C++ for that, but
// TCL is perfectly capable of storing our information for us. May want
// to put it in its own namespace for cleanliness reasons.
//
// Since AOLServer assures us that each Tcl_Interp is only used by one
// thread, we don't have to do any locking here.
//
// I couldn't easily implement it in terms of either KnSmartRef or
// RefCount; accordingly, it's given its own implementation here, but
// I'm sure a better way to build these things -- I just
// don't know what it is yet. -- ReC 4/1/2005
// The whole implementation is in the header file.
// Should namespace this in TCL, but don't know how;
// got 'parent namespace doesn't exist'.
char * KnTclInterp::REFCOUNT_TCLVAR = "kn_interp_depth";
int KnTclInterp::REFCOUNT_VARFLAGS = TCL_LEAVE_ERR_MSG | TCL_GLOBAL_ONLY ;
KnError
KnTclInterp::createObjCommand(CONST char* name, Tcl_ObjCmdProc* proc, void* data, Tcl_CmdDeleteProc* delProc)
{
Tcl_CreateObjCommand(m_pointer, name, proc, data, delProc);
return KnErrorSuccess;
}
KnError
KnTclInterp::evalFile(KnString& filename, KnString& file)
{
file.reset();
int ret = Tcl_EvalFile(m_pointer, const_cast<char*>(filename.c_str()));
if (ret == TCL_OK)
{
int len;
file = Tcl_GetStringFromObj(Tcl_GetObjResult(m_pointer), &len);
return KnErrorSuccess;
}
else
{
const char *result = Tcl_GetStringResult(m_pointer);
KnLog(KnLogError, "kntclinterp: Script %s failed (returned %d) @ line %d: '%s'",
filename.c_str(), ret, m_pointer->errorLine, result);
return KnErrorFailure;
}
}
bool
KnTclInterp::getValue(Tcl_Obj*& valueObj, int& value) {
valueObj = Tcl_GetVar2Ex(m_pointer,REFCOUNT_TCLVAR,NULL,REFCOUNT_VARFLAGS);
if (!valueObj) {
value = 0 ;
valueObj = Tcl_NewIntObj(value);
return (valueObj != 0);
} else if (TCL_OK != Tcl_GetIntFromObj(m_pointer,valueObj,&value)) {
KnLog(KnLogBug,"KnTclInterp: invalid reference count object: %s in interp %p",
Tcl_GetStringResult(m_pointer),
m_pointer);
return false ;
}
return true ;
}
bool
KnTclInterp::setValue(Tcl_Obj*& valueObj, int value) {
// It'd be nice to only do this once, but we're not worrying about
// speed right now:
Tcl_Obj *refcount_tclvar_obj = Tcl_NewStringObj(REFCOUNT_TCLVAR,strlen(REFCOUNT_TCLVAR));
Tcl_SetIntObj(valueObj,value);
bool success = (0 != Tcl_ObjSetVar2(m_pointer,refcount_tclvar_obj,NULL,valueObj,REFCOUNT_VARFLAGS));
if (!success) {
KnLog(KnLogError,
"KnTclInterp: could not set %s to %d in interp %p: %s",
REFCOUNT_TCLVAR,value,m_pointer,
Tcl_GetStringResult(m_pointer));
}
Tcl_DecrRefCount(refcount_tclvar_obj);
return success ;
}
KnError
KnTclInterp::setGlobalVariable(const KnString varName, const KnString value) const
{
Tcl_Obj *varNameObj = Tcl_NewStringObj(const_cast<char*>(varName.c_str()),varName.length());
Tcl_Obj *valueObj = Tcl_NewStringObj(const_cast<char*>(value.c_str()),value.length());
bool success = (0 != Tcl_ObjSetVar2(m_pointer,varNameObj,NULL,valueObj,TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG ));
if (!success) {
KnLog(KnLogError,
"KnTclInterp: could not set %s to %s in interp %p: %s",
varName.c_str(),value.c_str(),m_pointer,
Tcl_GetStringResult(m_pointer));
Tcl_DecrRefCount(valueObj);
}
Tcl_DecrRefCount(varNameObj);
return success ? KnErrorSuccess : KnErrorFailure ;
}
KnError
KnTclInterp::getGlobalVariable(KnString varName, KnString& value) const
{
Tcl_Obj *valueObj = Tcl_GetVar2Ex(m_pointer, const_cast<char*>(varName.c_str()),NULL,TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG );
char *valuePtr = 0;
int len;
if (!valueObj) {
return KnErrorNotFound ;
}
else if (0 == (valuePtr = Tcl_GetStringFromObj(valueObj,&len))) {
KnLog(KnLogBug,"KnTclInterp: invalid reference count object: %s in interp %p",
Tcl_GetStringResult(m_pointer),
m_pointer);
return KnErrorFailure ;
}
else {
value.copy(valuePtr,len);
return KnErrorSuccess;
}
}
KnTclInterp::KnTclInterp(Tcl_Interp* pointer)
: m_amOwner(pointer ? false : true ),
m_pointer(pointer ? pointer : Ns_TclAllocateInterp(NULL))
{
increment() ;
if (0 == Tcl_SetVar(m_pointer,"kn_interp_depth","1",TCL_LEAVE_ERR_MSG | TCL_GLOBAL_ONLY))
{
KnLog(KnLogWarning,"kntclinterp: could not set reference count: %s",
Tcl_GetStringResult(m_pointer));
}
}
KnTclInterp::KnTclInterp(const char* server)
: m_amOwner(true),
m_pointer(Ns_TclAllocateInterp(const_cast<char*>(server)))
{
increment() ;
if (0 == Tcl_SetVar(m_pointer,"kn_interp_depth","1",TCL_LEAVE_ERR_MSG | TCL_GLOBAL_ONLY))
{
KnLog(KnLogWarning,"kntclinterp: could not set reference count: %s",
Tcl_GetStringResult(m_pointer));
}
}
KnTclInterp::~KnTclInterp()
{
decrement();
}
int KnTclInterp::increment(int count)
{
assert(isValid());
int value = -1;
Tcl_Obj* valueObj;
if (getValue(valueObj,value)) {
value += count;
setValue(valueObj,value);
}
//KnLog(KnLogDev,"KnTclInterp: incremented count of %p to %d",m_pointer,value);
return (value);
}
int KnTclInterp::decrement(int count)
{
assert(isValid());
int value = -1;
Tcl_Obj* valueObj;
if (getValue(valueObj,value)) {
value -= count;
if (value <= 0 && m_amOwner) {
Ns_TclDeAllocateInterp(m_pointer);
//KnLog(KnLogDev,"KnTclInterp: deallocated %p based on value %d",m_pointer,value);
m_pointer = 0 ;
} else {
setValue(valueObj,value);
//KnLog(KnLogDev,"KnTclInterp: decremented count of %p to %d",m_pointer,value);
}
}
return (value);
}
bool KnTclInterp::isValid() const { return (0 != m_pointer); }
Tcl_Interp* KnTclInterp::operator->() { assert(isValid()); return m_pointer ; }
const Tcl_Interp* KnTclInterp::operator->() const { assert(isValid());return m_pointer;}
Tcl_Interp& KnTclInterp::operator*() { assert(isValid());return *m_pointer; }
const Tcl_Interp& KnTclInterp::operator*() const { assert(isValid());return *m_pointer; }
KnError KnTclInterp::executeBooleanCommand(KnString& tclCommand, bool& value)
{
KnLog(KnLogDebug,"KnTclInterp: Evaluating boolean command '%s'", tclCommand.c_str());
if (Ns_CheckStack() == NS_BREAK)
{
KnLog(KnLogError, "KnTclInterp: stack grown too large; not executing '%s'", tclCommand.c_str());
return KnErrorFailure;
}
Ns_DString tmpbuf;
Ns_DStringInit(&tmpbuf);
Ns_DStringNAppend(&tmpbuf, const_cast<char*>(tclCommand.c_str()), tclCommand.length());
int status = Tcl_EvalEx(m_pointer, Ns_DStringValue(&tmpbuf), Ns_DStringLength(&tmpbuf), TCL_EVAL_DIRECT);
Ns_DStringFree(&tmpbuf);
if (status != TCL_OK) {
KnLog(KnLogError,"KnTclInterp: Couldn't evaluate '%s': %s",tclCommand.c_str(), Tcl_GetStringResult(m_pointer));
Ns_TclLogError(m_pointer);
return KnErrorFailure ;
}
Tcl_Obj* result = Tcl_GetObjResult(m_pointer);
if (!result) {
KnLog(KnLogWarning,"KnTclInterp: tcl expression '%s' did not return a usable result; not changing value", tclCommand.c_str());
} else {
int intValue ;
if (TCL_OK != Tcl_GetBooleanFromObj(m_pointer,result,&intValue)) {
KnLog(KnLogError,"KnTclInterp: couldn't convert result '%s' to boolean value value",Tcl_GetStringResult(m_pointer));
return KnErrorFailure;
}
KnLog(KnLogDebug, "KnTclInterp: boolean return %d", intValue);
value = (intValue != 0);
}
KnLog(KnLogDebug,"KnTclInterp: executed boolean command successffully");
return KnErrorSuccess ;
}
--- NEW FILE: kntemplateuri.cpp ---
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knexportlibraryknutilmodule.h"
#include "knutil/knlog.h"
#include "knutil/kntemplateuri.h"
class KnTempItemDesc
{
public:
KnTempItemDesc(const char* name, int templatePosition, int regexIndex) :
name(name), templatePosition(templatePosition)
{
startRegexIndex = stopRegexIndex = regexIndex;
}
std::string name;
int templatePosition;
int startRegexIndex;
int stopRegexIndex;
};
KnTemplateURI::KnTemplateURI(const KnString& name, const KnString& expr)
: KnUrlPattern(name)
{
m_template = expr;
m_regexPattern += "^";
m_validTemplate = makePathRegex(m_regexPattern,expr);
m_regexPattern += "$";
if(m_validTemplate)
{
setRegexPattern(m_regexPattern.c_str());
m_validTemplate = m_validTemplate && isValidRegex();
}
}
KnTemplateURI::~KnTemplateURI()
{
for(int i = 0; i < m_items.size(); i++)
{
SAFE_DELETE(m_items[i]);
}
}
int KnTemplateURI::getSubCount() const
{
return m_items.size();
}
bool KnTemplateURI::isValidTemplate() const
{
return m_validTemplate;
}
bool KnTemplateURI::matchSubs(const char *str, KnSet& result)
{
if(!isValidTemplate())
{
KnLog(KnLogDev, "KnTemplateURI: invalid template [%s]",str);
return false;
}
result.clear();
int subCount = getSubCount();
if(!subCount)
return m_regex.match(str);
KnMutex::KnLock lock(m_regexSubsMutex);
if(!m_regex.matchSubs(str))
return false;
char* mstart;
int mlen;
KnTempItemDesc* item;
std::string matched;
for(int i = 0; i < subCount; i++)
{
item = m_items[i];
for(int index = item->startRegexIndex; index <= item->stopRegexIndex; index++)
{
m_regex.getSubRange(index, mstart, mlen);
if(mlen)
break;
}
matched = "";
if(mlen)
matched.append(mstart,mlen);
result.put(item->name.c_str(), matched.c_str());
}
return true;
}
bool KnTemplateURI::replaceInto(KnSet& set, const char* expr, std::string& result, bool removeMatched)
{
int len = strlen(expr);
bool inside = false;
std::string name;
for(int i = 0; i < len; i++)
{
switch (expr[i])
{
case '{':
{
if(inside)
return false;
inside = true;
name = "";
break;
}
case '}':
{
if(!inside)
return false;
inside = false;
const KnString* value = set.get(name.c_str());
if(!value)
return false;
result += value->c_str();
if(removeMatched)
set.remove(name.c_str());
break;
}
default:
if(inside)
{
name += expr[i];
}else
{
result += expr[i];
}
}
}
return !inside;
}
bool KnTemplateURI::escape( std::string& putItHere, char aChar)
{
switch(aChar)
{
case '\\':
case '*':
case '[':
case ']':
case '(':
case ')':
case '{':
case '}':
case '.':
case '^':
case '$':
case '?':
putItHere += "\\";
break;
default:
break;
}
putItHere.append(&aChar,1);
return true;
}
// replace ( with (?: not to interfere with subexpression matching
std::string replacePars(std::string& in)
{
std::string result;
result.reserve(in.length());
for(int i = 0; i < in.length(); i++)
if(in[i] == '(')
result += "(?:";
else
result += in[i];
return result;
}
namespace {
const std::string AnyPart(".*");
const std::string AnyUrlComponent("[^/]*");
const std::string AndSubTree("(?:/.*){0,1}");
const std::string Anywhere("(?:(?:/.*/)|/)");
const std::string SubTreeOnly("/.*");
};
// Pattern taken from WS-Topics, Draft 1.2 of 6/17/2004, lines 446 - 477
// Only oddity is '//.'; that document specifies that this at the end of
// a string means AndSubtree. So we go out of our way to delete the . if
// it's at the end of the string.
// Getting the counts right takes an embedded state machine. Idea is:
// foreach component
// if (no wildcards yet) { ++leaders, ++trailers }
// else if (component has a wildcard) {++<wildcardtype>, trailers = 0}
// else { ++trailers }
// we recognize a component when we reach a '/' other than at the
// beginning of the parse, and at the end of the parse, presuming
// the last thing was not a /.
typedef enum { State_normal, State_oneslash, State_twoslash, State_withdot, State_inside} ParseState;
void addTemplate(std::string& here, KnTempItemDesc* item, int& subIndex, bool subtreeOnly = false)
{
std::string op;
std::string arg;
std::string var;
int firstpos;
int lastpos;
int len = item->name.length();
subIndex++;
for(firstpos = 0; firstpos < len; firstpos++)
if(item->name[firstpos] == '|')
break;
for(lastpos = len-1; lastpos >= 0; lastpos--)
if(item->name[lastpos] == '|')
break;
if((firstpos == len))
{
here += '(';
if(subtreeOnly)
here += SubTreeOnly;
else
here += AnyUrlComponent;
here += ')';
return;
}
op = item->name.substr( 0, firstpos);
arg = item->name.substr( firstpos+1, lastpos-firstpos-1);
var = item->name.substr( lastpos+1, len-lastpos);
if(op == "-prefix")
{
here += "(?:";
here += "(?:";
here += arg;
here += "(";
if(subtreeOnly)
here += SubTreeOnly;
else
here += AnyUrlComponent;
here += ")";
here += ")";
here += "|";
here += "(";
if(subtreeOnly)
here += SubTreeOnly;
else
here += AnyUrlComponent;
here += ")";
here += ")";
item->name = var;
item->startRegexIndex = subIndex-1;
item->stopRegexIndex = subIndex;
subIndex++;
return;
}
if(op == "-regex")
{
here += "(";
here += replacePars(arg);
here += ")";
item->name = var;
return;
}
here += '(';
if(subtreeOnly)
here += SubTreeOnly;
else
here += AnyUrlComponent;
here += ')';
}
bool KnTemplateURI::makePathRegex(KnString& result, const KnString& part)
{
bool countTemplates = true;
ParseState state = State_normal;
ParseState lastState = State_normal;
bool bOK = true;
KnUrlPattern::ComponentCounter counter(*this);
std::string putItHere = result.c_str();
int itemIndex = 1;
KnTempItemDesc* currentItem = NULL;
bool needPar = false;
int i = 0 ;
bool subTopicCounted = false;
char nextChar;
for (; i < (int)part.length() ; ++i)
{
nextChar = part[i];
if(nextChar == '{')
{
if(state == State_inside)
return false;
lastState = state;
state = State_inside;
currentItem = new KnTempItemDesc("", i, itemIndex);
continue;
}else
if(nextChar == '}')
{
if(state != State_inside)
return false;
state = lastState;
m_items.push_back(currentItem);
needPar = true;
nextChar = '*';
}
switch (state)
{
case State_inside:
currentItem->name += m_template[i];
break;
default:
case State_normal:
switch(nextChar)
{
case '/':
if (i != 0)
{
// ends a component, so count it.
counter.countComponent();
}
state = State_oneslash;
subTopicCounted = false;
break ;
case '*':
if(needPar)
{
addTemplate(putItHere, currentItem, itemIndex);
if(countTemplates)
counter.countFileCard();
}else
{
putItHere += AnyUrlComponent;
counter.countFileCard();
}
break ;
default:
if (!subTopicCounted)
{
counter.countSubTopics();
subTopicCounted = true;
}
bOK = escape(putItHere,nextChar);
}
break ;
case State_oneslash:
switch(nextChar)
{
case '/':
// It's definitely a wildcard, now to recognize it.
counter.countPathCard();
state = State_twoslash;
break;
case '*':
// This is the case foo/*..., so we're in a component
// and want a filecard. And we're back in State_normal state.
//
// So what happens with foo/*//. ? Should mean there
// must be one complete component; I think it does.
putItHere += "/";
if(needPar)
{
addTemplate(putItHere, currentItem, itemIndex);
if(countTemplates)
counter.countFileCard();
}else
{
putItHere += AnyUrlComponent;
counter.countFileCard();
}
state = State_normal ;
break ;
default:
putItHere += "/";
bOK = escape(putItHere,nextChar);
state = State_normal;
break;
}
break;
case State_twoslash:
switch(nextChar)
{
case '.':
putItHere += AndSubTree;
state = State_withdot;
break;
case '*':
if(needPar)
{
addTemplate(putItHere, currentItem, itemIndex, true);
if(countTemplates)
counter.countComponent();
}else
{
putItHere += SubTreeOnly;
counter.countComponent();
}
state = State_normal;
break ;
default:
putItHere += Anywhere;
bOK = escape(putItHere,nextChar);
counter.countComponent();
state = State_normal;
break;
}
break ;
case State_withdot:
// Well, we're still in the string, so include the .
putItHere += ".";
bOK = escape(putItHere,nextChar);
// Didn't know whether to count even the first slash till now.
counter.countComponent() ;
state = State_normal;
break ;
}
}
switch (state)
{
case State_withdot: // Trash the dot; we already output the pattern.
case State_normal:
counter.countComponent();
break;
case State_oneslash:
putItHere += "/";
break;
case State_twoslash:
putItHere += AndSubTree;
break;
}
if (i == 0)
{
putItHere += AndSubTree;
counter.countPathCard();
}
result = putItHere.c_str();
return state != State_inside;
}
--- NEW FILE: kntime.cpp ---
/*
/**
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
* Copyright 2003 Knownow, Inc.
*/
#include "knexportlibraryknutilmodule.h"
#include "knutil/kntime.h"
#include "knutil/knlog.h"
#ifdef WIN32
#include "windows.h"
#else
#include "sys/time.h"
#endif
#ifdef WIN32
#pragma warning ( disable : 4710 ) // KnTimeImpl won't inline
#endif
#define TIMEVAL_RESOLUTION (1000000)
/**
* KnTime is implemented using the handle/body or "pimpl" idiom. That
* enables us to change the underlying implementation without requiring
* clients to recompile.
*/
struct KnTime::KnTimeImpl {
explicit KnTimeImpl() :
m_delta(false)
{
setToNow();
};
explicit KnTimeImpl( const KnString& relativeTime, KnTimeDefault dir, bool isDelta ):
m_delta(isDelta)
{
if (!m_delta)
{
setToNow();
}
else
{
m_tv.tv_sec = 0UL;
m_tv.tv_usec = 0UL;
}
setString();
if (relativeTime.startsWith("+"))
{
parseRelative( relativeTime.c_str()+1, KNTIME_FUTURE );
}
else if (relativeTime.startsWith("-"))
{
parseRelative( relativeTime.c_str()+1, KNTIME_PAST );
}
else if (relativeTime.startsWith("@"))
{
parseRelative( relativeTime.c_str()+1, KNTIME_ABSOLUTE );
}
else
{
parseRelative( relativeTime.c_str(), dir );
}
};
const KnString& asString() const {
setString() ;
return m_string ;
};
const char * c_str() const {
setString() ;
return m_string.c_str();
};
KnTime_t asTime_T() const {
return static_cast<KnTime_t>(m_tv.tv_sec);
};
double asFloat() const {
double ret = double(m_tv.tv_sec) + ((double)m_tv.tv_usec / TIMEVAL_RESOLUTION);
return ret ;
};
struct timeval asTimeVal() const {
return m_tv ;
};
KnTimeImpl& operator+=( const struct timeval& other )
{
KnLog(KnLogDebug,"kntime: Adding %lu.%lu to %lu.%lu",other.tv_sec,other.tv_usec, m_tv.tv_sec, m_tv.tv_usec);
m_tv.tv_sec += other.tv_sec ;
m_tv.tv_usec += other.tv_usec ;
if (m_tv.tv_usec >= TIMEVAL_RESOLUTION)
{
++(m_tv.tv_sec);
m_tv.tv_usec -= TIMEVAL_RESOLUTION;
}
return *this;
}
KnTimeImpl& operator-=( const struct timeval& other )
{
m_tv.tv_sec -= other.tv_sec ;
m_tv.tv_usec -= other.tv_usec ;
if (m_tv.tv_usec < 0)
{
--(m_tv.tv_sec);
m_tv.tv_usec += TIMEVAL_RESOLUTION;
if (m_tv.tv_usec < 0)
{
KnLog(KnLogBug,"KnTime: failed adjustment for underflow [%ld.%ld - %ld.%ld]?!",
m_tv.tv_sec,m_tv.tv_usec,other.tv_sec,other.tv_usec);
}
}
return *this;
}
KnTimeImpl& operator+=( const KnTimeImpl& other )
{
if (!(m_delta || other.m_delta))
{
KnLog(KnLogBug,"kntime: Adding two absolutes!");
}
return *this += other.m_tv;
}
bool operator<( const KnTimeImpl& other) const
{
return (m_tv.tv_sec < other.m_tv.tv_sec) ||
((m_tv.tv_sec == other.m_tv.tv_sec) && (m_tv.tv_usec < other.m_tv.tv_usec));
}
bool operator<=( const KnTimeImpl& other) const
{
return (*this < other) || (*this == other);
}
bool operator>( const KnTimeImpl& other) const
{
return !(*this <= other);
}
bool operator>=( const KnTimeImpl& other) const
{
return !(*this < other);
}
bool operator==( const KnTimeImpl& other ) const
{
return (m_tv.tv_sec == other.m_tv.tv_sec && m_tv.tv_usec == other.m_tv.tv_usec);
}
bool operator==( KnTime_t other ) const
{
return (static_cast<KnTime_t>(m_tv.tv_sec) == other);
}
KnTimeImpl& operator=( const KnTimeImpl& other )
{
m_delta = other.m_delta;
m_tv.tv_sec = other.m_tv.tv_sec ;
m_tv.tv_usec = other.m_tv.tv_usec ;
return *this ;
}
void setToNow()
{
#ifdef WIN32
#pragma warning ( disable: 4244 ) // "Possible loss of data" in 64->32 conversions.
// This code assumes filetime resolution is finer than
// timeval resolution.
#define FILETIME_RESOLUTION (10*1000*1000) // 100 nsec interval expressed as ticks/second
static ULARGE_INTEGER epochTime;
if (epochTime.QuadPart == 0UL)
{
FILETIME epochTemp;
SYSTEMTIME unix;
unix.wYear = 1970;
unix.wMonth = 1;
unix.wDay = 1;
unix.wHour = 0;
unix.wMinute = 0;
unix.wSecond = 0;
unix.wMilliseconds = 0;
if (!SystemTimeToFileTime(&unix, &epochTemp))
{
KnLog(KnLogBug,"kntime: could not reference to 1970");
}
epochTime.LowPart = epochTemp.dwLowDateTime;
epochTime.HighPart = epochTemp.dwHighDateTime;
}
FILETIME myTime;
GetSystemTimeAsFileTime( &myTime );
ULARGE_INTEGER temp;
temp.LowPart = myTime.dwLowDateTime;
temp.HighPart = myTime.dwHighDateTime;
temp.QuadPart -= epochTime.QuadPart ;
m_tv.tv_sec = temp.QuadPart / FILETIME_RESOLUTION;
m_tv.tv_usec = (temp.QuadPart % FILETIME_RESOLUTION) /
(FILETIME_RESOLUTION / TIMEVAL_RESOLUTION) ;
#if 0
KnLog(KnLogDebug,"KnTime: At time(0) %ld got m_tv %ld.%ld",
time(0),m_tv.tv_sec,m_tv.tv_usec);
#endif
#else
gettimeofday( &m_tv, 0 ) ;
#endif // WIN32
}
private:
void setString() const
{
m_string.reset();
m_string.printf("%ld.%6.6ld",m_tv.tv_sec, m_tv.tv_usec);
};
bool parseAbsolute( struct timeval& tv, const char *pStr )
{
const char* pDot = strchr(pStr,'.');
if ( pDot != 0 )
{
KnString high( pStr, pDot - pStr);
KnString low( pDot+1);
tv.tv_sec = high.toLong();
tv.tv_usec = low.toLong();
}
else
{
KnString sec( pStr );
tv.tv_usec = 0;
tv.tv_sec = sec.toLong();
}
return true ;
};
// Set *this to the time you want to be relative to before calling
// this function.
void parseRelative(const char *pStr, KnTimeDefault dir )
{
if (KnStringConstants::infinity.equalsIgnoreCase(pStr))
{
switch (dir)
{
case KNTIME_ABSOLUTE:
case KNTIME_FUTURE:
m_tv.tv_sec = LONG_MAX ;
m_tv.tv_usec = LONG_MAX ;
break ;
case KNTIME_PAST:
m_tv.tv_sec = 0UL ;
m_tv.tv_usec = 0UL ;
break ;
}
}
else if (KnStringConstants::now.equalsIgnoreCase(pStr))
{
if (m_delta)
{
KnLog(KnLogBug,"kntime: Setting relative time to now?!");
}
setToNow();
}
else
{
struct timeval temp ;
parseAbsolute( temp, pStr );
switch (dir)
{
case KNTIME_ABSOLUTE:
m_tv = temp ;
break ;
case KNTIME_FUTURE:
*this += temp;
break ;
case KNTIME_PAST:
*this -= temp;
break ;
}
}
}
mutable KnString m_string;
struct timeval m_tv;
bool m_delta;
};
KnTime::KnTime()
: pImpl( new KnTime::KnTimeImpl )
{}
KnTime::KnTime( const KnString ×tring, KnTimeDefault bDflt, bool bDelta )
: pImpl( new KnTime::KnTimeImpl( timestring, bDflt, bDelta ) )
{}
KnTime::KnTime( const KnTime& other )
: pImpl( new KnTime::KnTimeImpl )
{
*pImpl = *(other.pImpl);
}
KnTime::~KnTime()
{
if (pImpl) delete( pImpl ) ;
pImpl = 0;
}
const KnString&
KnTime::asString() const
{
return pImpl->asString();
}
const char *
KnTime::c_str() const
{
return pImpl->c_str();
}
KnTime_t
KnTime::asTime_T() const
{
return pImpl->asTime_T();
}
double
KnTime::asFloat() const
{
return pImpl->asFloat();
}
struct timeval
KnTime::asTimeVal() const
{
return pImpl->asTimeVal();
}
KnTime& KnTime::operator+=( const KnTime& other )
{
*pImpl += *(other.pImpl);
return *this;
}
bool KnTime::operator<( const KnTime& other) const
{
return *pImpl < *(other.pImpl);
}
bool KnTime::operator<=( const KnTime& other) const
{
return *pImpl <= *(other.pImpl);
}
bool KnTime::operator>( const KnTime& other) const
{
return *pImpl > *(other.pImpl);
}
bool KnTime::operator>=( const KnTime& other) const
{
return *pImpl >= *(other.pImpl);
}
bool KnTime::operator==( const KnTime& other) const
{
return *pImpl == *(other.pImpl);
}
bool KnTime::operator==( const KnTime_t other) const
{
return *pImpl == other;
}
bool KnTime::operator!=( const KnTime& other) const
{
return !(*pImpl == *(other.pImpl));
}
bool KnTime::operator!=( const KnTime_t other) const
{
return !(*pImpl == other);
}
KnTime& KnTime::operator=( const KnTime& other )
{
*pImpl = *(other.pImpl);
return *this ;
}
void KnTime::setToNow()
{
pImpl->setToNow();
}
EXPORT_LIBRARY_KNUTIL
KnTime operator+( const KnTime& lhs, const KnTime& rhs )
{
return KnTime(lhs) += rhs;
};
--- NEW FILE: knurl.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/knurl.h"
#include <sstream>
// This class might well be extended to:
//
// Reference-count the URL for various storage strategies.
//
// Provide the last file component (for AOL-style pattern matching).
// (actually, the tail part should be separated out (from ? on)),
// A direct access routine for the last filecomponent would be nice)
KnUrl::KnUrl(const KnString &url)
: m_parsed(false)
{
m_string = new char[url.length()+1];
memset(m_string, 0, url.length()+1);
memcpy(m_string, url.c_str(), url.length());
}
KnUrl::KnUrl(const char *url)
: m_parsed(false)
{
m_string = new char[strlen(url)+1];
memset(m_string, 0, strlen(url)+1);
memcpy(m_string, url, strlen(url));
}
KnUrl::KnUrl( const char *scheme
, const char *user
, const char *password
, const char *host
, const int port
, const char *path
, const char * /* query */
, const char * /* fragment */
) :
m_string(0),
m_parsed(true),
m_scheme(scheme),
m_user(user),
m_password(password),
m_host(host),
m_port(),
m_path(path)
{
m_port.printf("%d",port);
std::string reverse(path);
int pos = m_path.lastIndexOf('/');
if (pos >= 0) {
m_basename += &(m_path.c_str()[pos + 1]);
} else {
m_basename = m_path;
}
}
KnUrl::~KnUrl()
{
SAFE_DELETE_ARRAY(m_string);
}
std::string
KnUrl::get_root() const {
if (!m_parsed) { parse(); }
if (!isAbsolute()) { return ""; };
std::string value(m_scheme.c_str());
value.append("://");
value.append(m_host.c_str());
#if 0
KnLog(KnLogDev,"knurl: parsed host part is '%s'",m_host.c_str());
#endif
// m_port is 0-length if you parse http://foo/fum . OTOH, it's
// got values like 80 & 443 in it sometimes, too. Maybe the parse
// should take care of that....
if (m_port.length() &&
!((m_scheme == KnStringConstants::http && m_port == "80") ||
(m_scheme == KnStringConstants::https && m_port == "443"))) {
value.append(":");
value.append(m_port.c_str());
}
#if 0
KnLog(KnLogDev,"knurl: reassembled root is '%s'",value.c_str());
#endif
return value;
}
void
KnUrl::parse() const
{
char *protocol, *host, *port, *path, *tail;
if (!m_string)
return;
Ns_ParseUrl(m_string,&protocol,&host,&port,&path,&tail);
m_host = host ;
m_port = port ;
m_scheme = protocol ;
m_path = "/";
m_path += path ;
m_basename = tail ;
m_path += tail ;
// Ns_ParseUrl won't find the user & password components if provided
// In fact, it'll take 'foo:fum@scum:30" and give us foo in m_host,
// and fum@scum:30 in port. Almost tempted me to rewrite without it.
int indexAt = m_port.indexOf('@');
if (indexAt >= 0)
{
m_user = m_host ;
m_host.reset();
m_password.reset();
m_password.printf("%.*s",indexAt,m_port.c_str());
int indexColon = m_port.indexOf(':',indexAt);
if (indexColon >= 0)
{
m_host.printf("%.*s",indexColon - indexAt - 1, m_port.c_str() + indexAt + 1);
KnString tmp(m_port);
m_port.reset() ; // Should be redundant.
m_port += tmp.c_str() + indexColon + 1;
}
else
{
m_host.printf("%s",m_port.c_str() + indexAt + 1);
m_port.reset();
}
}
#if 0
KnLog(KnLogDev,"knurl: parsed '%s' to protocol '%s', user/pw '%s/%s', location '%s', port '%s', path '%s'",
url,
m_protocol.c_str(),
m_user.c_str(),
m_password.c_str(),
m_host.c_str(),
m_port.c_str(),
m_path.c_str()
);
#endif
m_parsed = true ;
}
void
KnUrl::encode(std::string& buffer, const char *str)
{
Ns_DString tmp;
Ns_DStringInit(&tmp);
Ns_EncodeUrl(&tmp, const_cast<char*>(str));
buffer += tmp.string;
Ns_DStringFree(&tmp);
}
void
KnUrl::encode(KnString &buffer, const KnString &name)
{
Ns_DString tmp;
Ns_DStringInit(&tmp);
Ns_EncodeUrl(&tmp, const_cast<char*>(name.c_str()));
buffer += tmp.string;
Ns_DStringFree(&tmp);
}
void
KnUrl::encode(std::string& buffer, const KnString& name)
{
Ns_DString tmp;
Ns_DStringInit(&tmp);
Ns_EncodeUrl(&tmp, const_cast<char*>(name.c_str()));
buffer += tmp.string;
Ns_DStringFree(&tmp);
}
// Presume caller knows whether to put ? on or not depending
// on context.
void
KnUrl::encode(std::string& buf, const KnSet& set)
{
for (size_t ix = 0; ix < set.size(); ++ix)
{
if (ix)
{
buf += "&";
}
encode(buf, set[ix].m_key);
encode(buf += "=",set[ix].m_value);
}
}
// And now for NsSet. These should just be free functions, I think
void
KnUrl::encode(std::string& buf, const Ns_Set* set)
{
for (int jx = 0; jx < Ns_SetSize(set); ++jx)
{
if (jx)
{
buf += "&";
}
encode(buf, Ns_SetKey(set,jx));
encode(buf += "=",Ns_SetValue(set,jx));
}
}
--- NEW FILE: mtrand.cpp ---
#include "knexportlibraryknutilmodule.h"
#include "knutil/mtrand.h"
// mtrand.cpp
//
// Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. The names of its contributors may not be used to endorse or promote
// products derived from this software without specific prior written
// permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Any feedback is very welcome.
// http://www.math.keio.ac.jp/matumoto/emt.html
// email: matu...@math.keio.ac.jp
//
// Feedback about the C++ port should be sent to Jasper Bedaux,
// see http://www.bedaux.net/mtrand/ for e-mail address and info.
// non-inline function definitions and static member definitions cannot
// reside in header file because of the risk of multiple declarations
// initialization of static private members
unsigned long MTRand_int32::state[n] = {0x0UL};
int MTRand_int32::p = 0;
bool MTRand_int32::init = false;
void MTRand_int32::gen_state() { // generate new state vector
for (int i = 0; i < (n - m); ++i)
state[i] = state[i + m] ^ twiddle(state[i], state[i + 1]);
for (int i = n - m; i < (n - 1); ++i)
state[i] = state[i + m - n] ^ twiddle(state[i], state[i + 1]);
state[n - 1] = state[m - 1] ^ twiddle(state[n - 1], state[0]);
p = 0; // reset position
}
void MTRand_int32::seed(unsigned long s) { // init by 32 bit seed
state[0] = s & 0xFFFFFFFFUL; // for > 32 bit machines
for (int i = 1; i < n; ++i) {
state[i] = 1812433253UL * (state[i - 1] ^ (state[i - 1] >> 30)) + i;
// see Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier
// in the previous versions, MSBs of the seed affect only MSBs of the array state
// 2002/01/09 modified by Makoto Matsumoto
state[i] &= 0xFFFFFFFFUL; // for > 32 bit machines
}
p = n; // force gen_state() to be called for next random number
}
void MTRand_int32::seed(const unsigned long* array, int size) { // init by array
seed(19650218UL);
int i = 1, j = 0;
for (int k = ((n > size) ? n : size); k; --k) {
state[i] = (state[i] ^ ((state[i - 1] ^ (state[i - 1] >> 30)) * 1664525UL))
+ array[j] + j; // non linear
state[i] &= 0xFFFFFFFFUL; // for > 32 bit machines
++j; j %= size;
if ((++i) == n) { state[0] = state[n - 1]; i = 1; }
}
for (int k = n - 1; k; --k) {
state[i] = (state[i] ^ ((state[i - 1] ^ (state[i - 1] >> 30)) * 1566083941UL)) - i;
state[i] &= 0xFFFFFFFFUL; // for > 32 bit machines
if ((++i) == n) { state[0] = state[n - 1]; i = 1; }
}
state[0] = 0x80000000UL; // MSB is 1; assuring non-zero initial array
p = n; // force gen_state() to be called for next random number
}
--- NEW FILE: randomnumber.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/randomnumber.h"
#define ind(mm, x) \
(*(reinterpret_cast<uint32_t *>( \
reinterpret_cast<uint8_t *>(mm) + ((x) & ((RANDSIZ - 1) << 2)))))
#define mix(a, b, c, d, e, f, g, h) \
{ \
a ^= b << 11; d += a; b += c; \
b ^= c >> 2; e += b; c += d; \
c ^= d << 8; f += c; d += e; \
d ^= e >> 16; g += d; e += f; \
e ^= f << 10; h += e; f += g; \
f ^= g >> 4; a += f; g += h; \
g ^= h << 8; b += g; h += a; \
h ^= a >> 9; c += h; a += b; \
}
#define rngstep(mix, a, b, mm, m, m2, r, x) \
{ \
x = *m; \
a = (a ^ (mix)) + *(m2++); \
*(m++) = y = ind(mm, x) + a + b; \
*(r++) = b = ind(mm, y >> RANDSIZL) + x; \
}
RandomNumber::RandomNumber(const uint32_t *seed, uint32_t len)
{
memset(m_randrsl, 0, sizeof(m_randrsl));
if (len > RANDSIZ)
len = RANDSIZ;
if (seed != 0)
memcpy(m_randrsl, seed, len * sizeof(*seed));
randinit();
}
RandomNumber::~RandomNumber()
{
}
uint32_t RandomNumber::rand()
{
if (m_randcnt-- == 0)
{
isaac();
m_randcnt = RANDSIZ - 1;
}
return m_randrsl[m_randcnt];
}
void RandomNumber::isaac()
{
uint32_t a = m_randa;
uint32_t b = m_randb + (++m_randc);
uint32_t x = 0;
uint32_t y = 0;
uint32_t *m = m_randmem;
uint32_t *m2 = m_randmem + (RANDSIZ / 2);
uint32_t *mm = m_randmem;
uint32_t *r = m_randrsl;
while (m < m_randmem + (RANDSIZ / 2))
{
rngstep(a << 13, a, b, mm, m, m2, r, x);
rngstep(a >> 6, a, b, mm, m, m2, r, x);
rngstep(a << 2, a, b, mm, m, m2, r, x);
rngstep(a >> 16, a, b, mm, m, m2, r, x);
}
m2 = mm;
while (m2 < m_randmem + (RANDSIZ / 2))
{
rngstep(a << 13, a, b, mm, m, m2, r, x);
rngstep(a >> 6, a, b, mm, m, m2, r, x);
rngstep(a << 2, a, b, mm, m, m2, r, x);
rngstep(a >> 16, a, b, mm, m, m2, r, x);
}
m_randa = a;
m_randb = b;
}
void RandomNumber::randinit()
{
uint32_t a = 0x9e3779b9;
uint32_t b = 0x9e3779b9;
uint32_t c = 0x9e3779b9;
uint32_t d = 0x9e3779b9;
uint32_t e = 0x9e3779b9;
uint32_t f = 0x9e3779b9;
uint32_t g = 0x9e3779b9;
uint32_t h = 0x9e3779b9;
m_randa = 0;
m_randb = 0;
m_randc = 0;
for (uint32_t i = 0; i < 4; ++i)
{
mix(a, b, c, d, e, f, g, h);
}
for (uint32_t j = 0; j < RANDSIZ; j += 8)
{
a += m_randrsl[j ]; b += m_randrsl[j + 1]; c += m_randrsl[j + 2]; d += m_randrsl[j + 3];
e += m_randrsl[j + 4]; f += m_randrsl[j + 5]; g += m_randrsl[j + 6]; h += m_randrsl[j + 7];
mix(a, b, c, d, e, f, g, h);
m_randmem[j ] = a; m_randmem[j + 1] = b; m_randmem[j + 2] = c; m_randmem[j + 3] = d;
m_randmem[j + 4] = e; m_randmem[j + 5] = f; m_randmem[j + 6] = g; m_randmem[j + 7] = h;
}
for (uint32_t k = 0; k < RANDSIZ; k += 8)
{
a += m_randmem[k ]; b += m_randmem[k + 1]; c += m_randmem[k + 2]; d += m_randmem[k + 3];
e += m_randmem[k + 4]; f += m_randmem[k + 5]; g += m_randmem[k + 6]; h += m_randmem[k + 7];
mix(a, b, c, d, e, f, g, h);
m_randmem[k ] = a; m_randmem[k + 1] = b; m_randmem[k + 2] = c; m_randmem[k + 3] = d;
m_randmem[k + 4] = e; m_randmem[k + 5] = f; m_randmem[k + 6] = g; m_randmem[k + 7] = h;
}
isaac();
m_randcnt = RANDSIZ;
}
--- NEW FILE: shahash.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/shahash.h"
#ifdef WIN32
#define SHA_ROTATE(a, n) _lrotl(a, n)
#else
#define SHA_ROTATE(a, n) ((a << n) | (a >> (32 - n)))
#endif /* WIN32 */
#if defined(__linux) && defined(__i386)
#define SHA_UPDATE(f, i) \
{ \
f = block[i & 0xF] ^ block[(i + 2) & 0xF] ^ block[(i + 8) & 0xF] ^ \
block[(i + 13) & 0xF]; \
block[i & 0xF] = f = SHA_ROTATE(f, 1); \
}
#else
#define SHA_UPDATE(f, i) \
{ \
f = block[i & 0xF] ^ block[(i + 2) & 0xF] ^ block[(i + 8) & 0xF] ^ \
block[(i + 13) & 0xF]; \
f = block[i & 0xF] = SHA_ROTATE(f, 1); \
}
#endif /* __linux */
#define SHA_00_15(i, a, b, c, d, e, f) \
{ \
f = block[i] + e + 0x5A827999 + SHA_ROTATE(a, 5) + (((c ^ d) & b) ^ d); \
b = SHA_ROTATE(b, 30); \
}
#define SHA_16_19(i, a, b, c, d, e, f) \
{ \
SHA_UPDATE(f, i); \
f += e + 0x5A827999 + SHA_ROTATE(a, 5) + (((c ^ d) & b) ^ d); \
b = SHA_ROTATE(b, 30); \
}
#define SHA_20_39(i, a, b, c, d, e, f) \
{ \
SHA_UPDATE(f, i); \
f += e + 0x6ED9EBA1 + SHA_ROTATE(a, 5) + (b ^ c ^ d); \
b = SHA_ROTATE(b, 30); \
}
#define SHA_40_59(i, a, b, c, d, e, f) \
{ \
SHA_UPDATE(f, i); \
f += e + 0x8F1BBCDC + SHA_ROTATE(a, 5) + ((b & c) | ((b | c) & d)); \
b = SHA_ROTATE(b, 30); \
}
#define SHA_60_79(i, a, b, c, d, e, f) \
{ \
SHA_UPDATE(f, i); \
f = block[i & 0xF] + e + 0xCA62C1D6 + SHA_ROTATE(a, 5) + (b ^ c ^ d); \
b = SHA_ROTATE(b, 30); \
}
ShaHash::ShaHash()
{
m_digest[0] = 0x67452301;
m_digest[1] = 0xEFCDAB89;
m_digest[2] = 0x98BADCFE;
m_digest[3] = 0x10325476;
m_digest[4] = 0xC3D2E1F0;
// Didn't used to initialize, but Purify complains
// and I can't see how initializing it can hurt. -- ReC 03/02/04
for (int ix = 0; ix < BlockWords;++ix)
{
m_block[ix] = 0;
}
m_count[0] = 0;
m_count[1] = 0;
m_index = 0;
}
ShaHash::~ShaHash()
{
}
void ShaHash::transform(uint32_t *digest, uint32_t *block)
{
uint32_t a = digest[0];
uint32_t b = digest[1];
uint32_t c = digest[2];
uint32_t d = digest[3];
uint32_t e = digest[4];
uint32_t t = 0;
SHA_00_15( 0, a, b, c, d, e, t);
SHA_00_15( 1, t, a, b, c, d, e);
SHA_00_15( 2, e, t, a, b, c, d);
SHA_00_15( 3, d, e, t, a, b, c);
SHA_00_15( 4, c, d, e, t, a, b);
SHA_00_15( 5, b, c, d, e, t, a);
SHA_00_15( 6, a, b, c, d, e, t);
SHA_00_15( 7, t, a, b, c, d, e);
SHA_00_15( 8, e, t, a, b, c, d);
SHA_00_15( 9, d, e, t, a, b, c);
SHA_00_15(10, c, d, e, t, a, b);
SHA_00_15(11, b, c, d, e, t, a);
SHA_00_15(12, a, b, c, d, e, t);
SHA_00_15(13, t, a, b, c, d, e);
SHA_00_15(14, e, t, a, b, c, d);
SHA_00_15(15, d, e, t, a, b, c);
SHA_16_19(16, c, d, e, t, a, b);
SHA_16_19(17, b, c, d, e, t, a);
SHA_16_19(18, a, b, c, d, e, t);
SHA_16_19(19, t, a, b, c, d, e);
SHA_20_39(20, e, t, a, b, c, d);
SHA_20_39(21, d, e, t, a, b, c);
SHA_20_39(22, c, d, e, t, a, b);
SHA_20_39(23, b, c, d, e, t, a);
SHA_20_39(24, a, b, c, d, e, t);
SHA_20_39(25, t, a, b, c, d, e);
SHA_20_39(26, e, t, a, b, c, d);
SHA_20_39(27, d, e, t, a, b, c);
SHA_20_39(28, c, d, e, t, a, b);
SHA_20_39(29, b, c, d, e, t, a);
SHA_20_39(30, a, b, c, d, e, t);
SHA_20_39(31, t, a, b, c, d, e);
SHA_20_39(32, e, t, a, b, c, d);
SHA_20_39(33, d, e, t, a, b, c);
SHA_20_39(34, c, d, e, t, a, b);
SHA_20_39(35, b, c, d, e, t, a);
SHA_20_39(36, a, b, c, d, e, t);
SHA_20_39(37, t, a, b, c, d, e);
SHA_20_39(38, e, t, a, b, c, d);
SHA_20_39(39, d, e, t, a, b, c);
SHA_40_59(40, c, d, e, t, a, b);
SHA_40_59(41, b, c, d, e, t, a);
SHA_40_59(42, a, b, c, d, e, t);
SHA_40_59(43, t, a, b, c, d, e);
SHA_40_59(44, e, t, a, b, c, d);
SHA_40_59(45, d, e, t, a, b, c);
SHA_40_59(46, c, d, e, t, a, b);
SHA_40_59(47, b, c, d, e, t, a);
SHA_40_59(48, a, b, c, d, e, t);
SHA_40_59(49, t, a, b, c, d, e);
SHA_40_59(50, e, t, a, b, c, d);
SHA_40_59(51, d, e, t, a, b, c);
SHA_40_59(52, c, d, e, t, a, b);
SHA_40_59(53, b, c, d, e, t, a);
SHA_40_59(54, a, b, c, d, e, t);
SHA_40_59(55, t, a, b, c, d, e);
SHA_40_59(56, e, t, a, b, c, d);
SHA_40_59(57, d, e, t, a, b, c);
SHA_40_59(58, c, d, e, t, a, b);
SHA_40_59(59, b, c, d, e, t, a);
SHA_60_79(60, a, b, c, d, e, t);
SHA_60_79(61, t, a, b, c, d, e);
SHA_60_79(62, e, t, a, b, c, d);
SHA_60_79(63, d, e, t, a, b, c);
SHA_60_79(64, c, d, e, t, a, b);
SHA_60_79(65, b, c, d, e, t, a);
SHA_60_79(66, a, b, c, d, e, t);
SHA_60_79(67, t, a, b, c, d, e);
SHA_60_79(68, e, t, a, b, c, d);
SHA_60_79(69, d, e, t, a, b, c);
SHA_60_79(70, c, d, e, t, a, b);
SHA_60_79(71, b, c, d, e, t, a);
SHA_60_79(72, a, b, c, d, e, t);
SHA_60_79(73, t, a, b, c, d, e);
SHA_60_79(74, e, t, a, b, c, d);
SHA_60_79(75, d, e, t, a, b, c);
SHA_60_79(76, c, d, e, t, a, b);
SHA_60_79(77, b, c, d, e, t, a);
SHA_60_79(78, a, b, c, d, e, t);
SHA_60_79(79, t, a, b, c, d, e);
digest[0] += e;
digest[1] += t;
digest[2] += a;
digest[3] += b;
digest[4] += c;
}
void ShaHash::final(uint8_t *digest)
{
uint32_t *p = m_block;
uint32_t i = m_index >> 2;
uint32_t l = p[i];
switch (m_index & 0x3)
{
case 0: l = 0x80000000; break;
case 1: l |= 0x00800000; break;
case 2: l |= 0x00008000; break;
case 3: l |= 0x00000080; break;
}
p[i++] = l;
if (m_index >= 56)
{
for (; i < BlockWords; ++i)
{
p[i] = 0;
}
transform(m_digest, p);
i = 0;
}
for (; i < BlockWords - 2; ++i)
{
p[i] = 0;
}
p[BlockWords - 2] = m_count[1];
p[BlockWords - 1] = m_count[0];
transform(m_digest, p);
uint32_t a = m_digest[0];
uint32_t b = m_digest[1];
uint32_t c = m_digest[2];
uint32_t d = m_digest[3];
uint32_t e = m_digest[4];
digest[ 0] = static_cast<uint8_t>(a >> 24);
digest[ 1] = static_cast<uint8_t>(a >> 16);
digest[ 2] = static_cast<uint8_t>(a >> 8);
digest[ 3] = static_cast<uint8_t>(a);
digest[ 4] = static_cast<uint8_t>(b >> 24);
digest[ 5] = static_cast<uint8_t>(b >> 16);
digest[ 6] = static_cast<uint8_t>(b >> 8);
digest[ 7] = static_cast<uint8_t>(b);
digest[ 8] = static_cast<uint8_t>(c >> 24);
digest[ 9] = static_cast<uint8_t>(c >> 16);
digest[10] = static_cast<uint8_t>(c >> 8);
digest[11] = static_cast<uint8_t>(c);
digest[12] = static_cast<uint8_t>(d >> 24);
digest[13] = static_cast<uint8_t>(d >> 16);
digest[14] = static_cast<uint8_t>(d >> 8);
digest[15] = static_cast<uint8_t>(d);
digest[16] = static_cast<uint8_t>(e >> 24);
digest[17] = static_cast<uint8_t>(e >> 16);
digest[18] = static_cast<uint8_t>(e >> 8);
digest[19] = static_cast<uint8_t>(e);
m_index = 0;
}
void ShaHash::update(const void *buf, uint32_t len)
{
if (len == 0)
return;
uint32_t l = m_count[0] + (len << 3);
if (l < m_count[0])
++m_count[1];
m_count[1] += len >> 29;
m_count[0] = l;
const uint8_t *data = static_cast<const uint8_t *>(buf);
if (m_index > 0)
{
uint32_t *p = m_block;
uint32_t sc = m_index & 0x3;
uint32_t sw = m_index >> 2;
if (m_index + len >= BlockLen)
{
l = p[sw];
switch (sc)
{
case 0: l = static_cast<uint32_t>(*data++) << 24;
case 1: l |= static_cast<uint32_t>(*data++) << 16;
case 2: l |= static_cast<uint32_t>(*data++) << 8;
case 3: l |= static_cast<uint32_t>(*data++);
}
p[sw++] = l;
for (; sw < BlockWords; ++sw, data += 4)
{
p[sw] = ((static_cast<uint32_t>(data[0]) << 24) |
(static_cast<uint32_t>(data[1]) << 16) |
(static_cast<uint32_t>(data[2]) << 8) |
(static_cast<uint32_t>(data[3])));
}
len -= BlockLen - m_index;
transform(m_digest, p);
m_index = 0;
}
else
{
m_index += len;
if (sc + len < 4)
{
l = p[sw];
switch (sc)
{
case 0:
l = static_cast<uint32_t>(*data++) << 24;
if (--len == 0) break;
case 1:
l |= static_cast<uint32_t>(*data++) << 16;
if (--len == 0) break;
case 2:
l |= static_cast<uint32_t>(*data++) << 8;
}
p[sw] = l;
}
else
{
uint32_t ec = m_index & 0x3;
uint32_t ew = m_index >> 2;
l = p[sw];
switch (sc)
{
case 0: l = static_cast<uint32_t>(*data++) << 24;
case 1: l |= static_cast<uint32_t>(*data++) << 16;
case 2: l |= static_cast<uint32_t>(*data++) << 8;
case 3: l |= static_cast<uint32_t>(*data++);
}
p[sw++] = l;
for (; sw < ew; ++sw, data += 4)
{
p[sw] = ((static_cast<uint32_t>(data[0]) << 24) |
(static_cast<uint32_t>(data[1]) << 16) |
(static_cast<uint32_t>(data[2]) << 8) |
(static_cast<uint32_t>(data[3])));
}
if (ec > 0)
{
l = 0;
data += ec;
switch (ec)
{
case 3: l = static_cast<uint32_t>(*--data) << 8;
case 2: l |= static_cast<uint32_t>(*--data) << 16;
case 1: l |= static_cast<uint32_t>(*--data) << 24;
}
p[sw] = l;
}
}
return;
}
}
uint32_t *p = m_block;
while (len >= BlockLen)
{
for (uint32_t sw = BlockWords / 4; sw > 0; --sw, data += 16)
{
*p++ = ((static_cast<uint32_t>(data[ 0]) << 24) |
(static_cast<uint32_t>(data[ 1]) << 16) |
(static_cast<uint32_t>(data[ 2]) << 8) |
(static_cast<uint32_t>(data[ 3])));
*p++ = ((static_cast<uint32_t>(data[ 4]) << 24) |
(static_cast<uint32_t>(data[ 5]) << 16) |
(static_cast<uint32_t>(data[ 6]) << 8) |
(static_cast<uint32_t>(data[ 7])));
*p++ = ((static_cast<uint32_t>(data[ 8]) << 24) |
(static_cast<uint32_t>(data[ 9]) << 16) |
(static_cast<uint32_t>(data[10]) << 8) |
(static_cast<uint32_t>(data[11])));
*p++ = ((static_cast<uint32_t>(data[12]) << 24) |
(static_cast<uint32_t>(data[13]) << 16) |
(static_cast<uint32_t>(data[14]) << 8) |
(static_cast<uint32_t>(data[15])));
}
p = m_block;
transform(m_digest, p);
len -= BlockLen;
}
m_index = len;
uint32_t ec = len & 0x3;
uint32_t ew = len >> 2;
uint32_t sw = 0;
for (; sw < ew; ++sw, data += 4)
{
p[sw] = ((static_cast<uint32_t>(data[0]) << 24) |
(static_cast<uint32_t>(data[1]) << 16) |
(static_cast<uint32_t>(data[2]) << 8) |
(static_cast<uint32_t>(data[3])));
}
l = 0;
data += ec;
switch (ec)
{
case 3: l = static_cast<uint32_t>(*--data) << 8;
case 2: l |= static_cast<uint32_t>(*--data) << 16;
case 1: l |= static_cast<uint32_t>(*--data) << 24;
}
p[sw] = l;
}
--- NEW FILE: uuidgenerator.cpp ---
#include "knexportlibraryknutilmodule.h"
/**
* (c) Copyright 2008 KnowNow, Inc., Sunnyvale CA
*
* @KNOWNOW_LICENSE_START@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "KnowNow" is a trademark of KnowNow, Inc. and may not
* be used to endorse or promote any product without prior written
* permission from KnowNow, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL KNOWNOW, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @KNOWNOW_LICENSE_END@
**/
#include "knutil/uuidgenerator.h"
#ifdef WIN32
#include <rpc.h>
bool UuidGenerator::next(KnString &kns)
{
/*
* Create the uuid in binary form.
*/
UUID uuid;
if (UuidCreate(&uuid) != RPC_S_OK)
return false;
/*
* Convert from binary into a string.
*/
uint8_t *str;
if (UuidToString(&uuid, &str) != RPC_S_OK)
return false;
/*
* Append to the given string.
*/
kns += reinterpret_cast<char *>(str);
/*
* Dispose of temporary memory _not from nsd_.
*/
RpcStringFree(&str);
return true;
}
#else
bool UuidGenerator::next(KnString &kns)
{
/*
* File descriptor to the crytographic device.
*/
static int fd = open("/dev/urandom", O_RDONLY);
/*
* A uuid consists of 16 octets.
*/
uint8_t oc[16];
/*
* Loop until all octets have been read.
*/
for (size_t n = 0; n < sizeof(oc);)
{
ssize_t len = read(fd, &oc[n], sizeof(oc) - n);
if (len > 0)
{
n += len;
}
else if (len == 0 || (len < 0 && errno != EINTR))
{
return false;
}
}
/*
* Unpack octets.
*/
uint32_t time_low = ((static_cast<uint32_t>(oc[0]) << 24) |
(static_cast<uint32_t>(oc[1]) << 16) |
(static_cast<uint32_t>(oc[2]) << 8) |
(static_cast<uint32_t>(oc[3])));
uint16_t time_mid = ((static_cast<uint16_t>(oc[4]) << 8) |
(static_cast<uint16_t>(oc[5])));
uint16_t time_hi = ((static_cast<uint16_t>(oc[6]) << 8) |
(static_cast<uint16_t>(oc[7])));
/*
* Set version information.
*/
kns.printf("%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
time_low, time_mid, ((time_hi & 0x0FFF) | 0x4000),
((oc[8] & 0x3F) | 0x80), oc[9], oc[10], oc[11],
oc[12], oc[13], oc[14], oc[15]);
return true;
}
#endif