class GrowableArray {
private:
typedef enum { StartArraySize = 64 } bogus;
size_t num_elem; // number of data elements in the array
size_t array_size; // array size (e.g., data capacity)
T *pArray;
private:
/**
Double the amount of memory allocated for the array.
Return true if memory allocation succeeded, false
otherwise.
*/
bool twice()
{
bool rslt;
T *old_array = pArray;
size_t new_size = array_size * 2;
pArray = new T [ new_size ];
if (pArray != 0) {
rslt = true;
for (int i = 0; i < array_size; i++) {
pArray[i] = old_array[i];
}
delete [] old_array;
array_size = new_size;
}
else {
rslt = false;
}
return rslt;
} // twice
void init(size_t initialSize )
{
array_size = StartArraySize;
while (initialSize > array_size) {
array_size = array_size * 2;
}
pArray = new T[ array_size ];
num_elem = initialSize;
} // init
public:
GrowableArray()
{
init( 0 );
} // GrowableArray constructor
GrowableArray(size_t initialSize)
{
init( initialSize );
}
~GrowableArray()
{
if (pArray != 0) {
delete [] pArray;
}
} // GrowableArray destructor
const size_t length(void) const { return num_elem; }
T &operator[](const size_t i)
throw(std::out_of_range)
{
if (i >= num_elem) {
const char *errMsg = "1. GrowableArray::[] - index out of bounds";
throw std::out_of_range(errMsg);
}
return pArray[ i ];
}
T operator[](const size_t i ) const
throw(std::out_of_range)
{
if (i >= num_elem) {
const char *errMsg = "2. GrowableArray::[] - index out of bounds";
throw std::out_of_range(errMsg);
}
return pArray[ i ];
}
const T *getData() const { return pArray; }
/** append an item to the end of the array */
void append( T item )
throw(std::runtime_error)
{
if (num_elem == array_size) {
bool allocOK = twice();
if (!allocOK) {
const char *errMsg = "GrowableArray::append - memory allocation error";
throw std::runtime_error( errMsg );
}
}
pArray[ num_elem ] = item;
num_elem++;
} // append
/**
Expand the number of array data slots by "amount" elements.
Note that "array_size" is the total amount of storage available
for data slots. "num_elem" is the number of data slots.
The bounds over which the array can be indexed is governed
by num_elem. Note that after expand() is called the new
data elements can be read, but their value is undefined until
they are initialized.
This function calls twice(), which copies the old elements
into the new array. This function is not called by the
constructor which takes a size argument, because there is
not existing array at construction time.
*/
void expand( size_t amount )
throw(std::runtime_error)
{
bool allocOK = true;
while (allocOK && num_elem + amount >= array_size) {
allocOK = twice();
if (!allocOK) {
const char *errMsg = "GrowableArray::expand - memory allocation error";
throw std::runtime_error( errMsg );
}
}
num_elem += amount;
} // expand
/** Remove one item from the end of the array. */
void remove(void)
{
if (num_elem > 0)
num_elem--;
} // remove
/**
Set the number of data elements in the array to
a new value (note that this will usually be
smaller than the array size, unless a power of
two is chosen for "new_size").
*/
void set_size( size_t new_size )
{
if (new_size <= array_size) {
num_elem = new_size;
}
else { // new_size > array_size
size_t num_new_elem = new_size - num_elem;
expand( num_new_elem );
}
} // set_size
}; // GrowableArray
#endif
string/MainPage 0100600 0000764 0000764 00000005677 07733225371 012447 0 ustar iank iank
/*! \mainpage
A String Class Based on a Reference Counted Array
This source base implements a String container. Closely associated
with the String class is a SubString class which defines sub-sections
of Strings.
The String class is a reference counted object. Rather than copying
data, reference counted classes increment a reference count when one
object is assigned to another. For example, when a is assigned
to b below, the reference count will be incremented. Both
objects will share a common data object, which in this case contains
the string "slithey toves".
String a = "slithey toves";
String b = a;
Reference counted objects implement copy-on-write semantics. A
unique copy will be made of an object before it is modified it
it shares data with other objects. For example, when a
is changed by the statement below, b will remain unchanged
(e.g., it will still contain "slithey toves").
a(8, 5) = "Boyd"; // a is now "slighey Boyd"
The String object is derived from an instance of the RCArray
template (e.g., RCArray).
The String object is complicated because it provides a number of
functions that are specific to the String container. The DoubleVec
class is simpler. DoubleVec demonstrates how a growable
reference counted array can be derived from RCArray.
Regression tests are included for the String class (StringTest.C), the
SubString class (SubStrTest.C) and the DoubleVec class (DblVecTest.C).
The DblVecTest.C regression tests serve as a test for the RCArray
template.
Generating the Documentation
The source code documentation for this software is formatted for
doxygen, which is available from www.doxygen.org. Doxygen creates
diagrams using the dot program, which is part of AT&T's Graphviz.
Both Doxygen and Graphviz run on the major platforms (e.g., Linux,
Windoz and Solaris).
Assuming that doxygen is installed on your system, along with
dot you can regenerate this documentation with the command
doxygen doxygenTemplate
Copyright and Use
You may use this source code without limitation and without
fee as long as you include:
This software was written and is copyrighted by Ian Kaplan, Bear
Products International, www.bearcave.com, 2001, 2002, 2003.
This software is provided "as is", without any warrenty or
claim as to its usefulness. Anyone who uses this source code
uses it at their own risk. Nor is any support provided by
Ian Kaplan and Bear Products International.
Please send any bug fixes or suggested source changes to:
iank@bearcave.com
*/
string/RCArray.h 0100600 0000764 0000764 00000020422 07733701275 012502 0 ustar iank iank
#ifndef RCARRAY_H
#define RCARRAY_H
/** \file
Copyright and Use
You may use this source code without limitation and without
fee as long as you include:
This software was written and is copyrighted by Ian Kaplan, Bear
Products International, www.bearcave.com, 2001, 2002, 2003
This software is provided "as is", without any warrenty or
claim as to its usefulness. Anyone who uses this source code
uses it at their own risk. Nor is any support provided by
Ian Kaplan and Bear Products International.
Please send any bug fixes or suggested source changes to:
iank@bearcave.com
*/
#include "GrowableArray.h"
#include "RCObject.h"
#include "RCPtr.h"
#include "RCBase.h"
/**
A reference counted, growable array template.
Objects that are instantiated with this template have three
characteristics:
-
An assignment of the object increments the reference count
rather than copying memory. For example, in the code below
myObj1 is initialized and then assigned to myObj2. myObj1
and myObj2 now share the same memory (there was no copy)
and the reference count will be 2.
#include <iostream>
#include "RCArray.h"
typedef RCArray IntVec;
main()
{
IntVec myObj1, myObj2;
for (int i = 1; i %lt;= 10; i++) {
myObj1.append( i );
}
myObj2 = myObj1;
for (i = 0; i %lt; 10; i++) {
std::cout %lt;%lt; myObj2[i] %lt;%lt; " ";
}
std::cout %lt;%lt; std::endl;
}
If this code is complex and executed it will print:
1 2 3 4 5 6 7 8 9 10
-
A change to an object with multiple references results in
a copy-on-write. That instance of the object will be changed
while the others remain unchanged. Since a unique copy has
been made, the reference count will be reduced by one.
For example, the assignment
myObj1[4] = 42;
will only change myObj1. The contents of myObj2 remain
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}.
-
When the reference count reaches zero, the memory for the object
will be automatically deallocated. This allows reference
counted array used as if they were elemental values, without
worrying about memory allocation and deallocation.
Only the argumentless constructor of RCArray is made public.
Classes can expose the other constructors and protected
functions like length() and getRefCnt() (which is
useful in testing and verification) by deriving a class from an
instance of this template. For example:
#include "RCArray.h"
typedef RCArray DoubleArray;
class DoubleVec : public DoubleArray
{
private:
void init(double intialVal );
public:
DoubleVec() : DoubleArray() {}
DoubleVec(size_t initialSize) : DoubleArray( initialSize ) {}
DoubleVec(size_t initialSize, double initVal ) :
DoubleArray(initialSize, initVal) {}
size_t length() { return DoubleArray::length(); }
size_t getRefCnt() { return DoubleArray::getRefCnt(); }
};
The RCArray template has a single data element, value, which is a
"smart" pointer. Note that there is no overloaded assignment
operator. The default RCArray assignment copies the value
element. This in turn results in a copy of the RCPtr instantiated
template object (the smart pointer).
The RCArray template uses the default destructor (oh, the horror!)
The default destructor will call the destructor on value which
will, in turn invoke the removeReference() function in RCObject.
This will decrement the reference count. When the reference count
reaches zero, the shared data will be deleted.
Compiling the code
The GrowableArray template will throw a runtime exception if
the array is indexed beyond the bounds of the data. At least
in the case of the Microsoft compilers, this requires that
you include the -GX flag. For example:
cl -TP -GX -Zi test.C -o test
(the -Zi flag compiles for debug).
*/
template
class RCArray : public RCBase
{
protected:
class SharedData : public RCObject
{
public:
GrowableArray data;
SharedData() {};
SharedData(const GrowableArray &v ) { copy( v ); }
SharedData( size_t initialSize ) : data( initialSize ) {}
SharedData(const SharedData& rhs) { copy( rhs.data ); }
~SharedData() {}
size_t getRefCnt() { return refCnt(); }
private:
void copy(const GrowableArray &v )
{
size_t len = v.length();
for (size_t i = 0; i < len; i++) {
data.append( v[i] );
}
} // copy
}; // class SharedData
RCPtr value;
void init(T intialVal );
RCArray(size_t initialSize);
RCArray(size_t initialSize, T initVal );
T read(const int i) const;
void write(const int i, const T v);
size_t length() const;
size_t getRefCnt();
public:
RCArray();
ValRef operator[](const int ix) throw(std::out_of_range);
T operator[](const int ix) const;
ValRef operator[](const size_t ix) throw(std::out_of_range);
T operator[](const size_t ix) const;
void append(T val);
}; // RCArray
/**
Initialize the array with a value of type T
*/
template
inline
void RCArray::init( T initialVal )
{
size_t len = value->data.length();
for (size_t i = 0; i < len; i++) {
value->data[i] = initialVal;
}
}
/**
Argumentless constructor: the RCArray will contain no data elements
*/
template
inline
RCArray::RCArray() : value( new SharedData() ) {}
/**
Create an RCArray with length() == initialSize. The length()
data elements are uninitialized and their value is undefined.
*/
template
inline
RCArray::RCArray( size_t initialSize ) :
value( new SharedData(initialSize) ) {}
/**
Create an RCArray with length() == initialSize, where
length() data elements are initialized to initialValue.
*/
template
inline
RCArray::RCArray( size_t initialSize, T initialVal ) :
value( new SharedData(initialSize) )
{
init( initialVal );
}
/**
Add a new element to the end of the RCArray, resulting
in length() + 1 data elements.
*/
template
inline
void RCArray::append( T val )
{
value->data.append( val );
}
/**
Return the number of data elements in the RCArray.
*/
template
inline
size_t RCArray::length() const
{
return value->data.length();
}
/**
Return the reference count.
*/
template
inline
size_t RCArray::getRefCnt()
{
return value->getRefCnt();
}
template
inline
T RCArray::operator[](const int ix) const
{
return value->data[ix];
}
template
inline
RCArray::ValRef RCArray::operator[](const int ix)
throw(std::out_of_range)
{
if (ix < 0 || ix >= value->data.length()) {
const char *errMsg = "operator[] - index out of bounds";
throw std::out_of_range( errMsg );
}
return ValRef( *this, ix );
}
template
inline
T RCArray::operator[](const size_t ix) const
{
return value->data[ix];
}
template
inline
RCArray::ValRef RCArray::operator[](const size_t ix)
throw(std::out_of_range)
{
if (ix >= value->data.length()) {
const char *errMsg = "operator[] - index out of bounds";
throw std::out_of_range( errMsg );
}
return ValRef( *this, ix );
}
/**
Return the element at index i
*/
template
inline
T RCArray::read(const int i) const
{
return value->data[i];
}
/**
Write value v at index i
*/
template
inline
void RCArray::write(const int i, const T v)
{
if (value->isShared()) {
value = new SharedData( value->data );
}
value->data[i] = v;
}
#endif
string/RCBase.h 0100600 0000764 0000764 00000010540 07733211737 012275 0 ustar iank iank
#ifndef RCBASE_H
#define RCBASE_h
/** \file
The ValRef object is losely modeled after the Cref class in
The C++ Programming Language, Third Edition by Bjarne
Stroustrup, Section 11.12. As a result, this code is in the
public domain.
\author Ian Kaplan, www.bearcave.com
*/
/**
An abstract base class for reference counted arrays. These array
objects implement copy-on-write semantics.
This abstract class defines two pure virtual functions, read
and write and the local ValRef class. The ValRef class is
returned for referenced array elements. This class is a proxy
which avoids copy-on-read.
An object that implements copy-on-write arrays defines the
[] operator (the index operator). The index operator
can be used on either the right or left hand size of an
assignment. For example, in the statements below a is
an instance of an object that implements the index operator.
MyObj b = a; // b is a reference to a
MyType v = a[i]; // A right-hand-side reference
a[j] = v; // A left-hand-size reference
References counted objects share a reference to common data
until an object is modified. The copy-on-write semantics
means that a unique copy will be made before shared object
is modified. Ideally this saves memory and improves
performance.
In the example above a and b share a reference to the
same data set. When a is read, there should be no effect
(other than the read). However, when a is modified (by
being written), a unique copy is made first. Although previously
a and b referenced the same data, the write operation
will force the creation of a unique copy for a and only
a will be modified. The b object will retain its
previous value.
In a better world there would be some way that the programmer could
indicate to the C++ compiler that a particular operator []
function implements the right-hand-side or the left-hand-size
semantics.
The naive implementation (which I used in the first two versions of
a String class) turns out to be wrong. Here is the slightly
simplified code from this somewhat incorrect String class. My
intention was that the operator [] function below labeled
"LHS" would implement copy-on-write. The operator [] labeled
RHS would do a read and nothing more.
This code does not properly implement copy-on-write
//
// operator [] (RHS) with an integer index
//
inline char String::operator [](const int ix) const
{
return pShare->str[ix];
}
//
// operator[] (LHS), integer index
//
inline char & String::operator [](const int ix)
{
makeUnique(); // make a unique copy of the shared data
return pShare->str[ix];
} // operator []
As it turned out, the function labeled LHS is called in most
cases, whether the operator [] is on the left or right
hand side of the expression. This results in copy-on-read
which is, obviously, not desirable and destroys much of the utility
for a reference counted object.
The proper implementation of the String object uses an instance
of the ValRef object (instantiated for char).
inline
String::ValRef String::operator[](const int ix)
{
return ValRef( *this, ix );
}
The ValRef object is losely modeled after the Cref class in
The C++ Programming Language, Third Edition by Bjarne
Stroustrup, Section 11.12.
\author Ian Kaplan www.bearcave.com
*/
template
class RCBase
{
public:
class ValRef
{
RCBase& o;
const int i;
public:
ValRef(RCBase& oo, const int ii) : o(oo), i(ii) {}
operator Type() const
{
// printf("RCBase::Type\n");
return o.read(i);
}
void operator=(Type v)
{
// printf("RCBase::operator=\n");
o.write(i, v);
}
}; // class ValRef
virtual Type read( const int i ) const = 0;
virtual void write( const int i, const Type v ) = 0;
};
#endif
string/RCObject.h 0100600 0000764 0000764 00000004356 07733166167 012647 0 ustar iank iank
#ifndef RCOBJECT_H
#define RCOBJECT_H
/** \file
This code is based on Scott Meyers' RCObject class. This was
published in Mr. Meyers' book More Effective C++ by Scott
Meyers, Addison-Wesley, 1996. The RCObject class is part of Scott
Meyers reference counted String class, published in "Item 29".
This code is in the public domain.
\author Ian Kaplan www.bearcave.com
*/
/**
This the base class for a shared data class that is used in a
reference counted copy-on-write object (see RCArray). This class
encapsulates the reference count. The shared data class is derived
from this class and is a container for the the shared data.
This class is based on the RCObject class published in More
Effective C++ by Scott Meyers, Addison-Wesley, 1996. This is
part of Scott Meyers reference counted String class, published in
"Item 29". This code is in the public domain.
I have the first edition and first printing of Mr. Meyers' book.
I've added some of the changes from Mr. Meyers' errata pages, which
are not included in this edition. I've also made some to Mr. Meyers'
version of RCObject. In particular, I've removed the shareable flag,
since as far as I can tell, it is not needed. I have also made the
reference count available for testing purposes.
\author Ian Kaplan www.bearcave.com
*/
class RCObject {
public:
void addReference();
void removeReference();
const bool isShared() const;
const size_t refCnt() const;
protected:
RCObject();
RCObject(const RCObject& rhs);
RCObject& operator=(const RCObject &rhs);
virtual ~RCObject();
private:
size_t refCount;
};
inline
RCObject::RCObject() : refCount(0) {}
inline
RCObject::RCObject(const RCObject& ) : refCount(0) {}
inline
RCObject& RCObject::operator=(const RCObject&)
{
return *this;
}
inline
RCObject::~RCObject() {}
inline
void RCObject::addReference() { refCount++; }
inline
void RCObject::removeReference()
{
if (--refCount == 0) {
delete this;
}
}
inline
const bool RCObject::isShared() const
{
return refCount > 1;
}
inline
const size_t RCObject::refCnt() const
{
return refCount;
}
#endif
string/RCPtr.h 0100600 0000764 0000764 00000007037 07733223737 012202 0 ustar iank iank
#ifndef RCPTR_H
#define RCPTR_H
/** \file
This template was published in More Effective C++
by Scott Meyers, Addison-Wesley, 1996.
This is part of Scott Meyers reference counted String class,
published in "Item 29". This code is in the public domain.
\author Ian Kaplan www.bearcave.com
*/
/**
This template implements a "smart pointer" for reference counted
objects. This template is designed to be used with the RCObject
base class for reference counted objects.
This template was published in More Effective C++
by Scott Meyers, Addison-Wesley, 1996. The RCPtr template
is part of Scott Meyers reference counted String class,
published in "Item 29".
The RCPtr template is used to instantiate a shared data
object for a reference counted object. A simplified version
of the SubString object is shown below. Note that the
SubString class variable value is an instance
of the local shared data object SharedData.
class SubString
{
private:
class SharedData : public RCObject
{
private:
String *pStr_;
size_t start_;
size_t subStrLen_;
public:
SharedData();
}; // class sharedData
RCPtr<SharedData> value;
}
The RCPtr "smart pointer" is designed to be instantiated with
a class that is derived from RCObject. RCObject support
the reference count. The RCPtr "smart pointer" handles assignment
by deleting the shared data of "this" and assigning it the shared
data from the right hand side object. The RCPtr template also
decrements the reference count when the destructor is called.
\author Ian Kaplan www.bearcave.com
*/
template
class RCPtr
{
public:
RCPtr(T* realPtr = 0);
RCPtr(const RCPtr &rhs);
~RCPtr();
bool operator ==(const RCPtr& rhs );
RCPtr& operator=(const RCPtr& rhs );
T* operator->() const;
T& operator*() const;
private:
T *pointee;
void init();
};
template
inline
void RCPtr::init()
{
if (pointee != 0) {
pointee->addReference();
}
} // init
template
inline
RCPtr::RCPtr(T* realPtr) : pointee(realPtr)
{
init();
}
template
inline
RCPtr::RCPtr( const RCPtr& rhs) : pointee( rhs.pointee )
{
init();
}
template
inline
RCPtr::~RCPtr()
{
if (pointee) {
pointee->removeReference();
}
}
/**
Assign to the "smart pointer". The "smart pointer" should be
instantiated with an object derived from RCObject. The
RCObject base class supports the removeReference function
which deallocates "this".
In the code below, the new value for the smart pointer is assigned
to pointee. The reference count is incremented to 1 (in init())
and the old object (oldPointee) is deallocated.
*/
template
inline
RCPtr& RCPtr::operator=(const RCPtr &rhs)
{
if (pointee != rhs.pointee) {
T *oldPointee = pointee;
pointee = rhs.pointee;
init();
if (oldPointee) {
oldPointee->removeReference();
}
}
return *this;
}
template
inline
bool RCPtr::operator ==(const RCPtr &rhs)
{
bool rslt = (pointee == rhs.pointee);
return rslt;
}
template
inline
T* RCPtr::operator->() const
{
return pointee;
}
template
inline
T& RCPtr::operator*() const
{
return *pointee;
}
#endif
string/String.C 0100600 0000764 0000764 00000026111 07733663357 012411 0 ustar iank iank
/** \file
Copyright and Use
You may use this source code without limitation and without
fee as long as you include:
This software was written and is copyrighted by Ian Kaplan, Bear
Products International, www.bearcave.com, 2001, 2002, 2003
This software is provided "as is", without any warrenty or
claim as to its usefulness. Anyone who uses this source code
uses it at their own risk. Nor is any support provided by
Ian Kaplan and Bear Products International.
Please send any bug fixes or suggested source changes to:
iank@bearcave.com
*/
#include
#include
using namespace std;
/** POSIX string operations */
#include
#include "String.h"
/**
Initialize the underlying data structure for a String container
from a C string.
*/
void String::init( const char *initCstr )
{
if (initCstr != 0) {
char ch;
for (ch = *initCstr; ch != '\0'; ch = *initCstr) {
value->data.append( ch );
initCstr++;
}
value->data.append( ch );
}
}
/**
Append a C string at the end of the character string stored in the
String object.
The character data stored in a String object should be terminated
by a null character ('\0'). The null terminator for the "this"
string will be overwritten by the first character of CStr.
Reference count and allocation issues should be taken care
of by the code that calls this function.
*/
void String::appendCString( const char *CStr )
{
if (CStr != 0) {
size_t len = length();
if (len > 0) {
// if there is a null at the end of the string
// (which there should be) write over it.
if (value->data[len-1] == '\0') {
value->data[len-1] = *CStr++;
}
}
while (*CStr) {
value->data.append( *CStr++ );
}
value->data.append('\0');
}
} // appendCString
/**
Append the characters from "s" onto the end of the characters
stored in the String object.
Note that when String 's' is appended to the String object
the null that terminates 's' should be appended as well.
Reference count and allocation issues should be taken care
of by the code that calls this function.
*/
void String::appendString( String &s )
{
size_t i = 0;
size_t len = s.length();
size_t thisLen = length(); // length of "this" String
if (thisLen > 0 && len > 0) {
// write over the null character
if (value->data[thisLen-1] == '\0') {
value->data[thisLen-1] = s.value->data[i];
i++;
}
} // if
while (i < len) {
char ch = s.value->data[i];
value->data.append( ch );
i++;
} // while
} // appendString
/**
Compare two strings for equality
*/
int String::compareTo( String &s )
{
if (length() == 0 && s.length() == 0) {
// Both String objects contain no data and so are
// equal
return 0;
}
if (value == s.value) {
// The two String objects point to the same shared data
return 0;
}
const char *pThisCStr = *this;
const char *Cstr = s;
return Cstrcmp( pThisCStr, Cstr );
} // compareTo
/**
Set the string length of the String to newSize. This will be the
size returned by String::strlen(). So the actual amount of data
allocated will be newSize+1 to account for the null character,
assuming that newSize > 0. If newSize == 0, strlen() will be
zero and the number of data elements will be zero.
The private function length() will return new_size + 1, since the
String data is terminated by a null character. If the String is
expanded, the String will be padded with spaces.
If the reference count of the String object is greater than one,
a unique copy will be made when it is resized, since other
copies should be unaffected.
*/
String &String::resize( const size_t newSize )
{
if (newSize == 0) {
newEmptyString();
}
else { // newSize > 0
size_t new_len = newSize + 1;
size_t old_len = length();
if (value->isShared()) {
if (new_len >= old_len) {
// this will copy the data into a new shared data object
newSharedData();
value->data.set_size( new_len );
}
else { // new_len < old_len
RCPtr new_value = new SharedData();
for (size_t i = 0; i < newSize; i++) {
new_value->data.append( value->data[i] );
}
new_value->data.append('\0');
// note: smart pointer assignment
value = new_value;
}
}
else {
value->data.set_size( new_len );
}
for (size_t i = old_len-1; i < new_len-1; i++) {
value->data[i] = ' ';
}
value->data[ new_len-1 ] = '\0';
}
return *this;
} // resize
/**
Compare two C strings pointed to by const char * pointers.
Return 1 if *pThisCStr is greater than *Cstr, -1 if
*pThisCstr is less than *Cstr and 0 if *pThisCstr is equal
to *Cstr.
*/
int String::Cstrcmp(const char *pThisCStr, const char *Cstr )
{
int rslt;
if (pThisCStr == 0) {
if (Cstr == 0)
rslt = 0; // both this and CStr are null
else {
if (::strlen( Cstr ) == 0)
rslt = 0; // compare to an empty string ("")
else
rslt = -1; // this < Cstr
}
}
else { // pThisCStr != 0
if (Cstr == 0)
rslt = 1; // this > Cstr
else // pThis != 0 && Cstr != 0
rslt = strcmp(pThisCStr, Cstr );
}
return rslt;
} // Cstrcmp
/**
This operator allows the assignment of a String to a const char
*. For example, in the code below a is assigned to
pStr in the code below:
String a("wierd operator");
const char *pStr;
pStr = a;
The compiler fills in the cast (e.g., const char *), it
does not have to be explicit. That is we don't have to
write
pStr = (const char *)a; -- unnecessary cast
A C++ compiler should issue an error if an attempt is made
to assign a String object to a "char *". The data pointed
to by the address returned by this operator should never
be changed since it belongs to the String object.
*/
String::operator const char *() const
{
const char *pCstr = "";
size_t len = value->data.length();
if (len > 0) {
if (value->data[len-1] != '\0') {
// then add a NULL terminator
value->data.append('\0');
}
pCstr = value->data.getData();
}
return pCstr;
} // operator const char *
/**
Make "this" into an empty String. If "this" is shared,
allocate a new SharedData object. If "this" is not
shared, set the length to zero.
*/
void String::newEmptyString()
{
if (value->isShared()) {
value = new SharedData();
}
else {
value->data.set_size(0);
}
} // newEmptyString
/**
Assign a character to a String
*/
String &String::operator =(const char ch)
{
newEmptyString();
if (ch != '\0') {
value->data.append( ch );
value->data.append('\0');
}
return *this;
} // operator= (String = char)
/**
Assign a C-string to a String.
The String(C-string) constructor could be used for this.
However, using this default generates code which is more
inefficient. The write() function is used here since it
properly handles the String shared data.
If a null string is assigned to a String, the result will
be the same as if an empty String were assigned. For
example:
char *pCstr = 0;
String a = "fubar";
a = pCstr; // same as if "" were assigned
*/
String &String::operator =(const char *Cstr)
{
newEmptyString();
if (Cstr != 0) {
char ch;
for (ch = *Cstr; ch != '\0'; ch = *Cstr) {
value->data.append(ch);
Cstr++;
}
value->data.append(ch);
}
return *this;
} // operator= (String = C-string)
/**
Concatenate a C string on to the end of the String object. For
example:
String s("abcd ");
s += "efgh";
will result in "s" containing "abcd efgh".
*/
String &String::operator +=( const char *Cstr )
{
if (value->isShared()) {
newSharedData();
}
// If there is a null character, delete it so we don't
// append after the null.
size_t len = value->data.length();
if (len > 0 && value->data[len-1] == '\0')
value->data.remove();
appendCString( Cstr );
return *this;
} // operator +=
/**
Concatenate two String objects.
*/
String &String::operator +=(String &s )
{
if (s.value->data.length() > 0) {
if (value->isShared()) {
newSharedData();
}
// If there is a null character, delete it so we don't
// append after the null.
size_t len = value->data.length();
if (len > 0 && value->data[len-1] == '\0') {
value->data.remove();
}
appendString( s );
}
return *this;
} // operator +=
/*
* ======== Global binary operators ========
*
*/
/**
* Global operator +: "abcd" + String(" efgh")
*
This operator supports an operation like "abcd" + String(" efgh").
The result of this operator would be a String object containing
"abcd efgh".
*/
String operator +(const char *Cstr, String &s)
{
String tmp;
if (Cstr != 0) {
tmp = Cstr;
}
tmp += s;
return tmp;
} // global operator +
/**
Global operator +: Concatenate a character and a String object.
String Str("ab");
String a;
a = 'c' + Str
Here, "a" will contain "abc"
*/
String operator +(const char ch, String &s)
{
String tmp;
tmp = ch;
tmp += s;
return tmp;
} // global operator +
/**
Global Cstr == String operator, where Cstr is a
"C" string.
*/
bool operator ==(const char *Cstr, String &s)
{
return (s == Cstr);
} // global operator ==
/**
Global Cstr != String operator, where Cstr is a
"C" string.
*/
bool operator !=(const char *Cstr, String &s)
{
return (s != Cstr);
} // operator !=
/**
* Cstr <= s: however, we must use the
* comparision s to Cstr.
* So this expression is true
* if s >= Cstr.
*/
bool operator <=(const char *Cstr, String &s)
{
return (s >= Cstr);
} // global operator <=
/**
* Cstr >= s: however, we must use the
* comparision s to Cstr.
* So this expression is true
* if s <= Cstr.
*/
bool operator >=(const char *Cstr, String &s)
{
return (s <= Cstr);
} // global operator >=
/**
* Cstr < s: however, we must use the
* comparision s to Cstr.
* So this expression is true
* if s > Cstr.
*/
bool operator <(const char *Cstr, String &s)
{
return (s > Cstr);
} // global operator <
/**
* Cstr > s: however, we must use the
* comparision s to Cstr.
* So this expression is true
* if s < Cstr.
*/
bool operator >(const char *Cstr, String &s)
{
return (s < Cstr);
} // global operator >
string/String.h 0100600 0000764 0000764 00000026163 07733672723 012462 0 ustar iank iank
#ifndef MY_STRING_H
#define MY_STRING_H
// don't want a collision with another string macro, so I have
// not used STRING_H.
/** \file
Copyright and Use
You may use this source code without limitation and without
fee as long as you include:
This software was written and is copyrighted by Ian Kaplan, Bear
Products International, www.bearcave.com, 2001, 2002, 2003
This software is provided "as is", without any warrenty or
claim as to its usefulness. Anyone who uses this source code
uses it at their own risk. Nor is any support provided by
Ian Kaplan and Bear Products International.
Please send any bug fixes or suggested source changes to:
iank@bearcave.com
*/
#include
#include
#include "SubString.h"
#include "RCArray.h"
/**
Define a type for a growable reference counted array object
that implements copy-on-write semantics.
*/
typedef RCArray CharArray;
/**
This class is a container for strings composed of 8-bit ASCII
characters. This class uses reference counts and copy-on-write
to improve memory use and, perhaps, performance.
This is the version 3.0 of this class. This class was originally
modeled after the Rogue Wave RWCString class (using the
documentation available on the Rogue Wave web site).
Why Yet Another String Container
Although I like the Rogue Wave class and the RWCString class in
particular, I don't use the Rogue Wave classes in the software I
write for Bear Products International. There are several reasons
for this. I did not want to pay the Rogue Wave license fees, which
are high for a Cave based
software developer. Nor did I want to force anyone who licenses my
software to also Rogue Wave license fees (sorry guys). I also
wanted software that was as transparent as possible. Rogue Wave
classes tend to be incompletely documented and it is not always
easy to fully understand the way a particular class functions.
Rogue Wave is not the only alternative when it comes to reference
counted String containers. The Microsoft Foundation Classes
include a string container and some versions of the STL template
library implement the STL string container as a reference counted
object. Each of these alternatives has some draw back. Microsoft
Foundation Class code is not portable outside of Windoz. Also,
with the new .NET framework, it seems possible that Microsoft would
at some point phase out the Foundation Classes. The reference
counted versions of the STL string container that I'm aware of also
incur a license fee, although not as steep as Rogue Wave (the
Dinkumware STL library is one example of an STL implementation that
includes reference counted strings).
Finally, to quote Bjarne Staroustrup
Writing string classes has great educational value...
Chapter 20, The C++ Programming Language, third edition
Bjarne Staroustrup
The String class implements a sub-string (string section) operation
which results in a SubString object. As a result, the String
and SubString classes are closely related.
This class comes with a set of regression tests. I recommend that
you run these regression tests with your compiler, on your system.
Not all compilers implement C++ in the same way.
\author Ian Kaplan www.bearcave.com
*/
class String : public CharArray
{
// Make SubString a "friend" so it can access the protected read() and
// write functions.
friend SubString;
protected:
void init(const char *initCstr );
size_t length();
void appendCString( const char *CStr );
void appendString( String &Str );
void newEmptyString();
void newSharedData();
int Cstrcmp( const char *s1, const char *s2 );
int compareTo( String &s );
public:
String();
String( const char ch );
String( const char *initCstr );
String( const String &Str );
// sub-string operator (can be used on both lhs and rhs)
SubString operator()(const size_t start, const size_t len);
/** strlen does not include the null at the end of the string */
size_t strlen();
size_t getRefCnt();
int compareTo( const char *Cstr );
String &resize(const size_t new_size );
//
// operators
//
operator const char*() const;
String &operator =(const char ch); // String = char
String &operator =(const char *Cstr); // String = C-string
String &operator =(const SubString &sub); // String = SubString
String operator +(const char *Cstr ); // concatenate a String and a C string
String operator +(String &s ); // concatenate two Strings
String operator +(const char ch ); // concatenate a String and a char
// concatenate a String and a SubString
String operator +(const SubString &sub);
String &operator +=(const char *Cstr ); // concatenate a C string to a String
String &operator +=(String &s ); // concatenate a String to a String
String &operator +=(const char ch); // concatenate a char to a String
bool operator ==(const char *Cstr ) { return (compareTo( Cstr ) == 0); }
bool operator ==(String &s ) { return (compareTo( s ) == 0); }
bool operator !=(const char *Cstr ) { return (compareTo( Cstr ) != 0); }
bool operator !=(String &s ) { return (compareTo( s ) != 0); }
bool operator <=(const char *Cstr ) { return (compareTo( Cstr ) <= 0); }
bool operator <=(String &s ) { return (compareTo( s ) <= 0); }
bool operator >=(const char *Cstr ) { return (compareTo( Cstr ) >= 0); }
bool operator >=(String &s ) { return (compareTo( s ) >= 0); }
bool operator <(const char *Cstr ) { return (compareTo( Cstr ) < 0); }
bool operator <(String &s ) { return (compareTo( s ) < 0); }
bool operator >(const char *Cstr ) { return (compareTo( Cstr ) > 0); }
bool operator >(String &s ) { return (compareTo( s ) > 0); }
}; // String
/*
* Global operators
*
*/
String operator +(const char *Cstr, String &s);
String operator +(const char ch, String &s);
bool operator ==(const char *Cstr, String &s);
bool operator !=(const char *Cstr, String &s);
bool operator <=(const char *Cstr, String &s);
bool operator >=(const char *Cstr, String &s);
bool operator <(const char *Cstr, String &s);
bool operator >(const char *Cstr, String &s);
inline
String::String() : CharArray() {}
/** String constructor with a character argument */
inline
String::String( const char ch ) : CharArray()
{
if (ch != '\0') {
value->data.append( ch );
value->data.append('\0');
}
}
/** String constructor with a C-string argument */
inline
String::String( const char *initCstr ) : CharArray()
{
init( initCstr );
}
/**
String copy constructor
*/
inline
String::String( const String &Str ) : CharArray()
{
value = Str.value;
}
/**
Return the number of data elements (characters) consumed
by the String. This includes the null character.
So length() == strlen() + 1
*/
inline
size_t String::length()
{
return CharArray::length();
}
/**
Return the reference count. This useful primarily for debug
and verification. In the perfect world of bug free software
the reference count could be entirely hidden.
*/
inline
size_t String::getRefCnt()
{
return CharArray::getRefCnt();
}
/**
Sub-string operator: this function supports the left hand side
(l-value) form for the sub-string operation. For example:
String a("abcdefghijkl");
a(4, 5) = "12345";
The String object "a" now contains "abcd12345jkl"
This function returns a SubString object. The SubString object
supports the assignment operator, which will assign the
C-string to a
*/
inline
SubString String::operator ()(const size_t start, const size_t len)
{
SubString subStr( *this, start, len );
return subStr;
}
/**
Append a String and a SubString
String a = "abcde";
// indices: 01234567
String b = "123 5678";
String c = a + b(3,4); // result will be "abcde 567"
*/
inline
String String::operator +(const SubString &sub)
{
String section = sub;
String result = *this + section;
return result;
} // operator +
/**
Assign a SubString to a String. For example:
String a = "lazy dog";
String b = a(5, 3);
*/
inline
String &String::operator =(const SubString &sub)
{
*this = (String)sub;
return *this;
} // operator =
/**
Allocate a new SharedData object which contains a copy of the old
data. Note that this code relies on the fact that value is
a "smart pointer" which will decrement the reference count and
deallocate the space if the reference count is zero.
*/
inline
void String::newSharedData()
{
value = new SharedData( value->data );
}
/**
Compare the character string in "this" object with the
argument Cstr.
if (this < Cstr) return -1;
if (this == Cstr) return 0;
if (this > Cstr) return 1;
*/
inline
int String::compareTo( const char *Cstr )
{
const char *pThisCStr = *this;
return Cstrcmp( pThisCStr, Cstr );
}
/**
Append a character to the end of "this" string
*/
inline
String &String::operator +=(const char ch)
{
char buf[4];
buf[0] = ch;
buf[1] = '\0';
*this += buf; // call String += Cstr
return *this;
} // operator +=
/**
* operator +: String("abcd") + " efgh"
*
Create a new String object which contains the concatenation of the
two operand strings (self and the argument "s"). The reference
count for the new String object will be zero. If it is assigned
then it will be incremented.
Example:
String a("abcd");
String b = a + " efgh";
When these statements are executed String 'a' will contain "abcd"
and String 'b' will contain "abcd efgh". The reference count
for both String objects will be 1.
*/
inline
String String::operator +( const char *Cstr )
{
String tmp;
tmp.appendString( *this );
tmp.appendCString( Cstr );
return tmp;
} // operator +
/**
* operator +: String("abcd") + String(" efgh")
*
*/
inline
String String::operator +(String &s)
{
String tmp;
tmp.newSharedData();
tmp.appendString( *this );
tmp.appendString( s );
return tmp;
} // operator +
/**
* operator+ : String + ch
*
Append a String and a character.
*/
inline
String String::operator +(const char ch)
{
char buf[4];
buf[0] = ch;
buf[1] = '\0';
String tmp;
tmp = *this + buf; // call String + Cstr
return tmp;
} // operator +
/**
strlen() returns the number of characters in the string,
not including the null character which terminates the
String.
*/
inline
size_t String::strlen()
{
int len = value->data.length();
if (len > 0 && value->data[len-1] == '\0')
len--;
return len;
}
#endif
string/StringTest.C 0100600 0000764 0000764 00000071061 07733211532 013235 0 ustar iank iank
/** \file
Regression and verification code for the String class. No errors
should be printed when this code runs.
You really need to run these regression tests before using this
code on your system. This code has been tested on a variety of
platforms. Although C++ compilers are getting more standard
conformant, this the String class uses C++ features which
historically been supported differently by different compilers.
Note that the existence of these regression tests does not change
that statement this code is provided without waranty. You
use this code at your own risk.
I also recommend verifying this code using Purify or a similar
memory usage verification tool. This level of pain and difficulty
should not be necessary, but hey, its C++.
In an earlier version of the String class the SubString class was
included in the same .h file, with some function implementations
in String.C.
This version separates the two classes. A set of regression tests
(SubStrTest.C) was written for the SubString class. However, for
historical reasons, this file also include some tests for
SubString, which overlap with the tests in SubStrTest.C.
Compiling this code:
This code is written to compile on Microsoft Visual C++ 6.0 and
higher. It will also compile on GNU C++ and Sun's 6.0 and later
C++ compilers. To compile under Microsoft VC++:
cl -Zi -TP -GX SubString.C String.C StringTest.C -o StringTest
The -GX flag is needed to support exceptions, which are used
rather than the assert() functions in the earlier versions.
Copyright and Use
You may use this source code without limitation and without
fee as long as you include:
This software was written and is copyrighted by Ian Kaplan, Bear
Products International, www.bearcave.com, 2001.
This software is provided "as is", without any warrenty or
claim as to its usefulness. Anyone who uses this source code
uses it at their own risk. Nor is any support provided by
Ian Kaplan and Bear Products International.
Please send any bug fixes or suggested source changes to:
iank@bearcave.com
*/
#include
#include
#include "String.h"
/**
* checkRefCntVal: pass by value, where reference count gets incremented
by one on entry and decremented by one on exit.
*/
void checkRefCntVal( String s, size_t shouldBe, const char *prefix )
{
if (s.getRefCnt() != shouldBe)
printf("%s refCnt = %d (should be %d)\n",
prefix, s.getRefCnt(), shouldBe );
} // checkRefCntVal
/**
* checkRefCnt: pass by reference. The reference count in the String
object is unchanged, since the object is passed by reference.
*/
void checkRefCnt( String &s, size_t shouldBe, const char *prefix )
{
if (s.getRefCnt() != shouldBe)
printf("%s refCnt = %d (should be %d)\n",
prefix, s.getRefCnt(), shouldBe );
} // checkRefCnt
/**
* test_constructors
*/
void test_constructors()
{
String a( "abcd" );
String b( a );
printf("Test String constructors\n");
if (b.getRefCnt() != 2)
printf("1. Reference count is wrong: refCnt = %d (should be 2)\n",
b.getRefCnt() );
String c = a;
if (b.getRefCnt() != 3)
printf("2. Reference count is wrong: refCnt = %d (should be 3)\n",
b.getRefCnt() );
if (a.getRefCnt() != 3)
printf("3. Reference count is wrong: refCnt = %d (should be 3)\n",
b.getRefCnt() );
checkRefCntVal( a, 4, "4. ");
if (a.getRefCnt() != 3)
printf("4. Reference count is wrong: refCnt = %d (should be 3)\n",
b.getRefCnt() );
checkRefCnt( a, 3, "5. ");
String d( 'd' );
// test for construction with an empty string
String e;
String f( e );
checkRefCnt( f, 2, "6. ");
checkRefCntVal( f, 3, "7. ");
// test of a pointer containing null
const char *nullPtr = 0;
String g( nullPtr );
// test of a null address
String h( (const char *)0 );
String i( '\0' );
if (i.strlen() != 0) {
printf("8. Adding a null character should not increase length\n");
}
} // test_constructors
/**
* test_char_cast
*/
void test_char_cast()
{
printf("Test character cast\n");
const char *testStr = "the quick brown fox";
String a( testStr );
const char *tmp = a;
if (tmp == 0)
printf("1. error: result of cast is null\n");
else if (strcmp(testStr, tmp) != 0)
printf("2. error: strings are not equal\n");
} // test_char_cast
/*
* test_assign
*
The privious version of the String class created a new copy when
ever the index operator ([]) was used. I was surprised to
discover that this was the case even with the index operator
was on the right hand size. This function includes a test
which checks that copy on read does not take place in this
version.
*/
void test_assign()
{
printf("Test assignment\n");
const char *testStr = "my girl is the best";
String a = "abcd";
const char *tmp = a;
if (strcmp(tmp, "abcd") != 0)
printf("1. Assignment in declaration failed\n");
const char *init_b = "this is not it";
String b(init_b);
String original_b;
original_b = b;
b = testStr;
if (b.getRefCnt() != 1)
printf("2. reference count for b is wrong\n");
tmp = b;
if (strcmp(tmp, testStr) != 0)
printf("3. String has incorrect contents\n");
if (original_b.getRefCnt() != 1)
printf("4. reference count for original_b is wrong\n");
if (original_b != init_b)
printf("5. modification of b improperly changed original_b\n");
String c( testStr );
c = b;
if (b.getRefCnt() != 2)
printf("6. reference count is wrong\n");
const char *nullPtr = 0;
String d;
if (d != "") {
printf("7. comparision to a null string failed\n");
}
d = nullPtr;
if (d != "") {
printf("8. assignment of a null C-string failed\n");
}
d = testStr;
tmp = d;
if (strcmp(tmp, testStr) != 0)
printf("9. String has incorrect contents\n");
String e = String( testStr );
tmp = e;
if (strcmp(tmp, testStr) != 0)
printf("10. String has incorrect contents\n");
if (e.getRefCnt() != 1)
printf("11. refCnt is wrong: refCnt = %d, should be 1\n",
e.getRefCnt() );
const char *constCStr = "1234567890";
const size_t len = sizeof(constCStr) / sizeof(char);
String foo = constCStr;
String bar = foo;
if (foo.getRefCnt() != 2) {
printf("12. refcnt is wrong: refCnt = %d, should be 2\n",
foo.getRefCnt() );
}
// This makes sure that the [] operator is implemented properly
// and does not cause a "copy-on-write" on a read operation.
bool contentOK = true;
for (size_t i = 0; i < len; i++) {
if (constCStr[i] != foo[i]) {
contentOK = false;
break;
}
}
if (!contentOK) {
printf("13: content is wrong\n");
}
// make sure refCnt is still OK
if (bar.getRefCnt() != 2) {
printf("14. refcnt is wrong: refCnt = %d, should be 2\n",
bar.getRefCnt() );
}
const char *testStr2 = "null is a lonely number";
String r = testStr2;
String r2 = r;
r = '\0';
if (r != "") {
printf("15. assignment of null character did not result in empty str\n");
}
if (r2 != testStr2) {
printf("16. null character assignment changed a shared string\n");
}
if (r2.getRefCnt() != 1) {
printf("17. reference count is wrong\n");
}
const char *testStr3 = "\"Writing tests is hard!\" said Barbie";
String s = testStr3;
String s2 = s;
s = "";
if (s != "") {
printf("18. assignment of empty string did not result in empty str\n");
}
if (s2 != testStr3) {
printf("19. empty string assignment changed a shared string\n");
}
if (s2.getRefCnt() != 1) {
printf("17. reference count is wrong\n");
}
// Test chained assignment
const char *testStr4 = "working on the chain gang";
String w, x, y;
w = x = y = testStr4;
if (w != testStr4 ||
x != testStr4 ||
y != testStr4) {
printf("18. chained assignment failed\n");
}
if (y.getRefCnt() != 3) {
printf("19. reference count in chained assignment is wrong\n");
}
String z = "still working on the gang";
w = x = y = z;
if (w != x &&
x != y &&
y != z) {
printf("20. chained assignment failed\n");
}
if (y.getRefCnt() != 4) {
printf("21. reference count in chained assignment is wrong\n");
}
} // test_assign
/**
* test_plus_equal
*/
void test_plus_equal()
{
const char *firstHalf = "abcd";
const char *secondHalf = " efgh";
const char *concatStr = "abcd efgh";
printf("Test += operator\n");
String a( firstHalf );
a += secondHalf;
const char *tmp = a;
if (strcmp(tmp, concatStr) != 0)
printf("1. Strings did not match: str = %s (should be [%s]\n",
tmp, concatStr );
String b;
b += firstHalf;
tmp = b;
if (strcmp(tmp, firstHalf) != 0)
printf("2. Strings did not match: str = %s (should be [%s]\n",
tmp, firstHalf );
String d, c;
c += d;
if (c.getRefCnt() != 1)
printf("3. refCnt should (still) be 1\n");
if (d != "" || c != "") {
printf("4. Strings c and d should be the empty string, but are not\n");
}
c += secondHalf;
tmp = c;
if (strcmp(tmp, secondHalf) != 0)
printf("5. Strings did not match: str = %s (should be [%s]\n",
tmp, secondHalf );
String e("1234");
for (size_t i = 5; i < 10; i++) {
e += (char)(i + (char)'0');
}
if (e != "123456789") {
tmp = e;
printf("6. Character concat failed: d = %s, should be 123456789\n",
tmp );
}
const char *testStr1 = "metal jacket";
String empty;
String full(testStr1);
empty += full;
if (empty.getRefCnt() != 1) {
printf("7. empty.getRefCnt() = %d, should be 1\n", empty.getRefCnt() );
}
if (empty != testStr1) {
printf("8. empty string += String failed\n");
}
const char *testStr2 = "foo";
String empty2;
const char *str = testStr2;
empty2 += str;
if (empty2.getRefCnt() != 1) {
printf("9. empty2.getRefCnt() = %d, should be 1\n", empty2.getRefCnt() );
}
if (empty2 != testStr2) {
printf("10. empty string += C-string failed\n");
}
// test chained assignment
const char *testStr3 = "twas brillig";
String s1 = "twas ";
String s2 = "brillig";
String s3 = s1 += s2;
if (s3 != s1 && s1 != testStr3) {
printf("11. chained assignment with += failed\n");
}
if (s3.getRefCnt() != 2 &&
s1.getRefCnt() != 2 &&
s2.getRefCnt() != 1) {
printf("12. reference count for chained assignment with += failed\n");
}
} // test_plus_equal
/**
* test_plus
*/
void test_plus()
{
printf("Test + operator\n");
const char *firstHalf = "abcd";
const char *secondHalf = " efgh";
const char *concatStr = "abcd efgh";
//
// Test String + String
//
String t1( firstHalf );
String t2( secondHalf );
String a = t1 + t2;
if (a.getRefCnt() != 1) {
printf("1. refCnt is wrong: refCnt = %d, should be 1\n",
a.getRefCnt() );
}
if (strcmp((const char *)a, concatStr) != 0)
printf("2. String contents are not correct: a = %s (should be [%s])\n",
(const char *)a, concatStr );
//
// Test String + const char *
//
String b = t1 + secondHalf;
if (b.getRefCnt() != 1) {
printf("3. refCnt is wrong: refCnt = %d, should be 1\n",
b.getRefCnt() );
}
const char *tmp = b;
if (strcmp(tmp, concatStr) != 0)
printf("4. String contents are not correct: b = %s (should be [%s])\n",
tmp, concatStr );
//
// test the global String addition operator const char * + String
//
String c = firstHalf + t2;
tmp = c;
if (strcmp(tmp, concatStr) != 0)
printf("5. String contents are not correct: c = %s (should be [%s])\n",
tmp, concatStr );
//
// Make sure that the operands of the addition are not altered by
// the addition
//
String first( firstHalf );
String second( secondHalf );
String d = first + second;
tmp = first;
if (strcmp(tmp, firstHalf) != 0)
printf("6. first has been altered: first = %s (should be [%s])\n",
tmp, firstHalf );
tmp = second;
if (strcmp(tmp, secondHalf) != 0)
printf("7. second has been altered: second = %s (should be [%s])\n",
tmp, secondHalf );
tmp = d;
if (strcmp(tmp, concatStr) != 0)
printf("8. String contents are not correct: d = %s (should be [%s])\n",
tmp, concatStr );
//
// Test character concatenation. Here the character operands
// are converted to String objects.
//
String e("12345");
String f;
String g;
f = e + '6' + '7' + '8' + '9';
g = 'a' + f;
if (e != "12345")
printf("9. String e changed\n");
if (f != "123456789") {
const char *tmp = f;
printf("10. String f is %s, it should be \"123456789\"\n", tmp);
}
if (g != "a123456789")
printf("11. g is incorrect\n");
// Test addition with chained assignment and string operands
String w( "foo" );
String x( "bar" );
String y;
String z = y = w + x;
if (y != "foobar") {
const char *tmp = y;
printf("12. String y is %s, it should be \"foobar\"\n", tmp );
}
if (z != "foobar") {
const char *tmp = z;
printf("13. String z is %s, it should be \"foobar\"\n", tmp );
}
// The reference counts for w and x should both be 1
if (w.getRefCnt() != 1) {
printf("14. w.getRefCnt() = %d, it should be 1\n", w.getRefCnt() );
}
if (x.getRefCnt() != 1) {
printf("15. x.getRefCnt() = %d, it should be 1\n", x.getRefCnt() );
}
if (y.getRefCnt() != 2) {
printf("16. y.getRefCnt() = %d, it should be 2\n", y.getRefCnt() );
}
if (z.getRefCnt() != 2) {
printf("17. z.getRefCnt() = %d, it should be 2\n", z.getRefCnt() );
}
} // test_plus
/**
* test_relops
*
Test relational operators
*/
void test_relops()
{
printf("Test relational operators\n");
const char *less = "abcd";
const char *greater = "wxyz";
const char *equal = "abcd";
String lessString( less );
String greaterString( greater );
String equalString( equal ); // note that equalString == lessString == less
String same;
same = less;
//
// ==
//
// String String
//
// Check the case where both strings contain no data
// (and so are equal).
String x, y; // two empty strings
if (x != y)
printf("0. empty strings are not equal\n");
if (x != "") {
printf("0.5: String not equal to empty C string\n");
}
if (! (lessString == equalString))
printf("1. String == String failed\n");
// String const char *
if (! (lessString == equal))
printf("2. String == const char * failed\n");
// const char * String
if (! (equal == lessString))
printf("3. const char * == String failed\n");
if (! (same == less))
printf("String == String failed for String objs w/same shared data\n");
//
// !=
//
// String String
if (! (lessString != greaterString))
printf("4. String != String failed\n");
// String const char *
if (! (lessString != greater))
printf("5. String != const char * failed\n");
// const char * String
if (! (less != greaterString))
printf("6. const char * != String failed\n");
//
// >=
//
// String String
if (! (greaterString >= lessString))
printf("7. String >= String failed for >\n");
if (! (lessString >= equalString))
printf("8. String >= String failed for ==\n");
// String const char *
if (! (greaterString >= less))
printf("9. String >= const char * failed for >\n");
if (! (lessString >= equal))
printf("10. String >= const char * failed ==\n");
// const char * String
if (! (greater >= lessString))
printf("11. const char * >= String failed for >\n");
if (! (equal >= lessString))
printf("12. const char * >= String failed for ==\n");
//
// <=
//
// String String
if (! (lessString <= greaterString))
printf("13. String <= String failed for <\n");
if (! (lessString <= equalString))
printf("14. String <= String failed for ==\n");
// String const char *
if (! (lessString <= greater))
printf("15. String <= const char * failed for <\n");
if (! (lessString <= equal))
printf("16. String <= const char * failed for ==\n");
// const char * String
if (! (less <= greaterString))
printf("17. const char * <= String failed for <\n");
if (! (equal <= lessString))
printf("18. const char * <= String failed for ==\n");
//
// >
//
// String String
if (! (greaterString > lessString))
printf("19. String > String failed\n");
// String const char *
if (! (greaterString > less))
printf("20. String > const char * failed\n");
// const char * String
if (! (greater > lessString))
printf("21. const char * > String failed\n");
//
// <
//
// String String
if (! (lessString < greaterString))
printf("22. String < String failed\n");
// String const char *
if (! (lessString < greater))
printf("23. String < const char * failed\n");
// const char * String
if (! (less < greaterString))
printf("24. const char * < String failed\n");
} // test_relops
/**
* Array reference [] operator tests
*
There are two versions: left hand size and right
hand size array operators. The right hand side
version returns a value, the left hand size
operator returns an address (to which a value
can be assigned).
*/
void test_arrayop()
{
const char *jabbar = "He took his vorpal sword in hand";
const char *newJabbar1 = "He took her vorpal sword in hand";
// index 9 -----------------^
const char *newJabbar2 = "He took her vorpal ruler in hand";
// index 19 ----------------------------^
const char *newWord = "ruler";
const size_t len = strlen( jabbar );
String jabbarString( jabbar );
String jabbarRefStr = jabbarString;
//
// Make sure that integer operators on the RHS work properly
//
if (jabbarString[3] != 't') {
printf("0. string index failed\n");
}
// make sure than in index operation does not cause a copy
if (jabbarRefStr.getRefCnt() != 2) {
printf("0.5. string index seems to have caused a copy\n");
}
for (size_t i = 0; i < len; i++) {
char lhsCh = jabbarString[i];
char rhsCh = jabbar[i];
if (lhsCh != rhsCh) {
printf("1. mismatch on rhs String index\n");
}
}
// references are jabbarString, jabbarRefStr and now, "a"
String a = jabbarString;
if (a.getRefCnt() != 3)
printf("2. reference count is wrong\n");
a[9] = 'e';
a[10] = 'r';
// The string has been changed, so a "copy on write" should
// have taken place. Now there is a single copy, with the
// change.
if (a.getRefCnt() != 1)
printf("3. 'a' reference count is wrong\n");
if (jabbarString.getRefCnt() != 2)
printf("4. jabbarString reference count is wrong\n");
const char *tmp = a;
if (strcmp(tmp, newJabbar1) != 0) {
printf("5. strings don't match: a = %s, should be %s\n",
tmp, newJabbar1 );
}
// make sure that the original string is unchanged
tmp = jabbarString;
if (strcmp(tmp, jabbar) != 0) {
printf("6. strings don't match: a = %s, should be %s\n",
tmp, jabbar );
}
} // test_arrayop
/**
* test_insert()
*
In an earlier version of the String object used an explicit insert
function to insert a string into another string. This is very much
like assigning to a SubString. In the case of an insert the
length of the section being replaced is 0.
This code attempts to not only make sure that insert works, but
also that insert works for various corner cases (e.g., insert of an
empty string or a null C string).
*/
void test_insert()
{
printf("Test string insert\n");
const char *origA = "abcdefgh";
const char *rslt1 = "abcd1234efgh";
String a(origA);
String b("1234");
String c = a;
// test insert of a String into a String
// reference count should be 2, since a and c have the
// same shared data
if (a.getRefCnt() != 2)
printf("1. reference count is wrong\n");
a(4,0) = b;
// make sure a is "abcd1234efgh"
if (a != rslt1) {
printf("2. insert failed. a = [%s], should be [%s]\n",
(const char *)a, rslt1 );
}
// a should be unique
if (a.getRefCnt() != 1)
printf("3. reference count in a is wrong\n");
// c should be unique
if (c.getRefCnt() != 1)
printf("4. reference count in c is wrong\n");
// The contents of c should be unchanged
if (c != origA)
printf("5. contents of c is wrong\n");
// test insert of C-string into a String
String d = c;
d(4,0) = "1234";
// make sure d is "abcd1234efgh"
if (d != rslt1) {
printf("6. insert failed. d = [%s], should be [%s]\n",
(const char *)d, rslt1 );
}
String c_prime = c; // reference count is 2
// test insert of a zero length C string into a String
// (remember, c is still "abcdefgh")
c(4, 0) = "";
// make sure c is "abcdefgh" (e.g., nothing happened)
if (c != origA) {
printf("7. insert failed. c = [%s], should be [%s]\n",
(const char *)c, origA );
}
// Insert a null string into a String object (should do nothing)
c_prime(4, 0) = (const char *)0;
// make sure that reference count is still 2
if (c.getRefCnt() != 2)
printf("8. c.getRefCnt() = %d, should be 2\n", c.getRefCnt());
if (c_prime.getRefCnt() != 2)
printf("9. c_prime.getRefCnt() = %d, should be 2\n", c_prime.getRefCnt());
if (c_prime != origA) {
printf("10. insert failed. c_prime = [%s], should be [%s]\n",
(const char *)c_prime, origA );
}
// test insert of an empty String into "c", an non-empty string.
String emptyString;
// try insert at index 0
c(0,0) = emptyString;
// make sure c is "abcdefgh" (e.g., nothing happened)
if (c != origA) {
printf("11. insert failed. c = [%s], should be [%s]\n",
(const char *)c, origA );
}
// try insert at index 4
c(4,0) = emptyString;
// make sure c is "abcdefgh" (e.g., nothing happened)
if (c != origA) {
printf("12. insert failed. c = [%s], should be [%s]\n",
(const char *)c, origA );
}
} // test_insert
/**
*/
void test_substr_func_valarg( SubString sub, size_t errorNum )
{
if (sub.getRefCnt() != 1) {
printf("%d. sub.getRefCnt() = %d, should be 1\n",
errorNum, sub.getRefCnt() );
}
if (sub != "de") {
printf("%d. sub.string = %s, should be \"de\"\n",
errorNum + 1, (const char *)((String)sub) );
}
} // test_substr_func_valarg
/**
* test_substring
*
Test operations on SubStrings via the String() operator.
These test overlap the SubString class tests. They
are here for historical reasons.
*/
void test_substring()
{
printf("Test sub-string operations\n");
const char *init_a = "abcdefgh";
String a(init_a);
if (a(3, 4).getRefCnt() != 1) {
printf("1. reference count is wrong\n");
}
test_substr_func_valarg( a(3, 2 ), 2 );
String b = a;
// b is "abcdefgh
// insert "1234" at position 3
b(3, 4) = "1234";
if (a != init_a) {
printf("3. \"a\" was altered when b(3, 4) was changed\n");
}
if (b != "abc1234h") {
printf("4. b = %s, should be \"abc1234h\"\n", (const char *)b);
}
if (a.getRefCnt() != 1 || b.getRefCnt() != 1) {
printf("5. a.getRefCnt() = %d, b.getRefCnt() = %d (both should be 1)\n",
a.getRefCnt(), b.getRefCnt() );
}
String c;
c = b(3, 4);
if (c != "1234") {
printf("6. c = %s, should be \"1234\"\n", (const char *)c);
}
if (c.strlen() != 4) {
printf("7. c.strlen() = %d, should be 4\n", c.strlen());
}
String d("1234abcdefgh");
String e;
e = d(4, 8) + d(0, 4); // e = d << 4
if (e != "abcdefgh1234") {
printf("8. e = %s, should be \"abcdefgh1234\"\n", (const char *)e );
}
if (d != "1234abcdefgh") {
printf("9. d was changed by the SubString operations\n");
}
if (d.getRefCnt() != 1) {
printf("10. d.getRefCnt() = %d, it should be 1\n", d.getRefCnt());
}
//
// According to 10.4.10 Temporary Objects in "The C++ Programming
// Language", Third Edition, by Stroustrup:
//
// Unless bound to a reference or used to initialize a named
// object, a temporary object is destroyed at the end of teh full
// expression in which it was created. A full expression is an
// expression that is not a subexpression of some other
// expression.
//
// Stroustrup goes on to provide an example using the Standard
// Template Library (STL) "string" class. Here the c_str()
// function returns a "C" language string.
//
// const char* cs = (s1 + s2).c_str().
//
// A temporary object of class string is created to hold s1+s2.
// Next, a pointer to a C-style string is extracted from the
// object. Then - at the end of the expression - the temporary
// object is deleted. Now, where was the C-style string
// allocated? Probably as part of the temporary object holding
// s1+s2, and that storage is not guaranteed to exist after that
// temporary is destroyted. Consequently, cs points to
// deallocated storage.
//
// In the case of the String container, the expression
//
// e = d(4, 8) + d(0, 4);
//
// creates a String object temporary, which is assigned to "e"
//
//
// = d(4, 8) + d(0, 4);
// e = Note: at this point getRefCnt = 2
// Note: refCnt decremented
// next statement
//
// By the time we reach "next statement" the destructor is called
// and the reference counter for "e" is 1, which is what we would
// expect.
//
// If this example does not convience you that C++ is a complicated
// language, nothing will.
//
if (e.getRefCnt() != 1) {
printf("11. e.getRefCnt() = %d, it should be 1\n", e.getRefCnt());
}
//
// Note that the SubString object created by the () operator is
// a new object and so has a reference count of 1, although the
// String associated with it has a reference count of two.
String z = "lost Z-man";
String y = z; // refCnt is now 2
if (z(5, 5).getRefCnt() != 1) {
printf("12. z(8, 4).getRefCnt() = %d, should be 1\n",
z(8, 4).getRefCnt() );
}
String f("chopsock");
f(4, 4) = f(0, 4);
if (f != "chopchop") {
printf("13. f = %s, should be \"chopchop\"\n", (const char *)f );
}
} // test_substring
/**
* test_resize
*
Test the String object resize function.
*/
void test_resize()
{
const char *init_a = "01234567890123456789";
String a( init_a );
String b = a;
const char *tmp;
printf("Test resize\n");
b.resize(10); // set size of String b to 10
if (b.strlen() != 10)
printf("1. b.strlen() = %d, should be 10\n", b.strlen() );
if (b != "0123456789") {
tmp = b;
printf("2. b = %s, should be \"0123456789\"\n", tmp );
}
if (a != init_a)
printf("3. a was improperly modified by resizing b\n");
if (a.strlen() != 20)
printf("4. a.strlen() = %d, should be 20\n", a.strlen() );
b.resize(20);
if (b != "0123456789 ") {
tmp = b;
printf("5. b = %s, should be \"0123456789 \"\n", tmp );
}
if (b.strlen() != 20)
printf("6. b.strlen() = %d, should be 20\n", b.strlen() );
if (a != init_a)
printf("8. resizing b modified a\n");
b.resize( 0 );
String empty;
if (b != empty)
printf("9. b is not the same as the empty string\n");
if (a != init_a)
printf("10. resizing b modified a\n");
if (b.strlen() != 0)
printf("11. b.strlen() = %d, should be 0\n", b.strlen() );
if (b != "") {
printf("12. b should be the same as the empty string\n");
}
} // test_resize
/**
*/
main()
{
test_constructors();
test_char_cast();
test_assign();
test_plus_equal();
test_plus();
test_relops();
test_arrayop();
test_insert();
test_substring();
test_resize();
return 0;
}
string/SubString.C 0100600 0000764 0000764 00000037123 07733671250 013057 0 ustar iank iank /** \file
Copyright and Use
You may use this source code without limitation and without
fee as long as you include:
This software was written and is copyrighted by Ian Kaplan, Bear
Products International, www.bearcave.com, 2001, 2002, 2003.
This software is provided "as is", without any warrenty or
claim as to its usefulness. Anyone who uses this source code
uses it at their own risk. Nor is any support provided by
Ian Kaplan and Bear Products International.
Please send any bug fixes or suggested source changes to:
iank@bearcave.com
*/
/** POSIX string operations */
#include
#include "String.h"
#include "SubString.h"
/**
User defined type conversion between SubString and String.
For example
String a("abcdefg");
String b = a(0,4);
The string() function sections the SubString, resulting in a String.
*/
SubString::operator String() const
{
return this->string();
}
/**
Check that start and len are within the bounds of the String
argument s.
*/
void SubString::rangeCheck(String &s, const size_t start, const size_t len)
throw(std::out_of_range)
{
char *errMsg = 0;
bool rangeError = false;
size_t sLen = s.strlen();
if (start >= sLen) {
errMsg = "SubString: start >= String length";
rangeError = true;
}
else if (start + len > sLen) {
errMsg = "SubString: start + len >= String length";
rangeError = true;
}
if (rangeError) {
throw std::out_of_range( errMsg );
}
} // rangeCheck
/**
This constructor initializes the SubString object with a
reference to a String object, a start index for the SubString
section and a length. For example:
String s = "max is a cat";
SubString t(s, 9, 3);
Except for initialization, nothing happens in the SubString
constructor. It is only when a SubString is assigned or otherwise
operated on that a new String is created (the region of the
initialization String defined by the start and length).
SubStrings are commonly created by the String class. When creating
a SubString object the String class initializes the SubString with
itself, and with the starting index and length. The code below
shows an example of an sub-string expression created by the String
class.
String a("abcd12345jkl");
String b = a(4, 5);
The first argument is the starting index (where Strings start
with index 0). The second argument is the length.
This class throws the std::out_of_range error when either the start
or (start+len) are out of range (beyond the end of the String).
Exceptions are described in The C++ Programming Language, Third
Edition by Stroustrup, section 14.10.
One of the corner cases that must be considered is a SubString
of an empty String:
String empty; // empty string
String empty2 = empty(0, 0); // This will throw an exception
Ok, so we're taking a zero length section of a String starting at
zero. Is that so wrong? I don't know about so wrong, but
it does seem incorrect. The rules with with a SubString is that
the starting index must be less than the String length. In this
case the starting index is equal to the String length. As a result
this example would throw an exception.
A SubString can be thought of as copy on demand. The SubString
expression will not actually section the String until the
SubString is assigned, either explicitly or implicitly.
The SubString will not alter the reference count of the String
that it references.
*/
SubString::SubString( String &s, const size_t start, const size_t len )
throw(std::out_of_range)
: value( new SharedData() )
{
try {
rangeCheck(s, start, len);
size_t sLen = s.strlen();
value->pStr( &s );
value->start( start );
value->subStrLen(len);
}
catch (std::out_of_range e) {
throw e;
}
} // SubString constructor
/**
A SubString defines a region in a String. When an assignment is
made to a SubString and the length of the data being assigned is
not the same as the SubString length, a copy space must be
created.
In the case where the length of the data being copied is larger
than the SubString length, the area defined by the SubString must
be made larger. This extends the length of the string and moves
characters above the region to higher indices.
In the case where the length of the data being copied is less
than the SubString length, the area defined by the SubString
must be reduced. This reduces the length of the String and
results in moving characters above the copy region to lower
indices.
This function allows assignment code to simply copy data into
the assignment region. Note that both the write() and resize()
functions will create an unshared String if the String is shared.
*/
void SubString::createCopySpace(size_t start, size_t charsToCopy)
{
size_t subStrLen = value->subStrLen();
if (charsToCopy != subStrLen) {
size_t strlen = value->pStr()->strlen();
if (charsToCopy < subStrLen) {
// The end of the copy region will be start+charsToCopy
// The end of the SubString section is start+subStrLen
size_t dest = start+charsToCopy;
for (size_t i = start+subStrLen; i < strlen; i++, dest++) {
char ch = value->pStr()->read(i);
value->pStr()->write(dest, ch );
}
size_t shrink = subStrLen - charsToCopy;
size_t newLen = strlen - shrink;
value->pStr()->resize( newLen );
}
else if (charsToCopy > subStrLen) {
size_t expand = charsToCopy - subStrLen;
size_t newLen = strlen + expand;
value->pStr()->resize( newLen );
size_t dest = newLen-1;
for (int i = strlen-1; i >= (int)start; i--, dest--) {
char ch = value->pStr()->read(i);
value->pStr()->write(dest, ch );
}
}
}
} // createCopySpace
/**
String = SubString + Cstr
*/
String SubString::operator +(const char *Cstr)
{
String result = *this;
if (Cstr != 0) {
result += Cstr;
}
return result;
}
/**
String = SubString + SubString
*/
String SubString::operator +(const SubString& sub)
{
String s1 = *this;
String s2 = sub;
String result = s1 + s2;
return result;
}
/**
String = SubString + String
*/
String SubString::operator +(const String &str)
{
String s1 = *this;
String result = s1 + str;
return result;
}
/**
operator =: This function assigns a C character string to a
SubString. For example:
String foo("abcdefghijkl");
String bar = foo;
foo(4, 4) = "1234";
After the SubString assignment is executed, the String "foo" will
contain "abcd1234ijkl". Note that the contents of bar will
be unchanged. However, the reference count of bar will be
1.
A smaller string can be assigned to a larger string:
foo(4, 4) = "12";
will result in "abcd12ijkl" (note that in this case a length
4 string ("efgh") was replaced by a length 2 string ("12"))
The symetric case, where a longer string is assigned to a
smaller string:
foo(4, 4) = "123456
Results in a longer String, as the length 4 string is replaced by
a length 6 string: "abcd123456ijkl".
Assignment to a SubString results in a String. The String is the
String that was used to initialize the SubString constructor,
modified by the assignment. The assignment
foo(4, 4) = "1234";
can be broken up into two steps:
There are three cases that are dealt with in this assignment:
-
The length of the Cstr is equal to the length of the SubString
section. In this case the Cstr can simply be copied into the
String.
-
The length of the Cstr is less than the length of the SubString
section. In this case the String is collapsed before the assignment.
-
The length of the Cstr is greater than the length of the SubString
section. In this case the String is expanded before the assignment.
As a result of the limitations imposed by the SubString
constructor, a SubString will always be initialized with a String
and the start and length will always be within bounds of that
String.
*/
String SubString::operator =( const char *Cstr )
{
size_t charsToCopy;
if (Cstr && (charsToCopy = ::strlen( Cstr)) > 0) {
size_t start = value->start();
// create a copy region that is the correct size
createCopySpace( start, charsToCopy );
size_t cnt = 0;
for (size_t i = start; i < start + charsToCopy; i++, cnt++) {
value->pStr()->write(i, Cstr[ cnt ] );
}
}
//
// C/C++ allows chained assignment, so one can do something like
//
// String a = b(2,4) = "abcd";
//
// So the string that results from the substring assignment should
// be returned.
//
String retStr( *(value->pStr()) );
return retStr;
} // operator = (Cstr)
/**
operator =: Assign a String to a SubString. For example:
String a("the quick brown fox jumped");
String b("dog");
a(16, 3) = b;
This function does the same thing as the C-string version
of operator=. Please see the comment associated with this
function.
*/
String SubString::operator =( String& str )
{
size_t charsToCopy;
if ((charsToCopy = str.strlen()) > 0) {
size_t start = value->start();
// create a copy region that is the correct size
createCopySpace(start, charsToCopy );
if (value->pStr()) {
size_t i, cnt;
for (i = start, cnt = 0; i < start + charsToCopy; i++,cnt++) {
value->pStr()->write(i, str[cnt]);
}
}
}
if (value->pStr())
return *(value->pStr());
else {
String empty;
return empty;
}
} // operator = (String)
/**
* operator = (SubStr &)
*
For example:
a(0, 4) = b(4, 4);
*/
String SubString::operator =(const SubString& str )
{
// convert to a SubStr = String
*this = str.string();
return (*this).string();
} // operator=
/**
Return the String section defined by the SubString as a
String result.
A SubString object consists of a String and a range (e.g., start
index and length) which defines a string section. In a sense, a
SubString object is intent, not action. The string() function
performs the action by sectioning the SubString into a String.
No bounds check is made here (for example, to assure that
subStrEnd is less than sLen) because this check is made
when the SubString is constructed.
*/
String SubString::string() const
{
String retVal;
if (value->pStr()) {
size_t sLen = value->pStr()->strlen();
size_t start = value->start();
size_t subStrLen = value->subStrLen();
size_t subStrEnd = start + subStrLen;
for (size_t i = start; i < subStrEnd; i++) {
retVal += (*(value->pStr()))[i];
}
}
return retVal;
} // SubString::string
/**
Compare the SubString to a C string (e.g., const char *). The function
returns:
-
result == 0 when SubString is equal to the CString
-
result > 0 when the SubString is greater than the CString
-
result < 0 when the Substring is less than the Cstring
Notes:
value->pStr() returns a pointer to the String which was used
to initialize the SubString. This String pointer is stored
as part of the shared data. Since compareTo is called when
a SubString operation is being applied, we know that pStr()
will is not NULL. However, it IS possible that the string
is empty. In this case the String "const char *" operator
will return null.
If the strings are not the same length, the compareTo function
acts as if the shorter string is extended with null characters
until both strings are the same length.
The compareTo function does not have to check the
range of the SubString, since it was checked by the SubString
constructor.
*/
int SubString::compareTo( const char *CStr )
{
int result;
const char *pThisCStr = *(value->pStr());
if (pThisCStr == 0 && CStr == 0) {
result = 0;
} else if (pThisCStr == 0 && CStr != 0) {
result = -1;
} else if (pThisCStr != 0 && CStr == 0) {
result = 1;
} else { // both pThisCStr and CStr are non-NULL
size_t subStrLen = value->subStrLen();
size_t start = value->start();
result = strncmp((pThisCStr + start), CStr, subStrLen);
if (result == 0) {
size_t CStrLen = strlen( CStr );
if (CStrLen > subStrLen) {
result = -1; // ThisCStr < CStr
}
}
}
return result;
} // compareTo for const char *
/**
Compare the SubString to a String. The function
returns:
-
result == 0 when SubString is equal to the CString
-
result > 0 when the SubString is greater than the CString
-
result < 0 when the Substring is less than the Cstring
*/
int SubString::compareTo( String &s )
{
const char *pCStr = s;
return compareTo( pCStr );
} // compareTo for String
/**
Compare two SubStrings.
As noted above, we know that the String is initialized in each
case, although the Strings might still be null. We also
know that the start and length are in range, since this is
checked in the constructor.
*/
int SubString::compareTo( SubString &subStr)
{
int result;
const char *pThisCStr = *(value->pStr());
const char *pRhsCStr = *(subStr.value->pStr());
// The Strings in both SubStrings are NULL
if (pThisCStr == 0 && pRhsCStr == 0) {
result = 0;
}
else if (pThisCStr != 0 && pRhsCStr == 0) {
result = 1;
}
else if (pThisCStr == 0 && pRhsCStr != 0) {
result = -1;
}
else {
size_t thisLen = value->subStrLen();
size_t rhsLen = subStr.value->subStrLen();
size_t compareLen = thisLen;
if (compareLen > rhsLen) {
compareLen = rhsLen;
}
size_t thisStart = value->start();
size_t rhsStart = subStr.value->start();
result = strncmp( pThisCStr+thisStart,
pRhsCStr+rhsStart,
compareLen );
if (result == 0) {
if (thisLen < rhsLen) {
result = -1;
}
else if (thisLen > rhsLen) {
result = 1;
}
}
}
return result;
} // compareTo for SubString
//
// Global Operators
//
bool operator ==(const char *Cstr, SubString s)
{
return (s == Cstr);
} // global operator ==
bool operator !=(const char *Cstr, SubString s)
{
return (s != Cstr);
} // global operator !=
bool operator <=(const char *Cstr, SubString s)
{
return (s >= Cstr);
} // global operator <=
bool operator >=(const char *Cstr, SubString s)
{
return (s <= Cstr);
} // global operator >=
bool operator <(const char *Cstr, SubString s)
{
return (s > Cstr);
} // global operator <
bool operator >(const char *Cstr, SubString s)
{
return (s < Cstr);
} // global operator >
string/SubString.h 0100600 0000764 0000764 00000022314 07733672276 013131 0 ustar iank iank
#ifndef SUBSTRING_H
#define SUBSTRING_H
#include
/** \file
Copyright and Use
You may use this source code without limitation and without
fee as long as you include:
This software was written and is copyrighted by Ian Kaplan, Bear
Products International, www.bearcave.com, 2001, 2002, 2003
This software is provided "as is", without any warrenty or
claim as to its usefulness. Anyone who uses this source code
uses it at their own risk. Nor is any support provided by
Ian Kaplan and Bear Products International.
Please send any bug fixes or suggested source changes to:
iank@bearcave.com
*/
#include "RCObject.h"
#include "RCPtr.h"
class String;
/**
This class supports the creation and manipulation of SubStrings,
which are sections of Strings. SubStrings are usually created by
the "()" operator of the String class. As a result, this class
and the String class are closely related.
The String class "()" operator takes two numeric arguments, the
starting index and the length (String objects follow C/C++ indexing
and start at 0).
Examples:
String a("abcdefgh");
String b;
String tmp;
b = a(0,4); -- assignment of SubString to String
a(0, 4) = "0123"; -- assignment of CString to SubString
a(0, 4) = a(4, 4); -- assignment of SubString to SubString
a(0, 4) = b; -- assignment of String to SubString
Although a SubString is usually created by the String "()"
operator, it can also be created explicitly. For example:
String s = "the lazy dog";
SubString t(s, 9, 3);
t = "golden";
std::cout << (const char *)s << std::endl;
Note that a SubString is a reference to a String. So the
assignment to the SubString alters the String. The example
above will write "the lazy golden" to std::cout.
The SubString class suports relational operators. This is faster
than converting the SubString to a String and performing the
relational operation on String operands.
The SubString class does not support an argumentless constructor.
This assures that all SubString objects are properly initialized
with a String and a range (start index and length). The starting
index and length are checked in the constructor to assure that they
are within the bounds of the String argument. If they are not,
an std::out_of_range exception will be thrown.
There is no type cast supported for SubString to const char *
although it might be nice to be able to do the following
String a = "morgan dreams of being fat";
const char *sleep = a(0, 13); // Not supported!
Unfortunately, there is no practical way to implement this. The
right-hand-side operation a(0, 13) creates a compiler
generated String temporary. This temporary will be
deallocated after the statement containing the assignment.
If the memory for the temporary were referenced from the
const char *, the const char * pointer would
point to deallocated memory.
There are some corner cases that I could not figure out how to
protect against. For example, it is possible to create a SubString
object that references a deallocated string. This is shown in the
function makeSubString below:
//
// Don't do this!
// This function will result in a SubString that references
// a String which has been deallocated.
//
SubString makeSubString(const char *Cstr, size_t start, size_t len)
{
String s( Cstr );
SubString t(s, start, len);
return t; // The String "s" will be deallocated on exit
}
For this function to work, the SubString constructor would have to
increment the reference count of the String, so that the String
would not be deallocated on exit (when its reference count reaches
zero). This could be done if an actual String, rather than a
pointer to a String were used in the SubString shared data.
Implementing the SubString in this way break the functionality of
the SubString object. Consider the code below:
String a = "12345678";
a(3,0) = "abcd";
If the SubString shared data contained a String, rather than a
pointer to a string, the constructor for the SubString would
increment the reference count for a to 2. This would work
well for the makeSubString function, but would break the
assignment above.
If the SubString shared data contained a String, the assignment to
this String would result in a unique copy being be made, since the
String is shared (e.g., reference count > 1). The C-string "abcd"
would be inserted into the String in the SubString shared data
(which is now unique). The result will be that "a" is unchanged
and the String we have assigned into will be deallocated when the
SubString destructor is executed.
In contrast to the String class, only a few SubString functions are
inlined. Many SubString functions reference the String class. The
String class is defined only as an abstract class here. This is
required by the fact that the String class also includes
SubString.h.
\author Ian Kaplan www.bearcave.com
*/
class SubString
{
private:
class SharedData : public RCObject
{
private:
String *pStr_;
size_t start_;
size_t subStrLen_;
private:
void copy(const SharedData &rhs);
public:
SharedData();
SharedData(const SharedData &rhs) { copy( rhs ); }
~SharedData() {}
void pStr( String *strRef ) { pStr_ = strRef; }
String *pStr() { return pStr_; }
void start(int start) { start_ = static_cast(start); }
size_t start() { return start_; }
void subStrLen(int val) { subStrLen_ = static_cast(val); }
size_t subStrLen() { return subStrLen_; }
size_t getRefCnt() { return refCnt(); }
}; // class sharedData
RCPtr value;
void rangeCheck(String &s, const size_t start, const size_t len)
throw(std::out_of_range);
int compareTo( const char *CStr );
int compareTo( String &s );
int compareTo( SubString &subStr );
void createCopySpace(size_t start, size_t charsToCopy);
String string() const;
public:
SubString(String &s, const size_t start, const size_t len)
throw(std::out_of_range);
operator String() const;
size_t getRefCnt() { return value->getRefCnt(); }
size_t length() { return value->subStrLen(); }
String operator +(const char *Cstr); // String = SubString + Cstr
String operator +(const SubString& sub); // String = SubString + SubString
String operator +(const String &str); // String = SubString + String
String operator =(const char *CStr ); // lhs SubString = Cstr
String operator =(String& str ); // lhs SubString = String
String operator =(const SubString& sub ); // lhs SubString = SubString
// Relational operators
bool operator ==(const char *Cstr ) { return (compareTo( Cstr ) == 0); }
bool operator ==(String &s ) { return (compareTo( s ) == 0); }
bool operator ==(SubString subStr) { return (compareTo(subStr) == 0); }
bool operator !=(const char *Cstr ) { return (compareTo( Cstr ) != 0); }
bool operator !=(String &s ) { return (compareTo( s ) != 0); }
bool operator !=(SubString subStr) { return (compareTo(subStr) != 0); }
bool operator <=(const char *Cstr ) { return (compareTo( Cstr ) <= 0); }
bool operator <=(String &s ) { return (compareTo( s ) <= 0); }
bool operator <=(SubString subStr) { return (compareTo(subStr) <= 0); }
bool operator >=(const char *Cstr ) { return (compareTo( Cstr ) >= 0); }
bool operator >=(String &s ) { return (compareTo( s ) >= 0); }
bool operator >=(SubString subStr) { return (compareTo(subStr) >= 0); }
bool operator <(const char *Cstr ) { return (compareTo( Cstr ) < 0); }
bool operator <(String &s ) { return (compareTo( s ) < 0); }
bool operator <(SubString subStr) { return (compareTo(subStr) < 0); }
bool operator >(const char *Cstr ) { return (compareTo( Cstr ) > 0); }
bool operator >(String &s ) { return (compareTo( s ) > 0); }
bool operator >(SubString subStr) { return (compareTo(subStr) > 0); }
}; // SubString
//
// Global Operators
//
bool operator ==(const char *Cstr, SubString s);
bool operator !=(const char *Cstr, SubString s);
bool operator <=(const char *Cstr, SubString s);
bool operator >=(const char *Cstr, SubString s);
bool operator <(const char *Cstr, SubString s);
bool operator >(const char *Cstr, SubString s);
/** ----------------- SharedData methods ----------------- */
inline
SubString::SharedData::SharedData()
{
pStr_ = 0;
start_ = 0;
subStrLen_ = 0;
}
inline
void SubString::SharedData::copy(const SharedData &rhs)
{
pStr_ = rhs.pStr_;
start_ = rhs.start_;
subStrLen_ = subStrLen_;
} // copy
#include "String.h"
#endif
string/SubStrTest.C 0100600 0000764 0000764 00000042720 07733672623 013225 0 ustar iank iank
/** \file
Regression tests for the SubString class.
Note that the existence of these regression tests does not change
that statement this code is provided without waranty. You
use this code at your own risk.
Compiling this code:
This code is written to compile on Microsoft Visual C++ 6.0 and
higher. It will also compile on GNU C++ and Sun's 6.0 and later
C++ compilers. To compile under Microsoft VC++:
cl -Zi -TP -GX SubString.C String.C SubStrTest.C -o SubStrTest
The -GX flag is needed to support exceptions, which are used
rather than the assert() functions in the earlier versions.
Copyright and Use
You may use this source code without limitation and without
fee as long as you include:
This software was written and is copyrighted by Ian Kaplan, Bear
Products International, www.bearcave.com, 2001.
This software is provided "as is", without any warrenty or
claim as to its usefulness. Anyone who uses this source code
uses it at their own risk. Nor is any support provided by
Ian Kaplan and Bear Products International.
Please send any bug fixes or suggested source changes to:
iank@bearcave.com
*/
#include
#include "SubString.h"
bool SubStringPassByValue( SubString subStr, size_t previousRefCnt )
{
bool result = (subStr.getRefCnt() == previousRefCnt + 1);
if (result == false) {
printf("SubStringPassByValue: argument subStr.getRefCnt() = %d\n",
subStr.getRefCnt() );
printf("SubStringPassByValue: previousRefCnt = %d\n", previousRefCnt );
}
return result;
}
bool SubStringPassByReference( SubString &subStr, size_t previousRefCnt )
{
bool result = (subStr.getRefCnt() == previousRefCnt);
return result;
}
/**
Test to verify that reference counting is handled properly for
the SubString class. This also tests assignment as a side-effect.
*/
bool subStrRefCnt()
{
bool result = true;
printf("Test SubString reference count\n");
String a("abcdefghijk");
//
// Test reference count
//
SubString subStrA(a, 5, 6);
if (subStrA.getRefCnt() != 1) {
printf("1. reference count is %d, should be 1\n",
subStrA.getRefCnt());
result = false;
}
size_t previousRefCnt = subStrA.getRefCnt();
if (! SubStringPassByReference( subStrA, previousRefCnt) ) {
printf("2. Pass by reference failed\n");
result = false;
}
previousRefCnt = subStrA.getRefCnt();
if (! SubStringPassByValue( subStrA, previousRefCnt) ) {
printf("3. Pass by reference value\n");
result = false;
}
if (a.getRefCnt() != 1) {
printf("4. refCnt of the String referenced by the SubString = %d, should be 1\n", a.getRefCnt() );
result = false;
}
return result;
} // subStrRefCnt
/**
Test SubString section operations
The SubString constructor creates a section:
String s = "Mr. Golden Noodle";
String pasta = s(11, 6);
were 11 is the start index and 6 is the length.
The construction of a SubString should always result in a correct
SubString. This means that the start index should be within the
bounds of the String and the start index plus the size should be in
the bounds as well. If this is not the case, the SubString
constructor will throw an exception.
So, what about:
String empty;
String empty2 = empty(0, 0);
Ok, so we're taking a zero length section of a String starting at
zero. Is that so wrong? I don't know about so wrong, but
it does seem incorrect. The rules with with a SubString is that
the starting index must be less than the String length. In this
case the starting index is equal to the String length.
*/
bool subStrSection()
{
printf("Test SubString section operations\n");
bool rslt = true;
String a("abcdefghij");
String d = a;
// a(0,5) means start at index 0, length of the section is 5
String subA = a(0,5);
if (subA.strlen() != 5) {
printf("1. SubString length = %d, should be 5\n", subA.strlen() );
rslt = false;
}
// make sure that the reference count for the original string
// is not affected. Note that a right hand size SubString
// operation does not affect the String on which the SubString
// is defined.
if (d.getRefCnt() != 2) {
printf("2. d.getRefCnt() = %d, should be 2\n", d.getRefCnt() );
}
String b = subA;
if (b != "abcde") {
printf("3. SubString section is incorrect. ");
const char *Cstr = b;
printf("SubStr = %s, should be \"abcde\"\n", Cstr );
rslt = false;
}
String c = a(5,5);
if (c != "fghij") {
printf("4. SubString section is incorrect. ");
const char *Cstr = b;
printf("SubStr = %s, should be \"abcde\"\n", Cstr );
rslt = false;
}
if (a.getRefCnt() != 2) {
printf("5. a.getRefCnt() = %d, should be 2\n", a.getRefCnt() );
rslt = false;
}
bool exceptionTest1 = false;
// Test exceptions on an out of bound reference
try {
// use a length (8) that places the section beyond the end
// of the String.
String x = a(5, 8);
}
catch (std::out_of_range e) {
exceptionTest1 = true;
}
if (!exceptionTest1) {
printf("6. There should have been a SubString construction exception\n");
rslt = false;
}
bool exceptionTest2 = false;
try {
// use a start that is beyond the end of the String
String x = a(10, 1);
}
catch (std::out_of_range e) {
exceptionTest2 = true;
}
if (!exceptionTest2) {
printf("7. There should have been a SubString construction exception\n");
rslt = false;
}
String empty;
if (empty.strlen() != 0) {
printf("8. length of an empty string is wrong\n");
rslt = false;
}
bool exceptionTest3 = false;
String empty2;
try {
empty2 = empty(0,0);
}
catch(std::out_of_range e) {
exceptionTest3 = true;
}
if (!exceptionTest3) {
printf("9. There should have been a SubString construction exception\n");
rslt = false;
}
if (empty2.strlen() != 0) {
printf("10. length of empty2 is wrong\n");
rslt = false;
}
if (empty2.getRefCnt() != 1) {
printf("11. reference count of empty2 is wrong\n");
rslt = false;
}
String e = "12345678";
String f = e;
e(4,0) = "abcd";
if (e != "1234abcd5678") {
printf("12. insert, via a zero length section, failed\n");
size_t len = e.strlen();
printf("12. e.strlen() = %d\n", len );
if (len > 0) {
printf("12. e = %s\n", (const char *)e );
}
}
if (e.strlen() != 12) {
printf("13. e.strlen() = %d, should be 12\n", e.strlen() );
}
if (f != "12345678") {
printf("14. improperly altered string in 'f'\n");
}
if (f.getRefCnt() != 1 && e.getRefCnt() != 1) {
printf("15. reference counts are wrong\n");
}
return rslt;
} // subStrSection
/**
Test assignment to and from a SubString object
*/
bool subStrAssign()
{
bool rslt = true;
printf("Test assignment to and from a SubString object\n");
const char *CStr1 = "abcde1234jklm";
const char *CStr2 = "abcdefghijklm";
String a = CStr1;
if (a != CStr1) {
printf("1. Contents of String is incorrect\n");
rslt = false;
}
String b = a;
if (b.getRefCnt() != 2) {
printf("2. Reference count is incorrect\n");
rslt = false;
}
a(5, 4) = "fghi";
if (a != CStr2) {
printf("3. Contents of String is incorrect\n");
rslt = false;
}
if (b.getRefCnt() != 1) {
printf("4. Reference count is incorrect\n");
rslt = false;
}
String c = a(0, 5);
if (c != "abcde") {
printf("5. Contents of String is incorrect\n");
rslt = false;
}
// assign a string to a substring section
a(5, 5) = c;
if (a(0,10) != "abcdeabcde") {
printf("6. Contents of String is incorrect\n");
rslt = false;
}
// Now check assignment to regions that are not the same
// length as the string being assigned.
String target = "the lazy golden";
String t2 = target;
// assign a longer C-string to a shorter assignment region
t2(0, 3) = "morgie is a";
if (t2 != "morgie is a lazy golden") {
printf("7. C-string assignment to SubString is wrong\n");
rslt = false;
}
if (target.getRefCnt() != 1) {
printf("8. Reference count is wrong\n");
rslt = false;
}
if (target != "the lazy golden") {
printf("9. The contents of target is wrong\n");
rslt = false;
}
t2(17, 6) = "spaniel";
// assign a shorter C-string to a longer assignment region
t2(0, 6) = "max";
if (t2 != "max is a lazy spaniel") {
const char *Cstr = t2;
printf("10. The contents of t2 is incorrect. t2 = %s\n", Cstr );
rslt = false;
}
// Test assignments of String to SubString
// Longer assigned to shorter
target = "max is a dog";
String replaceAnimal = "people";
String replaceName = "Maggie";
target(9,3) = replaceAnimal;
target(0,3) = replaceName;
if (target != "Maggie is a people") {
printf("11. String to SubString assignment failed\n");
rslt = false;
}
// shorter assignments to longer SubString region
replaceName = "Ian";
target(0, 6) = replaceName;
if (target != "Ian is a people") {
printf("11. String to SubString assignment failed\n");
rslt = false;
}
if (target.strlen() != strlen("Ian is a people")) {
printf("12. length of the string is wrong\n");
rslt = false;
}
// Test the explicit SubString constructor with assignment.
// Also make sure that chained assignment works in this case.
const char *expected_result = "the lazy golden";
String u;
String s = "the lazy dog";
SubString t(s, 9, 3);
u = t = "golden";
if (s != expected_result) {
printf("13. the contents of s is \"%s\", should be \"%s\"\n",
(const char *)s, expected_result);
rslt = false;
}
if (u != expected_result) {
printf("14. the contents of u is \"%s\", should be \"%s\"\n",
(const char *)u, expected_result);
rslt = false;
}
// The result of the assignment to the SubString should be the
// String, "s" passed to the constructor. This is assigned to
// the String u, so the reference count should be 2.
if (u.getRefCnt() != 2) {
printf("15. reference count is %d, should be 2\n", u.getRefCnt() );
rslt = false;
}
return rslt;
} // subStrAssign
/**
Test SubString relational operations
*/
bool subStrRelations()
{
bool rslt = true;
String a = "abcdeabcde";
printf("Test SubString relational operators\n");
//
// == operator
//
// SubString != SubString
bool t1 = false;
if (a(0,5) == a(5, 5)) {
t1 = true;
}
if (!t1) {
printf("1. SubString == SubString failed\n");
rslt = false;
}
// SubString == Cstr
bool t2 = false;
if (a(0,5) == "abcde") {
t2 = true;
}
if (!t2) {
printf("2. SubString == Cstr failed\n");
rslt = false;
}
// SubString == String
bool t3 = false;
String b = "abcde";
if (a(0,5) == b) {
t3 = true;
}
if (!t3) {
printf("3. SubString == String failed\n");
rslt = false;
}
t1 = true;
if (a(1,4) == "bcd") {
t1 = false;
}
if (!t1) {
printf("4. SubString == CStr of unequal length should have been false\n");
rslt = false;
}
//
// != operator
//
t1 = false;
t2 = false;
t3 = false;
// 012345678901234
String c = "abcdexyzzyabcde";
// SubString != SubString
if (a(0,5) != c(5,5)) {
t1 = true;
}
if (!t1) {
printf("5. SubString != SubString failed\n");
rslt = false;
}
// SubString != Cstr
if (a(0,5) != "xyzzy") {
t2 = true;
}
if (!t2) {
printf("6. SubString != SubString failed\n");
rslt = false;
}
// SubString != String
b = "zyzzy";
if (a(0,5) != b) {
t3 = true;
}
if (!t3) {
printf("7. SubString != String failed\n");
rslt = false;
}
bool t4 = false;
if (a(0, 5) != "abcd") {
t4 = true;
}
if (!t4) {
printf("8. SubString != CStr of unequal length failed\n");
rslt = false;
}
t4 = true;
if (a(1, 4) != "bcde") {
t4 = false;
}
if (!t4) {
printf("9. SubString != CStr are equal - test should be false\n");
rslt = false;
}
//
// < operator
//
t1 = false;
t2 = false;
t3 = false;
t4 = false;
// SubString < SubString
if (a(1,5) < c(5,5)) {
t1 = true;
}
if (!t1) {
printf("10. SubString < SubString failed\n");
rslt = false;
}
// SubString < SubString: overlapping SubString sections on the same String
t1 = false;
if (c(1,5) < c(5,5)) {
t1 = true;
}
if (!t1) {
printf("11. SubString < SubString failed for overlapping sections\n");
rslt = false;
}
// SubString < Cstr
t1 = false;
if (c(1,5) < "pdsrsmzf") {
t1 = true;
}
if (!t1) {
printf("12. SubString < Cstr \n");
rslt = false;
}
// SubString < String
// note that b is still "xyzzy"
t1 = false;
if (c(0,8) < b) {
t1 = true;
}
if (!t1) {
printf("13. SubString < String \n");
rslt = false;
}
//
// > operator
//
// SubString > SubString
// 0123456789
String d = " abc ";
t1 = false;
if (c(10, 5) > d(2, 5)) {
t1 = true;
}
if (!t1) {
printf("14. SubString > SubString\n");
rslt = false;
}
// SubString > Cstr (note, "xyz" will be "extended" with null chars)
t1 = false;
if (c(5, 5) > "xyz") {
t1 = true;
}
if (!t1) {
printf("15. SubString > Cstr\n");
rslt = false;
}
// SubString > String
b = "lesser of two evils";
t1 = false;
if (c(5, 5) > b) {
t1 = true;
}
if (!t1) {
printf("16. SubString > String\n");
String ts = c(0, 5);
const char *t1 = ts;
const char *t2 = b;
printf("c(0, 5) = %s, b = %s\n", t1, t2);
rslt = false;
}
//
// >= operator
//
t1 = false;
if (c(0,5) >= c(10, 5)) {
t1 = true;
}
if (!t1) {
printf("17. SubString >= SubString\n");
rslt = false;
}
t1 = false;
if (c(0,5) >= "abcde") {
t1 = true;
}
if (!t1) {
printf("18. SubString >= CStr\n");
rslt = false;
}
t1 = false;
if (c(5,5) >= "abcde") {
t1 = true;
}
if (!t1) {
printf("19. SubString >= CStr\n");
rslt = false;
}
t1 = false;
b = "abcde";
if (c(0,5) >= b) {
t1 = true;
}
if (!t1) {
printf("20. SubString >= String\n");
rslt = false;
}
//
// <= operator
//
t1 = false;
if (c(0,5) <= c(5, 5)) {
t1 = true;
}
if (!t1) {
printf("21. SubString <= SubString\n");
rslt = false;
}
t1 = false;
if (c(0,5) <= "xyzzy") {
t1 = true;
}
if (!t1) {
printf("22. SubString <= CStr\n");
rslt = false;
}
t1 = false;
b = "xyzzy";
if (c(0,5) <= b) {
t1 = true;
}
if (!t1) {
printf("23. SubString >= String\n");
rslt = false;
}
t1 = false;
b = "abcde";
if (c(0,5) <= b) {
t1 = true;
}
if (!t1) {
printf("24. SubString >= String\n");
rslt = false;
}
return rslt;
} // subStrRelations
/**
Test for other types on the LHS of a relation. This
must be supported by global operators.
*/
bool globalSubStrRelations()
{
printf("Test global SubString relational operators\n");
bool rslt = true;
// 012345678901234
String a = "abcdexyzzyabcde";
// Cstr == SubString
bool t1 = false;
if ("abcde" == a(0, 5)) {
t1 = true;
}
if (!t1) {
printf("1. Cstr == SubString\n");
rslt = false;
}
// Cstr != SubString
t1 = false;
if ("abcde" != a(5, 5)) {
t1 = true;
}
if (!t1) {
printf("2. Cstr != SubString\n");
rslt = false;
}
// Cstr < SubString
t1 = false;
if ("abcde" < a(5, 5)) {
t1 = true;
}
if (!t1) {
printf("3. Cstr < SubString\n");
rslt = false;
}
// Cstr > SubString
t1 = false;
if ("xyzzy" > a(0, 5)) {
t1 = true;
}
if (!t1) {
printf("4. Cstr > SubString\n");
rslt = false;
}
// Cstr >= SubString
t1 = false;
if ("abcde" >= a(0, 5)) {
t1 = true;
}
if (!t1) {
printf("5. Cstr >= SubString\n");
rslt = false;
}
// Cstr >= SubString
t1 = false;
if ("xyzzy" >= a(0, 5)) {
t1 = true;
}
if (!t1) {
printf("6. Cstr >= SubString\n");
rslt = false;
}
// Cstr <= SubString
t1 = false;
if ("abcde" <= a(0, 5)) {
t1 = true;
}
if (!t1) {
printf("7. Cstr <= SubString\n");
rslt = false;
}
// Cstr <= SubString
t1 = false;
if ("abcde" <= a(5, 5)) {
t1 = true;
}
if (!t1) {
printf("8. Cstr <= SubString\n");
rslt = false;
}
return rslt;
} // globalSubStrRelations
int
main()
{
bool passed = true;
printf("SubString tests \n");
if (!subStrRefCnt()) {
passed = false;
}
if (!subStrSection()) {
passed = false;
}
if (!subStrAssign()) {
passed = false;
}
if (!subStrRelations()) {
passed = false;
}
if (! globalSubStrRelations()) {
passed = false;
}
printf("Tests ");
if (passed)
printf("passed\n");
else
printf("failed\n");
return 0;
}