Added ChatServer and soePlatform
This commit is contained in:
@@ -0,0 +1,299 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// The author of this code is Justin Randall
|
||||
//
|
||||
// I have made modifications to the ByteStream
|
||||
// and AutoByteStream classes in order to make them suitable
|
||||
// for use in messaging systems which require objects that
|
||||
// are copyable and assignable. It is also desirable for
|
||||
// the ByteStream object to use a flexible allocator system
|
||||
// that may support multi-threaded programming models.
|
||||
|
||||
|
||||
#include "Archive.h"
|
||||
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#if defined(USE_ARCHIVE_MUTEX)
|
||||
CMutex ByteStreamMutex;
|
||||
#endif
|
||||
|
||||
// Default to Data object reuse
|
||||
bool ByteStream::ms_reuseData = true;
|
||||
|
||||
#if defined (PASCAL_STRING)
|
||||
#pragma message ("--- Packing pascal style strings ---")
|
||||
unsigned get(Base::ByteStream::ReadIterator& source, std::string& target)
|
||||
{
|
||||
unsigned int size = 0;
|
||||
if (source.getSize() < sizeof(size))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
Base::get (source, size);
|
||||
|
||||
const unsigned char *buf = source.getBuffer();
|
||||
|
||||
target.assign((const char *)buf, (const char *)(buf + size));
|
||||
|
||||
const unsigned int readSize = size * sizeof(char);
|
||||
source.advance(readSize);
|
||||
|
||||
return (readSize + sizeof(size));
|
||||
}
|
||||
|
||||
void put(ByteStream& target, const std::string& source)
|
||||
{
|
||||
const unsigned int size = source.size();
|
||||
put(target, size);
|
||||
target.put(source.data(), size * sizeof (char));
|
||||
}
|
||||
|
||||
#else
|
||||
#pragma message ("--- Packing c style strings ---")
|
||||
unsigned get(ByteStream::ReadIterator & source, std::string & target)
|
||||
{
|
||||
target = reinterpret_cast<const char *>(source.getBuffer());
|
||||
|
||||
if (target.length() > source.getSize())
|
||||
return 0;
|
||||
|
||||
source.advance(target.length() + 1);
|
||||
|
||||
return (target.length() + 1);
|
||||
}
|
||||
|
||||
|
||||
void put(ByteStream & target, const std::string & source)
|
||||
{
|
||||
target.put(source.c_str(), source.size()+1);
|
||||
}
|
||||
#endif
|
||||
|
||||
ByteStream::ReadIterator::ReadIterator() :
|
||||
readPtr(0),
|
||||
stream(0)
|
||||
{
|
||||
}
|
||||
|
||||
ByteStream::ReadIterator::ReadIterator(const ReadIterator & source) :
|
||||
readPtr(source.readPtr),
|
||||
stream(source.stream)
|
||||
{
|
||||
}
|
||||
|
||||
ByteStream::ReadIterator::ReadIterator(const ByteStream & source) :
|
||||
readPtr(0),
|
||||
stream(&source)
|
||||
{
|
||||
}
|
||||
|
||||
ByteStream::ReadIterator::~ReadIterator()
|
||||
{
|
||||
stream = 0;
|
||||
}
|
||||
|
||||
ByteStream::ByteStream() :
|
||||
allocatedSize(0),
|
||||
beginReadIterator(),
|
||||
data(NULL),
|
||||
size(0),
|
||||
lastPutSize(0)
|
||||
{
|
||||
data = Data::getNewData();
|
||||
beginReadIterator = ReadIterator(*this);
|
||||
}
|
||||
|
||||
ByteStream::ByteStream(const unsigned char * const newBuffer, const unsigned int bufferSize) :
|
||||
allocatedSize(bufferSize),
|
||||
data(0),
|
||||
size(bufferSize),
|
||||
lastPutSize(0)
|
||||
{
|
||||
data = Data::getNewData();
|
||||
if(data->size < size)
|
||||
{
|
||||
delete[] data->buffer;
|
||||
data->buffer = new unsigned char[size];
|
||||
data->size = size;
|
||||
}
|
||||
memcpy(data->buffer, newBuffer, size);
|
||||
beginReadIterator = ReadIterator(*this);
|
||||
}
|
||||
|
||||
ByteStream::ByteStream(const ByteStream & source):
|
||||
allocatedSize(source.getSize()), // only allocate what is really there, be opportinistic when grow()'ing
|
||||
data(source.data),
|
||||
size(source.getSize()),
|
||||
lastPutSize(source.lastPutSize)
|
||||
{
|
||||
source.data->ref();
|
||||
beginReadIterator = ReadIterator(*this);
|
||||
}
|
||||
|
||||
ByteStream::ByteStream(ReadIterator & source) :
|
||||
allocatedSize(0),
|
||||
size(0),
|
||||
lastPutSize(0)
|
||||
{
|
||||
data = Data::getNewData();
|
||||
put(source.getBuffer(), source.getSize());
|
||||
source.advance(source.getSize());
|
||||
beginReadIterator = ReadIterator(*this);
|
||||
}
|
||||
|
||||
ByteStream::~ByteStream()
|
||||
{
|
||||
data->deref();
|
||||
allocatedSize = 0;
|
||||
data = 0; //lint !e672 (data deref insures the data is deleted if no one references it)
|
||||
size = 0;
|
||||
}
|
||||
|
||||
ByteStream & ByteStream::operator=(const ByteStream & rhs)
|
||||
{
|
||||
if(this != &rhs)
|
||||
{
|
||||
data->deref(); // deref local data
|
||||
rhs.data->ref();
|
||||
allocatedSize = rhs.allocatedSize;
|
||||
size = rhs.size;
|
||||
data = rhs.data; //lint !e672 (data is ref counted)
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void ByteStream::get(void * target, ReadIterator & readIterator, const unsigned long int targetSize) const
|
||||
{
|
||||
assert(readIterator.getReadPosition() + targetSize <= allocatedSize);
|
||||
memcpy(target, &data->buffer[readIterator.getReadPosition()], targetSize);
|
||||
}
|
||||
|
||||
void ByteStream::put(const void * const source, const unsigned int sourceSize)
|
||||
{
|
||||
if(data->getRef() > 1)
|
||||
{
|
||||
const unsigned char * const tmp = data->buffer;
|
||||
data->deref();
|
||||
data = Data::getNewData();
|
||||
if(data->size < sourceSize)
|
||||
{
|
||||
delete[] data->buffer;
|
||||
data->buffer = new unsigned char[size];
|
||||
data->size = size;
|
||||
}
|
||||
memcpy(data->buffer, tmp, size);
|
||||
allocatedSize = size;
|
||||
}
|
||||
growToAtLeast(size + sourceSize);
|
||||
memcpy(&data->buffer[size], source, sourceSize);
|
||||
size += sourceSize;
|
||||
if (sourceSize > 0)
|
||||
lastPutSize = sourceSize;
|
||||
}
|
||||
|
||||
bool ByteStream::overwriteEnd(const void * const source, const unsigned int sourceSize)
|
||||
{
|
||||
if(data->getRef() <= 1 &&
|
||||
lastPutSize == sourceSize &&
|
||||
sourceSize <= data->size)
|
||||
{
|
||||
memcpy(&data->buffer[size-sourceSize], source, sourceSize);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void ByteStream::reAllocate(const unsigned int newSize)
|
||||
{
|
||||
allocatedSize = newSize;
|
||||
if(data->size < allocatedSize)
|
||||
{
|
||||
unsigned char * tmp = new unsigned char[newSize];
|
||||
if(data->buffer != NULL)
|
||||
memcpy(tmp, data->buffer, size);
|
||||
delete[] data->buffer;
|
||||
data->buffer = tmp;
|
||||
data->size = newSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
AutoByteStream::AutoByteStream() :
|
||||
members()
|
||||
{
|
||||
}
|
||||
|
||||
AutoByteStream::~AutoByteStream()
|
||||
{
|
||||
}
|
||||
|
||||
void AutoByteStream::addVariable(AutoVariableBase & newVariable)
|
||||
{
|
||||
members.push_back(&newVariable);
|
||||
}
|
||||
|
||||
const unsigned int AutoByteStream::getItemCount() const
|
||||
{
|
||||
return members.size();
|
||||
}
|
||||
|
||||
void AutoByteStream::pack(ByteStream & target) const
|
||||
{
|
||||
std::vector<AutoVariableBase *>::const_iterator i;
|
||||
unsigned short packedSize=static_cast<unsigned short>(members.size());
|
||||
put(target,packedSize);
|
||||
for(i = members.begin(); i != members.end(); ++i)
|
||||
{
|
||||
(*i)->pack(target);
|
||||
}
|
||||
}
|
||||
|
||||
void AutoByteStream::unpack(ByteStream::ReadIterator & source)
|
||||
{
|
||||
std::vector<AutoVariableBase *>::iterator i;
|
||||
unsigned short packedSize;
|
||||
get(source,packedSize);
|
||||
for(i = members.begin(); i != members.end(); ++i)
|
||||
{
|
||||
(*i)->unpack(source);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
AutoVariableBase::AutoVariableBase()
|
||||
{
|
||||
}
|
||||
|
||||
AutoVariableBase::~AutoVariableBase()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,756 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// The author of this code is Justin Randall
|
||||
//
|
||||
// I have made modifications to the ByteStream
|
||||
// and AutoByteStream classes in order to make them suitable
|
||||
// for use in messaging systems which require objects that
|
||||
// are copyable and assignable. It is also desirable for
|
||||
// the ByteStream object to use a flexible allocator system
|
||||
// that may support multi-threaded programming models.
|
||||
|
||||
|
||||
#ifndef BASE_ARCHIVE_H
|
||||
#define BASE_ARCHIVE_H
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "Platform.h"
|
||||
#include "Mutex.h"
|
||||
|
||||
|
||||
#if defined _MT || defined _REENTRANT
|
||||
# define USE_ARCHIVE_MUTEX
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
# include "win32/Archive.h"
|
||||
#elif linux
|
||||
# include "linux/Archive.h"
|
||||
#elif sparc
|
||||
# include "solaris/Archive.h"
|
||||
#else
|
||||
#error /Base/Archive.h: Undefine platform type
|
||||
#endif
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
const unsigned MAX_ARRAY_SIZE = 1024;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if defined(USE_ARCHIVE_MUTEX)
|
||||
extern CMutex ByteStreamMutex;
|
||||
#endif
|
||||
|
||||
class ByteStream
|
||||
{
|
||||
public:
|
||||
class ReadIterator
|
||||
{
|
||||
public:
|
||||
ReadIterator();
|
||||
ReadIterator(const ReadIterator & source);
|
||||
explicit ReadIterator(const ByteStream & source);
|
||||
~ReadIterator();
|
||||
|
||||
ReadIterator & operator = (const ReadIterator & source);
|
||||
void advance (const unsigned int distance);
|
||||
unsigned get (void * target, const unsigned long int readSize);
|
||||
const unsigned int getSize () const;
|
||||
const unsigned char * const getBuffer () const;
|
||||
const unsigned int getReadPosition () const;
|
||||
|
||||
private:
|
||||
unsigned int readPtr;
|
||||
const ByteStream * stream;
|
||||
};
|
||||
|
||||
private:
|
||||
class Data
|
||||
{
|
||||
friend class ByteStream;
|
||||
friend class ReadIterator;
|
||||
public:
|
||||
~Data();
|
||||
|
||||
static Data * getNewData();
|
||||
|
||||
const int getRef () const;
|
||||
void deref ();
|
||||
void ref ();
|
||||
protected:
|
||||
unsigned char * buffer;
|
||||
unsigned long size;
|
||||
private:
|
||||
struct DataFreeList
|
||||
{
|
||||
~DataFreeList()
|
||||
{
|
||||
std::vector<ByteStream::Data *>::iterator i;
|
||||
for(i = freeList.begin(); i != freeList.end(); ++i)
|
||||
{
|
||||
delete (*i);
|
||||
}
|
||||
};
|
||||
std::vector<Data *> freeList;
|
||||
};
|
||||
Data();
|
||||
//explicit Data(unsigned char * buffer);
|
||||
static std::vector<Data *> & getDataFreeList();
|
||||
static void releaseOldData(Data * oldData);
|
||||
private:
|
||||
int refCount;
|
||||
};
|
||||
|
||||
friend class ReadIterator;
|
||||
public:
|
||||
ByteStream();
|
||||
ByteStream(const unsigned char * const buffer, const unsigned int bufferSize);
|
||||
ByteStream(const ByteStream & source);
|
||||
virtual ~ByteStream();
|
||||
|
||||
public:
|
||||
ByteStream(ReadIterator & source);
|
||||
ByteStream & operator = (const ByteStream & source);
|
||||
ByteStream & operator = (ReadIterator & source);
|
||||
const ReadIterator & begin() const;
|
||||
void clear();
|
||||
const unsigned char * const getBuffer() const;
|
||||
const unsigned int getSize() const;
|
||||
void put(const void * const source, const unsigned int sourceSize);
|
||||
bool overwriteEnd(const void * const source, const unsigned int sourceSize);
|
||||
|
||||
static bool ms_reuseData;
|
||||
|
||||
private:
|
||||
void get(void * target, ReadIterator & readIterator, const unsigned long int readSize) const;
|
||||
void growToAtLeast(const unsigned int targetSize);
|
||||
void reAllocate(const unsigned int newSize);
|
||||
|
||||
private:
|
||||
unsigned int allocatedSize;
|
||||
ReadIterator beginReadIterator;
|
||||
Data * data;
|
||||
unsigned int size;
|
||||
unsigned int lastPutSize;
|
||||
|
||||
};
|
||||
|
||||
inline ByteStream::Data::Data() :
|
||||
buffer(0),
|
||||
size(0),
|
||||
refCount(1)
|
||||
{
|
||||
}
|
||||
|
||||
inline ByteStream::Data::~Data()
|
||||
{
|
||||
#if defined(USE_ARCHIVE_MUTEX)
|
||||
ByteStreamMutex.Lock();
|
||||
#endif
|
||||
|
||||
refCount = 0;
|
||||
delete[] buffer;
|
||||
|
||||
#if defined(USE_ARCHIVE_MUTEX)
|
||||
ByteStreamMutex.Unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void ByteStream::Data::deref()
|
||||
{
|
||||
#if defined(USE_ARCHIVE_MUTEX)
|
||||
ByteStreamMutex.Lock();
|
||||
#endif
|
||||
|
||||
refCount--;
|
||||
|
||||
#if defined(USE_ARCHIVE_MUTEX)
|
||||
ByteStreamMutex.Unlock();
|
||||
#endif
|
||||
|
||||
if(refCount < 1)
|
||||
releaseOldData(this);
|
||||
}
|
||||
|
||||
inline std::vector<ByteStream::Data *> & ByteStream::Data::getDataFreeList()
|
||||
{
|
||||
static DataFreeList freeList;
|
||||
return freeList.freeList;
|
||||
}
|
||||
|
||||
inline ByteStream::Data * ByteStream::Data::getNewData()
|
||||
{
|
||||
#if defined(USE_ARCHIVE_MUTEX)
|
||||
ByteStreamMutex.Lock();
|
||||
#endif
|
||||
|
||||
Data * result = 0;
|
||||
if(getDataFreeList().empty())
|
||||
{
|
||||
result = new Data;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = getDataFreeList().back();
|
||||
getDataFreeList().pop_back();
|
||||
}
|
||||
result->refCount = 1;
|
||||
|
||||
#if defined(USE_ARCHIVE_MUTEX)
|
||||
ByteStreamMutex.Unlock();
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline const int ByteStream::Data::getRef() const
|
||||
{
|
||||
return refCount;
|
||||
}
|
||||
|
||||
inline void ByteStream::Data::ref()
|
||||
{
|
||||
#if defined(USE_ARCHIVE_MUTEX)
|
||||
ByteStreamMutex.Lock();
|
||||
#endif
|
||||
|
||||
refCount++;
|
||||
|
||||
#if defined(USE_ARCHIVE_MUTEX)
|
||||
ByteStreamMutex.Unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void ByteStream::Data::releaseOldData(ByteStream::Data * oldData)
|
||||
{
|
||||
if (ByteStream::ms_reuseData)
|
||||
{
|
||||
#if defined(USE_ARCHIVE_MUTEX)
|
||||
ByteStreamMutex.Lock();
|
||||
#endif
|
||||
|
||||
getDataFreeList().push_back(oldData);
|
||||
oldData->refCount = 0;
|
||||
|
||||
#if defined(USE_ARCHIVE_MUTEX)
|
||||
ByteStreamMutex.Unlock();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
delete oldData;
|
||||
}
|
||||
}
|
||||
|
||||
inline ByteStream::ReadIterator & ByteStream::ReadIterator::operator = (const ByteStream::ReadIterator & rhs)
|
||||
{
|
||||
if(&rhs != this)
|
||||
{
|
||||
readPtr = rhs.readPtr;
|
||||
stream = rhs.stream;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline unsigned ByteStream::ReadIterator::get(void * target, const unsigned long int readSize)
|
||||
{
|
||||
assert(stream);
|
||||
stream->get(target, *this, readSize);
|
||||
readPtr += readSize;
|
||||
return readSize;
|
||||
}
|
||||
|
||||
inline const unsigned int ByteStream::ReadIterator::getSize() const
|
||||
{
|
||||
assert(stream);
|
||||
return stream->getSize() - readPtr;
|
||||
}
|
||||
|
||||
inline const ByteStream::ReadIterator & ByteStream::begin() const
|
||||
{
|
||||
return beginReadIterator;
|
||||
}
|
||||
|
||||
inline void ByteStream::ReadIterator::advance(const unsigned int distance)
|
||||
{
|
||||
readPtr += distance;
|
||||
}
|
||||
|
||||
inline const unsigned int ByteStream::ReadIterator::getReadPosition() const
|
||||
{
|
||||
return readPtr;
|
||||
}
|
||||
|
||||
inline const unsigned char * const ByteStream::ReadIterator::getBuffer() const
|
||||
{
|
||||
if(stream)
|
||||
return &stream->data->buffer[readPtr];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline void ByteStream::clear()
|
||||
{
|
||||
#if defined(USE_ARCHIVE_MUTEX)
|
||||
ByteStreamMutex.Lock();
|
||||
#endif
|
||||
|
||||
size = 0;
|
||||
|
||||
#if defined(USE_ARCHIVE_MUTEX)
|
||||
ByteStreamMutex.Unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
inline const unsigned char * const ByteStream::getBuffer() const
|
||||
{
|
||||
return data->buffer;
|
||||
}
|
||||
|
||||
inline const unsigned int ByteStream::getSize() const
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
inline void ByteStream::growToAtLeast(const unsigned int targetSize)
|
||||
{
|
||||
if(allocatedSize < targetSize)
|
||||
{
|
||||
reAllocate(allocatedSize + allocatedSize + targetSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class AutoVariableBase
|
||||
{
|
||||
public:
|
||||
AutoVariableBase();
|
||||
virtual ~AutoVariableBase();
|
||||
|
||||
virtual void pack(ByteStream & target) const = 0;
|
||||
virtual void unpack(ByteStream::ReadIterator & source) = 0;
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class AutoByteStream
|
||||
{
|
||||
public:
|
||||
AutoByteStream();
|
||||
virtual ~AutoByteStream();
|
||||
void addVariable(AutoVariableBase & newVariable);
|
||||
virtual const unsigned int getItemCount() const;
|
||||
virtual void pack(ByteStream & target) const;
|
||||
virtual void unpack(ByteStream::ReadIterator & source);
|
||||
protected:
|
||||
std::vector<AutoVariableBase *> members;
|
||||
private:
|
||||
AutoByteStream(const AutoByteStream & source);
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
template<class ValueType>
|
||||
class AutoVariable : public AutoVariableBase
|
||||
{
|
||||
public:
|
||||
AutoVariable();
|
||||
explicit AutoVariable(const ValueType & source);
|
||||
virtual ~AutoVariable();
|
||||
|
||||
const ValueType & get() const;
|
||||
virtual void pack(ByteStream & target) const;
|
||||
void set(const ValueType & rhs);
|
||||
virtual void unpack(ByteStream::ReadIterator & source);
|
||||
|
||||
private:
|
||||
ValueType value;
|
||||
};
|
||||
|
||||
template<class ValueType>
|
||||
AutoVariable<ValueType>::AutoVariable() :
|
||||
AutoVariableBase(),
|
||||
value()
|
||||
{
|
||||
}
|
||||
|
||||
template<class ValueType>
|
||||
AutoVariable<ValueType>::AutoVariable(const ValueType & source) :
|
||||
AutoVariableBase(),
|
||||
value(source)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ValueType>
|
||||
AutoVariable<ValueType>::~AutoVariable()
|
||||
{
|
||||
}
|
||||
|
||||
template<class ValueType>
|
||||
const ValueType & AutoVariable<ValueType>::get() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
template<class ValueType>
|
||||
void AutoVariable<ValueType>::pack(ByteStream & target) const
|
||||
{
|
||||
Base::put(target, value);
|
||||
}
|
||||
|
||||
template<class ValueType>
|
||||
void AutoVariable<ValueType>::set(const ValueType & rhs)
|
||||
{
|
||||
value = rhs;
|
||||
}
|
||||
|
||||
template<class ValueType>
|
||||
void AutoVariable<ValueType>::unpack(ByteStream::ReadIterator & source)
|
||||
{
|
||||
Base::get(source, value);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
template<class ValueType>
|
||||
class AutoArray : public AutoVariableBase
|
||||
{
|
||||
public:
|
||||
AutoArray();
|
||||
AutoArray(const AutoArray & source);
|
||||
~AutoArray();
|
||||
|
||||
const std::vector<ValueType> & get() const;
|
||||
void set(const std::vector<ValueType> & source);
|
||||
|
||||
virtual void pack(ByteStream & target) const;
|
||||
virtual void unpack(ByteStream::ReadIterator & source);
|
||||
|
||||
private:
|
||||
std::vector<ValueType> array;
|
||||
};
|
||||
|
||||
template<class ValueType>
|
||||
inline AutoArray<ValueType>::AutoArray()
|
||||
{
|
||||
}
|
||||
|
||||
template<class ValueType>
|
||||
inline AutoArray<ValueType>::AutoArray(const AutoArray & source) :
|
||||
array(source.array)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ValueType>
|
||||
inline AutoArray<ValueType>::~AutoArray()
|
||||
{
|
||||
}
|
||||
|
||||
template<class ValueType>
|
||||
inline const std::vector<ValueType> & AutoArray<ValueType>::get() const
|
||||
{
|
||||
return array;
|
||||
}
|
||||
|
||||
template<class ValueType>
|
||||
inline void AutoArray<ValueType>::set(const std::vector<ValueType> & source)
|
||||
{
|
||||
array = source;
|
||||
}
|
||||
|
||||
template<class ValueType>
|
||||
inline void AutoArray<ValueType>::pack(ByteStream & target) const
|
||||
{
|
||||
unsigned int arraySize = array.size();
|
||||
Base::put(target, arraySize);
|
||||
|
||||
typename std::vector<ValueType>::const_iterator i;
|
||||
for(i = array.begin(); i != array.end(); ++i)
|
||||
{
|
||||
ValueType v = (*i);
|
||||
Base::put(target, v);
|
||||
}
|
||||
}
|
||||
|
||||
template<class ValueType>
|
||||
inline void AutoArray<ValueType>::unpack(ByteStream::ReadIterator & source)
|
||||
{
|
||||
unsigned int arraySize;
|
||||
Base::get(source, arraySize);
|
||||
ValueType v;
|
||||
|
||||
if (arraySize > MAX_ARRAY_SIZE)
|
||||
arraySize = 0;
|
||||
|
||||
for(unsigned int i = 0; i < arraySize; ++i)
|
||||
{
|
||||
Base::get(source, v);
|
||||
array.push_back(v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
inline void get(ByteStream::ReadIterator & source, ByteStream & target)
|
||||
{
|
||||
target.put(source.getBuffer(), source.getSize());
|
||||
source.advance(source.getSize());
|
||||
}
|
||||
|
||||
inline void get(ByteStream::ReadIterator & source, double & target)
|
||||
{
|
||||
source.get(&target, 8);
|
||||
target = byteSwap(target);
|
||||
}
|
||||
|
||||
inline void get(ByteStream::ReadIterator & source, float & target)
|
||||
{
|
||||
source.get(&target, 4);
|
||||
target = byteSwap(target);
|
||||
}
|
||||
|
||||
inline void get(ByteStream::ReadIterator & source, uint64 & target)
|
||||
{
|
||||
source.get(&target, 8);
|
||||
target = byteSwap(target);
|
||||
}
|
||||
|
||||
inline void get(ByteStream::ReadIterator & source, int64 & target)
|
||||
{
|
||||
source.get(&target, 8);
|
||||
target = byteSwap(target);
|
||||
}
|
||||
|
||||
inline void get(ByteStream::ReadIterator & source, uint32 & target)
|
||||
{
|
||||
source.get(&target, 4);
|
||||
target = byteSwap(target);
|
||||
}
|
||||
|
||||
inline void get(ByteStream::ReadIterator & source, int32 & target)
|
||||
{
|
||||
source.get(&target, 4);
|
||||
target = byteSwap(target);
|
||||
}
|
||||
|
||||
inline void get(ByteStream::ReadIterator & source, uint16 & target)
|
||||
{
|
||||
source.get(&target, 2);
|
||||
target = byteSwap(target);
|
||||
}
|
||||
|
||||
inline void get(ByteStream::ReadIterator & source, int16 & target)
|
||||
{
|
||||
source.get(&target, 2);
|
||||
target = byteSwap(target);
|
||||
}
|
||||
|
||||
inline void get(ByteStream::ReadIterator & source, uint8 & target)
|
||||
{
|
||||
source.get(&target, 1);
|
||||
}
|
||||
|
||||
inline void get(ByteStream::ReadIterator & source, int8 & target)
|
||||
{
|
||||
source.get(&target, 1);
|
||||
}
|
||||
|
||||
inline unsigned get(ByteStream::ReadIterator & source, unsigned char * const target, const unsigned int targetSize)
|
||||
{
|
||||
return source.get(target, targetSize);
|
||||
}
|
||||
|
||||
inline void get(ByteStream::ReadIterator & source, bool & target)
|
||||
{
|
||||
source.get(&target, 1);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
inline void put(ByteStream & target, ByteStream::ReadIterator & source)
|
||||
{
|
||||
target.put(source.getBuffer(), source.getSize());
|
||||
source.advance(source.getSize());
|
||||
}
|
||||
|
||||
inline void put(ByteStream & target, const double value)
|
||||
{
|
||||
double temp = byteSwap(value);
|
||||
target.put(&temp, 8);
|
||||
}
|
||||
|
||||
inline void put(ByteStream & target, const float value)
|
||||
{
|
||||
float temp = byteSwap(value);
|
||||
target.put(&temp, 4);
|
||||
}
|
||||
|
||||
inline void put(ByteStream & target, const uint64 value)
|
||||
{
|
||||
uint64 temp = byteSwap(value);
|
||||
target.put(&temp, 8);
|
||||
}
|
||||
|
||||
inline void put(ByteStream & target, const int64 value)
|
||||
{
|
||||
int64 temp = byteSwap(value);
|
||||
target.put(&temp, 8);
|
||||
}
|
||||
|
||||
inline void put(ByteStream & target, const uint32 value)
|
||||
{
|
||||
uint32 temp = byteSwap(value);
|
||||
target.put(&temp, 4);
|
||||
}
|
||||
|
||||
inline void put(ByteStream & target, const int32 value)
|
||||
{
|
||||
int32 temp = byteSwap(value);
|
||||
target.put(&temp, 4);
|
||||
}
|
||||
|
||||
inline void put(ByteStream & target, const uint16 value)
|
||||
{
|
||||
uint16 temp = byteSwap(value);
|
||||
target.put(&temp, 2);
|
||||
}
|
||||
|
||||
inline void put(ByteStream & target, const int16 value)
|
||||
{
|
||||
int16 temp = byteSwap(value);
|
||||
target.put(&temp, 2);
|
||||
}
|
||||
|
||||
inline void put(ByteStream & target, const uint8 value)
|
||||
{
|
||||
target.put(&value, 1);
|
||||
}
|
||||
|
||||
inline void put(ByteStream & target, const int8 value)
|
||||
{
|
||||
target.put(&value, 1);
|
||||
}
|
||||
|
||||
inline void put(ByteStream & target, const bool & source)
|
||||
{
|
||||
target.put(&source, 1);
|
||||
}
|
||||
|
||||
|
||||
inline void put(ByteStream & target, const unsigned char * const source, const unsigned int sourceSize)
|
||||
{
|
||||
target.put(source, sourceSize);
|
||||
}
|
||||
|
||||
inline void put(ByteStream & target, const ByteStream & source)
|
||||
{
|
||||
target.put(source.begin().getBuffer(), source.begin().getSize());
|
||||
}
|
||||
|
||||
unsigned get(ByteStream::ReadIterator & source, std::string & target);
|
||||
void put(ByteStream & target, const std::string & source);
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
inline bool overwriteEnd(ByteStream & target, const double value)
|
||||
{
|
||||
double temp = byteSwap(value);
|
||||
return target.overwriteEnd(&temp, 8);
|
||||
}
|
||||
|
||||
inline bool overwriteEnd(ByteStream & target, const float value)
|
||||
{
|
||||
float temp = byteSwap(value);
|
||||
return target.overwriteEnd(&temp, 4);
|
||||
}
|
||||
|
||||
inline bool overwriteEnd(ByteStream & target, const uint64 value)
|
||||
{
|
||||
uint64 temp = byteSwap(value);
|
||||
return target.overwriteEnd(&temp, 8);
|
||||
}
|
||||
|
||||
inline bool overwriteEnd(ByteStream & target, const int64 value)
|
||||
{
|
||||
int64 temp = byteSwap(value);
|
||||
return target.overwriteEnd(&temp, 8);
|
||||
}
|
||||
|
||||
inline bool overwriteEnd(ByteStream & target, const uint32 value)
|
||||
{
|
||||
uint32 temp = byteSwap(value);
|
||||
return target.overwriteEnd(&temp, 4);
|
||||
}
|
||||
|
||||
inline bool overwriteEnd(ByteStream & target, const int32 value)
|
||||
{
|
||||
int32 temp = byteSwap(value);
|
||||
return target.overwriteEnd(&temp, 4);
|
||||
}
|
||||
|
||||
inline bool overwriteEnd(ByteStream & target, const uint16 value)
|
||||
{
|
||||
uint16 temp = byteSwap(value);
|
||||
return target.overwriteEnd(&temp, 2);
|
||||
}
|
||||
|
||||
inline bool overwriteEnd(ByteStream & target, const int16 value)
|
||||
{
|
||||
int16 temp = byteSwap(value);
|
||||
return target.overwriteEnd(&temp, 2);
|
||||
}
|
||||
|
||||
inline bool overwriteEnd(ByteStream & target, const uint8 value)
|
||||
{
|
||||
return target.overwriteEnd(&value, 1);
|
||||
}
|
||||
|
||||
inline bool overwriteEnd(ByteStream & target, const int8 value)
|
||||
{
|
||||
return target.overwriteEnd(&value, 1);
|
||||
}
|
||||
|
||||
inline bool overwriteEnd(ByteStream & target, const bool & source)
|
||||
{
|
||||
return target.overwriteEnd(&source, 1);
|
||||
}
|
||||
|
||||
inline bool overwriteEnd(ByteStream & target, const unsigned char * const source, const unsigned int sourceSize)
|
||||
{
|
||||
return target.overwriteEnd(source, sourceSize);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,349 @@
|
||||
#include "AutoLog.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
#ifdef WIN32
|
||||
#include <direct.h> // for NT directory commands
|
||||
#define SLASHCHAR "\\"
|
||||
#define WRONGSLASH '/'
|
||||
#else
|
||||
#define SLASHCHAR "/"
|
||||
#define WRONGSLASH '\\'
|
||||
#endif
|
||||
|
||||
// set default values for global masks
|
||||
CAutoLog::eLogLevel CAutoLog::nLogMask = eLOG_NORMAL; // what is logged in log files
|
||||
CAutoLog::eLogLevel CAutoLog::nPrintMask = eLOG_ERROR; // what is printed on the screen
|
||||
CAutoLog::eLogLevel CAutoLog::nFlushMask = eLOG_ERROR; // when is file flushed on every entry?
|
||||
|
||||
// class info
|
||||
|
||||
//-------------------------------------
|
||||
bool CAutoLog::Open(const char * filename)
|
||||
//-------------------------------------
|
||||
{
|
||||
|
||||
if( NULL == filename)
|
||||
return false;
|
||||
|
||||
if (pFilename)
|
||||
return false;
|
||||
|
||||
pFilename = strdup(filename);
|
||||
|
||||
// Sanitize slashes
|
||||
char *ptr;
|
||||
while ((ptr = strchr(pFilename,WRONGSLASH)) != NULL)
|
||||
*ptr = SLASHCHAR[0];
|
||||
|
||||
char strPath[1024];
|
||||
strcpy(strPath,pFilename);
|
||||
ptr = strrchr(strPath, SLASHCHAR[0]);
|
||||
if (ptr != NULL)
|
||||
{
|
||||
*ptr = 0;
|
||||
|
||||
#ifdef WIN32
|
||||
char curdir[128];
|
||||
|
||||
// remember current directory
|
||||
if( getcwd(curdir,sizeof(curdir)) == NULL )
|
||||
{
|
||||
fprintf(stderr,"CAutoLog::Archive failed to get current directory!\n");
|
||||
free(pFilename);
|
||||
pFilename = NULL;
|
||||
return false; // error, can't proceed
|
||||
}
|
||||
|
||||
if (chdir(strPath))
|
||||
{
|
||||
// failed to find the directory, so we must create it
|
||||
if (mkdir(strPath))
|
||||
{
|
||||
fprintf(stderr,"CAutoLog::Archive failed to make directory: %s\n", strPath);
|
||||
free(pFilename);
|
||||
pFilename = NULL;
|
||||
return false; // error, can't proceed
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// found the directory, but we don't want to be there, so go back
|
||||
chdir(curdir);
|
||||
}
|
||||
#else
|
||||
struct stat SS;
|
||||
|
||||
// see if directory exists
|
||||
if (stat(strPath,&SS))
|
||||
{
|
||||
// create directory
|
||||
if (mkdir(strPath,0777))
|
||||
{
|
||||
fprintf(stderr,"CAutoLog::Archive failed to make directory: %s\n", strPath);
|
||||
free(pFilename);
|
||||
pFilename = NULL;
|
||||
return false; // error, can't proceed
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
pFile = fopen(pFilename,"a+");
|
||||
if( pFile == NULL || pFile == (FILE *)-1)
|
||||
{
|
||||
//fprintf(stderr,"CAutoLog::Open failed to open log file: %s\n",pFilename);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
time_t t;
|
||||
time(&t);
|
||||
struct tm *ptm = localtime(&t);
|
||||
nTodaysDayOfYear = ptm->tm_yday;
|
||||
|
||||
//printf("Open log file: %s\n",pFilename);
|
||||
return true;
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
void CAutoLog::Close(void)
|
||||
//-------------------------------------
|
||||
{
|
||||
if( pFile != (FILE *)-1 && pFile != NULL)
|
||||
{
|
||||
fclose(pFile);
|
||||
}
|
||||
pFile = (FILE *)-1;
|
||||
|
||||
if (pFilename)
|
||||
{
|
||||
free(pFilename);
|
||||
}
|
||||
pFilename = NULL;
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
void CAutoLog::LogError(char * format, ...)
|
||||
{
|
||||
char strInput[1024];
|
||||
|
||||
// parse format and input arguments
|
||||
va_list varg;
|
||||
va_start(varg, format);
|
||||
vsprintf(strInput, format, varg);
|
||||
va_end(varg);
|
||||
|
||||
Log(eLOG_ERROR, strInput);
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
void CAutoLog::LogAlert(char * format, ...)
|
||||
{
|
||||
char strInput[1024];
|
||||
|
||||
// parse format and input arguments
|
||||
va_list varg;
|
||||
va_start(varg, format);
|
||||
vsprintf(strInput, format, varg);
|
||||
va_end(varg);
|
||||
|
||||
Log(eLOG_ALERT, strInput);
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
void CAutoLog::Log(char * format, ...)
|
||||
{
|
||||
char strInput[1024];
|
||||
|
||||
// parse format and input arguments
|
||||
va_list varg;
|
||||
va_start(varg, format);
|
||||
vsprintf(strInput, format, varg);
|
||||
va_end(varg);
|
||||
|
||||
Log(eLOG_NORMAL, strInput);
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
void CAutoLog::LogDebug(char * format, ...)
|
||||
{
|
||||
char strInput[1024];
|
||||
|
||||
// parse format and input arguments
|
||||
va_list varg;
|
||||
va_start(varg, format);
|
||||
vsprintf(strInput, format, varg);
|
||||
va_end(varg);
|
||||
|
||||
Log(eLOG_DEBUG, strInput);
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
void CAutoLog::Log(eLogLevel severity, char *fmt, ...)
|
||||
//-------------------------------------
|
||||
{
|
||||
if (pFile == (FILE *)-1 || pFile == NULL)
|
||||
{
|
||||
//fprintf(stderr,"CAutoLog::Log called with no file open!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (nLogMask == eLOG_NONE || nLogMask < severity)
|
||||
{
|
||||
if (nPrintMask == eLOG_NONE || nPrintMask < severity)
|
||||
{ // no point in parsing, we're doing nothing.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
char strTime[128];
|
||||
char strInput[1024];
|
||||
char strOutput[1024+128];
|
||||
time_t t;
|
||||
|
||||
// parse format and input arguments
|
||||
va_list varg;
|
||||
va_start(varg, fmt);
|
||||
vsprintf(strInput, fmt, varg);
|
||||
va_end(varg);
|
||||
|
||||
// make timestamp
|
||||
time(&t);
|
||||
strftime(strTime, sizeof(strTime), "[%m/%d/%Y %H:%M:%S]", localtime(&t) );
|
||||
|
||||
// construct output string
|
||||
sprintf(strOutput, "%s %s\n", strTime, strInput);
|
||||
|
||||
// check to see if current file needs to be archived
|
||||
Archive();
|
||||
|
||||
// log to file
|
||||
if (!(nLogMask == eLOG_NONE || nLogMask < severity))
|
||||
{
|
||||
fwrite(strOutput,strlen(strOutput),1,pFile);
|
||||
}
|
||||
|
||||
// print as standard output
|
||||
if (!(nPrintMask == eLOG_NONE || nPrintMask < severity))
|
||||
{
|
||||
printf("%s", strOutput);
|
||||
}
|
||||
|
||||
// flush file buffer if error is high severity
|
||||
if (severity <= nFlushMask)
|
||||
fflush(pFile);
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
void CAutoLog::Archive(void)
|
||||
//-------------------------------------
|
||||
{
|
||||
if( pFile == (FILE *)-1 || pFile == NULL)
|
||||
{
|
||||
//fprintf(stderr,"CAutoLog::Archive called with no file open!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// check to see if it's time to archive
|
||||
time_t t;
|
||||
time(&t);
|
||||
struct tm *ptm = localtime(&t);
|
||||
if (ptm->tm_yday == nTodaysDayOfYear)
|
||||
{
|
||||
return; // nope, not time to archive
|
||||
}
|
||||
nTodaysDayOfYear = ptm->tm_yday;
|
||||
|
||||
char strPath[1024];
|
||||
char strCurrent[1024];
|
||||
char strTime[128];
|
||||
char * pCurrent;
|
||||
|
||||
#ifdef WIN32
|
||||
char curdir[128];
|
||||
#else
|
||||
struct stat SS;
|
||||
#endif
|
||||
|
||||
strftime(strTime, sizeof(strTime), "%m%d%Y", localtime(&t) ); // numerical time e.g. 041698
|
||||
|
||||
strcpy(strCurrent,pFilename);
|
||||
pCurrent = strrchr(strCurrent, SLASHCHAR[0]);
|
||||
if (pCurrent != NULL)
|
||||
*pCurrent = 0;
|
||||
else
|
||||
sprintf(strCurrent,".");
|
||||
|
||||
sprintf(strPath,"%s"SLASHCHAR"%s", strCurrent, strTime); // logs/041698
|
||||
|
||||
#ifdef WIN32
|
||||
// remember current directory
|
||||
if( getcwd(curdir,sizeof(curdir)) == NULL )
|
||||
{
|
||||
fprintf(stderr,"CAutoLog::Archive failed to get current directory!\n");
|
||||
return; // error, can't proceed
|
||||
}
|
||||
|
||||
if (chdir(strPath))
|
||||
{
|
||||
// failed to find the directory, so we must create it
|
||||
if (mkdir(strPath))
|
||||
{
|
||||
fprintf(stderr,"CAutoLog::Archive failed to make directory: %s\n", strPath);
|
||||
return; // error, can't proceed
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// found the directory, but we don't want to be there, so go back
|
||||
chdir(curdir);
|
||||
}
|
||||
#else
|
||||
// see if directory exists
|
||||
if (stat(strPath,&SS))
|
||||
{
|
||||
// create directory
|
||||
if (mkdir(strPath,0777))
|
||||
{
|
||||
fprintf(stderr,"CAutoLog::Archive failed to make directory: %s\n", strPath);
|
||||
return; // error, can't proceed
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
pCurrent = strrchr(pFilename, SLASHCHAR[0]);
|
||||
if (pCurrent == NULL)
|
||||
pCurrent = pFilename;
|
||||
else
|
||||
pCurrent++;
|
||||
|
||||
sprintf(strCurrent,"%s"SLASHCHAR"%s",strPath,pCurrent);
|
||||
|
||||
fflush(pFile);
|
||||
fclose(pFile);
|
||||
|
||||
rename(pFilename,strCurrent);
|
||||
|
||||
pFile = fopen(pFilename,"w+");
|
||||
}
|
||||
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
#ifndef _AUTOLOG_H
|
||||
#define _AUTOLOG_H
|
||||
|
||||
#pragma warning (disable : 4786)
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
class CAutoLog
|
||||
{
|
||||
public:
|
||||
enum eLogLevel {
|
||||
eLOG_NONE = 0, // log entries are discarded
|
||||
eLOG_ERROR = 1, // log errors only
|
||||
eLOG_ALERT = 2, // log alerts and errors only
|
||||
eLOG_NORMAL = 3, // log all normal events, no debug
|
||||
eLOG_DEBUG = 4 // log everything
|
||||
};
|
||||
|
||||
// Global setting for all AutoLog instances for this application. Can be set on the fly. Default is eLOG_NORMAL.
|
||||
void SetLogLevel(eLogLevel loglevel);
|
||||
|
||||
// Global setting for all AutoLog instances for this application. Can be set on the fly. Default is eLOG_ERROR.
|
||||
void SetPrintLevel(eLogLevel printlevel);
|
||||
|
||||
// Global setting for all AutoLog instances for this application. Can be set on the fly. Default is eLOG_ERROR.
|
||||
void SetFlushLevel(eLogLevel flushlevel);
|
||||
|
||||
// constructor, no file loaded
|
||||
CAutoLog();
|
||||
|
||||
// version of constructor that calls Open(file)
|
||||
CAutoLog(const char * file);
|
||||
|
||||
// destructor, will close file if opened
|
||||
~CAutoLog();
|
||||
|
||||
// Open log file. If a file is already open, function will fail.
|
||||
// returns true if no error
|
||||
bool Open(const char * file);
|
||||
|
||||
// Close log file if opened.
|
||||
void Close(void);
|
||||
|
||||
// Make an entry in the log. Format and variable arguments are identical to printf.
|
||||
// severity will be compared to master log level to determine if the log entry will be made
|
||||
// severity will be compared to master print level to determine if the log entry will be printed to stdout
|
||||
void Log(eLogLevel severity, char * format, ...);
|
||||
|
||||
// Equivalent to the above with the approriate severity argument
|
||||
void LogError(char * format, ...);
|
||||
void LogAlert(char * format, ...);
|
||||
void Log(char * format, ...);
|
||||
void LogDebug(char * format, ...);
|
||||
|
||||
private:
|
||||
FILE * pFile; // current log file opened
|
||||
char * pFilename; // current log file opened
|
||||
int nTodaysDayOfYear; // remember the current day to detect change of day
|
||||
void Archive(void); // archives current log
|
||||
|
||||
static eLogLevel nLogMask; // master severity level
|
||||
static eLogLevel nPrintMask; // master severity level
|
||||
static eLogLevel nFlushMask; // master severity level
|
||||
};
|
||||
|
||||
//-------------------------------------
|
||||
inline void CAutoLog::SetLogLevel(eLogLevel loglevel)
|
||||
{
|
||||
nLogMask = loglevel;
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
inline void CAutoLog::SetPrintLevel(eLogLevel printlevel)
|
||||
{
|
||||
nPrintMask = printlevel;
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
inline void CAutoLog::SetFlushLevel(eLogLevel flushlevel)
|
||||
{
|
||||
nFlushMask = flushlevel;
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
inline CAutoLog::CAutoLog()
|
||||
{
|
||||
pFilename = NULL;
|
||||
pFile = (FILE *)-1;
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
inline CAutoLog::CAutoLog(const char * file)
|
||||
{
|
||||
pFilename = NULL;
|
||||
pFile = (FILE *)-1;
|
||||
Open(file);
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
inline CAutoLog::~CAutoLog()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
#include "Base.h"
|
||||
@@ -0,0 +1,63 @@
|
||||
////////////////////////////////////////
|
||||
// Base.h
|
||||
//
|
||||
// Purpose:
|
||||
// 1. Provide a single header file that may be used to include all
|
||||
// Base library header files.
|
||||
//
|
||||
// Revisions:
|
||||
// 07/10/2001 Created
|
||||
//
|
||||
|
||||
#ifndef _BASEPCH_H
|
||||
#define _BASEPCH_H
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
#include "win32/Platform.h"
|
||||
#include "win32/Types.h"
|
||||
#include "win32/Mutex.h"
|
||||
#include "win32/Event.h"
|
||||
#include "win32/Thread.h"
|
||||
#include "Logger.h"
|
||||
#include "Config.h"
|
||||
#include "ScopeLock.h"
|
||||
#include "Statistics.h"
|
||||
#include "TemplateObjectAllocator.h"
|
||||
#include "BlockAllocator.h"
|
||||
|
||||
#elif linux
|
||||
|
||||
#include "linux/Platform.h"
|
||||
#include "linux/Types.h"
|
||||
#include "linux/Mutex.h"
|
||||
#include "linux/Event.h"
|
||||
#include "linux/Thread.h"
|
||||
#include "Logger.h"
|
||||
#include "Config.h"
|
||||
#include "ScopeLock.h"
|
||||
#include "Statistics.h"
|
||||
#include "TemplateObjectAllocator.h"
|
||||
#include "BlockAllocator.h"
|
||||
|
||||
#elif sparc
|
||||
|
||||
#include "solaris/Platform.h"
|
||||
#include "solaris/Types.h"
|
||||
#include "solaris/Mutex.h"
|
||||
#include "solaris/Event.h"
|
||||
#include "solaris/Thread.h"
|
||||
#include "Logger.h"
|
||||
#include "Config.h"
|
||||
#include "ScopeLock.h"
|
||||
#include "Statistics.h"
|
||||
#include "TemplateObjectAllocator.h"
|
||||
#include "BlockAllocator.h"
|
||||
|
||||
#else
|
||||
|
||||
#error Base.h: Undefine platform type
|
||||
|
||||
#endif
|
||||
|
||||
#endif // _BASEPCH_H
|
||||
@@ -0,0 +1,387 @@
|
||||
#include "Base64.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Constants
|
||||
//----------------------------------------------------------------------------
|
||||
#define BASE64_PAD '='
|
||||
#define BASE64_PAD_INDEX 64
|
||||
#define BASE64_ERR ((unsigned char)-1)
|
||||
#define BASE64_PAD_CODE ((unsigned char)100)
|
||||
#define BASE64_OUTPUT_LINE 76
|
||||
#define BASE64_ENCODE_BUFFER ((BASE64_OUTPUT_LINE*3)/4)
|
||||
#define BASE64_DECODE_ERROR ((unsigned)-1)
|
||||
|
||||
#define HEX_ERR 16
|
||||
#define HEX_LOWER_FORMAT "%2.2x"
|
||||
#define HEX_UPPER_FORMAT "%2.2X";
|
||||
|
||||
const char Base64::ms_base64Table[] =
|
||||
{
|
||||
'A',
|
||||
'B',
|
||||
'C',
|
||||
'D',
|
||||
'E',
|
||||
'F',
|
||||
'G',
|
||||
'H',
|
||||
'I',
|
||||
'J',
|
||||
'K',
|
||||
'L',
|
||||
'M',
|
||||
'N',
|
||||
'O',
|
||||
'P',
|
||||
'Q',
|
||||
'R',
|
||||
'S',
|
||||
'T',
|
||||
'U',
|
||||
'V',
|
||||
'W',
|
||||
'X',
|
||||
'Y',
|
||||
'Z',
|
||||
'a',
|
||||
'b',
|
||||
'c',
|
||||
'd',
|
||||
'e',
|
||||
'f',
|
||||
'g',
|
||||
'h',
|
||||
'i',
|
||||
'j',
|
||||
'k',
|
||||
'l',
|
||||
'm',
|
||||
'n',
|
||||
'o',
|
||||
'p',
|
||||
'q',
|
||||
'r',
|
||||
's',
|
||||
't',
|
||||
'u',
|
||||
'v',
|
||||
'w',
|
||||
'x',
|
||||
'y',
|
||||
'z',
|
||||
'0',
|
||||
'1',
|
||||
'2',
|
||||
'3',
|
||||
'4',
|
||||
'5',
|
||||
'6',
|
||||
'7',
|
||||
'8',
|
||||
'9',
|
||||
'+',
|
||||
'/',
|
||||
BASE64_PAD
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Functions
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
unsigned char Base64::GetBase64Code(char base64char)
|
||||
{
|
||||
if (base64char == BASE64_PAD)
|
||||
return BASE64_PAD_CODE;
|
||||
|
||||
if (base64char >= 'A' && base64char <= 'Z')
|
||||
return (unsigned char)(base64char - 'A');
|
||||
if (base64char >= 'a' && base64char <= 'z')
|
||||
return (unsigned char)(base64char - 'a' + 26);
|
||||
if (base64char >= '0' && base64char <= '9')
|
||||
return (unsigned char)(base64char - '0' + 2*26);
|
||||
if (base64char == '+')
|
||||
return 2*26+10;
|
||||
if (base64char == '/')
|
||||
return 2*26+11;
|
||||
|
||||
return BASE64_ERR;
|
||||
}
|
||||
|
||||
unsigned char Base64::GetHexCode(char hexChar)
|
||||
{
|
||||
if (hexChar >= '0' && hexChar <= '9')
|
||||
return (unsigned char)(hexChar - '0');
|
||||
if (hexChar >= 'A' && hexChar <= 'F')
|
||||
return (unsigned char)(hexChar - 'A' + 10);
|
||||
if (hexChar >= 'a' && hexChar <= 'f')
|
||||
return (unsigned char)(hexChar - 'a' + 10);
|
||||
|
||||
return HEX_ERR;
|
||||
}
|
||||
|
||||
unsigned Base64::UUDecodeQuadToTriple(const char *quad, unsigned char *triple)
|
||||
{
|
||||
unsigned char b;
|
||||
unsigned char quadByteCodes[4];
|
||||
unsigned bytes = 3, i;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
b = GetBase64Code(quad[i]);
|
||||
|
||||
if (b == BASE64_PAD_CODE)
|
||||
{
|
||||
switch(i)
|
||||
{
|
||||
case 0: bytes=0; break;
|
||||
case 1: bytes=0; break;
|
||||
case 2: bytes=1; break;
|
||||
case 3: bytes=2; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (b == BASE64_ERR)
|
||||
{
|
||||
bytes = BASE64_DECODE_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
quadByteCodes[i] = b;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
triple[0] = (quadByteCodes[0] << 2) | (quadByteCodes[1] >> 4);
|
||||
triple[1] = (quadByteCodes[1] << 4) | (quadByteCodes[2] >> 2);
|
||||
triple[2] = (quadByteCodes[2] << 6) | (quadByteCodes[3] >> 0);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
bool Base64::UUDecode(const char *source, unsigned sourceLen, unsigned char *dest, unsigned *destLen)
|
||||
{
|
||||
unsigned char q[3];
|
||||
unsigned index;
|
||||
unsigned bytes;
|
||||
unsigned char *destIn = dest;
|
||||
bool noErrors = true;
|
||||
|
||||
for (index = 0; index < sourceLen; index += 4)
|
||||
{
|
||||
bytes = UUDecodeQuadToTriple(source + index, q);
|
||||
|
||||
if (bytes != BASE64_DECODE_ERROR) {
|
||||
*destLen -= bytes;
|
||||
if (*destLen >= 0) {
|
||||
memcpy(dest, (char*)&q, bytes);
|
||||
} else {
|
||||
noErrors = false;
|
||||
}
|
||||
dest += bytes;
|
||||
} else {
|
||||
noErrors = false;
|
||||
}
|
||||
}
|
||||
*destLen = (int)(dest - destIn);
|
||||
|
||||
return noErrors;
|
||||
}
|
||||
|
||||
bool Base64::UUDecode(const char *source, unsigned sourceLen, std::string &destString)
|
||||
{
|
||||
unsigned char q[3];
|
||||
unsigned index;
|
||||
unsigned bytes;
|
||||
bool noErrors = true;
|
||||
|
||||
for (index = 0; index < sourceLen; index += 4)
|
||||
{
|
||||
bytes = UUDecodeQuadToTriple(source + index, q);
|
||||
|
||||
if (bytes != BASE64_DECODE_ERROR) {
|
||||
destString.append((char*)&q, bytes);
|
||||
} else {
|
||||
noErrors = false;
|
||||
}
|
||||
}
|
||||
|
||||
return noErrors;
|
||||
}
|
||||
|
||||
unsigned Base64::UUEncodeTripleToQuad(const unsigned char *triple, char* quad, unsigned bytes)
|
||||
{
|
||||
unsigned char indices[4];
|
||||
int quadIndex;
|
||||
|
||||
memset(indices, BASE64_PAD_INDEX, sizeof(indices));
|
||||
|
||||
switch(bytes)
|
||||
{
|
||||
case 3:
|
||||
indices[3] = (triple[2] ) & 0x0000003F;
|
||||
indices[2] = (triple[2] >> 6) & 0x0000003F;
|
||||
case 2:
|
||||
indices[2] = (indices[2] | (triple[1] << 2)) & 0x0000003F;
|
||||
indices[1] = (triple[1] >> 4) & 0x0000003F;
|
||||
case 1:
|
||||
indices[1] = (indices[1] | (triple[0] << 4)) & 0x0000003F;
|
||||
indices[0] = (triple[0] >> 2) & 0x0000003F;
|
||||
case 0:
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
for (quadIndex = 0; quadIndex < 4; quadIndex++) {
|
||||
quad[quadIndex] = ms_base64Table[indices[quadIndex]];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Base64::UUEncode(const unsigned char *source, unsigned sourceLen, char *dest, unsigned destLen)
|
||||
{
|
||||
char quad[5];
|
||||
unsigned z;
|
||||
bool noErrors = true;
|
||||
|
||||
quad[4] = 0;
|
||||
|
||||
// The normal case
|
||||
for(z = 0; z < sourceLen;z += 3)
|
||||
{
|
||||
UUEncodeTripleToQuad(source + z,quad,(z + 3 < sourceLen) ? 3 : (sourceLen - z));
|
||||
destLen -= strlen(quad);
|
||||
if (destLen > 0) {
|
||||
dest += sprintf(dest,quad);
|
||||
} else {
|
||||
noErrors = false;
|
||||
}
|
||||
}
|
||||
|
||||
return noErrors;
|
||||
}
|
||||
|
||||
bool Base64::UUEncode(const unsigned char *source, unsigned sourceLen, std::string &destString)
|
||||
{
|
||||
char quad[5];
|
||||
unsigned z;
|
||||
bool noErrors = true;
|
||||
|
||||
quad[4] = 0;
|
||||
|
||||
// The normal case
|
||||
for(z = 0; z < sourceLen;z += 3)
|
||||
{
|
||||
UUEncodeTripleToQuad(source + z,quad,(z + 3 < sourceLen) ? 3 : (sourceLen - z));
|
||||
|
||||
destString.append(quad);
|
||||
}
|
||||
|
||||
return noErrors;
|
||||
}
|
||||
|
||||
bool Base64::HexEncode(const unsigned char *source, unsigned sourceLen, char *dest, unsigned destLen, bool lowercase)
|
||||
{
|
||||
const char *format = lowercase ? HEX_LOWER_FORMAT : HEX_UPPER_FORMAT;
|
||||
bool noErrors = true;
|
||||
|
||||
for (; sourceLen > 0; sourceLen--, source++)
|
||||
{
|
||||
if (destLen >= 2) {
|
||||
destLen -= sprintf(dest, format, *source);
|
||||
} else {
|
||||
noErrors = false;
|
||||
}
|
||||
}
|
||||
|
||||
return noErrors;
|
||||
}
|
||||
|
||||
bool Base64::HexEncode(const unsigned char *source, unsigned sourceLen, std::string &destString, bool lowercase)
|
||||
{
|
||||
const char *format = lowercase ? HEX_LOWER_FORMAT : HEX_UPPER_FORMAT;
|
||||
char hexBuffer[3];
|
||||
bool noErrors = true;
|
||||
|
||||
hexBuffer[2] = '\0';
|
||||
|
||||
for (; sourceLen > 0; sourceLen--, source++)
|
||||
{
|
||||
hexBuffer[0] = '\0';
|
||||
sprintf(hexBuffer, format, *source);
|
||||
destString.append(hexBuffer);
|
||||
}
|
||||
|
||||
return noErrors;
|
||||
}
|
||||
|
||||
bool Base64::HexDecode(const char *source, unsigned sourceLen, unsigned char *dest, unsigned *destLen)
|
||||
{
|
||||
unsigned char *destIn = dest;
|
||||
unsigned char digits[2];
|
||||
bool noErrors = true;
|
||||
|
||||
for (; sourceLen > 1; sourceLen -= 2, source += 2)
|
||||
{
|
||||
if (*destLen > 0) {
|
||||
digits[0] = GetHexCode(source[0]);
|
||||
digits[1] = GetHexCode(source[1]);
|
||||
if ((digits[0] != HEX_ERR) && (digits[1] != HEX_ERR)) {
|
||||
*dest = (digits[0] * 16) + digits[1];
|
||||
dest++;
|
||||
(*destLen)--;
|
||||
} else {
|
||||
noErrors = false;
|
||||
}
|
||||
} else {
|
||||
noErrors = false;
|
||||
}
|
||||
}
|
||||
*destLen = dest - destIn;
|
||||
|
||||
if (sourceLen == 1) {
|
||||
noErrors = false;
|
||||
}
|
||||
|
||||
return noErrors;
|
||||
}
|
||||
|
||||
bool Base64::HexDecode(const char *source, unsigned sourceLen, std::string &destString)
|
||||
{
|
||||
unsigned char digits[2];
|
||||
unsigned char byte;
|
||||
bool noErrors = true;
|
||||
|
||||
for (; sourceLen > 1; sourceLen -= 2, source += 2)
|
||||
{
|
||||
digits[0] = GetHexCode(source[0]);
|
||||
digits[1] = GetHexCode(source[1]);
|
||||
if ((digits[0] != HEX_ERR) && (digits[1] != HEX_ERR)) {
|
||||
byte = (digits[0] * 16) + digits[1];
|
||||
destString.append((char *)&byte, 1);
|
||||
} else {
|
||||
noErrors = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (sourceLen == 1) {
|
||||
noErrors = false;
|
||||
}
|
||||
|
||||
return noErrors;
|
||||
}
|
||||
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef BASE64_H
|
||||
#define BASE64_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
class Base64
|
||||
{
|
||||
public:
|
||||
static bool UUEncode(const unsigned char *source, unsigned sourceLen, char *dest, unsigned destLen);
|
||||
static bool UUEncode(const unsigned char *source, unsigned sourceLen, std::string &destString);
|
||||
static bool UUDecode(const char *source, unsigned sourceLen, unsigned char *dest, unsigned *destLen);
|
||||
static bool UUDecode(const char *source, unsigned sourceLen, std::string &destString);
|
||||
|
||||
// these don't *exactly* belong here, but it's a convenient place for them
|
||||
static bool HexEncode(const unsigned char *source, unsigned sourceLen, char *dest, unsigned destLen, bool lowercase = false);
|
||||
static bool HexEncode(const unsigned char *source, unsigned sourceLen, std::string &destString, bool lowercase = false);
|
||||
static bool HexDecode(const char *source, unsigned sourceLen, unsigned char *dest, unsigned *destLen);
|
||||
static bool HexDecode(const char *source, unsigned sourceLen, std::string &destString);
|
||||
|
||||
private:
|
||||
static unsigned UUEncodeTripleToQuad(const unsigned char *triple, char *quad, unsigned bytes);
|
||||
static unsigned UUDecodeQuadToTriple(const char *quad, unsigned char *triple);
|
||||
|
||||
static void Init();
|
||||
static unsigned char GetBase64Code(char base64char);
|
||||
static unsigned char GetHexCode(char hexChar);
|
||||
|
||||
static const char ms_base64Table[];
|
||||
};
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // BASE64_H
|
||||
@@ -0,0 +1,30 @@
|
||||
#if !defined (BLOCKALLOCATOR_H_)
|
||||
#define BLOCKALLOCATOR_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
class BlockAllocator
|
||||
{
|
||||
public:
|
||||
BlockAllocator();
|
||||
~BlockAllocator();
|
||||
void *getBlock(unsigned accum);
|
||||
void returnBlock(unsigned *handle);
|
||||
|
||||
private:
|
||||
unsigned *m_blocks[31];
|
||||
};
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,291 @@
|
||||
/*------------------------------------------------------
|
||||
CCmdLine
|
||||
|
||||
A utility for parsing command lines.
|
||||
|
||||
Copyright (C) 1999 Chris Losinger, Smaller Animals Software.
|
||||
http://www.smalleranimals.com
|
||||
|
||||
This software is provided 'as-is', without any express
|
||||
or implied warranty. In no event will the authors be
|
||||
held liable for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software
|
||||
for any purpose, including commercial applications, and
|
||||
to alter it and redistribute it freely, subject to the
|
||||
following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented;
|
||||
you must not claim that you wrote the original software.
|
||||
If you use this software in a product, an acknowledgment
|
||||
in the product documentation would be appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such,
|
||||
and must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
|
||||
See SACmds.h for more info.
|
||||
------------------------------------------------------*/
|
||||
|
||||
#pragma warning (disable: 4267)
|
||||
// if you're using MFC, you'll need to un-comment this line
|
||||
// #include "stdafx.h"
|
||||
|
||||
#include "CmdLine.h"
|
||||
//#include "crtdbg.h"
|
||||
#include <ctype.h>
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
#endif
|
||||
|
||||
namespace Base
|
||||
{
|
||||
|
||||
/*------------------------------------------------------
|
||||
int CCmdLine::SplitLine(int argc, char **argv)
|
||||
|
||||
parse the command line into switches and arguments
|
||||
|
||||
returns number of switches found
|
||||
------------------------------------------------------*/
|
||||
int CCmdLine::SplitLine(int argc, char **argv)
|
||||
{
|
||||
clear();
|
||||
|
||||
StringType curParam; // current argv[x]
|
||||
|
||||
// skip the exe name (start with i = 1)
|
||||
for (int i = 1; i < argc; i++)
|
||||
{
|
||||
// if it's a switch, start a new CCmdLine
|
||||
if (IsSwitch(argv[i]))
|
||||
{
|
||||
curParam = argv[i];
|
||||
|
||||
StringType arg;
|
||||
|
||||
// look at next input string to see if it's a switch or an argument
|
||||
if (i + 1 < argc)
|
||||
{
|
||||
if (!IsSwitch(argv[i + 1]))
|
||||
{
|
||||
// it's an argument, not a switch
|
||||
arg = argv[i + 1];
|
||||
|
||||
// skip to next
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
arg = "";
|
||||
}
|
||||
}
|
||||
|
||||
// add it
|
||||
CCmdParam cmd;
|
||||
|
||||
// only add non-empty args
|
||||
if (arg != "")
|
||||
{
|
||||
cmd.m_strings.push_back(arg);
|
||||
}
|
||||
|
||||
// add the CCmdParam to 'this'
|
||||
std::pair<CCmdLine::iterator, bool> res = insert(CCmdLine::value_type(curParam, cmd));
|
||||
if (res.second) continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// it's not a new switch, so it must be more stuff for the last switch
|
||||
|
||||
// ...let's add it
|
||||
CCmdLine::iterator theIterator;
|
||||
|
||||
// get an iterator for the current param
|
||||
theIterator = find(curParam);
|
||||
if (theIterator!=end())
|
||||
{
|
||||
(*theIterator).second.m_strings.push_back(argv[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// ??
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return size();
|
||||
}
|
||||
|
||||
/*------------------------------------------------------
|
||||
|
||||
protected member function
|
||||
test a parameter to see if it's a switch :
|
||||
|
||||
switches are of the form : -x
|
||||
where 'x' is one or more characters.
|
||||
the first character of a switch must be non-numeric!
|
||||
|
||||
------------------------------------------------------*/
|
||||
|
||||
bool CCmdLine::IsSwitch(const char *pParam)
|
||||
{
|
||||
if (pParam==NULL)
|
||||
return false;
|
||||
|
||||
// switches must non-empty
|
||||
// must have at least one character after the '-'
|
||||
int len = strlen(pParam);
|
||||
if (len <= 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// switches always start with '-'
|
||||
if (pParam[0]=='-')
|
||||
{
|
||||
// allow negative numbers as arguments.
|
||||
// ie., don't count them as switches
|
||||
return (!isdigit(pParam[1]));
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*------------------------------------------------------
|
||||
bool CCmdLine::HasSwitch(const char *pSwitch)
|
||||
|
||||
was the switch found on the command line ?
|
||||
|
||||
ex. if the command line is : app.exe -a p1 p2 p3 -b p4 -c -d p5
|
||||
|
||||
call return
|
||||
---- ------
|
||||
cmdLine.HasSwitch("-a") true
|
||||
cmdLine.HasSwitch("-z") false
|
||||
------------------------------------------------------*/
|
||||
|
||||
bool CCmdLine::HasSwitch(const char *pSwitch)
|
||||
{
|
||||
CCmdLine::iterator theIterator;
|
||||
theIterator = find(pSwitch);
|
||||
return (theIterator!=end());
|
||||
}
|
||||
|
||||
/*------------------------------------------------------
|
||||
|
||||
StringType CCmdLine::GetSafeArgument(const char *pSwitch, int iIdx, const char *pDefault)
|
||||
|
||||
fetch an argument associated with a switch . if the parameter at
|
||||
index iIdx is not found, this will return the default that you
|
||||
provide.
|
||||
|
||||
example :
|
||||
|
||||
command line is : app.exe -a p1 p2 p3 -b p4 -c -d p5
|
||||
|
||||
call return
|
||||
---- ------
|
||||
cmdLine.GetSafeArgument("-a", 0, "zz") p1
|
||||
cmdLine.GetSafeArgument("-a", 1, "zz") p2
|
||||
cmdLine.GetSafeArgument("-b", 0, "zz") p4
|
||||
cmdLine.GetSafeArgument("-b", 1, "zz") zz
|
||||
|
||||
------------------------------------------------------*/
|
||||
|
||||
StringType CCmdLine::GetSafeArgument(const char *pSwitch, int iIdx, const char *pDefault)
|
||||
{
|
||||
StringType sRet;
|
||||
|
||||
if (pDefault!=NULL)
|
||||
sRet = pDefault;
|
||||
|
||||
try
|
||||
{
|
||||
sRet = GetArgument(pSwitch, iIdx);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
|
||||
return sRet;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------
|
||||
|
||||
StringType CCmdLine::GetArgument(const char *pSwitch, int iIdx)
|
||||
|
||||
fetch a argument associated with a switch. throws an exception
|
||||
of (int)0, if the parameter at index iIdx is not found.
|
||||
|
||||
example :
|
||||
|
||||
command line is : app.exe -a p1 p2 p3 -b p4 -c -d p5
|
||||
|
||||
call return
|
||||
---- ------
|
||||
cmdLine.GetArgument("-a", 0) p1
|
||||
cmdLine.GetArgument("-b", 1) throws (int)0, returns an empty string
|
||||
|
||||
------------------------------------------------------*/
|
||||
|
||||
StringType CCmdLine::GetArgument(const char *pSwitch, int iIdx)
|
||||
{
|
||||
if (HasSwitch(pSwitch))
|
||||
{
|
||||
CCmdLine::iterator theIterator;
|
||||
|
||||
theIterator = find(pSwitch);
|
||||
if (theIterator!=end())
|
||||
{
|
||||
if ((int)(*theIterator).second.m_strings.size() > iIdx)
|
||||
{
|
||||
return (*theIterator).second.m_strings[iIdx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw (int)0;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/*------------------------------------------------------
|
||||
int CCmdLine::GetArgumentCount(const char *pSwitch)
|
||||
|
||||
returns the number of arguments found for a given switch.
|
||||
|
||||
returns -1 if the switch was not found
|
||||
|
||||
------------------------------------------------------*/
|
||||
|
||||
int CCmdLine::GetArgumentCount(const char *pSwitch)
|
||||
{
|
||||
int iArgumentCount = -1;
|
||||
|
||||
if (HasSwitch(pSwitch))
|
||||
{
|
||||
CCmdLine::iterator theIterator;
|
||||
|
||||
theIterator = find(pSwitch);
|
||||
if (theIterator!=end())
|
||||
{
|
||||
iArgumentCount = (*theIterator).second.m_strings.size();
|
||||
}
|
||||
}
|
||||
|
||||
return iArgumentCount;
|
||||
}
|
||||
|
||||
}; // end namespace Base
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
}; // end namespace NAMESPACE
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
/*------------------------------------------------------
|
||||
CCmdLine
|
||||
|
||||
A utility for parsing command lines.
|
||||
|
||||
Copyright (C) 1999 Chris Losinger, Smaller Animals Software.
|
||||
http://www.smalleranimals.com
|
||||
|
||||
This software is provided 'as-is', without any express
|
||||
or implied warranty. In no event will the authors be
|
||||
held liable for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software
|
||||
for any purpose, including commercial applications, and
|
||||
to alter it and redistribute it freely, subject to the
|
||||
following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented;
|
||||
you must not claim that you wrote the original software.
|
||||
If you use this software in a product, an acknowledgment
|
||||
in the product documentation would be appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such,
|
||||
and must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
|
||||
-------------------------
|
||||
|
||||
Example :
|
||||
|
||||
Our example application uses a command line that has two
|
||||
required switches and two optional switches. The app should abort
|
||||
if the required switches are not present and continue with default
|
||||
values if the optional switches are not present.
|
||||
|
||||
Sample command line :
|
||||
MyApp.exe -p1 text1 text2 -p2 "this is a big argument" -opt1 -55 -opt2
|
||||
|
||||
Switches -p1 and -p2 are required.
|
||||
p1 has two arguments and p2 has one.
|
||||
|
||||
Switches -opt1 and -opt2 are optional.
|
||||
opt1 requires a numeric argument.
|
||||
opt2 has no arguments.
|
||||
|
||||
Also, assume that the app displays a 'help' screen if the '-h' switch
|
||||
is present on the command line.
|
||||
|
||||
#include "CmdLine.h"
|
||||
|
||||
void main(int argc, char **argv)
|
||||
{
|
||||
// our cmd line parser object
|
||||
CCmdLine cmdLine;
|
||||
|
||||
// parse argc,argv
|
||||
if (cmdLine.SplitLine(argc, argv) < 1)
|
||||
{
|
||||
// no switches were given on the command line, abort
|
||||
ASSERT(0);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// test for the 'help' case
|
||||
if (cmdLine.HasSwitch("-h"))
|
||||
{
|
||||
show_help();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// get the required arguments
|
||||
StringType p1_1, p1_2, p2_1;
|
||||
try
|
||||
{
|
||||
// if any of these fail, we'll end up in the catch() block
|
||||
p1_1 = cmdLine.GetArgument("-p1", 0);
|
||||
p1_2 = cmdLine.GetArgument("-p1", 1);
|
||||
p2_1 = cmdLine.GetArgument("-p2", 0);
|
||||
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// one of the required arguments was missing, abort
|
||||
ASSERT(0);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// get the optional parameters
|
||||
|
||||
// convert to an int, default to '100'
|
||||
int iOpt1Val = atoi(cmdLine.GetSafeArgument("-opt1", 0, 100));
|
||||
|
||||
// since opt2 has no arguments, just test for the presence of
|
||||
// the '-opt2' switch
|
||||
bool bOptVal2 = cmdLine.HasSwitch("-opt2");
|
||||
|
||||
.... and so on....
|
||||
|
||||
}
|
||||
|
||||
If this class is used in an MFC application, StringType is CString, else
|
||||
it uses the STL 'string' type.
|
||||
|
||||
If this is an MFC app, you can use the __argc and __argv macros from
|
||||
you CYourWinApp::InitInstance() function in place of the standard argc
|
||||
and argv variables.
|
||||
|
||||
------------------------------------------------------*/
|
||||
#ifndef SACMDSH
|
||||
#define SACMDSH
|
||||
|
||||
|
||||
//#include <iostream> // you may need this
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
//using namespace std ;
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
#endif
|
||||
|
||||
namespace Base
|
||||
{
|
||||
|
||||
#ifdef __AFX_H__
|
||||
// if we're using MFC, use CStrings
|
||||
#define StringType CString
|
||||
#else
|
||||
// if we're not using MFC, use STL strings
|
||||
#define StringType std::string
|
||||
#endif
|
||||
|
||||
// tell the compiler to shut up
|
||||
#pragma warning(disable:4786)
|
||||
|
||||
|
||||
// handy little container for our argument vector
|
||||
struct CCmdParam
|
||||
{
|
||||
std::vector<StringType> m_strings;
|
||||
};
|
||||
|
||||
// this class is actually a map of strings to vectors
|
||||
typedef std::map<StringType, CCmdParam> _CCmdLine;
|
||||
|
||||
// the command line parser class
|
||||
class CCmdLine : public _CCmdLine
|
||||
{
|
||||
|
||||
public:
|
||||
/*------------------------------------------------------
|
||||
int CCmdLine::SplitLine(int argc, char **argv)
|
||||
|
||||
parse the command line into switches and arguments.
|
||||
|
||||
returns number of switches found
|
||||
------------------------------------------------------*/
|
||||
int SplitLine(int argc, char **argv);
|
||||
|
||||
/*------------------------------------------------------
|
||||
bool CCmdLine::HasSwitch(const char *pSwitch)
|
||||
|
||||
was the switch found on the command line ?
|
||||
|
||||
ex. if the command line is : app.exe -a p1 p2 p3 -b p4 -c -d p5
|
||||
|
||||
call return
|
||||
---- ------
|
||||
cmdLine.HasSwitch("-a") true
|
||||
cmdLine.HasSwitch("-z") false
|
||||
------------------------------------------------------*/
|
||||
bool HasSwitch(const char *pSwitch);
|
||||
|
||||
/*------------------------------------------------------
|
||||
|
||||
StringType CCmdLine::GetSafeArgument(const char *pSwitch, int iIdx, const char *pDefault)
|
||||
|
||||
fetch an argument associated with a switch . if the parameter at
|
||||
index iIdx is not found, this will return the default that you
|
||||
provide.
|
||||
|
||||
example :
|
||||
|
||||
command line is : app.exe -a p1 p2 p3 -b p4 -c -d p5
|
||||
|
||||
call return
|
||||
---- ------
|
||||
cmdLine.GetSafeArgument("-a", 0, "zz") p1
|
||||
cmdLine.GetSafeArgument("-a", 1, "zz") p2
|
||||
cmdLine.GetSafeArgument("-b", 0, "zz") p4
|
||||
cmdLine.GetSafeArgument("-b", 1, "zz") zz
|
||||
|
||||
------------------------------------------------------*/
|
||||
|
||||
StringType GetSafeArgument(const char *pSwitch, int iIdx, const char *pDefault);
|
||||
|
||||
/*------------------------------------------------------
|
||||
|
||||
StringType CCmdLine::GetArgument(const char *pSwitch, int iIdx)
|
||||
|
||||
fetch a argument associated with a switch. throws an exception
|
||||
of (int)0, if the parameter at index iIdx is not found.
|
||||
|
||||
example :
|
||||
|
||||
command line is : app.exe -a p1 p2 p3 -b p4 -c -d p5
|
||||
|
||||
call return
|
||||
---- ------
|
||||
cmdLine.GetArgument("-a", 0) p1
|
||||
cmdLine.GetArgument("-b", 1) throws (int)0, returns an empty string
|
||||
|
||||
------------------------------------------------------*/
|
||||
StringType GetArgument(const char *pSwitch, int iIdx);
|
||||
|
||||
/*------------------------------------------------------
|
||||
int CCmdLine::GetArgumentCount(const char *pSwitch)
|
||||
|
||||
returns the number of arguments found for a given switch.
|
||||
|
||||
returns -1 if the switch was not found
|
||||
|
||||
------------------------------------------------------*/
|
||||
int GetArgumentCount(const char *pSwitch);
|
||||
|
||||
protected:
|
||||
/*------------------------------------------------------
|
||||
|
||||
protected member function
|
||||
test a parameter to see if it's a switch :
|
||||
|
||||
switches are of the form : -x
|
||||
where 'x' is one or more characters.
|
||||
the first character of a switch must be non-numeric!
|
||||
|
||||
------------------------------------------------------*/
|
||||
bool IsSwitch(const char *pParam);
|
||||
};
|
||||
|
||||
}; // end namespace Base
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
}; // end namespace NAMESPACE
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
#include "Config.h"
|
||||
|
||||
//#include <stdlib.h>
|
||||
//#include <stdio.h>
|
||||
//#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "Platform.h"
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
//-----------------------------------
|
||||
/// Read the config file into memory
|
||||
/// Returns true for success
|
||||
bool CConfig::LoadFile(char * file)
|
||||
//-----------------------------------
|
||||
{
|
||||
char ch;
|
||||
FILE * fp;
|
||||
|
||||
UnloadFile();
|
||||
|
||||
// open file
|
||||
fp = fopen(file, "r");
|
||||
if (fp == NULL || fp == (FILE *)-1)
|
||||
{
|
||||
//fprintf(stderr,"Failed to open config file %s!",file);
|
||||
return false;
|
||||
}
|
||||
|
||||
// get length of file
|
||||
fseek(fp,0,SEEK_END);
|
||||
long length = ftell(fp);
|
||||
rewind(fp);
|
||||
|
||||
// allocate buffer
|
||||
pConfig = new char[length + 1];
|
||||
|
||||
if (pConfig == NULL)
|
||||
{
|
||||
fclose(fp);
|
||||
fprintf(stderr,"Failed to alloc config buffer length %d",(int32)length);
|
||||
return false;
|
||||
}
|
||||
memset(pConfig,0,length);
|
||||
|
||||
// read data, stripping comments
|
||||
int i = 0;
|
||||
while (i < length)
|
||||
{
|
||||
ch = fgetc(fp);
|
||||
if (feof(fp))
|
||||
break;
|
||||
if (ch == '#')
|
||||
{
|
||||
while (ch != 10 && !feof (fp))
|
||||
{
|
||||
ch = fgetc(fp);
|
||||
}
|
||||
}
|
||||
pConfig[i++] = ch;
|
||||
}
|
||||
pConfig[i] = 0; // mark end of file buffer
|
||||
|
||||
fclose(fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------
|
||||
/// remove the config file from memory
|
||||
void CConfig::UnloadFile(void)
|
||||
//-----------------------------------
|
||||
{
|
||||
if (pConfig)
|
||||
delete[] pConfig;
|
||||
pConfig = NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------
|
||||
/// finds a key of the config in memory
|
||||
// key argument is case-insensitive, but must be upper case in config file
|
||||
// Returns true for success (passing NULL is a special case returned as success)
|
||||
bool CConfig::FindKey(char *key)
|
||||
//-----------------------------------
|
||||
{
|
||||
if (pConfig == NULL)
|
||||
return false;
|
||||
|
||||
// special case...continue with existing key
|
||||
if (key == NULL)
|
||||
return true;
|
||||
|
||||
// form the search key
|
||||
strcpy(strBuffer, "[");
|
||||
strcat(strBuffer, key);
|
||||
strcat(strBuffer,"]");
|
||||
strupr(strBuffer); // keywords must be upper case
|
||||
|
||||
// find the key heading
|
||||
pCursor = strstr(pConfig,strBuffer);
|
||||
if (pCursor==NULL)
|
||||
return false;
|
||||
|
||||
// find the closing bracket of key heading
|
||||
pCursor = strchr(pCursor,']');
|
||||
if (pCursor==NULL)
|
||||
return false;
|
||||
pCursor++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------
|
||||
/// extract a number from the config string
|
||||
// pass the key to find the first number in the list, else NULL to find the next number in the list
|
||||
// returns 0 if no number is found, else returns the number
|
||||
long CConfig::GetLong(char *key)
|
||||
//-----------------------------------
|
||||
{
|
||||
if (!FindKey(key))
|
||||
return 0;
|
||||
|
||||
|
||||
// look for start of number or end of key
|
||||
while (*pCursor && !isdigit(*pCursor) && *pCursor != '[' && *pCursor != '-')
|
||||
pCursor++;
|
||||
|
||||
int c = 0;
|
||||
while (*pCursor && (isdigit(*pCursor) || *pCursor == '-') && c < (int)sizeof(strBuffer))
|
||||
strBuffer[c++] = *(pCursor++);
|
||||
strBuffer[c] = '\0';
|
||||
|
||||
return atol(strBuffer);
|
||||
}
|
||||
|
||||
//-----------------------------------
|
||||
/// extract string (in double-quotes) from the list.
|
||||
// pass the key to find the first string in the list, else NULL to find the next string in the list
|
||||
// returns NULL if no string found, else returns a temporary copy of the string (without quotes)
|
||||
char * CConfig::GetString(char *key)
|
||||
//-----------------------------------
|
||||
{
|
||||
if (!FindKey(key))
|
||||
return NULL;
|
||||
|
||||
|
||||
// look for start of string or end of key
|
||||
while (*pCursor && *pCursor != '"' && *pCursor != '[')
|
||||
pCursor++;
|
||||
if (*(pCursor++) != '"')
|
||||
return NULL;
|
||||
|
||||
// until closing quote
|
||||
int c = 0;
|
||||
while (*pCursor && c < (int)sizeof(strBuffer))
|
||||
{
|
||||
strBuffer[c++] = *pCursor; // extract string
|
||||
if (*pCursor++ == '"')
|
||||
{
|
||||
strBuffer[--c] = '\0';
|
||||
return strBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
; Example syntax of a config file.
|
||||
# This is a comment to end of line.
|
||||
; This is a comment too....both comments are to end of line
|
||||
[KEYWORD] ; all keywords must be upper case to be recognized
|
||||
|
||||
; single number, with code example
|
||||
# long number = config.GetLong("PORT");
|
||||
[PORT] 5999 ; common single-value case
|
||||
|
||||
; list of strings, with code example
|
||||
# config.FindKey("NUMBERS");
|
||||
# while (number = config.GetLong(NULL))
|
||||
# YourHandleNumber(number);
|
||||
[NUMBERS] 100
|
||||
200, 300 ; formatting is very flexible as long as numbers are delimited by non-numbers
|
||||
400 500
|
||||
600 ; This is badly formatted for example purposes only
|
||||
|
||||
; single string, with code example
|
||||
# strcpy(mystring, config.GetString("STRING"));
|
||||
[STRING] "This is a string"
|
||||
|
||||
; list of strings, with code example
|
||||
# config.FindKey("HOSTS");
|
||||
# while (string = config.GetString(NULL))
|
||||
# YourHandleString(string);
|
||||
[HOSTS]
|
||||
"206.19.151.173:5999"
|
||||
"206.19.151.173:5998"
|
||||
"206.19.151.173:2000"
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _CONFIG_H
|
||||
#define _CONFIG_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
class CConfig
|
||||
{
|
||||
public:
|
||||
// constructor, no file loaded
|
||||
CConfig();
|
||||
|
||||
// version of constructor that calls LoadFile(file)
|
||||
CConfig(char * file);
|
||||
|
||||
// destructor, will delete all buffers
|
||||
~CConfig();
|
||||
|
||||
// copy text config file into memory. Required to use extraction methods.
|
||||
// if called with a different filename, existing file will be discarded.
|
||||
bool LoadFile(char * file);
|
||||
|
||||
// delete memory buffer of file
|
||||
void UnloadFile();
|
||||
|
||||
// set cursor in file based on key name. Alternate way to select first key when extracting lists.
|
||||
// returns TRUE if key is found.
|
||||
bool FindKey(char *key);
|
||||
|
||||
// extract string from config. If key is NULL, extract next string in list, else extract first string.
|
||||
// if key was not found, returns NULL
|
||||
char * GetString(char *key = NULL);
|
||||
|
||||
// extract number from config. If key is NULL, extract next number in list, else extract first number.
|
||||
// if key was not found, returns 0
|
||||
long GetLong(char *key = NULL); // extract number from config.
|
||||
|
||||
// indicate if config file has been loaded
|
||||
inline bool FileLoaded() { return pConfig == NULL ? false : true; }
|
||||
|
||||
private:
|
||||
char * pConfig; // pointer to file memory
|
||||
char * pCursor; // current cursor into file memory
|
||||
char strBuffer[8196]; // buffer for GetString return pointer -- reused
|
||||
};
|
||||
|
||||
inline CConfig::CConfig()
|
||||
{
|
||||
pConfig = NULL;
|
||||
pCursor = NULL;
|
||||
}
|
||||
|
||||
inline CConfig::CConfig(char * file)
|
||||
{
|
||||
pConfig = NULL;
|
||||
pCursor = NULL;
|
||||
LoadFile(file);
|
||||
}
|
||||
|
||||
inline CConfig::~CConfig()
|
||||
{
|
||||
UnloadFile();
|
||||
}
|
||||
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef BASE_EVENT_H
|
||||
#define BASE_EVENT_H
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
#include "win32/Event.h"
|
||||
|
||||
#elif linux
|
||||
|
||||
#include "linux/Event.h"
|
||||
|
||||
#elif sparc
|
||||
|
||||
#include "solaris/Event.h"
|
||||
|
||||
#else
|
||||
|
||||
#error /Base/Event.h: Undefine platform type
|
||||
|
||||
#endif
|
||||
|
||||
#endif // BASE_EVENT_H
|
||||
@@ -0,0 +1,756 @@
|
||||
#include "Logger.h"
|
||||
#include "Mutex.h"
|
||||
#include <sys/stat.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
const long kDateLenAllowance = 256;
|
||||
|
||||
#ifndef _BASE_LOGGER_MAX_BUF_SIZE_
|
||||
#define _BASE_LOGGER_MAX_BUF_SIZE_ 2048
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
const char file_sep = '\\';
|
||||
|
||||
#define LOGGER_GET_CURR_TIME(__tm_struct__, __time_t_var__) \
|
||||
memcpy(&__tm_struct__, localtime(&__time_t_var__), sizeof(__tm_struct__))
|
||||
|
||||
#define LOGGER_MAKE_DIR(__dir_name__, __mode__) mkdir(__dir_name__)
|
||||
|
||||
# ifdef _MT
|
||||
# define LOGGER_LOCK rLock.Lock();
|
||||
# define LOGGER_UNLOCK rLock.Unlock();
|
||||
# else // ifndef _MT
|
||||
# define LOGGER_LOCK
|
||||
# define LOGGER_UNLOCK
|
||||
# endif // _MT
|
||||
|
||||
|
||||
|
||||
|
||||
#define vsnprintf _vsnprintf
|
||||
|
||||
#else // ifndef WIN32
|
||||
const char file_sep = '/';
|
||||
|
||||
const char *syslogLevels[] = {
|
||||
"EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG" };
|
||||
|
||||
const int numSysLogLevels = sizeof(syslogLevels) / sizeof(char *);
|
||||
|
||||
#define LOGGER_GET_CURR_TIME(__tm_struct__, __time_t_var__) \
|
||||
localtime_r(&__time_t_var__, &__tm_struct__)
|
||||
|
||||
#define LOGGER_MAKE_DIR(__dir_name__, __mode__) mkdir(__dir_name__, __mode__)
|
||||
|
||||
# ifdef _REENTRANT
|
||||
# define LOGGER_LOCK rLock.Lock();
|
||||
# define LOGGER_UNLOCK rLock.Unlock();
|
||||
# else // ifndef _REENTRANT
|
||||
# define LOGGER_LOCK
|
||||
# define LOGGER_UNLOCK
|
||||
# endif // _REENTRANT
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // WIN32
|
||||
|
||||
|
||||
Logger::Logger(const char *prefix,
|
||||
int level,
|
||||
unsigned size,
|
||||
bool rollDate)
|
||||
: m_defaultLevel(level),
|
||||
m_defaultSize(size),
|
||||
m_dirPrefix(prefix),
|
||||
m_rollDate(rollDate),
|
||||
m_opennedSyslog(false)
|
||||
{
|
||||
ELogType logType = eUseLocalFile;
|
||||
|
||||
m_logLevelToTypeMap[LOG_DEBUG] = logType;
|
||||
m_logLevelToTypeMap[LOG_INFO] = logType;
|
||||
m_logLevelToTypeMap[LOG_NOTICE] = logType;
|
||||
m_logLevelToTypeMap[LOG_WARNING] = logType;
|
||||
m_logLevelToTypeMap[LOG_ERR] = logType;
|
||||
m_logLevelToTypeMap[LOG_CRIT] = logType;
|
||||
m_logLevelToTypeMap[LOG_ALERT] = logType;
|
||||
m_logLevelToTypeMap[LOG_EMERG] = logType;
|
||||
m_combinedLogType = logType;
|
||||
|
||||
LoggerInit("UnNamedServer");
|
||||
}
|
||||
|
||||
Logger::Logger(const char *programName,
|
||||
const ELogType logType,
|
||||
const char *prefix,
|
||||
int level,
|
||||
unsigned size,
|
||||
bool rollDate)
|
||||
: m_defaultLevel(level),
|
||||
m_defaultSize(size),
|
||||
m_dirPrefix(prefix),
|
||||
m_rollDate(rollDate),
|
||||
m_opennedSyslog(false)
|
||||
{
|
||||
m_logLevelToTypeMap[LOG_DEBUG] = logType;
|
||||
m_logLevelToTypeMap[LOG_INFO] = logType;
|
||||
m_logLevelToTypeMap[LOG_NOTICE] = logType;
|
||||
m_logLevelToTypeMap[LOG_WARNING] = logType;
|
||||
m_logLevelToTypeMap[LOG_ERR] = logType;
|
||||
m_logLevelToTypeMap[LOG_CRIT] = logType;
|
||||
m_logLevelToTypeMap[LOG_ALERT] = logType;
|
||||
m_logLevelToTypeMap[LOG_EMERG] = logType;
|
||||
m_combinedLogType = logType;
|
||||
|
||||
LoggerInit(programName);
|
||||
}
|
||||
|
||||
|
||||
void Logger::LoggerInit(const char *programName)
|
||||
{
|
||||
char buf[1024];
|
||||
FILE *logDir = NULL;
|
||||
|
||||
if (0 != (m_combinedLogType & eUseLocalFile))
|
||||
{
|
||||
logDir = fopen(m_dirPrefix.c_str(), "r+");
|
||||
if(errno == ENOENT)
|
||||
{
|
||||
cmkdir(m_dirPrefix.c_str(), 0755);
|
||||
}
|
||||
else if(logDir != NULL)
|
||||
{
|
||||
fclose(logDir);
|
||||
}
|
||||
|
||||
tm now;
|
||||
time_t t = time(NULL);
|
||||
|
||||
LOGGER_GET_CURR_TIME(now, t);
|
||||
|
||||
memcpy(&m_lastDateTime, &now, sizeof(tm));
|
||||
if(m_rollDate)
|
||||
{
|
||||
sprintf(buf, "%s%c%2.2d-%2.2d-%2.2d", m_dirPrefix.c_str(), file_sep, (now.tm_mon + 1), now.tm_mday, (now.tm_year % 100));
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(buf, "%s", m_dirPrefix.c_str());
|
||||
}
|
||||
logDir = fopen(buf, "r+");
|
||||
if(errno == ENOENT)
|
||||
{
|
||||
cmkdir(buf, 0755);
|
||||
}
|
||||
else if(logDir != NULL)
|
||||
{
|
||||
fclose(logDir);
|
||||
}
|
||||
m_logPrefix = buf;
|
||||
m_logRolloverSize = 0x80000000; // Default: 2 GB upper limit on file size
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
// Open connection to syslog
|
||||
if (0 != (m_combinedLogType & eUseSysLog)) {
|
||||
openlog(programName, LOG_NDELAY | LOG_PID, LOG_LOCAL5);
|
||||
m_opennedSyslog = true;
|
||||
}
|
||||
#endif // !WIN32
|
||||
|
||||
}
|
||||
|
||||
Logger::~Logger()
|
||||
{
|
||||
map<unsigned, LogInfo *>::iterator iter;
|
||||
for(iter = m_logTable.begin(); iter != m_logTable.end(); iter++)
|
||||
{
|
||||
logWithSys((*iter).first, LOG_INFO, LOG_FILEONLY, "---=== Log Stopped ===---");
|
||||
fflush((*iter).second->file);
|
||||
fclose((*iter).second->file);
|
||||
delete((*iter).second);
|
||||
|
||||
}
|
||||
m_logTable.clear();
|
||||
|
||||
#ifndef WIN32
|
||||
// close connection to syslog
|
||||
if (m_opennedSyslog)
|
||||
closelog();
|
||||
#endif // !WIN32
|
||||
}
|
||||
|
||||
ELogType Logger::getLoggingType(unsigned logLevel) const
|
||||
{
|
||||
std::map<unsigned, ELogType>::const_iterator levelIter = m_logLevelToTypeMap.find(logLevel);
|
||||
ELogType logType = eUseNone;
|
||||
|
||||
if (levelIter != m_logLevelToTypeMap.end())
|
||||
{
|
||||
logType = levelIter->second;
|
||||
}
|
||||
|
||||
return logType;
|
||||
}
|
||||
|
||||
void Logger::setLoggingType(unsigned logLevel, ELogType logType)
|
||||
{
|
||||
std::map<unsigned, ELogType>::iterator levelIter = m_logLevelToTypeMap.find(logLevel);
|
||||
|
||||
if (levelIter != m_logLevelToTypeMap.end())
|
||||
{
|
||||
levelIter->second = logType;
|
||||
|
||||
if (logType > m_combinedLogType) {
|
||||
m_combinedLogType = (ELogType)(m_combinedLogType | logType);
|
||||
} else {
|
||||
m_combinedLogType = eUseNone;
|
||||
|
||||
for (levelIter = m_logLevelToTypeMap.begin();
|
||||
levelIter != m_logLevelToTypeMap.end();
|
||||
levelIter++)
|
||||
{
|
||||
m_combinedLogType = (ELogType)(m_combinedLogType | logType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::flush(unsigned logenum)
|
||||
{
|
||||
map<unsigned, LogInfo *>::iterator iter = m_logTable.find(logenum);
|
||||
|
||||
if(iter != m_logTable.end())
|
||||
{
|
||||
LogInfo *info = (*iter).second;
|
||||
fflush(info->file);
|
||||
info->used = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::flushAll()
|
||||
{
|
||||
if (0 != (m_combinedLogType & eUseLocalFile))
|
||||
{
|
||||
map<unsigned, LogInfo *>::iterator iter;
|
||||
|
||||
for(iter = m_logTable.begin(); iter != m_logTable.end(); iter++)
|
||||
{
|
||||
LogInfo *info = (*iter).second;
|
||||
fflush(info->file);
|
||||
info->used = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::addLog(const char *id, unsigned logenum, int level, unsigned size)
|
||||
{
|
||||
if (0 != (m_combinedLogType & eUseLocalFile))
|
||||
{
|
||||
LogInfo *newLog = new LogInfo;
|
||||
newLog->filename = m_logPrefix + file_sep + id + ".log";
|
||||
newLog->name = id;
|
||||
newLog->used = 0;
|
||||
newLog->max = size;
|
||||
newLog->last = 0;
|
||||
newLog->level = level;
|
||||
|
||||
m_logTable.insert(pair<unsigned, LogInfo *>(logenum, newLog));
|
||||
|
||||
if(strcmp("stdout", id) == 0)
|
||||
{
|
||||
newLog->file = stdout;
|
||||
}
|
||||
else if(strcmp("stderr", id) == 0)
|
||||
{
|
||||
newLog->file = stderr;
|
||||
}
|
||||
else
|
||||
{
|
||||
newLog->file = fopen(newLog->filename.c_str(), "a+");
|
||||
if(newLog->file == NULL)
|
||||
{
|
||||
printf("Open file %s failed\n", newLog->filename.c_str());
|
||||
}
|
||||
}
|
||||
logWithSys(logenum, LOG_INFO, LOG_FILEONLY, "---=== Log Started ===---");
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::addLog(const char *id, unsigned logenum)
|
||||
{
|
||||
if (0 != (m_combinedLogType & eUseLocalFile))
|
||||
{
|
||||
LogInfo *newLog = new LogInfo;
|
||||
newLog->filename = m_logPrefix + file_sep + id + ".log";
|
||||
newLog->name = id;
|
||||
newLog->used = 0;
|
||||
newLog->max = m_defaultSize;
|
||||
newLog->last = 0;
|
||||
newLog->level = m_defaultLevel;
|
||||
|
||||
m_logTable.insert(pair<unsigned, LogInfo *>(logenum, newLog));
|
||||
|
||||
if(strcmp("stdout", id) == 0)
|
||||
{
|
||||
newLog->file = stdout;
|
||||
}
|
||||
else if(strcmp("stderr", id) == 0)
|
||||
{
|
||||
newLog->file = stderr;
|
||||
}
|
||||
else
|
||||
{
|
||||
newLog->file = fopen(newLog->filename.c_str(), "a+");
|
||||
if(newLog->file == NULL)
|
||||
{
|
||||
printf("Open file %s failed\n", newLog->filename.c_str());
|
||||
}
|
||||
}
|
||||
logWithSys(logenum, LOG_INFO, LOG_FILEONLY, "---===Log Started ===---");
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::updateLog(unsigned logenum, int level, unsigned size)
|
||||
{
|
||||
if (0 != (m_combinedLogType & eUseLocalFile))
|
||||
{
|
||||
map<unsigned, LogInfo *>::iterator iter = m_logTable.find(logenum);
|
||||
|
||||
if(iter != m_logTable.end())
|
||||
{
|
||||
(*iter).second->level = level;
|
||||
(*iter).second->max = size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::removeLog(unsigned logenum)
|
||||
{
|
||||
if (0 != (m_combinedLogType & eUseLocalFile))
|
||||
{
|
||||
map<unsigned, LogInfo *>::iterator iter = m_logTable.find(logenum);
|
||||
|
||||
if(iter != m_logTable.end())
|
||||
{
|
||||
#ifdef WIN32
|
||||
logWithSys((*iter).first, LOG_INFO, LOG_FILEONLY, "---=== Log Stopped ===---");
|
||||
#else // ifndef WIN32
|
||||
logWithSys((*iter).first, LOG_INFO, LOG_ALWAYS, "---=== Log Stopped ===---");
|
||||
#endif // WIN32
|
||||
fflush((*iter).second->file);
|
||||
fclose((*iter).second->file);
|
||||
delete((*iter).second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void Logger::logSimple(unsigned logenum, int level, const char *message)
|
||||
{
|
||||
map<unsigned, LogInfo *>::iterator iter = m_logTable.find(logenum);
|
||||
|
||||
if(iter == m_logTable.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
time_t t = time(NULL);
|
||||
LogInfo *info = (*iter).second;
|
||||
if(level >= info->level)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#ifndef WIN32
|
||||
if(level == LOG_FILEONLY && ((info->file == stderr) || (info->file == stdout)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif // !WIN32
|
||||
if(info->last != t)
|
||||
{
|
||||
memcpy(&info->ts, localtime(&t), sizeof(tm));
|
||||
info->last = t;
|
||||
}
|
||||
|
||||
if(m_rollDate && info->ts.tm_mday != m_lastDateTime.tm_mday)
|
||||
{
|
||||
LOGGER_LOCK
|
||||
|
||||
if(info->ts.tm_mday != m_lastDateTime.tm_mday)
|
||||
{
|
||||
memcpy(&m_lastDateTime, &info->ts, sizeof(tm));
|
||||
rollDate(t);
|
||||
}
|
||||
|
||||
LOGGER_UNLOCK
|
||||
}
|
||||
if(iter != m_logTable.end())
|
||||
{
|
||||
if(info->max > 0)
|
||||
{
|
||||
info->used += fprintf(info->file, "[%2.2d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d] %s", (info->ts.tm_mon + 1), info->ts.tm_mday, (info->ts.tm_year % 100), info->ts.tm_hour, info->ts.tm_min, info->ts.tm_sec, message);
|
||||
info->used += fputc('\n', info->file);
|
||||
if(info->used > info->max)
|
||||
{
|
||||
fflush(info->file);
|
||||
info->used = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(info->file, "[%2.2d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d] %s", (info->ts.tm_mon + 1), info->ts.tm_mday, (info->ts.tm_year % 100), info->ts.tm_hour, info->ts.tm_min, info->ts.tm_sec, message);
|
||||
fputc('\n', info->file);
|
||||
fflush(info->file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void Logger::logSimpleWithSys(unsigned logenum, unsigned priority, int level, const char *message)
|
||||
{
|
||||
#ifndef WIN32
|
||||
if (0 != (m_combinedLogType & eUseSysLog))
|
||||
syslog(priority, "%s: %s", syslogLevels[priority % numSysLogLevels], message);
|
||||
#endif // !WIN32
|
||||
|
||||
if (0 != (m_combinedLogType & eUseLocalFile))
|
||||
{
|
||||
map<unsigned, LogInfo *>::iterator iter = m_logTable.find(logenum);
|
||||
|
||||
if(iter == m_logTable.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
time_t t = time(NULL);
|
||||
LogInfo *info = (*iter).second;
|
||||
|
||||
if(level >= info->level)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
if(level == LOG_FILEONLY && ((info->file == stderr) || (info->file == stdout)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif // !WIN32
|
||||
|
||||
if(info->last != t)
|
||||
{
|
||||
memcpy(&info->ts, localtime(&t), sizeof(tm));
|
||||
info->last = t;
|
||||
}
|
||||
|
||||
if(m_rollDate && info->ts.tm_mday != m_lastDateTime.tm_mday)
|
||||
{
|
||||
LOGGER_LOCK
|
||||
|
||||
if(info->ts.tm_mday != m_lastDateTime.tm_mday)
|
||||
{
|
||||
memcpy(&m_lastDateTime, &info->ts, sizeof(tm));
|
||||
rollDate(t);
|
||||
}
|
||||
|
||||
LOGGER_UNLOCK
|
||||
}
|
||||
if(iter != m_logTable.end())
|
||||
{
|
||||
if (m_logRolloverSize> 0)
|
||||
checkLogRoll(info, ::strlen(message) + kDateLenAllowance);
|
||||
|
||||
if(info->max > 0)
|
||||
{
|
||||
info->used += fprintf(info->file, "[%2.2d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d] %s", (info->ts.tm_mon + 1), info->ts.tm_mday, (info->ts.tm_year % 100), info->ts.tm_hour, info->ts.tm_min, info->ts.tm_sec, message);
|
||||
info->used += fputc('\n', info->file);
|
||||
if(info->used > info->max)
|
||||
{
|
||||
fflush(info->file);
|
||||
info->used = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(info->file, "[%2.2d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d] %s", (info->ts.tm_mon + 1), info->ts.tm_mday, (info->ts.tm_year % 100), info->ts.tm_hour, info->ts.tm_min, info->ts.tm_sec, message);
|
||||
fputc('\n', info->file);
|
||||
fflush(info->file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::log(unsigned logenum, int level, const char *message, ...)
|
||||
{
|
||||
char buf[_BASE_LOGGER_MAX_BUF_SIZE_];
|
||||
va_list varg;
|
||||
va_start(varg, message);
|
||||
vsnprintf(buf, _BASE_LOGGER_MAX_BUF_SIZE_, message, varg);
|
||||
buf[_BASE_LOGGER_MAX_BUF_SIZE_ - 1] = 0;
|
||||
va_end(varg);
|
||||
|
||||
// ensure that the buf does not contain any '%' characters.
|
||||
// prevent crash problem
|
||||
char *rv;
|
||||
while((rv = strchr(buf, '%')) != NULL)
|
||||
{
|
||||
*rv = ' '; // replace with space
|
||||
}
|
||||
|
||||
|
||||
logWithSys(logenum, LOG_NOTICE, level, buf);
|
||||
}
|
||||
|
||||
|
||||
void Logger::logWithSys(unsigned logenum, unsigned priority, int level, const char *message, ...)
|
||||
{
|
||||
ELogType logType = getLoggingType(priority);
|
||||
char buf[_BASE_LOGGER_MAX_BUF_SIZE_];
|
||||
va_list varg;
|
||||
va_start(varg, message);
|
||||
vsnprintf(buf, _BASE_LOGGER_MAX_BUF_SIZE_, message, varg);
|
||||
buf[_BASE_LOGGER_MAX_BUF_SIZE_ - 1] = 0;
|
||||
va_end(varg);
|
||||
|
||||
#ifndef WIN32
|
||||
if (0 != (logType & eUseSysLog))
|
||||
syslog(priority, "%s: %s", syslogLevels[priority % numSysLogLevels], buf);
|
||||
#endif // !WIN32
|
||||
|
||||
if (0 != (logType & eUseLocalFile))
|
||||
{
|
||||
map<unsigned, LogInfo *>::iterator iter = m_logTable.find(logenum);
|
||||
if(iter == m_logTable.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
time_t t = time(NULL);
|
||||
LogInfo *info = (*iter).second;
|
||||
|
||||
if(level >= info->level)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(level == LOG_FILEONLY && ((info->file == stderr) || (info->file == stdout)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(info->last != t)
|
||||
{
|
||||
LOGGER_GET_CURR_TIME(info->ts, t);
|
||||
|
||||
info->last = t;
|
||||
}
|
||||
if(m_rollDate && info->ts.tm_mday != m_lastDateTime.tm_mday)
|
||||
{
|
||||
LOGGER_LOCK
|
||||
|
||||
if(info->ts.tm_mday != m_lastDateTime.tm_mday)
|
||||
{
|
||||
memcpy(&m_lastDateTime, &info->ts, sizeof(tm));
|
||||
rollDate(t);
|
||||
}
|
||||
|
||||
LOGGER_UNLOCK
|
||||
}
|
||||
|
||||
if(iter != m_logTable.end())
|
||||
{
|
||||
if (m_logRolloverSize> 0)
|
||||
checkLogRoll(info, ::strlen(buf) + kDateLenAllowance);
|
||||
|
||||
if(info->max > 0)
|
||||
{
|
||||
info->used += fprintf(info->file, "[%2.2d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d] %s", (info->ts.tm_mon + 1), info->ts.tm_mday, (info->ts.tm_year % 100), info->ts.tm_hour, info->ts.tm_min, info->ts.tm_sec, buf);
|
||||
info->used += fputc('\n', info->file);
|
||||
if(info->used > info->max)
|
||||
{
|
||||
fflush(info->file);
|
||||
info->used = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(info->file, "[%2.2d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d] %s", (info->ts.tm_mon + 1), info->ts.tm_mday, (info->ts.tm_year % 100), info->ts.tm_hour, info->ts.tm_min, info->ts.tm_sec, buf);
|
||||
fputc('\n', info->file);
|
||||
fflush(info->file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::rollDate(time_t t)
|
||||
{
|
||||
char buf[80];
|
||||
FILE *logDir = NULL;
|
||||
|
||||
logDir = fopen(m_dirPrefix.c_str(), "r+");
|
||||
if(errno == ENOENT)
|
||||
{
|
||||
cmkdir(m_dirPrefix.c_str(), 0755);
|
||||
}
|
||||
else if(logDir != NULL)
|
||||
{
|
||||
fclose(logDir);
|
||||
}
|
||||
|
||||
tm now;
|
||||
|
||||
LOGGER_GET_CURR_TIME(now, t);
|
||||
|
||||
sprintf(buf, "%s%c%2.2d-%2.2d-%2.2d", m_dirPrefix.c_str(), file_sep, (now.tm_mon + 1), now.tm_mday, (now.tm_year % 100));
|
||||
logDir = fopen(buf, "r+");
|
||||
|
||||
if(errno == ENOENT)
|
||||
{
|
||||
cmkdir(buf, 0755);
|
||||
}
|
||||
else if(logDir != NULL)
|
||||
{
|
||||
fclose(logDir);
|
||||
}
|
||||
m_logPrefix = buf;
|
||||
|
||||
map<unsigned, LogInfo *>::iterator iter;
|
||||
for(iter = m_logTable.begin(); iter != m_logTable.end(); iter++)
|
||||
{
|
||||
(*iter).second->filename = m_logPrefix + file_sep + (*iter).second->name.c_str() + ".log";
|
||||
fflush((*iter).second->file);
|
||||
fclose((*iter).second->file);
|
||||
(*iter).second->file = fopen((*iter).second->filename.c_str(), "a+");
|
||||
if ((*iter).second->file == 0)
|
||||
abort();
|
||||
memcpy(&((*iter).second->ts), &now, sizeof(tm));
|
||||
}
|
||||
}
|
||||
|
||||
// mkdir function that creates intermediate directories
|
||||
void Logger::cmkdir(const char *dir, int mode)
|
||||
{
|
||||
char dirbuf[128];
|
||||
strncpy(dirbuf, dir, 127);
|
||||
dirbuf[127] = 0;
|
||||
char *j = dirbuf, *i = dirbuf;
|
||||
int handle;
|
||||
|
||||
while(*i)
|
||||
{
|
||||
if(*i == file_sep)
|
||||
{
|
||||
(*i) = 0;
|
||||
#ifdef WIN32
|
||||
handle = open(j, O_EXCL);
|
||||
|
||||
if((handle > 0) || (errno != EISDIR && errno != ENOENT && errno != EACCES))
|
||||
{
|
||||
perror("Logger::cmkdir():");
|
||||
abort();
|
||||
}
|
||||
#else // ifndef WIN32
|
||||
// handle = open(j, O_EXCL); // Ben's original code
|
||||
// if((handle > 0) || (errno != EISDIR && errno != ENOENT))
|
||||
// {
|
||||
// perror("Logger::cmkdir():");
|
||||
// abort();
|
||||
// }
|
||||
|
||||
// This doesnt work under Linux, it returns a valid handle
|
||||
// Instead: see if file exists. If it doesnt, create directory ok
|
||||
// If it exists, do stat to see if it is a dir.
|
||||
// If it is a dir, then ok, create the directory.
|
||||
// If it is a file, error
|
||||
// ging 9-16-2002
|
||||
|
||||
handle = open(j, O_RDONLY);
|
||||
if (handle > 0)
|
||||
{
|
||||
struct stat stat_buffer;
|
||||
int ret = fstat(handle,&stat_buffer);
|
||||
if ((ret == -1) || ((stat_buffer.st_mode | S_IFDIR) == 0))
|
||||
{
|
||||
perror("Logger::cmkdir():");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
#endif // WIN32
|
||||
LOGGER_MAKE_DIR(j, mode);
|
||||
close(handle);
|
||||
(*i) = file_sep;
|
||||
}
|
||||
else if(*(i + 1) == 0)
|
||||
{
|
||||
LOGGER_MAKE_DIR(j, mode);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::rollLog(LogInfo *logInfo)
|
||||
{
|
||||
std::string newLogName;
|
||||
char timeStampBuffer[256];
|
||||
time_t t = time(NULL);
|
||||
tm now;
|
||||
int r;
|
||||
int nTries = 10;
|
||||
|
||||
fflush(logInfo->file);
|
||||
fclose(logInfo->file);
|
||||
|
||||
do
|
||||
{
|
||||
LOGGER_GET_CURR_TIME(now, t);
|
||||
sprintf(timeStampBuffer, "-%4.4d-%2.2d-%2.2d-%2.2d-%2.2d-%2.2d.log",
|
||||
(now.tm_year + 1900), (now.tm_mon + 1), now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec);
|
||||
newLogName = logInfo->filename.substr(0, logInfo->filename.length() - 4) + timeStampBuffer;
|
||||
r = rename(logInfo->filename.c_str(), newLogName.c_str());
|
||||
nTries--;
|
||||
t++;
|
||||
}
|
||||
while ((r != 0) && (nTries > 0));
|
||||
|
||||
logInfo->file = fopen(logInfo->filename.c_str(), "a+");
|
||||
memcpy(&(logInfo->ts), &now, sizeof(tm));
|
||||
}
|
||||
|
||||
void Logger::checkLogRoll(LogInfo *logInfo, unsigned long lenToAdd)
|
||||
{
|
||||
unsigned long logSize = ftell(logInfo->file);
|
||||
|
||||
if ((logSize + lenToAdd) > m_logRolloverSize)
|
||||
{
|
||||
LOGGER_LOCK
|
||||
|
||||
rollLog(logInfo);
|
||||
|
||||
LOGGER_UNLOCK
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
#ifndef __LOGGER_H__
|
||||
#define __LOGGER_H__
|
||||
|
||||
// make the compiler shut up
|
||||
#pragma warning (disable : 4786)
|
||||
|
||||
#include "Platform.h"
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include "Mutex.h"
|
||||
#ifndef WIN32
|
||||
#include <syslog.h>
|
||||
#endif
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
#define LOG_ALWAYS -1
|
||||
#define LOG_FILEONLY -2
|
||||
|
||||
struct LogInfo
|
||||
{
|
||||
FILE *file;
|
||||
unsigned used;
|
||||
tm ts;
|
||||
time_t last;
|
||||
unsigned max;
|
||||
int level;
|
||||
std::string filename;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
/* Use these flags defined in syslog.h for the priority level
|
||||
LOG_EMERG,
|
||||
LOG_ALERT,
|
||||
LOG_CRIT,
|
||||
LOG_ERR,
|
||||
LOG_WARNING,
|
||||
LOG_NOTICE,
|
||||
LOG_INFO,
|
||||
LOG_DEBUG
|
||||
*/
|
||||
|
||||
//-----------------------------------------------
|
||||
// Windows doesn't have syslog (?) so we define the
|
||||
// syslog warning levels here to satisfy the compiler
|
||||
// but then ignore them in log() and logSimple()
|
||||
//-----------------------------------------------
|
||||
|
||||
// PLEASE USE THE FOLLOWING ENUMERATIONS FOR LOGGING INSTEAD OF THE SYSLOG ONES.
|
||||
enum LogLevel
|
||||
{
|
||||
#ifndef WIN32
|
||||
BASE_LOG_EMERG = LOG_EMERG,
|
||||
BASE_LOG_ALERT = LOG_ALERT,
|
||||
BASE_LOG_CRIT = LOG_CRIT,
|
||||
BASE_LOG_ERR = LOG_ERR,
|
||||
BASE_LOG_WARNING = LOG_WARNING,
|
||||
BASE_LOG_NOTICE = LOG_NOTICE,
|
||||
BASE_LOG_INFO = LOG_INFO,
|
||||
BASE_LOG_DEBUG = LOG_DEBUG
|
||||
#else
|
||||
BASE_LOG_EMERG = 0,
|
||||
BASE_LOG_ALERT,
|
||||
BASE_LOG_CRIT,
|
||||
BASE_LOG_ERR,
|
||||
BASE_LOG_WARNING,
|
||||
BASE_LOG_NOTICE,
|
||||
BASE_LOG_INFO,
|
||||
BASE_LOG_DEBUG
|
||||
#endif
|
||||
};
|
||||
|
||||
// The _WIN32_LOG_LEVELS_ remain for backwards compatability.
|
||||
#ifdef WIN32
|
||||
enum _WIN32_LOG_LEVELS_
|
||||
{
|
||||
LOG_EMERG = 0,
|
||||
LOG_ALERT,
|
||||
LOG_CRIT,
|
||||
LOG_ERR,
|
||||
LOG_WARNING,
|
||||
LOG_NOTICE,
|
||||
LOG_INFO,
|
||||
LOG_DEBUG
|
||||
};
|
||||
#endif
|
||||
|
||||
enum ELogType
|
||||
{
|
||||
eUseNone = 0,
|
||||
eUseLocalFile = 1,
|
||||
eUseSysLog = 2,
|
||||
eUseBoth = 3
|
||||
};
|
||||
|
||||
class Logger
|
||||
{
|
||||
public:
|
||||
Logger(const char *programName,
|
||||
const ELogType logType = eUseLocalFile,
|
||||
const char *prefix = "logs",
|
||||
int level = 10,
|
||||
unsigned size = 0,
|
||||
bool rollDate = true);
|
||||
|
||||
Logger(const char *prefix,
|
||||
int level,
|
||||
unsigned size,
|
||||
bool rollDate = true); // Backwards compatibility
|
||||
void LoggerInit(const char *programName);
|
||||
|
||||
~Logger();
|
||||
void addLog(const char *id, unsigned logenum, int level, unsigned size);
|
||||
void addLog(const char *id, unsigned logenum);
|
||||
void removeLog(unsigned logenum);
|
||||
void updateLog(unsigned logenum, int level, unsigned size);
|
||||
void logSimple(unsigned logenum, int level, const char *message);
|
||||
void logSimpleWithSys(unsigned logenum, unsigned priority, int level, const char *message);
|
||||
void logWithSys(unsigned logenum, unsigned priority, int level, const char *message, ...);
|
||||
void log(unsigned logenum, int level, const char *message, ...);
|
||||
|
||||
void flushAll();
|
||||
void flush(unsigned logenum);
|
||||
unsigned long getLogRolloverSize() { return m_logRolloverSize; }
|
||||
void setLogRolloverSize(unsigned long maxLogSize) { m_logRolloverSize = maxLogSize; }
|
||||
ELogType getLoggingType(unsigned logLevel) const;
|
||||
void setLoggingType(unsigned logLevel, ELogType logType);
|
||||
|
||||
private:
|
||||
void rollDate(time_t now);
|
||||
void cmkdir(const char *dir, int mode);
|
||||
void rollLog(LogInfo *logInfo);
|
||||
void checkLogRoll(LogInfo *logInfo, unsigned long lenToAdd);
|
||||
|
||||
unsigned m_defaultLevel;
|
||||
unsigned m_defaultSize;
|
||||
std::string m_lastDateText;
|
||||
tm m_lastDateTime;
|
||||
std::map<unsigned, LogInfo *> m_logTable;
|
||||
std::string m_logPrefix;
|
||||
std::string m_dirPrefix;
|
||||
#if defined (_REENTRANT) || defined (_MT)
|
||||
Base::CMutex rLock;
|
||||
#endif
|
||||
bool m_rollDate;
|
||||
unsigned long m_logRolloverSize;
|
||||
std::map<unsigned, ELogType> m_logLevelToTypeMap;
|
||||
ELogType m_combinedLogType;
|
||||
bool m_opennedSyslog;
|
||||
};
|
||||
|
||||
extern const char file_sep;
|
||||
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,325 @@
|
||||
// MD5.cpp: implementation of the MD5 class.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "MD5.h"
|
||||
#include "Types.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
|
||||
MD5::State::State() :
|
||||
state(4,0),
|
||||
count(2,0),
|
||||
buffer(64,0)
|
||||
{
|
||||
state[0] = 0x67452301;
|
||||
state[1] = 0xefcdab89;
|
||||
state[2] = 0x98badcfe;
|
||||
state[3] = 0x10325476;
|
||||
}
|
||||
|
||||
static char const init[64] =
|
||||
{
|
||||
-128, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
vector<char> MD5::padding(init,init+sizeof(init)/sizeof(init[0]));
|
||||
|
||||
MD5::MD5()
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
MD5::MD5(const string & input)
|
||||
{
|
||||
Init();
|
||||
Update(input);
|
||||
}
|
||||
|
||||
vector<int> MD5::Decode(vector<char> achar0, int i, int j)
|
||||
{
|
||||
vector<int> ai(16,0);
|
||||
int l;
|
||||
int k = l = 0;
|
||||
for(; l < i; l += 4)
|
||||
{
|
||||
ai[k] = achar0[l + j] & 0xff | (achar0[l + 1 + j] & 0xff) << 8 | (achar0[l + 2 + j] & 0xff) << 16 | (achar0[l + 3 + j] & 0xff) << 24;
|
||||
k++;
|
||||
}
|
||||
|
||||
return ai;
|
||||
}
|
||||
|
||||
vector<char> MD5::Encode(vector<int> ai, int i)
|
||||
{
|
||||
vector<char> achar0(i,0);
|
||||
int k;
|
||||
int j = k = 0;
|
||||
for(; k < i; k += 4){
|
||||
achar0[k] = (char)(ai[j] & 0xff);
|
||||
achar0[k + 1] = (char)(ai[j] >> 8 & 0xff);
|
||||
achar0[k + 2] = (char)(ai[j] >> 16 & 0xff);
|
||||
achar0[k + 3] = (char)(ai[j] >> 24 & 0xff);
|
||||
j++;
|
||||
}
|
||||
|
||||
return achar0;
|
||||
}
|
||||
|
||||
int MD5::FF(int i, int j, int k, int l, int i1, int j1, int k1)
|
||||
{
|
||||
i = uadd(i, j & k | ~j & l, i1, k1);
|
||||
return uadd(rotate_left(i, j1), j);
|
||||
}
|
||||
|
||||
vector<char> MD5::Final()
|
||||
{
|
||||
if (finalsNull)
|
||||
{
|
||||
State &state1 = state;
|
||||
vector<char> achar0 = Encode(state1.count, 8);
|
||||
int i = state1.count[0] >> 3 & 0x3f;
|
||||
int j = i >= 56 ? 120 - i : 56 - i;
|
||||
Update(state1, padding, 0, j);
|
||||
Update(state1, achar0, 0, 8);
|
||||
finals = state1;
|
||||
finalsNull = false;
|
||||
}
|
||||
return Encode(finals.state, 16);
|
||||
}
|
||||
|
||||
int MD5::GG(int i, int j, int k, int l, int i1, int j1, int k1)
|
||||
{
|
||||
i = uadd(i, j & l | k & ~l, i1, k1);
|
||||
return uadd(rotate_left(i, j1), j);
|
||||
}
|
||||
|
||||
int MD5::HH(int i, int j, int k, int l, int i1, int j1, int k1)
|
||||
{
|
||||
i = uadd(i, j ^ k ^ l, i1, k1);
|
||||
return uadd(rotate_left(i, j1), j);
|
||||
}
|
||||
|
||||
int MD5::II(int i, int j, int k, int l, int i1, int j1, int k1)
|
||||
{
|
||||
i = uadd(i, k ^ (j | ~l), i1, k1);
|
||||
return uadd(rotate_left(i, j1), j);
|
||||
}
|
||||
|
||||
void MD5::Init()
|
||||
{
|
||||
if (padding.size() == 0)
|
||||
{
|
||||
padding.resize(64,0);
|
||||
padding[0] = -128;
|
||||
}
|
||||
finalsNull = true;
|
||||
}
|
||||
|
||||
void MD5::Transform(State & state1, vector<char> achar0, int i)
|
||||
{
|
||||
int j = state1.state[0];
|
||||
int k = state1.state[1];
|
||||
int l = state1.state[2];
|
||||
int i1 = state1.state[3];
|
||||
vector<int> ai = Decode(achar0, 64, i);
|
||||
j = FF(j, k, l, i1, ai[0], 7, 0xd76aa478);
|
||||
i1 = FF(i1, j, k, l, ai[1], 12, 0xe8c7b756);
|
||||
l = FF(l, i1, j, k, ai[2], 17, 0x242070db);
|
||||
k = FF(k, l, i1, j, ai[3], 22, 0xc1bdceee);
|
||||
j = FF(j, k, l, i1, ai[4], 7, 0xf57c0faf);
|
||||
i1 = FF(i1, j, k, l, ai[5], 12, 0x4787c62a);
|
||||
l = FF(l, i1, j, k, ai[6], 17, 0xa8304613);
|
||||
k = FF(k, l, i1, j, ai[7], 22, 0xfd469501);
|
||||
j = FF(j, k, l, i1, ai[8], 7, 0x698098d8);
|
||||
i1 = FF(i1, j, k, l, ai[9], 12, 0x8b44f7af);
|
||||
l = FF(l, i1, j, k, ai[10], 17, -42063);
|
||||
k = FF(k, l, i1, j, ai[11], 22, 0x895cd7be);
|
||||
j = FF(j, k, l, i1, ai[12], 7, 0x6b901122);
|
||||
i1 = FF(i1, j, k, l, ai[13], 12, 0xfd987193);
|
||||
l = FF(l, i1, j, k, ai[14], 17, 0xa679438e);
|
||||
k = FF(k, l, i1, j, ai[15], 22, 0x49b40821);
|
||||
j = GG(j, k, l, i1, ai[1], 5, 0xf61e2562);
|
||||
i1 = GG(i1, j, k, l, ai[6], 9, 0xc040b340);
|
||||
l = GG(l, i1, j, k, ai[11], 14, 0x265e5a51);
|
||||
k = GG(k, l, i1, j, ai[0], 20, 0xe9b6c7aa);
|
||||
j = GG(j, k, l, i1, ai[5], 5, 0xd62f105d);
|
||||
i1 = GG(i1, j, k, l, ai[10], 9, 0x2441453);
|
||||
l = GG(l, i1, j, k, ai[15], 14, 0xd8a1e681);
|
||||
k = GG(k, l, i1, j, ai[4], 20, 0xe7d3fbc8);
|
||||
j = GG(j, k, l, i1, ai[9], 5, 0x21e1cde6);
|
||||
i1 = GG(i1, j, k, l, ai[14], 9, 0xc33707d6);
|
||||
l = GG(l, i1, j, k, ai[3], 14, 0xf4d50d87);
|
||||
k = GG(k, l, i1, j, ai[8], 20, 0x455a14ed);
|
||||
j = GG(j, k, l, i1, ai[13], 5, 0xa9e3e905);
|
||||
i1 = GG(i1, j, k, l, ai[2], 9, 0xfcefa3f8);
|
||||
l = GG(l, i1, j, k, ai[7], 14, 0x676f02d9);
|
||||
k = GG(k, l, i1, j, ai[12], 20, 0x8d2a4c8a);
|
||||
j = HH(j, k, l, i1, ai[5], 4, 0xfffa3942);
|
||||
i1 = HH(i1, j, k, l, ai[8], 11, 0x8771f681);
|
||||
l = HH(l, i1, j, k, ai[11], 16, 0x6d9d6122);
|
||||
k = HH(k, l, i1, j, ai[14], 23, 0xfde5380c);
|
||||
j = HH(j, k, l, i1, ai[1], 4, 0xa4beea44);
|
||||
i1 = HH(i1, j, k, l, ai[4], 11, 0x4bdecfa9);
|
||||
l = HH(l, i1, j, k, ai[7], 16, 0xf6bb4b60);
|
||||
k = HH(k, l, i1, j, ai[10], 23, 0xbebfbc70);
|
||||
j = HH(j, k, l, i1, ai[13], 4, 0x289b7ec6);
|
||||
i1 = HH(i1, j, k, l, ai[0], 11, 0xeaa127fa);
|
||||
l = HH(l, i1, j, k, ai[3], 16, 0xd4ef3085);
|
||||
k = HH(k, l, i1, j, ai[6], 23, 0x4881d05);
|
||||
j = HH(j, k, l, i1, ai[9], 4, 0xd9d4d039);
|
||||
i1 = HH(i1, j, k, l, ai[12], 11, 0xe6db99e5);
|
||||
l = HH(l, i1, j, k, ai[15], 16, 0x1fa27cf8);
|
||||
k = HH(k, l, i1, j, ai[2], 23, 0xc4ac5665);
|
||||
j = II(j, k, l, i1, ai[0], 6, 0xf4292244);
|
||||
i1 = II(i1, j, k, l, ai[7], 10, 0x432aff97);
|
||||
l = II(l, i1, j, k, ai[14], 15, 0xab9423a7);
|
||||
k = II(k, l, i1, j, ai[5], 21, 0xfc93a039);
|
||||
j = II(j, k, l, i1, ai[12], 6, 0x655b59c3);
|
||||
i1 = II(i1, j, k, l, ai[3], 10, 0x8f0ccc92);
|
||||
l = II(l, i1, j, k, ai[10], 15, 0xffeff47d);
|
||||
k = II(k, l, i1, j, ai[1], 21, 0x85845dd1);
|
||||
j = II(j, k, l, i1, ai[8], 6, 0x6fa87e4f);
|
||||
i1 = II(i1, j, k, l, ai[15], 10, 0xfe2ce6e0);
|
||||
l = II(l, i1, j, k, ai[6], 15, 0xa3014314);
|
||||
k = II(k, l, i1, j, ai[13], 21, 0x4e0811a1);
|
||||
j = II(j, k, l, i1, ai[4], 6, 0xf7537e82);
|
||||
i1 = II(i1, j, k, l, ai[11], 10, 0xbd3af235);
|
||||
l = II(l, i1, j, k, ai[2], 15, 0x2ad7d2bb);
|
||||
k = II(k, l, i1, j, ai[9], 21, 0xeb86d391);
|
||||
state1.state[0] += j;
|
||||
state1.state[1] += k;
|
||||
state1.state[2] += l;
|
||||
state1.state[3] += i1;
|
||||
}
|
||||
|
||||
void MD5::Update(char char0)
|
||||
{
|
||||
vector<char> achar0(1,0);
|
||||
achar0[0] = char0;
|
||||
Update(achar0, 1);
|
||||
}
|
||||
|
||||
void MD5::Update(State & state1, vector<char> achar0, int i, int j)
|
||||
{
|
||||
finalsNull = true;
|
||||
if (j - i > (int)achar0.size())
|
||||
j = achar0.size() - i;
|
||||
int k = state1.count[0] >> 3 & 0x3f;
|
||||
if ((state1.count[0] += j << 3) < j << 3)
|
||||
state1.count[1]++;
|
||||
state1.count[1] += j >> 29;
|
||||
int l = 64 - k;
|
||||
int j1;
|
||||
if(j >= l)
|
||||
{
|
||||
for(int i1 = 0; i1 < l; i1++)
|
||||
state1.buffer[i1 + k] = achar0[i1 + i];
|
||||
|
||||
Transform(state1, state1.buffer, 0);
|
||||
for(j1 = l; j1 + 63 < j; j1 += 64)
|
||||
Transform(state1, achar0, j1);
|
||||
|
||||
k = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
j1 = 0;
|
||||
}
|
||||
if (j1 < j)
|
||||
{
|
||||
int k1 = j1;
|
||||
for(; j1 < j; j1++)
|
||||
state1.buffer[(k + j1) - k1] = achar0[j1 + i];
|
||||
}
|
||||
}
|
||||
|
||||
void MD5::Update(string s)
|
||||
{
|
||||
vector<char> achar(s.size(),0);
|
||||
for (int i=0; i<(int)s.size(); i++)
|
||||
achar[i] = s[i];
|
||||
Update( achar, achar.size() );
|
||||
}
|
||||
|
||||
void MD5::Update(vector<char> achar0)
|
||||
{
|
||||
Update(achar0, 0, achar0.size());
|
||||
}
|
||||
|
||||
void MD5::Update(vector<char> achar0, int i)
|
||||
{
|
||||
Update(state, achar0, 0, i);
|
||||
}
|
||||
|
||||
void MD5::Update(vector<char> achar0, int i, int j)
|
||||
{
|
||||
Update(state, achar0, i, j);
|
||||
}
|
||||
|
||||
string MD5::asHex()
|
||||
{
|
||||
return asHex(Final());
|
||||
}
|
||||
|
||||
string MD5::asHex(vector<char> achar0)
|
||||
{
|
||||
const string hex = "0123456789abcdef";
|
||||
|
||||
string stringbuffer;
|
||||
for (int i = 0; i < (int)achar0.size(); i++)
|
||||
{
|
||||
stringbuffer += hex.substr((achar0[i] >> 4) & 0x0f,1);
|
||||
stringbuffer += hex.substr(achar0[i] & 0x0f,1);
|
||||
}
|
||||
|
||||
return stringbuffer;
|
||||
}
|
||||
|
||||
int MD5::rotate_left(int i, int j)
|
||||
{
|
||||
unsigned i1 = i;
|
||||
unsigned j1 = j;
|
||||
return i1 << j1 | i1 >> (32 - j1);
|
||||
}
|
||||
|
||||
int MD5::uadd(int i, int j)
|
||||
{
|
||||
int64 l = (int64)i & 0x0ffffffffL;
|
||||
int64 l1 = (int64)j & 0x0ffffffffL;
|
||||
l += l1;
|
||||
return (int)(l & 0x0ffffffffL);
|
||||
}
|
||||
|
||||
int MD5::uadd(int i, int j, int k)
|
||||
{
|
||||
return uadd(uadd(i, j), k);
|
||||
}
|
||||
|
||||
int MD5::uadd(int i, int j, int k, int l)
|
||||
{
|
||||
return uadd(uadd(i, j, k), l);
|
||||
}
|
||||
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
// MD5.h: interface for the MD5 class.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef MD5_H
|
||||
#define MD5_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
|
||||
class MD5
|
||||
{
|
||||
struct State
|
||||
{
|
||||
State();
|
||||
|
||||
std::vector<int> state;
|
||||
std::vector<int> count;
|
||||
std::vector<char> buffer;
|
||||
};
|
||||
|
||||
public:
|
||||
MD5::MD5();
|
||||
MD5(const std::string &input);
|
||||
|
||||
void Init();
|
||||
std::vector<char> Final();
|
||||
void Update(char char0);
|
||||
void Update(State & state1, std::vector<char> achar0, int i, int j);
|
||||
void Update(std::string s);
|
||||
void Update(std::vector<char> achar0);
|
||||
void Update(std::vector<char> achar0, int i);
|
||||
void Update(std::vector<char> achar0, int i, int j);
|
||||
std::string asHex();
|
||||
static std::string asHex(std::vector<char> achar0);
|
||||
|
||||
private:
|
||||
std::vector<int> Decode(std::vector<char> achar0, int i, int j);
|
||||
std::vector<char> Encode(std::vector<int> ai, int i);
|
||||
int FF(int i, int j, int k, int l, int i1, int j1, int k1);
|
||||
int GG(int i, int j, int k, int l, int i1, int j1, int k1);
|
||||
int HH(int i, int j, int k, int l, int i1, int j1, int k1);
|
||||
int II(int i, int j, int k, int l, int i1, int j1, int k1);
|
||||
void Transform(State & state1, std::vector<char> achar0, int i);
|
||||
int rotate_left(int i, int j);
|
||||
int uadd(int i, int j);
|
||||
int uadd(int i, int j, int k);
|
||||
int uadd(int i, int j, int k, int l);
|
||||
|
||||
private:
|
||||
State state;
|
||||
State finals;
|
||||
bool finalsNull;
|
||||
static std::vector<char> padding;
|
||||
|
||||
};
|
||||
|
||||
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
#endif // MD5_H
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef BASE_MUTEX_H
|
||||
#define BASE_MUTEX_H
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
#include "win32/Mutex.h"
|
||||
|
||||
#elif linux
|
||||
|
||||
#include "linux/Mutex.h"
|
||||
|
||||
#elif sparc
|
||||
|
||||
#include "solaris/Mutex.h"
|
||||
|
||||
#else
|
||||
|
||||
#error /Base/Mutex.h: Undefine platform type
|
||||
|
||||
#endif
|
||||
|
||||
#endif // BASE_MUTEX_H
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
#ifndef BASE_PLATFORM_H
|
||||
#define BASE_PLATFORM_H
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32/Platform.h"
|
||||
#elif linux
|
||||
#include "linux/Platform.h"
|
||||
#elif sparc
|
||||
#include "solaris/Platform.h"
|
||||
#else
|
||||
#error /Base/Platform.h: Undefine platform type
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
|
||||
template <class T> inline T rotlFixed(T x, unsigned int y)
|
||||
{
|
||||
assert(y < sizeof(T)*8);
|
||||
return (T)((x<<y) | (x>>(sizeof(T)*8-y)));
|
||||
}
|
||||
|
||||
template <class T> inline T rotrFixed(T x, unsigned int y)
|
||||
{
|
||||
assert(y < sizeof(T)*8);
|
||||
return (x>>y) | (x<<(sizeof(T)*8-y));
|
||||
}
|
||||
|
||||
template <class T> inline T rotlMod(T x, unsigned int y)
|
||||
{
|
||||
y %= sizeof(T)*8;
|
||||
return (x<<y) | (x>>(sizeof(T)*8-y));
|
||||
}
|
||||
|
||||
template <class T> inline T rotrMod(T x, unsigned int y)
|
||||
{
|
||||
y %= sizeof(T)*8;
|
||||
return (x>>y) | (x<<(sizeof(T)*8-y));
|
||||
}
|
||||
|
||||
inline uint16 byteReverse16(void * data)
|
||||
{
|
||||
uint16 value = *static_cast<uint16 *>(data);
|
||||
return *static_cast<uint16 *>(data) = rotlFixed(value, 8U);
|
||||
// return rotlFixed(value, 8U);
|
||||
}
|
||||
|
||||
inline uint32 byteReverse32(void * data)
|
||||
{
|
||||
uint32 value = *static_cast<uint32 *>(data);
|
||||
return *static_cast<uint32 *>(data) = (rotrFixed(value, 8U) & 0xff00ff00) | (rotlFixed(value, 8U) & 0x00ff00ff);
|
||||
// return (rotrFixed(value, 8U) & 0xff00ff00) | (rotlFixed(value, 8U) & 0x00ff00ff);
|
||||
}
|
||||
inline uint64 byteReverse64(void * data)
|
||||
{
|
||||
uint64 value = *static_cast<uint64 *>(data);
|
||||
return *static_cast<uint64 *>(data) = (
|
||||
uint64((rotrFixed(uint32(value), 8U) & 0xff00ff00) | (rotlFixed(uint32(value), 8U) & 0x00ff00ff)) << 32) |
|
||||
(rotrFixed(uint32(value>>32), 8U) & 0xff00ff00) | (rotlFixed(uint32(value>>32), 8U) & 0x00ff00ff);
|
||||
// return (uint64(byteReverse(uint32(value))) << 32) | byteReverse(uint32(value>>32));
|
||||
}
|
||||
|
||||
inline uint32 strlen(const unsigned short * string)
|
||||
{
|
||||
if (string == 0)
|
||||
return 0;
|
||||
|
||||
uint32 length=0;
|
||||
while (*(string+length++) != 0);
|
||||
|
||||
return length-1;
|
||||
}
|
||||
|
||||
inline double getTimerLatency(Base::uint64 startTime, Base::uint64 finishTime=0)
|
||||
{
|
||||
Base::int64 requestAge;
|
||||
Base::int64 freq = Base::getTimerFrequency();
|
||||
Base::uint64 finish = (finishTime ? finishTime : Base::getTimer());
|
||||
if (finish < startTime)
|
||||
requestAge = (0 - 1) - startTime - finish;
|
||||
else
|
||||
requestAge = finish - startTime;
|
||||
return (double)requestAge/freq;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
#endif // BASE_PLATFORM_H
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#include "ScopeLock.h"
|
||||
|
||||
#ifdef INCLUDE_SCOPELOCK
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
CScopeLock::CScopeLock(CMutex& mutex) :
|
||||
mMutex(&mutex)
|
||||
{
|
||||
mMutex->Lock();
|
||||
}
|
||||
|
||||
CScopeLock::CScopeLock(CScopeLock& lock) :
|
||||
mMutex(lock.mMutex)
|
||||
{
|
||||
mMutex->Lock();
|
||||
}
|
||||
|
||||
CScopeLock::~CScopeLock()
|
||||
{
|
||||
mMutex->Unlock();
|
||||
}
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // INCLUDE_SCOPELOCK
|
||||
@@ -0,0 +1,40 @@
|
||||
// ScopeLock.h: interface for the CScopeLock class.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef SCOPELOCK_H
|
||||
#define SCOPELOCK_H
|
||||
|
||||
#if defined _MT || defined _REENTRANT
|
||||
# define INCLUDE_SCOPELOCK
|
||||
#endif
|
||||
|
||||
#ifdef INCLUDE_SCOPELOCK
|
||||
|
||||
#include "Mutex.h"
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
class CScopeLock
|
||||
{
|
||||
public:
|
||||
CScopeLock(CMutex& mutex);
|
||||
CScopeLock(CScopeLock& lock);
|
||||
virtual ~CScopeLock();
|
||||
|
||||
private:
|
||||
CMutex *mMutex;
|
||||
};
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // INCLUDE_SCOPELOCK
|
||||
|
||||
#endif // SCOPELOCK_H
|
||||
@@ -0,0 +1,45 @@
|
||||
#include "Statistics.h"
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
CStatisticTimer::CStatisticTimer(bool running) :
|
||||
mTotal(0),
|
||||
mStart(getTimer()),
|
||||
mRunning(running)
|
||||
{
|
||||
}
|
||||
|
||||
double CStatisticTimer::GetTime()
|
||||
{
|
||||
uint64 total = mTotal;
|
||||
|
||||
if (mRunning)
|
||||
total += getTimer()-mStart;
|
||||
|
||||
return (double)((int64)(total/getTimerFrequency()));
|
||||
}
|
||||
|
||||
uint64 CStatisticTimer::GetFraction(uint32 fraction)
|
||||
{
|
||||
uint64 total = mTotal;
|
||||
|
||||
if (mRunning)
|
||||
total += getTimer()-mStart;
|
||||
|
||||
if (fraction == 0)
|
||||
return total;
|
||||
else
|
||||
return total/(getTimerFrequency()/fraction);
|
||||
}
|
||||
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
#ifndef STATISTICS_H
|
||||
#define STATISTICS_H
|
||||
|
||||
#include <time.h>
|
||||
#include "Platform.h"
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
template <class TYPE, uint32 COUNT>
|
||||
class CStatistic
|
||||
{
|
||||
public:
|
||||
CStatistic(uint32 freq=0) :
|
||||
mSampleFrequency(freq),
|
||||
mLastSampleTime(0),
|
||||
mSample(0),
|
||||
mSampleTotal(0),
|
||||
mAggregateTotal(0),
|
||||
mAggregateMaximum(0),
|
||||
mAggregateMinimum(0),
|
||||
mAverageTotal(0),
|
||||
mAverageIndex(0),
|
||||
mAverageCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
bool Sample(TYPE value)
|
||||
{
|
||||
bool commit = false;
|
||||
|
||||
if (!mLastSampleTime)
|
||||
mLastSampleTime = time(NULL);
|
||||
|
||||
if (!mSampleFrequency || mLastSampleTime + mSampleFrequency < (unsigned)time(NULL))
|
||||
{
|
||||
mSampleTotal++;
|
||||
mAggregateTotal += value;
|
||||
if (value > mAggregateMaximum)
|
||||
mAggregateMaximum = value;
|
||||
if (value < mAggregateMinimum)
|
||||
mAggregateMinimum = value;
|
||||
|
||||
if (mAverageIndex == COUNT)
|
||||
mAverageIndex = 0;
|
||||
if (mAverageCount > COUNT)
|
||||
mAverageTotal -= mAverageData[mAverageIndex];
|
||||
|
||||
mAverageData[mAverageIndex++] = value;
|
||||
mAverageTotal += value;
|
||||
|
||||
if (mAverageCount < COUNT)
|
||||
mAverageCount++;
|
||||
|
||||
commit = true;
|
||||
mSample = 0;
|
||||
}
|
||||
|
||||
mSample += value;
|
||||
|
||||
return commit;
|
||||
}
|
||||
|
||||
TYPE GetSample(uint32 age = 0)
|
||||
{
|
||||
if (age > mAverageCount)
|
||||
return 0;
|
||||
|
||||
uint32 index = mAverageIndex;
|
||||
if (age > index)
|
||||
index = COUNT - (age - index);
|
||||
else
|
||||
index -= age;
|
||||
|
||||
return mAverageData[index];
|
||||
}
|
||||
|
||||
double GetAverage()
|
||||
{
|
||||
return (double)mAverageTotal/mAverageCount;
|
||||
}
|
||||
|
||||
double GetAggregateAverage()
|
||||
{
|
||||
return (double)mAggregateTotal/mSampleTotal;
|
||||
}
|
||||
|
||||
TYPE GetMaximum()
|
||||
{
|
||||
return mAggregateMaximum;
|
||||
}
|
||||
|
||||
TYPE GetMinimum()
|
||||
{
|
||||
return mAggregateMinimum;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32 mSampleFrequency;
|
||||
time_t mLastSampleTime;
|
||||
TYPE mSample;
|
||||
uint32 mSampleTotal;
|
||||
TYPE mAggregateTotal;
|
||||
TYPE mAggregateMaximum;
|
||||
TYPE mAggregateMinimum;
|
||||
|
||||
TYPE mAverageData[COUNT];
|
||||
TYPE mAverageTotal;
|
||||
uint32 mAverageIndex;
|
||||
uint32 mAverageCount;
|
||||
};
|
||||
|
||||
class CStatisticTimer
|
||||
{
|
||||
public:
|
||||
CStatisticTimer(bool running = true);
|
||||
|
||||
void Start() { if (mRunning) return; mStart = getTimer(); mRunning = true; }
|
||||
void Stop() { if (!mRunning) return; mTotal += getTimer()-mStart; mRunning = false; }
|
||||
void Reset() { mTotal = 0; mStart = getTimer(); }
|
||||
|
||||
double GetTime();
|
||||
uint64 GetFraction(uint32 fraction=1000);
|
||||
|
||||
private:
|
||||
uint64 mTotal;
|
||||
uint64 mStart;
|
||||
bool mRunning;
|
||||
};
|
||||
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // STATISTICS_H
|
||||
+238
@@ -0,0 +1,238 @@
|
||||
#ifndef BLOCK_ALLOCATOR_H
|
||||
#define BLOCK_ALLOCATOR_H
|
||||
|
||||
#if defined _MT || defined _REENTRANT
|
||||
# define USE_ALLOCATOR_MUTEX
|
||||
#endif
|
||||
|
||||
|
||||
#include <new>
|
||||
#include "Mutex.h"
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
template<int BLOCK_SIZE>
|
||||
class CBlockAllocator
|
||||
{
|
||||
|
||||
private:
|
||||
class Node
|
||||
{
|
||||
public:
|
||||
Node * mNext;
|
||||
unsigned mBuffer[(BLOCK_SIZE-1)/sizeof(unsigned)+1];
|
||||
};
|
||||
|
||||
public:
|
||||
CBlockAllocator() :
|
||||
mMemoryBlockCount(0),
|
||||
mNodesAllocated(0),
|
||||
mNodesUsed(0),
|
||||
mUnusedList(0)
|
||||
{
|
||||
for (int block=0; block<MAX_BLOCK_COUNT; block++)
|
||||
mMemoryBlock[block] = 0;
|
||||
Allocate();
|
||||
}
|
||||
|
||||
~CBlockAllocator()
|
||||
{
|
||||
for (int block=0; block<MAX_BLOCK_COUNT; block++)
|
||||
delete[] mMemoryBlock[block];
|
||||
}
|
||||
|
||||
void * Allocate()
|
||||
{
|
||||
#ifdef USE_ALLOCATOR_MUTEX
|
||||
mMutex.Lock();
|
||||
#endif
|
||||
void * data;
|
||||
|
||||
Node * node = mUnusedList;
|
||||
mUnusedList = node->mNext;
|
||||
node->mNext = (Node *)1;
|
||||
|
||||
data = node->mBuffer;
|
||||
|
||||
mNodesUsed++;
|
||||
|
||||
#ifdef USE_ALLOCATOR_MUTEX
|
||||
mMutex.Unlock();
|
||||
#endif
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void Deallocate(void * data)
|
||||
{
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
char * memoryPtr = reinterpret_cast<char *>(data) - sizeof(Node *);
|
||||
Node * node = reinterpret_cast<Node *>(memoryPtr);
|
||||
|
||||
#ifdef USE_ALLOCATOR_MUTEX
|
||||
mMutex.Lock();
|
||||
#endif
|
||||
|
||||
node->mNext = mUnusedList;
|
||||
mUnusedList = node;
|
||||
mNodesUsed--;
|
||||
|
||||
#ifdef USE_ALLOCATOR_MUTEX
|
||||
mMutex.Unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
template<class T> T * Construct(const T & object)
|
||||
{
|
||||
T * objectPtr;
|
||||
|
||||
#ifdef USE_ALLOCATOR_MUTEX
|
||||
mMutex.Lock();
|
||||
#endif
|
||||
if (sizeof(T) > BLOCK_SIZE || (!mUnusedList && !Allocate()))
|
||||
{
|
||||
char * memoryPtr = reinterpret_cast<char *>(new unsigned[(sizeof(Node *)+sizeof(T)-1)/sizeof(unsigned)+1]);
|
||||
objectPtr = reinterpret_cast<T *>(memoryPtr+sizeof(Node *));
|
||||
|
||||
*reinterpret_cast<unsigned *>(memoryPtr) = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Node * node = mUnusedList;
|
||||
mUnusedList = node->mNext;
|
||||
node->mNext = (Node *)1;
|
||||
|
||||
objectPtr = reinterpret_cast<T *>(node->mBuffer);
|
||||
|
||||
mNodesUsed++;
|
||||
}
|
||||
|
||||
new (objectPtr) T(object);
|
||||
|
||||
#ifdef USE_ALLOCATOR_MUTEX
|
||||
mMutex.Unlock();
|
||||
#endif
|
||||
return objectPtr;
|
||||
}
|
||||
|
||||
template<class T> void Destroy(T * object)
|
||||
{
|
||||
if (!object)
|
||||
return;
|
||||
|
||||
char * memoryPtr = reinterpret_cast<char *>(object) - sizeof(Node *);
|
||||
Node * node = reinterpret_cast<Node *>(memoryPtr);
|
||||
|
||||
object->~T();
|
||||
|
||||
#ifdef USE_ALLOCATOR_MUTEX
|
||||
mMutex.Lock();
|
||||
#endif
|
||||
if (node->mNext == 0)
|
||||
delete []memoryPtr;
|
||||
else
|
||||
{
|
||||
node->mNext = mUnusedList;
|
||||
mUnusedList = node;
|
||||
mNodesUsed--;
|
||||
}
|
||||
#ifdef USE_ALLOCATOR_MUTEX
|
||||
mMutex.Unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
template<class T> T * FastConstruct(const T & object)
|
||||
{
|
||||
T * objectPtr;
|
||||
|
||||
if (sizeof(T) > BLOCK_SIZE || (!mUnusedList && !Allocate()))
|
||||
{
|
||||
char * memoryPtr = reinterpret_cast<char *>(new unsigned[(sizeof(Node *)+sizeof(T)-1)/sizeof(unsigned)+1]);
|
||||
objectPtr = reinterpret_cast<T *>(memoryPtr+sizeof(Node *));
|
||||
|
||||
*reinterpret_cast<unsigned *>(memoryPtr) = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Node * node = mUnusedList;
|
||||
mUnusedList = node->mNext;
|
||||
node->mNext = (Node *)1;
|
||||
|
||||
objectPtr = reinterpret_cast<T *>(node->mBuffer);
|
||||
|
||||
mNodesUsed++;
|
||||
}
|
||||
|
||||
new (objectPtr) T(object);
|
||||
|
||||
return objectPtr;
|
||||
}
|
||||
|
||||
template<class T> void FastDestroy(T * object)
|
||||
{
|
||||
if (!object)
|
||||
return;
|
||||
|
||||
char * memoryPtr = reinterpret_cast<char *>(object) - sizeof(Node *);
|
||||
Node * node = reinterpret_cast<Node *>(memoryPtr);
|
||||
|
||||
object->~T();
|
||||
|
||||
if (node->mNext == 0)
|
||||
delete []memoryPtr;
|
||||
else
|
||||
{
|
||||
node->mNext = mUnusedList;
|
||||
mUnusedList = node;
|
||||
mNodesUsed--;
|
||||
}
|
||||
}
|
||||
|
||||
bool Allocate()
|
||||
{
|
||||
if (mMemoryBlockCount == MAX_BLOCK_COUNT)
|
||||
return false;
|
||||
|
||||
unsigned count = (mNodesAllocated ? mNodesAllocated : 32);
|
||||
|
||||
Node* newMemoryBlock = new Node[count];
|
||||
for (unsigned i=0; i<count-1; i++)
|
||||
newMemoryBlock[i].mNext = &newMemoryBlock[i+1];
|
||||
newMemoryBlock[count-1].mNext = mUnusedList;
|
||||
|
||||
mUnusedList = newMemoryBlock;
|
||||
mMemoryBlock[mMemoryBlockCount++] = newMemoryBlock;
|
||||
mNodesAllocated += count;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
private:
|
||||
enum { MAX_BLOCK_COUNT = 32 };
|
||||
|
||||
void * mMemoryBlock[MAX_BLOCK_COUNT];
|
||||
unsigned mMemoryBlockCount;
|
||||
unsigned mNodesAllocated;
|
||||
unsigned mNodesUsed;
|
||||
Node * mUnusedList;
|
||||
|
||||
#ifdef USE_ALLOCATOR_MUTEX
|
||||
Base::CMutex mMutex;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
};
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // BLOCK_ALLOCATOR_H
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
// TemplateCBlockAlloc.h: interface for the CBlockAlloc class.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _TEMPLATE_COBJECTALLOCATOR_H
|
||||
#define _TEMPLATE_COBJECTALLOCATOR_H
|
||||
|
||||
#if defined _MT || defined _REENTRANT
|
||||
# define USE_ALLOCATOR_MUTEX
|
||||
#endif
|
||||
|
||||
|
||||
#include <new>
|
||||
#include "Types.h"
|
||||
#include "Mutex.h"
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
const uint32 MAX_BLOCK_COUNT = 32;
|
||||
|
||||
template <class TYPE>
|
||||
class CObjectAllocator
|
||||
{
|
||||
public:
|
||||
CObjectAllocator(uint32 size=32)
|
||||
{
|
||||
if (size < 32)
|
||||
size = 32;
|
||||
|
||||
for (uint32 index=0; index<MAX_BLOCK_COUNT; index++)
|
||||
mMemoryBlock[index] = 0;
|
||||
|
||||
mMemoryBlockCount = 0;
|
||||
mObjectsAllocated = 0;
|
||||
mObjectCount = 0;
|
||||
mUnusedList = 0;
|
||||
|
||||
mBytesAllocated = 0;
|
||||
|
||||
Allocate(size);
|
||||
}
|
||||
|
||||
~CObjectAllocator()
|
||||
{
|
||||
for (uint32 index=0; index<MAX_BLOCK_COUNT; index++)
|
||||
delete[] mMemoryBlock[index];
|
||||
}
|
||||
|
||||
TYPE *Construct()
|
||||
{
|
||||
#ifdef USE_ALLOCATOR_MUTEX
|
||||
mMutex.Lock();
|
||||
#endif
|
||||
if (!mUnusedList)
|
||||
Allocate(mObjectCount);
|
||||
|
||||
Node *node = mUnusedList;
|
||||
mUnusedList = mUnusedList->next;
|
||||
|
||||
new (node) TYPE;
|
||||
|
||||
mObjectCount++;
|
||||
#ifdef USE_ALLOCATOR_MUTEX
|
||||
mMutex.Unlock();
|
||||
#endif
|
||||
return (TYPE *)node;
|
||||
}
|
||||
|
||||
TYPE *Construct(const TYPE& object)
|
||||
{
|
||||
#ifdef USE_ALLOCATOR_MUTEX
|
||||
mMutex.Lock();
|
||||
#endif
|
||||
if (!mUnusedList)
|
||||
Allocate(mObjectCount);
|
||||
|
||||
Node *node = mUnusedList;
|
||||
mUnusedList = mUnusedList->next;
|
||||
|
||||
new (node) TYPE(object);
|
||||
|
||||
mObjectCount++;
|
||||
#ifdef USE_ALLOCATOR_MUTEX
|
||||
mMutex.Unlock();
|
||||
#endif
|
||||
return (TYPE *)node;
|
||||
}
|
||||
|
||||
void Destroy(TYPE *object)
|
||||
{
|
||||
if (object == NULL)
|
||||
return;
|
||||
|
||||
#ifdef USE_ALLOCATOR_MUTEX
|
||||
mMutex.Lock();
|
||||
#endif
|
||||
object->~TYPE();
|
||||
|
||||
Node *node = reinterpret_cast<Node *>(object);
|
||||
|
||||
node->next = mUnusedList;
|
||||
mUnusedList = node;
|
||||
|
||||
mObjectCount--;
|
||||
#ifdef USE_ALLOCATOR_MUTEX
|
||||
mMutex.Unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
TYPE *FastConstruct()
|
||||
{
|
||||
if (!mUnusedList)
|
||||
Allocate(mObjectCount);
|
||||
|
||||
Node *node = mUnusedList;
|
||||
mUnusedList = mUnusedList->next;
|
||||
|
||||
new (node) TYPE;
|
||||
|
||||
mObjectCount++;
|
||||
return (TYPE *)node;
|
||||
}
|
||||
|
||||
TYPE *FastConstruct(const TYPE& object)
|
||||
{
|
||||
if (!mUnusedList)
|
||||
Allocate(mObjectCount);
|
||||
|
||||
Node *node = mUnusedList;
|
||||
mUnusedList = mUnusedList->next;
|
||||
|
||||
new (node) TYPE(object);
|
||||
|
||||
mObjectCount++;
|
||||
return (TYPE *)node;
|
||||
}
|
||||
|
||||
void FastDestroy(TYPE *object)
|
||||
{
|
||||
if (object == NULL)
|
||||
return;
|
||||
|
||||
object->~TYPE();
|
||||
|
||||
Node *node = reinterpret_cast<Node *>(object);
|
||||
|
||||
node->next = mUnusedList;
|
||||
mUnusedList = node;
|
||||
|
||||
mObjectCount--;
|
||||
}
|
||||
|
||||
private:
|
||||
struct Node
|
||||
{
|
||||
char buffer[sizeof(TYPE)];
|
||||
Node* next;
|
||||
};
|
||||
|
||||
bool Allocate(uint32 size)
|
||||
{
|
||||
if (mMemoryBlockCount == MAX_BLOCK_COUNT)
|
||||
return false;
|
||||
|
||||
Node* newMemoryBlock = new Node[size];
|
||||
mBytesAllocated += size * sizeof(Node);
|
||||
|
||||
for (uint32 i=0; i<size-1; i++)
|
||||
newMemoryBlock[i].next = &newMemoryBlock[i+1];
|
||||
newMemoryBlock[size-1].next = mUnusedList;
|
||||
|
||||
mUnusedList = newMemoryBlock;
|
||||
mMemoryBlock[mMemoryBlockCount++] = newMemoryBlock;
|
||||
mObjectsAllocated += size;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
Node* mMemoryBlock[MAX_BLOCK_COUNT];
|
||||
uint32 mMemoryBlockCount;
|
||||
uint32 mObjectsAllocated;
|
||||
uint32 mObjectCount;
|
||||
Node* mUnusedList;
|
||||
#ifdef USE_ALLOCATOR_MUTEX
|
||||
CMutex mMutex;
|
||||
#endif
|
||||
|
||||
uint64 mBytesAllocated;
|
||||
};
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // _TEMPLATE_CBLOCKALLOC_H
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef BASE_THREAD_H
|
||||
#define BASE_THREAD_H
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
#include "win32/Thread.h"
|
||||
|
||||
#elif linux
|
||||
|
||||
#include "linux/Thread.h"
|
||||
|
||||
#elif sparc
|
||||
|
||||
#include "solaris/Thread.h"
|
||||
|
||||
#else
|
||||
|
||||
#error /Base/Thread.h: Undefine platform type
|
||||
|
||||
#endif
|
||||
|
||||
#endif // BASE_THREAD_H
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef BASE_TYPES_H
|
||||
#define BASE_TYPES_H
|
||||
|
||||
#ifdef WIN32
|
||||
# define FILENAME (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
|
||||
#else
|
||||
# define FILENAME __FILE__
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
#include "win32/Types.h"
|
||||
|
||||
#elif linux
|
||||
|
||||
#include "linux/Types.h"
|
||||
|
||||
#elif sparc
|
||||
|
||||
#include "solaris/Types.h"
|
||||
|
||||
#else
|
||||
|
||||
#error /Base/Types.h: Undefine platform type
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,664 @@
|
||||
#ifndef BASE__UCS2_H
|
||||
#define BASE__UCS2_H
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "Base/Archive.h"
|
||||
|
||||
|
||||
namespace ucs2
|
||||
{
|
||||
|
||||
class string
|
||||
{
|
||||
public:
|
||||
typedef unsigned short char_type;
|
||||
typedef unsigned size_type;
|
||||
|
||||
public:
|
||||
string();
|
||||
string(const string & copy);
|
||||
string(const char * copy);
|
||||
string(const char_type * copy);
|
||||
explicit string(const std::string & copy);
|
||||
~string();
|
||||
|
||||
string & operator=(const char * rhs);
|
||||
string & operator=(const std::string & rhs);
|
||||
string & operator=(const char_type * rhs);
|
||||
string & operator=(const string & rhs);
|
||||
|
||||
string & operator+=(const char * rhs);
|
||||
string & operator+=(const std::string & rhs);
|
||||
string & operator+=(const char_type * rhs);
|
||||
string & operator+=(const string & rhs);
|
||||
|
||||
const char_type & at(size_type index) const;
|
||||
char_type & at(size_type index);
|
||||
|
||||
const char_type & operator[](size_type index) const;
|
||||
char_type & operator[](size_type index);
|
||||
|
||||
bool operator==(const string & rhs) const;
|
||||
bool operator!=(const string & rhs) const;
|
||||
bool operator<(const string & rhs) const;
|
||||
bool operator<=(const string & rhs) const;
|
||||
bool operator>(const string & rhs) const;
|
||||
bool operator>=(const string & rhs) const;
|
||||
|
||||
string & assign(const char_type * rhs);
|
||||
string & assign(const char_type * rhs, size_type count);
|
||||
string & assign(const string & rhs, size_type position, size_type count);
|
||||
string & assign(const string & rhs);
|
||||
string & assign(size_type count, char_type c);
|
||||
string & assign(const char_type * first, const char_type * last);
|
||||
|
||||
string & append(const char_type * rhs);
|
||||
string & append(const char_type * rhs, size_type count);
|
||||
string & append(const string & rhs, size_type position, size_type count);
|
||||
string & append(const string & rhs);
|
||||
string & append(size_type count, char_type c);
|
||||
string & append(const char_type * first, const char_type * last);
|
||||
|
||||
std::string narrow() const;
|
||||
const char_type * c_str() const;
|
||||
const char_type * data() const;
|
||||
|
||||
size_type length() const;
|
||||
size_type size() const;
|
||||
size_type max_size() const;
|
||||
void resize(size_type n, char_type c = 0x0032);
|
||||
size_type capacity() const;
|
||||
void reserve(size_type n = 0);
|
||||
bool empty() const;
|
||||
|
||||
private:
|
||||
size_type mLength;
|
||||
std::vector<char_type> mData;
|
||||
static char_type mOutOfRangeCharacter;
|
||||
};
|
||||
|
||||
inline char WideToNarrow(string::char_type c) { return (char)c; }
|
||||
inline string::char_type NarrowToWide(char c) { return (string::char_type)c; }
|
||||
|
||||
string::char_type string::mOutOfRangeCharacter(0);
|
||||
|
||||
////////////////////////////////////////
|
||||
// default constructor allocates 8 characters and sets length to zero
|
||||
inline string::string() :
|
||||
mLength(0),
|
||||
mData(8,0)
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
// target string is a null-terminated C string
|
||||
inline string::string(const char * copy) :
|
||||
mLength(0),
|
||||
mData(8,0)
|
||||
{
|
||||
// (1) protect from null pointer
|
||||
if (!copy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// (2) linear copy the string, must reserve before each character
|
||||
// because the string length is unknown
|
||||
while (*copy)
|
||||
{
|
||||
reserve(mLength+1);
|
||||
mData[mLength++] = *copy++;
|
||||
}
|
||||
// (3) ensure null termination
|
||||
mData[mLength]=0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
// target string is a C string that may or may not include null characters
|
||||
inline string::string(const std::string & copy) :
|
||||
mLength(copy.length()),
|
||||
mData(8,0)
|
||||
{
|
||||
// (1) ensure we have the storage for the entire string
|
||||
reserve(mLength);
|
||||
// (2) perform transform to copy the narrow string to our
|
||||
// wide string
|
||||
std::transform(copy.begin(), copy.end(), mData.begin(), NarrowToWide);
|
||||
// (3) ensure null termination
|
||||
mData[mLength] = 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
// target string is a wide null-terminated C string
|
||||
inline string::string(const char_type * copy) :
|
||||
mLength(0),
|
||||
mData(8,0)
|
||||
{
|
||||
// (1) protect from null pointer
|
||||
if (!copy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// (2) linear copy the string, must reserve before each character
|
||||
// because the string length is unknown
|
||||
while (*copy)
|
||||
{
|
||||
reserve(mLength+1);
|
||||
mData[mLength++] = *copy++;
|
||||
}
|
||||
// (3) ensure null termination
|
||||
mData[mLength]=0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
// copy constructor
|
||||
inline string::string(const string & copy) :
|
||||
mLength(copy.mLength),
|
||||
mData(8,0)
|
||||
{
|
||||
reserve(mLength);
|
||||
mData.assign(copy.mData.begin(), copy.mData.begin()+copy.mLength+1);
|
||||
}
|
||||
|
||||
inline string::~string()
|
||||
{
|
||||
}
|
||||
|
||||
inline string & string::operator=(const char * rhs)
|
||||
{
|
||||
mLength = 0;
|
||||
// (1) protect from null pointer
|
||||
if (!rhs)
|
||||
{
|
||||
mData[0] = 0;
|
||||
return *this;
|
||||
}
|
||||
// (2) linear copy the string, must reserve before each character
|
||||
// because the string length is unknown
|
||||
while (*rhs)
|
||||
{
|
||||
reserve(mLength+1);
|
||||
mData[mLength++] = *rhs++;
|
||||
}
|
||||
// (3) ensure null termination
|
||||
mData[mLength]=0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline string & string::operator=(const std::string & rhs)
|
||||
{
|
||||
mLength = rhs.length();
|
||||
// (1) ensure we have the storage for the entire string
|
||||
reserve(mLength);
|
||||
// (2) perform transform to copy the narrow string to our
|
||||
// wide string
|
||||
std::transform(rhs.begin(), rhs.end(), mData.begin(), NarrowToWide);
|
||||
// (3) ensure null termination
|
||||
mData[mLength] = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline string & string::operator=(const char_type * rhs)
|
||||
{
|
||||
mLength = 0;
|
||||
// (1) protect from null pointer
|
||||
if (!rhs)
|
||||
{
|
||||
mData[0] = 0;
|
||||
return *this;
|
||||
}
|
||||
// (2) linear copy the string, must reserve before each character
|
||||
// because the string length is unknown
|
||||
while (*rhs)
|
||||
{
|
||||
reserve(mLength+1);
|
||||
mData[mLength++] = *rhs++;
|
||||
}
|
||||
// (3) ensure null termination
|
||||
mData[mLength]=0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline string & string::operator=(const string & rhs)
|
||||
{
|
||||
// (1) protect from assigning to self
|
||||
if (&rhs != this)
|
||||
{
|
||||
mLength = rhs.mLength;
|
||||
reserve(mLength);
|
||||
mData.assign(rhs.mData.begin(), rhs.mData.begin()+rhs.mLength+1);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline string & string::operator+=(const char * rhs)
|
||||
{
|
||||
// (1) protect from null pointer
|
||||
if (!rhs)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
// (2) linear copy the string, must reserve before each character
|
||||
// because the string length is unknown
|
||||
while (*rhs)
|
||||
{
|
||||
reserve(mLength+1);
|
||||
mData[mLength++] = *rhs++;
|
||||
}
|
||||
// (3) ensure null termination
|
||||
mData[mLength]=0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline string & string::operator+=(const std::string & rhs)
|
||||
{
|
||||
// (1) ensure we have the storage for the entire string
|
||||
reserve(mLength+rhs.length());
|
||||
// (2) perform transform to copy the narrow string to our
|
||||
// wide string
|
||||
std::transform(rhs.begin(), rhs.end(), mData.begin()+mLength, NarrowToWide);
|
||||
// (3) ensure null termination
|
||||
mLength += rhs.length();
|
||||
mData[mLength] = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline string & string::operator+=(const char_type * rhs)
|
||||
{
|
||||
// (1) protect from null pointer
|
||||
if (!rhs)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
// (2) linear copy the string, must reserve before each character
|
||||
// because the string length is unknown
|
||||
while (*rhs)
|
||||
{
|
||||
reserve(mLength+1);
|
||||
mData[mLength++] = *rhs++;
|
||||
}
|
||||
// (3) ensure null termination
|
||||
mData[mLength]=0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline string & string::operator+=(const string & rhs)
|
||||
{
|
||||
reserve(mLength+rhs.mLength);
|
||||
std::copy(rhs.mData.begin(), rhs.mData.begin()+rhs.mLength+1, mData.begin()+mLength);
|
||||
mLength += rhs.mLength;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline const string::char_type & string::at(string::size_type index) const
|
||||
{
|
||||
return (index > mLength) ? mOutOfRangeCharacter : mData[index];
|
||||
}
|
||||
|
||||
inline string::char_type & string::at(string::size_type index)
|
||||
{
|
||||
return (index > mLength) ? mOutOfRangeCharacter : mData[index];
|
||||
}
|
||||
|
||||
inline const string::char_type & string::operator[](string::size_type index) const
|
||||
{
|
||||
return (index > mLength) ? mOutOfRangeCharacter : mData[index];
|
||||
}
|
||||
|
||||
inline string::char_type & string::operator[](string::size_type index)
|
||||
{
|
||||
return (index > mLength) ? mOutOfRangeCharacter : mData[index];
|
||||
}
|
||||
|
||||
inline bool string::operator==(const string & rhs) const
|
||||
{
|
||||
if (mLength != rhs.mLength)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (memcmp(&mData[0], &rhs.mData[0], sizeof(size_type)*mLength) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool string::operator!=(const string & rhs) const
|
||||
{
|
||||
if (mLength != rhs.mLength)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (memcmp(&mData[0], &rhs.mData[0], sizeof(size_type)*mLength) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool string::operator<(const string & rhs) const
|
||||
{
|
||||
if (mLength <= rhs.mLength)
|
||||
{
|
||||
return (memcmp(&mData[0], &rhs.mData[0], sizeof(size_type)*mLength) < 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (memcmp(&mData[0], &rhs.mData[0], sizeof(size_type)*rhs.mLength) < 0);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool string::operator<=(const string & rhs) const
|
||||
{
|
||||
if (mLength <= rhs.mLength)
|
||||
{
|
||||
return (memcmp(&mData[0], &rhs.mData[0], sizeof(size_type)*mLength) <= 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (memcmp(&mData[0], &rhs.mData[0], sizeof(size_type)*rhs.mLength) <= 0);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool string::operator>(const string & rhs) const
|
||||
{
|
||||
if (mLength <= rhs.mLength)
|
||||
{
|
||||
return (memcmp(&mData[0], &rhs.mData[0], sizeof(size_type)*mLength) > 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (memcmp(&mData[0], &rhs.mData[0], sizeof(size_type)*rhs.mLength) > 0);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool string::operator>=(const string & rhs) const
|
||||
{
|
||||
if (mLength <= rhs.mLength)
|
||||
{
|
||||
return (memcmp(&mData[0], &rhs.mData[0], sizeof(size_type)*mLength) >= 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (memcmp(&mData[0], &rhs.mData[0], sizeof(size_type)*rhs.mLength) >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
inline string & string::assign(const char_type * rhs)
|
||||
{
|
||||
mLength = 0;
|
||||
// (1) protect from null pointer
|
||||
if (!rhs)
|
||||
{
|
||||
mData[0] = 0;
|
||||
return *this;
|
||||
}
|
||||
// (2) linear copy the string, must reserve before each character
|
||||
// because the string length is unknown
|
||||
while (*rhs)
|
||||
{
|
||||
reserve(mLength+1);
|
||||
mData[mLength++] = *rhs++;
|
||||
}
|
||||
// (3) ensure null termination
|
||||
mData[mLength]=0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline string & string::assign(const char_type * rhs, size_type count)
|
||||
{
|
||||
mLength = 0;
|
||||
// (1) protect from null pointer
|
||||
if (!rhs)
|
||||
{
|
||||
mData[0] = 0;
|
||||
return *this;
|
||||
}
|
||||
// (2) linear copy the string, must reserve before each character
|
||||
// because the string length is unknown
|
||||
while (count--)
|
||||
{
|
||||
reserve(mLength+1);
|
||||
mData[mLength++] = *rhs++;
|
||||
}
|
||||
// (3) ensure null termination
|
||||
mData[mLength]=0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline string & string::assign(const string & rhs, size_type position, size_type count)
|
||||
{
|
||||
// (1) protect from assigning to self
|
||||
if (&rhs != this)
|
||||
{
|
||||
mLength = count;
|
||||
reserve(mLength);
|
||||
mData.assign(rhs.mData.begin()+position, rhs.mData.begin()+position+count+1);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline string & string::assign(const string & rhs)
|
||||
{
|
||||
// (1) protect from assigning to self
|
||||
if (&rhs != this)
|
||||
{
|
||||
mLength = rhs.mLength;
|
||||
reserve(mLength);
|
||||
mData.assign(rhs.mData.begin(), rhs.mData.begin()+rhs.mLength+1);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline string & string::assign(size_type count, char_type c)
|
||||
{
|
||||
mLength = count;
|
||||
reserve(mLength);
|
||||
std::fill(mData.begin(), mData.end(), c);
|
||||
mData[mLength]=0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline string & string::assign(const char_type * first, const char_type * last)
|
||||
{
|
||||
mLength = 0;
|
||||
// (1) protect from null pointer
|
||||
if (!first || !last)
|
||||
{
|
||||
mData[0] = 0;
|
||||
return *this;
|
||||
}
|
||||
// (2) linear copy the string, must reserve before each character
|
||||
// because the string length is unknown
|
||||
while (first != last)
|
||||
{
|
||||
reserve(mLength+1);
|
||||
mData[mLength++] = *first++;
|
||||
}
|
||||
// (3) ensure null termination
|
||||
mData[mLength]=0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline string & string::append(const char_type * rhs)
|
||||
{
|
||||
// (1) protect from null pointer
|
||||
if (!rhs)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
// (2) linear copy the string, must reserve before each character
|
||||
// because the string length is unknown
|
||||
while (*rhs)
|
||||
{
|
||||
reserve(mLength+1);
|
||||
mData[mLength++] = *rhs++;
|
||||
}
|
||||
// (3) ensure null termination
|
||||
mData[mLength]=0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline string & string::append(const char_type * rhs, size_type count)
|
||||
{
|
||||
// (1) protect from null pointer
|
||||
if (!rhs)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
// (2) linear copy the string, must reserve before each character
|
||||
// because the string length is unknown
|
||||
while (count--)
|
||||
{
|
||||
reserve(mLength+1);
|
||||
mData[mLength++] = *rhs++;
|
||||
}
|
||||
// (3) ensure null termination
|
||||
mData[mLength]=0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline string & string::append(const string & rhs, size_type position, size_type count)
|
||||
{
|
||||
// (1) protect from invalid position value
|
||||
if (rhs.mLength <= position)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
// (2) make sure we copy count charatcers, or to the end of the string
|
||||
if (rhs.mLength < position+count)
|
||||
{
|
||||
count = rhs.mLength-position;
|
||||
}
|
||||
// (3) perform copy
|
||||
reserve(mLength+count);
|
||||
std::copy(rhs.mData.begin()+position, rhs.mData.begin()+position+count+1, mData.begin()+mLength);
|
||||
mLength += count;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline string & string::append(const string & rhs)
|
||||
{
|
||||
// (1) perform copy
|
||||
reserve(mLength+rhs.mLength);
|
||||
std::copy(rhs.mData.begin(), rhs.mData.begin()+rhs.mLength+1, mData.begin()+mLength);
|
||||
mLength += rhs.mLength;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline string & string::append(size_type count, char_type c)
|
||||
{
|
||||
reserve(mLength+count);
|
||||
std::fill(mData.begin()+mLength, mData.begin()+mLength+count, c);
|
||||
mLength += count;
|
||||
mData[mLength]=0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline string & string::append(const char_type * first, const char_type * last)
|
||||
{
|
||||
// (1) protect from null pointer
|
||||
if (!first || !last)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
// (2) linear copy the string, must reserve before each character
|
||||
// because the string length is unknown
|
||||
while (first != last)
|
||||
{
|
||||
reserve(mLength+1);
|
||||
mData[mLength++] = *first++;
|
||||
}
|
||||
// (3) ensure null termination
|
||||
mData[mLength]=0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline std::string string::narrow() const
|
||||
{
|
||||
std::string narrow;
|
||||
narrow.resize(mLength);
|
||||
std::transform(mData.begin(), mData.begin()+mLength+1, narrow.begin(), WideToNarrow);
|
||||
return narrow;
|
||||
}
|
||||
|
||||
inline const string::char_type * string::c_str() const
|
||||
{
|
||||
return &mData[0];
|
||||
}
|
||||
|
||||
inline const string::char_type * string::data() const
|
||||
{
|
||||
return &mData[0];
|
||||
}
|
||||
|
||||
inline string::size_type string::length() const
|
||||
{
|
||||
return mLength;
|
||||
}
|
||||
|
||||
inline string::size_type string::size() const
|
||||
{
|
||||
return mLength;
|
||||
}
|
||||
|
||||
inline void string::resize(string::size_type n, string::char_type c)
|
||||
{
|
||||
reserve(n);
|
||||
|
||||
while (mLength<n)
|
||||
{
|
||||
mData[mLength++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
inline string::size_type string::capacity() const
|
||||
{
|
||||
return mData.size()-1;
|
||||
}
|
||||
|
||||
inline void string::reserve(string::size_type n)
|
||||
{
|
||||
while (n > capacity())
|
||||
mData.resize((capacity()+1)*2,0x0000);
|
||||
}
|
||||
|
||||
inline bool string::empty() const
|
||||
{
|
||||
return mLength == 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
namespace Base
|
||||
{
|
||||
|
||||
|
||||
inline void get(ByteStream::ReadIterator & source, ucs2::string & target)
|
||||
{
|
||||
unsigned int size = 0;
|
||||
get(source, size);
|
||||
|
||||
const unsigned char * const buf = source.getBuffer();
|
||||
const ucs2::string::char_type * const ubuf = reinterpret_cast<const ucs2::string::char_type *>(buf);
|
||||
|
||||
target.assign(ubuf, ubuf + size);
|
||||
|
||||
const unsigned int readSize = size * sizeof(ucs2::string::char_type);
|
||||
source.advance(readSize);
|
||||
}
|
||||
|
||||
inline void put(ByteStream & target, const ucs2::string & source)
|
||||
{
|
||||
const unsigned int size = source.size();
|
||||
put(target, size);
|
||||
put(target, (const unsigned char *)source.data(), size*sizeof(ucs2::string::char_type));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,165 @@
|
||||
#include "ccspanutil.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
char * CSpanUtil::getDescriptionFromCardNum(const char *cardNum)
|
||||
{
|
||||
// if the card number is less than this we wont be able to determine it, and we dont want to crash so . . .
|
||||
if(strlen(cardNum) < 8 )
|
||||
return "Credit Card";
|
||||
|
||||
switch(cardNum[0])
|
||||
{
|
||||
case '3':
|
||||
if(cardNum[1] == '4')
|
||||
return "AMEX";
|
||||
|
||||
if(cardNum[1] == '7')
|
||||
{
|
||||
// RG 5/9 changed to fix rrc=233. We do not support OP anyway.
|
||||
// Card prefix check is unclear.
|
||||
//
|
||||
// if(cardNum[3] == '7')
|
||||
// return "OP";
|
||||
// else
|
||||
return "AMEX";
|
||||
}
|
||||
if((cardNum[1] == '0') || (cardNum[1] == '6'))
|
||||
return "DINERS";
|
||||
if(cardNum[1] == '8')
|
||||
{
|
||||
if((cardNum[2] >= '1') && (cardNum[2] <= '8'))
|
||||
return "DINERS";
|
||||
}
|
||||
if(cardNum[1] == '5')
|
||||
{
|
||||
char temp[3];
|
||||
memcpy(temp, &cardNum[2], 2);
|
||||
temp[2] = 0;
|
||||
|
||||
int value = atoi(temp);
|
||||
if((value >= 28) && (value <= 89))
|
||||
return "JCB";
|
||||
}
|
||||
break;
|
||||
case '4':
|
||||
// RG 5/6 changed to fix rrc=233. We do not support SW anyway.
|
||||
// Card prefix check is unclear.
|
||||
//
|
||||
// if(cardNum[1] == '9')
|
||||
// return "SW";
|
||||
// else
|
||||
return "VISA";
|
||||
break;
|
||||
case '5':
|
||||
if(cardNum[1] == '6')
|
||||
return "Switch/solo";
|
||||
if((cardNum[1] >= '1') && (cardNum[1] <= '5'))
|
||||
return "MC";
|
||||
if(memcmp(cardNum, "504990", 6) == 0)
|
||||
return "Bill me Later";
|
||||
break;
|
||||
case '6':
|
||||
{
|
||||
char temp[8];
|
||||
memcpy(temp, cardNum, 7);
|
||||
temp[7] = 0;
|
||||
int value7 = atoi(temp);
|
||||
temp[6] = 0;
|
||||
int value6 = atoi(temp);
|
||||
temp[5] = 0;
|
||||
int value5 = atoi(temp);
|
||||
|
||||
// september 29 2005 added new card IIN ranges for Discover per email from
|
||||
// paymentTech email effective Oct 1, 2005
|
||||
if((value5 == 60110) ||
|
||||
(value5 == 60112) ||
|
||||
(value5 == 60113) ||
|
||||
(value5 == 60114) ||
|
||||
(value5 == 60115) ||
|
||||
(value5 == 60116) ||
|
||||
(value6 == 601174) ||
|
||||
(value6 >= 601177 && value6 <= 601179) ||
|
||||
(value6 >=601186 && value6 <= 600189) ||
|
||||
(value5 == 60119) ||
|
||||
(value6 >= 650000 && value6 <=650999))
|
||||
return "DISCOVER";
|
||||
|
||||
if(value6 == 603571)
|
||||
return "Saved Value";
|
||||
|
||||
return "Switch/Solo";
|
||||
}
|
||||
break;
|
||||
case '7':
|
||||
return "Switch/Solo";
|
||||
break;
|
||||
case '9':
|
||||
//if((cardNum[1] == '4') && (cardNum[1] == '5'))
|
||||
return "Carte Blanche";
|
||||
break;
|
||||
}
|
||||
|
||||
return "Credit Card";
|
||||
}
|
||||
|
||||
|
||||
// generates a credit card span for an account number string up to a length of any size.
|
||||
std::string CSpanUtil::generateCCSpan(std::string cardNumber)
|
||||
{
|
||||
// according to the PCI standard on visa's web site, the max number of displayable digits for a cc are the first 6 and last 4
|
||||
// we will however require that at least the middle half of the card is not shown
|
||||
std::string ccSpan;
|
||||
const std::string asteriks = "*";
|
||||
|
||||
// if the length of the card is an odd number we will display the larger portion of the card on the front viewable amount.
|
||||
// for Amex this will look like 1234********123
|
||||
// on a 16 digit card, the most digits will be displayed being 1234********1234
|
||||
// a 12 digit card would look like 123******123
|
||||
|
||||
// as per qa request and devtrack bug 2440 masking is first 4 and las 4 visible
|
||||
|
||||
unsigned displayableBeginningDigits = 4;
|
||||
unsigned displayableEndingDigits = 4;
|
||||
|
||||
if(cardNumber.length() <= 8)
|
||||
return cardNumber;
|
||||
|
||||
ccSpan = cardNumber.substr(0, displayableBeginningDigits);
|
||||
|
||||
unsigned totalMasked = cardNumber.length() - displayableBeginningDigits - displayableEndingDigits;
|
||||
|
||||
// now add in characters to mask the middle numbers
|
||||
for(unsigned maskedDigits = 0; maskedDigits < totalMasked; maskedDigits++)
|
||||
ccSpan += asteriks;
|
||||
|
||||
ccSpan += cardNumber.substr((totalMasked+displayableBeginningDigits));
|
||||
|
||||
return ccSpan;
|
||||
}
|
||||
|
||||
std::string CSpanUtil::generateCCDescription(std::string cardNumber)
|
||||
{
|
||||
std::string ccDescription;
|
||||
|
||||
// get the card type
|
||||
|
||||
ccDescription = std::string(getDescriptionFromCardNum(cardNumber.c_str())) + std::string("(") + generateCCSpan(cardNumber) + std::string(")");
|
||||
|
||||
return ccDescription;
|
||||
}
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#ifndef _CCSPANUTIL_H
|
||||
#define _CCSPANUTIL_H
|
||||
|
||||
#pragma warning (disable : 4786)
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
class CSpanUtil
|
||||
{
|
||||
public:
|
||||
|
||||
//determine the type of credit card according to credit number
|
||||
static char *getDescriptionFromCardNum(const char *cardNum);
|
||||
|
||||
// generates a credit card span for an account number string up to a length of any size.
|
||||
static std::string generateCCSpan(std::string cardNumber);
|
||||
|
||||
// generartes credit card description consist of card type + a credit card span
|
||||
static std::string generateCCDescription(std::string cardNumber);
|
||||
|
||||
// constructor
|
||||
CSpanUtil();
|
||||
|
||||
// destructor
|
||||
~CSpanUtil();
|
||||
};
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,44 @@
|
||||
#ifndef BASE_LINUX_ARCHIVE_H
|
||||
#define BASE_LINUX_ARCHIVE_H
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
|
||||
namespace Base
|
||||
{
|
||||
|
||||
|
||||
#ifdef PACK_BIG_ENDIAN
|
||||
|
||||
inline double byteSwap(double value) { byteReverse(&value); return value; }
|
||||
inline float byteSwap(float value) { byteReverse(&value); return value; }
|
||||
inline uint64 byteSwap(uint64 value) { byteReverse(&value); return value; }
|
||||
inline int64 byteSwap(int64 value) { byteReverse(&value); return value; }
|
||||
inline uint32 byteSwap(uint32 value) { byteReverse(&value); return value; }
|
||||
inline int32 byteSwap(int32 value) { byteReverse(&value); return value; }
|
||||
inline uint16 byteSwap(uint16 value) { byteReverse(&value); return value; }
|
||||
inline int16 byteSwap(int16 value) { byteReverse(&value); return value; }
|
||||
|
||||
#else
|
||||
|
||||
inline double byteSwap(double value) { return value; }
|
||||
inline float byteSwap(float value) { return value; }
|
||||
inline uint64 byteSwap(uint64 value) { return value; }
|
||||
inline int64 byteSwap(int64 value) { return value; }
|
||||
inline uint32 byteSwap(uint32 value) { return value; }
|
||||
inline int32 byteSwap(int32 value) { return value; }
|
||||
inline uint16 byteSwap(uint16 value) { return value; }
|
||||
inline int16 byteSwap(int16 value) { return value; }
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
#include "../BlockAllocator.h"
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
BlockAllocator::BlockAllocator()
|
||||
{
|
||||
for(unsigned i = 0; i < 31; i++)
|
||||
{
|
||||
m_blocks[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
BlockAllocator::~BlockAllocator()
|
||||
{
|
||||
// free all allocated memory blocks
|
||||
for(unsigned i = 0; i < 31; i++)
|
||||
{
|
||||
while(m_blocks[i] != NULL)
|
||||
{
|
||||
unsigned *tmp = m_blocks[i];
|
||||
m_blocks[i] = (unsigned *)*m_blocks[i];
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate a block that is the next power of two greater than the # of bytes passed.
|
||||
// 33 bytes yields a 64 byte block of memory and so forth.
|
||||
|
||||
void *BlockAllocator::getBlock(unsigned bytes)
|
||||
{
|
||||
unsigned accum = 16, bits = 16;
|
||||
unsigned *handle = NULL;
|
||||
|
||||
// Perform a binary search looking for the highest bit.
|
||||
|
||||
while(bits != 0)
|
||||
{
|
||||
// If bytes is less than the bit we're testing for, subtract half
|
||||
// from the bit value and repeat
|
||||
if(bytes < (unsigned)(1 << accum))
|
||||
{
|
||||
bits /= 2;
|
||||
accum -= bits;
|
||||
}
|
||||
// If bytes is greater than the bit we're testing for, add half
|
||||
// from the but value and repeat
|
||||
else if(bytes > (unsigned)(1 << accum))
|
||||
{
|
||||
bits /= 2;
|
||||
accum += bits;
|
||||
}
|
||||
// Got lucky and hit the value dead on
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
// At this point accum contains the most significant bit index, increment
|
||||
accum++;
|
||||
if(accum < 2)
|
||||
{
|
||||
accum = 2;
|
||||
}
|
||||
|
||||
// Note that when memory is actually allocated, 8 extra bytes will be allocated.at the front
|
||||
// The first integer is the address of the next block of memory when the block is in the allocator
|
||||
// The second integer is the bit length of the block
|
||||
// Memory is allocated on 4 byte boundaries to sidestep byte alignment problems
|
||||
|
||||
|
||||
// Check if the allocator already has a block of that size
|
||||
if(m_blocks[accum] == 0)
|
||||
{
|
||||
// remove the pre allocated block from the linked list
|
||||
handle = (unsigned *)calloc(((1 << accum) / 4) + 2, sizeof(unsigned));
|
||||
handle[1] = accum;
|
||||
handle[0] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Allocate a new block
|
||||
handle = m_blocks[accum];
|
||||
m_blocks[accum] = (unsigned *)handle[0];
|
||||
handle[0] = 0;
|
||||
}
|
||||
// return a pointer that skips over the header used for the allocator's purposes
|
||||
return(handle + 2);
|
||||
}
|
||||
|
||||
void BlockAllocator::returnBlock(unsigned *handle)
|
||||
{
|
||||
// C++ allows for safe deletion of a NULL pointer
|
||||
if(handle)
|
||||
{
|
||||
// Update the allocator linked list, insert this entry at the head
|
||||
*(handle - 2) = (unsigned)m_blocks[*(handle - 1)];
|
||||
// Add this entry to the proper linked list node
|
||||
m_blocks[*(handle - 1)] = (handle - 2);
|
||||
}
|
||||
}
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,103 @@
|
||||
////////////////////////////////////////
|
||||
// Event.cpp
|
||||
//
|
||||
// Purpose:
|
||||
// 1. Implementation of the CEvent class.
|
||||
//
|
||||
// Revisions:
|
||||
// 07/10/2001 Created
|
||||
//
|
||||
|
||||
#if defined(_REENTRANT)
|
||||
|
||||
|
||||
#include "Event.h"
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
CEvent::CEvent() :
|
||||
mMutex(),
|
||||
mCond(),
|
||||
mThreadCount(0)
|
||||
{
|
||||
pthread_mutex_init(&mMutex, NULL);
|
||||
pthread_cond_init(&mCond, NULL);
|
||||
}
|
||||
|
||||
CEvent::~CEvent()
|
||||
{
|
||||
pthread_cond_destroy(&mCond);
|
||||
pthread_mutex_destroy(&mMutex);
|
||||
}
|
||||
|
||||
bool CEvent::Signal()
|
||||
{
|
||||
pthread_mutex_lock(&mMutex);
|
||||
if (mThreadCount == 0)
|
||||
mThreadCount = SIGNALED;
|
||||
pthread_cond_signal(&mCond);
|
||||
|
||||
pthread_mutex_unlock(&mMutex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int32 CEvent::Wait(uint32 timeout)
|
||||
{
|
||||
int result;
|
||||
|
||||
pthread_mutex_lock(&mMutex);
|
||||
if (mThreadCount == SIGNALED)
|
||||
{
|
||||
mThreadCount = 0;
|
||||
pthread_mutex_unlock(&mMutex);
|
||||
|
||||
return eWAIT_SIGNAL;
|
||||
}
|
||||
|
||||
if (!timeout)
|
||||
{
|
||||
mThreadCount++;
|
||||
result = pthread_cond_wait(&mCond, &mMutex);
|
||||
mThreadCount--;
|
||||
|
||||
pthread_mutex_unlock(&mMutex);
|
||||
}
|
||||
else
|
||||
{
|
||||
struct timeval now;
|
||||
struct timespec abs_timeout;
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
abs_timeout.tv_sec = now.tv_sec + timeout/1000;
|
||||
abs_timeout.tv_nsec = now.tv_usec * 1000 + (timeout%1000)*1000000;
|
||||
abs_timeout.tv_sec += abs_timeout.tv_nsec / 1000000000;
|
||||
abs_timeout.tv_nsec %= 1000000000;
|
||||
|
||||
mThreadCount++;
|
||||
result = pthread_cond_timedwait(&mCond, &mMutex, &abs_timeout);
|
||||
mThreadCount--;
|
||||
|
||||
pthread_mutex_unlock(&mMutex);
|
||||
}
|
||||
|
||||
if (result == 0 || result == EINTR)
|
||||
return eWAIT_SIGNAL;
|
||||
else if (result == ETIMEDOUT)
|
||||
return eWAIT_TIMEOUT;
|
||||
else
|
||||
return eWAIT_ERROR;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
#endif // #if defined(_REENTRANT)
|
||||
@@ -0,0 +1,74 @@
|
||||
////////////////////////////////////////
|
||||
// Event.h
|
||||
//
|
||||
// Purpose:
|
||||
// 1. Declair the CEvent class that encapsulates the functionality of a
|
||||
// single-locking semaphore.
|
||||
//
|
||||
// Revisions:
|
||||
// 07/10/2001 Created
|
||||
//
|
||||
|
||||
#ifndef BASE_LINUX_EVENT_H
|
||||
#define BASE_LINUX_EVENT_H
|
||||
|
||||
#if !defined(_REENTRANT)
|
||||
# pragma message( "Excluding Base::CEvent - requires multi-threaded compile. (_REENTRANT)" )
|
||||
#else
|
||||
|
||||
|
||||
#include <pthread.h>
|
||||
#include "Platform.h"
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
////////////////////////////////////////
|
||||
// Class:
|
||||
// CEvent
|
||||
//
|
||||
// Purpose:
|
||||
// Encapsulates the functionality of a singal-locking semaphore.
|
||||
// This class is valuable for thread syncronization when a thead's
|
||||
// execution needs to be dependent upon another thread.
|
||||
//
|
||||
// Public Methods:
|
||||
// Signal() : Signals a thread that has called Wait() so that it can
|
||||
// continue execution. This function returns true if the waiting
|
||||
// thread was signalled successfully, otherwise false is returned.
|
||||
// Wait() : Halts the calling thread's execution indefinately until
|
||||
// a Singal() call is made by an external thread. If the thread is
|
||||
// successfully signalled, the function returns eWAIT_SIGNAL. If
|
||||
// timeout period expires without a signal, eWAIT_TIMEOUT is returned.
|
||||
// If the function fails, eWAIT_ERROR is returned.
|
||||
//
|
||||
class CEvent
|
||||
{
|
||||
public:
|
||||
CEvent();
|
||||
virtual ~CEvent();
|
||||
|
||||
bool Signal();
|
||||
int32 Wait(uint32 timeout = 0);
|
||||
|
||||
public:
|
||||
enum { eWAIT_ERROR, eWAIT_SIGNAL, eWAIT_TIMEOUT };
|
||||
enum { SIGNALED = -1 };
|
||||
private:
|
||||
pthread_mutex_t mMutex;
|
||||
pthread_cond_t mCond;
|
||||
int mThreadCount;
|
||||
};
|
||||
|
||||
}
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
#endif // #if defined(_MT)
|
||||
|
||||
#endif // BASE_LINUX_EVENT_H
|
||||
@@ -0,0 +1,40 @@
|
||||
////////////////////////////////////////
|
||||
// Mutex.cpp
|
||||
//
|
||||
// Purpose:
|
||||
// 1. Implementation of the CMutex class.
|
||||
//
|
||||
// Revisions:
|
||||
// 07/10/2001 Created
|
||||
//
|
||||
|
||||
#if defined(_REENTRANT)
|
||||
|
||||
|
||||
#include "Mutex.h"
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
CMutex::CMutex()
|
||||
{
|
||||
mInitialized = (pthread_mutex_init(&mMutex, 0) == 0);
|
||||
}
|
||||
|
||||
CMutex::~CMutex()
|
||||
{
|
||||
if (mInitialized)
|
||||
pthread_mutex_destroy(&mMutex);
|
||||
}
|
||||
|
||||
}
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // #if defined(_REENTRANT)
|
||||
@@ -0,0 +1,79 @@
|
||||
////////////////////////////////////////
|
||||
// Mutex.h
|
||||
//
|
||||
// Purpose:
|
||||
// 1. Declair the CMutex class that encapsulates the functionality of a
|
||||
// mutually-exclusive device.
|
||||
//
|
||||
// Revisions:
|
||||
// 07/10/2001 Created
|
||||
//
|
||||
|
||||
#ifndef BASE_LINUX_MUTEX_H
|
||||
#define BASE_LINUX_MUTEX_H
|
||||
|
||||
#if !defined(_REENTRANT)
|
||||
# pragma message( "Excluding Base::CMutex - requires multi-threaded compile. (_REENTRANT)" )
|
||||
#else
|
||||
|
||||
|
||||
#include "Platform.h"
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
////////////////////////////////////////
|
||||
// Class:
|
||||
// CMutex
|
||||
//
|
||||
// Purpose:
|
||||
// Encapsulates the functionality of a mutually-exclusive device.
|
||||
// This class is valuable for protecting against race conditions
|
||||
// within threaded applications. The CMutex class can be used to
|
||||
// only allow a single thread to run within a specified code
|
||||
// segment at a time.
|
||||
//
|
||||
// Public Methods:
|
||||
// Lock() : Locks the mutex. If the mutex is already locked, the
|
||||
// operating system will block the calling thread until another
|
||||
// thread has unlocked the mutex.
|
||||
// Unlock() : Unlocks the mutex.
|
||||
//
|
||||
class CMutex
|
||||
{
|
||||
public:
|
||||
CMutex();
|
||||
~CMutex();
|
||||
|
||||
void Lock();
|
||||
void Unlock();
|
||||
private:
|
||||
pthread_mutex_t mMutex;
|
||||
bool mInitialized;
|
||||
};
|
||||
|
||||
inline void CMutex::Lock(void)
|
||||
{
|
||||
if (mInitialized)
|
||||
pthread_mutex_lock(&mMutex);
|
||||
}
|
||||
|
||||
inline void CMutex::Unlock(void)
|
||||
{
|
||||
if (mInitialized)
|
||||
pthread_mutex_unlock(&mMutex);
|
||||
}
|
||||
|
||||
}
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
#endif // #if defined(_MT)
|
||||
|
||||
#endif // BASE_LINUX_MUTEX_H
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
////////////////////////////////////////
|
||||
// Platform.cpp
|
||||
//
|
||||
// Purpose:
|
||||
// 1. Implementation of the global functionality declaired in Platform.h.
|
||||
//
|
||||
// Revisions:
|
||||
// 07/10/2001 Created
|
||||
//
|
||||
|
||||
#include <ctype.h>
|
||||
#include "Platform.h"
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
// Implementation of microsoft strlwr extension
|
||||
// This non-ANSI function is not supported under UNIX
|
||||
void strlwr(char * s)
|
||||
{
|
||||
while (*s)
|
||||
{
|
||||
*s = tolower(*s);
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation of microsoft strlwr extension
|
||||
// This non-ANSI function is not supported under UNIX
|
||||
void strupr(char * s)
|
||||
{
|
||||
while (*s)
|
||||
{
|
||||
*s = toupper(*s);
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CTimer::CTimer() :
|
||||
mTimer(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
////////////////////////////////////////
|
||||
// Platform.h
|
||||
//
|
||||
// Purpose:
|
||||
// 1. Include relevent system headers that are platform specific.
|
||||
// 2. Declair global platform specific functionality.
|
||||
// 3. Include primative type definitions
|
||||
//
|
||||
// Global Functions:
|
||||
// getTimer() : Return the current high resolution clock count.
|
||||
// getTimerFrequency() : Return the frequency of the high resolution clock.
|
||||
// sleep() : Voluntarily relinquish timeslice of the calling thread for a
|
||||
// specified number of milliseconds.
|
||||
// strlwr() : Alters the contents of a string, making it all lower-case.
|
||||
//
|
||||
// Revisions:
|
||||
// 07/10/2001 Created
|
||||
//
|
||||
|
||||
#ifndef BASE_LINUX_PLATFORM_H
|
||||
#define BASE_LINUX_PLATFORM_H
|
||||
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <sys/errno.h>
|
||||
#include <pthread.h>
|
||||
#include <resolv.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
uint64 getTimer(void);
|
||||
uint64 getTimerFrequency(void);
|
||||
void sleep(uint32 ms);
|
||||
|
||||
inline uint64 getTimer(void)
|
||||
{
|
||||
uint64 t;
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, 0);
|
||||
t = tv.tv_sec;
|
||||
t = t * 1000000;
|
||||
t += tv.tv_usec;
|
||||
return t;
|
||||
}
|
||||
|
||||
inline uint64 getTimerFrequency(void)
|
||||
{
|
||||
uint64 f = 1000000;
|
||||
return f;
|
||||
}
|
||||
|
||||
inline void sleep(uint32 ms)
|
||||
{
|
||||
usleep(static_cast<unsigned long>(ms * 1000));
|
||||
}
|
||||
|
||||
void strlwr(char * s);
|
||||
void strupr(char * s);
|
||||
|
||||
|
||||
class CTimer
|
||||
{
|
||||
public:
|
||||
CTimer();
|
||||
|
||||
void Set(uint32 seconds);
|
||||
void Signal();
|
||||
bool Expired();
|
||||
|
||||
private:
|
||||
uint32 mTimer;
|
||||
};
|
||||
|
||||
inline void CTimer::Set(uint32 interval)
|
||||
{
|
||||
mTimer = (uint32)time(0) + interval;
|
||||
}
|
||||
|
||||
inline void CTimer::Signal()
|
||||
{
|
||||
mTimer = 0;
|
||||
}
|
||||
|
||||
inline bool CTimer::Expired()
|
||||
{
|
||||
return (mTimer <= (uint32)time(0));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
#endif // BASE_LINUX_PLATFORM_H
|
||||
@@ -0,0 +1,299 @@
|
||||
////////////////////////////////////////
|
||||
// Thread.cpp
|
||||
//
|
||||
// Purpose:
|
||||
// 1. Implementation of the CThread class.
|
||||
//
|
||||
// Revisions:
|
||||
// 07/10/2001 Created
|
||||
//
|
||||
|
||||
#if defined(_REENTRANT)
|
||||
|
||||
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
#include "Thread.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
void *threadProc(void *threadPtr)
|
||||
{
|
||||
CThread &thread = *((CThread*)threadPtr);
|
||||
thread.mThreadActive = true;
|
||||
thread.ThreadProc();
|
||||
thread.mThreadActive = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
CThread::CThread()
|
||||
{
|
||||
mThreadID = 0;
|
||||
mThreadActive = false;
|
||||
mThreadContinue = false;
|
||||
}
|
||||
|
||||
CThread::~CThread()
|
||||
{
|
||||
StopThread();
|
||||
}
|
||||
|
||||
void CThread::StartThread()
|
||||
{
|
||||
mThreadContinue = true;
|
||||
if (pthread_create(&mThreadID,0,threadProc,this) == 0)
|
||||
pthread_detach(mThreadID);
|
||||
while (!IsThreadActive())
|
||||
Base::sleep(1);
|
||||
}
|
||||
|
||||
int32 CThread::StopThread(int timeout)
|
||||
{
|
||||
timeout += time(0);
|
||||
|
||||
mThreadContinue = false;
|
||||
while (mThreadActive && time(0)<timeout)
|
||||
sleep(1);
|
||||
if (mThreadActive)
|
||||
{
|
||||
mThreadActive = false;
|
||||
return eSTOP_TIMEOUT;
|
||||
}
|
||||
return eSTOP_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
CThreadPool::CMember::CMember(CThreadPool * parent) :
|
||||
mParent(parent),
|
||||
mFunction(NULL),
|
||||
mArgument(NULL),
|
||||
mSemaphore()
|
||||
{
|
||||
StartThread();
|
||||
}
|
||||
|
||||
CThreadPool::CMember::~CMember()
|
||||
{
|
||||
}
|
||||
|
||||
void CThreadPool::CMember::Destroy()
|
||||
{
|
||||
mThreadContinue = false;
|
||||
mSemaphore.Signal();
|
||||
}
|
||||
|
||||
bool CThreadPool::CMember::Execute(void( *function )( void * ), void * arg)
|
||||
{
|
||||
if (mFunction)
|
||||
return false;
|
||||
|
||||
mArgument = arg;
|
||||
mFunction = function;
|
||||
mSemaphore.Signal();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CThreadPool::CMember::ThreadProc()
|
||||
{
|
||||
mParent->OnStartup(this);
|
||||
while (mThreadContinue)
|
||||
{
|
||||
mParent->OnIdle(this);
|
||||
mSemaphore.Wait(mParent->GetTimeOut()*1000);
|
||||
|
||||
if (mFunction)
|
||||
{
|
||||
mFunction(mArgument);
|
||||
mArgument = NULL;
|
||||
mFunction = NULL;
|
||||
}
|
||||
else if (mParent->OnDestory(this))
|
||||
mThreadContinue = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
CThreadPool::CThreadPool(uint32 maxThreads, uint32 minThreads, uint32 timeout) :
|
||||
mMutex(),
|
||||
mIdleMember(),
|
||||
mBusyMember(),
|
||||
mNullMember(),
|
||||
mThreadCount(0),
|
||||
mMaxThreads(maxThreads),
|
||||
mMinThreads(minThreads),
|
||||
mTimeOut(timeout)
|
||||
{
|
||||
if (mMaxThreads == 0) mMaxThreads = 1;
|
||||
if (mMinThreads == 0) mMinThreads = 1;
|
||||
if (mMinThreads > mMaxThreads) mMinThreads = mMaxThreads;
|
||||
|
||||
for (uint32 i=0; i<mMinThreads; i++)
|
||||
new CMember(this);
|
||||
}
|
||||
|
||||
CThreadPool::~CThreadPool()
|
||||
{
|
||||
set<CMember *>::iterator setIterator;
|
||||
|
||||
////////////////////////////////////////
|
||||
// (1) Destory all busy member threads
|
||||
mMutex.Lock();
|
||||
setIterator = mBusyMember.begin();
|
||||
while (setIterator != mBusyMember.end())
|
||||
(*setIterator++)->Destroy();
|
||||
mMutex.Unlock();
|
||||
|
||||
////////////////////////////////////////
|
||||
// (2) Destory all idle member threads
|
||||
while (mThreadCount)
|
||||
{
|
||||
mMutex.Lock();
|
||||
setIterator = mIdleMember.begin();
|
||||
while (setIterator != mIdleMember.end())
|
||||
(*setIterator++)->Destroy();
|
||||
mMutex.Unlock();
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
// (3) Delete the null member threads
|
||||
mMutex.Lock();
|
||||
while (!mNullMember.empty())
|
||||
{
|
||||
delete mNullMember.front();
|
||||
mNullMember.pop_front();
|
||||
}
|
||||
mMutex.Unlock();
|
||||
}
|
||||
|
||||
bool CThreadPool::Execute(void( *function )( void * ), void * arg, uint32 poolGrowthSize )
|
||||
{
|
||||
mMutex.Lock();
|
||||
|
||||
////////////////////////////////////////
|
||||
// (1) If no idle members, return false to indicate that no threads
|
||||
// were available. If the thread count is below the max, create
|
||||
// a new thread.
|
||||
if (mIdleMember.empty())
|
||||
{
|
||||
if (mThreadCount < mMaxThreads)
|
||||
{
|
||||
if (!poolGrowthSize)
|
||||
poolGrowthSize = mMinThreads;
|
||||
|
||||
if ((mThreadCount + poolGrowthSize) > mMaxThreads)
|
||||
poolGrowthSize = mMaxThreads - mThreadCount;
|
||||
|
||||
for (uint32 i(0); i < poolGrowthSize; i++)
|
||||
new CMember(this);
|
||||
mMutex.Unlock();
|
||||
time_t idleTimeout = time(0) + 5;
|
||||
while(mIdleMember.empty())
|
||||
{
|
||||
if (time(0) >= idleTimeout)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Base::sleep(10);
|
||||
}
|
||||
mMutex.Lock();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
mMutex.Unlock();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
// (2) Delete any null member threads.
|
||||
while (!mNullMember.empty())
|
||||
{
|
||||
delete mNullMember.front();
|
||||
mNullMember.pop_front();
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
// (3) Move the first idle thread to the busy set and signal the
|
||||
// thread to execute the specified function.
|
||||
CMember * member = *(mIdleMember.begin());
|
||||
mIdleMember.erase(member);
|
||||
mBusyMember.insert(member);
|
||||
member->Execute(function,arg);
|
||||
|
||||
mMutex.Unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 CThreadPool::GetTimeOut()
|
||||
{
|
||||
return mTimeOut;
|
||||
}
|
||||
|
||||
void CThreadPool::OnStartup(CMember * member)
|
||||
{
|
||||
mMutex.Lock();
|
||||
|
||||
mThreadCount++;
|
||||
mIdleMember.insert(member);
|
||||
|
||||
mMutex.Unlock();
|
||||
}
|
||||
|
||||
void CThreadPool::OnIdle(CMember * member)
|
||||
{
|
||||
mMutex.Lock();
|
||||
|
||||
mBusyMember.erase(member);
|
||||
mIdleMember.insert(member);
|
||||
|
||||
mMutex.Unlock();
|
||||
}
|
||||
|
||||
bool CThreadPool::OnDestory(CMember * member)
|
||||
{
|
||||
set<CMember *>::iterator setIterator;
|
||||
|
||||
mMutex.Lock();
|
||||
|
||||
bool result = (setIterator = mIdleMember.find(member)) != mIdleMember.end();
|
||||
if (result)
|
||||
{
|
||||
mNullMember.push_back(member);
|
||||
mIdleMember.erase(setIterator);
|
||||
mThreadCount--;
|
||||
}
|
||||
|
||||
mMutex.Unlock();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
}
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
#endif // #if defined(_REENTRANT)
|
||||
@@ -0,0 +1,146 @@
|
||||
////////////////////////////////////////
|
||||
// Thread.h
|
||||
//
|
||||
// Purpose:
|
||||
// 1. Declair the CThread class that encapsulates threading functionality.
|
||||
// This abstract base class in intended to be used to encapsulate
|
||||
// individual tasks that require threading in derived classes.
|
||||
//
|
||||
// Revisions:
|
||||
// 07/10/2001 Created
|
||||
//
|
||||
|
||||
#ifndef BASE_LINUX_THREAD_H
|
||||
#define BASE_LINUX_THREAD_H
|
||||
|
||||
#if !defined(_REENTRANT)
|
||||
# pragma message( "Excluding Base::CThread - requires multi-threaded compile. (_REENTRANT)" )
|
||||
#else
|
||||
|
||||
|
||||
#pragma warning( disable : 4786)
|
||||
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include "Platform.h"
|
||||
#include "Mutex.h"
|
||||
#include "Event.h"
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
////////////////////////////////////////
|
||||
// Class:
|
||||
// CThread
|
||||
//
|
||||
// Purpose:
|
||||
// Encapsulates threading functionality. Creating classes derived
|
||||
// from CThread provides an easy way to encapsulate tasks that require
|
||||
// their own thread.
|
||||
//
|
||||
// Public Methods:
|
||||
// StartThread() : Creates the low-level thread handle and begins executing
|
||||
// the CThread::ThreadProc() function within the new thread.
|
||||
// StopThread() : Signals the ThreadProc() function to stop executing using
|
||||
// the mThreadContinue member variable, and waits for the ThreadProc()
|
||||
// function to exit. By default, the function will block for a maximum
|
||||
// of 5 seconds before exiting without the thread halting.
|
||||
// IsThreadActive() : Returns true if the physical thread is still executing
|
||||
// within the ThreadProc() function, otherwise it returns false.
|
||||
// ThreadProc() : Pure-virtual function that will be executed when the StartThread()
|
||||
// function is called. Derived classes must implement this function. The
|
||||
// mThreadContinue member variable should be used internal the the ThreadProc()
|
||||
// function to indicate whether it should continue executing or exit.
|
||||
// Protected Attributes:
|
||||
// mThreadContinue : Boolean value indicating to the ThreadProc() function
|
||||
// whether to continue executing or to exit. If mThreadContinue is true,
|
||||
// ThreadProc() should continue, otherwise ThreadProc() should exit. It
|
||||
// left up to the derived class to implement a ThreadProc() function that
|
||||
// uses the mThreadContinue member.
|
||||
//
|
||||
//
|
||||
class CThread
|
||||
{
|
||||
friend void * threadProc(void *);
|
||||
|
||||
public:
|
||||
enum { eSTOP_SUCCESS, eSTOP_TIMEOUT };
|
||||
public:
|
||||
CThread();
|
||||
virtual ~CThread();
|
||||
|
||||
void StartThread();
|
||||
int32 StopThread(int timeout=5);
|
||||
bool IsThreadActive() { return mThreadActive; }
|
||||
|
||||
protected:
|
||||
virtual void ThreadProc() {}
|
||||
|
||||
protected:
|
||||
bool mThreadContinue;
|
||||
private:
|
||||
pthread_t mThreadID;
|
||||
bool mThreadActive;
|
||||
};
|
||||
|
||||
|
||||
class CThreadPool
|
||||
{
|
||||
private:
|
||||
class CMember : public CThread
|
||||
{
|
||||
public:
|
||||
CMember(CThreadPool * parent);
|
||||
virtual ~CMember();
|
||||
|
||||
bool Execute(void( *function )( void * ), void * arg);
|
||||
void Destroy();
|
||||
|
||||
protected:
|
||||
virtual void ThreadProc();
|
||||
|
||||
private:
|
||||
CThreadPool * mParent;
|
||||
void( * mFunction )( void * );
|
||||
void * mArgument;
|
||||
CEvent mSemaphore;
|
||||
};
|
||||
friend class CMember;
|
||||
|
||||
public:
|
||||
CThreadPool(uint32 maxThreads, uint32 minThreads=1, uint32 timeout=15*60);
|
||||
virtual ~CThreadPool();
|
||||
|
||||
virtual bool Execute(void( *function )( void * ), void * arg, uint32 poolGrowthSize = 0);
|
||||
|
||||
private:
|
||||
uint32 GetTimeOut();
|
||||
void OnStartup(CMember * member);
|
||||
void OnIdle(CMember * member);
|
||||
bool OnDestory(CMember * member);
|
||||
|
||||
private:
|
||||
CMutex mMutex;
|
||||
std::set<CMember *> mIdleMember;
|
||||
std::set<CMember *> mBusyMember;
|
||||
std::list<CMember *> mNullMember;
|
||||
uint32 mThreadCount;
|
||||
|
||||
uint32 mMaxThreads;
|
||||
uint32 mMinThreads;
|
||||
uint32 mTimeOut;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
#endif // #if defined(_REENTRANT)
|
||||
|
||||
#endif // BASE_LINUX_THREAD_H
|
||||
@@ -0,0 +1,42 @@
|
||||
////////////////////////////////////////
|
||||
// Types.h
|
||||
//
|
||||
// Purpose:
|
||||
// 1. Define integer types that are unambiguous with respect to size
|
||||
//
|
||||
// Revisions:
|
||||
// 07/10/2001 Created
|
||||
//
|
||||
|
||||
#ifndef BASE_LINUX_TYPES_H
|
||||
#define BASE_LINUX_TYPES_H
|
||||
|
||||
#include <sys/bitypes.h>
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
|
||||
namespace Base
|
||||
{
|
||||
#define INT32_MAX 0x7FFFFFFF
|
||||
#define INT32_MIN 0x80000000
|
||||
#define UINT32_MAX 0xFFFFFFFF
|
||||
|
||||
typedef signed char int8;
|
||||
typedef unsigned char uint8;
|
||||
typedef signed short int16;
|
||||
typedef unsigned short uint16;
|
||||
|
||||
typedef int32_t int32;
|
||||
typedef u_int32_t uint32;
|
||||
typedef int64_t int64;
|
||||
typedef u_int64_t uint64;
|
||||
}
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
#endif // BASE_LINUX_TYPES_H
|
||||
|
||||
@@ -0,0 +1,585 @@
|
||||
#ifndef SOE__SERIALIZE_H
|
||||
#define SOE__SERIALIZE_H
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "Types.h"
|
||||
|
||||
#if (defined(WIN32) || defined(linux)) && !defined(NETWORK_BIG_ENDIAN)
|
||||
#define BYTE1 0
|
||||
#define BYTE2 1
|
||||
#define BYTE3 2
|
||||
#define BYTE4 3
|
||||
#define BYTE5 4
|
||||
#define BYTE6 5
|
||||
#define BYTE7 6
|
||||
#define BYTE8 7
|
||||
#elif defined(sparc) || defined(NETWORK_BIG_ENDIAN)
|
||||
#define BYTE1 7
|
||||
#define BYTE2 6
|
||||
#define BYTE3 5
|
||||
#define BYTE4 4
|
||||
#define BYTE5 3
|
||||
#define BYTE6 2
|
||||
#define BYTE7 1
|
||||
#define BYTE8 0
|
||||
#endif
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
|
||||
namespace soe
|
||||
{
|
||||
|
||||
using namespace Base;//for types
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// 388 ns (50 bytes)
|
||||
inline unsigned Write(unsigned char * stream, unsigned size, const unsigned char * data, unsigned dataLen)
|
||||
{
|
||||
if (size < dataLen)
|
||||
return 0;
|
||||
memcpy(stream, data, dataLen);
|
||||
return dataLen;
|
||||
}
|
||||
|
||||
// 353 ns
|
||||
inline unsigned Write(unsigned char * stream, unsigned size, bool data)
|
||||
{
|
||||
if (size < sizeof(uint8))
|
||||
return 0;
|
||||
stream[BYTE1] = data;
|
||||
return sizeof(uint8);
|
||||
}
|
||||
|
||||
// 362 ns
|
||||
inline unsigned Write(unsigned char * stream, unsigned size, int8 data)
|
||||
{
|
||||
if (size < sizeof(int8))
|
||||
return 0;
|
||||
stream[BYTE1] = data;
|
||||
return sizeof(int8);
|
||||
}
|
||||
|
||||
// 362 ns
|
||||
inline unsigned Write(unsigned char * stream, unsigned size, uint8 data)
|
||||
{
|
||||
if (size < sizeof(uint8))
|
||||
return 0;
|
||||
stream[BYTE1] = data;
|
||||
return sizeof(uint8);
|
||||
}
|
||||
|
||||
// 355 ns
|
||||
inline unsigned Write(unsigned char * stream, unsigned size, int16 data)
|
||||
{
|
||||
if (size < sizeof(int16))
|
||||
return 0;
|
||||
stream[BYTE1] = data&0xff;
|
||||
stream[BYTE2] = (data>>8)&0xff;
|
||||
return sizeof(int16);
|
||||
}
|
||||
|
||||
// 360 ns
|
||||
inline unsigned Write(unsigned char * stream, unsigned size, uint16 data)
|
||||
{
|
||||
if (size < sizeof(uint16))
|
||||
return 0;
|
||||
stream[BYTE1] = data&0xff;
|
||||
stream[BYTE2] = (data>>8)&0xff;
|
||||
return sizeof(uint16);
|
||||
}
|
||||
|
||||
// 360 ns
|
||||
inline unsigned Write(unsigned char * stream, unsigned size, int32 data)
|
||||
{
|
||||
if (size < sizeof(int32))
|
||||
return 0;
|
||||
stream[BYTE1] = data&0xff;
|
||||
stream[BYTE2] = (data>>8)&0xff;
|
||||
stream[BYTE3] = (data>>16)&0xff;
|
||||
stream[BYTE4] = (data>>24)&0xff;
|
||||
return sizeof(int32);
|
||||
}
|
||||
|
||||
// 360 ns
|
||||
inline unsigned Write(unsigned char * stream, unsigned size, uint32 data)
|
||||
{
|
||||
if (size < sizeof(uint32))
|
||||
return 0;
|
||||
stream[BYTE1] = data&0xff;
|
||||
stream[BYTE2] = (data>>8)&0xff;
|
||||
stream[BYTE3] = (data>>16)&0xff;
|
||||
stream[BYTE4] = (data>>24)&0xff;
|
||||
return sizeof(uint32);
|
||||
}
|
||||
|
||||
// 370 ns
|
||||
inline unsigned Write(unsigned char * stream, unsigned size, int64 data)
|
||||
{
|
||||
if (size < sizeof(int64))
|
||||
return 0;
|
||||
#if (defined(WIN32) || defined(linux))
|
||||
uint32 low = *(uint32 *)(&data);
|
||||
uint32 high = *((uint32 *)&data+1);
|
||||
#elif defined(sparc)
|
||||
uint32 low = *((uint32 *)&data+1);
|
||||
uint32 high = *(uint32 *)(&data);
|
||||
#endif
|
||||
stream[BYTE1] = (unsigned char)low&0xff;
|
||||
stream[BYTE2] = (unsigned char)(low>>8)&0xff;
|
||||
stream[BYTE3] = (unsigned char)(low>>16)&0xff;
|
||||
stream[BYTE4] = (unsigned char)(low>>24)&0xff;
|
||||
stream[BYTE5] = (unsigned char)high&0xff;
|
||||
stream[BYTE6] = (unsigned char)(high>>8)&0xff;
|
||||
stream[BYTE7] = (unsigned char)(high>>16)&0xff;
|
||||
stream[BYTE8] = (unsigned char)(high>>24)&0xff;
|
||||
return sizeof(int64);
|
||||
}
|
||||
|
||||
// 370 ns
|
||||
inline unsigned Write(unsigned char * stream, unsigned size, uint64 data)
|
||||
{
|
||||
if (size < sizeof(uint64))
|
||||
return 0;
|
||||
#if (defined(WIN32) || defined(linux))
|
||||
uint32 low = *(uint32 *)(&data);
|
||||
uint32 high = *((uint32 *)&data+1);
|
||||
#elif defined(sparc)
|
||||
uint32 low = *((uint32 *)&data+1);
|
||||
uint32 high = *(uint32 *)(&data);
|
||||
#endif
|
||||
stream[BYTE1] = (unsigned char)low&0xff;
|
||||
stream[BYTE2] = (unsigned char)(low>>8)&0xff;
|
||||
stream[BYTE3] = (unsigned char)(low>>16)&0xff;
|
||||
stream[BYTE4] = (unsigned char)(low>>24)&0xff;
|
||||
stream[BYTE5] = (unsigned char)high&0xff;
|
||||
stream[BYTE6] = (unsigned char)(high>>8)&0xff;
|
||||
stream[BYTE7] = (unsigned char)(high>>16)&0xff;
|
||||
stream[BYTE8] = (unsigned char)(high>>24)&0xff;
|
||||
return sizeof(uint64);
|
||||
}
|
||||
|
||||
// 360 ns
|
||||
inline unsigned Write(unsigned char * stream, unsigned size, float data)
|
||||
{
|
||||
uint32 & dataRef = *(uint32 *)(&data);
|
||||
if (size < sizeof(float))
|
||||
return 0;
|
||||
stream[BYTE1] = dataRef&0xff;
|
||||
stream[BYTE2] = (dataRef>>8)&0xff;
|
||||
stream[BYTE3] = (dataRef>>16)&0xff;
|
||||
stream[BYTE4] = (dataRef>>24)&0xff;
|
||||
return sizeof(float);
|
||||
}
|
||||
|
||||
// 370 ns
|
||||
inline unsigned Write(unsigned char * stream, unsigned size, double data)
|
||||
{
|
||||
if (size < sizeof(double))
|
||||
return 0;
|
||||
#if (defined(WIN32) || defined(linux))
|
||||
uint32 low = *(uint32 *)(&data);
|
||||
uint32 high = *((uint32 *)&data+1);
|
||||
#elif defined(sparc)
|
||||
uint32 low = *((uint32 *)&data+1);
|
||||
uint32 high = *(uint32 *)(&data);
|
||||
#endif
|
||||
stream[BYTE1] = (unsigned char)low&0xff;
|
||||
stream[BYTE2] = (unsigned char)(low>>8)&0xff;
|
||||
stream[BYTE3] = (unsigned char)(low>>16)&0xff;
|
||||
stream[BYTE4] = (unsigned char)(low>>24)&0xff;
|
||||
stream[BYTE5] = (unsigned char)high&0xff;
|
||||
stream[BYTE6] = (unsigned char)(high>>8)&0xff;
|
||||
stream[BYTE7] = (unsigned char)(high>>16)&0xff;
|
||||
stream[BYTE8] = (unsigned char)(high>>24)&0xff;
|
||||
return sizeof(double);
|
||||
}
|
||||
|
||||
// 361 ns
|
||||
inline unsigned WriteEncoded(unsigned char * stream, unsigned size, uint32 data)
|
||||
{
|
||||
unsigned encoded = ((data & 0xffffffc0) << 2) | (data & 0x3f);
|
||||
if (data > 0x3fffffff)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if (data > 0x3fffff && size >= 4)
|
||||
{
|
||||
encoded |= 0xc0;
|
||||
stream[BYTE1] = encoded & 0xff;
|
||||
stream[BYTE2] = (encoded>>8) & 0xff;
|
||||
stream[BYTE3] = (encoded>>16) & 0xff;
|
||||
stream[BYTE4] = (encoded>>24) & 0xff;
|
||||
return 4;
|
||||
}
|
||||
else if (data > 0x3fff)
|
||||
{
|
||||
encoded |= 0x80;
|
||||
stream[BYTE1] = encoded & 0xff;
|
||||
stream[BYTE2] = (encoded>>8) & 0xff;
|
||||
stream[BYTE3] = (encoded>>16) & 0xff;
|
||||
return 3;
|
||||
}
|
||||
else if (data > 0x3f)
|
||||
{
|
||||
encoded |= 0x40;
|
||||
stream[BYTE1] = encoded & 0xff;
|
||||
stream[BYTE2] = (encoded>>8) & 0xff;
|
||||
return 2;
|
||||
}
|
||||
else if (size >= 1)
|
||||
{
|
||||
stream[BYTE1] = encoded & 0xff;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 630 ns
|
||||
inline unsigned Write(unsigned char * stream, unsigned size, const std::string & data, bool isPascal=true)
|
||||
{
|
||||
if (isPascal)
|
||||
{
|
||||
unsigned fieldLen = 0;
|
||||
unsigned bytes = 0;
|
||||
fieldLen = Write(stream, size, static_cast<unsigned>(data.length()));
|
||||
if (!fieldLen)
|
||||
return 0;
|
||||
bytes += fieldLen;
|
||||
if (data.length())
|
||||
{
|
||||
fieldLen = Write(stream+bytes, size-bytes, reinterpret_cast<const unsigned char *>(data.data()), data.length());
|
||||
if (!fieldLen)
|
||||
return 0;
|
||||
bytes += fieldLen;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned bytes = Write(stream, size, reinterpret_cast<const unsigned char *>(data.c_str()), data.size()+1);
|
||||
if (!bytes)
|
||||
return 0;
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline unsigned Write(unsigned char * stream, unsigned size, const std::vector<T> & data)
|
||||
{
|
||||
unsigned bytes = 0;
|
||||
unsigned elementBytes = 0;
|
||||
bytes += Write(stream, size, static_cast<unsigned>(data.size()));
|
||||
if (!bytes)
|
||||
return 0;
|
||||
for (typename std::vector<T>::const_iterator iter = data.begin(); iter != data.end(); iter++)
|
||||
{
|
||||
elementBytes = Write(stream+bytes, size-bytes, *iter);
|
||||
if (!elementBytes)
|
||||
return 0;
|
||||
bytes += elementBytes;
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
inline unsigned Read(const unsigned char * stream, unsigned size, unsigned char * data, unsigned dataLen)
|
||||
{
|
||||
if (size < dataLen)
|
||||
return 0;
|
||||
memcpy(data, stream, dataLen);
|
||||
return dataLen;
|
||||
}
|
||||
|
||||
// 355 ns
|
||||
inline unsigned Read(const unsigned char * stream, unsigned size, bool & data, unsigned = 1/*unused*/)
|
||||
{
|
||||
if (size < sizeof(uint8))
|
||||
return 0;
|
||||
data = stream[BYTE1] != 0;
|
||||
return sizeof(uint8);
|
||||
}
|
||||
|
||||
// 355 ns
|
||||
inline unsigned Read(const unsigned char * stream, unsigned size, int8 & data, unsigned = 1/*unused*/)
|
||||
{
|
||||
if (size < sizeof(int8))
|
||||
return 0;
|
||||
data = stream[BYTE1];
|
||||
return sizeof(int8);
|
||||
}
|
||||
|
||||
// 355 ns
|
||||
inline unsigned Read(const unsigned char * stream, unsigned size, uint8 & data, unsigned = 1/*unused*/)
|
||||
{
|
||||
if (size < sizeof(uint8))
|
||||
return 0;
|
||||
data = stream[BYTE1];
|
||||
return sizeof(uint8);
|
||||
}
|
||||
|
||||
// 355 ns
|
||||
inline unsigned Read(const unsigned char * stream, unsigned size, int16 & data, unsigned = 1/*unused*/)
|
||||
{
|
||||
if (size < sizeof(int16))
|
||||
return 0;
|
||||
data = stream[BYTE1];
|
||||
data |= stream[BYTE2]<<8;
|
||||
return sizeof(int16);
|
||||
}
|
||||
|
||||
// 355 ns
|
||||
inline unsigned Read(const unsigned char * stream, unsigned size, uint16 & data, unsigned = 1/*unused*/)
|
||||
{
|
||||
if (size < sizeof(uint16))
|
||||
return 0;
|
||||
data = stream[BYTE1];
|
||||
data |= stream[BYTE2]<<8;
|
||||
return sizeof(uint16);
|
||||
}
|
||||
|
||||
// 360 ns
|
||||
inline unsigned Read(const unsigned char * stream, unsigned size, int32 & data, unsigned = 1/*unused*/)
|
||||
{
|
||||
if (size < sizeof(int32))
|
||||
return 0;
|
||||
data = stream[BYTE1];
|
||||
data |= stream[BYTE2]<<8;
|
||||
data |= stream[BYTE3]<<16;
|
||||
data |= stream[BYTE4]<<24;
|
||||
return sizeof(int32);
|
||||
}
|
||||
|
||||
// 360 ns
|
||||
inline unsigned Read(const unsigned char * stream, unsigned size, uint32 & data, unsigned = 1/*unused*/)
|
||||
{
|
||||
if (size < sizeof(uint32))
|
||||
return 0;
|
||||
data = stream[BYTE1];
|
||||
data |= stream[BYTE2]<<8;
|
||||
data |= stream[BYTE3]<<16;
|
||||
data |= stream[BYTE4]<<24;
|
||||
return sizeof(uint32);
|
||||
}
|
||||
|
||||
// 380 ns
|
||||
inline unsigned Read(const unsigned char * stream, unsigned size, int64 & data, unsigned = 1/*unused*/)
|
||||
{
|
||||
if (size < sizeof(int64))
|
||||
return 0;
|
||||
#if (defined(WIN32) || defined(linux))
|
||||
uint32 & low = *(uint32 *)(&data);
|
||||
uint32 & high = *((uint32 *)&data+1);
|
||||
#elif defined(sparc)
|
||||
uint32 & low = *(uint32 *)(&data+1);
|
||||
uint32 & high = *(uint32 *)(&data);
|
||||
#endif
|
||||
low = stream[BYTE1];
|
||||
low |= stream[BYTE2]<<8;
|
||||
low |= stream[BYTE3]<<16;
|
||||
low |= stream[BYTE4]<<24;
|
||||
high = stream[BYTE5];
|
||||
high |= stream[BYTE6]<<8;
|
||||
high |= stream[BYTE7]<<16;
|
||||
high |= stream[BYTE8]<<24;
|
||||
return sizeof(int64);
|
||||
}
|
||||
|
||||
// 380 ns
|
||||
inline unsigned Read(const unsigned char * stream, unsigned size, uint64 & data, unsigned = 1/*unused*/)
|
||||
{
|
||||
if (size < sizeof(uint64))
|
||||
return 0;
|
||||
#if (defined(WIN32) || defined(linux))
|
||||
uint32 & low = *(uint32 *)(&data);
|
||||
uint32 & high = *((uint32 *)&data+1);
|
||||
#elif defined(sparc)
|
||||
uint32 & low = *(uint32 *)(&data+1);
|
||||
uint32 & high = *(uint32 *)(&data);
|
||||
#endif
|
||||
low = stream[BYTE1];
|
||||
low |= stream[BYTE2]<<8;
|
||||
low |= stream[BYTE3]<<16;
|
||||
low |= stream[BYTE4]<<24;
|
||||
high = stream[BYTE5];
|
||||
high |= stream[BYTE6]<<8;
|
||||
high |= stream[BYTE7]<<16;
|
||||
high |= stream[BYTE8]<<24;
|
||||
return sizeof(uint64);
|
||||
}
|
||||
|
||||
// 370 ns
|
||||
inline unsigned Read(const unsigned char * stream, unsigned size, float & data, unsigned = 1/*unused*/)
|
||||
{
|
||||
assert(sizeof(float) == 4);
|
||||
uint32 & dataRef = *(uint32 *)(&data);
|
||||
if (size < sizeof(float))
|
||||
return 0;
|
||||
dataRef = stream[BYTE1];
|
||||
dataRef |= stream[BYTE2]<<8;
|
||||
dataRef |= stream[BYTE3]<<16;
|
||||
dataRef |= stream[BYTE4]<<24;
|
||||
return sizeof(float);
|
||||
}
|
||||
|
||||
// 380 ns
|
||||
inline unsigned Read(const unsigned char * stream, unsigned size, double & data, unsigned = 1/*unused*/)
|
||||
{
|
||||
assert(sizeof(double) == 8);
|
||||
if (size < sizeof(double))
|
||||
return 0;
|
||||
#if (defined(WIN32) || defined(linux))
|
||||
uint32 & low = *(uint32 *)(&data);
|
||||
uint32 & high = *((uint32 *)&data+1);
|
||||
#elif defined(sparc)
|
||||
uint32 & low = *(uint32 *)(&data+1);
|
||||
uint32 & high = *(uint32 *)(&data);
|
||||
#endif
|
||||
low = stream[BYTE1];
|
||||
low |= stream[BYTE2]<<8;
|
||||
low |= stream[BYTE3]<<16;
|
||||
low |= stream[BYTE4]<<24;
|
||||
high = stream[BYTE5];
|
||||
high |= stream[BYTE6]<<8;
|
||||
high |= stream[BYTE7]<<16;
|
||||
high |= stream[BYTE8]<<24;
|
||||
return sizeof(double);
|
||||
}
|
||||
|
||||
// 360 ns
|
||||
inline unsigned ReadEncoded(const unsigned char * stream, unsigned size, uint32 & data)
|
||||
{
|
||||
if (size < 1)
|
||||
return 0;
|
||||
|
||||
unsigned encoded = stream[BYTE1];
|
||||
unsigned mask = encoded & 0xc0;
|
||||
if (mask == 0)
|
||||
{
|
||||
data = encoded & 0x3f;
|
||||
return 1;
|
||||
}
|
||||
else if (mask == 0x40 && size >=2)
|
||||
{
|
||||
data = (stream[BYTE2]<<6) | (encoded & 0x3f);
|
||||
return 2;
|
||||
}
|
||||
else if (mask == 0x80 && size >=3)
|
||||
{
|
||||
data = (stream[BYTE3]<<14) | (stream[BYTE2]<<6) | (encoded & 0x3f);
|
||||
return 3;
|
||||
}
|
||||
else if (mask == 0xc0 && size >=4)
|
||||
{
|
||||
data = (stream[BYTE4]<<22) | (stream[BYTE3]<<14) | (stream[BYTE2]<<6) | (encoded & 0x3f);
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline unsigned Read(const unsigned char * stream, unsigned size, std::string & data, unsigned maxLen, bool isPascal=true)
|
||||
{
|
||||
if (isPascal)
|
||||
{
|
||||
unsigned bytes = 0;
|
||||
unsigned length = 0;
|
||||
bytes += Read(stream, size, length);
|
||||
if (!bytes || size < bytes+length || length > maxLen)
|
||||
return 0;
|
||||
data.assign((const char *)stream+bytes, (const char *)stream+bytes+length); // assign would be dangerous if unicode, single-byte characters are ok
|
||||
return bytes + length;
|
||||
}
|
||||
else
|
||||
{
|
||||
//find null terminator, if don't find it before maxLen, then err
|
||||
const unsigned char *strEnd = NULL;
|
||||
unsigned strLen = 0;
|
||||
for (;strLen<maxLen && strLen < size;strLen++)
|
||||
{
|
||||
if (stream[strLen] == 0)
|
||||
{
|
||||
//found it
|
||||
strEnd = (stream+strLen);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (strEnd == NULL)
|
||||
return 0;
|
||||
|
||||
data.assign((const char *)stream, (const char *)strEnd);
|
||||
return strLen+1;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline unsigned Read(const unsigned char * stream, unsigned size, std::vector<T> & data, unsigned maxVecLen, unsigned maxFieldLen = 1)
|
||||
{
|
||||
T element;
|
||||
unsigned bytes = 0;
|
||||
unsigned elementBytes = 0;
|
||||
unsigned length = 0;
|
||||
data.clear();
|
||||
bytes += Read(stream, size, length);
|
||||
if (!bytes || length > maxVecLen)
|
||||
return 0;
|
||||
for (unsigned i=0; i<length; i++)
|
||||
{
|
||||
elementBytes = Read(stream+bytes, size-bytes, element, maxFieldLen);
|
||||
if (!elementBytes)
|
||||
return 0;
|
||||
data.push_back(element);
|
||||
bytes += elementBytes;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class AutoVariableBase
|
||||
{
|
||||
public:
|
||||
AutoVariableBase() {}
|
||||
virtual ~AutoVariableBase() {}
|
||||
|
||||
virtual unsigned Write(unsigned char * stream, unsigned size) const = 0;
|
||||
virtual unsigned Read(const unsigned char * stream, unsigned size, unsigned maxLen=1) = 0;
|
||||
};
|
||||
|
||||
template<class ValueType>
|
||||
class AutoVariable : public AutoVariableBase
|
||||
{
|
||||
public:
|
||||
AutoVariable() : AutoVariableBase(), mValue() {}
|
||||
explicit AutoVariable(const ValueType & source) : AutoVariableBase(), mValue(source) {}
|
||||
AutoVariable(const AutoVariable & copy) : AutoVariableBase(), mValue(copy.mValue) {}
|
||||
virtual ~AutoVariable() {}
|
||||
|
||||
const ValueType & Get() const { return mValue; }
|
||||
void Set(const ValueType & rhs) { mValue = rhs; }
|
||||
virtual unsigned Write(unsigned char * stream, unsigned size) const { return soe::Write(stream, size, mValue); }
|
||||
virtual unsigned Read(const unsigned char * stream, unsigned size, unsigned maxLen) { return soe::Read(stream, size, mValue, maxLen); }
|
||||
|
||||
private:
|
||||
ValueType mValue;
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
}
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef BASE_SOLARIS_ARCHIVE_H
|
||||
#define BASE_SOLARIS_ARCHIVE_H
|
||||
|
||||
|
||||
namespace Base
|
||||
{
|
||||
|
||||
|
||||
#ifdef PACK_BIG_ENDIAN
|
||||
|
||||
inline double byteSwap(double value) { return value; }
|
||||
inline float byteSwap(float value) { return value; }
|
||||
inline uint64 byteSwap(uint64 value) { return value; }
|
||||
inline int64 byteSwap(int64 value) { return value; }
|
||||
inline uint32 byteSwap(uint32 value) { return value; }
|
||||
inline int32 byteSwap(int32 value) { return value; }
|
||||
inline uint16 byteSwap(uint16 value) { return value; }
|
||||
inline int16 byteSwap(int16 value) { return value; }
|
||||
|
||||
#else
|
||||
|
||||
inline double byteSwap(double value) { byteReverse64(&value); return value; }
|
||||
inline float byteSwap(float value) { byteReverse32(&value); return value; }
|
||||
inline uint64 byteSwap(uint64 value) { byteReverse64(&value); return value; }
|
||||
inline int64 byteSwap(int64 value) { byteReverse64(&value); return value; }
|
||||
inline uint32 byteSwap(uint32 value) { byteReverse32(&value); return value; }
|
||||
inline int32 byteSwap(int32 value) { byteReverse32(&value); return value; }
|
||||
inline uint16 byteSwap(uint16 value) { byteReverse16(&value); return value; }
|
||||
inline int16 byteSwap(int16 value) { byteReverse16(&value); return value; }
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
#include "../BlockAllocator.h"
|
||||
|
||||
namespace Base
|
||||
{
|
||||
BlockAllocator::BlockAllocator()
|
||||
{
|
||||
for(unsigned i = 0; i < 31; i++)
|
||||
{
|
||||
m_blocks[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
BlockAllocator::~BlockAllocator()
|
||||
{
|
||||
// free all allocated memory blocks
|
||||
for(unsigned i = 0; i < 31; i++)
|
||||
{
|
||||
while(m_blocks[i] != NULL)
|
||||
{
|
||||
unsigned *tmp = m_blocks[i];
|
||||
m_blocks[i] = (unsigned *)*m_blocks[i];
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate a block that is the next power of two greater than the # of bytes passed.
|
||||
// 33 bytes yields a 64 byte block of memory and so forth.
|
||||
|
||||
void *BlockAllocator::getBlock(unsigned bytes)
|
||||
{
|
||||
unsigned accum = 16, bits = 16;
|
||||
unsigned *handle = NULL;
|
||||
|
||||
// Perform a binary search looking for the highest bit.
|
||||
|
||||
while(bits != 0)
|
||||
{
|
||||
// If bytes is less than the bit we're testing for, subtract half
|
||||
// from the bit value and repeat
|
||||
if(bytes < (unsigned)(1 << accum))
|
||||
{
|
||||
bits /= 2;
|
||||
accum -= bits;
|
||||
}
|
||||
// If bytes is greater than the bit we're testing for, add half
|
||||
// from the but value and repeat
|
||||
else if(bytes > (unsigned)(1 << accum))
|
||||
{
|
||||
bits /= 2;
|
||||
accum += bits;
|
||||
}
|
||||
// Got lucky and hit the value dead on
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
// At this point accum contains the most significant bit index, increment
|
||||
accum++;
|
||||
if(accum < 2)
|
||||
{
|
||||
accum = 2;
|
||||
}
|
||||
|
||||
// Note that when memory is actually allocated, 8 extra bytes will be allocated.at the front
|
||||
// The first integer is the address of the next block of memory when the block is in the allocator
|
||||
// The second integer is the bit length of the block
|
||||
// Memory is allocated on 4 byte boundaries to sidestep byte alignment problems
|
||||
|
||||
|
||||
// Check if the allocator already has a block of that size
|
||||
if(m_blocks[accum] == 0)
|
||||
{
|
||||
// remove the pre allocated block from the linked list
|
||||
handle = (unsigned *)calloc(((1 << accum) / 4) + 2, sizeof(unsigned));
|
||||
handle[1] = accum;
|
||||
handle[0] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Allocate a new block
|
||||
handle = m_blocks[accum];
|
||||
m_blocks[accum] = (unsigned *)handle[0];
|
||||
handle[0] = 0;
|
||||
}
|
||||
// return a pointer that skips over the header used for the allocator's purposes
|
||||
return(handle + 2);
|
||||
}
|
||||
|
||||
void BlockAllocator::returnBlock(unsigned *handle)
|
||||
{
|
||||
// C++ allows for safe deletion of a NULL pointer
|
||||
if(handle)
|
||||
{
|
||||
// Update the allocator linked list, insert this entry at the head
|
||||
*(handle - 2) = (unsigned)m_blocks[*(handle - 1)];
|
||||
// Add this entry to the proper linked list node
|
||||
m_blocks[*(handle - 1)] = (handle - 2);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,93 @@
|
||||
////////////////////////////////////////
|
||||
// Event.cpp
|
||||
//
|
||||
// Purpose:
|
||||
// 1. Implementation of the CEvent class.
|
||||
//
|
||||
// Revisions:
|
||||
// 07/10/2001 Created
|
||||
//
|
||||
|
||||
#if defined(_REENTRANT)
|
||||
|
||||
|
||||
#include "Event.h"
|
||||
|
||||
namespace Base
|
||||
{
|
||||
|
||||
CEvent::CEvent()
|
||||
{
|
||||
mWaiting = 0;
|
||||
mSignaled = false;
|
||||
mInitialized = false;
|
||||
if (pthread_cond_init( &mCond, NULL ) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (pthread_mutex_init( &mMutex, NULL ) != 0)
|
||||
{
|
||||
pthread_cond_destroy( &mCond );
|
||||
return;
|
||||
}
|
||||
mInitialized = true;
|
||||
}
|
||||
|
||||
CEvent::~CEvent()
|
||||
{
|
||||
if (mInitialized)
|
||||
{
|
||||
pthread_cond_destroy(&mCond);
|
||||
pthread_mutex_destroy(&mMutex);
|
||||
}
|
||||
}
|
||||
|
||||
int32 CEvent::Wait(uint32 timeout)
|
||||
{
|
||||
if (!mInitialized)
|
||||
return CEvent::eWAIT_ERROR;
|
||||
|
||||
int result;
|
||||
pthread_mutex_lock(&mMutex);
|
||||
|
||||
if (mSignaled)
|
||||
{
|
||||
mSignaled = false;
|
||||
}
|
||||
else if (!timeout)
|
||||
{
|
||||
mWaiting++;
|
||||
result = pthread_cond_wait(&mCond, &mMutex);
|
||||
mWaiting--;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct timespec wake_time;
|
||||
|
||||
clock_gettime( CLOCK_REALTIME, &wake_time );
|
||||
wake_time.tv_sec += timeout/1000;
|
||||
wake_time.tv_nsec += (timeout%1000)*1000000;
|
||||
|
||||
// normalize new time
|
||||
wake_time.tv_sec += wake_time.tv_nsec / 1000000000;
|
||||
wake_time.tv_nsec %= 1000000000;
|
||||
|
||||
mWaiting++;
|
||||
result = pthread_cond_timedwait( &mCond, &mMutex, &wake_time );
|
||||
mWaiting--;
|
||||
}
|
||||
pthread_mutex_unlock(&mMutex);
|
||||
|
||||
if (result == 0)
|
||||
return CEvent::eWAIT_SIGNAL;
|
||||
else if (result == ETIMEDOUT)
|
||||
return CEvent::eWAIT_TIMEOUT;
|
||||
else
|
||||
return CEvent::eWAIT_ERROR;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // #if defined(_REENTRANT)
|
||||
@@ -0,0 +1,83 @@
|
||||
////////////////////////////////////////
|
||||
// Event.h
|
||||
//
|
||||
// Purpose:
|
||||
// 1. Declair the CEvent class that encapsulates the functionality of a
|
||||
// single-locking semaphore.
|
||||
//
|
||||
// Revisions:
|
||||
// 07/10/2001 Created
|
||||
//
|
||||
|
||||
#ifndef BASE_SOLARIS_EVENT_H
|
||||
#define BASE_SOLARIS_EVENT_H
|
||||
|
||||
#if !defined(_REENTRANT)
|
||||
# pragma message( "Excluding Base::CEvent - requires multi-threaded compile. (_REENTRANT)" )
|
||||
#else
|
||||
|
||||
|
||||
#include "Platform.h"
|
||||
|
||||
namespace Base
|
||||
{
|
||||
|
||||
////////////////////////////////////////
|
||||
// Class:
|
||||
// CEvent
|
||||
//
|
||||
// Purpose:
|
||||
// Encapsulates the functionality of a singal-locking semaphore.
|
||||
// This class is valuable for thread syncronization when a thead's
|
||||
// execution needs to be dependent upon another thread.
|
||||
//
|
||||
// Public Methods:
|
||||
// Signal() : Signals a thread that has called Wait() so that it can
|
||||
// continue execution. This function returns true if the waiting
|
||||
// thread was signalled successfully, otherwise false is returned.
|
||||
// Wait() : Halts the calling thread's execution indefinately until
|
||||
// a Singal() call is made by an external thread. If the thread is
|
||||
// successfully signalled, the function returns eWAIT_SIGNAL. If
|
||||
// timeout period expires without a signal, eWAIT_TIMEOUT is returned.
|
||||
// If the function fails, eWAIT_ERROR is returned.
|
||||
//
|
||||
class CEvent
|
||||
{
|
||||
public:
|
||||
CEvent();
|
||||
virtual ~CEvent();
|
||||
|
||||
bool Signal();
|
||||
int32 Wait(uint32 timeout = 0);
|
||||
public:
|
||||
enum { eWAIT_ERROR, eWAIT_SIGNAL, eWAIT_TIMEOUT };
|
||||
private:
|
||||
pthread_mutex_t mMutex;
|
||||
pthread_cond_t mCond;
|
||||
bool mInitialized;
|
||||
bool mSignaled;
|
||||
int32 mWaiting;
|
||||
};
|
||||
|
||||
inline bool CEvent::Signal()
|
||||
{
|
||||
if (!mInitialized)
|
||||
return false;
|
||||
pthread_mutex_lock(&mMutex);
|
||||
if(mWaiting > 0)
|
||||
{
|
||||
pthread_cond_signal(&mCond);
|
||||
}
|
||||
else
|
||||
{
|
||||
mWaiting = true;
|
||||
}
|
||||
pthread_mutex_unlock(&mMutex);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // #if defined(_MT)
|
||||
|
||||
#endif // BASE_SOLARIS_EVENT_H
|
||||
@@ -0,0 +1,387 @@
|
||||
#include "../Logger.h"
|
||||
#include "Mutex.h"
|
||||
#include <sys/stat.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Base
|
||||
{
|
||||
const char file_sep = '/';
|
||||
|
||||
Logger::Logger(const char *prefix, int level, unsigned size, bool rollDate)
|
||||
: m_defaultLevel(level), m_defaultSize(size), m_dirPrefix(prefix), m_rollDate(rollDate)
|
||||
{
|
||||
char buf[1024];
|
||||
FILE *logDir = NULL;
|
||||
|
||||
logDir = fopen(m_dirPrefix.c_str(), "r+");
|
||||
if(errno == ENOENT)
|
||||
{
|
||||
cmkdir(m_dirPrefix.c_str(), 0755);
|
||||
}
|
||||
else if(logDir != NULL)
|
||||
{
|
||||
fclose(logDir);
|
||||
}
|
||||
|
||||
tm now;
|
||||
time_t t = time(NULL);
|
||||
#ifdef REENTRANT
|
||||
localtime_r(&t, &now);
|
||||
#else
|
||||
now = *(localtime(&t));
|
||||
#endif
|
||||
|
||||
memcpy(&m_lastDateTime, &now, sizeof(tm));
|
||||
if(m_rollDate)
|
||||
{
|
||||
sprintf(buf, "%s%c%2.2d-%2.2d-%2.2d", m_dirPrefix.c_str(), file_sep, (now.tm_mon + 1), now.tm_mday, (now.tm_year % 100));
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(buf, "%s", m_dirPrefix.c_str());
|
||||
}
|
||||
logDir = fopen(buf, "r+");
|
||||
if(errno == ENOENT)
|
||||
{
|
||||
cmkdir(buf, 0755);
|
||||
}
|
||||
else if(logDir != NULL)
|
||||
{
|
||||
fclose(logDir);
|
||||
}
|
||||
m_logPrefix = buf;
|
||||
}
|
||||
|
||||
Logger::~Logger()
|
||||
{
|
||||
map<unsigned, LogInfo *>::iterator iter;
|
||||
for(iter = m_logTable.begin(); iter != m_logTable.end(); iter++)
|
||||
{
|
||||
log((*iter).first, LOG_FILEONLY, "---=== Log Stopped ===---");
|
||||
fflush((*iter).second->file);
|
||||
fclose((*iter).second->file);
|
||||
delete((*iter).second);
|
||||
|
||||
}
|
||||
m_logTable.clear();
|
||||
}
|
||||
|
||||
void Logger::flush(unsigned logenum)
|
||||
{
|
||||
map<unsigned, LogInfo *>::iterator iter = m_logTable.find(logenum);
|
||||
|
||||
if(iter != m_logTable.end())
|
||||
{
|
||||
LogInfo *info = (*iter).second;
|
||||
fflush(info->file);
|
||||
info->used = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::flushAll()
|
||||
{
|
||||
map<unsigned, LogInfo *>::iterator iter;
|
||||
|
||||
for(iter = m_logTable.begin(); iter != m_logTable.end(); iter++)
|
||||
{
|
||||
LogInfo *info = (*iter).second;
|
||||
fflush(info->file);
|
||||
info->used = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::addLog(const char *id, unsigned logenum, int level, unsigned size)
|
||||
{
|
||||
LogInfo *newLog = new LogInfo;
|
||||
newLog->filename = m_logPrefix + file_sep + id + ".log";
|
||||
newLog->name = id;
|
||||
newLog->used = 0;
|
||||
newLog->max = size;
|
||||
newLog->last = 0;
|
||||
newLog->level = level;
|
||||
|
||||
m_logTable.insert(pair<unsigned, LogInfo *>(logenum, newLog));
|
||||
|
||||
if(strcmp("stdout", id) == 0)
|
||||
{
|
||||
newLog->file = stdout;
|
||||
}
|
||||
else if(strcmp("stderr", id) == 0)
|
||||
{
|
||||
newLog->file = stderr;
|
||||
}
|
||||
else
|
||||
{
|
||||
newLog->file = fopen(newLog->filename.c_str(), "a+");
|
||||
}
|
||||
log(logenum, LOG_FILEONLY, "---=== Log Started ===---");
|
||||
}
|
||||
|
||||
void Logger::addLog(const char *id, unsigned logenum)
|
||||
{
|
||||
LogInfo *newLog = new LogInfo;
|
||||
newLog->filename = m_logPrefix + file_sep + id + ".log";
|
||||
newLog->name = id;
|
||||
newLog->used = 0;
|
||||
newLog->max = m_defaultSize;
|
||||
newLog->last = 0;
|
||||
newLog->level = m_defaultLevel;
|
||||
|
||||
m_logTable.insert(pair<unsigned, LogInfo *>(logenum, newLog));
|
||||
|
||||
if(strcmp("stdout", id) == 0)
|
||||
{
|
||||
newLog->file = stdout;
|
||||
}
|
||||
else if(strcmp("stderr", id) == 0)
|
||||
{
|
||||
newLog->file = stderr;
|
||||
}
|
||||
else
|
||||
{
|
||||
newLog->file = fopen(newLog->filename.c_str(), "a+");
|
||||
}
|
||||
log(logenum, LOG_FILEONLY, "---===Log Started ===---");
|
||||
}
|
||||
|
||||
void Logger::updateLog(unsigned logenum, int level, unsigned size)
|
||||
{
|
||||
map<unsigned, LogInfo *>::iterator iter = m_logTable.find(logenum);
|
||||
|
||||
if(iter != m_logTable.end())
|
||||
{
|
||||
(*iter).second->level = level;
|
||||
(*iter).second->max = size;
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::removeLog(unsigned logenum)
|
||||
{
|
||||
map<unsigned, LogInfo *>::iterator iter = m_logTable.find(logenum);
|
||||
|
||||
if(iter != m_logTable.end())
|
||||
{
|
||||
log((*iter).first, LOG_ALWAYS, "---=== Log Stopped ===---");
|
||||
fflush((*iter).second->file);
|
||||
fclose((*iter).second->file);
|
||||
delete((*iter).second);
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::logSimple(unsigned logenum, int level, const char *message)
|
||||
{
|
||||
map<unsigned, LogInfo *>::iterator iter = m_logTable.find(logenum);
|
||||
if(iter == m_logTable.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
time_t t = time(NULL);
|
||||
LogInfo *info = (*iter).second;
|
||||
if(level >= info->level)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(info->last != t)
|
||||
{
|
||||
memcpy(&info->ts, localtime(&t), sizeof(tm));
|
||||
info->last = t;
|
||||
}
|
||||
|
||||
if(m_rollDate && info->ts.tm_mday != m_lastDateTime.tm_mday)
|
||||
{
|
||||
#if defined(_REENTRANT)
|
||||
rLock.Lock();
|
||||
#endif
|
||||
if(info->ts.tm_mday != m_lastDateTime.tm_mday)
|
||||
{
|
||||
memcpy(&m_lastDateTime, &info->ts, sizeof(tm));
|
||||
rollDate(t);
|
||||
}
|
||||
#if defined(_REENTRANT)
|
||||
rLock.Unlock();
|
||||
#endif
|
||||
}
|
||||
if(iter != m_logTable.end())
|
||||
{
|
||||
if(info->max > 0)
|
||||
{
|
||||
int tmp = fprintf(info->file, "[%2.2d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d] %s\n", (info->ts.tm_mon + 1), info->ts.tm_mday, (info->ts.tm_year % 100), info->ts.tm_hour, info->ts.tm_min, info->ts.tm_sec, message);
|
||||
info->used += tmp;
|
||||
if(info->used > info->max)
|
||||
{
|
||||
fflush(info->file);
|
||||
info->used = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(info->file, "[%2.2d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d] %s\n", (info->ts.tm_mon + 1), info->ts.tm_mday, (info->ts.tm_year % 100), info->ts.tm_hour, info->ts.tm_min, info->ts.tm_sec, message);
|
||||
fflush(info->file);
|
||||
}
|
||||
}
|
||||
}
|
||||
void Logger::log(unsigned logenum, int level, const char *message, ...)
|
||||
{
|
||||
char buf[2048];
|
||||
va_list varg;
|
||||
va_start(varg, message);
|
||||
vsnprintf(buf, 2047, message, varg);
|
||||
buf[2047] = 0;
|
||||
va_end(varg);
|
||||
|
||||
map<unsigned, LogInfo *>::iterator iter = m_logTable.find(logenum);
|
||||
if(iter == m_logTable.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
time_t t = time(NULL);
|
||||
LogInfo *info = (*iter).second;
|
||||
if(level >= info->level)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(info->last != t)
|
||||
{
|
||||
#ifdef REENTRANT
|
||||
localtime_r(&t, &info->ts);
|
||||
#else
|
||||
info->ts = *(localtime(&t));
|
||||
#endif
|
||||
|
||||
info->last = t;
|
||||
}
|
||||
if(info->ts.tm_mday != m_lastDateTime.tm_mday)
|
||||
{
|
||||
#if defined(_REENTRANT)
|
||||
rLock.Lock();
|
||||
#endif
|
||||
if(info->ts.tm_mday != m_lastDateTime.tm_mday)
|
||||
{
|
||||
memcpy(&m_lastDateTime, &info->ts, sizeof(tm));
|
||||
rollDate(t);
|
||||
}
|
||||
#if defined(_REENTRANT)
|
||||
rLock.Unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
if(iter != m_logTable.end())
|
||||
{
|
||||
if(info->max > 0)
|
||||
{
|
||||
int tmp = fprintf(info->file, "[%2.2d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d] %s\n", (info->ts.tm_mon + 1), info->ts.tm_mday, (info->ts.tm_year % 100), info->ts.tm_hour, info->ts.tm_min, info->ts.tm_sec, buf);
|
||||
info->used += tmp;
|
||||
if(info->used > info->max)
|
||||
{
|
||||
fflush(info->file);
|
||||
info->used = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(info->file, "[%2.2d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d] %s\n", (info->ts.tm_mon + 1), info->ts.tm_mday, (info->ts.tm_year % 100), info->ts.tm_hour, info->ts.tm_min, info->ts.tm_sec, buf);
|
||||
fflush(info->file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::rollDate(time_t t)
|
||||
{
|
||||
char buf[80];
|
||||
FILE *logDir = NULL;
|
||||
|
||||
logDir = fopen(m_dirPrefix.c_str(), "r+");
|
||||
if(errno == ENOENT)
|
||||
{
|
||||
cmkdir(m_dirPrefix.c_str(), 0755);
|
||||
}
|
||||
else if(logDir != NULL)
|
||||
{
|
||||
fclose(logDir);
|
||||
}
|
||||
|
||||
tm now;
|
||||
#ifdef REENTRANT
|
||||
localtime_r(&t, &now);
|
||||
#else
|
||||
now = *(localtime(&t));
|
||||
#endif
|
||||
|
||||
sprintf(buf, "%s%c%2.2d-%2.2d-%2.2d", m_dirPrefix.c_str(), file_sep, (now.tm_mon + 1), now.tm_mday, (now.tm_year % 100));
|
||||
logDir = fopen(buf, "r+");
|
||||
|
||||
if(errno == ENOENT)
|
||||
{
|
||||
cmkdir(buf, 0755);
|
||||
}
|
||||
else if(logDir != NULL)
|
||||
{
|
||||
fclose(logDir);
|
||||
}
|
||||
m_logPrefix = buf;
|
||||
|
||||
map<unsigned, LogInfo *>::iterator iter;
|
||||
for(iter = m_logTable.begin(); iter != m_logTable.end(); iter++)
|
||||
{
|
||||
(*iter).second->filename = m_logPrefix + file_sep + (*iter).second->name.c_str() + ".log";
|
||||
fflush((*iter).second->file);
|
||||
fclose((*iter).second->file);
|
||||
(*iter).second->file = fopen((*iter).second->filename.c_str(), "a+");
|
||||
memcpy(&((*iter).second->ts), &now, sizeof(tm));
|
||||
}
|
||||
}
|
||||
|
||||
// mkdir function that creates intermediate directories
|
||||
void Logger::cmkdir(const char *dir, int mode)
|
||||
{
|
||||
char dirbuf[128];
|
||||
strncpy(dirbuf, dir, 127);
|
||||
dirbuf[127] = 0;
|
||||
char *j = dirbuf, *i = dirbuf;
|
||||
int handle;
|
||||
|
||||
while(*i)
|
||||
{
|
||||
if(*i == file_sep)
|
||||
{
|
||||
(*i) = 0;
|
||||
// handle = open(j, O_EXCL); // Ben's original code
|
||||
// if((handle > 0) || (errno != EISDIR && errno != ENOENT))
|
||||
// {
|
||||
// perror("Logger::cmkdir():");
|
||||
// abort();
|
||||
// }
|
||||
|
||||
// This doesnt work under Linux, it returns a valid handle
|
||||
// Instead: see if file exists. If it doesnt, create directory ok
|
||||
// If it exists, do stat to see if it is a dir.
|
||||
// If it is a dir, then ok, create the directory.
|
||||
// If it is a file, error
|
||||
// ging 9-16-2002
|
||||
|
||||
handle = open(j, O_RDONLY);
|
||||
if (handle > 0)
|
||||
{
|
||||
struct stat stat_buffer;
|
||||
int ret = fstat(handle,&stat_buffer);
|
||||
if ((ret == -1) || ((stat_buffer.st_mode | S_IFDIR) == 0))
|
||||
{
|
||||
perror("Logger::cmkdir():");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
mkdir(j, mode);
|
||||
close(handle);
|
||||
(*i) = file_sep;
|
||||
}
|
||||
else if(*(i + 1) == 0)
|
||||
{
|
||||
mkdir(j, mode);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
////////////////////////////////////////
|
||||
// Mutex.cpp
|
||||
//
|
||||
// Purpose:
|
||||
// 1. Implementation of the CMutex class.
|
||||
//
|
||||
// Revisions:
|
||||
// 07/10/2001 Created
|
||||
//
|
||||
|
||||
#if defined(_REENTRANT)
|
||||
|
||||
|
||||
#include "Mutex.h"
|
||||
|
||||
namespace Base
|
||||
{
|
||||
|
||||
CMutex::CMutex()
|
||||
{
|
||||
mInitialized = (pthread_mutex_init(&mMutex, 0) == 0);
|
||||
}
|
||||
|
||||
CMutex::~CMutex()
|
||||
{
|
||||
if (mInitialized)
|
||||
pthread_mutex_destroy(&mMutex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // #if defined(_REENTRANT)
|
||||
@@ -0,0 +1,71 @@
|
||||
////////////////////////////////////////
|
||||
// Mutex.h
|
||||
//
|
||||
// Purpose:
|
||||
// 1. Declair the CMutex class that encapsulates the functionality of a
|
||||
// mutually-exclusive device.
|
||||
//
|
||||
// Revisions:
|
||||
// 07/10/2001 Created
|
||||
//
|
||||
|
||||
#ifndef BASE_SOLARIS_MUTEX_H
|
||||
#define BASE_SOLARIS_MUTEX_H
|
||||
|
||||
#if !defined(_REENTRANT)
|
||||
# pragma message( "Excluding Base::CMutex - requires multi-threaded compile. (_REENTRANT)" )
|
||||
#else
|
||||
|
||||
|
||||
#include "Platform.h"
|
||||
|
||||
namespace Base
|
||||
{
|
||||
|
||||
////////////////////////////////////////
|
||||
// Class:
|
||||
// CMutex
|
||||
//
|
||||
// Purpose:
|
||||
// Encapsulates the functionality of a mutually-exclusive device.
|
||||
// This class is valuable for protecting against race conditions
|
||||
// within threaded applications. The CMutex class can be used to
|
||||
// only allow a single thread to run within a specified code
|
||||
// segment at a time.
|
||||
//
|
||||
// Public Methods:
|
||||
// Lock() : Locks the mutex. If the mutex is already locked, the
|
||||
// operating system will block the calling thread until another
|
||||
// thread has unlocked the mutex.
|
||||
// Unlock() : Unlocks the mutex.
|
||||
//
|
||||
class CMutex
|
||||
{
|
||||
public:
|
||||
CMutex();
|
||||
~CMutex();
|
||||
|
||||
void Lock();
|
||||
void Unlock();
|
||||
private:
|
||||
pthread_mutex_t mMutex;
|
||||
bool mInitialized;
|
||||
};
|
||||
|
||||
inline void CMutex::Lock(void)
|
||||
{
|
||||
if (mInitialized)
|
||||
pthread_mutex_lock(&mMutex);
|
||||
}
|
||||
|
||||
inline void CMutex::Unlock(void)
|
||||
{
|
||||
if (mInitialized)
|
||||
pthread_mutex_unlock(&mMutex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // #if defined(_MT)
|
||||
|
||||
#endif // BASE_SOLARIS_MUTEX_H
|
||||
@@ -0,0 +1,46 @@
|
||||
////////////////////////////////////////
|
||||
// Platform.cpp
|
||||
//
|
||||
// Purpose:
|
||||
// 1. Implementation of the global functionality declaired in Platform.h.
|
||||
//
|
||||
// Revisions:
|
||||
// 07/10/2001 Created
|
||||
//
|
||||
|
||||
#include <ctype.h>
|
||||
#include "Platform.h"
|
||||
|
||||
namespace Base
|
||||
{
|
||||
|
||||
// Implementation of microsoft strlwr extension
|
||||
// This non-ANSI function is not supported under UNIX
|
||||
void strlwr(char * s)
|
||||
{
|
||||
while (*s)
|
||||
{
|
||||
*s = tolower(*s);
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation of microsoft strupr extension
|
||||
// This non-ANSI function is not supported under UNIX
|
||||
void strupr(char * s)
|
||||
{
|
||||
while (*s)
|
||||
{
|
||||
*s = toupper(*s);
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CTimer::CTimer() :
|
||||
mTimer(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
////////////////////////////////////////
|
||||
// Platform.h
|
||||
//
|
||||
// Purpose:
|
||||
// 1. Include relevent system headers that are platform specific.
|
||||
// 2. Declair global platform specific functionality.
|
||||
// 3. Include primative type definitions
|
||||
//
|
||||
// Global Functions:
|
||||
// getTimer() : Return the current high resolution clock count.
|
||||
// getTimerFrequency() : Return the frequency of the high resolution clock.
|
||||
// sleep() : Voluntarily relinquish timeslice of the calling thread for a
|
||||
// specified number of milliseconds.
|
||||
// strlwr() : Alters the contents of a string, making it all lower-case.
|
||||
//
|
||||
// Revisions:
|
||||
// 07/10/2001 Created
|
||||
//
|
||||
|
||||
#ifndef BASE_SOLARIS_PLATFORM_H
|
||||
#define BASE_SOLARIS_PLATFORM_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include "Types.h"
|
||||
|
||||
|
||||
namespace Base
|
||||
{
|
||||
uint64 getTimer(void);
|
||||
uint64 getTimerFrequency(void);
|
||||
void Sleep(uint32 ms);
|
||||
|
||||
inline uint64 getTimer(void)
|
||||
{
|
||||
uint64 t;
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, 0);
|
||||
t = tv.tv_sec;
|
||||
t = t * 1000000;
|
||||
t += tv.tv_usec;
|
||||
return t;
|
||||
}
|
||||
|
||||
inline uint64 getTimerFrequency(void)
|
||||
{
|
||||
uint64 f = 1000000;
|
||||
return f;
|
||||
}
|
||||
|
||||
inline void sleep(uint32 ms)
|
||||
{
|
||||
//usleep(static_cast<unsigned long>(ms * 1000));
|
||||
struct timeval sleep_tv;
|
||||
sleep_tv.tv_sec = ms / 1000;
|
||||
sleep_tv.tv_usec = (ms % 1000) * 1000;
|
||||
select(1, NULL, NULL, NULL, &sleep_tv);
|
||||
}
|
||||
|
||||
void strlwr(char * s);
|
||||
void strupr(char * s);
|
||||
|
||||
|
||||
class CTimer
|
||||
{
|
||||
public:
|
||||
CTimer();
|
||||
|
||||
void Set(uint32 seconds);
|
||||
void Signal();
|
||||
bool Expired();
|
||||
|
||||
private:
|
||||
uint32 mTimer;
|
||||
};
|
||||
|
||||
inline void CTimer::Set(uint32 interval)
|
||||
{
|
||||
mTimer = (uint32)time(0) + interval;
|
||||
}
|
||||
|
||||
inline void CTimer::Signal()
|
||||
{
|
||||
mTimer = 0;
|
||||
}
|
||||
|
||||
inline bool CTimer::Expired()
|
||||
{
|
||||
return (mTimer <= (uint32)time(0));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif BASE_SOLARIS_PLATFORM_H
|
||||
@@ -0,0 +1,282 @@
|
||||
////////////////////////////////////////
|
||||
// Thread.cpp
|
||||
//
|
||||
// Purpose:
|
||||
// 1. Implementation of the CThread class.
|
||||
//
|
||||
// Revisions:
|
||||
// 07/10/2001 Created
|
||||
//
|
||||
|
||||
#if defined(_REENTRANT)
|
||||
|
||||
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
#include "Thread.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
namespace Base
|
||||
{
|
||||
|
||||
void *threadProc(void *threadPtr)
|
||||
{
|
||||
CThread &thread = *((CThread*)threadPtr);
|
||||
thread.mThreadActive = true;
|
||||
thread.ThreadProc();
|
||||
thread.mThreadActive = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
CThread::CThread()
|
||||
{
|
||||
mThreadID = 0;
|
||||
mThreadActive = false;
|
||||
mThreadContinue = false;
|
||||
}
|
||||
|
||||
CThread::~CThread()
|
||||
{
|
||||
StopThread();
|
||||
}
|
||||
|
||||
void CThread::StartThread()
|
||||
{
|
||||
mThreadContinue = true;
|
||||
pthread_create(&mThreadID,0,threadProc,this);
|
||||
while (!IsThreadActive())
|
||||
Base::sleep(1);
|
||||
}
|
||||
|
||||
int32 CThread::StopThread(int timeout)
|
||||
{
|
||||
timeout += time(0);
|
||||
|
||||
mThreadContinue = false;
|
||||
while (mThreadActive && time(0)<timeout)
|
||||
sleep(1);
|
||||
if (mThreadActive)
|
||||
{
|
||||
mThreadActive = false;
|
||||
return eSTOP_TIMEOUT;
|
||||
}
|
||||
return eSTOP_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
CThreadPool::CMember::CMember(CThreadPool * parent) :
|
||||
mParent(parent),
|
||||
mFunction(NULL),
|
||||
mArgument(NULL),
|
||||
mSemaphore()
|
||||
{
|
||||
StartThread();
|
||||
}
|
||||
|
||||
CThreadPool::CMember::~CMember()
|
||||
{
|
||||
}
|
||||
|
||||
void CThreadPool::CMember::Destroy()
|
||||
{
|
||||
mThreadContinue = false;
|
||||
mSemaphore.Signal();
|
||||
}
|
||||
|
||||
bool CThreadPool::CMember::Execute(void( *function )( void * ), void * arg)
|
||||
{
|
||||
if (mFunction)
|
||||
return false;
|
||||
|
||||
mArgument = arg;
|
||||
mFunction = function;
|
||||
mSemaphore.Signal();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CThreadPool::CMember::ThreadProc()
|
||||
{
|
||||
while (mThreadContinue)
|
||||
{
|
||||
mParent->OnIdle(this);
|
||||
mSemaphore.Wait(mParent->GetTimeOut()*1000);
|
||||
mParent->OnBusy(this);
|
||||
|
||||
if (mFunction)
|
||||
{
|
||||
mFunction(mArgument);
|
||||
mArgument = NULL;
|
||||
mFunction = NULL;
|
||||
}
|
||||
else
|
||||
mThreadContinue = false;
|
||||
}
|
||||
|
||||
mParent->OnDestory(this);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
CThreadPool::CThreadPool(uint32 maxThreads, uint32 minThreads, uint32 timeout) :
|
||||
mMutex(),
|
||||
mIdleMember(),
|
||||
mBusyMember(),
|
||||
mNullMember(),
|
||||
mThreadCount(0),
|
||||
mMaxThreads(maxThreads),
|
||||
mMinThreads(minThreads),
|
||||
mTimeOut(timeout)
|
||||
{
|
||||
if (mMaxThreads == 0) mMaxThreads = 1;
|
||||
if (mMinThreads == 0) mMinThreads = 1;
|
||||
if (mMinThreads > mMaxThreads) mMinThreads = mMaxThreads;
|
||||
|
||||
if (mTimeOut < 60)
|
||||
mTimeOut = 60;
|
||||
|
||||
for (uint32 i=0; i<mMinThreads; i++)
|
||||
new CMember(this);
|
||||
}
|
||||
|
||||
CThreadPool::~CThreadPool()
|
||||
{
|
||||
set<CMember *>::iterator setIterator;
|
||||
|
||||
mMutex.Lock();
|
||||
setIterator = mBusyMember.begin();
|
||||
while (setIterator != mBusyMember.end())
|
||||
(*setIterator++)->Destroy();
|
||||
mMutex.Unlock();
|
||||
|
||||
while (mThreadCount)
|
||||
{
|
||||
mMutex.Lock();
|
||||
setIterator = mIdleMember.begin();
|
||||
while (setIterator != mIdleMember.end())
|
||||
(*setIterator++)->Destroy();
|
||||
mMutex.Unlock();
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
mMutex.Lock();
|
||||
while (!mNullMember.empty())
|
||||
{
|
||||
delete mNullMember.front();
|
||||
mNullMember.pop_front();
|
||||
}
|
||||
mMutex.Unlock();
|
||||
}
|
||||
|
||||
bool CThreadPool::Execute(void( *function )( void * ), void * arg, uint32 poolGrowthSize )
|
||||
{
|
||||
mMutex.Lock();
|
||||
|
||||
if (mIdleMember.empty())
|
||||
{
|
||||
if (mThreadCount < mMaxThreads)
|
||||
{
|
||||
if (!poolGrowthSize)
|
||||
poolGrowthSize = mMinThreads;
|
||||
|
||||
if ((mThreadCount + poolGrowthSize) > mMaxThreads)
|
||||
poolGrowthSize = mMaxThreads - mThreadCount;
|
||||
|
||||
for (uint32 i(0); i < poolGrowthSize; i++)
|
||||
new CMember(this);
|
||||
mMutex.Unlock();
|
||||
time_t idleTimeout = time(0) + 5;
|
||||
while(mIdleMember.empty())
|
||||
{
|
||||
if (time(0) >= idleTimeout)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Base::sleep(10);
|
||||
}
|
||||
mMutex.Lock();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
mMutex.Unlock();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
while (!mNullMember.empty())
|
||||
{
|
||||
delete mNullMember.front();
|
||||
mNullMember.pop_front();
|
||||
}
|
||||
|
||||
CMember * member = *(mIdleMember.begin());
|
||||
mIdleMember.erase(member);
|
||||
member->Execute(function,arg);
|
||||
|
||||
mMutex.Unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 CThreadPool::GetTimeOut()
|
||||
{
|
||||
return mTimeOut;
|
||||
}
|
||||
|
||||
void CThreadPool::OnIdle(CMember * member)
|
||||
{
|
||||
mMutex.Lock();
|
||||
|
||||
set<CMember *>::iterator setIterator = mBusyMember.find(member);
|
||||
if (setIterator != mBusyMember.end())
|
||||
mBusyMember.erase(member);
|
||||
else
|
||||
mThreadCount++;
|
||||
mIdleMember.insert(member);
|
||||
|
||||
mMutex.Unlock();
|
||||
}
|
||||
|
||||
void CThreadPool::OnBusy(CMember * member)
|
||||
{
|
||||
mMutex.Lock();
|
||||
|
||||
mBusyMember.insert(member);
|
||||
|
||||
mMutex.Unlock();
|
||||
}
|
||||
|
||||
void CThreadPool::OnDestory(CMember * member)
|
||||
{
|
||||
set<CMember *>::iterator setIterator;
|
||||
|
||||
mMutex.Lock();
|
||||
|
||||
setIterator = mBusyMember.find(member);
|
||||
if (setIterator != mBusyMember.end())
|
||||
{
|
||||
mNullMember.push_back(member);
|
||||
mBusyMember.erase(member);
|
||||
mThreadCount--;
|
||||
}
|
||||
|
||||
mMutex.Unlock();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // #if defined(_REENTRANT)
|
||||
@@ -0,0 +1,139 @@
|
||||
////////////////////////////////////////
|
||||
// Thread.h
|
||||
//
|
||||
// Purpose:
|
||||
// 1. Declair the CThread class that encapsulates threading functionality.
|
||||
// This abstract base class in intended to be used to encapsulate
|
||||
// individual tasks that require threading in derived classes.
|
||||
//
|
||||
// Revisions:
|
||||
// 07/10/2001 Created
|
||||
//
|
||||
|
||||
#ifndef BASE_SOLARIS_THREAD_H
|
||||
#define BASE_SOLARIS_THREAD_H
|
||||
|
||||
#if !defined(_REENTRANT)
|
||||
# pragma message( "Excluding Base::CThread - requires multi-threaded compile. (_REENTRANT)" )
|
||||
#else
|
||||
|
||||
|
||||
#pragma warning( disable : 4786)
|
||||
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include "Platform.h"
|
||||
#include "Mutex.h"
|
||||
#include "Event.h"
|
||||
|
||||
namespace Base
|
||||
{
|
||||
|
||||
////////////////////////////////////////
|
||||
// Class:
|
||||
// CThread
|
||||
//
|
||||
// Purpose:
|
||||
// Encapsulates threading functionality. Creating classes derived
|
||||
// from CThread provides an easy way to encapsulate tasks that require
|
||||
// their own thread.
|
||||
//
|
||||
// Public Methods:
|
||||
// StartThread() : Creates the low-level thread handle and begins executing
|
||||
// the CThread::ThreadProc() function within the new thread.
|
||||
// StopThread() : Signals the ThreadProc() function to stop executing using
|
||||
// the mThreadContinue member variable, and waits for the ThreadProc()
|
||||
// function to exit. By default, the function will block for a maximum
|
||||
// of 5 seconds before exiting without the thread halting.
|
||||
// IsThreadActive() : Returns true if the physical thread is still executing
|
||||
// within the ThreadProc() function, otherwise it returns false.
|
||||
// ThreadProc() : Pure-virtual function that will be executed when the StartThread()
|
||||
// function is called. Derived classes must implement this function. The
|
||||
// mThreadContinue member variable should be used internal the the ThreadProc()
|
||||
// function to indicate whether it should continue executing or exit.
|
||||
// Protected Attributes:
|
||||
// mThreadContinue : Boolean value indicating to the ThreadProc() function
|
||||
// whether to continue executing or to exit. If mThreadContinue is true,
|
||||
// ThreadProc() should continue, otherwise ThreadProc() should exit. It
|
||||
// left up to the derived class to implement a ThreadProc() function that
|
||||
// uses the mThreadContinue member.
|
||||
//
|
||||
//
|
||||
class CThread
|
||||
{
|
||||
friend void * threadProc(void *);
|
||||
|
||||
public:
|
||||
enum { eSTOP_SUCCESS, eSTOP_TIMEOUT };
|
||||
public:
|
||||
CThread();
|
||||
virtual ~CThread();
|
||||
|
||||
void StartThread();
|
||||
int32 StopThread(int timeout=5);
|
||||
bool IsThreadActive() { return mThreadActive; }
|
||||
|
||||
protected:
|
||||
virtual void ThreadProc() = 0;
|
||||
|
||||
protected:
|
||||
bool mThreadContinue;
|
||||
private:
|
||||
bool mThreadActive;
|
||||
pthread_t mThreadID;
|
||||
};
|
||||
|
||||
|
||||
class CThreadPool
|
||||
{
|
||||
private:
|
||||
class CMember : public CThread
|
||||
{
|
||||
public:
|
||||
CMember(CThreadPool * parent);
|
||||
virtual ~CMember();
|
||||
|
||||
bool Execute(void( *function )( void * ), void * arg);
|
||||
void Destroy();
|
||||
|
||||
protected:
|
||||
virtual void ThreadProc();
|
||||
|
||||
private:
|
||||
CThreadPool * mParent;
|
||||
void( * mFunction )( void * );
|
||||
void * mArgument;
|
||||
CEvent mSemaphore;
|
||||
};
|
||||
friend class CMember;
|
||||
|
||||
public:
|
||||
CThreadPool(uint32 maxThreads, uint32 minThreads=1, uint32 timeout=15*60);
|
||||
virtual ~CThreadPool();
|
||||
|
||||
virtual bool Execute(void( *function )( void * ), void * arg, uint32 poolGrowthSize = 0);
|
||||
|
||||
private:
|
||||
uint32 GetTimeOut();
|
||||
void OnIdle(CMember * member);
|
||||
void OnBusy(CMember * member);
|
||||
void OnDestory(CMember * member);
|
||||
|
||||
private:
|
||||
CMutex mMutex;
|
||||
std::set<CMember *> mIdleMember;
|
||||
std::set<CMember *> mBusyMember;
|
||||
std::list<CMember *> mNullMember;
|
||||
uint32 mThreadCount;
|
||||
|
||||
uint32 mMaxThreads;
|
||||
uint32 mMinThreads;
|
||||
uint32 mTimeOut;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif // #if defined(_MT)
|
||||
|
||||
#endif // BASE_SOLARIS_THREAD_H
|
||||
@@ -0,0 +1,28 @@
|
||||
////////////////////////////////////////
|
||||
// Types.h
|
||||
//
|
||||
// Purpose:
|
||||
// 1. Define integer types that are unambiguous with respect to size
|
||||
//
|
||||
// Revisions:
|
||||
// 07/10/2001 Created
|
||||
//
|
||||
|
||||
#ifndef BASE_SOLARIS_TYPES_H
|
||||
#define BASE_SOLARIS_TYPES_H
|
||||
|
||||
namespace Base
|
||||
{
|
||||
typedef signed char int8;
|
||||
typedef unsigned char uint8;
|
||||
typedef signed short int16;
|
||||
typedef unsigned short uint16;
|
||||
|
||||
typedef signed int int32;
|
||||
typedef unsigned int uint32;
|
||||
typedef signed long long int64;
|
||||
typedef unsigned long long uint64;
|
||||
}
|
||||
|
||||
#endif // BASE_SOLARIS_TYPES_H
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef BASE_WIN32_ARCHIVE_H
|
||||
#define BASE_WIN32_ARCHIVE_H
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
|
||||
#ifdef PACK_BIG_ENDIAN
|
||||
|
||||
inline double byteSwap(double value) { byteReverse64(&value); return value; }
|
||||
inline float byteSwap(float value) { byteReverse32(&value); return value; }
|
||||
inline uint64 byteSwap(uint64 value) { byteReverse64(&value); return value; }
|
||||
inline int64 byteSwap(int64 value) { byteReverse64(&value); return value; }
|
||||
inline uint32 byteSwap(uint32 value) { byteReverse32(&value); return value; }
|
||||
inline int32 byteSwap(int32 value) { byteReverse32(&value); return value; }
|
||||
inline uint16 byteSwap(uint16 value) { byteReverse16(&value); return value; }
|
||||
inline int16 byteSwap(int16 value) { byteReverse16(&value); return value; }
|
||||
|
||||
#else
|
||||
|
||||
inline double byteSwap(double value) { return value; }
|
||||
inline float byteSwap(float value) { return value; }
|
||||
inline uint64 byteSwap(uint64 value) { return value; }
|
||||
inline int64 byteSwap(int64 value) { return value; }
|
||||
inline uint32 byteSwap(uint32 value) { return value; }
|
||||
inline int32 byteSwap(int32 value) { return value; }
|
||||
inline uint16 byteSwap(uint16 value) { return value; }
|
||||
inline int16 byteSwap(int16 value) { return value; }
|
||||
|
||||
#endif
|
||||
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
#include "../BlockAllocator.h"
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
BlockAllocator::BlockAllocator()
|
||||
{
|
||||
for(unsigned i = 0; i < 31; i++)
|
||||
{
|
||||
m_blocks[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
BlockAllocator::~BlockAllocator()
|
||||
{
|
||||
// free all allocated memory blocks
|
||||
for(unsigned i = 0; i < 31; i++)
|
||||
{
|
||||
while(m_blocks[i] != NULL)
|
||||
{
|
||||
unsigned *tmp = m_blocks[i];
|
||||
m_blocks[i] = (unsigned *)*m_blocks[i];
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate a block that is the next power of two greater than the # of bytes passed.
|
||||
// 33 bytes yields a 64 byte block of memory and so forth.
|
||||
|
||||
void *BlockAllocator::getBlock(unsigned bytes)
|
||||
{
|
||||
unsigned accum = 16, bits = 16;
|
||||
unsigned *handle = NULL;
|
||||
|
||||
// Perform a binary search looking for the highest bit.
|
||||
|
||||
while(bits != 0)
|
||||
{
|
||||
// If bytes is less than the bit we're testing for, subtract half
|
||||
// from the bit value and repeat
|
||||
if(bytes < (unsigned)(1 << accum))
|
||||
{
|
||||
bits /= 2;
|
||||
accum -= bits;
|
||||
}
|
||||
// If bytes is greater than the bit we're testing for, add half
|
||||
// from the but value and repeat
|
||||
else if(bytes > (unsigned)(1 << accum))
|
||||
{
|
||||
bits /= 2;
|
||||
accum += bits;
|
||||
}
|
||||
// Got lucky and hit the value dead on
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
// At this point accum contains the most significant bit index, increment
|
||||
accum++;
|
||||
if(accum < 2)
|
||||
{
|
||||
accum = 2;
|
||||
}
|
||||
|
||||
// Note that when memory is actually allocated, 8 extra bytes will be allocated.at the front
|
||||
// The first integer is the address of the next block of memory when the block is in the allocator
|
||||
// The second integer is the bit length of the block
|
||||
// Memory is allocated on 4 byte boundaries to sidestep byte alignment problems
|
||||
|
||||
|
||||
// Check if the allocator already has a block of that size
|
||||
if(m_blocks[accum] == 0)
|
||||
{
|
||||
// remove the pre allocated block from the linked list
|
||||
handle = (unsigned *)calloc(((1 << accum) / 4) + 2, sizeof(unsigned));
|
||||
handle[1] = accum;
|
||||
handle[0] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Allocate a new block
|
||||
handle = m_blocks[accum];
|
||||
m_blocks[accum] = (unsigned *)handle[0];
|
||||
handle[0] = 0;
|
||||
}
|
||||
// return a pointer that skips over the header used for the allocator's purposes
|
||||
return(handle + 2);
|
||||
}
|
||||
|
||||
void BlockAllocator::returnBlock(unsigned *handle)
|
||||
{
|
||||
// C++ allows for safe deletion of a NULL pointer
|
||||
if(handle)
|
||||
{
|
||||
// Update the allocator linked list, insert this entry at the head
|
||||
*(handle - 2) = (unsigned)m_blocks[*(handle - 1)];
|
||||
// Add this entry to the proper linked list node
|
||||
m_blocks[*(handle - 1)] = (handle - 2);
|
||||
}
|
||||
}
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
////////////////////////////////////////
|
||||
// Event.cpp
|
||||
//
|
||||
// Purpose:
|
||||
// 1. Implementation of the CEvent class.
|
||||
//
|
||||
// Revisions:
|
||||
// 07/10/2001 Created
|
||||
//
|
||||
|
||||
|
||||
#if !defined(_MT)
|
||||
# pragma message( "Excluding Base::CEvent - requires multi-threaded compile. (_MT)" )
|
||||
#else
|
||||
|
||||
#include "Event.h"
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
CEvent::CEvent()
|
||||
{
|
||||
mEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
|
||||
}
|
||||
|
||||
CEvent::~CEvent()
|
||||
{
|
||||
if (mEvent)
|
||||
CloseHandle(mEvent);
|
||||
}
|
||||
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
#endif // #if defined(_MT)
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
////////////////////////////////////////
|
||||
// Event.h
|
||||
//
|
||||
// Purpose:
|
||||
// 1. Declair the CEvent class that encapsulates the functionality of a
|
||||
// single-locking semaphore.
|
||||
//
|
||||
// Revisions:
|
||||
// 07/10/2001 Created
|
||||
//
|
||||
|
||||
#ifndef BASE_WIN32_EVENT_H
|
||||
#define BASE_WIN32_EVENT_H
|
||||
|
||||
#if defined(_MT)
|
||||
|
||||
|
||||
|
||||
#include "Platform.h"
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
////////////////////////////////////////
|
||||
// Class:
|
||||
// CEvent
|
||||
//
|
||||
// Purpose:
|
||||
// Encapsulates the functionality of a singal-locking semaphore.
|
||||
// This class is valuable for thread syncronization when a thead's
|
||||
// execution needs to be dependent upon another thread.
|
||||
//
|
||||
// Public Methods:
|
||||
// Signal() : Signals a thread that has called Wait() so that it can
|
||||
// continue execution. This function returns true if the waiting
|
||||
// thread was signalled successfully, otherwise false is returned.
|
||||
// Wait() : Halts the calling thread's execution indefinately until
|
||||
// a Singal() call is made by an external thread. If the thread is
|
||||
// successfully signalled, the function returns eWAIT_SIGNAL. If
|
||||
// timeout period expires without a signal, eWAIT_TIMEOUT is returned.
|
||||
// If the function fails, eWAIT_ERROR is returned.
|
||||
//
|
||||
class CEvent
|
||||
{
|
||||
public:
|
||||
CEvent();
|
||||
virtual ~CEvent();
|
||||
|
||||
bool Signal();
|
||||
int32 Wait(uint32 timeout = 0);
|
||||
|
||||
public:
|
||||
enum { eWAIT_ERROR, eWAIT_SIGNAL, eWAIT_TIMEOUT };
|
||||
private:
|
||||
HANDLE mEvent;
|
||||
};
|
||||
|
||||
inline bool CEvent::Signal()
|
||||
{
|
||||
if (mEvent)
|
||||
return SetEvent(mEvent)!=0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline int32 CEvent::Wait(uint32 timeout)
|
||||
{
|
||||
if (!mEvent)
|
||||
return CEvent::eWAIT_ERROR;
|
||||
|
||||
DWORD result = WaitForSingleObjectEx(mEvent, timeout ? timeout : INFINITE, FALSE);
|
||||
if (result == WAIT_OBJECT_0)
|
||||
return CEvent::eWAIT_SIGNAL;
|
||||
else if (result == WAIT_TIMEOUT)
|
||||
return CEvent::eWAIT_TIMEOUT;
|
||||
else
|
||||
return CEvent::eWAIT_ERROR;
|
||||
}
|
||||
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
#endif // #if defined(_MT)
|
||||
|
||||
|
||||
#endif // BASE_WIN32_EVENT_H
|
||||
@@ -0,0 +1,22 @@
|
||||
// File.cpp: implementation of the CFile class.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "File.h"
|
||||
|
||||
namespace Base
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Construction/Destruction
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
CFile::CFile(const char *)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CFile::~CFile()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// File.h: interface for the CFile class.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef BASE_FILE_H
|
||||
#define BASE_FILE_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
namespace Base
|
||||
{
|
||||
|
||||
class CFile
|
||||
{
|
||||
public:
|
||||
CFile(const char *);
|
||||
virtual ~CFile();
|
||||
|
||||
bool IsOpen();
|
||||
|
||||
private:
|
||||
FILE* mFileHandle;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // BASE_FILE_H
|
||||
@@ -0,0 +1,40 @@
|
||||
////////////////////////////////////////
|
||||
// Mutex.cpp
|
||||
//
|
||||
// Purpose:
|
||||
// 1. Implementation of the CMutex class.
|
||||
//
|
||||
// Revisions:
|
||||
// 07/10/2001 Created
|
||||
//
|
||||
#if !defined(_MT)
|
||||
# pragma message( "Excluding Base::CMutex - requires multi-threaded compile. (_MT)" )
|
||||
#else
|
||||
|
||||
#include "Mutex.h"
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
CMutex::CMutex()
|
||||
{
|
||||
InitializeCriticalSection(&mCriticalSection);
|
||||
}
|
||||
|
||||
CMutex::~CMutex()
|
||||
{
|
||||
DeleteCriticalSection(&mCriticalSection);
|
||||
}
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
#endif // #if defined(_MT)
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
////////////////////////////////////////
|
||||
// Mutex.h
|
||||
//
|
||||
// Purpose:
|
||||
// 1. Declair the CMutex class that encapsulates the functionality of a
|
||||
// mutually-exclusive device.
|
||||
//
|
||||
// Revisions:
|
||||
// 07/10/2001 Created
|
||||
//
|
||||
|
||||
#ifndef BASE_WIN32_MUTEX_H
|
||||
#define BASE_WIN32_MUTEX_H
|
||||
|
||||
#if defined (_MT)
|
||||
|
||||
#include "Platform.h"
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
////////////////////////////////////////
|
||||
// Class:
|
||||
// CMutex
|
||||
//
|
||||
// Purpose:
|
||||
// Encapsulates the functionality of a mutually-exclusive device.
|
||||
// This class is valuable for protecting against race conditions
|
||||
// within threaded applications. The CMutex class can be used to
|
||||
// only allow a single thread to run within a specified code
|
||||
// segment at a time.
|
||||
//
|
||||
// Public Methods:
|
||||
// Lock() : Locks the mutex. If the mutex is already locked, the
|
||||
// operating system will block the calling thread until another
|
||||
// thread has unlocked the mutex.
|
||||
// Unlock() : Unlocks the mutex.
|
||||
//
|
||||
class CMutex
|
||||
{
|
||||
public:
|
||||
CMutex();
|
||||
~CMutex();
|
||||
|
||||
void Lock();
|
||||
void Unlock();
|
||||
private:
|
||||
CRITICAL_SECTION mCriticalSection;
|
||||
};
|
||||
|
||||
inline void CMutex::Lock()
|
||||
{
|
||||
EnterCriticalSection(&mCriticalSection);
|
||||
}
|
||||
|
||||
inline void CMutex::Unlock()
|
||||
{
|
||||
LeaveCriticalSection(&mCriticalSection);
|
||||
}
|
||||
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // #if defined(_MT)
|
||||
|
||||
#endif // BASE_WIN32_MUTEX_H
|
||||
@@ -0,0 +1,31 @@
|
||||
////////////////////////////////////////
|
||||
// Platform.cpp
|
||||
//
|
||||
// Purpose:
|
||||
// 1. Implementation of the global functionality declaired in Platform.h.
|
||||
//
|
||||
// Revisions:
|
||||
// 07/10/2001 Created
|
||||
//
|
||||
|
||||
#include "Platform.h"
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
|
||||
CTimer::CTimer() :
|
||||
mTimer(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,98 @@
|
||||
////////////////////////////////////////
|
||||
// Platform.h
|
||||
//
|
||||
// Purpose:
|
||||
// 1. Include relevent system headers that are platform specific.
|
||||
// 2. Declair global platform specific functionality.
|
||||
// 3. Include primative type definitions
|
||||
//
|
||||
// Global Functions:
|
||||
// getTimer() : Return the current high resolution clock count.
|
||||
// getTimerFrequency() : Return the frequency of the high resolution clock.
|
||||
// sleep() : Voluntarily relinquish timeslice of the calling thread for a
|
||||
// specified number of milliseconds.
|
||||
//
|
||||
// Revisions:
|
||||
// 07/10/2001 Created
|
||||
//
|
||||
|
||||
#ifndef BASE_WIN32_PLATFORM_H
|
||||
#define BASE_WIN32_PLATFORM_H
|
||||
|
||||
#include <memory.h>
|
||||
#include <winsock2.h>
|
||||
#include <time.h>
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
#include <direct.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include "Types.h"
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
uint64 getTimer(void);
|
||||
uint64 getTimerFrequency(void);
|
||||
|
||||
inline uint64 getTimer(void)
|
||||
{
|
||||
uint64 result;
|
||||
if (!QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER *>(&result)))
|
||||
result = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline uint64 getTimerFrequency(void)
|
||||
{
|
||||
uint64 result;
|
||||
if (!QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER *>(&result)))
|
||||
result = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void sleep(uint32 ms)
|
||||
{
|
||||
Sleep(ms);
|
||||
}
|
||||
|
||||
|
||||
class CTimer
|
||||
{
|
||||
public:
|
||||
CTimer();
|
||||
|
||||
void Set(uint32 seconds);
|
||||
void Signal();
|
||||
bool Expired();
|
||||
|
||||
private:
|
||||
uint32 mTimer;
|
||||
};
|
||||
|
||||
inline void CTimer::Set(uint32 interval)
|
||||
{
|
||||
mTimer = (uint32)time(0) + interval;
|
||||
}
|
||||
|
||||
inline void CTimer::Signal()
|
||||
{
|
||||
mTimer = 0;
|
||||
}
|
||||
|
||||
inline bool CTimer::Expired()
|
||||
{
|
||||
return (mTimer <= (uint32)time(0));
|
||||
}
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
#endif BASE_WIN32_PLATFORM_H
|
||||
@@ -0,0 +1,315 @@
|
||||
////////////////////////////////////////
|
||||
// Thread.cpp
|
||||
//
|
||||
// Purpose:
|
||||
// 1. Implementation of the CThread class.
|
||||
//
|
||||
// Revisions:
|
||||
// 07/10/2001 Created
|
||||
//
|
||||
|
||||
|
||||
#pragma warning ( disable: 4786 )
|
||||
|
||||
|
||||
#if !defined(_MT)
|
||||
# pragma message( "Excluding Base::CThread - requires multi-threaded compile. (_MT)" )
|
||||
#else
|
||||
|
||||
#include <process.h>
|
||||
#include <time.h>
|
||||
#include "Thread.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
void threadProc(void *threadPtr)
|
||||
{
|
||||
CThread &thread = *((CThread*)threadPtr);
|
||||
thread.mThreadActive = true;
|
||||
thread.ThreadProc();
|
||||
thread.mThreadActive = false;
|
||||
}
|
||||
|
||||
CThread::CThread()
|
||||
{
|
||||
mThreadID = 0;
|
||||
mThreadActive = false;
|
||||
mThreadContinue = false;
|
||||
}
|
||||
|
||||
CThread::~CThread()
|
||||
{
|
||||
StopThread();
|
||||
}
|
||||
|
||||
void CThread::StartThread()
|
||||
{
|
||||
mThreadContinue = true;
|
||||
mThreadID = _beginthread(threadProc,0,this);
|
||||
while (!IsThreadActive())
|
||||
Base::sleep(1);
|
||||
}
|
||||
|
||||
int32 CThread::StopThread(int32 timeout)
|
||||
{
|
||||
timeout += time(0);
|
||||
|
||||
mThreadContinue = false;
|
||||
while (mThreadActive && time(0)<timeout)
|
||||
sleep(1);
|
||||
if (mThreadActive)
|
||||
{
|
||||
mThreadActive = false;
|
||||
return eSTOP_TIMEOUT;
|
||||
}
|
||||
return eSTOP_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
CThreadPool::CMember::CMember(CThreadPool * parent) :
|
||||
mParent(parent),
|
||||
mFunction(NULL),
|
||||
mArgument(NULL),
|
||||
mSemaphore()
|
||||
{
|
||||
StartThread();
|
||||
}
|
||||
|
||||
CThreadPool::CMember::~CMember()
|
||||
{
|
||||
}
|
||||
|
||||
void CThreadPool::CMember::Destroy()
|
||||
{
|
||||
mThreadContinue = false;
|
||||
mSemaphore.Signal();
|
||||
}
|
||||
|
||||
bool CThreadPool::CMember::Execute(void( __cdecl *function )( void * ), void * arg)
|
||||
{
|
||||
if (mFunction)
|
||||
return false;
|
||||
|
||||
mArgument = arg;
|
||||
mFunction = function;
|
||||
mSemaphore.Signal();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CThreadPool::CMember::ThreadProc()
|
||||
{
|
||||
mParent->OnStartup(this);
|
||||
while (mThreadContinue)
|
||||
{
|
||||
mParent->OnIdle(this);
|
||||
mSemaphore.Wait(mParent->GetTimeOut()*1000);
|
||||
|
||||
if (mFunction)
|
||||
{
|
||||
mFunction(mArgument);
|
||||
mArgument = NULL;
|
||||
mFunction = NULL;
|
||||
}
|
||||
else if (mParent->OnDestory(this))
|
||||
mThreadContinue = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
CThreadPool::CThreadPool(uint32 maxThreads, uint32 minThreads, uint32 timeout) :
|
||||
mMutex(),
|
||||
mIdleMember(),
|
||||
mBusyMember(),
|
||||
mNullMember(),
|
||||
mThreadCount(0),
|
||||
mMaxThreads(maxThreads),
|
||||
mMinThreads(minThreads),
|
||||
mTimeOut(timeout)
|
||||
{
|
||||
if (mMaxThreads == 0) mMaxThreads = 1;
|
||||
if (mMinThreads == 0) mMinThreads = 1;
|
||||
if (mMinThreads > mMaxThreads) mMinThreads = mMaxThreads;
|
||||
|
||||
for (uint32 i=0; i<mMinThreads; i++)
|
||||
new CMember(this);
|
||||
}
|
||||
|
||||
CThreadPool::~CThreadPool()
|
||||
{
|
||||
set<CMember *>::iterator setIterator;
|
||||
|
||||
////////////////////////////////////////
|
||||
// (1) Destory all busy member threads
|
||||
mMutex.Lock();
|
||||
setIterator = mBusyMember.begin();
|
||||
while (setIterator != mBusyMember.end())
|
||||
(*setIterator++)->Destroy();
|
||||
mMutex.Unlock();
|
||||
|
||||
////////////////////////////////////////
|
||||
// (2) Destory all idle member threads
|
||||
while (mThreadCount)
|
||||
{
|
||||
mMutex.Lock();
|
||||
setIterator = mIdleMember.begin();
|
||||
while (setIterator != mIdleMember.end())
|
||||
(*setIterator++)->Destroy();
|
||||
mMutex.Unlock();
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
// (3) Delete the null member threads
|
||||
mMutex.Lock();
|
||||
while (!mNullMember.empty())
|
||||
{
|
||||
delete mNullMember.front();
|
||||
mNullMember.pop_front();
|
||||
}
|
||||
mMutex.Unlock();
|
||||
}
|
||||
|
||||
bool CThreadPool::Execute(void( __cdecl *function )( void * ), void * arg, uint32 poolGrowthSize )
|
||||
{
|
||||
mMutex.Lock();
|
||||
|
||||
////////////////////////////////////////
|
||||
// (1) If no idle members, return false to indicate that no threads
|
||||
// were available. If the thread count is below the max, create
|
||||
// a new thread.
|
||||
if (mIdleMember.empty())
|
||||
{
|
||||
if (mThreadCount < mMaxThreads)
|
||||
{
|
||||
if (!poolGrowthSize)
|
||||
poolGrowthSize = mMinThreads;
|
||||
|
||||
if ((mThreadCount + poolGrowthSize) > mMaxThreads)
|
||||
poolGrowthSize = mMaxThreads - mThreadCount;
|
||||
|
||||
for (uint32 i(0); i < poolGrowthSize; i++)
|
||||
new CMember(this);
|
||||
mMutex.Unlock();
|
||||
time_t idleTimeout = time(0) + 5;
|
||||
while(mIdleMember.empty())
|
||||
{
|
||||
if (time(0) >= idleTimeout)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Base::sleep(10);
|
||||
}
|
||||
mMutex.Lock();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
mMutex.Unlock();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
// (2) Delete any null member threads.
|
||||
while (!mNullMember.empty())
|
||||
{
|
||||
delete mNullMember.front();
|
||||
mNullMember.pop_front();
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
// (3) Move the first idle thread to the busy set and signal the
|
||||
// thread to execute the specified function.
|
||||
CMember * member = *(mIdleMember.begin());
|
||||
mIdleMember.erase(member);
|
||||
mBusyMember.insert(member);
|
||||
member->Execute(function,arg);
|
||||
|
||||
mMutex.Unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 CThreadPool::GetTimeOut()
|
||||
{
|
||||
return mTimeOut;
|
||||
}
|
||||
|
||||
void CThreadPool::OnStartup(CMember * member)
|
||||
{
|
||||
mMutex.Lock();
|
||||
|
||||
mThreadCount++;
|
||||
mIdleMember.insert(member);
|
||||
|
||||
mMutex.Unlock();
|
||||
}
|
||||
|
||||
void CThreadPool::OnIdle(CMember * member)
|
||||
{
|
||||
mMutex.Lock();
|
||||
|
||||
mBusyMember.erase(member);
|
||||
mIdleMember.insert(member);
|
||||
|
||||
mMutex.Unlock();
|
||||
}
|
||||
|
||||
bool CThreadPool::OnDestory(CMember * member)
|
||||
{
|
||||
set<CMember *>::iterator setIterator;
|
||||
|
||||
mMutex.Lock();
|
||||
|
||||
bool result = (setIterator = mIdleMember.find(member)) != mIdleMember.end();
|
||||
if (result)
|
||||
{
|
||||
mNullMember.push_back(member);
|
||||
mIdleMember.erase(setIterator);
|
||||
mThreadCount--;
|
||||
}
|
||||
|
||||
mMutex.Unlock();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
SwitchableScopeLock::SwitchableScopeLock(CMutex & mutex, bool isOn) :
|
||||
mMutex(mutex),
|
||||
mIsOn(isOn)
|
||||
{
|
||||
if(mIsOn) { mMutex.Lock(); }
|
||||
}
|
||||
|
||||
SwitchableScopeLock::~SwitchableScopeLock()
|
||||
{
|
||||
if(mIsOn) { mMutex.Unlock(); }
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
#endif // #if defined(_MT)
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
////////////////////////////////////////
|
||||
// Thread.h
|
||||
//
|
||||
// Purpose:
|
||||
// 1. Declair the CThread class that encapsulates threading functionality.
|
||||
// This abstract base class in intended to be used to encapsulate
|
||||
// individual tasks that require threading in derived classes.
|
||||
//
|
||||
// Revisions:
|
||||
// 07/10/2001 Created
|
||||
//
|
||||
|
||||
#ifndef BASE_WIN32_THREAD_H
|
||||
#define BASE_WIN32_THREAD_H
|
||||
|
||||
#if defined(_MT)
|
||||
|
||||
#pragma warning( disable : 4786)
|
||||
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include "Platform.h"
|
||||
#include "Mutex.h"
|
||||
#include "Event.h"
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
////////////////////////////////////////
|
||||
// Class:
|
||||
// CThread
|
||||
//
|
||||
// Purpose:
|
||||
// Encapsulates threading functionality. Creating classes derived
|
||||
// from CThread provides an easy way to encapsulate tasks that require
|
||||
// their own thread.
|
||||
//
|
||||
// Public Methods:
|
||||
// StartThread() : Creates the low-level thread handle and begins executing
|
||||
// the CThread::ThreadProc() function within the new thread.
|
||||
// StopThread() : Signals the ThreadProc() function to stop executing using
|
||||
// the mThreadContinue member variable, and waits for the ThreadProc()
|
||||
// function to exit. By default, the function will block for a maximum
|
||||
// of 5 seconds before exiting without the thread halting.
|
||||
// IsThreadActive() : Returns true if the physical thread is still executing
|
||||
// within the ThreadProc() function, otherwise it returns false.
|
||||
// ThreadProc() : Pure-virtual function that will be executed when the StartThread()
|
||||
// function is called. Derived classes must implement this function. The
|
||||
// mThreadContinue member variable should be used internal the the ThreadProc()
|
||||
// function to indicate whether it should continue executing or exit.
|
||||
// Protected Attributes:
|
||||
// mThreadContinue : Boolean value indicating to the ThreadProc() function
|
||||
// whether to continue executing or to exit. If mThreadContinue is true,
|
||||
// ThreadProc() should continue, otherwise ThreadProc() should exit. It
|
||||
// left up to the derived class to implement a ThreadProc() function that
|
||||
// uses the mThreadContinue member.
|
||||
//
|
||||
//
|
||||
class CThread
|
||||
{
|
||||
friend void threadProc(void *);
|
||||
|
||||
public:
|
||||
enum { eSTOP_SUCCESS, eSTOP_TIMEOUT };
|
||||
public:
|
||||
CThread();
|
||||
virtual ~CThread();
|
||||
|
||||
void StartThread();
|
||||
int32 StopThread(int32 timeout=5);
|
||||
bool IsThreadActive() { return mThreadActive; }
|
||||
|
||||
protected:
|
||||
virtual void ThreadProc() = 0;
|
||||
|
||||
protected:
|
||||
bool mThreadContinue;
|
||||
private:
|
||||
uint32 mThreadID;
|
||||
bool mThreadActive;
|
||||
};
|
||||
|
||||
|
||||
class CThreadPool
|
||||
{
|
||||
private:
|
||||
class CMember : public CThread
|
||||
{
|
||||
public:
|
||||
CMember(CThreadPool * parent);
|
||||
virtual ~CMember();
|
||||
|
||||
bool Execute(void( __cdecl *function )( void * ), void * arg);
|
||||
void Destroy();
|
||||
|
||||
protected:
|
||||
virtual void ThreadProc();
|
||||
|
||||
private:
|
||||
CThreadPool * mParent;
|
||||
void( __cdecl * mFunction )( void * );
|
||||
void * mArgument;
|
||||
CEvent mSemaphore;
|
||||
};
|
||||
friend class CMember;
|
||||
|
||||
public:
|
||||
CThreadPool(uint32 maxThreads, uint32 minThreads=1, uint32 timeout=15*60);
|
||||
~CThreadPool();
|
||||
|
||||
virtual bool Execute(void( __cdecl *function )( void * ), void * arg, uint32 poolGrowthSize = 0);
|
||||
|
||||
private:
|
||||
uint32 GetTimeOut();
|
||||
void OnStartup(CMember * member);
|
||||
void OnIdle(CMember * member);
|
||||
bool OnDestory(CMember * member);
|
||||
|
||||
private:
|
||||
CMutex mMutex;
|
||||
std::set<CMember *> mIdleMember;
|
||||
std::set<CMember *> mBusyMember;
|
||||
std::list<CMember *> mNullMember;
|
||||
uint32 mThreadCount;
|
||||
|
||||
uint32 mMaxThreads;
|
||||
uint32 mMinThreads;
|
||||
uint32 mTimeOut;
|
||||
};
|
||||
|
||||
class SwitchableScopeLock
|
||||
{
|
||||
public:
|
||||
SwitchableScopeLock(CMutex & mutex, bool isOn);
|
||||
~SwitchableScopeLock();
|
||||
private:
|
||||
CMutex & mMutex;
|
||||
bool mIsOn;
|
||||
};
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // #if defined(_MT)
|
||||
|
||||
#endif // BASE_WIN32_THREAD_H
|
||||
@@ -0,0 +1,42 @@
|
||||
////////////////////////////////////////
|
||||
// Types.h
|
||||
//
|
||||
// Purpose:
|
||||
// 1. Define integer types that are unambiguous with respect to size
|
||||
//
|
||||
// Revisions:
|
||||
// 07/10/2001 Created
|
||||
//
|
||||
|
||||
#ifndef BASE_WIN32_TYPES_H
|
||||
#define BASE_WIN32_TYPES_H
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace Base
|
||||
{
|
||||
|
||||
#define INT32_MAX 0x7FFFFFFF
|
||||
#define INT32_MIN 0x80000000
|
||||
#define UINT32_MAX 0xFFFFFFFF
|
||||
|
||||
typedef signed char int8;
|
||||
typedef unsigned char uint8;
|
||||
typedef short int16;
|
||||
typedef unsigned short uint16;
|
||||
|
||||
typedef int int32;
|
||||
typedef unsigned uint32;
|
||||
typedef __int64 int64;
|
||||
typedef unsigned __int64 uint64;
|
||||
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // BASE_WIN32_TYPES_H
|
||||
|
||||
+256
@@ -0,0 +1,256 @@
|
||||
#include "GenericApiCore.h"
|
||||
#include "GenericConnection.h"
|
||||
#include "GenericMessage.h"
|
||||
|
||||
|
||||
#ifdef USE_SERIALIZE_LIB
|
||||
#include <Base/serialize.h>
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace GenericAPI
|
||||
{
|
||||
|
||||
|
||||
GenericAPICore::GenericAPICore(const char *host,
|
||||
short port,
|
||||
unsigned reqTimeout,
|
||||
unsigned reconnectTimeout,
|
||||
unsigned noDataTimeoutSecs,
|
||||
unsigned noAckTimeoutSecs,
|
||||
unsigned incomingBufSizeInKB,
|
||||
unsigned outgoingBufSizeInKB,
|
||||
unsigned keepAlive,
|
||||
unsigned maxRecvMessageSizeInKB)
|
||||
: m_currTrack(0),
|
||||
m_reconnectTimeout(0),
|
||||
m_outCount(0),
|
||||
m_pendingCount(0),
|
||||
m_requestTimeout(reqTimeout),
|
||||
m_suspended(false),
|
||||
m_nextConnectionIndex(0)
|
||||
{
|
||||
m_serverConnections.push_back(new GenericConnection(host, port, this, reconnectTimeout, noDataTimeoutSecs, noAckTimeoutSecs, incomingBufSizeInKB, outgoingBufSizeInKB, keepAlive, maxRecvMessageSizeInKB));
|
||||
}
|
||||
|
||||
GenericAPICore::GenericAPICore(const char *hosts[],
|
||||
const short port[],
|
||||
unsigned arraySize,
|
||||
unsigned reqTimeout,
|
||||
unsigned reconnectTimeout,
|
||||
unsigned noDataTimeoutSecs,
|
||||
unsigned noAckTimeoutSecs,
|
||||
unsigned incomingBufSizeInKB,
|
||||
unsigned outgoingBufSizeInKB,
|
||||
unsigned keepAlive,
|
||||
unsigned maxRecvMessageSizeInKB)
|
||||
: m_currTrack(0),
|
||||
m_reconnectTimeout(0),
|
||||
m_outCount(0),
|
||||
m_pendingCount(0),
|
||||
m_requestTimeout(reqTimeout),
|
||||
m_suspended(false),
|
||||
m_nextConnectionIndex(0)
|
||||
{
|
||||
for (unsigned i=0; i<arraySize; i++)
|
||||
{
|
||||
m_serverConnections.push_back(new GenericConnection(hosts[i], port[i], this, reconnectTimeout, noDataTimeoutSecs, noAckTimeoutSecs, incomingBufSizeInKB, outgoingBufSizeInKB, keepAlive, maxRecvMessageSizeInKB));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
GenericAPICore::~GenericAPICore()
|
||||
{
|
||||
for (std::vector<GenericConnection *>::iterator conIter = m_serverConnections.begin(); conIter != m_serverConnections.end(); conIter++)
|
||||
{
|
||||
GenericConnection *con = *conIter;
|
||||
delete con;
|
||||
}
|
||||
m_serverConnections.clear();
|
||||
|
||||
for(map<unsigned, GenericResponse *>::iterator iter = m_pending.begin(); iter != m_pending.end(); ++iter)
|
||||
{
|
||||
delete (*iter).second;
|
||||
}
|
||||
|
||||
m_pending.empty();
|
||||
|
||||
while(m_outCount > 0)
|
||||
{
|
||||
delete m_outboundQueue.front().second;
|
||||
delete m_outboundQueue.front().first;
|
||||
m_outboundQueue.pop();
|
||||
--m_outCount;
|
||||
}
|
||||
}
|
||||
|
||||
void GenericAPICore::changeHostPort(unsigned connectionIndex, const char *host, short port)
|
||||
{
|
||||
if (connectionIndex <= m_serverConnections.size() - 1)
|
||||
{
|
||||
m_serverConnections[connectionIndex]->changeHostPort(host, port);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned GenericAPICore::submitRequest(GenericRequest *req, GenericResponse *res)
|
||||
{
|
||||
++m_outCount;
|
||||
if(m_currTrack == 0)
|
||||
{
|
||||
m_currTrack++;
|
||||
}
|
||||
req->setTrack(m_currTrack);
|
||||
res->setTrack(m_currTrack);
|
||||
time_t timeout = time(NULL) + m_requestTimeout;
|
||||
|
||||
req->setTimeout(timeout);
|
||||
res->setTimeout(timeout);
|
||||
|
||||
m_outboundQueue.push(pair<GenericRequest *, GenericResponse *>(req, res));
|
||||
return(m_currTrack++);
|
||||
}
|
||||
|
||||
unsigned GenericAPICore::submitRequest(GenericRequest *req, GenericResponse *res, unsigned reqTimeout)
|
||||
{
|
||||
++m_outCount;
|
||||
if(m_currTrack == 0)
|
||||
{
|
||||
m_currTrack++;
|
||||
}
|
||||
req->setTrack(m_currTrack);
|
||||
res->setTrack(m_currTrack);
|
||||
time_t timeout = time(NULL) + reqTimeout;
|
||||
|
||||
req->setTimeout(timeout);
|
||||
res->setTimeout(timeout);
|
||||
|
||||
m_outboundQueue.push(pair<GenericRequest *, GenericResponse *>(req, res));
|
||||
return(m_currTrack++);
|
||||
}
|
||||
|
||||
void GenericAPICore::process()
|
||||
{
|
||||
GenericRequest *req;
|
||||
GenericResponse *res;
|
||||
|
||||
// Process timeout on pending requests - regardless of whether processing is suspended or not
|
||||
while((m_outCount > 0) && ((req = m_outboundQueue.front().first)->getTimeout() <= time(NULL)))
|
||||
{
|
||||
--m_outCount;
|
||||
res = m_outboundQueue.front().second;
|
||||
m_outboundQueue.pop();
|
||||
|
||||
responseCallback(res);
|
||||
delete res;
|
||||
delete req;
|
||||
}
|
||||
|
||||
// Process timeout on pending responses
|
||||
while((m_pendingCount > 0) && ((res = (*m_pending.begin()).second)->getTimeout() <= time(NULL)))
|
||||
{
|
||||
--m_pendingCount;
|
||||
m_pending.erase(m_pending.begin());
|
||||
responseCallback(res);
|
||||
delete res;
|
||||
}
|
||||
|
||||
if (!m_suspended)
|
||||
{
|
||||
while(m_outCount > 0)
|
||||
{
|
||||
GenericConnection *con = getNextActiveConnection();
|
||||
if (con != NULL)
|
||||
{
|
||||
pair<GenericRequest *, GenericResponse *> out_pair = m_outboundQueue.front();
|
||||
|
||||
req = out_pair.first;
|
||||
res = out_pair.second;
|
||||
if (!req->isInputValid())
|
||||
{
|
||||
res->setResult(req->getInputError());
|
||||
--m_outCount;
|
||||
m_outboundQueue.pop();
|
||||
|
||||
responseCallback(res);
|
||||
delete res;
|
||||
delete req;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef USE_SERIALIZE_LIB
|
||||
const unsigned char *msgBuf = NULL;
|
||||
unsigned msgSize = 0;
|
||||
msgBuf = req->pack(msgSize);
|
||||
con->Send(msgBuf, msgSize);
|
||||
#else
|
||||
Base::ByteStream msg;
|
||||
req->pack(msg);
|
||||
con->Send(msg);
|
||||
#endif
|
||||
m_pending.insert(pair<unsigned, GenericResponse *>(res->getTrack(), res));
|
||||
--m_outCount;
|
||||
++m_pendingCount;
|
||||
m_outboundQueue.pop();
|
||||
delete req;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//no active connections
|
||||
break; //from while loop
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for (std::vector<GenericConnection *>::iterator conIter = m_serverConnections.begin(); conIter != m_serverConnections.end(); conIter++)
|
||||
{
|
||||
GenericConnection *con = *conIter;
|
||||
con->process();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GenericConnection *GenericAPICore::getNextActiveConnection()
|
||||
{
|
||||
unsigned startIndex = m_nextConnectionIndex;
|
||||
unsigned maxIndex = m_serverConnections.size() - 1;
|
||||
|
||||
GenericConnection *con = NULL;
|
||||
|
||||
//loop until we find an active connection, or until we get back
|
||||
// to where we started
|
||||
do
|
||||
{
|
||||
if (m_serverConnections[m_nextConnectionIndex]->isConnected())
|
||||
{
|
||||
con = m_serverConnections[m_nextConnectionIndex];
|
||||
if (m_nextConnectionIndex == maxIndex)
|
||||
m_nextConnectionIndex = 0;
|
||||
else
|
||||
m_nextConnectionIndex++;
|
||||
|
||||
}
|
||||
else if (++m_nextConnectionIndex > maxIndex)
|
||||
{
|
||||
//went past end of vector, start back at 0
|
||||
m_nextConnectionIndex = 0;
|
||||
}
|
||||
}while (con == NULL && m_nextConnectionIndex != startIndex);
|
||||
|
||||
return con;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,102 @@
|
||||
#if !defined (GENERICAPICORE_H_)
|
||||
#define GENERICAPICORE_H_
|
||||
|
||||
#pragma warning (disable: 4786)
|
||||
|
||||
#include <map>
|
||||
#include <queue>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <Base/Archive.h>
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace GenericAPI
|
||||
{
|
||||
|
||||
class GenericRequest;
|
||||
class GenericResponse;
|
||||
class GenericConnection;
|
||||
class GenericAPI;
|
||||
|
||||
class GenericAPICore
|
||||
{
|
||||
public:
|
||||
friend class GenericConnection;
|
||||
friend class GenericAPI;
|
||||
|
||||
GenericAPICore::GenericAPICore(const char *host,
|
||||
short port,
|
||||
unsigned reqTimeout,
|
||||
unsigned reconnectTimeout,
|
||||
unsigned noDataTimeoutSecs = 5,
|
||||
unsigned noAckTimeoutSecs = 5,
|
||||
unsigned incomingBufSizeInKB = 32,
|
||||
unsigned outgoingBufSizeInKB = 32,
|
||||
unsigned keepAlive = 1,
|
||||
unsigned maxRecvMessageSizeInKB = 0);
|
||||
|
||||
/**
|
||||
* NOTE: arraySize must be actual size of host and port arrays.
|
||||
* ALSO: cannot specify a 0 array size.
|
||||
*/
|
||||
GenericAPICore(const char *hosts[],
|
||||
const short port[],
|
||||
unsigned arraySize,
|
||||
unsigned reqTimeout,
|
||||
unsigned reconnectTimeout,
|
||||
unsigned noDataTimeoutSecs = 5,
|
||||
unsigned noAckTimeoutSecs = 5,
|
||||
unsigned incomingBufSizeInKB = 32,
|
||||
unsigned outgoingBufSizeInKB = 32,
|
||||
unsigned keepAlive = 1,
|
||||
unsigned maxRecvMessageSizeInKB = 0);
|
||||
|
||||
virtual ~GenericAPICore();
|
||||
|
||||
void process();
|
||||
#ifdef USE_SERIALIZE_LIB
|
||||
virtual void responseCallback(short type, const unsigned char *data, unsigned dataLen) = 0;
|
||||
#else
|
||||
virtual void responseCallback(short type, Base::ByteStream::ReadIterator &iter) = 0;
|
||||
#endif
|
||||
virtual void responseCallback(GenericResponse *R) = 0;
|
||||
|
||||
virtual void OnDisconnect(const char *host, short port) = 0;
|
||||
virtual void OnConnect(const char *host, short port) = 0;
|
||||
|
||||
void suspendProcessing() { m_suspended = true; }
|
||||
void resumeProcessing() { m_suspended = false; }
|
||||
|
||||
// change the host and port for a connection object, will take effect
|
||||
// on connection's next CON_DISCONNECT state.
|
||||
void changeHostPort(unsigned connectionIndex, const char *host, short port);
|
||||
|
||||
unsigned submitRequest(GenericRequest *req, GenericResponse *res);
|
||||
unsigned submitRequest(GenericRequest *req, GenericResponse *res, unsigned reqTimeout);
|
||||
|
||||
protected:
|
||||
std::vector<GenericConnection *> m_serverConnections;
|
||||
std::map<unsigned, GenericResponse *> m_pending;
|
||||
std::queue<std::pair<GenericRequest *, GenericResponse *> > m_outboundQueue;
|
||||
unsigned m_currTrack;
|
||||
time_t m_reconnectTimeout;
|
||||
unsigned m_outCount;
|
||||
unsigned m_pendingCount;
|
||||
unsigned m_requestTimeout;
|
||||
private:
|
||||
bool m_suspended;
|
||||
unsigned m_nextConnectionIndex;
|
||||
|
||||
GenericConnection *getNextActiveConnection();
|
||||
};
|
||||
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
+298
@@ -0,0 +1,298 @@
|
||||
#include "GenericAPI/GenericApiCore.h"
|
||||
#include "GenericAPI/GenericConnection.h"
|
||||
#include "GenericAPI/GenericMessage.h"
|
||||
|
||||
|
||||
using namespace UdpLibrary;
|
||||
|
||||
#ifdef USE_SERIALIZE_LIB
|
||||
#include <Base/serialize.h>
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
|
||||
#endif
|
||||
namespace GenericAPI
|
||||
{
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace Base;
|
||||
|
||||
unsigned GenericConnection::ms_crcBytes = 0;
|
||||
|
||||
GenericConnection::GenericConnection(const char *host, short port, GenericAPICore *apiCore, unsigned reconnectTimeout, unsigned noDataTimeoutSecs, unsigned noAckTimeoutSecs, unsigned incomingBufSizeInKB, unsigned outgoingBufSizeInKB, unsigned keepAlive, unsigned maxRecvMessageSizeInKB, unsigned holdTime)
|
||||
: m_bConnected(false),
|
||||
m_apiCore(apiCore),
|
||||
m_con(NULL),
|
||||
m_host(host),
|
||||
m_nextHost(host),
|
||||
m_port(port),
|
||||
m_nextPort(port),
|
||||
m_lastTrack(123455), //random choice != 1
|
||||
m_conState(CON_DISCONNECT),
|
||||
m_reconnectTimeout(reconnectTimeout)
|
||||
{
|
||||
#ifdef USE_TCP_LIBRARY
|
||||
TcpManager::TcpParams params;
|
||||
|
||||
params.incomingBufferSize = incomingBufSizeInKB * 1024;
|
||||
params.outgoingBufferSize = outgoingBufSizeInKB * 1024;
|
||||
params.maxConnections = 1;
|
||||
params.port = 0;
|
||||
params.maxRecvMessageSize = maxRecvMessageSizeInKB*1024;
|
||||
params.keepAliveDelay = keepAlive * 1000;
|
||||
params.noDataTimeout = noDataTimeoutSecs * 1000;
|
||||
//params.oldestUnacknowledgedTimeout = noAckTimeoutSecs * 1000;
|
||||
|
||||
m_manager = new TcpManager(params);
|
||||
#else //default to UDP_LIBRARY
|
||||
UdpManager::Params params;
|
||||
|
||||
params.keepAliveDelay = keepAlive * 1000;
|
||||
params.maxDataHoldTime = holdTime;
|
||||
params.incomingBufferSize = incomingBufSizeInKB * 1024;
|
||||
params.outgoingBufferSize = outgoingBufSizeInKB * 1024;
|
||||
params.maxConnections = 1;
|
||||
params.port = 0;
|
||||
params.noDataTimeout = noDataTimeoutSecs * 1000;
|
||||
params.oldestUnacknowledgedTimeout = noAckTimeoutSecs * 1000;
|
||||
params.crcBytes = ms_crcBytes;
|
||||
|
||||
m_manager = new UdpManager(¶ms);
|
||||
#endif //USE_TCP_LIBRARY
|
||||
|
||||
}
|
||||
|
||||
GenericConnection::~GenericConnection()
|
||||
{
|
||||
if(m_con)
|
||||
{
|
||||
m_con->SetHandler(NULL);
|
||||
m_con->Disconnect();//don't worry about onterminated being called, we've set it's handler to null, so it wont
|
||||
m_con->Release();
|
||||
}
|
||||
|
||||
m_manager->Release();
|
||||
}
|
||||
|
||||
void GenericConnection::changeHostPort(const char *host, short port)
|
||||
{
|
||||
if (host &&
|
||||
strcmp(host, "") != 0)
|
||||
{
|
||||
m_nextHost = host;
|
||||
m_nextPort = port;
|
||||
}
|
||||
}
|
||||
|
||||
void GenericConnection::disconnect()
|
||||
{
|
||||
if (m_con)
|
||||
{
|
||||
m_con->Disconnect();
|
||||
//no need to release, since callback to onTerminated releases it, and callback is allways made m_con->Release();
|
||||
m_con = NULL;
|
||||
}
|
||||
m_conState = CON_DISCONNECT;
|
||||
m_bConnected = false;
|
||||
}
|
||||
|
||||
#ifdef USE_TCP_LIBRARY
|
||||
void GenericConnection::OnTerminated(TcpConnection *con)
|
||||
#else //default to UDP_LIBRARY
|
||||
void GenericConnection::OnTerminated(UdpConnection *con)
|
||||
#endif
|
||||
{
|
||||
m_apiCore->OnDisconnect(m_host.c_str(), m_port);
|
||||
if(m_con)
|
||||
{
|
||||
m_con->Release();
|
||||
m_con = NULL;
|
||||
}
|
||||
m_conState = CON_DISCONNECT;
|
||||
m_bConnected = false;
|
||||
}
|
||||
|
||||
#ifdef USE_TCP_LIBRARY
|
||||
void GenericConnection::OnRoutePacket(TcpConnection *con, const unsigned char *data, int dataLen)
|
||||
#else //default to UDP_LIBRARY
|
||||
void GenericConnection::OnRoutePacket(UdpConnection *con, const unsigned char *data, int dataLen)
|
||||
#endif
|
||||
{
|
||||
short type;
|
||||
unsigned track;
|
||||
|
||||
|
||||
#ifdef USE_SERIALIZE_LIB
|
||||
unsigned bytes = 0;
|
||||
unsigned fieldLen = soe::Read(data, dataLen, type);
|
||||
if (fieldLen == 0)
|
||||
return;//invalid message
|
||||
bytes += fieldLen;
|
||||
|
||||
fieldLen = soe::Read(data+bytes, dataLen-bytes, track);
|
||||
if (fieldLen == 0)
|
||||
return;//invalid message
|
||||
bytes += fieldLen;
|
||||
#else
|
||||
ByteStream msg(data, dataLen);
|
||||
ByteStream::ReadIterator iter = msg.begin();
|
||||
|
||||
get(iter, type);
|
||||
get(iter, track);
|
||||
#endif
|
||||
GenericResponse *res = NULL;
|
||||
|
||||
// the following if block is a temporary fix that prevents
|
||||
// a crash with a game team in which they occasionally find
|
||||
// themselves receiving a dupe track in consecutive calls to
|
||||
// OnRoutePacket (which then leads to a callback being called
|
||||
// twice and data being invalid on the second call -> crash!).
|
||||
if (track != 0 &&
|
||||
track == m_lastTrack)
|
||||
{
|
||||
printf("!!! ERROR !!! Got a duplicate track ID %u\n", track);
|
||||
return;
|
||||
}
|
||||
m_lastTrack = track;
|
||||
|
||||
// end temporary fix.
|
||||
|
||||
if(track == 0)
|
||||
{
|
||||
#ifdef USE_SERIALIZE_LIB
|
||||
m_apiCore->responseCallback(type, data+bytes, dataLen-bytes);
|
||||
#else
|
||||
m_apiCore->responseCallback(type, iter);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
map<unsigned, GenericResponse *>::iterator mapIter = m_apiCore->m_pending.find(track);
|
||||
|
||||
if(mapIter != m_apiCore->m_pending.end())
|
||||
{
|
||||
res = (*mapIter).second;
|
||||
#ifdef USE_SERIALIZE_LIB
|
||||
res->unpack(data, dataLen);
|
||||
#else
|
||||
iter = msg.begin();
|
||||
res->unpack(iter);
|
||||
#endif
|
||||
m_apiCore->m_pending.erase(mapIter);
|
||||
m_apiCore->m_pendingCount--;
|
||||
m_apiCore->responseCallback(res);
|
||||
delete res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GenericConnection::process(bool giveTime)
|
||||
{
|
||||
switch(m_conState)
|
||||
{
|
||||
case CON_DISCONNECT:
|
||||
// if host/port was changed, it takes effect here
|
||||
m_host = m_nextHost;
|
||||
m_port = m_nextPort;
|
||||
|
||||
// create connection object, attempting to connect and
|
||||
// checking for connection in next state, CON_NEGOTIATE
|
||||
m_con = m_manager->EstablishConnection(m_host.c_str(), m_port);
|
||||
if(m_con)
|
||||
{
|
||||
m_con->SetHandler(this);
|
||||
m_conState = CON_NEGOTIATE;
|
||||
m_conTimeout = time(NULL) + m_reconnectTimeout;
|
||||
}
|
||||
break;
|
||||
case CON_NEGOTIATE:
|
||||
// check for connection
|
||||
|
||||
#ifdef USE_TCP_LIBRARY
|
||||
if(m_con->GetStatus() == TcpConnection::StatusConnected)
|
||||
#else //default to UDP_LIBRARY
|
||||
if(m_con->GetStatus() == UdpConnection::cStatusConnected)
|
||||
#endif
|
||||
{
|
||||
// we're connected
|
||||
m_conState = CON_CONNECT;
|
||||
m_apiCore->OnConnect(m_host.c_str(), m_port);
|
||||
m_bConnected = true;
|
||||
}
|
||||
else if(time(NULL) > m_conTimeout)
|
||||
{
|
||||
// we did not connect
|
||||
m_con->Disconnect();
|
||||
//no need to release, since callback to onTerminated releases it, and callback is allways made m_con->Release();
|
||||
m_con = NULL;
|
||||
m_conState = CON_DISCONNECT;
|
||||
m_bConnected = false;
|
||||
}
|
||||
break;
|
||||
case CON_CONNECT:
|
||||
// do nothing
|
||||
break;
|
||||
default:
|
||||
// this should not occur, but we revert to CON_DISCONNECT if it does
|
||||
m_conState = CON_DISCONNECT;
|
||||
m_bConnected = false;
|
||||
if (m_con)
|
||||
{
|
||||
m_con->Disconnect();
|
||||
//no need to release, since callback to onTerminated releases it, and callback is allways made m_con->Release();
|
||||
m_con = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (giveTime)
|
||||
{
|
||||
m_manager->GiveTime();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#ifdef USE_SERIALIZE_LIB
|
||||
void GenericConnection::Send(const unsigned char *data, int dataLen)
|
||||
{
|
||||
#ifdef USE_TCP_LIBRARY
|
||||
if(m_con && m_con->GetStatus() == TcpConnection::StatusConnected)
|
||||
{
|
||||
m_con->Send((const char *)data, dataLen);
|
||||
}
|
||||
#else//USE_TCP_LIBRARY
|
||||
if(m_con && m_con->GetStatus() == UdpConnection::cStatusConnected)
|
||||
{
|
||||
m_con->Send(cUdpChannelReliable1, data, dataLen);
|
||||
}
|
||||
#endif//USE_TCP_LIBRARY
|
||||
}
|
||||
#else //USE_SERIALIZE_LIB
|
||||
void GenericConnection::Send(Base::ByteStream &msg)
|
||||
{
|
||||
#ifdef USE_TCP_LIBRARY
|
||||
if(m_con && m_con->GetStatus() == TcpConnection::StatusConnected)
|
||||
{
|
||||
m_con->Send((const char *)msg.getBuffer(), msg.getSize());
|
||||
}
|
||||
#else //USE_TCP_LIBRARY
|
||||
if(m_con && m_con->GetStatus() == UdpConnection::cStatusConnected)
|
||||
{
|
||||
m_con->Send(cUdpChannelReliable1, msg.getBuffer(), msg.getSize());
|
||||
}
|
||||
#endif//USE_TCP_LIBRARY
|
||||
}
|
||||
#endif //USE_SERIALIZE_LIB
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
#if !defined (GENERICCONNECTION_H_)
|
||||
#define GENERICCONNECTION_H_
|
||||
|
||||
#include <Base/Archive.h>
|
||||
|
||||
#ifdef USE_TCP_LIBRARY
|
||||
#include <TcpLibrary/TcpManager.h>
|
||||
#include <TcpLibrary/TcpConnection.h>
|
||||
#include <TcpLibrary/TcpHandlers.h>
|
||||
#else //default to UDP_LIBRARY
|
||||
#include <UdpLibrary/UdpLibrary.h>
|
||||
#include <UdpLibrary/UdpHandler.h>
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace GenericAPI
|
||||
{
|
||||
|
||||
enum eConState
|
||||
{
|
||||
CON_DISCONNECT,
|
||||
CON_NEGOTIATE,
|
||||
CON_CONNECT
|
||||
};
|
||||
|
||||
#ifdef USE_TCP_LIBRARY
|
||||
class GenericConnection : public TcpConnectionHandler
|
||||
#else //default to UDP_LIBRARY
|
||||
class GenericConnection : public UdpLibrary::UdpConnectionHandler
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
GenericConnection(const char *host,
|
||||
short port,
|
||||
GenericAPICore *apiCore,
|
||||
unsigned reconnectTimeout,
|
||||
unsigned noDataTimeoutSecs = 5,
|
||||
unsigned noAckTimeoutSecs = 5,
|
||||
unsigned incomingBufSizeInKB = 32,
|
||||
unsigned outgoingBufSizeInKB = 32,
|
||||
unsigned keepAlive = 1,
|
||||
unsigned maxRecvMessageSizeInKB = 0,
|
||||
unsigned holdTime = 0);
|
||||
|
||||
virtual ~GenericConnection();
|
||||
|
||||
#ifdef USE_TCP_LIBRARY
|
||||
virtual void OnRoutePacket(TcpConnection *con, const unsigned char *data, int dataLen);
|
||||
virtual void OnTerminated(TcpConnection *con);
|
||||
#else //default to UDP_LIBRARY
|
||||
virtual void OnRoutePacket(UdpLibrary::UdpConnection *con, const unsigned char *data, int dataLen);
|
||||
virtual void OnTerminated(UdpLibrary::UdpConnection *con);
|
||||
#endif
|
||||
#ifdef USE_SERIALIZE_LIB
|
||||
void Send(const unsigned char *data, int dataLen);
|
||||
#else
|
||||
void Send(Base::ByteStream &msg);
|
||||
#endif
|
||||
|
||||
|
||||
void changeHostPort(const char *host, short port);
|
||||
bool isConnected() { return m_bConnected; }
|
||||
void disconnect();
|
||||
void process(bool giveTime = true);
|
||||
|
||||
static unsigned ms_crcBytes;
|
||||
private:
|
||||
bool m_bConnected;
|
||||
GenericAPICore *m_apiCore;
|
||||
#ifdef USE_TCP_LIBRARY
|
||||
TcpManager *m_manager;
|
||||
TcpConnection *m_con;
|
||||
#else //default to UDP_LIBRARY
|
||||
UdpLibrary::UdpManager *m_manager;
|
||||
UdpLibrary::UdpConnection *m_con;
|
||||
#endif
|
||||
std::string m_host;
|
||||
std::string m_nextHost;
|
||||
short m_port;
|
||||
short m_nextPort;
|
||||
|
||||
unsigned m_lastTrack;
|
||||
|
||||
eConState m_conState;
|
||||
time_t m_conTimeout;
|
||||
unsigned m_reconnectTimeout;
|
||||
};
|
||||
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,32 @@
|
||||
#include "GenericMessage.h"
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace GenericAPI
|
||||
{
|
||||
|
||||
GenericMessage::GenericMessage(short type)
|
||||
: m_type(type)
|
||||
{
|
||||
}
|
||||
|
||||
GenericRequest::GenericRequest(short type)
|
||||
: GenericMessage(type)
|
||||
{
|
||||
}
|
||||
|
||||
GenericResponse::GenericResponse(short type, unsigned result, void *user)
|
||||
: GenericMessage(type),
|
||||
m_result(result),
|
||||
m_user(user)
|
||||
{
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,84 @@
|
||||
#if !defined (GENERICMESSAGE_H_)
|
||||
#define GENERICMESSAGE_H_
|
||||
|
||||
#include <time.h>
|
||||
#include <Base/Archive.h>
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
|
||||
#endif
|
||||
namespace GenericAPI
|
||||
{
|
||||
|
||||
// Basic request message, implements a type, and a timeout mechanism
|
||||
class GenericMessage
|
||||
{
|
||||
public:
|
||||
GenericMessage(short type);
|
||||
virtual ~GenericMessage() {};
|
||||
|
||||
short getType() const { return m_type; }
|
||||
protected:
|
||||
short m_type;
|
||||
};
|
||||
|
||||
class GenericRequest : public GenericMessage
|
||||
{
|
||||
public:
|
||||
GenericRequest(short type);
|
||||
virtual ~GenericRequest() {};
|
||||
#ifdef USE_SERIALIZE_LIB
|
||||
virtual const unsigned char *pack(unsigned &msgSize) = 0;
|
||||
#else
|
||||
virtual void pack(Base::ByteStream &msg) = 0;
|
||||
#endif
|
||||
|
||||
const time_t &getTimeout() const { return m_timeout; }
|
||||
void setTimeout(time_t t) { m_timeout = t; }
|
||||
void setTrack(unsigned t) { m_track = t; }
|
||||
unsigned getTrack() const { return m_track; }
|
||||
|
||||
virtual bool isInputValid() const { return true; }
|
||||
virtual unsigned getInputError() const { return 0; }
|
||||
|
||||
protected:
|
||||
unsigned m_track;
|
||||
time_t m_timeout;
|
||||
};
|
||||
|
||||
// Basic response message from server. In the case that this response to a request
|
||||
// submitted from this API, the response would have been generated at request submission
|
||||
// time, and the timeout value filled in appropriatly
|
||||
class GenericResponse : public GenericMessage
|
||||
{
|
||||
public:
|
||||
GenericResponse(short type, unsigned result, void *user);
|
||||
virtual ~GenericResponse() {};
|
||||
#ifdef USE_SERIALIZE_LIB
|
||||
virtual void unpack(const unsigned char *data, unsigned dataLen) = 0;
|
||||
#else
|
||||
virtual void unpack(Base::ByteStream::ReadIterator &iter) = 0;
|
||||
#endif
|
||||
|
||||
time_t getTimeout() const { return m_timeout; }
|
||||
void setTimeout(time_t t) { m_timeout = t; }
|
||||
void setTrack(unsigned t) { m_track = t; }
|
||||
unsigned getTrack() const { return m_track; }
|
||||
unsigned getResult() const { return m_result; }
|
||||
void setResult(unsigned res) { m_result = res; }
|
||||
void *getUser() const { return m_user; }
|
||||
protected:
|
||||
unsigned m_track;
|
||||
unsigned m_result;
|
||||
void *m_user;
|
||||
time_t m_timeout;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
+1852
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,726 @@
|
||||
#ifndef UDPLIBRARY_UDPCONNECTION_H
|
||||
#define UDPLIBRARY_UDPCONNECTION_H
|
||||
|
||||
// Copyright 2004 Sony Online Entertainment, all rights reserved.
|
||||
// Author: Jeff Petersen
|
||||
|
||||
namespace UdpLibrary
|
||||
{
|
||||
|
||||
class UdpReliableChannel;
|
||||
|
||||
struct UdpConnectionStatistics
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// these statistics are valid even if clock-sync is not used
|
||||
// these statistics are never reset and should not be as the negotiated
|
||||
// packetloss stats would get messed up if they were
|
||||
// as such, use UdpConnection::ConnectionAge to determine how long they have been accumulating
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
udp_int64 totalBytesSent;
|
||||
udp_int64 totalBytesReceived;
|
||||
udp_int64 totalPacketsSent; // total packets we have sent
|
||||
udp_int64 totalPacketsReceived; // total packets we have received
|
||||
udp_int64 crcRejectedPackets; // total packets on our connection that have been rejected due to a crc error
|
||||
udp_int64 orderRejectedPackets; // total packets on our connection that have been rejected due to an order error (only applicable for ordered channel)
|
||||
udp_int64 duplicatePacketsReceived; // total reliable packets that we received where we had already received it before and threw it away
|
||||
udp_int64 resentPacketsAccelerated; // number of times we have resent a packet due to receiving a later packet in the series
|
||||
udp_int64 resentPacketsTimedOut; // number of times we have resent a packet due to the ack-timeout expiring
|
||||
udp_int64 applicationPacketsSent;
|
||||
udp_int64 applicationPacketsReceived;
|
||||
udp_int64 iterations; // number of times this connection has been given processing time
|
||||
udp_int64 corruptPacketErrors; // number of misformed/corrupt packets
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// these statistics are only valid if clock-sync'ing is enabled (highly recommended) (will be valid on both client and server side)
|
||||
// these statistics are reset by PingStatReset and are negotiated periodically by the clock-sync stuff (Params::clockSyncDelay)
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
int masterPingAge; // only valid (and applicable) on client side
|
||||
int masterPingTime;
|
||||
int averagePingTime;
|
||||
int lowPingTime;
|
||||
int highPingTime;
|
||||
int lastPingTime;
|
||||
int reliableAveragePing; // the average time (over last 3 acks) for a reliable packet to get acked (when packet is not lost)
|
||||
udp_int64 syncOurSent; // total packets we have sent at time they reported their numbers
|
||||
udp_int64 syncOurReceived; // total packets we have received at time they reported their numbers
|
||||
udp_int64 syncTheirSent; // total packets they have sent
|
||||
udp_int64 syncTheirReceived; // total packets they have received
|
||||
float percentSentSuccess;
|
||||
float percentReceivedSuccess;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// The purpose of the UdpConnection is to manage a single logical connection
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
class UdpConnection : public UdpGuardedRefCount, public PriorityQueueMember, public HashTableMember1<UdpConnection>, public HashTableMember2<UdpConnection>
|
||||
{
|
||||
public:
|
||||
enum Status { cStatusNegotiating, cStatusConnected, cStatusDisconnected, cStatusDisconnectPending, cStatusCount };
|
||||
|
||||
enum DisconnectReason { cDisconnectReasonNone, cDisconnectReasonIcmpError, cDisconnectReasonTimeout
|
||||
, cDisconnectReasonOtherSideTerminated, cDisconnectReasonManagerDeleted
|
||||
, cDisconnectReasonConnectFail, cDisconnectReasonApplication
|
||||
, cDisconnectReasonUnreachableConnection, cDisconnectReasonUnacknowledgedTimeout
|
||||
, cDisconnectReasonNewConnectionAttempt, cDisconnectReasonConnectionRefused
|
||||
, cDisconnectReasonMutualConnectError, cDisconnectReasonConnectingToSelf
|
||||
, cDisconnectReasonReliableOverflow, cDisconnectReasonApplicationReleased
|
||||
, cDisconnectReasonCorruptPacket
|
||||
, cDisconnectReasonCount };
|
||||
|
||||
// returns the current status of this connection
|
||||
Status GetStatus() const;
|
||||
|
||||
// returns the reason that a connection was disconnected. See the enum above for a list of all the reasons
|
||||
DisconnectReason GetDisconnectReason() const;
|
||||
char *GetDisconnectReasonText(char *buf, int bufLen) const;
|
||||
DisconnectReason GetOtherSideDisconnectReason() const;
|
||||
static const char *DisconnectReasonText(DisconnectReason reason); // text-description of disconnect reason to aid in logging
|
||||
|
||||
// sets the handler object for this connection. If a handler object is specified, then the callback functions specified
|
||||
// in UdpManager::Params are ignored for this connection and the handler is used for the callback instead.
|
||||
// by default there is no handler.
|
||||
void SetHandler(UdpConnectionHandler *handler);
|
||||
UdpConnectionHandler *GetHandler() const;
|
||||
|
||||
// set and get the pass-through data value. Typically the application will set the pass through data
|
||||
// in the callback function for establishing a connection, then it will use the pass through data
|
||||
// in the callback function for routing packets.
|
||||
void SetPassThroughData(void *passThroughData);
|
||||
void *GetPassThroughData() const;
|
||||
|
||||
// when called this connection is marked as terminated. It is the responsibility of the application
|
||||
// to explicitly destroy connections that are no longer connected. When this object is disconnected
|
||||
// it calls the UdpManager and has itself removed from the list of active connections, at which point
|
||||
// the only person having a pointer to this object is the application itself (which owns it)
|
||||
// (if the UdpManager is deleted before all UdpConnections are destroyed, the UdpManager loops through
|
||||
// all of the connections it has calling Disconnect on them such that they know that they no longer
|
||||
// have a udp manager that they can send data through)
|
||||
//
|
||||
// setting a flushTimeout tells the connection to stay alive for that amount of time trying to send any pending
|
||||
// reliable data before shutting down. Once the application calls Disconnect even with a flushTimeout, the application
|
||||
// should not attempt to use the connection in any significant way (see docs and release notes for details)
|
||||
// note: the notifyApplication parameter was removed from this function and the functionality of the library
|
||||
// was changed such that ANYTIME the connection objects state changes to cStatusDisconnected, the OnTerminated callback
|
||||
// function gets called.
|
||||
void Disconnect(int flushTimeout = 0);
|
||||
|
||||
// sends a logical packet on the specified channel, returns FALSE if packet could not be queued for sending (should never happen)
|
||||
// Internally, a packet that starts with a 0 byte is considered an internal control packet. If a logical packet starts with a 0
|
||||
// byte, then there will an extra control byte of overhead in order to facilitate it. It is recommended that if packet size
|
||||
// is critical, that you don't start the packet with a 0 byte. Typically an application will have a packet-type byte on the front
|
||||
// of application packets; the application packet types should simply start at 1.
|
||||
bool Send(UdpChannel channel, const void *data, int dataLen);
|
||||
|
||||
// same as the regular Send only it takes a LogicalPacket instead. There are two huge advantages to having it take a
|
||||
// LogicalPacket. First, we can send the same LogicalPacket to multiple locations and each connection will not necessarily
|
||||
// have to make its own copy of the data at the time it is put into the send queue (instead each connection just increments
|
||||
// the buffer ref-count). Second, it allows the application to pre-generate very large packets (like file update packets potentially)
|
||||
// and hold onto them for the entire length of the application, then, whenever any player needs that chunk of data, it can send them
|
||||
// the already formatted LogicalPacket.
|
||||
bool Send(UdpChannel channel, const LogicalPacket *packet);
|
||||
|
||||
// manually forces all channels to send-off any data they have queued up waiting for processing time to send
|
||||
// this mainly applies to reliable channels. When you send a reliable packet, it actually only adds it to the reliable
|
||||
// queue until the connection is given processing time by the manager object. This call forces it to attempt to
|
||||
// send that queued data immediately (subject to normal flow control restrictions). This also flushes the multi-buffer
|
||||
// for the channel. If you send reliable data and want to ensure that it goes out immediately after the send, this is the
|
||||
// best call to make.
|
||||
void FlushChannels();
|
||||
|
||||
// manually forces buffered data to be sent immediately
|
||||
void FlushMultiBuffer();
|
||||
|
||||
// returns the number of bytes sent/received in the last second to this connection (accurate to within cBinResolution(25) milliseconds)
|
||||
// these functions are not const as they expire the older bin data internally in order to calculate the number
|
||||
int OutgoingBytesLastSecond();
|
||||
int IncomingBytesLastSecond();
|
||||
|
||||
// returns the total number of bytes outstanding in all reliable channels. When this is zero, you know for sure
|
||||
// that all sent reliable data has arrived at destination and is confirmed.
|
||||
int TotalPendingBytes() const;
|
||||
|
||||
// returns how long has elapsed since this connection received data (in milliseconds)
|
||||
int LastReceive() const;
|
||||
|
||||
// returns how long has elapsed since this connection received data (in milliseconds), using useStamp as the current time (optimization)
|
||||
int LastReceive(UdpClockStamp useStamp) const;
|
||||
|
||||
// returns how long has elapsed since this connection sent data (in milliseconds)
|
||||
int LastSend() const;
|
||||
|
||||
// returns how long this connection has been in existence (in milliseconds)
|
||||
int ConnectionAge() const;
|
||||
|
||||
// returns the UdpManager object that is managing this connection
|
||||
// will return NULL if the connection has been disconnected for some reason (because disconnecting severes the link to UdpManager)
|
||||
UdpManager *GetUdpManager() const;
|
||||
|
||||
// returns the 32-bit encryption-code that was negotiated as part of the connection-establishment process.
|
||||
// this is a randomly generated number that both the client and the server have in common. It is exposed
|
||||
// via this interface primarily to allow user-supplied encrypt routines access to it.
|
||||
// this code is generated by the server side in response to a connect request.
|
||||
int GetEncryptCode() const;
|
||||
|
||||
// this returns the connection-code. This is very similar to the encrypt-code in that it is randomly
|
||||
// generated and both ends of the connection will report the same value. The difference is that this
|
||||
// code's purpose is part of the internal protocol to ensure that old connections don't try to process
|
||||
// new connection request packets. Unlike the encrypt-code, this value is generated by the client
|
||||
// and is part of the connect-request packet. Nevertheless, since this number will be the same random
|
||||
// number on both ends of the connection, it too can be used as a potential encryption key for the user
|
||||
// supplied encrypt routines. It's not quite as secure as the encrypt code since this value in theory
|
||||
// could be hacked to be something predictable on the client side.
|
||||
int GetConnectCode() const;
|
||||
|
||||
// returns a sync-stamp that can be compared to other ServerSyncStamp's generated on other machines
|
||||
// in order to calculate the one-way travel time for a packet. It can only accurate calculate
|
||||
// packet travel times under 32 seconds, would should be completely safe. You must use the
|
||||
// UdpManager::SyncStampDeltaTime function in order to calculate the elapsed time between
|
||||
// the two stamps.
|
||||
udp_ushort ServerSyncStampShort() const;
|
||||
udp_uint ServerSyncStampLong() const;
|
||||
int ServerSyncStampShortElapsed(udp_ushort syncStamp) const;
|
||||
int ServerSyncStampLongElapsed(udp_uint syncStamp) const;
|
||||
|
||||
// returns the IP address/port this connection is linked to
|
||||
UdpPlatformAddress GetDestinationIp() const;
|
||||
int GetDestinationPort() const;
|
||||
char *GetDestinationString(char *buf, int bufLen) const;
|
||||
|
||||
// statistical functions
|
||||
void GetStats(UdpConnectionStatistics *cs);
|
||||
void PingStatReset(); // resets the ping-stat information, causing it to resync the clock etc (if in clock-sync mode). Generally this is not done, it was added for backward compatibility
|
||||
|
||||
|
||||
// functions for manipulating the automatic no-data-disconnect stuff on a per-connection basis
|
||||
void SetNoDataTimeout(int noDataTimeout); // 0=never timeout, otherwise in milliseconds (overrides UdpManager::Params::noDataTimeout setting, which is the default)
|
||||
int GetNoDataTimeout() const;
|
||||
|
||||
// functions for manipulating the keep-alive packet sending on a per-connection basis
|
||||
void SetKeepAliveDelay(int keepAliveDelay);
|
||||
int GetKeepAliveDelay() const;
|
||||
|
||||
// configures whether this connection is in silent-disconnect mode or not. By default, the connection is not in silent
|
||||
// disconnect mode, which means that when this connection is terminated, it will send a final terminate-packet to the
|
||||
// other side telling them that we are disconnected, allowing them to quickly realize that the connection is now dead.
|
||||
// In some circumstances, it may be desireable to not do this, and this can be accomplished by calling this function
|
||||
// passing in 'true' to put it in silent mode. This may be desireable in cases where you are disconnecting a cheater
|
||||
// and don't want them to have immediate notification that they did something bad. Or, if you are attempting to test
|
||||
// timeout functionality on the other end and want to simulate a truly dead connection. Normally, you will not want
|
||||
// to mess with this. It was added to the API to support some internal functionality, see its use in the source-code
|
||||
// or release-notes for details.
|
||||
void SetSilentDisconnect(bool silent);
|
||||
|
||||
|
||||
// returns the current queue-status of the reliable channel specified. Unreliable channels will always report zero.
|
||||
struct ChannelStatus
|
||||
{
|
||||
int totalPendingBytes; // total bytes of data in channel that have yet to be acknowledged (includes queuedBytes plus physical-packet bytes that have yet to be acknowledged)
|
||||
int queuedPackets; // number of logical packets in the queue
|
||||
int queuedBytes; // number of bytes in the logical queue (the logical queue does NOT include pending physical packets)
|
||||
int incomingLargeTotal; // total number of bytes in the currently incoming logical packet (only meaningful obviously if a fragmented file is in tranist)
|
||||
int incomingLargeSoFar; // number of bytes received so far in the currently incoming logical packet
|
||||
int oldestUnacknowledgedAge; // age of the oldest unacknowledged (but sent) packet (in milliseconds)
|
||||
int duplicatePacketsReceived; // number of times we received a packet that we had already received
|
||||
int resentPacketsAccelerated; // number of times we have resent a packet due to receiving a later packet in the series
|
||||
int resentPacketsTimedOut; // number of times we have resent a packet due to the ack-timeout expiring
|
||||
int congestionSlowStartThreshhold; // current threshhold for slow-start algorithm
|
||||
int congestionWindowSize; // current sliding window size
|
||||
int ackAveragePing; // average time for a packet to be acknowledged (used in calculating optimal resend timeouts)
|
||||
};
|
||||
void GetChannelStatus(UdpChannel channel, ChannelStatus *channelStatus) const;
|
||||
|
||||
protected:
|
||||
friend class UdpManager;
|
||||
friend class UdpReliableChannel;
|
||||
|
||||
// note: if connectPacket is NULL, that means this connection object is being created to establish
|
||||
// a new connection to the specified ip/port (ie. the connection starts out in cStatusNegotiating mode)
|
||||
// if connectPacket is non-NULL, that menas this connection object is being created to handle an
|
||||
// incoming connect request and it will start out in cStatusConnected mode.
|
||||
UdpConnection(UdpManager *udpManager, UdpPlatformAddress destIp, int destPort, int timeout); // starts connection-establishment protocol
|
||||
UdpConnection(UdpManager *udpManager, const UdpManager::PacketHistoryEntry *e); // starts already connected, replying to connection request
|
||||
|
||||
// gives this connection processing time (only given processing time by the manager object and then
|
||||
// only when the connection has scheduled itself to receive processing time)
|
||||
void GiveTime(bool fromManager);
|
||||
void ProcessRawPacket(const UdpManager::PacketHistoryEntry *e);
|
||||
void PortUnreachable();
|
||||
void FlagPortUnreachable();
|
||||
|
||||
// these functions are called by the manager to forward these events to this connection
|
||||
// all events get sent to the UdpManager for potential event-queuing, then forwarded back
|
||||
// to the connection for actual deliver, since the connection object needs to hold a
|
||||
// guard such that the handler doesn't get deleted during event delivery
|
||||
void OnRoutePacket(const udp_uchar *data, int dataLen);
|
||||
void OnConnectComplete();
|
||||
void OnTerminated();
|
||||
void OnCrcReject(const udp_uchar *data, int dataLen);
|
||||
void OnPacketCorrupt(const udp_uchar *data, int dataLen, UdpCorruptionReason reason);
|
||||
|
||||
protected:
|
||||
typedef int (UdpConnection::* IEncryptFunction)(udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
|
||||
typedef int (UdpConnection::* IDecryptFunction)(udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
|
||||
IDecryptFunction mDecryptFunction[cEncryptPasses];
|
||||
IEncryptFunction mEncryptFunction[cEncryptPasses];
|
||||
|
||||
UdpPlatformAddress mIp;
|
||||
int mPort;
|
||||
int mSimulateOutgoingQueueBytes; // used by UdpManager to track how many bytes are in it's simulation queue headed to each destination
|
||||
|
||||
private:
|
||||
~UdpConnection();
|
||||
void Init(UdpManager *udpManager, UdpPlatformAddress destIp, int destPort);
|
||||
|
||||
// note: BufferedSend is capable of optionally taking two chunks of data at once, which are then concatenated together as if they were one chunk of data
|
||||
// into the multi-buffer. Providing this facility prevents the UdpReliableChannel object from having to make a copy of all the data it sends
|
||||
// in order to stick a realiable header on it.
|
||||
// we don't bother extending this down to the PhysicalSend (in case the BufferedSend does a pass through due to size) because the encryption code
|
||||
// is incapable of sourcing from two different chunks and outputting to one chunk. It's not possible to change that either, since the encyption
|
||||
// takes place 32 bits at a time and you could end up straddling boundaries between chunks.
|
||||
void RawSend(const udp_uchar *data, int dataLen); // nothing happens to the data here, it is given to the udpmanager and sent out the port
|
||||
void PhysicalSend(const udp_uchar *data, int dataLen, bool appendAllowed); // sends a physical packet (encrypts and adds crc bytes)
|
||||
udp_uchar *BufferedSend(const udp_uchar *data, int dataLen, const udp_uchar *data2, int dataLen2, bool appendAllowed); // buffers logical packets waiting til we have more data (makes multi-packets)
|
||||
bool InternalSend(UdpChannel channel, const udp_uchar *data, int dataLen, const udp_uchar *data2 = NULL, int dataLen2 = 0);
|
||||
|
||||
void InternalGiveTime();
|
||||
void InternalDisconnect(int flushTimeout, DisconnectReason reason);
|
||||
void ProcessCookedPacket(const udp_uchar *data, int dataLen);
|
||||
void DecryptIt(const udp_uchar *data, int dataLen);
|
||||
void ScheduleTimeNow();
|
||||
void ExpireSendBin();
|
||||
void ExpireReceiveBin();
|
||||
void SendTerminatePacket(int connectCode, DisconnectReason reason);
|
||||
void CallbackRoutePacket(const udp_uchar *data, int dataLen);
|
||||
void CallbackCorruptPacket(const udp_uchar *data, int dataLen, UdpCorruptionReason reason);
|
||||
bool IsNonEncryptPacket(const udp_uchar *data) const;
|
||||
|
||||
// these encrypt-method functions return the length of the encrypted/decrypted data
|
||||
// new methods of encryption/compression can be easily added by simply creating the
|
||||
// functions for them and changing the SetupEncryptModel function as appropriate
|
||||
// since raw packets are encrypted in the first place and have a limited size
|
||||
// the decrypted data will never be larger than a maxRawPacketSize. Both of encrypt
|
||||
// and decrypt are guaranteed to have enough room in dest buffers to hold the results.
|
||||
// Encryption function is allowed to expand the data at most the number of bytes
|
||||
// it reserves for this purpose in the SetupEncryptModel function.
|
||||
int EncryptNone(udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
|
||||
int DecryptNone(udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
|
||||
int EncryptXor(udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
|
||||
int DecryptXor(udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
|
||||
int EncryptXorBuffer(udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
|
||||
int DecryptXorBuffer(udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
|
||||
int EncryptUserSupplied(udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
|
||||
int DecryptUserSupplied(udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
|
||||
int EncryptUserSupplied2(udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
|
||||
int DecryptUserSupplied2(udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
|
||||
void SetupEncryptModel();
|
||||
|
||||
|
||||
UdpLinkedListMember<UdpConnection> mConnectionLink;
|
||||
UdpLinkedListMember<UdpConnection> mDisconnectPendingLink;
|
||||
|
||||
Status mStatus;
|
||||
void *mPassThroughData;
|
||||
UdpManager *mUdpManager;
|
||||
int mConnectCode;
|
||||
UdpConnectionStatistics mConnectionStats;
|
||||
UdpClockStamp mConnectionCreateTime;
|
||||
int mConnectAttemptTimeout;
|
||||
int mNoDataTimeout;
|
||||
DisconnectReason mDisconnectReason;
|
||||
DisconnectReason mOtherSideDisconnectReason;
|
||||
bool mFlaggedPortUnreachable;
|
||||
bool mSilentDisconnect;
|
||||
|
||||
UdpReliableChannel *mChannel[cReliableChannelCount];
|
||||
|
||||
struct Configuration
|
||||
{
|
||||
int encryptCode;
|
||||
int crcBytes;
|
||||
EncryptMethod encryptMethod[cEncryptPasses];
|
||||
int maxRawPacketSize; // negotiated maxRawPacketSize (ie. smaller of what two sides are set to)
|
||||
};
|
||||
|
||||
Configuration mConnectionConfig;
|
||||
int mOtherSideProtocolVersion;
|
||||
|
||||
UdpClockStamp mLastClockSyncTime;
|
||||
UdpClockStamp mDataHoldTime;
|
||||
UdpClockStamp mLastSendTime;
|
||||
UdpClockStamp mLastReceiveTime;
|
||||
UdpClockStamp mLastPortAliveTime;
|
||||
udp_uchar *mMultiBufferData;
|
||||
udp_uchar *mMultiBufferPtr;
|
||||
|
||||
int mOrderedCountOutgoing;
|
||||
int mOrderedCountOutgoing2;
|
||||
udp_ushort mOrderedStampLast;
|
||||
udp_ushort mOrderedStampLast2;
|
||||
|
||||
udp_uchar *mEncryptXorBuffer;
|
||||
int mEncryptExpansionBytes;
|
||||
|
||||
udp_uint mSyncTimeDelta;
|
||||
int mSyncStatTotal;
|
||||
int mSyncStatCount;
|
||||
int mSyncStatLow;
|
||||
int mSyncStatHigh;
|
||||
int mSyncStatLast;
|
||||
int mSyncStatMasterRoundTime;
|
||||
UdpClockStamp mSyncStatMasterFixupTime;
|
||||
|
||||
bool mGettingTime;
|
||||
UdpConnectionHandler *mHandler;
|
||||
|
||||
int mKeepAliveDelay;
|
||||
|
||||
UdpClockStamp mIcmpErrorRetryStartStamp;
|
||||
UdpClockStamp mPortRemapRequestStartStamp;
|
||||
|
||||
UdpClockStamp mDisconnectFlushStamp;
|
||||
int mDisconnectFlushTimeout;
|
||||
mutable UdpPlatformGuardObject mGuard;
|
||||
mutable UdpPlatformGuardObject mHandlerGuard;
|
||||
|
||||
|
||||
// data rate management functions
|
||||
enum { cBinResolution = 25, cBinCount = 1000 / cBinResolution };
|
||||
udp_int64 mLastSendBin;
|
||||
udp_int64 mLastReceiveBin;
|
||||
int mOutgoingBytesLastSecond;
|
||||
int mIncomingBytesLastSecond;
|
||||
int mSendBin[cBinCount];
|
||||
int mReceiveBin[cBinCount];
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// The following structs represent what the internal packets look like. In practice, most of these
|
||||
// structs are never used and exist only for documentation clarity. Internally packets are
|
||||
// manually assembled such that struct packing and byte-ordering issues won't be an issue.
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
struct UdpPacketConnect
|
||||
{
|
||||
udp_uchar zeroByte;
|
||||
udp_uchar packetType;
|
||||
int protocolVersion;
|
||||
int connectCode;
|
||||
int maxRawPacketSize;
|
||||
};
|
||||
|
||||
struct UdpPacketConfirm
|
||||
{
|
||||
udp_uchar zeroByte;
|
||||
udp_uchar packetType;
|
||||
int connectCode;
|
||||
Configuration config;
|
||||
int maxRawPacketSize;
|
||||
};
|
||||
|
||||
struct UdpPacketTerminate
|
||||
{
|
||||
udp_uchar zeroByte;
|
||||
udp_uchar packetType;
|
||||
int connectCode;
|
||||
};
|
||||
|
||||
struct UdpPacketKeepAlive
|
||||
{
|
||||
udp_uchar zeroByte;
|
||||
udp_uchar packetType;
|
||||
};
|
||||
|
||||
struct UdpPacketGroup
|
||||
{
|
||||
// this format is prepped by the GroupLogicalPacket object, which reports itself to the UdpConnection object as an internal packet
|
||||
// type such that it doesn't get treated as an application-packet even though the application is the one sending it
|
||||
udp_uchar zeroByte;
|
||||
udp_uchar packetType;
|
||||
// variableValue/data, repeated...
|
||||
};
|
||||
|
||||
struct UdpPacketClockSync
|
||||
{
|
||||
udp_uchar zeroByte;
|
||||
udp_uchar packetType;
|
||||
udp_ushort timeStamp;
|
||||
int masterPingTime;
|
||||
int averagePingTime;
|
||||
int lowPingTime;
|
||||
int highPingTime;
|
||||
int lastPingTime;
|
||||
udp_int64 ourSent;
|
||||
udp_int64 ourReceived;
|
||||
};
|
||||
|
||||
struct UdpPacketClockReflect
|
||||
{
|
||||
udp_uchar zeroByte;
|
||||
udp_uchar packetType;
|
||||
udp_ushort timeStamp;
|
||||
udp_uint serverSyncStampLong;
|
||||
udp_int64 yourSent;
|
||||
udp_int64 yourReceived;
|
||||
udp_int64 ourSent;
|
||||
udp_int64 ourReceived;
|
||||
};
|
||||
|
||||
struct UdpPacketReliable
|
||||
{
|
||||
udp_uchar zeroByte;
|
||||
udp_uchar packetType;
|
||||
udp_ushort reliableStamp;
|
||||
};
|
||||
|
||||
struct UdpPacketReliableFragmentStart
|
||||
{
|
||||
UdpPacketReliable reliable;
|
||||
int length;
|
||||
};
|
||||
|
||||
struct UdpPacketAck
|
||||
{
|
||||
udp_uchar zeroByte;
|
||||
udp_uchar packetType;
|
||||
udp_ushort reliableStamp;
|
||||
};
|
||||
|
||||
struct UdpPacketOrdered
|
||||
{
|
||||
udp_uchar zeroByte;
|
||||
udp_uchar packetType;
|
||||
udp_ushort orderStamp;
|
||||
};
|
||||
|
||||
|
||||
enum { cUdpPacketReliableSize = 4 };
|
||||
enum { cUdpPacketOrderedSize = 4 };
|
||||
|
||||
protected:
|
||||
friend class GroupLogicalPacket; // so it can see cUdpPacketGroup enum
|
||||
|
||||
// note: cUdpPacketReliable, cUdpPacketFragment both indicate a reliable-packet header. They are marked
|
||||
// differently such that we can support large packets without any additional header overhead, a fragment marked packet means
|
||||
// that the packet is part of a larger packet being assembled. The first fragment has an additional 4 bytes on the header specifying
|
||||
// the length to follow. The order of those entries is important
|
||||
enum UdpPacketType { cUdpPacketZeroEscape, cUdpPacketConnect, cUdpPacketConfirm, cUdpPacketMulti, cUdpPacketBig
|
||||
, cUdpPacketTerminate, cUdpPacketKeepAlive
|
||||
, cUdpPacketClockSync, cUdpPacketClockReflect
|
||||
, cUdpPacketReliable1, cUdpPacketReliable2, cUdpPacketReliable3, cUdpPacketReliable4
|
||||
, cUdpPacketFragment1, cUdpPacketFragment2, cUdpPacketFragment3, cUdpPacketFragment4
|
||||
, cUdpPacketAck1, cUdpPacketAck2, cUdpPacketAck3, cUdpPacketAck4
|
||||
, cUdpPacketAckAll1, cUdpPacketAckAll2, cUdpPacketAckAll3, cUdpPacketAckAll4
|
||||
, cUdpPacketGroup, cUdpPacketOrdered, cUdpPacketOrdered2, cUdpPacketPortAlive
|
||||
, cUdpPacketUnreachableConnection, cUdpPacketRequestRemap };
|
||||
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// inline implementations
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// UdpConnection
|
||||
inline void UdpConnection::ScheduleTimeNow()
|
||||
{
|
||||
// if we are current in our GiveTime function getting time, then there is no need to reprioritize to 0 when we send a raw packet, since
|
||||
// the last thing we do in out GiveTime is do a scheduling calculation based on the last time a packet was sent. This little check
|
||||
// prevents us from reprioritizing to 0, only to shortly thereafter be reprioritized to where we actually belong.
|
||||
if (!mGettingTime)
|
||||
{
|
||||
if (mUdpManager != NULL)
|
||||
mUdpManager->SetPriority(this, 0);
|
||||
}
|
||||
}
|
||||
|
||||
inline void UdpConnection::SetHandler(UdpConnectionHandler *handler)
|
||||
{
|
||||
UdpGuard guard(&mHandlerGuard);
|
||||
mHandler = handler;
|
||||
}
|
||||
|
||||
inline UdpConnectionHandler *UdpConnection::GetHandler() const
|
||||
{
|
||||
UdpGuard guard(&mHandlerGuard);
|
||||
return(mHandler);
|
||||
}
|
||||
|
||||
inline bool UdpConnection::IsNonEncryptPacket(const udp_uchar *data) const
|
||||
{
|
||||
if (data[0] == 0)
|
||||
{
|
||||
if (data[1] == cUdpPacketConnect || data[1] == cUdpPacketConfirm || data[1] == cUdpPacketUnreachableConnection || data[1] == cUdpPacketRequestRemap)
|
||||
return(true);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
|
||||
inline int UdpConnection::GetEncryptCode() const
|
||||
{
|
||||
UdpGuard guard(&mGuard);
|
||||
return(mConnectionConfig.encryptCode);
|
||||
}
|
||||
|
||||
inline int UdpConnection::GetConnectCode() const
|
||||
{
|
||||
UdpGuard guard(&mGuard);
|
||||
return(mConnectCode);
|
||||
}
|
||||
|
||||
inline int UdpConnection::LastReceive(UdpClockStamp useStamp) const
|
||||
{
|
||||
UdpGuard guard(&mGuard);
|
||||
return(UdpMisc::ClockDiff(mLastReceiveTime, useStamp));
|
||||
}
|
||||
|
||||
inline int UdpConnection::LastReceive() const
|
||||
{
|
||||
UdpGuard guard(&mGuard);
|
||||
if (mUdpManager == NULL)
|
||||
return(0);
|
||||
return(mUdpManager->CachedClockElapsed(mLastReceiveTime));
|
||||
}
|
||||
|
||||
inline int UdpConnection::ConnectionAge() const
|
||||
{
|
||||
UdpGuard guard(&mGuard);
|
||||
if (mUdpManager == NULL)
|
||||
return(0);
|
||||
return(mUdpManager->CachedClockElapsed(mConnectionCreateTime));
|
||||
}
|
||||
|
||||
inline int UdpConnection::LastSend() const
|
||||
{
|
||||
UdpGuard guard(&mGuard);
|
||||
if (mUdpManager == NULL)
|
||||
return(0);
|
||||
return(mUdpManager->CachedClockElapsed(mLastSendTime));
|
||||
}
|
||||
|
||||
inline udp_ushort UdpConnection::ServerSyncStampShort() const
|
||||
{
|
||||
UdpGuard guard(&mGuard);
|
||||
if (mUdpManager == NULL)
|
||||
return(0);
|
||||
return((udp_ushort)(mUdpManager->LocalSyncStampShort() + (mSyncTimeDelta & 0xffff)));
|
||||
}
|
||||
|
||||
inline udp_uint UdpConnection::ServerSyncStampLong() const
|
||||
{
|
||||
UdpGuard guard(&mGuard);
|
||||
if (mUdpManager == NULL)
|
||||
return(0);
|
||||
return(mUdpManager->LocalSyncStampLong() + mSyncTimeDelta);
|
||||
}
|
||||
|
||||
inline int UdpConnection::ServerSyncStampShortElapsed(udp_ushort syncStamp) const
|
||||
{
|
||||
return(UdpMisc::SyncStampShortDeltaTime(syncStamp, ServerSyncStampShort()));
|
||||
}
|
||||
|
||||
inline int UdpConnection::ServerSyncStampLongElapsed(udp_uint syncStamp) const
|
||||
{
|
||||
return(UdpMisc::SyncStampLongDeltaTime(syncStamp, ServerSyncStampLong()));
|
||||
}
|
||||
|
||||
inline UdpManager *UdpConnection::GetUdpManager() const
|
||||
{
|
||||
UdpGuard guard(&mGuard);
|
||||
return(mUdpManager);
|
||||
}
|
||||
|
||||
inline UdpConnection::Status UdpConnection::GetStatus() const
|
||||
{
|
||||
UdpGuard guard(&mGuard);
|
||||
return(mStatus);
|
||||
}
|
||||
|
||||
inline UdpConnection::DisconnectReason UdpConnection::GetDisconnectReason() const
|
||||
{
|
||||
UdpGuard guard(&mGuard);
|
||||
return(mDisconnectReason);
|
||||
}
|
||||
|
||||
inline UdpConnection::DisconnectReason UdpConnection::GetOtherSideDisconnectReason() const
|
||||
{
|
||||
UdpGuard guard(&mGuard);
|
||||
return(mOtherSideDisconnectReason);
|
||||
}
|
||||
|
||||
inline int UdpConnection::OutgoingBytesLastSecond()
|
||||
{
|
||||
UdpGuard guard(&mGuard);
|
||||
if (mUdpManager == NULL)
|
||||
return(0);
|
||||
|
||||
ExpireSendBin();
|
||||
return(mOutgoingBytesLastSecond);
|
||||
}
|
||||
|
||||
inline int UdpConnection::IncomingBytesLastSecond()
|
||||
{
|
||||
UdpGuard guard(&mGuard);
|
||||
if (mUdpManager == NULL)
|
||||
return(0);
|
||||
|
||||
ExpireReceiveBin();
|
||||
return(mIncomingBytesLastSecond);
|
||||
}
|
||||
|
||||
inline void UdpConnection::SetPassThroughData(void *passThroughData)
|
||||
{
|
||||
UdpGuard guard(&mGuard);
|
||||
mPassThroughData = passThroughData;
|
||||
}
|
||||
|
||||
inline void *UdpConnection::GetPassThroughData() const
|
||||
{
|
||||
UdpGuard guard(&mGuard);
|
||||
return(mPassThroughData);
|
||||
}
|
||||
|
||||
inline UdpPlatformAddress UdpConnection::GetDestinationIp() const
|
||||
{
|
||||
UdpGuard guard(&mGuard);
|
||||
return(mIp);
|
||||
}
|
||||
|
||||
inline int UdpConnection::GetDestinationPort() const
|
||||
{
|
||||
UdpGuard guard(&mGuard);
|
||||
return(mPort);
|
||||
}
|
||||
|
||||
inline void UdpConnection::SetNoDataTimeout(int noDataTimeout)
|
||||
{
|
||||
UdpGuard guard(&mGuard);
|
||||
mNoDataTimeout = noDataTimeout;
|
||||
}
|
||||
|
||||
inline int UdpConnection::GetNoDataTimeout() const
|
||||
{
|
||||
UdpGuard guard(&mGuard);
|
||||
return(mNoDataTimeout);
|
||||
}
|
||||
|
||||
inline void UdpConnection::Disconnect(int flushTimeout)
|
||||
{
|
||||
UdpRef ref(this); // in case application releases us during the disconnect, we need to hold this reference so our guard object can be destroyed first
|
||||
UdpGuard guard(&mGuard);
|
||||
InternalDisconnect(flushTimeout, cDisconnectReasonApplication);
|
||||
}
|
||||
|
||||
inline void UdpConnection::SetKeepAliveDelay(int keepAliveDelay)
|
||||
{
|
||||
UdpGuard guard(&mGuard);
|
||||
mKeepAliveDelay = keepAliveDelay;
|
||||
}
|
||||
|
||||
inline int UdpConnection::GetKeepAliveDelay() const
|
||||
{
|
||||
UdpGuard guard(&mGuard);
|
||||
return(mKeepAliveDelay);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,124 @@
|
||||
#ifndef UDPLIBRARY_UDPDRIVER_H
|
||||
#define UDPLIBRARY_UDPDRIVER_H
|
||||
|
||||
#include "UdpTypes.h"
|
||||
|
||||
namespace UdpLibrary
|
||||
{
|
||||
|
||||
// Copyright 2004 Sony Online Entertainment, all rights reserved.
|
||||
// Author: Jeff Petersen
|
||||
|
||||
class UdpDriver
|
||||
{
|
||||
public:
|
||||
virtual ~UdpDriver() {}
|
||||
|
||||
// socket related functions
|
||||
virtual bool SocketOpen(int port, int incomingBufferSize, int outgoingBufferSize, const char *bindIpAddress) = 0; // returns true if successful, false if it could not allocate/bind the socket to specified port
|
||||
virtual void SocketClose() = 0;
|
||||
virtual int SocketReceive(char *buffer, int bufferSize, UdpPlatformAddress *ipAddress, int *port) = 0; // returns bytes read, -1 if no data, 0 for ICMP errors (from ip/port is filled in with who got the error)
|
||||
virtual bool SocketSend(const char *data, int dataLen, const UdpPlatformAddress *ipAddress, int port) = 0;
|
||||
virtual void SocketSendPortAlive(const char *data, int dataLen, const UdpPlatformAddress *ipAddress, int port) = 0;
|
||||
virtual bool SocketGetLocalIp(UdpPlatformAddress *ipAddress) = 0;
|
||||
virtual int SocketGetLocalPort() = 0;
|
||||
|
||||
// non-socket related functions
|
||||
virtual bool GetHostByName(UdpPlatformAddress *ipAddress, const char *hostName) = 0;
|
||||
virtual bool GetSelfAddress(UdpPlatformAddress *ipAddress, bool preferNoRoute) = 0;
|
||||
virtual void Sleep(int milliseconds) = 0;
|
||||
virtual UdpClockStamp Clock() = 0;
|
||||
};
|
||||
|
||||
class UdpPlatformData;
|
||||
class UdpPlatformDriver : public UdpDriver
|
||||
{
|
||||
public:
|
||||
UdpPlatformDriver();
|
||||
virtual ~UdpPlatformDriver();
|
||||
|
||||
// socket related functions
|
||||
virtual bool SocketOpen(int port, int incomingBufferSize, int outgoingBufferSize, const char *bindIpAddress);
|
||||
virtual void SocketClose();
|
||||
virtual int SocketReceive(char *buffer, int bufferSize, UdpPlatformAddress *ipAddress, int *port);
|
||||
virtual bool SocketSend(const char *data, int dataLen, const UdpPlatformAddress *ipAddress, int port);
|
||||
virtual void SocketSendPortAlive(const char *data, int dataLen, const UdpPlatformAddress *ipAddress, int port);
|
||||
virtual bool SocketGetLocalIp(UdpPlatformAddress *ipAddress);
|
||||
virtual int SocketGetLocalPort();
|
||||
|
||||
// non-socket related functions that only the platform driver has
|
||||
virtual bool GetHostByName(UdpPlatformAddress *ipAddress, const char *hostName);
|
||||
virtual bool GetSelfAddress(UdpPlatformAddress *ipAddress, bool preferNoRoute);
|
||||
virtual void Sleep(int milliseconds);
|
||||
virtual UdpClockStamp Clock();
|
||||
|
||||
private:
|
||||
UdpPlatformData *mData;
|
||||
};
|
||||
|
||||
class UdpPlatformGuardData;
|
||||
class UdpPlatformGuardObject
|
||||
{
|
||||
public:
|
||||
UdpPlatformGuardObject();
|
||||
~UdpPlatformGuardObject();
|
||||
void Enter();
|
||||
void Leave();
|
||||
|
||||
private:
|
||||
UdpPlatformGuardData *mData;
|
||||
};
|
||||
|
||||
class UdpGuardedRefCount : public UdpRefCount
|
||||
{
|
||||
// portable: implemented in UdpLibrary.cpp
|
||||
public:
|
||||
virtual void AddRef() const;
|
||||
virtual void Release() const;
|
||||
|
||||
protected:
|
||||
mutable UdpPlatformGuardObject mGuard;
|
||||
};
|
||||
|
||||
class UdpPlatformThreadData;
|
||||
class UdpPlatformThreadObject : public UdpGuardedRefCount
|
||||
{
|
||||
public:
|
||||
UdpPlatformThreadObject();
|
||||
|
||||
virtual void Start(); // starts thread running (can't do this in constructor because we need to give derived classes time to finish constructing)
|
||||
virtual bool IsRunning();
|
||||
virtual void Run() = 0;
|
||||
|
||||
protected:
|
||||
virtual ~UdpPlatformThreadObject();
|
||||
|
||||
public:
|
||||
UdpPlatformThreadData *mThreadData;
|
||||
};
|
||||
|
||||
class UdpPlatformAddress
|
||||
{
|
||||
public:
|
||||
UdpPlatformAddress();
|
||||
UdpPlatformAddress(const UdpPlatformAddress &source);
|
||||
bool operator==(const UdpPlatformAddress &e) const;
|
||||
UdpPlatformAddress &operator=(const UdpPlatformAddress &e);
|
||||
char *GetAddress(char *buffer, int bufferLen) const;
|
||||
void SetAddress(const char *address);
|
||||
int GetHash();
|
||||
|
||||
protected:
|
||||
friend class UdpPlatformDriver;
|
||||
// note: platforms are required to be able to store their representation of the address in these 4 bytes
|
||||
// if we come across a platform that needs more space than this (or when we start doing IPv6), then we will
|
||||
// increase this space a bit. This is sufficient for every platform so far and it avoids us having to
|
||||
// do an allocation, particularly since these things are passed around by-value a lot.
|
||||
unsigned char mData[4];
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
+467
@@ -0,0 +1,467 @@
|
||||
// Copyright 2004 Sony Online Entertainment, all rights reserved.
|
||||
// Author: Jeff Petersen
|
||||
|
||||
#include "UdpDriver.h"
|
||||
#include "UdpHelper.h"
|
||||
|
||||
#include <memory.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <netinet/ip_icmp.h> // needed by gcc 3.1 for linux
|
||||
#include <pthread.h>
|
||||
|
||||
|
||||
namespace UdpLibrary
|
||||
{
|
||||
|
||||
typedef int SOCKET;
|
||||
const int INVALID_SOCKET = 0xFFFFFFFF;
|
||||
const int SOCKET_ERROR = 0xFFFFFFFF;
|
||||
|
||||
int IcmpReceive(SOCKET socket, unsigned *ipAddress, int *port);
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// internal data definition
|
||||
////////////////////////////////////////////////////////////
|
||||
class UdpPlatformData
|
||||
{
|
||||
public:
|
||||
SOCKET socket;
|
||||
unsigned startTtl;
|
||||
UdpPlatformGuardObject clockGuard;
|
||||
UdpClockStamp lastStamp;
|
||||
UdpClockStamp currentCorrection;
|
||||
};
|
||||
|
||||
class UdpPlatformGuardData
|
||||
{
|
||||
public:
|
||||
pthread_mutex_t mutex;
|
||||
};
|
||||
|
||||
class UdpPlatformThreadData
|
||||
{
|
||||
public:
|
||||
pthread_t handle;
|
||||
bool running;
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// platform specific implementation
|
||||
////////////////////////////////////////////////////////////
|
||||
UdpPlatformDriver::UdpPlatformDriver()
|
||||
{
|
||||
mData = new UdpPlatformData;
|
||||
mData->socket = INVALID_SOCKET;
|
||||
mData->startTtl = 32;
|
||||
mData->currentCorrection = 0;
|
||||
mData->lastStamp = 0;
|
||||
}
|
||||
|
||||
UdpPlatformDriver::~UdpPlatformDriver()
|
||||
{
|
||||
delete mData;
|
||||
}
|
||||
|
||||
bool UdpPlatformDriver::SocketOpen(int port, int incomingBufferSize, int outgoingBufferSize, const char *bindIpAddress)
|
||||
{
|
||||
mData->socket = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
if (mData->socket != INVALID_SOCKET)
|
||||
{
|
||||
// open socket stuff
|
||||
unsigned long nb = 1;
|
||||
int err = ioctl(mData->socket, FIONBIO, &nb);
|
||||
assert(err != -1);
|
||||
nb = outgoingBufferSize;
|
||||
err = setsockopt(mData->socket, SOL_SOCKET, SO_SNDBUF, &nb, sizeof(nb));
|
||||
assert(err == 0);
|
||||
nb = incomingBufferSize;
|
||||
err = setsockopt(mData->socket, SOL_SOCKET, SO_RCVBUF, &nb, sizeof(nb));
|
||||
assert(err == 0);
|
||||
nb = 0;
|
||||
err = setsockopt(mData->socket, SOL_SOCKET, SO_BSDCOMPAT, &nb, sizeof(nb));
|
||||
assert(err == 0);
|
||||
nb = 1;
|
||||
err = setsockopt(mData->socket, SOL_IP, IP_RECVERR, &nb, sizeof(nb));
|
||||
assert(err == 0);
|
||||
int optLen = sizeof(mData->startTtl);
|
||||
getsockopt(mData->socket, IPPROTO_IP, IP_TTL, &mData->startTtl, (socklen_t *)&optLen);
|
||||
|
||||
// bind it to any address
|
||||
struct sockaddr_in addr_loc;
|
||||
addr_loc.sin_family = PF_INET;
|
||||
addr_loc.sin_port = htons((unsigned short)port);
|
||||
addr_loc.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
if (bindIpAddress != NULL && bindIpAddress[0] != 0)
|
||||
{
|
||||
unsigned long address = inet_addr(bindIpAddress);
|
||||
if (address != INADDR_NONE)
|
||||
{
|
||||
addr_loc.sin_addr.s_addr = address; // this is already in network order from the call above
|
||||
}
|
||||
}
|
||||
|
||||
if (bind(mData->socket, (struct sockaddr *)&addr_loc, sizeof(addr_loc)) != 0)
|
||||
{
|
||||
SocketClose();
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
void UdpPlatformDriver::SocketClose()
|
||||
{
|
||||
if (mData->socket != INVALID_SOCKET)
|
||||
{
|
||||
close(mData->socket);
|
||||
mData->socket = INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
|
||||
int UdpPlatformDriver::SocketReceive(char *buffer, int bufferSize, UdpPlatformAddress *ipAddress, int *port)
|
||||
{
|
||||
// check for standard packets
|
||||
struct sockaddr_in addr_from;
|
||||
socklen_t sf = sizeof(addr_from);
|
||||
int res = recvfrom(mData->socket, buffer, bufferSize, 0, (struct sockaddr *)&addr_from, &sf);
|
||||
if (res != SOCKET_ERROR)
|
||||
{
|
||||
memcpy(ipAddress->mData, &addr_from.sin_addr.s_addr, 4);
|
||||
*port = (int)ntohs(addr_from.sin_port);
|
||||
return(res);
|
||||
}
|
||||
|
||||
// out of standard packets, check for ICMP error packets
|
||||
unsigned address;
|
||||
int ret = IcmpReceive(mData->socket, &address, port);
|
||||
memcpy(ipAddress->mData, &address, 4);
|
||||
return(ret);
|
||||
}
|
||||
|
||||
bool UdpPlatformDriver::SocketSend(const char *data, int dataLen, const UdpPlatformAddress *ipAddress, int port)
|
||||
{
|
||||
struct sockaddr_in addr_dest;
|
||||
addr_dest.sin_family = PF_INET;
|
||||
memcpy(&addr_dest.sin_addr.s_addr, ipAddress->mData, 4);
|
||||
addr_dest.sin_port = htons((unsigned short)port);
|
||||
if (SOCKET_ERROR == sendto(mData->socket, data, dataLen, 0, (struct sockaddr *)&addr_dest, sizeof(addr_dest)))
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
void UdpPlatformDriver::SocketSendPortAlive(const char *data, int dataLen, const UdpPlatformAddress *ipAddress, int port)
|
||||
{
|
||||
// port alive packet send
|
||||
unsigned long val = 5;
|
||||
setsockopt(mData->socket, IPPROTO_IP, IP_TTL, &val, sizeof(val));
|
||||
SocketSend(data, dataLen, ipAddress, port);
|
||||
val = mData->startTtl;
|
||||
setsockopt(mData->socket, IPPROTO_IP, IP_TTL, &val, sizeof(val));
|
||||
}
|
||||
|
||||
bool UdpPlatformDriver::SocketGetLocalIp(UdpPlatformAddress *ipAddress)
|
||||
{
|
||||
struct sockaddr_in addr_self;
|
||||
memset(&addr_self, 0, sizeof(addr_self));
|
||||
socklen_t len = sizeof(addr_self);
|
||||
getsockname(mData->socket, (struct sockaddr *)&addr_self, &len);
|
||||
memcpy(ipAddress->mData, &addr_self.sin_addr.s_addr, 4);
|
||||
return(true);
|
||||
}
|
||||
|
||||
int UdpPlatformDriver::SocketGetLocalPort()
|
||||
{
|
||||
struct sockaddr_in addr_self;
|
||||
memset(&addr_self, 0, sizeof(addr_self));
|
||||
socklen_t len = sizeof(addr_self);
|
||||
getsockname(mData->socket, (struct sockaddr *)&addr_self, &len);
|
||||
return(ntohs(addr_self.sin_port));
|
||||
}
|
||||
|
||||
bool UdpPlatformDriver::GetHostByName(UdpPlatformAddress *ipAddress, const char *hostName)
|
||||
{
|
||||
unsigned long address = inet_addr(hostName);
|
||||
if (address == INADDR_NONE)
|
||||
{
|
||||
struct hostent *lphp;
|
||||
lphp = gethostbyname(hostName);
|
||||
if (lphp == NULL)
|
||||
{
|
||||
address = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
address = ((struct in_addr *)(lphp->h_addr))->s_addr;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(ipAddress->mData, &address, 4);
|
||||
return(address != 0);
|
||||
}
|
||||
|
||||
bool UdpPlatformDriver::GetSelfAddress(UdpPlatformAddress *ipAddress, bool preferNoRoute)
|
||||
{
|
||||
unsigned long address = 0;
|
||||
|
||||
char hostname[1024];
|
||||
if (gethostname(hostname, sizeof(hostname)) == 0)
|
||||
{
|
||||
struct hostent *entry = gethostbyname(hostname);
|
||||
if (entry != NULL)
|
||||
{
|
||||
for (int i = 0; entry->h_addr_list[i] != 0; i++)
|
||||
{
|
||||
in_addr *sin = (in_addr *)entry->h_addr_list[i];
|
||||
int s_net = (sin->s_addr >> 24);
|
||||
int s_host = ((sin->s_addr >> 16) & 0xff);
|
||||
|
||||
if (address == 0)
|
||||
{
|
||||
address = sin->s_addr;
|
||||
}
|
||||
else if (s_net != 127) // never return 127.0.0.1 as self address if there is another option
|
||||
{
|
||||
bool routeable = true;
|
||||
if (s_net == 10 || (s_net == 172 && s_host == 16) || (s_net == 192 && s_host == 168))
|
||||
{
|
||||
routeable = false;
|
||||
}
|
||||
|
||||
if (preferNoRoute && !routeable)
|
||||
{
|
||||
address = sin->s_addr;
|
||||
}
|
||||
else if (!preferNoRoute && routeable)
|
||||
{
|
||||
address = sin->s_addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(ipAddress->mData, &address, 4);
|
||||
return(address != 0);
|
||||
}
|
||||
|
||||
void UdpPlatformDriver::Sleep(int milliseconds)
|
||||
{
|
||||
struct timeval tv;
|
||||
tv.tv_sec = milliseconds / 1000;
|
||||
tv.tv_usec = (milliseconds % 1000) * 1000;
|
||||
select(0, 0, 0, 0, &tv);
|
||||
}
|
||||
|
||||
UdpClockStamp UdpPlatformDriver::Clock()
|
||||
{
|
||||
UdpGuard guard(&mData->clockGuard);
|
||||
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
UdpClockStamp cs = static_cast<UdpClockStamp>(tv.tv_sec) * 1000 + static_cast<UdpClockStamp>(tv.tv_usec / 1000);
|
||||
cs += mData->currentCorrection;
|
||||
if (cs < mData->lastStamp)
|
||||
{
|
||||
// clock moved backwards (somebody changed it), don't ever let this happen
|
||||
// if clock moves forward, there is no way we can recognize it, code will just
|
||||
// have to deal with it. In the case of the UdpLibrary, it will likely result
|
||||
// in a ton of pending packets thinking they have gotten lost and being sent, fairly harmless.
|
||||
mData->currentCorrection += (mData->lastStamp - cs);
|
||||
cs = mData->lastStamp;
|
||||
}
|
||||
mData->lastStamp = cs;
|
||||
return(cs);
|
||||
}
|
||||
|
||||
int IcmpReceive(SOCKET socket, unsigned *address, int *port)
|
||||
{
|
||||
// we can use some ICMP errors to our advantage to more quickly realize that the connection on the other end has disappeared
|
||||
struct sock_extended_err
|
||||
{
|
||||
unsigned ee_errno;
|
||||
unsigned char ee_origin;
|
||||
unsigned char ee_type;
|
||||
unsigned char ee_code;
|
||||
unsigned char ee_pad;
|
||||
unsigned ee_info;
|
||||
unsigned ee_data;
|
||||
};
|
||||
|
||||
unsigned char msg_control[1024];
|
||||
struct msghdr msgh = {0};
|
||||
struct sockaddr_in msg_name;
|
||||
socklen_t sf = sizeof(msg_name);
|
||||
msgh.msg_name = &msg_name;
|
||||
msgh.msg_namelen = sf;
|
||||
msgh.msg_iov = 0;
|
||||
msgh.msg_iovlen = 0;
|
||||
msgh.msg_control = msg_control;
|
||||
msgh.msg_controllen = sizeof(msg_control);
|
||||
|
||||
int err = recvmsg(socket, &msgh, MSG_ERRQUEUE);
|
||||
if (err == -1)
|
||||
return(-1);
|
||||
|
||||
struct cmsghdr *cmsg;
|
||||
for(cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(&msgh, cmsg))
|
||||
{
|
||||
if(cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR)
|
||||
{
|
||||
sock_extended_err *ee = (sock_extended_err *)CMSG_DATA(cmsg);
|
||||
if (ee->ee_origin == 2 && ee->ee_type == ICMP_DEST_UNREACH) // ICMP origin, destination-unreachable error // note: process all code types: && ee->ee_code == ICMP_PORT_UNREACH
|
||||
{
|
||||
memcpy(address, &msg_name.sin_addr.s_addr, 4);
|
||||
*port = (int)ntohs(msg_name.sin_port);
|
||||
return(0); // ICMP error return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return(-1);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// UdpPlatformGuardObject object implementation
|
||||
////////////////////////////////////////////////////////////
|
||||
UdpPlatformGuardObject::UdpPlatformGuardObject()
|
||||
{
|
||||
mData = new UdpPlatformGuardData;
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
pthread_mutex_init(&mData->mutex, &attr);
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
}
|
||||
|
||||
UdpPlatformGuardObject::~UdpPlatformGuardObject()
|
||||
{
|
||||
pthread_mutex_destroy(&mData->mutex);
|
||||
delete mData;
|
||||
}
|
||||
|
||||
void UdpPlatformGuardObject::Enter()
|
||||
{
|
||||
pthread_mutex_lock(&mData->mutex);
|
||||
}
|
||||
|
||||
void UdpPlatformGuardObject::Leave()
|
||||
{
|
||||
pthread_mutex_unlock(&mData->mutex);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
// UdpPlatformThreadObject implementation
|
||||
///////////////////////////////////////////////////////////////
|
||||
void *GoThread(void *param)
|
||||
{
|
||||
UdpPlatformThreadObject *thread = (UdpPlatformThreadObject *)param;
|
||||
thread->mThreadData->running = true;
|
||||
thread->Run();
|
||||
thread->mThreadData->running = false;
|
||||
thread->mThreadData->handle = 0;
|
||||
thread->Release();
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
UdpPlatformThreadObject::~UdpPlatformThreadObject()
|
||||
{
|
||||
delete mThreadData;
|
||||
}
|
||||
|
||||
void UdpPlatformThreadObject::Start()
|
||||
{
|
||||
AddRef();
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
pthread_create(&mThreadData->handle, &attr, &GoThread, (void *)this);
|
||||
pthread_attr_destroy(&attr);
|
||||
}
|
||||
|
||||
UdpPlatformThreadObject::UdpPlatformThreadObject()
|
||||
{
|
||||
mThreadData = new UdpPlatformThreadData;
|
||||
mThreadData->handle = 0;
|
||||
mThreadData->running = false;
|
||||
}
|
||||
|
||||
bool UdpPlatformThreadObject::IsRunning()
|
||||
{
|
||||
return(mThreadData->running);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// UdpPlatformAddress implementation
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
UdpPlatformAddress::UdpPlatformAddress()
|
||||
{
|
||||
memset(mData, 0, sizeof(mData));
|
||||
}
|
||||
|
||||
UdpPlatformAddress::UdpPlatformAddress(const UdpPlatformAddress &source)
|
||||
{
|
||||
memcpy(mData, source.mData, sizeof(mData));
|
||||
}
|
||||
|
||||
UdpPlatformAddress &UdpPlatformAddress::operator=(const UdpPlatformAddress &e)
|
||||
{
|
||||
memcpy(mData, e.mData, sizeof(mData));
|
||||
return(*this);
|
||||
}
|
||||
|
||||
char *UdpPlatformAddress::GetAddress(char *buffer, int bufferLen) const
|
||||
{
|
||||
if (bufferLen < 16)
|
||||
{
|
||||
*buffer = 0;
|
||||
return(buffer);
|
||||
}
|
||||
assert(buffer != NULL);
|
||||
sprintf(buffer, "%d.%d.%d.%d", mData[0], mData[1], mData[2], mData[3]);
|
||||
return(buffer);
|
||||
}
|
||||
|
||||
void UdpPlatformAddress::SetAddress(const char *address)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
mData[i] = (unsigned char)strtol(address, NULL, 10);
|
||||
while (*address >= '0' && *address <= '9')
|
||||
address++;
|
||||
if (*address != 0)
|
||||
address++;
|
||||
}
|
||||
}
|
||||
|
||||
bool UdpPlatformAddress::operator==(const UdpPlatformAddress &e) const
|
||||
{
|
||||
return(memcmp(mData, e.mData, sizeof(mData)) == 0);
|
||||
}
|
||||
|
||||
|
||||
int UdpPlatformAddress::GetHash()
|
||||
{
|
||||
return(*(int *)mData);
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
+421
@@ -0,0 +1,421 @@
|
||||
// Copyright 2004 Sony Online Entertainment, all rights reserved.
|
||||
// Author: Jeff Petersen
|
||||
|
||||
#if defined(WIN32)
|
||||
#define _CRT_SECURE_NO_DEPRECATE // gets rid of deprecation warnings in VS 2005 (don't want to change to secure-versions as it hampers portability)
|
||||
#endif
|
||||
|
||||
#include "UdpDriver.h"
|
||||
#include "UdpHelper.h"
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
#pragma warning(push, 3)
|
||||
#pragma warning(disable:4706)
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <windows.h>
|
||||
|
||||
#ifndef UDPLIBRARY_SINGLE_THREAD
|
||||
#include <process.h>
|
||||
#endif
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
|
||||
namespace UdpLibrary
|
||||
{
|
||||
|
||||
typedef int socklen_t;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// internal data definition
|
||||
////////////////////////////////////////////////////////////
|
||||
class UdpPlatformData
|
||||
{
|
||||
public:
|
||||
SOCKET socket;
|
||||
unsigned startTtl;
|
||||
UdpPlatformGuardObject clockGuard;
|
||||
unsigned globalLow;
|
||||
int globalHigh;
|
||||
};
|
||||
|
||||
class UdpPlatformGuardData
|
||||
{
|
||||
public:
|
||||
CRITICAL_SECTION mCriticalSection;
|
||||
};
|
||||
|
||||
class UdpPlatformThreadData
|
||||
{
|
||||
public:
|
||||
HANDLE handle;
|
||||
bool running;
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// platform specific implementation
|
||||
////////////////////////////////////////////////////////////
|
||||
UdpPlatformDriver::UdpPlatformDriver()
|
||||
{
|
||||
mData = new UdpPlatformData;
|
||||
mData->socket = INVALID_SOCKET;
|
||||
mData->startTtl = 32;
|
||||
mData->globalLow = 0;
|
||||
mData->globalHigh = 0;
|
||||
|
||||
WSADATA wsaData;
|
||||
#if defined(UDPLIBRARY_WINSOCK2)
|
||||
WSAStartup(MAKEWORD(2,0), &wsaData);
|
||||
#else
|
||||
WSAStartup(MAKEWORD(1,1), &wsaData);
|
||||
#endif
|
||||
}
|
||||
|
||||
UdpPlatformDriver::~UdpPlatformDriver()
|
||||
{
|
||||
WSACleanup();
|
||||
|
||||
delete mData;
|
||||
}
|
||||
|
||||
bool UdpPlatformDriver::SocketOpen(int port, int incomingBufferSize, int outgoingBufferSize, const char *bindIpAddress)
|
||||
{
|
||||
mData->socket = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
if (mData->socket != INVALID_SOCKET)
|
||||
{
|
||||
unsigned long lb = 1;
|
||||
int err = ioctlsocket(mData->socket, FIONBIO, &lb);
|
||||
int nb = outgoingBufferSize;
|
||||
err = setsockopt(mData->socket, SOL_SOCKET, SO_SNDBUF, (char *)&nb, sizeof(nb));
|
||||
nb = incomingBufferSize;
|
||||
err = setsockopt(mData->socket, SOL_SOCKET, SO_RCVBUF, (char *)&nb, sizeof(nb));
|
||||
int optLen = sizeof(mData->startTtl);
|
||||
getsockopt(mData->socket, IPPROTO_IP, IP_TTL, (char *)&mData->startTtl, &optLen);
|
||||
|
||||
// bind it to any address
|
||||
struct sockaddr_in addr_loc;
|
||||
addr_loc.sin_family = PF_INET;
|
||||
addr_loc.sin_port = htons((unsigned short)port);
|
||||
addr_loc.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
if (bindIpAddress != NULL && bindIpAddress[0] != 0)
|
||||
{
|
||||
unsigned long address = inet_addr(bindIpAddress);
|
||||
if (address != INADDR_NONE)
|
||||
{
|
||||
addr_loc.sin_addr.s_addr = address; // this is already in network order from the call above
|
||||
}
|
||||
}
|
||||
|
||||
if (bind(mData->socket, (struct sockaddr *)&addr_loc, sizeof(addr_loc)) != 0)
|
||||
{
|
||||
SocketClose();
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
void UdpPlatformDriver::SocketClose()
|
||||
{
|
||||
if (mData->socket != INVALID_SOCKET)
|
||||
{
|
||||
closesocket(mData->socket);
|
||||
mData->socket = INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
|
||||
int UdpPlatformDriver::SocketReceive(char *buffer, int bufferSize, UdpPlatformAddress *ipAddress, int *port)
|
||||
{
|
||||
struct sockaddr_in addr_from;
|
||||
socklen_t sf = sizeof(addr_from);
|
||||
int res = recvfrom(mData->socket, buffer, bufferSize, 0, (struct sockaddr *)&addr_from, &sf);
|
||||
|
||||
if (res != SOCKET_ERROR)
|
||||
{
|
||||
memcpy(ipAddress->mData, &addr_from.sin_addr.s_addr, 4);
|
||||
*port = (int)ntohs(addr_from.sin_port);
|
||||
return(res);
|
||||
}
|
||||
|
||||
// windows is kind enough to put ICMP error packets inline within the stream as errors, so we
|
||||
// can easily see the errors indicating that the destination address is unreachable for some reason
|
||||
if (WSAGetLastError() == WSAECONNRESET)
|
||||
{
|
||||
memcpy(ipAddress->mData, &addr_from.sin_addr.s_addr, 4);
|
||||
*port = (int)ntohs(addr_from.sin_port);
|
||||
return(0);
|
||||
}
|
||||
|
||||
return(-1); // no more packets found
|
||||
}
|
||||
|
||||
bool UdpPlatformDriver::SocketSend(const char *data, int dataLen, const UdpPlatformAddress *ipAddress, int port)
|
||||
{
|
||||
struct sockaddr_in addr_dest;
|
||||
addr_dest.sin_family = PF_INET;
|
||||
memcpy(&addr_dest.sin_addr.s_addr, ipAddress->mData, 4);
|
||||
addr_dest.sin_port = htons((unsigned short)port);
|
||||
if (SOCKET_ERROR == sendto(mData->socket, data, dataLen, 0, (struct sockaddr *)&addr_dest, sizeof(addr_dest)))
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
void UdpPlatformDriver::SocketSendPortAlive(const char *data, int dataLen, const UdpPlatformAddress *ipAddress, int port)
|
||||
{
|
||||
int val = 5;
|
||||
setsockopt(mData->socket, IPPROTO_IP, IP_TTL, (char *)&val, sizeof(val));
|
||||
SocketSend(data, dataLen, ipAddress, port);
|
||||
setsockopt(mData->socket, IPPROTO_IP, IP_TTL, (char *)&mData->startTtl, sizeof(mData->startTtl));
|
||||
}
|
||||
|
||||
bool UdpPlatformDriver::SocketGetLocalIp(UdpPlatformAddress *ipAddress)
|
||||
{
|
||||
struct sockaddr_in addr_self;
|
||||
memset(&addr_self, 0, sizeof(addr_self));
|
||||
socklen_t len = sizeof(addr_self);
|
||||
getsockname(mData->socket, (struct sockaddr *)&addr_self, &len);
|
||||
memcpy(ipAddress->mData, &addr_self.sin_addr.s_addr, 4);
|
||||
return(true);
|
||||
}
|
||||
|
||||
int UdpPlatformDriver::SocketGetLocalPort()
|
||||
{
|
||||
struct sockaddr_in addr_self;
|
||||
memset(&addr_self, 0, sizeof(addr_self));
|
||||
socklen_t len = sizeof(addr_self);
|
||||
getsockname(mData->socket, (struct sockaddr *)&addr_self, &len);
|
||||
return(ntohs(addr_self.sin_port));
|
||||
}
|
||||
|
||||
bool UdpPlatformDriver::GetHostByName(UdpPlatformAddress *ipAddress, const char *hostName)
|
||||
{
|
||||
unsigned long address = inet_addr(hostName);
|
||||
if (address == INADDR_NONE)
|
||||
{
|
||||
struct hostent *lphp;
|
||||
lphp = gethostbyname(hostName);
|
||||
if (lphp == NULL)
|
||||
{
|
||||
address = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
address = ((struct in_addr *)(lphp->h_addr))->s_addr;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(ipAddress->mData, &address, 4);
|
||||
return(address != 0);
|
||||
}
|
||||
|
||||
bool UdpPlatformDriver::GetSelfAddress(UdpPlatformAddress *ipAddress, bool preferNoRoute)
|
||||
{
|
||||
unsigned long address = 0;
|
||||
|
||||
char hostname[1024];
|
||||
if (gethostname(hostname, sizeof(hostname)) == 0)
|
||||
{
|
||||
struct hostent *entry = gethostbyname(hostname);
|
||||
if (entry != NULL)
|
||||
{
|
||||
for (int i = 0; entry->h_addr_list[i] != 0; i++)
|
||||
{
|
||||
in_addr *sin = (in_addr *)entry->h_addr_list[i];
|
||||
|
||||
if (address == 0)
|
||||
{
|
||||
address = sin->s_addr;
|
||||
}
|
||||
else if (sin->s_net != 127) // never return 127.0.0.1 as self address if there is another option
|
||||
{
|
||||
bool routeable = true;
|
||||
if (sin->s_net == 10 || (sin->s_net == 172 && sin->s_host == 16) || (sin->s_net == 192 && sin->s_host == 168))
|
||||
{
|
||||
routeable = false;
|
||||
}
|
||||
|
||||
if (preferNoRoute && !routeable)
|
||||
{
|
||||
address = sin->s_addr;
|
||||
}
|
||||
else if (!preferNoRoute && routeable)
|
||||
{
|
||||
address = sin->s_addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(ipAddress->mData, &address, 4);
|
||||
return(address != 0);
|
||||
}
|
||||
|
||||
void UdpPlatformDriver::Sleep(int milliseconds)
|
||||
{
|
||||
::Sleep((DWORD)milliseconds);
|
||||
}
|
||||
|
||||
UdpClockStamp UdpPlatformDriver::Clock()
|
||||
{
|
||||
UdpGuard guard(&mData->clockGuard);
|
||||
unsigned low = GetTickCount();
|
||||
if (low < mData->globalLow)
|
||||
{
|
||||
mData->globalHigh++;
|
||||
}
|
||||
mData->globalLow = low;
|
||||
return(((UdpClockStamp)mData->globalHigh << 32) | low);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// UdpPlatformGuardObject object implementation
|
||||
////////////////////////////////////////////////////////////
|
||||
UdpPlatformGuardObject::UdpPlatformGuardObject()
|
||||
{
|
||||
#ifndef UDPLIBRARY_SINGLE_THREAD
|
||||
mData = new UdpPlatformGuardData;
|
||||
InitializeCriticalSection(&mData->mCriticalSection);
|
||||
#endif
|
||||
}
|
||||
|
||||
UdpPlatformGuardObject::~UdpPlatformGuardObject()
|
||||
{
|
||||
#ifndef UDPLIBRARY_SINGLE_THREAD
|
||||
DeleteCriticalSection(&mData->mCriticalSection);
|
||||
delete mData;
|
||||
#endif
|
||||
}
|
||||
|
||||
void UdpPlatformGuardObject::Enter()
|
||||
{
|
||||
#ifndef UDPLIBRARY_SINGLE_THREAD
|
||||
EnterCriticalSection(&mData->mCriticalSection);
|
||||
#endif
|
||||
}
|
||||
|
||||
void UdpPlatformGuardObject::Leave()
|
||||
{
|
||||
#ifndef UDPLIBRARY_SINGLE_THREAD
|
||||
LeaveCriticalSection(&mData->mCriticalSection);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
// UdpPlatformThreadObject implementation
|
||||
///////////////////////////////////////////////////////////////
|
||||
unsigned __stdcall GoThread(void *param)
|
||||
{
|
||||
UdpPlatformThreadObject *thread = (UdpPlatformThreadObject *)param;
|
||||
thread->mThreadData->running = true;
|
||||
thread->Run();
|
||||
thread->mThreadData->running = false;
|
||||
thread->Release();
|
||||
return(0);
|
||||
}
|
||||
|
||||
UdpPlatformThreadObject::~UdpPlatformThreadObject()
|
||||
{
|
||||
#ifndef UDPLIBRARY_SINGLE_THREAD
|
||||
if (mThreadData->handle != NULL)
|
||||
{
|
||||
CloseHandle(mThreadData->handle);
|
||||
mThreadData->handle = NULL;
|
||||
}
|
||||
#endif
|
||||
delete mThreadData;
|
||||
}
|
||||
|
||||
void UdpPlatformThreadObject::Start()
|
||||
{
|
||||
AddRef();
|
||||
#ifndef UDPLIBRARY_SINGLE_THREAD
|
||||
unsigned threadId;
|
||||
mThreadData->handle = (HANDLE)_beginthreadex(NULL, 0, &GoThread, this, 0, &threadId);
|
||||
#else
|
||||
GoThread(this); // run it inline in main thread (blocks til it's finished, so odds are it won't work, but they shouldn't be using it anyhow in this mode)
|
||||
#endif
|
||||
}
|
||||
|
||||
UdpPlatformThreadObject::UdpPlatformThreadObject()
|
||||
{
|
||||
mThreadData = new UdpPlatformThreadData;
|
||||
mThreadData->handle = NULL;
|
||||
mThreadData->running = false;
|
||||
}
|
||||
|
||||
bool UdpPlatformThreadObject::IsRunning()
|
||||
{
|
||||
return(mThreadData->running);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// UdpPlatformAddress implementation
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
UdpPlatformAddress::UdpPlatformAddress()
|
||||
{
|
||||
memset(mData, 0, sizeof(mData));
|
||||
}
|
||||
|
||||
UdpPlatformAddress::UdpPlatformAddress(const UdpPlatformAddress &source)
|
||||
{
|
||||
memcpy(mData, source.mData, sizeof(mData));
|
||||
}
|
||||
|
||||
UdpPlatformAddress &UdpPlatformAddress::operator=(const UdpPlatformAddress &e)
|
||||
{
|
||||
memcpy(mData, e.mData, sizeof(mData));
|
||||
return(*this);
|
||||
}
|
||||
|
||||
char *UdpPlatformAddress::GetAddress(char *buffer, int bufferLen) const
|
||||
{
|
||||
if (bufferLen < 16)
|
||||
{
|
||||
*buffer = 0;
|
||||
return(buffer);
|
||||
}
|
||||
assert(buffer != NULL);
|
||||
sprintf(buffer, "%d.%d.%d.%d", mData[0], mData[1], mData[2], mData[3]);
|
||||
return(buffer);
|
||||
}
|
||||
|
||||
void UdpPlatformAddress::SetAddress(const char *address)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
mData[i] = (unsigned char)atoi(address);
|
||||
while (*address >= '0' && *address <= '9')
|
||||
address++;
|
||||
if (*address != 0)
|
||||
address++;
|
||||
}
|
||||
}
|
||||
|
||||
bool UdpPlatformAddress::operator==(const UdpPlatformAddress &e) const
|
||||
{
|
||||
return(memcmp(mData, e.mData, sizeof(mData)) == 0);
|
||||
}
|
||||
|
||||
|
||||
int UdpPlatformAddress::GetHash()
|
||||
{
|
||||
return(*(int *)mData);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif // WIN32
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
#ifndef UDPLIBRARY_UDPHANDLER_H
|
||||
#define UDPLIBRARY_UDPHANDLER_H
|
||||
|
||||
// Copyright 2004 Sony Online Entertainment, all rights reserved.
|
||||
// Author: Jeff Petersen
|
||||
|
||||
#include "UdpTypes.h"
|
||||
|
||||
namespace UdpLibrary
|
||||
{
|
||||
|
||||
class UdpManagerHandler
|
||||
{
|
||||
public:
|
||||
// This function is called when a listening UdpManager receives a new connection request. If the application wishes to
|
||||
// accept this connection, it is the responsibility of the application to AddRef the UdpConnection object such that it
|
||||
// doesn't get destroyed upon returning from the callback. Additionally, the application should set the handler object
|
||||
// for the connection. See sample code in the UdpLibrary.doc file for an example.
|
||||
virtual void OnConnectRequest(UdpConnection *con);
|
||||
|
||||
// The following functions are called when user-supplied encryption is specified. In particular, the functions may wish
|
||||
// to query the connection object for the encryption-code it is using. The encryption code is simply a randomly generated
|
||||
// 32-bit value that the client and server negotiated during the connection-establishment. It can be used as a key
|
||||
// of sorts for encrypting data uniquely on a per-connection basis. (see UdpConnection::GetEncryptCode())
|
||||
// For the encrypt-functions, the destination buffer will be (sourceLen + userSuppliedEncryptExpansionBytes1) long,
|
||||
// do not overflow it, the debug version has an assert check that ensures that you do not.
|
||||
// For the decrypt-functions, the destination buffer is guaranteed to be large enough to hold what the original
|
||||
// data used to be before it was encrypted.
|
||||
virtual int OnUserSuppliedEncrypt(UdpConnection *con, udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
|
||||
virtual int OnUserSuppliedDecrypt(UdpConnection *con, udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
|
||||
virtual int OnUserSuppliedEncrypt2(UdpConnection *con, udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
|
||||
virtual int OnUserSuppliedDecrypt2(UdpConnection *con, udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
|
||||
};
|
||||
|
||||
class UdpConnectionHandler
|
||||
{
|
||||
public:
|
||||
// This function is called whenever an application-packet is ready to be delivered to the application.
|
||||
// Packets will only ever be delivered to the application during UdpManager::GiveTime calls, so the application
|
||||
// has complete control of when it willing/able to receive packets.
|
||||
virtual void OnRoutePacket(UdpConnection *con, const udp_uchar *data, int dataLen) = 0;
|
||||
|
||||
// This function is called whenever a UdpManager::EstablishConnection call succeeds in establishing the connection.
|
||||
// Generally on client-side establishment of a connection, I prefer to sit in a tight loop polling the connection
|
||||
// to see if it has connected yet or not, rather than using this callback (see sample code)
|
||||
// note: If it is unable to establish the connection, the OnTerminated callback will be called.
|
||||
// note: this is a change from previous behavior, see release notes for details.
|
||||
virtual void OnConnectComplete(UdpConnection *con);
|
||||
|
||||
// This function is called anytime the UdpConnection object changes to a cStatusDisconnected state. Every UdpConnection
|
||||
// object is guaranteed to eventually call this once and only once, even connections that never successfully established.
|
||||
// note: this is a change from previous behavior, see release notes for details
|
||||
virtual void OnTerminated(UdpConnection *con);
|
||||
|
||||
// This function is called anytime an incoming packet fails the CRC check. Normally these packets are just ignored, as
|
||||
// corruption does occur from time to time; however, some applications may wish to log these events as often times, they
|
||||
// are an indication of somebody attempting to hack the data stream.
|
||||
virtual void OnCrcReject(UdpConnection *con, const udp_uchar *data, int dataLen);
|
||||
|
||||
// This function is called anytime a corrupt packet is sensed for some reason.
|
||||
// The reason we give a callback for it is because you may wish to log it as it could be an early indicator of
|
||||
// somebody trying to hack the protocol.
|
||||
|
||||
virtual void OnPacketCorrupt(UdpConnection *con, const udp_uchar *data, int dataLen, UdpCorruptionReason reason);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,625 @@
|
||||
#ifndef UDPLIBRARY_UDPHASHTABLE_H
|
||||
#define UDPLIBRARY_UDPHASHTABLE_H
|
||||
|
||||
// Copyright 2004 Sony Online Entertainment, all rights reserved.
|
||||
// Author: Jeff Petersen
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace UdpLibrary
|
||||
{
|
||||
|
||||
|
||||
int NextPrime(int value);
|
||||
|
||||
|
||||
// This is a simple hash table template. Any type of object can be stored in this hash table
|
||||
// provided it can be copied. Resizing the table while in use is generally not recommended.
|
||||
// putting the same object in the table twice is generally not recommended as it makes the find ambiguous
|
||||
struct HashTableStatistics
|
||||
{
|
||||
int tableSize;
|
||||
int usedSlots;
|
||||
int totalEntries;
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// HashTable
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template<typename T> class HashTable
|
||||
{
|
||||
public:
|
||||
HashTable(int hashSize);
|
||||
~HashTable();
|
||||
|
||||
void Insert(T& obj, int hashValue);
|
||||
bool Remove(T& obj, int hashValue);
|
||||
void Reset(); // removes all entries from the table
|
||||
|
||||
T *FindFirst(int hashValue) const; // returns NULL if not found
|
||||
T *FindNext(T *prevResult) const; // returns NULL if not found
|
||||
|
||||
T *WalkFirst() const; // returns NULL if not found
|
||||
T *WalkNext(T *prevResult) const; // returns NULL if not found
|
||||
|
||||
void Resize(int hashSize); // the actual hash size will be rounded up to the next larger prime number (very slow, not recommended)
|
||||
void GetStatistics(HashTableStatistics *stats) const;
|
||||
|
||||
protected:
|
||||
struct HashEntry
|
||||
{
|
||||
T obj;
|
||||
int hashValue;
|
||||
HashEntry *nextEntry;
|
||||
};
|
||||
|
||||
HashEntry **mTable;
|
||||
int mTableSize;
|
||||
int mEntryCount;
|
||||
int mStatUsedSlots;
|
||||
};
|
||||
|
||||
|
||||
template<typename T> HashTable<T>::HashTable(int hashSize)
|
||||
{
|
||||
mTable = NULL;
|
||||
mTableSize = 0;
|
||||
mEntryCount = 0;
|
||||
mStatUsedSlots = 0;
|
||||
Resize(hashSize);
|
||||
}
|
||||
|
||||
template<typename T> HashTable<T>::~HashTable()
|
||||
{
|
||||
Reset(); // deletes everything
|
||||
delete[] mTable;
|
||||
}
|
||||
|
||||
template<typename T> void HashTable<T>::Insert(T& obj, int hashValue)
|
||||
{
|
||||
HashEntry *entry = new HashEntry;
|
||||
entry->obj = obj;
|
||||
entry->hashValue = hashValue;
|
||||
|
||||
int spot = ((unsigned)hashValue) % mTableSize;
|
||||
if (mTable[spot] == NULL)
|
||||
{
|
||||
entry->nextEntry = NULL;
|
||||
mTable[spot] = entry;
|
||||
mStatUsedSlots++;
|
||||
}
|
||||
else
|
||||
{
|
||||
entry->nextEntry = mTable[spot];
|
||||
mTable[spot] = entry;
|
||||
}
|
||||
mEntryCount++;
|
||||
}
|
||||
|
||||
template<typename T> bool HashTable<T>::Remove(T& obj, int hashValue)
|
||||
{
|
||||
int spot = ((unsigned)hashValue) % mTableSize;
|
||||
HashEntry* next = mTable[spot];
|
||||
HashEntry** prev = &mTable[spot];
|
||||
while (next != NULL)
|
||||
{
|
||||
if (next->obj == obj && next->hashValue == hashValue)
|
||||
{
|
||||
*prev = next->nextEntry;
|
||||
delete next;
|
||||
mEntryCount--;
|
||||
if (mTable[spot] == NULL)
|
||||
mStatUsedSlots--;
|
||||
return(true);
|
||||
break;
|
||||
|
||||
}
|
||||
prev = &next->nextEntry;
|
||||
next = next->nextEntry;
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
|
||||
template<typename T> void HashTable<T>::Reset()
|
||||
{
|
||||
for (int spot = 0; spot < mTableSize; spot++)
|
||||
{
|
||||
HashEntry *curr = mTable[spot];
|
||||
if (curr != NULL)
|
||||
{
|
||||
mStatUsedSlots--;
|
||||
while (curr != NULL)
|
||||
{
|
||||
HashEntry *next = curr->nextEntry;
|
||||
delete curr;
|
||||
mEntryCount--;
|
||||
curr = next;
|
||||
}
|
||||
mTable[spot] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> T *HashTable<T>::FindFirst(int hashValue) const
|
||||
{
|
||||
HashEntry *entry = mTable[((unsigned)hashValue) % mTableSize];
|
||||
while (entry != NULL)
|
||||
{
|
||||
if (entry->hashValue == hashValue)
|
||||
return(&entry->obj);
|
||||
entry = entry->nextEntry;
|
||||
}
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
template<typename T> T *HashTable<T>::FindNext(T *prevResult) const
|
||||
{
|
||||
HashEntry *entry = (HashEntry *)(((char *)prevResult) - offsetof(HashEntry, obj));
|
||||
int hashValue = entry->hashValue;
|
||||
entry = entry->nextEntry;
|
||||
while (entry != NULL)
|
||||
{
|
||||
if (entry->hashValue == hashValue)
|
||||
return(&entry->obj);
|
||||
entry = entry->nextEntry;
|
||||
}
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
template<typename T> T *HashTable<T>::WalkFirst() const
|
||||
{
|
||||
for (int bucket = 0; bucket < mTableSize; bucket++)
|
||||
{
|
||||
HashEntry *entry = mTable[bucket];
|
||||
if (entry != NULL)
|
||||
return(&entry->obj);
|
||||
}
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
template<typename T> T *HashTable<T>::WalkNext(T *prevResult) const
|
||||
{
|
||||
HashEntry *entry = (HashEntry *)(((char *)prevResult) - offsetof(HashEntry, obj));
|
||||
int bucket = ((unsigned)entry->hashValue) % mTableSize;
|
||||
|
||||
entry = entry->nextEntry;
|
||||
if (entry != NULL)
|
||||
return(&entry->obj);
|
||||
|
||||
bucket++; // go onto next bucket
|
||||
for (; bucket < mTableSize; bucket++)
|
||||
{
|
||||
HashEntry *entry = mTable[bucket];
|
||||
if (entry != NULL)
|
||||
return(&entry->obj);
|
||||
}
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
template<typename T> void HashTable<T>::Resize(int hashSize)
|
||||
{
|
||||
HashEntry **oldTable = mTable;
|
||||
int oldSize = mTableSize;
|
||||
|
||||
// calculate next prime number greater than hashSize.
|
||||
mTableSize = NextPrime(hashSize);
|
||||
mTable = new HashEntry*[mTableSize];
|
||||
memset(mTable, 0, sizeof(HashEntry *) * mTableSize);
|
||||
|
||||
mStatUsedSlots = 0;
|
||||
|
||||
if (mEntryCount > 0)
|
||||
{
|
||||
for (int i = 0; i < oldSize; i++)
|
||||
{
|
||||
HashEntry* next = oldTable[i];
|
||||
while (next != NULL)
|
||||
{
|
||||
HashEntry* hold = next;
|
||||
next = next->nextEntry;
|
||||
|
||||
// insert hold into new table
|
||||
int spot = ((unsigned)hold->hashValue) % mTableSize;
|
||||
if (mTable[spot] == NULL)
|
||||
{
|
||||
hold->nextEntry = NULL;
|
||||
mTable[spot] = hold;
|
||||
mStatUsedSlots++;
|
||||
}
|
||||
else
|
||||
{
|
||||
hold->nextEntry = mTable[spot];
|
||||
mTable[spot] = hold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete[] oldTable;
|
||||
}
|
||||
|
||||
template<typename T> void HashTable<T>::GetStatistics(HashTableStatistics *stats) const
|
||||
{
|
||||
stats->totalEntries = mEntryCount;
|
||||
stats->usedSlots = mStatUsedSlots;
|
||||
stats->tableSize = mTableSize;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// ObjectHashTable
|
||||
//
|
||||
// similar to basic HashTable object, only this version requires the contents
|
||||
// be pointers to objects derived from class HashTableMember. If you can use
|
||||
// this version, it avoids allocations of HashEntry objects.
|
||||
// making the cost of object inserts/deletes dirt-cheap
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
template<typename M> class HashTableMember
|
||||
{
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1300) // MSVC 7.0 is the first version to support friend templates
|
||||
public:
|
||||
#else
|
||||
protected:
|
||||
template<typename T, typename P> friend class ObjectHashTable;
|
||||
#endif
|
||||
int mHashValue;
|
||||
M *mHashNextEntry;
|
||||
};
|
||||
|
||||
template<typename M> class HashTableMember1 : public HashTableMember<M> {};
|
||||
template<typename M> class HashTableMember2 : public HashTableMember<M> {};
|
||||
template<typename M> class HashTableMember3 : public HashTableMember<M> {};
|
||||
template<typename M> class HashTableMember4 : public HashTableMember<M> {};
|
||||
|
||||
|
||||
template<typename T, typename C = T> class ObjectHashTable
|
||||
{
|
||||
public:
|
||||
ObjectHashTable(int hashSize);
|
||||
~ObjectHashTable();
|
||||
|
||||
void Insert(T *obj, int hashValue);
|
||||
bool Remove(T *obj);
|
||||
void Reset(); // removes all entries from the table
|
||||
|
||||
T *FindFirst(int hashValue) const; // returns NULL if not found
|
||||
T *FindNext(T *prevResult) const; // returns NULL if not found
|
||||
|
||||
T *WalkFirst() const; // returns NULL if not found
|
||||
T *WalkNext(T *prevResult) const; // returns NULL if not found
|
||||
|
||||
void Resize(int hashSize); // the actual hash size will be rounded up to the next larger prime number (very slow, not recommended)
|
||||
void GetStatistics(HashTableStatistics *stats) const;
|
||||
|
||||
protected:
|
||||
T **mTable;
|
||||
int mTableSize;
|
||||
int mEntryCount;
|
||||
int mStatUsedSlots;
|
||||
};
|
||||
|
||||
|
||||
template<typename T, typename C> ObjectHashTable<T, C>::ObjectHashTable(int hashSize)
|
||||
{
|
||||
mTable = NULL;
|
||||
mTableSize = 0;
|
||||
mEntryCount = 0;
|
||||
mStatUsedSlots = 0;
|
||||
Resize(hashSize);
|
||||
}
|
||||
|
||||
template<typename T, typename C> ObjectHashTable<T, C>::~ObjectHashTable()
|
||||
{
|
||||
delete[] mTable;
|
||||
}
|
||||
|
||||
template<typename T, typename C> void ObjectHashTable<T, C>::Insert(T *obj, int hashValue)
|
||||
{
|
||||
static_cast<C *>(obj)->mHashValue = hashValue;
|
||||
|
||||
int spot = ((unsigned)hashValue) % mTableSize;
|
||||
if (mTable[spot] == NULL)
|
||||
{
|
||||
static_cast<C *>(obj)->mHashNextEntry = NULL;
|
||||
mTable[spot] = obj;
|
||||
mStatUsedSlots++;
|
||||
}
|
||||
else
|
||||
{
|
||||
static_cast<C *>(obj)->mHashNextEntry = mTable[spot];
|
||||
mTable[spot] = obj;
|
||||
}
|
||||
mEntryCount++;
|
||||
}
|
||||
|
||||
template<typename T, typename C> bool ObjectHashTable<T, C>::Remove(T *obj)
|
||||
{
|
||||
int spot = ((unsigned)static_cast<C *>(obj)->mHashValue) % mTableSize;
|
||||
T *cur = mTable[spot];
|
||||
T **prev = &mTable[spot];
|
||||
while (cur != NULL)
|
||||
{
|
||||
if (cur == obj)
|
||||
{
|
||||
*prev = static_cast<C *>(cur)->mHashNextEntry;
|
||||
static_cast<C *>(cur)->mHashNextEntry = NULL;
|
||||
mEntryCount--;
|
||||
if (mTable[spot] == NULL)
|
||||
mStatUsedSlots--;
|
||||
return(true);
|
||||
break;
|
||||
|
||||
}
|
||||
prev = &static_cast<C *>(cur)->mHashNextEntry;
|
||||
cur = static_cast<C *>(cur)->mHashNextEntry;
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
|
||||
template<typename T, typename C> void ObjectHashTable<T, C>::Reset()
|
||||
{
|
||||
memset(mTable, 0, sizeof(T *) * mTableSize);
|
||||
mStatUsedSlots = 0;
|
||||
mEntryCount = 0;
|
||||
}
|
||||
|
||||
template<typename T, typename C> T *ObjectHashTable<T, C>::FindFirst(int hashValue) const
|
||||
{
|
||||
T *entry = mTable[((unsigned)hashValue) % mTableSize];
|
||||
while (entry != NULL)
|
||||
{
|
||||
if (static_cast<C *>(entry)->mHashValue == hashValue)
|
||||
return(entry);
|
||||
entry = static_cast<C *>(entry)->mHashNextEntry;
|
||||
}
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
template<typename T, typename C> T *ObjectHashTable<T, C>::FindNext(T *prevResult) const
|
||||
{
|
||||
T *entry = prevResult;
|
||||
int hashValue = static_cast<C *>(entry)->mHashValue;
|
||||
entry = static_cast<C *>(entry)->mHashNextEntry;
|
||||
while (entry != NULL)
|
||||
{
|
||||
if (static_cast<C *>(entry)->mHashValue == hashValue)
|
||||
return(entry);
|
||||
entry = static_cast<C *>(entry)->mHashNextEntry;
|
||||
}
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
template<typename T, typename C> T *ObjectHashTable<T, C>::WalkFirst() const
|
||||
{
|
||||
for (int bucket = 0; bucket < mTableSize; bucket++)
|
||||
{
|
||||
T *entry = mTable[bucket];
|
||||
if (entry != NULL)
|
||||
return(entry);
|
||||
}
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
template<typename T, typename C> T *ObjectHashTable<T, C>::WalkNext(T *prevResult) const
|
||||
{
|
||||
T *entry = prevResult;
|
||||
int bucket = ((unsigned)static_cast<C *>(entry)->mHashValue) % mTableSize;
|
||||
|
||||
entry = static_cast<C *>(entry)->mHashNextEntry;
|
||||
if (entry != NULL)
|
||||
return(entry);
|
||||
|
||||
bucket++; // go onto next bucket
|
||||
for (; bucket < mTableSize; bucket++)
|
||||
{
|
||||
entry = mTable[bucket];
|
||||
if (entry != NULL)
|
||||
return(entry);
|
||||
}
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
template<typename T, typename C> void ObjectHashTable<T, C>::Resize(int hashSize)
|
||||
{
|
||||
T **oldTable = mTable;
|
||||
int oldSize = mTableSize;
|
||||
|
||||
// calculate next prime number greater than hashSize.
|
||||
mTableSize = NextPrime(hashSize);
|
||||
mTable = new T*[mTableSize];
|
||||
memset(mTable, 0, sizeof(T *) * mTableSize);
|
||||
|
||||
mStatUsedSlots = 0;
|
||||
|
||||
if (mEntryCount > 0)
|
||||
{
|
||||
for (int i = 0; i < oldSize; i++)
|
||||
{
|
||||
T *cur = oldTable[i];
|
||||
while (cur != NULL)
|
||||
{
|
||||
T *hold = cur;
|
||||
cur = static_cast<C *>(cur)->mHashNextEntry;
|
||||
|
||||
// insert hold into new table
|
||||
int spot = ((unsigned)static_cast<C *>(hold)->mHashValue) % mTableSize;
|
||||
if (mTable[spot] == NULL)
|
||||
{
|
||||
static_cast<C *>(hold)->mHashNextEntry = NULL;
|
||||
mTable[spot] = hold;
|
||||
mStatUsedSlots++;
|
||||
}
|
||||
else
|
||||
{
|
||||
static_cast<C *>(hold)->mHashNextEntry = mTable[spot];
|
||||
mTable[spot] = hold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete[] oldTable;
|
||||
}
|
||||
|
||||
template<typename T, typename C> void ObjectHashTable<T, C>::GetStatistics(HashTableStatistics *stats) const
|
||||
{
|
||||
stats->totalEntries = mEntryCount;
|
||||
stats->usedSlots = mStatUsedSlots;
|
||||
stats->tableSize = mTableSize;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// helper functions for the above hash table classes
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
inline bool IsPrime(int value) // works on positive numbers up 62,710,561
|
||||
{
|
||||
static int sPrimeTab[] =
|
||||
{ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
|
||||
31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
|
||||
73, 79, 83, 89, 97, 101, 103, 107, 109, 113,
|
||||
127, 131, 137, 139, 149, 151, 157, 163, 167, 173,
|
||||
179, 181, 191, 193, 197, 199, 211, 223, 227, 229,
|
||||
233, 239, 241, 251, 257, 263, 269, 271, 277, 281,
|
||||
283, 293, 307, 311, 313, 317, 331, 337, 347, 349,
|
||||
353, 359, 367, 373, 379, 383, 389, 397, 401, 409,
|
||||
419, 421, 431, 433, 439, 443, 449, 457, 461, 463,
|
||||
467, 479, 487, 491, 499, 503, 509, 521, 523, 541,
|
||||
547, 557, 563, 569, 571, 577, 587, 593, 599, 601,
|
||||
607, 613, 617, 619, 631, 641, 643, 647, 653, 659,
|
||||
661, 673, 677, 683, 691, 701, 709, 719, 727, 733,
|
||||
739, 743, 751, 757, 761, 769, 773, 787, 797, 809,
|
||||
811, 821, 823, 827, 829, 839, 853, 857, 859, 863,
|
||||
877, 881, 883, 887, 907, 911, 919, 929, 937, 941,
|
||||
947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013,
|
||||
1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069,
|
||||
1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151,
|
||||
1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223,
|
||||
1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291,
|
||||
1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373,
|
||||
1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451,
|
||||
1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511,
|
||||
1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583,
|
||||
1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657,
|
||||
1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733,
|
||||
1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811,
|
||||
1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889,
|
||||
1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987,
|
||||
1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053,
|
||||
2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129,
|
||||
2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213,
|
||||
2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287,
|
||||
2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357,
|
||||
2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423,
|
||||
2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531,
|
||||
2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617,
|
||||
2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687,
|
||||
2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741,
|
||||
2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801, 2803, 2819,
|
||||
2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903,
|
||||
2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999,
|
||||
3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079,
|
||||
3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181,
|
||||
3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257,
|
||||
3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331,
|
||||
3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413,
|
||||
3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511,
|
||||
3517, 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571,
|
||||
3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643,
|
||||
3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727,
|
||||
3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821,
|
||||
3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907,
|
||||
3911, 3917, 3919, 3923, 3929, 3931, 3943, 3947, 3967, 3989,
|
||||
4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057,
|
||||
4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139,
|
||||
4153, 4157, 4159, 4177, 4201, 4211, 4217, 4219, 4229, 4231,
|
||||
4241, 4243, 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297,
|
||||
4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409,
|
||||
4421, 4423, 4441, 4447, 4451, 4457, 4463, 4481, 4483, 4493,
|
||||
4507, 4513, 4517, 4519, 4523, 4547, 4549, 4561, 4567, 4583,
|
||||
4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657,
|
||||
4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751,
|
||||
4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813, 4817, 4831,
|
||||
4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, 4933, 4937,
|
||||
4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003,
|
||||
5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087,
|
||||
5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179,
|
||||
5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279,
|
||||
5281, 5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387,
|
||||
5393, 5399, 5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443,
|
||||
5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, 5519, 5521,
|
||||
5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639,
|
||||
5641, 5647, 5651, 5653, 5657, 5659, 5669, 5683, 5689, 5693,
|
||||
5701, 5711, 5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791,
|
||||
5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857,
|
||||
5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939,
|
||||
5953, 5981, 5987, 6007, 6011, 6029, 6037, 6043, 6047, 6053,
|
||||
6067, 6073, 6079, 6089, 6091, 6101, 6113, 6121, 6131, 6133,
|
||||
6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221,
|
||||
6229, 6247, 6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301,
|
||||
6311, 6317, 6323, 6329, 6337, 6343, 6353, 6359, 6361, 6367,
|
||||
6373, 6379, 6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473,
|
||||
6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571,
|
||||
6577, 6581, 6599, 6607, 6619, 6637, 6653, 6659, 6661, 6673,
|
||||
6679, 6689, 6691, 6701, 6703, 6709, 6719, 6733, 6737, 6761,
|
||||
6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833,
|
||||
6841, 6857, 6863, 6869, 6871, 6883, 6899, 6907, 6911, 6917,
|
||||
6947, 6949, 6959, 6961, 6967, 6971, 6977, 6983, 6991, 6997,
|
||||
7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103,
|
||||
7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207,
|
||||
7211, 7213, 7219, 7229, 7237, 7243, 7247, 7253, 7283, 7297,
|
||||
7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369, 7393, 7411,
|
||||
7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499,
|
||||
7507, 7517, 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561,
|
||||
7573, 7577, 7583, 7589, 7591, 7603, 7607, 7621, 7639, 7643,
|
||||
7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, 7723,
|
||||
7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829,
|
||||
7841, 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919
|
||||
};
|
||||
|
||||
for (unsigned int j = 0; j < sizeof(sPrimeTab) / sizeof(int) && sPrimeTab[j] * sPrimeTab[j] < value; j++)
|
||||
{
|
||||
if (value % sPrimeTab[j] == 0)
|
||||
return(false);
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
inline int NextPrime(int value)
|
||||
{
|
||||
if (value % 2 == 0)
|
||||
value++;
|
||||
while (!IsPrime(value))
|
||||
value += 2;
|
||||
return(value);
|
||||
}
|
||||
|
||||
inline int HashString(const char *string)
|
||||
{
|
||||
int h = 0;
|
||||
while (*string != 0)
|
||||
h = ((31 * h) + *string++) ^ (h >> 26);
|
||||
return(h);
|
||||
}
|
||||
|
||||
inline int UpperHashString(const char *string)
|
||||
{
|
||||
int h = 0;
|
||||
while (*string != 0)
|
||||
{
|
||||
h = ((31 * h) + (*string >= 'a' && *string <= 'z') ? (*string - 0x20) : *string) ^ (h >> 26);
|
||||
string++;
|
||||
}
|
||||
return(h);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef UDPLIBRARY_UDPHELPER_H
|
||||
#define UDPLIBRARY_UDPHELPER_H
|
||||
|
||||
// Copyright 2004 Sony Online Entertainment, all rights reserved.
|
||||
// Author: Jeff Petersen
|
||||
|
||||
namespace UdpLibrary
|
||||
{
|
||||
|
||||
class UdpGuard
|
||||
{
|
||||
public:
|
||||
UdpGuard(UdpPlatformGuardObject *obj) { mObj = obj; mObj->Enter(); }
|
||||
~UdpGuard() { mObj->Leave(); }
|
||||
protected:
|
||||
UdpPlatformGuardObject *mObj;
|
||||
};
|
||||
|
||||
class UdpRef
|
||||
{
|
||||
public:
|
||||
UdpRef(UdpRefCount *obj) { mObj = obj; mObj->AddRef(); }
|
||||
~UdpRef() { mObj->Release(); }
|
||||
|
||||
private:
|
||||
UdpRefCount *mObj;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright 2004 Sony Online Entertainment, all rights reserved.
|
||||
// Author: Jeff Petersen
|
||||
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "UdpLibrary.h"
|
||||
|
||||
namespace UdpLibrary
|
||||
{
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// UdpGuardedRefCount
|
||||
////////////////////////////////////////////////////////
|
||||
void UdpGuardedRefCount::AddRef() const
|
||||
{
|
||||
UdpGuard guard(&mGuard);
|
||||
UdpRefCount::AddRef();
|
||||
}
|
||||
|
||||
void UdpGuardedRefCount::Release() const
|
||||
{
|
||||
mGuard.Enter();
|
||||
assert(!mNoRef);
|
||||
assert(mRefCount > 0);
|
||||
bool done = (--mRefCount == 0);
|
||||
mGuard.Leave();
|
||||
|
||||
if (done)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef UDPLIBRARY_UDPLIBRARY_H
|
||||
#define UDPLIBRARY_UDPLIBRARY_H
|
||||
|
||||
// Copyright 2004 Sony Online Entertainment, all rights reserved.
|
||||
// Author: Jeff Petersen
|
||||
|
||||
#include <stdio.h>
|
||||
#include "UdpHandler.h"
|
||||
#include "UdpDriver.h"
|
||||
#include "UdpHelper.h"
|
||||
#include "UdpLogicalPacket.h"
|
||||
#include "UdpMisc.h"
|
||||
#include "UdpManager.h"
|
||||
#include "UdpConnection.h"
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
#ifndef UDPLIBRARY_UDPLINKEDLIST_H
|
||||
#define UDPLIBRARY_UDPLINKEDLIST_H
|
||||
|
||||
// Copyright 2004 Sony Online Entertainment, all rights reserved.
|
||||
// Author: Jeff Petersen
|
||||
|
||||
namespace UdpLibrary
|
||||
{
|
||||
|
||||
template<typename M> class UdpLinkedListMember;
|
||||
template<typename T> class UdpLinkedList;
|
||||
|
||||
|
||||
template<typename M> class UdpLinkedListMember
|
||||
{
|
||||
public:
|
||||
UdpLinkedListMember() { mPrev = NULL; mNext = NULL; }
|
||||
UdpLinkedListMember(const UdpLinkedListMember &) { mPrev = NULL; mNext = NULL; }
|
||||
~UdpLinkedListMember() {}
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1300) // MSVC 7.0 is the first version to support friend templates
|
||||
public:
|
||||
#else
|
||||
protected:
|
||||
template<typename T> friend class UdpLinkedList;
|
||||
#endif
|
||||
M *mPrev;
|
||||
M *mNext;
|
||||
};
|
||||
|
||||
|
||||
template<typename T> class UdpLinkedList
|
||||
{
|
||||
public:
|
||||
UdpLinkedList(UdpLinkedListMember<T> T::*node);
|
||||
virtual ~UdpLinkedList();
|
||||
|
||||
T *First() const;
|
||||
T *Last() const;
|
||||
T *Next(const T *cur) const;
|
||||
T *Prev(const T *cur) const;
|
||||
T *Position(int index) const;
|
||||
T *Remove(T *cur);
|
||||
T *RemoveHead();
|
||||
T *RemoveTail();
|
||||
T *InsertHead(T *cur);
|
||||
T *InsertTail(T *cur);
|
||||
T *InsertAfter(T *cur, T *prev);
|
||||
int Count() const;
|
||||
|
||||
void DeleteAll(); // calls delete on all contents
|
||||
void ReleaseAll(); // calls Release on all contents
|
||||
|
||||
protected:
|
||||
T *mHead;
|
||||
T *mTail;
|
||||
UdpLinkedListMember<T> T::*mNode;
|
||||
int mCount;
|
||||
};
|
||||
|
||||
template<typename T> UdpLinkedList<T>::UdpLinkedList(UdpLinkedListMember<T> T::*node)
|
||||
{
|
||||
mHead = NULL;
|
||||
mTail = NULL;
|
||||
mNode = node;
|
||||
mCount = 0;
|
||||
}
|
||||
|
||||
template<typename T> UdpLinkedList<T>::~UdpLinkedList()
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T> T *UdpLinkedList<T>::First() const
|
||||
{
|
||||
return(mHead);
|
||||
}
|
||||
|
||||
template<typename T> T *UdpLinkedList<T>::Next(const T *cur) const
|
||||
{
|
||||
return((cur->*mNode).mNext);
|
||||
}
|
||||
|
||||
template<typename T> T *UdpLinkedList<T>::Prev(const T *cur) const
|
||||
{
|
||||
return((cur->*mNode).mPrev);
|
||||
}
|
||||
|
||||
template<typename T> T *UdpLinkedList<T>::Last() const
|
||||
{
|
||||
return(mTail);
|
||||
}
|
||||
|
||||
template<typename T> int UdpLinkedList<T>::Count() const
|
||||
{
|
||||
return(mCount);
|
||||
}
|
||||
|
||||
template<typename T> T *UdpLinkedList<T>::Position(int index) const
|
||||
{
|
||||
T *cur = mHead;
|
||||
while (cur != NULL && index > 0)
|
||||
{
|
||||
cur = Next(cur);
|
||||
index--;
|
||||
}
|
||||
return(cur);
|
||||
}
|
||||
|
||||
template<typename T> T *UdpLinkedList<T>::Remove(T *cur)
|
||||
{
|
||||
UdpLinkedListMember<T> *node = &(cur->*mNode);
|
||||
if (node->mPrev == NULL)
|
||||
mHead = node->mNext;
|
||||
else
|
||||
((node->mPrev)->*mNode).mNext = node->mNext;
|
||||
|
||||
if (node->mNext == NULL)
|
||||
mTail = node->mPrev;
|
||||
else
|
||||
((node->mNext)->*mNode).mPrev = node->mPrev;
|
||||
|
||||
node->mNext = NULL;
|
||||
node->mPrev = NULL;
|
||||
mCount--;
|
||||
return(cur);
|
||||
}
|
||||
|
||||
template<typename T> T *UdpLinkedList<T>::RemoveHead()
|
||||
{
|
||||
if (mHead == NULL)
|
||||
return(NULL);
|
||||
return(Remove(mHead));
|
||||
}
|
||||
|
||||
template<typename T> T *UdpLinkedList<T>::RemoveTail()
|
||||
{
|
||||
if (mTail == NULL)
|
||||
return(NULL);
|
||||
return(Remove(mTail));
|
||||
}
|
||||
|
||||
template<typename T> T *UdpLinkedList<T>::InsertHead(T *cur)
|
||||
{
|
||||
assert((cur->*mNode).mPrev == NULL);
|
||||
assert((cur->*mNode).mNext == NULL);
|
||||
(cur->*mNode).mNext = mHead;
|
||||
|
||||
if (mHead != NULL)
|
||||
{
|
||||
(mHead->*mNode).mPrev = cur;
|
||||
mHead = cur;
|
||||
}
|
||||
else
|
||||
{
|
||||
mTail = mHead = cur;
|
||||
}
|
||||
|
||||
mCount++;
|
||||
return(cur);
|
||||
}
|
||||
|
||||
template<typename T> T *UdpLinkedList<T>::InsertTail(T *cur)
|
||||
{
|
||||
assert((cur->*mNode).mPrev == NULL);
|
||||
assert((cur->*mNode).mNext == NULL);
|
||||
|
||||
(cur->*mNode).mPrev = mTail;
|
||||
|
||||
if (mTail != NULL)
|
||||
{
|
||||
(mTail->*mNode).mNext = cur;
|
||||
mTail = cur;
|
||||
}
|
||||
else
|
||||
{
|
||||
mTail = mHead = cur;
|
||||
}
|
||||
|
||||
mCount++;
|
||||
return(cur);
|
||||
}
|
||||
|
||||
template<typename T> T *UdpLinkedList<T>::InsertAfter(T *cur, T *prev)
|
||||
{
|
||||
assert((cur->*mNode).mPrev == NULL);
|
||||
assert((cur->*mNode).mNext == NULL);
|
||||
|
||||
if (prev == NULL)
|
||||
return(InsertHead(cur));
|
||||
|
||||
(cur->*mNode).mPrev = prev;
|
||||
(cur->*mNode).mNext = (prev->*mNode).mNext;
|
||||
(prev->*mNode).mNext = cur;
|
||||
|
||||
if ((cur->*mNode).mNext != NULL)
|
||||
(((cur->*mNode).mNext)->*mNode).mPrev = cur;
|
||||
else
|
||||
mTail = cur;
|
||||
|
||||
mCount++;
|
||||
return(cur);
|
||||
}
|
||||
|
||||
template<typename T> void UdpLinkedList<T>::DeleteAll()
|
||||
{
|
||||
T *cur = First();
|
||||
while (cur != NULL)
|
||||
{
|
||||
T *next = Next(cur);
|
||||
Remove(cur);
|
||||
delete cur;
|
||||
cur = next;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> void UdpLinkedList<T>::ReleaseAll()
|
||||
{
|
||||
T *cur = First();
|
||||
while (cur != NULL)
|
||||
{
|
||||
T *next = Next(cur);
|
||||
Remove(cur);
|
||||
cur->Release();
|
||||
cur = next;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
template<typename T> class UdpLinkedListOwn : public UdpLinkedList<T>
|
||||
{
|
||||
public:
|
||||
virtual ~UdpLinkedListOwn()
|
||||
{
|
||||
this->DeleteAll();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
#endif
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
// Copyright 2004 Sony Online Entertainment, all rights reserved.
|
||||
// Author: Jeff Petersen
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "UdpLibrary.h"
|
||||
|
||||
namespace UdpLibrary
|
||||
{
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
// LogicalPacket implementation
|
||||
/////////////////////////////////////////////////////
|
||||
LogicalPacket::LogicalPacket()
|
||||
{
|
||||
}
|
||||
|
||||
LogicalPacket::~LogicalPacket()
|
||||
{
|
||||
}
|
||||
|
||||
bool LogicalPacket::IsInternalPacket() const
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
// SimpleLogicalPacket implementation
|
||||
/////////////////////////////////////////////////////
|
||||
SimpleLogicalPacket::SimpleLogicalPacket(const void *data, int dataLen)
|
||||
{
|
||||
mDataLen = dataLen;
|
||||
mData = new udp_uchar[mDataLen];
|
||||
if (data != NULL)
|
||||
memcpy(mData, data, mDataLen);
|
||||
}
|
||||
|
||||
SimpleLogicalPacket::~SimpleLogicalPacket()
|
||||
{
|
||||
delete[] mData;
|
||||
}
|
||||
|
||||
void *SimpleLogicalPacket::GetDataPtr()
|
||||
{
|
||||
return(mData);
|
||||
}
|
||||
|
||||
const void *SimpleLogicalPacket::GetDataPtr() const
|
||||
{
|
||||
return(mData);
|
||||
}
|
||||
|
||||
int SimpleLogicalPacket::GetDataLen() const
|
||||
{
|
||||
return(mDataLen);
|
||||
}
|
||||
|
||||
void SimpleLogicalPacket::SetDataLen(int len)
|
||||
{
|
||||
assert(len <= mDataLen);
|
||||
mDataLen = len;
|
||||
}
|
||||
/////////////////////////////////////////////////////
|
||||
// GroupLogicalPacket implementation
|
||||
/////////////////////////////////////////////////////
|
||||
GroupLogicalPacket::GroupLogicalPacket() : LogicalPacket()
|
||||
{
|
||||
mDataLen = 0;
|
||||
mData = NULL;
|
||||
}
|
||||
|
||||
GroupLogicalPacket::~GroupLogicalPacket()
|
||||
{
|
||||
UdpMisc::SmartResize(mData, 0); // free it
|
||||
}
|
||||
|
||||
void GroupLogicalPacket::AddPacket(const LogicalPacket *packet)
|
||||
{
|
||||
assert(packet != NULL);
|
||||
AddPacketInternal(packet->GetDataPtr(), packet->GetDataLen(), packet->IsInternalPacket());
|
||||
}
|
||||
|
||||
void GroupLogicalPacket::AddPacket(const void *data, int dataLen)
|
||||
{
|
||||
assert(data != NULL);
|
||||
assert(dataLen >= 0);
|
||||
AddPacketInternal(data, dataLen, false);
|
||||
}
|
||||
|
||||
void GroupLogicalPacket::AddPacketInternal(const void *data, int dataLen, bool isInternalPacket)
|
||||
{
|
||||
if (dataLen == 0)
|
||||
return;
|
||||
mData = (udp_uchar *)UdpMisc::SmartResize(mData, mDataLen + dataLen + 10, 512); // 7 is the most bytes that could be needed to specify the length of the data to follow, 2 is for internal header-bytes, 1 is for zero-escape if needed (if they need to be added on)
|
||||
if (mDataLen == 0)
|
||||
{
|
||||
mData[0] = 0;
|
||||
mData[1] = UdpConnection::cUdpPacketGroup;
|
||||
mDataLen = 2;
|
||||
}
|
||||
|
||||
udp_uchar *ptr = mData + mDataLen;
|
||||
if (!isInternalPacket && *(const udp_uchar *)data == 0)
|
||||
{
|
||||
ptr += UdpMisc::PutVariableValue(ptr, dataLen + 1);
|
||||
*ptr++ = 0; // packet is not internal and starts with 0, so we need to zero-escape it so it knows it's an application packet
|
||||
}
|
||||
else
|
||||
ptr += UdpMisc::PutVariableValue(ptr, dataLen);
|
||||
|
||||
memcpy(ptr, data, dataLen);
|
||||
ptr += dataLen;
|
||||
mDataLen = (int)(ptr - mData);
|
||||
}
|
||||
|
||||
void *GroupLogicalPacket::GetDataPtr()
|
||||
{
|
||||
return(mData);
|
||||
}
|
||||
|
||||
const void *GroupLogicalPacket::GetDataPtr() const
|
||||
{
|
||||
return(mData);
|
||||
}
|
||||
|
||||
int GroupLogicalPacket::GetDataLen() const
|
||||
{
|
||||
return(mDataLen);
|
||||
}
|
||||
|
||||
void GroupLogicalPacket::SetDataLen(int /*len*/)
|
||||
{
|
||||
assert(0); // not allowed to set the len of a group logical packet
|
||||
}
|
||||
|
||||
bool GroupLogicalPacket::IsInternalPacket() const
|
||||
{
|
||||
return(true);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PooledLogicalPacket implementation
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
PooledLogicalPacket::PooledLogicalPacket(UdpManager *manager, int len)
|
||||
{
|
||||
mMaxDataLen = len;
|
||||
mData = new udp_uchar[mMaxDataLen];
|
||||
mDataLen = 0;
|
||||
mUdpManager = manager;
|
||||
mUdpManager->PoolCreated(this);
|
||||
}
|
||||
|
||||
PooledLogicalPacket::~PooledLogicalPacket()
|
||||
{
|
||||
if (mUdpManager != NULL)
|
||||
{
|
||||
mUdpManager->PoolDestroyed(this);
|
||||
mUdpManager = NULL;
|
||||
}
|
||||
|
||||
delete[] mData;
|
||||
}
|
||||
|
||||
void PooledLogicalPacket::AddRef() const
|
||||
{
|
||||
LogicalPacket::AddRef();
|
||||
}
|
||||
|
||||
void PooledLogicalPacket::Release() const
|
||||
{
|
||||
if (GetRefCount() == 1 && mUdpManager != NULL)
|
||||
{
|
||||
// the PoolReturn function steals our reference (ie, we don't release, they don't addref), this is for thread safety reasons
|
||||
mUdpManager->PoolReturn(const_cast<PooledLogicalPacket *>(this));
|
||||
return; // it would be non-thread-safe for us to touch this object in any way after is is added back to the pool, since some other thread might steal it before we get back to here (or if the pool didn't want us, we are potentially deleted at this point)
|
||||
}
|
||||
LogicalPacket::Release();
|
||||
}
|
||||
|
||||
void PooledLogicalPacket::TrueRelease() const
|
||||
{
|
||||
// this function is used by the UdpManager to delete this object in situations where it has decided that it does
|
||||
// not want the object returned to the pool (pool is full)
|
||||
LogicalPacket::Release();
|
||||
}
|
||||
|
||||
void *PooledLogicalPacket::GetDataPtr()
|
||||
{
|
||||
return(mData);
|
||||
}
|
||||
|
||||
const void *PooledLogicalPacket::GetDataPtr() const
|
||||
{
|
||||
return(mData);
|
||||
}
|
||||
|
||||
int PooledLogicalPacket::GetDataLen() const
|
||||
{
|
||||
return(mDataLen);
|
||||
}
|
||||
|
||||
void PooledLogicalPacket::SetDataLen(int len)
|
||||
{
|
||||
assert(len <= mMaxDataLen);
|
||||
mDataLen = len;
|
||||
}
|
||||
|
||||
void PooledLogicalPacket::SetData(const void *data, int dataLen, const void *data2, int dataLen2)
|
||||
{
|
||||
mDataLen = dataLen + dataLen2;
|
||||
if (data != NULL)
|
||||
memcpy(mData, data, dataLen);
|
||||
if (data2 != NULL)
|
||||
memcpy(mData + dataLen, data2, dataLen2);
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
+299
@@ -0,0 +1,299 @@
|
||||
#ifndef UDPLIBRARY_UDPLOGICALPACKET_H
|
||||
#define UDPLIBRARY_UDPLOGICALPACKET_H
|
||||
|
||||
// Copyright 2004 Sony Online Entertainment, all rights reserved.
|
||||
// Author: Jeff Petersen
|
||||
|
||||
#include "UdpHandler.h"
|
||||
#include "UdpLinkedList.h"
|
||||
|
||||
namespace UdpLibrary
|
||||
{
|
||||
|
||||
class SimpleLogicalPacket;
|
||||
class GroupLogicalPacket;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// The purpose of the LogicalPacket class is to provide means whereby multiple connections can share
|
||||
// a queued packet to save having each connection make its own copy. It is highly recommended that
|
||||
// logical packets be used for optimal performance when sending reliable data as well, since internally
|
||||
// it will just end up creating one for you anyhow (LogicalPackets are used by the reliable layer to
|
||||
// hold onto the data until it is acknowledged)
|
||||
//
|
||||
// Logical packets passed in are never modified by the udp library.
|
||||
// After calling Send, application should immediately Release the logical packet if they don't need it for
|
||||
// something else (like sending to another connection). The udp library will addref it if it decided it
|
||||
// wants to keep it around past the Send call (for reliable data, it will always hang onto it, for unreliable
|
||||
// data, it will only hang onto it if it gets promoted to reliable due to size.)
|
||||
//
|
||||
// Application is encouraged to derive their own application-specific packet classes from the LogicalPacket
|
||||
// The object is required to be able to provide a pointer to the raw packet data that will remain valid
|
||||
// for as long as the LogicalPacket object exists.
|
||||
//
|
||||
// for example:
|
||||
// class PlayerLoginPacket : public LogicalPacket
|
||||
// {
|
||||
// public:
|
||||
// PlayerLoginPacket(char *userName, char *password) { mData.packetType = cPacketTypePlayerLogin; strcpy(mData.userName, userName); strcpy(mData.password, password); }
|
||||
// virtual const void *GetDataPtr() const { return(&mData); }
|
||||
// virtual int GetDataLen() const { return(sizeof(mData)); }
|
||||
// protected:
|
||||
// virtual ~PlayerLoginPacket() {};
|
||||
// struct
|
||||
// {
|
||||
// udp_uchar packetType;
|
||||
// char userName[32];
|
||||
// char password[32];
|
||||
// } mData;
|
||||
// };
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
class LogicalPacket : public UdpRefCount
|
||||
{
|
||||
public:
|
||||
LogicalPacket();
|
||||
|
||||
virtual void *GetDataPtr() = 0;
|
||||
virtual const void *GetDataPtr() const = 0;
|
||||
virtual int GetDataLen() const = 0;
|
||||
virtual void SetDataLen(int len) = 0;
|
||||
|
||||
// returns true if this logical packet is an internal packet
|
||||
// by default this returns false, and applications should not override this function to do
|
||||
// otherwise. Basically, if this returns true, it tells the udp library that the packet has
|
||||
// an internal-packet header (starts with a zero byte) on it that should not be escaped when sent.
|
||||
// The packet will ultimately be processed by the internal udp library code on the other side.
|
||||
// The purpose of this is to support features such as GroupLogicalPacket, such that the application
|
||||
// can prep grouped packets and then send them via traditional means (it's how the udp library
|
||||
// knows that the packet is a group packet instead of just a application-packet that happens to start with 0)
|
||||
virtual bool IsInternalPacket() const;
|
||||
protected:
|
||||
virtual ~LogicalPacket(); // protected since the class should only be deleted by Releasing it
|
||||
|
||||
private:
|
||||
friend class UdpReliableChannel;
|
||||
UdpLinkedListMember<LogicalPacket> mReliableLink; // used by reliable channel object to hold a list of these packets
|
||||
};
|
||||
|
||||
class SimpleLogicalPacket : public LogicalPacket
|
||||
{
|
||||
// this class is a simply dynamically allocated packet. Applications are welcome to use it, but
|
||||
// it was originally created to allow the internal code to handle reliable data that was sent
|
||||
// via the Send(char *, int) api call.
|
||||
public:
|
||||
SimpleLogicalPacket(const void *data, int dataLen); // data can be NULL if you want to populate it after it is allocated (get the pointer and write to it)
|
||||
virtual void *GetDataPtr();
|
||||
virtual const void *GetDataPtr() const;
|
||||
virtual int GetDataLen() const;
|
||||
virtual void SetDataLen(int len);
|
||||
protected:
|
||||
virtual ~SimpleLogicalPacket();
|
||||
udp_uchar *mData;
|
||||
int mDataLen;
|
||||
};
|
||||
|
||||
class WrappedLogicalPacket : public LogicalPacket
|
||||
{
|
||||
// this class is used internally by the library such that it can get a free and clear mNext pointer
|
||||
// in cases where a logical packet is being shared by multiple connections (such that the packet
|
||||
// can reside in the linked-list for the reliable channel queue)
|
||||
public:
|
||||
WrappedLogicalPacket(UdpManager *manager); // lives in a pool
|
||||
|
||||
virtual void AddRef() const;
|
||||
virtual void Release() const;
|
||||
virtual void *GetDataPtr();
|
||||
virtual const void *GetDataPtr() const;
|
||||
virtual int GetDataLen() const;
|
||||
virtual void SetDataLen(int len);
|
||||
protected:
|
||||
virtual ~WrappedLogicalPacket();
|
||||
const LogicalPacket *mPacket;
|
||||
|
||||
protected:
|
||||
friend class UdpManager;
|
||||
friend class UdpReliableChannel;
|
||||
void TrueRelease() const;
|
||||
void SetLogicalPacket(const LogicalPacket *packet);
|
||||
UdpManager *mUdpManager;
|
||||
UdpLinkedListMember<WrappedLogicalPacket> mAvailableLink; // for available linked list in manager (used by reliable channel object as well while it is borrowed)
|
||||
UdpLinkedListMember<WrappedLogicalPacket> mCreatedLink; // for created linked list in manager
|
||||
};
|
||||
|
||||
template<int t_quickSize> class FixedLogicalPacket : public LogicalPacket
|
||||
{
|
||||
// this class is similar to the SimpleLogicalPacket in that it is designed to just store raw
|
||||
// data of various sizes. The difference here is that this class may be created at compile
|
||||
// time to be any size desired (via template parameters), and is more efficient than the
|
||||
// SimpleLogicalPacket when used (it avoid the internal alloc as the data is inline). The
|
||||
// UdpMisc::CreateQuickLogicalPacket function automatically attempts to use this for small
|
||||
// packets and will fall back and use SimpleLogicalPackets for larger packets.
|
||||
public:
|
||||
FixedLogicalPacket(const void *data, int dataLen);
|
||||
virtual void *GetDataPtr();
|
||||
virtual const void *GetDataPtr() const;
|
||||
virtual int GetDataLen() const;
|
||||
virtual void SetDataLen(int len);
|
||||
protected:
|
||||
udp_uchar mData[t_quickSize];
|
||||
int mDataLen;
|
||||
};
|
||||
|
||||
template<typename T> class StructLogicalPacket : public LogicalPacket
|
||||
{
|
||||
// this class is designed to turn any raw struct into a logical packet. The application
|
||||
// can then access the struct through thisLogicalPacket->mStruct.structMember. The template
|
||||
// will take care of all the wrappings necessary to turn the struct into a fully functional
|
||||
// logical packet that can be sent to a connection. As a word of caution, sending struct's
|
||||
// across the wire is not considered very portable. There are byte-ordering and structure-packing
|
||||
// issues that are platform dependent. At a minimum, you will probably want to make sure that
|
||||
// struct's you use in this way are packed to 1-byte boundaries, such that you are not sending
|
||||
// wasteful information. Do not attempt to send the member-content of classes (particularly classes
|
||||
// with virtual functions) via this method as they may contain hidden data-members (such as pointers
|
||||
// to vtables).
|
||||
public:
|
||||
StructLogicalPacket(T *initData = NULL);
|
||||
virtual void *GetDataPtr();
|
||||
virtual const void *GetDataPtr() const;
|
||||
virtual int GetDataLen() const;
|
||||
virtual void SetDataLen(int len);
|
||||
protected:
|
||||
T mStruct;
|
||||
};
|
||||
|
||||
class GroupLogicalPacket : public LogicalPacket
|
||||
{
|
||||
// this class is a helper object intended for use by the application (it is not used internally)
|
||||
// It allows you to add multiple application-packets and them send them all as an autonomous unit.
|
||||
// The receiving end will automatically split the packet up and send them to the application callback function
|
||||
// as if the individual packets had all been sent one at at time.
|
||||
//
|
||||
// This facility is primarily intended for reliable data, though it can be used to group unreliable data as well.
|
||||
// Grouping unreliable data together would effectively give you an all-or-nothing type of delivery system. Be
|
||||
// mindful that if the group of packets gets larger than max-raw-packet-size, it will end up getting
|
||||
// promoted to being a reliable-packet with the associated overhead involved in that.
|
||||
//
|
||||
// You might be wondering what the advantage would be of grouping packets together at the application level as opposed to
|
||||
// just letting the internal multi-buffer take care of the problem for you. For starters, the internal multi-buffer
|
||||
// is incapable of combining partial-packets, so you end up sending less than maximum-size packets if you let the
|
||||
// internal layer take care of it. Additionally, there is additional overhead in that if you send 100 tiny logical
|
||||
// packets, each one will need to be ack'ed by the receiving end (even though they are getting combined down at the physical-packet
|
||||
// level in order to reduce UDP overhead). Finally, grouping them together at the higher level will allow the
|
||||
// logical-packet compression helper routines to operate on larger chunks of data at a time, which tends to improve
|
||||
// compression efficiency. The downside to combining is that none of the application packets get delivered until the
|
||||
// entire group arrives, then all are delivered in one fell swoop...the net effect being that in order to gain these efficeincies,
|
||||
// the first-packet takes longer to be delivered to the application than it otherwise would have (while the last packet will in theory
|
||||
// get there is less time as there will be less overhead).
|
||||
//
|
||||
// Internal packets can be added to the group (though that is somewhat unlikely), which means you can add a group to a group
|
||||
public:
|
||||
GroupLogicalPacket();
|
||||
void AddPacket(const LogicalPacket *packet); // cannot add internal logical packets to the group
|
||||
void AddPacket(const void *data, int dataLen);
|
||||
virtual void *GetDataPtr();
|
||||
virtual const void *GetDataPtr() const;
|
||||
virtual int GetDataLen() const;
|
||||
virtual void SetDataLen(int len);
|
||||
virtual bool IsInternalPacket() const; // returns true if this is considering an internal logical packet type
|
||||
|
||||
protected:
|
||||
virtual ~GroupLogicalPacket();
|
||||
void AddPacketInternal(const void *data, int dataLen, bool isInternalPacket);
|
||||
|
||||
udp_uchar *mData;
|
||||
int mDataLen;
|
||||
};
|
||||
|
||||
class PooledLogicalPacket : public LogicalPacket
|
||||
{
|
||||
// a pooled logical packet is like other logical packets, only when it's refCount gets down to 1, it notifies
|
||||
// its manager to add it back to the pool. (The manager keeps the last ref count on the packet)
|
||||
public:
|
||||
PooledLogicalPacket(UdpManager *manager, int len);
|
||||
|
||||
virtual void AddRef() const;
|
||||
virtual void Release() const;
|
||||
virtual void *GetDataPtr();
|
||||
virtual const void *GetDataPtr() const;
|
||||
virtual int GetDataLen() const;
|
||||
virtual void SetDataLen(int len);
|
||||
protected:
|
||||
virtual ~PooledLogicalPacket();
|
||||
|
||||
udp_uchar *mData;
|
||||
int mDataLen;
|
||||
int mMaxDataLen;
|
||||
protected:
|
||||
friend class UdpManager;
|
||||
void TrueRelease() const;
|
||||
void SetData(const void *data, int dataLen, const void *data2 = NULL, int dataLen2 = 0);
|
||||
UdpManager *mUdpManager;
|
||||
UdpLinkedListMember<PooledLogicalPacket> mAvailableLink; // for available linked list in manager
|
||||
UdpLinkedListMember<PooledLogicalPacket> mCreatedLink; // for created linked list in manager
|
||||
};
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// FixedLogicalPacket implementation
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
template<int t_quickSize> FixedLogicalPacket<t_quickSize>::FixedLogicalPacket(const void *data, int dataLen)
|
||||
{
|
||||
mDataLen = dataLen;
|
||||
if (data != NULL)
|
||||
memcpy(mData, data, mDataLen);
|
||||
}
|
||||
|
||||
template<int t_quickSize> void *FixedLogicalPacket<t_quickSize>::GetDataPtr()
|
||||
{
|
||||
return(mData);
|
||||
}
|
||||
|
||||
template<int t_quickSize> const void *FixedLogicalPacket<t_quickSize>::GetDataPtr() const
|
||||
{
|
||||
return(mData);
|
||||
}
|
||||
|
||||
template<int t_quickSize> int FixedLogicalPacket<t_quickSize>::GetDataLen() const
|
||||
{
|
||||
return(mDataLen);
|
||||
}
|
||||
|
||||
template<int t_quickSize> void FixedLogicalPacket<t_quickSize>::SetDataLen(int len)
|
||||
{
|
||||
mDataLen = len;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// StructLogicalPacket implementation
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
template<typename T> StructLogicalPacket<T>::StructLogicalPacket(T *initData)
|
||||
{
|
||||
if (initData != NULL)
|
||||
mStruct = *initData;
|
||||
}
|
||||
|
||||
template<typename T> void *StructLogicalPacket<T>::GetDataPtr()
|
||||
{
|
||||
return(&mStruct);
|
||||
}
|
||||
|
||||
template<typename T> const void *StructLogicalPacket<T>::GetDataPtr() const
|
||||
{
|
||||
return(&mStruct);
|
||||
}
|
||||
|
||||
template<typename T> int StructLogicalPacket<T>::GetDataLen() const
|
||||
{
|
||||
return(sizeof(mStruct));
|
||||
}
|
||||
|
||||
template<typename T> void StructLogicalPacket<T>::SetDataLen(int len)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,253 @@
|
||||
// Copyright 2004 Sony Online Entertainment, all rights reserved.
|
||||
// Author: Jeff Petersen
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "UdpMisc.h"
|
||||
#include "UdpLogicalPacket.h"
|
||||
|
||||
|
||||
namespace UdpLibrary
|
||||
{
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// UdpMisc functions
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int UdpMisc::SyncStampShortDeltaTime(udp_ushort stamp1, udp_ushort stamp2)
|
||||
{
|
||||
udp_ushort delta = (udp_ushort)(stamp1 - stamp2);
|
||||
if (delta > 0x7fff)
|
||||
return((int)(0xffff - delta));
|
||||
return((int)delta);
|
||||
}
|
||||
|
||||
int UdpMisc::SyncStampLongDeltaTime(udp_uint stamp1, udp_uint stamp2)
|
||||
{
|
||||
udp_uint delta = stamp1 - stamp2;
|
||||
if (delta > 0x7fffffff)
|
||||
return((int)(0xffffffff - delta));
|
||||
return((int)delta);
|
||||
}
|
||||
|
||||
int UdpMisc::Random(int *seed)
|
||||
{
|
||||
int hi = *seed / 127773;
|
||||
int lo = *seed % 127773;
|
||||
int t = lo * 16807 - hi * 2836 + 123;
|
||||
*seed = (t > 0) ? t : (t + 2147483647);
|
||||
return(*seed);
|
||||
}
|
||||
|
||||
int UdpMisc::Crc32(const void *buffer, int bufferLen, int encryptValue)
|
||||
{
|
||||
static unsigned crc32_table[256] = {
|
||||
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
|
||||
0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
|
||||
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
|
||||
0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
|
||||
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
|
||||
0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
|
||||
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
|
||||
0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
|
||||
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
|
||||
0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
|
||||
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
|
||||
0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
|
||||
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
|
||||
0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
|
||||
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
|
||||
0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
|
||||
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
|
||||
0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
|
||||
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
|
||||
0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
|
||||
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
|
||||
0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
|
||||
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
|
||||
0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
|
||||
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
|
||||
0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
|
||||
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
|
||||
0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
|
||||
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
|
||||
0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
|
||||
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
|
||||
0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
|
||||
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
|
||||
0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
|
||||
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
|
||||
0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
|
||||
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
|
||||
0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
|
||||
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
|
||||
0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
|
||||
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
|
||||
0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
|
||||
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D};
|
||||
|
||||
int crc = 0xffffffff;
|
||||
crc = ((crc >> 8) & 0x00FFFFFFL) ^ crc32_table[(crc ^ (encryptValue & 0xff)) & 0x000000FFL];
|
||||
crc = ((crc >> 8) & 0x00FFFFFFL) ^ crc32_table[(crc ^ ((encryptValue >> 8) & 0xff)) & 0x000000FFL];
|
||||
crc = ((crc >> 8) & 0x00FFFFFFL) ^ crc32_table[(crc ^ ((encryptValue >> 16) & 0xff)) & 0x000000FFL];
|
||||
crc = ((crc >> 8) & 0x00FFFFFFL) ^ crc32_table[(crc ^ ((encryptValue >> 24) & 0xff)) & 0x000000FFL];
|
||||
|
||||
const udp_uchar *bufPtr = (const udp_uchar *)buffer;
|
||||
const udp_uchar *endPtr = (const udp_uchar *)buffer + bufferLen;
|
||||
while (bufPtr < endPtr)
|
||||
{
|
||||
crc = ((crc >> 8) & 0x00FFFFFFL) ^ crc32_table[(crc ^ *bufPtr) & 0x000000FFL];
|
||||
bufPtr++;
|
||||
}
|
||||
crc = ~crc;
|
||||
|
||||
return(crc);
|
||||
}
|
||||
|
||||
void *UdpMisc::SmartResize(void *ptr, int bytes, int round)
|
||||
{
|
||||
enum { cAlignment = sizeof(int) };
|
||||
|
||||
// rounding stuff for speed
|
||||
bytes = ((bytes + round - 1) / round) * round;
|
||||
|
||||
if (bytes == 0)
|
||||
{
|
||||
if (ptr != NULL)
|
||||
{
|
||||
free((udp_uchar *)ptr - cAlignment);
|
||||
}
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
udp_uchar *ptr2;
|
||||
if (ptr == NULL)
|
||||
{
|
||||
ptr2 = (udp_uchar *)malloc(bytes + cAlignment);
|
||||
if (ptr2 == NULL)
|
||||
return(NULL);
|
||||
*(int *)ptr2 = bytes;
|
||||
return(ptr2 + cAlignment);
|
||||
}
|
||||
|
||||
int oldBytes = *((int *)((udp_uchar *)ptr - cAlignment));
|
||||
if (bytes == oldBytes)
|
||||
return(ptr);
|
||||
|
||||
ptr2 = (udp_uchar *)realloc((udp_uchar *)ptr - cAlignment, bytes + cAlignment);
|
||||
if (ptr2 == NULL)
|
||||
return(NULL);
|
||||
|
||||
*(int *)ptr2 = bytes;
|
||||
return(ptr2 + cAlignment);
|
||||
}
|
||||
|
||||
udp_uint UdpMisc::PutVariableValue(void *buffer, udp_uint value)
|
||||
{
|
||||
udp_uchar *bufptr = (udp_uchar *)buffer;
|
||||
udp_uint store = (udp_uint)value;
|
||||
if (store < 254)
|
||||
{
|
||||
*bufptr = (udp_uchar)store;
|
||||
return(1);
|
||||
}
|
||||
else if (store < 0xffff)
|
||||
{
|
||||
*bufptr = 0xff;
|
||||
*(bufptr + 1) = (udp_uchar)(store >> 8);
|
||||
*(bufptr + 2) = (udp_uchar)(store & 0xff);
|
||||
return(3);
|
||||
}
|
||||
else
|
||||
{
|
||||
*bufptr = 0xff;
|
||||
*(bufptr + 1) = 0xff;
|
||||
*(bufptr + 2) = 0xff;
|
||||
*(bufptr + 3) = (udp_uchar)(store >> 24);
|
||||
*(bufptr + 4) = (udp_uchar)((store >> 16) & 0xff);
|
||||
*(bufptr + 5) = (udp_uchar)((store >> 8) & 0xff);
|
||||
*(bufptr + 6) = (udp_uchar)(store & 0xff);
|
||||
return(7);
|
||||
}
|
||||
}
|
||||
|
||||
udp_uint UdpMisc::GetVariableValue(const void *buffer, udp_uint *value)
|
||||
{
|
||||
const udp_uchar *bufptr = (const udp_uchar *)buffer;
|
||||
if (*bufptr == 0xff)
|
||||
{
|
||||
if (*(bufptr + 1) == 0xff && *(bufptr + 2) == 0xff)
|
||||
{
|
||||
*value = (udp_uint)((*(bufptr + 3) << 24) | (*(bufptr + 4) << 16) | (*(bufptr + 5) << 8) | *(bufptr + 6));
|
||||
return(7);
|
||||
}
|
||||
|
||||
*value = (udp_uint)((*(bufptr + 1) << 8) | *(bufptr + 2));
|
||||
return(3);
|
||||
}
|
||||
|
||||
*value = (udp_uint)*bufptr;
|
||||
return(1);
|
||||
}
|
||||
|
||||
LogicalPacket *UdpMisc::CreateQuickLogicalPacket(const void *data, int dataLen, const void *data2, int dataLen2)
|
||||
{
|
||||
enum { cQuickFactor = 128 };
|
||||
int totalDataLen = dataLen + dataLen2;
|
||||
int q = (totalDataLen - 1) / cQuickFactor;
|
||||
LogicalPacket *tlp;
|
||||
switch(q)
|
||||
{
|
||||
case 0:
|
||||
tlp = new FixedLogicalPacket<cQuickFactor>(NULL, totalDataLen);
|
||||
break;
|
||||
case 1:
|
||||
tlp = new FixedLogicalPacket<cQuickFactor * 2>(NULL, totalDataLen);
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
tlp = new FixedLogicalPacket<cQuickFactor * 4>(NULL, totalDataLen);
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
tlp = new FixedLogicalPacket<cQuickFactor * 8>(NULL, totalDataLen);
|
||||
break;
|
||||
default:
|
||||
tlp = new SimpleLogicalPacket(NULL, totalDataLen);
|
||||
break;
|
||||
}
|
||||
|
||||
udp_uchar *dest = (udp_uchar *)tlp->GetDataPtr();
|
||||
if (data != NULL)
|
||||
memcpy(dest, data, dataLen);
|
||||
if (data2 != NULL)
|
||||
memcpy(dest + dataLen, data2, dataLen2);
|
||||
return(tlp);
|
||||
}
|
||||
|
||||
UdpPlatformAddress UdpMisc::GetHostByName(const char *hostName)
|
||||
{
|
||||
static UdpPlatformDriver sDriver;
|
||||
UdpPlatformAddress ip;
|
||||
sDriver.GetHostByName(&ip, hostName);
|
||||
return(ip);
|
||||
}
|
||||
|
||||
void UdpMisc::Sleep(int milliseconds)
|
||||
{
|
||||
static UdpPlatformDriver sDriver;
|
||||
sDriver.Sleep(milliseconds);
|
||||
}
|
||||
|
||||
UdpPlatformAddress UdpMisc::GetSelfAddress(bool preferNoRoute)
|
||||
{
|
||||
static UdpPlatformDriver sDriver;
|
||||
UdpPlatformAddress ip;
|
||||
sDriver.GetSelfAddress(&ip, preferNoRoute);
|
||||
return(ip);
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,237 @@
|
||||
#ifndef UDPLIBRARY_UDPMISC_H
|
||||
#define UDPLIBRARY_UDPMISC_H
|
||||
|
||||
// Copyright 2004 Sony Online Entertainment, all rights reserved.
|
||||
// Author: Jeff Petersen
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "UdpDriver.h"
|
||||
|
||||
namespace UdpLibrary
|
||||
{
|
||||
|
||||
class LogicalPacket;
|
||||
|
||||
class UdpMisc
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// This is a group of miscellaneous function used as part of the implementation
|
||||
// The application is free to use these helper functions as well if finds them useful.
|
||||
// they are stuck into this class so as to avoid conflicts with the application
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
public:
|
||||
|
||||
|
||||
#if 1 // DEPRECATED (here for backwards compatibility only. Not used by UdpLibrary, it uses the UdpPlatformDriver version)
|
||||
// Internally these create a static UdpPlatformDriver and chain the call through. If you have a UdpManager, I recommend
|
||||
// simply call UdpManager::Clock and UdpManager::ClockElapsed, which chain on through to the driver created by the UdpManager
|
||||
// values returned by this clock function should not be compared to values return from the UdpManager clock function as
|
||||
// they go through two different drivers to get the answer and may not return the same number (though in practice it
|
||||
// likely always will).
|
||||
typedef UdpClockStamp ClockStamp;
|
||||
static ClockStamp Clock(); // returns a timestamp (most likely in milliseconds)
|
||||
static int ClockElapsed(ClockStamp stamp); // returns a elapsed time since stamp in milliseconds (if elapsed is over 23 days, it returns 23 days)
|
||||
#endif
|
||||
|
||||
|
||||
static int ClockDiff(UdpClockStamp start, UdpClockStamp stop); // returns a time difference in milliseconds (if difference is over 23 days, it returns 23 days)
|
||||
static int Crc32(const void *buffer, int bufferLen, int encryptValue = 0); // calculate a 32-bit crc for a buffer (encrypt value simple scrambles the crc at the beginning so the same packet doesn't produce the same crc on different connections)
|
||||
static int Random(int *seed); // random number generator
|
||||
static void Sleep(int milliseconds);
|
||||
|
||||
// returns the time difference between two sync-stamps that are based on the same clock
|
||||
// for example, two LocalSyncStampShort stamps can be compared. Only differences up to
|
||||
// 32 seconds can be timed. There is also a UdpConnect::ServerSyncStampShort function that returns
|
||||
// you a sync-stamp that you can compare with ServerSyncStampShort values generated on other
|
||||
// machines that are synchronized against the same server. This ServerSyncStampShort function and
|
||||
// this delta time function serve as the basis for calculating one-way travel times for packets.
|
||||
static int SyncStampShortDeltaTime(udp_ushort stamp1, udp_ushort stamp2);
|
||||
static int SyncStampLongDeltaTime(udp_uint stamp1, udp_uint stamp2); // same as Short version only no limit
|
||||
|
||||
// used to alloc/resize/free an allocation previously created with this function
|
||||
// rounding causes that it pre-allocates additional space up to the rounded buffer size
|
||||
// this allows the function to be called over and over again as tiny members are added, yet
|
||||
// only do an actual realloc periodically. A 4-byte header is invisibly prepended on the
|
||||
// allocated block such that it can keep track of the actual size of the block. The built-in
|
||||
// memory manager does a little bit of this, but if you know you have an allocation that is likely
|
||||
// to grow quite a bit, you can set the round'ing size up to a fairly large number and avoid
|
||||
// unnecessary reallocs at the cost of a little potentially wasted space
|
||||
// initial allocations are done by passing in ptr==NULL, freeing is done by passing in bytes==0
|
||||
static void *SmartResize(void *ptr, int bytes, int round = 1);
|
||||
|
||||
// the following two functions store values in the buffer as a variable length (B1, 0xffB2B1, 0xffffffB4B3B2B1)
|
||||
// such that values under 254 take one byte, values under 65535 take three bytes, and larger values take seven bytes
|
||||
static udp_uint PutVariableValue(void *buffer, udp_uint value); // returns the number of bytes it took to store the value in the buffer
|
||||
static udp_uint GetVariableValue(const void *buffer, udp_uint *value); // returns the number of bytes it took to get the value from the buffer
|
||||
|
||||
// these functions are used to aid in portability and serve to ensure that the packet-headers are interpretted in the same
|
||||
// manner on all platforms
|
||||
static int PutValue64(void *buffer, udp_int64 value); // puts a 64-bit value into the buffer in big-endian format, returns number of bytes used(8)
|
||||
static int PutValue32(void *buffer, udp_uint value); // puts a 32-bit value into the buffer in big-endian format, returns number of bytes used(4)
|
||||
static int PutValue24(void *buffer, udp_uint value); // puts a 24-bit value into the buffer in big-endian format, returns number of bytes used(4)
|
||||
static int PutValue16(void *buffer, udp_ushort value); // puts a 16-bit value into the buffer in big-endian format, returns number of bytes used(2)
|
||||
static int PutValueLE32(void *buffer, udp_uint value); // puts a 32-bit value in little-endian format
|
||||
|
||||
static udp_int64 GetValue64(const void *buffer); // gets a 64-bit value from the buffer in big-endian format
|
||||
static udp_uint GetValue32(const void *buffer); // gets a 32-bit value from the buffer in big-endian format
|
||||
static udp_uint GetValue24(const void *buffer); // gets a 24-bit value from the buffer in big-endian format
|
||||
static udp_ushort GetValue16(const void *buffer); // gets a 16-bit value from the buffer in big-endian format
|
||||
|
||||
static LogicalPacket *CreateQuickLogicalPacket(const void *data, int dataLen, const void *data2 = NULL, int dataLen2 = 0);
|
||||
|
||||
// looks up the specified name and translates it to an IP address
|
||||
// this is a blocking call that can at times take a significant amount of time, but will generally be fast (less than 300ms)
|
||||
static UdpPlatformAddress GetHostByName(const char *hostName);
|
||||
static UdpPlatformAddress GetSelfAddress(bool preferNoRoute);
|
||||
|
||||
static char *Strncpy(char *dest, const char *source, int len);
|
||||
static char *Strncat(char *dest, const char *source, int len);
|
||||
};
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// inline implementations
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// UdpMisc
|
||||
#if 1 // DEPRECATED (see declaration)
|
||||
inline UdpMisc::ClockStamp UdpMisc::Clock()
|
||||
{
|
||||
static UdpPlatformDriver sClockDriver;
|
||||
return(sClockDriver.Clock());
|
||||
}
|
||||
|
||||
inline int UdpMisc::ClockElapsed(ClockStamp stamp)
|
||||
{
|
||||
return(UdpMisc::ClockDiff(stamp, Clock()));
|
||||
}
|
||||
#endif
|
||||
|
||||
inline int UdpMisc::ClockDiff(UdpClockStamp start, UdpClockStamp stop)
|
||||
{
|
||||
UdpClockStamp t = (stop - start);
|
||||
if (t > 0x7fffffff) // only time differences up to about 23 days can be measured with this function
|
||||
return(0x7fffffff);
|
||||
return((int)t);
|
||||
}
|
||||
|
||||
inline int UdpMisc::PutValue64(void *buffer, udp_int64 value)
|
||||
{
|
||||
udp_uchar *bufptr = (udp_uchar *)buffer;
|
||||
*bufptr++ = (udp_uchar)(value >> 56);
|
||||
*bufptr++ = (udp_uchar)((value >> 48) & 0xff);
|
||||
*bufptr++ = (udp_uchar)((value >> 40) & 0xff);
|
||||
*bufptr++ = (udp_uchar)((value >> 32) & 0xff);
|
||||
*bufptr++ = (udp_uchar)((value >> 24) & 0xff);
|
||||
*bufptr++ = (udp_uchar)((value >> 16) & 0xff);
|
||||
*bufptr++ = (udp_uchar)((value >> 8) & 0xff);
|
||||
*bufptr = (udp_uchar)(value & 0xff);
|
||||
return(8);
|
||||
}
|
||||
|
||||
inline udp_int64 UdpMisc::GetValue64(const void *buffer)
|
||||
{
|
||||
const udp_uchar *bufptr = (const udp_uchar *)buffer;
|
||||
return(((udp_int64)*bufptr << 56) | ((udp_int64)*(bufptr + 1) << 48) | ((udp_int64)*(bufptr + 2) << 40) | ((udp_int64)*(bufptr + 3) << 32) | ((udp_int64)*(bufptr + 4) << 24) | ((udp_int64)*(bufptr + 5) << 16) | ((udp_int64)*(bufptr + 6) << 8) | (udp_int64)*(bufptr + 7));
|
||||
}
|
||||
|
||||
inline int UdpMisc::PutValue32(void *buffer, udp_uint value)
|
||||
{
|
||||
udp_uchar *bufptr = (udp_uchar *)buffer;
|
||||
*bufptr++ = (udp_uchar)(value >> 24);
|
||||
*bufptr++ = (udp_uchar)((value >> 16) & 0xff);
|
||||
*bufptr++ = (udp_uchar)((value >> 8) & 0xff);
|
||||
*bufptr = (udp_uchar)(value & 0xff);
|
||||
return(4);
|
||||
}
|
||||
|
||||
inline int UdpMisc::PutValueLE32(void *buffer, udp_uint value)
|
||||
{
|
||||
udp_uchar *bufptr = (udp_uchar *)buffer;
|
||||
*bufptr++ = (udp_uchar)(value & 0xff);
|
||||
*bufptr++ = (udp_uchar)((value >> 8) & 0xff);
|
||||
*bufptr++ = (udp_uchar)((value >> 16) & 0xff);
|
||||
*bufptr = (udp_uchar)(value >> 24);
|
||||
return(4);
|
||||
}
|
||||
|
||||
inline udp_uint UdpMisc::GetValue32(const void *buffer)
|
||||
{
|
||||
const udp_uchar *bufptr = (const udp_uchar *)buffer;
|
||||
return((*bufptr << 24) | (*(bufptr + 1) << 16) | (*(bufptr + 2) << 8) | *(bufptr + 3));
|
||||
}
|
||||
|
||||
inline int UdpMisc::PutValue24(void *buffer, udp_uint value)
|
||||
{
|
||||
udp_uchar *bufptr = (udp_uchar *)buffer;
|
||||
*bufptr++ = (udp_uchar)((value >> 16) & 0xff);
|
||||
*bufptr++ = (udp_uchar)((value >> 8) & 0xff);
|
||||
*bufptr = (udp_uchar)(value & 0xff);
|
||||
return(3);
|
||||
}
|
||||
|
||||
inline udp_uint UdpMisc::GetValue24(const void *buffer)
|
||||
{
|
||||
const udp_uchar *bufptr = (const udp_uchar *)buffer;
|
||||
return((*bufptr << 16) | (*(bufptr + 1) << 8) | *(bufptr + 2));
|
||||
}
|
||||
|
||||
inline int UdpMisc::PutValue16(void *buffer, udp_ushort value)
|
||||
{
|
||||
udp_uchar *bufptr = (udp_uchar *)buffer;
|
||||
*bufptr++ = (udp_uchar)((value >> 8) & 0xff);
|
||||
*bufptr = (udp_uchar)(value & 0xff);
|
||||
return(2);
|
||||
}
|
||||
|
||||
inline udp_ushort UdpMisc::GetValue16(const void *buffer)
|
||||
{
|
||||
const udp_uchar *bufptr = (const udp_uchar *)buffer;
|
||||
return((udp_ushort)((*bufptr << 8) | *(bufptr + 1)));
|
||||
}
|
||||
|
||||
inline char *UdpMisc::Strncpy(char *dest, const char *source, int len)
|
||||
{
|
||||
if (len > 0)
|
||||
{
|
||||
char *walk = dest;
|
||||
char *end = dest + len - 1;
|
||||
while (*source != 0 && walk < end)
|
||||
{
|
||||
*walk++ = *source++;
|
||||
}
|
||||
*walk = 0;
|
||||
}
|
||||
return(dest);
|
||||
}
|
||||
|
||||
inline char *UdpMisc::Strncat(char *dest, const char *source, int len)
|
||||
{
|
||||
int clen = (int)strlen(dest);
|
||||
len -= clen;
|
||||
if (len > 1)
|
||||
{
|
||||
Strncpy(dest + clen, source, len);
|
||||
}
|
||||
return(dest);
|
||||
}
|
||||
|
||||
template <typename ValueType> const ValueType &udpMax(const ValueType &a, const ValueType &b)
|
||||
{
|
||||
if (a < b)
|
||||
return b;
|
||||
return a;
|
||||
}
|
||||
|
||||
template<typename ValueType> const ValueType &udpMin(const ValueType &a, const ValueType &b)
|
||||
{
|
||||
if (b < a)
|
||||
return b;
|
||||
return a;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,233 @@
|
||||
#ifndef UDPLIBRARY_UDPPRIORITY_H
|
||||
#define UDPLIBRARY_UDPPRIORITY_H
|
||||
|
||||
// Copyright 2004 Sony Online Entertainment, all rights reserved.
|
||||
// Author: Jeff Petersen
|
||||
|
||||
namespace UdpLibrary
|
||||
{
|
||||
|
||||
|
||||
template<typename T, typename P> class PriorityQueue;
|
||||
|
||||
// classes that wish to be members of the priority queue below must derive themselves from this class
|
||||
// in order to pull in the two member variables. Unlike normal priority queue classes which don't
|
||||
// have this restriction, it is required in order to support Remove and reprioritize functionality
|
||||
// in a timely manner (otherwise, in order to remove an entry that is not at the top, you would have
|
||||
// to linearly scan the entire queue). This is accomplished by having the member itself contain
|
||||
// a pointer to it's position in the queue (mPriorityQueuePosition).
|
||||
// note: a restriction of this ability is that an object cannot participate in more than one priority
|
||||
// queue at a time.
|
||||
class PriorityQueueMember
|
||||
{
|
||||
public:
|
||||
PriorityQueueMember();
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1300) // MSVC 7.0 is the first version to support friend templates
|
||||
public:
|
||||
#else
|
||||
protected:
|
||||
template<typename T, typename P> friend class PriorityQueue;
|
||||
#endif
|
||||
int mPriorityQueuePosition; // -1=not in queue
|
||||
};
|
||||
|
||||
// this class provides a priority queue that is capable of reprioritizing/removing entries. The
|
||||
// compiler will ensure that objects stored in this class are derived from PriorityQueueMember.
|
||||
// We don't really need this to be a template class, since we could just treat everything as a
|
||||
// PriorityQueueMember, but then the application would lose some type checking. We don't use references
|
||||
// in the api as most priority queue templates do as we can't support non pointer types anyways.
|
||||
template<typename T, typename P> class PriorityQueue
|
||||
{
|
||||
public:
|
||||
PriorityQueue(int queueSize);
|
||||
~PriorityQueue();
|
||||
|
||||
T* Top(); // returns NULL if queue is empty
|
||||
T* TopRemove(); // returns NULL if queue is empty
|
||||
T* TopRemove(P priority); // removes item from queue if it has a lower priority value, otherwise return NULL
|
||||
T* Add(T* entry, P priority); // reprioritizes if already in queue, returns entry always
|
||||
T* Remove(T* entry); // returns entry always (even if it was not in the queue)
|
||||
P *GetPriority(T* entry); // returns NULL if entry is not in the queue
|
||||
int QueueUsed(); // returns how many entries are in the queue
|
||||
protected:
|
||||
struct QueueEntry
|
||||
{
|
||||
T* entry;
|
||||
P priority;
|
||||
};
|
||||
|
||||
QueueEntry* mQueue;
|
||||
int mQueueSize;
|
||||
int mQueueEnd;
|
||||
|
||||
void Refloat(T* entry);
|
||||
};
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// PriorityQueueMember implementation
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
inline PriorityQueueMember::PriorityQueueMember()
|
||||
{
|
||||
mPriorityQueuePosition = -1;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// PriorityQueue implementation
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
template<typename T, typename P> PriorityQueue<T, P>::PriorityQueue(int queueSize)
|
||||
{
|
||||
mQueueEnd = 0;
|
||||
mQueueSize = queueSize;
|
||||
mQueue = new QueueEntry[mQueueSize];
|
||||
memset(mQueue, 0, sizeof(mQueue));
|
||||
}
|
||||
|
||||
template<typename T, typename P> PriorityQueue<T, P>::~PriorityQueue()
|
||||
{
|
||||
delete[] mQueue;
|
||||
}
|
||||
|
||||
template<typename T, typename P> T* PriorityQueue<T, P>::Top()
|
||||
{
|
||||
if (mQueueEnd == 0)
|
||||
return(NULL);
|
||||
return(mQueue[0].entry);
|
||||
}
|
||||
|
||||
template<typename T, typename P> T* PriorityQueue<T, P>::TopRemove()
|
||||
{
|
||||
if (mQueueEnd == 0)
|
||||
return(NULL);
|
||||
T* top = mQueue[0].entry;
|
||||
Remove(top);
|
||||
return(top);
|
||||
}
|
||||
|
||||
template<typename T, typename P> T* PriorityQueue<T, P>::TopRemove(P priority)
|
||||
{
|
||||
if (mQueueEnd > 0 && mQueue[0].priority <= priority)
|
||||
return(Remove(mQueue[0].entry));
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
template<typename T, typename P> P* PriorityQueue<T, P>::GetPriority(T* entry)
|
||||
{
|
||||
if (entry->mPriorityQueuePosition >= 0)
|
||||
return(&mQueue[entry->mPriorityQueuePosition].priority);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
template<typename T, typename P> T* PriorityQueue<T, P>::Add(T* entry, P priority)
|
||||
{
|
||||
if (entry->mPriorityQueuePosition == -1)
|
||||
{
|
||||
// not in queue, so add it to the bottom
|
||||
if (mQueueEnd >= mQueueSize)
|
||||
return(NULL);
|
||||
mQueue[mQueueEnd].entry = entry;
|
||||
mQueue[mQueueEnd].priority = priority;
|
||||
mQueue[mQueueEnd].entry->mPriorityQueuePosition = mQueueEnd;
|
||||
mQueueEnd++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// see if priority has actually changed, if not, just return, otherwise change priority and fall through to refloat it
|
||||
if (mQueue[entry->mPriorityQueuePosition].priority == priority)
|
||||
return(entry);
|
||||
mQueue[entry->mPriorityQueuePosition].priority = priority;
|
||||
}
|
||||
|
||||
Refloat(entry);
|
||||
return(entry);
|
||||
}
|
||||
|
||||
template<typename T, typename P> T* PriorityQueue<T, P>::Remove(T* entry)
|
||||
{
|
||||
if (entry->mPriorityQueuePosition == -1)
|
||||
return(entry);
|
||||
|
||||
// move end entry into place of one being removed
|
||||
mQueueEnd--;
|
||||
int spot = entry->mPriorityQueuePosition;
|
||||
if (spot != mQueueEnd) // don't remove last item in queue (bottom of tree), so no need to copy bottom one up and refloat it (we would be refloating our own removed entry)
|
||||
{
|
||||
mQueue[spot] = mQueue[mQueueEnd];
|
||||
mQueue[spot].entry->mPriorityQueuePosition = spot;
|
||||
Refloat(mQueue[spot].entry);
|
||||
}
|
||||
entry->mPriorityQueuePosition = -1;
|
||||
return(entry);
|
||||
}
|
||||
|
||||
template<typename T, typename P> void PriorityQueue<T, P>::Refloat(T* entry)
|
||||
{
|
||||
// float upward
|
||||
int spot = entry->mPriorityQueuePosition;
|
||||
bool tryDown = true;
|
||||
while (spot > 0 && mQueue[spot].priority < mQueue[(spot - 1) / 2].priority)
|
||||
{
|
||||
int newSpot = (spot - 1) / 2;
|
||||
QueueEntry hold = mQueue[spot];
|
||||
mQueue[spot] = mQueue[newSpot];
|
||||
mQueue[newSpot] = hold;
|
||||
mQueue[spot].entry->mPriorityQueuePosition = spot;
|
||||
mQueue[newSpot].entry->mPriorityQueuePosition = newSpot;
|
||||
spot = newSpot;
|
||||
tryDown = false;
|
||||
}
|
||||
|
||||
if (tryDown)
|
||||
{
|
||||
// if we didn't manage to float up at all, then we need to try floating down
|
||||
for (;;)
|
||||
{
|
||||
// pick smallest child
|
||||
int downSpot1 = (spot * 2) + 1;
|
||||
if (downSpot1 >= mQueueEnd)
|
||||
break;
|
||||
int downSpot2 = (spot * 2) + 2;
|
||||
|
||||
if (downSpot2 >= mQueueEnd || mQueue[downSpot1].priority < mQueue[downSpot2].priority)
|
||||
{
|
||||
if (mQueue[downSpot1].priority < mQueue[spot].priority)
|
||||
{
|
||||
QueueEntry hold = mQueue[spot];
|
||||
mQueue[spot] = mQueue[downSpot1];
|
||||
mQueue[downSpot1] = hold;
|
||||
mQueue[spot].entry->mPriorityQueuePosition = spot;
|
||||
mQueue[downSpot1].entry->mPriorityQueuePosition = downSpot1;
|
||||
spot = downSpot1;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mQueue[downSpot2].priority < mQueue[spot].priority)
|
||||
{
|
||||
QueueEntry hold = mQueue[spot];
|
||||
mQueue[spot] = mQueue[downSpot2];
|
||||
mQueue[downSpot2] = hold;
|
||||
mQueue[spot].entry->mPriorityQueuePosition = spot;
|
||||
mQueue[downSpot2].entry->mPriorityQueuePosition = downSpot2;
|
||||
spot = downSpot2;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename P> int PriorityQueue<T, P>::QueueUsed()
|
||||
{
|
||||
return(mQueueEnd);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
+881
@@ -0,0 +1,881 @@
|
||||
// Copyright 2004 Sony Online Entertainment, all rights reserved.
|
||||
// Author: Jeff Petersen
|
||||
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "UdpLibrary.h"
|
||||
#include "UdpReliableChannel.h"
|
||||
|
||||
|
||||
namespace UdpLibrary
|
||||
{
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ReliableChannel implementation
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
UdpReliableChannel::UdpReliableChannel(int channelNumber, UdpConnection *con, UdpReliableConfig *config) : mLogicalPacketList(&LogicalPacket::mReliableLink)
|
||||
{
|
||||
mUdpConnection = con;
|
||||
mChannelNumber = channelNumber;
|
||||
mConfig = *config;
|
||||
mConfig.maxOutstandingPackets = udpMin(mConfig.maxOutstandingPackets, (int)UdpManager::cHardMaxOutstandingPackets);
|
||||
|
||||
mAveragePingTime = 800; // start out fairly high so we don't do a lot of resends on a bad connection to start out with
|
||||
mTrickleLastSend = 0;
|
||||
|
||||
int fragmentSize = mConfig.fragmentSize;
|
||||
if (fragmentSize == 0 || fragmentSize > mUdpConnection->mConnectionConfig.maxRawPacketSize)
|
||||
fragmentSize = mUdpConnection->mConnectionConfig.maxRawPacketSize;
|
||||
mMaxDataBytes = (fragmentSize - UdpConnection::cUdpPacketReliableSize - mUdpConnection->mConnectionConfig.crcBytes - mUdpConnection->mEncryptExpansionBytes);
|
||||
assert(mMaxDataBytes > 0); // fragment size/max-raw-packet-size set too small to allow for reliable deliver
|
||||
if (mConfig.trickleSize != 0)
|
||||
mMaxDataBytes = udpMin(mMaxDataBytes, mConfig.trickleSize);
|
||||
|
||||
mMaxCoalesceAttemptBytes = -1;
|
||||
if (mConfig.coalesce)
|
||||
{
|
||||
mMaxCoalesceAttemptBytes = mMaxDataBytes - 5; // 2 bytes for group-header, 3 bytes for length of packet
|
||||
}
|
||||
|
||||
mReliableIncomingId = 0;
|
||||
mReliableOutgoingId = 0;
|
||||
mReliableOutgoingPendingId = 0;
|
||||
mReliableOutgoingBytes = 0;
|
||||
mLogicalBytesQueued = 0;
|
||||
|
||||
mCoalescePacket = NULL;
|
||||
mCoalesceStartPtr = NULL;
|
||||
mCoalesceEndPtr = NULL;
|
||||
mCoalesceCount = 0;
|
||||
|
||||
mBufferedAckPtr = NULL;
|
||||
|
||||
mStatDuplicatePacketsReceived = 0;
|
||||
mStatResentPacketsAccelerated = 0;
|
||||
mStatResentPacketsTimedOut = 0;
|
||||
|
||||
mWindowResetTime = 0;
|
||||
|
||||
mCongestionWindowMinimum = udpMax(mMaxDataBytes, mConfig.congestionWindowMinimum);
|
||||
mCongestionWindowStart = udpMin(4 * mMaxDataBytes, udpMax(2 * mMaxDataBytes, 4380));
|
||||
mCongestionWindowStart = udpMax(mCongestionWindowStart, mCongestionWindowMinimum);
|
||||
mCongestionSlowStartThreshhold = udpMin(mConfig.maxOutstandingPackets * mMaxDataBytes, mConfig.maxOutstandingBytes);
|
||||
mCongestionWindowSize = mCongestionWindowStart;
|
||||
|
||||
mBigDataLen = 0;
|
||||
mBigDataTargetLen = 0;
|
||||
mBigDataPtr = NULL;
|
||||
mFragmentNextPos = 0;
|
||||
mLastTimeStampAcknowledged = 0;
|
||||
mMaxxedOutCurrentWindow = false;
|
||||
mNextNeedTime = 0;
|
||||
|
||||
mPhysicalPackets = new PhysicalPacket[mConfig.maxOutstandingPackets];
|
||||
mReliableIncoming = new IncomingQueueEntry[mConfig.maxInstandingPackets];
|
||||
}
|
||||
|
||||
UdpReliableChannel::~UdpReliableChannel()
|
||||
{
|
||||
if (mCoalescePacket != NULL)
|
||||
{
|
||||
mCoalescePacket->Release();
|
||||
mCoalescePacket = NULL;
|
||||
}
|
||||
|
||||
const LogicalPacket *cur = mLogicalPacketList.RemoveHead();
|
||||
while (cur != NULL)
|
||||
{
|
||||
cur->Release();
|
||||
cur = mLogicalPacketList.RemoveHead();
|
||||
}
|
||||
|
||||
delete[] mPhysicalPackets; // destructor will release any logical packets as appropriate
|
||||
delete[] mReliableIncoming; // destructor will delete any data as appropriate
|
||||
|
||||
// delete any big packet that might be under construction
|
||||
delete[] mBigDataPtr;
|
||||
}
|
||||
|
||||
void UdpReliableChannel::Send(const udp_uchar *data, int dataLen, const udp_uchar *data2, int dataLen2)
|
||||
{
|
||||
if (mLogicalPacketList.Count() == 0 && mCoalescePacket == NULL)
|
||||
{
|
||||
// if we are adding something to a previously empty logical queue, then it is possible that
|
||||
// we may be able to send it, so mark ourselves to take time the next time it is offered
|
||||
mNextNeedTime = 0;
|
||||
mUdpConnection->ScheduleTimeNow();
|
||||
}
|
||||
|
||||
if (dataLen + dataLen2 <= mMaxCoalesceAttemptBytes)
|
||||
{
|
||||
SendCoalesce(data, dataLen, data2, dataLen2);
|
||||
}
|
||||
else
|
||||
{
|
||||
FlushCoalesce();
|
||||
LogicalPacket *packet = mUdpConnection->mUdpManager->CreatePacket(data, dataLen, data2, dataLen2);
|
||||
QueueLogicalPacket(packet);
|
||||
packet->Release();
|
||||
}
|
||||
}
|
||||
|
||||
void UdpReliableChannel::FlushCoalesce()
|
||||
{
|
||||
if (mCoalescePacket != NULL)
|
||||
{
|
||||
if (mCoalesceCount == 1)
|
||||
{
|
||||
// only one packet in coalesce, so remove the coalesce/group header and make it just a simple raw packet
|
||||
int firstLen;
|
||||
int skipLen = UdpMisc::GetVariableValue(mCoalesceStartPtr + 2, (udp_uint *)&firstLen);
|
||||
memmove(mCoalesceStartPtr, mCoalesceStartPtr + 2 + skipLen, firstLen);
|
||||
mCoalesceEndPtr = mCoalesceStartPtr + firstLen;
|
||||
}
|
||||
|
||||
mCoalescePacket->SetDataLen((int)(mCoalesceEndPtr - mCoalesceStartPtr));
|
||||
QueueLogicalPacket(mCoalescePacket);
|
||||
mCoalescePacket->Release();
|
||||
mCoalescePacket = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void UdpReliableChannel::SendCoalesce(const udp_uchar *data, int dataLen, const udp_uchar *data2, int dataLen2)
|
||||
{
|
||||
int totalLen = dataLen + dataLen2;
|
||||
if (mCoalescePacket == NULL)
|
||||
{
|
||||
mCoalescePacket = mUdpConnection->mUdpManager->CreatePacket(NULL, mMaxDataBytes);
|
||||
mCoalesceEndPtr = mCoalesceStartPtr = (udp_uchar *)mCoalescePacket->GetDataPtr();
|
||||
*mCoalesceEndPtr++ = 0;
|
||||
*mCoalesceEndPtr++ = UdpConnection::cUdpPacketGroup;
|
||||
mCoalesceCount = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int spaceLeft = mMaxDataBytes - (int)(mCoalesceEndPtr - mCoalesceStartPtr);
|
||||
if (totalLen + 3 > spaceLeft) // 3 bytes to ensure PutVariableValue has room for the length indicator (this limits us to 64k coalescing, which is ok, since fragments can't get that big)
|
||||
{
|
||||
FlushCoalesce();
|
||||
SendCoalesce(data, dataLen, data2, dataLen2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// append on end of coalesce
|
||||
mCoalesceCount++;
|
||||
mCoalesceEndPtr += UdpMisc::PutVariableValue(mCoalesceEndPtr, totalLen);
|
||||
if (data != NULL)
|
||||
memcpy(mCoalesceEndPtr, data, dataLen);
|
||||
mCoalesceEndPtr += dataLen;
|
||||
if (data2 != NULL)
|
||||
memcpy(mCoalesceEndPtr, data2, dataLen2);
|
||||
mCoalesceEndPtr += dataLen2;
|
||||
}
|
||||
|
||||
void UdpReliableChannel::QueueLogicalPacket(LogicalPacket *packet)
|
||||
{
|
||||
packet->AddRef();
|
||||
mLogicalBytesQueued += packet->GetDataLen();
|
||||
mLogicalPacketList.InsertTail(packet);
|
||||
}
|
||||
|
||||
bool UdpReliableChannel::PullDown(int windowSpaceLeft)
|
||||
{
|
||||
// the pull-down on-demand method will give us the opportunity to late-combine as many tiny logical packets as we can,
|
||||
// reducing the number of tracked-packets that are required. This effectively reduces the number of acks that we are
|
||||
// going to get back, which can be substantial in situations where there are a LOT of tiny reliable packets being sent.
|
||||
// operating with fewer outstanding physical-packets to track is more CPU efficient as well.
|
||||
|
||||
// (NOTE: as of this writing, the below implementation does not do the late-combine techique yet)
|
||||
// (NOTE: we currently combine on send instead, which generally accomplishes the same thing)
|
||||
|
||||
bool pulledDown = false;
|
||||
int physicalCount = (int)(mReliableOutgoingId - mReliableOutgoingPendingId);
|
||||
while (windowSpaceLeft > 0 && physicalCount < mConfig.maxOutstandingPackets)
|
||||
{
|
||||
if (mLogicalPacketList.Count() == 0)
|
||||
{
|
||||
FlushCoalesce(); // this is guaranteed to stick
|
||||
if (mLogicalPacketList.Count() == 0)
|
||||
break; // nothing flushed, so we are done
|
||||
}
|
||||
|
||||
int nextSpot = (int)(mReliableOutgoingId % mConfig.maxOutstandingPackets);
|
||||
|
||||
// ok, we can move something down, even if it is only a fragment of the logical packet
|
||||
PhysicalPacket *entry = &mPhysicalPackets[nextSpot];
|
||||
entry->mParent = mLogicalPacketList.First();
|
||||
entry->mParent->AddRef(); // add ref from physical packet
|
||||
entry->mFirstTimeStamp = 0;
|
||||
entry->mLastTimeStamp = 0;
|
||||
|
||||
// calculate how much we can send based on our starting position (mFragmentNextPos) in the logical packet.
|
||||
// if we can't send it the rest of data to end of packet, then send the fragment portion and addref, otherwise send the whole thing and pop the logical packet
|
||||
int dataLen = entry->mParent->GetDataLen();
|
||||
const udp_uchar *data = (const udp_uchar *)entry->mParent->GetDataPtr();
|
||||
int bytesLeft = dataLen - mFragmentNextPos;
|
||||
int bytesToSend = udpMin(bytesLeft, mMaxDataBytes);
|
||||
|
||||
entry->mDataPtr = data + mFragmentNextPos;
|
||||
|
||||
// if not sending entire packet
|
||||
if (bytesToSend != dataLen)
|
||||
{
|
||||
// mark it as a fragment
|
||||
if (mFragmentNextPos == 0)
|
||||
bytesToSend -= 4; // fragment start has a 4 byte header specifying size of following large data, so make room for it so we don't exceed max raw packet size
|
||||
}
|
||||
entry->mDataLen = bytesToSend;
|
||||
mReliableOutgoingBytes += bytesToSend;
|
||||
|
||||
if (bytesToSend == bytesLeft)
|
||||
{
|
||||
mFragmentNextPos = 0;
|
||||
const LogicalPacket *lp = mLogicalPacketList.RemoveHead();
|
||||
lp->Release(); // release from logical queue
|
||||
}
|
||||
else
|
||||
{
|
||||
mFragmentNextPos += bytesToSend;
|
||||
}
|
||||
|
||||
mLogicalBytesQueued -= bytesToSend; // as fragments are sent, decrease the number of logical bytes queued
|
||||
mReliableOutgoingId++;
|
||||
physicalCount++;
|
||||
windowSpaceLeft -= bytesToSend;
|
||||
pulledDown = true;
|
||||
}
|
||||
return(pulledDown);
|
||||
}
|
||||
|
||||
int UdpReliableChannel::GiveTime()
|
||||
{
|
||||
udp_uchar buf[256];
|
||||
udp_uchar *bufPtr;
|
||||
|
||||
UdpClockStamp hotClock = mUdpConnection->GetUdpManager()->CachedClock();
|
||||
|
||||
if (hotClock < mNextNeedTime)
|
||||
return(UdpMisc::ClockDiff(hotClock, mNextNeedTime));
|
||||
|
||||
// if we are a trickle channel, then don't try sending more until trickleRate has expired. We are only allowed
|
||||
// to send up to trickleBytes at a time every trickleRate milliseconds; however, if we don't send the full trickleBytes
|
||||
// in one GiveTime call, then it won't get to try sending more bytes until this timer has expired, even if we had not used
|
||||
// up the entire trickleBytes allotment the last time we were in here...this should not cause any significant problems
|
||||
if (mConfig.trickleRate > 0)
|
||||
{
|
||||
int nextAllowedSendTime = mConfig.trickleRate - UdpMisc::ClockDiff(mTrickleLastSend, hotClock);
|
||||
if (nextAllowedSendTime > 0)
|
||||
return(nextAllowedSendTime);
|
||||
}
|
||||
|
||||
|
||||
// lot a tweaking goes into calculating the optimal resend time. Set it too large and you can stall the pipe
|
||||
// at the beginning of the connection fairly easily
|
||||
int optimalResendDelay = (mAveragePingTime * mConfig.resendDelayPercent / 100) + mConfig.resendDelayAdjust; // percentage of average ping plus a fixed amount
|
||||
optimalResendDelay = udpMin(mConfig.resendDelayCap, optimalResendDelay); // never let the resend delay get over max
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// see if any of the physical packets can actually be sent (either resends, or initial sends, whatever
|
||||
// if not, calculate when exactly somebody is expected to need sending
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
mMaxxedOutCurrentWindow = false;
|
||||
int outstandingNextSendTime = 10 * 60000;
|
||||
// if we have something to do
|
||||
if (mReliableOutgoingPendingId < mReliableOutgoingId || mLogicalPacketList.Count() != 0 || mCoalescePacket != NULL)
|
||||
{
|
||||
// first, let's calculate how many bytes we figure is outstanding based on who is still waiting for an ack-packet
|
||||
UdpClockStamp oldestResendTime = udpMax(hotClock - optimalResendDelay, mLastTimeStampAcknowledged); // anything older than this, we need to resend
|
||||
|
||||
bool readyQueueOverflow;
|
||||
do
|
||||
{
|
||||
readyQueueOverflow = false;
|
||||
int useMaxOutstandingBytes = udpMin(mConfig.maxOutstandingBytes, mCongestionWindowSize);
|
||||
int outstandingBytes = 0;
|
||||
enum { cReadyQueueSize = 1000 };
|
||||
PhysicalPacket *readyQueue[cReadyQueueSize];
|
||||
PhysicalPacket **readyEnd = readyQueue + (sizeof(readyQueue) / sizeof(PhysicalPacket *));
|
||||
PhysicalPacket **readyPtr = readyQueue;
|
||||
|
||||
int windowSpaceLeft = useMaxOutstandingBytes;
|
||||
for (udp_int64 i = mReliableOutgoingPendingId; i <= mReliableOutgoingId; i++)
|
||||
{
|
||||
if (i == mReliableOutgoingId)
|
||||
{
|
||||
// this packet is not really here yet, we need to pull it down if possible
|
||||
// if not possible, we need to break out of the loop as we are done
|
||||
if (!PullDown(windowSpaceLeft))
|
||||
break;
|
||||
}
|
||||
|
||||
// if this packet has not been acked and it is NOT ready to be sent (was recently sent) then we consider it outstanding
|
||||
// note: packets needing re-sending probably got lost and are therefore not outstanding
|
||||
PhysicalPacket *entry = &mPhysicalPackets[i % mConfig.maxOutstandingPackets];
|
||||
if (entry->mDataPtr != NULL) // acked packets set the dataPtr to NULL
|
||||
{
|
||||
// if this packet is ready to be sent (ie: needs time now, or some later packet has already been ack'ed)
|
||||
windowSpaceLeft -= entry->mDataLen; // window-space is effectively taken whether we have sent it yet or not
|
||||
if (entry->mLastTimeStamp < oldestResendTime)
|
||||
{
|
||||
if (readyPtr < readyEnd) // if we have queue space
|
||||
{
|
||||
*readyPtr++ = entry;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we ran out of space in the ready-queue, so we are going to have to do this whole process
|
||||
// over again. Normally we would just want to make sure the readyQueue was really big to save
|
||||
// us doing all this stuff twice; however, that uses up a ton of stack space we may not have
|
||||
// and really only happens in situations where there is a ton of stuff waiting to be sent
|
||||
// (ie, super high bandwidth situations really). So I don't mind taking a performance hit
|
||||
// for those situations to keep the stack size small for the vast majority of normal situations.
|
||||
// note: this was initially added in support of the tiny stack the PS3 had.
|
||||
readyQueueOverflow = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
outstandingBytes += entry->mDataLen;
|
||||
outstandingNextSendTime = udpMin(outstandingNextSendTime, optimalResendDelay - UdpMisc::ClockDiff(entry->mLastTimeStamp, hotClock));
|
||||
}
|
||||
|
||||
// if we have reached a point in the queue where there are no sent packets
|
||||
// and our outstanding bytes plus how much we intend to send is greater than the window we have
|
||||
// then we can quit step-2, since there is nothing else to be gained by continuing.
|
||||
if (entry->mFirstTimeStamp == 0 && (windowSpaceLeft <= 0))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// second, send ready entries until the max outstanding is reached
|
||||
int toleranceLossCount = 0;
|
||||
bool allowWindowReset = (UdpMisc::ClockDiff(mWindowResetTime, hotClock) > mAveragePingTime); // only allow it to reset the window again if it has been longer than the average ping time
|
||||
int trickleSent = 0;
|
||||
PhysicalPacket **readyWalk = readyQueue;
|
||||
PhysicalPacket *pendingReliableBasePtr = &mPhysicalPackets[mReliableOutgoingPendingId % mConfig.maxOutstandingPackets];
|
||||
while (readyWalk < readyPtr && outstandingBytes < useMaxOutstandingBytes)
|
||||
{
|
||||
// prepare packet and send it
|
||||
PhysicalPacket *entry = *readyWalk++;
|
||||
|
||||
// prepare reliable header and send it with data
|
||||
const udp_uchar *parentBase = (const udp_uchar *)entry->mParent->GetDataPtr();
|
||||
|
||||
bool fragment = false;
|
||||
if (entry->mDataPtr != parentBase || entry->mDataLen != entry->mParent->GetDataLen())
|
||||
fragment = true;
|
||||
|
||||
// we can calculate what our reliableId should be based on our position in the array
|
||||
// need to handle the case where we wrap around the end of the array
|
||||
udp_int64 reliableId;
|
||||
if (entry >= pendingReliableBasePtr)
|
||||
{
|
||||
reliableId = mReliableOutgoingPendingId + (entry - pendingReliableBasePtr);
|
||||
}
|
||||
else
|
||||
{
|
||||
reliableId = mReliableOutgoingPendingId + (&mPhysicalPackets[mConfig.maxOutstandingPackets] - pendingReliableBasePtr) + (entry - &mPhysicalPackets[0]);
|
||||
}
|
||||
|
||||
// prep the actual packet and send it
|
||||
bufPtr = buf;
|
||||
*bufPtr++ = 0;
|
||||
*bufPtr++ = (udp_uchar)(((fragment) ? UdpConnection::cUdpPacketFragment1 : UdpConnection::cUdpPacketReliable1) + mChannelNumber); // mark us as a fragment if we are one
|
||||
bufPtr += UdpMisc::PutValue16(bufPtr, (udp_ushort)(reliableId & 0xffff));
|
||||
if (fragment && entry->mDataPtr == parentBase)
|
||||
bufPtr += UdpMisc::PutValue32(bufPtr, entry->mParent->GetDataLen()); // first fragment has a total-length byte after the reliable header
|
||||
mUdpConnection->BufferedSend(buf, (int)(bufPtr - buf), entry->mDataPtr, entry->mDataLen, false);
|
||||
|
||||
// update state information
|
||||
if (entry->mFirstTimeStamp == 0)
|
||||
{
|
||||
entry->mFirstTimeStamp = hotClock;
|
||||
}
|
||||
else
|
||||
{
|
||||
// trying to send the packet again, let's see how long we have been trying to send this packet. If we
|
||||
// have an unacknowledged timeout set and it is older than that, then terminate the connection.
|
||||
// note: we only check for the oldest unacknowledged age against the timeout at the point in time
|
||||
// that we are considering sending the packet again. This can technically cause it to wait slightly
|
||||
// longer than the specified timeout setting before disconnecting the connection, but should be
|
||||
// close enough for all practical purposes and allows for more efficient processing of this setting internally.
|
||||
if (mUdpConnection->mUdpManager->mParams.oldestUnacknowledgedTimeout > 0)
|
||||
{
|
||||
int age = UdpMisc::ClockDiff(entry->mFirstTimeStamp, hotClock);
|
||||
if (age > mUdpConnection->mUdpManager->mParams.oldestUnacknowledgedTimeout)
|
||||
{
|
||||
mUdpConnection->InternalDisconnect(0, UdpConnection::cDisconnectReasonUnacknowledgedTimeout);
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
// were we resent because of a later ack came in? or because we timed out?
|
||||
if (entry->mLastTimeStamp < mLastTimeStampAcknowledged)
|
||||
{
|
||||
// we are resending this packet due to an accelleration (receiving a later packet ack)
|
||||
// so recalc slow start threshhold and congestion window size as per Reno fast-recovery algorithm
|
||||
if (allowWindowReset && toleranceLossCount > mConfig.toleranceLossCount)
|
||||
{
|
||||
allowWindowReset = false;
|
||||
mWindowResetTime = hotClock;
|
||||
mCongestionWindowSize = mCongestionWindowSize * 3 / 4;
|
||||
mCongestionWindowSize = udpMax(mCongestionWindowMinimum, mCongestionWindowSize); // never let congestion window get smaller than a single packet
|
||||
mCongestionSlowStartThreshhold = mCongestionWindowSize;
|
||||
useMaxOutstandingBytes = udpMin(mConfig.maxOutstandingBytes, mCongestionWindowSize);
|
||||
}
|
||||
|
||||
// when resends are caused by a selective ack, that means later data is making it, so we may not be overloading
|
||||
// the connection after all. Reno fast recovery calls for less shrinking of the window in this circumstance
|
||||
// already. This tolerance code allows us to do even better, by allowing us to experience some small amount
|
||||
// of packetloss without immediately considering such loss to be indicative that the connection is being overloaded.
|
||||
// It does this by only allowing the window to get reset in cases where teh selective-ack forces a certain number
|
||||
// of packets to be accellerated. If the application sets the tolerance at something like 10, then isolated packet
|
||||
// losses will have no effect on the flow control window, yet a burst of loss of 10 would more strongly indicate
|
||||
// a overloaded condition. Typically an application will only set this tolerance if they need extremely high
|
||||
// bandwidth (ie, can't afford the window getting reset), and are known to be on a connection that may experience
|
||||
// random packetloss. This is typical of a LFN type network.
|
||||
toleranceLossCount++;
|
||||
|
||||
mStatResentPacketsAccelerated++;
|
||||
mUdpConnection->mConnectionStats.resentPacketsAccelerated++;
|
||||
mUdpConnection->mUdpManager->IncrementResentPacketsAccelerated();
|
||||
}
|
||||
else
|
||||
{
|
||||
// we are resending this packet due to a timeout, so we are seriously overloading things probably
|
||||
// so recalc slow start threshhold and congestion window size as per Reno algorithm
|
||||
// no tolerance on timed-out window resets, since by definition you have not received later acks
|
||||
// or this would be an accellerated timeout, which means that either your volume is really low
|
||||
// anyways (so resetting the window is fine), or you seriously overloaded the other side as nothing
|
||||
// later made it either.
|
||||
if (allowWindowReset)
|
||||
{
|
||||
allowWindowReset = false;
|
||||
mWindowResetTime = hotClock;
|
||||
|
||||
mCongestionSlowStartThreshhold = udpMax(mMaxDataBytes * 2, mCongestionWindowSize / 2);
|
||||
mCongestionWindowSize = mCongestionWindowStart;
|
||||
useMaxOutstandingBytes = udpMin(mConfig.maxOutstandingBytes, mCongestionWindowSize);
|
||||
|
||||
// because a resend has occurred due to a timeout, slow down the resend times slightly
|
||||
// when things start flowing again, it will fix itself up quickly anyways
|
||||
mAveragePingTime += 100;
|
||||
|
||||
// When a connection goes temporarily dead, everything that is in the current
|
||||
// window will end up getting timedout. If the window were large, this could result in the
|
||||
// mAveragePingTime growing quite large and creating very long stalls in the pipe once it does
|
||||
// start moving again. To prevent this, we cap mAveragePingTime when these events occur to prevent
|
||||
// long stalls when the pipe finally reopens.
|
||||
mAveragePingTime = udpMin(mConfig.resendDelayCap, mAveragePingTime);
|
||||
}
|
||||
|
||||
mStatResentPacketsTimedOut++;
|
||||
mUdpConnection->mConnectionStats.resentPacketsTimedOut++;
|
||||
mUdpConnection->mUdpManager->IncrementResentPacketsTimedOut();
|
||||
}
|
||||
}
|
||||
|
||||
entry->mLastTimeStamp = hotClock;
|
||||
|
||||
outstandingNextSendTime = udpMin(outstandingNextSendTime, optimalResendDelay); // this packet is now outstanding, so factor it into the outstandingNextSendTime calculation
|
||||
outstandingBytes += entry->mDataLen;
|
||||
mTrickleLastSend = hotClock;
|
||||
trickleSent += entry->mDataLen;
|
||||
|
||||
if (mConfig.trickleSize != 0 && trickleSent >= mConfig.trickleSize)
|
||||
break;
|
||||
}
|
||||
|
||||
if (outstandingBytes >= useMaxOutstandingBytes)
|
||||
{
|
||||
mMaxxedOutCurrentWindow = true;
|
||||
}
|
||||
|
||||
// if we filled up the ready queue (ie, if we may still have data to send)
|
||||
// and we still have space left in the flow window, then repeat the process.
|
||||
// this should only happen when the flow-control window is enormous.
|
||||
} while (readyQueueOverflow && !mMaxxedOutCurrentWindow);
|
||||
}
|
||||
else
|
||||
{
|
||||
// we have nothing in the pipe at all, reset the congestion window (this means everything has been acked, so the pipe is totally empty
|
||||
// we need to reset the window back to prevent a sudden flood next time a large chunk of data is sent.
|
||||
// we also need to avoid having the slowly sent reliable packets constantly increase the window size (since none will ever get lost)
|
||||
// such that when it does come time to send a big chunk of data, it thinks the window-size is enormous.
|
||||
// resetting the window back to small will only have an effect if a large chunk of data is then sent, at which time it will quickly
|
||||
// ramp up with the slow-start method.
|
||||
mCongestionWindowSize = mCongestionWindowStart;
|
||||
}
|
||||
|
||||
// printf("%d,%d\n", mCongestionWindowSize, mCongestionSlowStartThreshhold); // useful debug output for graphing congestion window
|
||||
|
||||
int nextAllowedSendTime = mConfig.trickleRate - UdpMisc::ClockDiff(mTrickleLastSend, hotClock);
|
||||
nextAllowedSendTime = udpMax(0, udpMax(nextAllowedSendTime, outstandingNextSendTime));
|
||||
mNextNeedTime = hotClock + nextAllowedSendTime;
|
||||
return(nextAllowedSendTime);
|
||||
}
|
||||
|
||||
void UdpReliableChannel::GetChannelStatus(UdpConnection::ChannelStatus *channelStatus) const
|
||||
{
|
||||
int coalesceBytes = 0;
|
||||
if (mCoalescePacket != NULL)
|
||||
coalesceBytes = (int)(mCoalesceEndPtr - mCoalesceStartPtr);
|
||||
|
||||
channelStatus->totalPendingBytes = mLogicalBytesQueued + mReliableOutgoingBytes + coalesceBytes;
|
||||
channelStatus->queuedPackets = mLogicalPacketList.Count();
|
||||
channelStatus->queuedBytes = mLogicalBytesQueued;
|
||||
channelStatus->incomingLargeTotal = mBigDataTargetLen;
|
||||
channelStatus->incomingLargeSoFar = mBigDataLen;
|
||||
channelStatus->duplicatePacketsReceived = mStatDuplicatePacketsReceived;
|
||||
channelStatus->resentPacketsAccelerated = mStatResentPacketsAccelerated;
|
||||
channelStatus->resentPacketsTimedOut = mStatResentPacketsTimedOut;
|
||||
channelStatus->congestionSlowStartThreshhold = mCongestionSlowStartThreshhold;
|
||||
channelStatus->congestionWindowSize = mCongestionWindowSize;
|
||||
channelStatus->ackAveragePing = mAveragePingTime;
|
||||
channelStatus->oldestUnacknowledgedAge = 0;
|
||||
|
||||
if (mReliableOutgoingPendingId < mReliableOutgoingId)
|
||||
{
|
||||
// oldest pending packet will be definition be the oldestUnacknowledged, since it is impossible
|
||||
// for any packet after it to have possibly been sent before it was sent for its first time
|
||||
// since queue is effectively in first-send order. It is also impossible for this packet to have
|
||||
// been acknowledged, since the pending id advances the moment the oldest is acknowledged, meaning
|
||||
// that the pendingId is always pointing to something that has either been sent (or not sent at all)
|
||||
PhysicalPacket *entry = &mPhysicalPackets[mReliableOutgoingPendingId % mConfig.maxOutstandingPackets];
|
||||
if (entry->mFirstTimeStamp != 0) // if has been sent (we know it hasn't been acknowledged or we couldn't possibly be pointing at it as pending)
|
||||
{
|
||||
if (mUdpConnection->GetUdpManager() != NULL)
|
||||
{
|
||||
channelStatus->oldestUnacknowledgedAge = mUdpConnection->GetUdpManager()->CachedClockElapsed(entry->mFirstTimeStamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UdpReliableChannel::ReliablePacket(const udp_uchar *data, int dataLen)
|
||||
{
|
||||
udp_uchar buf[256];
|
||||
udp_uchar *bufPtr;
|
||||
|
||||
if (dataLen <= UdpConnection::cUdpPacketReliableSize)
|
||||
{
|
||||
mUdpConnection->CallbackCorruptPacket(data, dataLen, cUdpCorruptionReasonReliablePacketTooShort);
|
||||
return;
|
||||
}
|
||||
|
||||
int packetType = data[1];
|
||||
udp_ushort reliableStamp = UdpMisc::GetValue16(data + 2);
|
||||
udp_int64 reliableId = GetReliableIncomingId(reliableStamp);
|
||||
|
||||
if (reliableId >= mReliableIncomingId + mConfig.maxInstandingPackets)
|
||||
return; // if we do not have buffer space to hold onto this packet, then we simply must pretend like it was lost
|
||||
|
||||
if (reliableId >= mReliableIncomingId)
|
||||
{
|
||||
ReliablePacketMode mode = (ReliablePacketMode)((packetType - UdpConnection::cUdpPacketReliable1) / cReliableChannelCount);
|
||||
|
||||
// is this the packet we are waiting for
|
||||
if (mReliableIncomingId == reliableId)
|
||||
{
|
||||
// if so, process it immediately
|
||||
ProcessPacket(mode, data + UdpConnection::cUdpPacketReliableSize, dataLen - UdpConnection::cUdpPacketReliableSize);
|
||||
mReliableIncomingId++;
|
||||
|
||||
// process other packets that have arrived
|
||||
while (mReliableIncoming[mReliableIncomingId % mConfig.maxInstandingPackets].mPacket != NULL)
|
||||
{
|
||||
int spot = (int)(mReliableIncomingId % mConfig.maxInstandingPackets);
|
||||
if (mReliableIncoming[spot].mMode != cReliablePacketModeDelivered)
|
||||
{
|
||||
ProcessPacket(mReliableIncoming[spot].mMode, (udp_uchar *)mReliableIncoming[spot].mPacket->GetDataPtr(), mReliableIncoming[spot].mPacket->GetDataLen());
|
||||
}
|
||||
|
||||
mReliableIncoming[spot].mPacket->Release();
|
||||
mReliableIncoming[spot].mPacket = NULL;
|
||||
mReliableIncomingId++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// not the one we need next, but it is later than the one we need , so store it in our buffer until it's turn comes up
|
||||
int spot = (int)(reliableId % mConfig.maxInstandingPackets);
|
||||
if (mReliableIncoming[spot].mPacket == NULL) // only make the copy of it if we don't already have it in our buffer (in cases where it was sent twice, there would be no harm in the copy again since it must be the same packet, it's just inefficient)
|
||||
{
|
||||
mReliableIncoming[spot].mMode = mode;
|
||||
mReliableIncoming[spot].mPacket = mUdpConnection->mUdpManager->CreatePacket(data + UdpConnection::cUdpPacketReliableSize, dataLen - UdpConnection::cUdpPacketReliableSize);
|
||||
|
||||
// on out of order deliver, we need to keep a copy of it as if we were doing ordered-delivery in order to prevent duplicates
|
||||
// we will mark the packet in the queue as already delivered to prevent it from getting delivered a second time when the stalled packet
|
||||
// arrives and unwinds the queue
|
||||
if (mode == cReliablePacketModeReliable && mConfig.outOfOrder)
|
||||
{
|
||||
ProcessPacket(cReliablePacketModeReliable, (udp_uchar *)mReliableIncoming[spot].mPacket->GetDataPtr(), mReliableIncoming[spot].mPacket->GetDataLen());
|
||||
mReliableIncoming[spot].mMode = cReliablePacketModeDelivered;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mStatDuplicatePacketsReceived++;
|
||||
mUdpConnection->mConnectionStats.duplicatePacketsReceived++;
|
||||
mUdpConnection->mUdpManager->IncrementDuplicatePacketsReceived();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mStatDuplicatePacketsReceived++;
|
||||
mUdpConnection->mConnectionStats.duplicatePacketsReceived++;
|
||||
mUdpConnection->mUdpManager->IncrementDuplicatePacketsReceived();
|
||||
}
|
||||
|
||||
bool ackAll = false;
|
||||
bufPtr = buf;
|
||||
*bufPtr++ = 0;
|
||||
if (mReliableIncomingId > reliableId)
|
||||
{
|
||||
// ack everything up to the current head of our chain (minus one since the stamp represents the next one we want to get)
|
||||
*bufPtr++ = (udp_uchar)(UdpConnection::cUdpPacketAckAll1 + mChannelNumber);
|
||||
bufPtr += UdpMisc::PutValue16(bufPtr, (udp_ushort)((mReliableIncomingId - 1) & 0xffff));
|
||||
ackAll = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// a simple ack for us only
|
||||
*bufPtr++ = (udp_uchar)(UdpConnection::cUdpPacketAck1 + mChannelNumber);
|
||||
bufPtr += UdpMisc::PutValue16(bufPtr, (udp_ushort)(reliableId & 0xffff));
|
||||
}
|
||||
|
||||
if (mBufferedAckPtr != NULL && mConfig.ackDeduping && ackAll)
|
||||
{
|
||||
memcpy(mBufferedAckPtr, buf, bufPtr - buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
udp_uchar *ptr = mUdpConnection->BufferedSend(buf, (int)(bufPtr - buf), NULL, 0, true); // safe to append on our data, it is stack data
|
||||
if (mBufferedAckPtr == NULL)
|
||||
{
|
||||
// the buffered-ack ptr should always point to the earliest ack in the buffer, such that
|
||||
// a replacement ack-all will be processed by the receiver before any selective acks that may
|
||||
// have been buffered up. Thus, only repoint the buffered ack ptr if it was previously unset.
|
||||
mBufferedAckPtr = ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UdpReliableChannel::ProcessPacket(ReliablePacketMode mode, const udp_uchar *data, int dataLen)
|
||||
{
|
||||
assert(dataLen > 0);
|
||||
|
||||
// if we have a big packet under construction already, or we are a fragment and thus need to be constructing one, then append this on the end (will create new if it is the first fragment)
|
||||
if (mode == cReliablePacketModeReliable)
|
||||
{
|
||||
// we are not a fragment, nor was there a fragment in progress, so we are a simple reliable packet, just send it to the app
|
||||
if (mBigDataPtr != NULL)
|
||||
{
|
||||
mUdpConnection->CallbackCorruptPacket(data, dataLen, cUdpCorruptionReasonFragmentExpected);
|
||||
return;
|
||||
}
|
||||
|
||||
mUdpConnection->ProcessCookedPacket(data, dataLen);
|
||||
}
|
||||
else if (mode == cReliablePacketModeFragment)
|
||||
{
|
||||
// append onto end of big packet (or create new big packet if not existing already)
|
||||
if (mBigDataPtr == NULL)
|
||||
{
|
||||
if (dataLen < 4)
|
||||
{
|
||||
mUdpConnection->CallbackCorruptPacket(data, dataLen, cUdpCorruptionReasonFragmentBad);
|
||||
return;
|
||||
}
|
||||
|
||||
mBigDataTargetLen = UdpMisc::GetValue32(data); // first fragment has a total-length int header on it.
|
||||
|
||||
if (mBigDataTargetLen <= 0) // negative or zero sized chunk to follow means packet corruption or tampering for sure
|
||||
{
|
||||
mUdpConnection->CallbackCorruptPacket(data, dataLen, cUdpCorruptionReasonFragmentBad);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mBigDataTargetLen > mUdpConnection->mUdpManager->mParams.incomingLogicalPacketMax)
|
||||
{
|
||||
mUdpConnection->CallbackCorruptPacket(data, dataLen, cUdpCorruptionReasonFragmentOversized);
|
||||
return;
|
||||
}
|
||||
|
||||
mBigDataPtr = new udp_uchar[mBigDataTargetLen];
|
||||
mBigDataLen = 0;
|
||||
data += 4;
|
||||
dataLen -= 4;
|
||||
}
|
||||
|
||||
int safetyMax = udpMin(mBigDataTargetLen - mBigDataLen, dataLen); // can't happen in theory since they should add up exact, but protect against it if it does
|
||||
assert(safetyMax == dataLen);
|
||||
memcpy(mBigDataPtr + mBigDataLen, data, safetyMax);
|
||||
mBigDataLen += safetyMax;
|
||||
|
||||
if (mBigDataTargetLen == mBigDataLen)
|
||||
{
|
||||
// send big-packet off to application
|
||||
mUdpConnection->ProcessCookedPacket(mBigDataPtr, mBigDataLen);
|
||||
|
||||
// delete big packet, and reset
|
||||
delete[] mBigDataPtr;
|
||||
mBigDataLen = 0;
|
||||
mBigDataTargetLen = 0;
|
||||
mBigDataPtr = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UdpReliableChannel::AckAllPacket(const udp_uchar *data, int dataLen)
|
||||
{
|
||||
if (dataLen < 4)
|
||||
{
|
||||
mUdpConnection->CallbackCorruptPacket(data, dataLen, cUdpCorruptionReasonAckBad);
|
||||
return;
|
||||
}
|
||||
|
||||
udp_int64 reliableId = GetReliableOutgoingId((udp_ushort)UdpMisc::GetValue16(data + 2));
|
||||
|
||||
if (mReliableOutgoingPendingId > reliableId)
|
||||
{
|
||||
// if we ackall'ed a packet and everything before the ackall address had already been acked, then we know
|
||||
// for certainty that we sent a packet over again that did not need to be sent over again (ie. wasn't lost, just slow)
|
||||
// so adjust the mAveragePingTime upward to slow down future resends
|
||||
mAveragePingTime += 400;
|
||||
mAveragePingTime = udpMin(mConfig.resendDelayCap, mAveragePingTime);
|
||||
}
|
||||
|
||||
for (udp_int64 i = mReliableOutgoingPendingId; i <= reliableId; i++)
|
||||
{
|
||||
Ack(i);
|
||||
}
|
||||
}
|
||||
|
||||
void UdpReliableChannel::Ack(udp_int64 reliableId)
|
||||
{
|
||||
// if packet being acknowledged is possibly in our resend queue, then check for it
|
||||
if (reliableId >= mReliableOutgoingPendingId && reliableId < mReliableOutgoingId)
|
||||
{
|
||||
int pos = (int)(reliableId % mConfig.maxOutstandingPackets);
|
||||
PhysicalPacket *entry = &mPhysicalPackets[pos];
|
||||
|
||||
if (entry->mDataPtr != NULL) // if this packet has not been acknowledged yet (sometimes we get back two acks for the same packet)
|
||||
{
|
||||
mNextNeedTime = 0; // something got acked, so we actually need to take the time next time it is offered
|
||||
|
||||
// if the last time we gave this reliable channel processing time, it filled up the entire sliding window
|
||||
// then go ahead and increase the window-size when incoming acks come in. However, if the window wasn't full
|
||||
// then don't increase the window size. The problem is, a game application is likely to send reliable data
|
||||
// at a relatively slow rate (2k/second for example), never filling the window. The net result would be that
|
||||
// every acknowledged packet would increase the window size, giving the reliable channel the impression that
|
||||
// it's window can be very very large, when in fact, it is only not losing packets because the application is pacing
|
||||
// itself. The window could grow enormous, even 200k for a modem. Then, if the application were to dump a load
|
||||
// of data onto us all at once, it would flush it all out at once thinking it had a big window. By only increasing
|
||||
// the window size when we have high enough volume to fill the window, we ensure this does not happen. TCP does a similar
|
||||
// thing, but what they do is reset the window if there has been a long stall. We do that too, but because we are a game
|
||||
// application that is likely to pace the data at the application level, we have a unique circumstances that need addressing.
|
||||
if (mMaxxedOutCurrentWindow)
|
||||
{
|
||||
if (mCongestionWindowSize < mCongestionSlowStartThreshhold)
|
||||
{
|
||||
mCongestionWindowSize += mMaxDataBytes; // slow-start mode
|
||||
}
|
||||
else
|
||||
{
|
||||
int increase = (mMaxDataBytes * mMaxDataBytes / mCongestionWindowSize);
|
||||
mCongestionWindowSize += udpMax(1, increase); // congestion mode
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (entry->mLastTimeStamp == entry->mFirstTimeStamp)
|
||||
{
|
||||
|
||||
|
||||
// if the packet that is being acknowledged was only sent once, then we can safely use
|
||||
// the round-trip time as an accurate measure of ping time. By knowing ping time, we can
|
||||
// better (more agressively) schedule resends of lost packets. We will use a moving average
|
||||
// that weights the current packet as 1/4 the average.
|
||||
int thisPingTime = mUdpConnection->GetUdpManager()->CachedClockElapsed(entry->mFirstTimeStamp);
|
||||
mAveragePingTime = (mAveragePingTime * 3 + thisPingTime) / 4;
|
||||
}
|
||||
|
||||
// what this is doing is if we receive an ACK for a packet that was sent at TIME_X
|
||||
// we can assume that all packets sent before TIME_X that are not yet acknowledge were lost
|
||||
// and we can resend them immediately
|
||||
// since we do not know whether this ack is for last packet we sent, we have to assume it is for the first time this packet was sent (if sent multiple times)
|
||||
// otherwise, we could resend it, then receive the ack from the first packet, and think our last-ack time is the time of the second outgoing packet
|
||||
// which would cause about every packet in the queue to resend, even if they had just been sent
|
||||
// in situations where the first packet truely was lost and this is an ACK of the second packet, then the only
|
||||
// harm done is that the we may not resend some of the earlier sent packets quite as quickly. This will only
|
||||
// happen in situations where a packet that was truely lost gets acked on it's second attempt...we just
|
||||
// won't be using that ack for the purposes of accelerating other resends...since odds are a non-lost packet
|
||||
// will accelerate those other resends shortly anyhow, there really is no loss
|
||||
// (note: we used to only set this value forward for packets that were never lost (one time sends); however, if this stamp
|
||||
// ever got set way high for some reason (in theory it can't happen), then we would get into a situation where it would
|
||||
// rapidly resend and possibly never get reset, causing infinite rapid resends, so we now set it every time to the first-stamp)
|
||||
// which will be safe, even if the packet were resent.)
|
||||
mLastTimeStampAcknowledged = entry->mFirstTimeStamp;
|
||||
|
||||
|
||||
// this packet we have queued has been acknowledged, so delete it from queue
|
||||
mReliableOutgoingBytes -= entry->mDataLen;
|
||||
entry->mDataLen = 0;
|
||||
entry->mDataPtr = NULL;
|
||||
entry->mParent->Release();
|
||||
entry->mParent = NULL;
|
||||
|
||||
// advance the pending ptr until it reaches outgoingId or an entry that has yet to acknowledged
|
||||
while (mReliableOutgoingPendingId < mReliableOutgoingId)
|
||||
{
|
||||
if (mPhysicalPackets[mReliableOutgoingPendingId % mConfig.maxOutstandingPackets].mDataPtr != NULL)
|
||||
break;
|
||||
mReliableOutgoingPendingId++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// we got an ack for a packet that has already been acked. This could be due to an ack-all packet that covered us so statistically
|
||||
// we can't do much with this information.
|
||||
}
|
||||
}
|
||||
|
||||
// we don't need to try rescheduling ourself here, since our connection object reschedules to go immediately whenever any type
|
||||
// of packet arrives (including ack packets)
|
||||
}
|
||||
|
||||
|
||||
UdpReliableChannel::IncomingQueueEntry::IncomingQueueEntry()
|
||||
{
|
||||
mPacket = NULL;
|
||||
mMode = UdpReliableChannel::cReliablePacketModeReliable;
|
||||
}
|
||||
|
||||
UdpReliableChannel::IncomingQueueEntry::~IncomingQueueEntry()
|
||||
{
|
||||
if (mPacket != NULL)
|
||||
mPacket->Release();
|
||||
}
|
||||
|
||||
|
||||
UdpReliableChannel::PhysicalPacket::PhysicalPacket()
|
||||
{
|
||||
mParent = NULL;
|
||||
}
|
||||
|
||||
UdpReliableChannel::PhysicalPacket::~PhysicalPacket()
|
||||
{
|
||||
if (mParent != NULL)
|
||||
mParent->Release();
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
#ifndef UDPLIBRARY_UDPRELIABLECHANNEL_H
|
||||
#define UDPLIBRARY_UDPRELIABLECHANNEL_H
|
||||
|
||||
// Copyright 2004 Sony Online Entertainment, all rights reserved.
|
||||
// Author: Jeff Petersen
|
||||
|
||||
namespace UdpLibrary
|
||||
{
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// The purpose of this class is to manage the reliable transmission of packets on top of the inherently
|
||||
// unreliable UDP layer. This is an internal object and should not be manually created or talked to by the user.
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
class UdpReliableChannel
|
||||
{
|
||||
protected:
|
||||
friend class UdpConnection;
|
||||
|
||||
UdpReliableChannel(int channelNumber, UdpConnection *connection, UdpReliableConfig *config);
|
||||
~UdpReliableChannel();
|
||||
void GetChannelStatus(UdpConnection::ChannelStatus *channelStatus) const;
|
||||
int GetAveragePing() const;
|
||||
int TotalPendingBytes() const; // returns total bytes outstanding
|
||||
|
||||
void ReliablePacket(const udp_uchar *data, int dataLen);
|
||||
void Send(const udp_uchar *data, int dataLen, const udp_uchar *data2, int dataLen2);
|
||||
void AckPacket(const udp_uchar *data, int dataLen);
|
||||
void AckAllPacket(const udp_uchar *data, int dataLen);
|
||||
void ClearBufferedAck();
|
||||
int GiveTime();
|
||||
|
||||
protected:
|
||||
enum ReliablePacketMode { cReliablePacketModeReliable, cReliablePacketModeFragment, cReliablePacketModeDelivered };
|
||||
|
||||
class PhysicalPacket
|
||||
{
|
||||
public:
|
||||
PhysicalPacket();
|
||||
~PhysicalPacket();
|
||||
|
||||
public:
|
||||
UdpClockStamp mFirstTimeStamp;
|
||||
UdpClockStamp mLastTimeStamp;
|
||||
const LogicalPacket *mParent; // physical packets hold an addref on the logical packet. Once all of the logical packet data has been divied out to physical packets, the logical queue releases it
|
||||
const udp_uchar *mDataPtr; // within parent's data (it's possible it is not pointing to the beginning in the case of large packets)
|
||||
int mDataLen;
|
||||
};
|
||||
|
||||
class IncomingQueueEntry
|
||||
{
|
||||
public:
|
||||
IncomingQueueEntry();
|
||||
~IncomingQueueEntry();
|
||||
|
||||
public:
|
||||
LogicalPacket *mPacket;
|
||||
ReliablePacketMode mMode;
|
||||
};
|
||||
friend class IncomingQueueEntry;
|
||||
|
||||
udp_int64 GetReliableOutgoingId(int reliableStamp) const;
|
||||
udp_int64 GetReliableIncomingId(int reliableStamp) const;
|
||||
void Ack(udp_int64 reliableId);
|
||||
void ProcessPacket(ReliablePacketMode mode, const udp_uchar *data, int dataLen);
|
||||
bool PullDown(int windowSpaceLeft);
|
||||
void FlushCoalesce();
|
||||
void SendCoalesce(const udp_uchar *data, int dataLen, const udp_uchar *data2 = NULL, int dataLen2 = 0);
|
||||
void QueueLogicalPacket(LogicalPacket *packet);
|
||||
|
||||
UdpReliableConfig mConfig;
|
||||
UdpConnection *mUdpConnection;
|
||||
UdpClockStamp mLastTimeStampAcknowledged;
|
||||
UdpClockStamp mTrickleLastSend;
|
||||
UdpClockStamp mNextNeedTime;
|
||||
UdpClockStamp mWindowResetTime;
|
||||
int mChannelNumber;
|
||||
udp_int64 mReliableOutgoingId;
|
||||
udp_int64 mReliableOutgoingPendingId;
|
||||
int mReliableOutgoingBytes;
|
||||
int mLogicalBytesQueued;
|
||||
udp_uchar *mBigDataPtr;
|
||||
int mBigDataLen;
|
||||
int mBigDataTargetLen;
|
||||
int mAveragePingTime;
|
||||
int mMaxDataBytes;
|
||||
int mFragmentNextPos;
|
||||
PhysicalPacket *mPhysicalPackets;
|
||||
UdpLinkedList<LogicalPacket> mLogicalPacketList;
|
||||
|
||||
int mCongestionWindowStart;
|
||||
int mCongestionWindowSize;
|
||||
int mCongestionSlowStartThreshhold;
|
||||
int mCongestionWindowMinimum;
|
||||
bool mMaxxedOutCurrentWindow;
|
||||
|
||||
udp_int64 mReliableIncomingId;
|
||||
IncomingQueueEntry *mReliableIncoming;
|
||||
|
||||
LogicalPacket *mCoalescePacket;
|
||||
udp_uchar *mCoalesceStartPtr;
|
||||
udp_uchar *mCoalesceEndPtr;
|
||||
int mCoalesceCount;
|
||||
int mMaxCoalesceAttemptBytes;
|
||||
|
||||
udp_uchar *mBufferedAckPtr;
|
||||
|
||||
int mStatDuplicatePacketsReceived;
|
||||
int mStatResentPacketsAccelerated;
|
||||
int mStatResentPacketsTimedOut;
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// inline implementations
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// UdpReliableChannel
|
||||
inline void UdpReliableChannel::AckPacket(const udp_uchar *data, int dataLen)
|
||||
{
|
||||
if (dataLen < 4)
|
||||
{
|
||||
mUdpConnection->CallbackCorruptPacket(data, dataLen, cUdpCorruptionReasonAckBad);
|
||||
return;
|
||||
}
|
||||
|
||||
Ack(GetReliableOutgoingId((udp_ushort)UdpMisc::GetValue16(data + 2)));
|
||||
}
|
||||
|
||||
inline int UdpReliableChannel::GetAveragePing() const
|
||||
{
|
||||
return(mAveragePingTime);
|
||||
}
|
||||
|
||||
inline int UdpReliableChannel::TotalPendingBytes() const
|
||||
{
|
||||
return(mLogicalBytesQueued + mReliableOutgoingBytes);
|
||||
}
|
||||
|
||||
inline void UdpReliableChannel::ClearBufferedAck()
|
||||
{
|
||||
mBufferedAckPtr = NULL;
|
||||
}
|
||||
|
||||
inline udp_int64 UdpReliableChannel::GetReliableOutgoingId(int reliableStamp) const
|
||||
{
|
||||
// since we can never have anywhere close to 65000 packets outstanding, we only need to
|
||||
// to send the low order word of the reliableId in the UdpPacketReliable and UdpPacketAck
|
||||
// packets, because we can reconstruct the full id from that, we just need to take
|
||||
// into account the wrap around issue. We calculate it based of the high-word of the
|
||||
// next packet we are going to send. If it ends up being larger then we know
|
||||
// we wrapped and can fix it up by simply subtracting 1 from the high-order word.
|
||||
udp_int64 reliableId = reliableStamp | (mReliableOutgoingId & (~(udp_int64)0xffff));
|
||||
if (reliableId > mReliableOutgoingId)
|
||||
reliableId -= 0x10000;
|
||||
return(reliableId);
|
||||
}
|
||||
|
||||
inline udp_int64 UdpReliableChannel::GetReliableIncomingId(int reliableStamp) const
|
||||
{
|
||||
// since we can never have anywhere close to 65000 packets outstanding, we only need to
|
||||
// to send the low order word of the reliableId in the UdpPacketReliable and UdpPacketAck
|
||||
// packets, because we can reconstruct the full id from that, we just need to take
|
||||
// into account the wrap around issue. We basically prepend the last-known
|
||||
// high-order word. If we end up significantly below the head of our chain, then we
|
||||
// know we need to pick the entry 0x10000 higher. If we fall significantly above
|
||||
// our previous high-end, then we know we need to go the other way.
|
||||
udp_int64 reliableId = reliableStamp | (mReliableIncomingId & (~(udp_int64)0xffff));
|
||||
if (reliableId < mReliableIncomingId - UdpManager::cHardMaxOutstandingPackets)
|
||||
reliableId += 0x10000;
|
||||
if (reliableId > mReliableIncomingId + UdpManager::cHardMaxOutstandingPackets)
|
||||
reliableId -= 0x10000;
|
||||
return(reliableId);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,119 @@
|
||||
#ifndef UDPLIBRARY_UDPTYPES_H
|
||||
#define UDPLIBRARY_UDPTYPES_H
|
||||
|
||||
// Copyright 2004 Sony Online Entertainment, all rights reserved.
|
||||
// Author: Jeff Petersen
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
namespace UdpLibrary
|
||||
{
|
||||
|
||||
class UdpManager;
|
||||
class UdpConnection;
|
||||
class LogicalPacket;
|
||||
class UdpPlatformAddress;
|
||||
|
||||
#if defined(WIN32)
|
||||
typedef __int64 udp_int64;
|
||||
#pragma warning(disable:4121) // an error in microsofts compiler makes it think that pointers-to-members require 12 byte alignment, and since we are only doing 8 byte alignment by default, we get this warning. Turns out that alignment is actually a non-issue for pointers-to-members since they are accessed only 4 bytes at a time. (found this info on the internet)
|
||||
#else
|
||||
typedef long long udp_int64;
|
||||
#endif
|
||||
|
||||
typedef unsigned char udp_uchar;
|
||||
typedef unsigned short udp_ushort;
|
||||
typedef unsigned int udp_uint;
|
||||
typedef unsigned long udp_ulong;
|
||||
typedef udp_int64 UdpClockStamp;
|
||||
typedef UdpPlatformAddress UdpIpAddress; // deprecated, use UdpPlatformAddress directly instead
|
||||
|
||||
enum UdpCorruptionReason { cUdpCorruptionReasonNone
|
||||
, cUdpCorruptionReasonMultiPacket
|
||||
, cUdpCorruptionReasonReliablePacketTooShort
|
||||
, cUdpCorruptionReasonInternalPacketTooShort
|
||||
, cUdpCorruptionReasonDecryptFailed
|
||||
, cUdpCorruptionReasonZeroLengthPacket
|
||||
, cUdpCorruptionReasonPacketShorterThanCrcBytes
|
||||
, cUdpCorruptionReasonMisformattedGroup
|
||||
, cUdpCorruptionReasonFragmentOversized
|
||||
, cUdpCorruptionReasonFragmentBad
|
||||
, cUdpCorruptionReasonFragmentExpected
|
||||
, cUdpCorruptionReasonAckBad
|
||||
};
|
||||
|
||||
|
||||
enum UdpChannel { cUdpChannelUnreliable // unreliable/unordered/buffered
|
||||
, cUdpChannelUnreliableUnbuffered // unreliable/unordered/unbuffered
|
||||
, cUdpChannelOrdered // unreliable/ordered/buffered
|
||||
, cUdpChannelOrderedUnbuffered // unreliable/ordered/unbuffered
|
||||
, cUdpChannelReliable1 // reliable (as per channel config)
|
||||
, cUdpChannelReliable2 // reliable (as per channel config)
|
||||
, cUdpChannelReliable3 // reliable (as per channel config)
|
||||
, cUdpChannelReliable4 // reliable (as per channel config)
|
||||
, cUdpChannelCount // count of number of channels
|
||||
};
|
||||
|
||||
class UdpRefCount
|
||||
{
|
||||
public:
|
||||
UdpRefCount();
|
||||
virtual void AddRef() const;
|
||||
virtual void Release() const;
|
||||
virtual void NoRef() const;
|
||||
virtual int GetRefCount() const;
|
||||
|
||||
protected:
|
||||
virtual ~UdpRefCount();
|
||||
|
||||
mutable int mRefCount;
|
||||
mutable bool mNoRef;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// UdpRefCount implementation (entirely inline)
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline UdpRefCount::UdpRefCount()
|
||||
{
|
||||
mRefCount = 1;
|
||||
mNoRef = false;
|
||||
}
|
||||
|
||||
inline UdpRefCount::~UdpRefCount()
|
||||
{
|
||||
assert(mRefCount == 0 || mNoRef);
|
||||
}
|
||||
|
||||
inline void UdpRefCount::AddRef() const
|
||||
{
|
||||
assert(!mNoRef);
|
||||
assert(mRefCount > 0);
|
||||
++mRefCount;
|
||||
}
|
||||
|
||||
inline void UdpRefCount::Release() const
|
||||
{
|
||||
assert(!mNoRef);
|
||||
assert(mRefCount > 0);
|
||||
if (--mRefCount == 0)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
inline void UdpRefCount::NoRef() const
|
||||
{
|
||||
assert(mRefCount == 1);
|
||||
mNoRef = true;
|
||||
}
|
||||
|
||||
inline int UdpRefCount::GetRefCount() const
|
||||
{
|
||||
return(mRefCount);
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// FirstUnicode.cpp
|
||||
// copyright (c) 2001 Sony Online Entertainment
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "FirstUnicode.h"
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// FirstUnicode.h
|
||||
// copyright (c) 2001 Sony Online Entertainment
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#ifndef INCLUDED_PlatFirstUnicode_H
|
||||
#define INCLUDED_PlatFirstUnicode_H
|
||||
|
||||
// ======================================================================
|
||||
// stl unref inline func removed
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
// C4514 unreferenced inline/local function has been removed
|
||||
// C4786 'identifier' : identifier was truncated to 'number' characters in the debug information
|
||||
|
||||
#pragma warning (disable:4514 4786)
|
||||
|
||||
#endif
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// Unicode.cpp
|
||||
// copyright (c) 2001 Sony Online Entertainment
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "FirstUnicode.h"
|
||||
#include "Unicode.h"
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
#endif
|
||||
|
||||
namespace Plat_Unicode
|
||||
{
|
||||
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
// ======================================================================
|
||||
@@ -0,0 +1,62 @@
|
||||
// ======================================================================
|
||||
// Unicode.h
|
||||
// copyright (c) 2001 Sony Online Entertainment
|
||||
//
|
||||
// jwatson
|
||||
//
|
||||
// Basic Unicode primitives and string handling/manipulating functions
|
||||
// ======================================================================
|
||||
|
||||
#ifndef INCLUDED_PlatUnicode_H
|
||||
#define INCLUDED_PlatUnicode_H
|
||||
|
||||
#if WIN32
|
||||
#pragma warning (disable:4710)
|
||||
#pragma warning (disable:4786)
|
||||
// stl warning func not inlined
|
||||
#pragma warning (disable:4514)
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
#endif
|
||||
|
||||
namespace Plat_Unicode
|
||||
{
|
||||
|
||||
typedef unsigned short unicode_char_t;
|
||||
|
||||
/**
|
||||
* Standard Unicode string is UTF-16
|
||||
*/
|
||||
typedef std::basic_string<unicode_char_t> String;
|
||||
|
||||
/**
|
||||
* NarrowString is a ascii string.
|
||||
*/
|
||||
|
||||
typedef std::string NarrowString;
|
||||
|
||||
const unicode_char_t whitespace [] = { ' ', '\n', '\r', '\t', 0 };
|
||||
const unicode_char_t endlines [] = { '\r', '\n', 0 };
|
||||
const char ascii_whitespace [] = { ' ', '\n', '\r', '\t', 0 };
|
||||
const char ascii_endlines [] = { '\r', '\n', 0 };
|
||||
|
||||
const unicode_char_t cyrillic_lower_first = 0x0410;
|
||||
const unicode_char_t cyrillic_lower_last = 0x042F;
|
||||
const unicode_char_t cyrillic_upper_first = 0x0430;
|
||||
const unicode_char_t cyrillic_upper_last = 0x044F;
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,267 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// UnicodeBlocks.cpp
|
||||
// copyright (c) 2001 Sony Online Entertainment
|
||||
//
|
||||
// This contains Unicode 3.0 compliant Blocks.
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "FirstUnicode.h"
|
||||
#include "UnicodeBlocks.h"
|
||||
|
||||
// ======================================================================
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
#endif
|
||||
using namespace Plat_Unicode::Blocks;
|
||||
using Plat_Unicode::unicode_char_t;
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
const Plat_Unicode::Blocks::RawData Plat_Unicode::Blocks::Mapping::ms_defaultBlockData [] =
|
||||
{
|
||||
{ Basic_Latin, "Basic Latin",0x0000,0x007F },
|
||||
{ Latin_1_Supplement,"Latin-1 Supplement",0x0080,0x00FF },
|
||||
{ Latin_Extended_A, "Latin Extended-A",0x0100,0x017F },
|
||||
{ Latin_Extended_B, "Latin Extended-B",0x0180,0x024F },
|
||||
{ IPA_Extensions, "IPA Extensions",0x0250,0x02AF },
|
||||
{ Spacing_Modifier_Letters, "Spacing Modifier Letters",0x02B0,0x02FF },
|
||||
{ Combining_Diacritical_Marks, "Combining Diacritical Marks",0x0300,0x036F },
|
||||
{ Greek, "Greek",0x0370,0x03FF },
|
||||
{ Cyrillic, "Cyrillic",0x0400,0x04FF },
|
||||
{ Armenian, "Armenian",0x0530,0x058F },
|
||||
{ Hebrew, "Hebrew",0x0590,0x05FF },
|
||||
{ Arabic, "Arabic",0x0600,0x06FF },
|
||||
{ Syriac, "Syriac ",0x0700,0x074F },
|
||||
{ Thaana, "Thaana",0x0780,0x07BF },
|
||||
{ Devanagari, "Devanagari",0x0900,0x097F },
|
||||
{ Bengali, "Bengali",0x0980,0x09FF },
|
||||
{ Gurmukhi, "Gurmukhi",0x0A00,0x0A7F },
|
||||
{ Gujarati, "Gujarati",0x0A80,0x0AFF },
|
||||
{ Oriya, "Oriya",0x0B00,0x0B7F },
|
||||
{ Tamil, "Tamil",0x0B80,0x0BFF },
|
||||
{ Telugu, "Telugu",0x0C00,0x0C7F },
|
||||
{ Kannada, "Kannada",0x0C80,0x0CFF },
|
||||
{ Malayalam, "Malayalam",0x0D00,0x0D7F },
|
||||
{ Sinhala, "Sinhala",0x0D80,0x0DFF },
|
||||
{ Thai, "Thai",0x0E00,0x0E7F },
|
||||
{ Lao, "Lao",0x0E80,0x0EFF },
|
||||
{ Tibetan, "Tibetan",0x0F00,0x0FFF },
|
||||
{ Myanmar, "Myanmar ",0x1000,0x109F },
|
||||
{ Georgian, "Georgian",0x10A0,0x10FF },
|
||||
{ Hangul_Jamo, "Hangul Jamo",0x1100,0x11FF },
|
||||
{ Ethiopic, "Ethiopic",0x1200,0x137F },
|
||||
{ Cherokee, "Cherokee",0x13A0,0x13FF },
|
||||
{ Unified_Canadian_Aboriginal_Syllabics, "Unified Canadian Aboriginal Syllabics",0x1400,0x167F },
|
||||
{ Ogham, "Ogham",0x1680,0x169F },
|
||||
{ Runic, "Runic",0x16A0,0x16FF },
|
||||
{ Khmer, "Khmer",0x1780,0x17FF },
|
||||
{ Mongolian, "Mongolian",0x1800,0x18AF },
|
||||
{ Latin_Extended_Additional, "Latin Extended Additional",0x1E00,0x1EFF },
|
||||
{ Greek_Extended, "Greek Extended",0x1F00,0x1FFF },
|
||||
{ General_Punctuation, "General Punctuation",0x2000,0x206F },
|
||||
{ Superscripts_and_Subscripts, "Superscripts and Subscripts",0x2070,0x209F },
|
||||
{ Currency_Symbols, "Currency Symbols",0x20A0,0x20CF },
|
||||
{ Combining_Marks_for_Symbols, "Combining Marks for Symbols",0x20D0,0x20FF },
|
||||
{ Letterlike_Symbols, "Letterlike Symbols",0x2100,0x214F },
|
||||
{ Number_Forms, "Number Forms",0x2150,0x218F },
|
||||
{ Arrows, "Arrows",0x2190,0x21FF },
|
||||
{ Mathematical_Operators, "Mathematical Operators",0x2200,0x22FF },
|
||||
{ Miscellaneous_Technical, "Miscellaneous Technical",0x2300,0x23FF },
|
||||
{ Control_Pictures, "Control Pictures",0x2400,0x243F },
|
||||
{ Optical_Character_Recognition, "Optical Character Recognition",0x2440,0x245F },
|
||||
{ Enclosed_Alphanumerics, "Enclosed Alphanumerics",0x2460,0x24FF },
|
||||
{ Box_Drawing, "Box Drawing",0x2500,0x257F },
|
||||
{ Block_Elements, "Block Elements",0x2580,0x259F },
|
||||
{ Geometric_Shapes, "Geometric Shapes",0x25A0,0x25FF },
|
||||
{ Miscellaneous_Symbols, "Miscellaneous Symbols",0x2600,0x26FF },
|
||||
{ Dingbats, "Dingbats",0x2700,0x27BF },
|
||||
{ Braille_Patterns, "Braille Patterns",0x2800,0x28FF },
|
||||
{ CJK_Radicals_Supplement, "CJK Radicals Supplement",0x2E80,0x2EFF },
|
||||
{ Kangxi_Radicals, "Kangxi Radicals",0x2F00,0x2FDF },
|
||||
{ Ideographic_Description_Characters, "Ideographic Description Characters",0x2FF0,0x2FFF },
|
||||
{ CJK_Symbols_and_Punctuation, "CJK Symbols and Punctuation",0x3000,0x303F },
|
||||
{ Hiragana, "Hiragana",0x3040,0x309F },
|
||||
{ Katakana, "Katakana",0x30A0,0x30FF },
|
||||
{ Bopomofo, "Bopomofo",0x3100,0x312F },
|
||||
{ Hangul_Compatibility_Jamo, "Hangul Compatibility Jamo",0x3130,0x318F },
|
||||
{ Kanbun, "Kanbun",0x3190,0x319F },
|
||||
{ Bopomofo_Extended, "Bopomofo Extended",0x31A0,0x31BF },
|
||||
{ Enclosed_CJK_Letters_and_Months, "Enclosed CJK Letters and Months",0x3200,0x32FF },
|
||||
{ CJK_Compatibility, "CJK Compatibility",0x3300,0x33FF },
|
||||
{ CJK_Unified_Ideographs_Extension_A, "CJK Unified Ideographs Extension A",0x3400,0x4DB5 },
|
||||
{ CJK_Unified_Ideographs, "CJK Unified Ideographs",0x4E00,0x9FFF },
|
||||
{ Yi_Syllables, "Yi Syllables",0xA000,0xA48F },
|
||||
{ Yi_Radicals, "Yi Radicals",0xA490,0xA4CF },
|
||||
{ Hangul_Syllables, "Hangul Syllables",0xAC00,0xD7A3 },
|
||||
{ High_Surrogates, "High Surrogates",0xD800,0xDB7F },
|
||||
{ High_Private_Use_Surrogates, "High Private Use Surrogates",0xDB80,0xDBFF },
|
||||
{ Low_Surrogates, "Low Surrogates",0xDC00,0xDFFF },
|
||||
{ Private_Use, "Private Use",0xE000,0xF8FF },
|
||||
{ CJK_Compatibility_Ideographs, "CJK Compatibility Ideographs",0xF900,0xFAFF },
|
||||
{ Alphabetic_Presentation_Forms, "Alphabetic Presentation Forms",0xFB00,0xFB4F },
|
||||
{ Arabic_Presentation_Forms_A, "Arabic Presentation Forms-A",0xFB50,0xFDFF },
|
||||
{ Combining_Half_Marks, "Combining Half Marks",0xFE20,0xFE2F },
|
||||
{ CJK_Compatibility_Forms, "CJK Compatibility Forms",0xFE30,0xFE4F },
|
||||
{ Small_Form_Variants, "Small Form Variants",0xFE50,0xFE6F },
|
||||
{ Arabic_Presentation_Forms_B, "Arabic Presentation Forms-B",0xFE70,0xFEFE },
|
||||
{ Specials_1, "Specials",0xFEFF,0xFEFF },
|
||||
{ Halfwidth_and_Fullwidth_Forms, "Halfwidth and Fullwidth Forms",0xFF00,0xFFEF },
|
||||
{ Specials_2, "Specials 2",0xFFF0,0xFFFD },
|
||||
{ End_Block_Ids, "End Block Ids",0xFFFE,0xFFFF }
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
Mapping * Mapping::ms_singleton = 0;
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Initialize the singleton. This must be called before mapping is used.
|
||||
*/
|
||||
|
||||
void Mapping::initSingleton ()
|
||||
{
|
||||
explicitDestroy ();
|
||||
ms_singleton = new Mapping (ms_defaultBlockData);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Destroys the singleton
|
||||
*/
|
||||
|
||||
void Mapping::explicitDestroy ()
|
||||
{
|
||||
if (ms_singleton)
|
||||
delete ms_singleton;
|
||||
|
||||
ms_singleton = 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Construct a Mapping from an array of RawData
|
||||
*/
|
||||
|
||||
Mapping::Mapping (const RawData dataArray []) :
|
||||
m_idMap (),
|
||||
m_nameMap (),
|
||||
m_errorData ()
|
||||
{
|
||||
|
||||
size_t i = 0;
|
||||
for (; dataArray [i].m_id != End_Block_Ids; ++i)
|
||||
{
|
||||
m_idMap [dataArray[i].m_id] = Data (dataArray [i]);
|
||||
m_nameMap [dataArray[i].m_name] = Data (dataArray [i]);
|
||||
}
|
||||
|
||||
m_errorData = Data (dataArray [i]);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Construct a complete set of code points in this block.
|
||||
* @param idset should enter this function empty
|
||||
*/
|
||||
|
||||
const std::set<unicode_char_t> & Data::generateFilteredIdSet (std::set<unicode_char_t> & idset) const
|
||||
{
|
||||
{
|
||||
for (unicode_char_t i = m_start; i < m_end; ++i)
|
||||
{
|
||||
idset.insert (i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (RangeGroups_t::const_iterator iter = m_rangeGroups.begin (); iter != m_rangeGroups.end (); ++iter)
|
||||
{
|
||||
const RangeGroup & rg = *iter;
|
||||
|
||||
if (rg.m_isInclusive)
|
||||
{
|
||||
for (RangeGroup::Ranges_t::const_iterator rg_iter = rg.m_ranges.begin (); rg_iter != rg.m_ranges.end (); ++rg_iter)
|
||||
{
|
||||
for (unicode_char_t i = (*rg_iter).m_low; i <= (*rg_iter).m_high; ++i)
|
||||
{
|
||||
idset.insert (i);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (RangeGroup::Ranges_t::const_iterator rg_iter = rg.m_ranges.begin (); rg_iter != rg.m_ranges.end (); ++rg_iter)
|
||||
{
|
||||
for (unicode_char_t i = (*rg_iter).m_low; i <= (*rg_iter).m_high; ++i)
|
||||
{
|
||||
idset.erase (i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return idset;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Add a range group with a given string representation. Valid strings include hexadecimal numbers, dashes, and commas.
|
||||
* @param inclusive indicates if this group should be additive or subtractive. It is an error to attempt to add code points outside the Block's gross range.
|
||||
*/
|
||||
|
||||
bool Data::addRangeGroup (bool inclusive, const Plat_Unicode::NarrowString & str)
|
||||
{
|
||||
RangeGroup rg;
|
||||
rg.m_isInclusive = inclusive;
|
||||
|
||||
size_t tokenStartPos = 0;
|
||||
|
||||
while (tokenStartPos != NarrowString::npos)
|
||||
{
|
||||
size_t tokenCommaPos = str.find_first_of (',', tokenStartPos);
|
||||
|
||||
size_t tokenDashPos = str.find_first_of ('-', tokenStartPos);
|
||||
|
||||
Range rng;
|
||||
|
||||
// this is a dashed range
|
||||
if (tokenDashPos < tokenCommaPos)
|
||||
{
|
||||
rng.m_low = static_cast<unicode_char_t> (strtoul ( str.substr (tokenStartPos, tokenDashPos - tokenStartPos).c_str (), 0, 16));
|
||||
tokenStartPos = str.find_first_not_of ('-', tokenDashPos);
|
||||
|
||||
if (tokenStartPos == NarrowString::npos)
|
||||
return false;
|
||||
|
||||
rng.m_high = static_cast<unicode_char_t> (strtoul (str.substr (tokenStartPos, tokenCommaPos - tokenStartPos).c_str (), 0, 16));
|
||||
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
rng.m_high = rng.m_low = static_cast<unicode_char_t> (strtoul ( str.substr (tokenStartPos, tokenCommaPos - tokenStartPos).c_str (), 0, 16));
|
||||
}
|
||||
|
||||
rg.m_ranges.push_back (rng);
|
||||
|
||||
tokenStartPos = str.find_first_not_of (',', tokenCommaPos);
|
||||
}
|
||||
|
||||
m_rangeGroups.push_back (rg);
|
||||
return true;
|
||||
|
||||
};
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
// ===================================================================== },
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user