#ifndef CWBUFFER_HXX_
#define CWBUFFER_HXX_
/* ====================================================================
* The Vovida Software License, Version 1.0
*
* Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The names "VOCAL", "Vovida Open Communication Application Library",
* and "Vovida Open Communication Application Library (VOCAL)" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact vocal@vovida.org.
*
* 4. Products derived from this software may not be called "VOCAL", nor
* may "VOCAL" appear in their name, without prior written
* permission of Vovida Networks, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
* NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
* NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
* IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* ====================================================================
*
* This software consists of voluntary contributions made by Vovida
* Networks, Inc. and many individuals on behalf of Vovida Networks,
* Inc. For more information on Vovida Networks, Inc., please see
* <http://www.vovida.org/>.
*
*/
static const char* const CWBuffer_hxx_Version =
"$Id: CWBuffer.hxx,v 1.18 2002/02/25 05:57:49 sprajpat Exp $";
#include "CountSemaphore.hxx"
#include <string.h>
#include <ctype.h>
#include "Sptr.hxx"
#include "fastmemcpy.hxx"
#include "Lock.hxx"
#include <iostream>
#include <cstdlib>
#include <cctype>
#define INLINE_ inline
#define BUFFER_STATISTICS 0
class CWCharArray;
INLINE_ int
mystrlen(const char* s)
{
int i = 0;
while (*s++ != '\0')
{
i++;
}
return i;
}
#if 1
INLINE_ void*
mmemcpy(char* dest, const char* s, int len)
{
char *d = dest;
while (len--)
{
*d++ = *s++;
}
return dest;
}
#else
#define mmemcpy memcpy
#endif
extern char* emptyStr;
/** character array structure used for copy on write data. Do not
* derive from this class */
class CWCharArray
{
public:
/// construct an array of length len
CWCharArray(int len);
/// construct an array of size allocsize, copying len bytes from p
CWCharArray(const char* p, int len, int allocsize);
/// destructor
~CWCharArray();
CountSemaphore mutex;
char* ptr;
};
#if (BUFFER_STATISTICS)
struct BufferStats
{
public:
BufferStats() : refCount(0), realCount(0)
{
}
int refCount;
int realCount;
};
class BufferStatistic
{
public:
static void add(int size, CWCharArray* ptr)
{
create();
Vocal::Process::Lock lock_(instance->bufferMutex);
instance->buffer[ptr] = size;
if(instance->rawSizes.find(size) == instance->rawSizes.end())
{
instance->rawSizes[size] = 1;
}
else
{
instance->rawSizes[size]++;
}
}
static void remove(CWCharArray* ptr)
{
create();
Vocal::Process::Lock lock_(instance->bufferMutex);
map <CWCharArray*, int>::iterator i = instance->buffer.find(ptr);
if(i != instance->buffer.end())
{
instance->buffer.erase(i);
}
}
static void print(ostream& s)
{
create();
Vocal::Process::Lock lock_(instance->bufferMutex);
s << "\n\t\tBuffer Report\n\n";
map <CWCharArray*, int>::iterator i;
map <int, BufferStats> myStats;
map <int, BufferStats>::iterator j;
for(i = instance->buffer.begin(); i != instance->buffer.end(); ++i)
{
j = myStats.find(i->second);
if(j == myStats.end())
{
BufferStats x;
x.refCount = (i->first)->mutex.getCount();
x.realCount = 1;
myStats[i->second] = x;
}
else
{
j->second.refCount += (i->first)->mutex.getCount();
j->second.realCount ++;
}
#if 0
s << i->first
<< ":"
<< i->second
<< ":"
<< (i->first)->mutex.getCount()
<< "\n";
#endif
}
s << "current:\n\n";
for(j = myStats.begin(); j != myStats.end(); ++j)
{
s << j->first
<< ":"
<< j->second.realCount
<< ":"
<< j->second.refCount
<< "\n";
}
s << "allocs:\n\n";
for(map <int, int>::iterator k = instance->rawSizes.begin();
k != instance->rawSizes.end();
++k)
{
s << k->first
<< ":"
<< k->second
<< "\n";
}
}
protected:
BufferStatistic()
: bufferMutex(),
buffer()
{
}
static void create()
{
if(!instance)
{
instance = new BufferStatistic;
}
}
static BufferStatistic* instance;
VMutex bufferMutex;
map <CWCharArray*, int> buffer;
map <int, int> rawSizes;
};
INLINE_
void printBufferStatistics(ostream& s)
{
BufferStatistic::print(s);
}
#else
INLINE_
void printBufferStatistics(ostream& s)
{
// do nothing
return;
}
#endif
extern "C" {
void dumpBufferStatistics();
};
INLINE_
CWCharArray::CWCharArray(int length)
: mutex()
{
ptr = new char[length];
memset(ptr, 0, length);
#if (BUFFER_STATISTICS)
BufferStatistic::add(length, this);
#endif
}
INLINE_
CWCharArray::CWCharArray(const char* p, int len, int allocsize)
: mutex()
{
assert(allocsize >= len);
ptr = new char[allocsize];
memset(ptr, 0, allocsize);
mmemcpy(ptr, p, len);
#if (BUFFER_STATISTICS)
BufferStatistic::add(len, this);
#endif
}
INLINE_
CWCharArray::~CWCharArray()
{
assert(mutex.compare(0));
delete[] ptr;
#if (BUFFER_STATISTICS)
BufferStatistic::remove(this);
#endif
}
INLINE_ void decrementCA(CWCharArray* ptr)
{
if (ptr)
{
if (ptr->mutex.compare(0))
{
delete (ptr);
ptr = 0;
}
else if (ptr->mutex.decrement())
{
delete (ptr);
ptr = 0;
}
}
}
/** copy on write buffer, used in conjunction with CopyOnWriteData.
* Do not derive from this class */
class CWBuffer
{
public:
/// default constructor (constructs an empty buffer of length len)
CWBuffer(int len = 0);
/// construct a buffer of size hint, copying length bytes from p
CWBuffer(const char* src, int length, int hint = 0);
/// copy constructor
CWBuffer(const CWBuffer& src);
/// destructor
~CWBuffer();
/// assignment operator
const CWBuffer& operator=(const CWBuffer& src);
// these functions don't make modifications to the underlying
// buffer, so they ought to be cheap
/// clear the string
void clear();
/** compare this buffer with a character array
@param str character array to compare against
@param rightSize size of array to compare against
*/
int compare(const char* str, int rightSize) const;
/** compare this buffer with a character array
@param str character array to compare against
@param rightSize size of array to compare against
*/
int compareCstr(const char* str) const;
/** compare this buffer with a character array case-insensitvely
@param str character array to compare against
@param rightSize size of array to compare against
*/
int compareNoCase(const char* str, int rightSize) const;
/** split the string into two. This object becomes the front,
which has all the characters w/ positions < p, while ptr
has the positions >= p. Note that this is special in that
it does NOT write to any of the string, so the underlying
buffer is never written.
@param p number of bytes to split at.
@param ptr returned buffer, containing the positions >= p
*/
void split(int p, CWBuffer* ptr);
/** truncate the current buffer.
@param front number of characters to remove from the beginning
of the string
@param back number of characters to remove from the end
of the string
*/
void truncate(int front, int back);
/** truncate the current buffer.
@param first the first character of the string to keep
@param last the last character of the string to keep
*/
void substr(int first, int last);
// these functions (potentially) modify the underlying buffer, so
// they are (potentially) expensive
/** verify that the current string can be written to, up to
length-1 characters.
@param length number of characters in bitsPtr which are writable.
*/
void makeWritable(int length);
/** convert this string into a C string (a NUL terminated one) */
void makeCString();
CWCharArray* bufPtr;
char* bitsPtr;
int size;
int allocSpace;
};
INLINE_
CWBuffer::CWBuffer(int length)
:
bufPtr(0),
bitsPtr(emptyStr),
size(0),
allocSpace(length)
{
if (allocSpace)
{
bufPtr = new CWCharArray(allocSpace);
bufPtr->mutex.increment();
bitsPtr = bufPtr->ptr;
}
}
INLINE_
CWBuffer::CWBuffer(const char* src, int length, int hint)
:
bufPtr(0),
bitsPtr(emptyStr),
size(length),
allocSpace(length)
{
if (hint > length)
{
allocSpace = hint;
}
if (allocSpace)
{
bufPtr = new CWCharArray(src, length, allocSpace);
bufPtr->mutex.increment();
bitsPtr = bufPtr->ptr;
}
}
INLINE_
CWBuffer::CWBuffer(const CWBuffer& src)
:
bufPtr(src.bufPtr),
bitsPtr(src.bitsPtr),
size(src.size),
allocSpace(src.allocSpace)
{
if (bufPtr)
{
bufPtr->mutex.increment();
}
}
INLINE_ const CWBuffer&
CWBuffer::operator=(const CWBuffer& src)
{
bufPtr = src.bufPtr;
bitsPtr = src.bitsPtr;
size = src.size;
allocSpace = src.allocSpace;
if (bufPtr)
{
bufPtr->mutex.increment();
}
return *this;
}
INLINE_ void
CWBuffer::clear()
{
if (bufPtr)
{
decrementCA(bufPtr);
}
bitsPtr = emptyStr;
size = 0;
allocSpace = 0;
bufPtr = 0;
}
INLINE_
CWBuffer::~CWBuffer()
{
decrementCA(bufPtr);
}
INLINE_ int
CWBuffer::compare(const char* str, int rightSize) const
{
char* ptr = bitsPtr;
int leftSize = size;
while (leftSize && rightSize)
{
if (*ptr < *str)
{
return -1;
}
if (*ptr > *str)
{
return 1;
}
leftSize--;
rightSize--;
ptr++;
str++;
}
if (leftSize < rightSize)
{
return -1;
}
else if (leftSize > rightSize)
{
return 1;
}
else
{
return 0;
}
}
INLINE_ int
CWBuffer::compareCstr(const char* str) const
{
char* ptr = bitsPtr;
int leftSize = size;
while (leftSize && (*str != '\0'))
{
if (*ptr < *str)
{
return -1;
}
if (*ptr > *str)
{
return 1;
}
leftSize--;
ptr++;
str++;
}
if ((leftSize == 0) && (*str != '\0'))
{
return -1;
}
else if ((leftSize > 0) && (*str == '\0'))
{
return 1;
}
else
{
return 0;
}
}
INLINE_ int
CWBuffer::compareNoCase(const char* str, int rightSize) const
{
char* ptr = bitsPtr;
int leftSize = size;
while (leftSize && rightSize)
{
if (::toupper(*ptr) < ::toupper(*str))
{
return -1;
}
if (::toupper(*ptr) > ::toupper(*str))
{
return 1;
}
leftSize--;
rightSize--;
ptr++;
str++;
}
if (leftSize < rightSize)
{
return -1;
}
else if (leftSize > rightSize)
{
return 1;
}
else
{
return 0;
}
}
INLINE_ void
CWBuffer::split(int p, CWBuffer* ptr)
{
assert((p >= 0) && (p <= size));
if (p == size)
{
// do not actually split, but return an empty CWBuffer. p ==
// size is a border case where we do nothing, and return an
// empty string, since nothing belongs in the second half of
// the split.
ptr->clear();
}
else
{
// do the split
*ptr = *this;
// correct the new ptr
ptr->bitsPtr = ptr->bitsPtr + p;
ptr->size = ptr->size - p;
ptr->allocSpace = ptr->allocSpace - p;
// correct the old ptr
allocSpace = p;
size = p;
}
}
INLINE_ void
CWBuffer::truncate(int front, int back)
{
assert((front + back) <= size);
bitsPtr = bitsPtr + front;
size = size - (front + back);
}
INLINE_ void
CWBuffer::substr(int first, int last)
{
assert((first <= last) &&
(first <= size) &&
(last <= size) &&
(first >= 0) &&
(last >= 0));
bitsPtr = bitsPtr + first;
size = (last - first);
}
INLINE_ void
CWBuffer::makeWritable(int length)
{
if (bufPtr)
{
if ( (length > allocSpace) || !bufPtr->mutex.compare(1))
{
// if we need more space than we have in this buffer, OR
// if this is not the only CWBuffer pointing to this
// CharArray, we need to copy it
#if 0
// this is the policy
if (length > allocSpace)
{
allocSpace = length + 128;
}
#endif
// replace above policy with an exponential backoff
// policy. See: http://www.gotw.ca/gotw/043.htm -- the
// comment is exponential increase of about 1.5 is best
// (allegedly from Andrew Koenig's column in the September
// 1998 issue of JOOP (Journal of Object-Oriented
// Programming).
if (length > allocSpace)
{
allocSpace = length + length / 2;
}
CWCharArray* tmp = new CWCharArray(bitsPtr, size, allocSpace);
// this must be done first or it will cause problems
tmp->mutex.increment();
bitsPtr = tmp->ptr;
tmp->mutex.exchange(reinterpret_cast<void**>(&bufPtr),
reinterpret_cast<void**>(&tmp));
// get rid of the old pointer, now in tmp
decrementCA(tmp);
}
}
else if (length > 0)
{
assert(size == 0);
// allocate a buffer
// allocSpace = length + 128;
// see above for the rationale for this
allocSpace = length + length / 2;
bufPtr = new CWCharArray(allocSpace);
bitsPtr = bufPtr->ptr;
bufPtr->mutex.increment();
}
}
INLINE_ void
CWBuffer::makeCString()
{
if (bitsPtr != emptyStr)
{
makeWritable(size + 1);
bitsPtr[size] = '\0';
}
}
/* Local Variables: */
/* c-file-style: "stroustrup" */
/* indent-tabs-mode: nil */
/* c-file-offsets: ((access-label . -) (inclass . ++)) */
/* c-basic-offset: 4 */
/* End: */
#endif
syntax highlighted by Code2HTML, v. 0.9.1