home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Source Code / Visual Basic Source Code.iso / vbsource / vbdatabs / ustring.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1999-03-31  |  17.0 KB  |  608 lines

  1. // ------------------------------- //
  2. // -------- Start of File -------- //
  3. // ------------------------------- //
  4. // ----------------------------------------------------------- // 
  5. // C++ Source Code File Name: ustring.cpp 
  6. // Compiler Used: MSVC40, DJGPP 2.7.2.1, GCC 2.7.2.1, HP CPP 10.24
  7. // Produced By: Doug Gaer 
  8. // File Creation Date: 11/29/1996  
  9. // Date Last Modified: 03/31/1999
  10. // Copyright (c) 1997 Douglas M. Gaer
  11. // ----------------------------------------------------------- // 
  12. // ------------- Program Description and Details ------------- // 
  13. // ----------------------------------------------------------- // 
  14. /*
  15. The VBD C++ classes are copyright (c) 1997, by Douglas M. Gaer.
  16. All those who put this code or its derivatives in a commercial
  17. product MUST mention this copyright in their documentation for
  18. users of the products in which this code or its derivative
  19. classes are used. Otherwise, you have the freedom to redistribute
  20. verbatim copies of this source code, adapt it to your specific
  21. needs, or improve the code and release your improvements to the
  22. public provided that the modified files carry prominent notices
  23. stating that you changed the files and the date of any change.
  24.  
  25. THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
  26. THE ENTIRE RISK OF THE QUALITY AND PERFORMANCE OF THIS SOFTWARE
  27. IS WITH YOU. SHOULD ANY ELEMENT OF THIS SOFTWARE PROVE DEFECTIVE,
  28. YOU WILL ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR
  29. CORRECTION.
  30.  
  31. The UString class is a user-defined string class used to create
  32. and manipulate non null-terminated resizable, variable-length
  33. strings of binary data. UString objects are implemented as a
  34. concrete data type, just like the built-in data types: char,
  35. int, long, float, and double. The UString class supports string
  36. concatenation, find, fill, and sub-string creation.
  37. */
  38. // ----------------------------------------------------------- // 
  39. #include <ctype.h>
  40. #include "ustring.h"
  41.  
  42. StrData StrData::Null_Ptr;
  43.  
  44. void *StrData::operator new(size_t StrSize, unsigned Bytes)
  45. // Overloaded new operator used to allocate memory for the size
  46. // of a StrData object plus number of bytes that represent the 
  47. // length of the string's data. The memory for the string is
  48. // allocated as an array of characters. It is the responsibility
  49. // of the UString class to keep track of the string's length.
  50. // The UString class must store the string data immediately following
  51. // the reference count. A null pointer object will be statically 
  52. // allocated when a new StrData object is constructed. Null strings
  53. // will always point to the null pointer object.
  54. {
  55.   // StrSize already accounts for one character
  56.   void *ptr = new char[StrSize + Bytes - 1];
  57.   if(!ptr) { // If allocation fails reference Null_Ptr
  58.     ptr = &Null_Ptr;
  59.     ((StrData *)ptr)->RefCount++;
  60.   }
  61.   return ptr;
  62. }
  63.  
  64. void StrData::operator delete(void *ptr)
  65. // The delete operator is overloaded so that
  66. // both the string data the reference count
  67. // are deleted.
  68. {
  69.   delete[] (char *)ptr;
  70. }
  71.  
  72. int UString::Alloc(unsigned Bytes, int GB)
  73. {
  74.   DataPtr = new(Bytes) StrData;
  75.   TextPtr = DataPtr->Data;
  76.   Len = 0;
  77.   if (IsNull()) { // Could not allocate memory for string
  78.     DimLen = 1;
  79.     GrowBy = 0;
  80.     return 0;
  81.   }
  82.   else {
  83.     DimLen = Bytes;
  84.     GrowBy = GB;
  85.     return 1;
  86.   }
  87. }
  88.       
  89. void UString::Bind(const UString &s)
  90. {
  91.   // Bind DataPtr to the same that s is bound to
  92.   DataPtr = s.DataPtr;
  93.   DataPtr->RefCount++;
  94. }
  95.  
  96. void UString::UnBind()
  97. {
  98.   DataPtr->RefCount--;
  99.  
  100.   // Make sure RefCount is a non-zero value
  101.   if(DataPtr == &StrData::Null_Ptr && DataPtr->RefCount == 0)
  102.     DataPtr->RefCount = 1;
  103.  
  104.   if(DataPtr->RefCount == 0) delete DataPtr;
  105. }
  106.  
  107. void UString::NewBinding(const UString &s)
  108. {
  109.   // Unbind string from DataPtr that holds it, and
  110.   // bind it to the DataPtr shared by s
  111.   if(DataPtr != s.DataPtr) {
  112.     UnBind();
  113.     Bind(s);
  114.   }
  115. }
  116.  
  117. UString::UString(const char *s, unsigned Bytes, int GB)
  118. {
  119.   if(Alloc(Bytes, GB)) CopyN(s, Bytes);
  120. }
  121.  
  122. UString::UString(const char *s, int GB)
  123. {
  124.   unsigned StringLen = strlen(s);
  125.   unsigned DimensionLen = StringLen;
  126.   if(GB) { // Compute next highest multiple of GB
  127.     unsigned AddValue = (DimensionLen % GB) ? GB : 0;
  128.     DimensionLen = (DimensionLen / GB) * GB + AddValue;
  129.   }
  130.   if(Alloc(DimensionLen, GB)) CopyN(s, StringLen);
  131. }
  132.  
  133. UString::UString(const UString &s, unsigned Offset, unsigned Bytes, int GB)
  134. // A constructor used to create a subset of this string.
  135. {
  136.   Bind(s);
  137.  
  138.   // Keep offset and length in range
  139.   if(Offset > s.Len-1) Offset = s.Len - 1;
  140.   if(Bytes + Offset > s.Len) Bytes = s.Len - Offset;
  141.  
  142.   // Set max and logical bounds of the substring
  143.   DimLen = Bytes;
  144.   Len = Bytes;
  145.   GrowBy = GB;
  146.   TextPtr = s.TextPtr + Offset; // Compute starting text element
  147. }
  148.  
  149. UString::UString(const UString &s)
  150. {
  151.   Bind(s);
  152.   Len = s.Len;
  153.   DimLen = s.DimLen;
  154.   GrowBy = s.GrowBy;
  155.   TextPtr = s.TextPtr;
  156. }
  157.  
  158. UString::~UString()
  159. {
  160.   UnBind();
  161. }
  162.  
  163. UString &UString::operator=(const UString &s)
  164. {
  165.   if(this != &s) {
  166.     NewBinding(s);
  167.     Len = s.Len;
  168.     DimLen = s.DimLen;
  169.     GrowBy = s.GrowBy;
  170.     TextPtr = s.TextPtr;
  171.   }
  172.   return *this;
  173. }
  174.  
  175. unsigned UString::InsReplAt(unsigned Pos,
  176.                const char *s, unsigned Bytes, int Ins)
  177. {
  178.   unsigned Room, Needed, AddVal;
  179.   
  180.   if(Ins) {
  181.     Room = DimLen - Len;
  182.     if(Bytes > Room && GrowBy != 0) {
  183.       Needed = (Bytes - Room);
  184.       AddVal = (Needed % GrowBy) ? GrowBy : 0;
  185.       Needed = (Needed / GrowBy) * GrowBy + AddVal;
  186.       Grow_By(Needed);
  187.     }
  188.   }
  189.  
  190.   if (!EnsureUnique()) return 0; // Can't update
  191.   if (Pos >= Len) Pos = Len; // Don't allow gaps
  192.  
  193.   if(Ins) { // Keep things in range. May have to truncate
  194.     if(Bytes > DimLen - Len) Bytes = DimLen - Len;
  195.     if(Pos < Len) {
  196.       // Make room in the middle for inserted data
  197.       memmove(TextPtr+Pos+Bytes, TextPtr+Pos, Len-Pos);
  198.     }
  199.   }
  200.   else {
  201.     if(Bytes > DimLen - Pos) Bytes = DimLen -Pos;
  202.   }
  203.  
  204.   // Copy in source, compute final length
  205.   memmove(TextPtr+Pos, s, Bytes);
  206.   if(Ins) {
  207.     Len += Bytes;
  208.   }
  209.   else {
  210.     if((Pos+Bytes) > Len) Len = Pos+Bytes;
  211.   }
  212.   return Bytes;
  213. }
  214.  
  215. int UString::Realloc(unsigned NewDimLen, int Keep)
  216. {
  217.   if(GrowBy == 0) return 0;
  218.  
  219.   StrData *NewStrData = new(NewDimLen) StrData;
  220.   if(NewStrData == &StrData::Null_Ptr) return 0;
  221.  
  222.   if(Keep) { // Copy old data into new space.
  223.     if(NewDimLen < Len) Len = NewDimLen;
  224.     memmove(NewStrData->Data, TextPtr, Len);
  225.   }
  226.   else { // Do not keep old data
  227.     Len = 0;
  228.   }
  229.  
  230.   UnBind();                // Unbind from old data
  231.   DataPtr = NewStrData;    // Bind to new Data
  232.   TextPtr = DataPtr->Data; // Point to starting text
  233.   DimLen = NewDimLen;      // Record new allocated length
  234.   return 1;
  235. }
  236.  
  237. int UString::EnsureUnique()
  238. {
  239.   if(!IsUnique()) {
  240.     // Create a unique clone of this string
  241.     UString buf(DimLen, GrowBy);
  242.     buf.CopyN(TextPtr, Len);
  243.     if(buf.IsNull()) return 0; // Could not copy
  244.     NewBinding(buf);           // Bind to copy
  245.     TextPtr = buf.TextPtr;     // Initialize starting text element
  246.   }
  247.   return 1;
  248. }
  249.  
  250. void UString::SetLength(unsigned Bytes)
  251. {
  252.   if(Bytes > DimLen) Bytes = DimLen;
  253.   Len = Bytes;
  254. }
  255.  
  256. void UString::CopyN(const char *s, unsigned Bytes)
  257. {
  258.   Len = 0; InsReplAt(0, s, Bytes);
  259. }
  260.  
  261. void UString::Copy(const char *s)
  262. {
  263.   CopyN(s, strlen(s));
  264. }
  265.  
  266. void UString::Copy(const UString &s)
  267. {
  268.   CopyN(s.TextPtr, s.Len);
  269. }
  270.  
  271. unsigned UString::Find(char *s, unsigned Offset)
  272. // Returns index of first occurrence of pattern char *s   
  273. // Returns 0xffff if pattern not found
  274. {
  275.   char *Start = TextPtr + Offset; // Start of string data
  276.   char *Next = Start;             // Next string element
  277.   char *Pattern = s;              // Next pattern element
  278.   unsigned i = Offset;            // Next string element index
  279.   
  280.   while(i < Len && *Pattern) {
  281.     if (*Next == *Pattern) {
  282.       Pattern++;
  283.       if(*Pattern == 0) return i; // Pattern was found
  284.       Next++;
  285.     }
  286.     else {
  287.       i++;
  288.       Start++;
  289.       Next = Start;
  290.       Pattern = s;
  291.     }
  292.   }
  293.   return NoMatch; // No match was found
  294. }
  295.  
  296. unsigned UString::IFind(char *s, unsigned Offset)
  297. // Returns index of first occurrence of pattern char *s
  298. // starting at specified offset using a case insensitive
  299. // compare. Returns 0xffff if pattern not found.
  300. {
  301.   char *Start = TextPtr + Offset; // Start of string data
  302.   char *Next = Start;             // Next string element
  303.   char *Pattern = s;              // Next pattern element
  304.   unsigned i = Offset;            // Next string element index
  305.   
  306.   while(i < Len && *Pattern) {
  307.     if (tolower(*Next) == tolower(*Pattern)) {
  308.       Pattern++;
  309.       if(*Pattern == 0) return i; // Pattern was found
  310.       Next++;
  311.     }
  312.     else {
  313.       i++;
  314.       Start++;
  315.       Next = Start;
  316.       Pattern = s;
  317.     }
  318.   }
  319.   return NoMatch; // No match was found
  320. }
  321.  
  322. unsigned UString::Find(char *s, unsigned Bytes, unsigned Offset) const
  323. // Returns index of first occurrence of pattern char *s   
  324. // Returns 0xffff if pattern not found
  325. {
  326.   char *Start = TextPtr + Offset; // Start of string data
  327.   char *Next = Start;             // Next string element
  328.   char *Pattern = s;              // Next pattern element
  329.   unsigned i = Offset, j = 1;     // String and pattern indexes
  330.   
  331.   while(i < Len && j <= Bytes) {
  332.     if (*Next == *Pattern) { // Matching character
  333.       if(j == Bytes) return i; // Entire match was found
  334.       Next++; Pattern++; j++;
  335.     }
  336.     else { // Try next spot in string
  337.       i++;
  338.       Start++;
  339.       Next = Start;
  340.       Pattern = s; j = 1;
  341.     }
  342.   }
  343.   return NoMatch; // No match was found
  344. }
  345.  
  346. unsigned UString::IFind(char *s, unsigned Bytes, unsigned Offset) const
  347. // Returns index of first occurrence of pattern char *s starting at 
  348. // specified offset using a case insensitive compare. Returns 0xffff
  349. // if pattern is not found.
  350. {
  351.   char *Start = TextPtr + Offset; // Start of string data
  352.   char *Next = Start;             // Next string element
  353.   char *Pattern = s;              // Next pattern element
  354.   unsigned i = Offset, j = 1;     // String and pattern indexes
  355.   
  356.   while(i < Len && j <= Bytes) {
  357.     if (tolower(*Next) == tolower(*Pattern)) {
  358.       if(j == Bytes) return i; // Entire match was found
  359.       Next++; Pattern++; j++;
  360.     }
  361.     else { // Try next spot in string
  362.       i++;
  363.       Start++;
  364.       Next = Start;
  365.       Pattern = s; j = 1;
  366.     }
  367.   }
  368.   return NoMatch; // No match was found
  369. }
  370.  
  371. unsigned UString::DeleteAt(unsigned Pos, unsigned Bytes)
  372. {
  373.   unsigned long buf; // long is used to prevent overflows
  374.   unsigned End;
  375.  
  376.   if(Pos < Len && Bytes !=0) {
  377.     if(!EnsureUnique()) return 0; // Can't update
  378.     buf = Pos + Bytes;
  379.     if(buf > Len) buf = Len;
  380.     End = unsigned(buf);
  381.     Bytes = End - Pos;
  382.     // Move the elements up to take their place
  383.     memmove(TextPtr+Pos, TextPtr+End, Len-End);
  384.     Len -= Bytes;
  385.   }
  386.   else
  387.     Bytes = 0;
  388.  
  389.   return Bytes;
  390. }
  391.  
  392. void UString::Fill(const char *p, unsigned Bytes, unsigned Offset, unsigned Ln)
  393. {
  394.   if (DimLen == 0 || Bytes == 0) return; // Nothing to do
  395.   if (!EnsureUnique()) return;           // Can't fill
  396.   // Keep parms in range
  397.   if (Ln == 0) Ln = DimLen;
  398.   if (Offset >= DimLen) Offset = DimLen-1; // DimLen must be > 0!
  399.   if (Ln + Offset > DimLen) Ln = DimLen - Offset;
  400.   Len = Ln + Offset;
  401.   char *t = TextPtr + Offset;
  402.   if (Bytes == 1) {
  403.      // Use fast method for single character fills
  404.      memset(t, *p, Ln);
  405.   }
  406.   else {
  407.     // Multi-character pattern fills
  408.     unsigned np = Bytes;
  409.     const char *s = p;
  410.     while(Ln) {
  411.       *t++ = *s++;
  412.       if (np == 1) {
  413.          np = Bytes;
  414.          s = p;
  415.       } else np--;
  416.       Ln--;
  417.     }
  418.   }
  419. }
  420.  
  421. UString UString::Mid(unsigned Index, unsigned Bytes) const 
  422. // Returns substring starting at Index, for number of bytes
  423. {
  424.   // Call the substring constructor 
  425.   UString buf(*this, Index, Bytes);
  426.  
  427.   return buf;
  428. }
  429.  
  430. UString UString::Left(unsigned Ln) const
  431. // Returns left substring of length Ln
  432. {
  433.   // Call the substring constructor 
  434.   UString buf(*this, 0, Ln);
  435.  
  436.   return buf;
  437. }
  438.  
  439. UString UString::Right(unsigned Ln) const
  440. // Returns right substring of length Ln
  441. {
  442.   if(Ln > Len) Ln = Len;
  443.  
  444.   // Call the substring constructor 
  445.   UString buf(*this, Len - Ln, Ln);
  446.   
  447.   return buf;
  448. }
  449.  
  450. const char *UString::NullTermStr()
  451. // If a null byte has to be added, the string is grown.
  452. // If the string can't grow, the last character is replaced.
  453. {
  454.   static char Null_String[1] = {0};
  455.   if(IsNull()) return Null_String;
  456.   char *ptr = TextPtr;
  457.   unsigned Bytes = Len;
  458.  
  459.   // Search for null byte already in string
  460.   while(Bytes--) if(*ptr++ == 0) return TextPtr;
  461.  
  462.   // Grow if length == allocated length grow the string
  463.   if(Len == DimLen) Grow();
  464.  
  465.   // Make sure string can grow and is unique
  466.   if(DimLen == 0 || !EnsureUnique()) return Null_String;
  467.   if(Len == DimLen) Len--; // Forced to replace good character
  468.   TextPtr[Len] = 0;
  469.   return TextPtr;
  470. }
  471.  
  472. char *UString::c_str() 
  473. // If a null byte has to be added, the string is grown.
  474. // If the string can't grow, the last character is replaced.
  475. {
  476.   static char Null_String[1] = {0};
  477.   if(IsNull()) return Null_String;
  478.   char *ptr = TextPtr;
  479.   unsigned Bytes = Len;
  480.  
  481.   // Search for null byte already in string
  482.   while(Bytes--) if(*ptr++ == 0) return TextPtr;
  483.  
  484.   // Grow if length == allocated length grow the string
  485.   if(Len == DimLen) Grow();
  486.  
  487.   // Make sure string can grow and is unique
  488.   if(DimLen == 0 || !EnsureUnique()) return Null_String;
  489.   if(Len == DimLen) Len--; // Forced to replace good character
  490.   TextPtr[Len] = 0;
  491.   return TextPtr;
  492. }
  493.  
  494. const char *UString::c_str() const 
  495. // If a null byte has to be added the last character is replaced.
  496. {
  497.   static char Null_String[1] = {0};
  498.   if(IsNull()) return Null_String;
  499.   char *ptr = TextPtr;
  500.   unsigned Bytes = Len;
  501.  
  502.   // Search for null byte already in string
  503.   while(Bytes--) if(*ptr++ == 0) return TextPtr;
  504.  
  505.   TextPtr[Len] = 0;
  506.   return TextPtr;
  507. }
  508.  
  509. ostream &operator<<(ostream &os, const UString &s)
  510. {
  511.   return os.write(s.TextPtr, s.Len);
  512. }
  513.  
  514. istream &operator>>(istream &os, UString &s)
  515. {
  516.   os >> setw(s.DimLen) >> s.TextPtr;
  517.   s.SetLength(strlen(s.TextPtr));
  518.   return os;
  519. }
  520.  
  521. int StringCompare(const UString &a, const UString &b)
  522. // Returns -1 if a < b, 0 if a == b, and 1 if a > b
  523. {
  524.   unsigned an = a.Len;
  525.   unsigned bn = b.Len;
  526.   unsigned sn = (an < bn) ? an : bn;
  527.   unsigned char *ap = (unsigned char *)a.TextPtr;
  528.   unsigned char *bp = (unsigned char *)b.TextPtr;
  529.  
  530.   for(unsigned i = 0; i < sn; i++) {
  531.     if(*ap < *bp) return -1;
  532.     if(*ap++ > *bp++) return 1;
  533.   }
  534.  
  535.   if(an == bn) return 0;
  536.   if(an < bn) return -1;
  537.   return 1;
  538. }
  539.  
  540. // General-purpose string routines that need to be ported from UNIX to DOS
  541. // -----------------------------------------------------------------------
  542.  
  543. int CaseICmp(const UString &s1, const UString &s2)
  544. // Compare two UString objects without regard to the case of the letters
  545. {
  546. #if defined (__DOS__) 
  547.   // Case-insensitive compare for most DOS/Windows Compilers
  548.   return stricmp(s1.c_str(), s2.c_str());
  549. #elif defined(__VISUALC__) || ( defined(__MWERKS__) && defined(__INTEL__) )
  550.   return stricmp(s1.c_str(), s2.c_str());
  551. #elif defined(__SC__)
  552.   return stricmp(s1.c_str(), s2.c_str());
  553. #elif defined(__SALFORDC__)
  554.   return stricmp(s1.c_str(), s2.c_str());
  555. #elif defined(__BORLANDC__)
  556.   return stricmp(s1.c_str(), s2.c_str());
  557. #elif defined(__WATCOMC__)
  558.   return stricmp(s1.c_str(), s2.c_str());
  559. #elif defined (__UNIX__) || defined(__GNUWIN32__)
  560.   // Case-insensitive compare for most UNIX Compilers
  561.   return strcasecmp(s1.c_str(), s2.c_str());
  562. #else
  563.     register char c1, c2;
  564.     const char *str1 = s1.c_str(); const char *str2 = s2.c_str();
  565.     do {
  566.       c1 = tolower(*str1++);
  567.       c2 = tolower(*str2++);
  568.     } while ( c1 && (c1 == c2) );
  569.  
  570.     return c1 - c2;
  571. #endif 
  572. }
  573.  
  574. int CaseICmp(UString &s1, UString &s2)
  575. // Compare two UString objects without regard to the case of the letters
  576. {
  577. #if defined (__DOS__) 
  578.   // Case-insensitive compare for most DOS/Windows Compilers
  579.   return stricmp(s1.c_str(), s2.c_str());
  580. #elif defined(__VISUALC__) || ( defined(__MWERKS__) && defined(__INTEL__) )
  581.   return stricmp(s1.c_str(), s2.c_str());
  582. #elif defined(__SC__)
  583.   return stricmp(s1.c_str(), s2.c_str());
  584. #elif defined(__SALFORDC__)
  585.   return stricmp(s1.c_str(), s2.c_str());
  586. #elif defined(__BORLANDC__)
  587.   return stricmp(s1.c_str(), s2.c_str());
  588. #elif defined(__WATCOMC__)
  589.   return stricmp(s1.c_str(), s2.c_str());
  590. #elif defined (__UNIX__) || defined(__GNUWIN32__)
  591.   // Case-insensitive compare for most UNIX Compilers
  592.   return strcasecmp(s1.c_str(), s2.c_str());
  593. #else
  594.     register char c1, c2;
  595.     char *str1 = s1.c_str(); char *str2 = s2.c_str();
  596.     do {
  597.       c1 = tolower(*str1++);
  598.       c2 = tolower(*str2++);
  599.     } while ( c1 && (c1 == c2) );
  600.  
  601.     return c1 - c2;
  602. #endif 
  603. }
  604. // ----------------------------------------------------------- //
  605. // ------------------------------- //
  606. // --------- End of File --------- //
  607. // ------------------------------- //
  608.