string/0040700000076400007640000000000007733704541011026 5ustar iankiankstring/DblVecTest.C0100600000076400007640000003225607733704351013137 0ustar iankiank /** \file Regression tests for the DoubleVec reference counted class. The DoubleVec class is an instantiation of the RCArray template, which supports reference counted arrays. As a result, this code serves as a regression test for RCArray. 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. Areas to test: 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 "DoubleVec.h" bool CheckConstructor() { bool rslt = true; size_t i; DoubleVec empty; if (empty.length() != 0) { printf("CheckConstructor: 1. bad length. Length = %d\n", empty.length()); rslt = false; } if (empty.getRefCnt() != 1) { printf("CheckConstructor: 2. bad refCnt. RefCnt = %d\n", empty.getRefCnt() ); rslt = false; } const size_t len = 10; DoubleVec vec( len, 0.0 ); if (vec.length() != len) { printf("CheckConstructor: 3. bad length. Length = %d, should be = %d\n", vec.length(), len ); rslt = false; } bool isZero = true; for (i = 0; i < len; i++) { if (vec[i] != 0.0) { isZero = false; break; } } if (!isZero) { printf("CheckConstructor: 4. Array should be initialized to zero\n"); rslt = false; } size_t answer = 42; DoubleVec TheAnswer( len, answer ); bool initOK = true; for (i = 0; i < len; i++) { if (TheAnswer[i] != answer) { initOK = false; break; } } if (!initOK) { printf("CheckConstructor: 5. Array should be initialized to %d\n", answer ); rslt = false; } const double avacadoNumber = 6.02e23; const size_t bigLen = 4000; DoubleVec MrBig( bigLen ); MrBig[ bigLen - 1 ] = avacadoNumber; if ((double)MrBig[ bigLen-1 ] != avacadoNumber) { printf("CheckConstructor: 6. Assignment to end of a largish array failed"); printf("(double)MrBig[bigLen-1] = %7.4e\n", (double)MrBig[ bigLen-1 ] ); rslt = false; } return rslt; } // CheckConstructor bool equals( DoubleVec &vec, const double *test) { const size_t len = vec.length(); bool rslt = true; size_t i; for (i = 0; i < len; i++) { if (vec[i] != test[i]) { rslt = false; break; } } return rslt; } // equals bool AppendNewValues() { bool rslt = true; DoubleVec vec; const size_t len = 16; double *testVec = new double[len]; size_t i; for (i = 0; i < len; i++) { vec.append( i+1 ); testVec[i] = i+1; } if (vec.length() != len) { printf("AppendNewValues: 1. length = %d, should be %d\n", vec.length(), len); rslt = false; } if (! equals( vec, testVec )) { printf("AppendNewValues: 2. short test failed\n"); rslt = false; } delete [] testVec; const size_t bigger = 6000; testVec = new double[bigger]; DoubleVec bigVec; for (i = 0; i < len; i++) { bigVec.append( i+1 ); testVec[i] = i+1; } if (! equals( vec, testVec )) { printf("AppendNewValues: 3. long test failed\n"); rslt = false; } delete [] testVec; DoubleVec a( len ); testVec = new double[len*2]; for (i = 0; i < len; i++) { a[i] = i; testVec[i] = i; } for (i = len; i < len*2; i++) { a.append( i ); testVec[i] = i; } if (! equals( a, testVec )) { printf("AppendNewValues: 4. append to existing array failed\n"); rslt = false; } delete [] testVec; return rslt; } // AppendNewValues bool Assignment() { bool rslt = true; const size_t len = 20; DoubleVec vec(len); size_t i; for (i = 0; i < len; i++) { vec[i] = i * 2; } DoubleVec a = vec; if (a.getRefCnt() != 2) { printf("Assignment: 1. refCnt = %d, should be 2\n", a.getRefCnt() ); rslt = false; } DoubleVec b(len/2); b = a; if (b.getRefCnt() != 3) { printf("Assignment: 2. refCnt = %d, should be 3\n", b.getRefCnt() ); rslt = false; } if (b.length() != len) { printf("Assignment: 3. b.length() = %d, should be %d\n", b.length(), len ); rslt = false; } bool contentOK = true; for (i = 0; i < len; i++) { if (b[i] != i * 2) { contentOK = false; break; } } if (!contentOK) { printf("Assignment: 3. Array content is wrong\n"); rslt = false; } // Assignment to "self" should not change the reference count size_t beforeRefCnt = b.getRefCnt(); b = b; if (b.getRefCnt() != beforeRefCnt) { printf("Assignment: 4. refCnt = %d, should be %d\n", b.getRefCnt(), beforeRefCnt ); rslt = false; } contentOK = true; for (i = 0; i < len; i++) { if (b[i] != i * 2) { contentOK = false; break; } } if (!contentOK) { printf("Assignment: 5. After self assignment, array content is wrong\n"); rslt = false; } return rslt; } // Assignment DoubleVec globalVec1; bool checkRef(DoubleVec &vec, size_t len) { bool rslt = true; if (vec.getRefCnt() != 1) { printf("PassByReference: 1. refCnt = %d, should be 1\n", vec.getRefCnt() ); rslt = false; } if (vec.length() != len) { printf("PassByReference: 2. length = %d, should be %d\n", vec.length(), len ); rslt = false; } DoubleVec a = vec; if (vec.getRefCnt() != 2) { printf("PassByReference: 3. refCnt = %d, should be 2\n", vec.getRefCnt() ); rslt = false; } bool contentOK = true; size_t i; for (i = 0; i < len; i++) { if (a[i] != i) { printf("PassByReference: 4. Content is wrong" ); rslt = false; break; } } globalVec1 = a; if (vec.getRefCnt() != 3) { printf("PassByReference: 5. refCnt = %d, should be 3\n", vec.getRefCnt() ); rslt = false; } return rslt; } // checkRef bool PassByReference() { bool rslt = true; size_t len = 17; DoubleVec a(len); size_t i; for (i = 0; i < len; i++) { a[i] = i; } if (! checkRef(a, len)) { rslt = false; } // remember, we assigned to a global variable in checkRef() if (a.getRefCnt() != 2) { printf("PassByReference: 6. refCnt = %d, should be 2\n", a.getRefCnt() ); rslt = false; } // remember, we assigned to a global variable in checkRef() if (globalVec1.length() != len) { printf("PassByReference: 7. length = %d, should be %d\n", globalVec1.getRefCnt(), len ); rslt = false; } bool contentOK = true; for (i = 0; i < len; i++) { if (globalVec1[i] != i) { printf("PassByReference: 8. Content is wrong" ); rslt = false; break; } } return rslt; } // PassByReference DoubleVec globalVec2; bool checkPassByValue( DoubleVec a, size_t len ) { bool rslt = true; if (a.getRefCnt() != 2) { printf("PassByValue: 1. refCnt = %d, should be 2\n", a.getRefCnt()); rslt = false; } if (a.length() != len) { printf("PassByValue: 2. length = %d, should be %d\n", a.length(), len); rslt = false; } DoubleVec b = a; if (a.getRefCnt() != 3) { printf("PassByValue: 3. refCnt = %d, should be 3\n", a.getRefCnt()); rslt = false; } bool contentOK = true; size_t i; for (i = 0; i < len; i++) { if (b[i] != i) { printf("PassByValue: 4. Content is wrong" ); rslt = false; break; } } globalVec2 = a; if (b.getRefCnt() != 4) { printf("PassByValue: 5. refCnt = %d, should be 4\n", b.getRefCnt()); rslt = false; } return rslt; } // checkPassByValue bool PassByValue() { bool rslt = true; const size_t len = 21; DoubleVec a(len); size_t i; for (i = 0; i < len; i++) { a[i] = i; } if (!checkPassByValue( a, len )) { rslt = false; } // note: copy to globalVec2 if (a.getRefCnt() != 2) { printf("PassByValue: 6. a.getRefCnt() = %d, should be 2\n", a.getRefCnt()); rslt = false; } if (globalVec2.getRefCnt() != 2) { printf("PassByValue: 7. globalVec2.getRefCnt() = %d, should be 2\n", globalVec2.getRefCnt()); rslt = false; } bool contentOK = true; for (i = 0; i < len; i++) { if (a[i] != i) { printf("PassByValue: 8. Content is wrong" ); rslt = false; break; } } contentOK = true; for (i = 0; i < len; i++) { if (globalVec2[i] != i) { printf("PassByValue: 9. Content is wrong" ); rslt = false; break; } } return rslt; } // PassByValue DoubleVec DoubleVecFactory(const size_t size) { DoubleVec v(size); return v; } // DoubleVecFactory DoubleVec &passThrough( DoubleVec &v ) { return v; } DoubleVec makeCopy(DoubleVec &v ) { size_t size = v.length(); DoubleVec newVec(size); for (size_t i = 0; i < size; i++) { newVec[i] = (double)v[i]; } return newVec; } // makeCopy /** Check that reference counted arrays work properly as function results. */ bool CheckFuncReturn() { bool rslt = true; const size_t size = 20; DoubleVec v(size); size_t i; for (i = 0; i < size; i++) { v[i] = i + 1; } DoubleVec a = makeCopy( v ); for (i = 0; i < size; i++) { if (a[i] != v[i]) { printf("CheckFuncReturn: 1. bad values\n"); rslt = false; break; } } if (a.getRefCnt() != 1) { printf("CheckFuncReturn: 2. unexpected reference count\n"); rslt = false; } if (v.getRefCnt() != 1) { printf("CheckFuncReturn: 3. unexpected reference count\n"); rslt = false; } DoubleVec b = passThrough( v ); if (b.getRefCnt() != 2) { printf("CheckFuncReturn: 4. unexpected reference count\n"); rslt = false; } if (v.getRefCnt() != 2) { printf("CheckFuncReturn: 5. unexpected reference count\n"); rslt = false; } const size_t s = 21; DoubleVec c = DoubleVecFactory( 21 ); if (c.getRefCnt() != 1) { printf("CheckFuncReturn: 6. unexpected reference count\n"); rslt = false; } if (c.length() != s) { printf("CheckFuncReturn: 7. size is not correct\n"); rslt = false; } return rslt; } // CheckFuncReturn /** At this point the RHS index is pretty well tested, since the tests above would have failed otherwise. So this function concentrates on testing the LHS index (e.g., address). */ bool Index() { bool rslt = true; const size_t len = 31; size_t i; DoubleVec a; for (i = 0; i < len; i++) { a.append( i ); } DoubleVec b = a; if (b.getRefCnt() != 2) { printf("Index: 1. reference count is wrong\n"); rslt = false; } a[len/2] = 0; if (b.getRefCnt() != 1) { printf("Index: 2. reference count is wrong\n"); rslt = false; } // b should be unchanged bool contentOK = true; for (i = 0; i < len; i++) { if (b[i] != i) { printf("Index: 3. Content is wrong" ); rslt = false; break; } } // a should be changed: a[len/2] == 0 contentOK = true; for (i = 0; i < len; i++) { if (i == len/2) { if (a[i] != 0) { printf("Index: 4. a[%d] = %f, should be 0.0\n", i, (double)a[i] ); rslt = false; break; } } else if (a[i] != i) { printf("Index: 5. Content is wrong" ); rslt = false; break; } } DoubleVec c(20, 42.0); DoubleVec d = c; if (c.getRefCnt() != 2) { printf("Index: 6. reference count is wrong\n"); rslt = false; } return rslt; } // Index main() { bool passed = true; printf("Check constructors\n"); if (! CheckConstructor()) { passed = false; } printf("Check append new values\n"); if (! AppendNewValues()) { passed = false; } printf("Check assignment\n"); if (! Assignment()) { passed = false; } printf("Check pass by reference\n"); if (! PassByReference()) { passed = false; } printf("Check function return values\n"); if (!CheckFuncReturn()) { passed = false; } printf("Check pass by value\n"); if (! PassByValue()) { passed = false; } printf("Check index operation\n"); if (! Index()) { passed = false; } if (passed) printf("test passed\n"); else printf("test failed\n"); } string/DoubleVec.h0100600000076400007640000000242507733166667013064 0ustar iankiank #ifndef DOUBLEVEC_H #define DOUBLEVEC_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 "RCArray.h" typedef RCArray DoubleArray; /** A growable reference counted array of doubles which implements copy-on-write semantics (see RCArray). */ 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(); } }; #endif string/doxygenTemplate0100600000076400007640000013116707733224211014121 0ustar iankiank# Doxyfile 1.3.3 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # General configuration options #--------------------------------------------------------------------------- # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = doc # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, # Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en # (Japanese with English messages), Korean, Norwegian, Polish, Portuguese, # Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. OUTPUT_LANGUAGE = English # This tag can be used to specify the encoding used in the generated output. # The encoding is not always determined by the language that is chosen, # but also whether or not the output is meant for Windows or non-Windows users. # In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES # forces the Windows encoding (this is the default for the Windows binary), # whereas setting the tag to NO uses a Unix-style encoding (the default for # all platforms other than Windows). USE_WINDOWS_ENCODING = NO # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = YES # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = YES # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited # members of a class in the documentation of that class as if those members were # ordinary class members. Constructors, destructors and assignment operators of # the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. It is allowed to use relative paths in the argument list. STRIP_FROM_PATH = # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like the Qt-style comments (thus requiring an # explict @brief command for a brief description. JAVADOC_AUTOBRIEF = YES # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the DETAILS_AT_TOP tag is set to YES then Doxygen # will output the detailed description near the top, like JavaDoc. # If set to NO, the detailed description appears after the member # documentation. DETAILS_AT_TOP = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # reimplements. INHERIT_DOCS = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources # only. Doxygen will then generate output that is more tailored for Java. # For instance, namespaces will be presented as packages, qualified scopes # will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = MainPage "." # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp # *.h++ *.idl *.odl *.cs FILE_PATTERNS = *.h *.C # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories # that are symbolic links (a Unix filesystem feature) are excluded from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. EXCLUDE_PATTERNS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. INPUT_FILTER = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = YES # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES (the default) # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES (the default) # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet HTML_STYLESHEET = # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compressed HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output dir. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # If the GENERATE_TREEVIEW tag is set to YES, a side panel will be # generated containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. GENERATE_TREEVIEW = YES # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimised for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assigments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_PREDEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse the # parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::addtions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or # super classes. Setting the tag to NO turns the diagrams off. Note that this # option is superceded by the HAVE_DOT option below. This is only a fallback. It is # recommended to install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = YES # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similiar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will # generate a call dependency graph for every global function or class method. # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected # functions only using the \callgraph command. CALL_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = jpg # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found on the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width # (in pixels) of the graphs generated by dot. If a graph becomes larger than # this value, doxygen will try to truncate the graph, so that it fits within # the specified constraint. Beware that most browsers cannot cope with very # large images. MAX_DOT_GRAPH_WIDTH = 1024 # The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height # (in pixels) of the graphs generated by dot. If a graph becomes larger than # this value, doxygen will try to truncate the graph, so that it fits within # the specified constraint. Beware that most browsers cannot cope with very # large images. MAX_DOT_GRAPH_HEIGHT = 1024 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes that # lay further from the root node will be omitted. Note that setting this option to # 1 or 2 may greatly reduce the computation time needed for large code bases. Also # note that a graph may be further truncated if the graph's image dimensions are # not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT). # If 0 is used for the depth value (the default), the graph is not depth-constrained. MAX_DOT_GRAPH_DEPTH = 0 # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::addtions related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO # The CGI_NAME tag should be the name of the CGI script that # starts the search engine (doxysearch) with the correct parameters. # A script with this name will be generated by doxygen. CGI_NAME = search.cgi # The CGI_URL tag should be the absolute URL to the directory where the # cgi binaries are located. See the documentation of your http daemon for # details. CGI_URL = # The DOC_URL tag should be the absolute URL to the directory where the # documentation is located. If left blank the absolute path to the # documentation, with file:// prepended to it, will be used. DOC_URL = # The DOC_ABSPATH tag should be the absolute path to the directory where the # documentation is located. If left blank the directory on the local machine # will be used. DOC_ABSPATH = # The BIN_ABSPATH tag must point to the directory where the doxysearch binary # is installed. BIN_ABSPATH = /usr/local/bin/ # The EXT_DOC_PATHS tag can be used to specify one or more paths to # documentation generated for other projects. This allows doxysearch to search # the documentation for these projects as well. EXT_DOC_PATHS = string/GrowableArray.h0100600000076400007640000001337207733703671013750 0ustar iankiank #ifndef _GROWABLEARRAY_H_ #define _GROWABLEARRAY_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
*/ /** GrowableArray template is used to create array objects that will grow as elements are added to the end of the array. This is similar to the STL <vector> class. This array class is designed for dense arrays where the all elements of the array are used. Usage:
  • Elements are added to the end of the array via the append function.

  • Elements in the array can be accessed via the [] operator.

  • If the elements in the array are dynamically allocated, the user is responsible for deallocating these elements. For example, if the GrowableArray template is used to define an array of pointers to an object, the user is responsible for deallocating the objects referenced by the pointers. The GrowableArray destructor will only deallocate the array itself.

A doubling algorithm is used when the data size is expanded because it minimizes the amount of copying that must be done. The array will quickly grow to a the size needed to accomodate the data set and no more copying will be necessary. For large arrays there is the drawback that more memory may be allocated than is needed, since the amount of memory used grows exponentially. */ template 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/MainPage0100600000076400007640000000567707733225371012447 0ustar iankiank /*! \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.h0100600000076400007640000002042207733701275012502 0ustar iankiank #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:
  1. 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
       
  2. 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}.

  3. 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.h0100600000076400007640000001054007733211737012275 0ustar iankiank #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.h0100600000076400007640000000435607733166167012647 0ustar iankiank #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.h0100600000076400007640000000703707733223737012202 0ustar iankiank #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.C0100600000076400007640000002611107733663357012411 0ustar iankiank /** \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.h0100600000076400007640000002616307733672723012462 0ustar iankiank #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.C0100600000076400007640000007106107733211532013235 0ustar iankiank /** \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.C0100600000076400007640000003712307733671250013057 0ustar iankiank/** \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:
  • SubString creation:
             SubString t1(foo, 4, 4);
         
  • Assignment to the SubString, resulting in a string:
             String t2 = (t1 = "1234");
         
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.h0100600000076400007640000002231407733672276013131 0ustar iankiank #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.C0100600000076400007640000004272007733672623013225 0ustar iankiank /** \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; }