[interfacekit] A patch for BString

Hi everyone,

I have finally found the time to finish the patch (which is rather large, 
I'm afraid...).

Due to the comments on the list, I have decided to make as few changes to 
the behaviour of BString (as compared to R5) as possible and only change 
things if the behaviour of BString is a clear bug or *very* annoying.

A summary of the changes:

- memory-allocation has been centralized and BString is now capable of
  handling out-of-memory situations (somewhat) gracefully: The string
  is just left as is (as it was before the call). 

- all length - arguments are clamped to the real length now, meaning that
    BString s( "some text");
        s.Remove( 4, 30);
  now correctly yields " text" (not "some text" as R5 does).

- passing negative values to a fromOffset argument causes BString to return
  B_ERROR if that is possible or send the app straight to the debugger.
  An exception to this rule are the Insert()-methods which accept negative
  values just like the R5-implemantation does (but we avoid crashing for 
  offsets that exceed the string-length).

- passing a insertion-position that exceeds the string-length sends the
  app to the debugger.

- passing a fromOffset/beforeOffset to any search-method returns B_ERROR.

- BString's only error-code is now B_ERROR just to be compatible with the
  documentation (R5 only ever returns B_ERROR, it crashes in cases where 
the 
  openbeos implementation used to return B_BAD_VALUE).

- I have streamlined the (I)Replace... and Remove... - methods such that
  BString never uses _OpenAtBy() / _ShrinkAtBy() inside a loop. This way
  it avoids the pathetic performance of the R5-implementation (which is
  indicated by the new tests that use large(ish) datasets).

- BString should no longer crash if given any weird offset/length arguments.

- Several tests have been added.

There is (at least >:oP ) one open problem: Since some tests will trigger a 
call to the debugger if compiled with DEBUG, the unit-test will never pass. 
That's a bit awkward, so I have thought about using a signal-handler in the 
test-app that could be used to implement a check for a signal like this:

        BString s;
        CPPUNIT_ASSERT_DEBUGGER( s.Insert( "dummy", 20));

Does this kind of stuff already exist in cppunit? Is it feasible at all?


In general, it seems obvious that BString needs some more work at a later 
stage. IMHO the idea of accepting negative offsets/values does make sense 
and there is a need for throwing exceptions (at least bad_alloc) but I 
believe this is all work that should go into R2.

As always: comments/complaints/suggestions are welcome!

cheers,
        Oliver
Index: headers/os/support/String.h
===================================================================
RCS file: /cvsroot/open-beos/current/headers/os/support/String.h,v
retrieving revision 1.7
diff -u -w -r1.7 String.h
--- headers/os/support/String.h 15 Feb 2003 20:08:36 -0000      1.7
+++ headers/os/support/String.h 6 Nov 2003 09:21:24 -0000
@@ -318,6 +318,14 @@
        void                    _SetUsingAsCString(bool) {}
        void                    _AssertNotUsingAsCString() const {}
 #endif
+
+       char                    *_Alloc( int32);
+
+       struct PosVect;
+       void                    _ReplaceAtPositions( const PosVect* positions,
+                                                                               
           int32 searchLen, 
+                                                                               
           const char* with,
+                                                                               
           int32 withLen);
 
 protected:
        char *_privateData;
Index: src/kits/support/String.cpp
===================================================================
RCS file: /cvsroot/open-beos/current/src/kits/support/String.cpp,v
retrieving revision 1.32
diff -u -r1.32 String.cpp
--- src/kits/support/String.cpp 11 Feb 2003 19:16:18 -0000      1.32
+++ src/kits/support/String.cpp 6 Nov 2003 09:28:30 -0000
@@ -19,11 +19,12 @@
 //     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 //     DEALINGS IN THE SOFTWARE.
 //
-//     File Name:              String.cpp
-//     Author(s):              Marc Flerackers (mflerackers@xxxxxxxxxx)
-//                                     Stefano Ceccherini (burton666@xxxxxxxxx)
+//     File Name:     String.cpp
+//     Author(s):     Marc Flerackers (mflerackers@xxxxxxxxxx)
+//                Stefano Ceccherini (burton666@xxxxxxxxx)
+//                Oliver Tappe (openbeos@xxxxxxxxxxxxxxx)
 //
-//     Description:    String class supporting common string operations.  
+//     Description:   String class supporting common string operations.  
 
//------------------------------------------------------------------------------
 
 // Standard Includes 
-----------------------------------------------------------
@@ -46,6 +47,104 @@
 #define KEEP_CASE false
 #define IGNORE_CASE true
 
+// define proper names for count-option of _DoReplace()
+#define REPLACE_ALL 0x7FFFFFFF
+
+
+
+// helper function, returns minimum of two given values (but clamps to 0):
+static inline int32 min_clamp0( int32 num1, int32 num2) 
+{ 
+       if (num1<num2)
+               return num1 > 0 ? num1 : 0;
+       else
+               return num2 > 0 ? num2 : 0;
+}
+
+// helper function, returns length of given string (but clamps to given 
maximum):
+static inline int32 strlen_clamp( const char* str, int32 max) 
+{      // this should yield 0 for max<0:
+       int32 len=0;
+       while( len<max && *str++)
+               len++;
+       return len;
+}
+
+// helper function, massages given pointer into a legal c-string:
+static inline const char* safestr( const char* str) 
+{
+       return str ? str : "";
+}
+
+
+// helper class for BString::_ReplaceAtPositions():
+struct BString::PosVect {
+       PosVect() 
+       : size( 0)
+       , bufSize( 20)
+       , buf( NULL)
+       {
+       }
+       ~PosVect()
+       {
+               if (buf)
+                       free( buf);
+       }
+       bool Add( int32 pos)
+       {
+               if (!buf || size==bufSize) {
+                       if (buf)
+                               bufSize *= 2;
+                       int32* newBuf = (int32*)realloc( buf, 
bufSize*sizeof(int32));
+                       if (!newBuf)
+                               return false;
+                       buf = newBuf;
+               }
+               buf[size++] = pos;
+               return true;
+       }
+       inline int32 ItemAt( int32 idx) const
+       {
+               return buf[idx];
+       }
+       inline int32 CountItems() const
+       {
+               return size;
+       }
+private:
+       int32 size;
+       int32 bufSize;
+       int32* buf;
+};
+
+
+// helper macro that is used to fall into debugger if a given param check 
fails:
+#ifdef DEBUG
+       #define CHECK_PARAM( expr, msg) \
+       if (!(expr)) \
+               debugger( msg)
+
+       #define CHECK_PARAM_RET( expr, msg, retval) \
+       if (!(expr)) \
+               debugger( msg)
+
+       #define CHECK_PARAM_VOID( expr, msg) \
+       if (!(expr)) \
+               debugger( msg)
+#else
+       #define CHECK_PARAM( expr, msg) \
+       if (!(expr)) \
+               return *this
+
+       #define CHECK_PARAM_RET( expr, msg, retval) \
+       if (!(expr)) \
+               return (retval);
+
+       #define CHECK_PARAM_VOID( expr, msg) 
+#endif
+
+// 
-----------------------------------------------------------------------------
+
 /*!
        \class BString
        \brief String class supporting common string operations
@@ -56,6 +155,7 @@
        
        \author <a href='mailto:mflerackers@xxxxxxxxxx>Marc Flerackers</a>
        \author <a href='mailto:burton666@xxxxxxxxxxx>Stefano Ceccherini</a>
+       \author <a href='mailto:openbeos@xxxxxxxxxxxxxx>Oliver Tappe</a>
 */     
 
 /*!    \var char* BString::_privateData
@@ -105,8 +205,8 @@
 {
        if (str != NULL)
        {
-               int32 len = (int32)strlen(str);
-               _Init(str, min_c(len, maxLength));
+               int32 len = strlen_clamp( str, maxLength);
+               _Init(str, len);
        }
 }
 
@@ -177,10 +277,9 @@
                count++;
 
                // Jump to next UTF8 character
-                for (; (*start & 0xc0) == 0x80; start++);
+               for (; (*start & 0xc0) == 0x80; start++);
        }
 
-       
        return count;
 }
 
@@ -228,12 +327,7 @@
 BString&
 BString::operator=(char c)
 {
-       int32 curLen = Length();
-       
-       if (curLen != 1)
-               _GrowBy(1 - curLen);
-       _privateData[0] = c;
-       
+       _DoAssign(&c, 1);
        return *this;
 }
 
@@ -250,8 +344,8 @@
 {
        if (str != NULL)
        {
-               int32 len = (int32)strlen(str);
-               _DoAssign(str, min_c(length, len));
+               int32 len = strlen_clamp( str, length);
+               _DoAssign(str, len);
        }
        else 
                _GrowBy(-Length()); // Empties the string
@@ -309,7 +403,7 @@
 BString::SetTo(const BString &string, int32 length)
 {
        if (&string != this) // Avoid auto-assignment
-               _DoAssign(string.String(), min_c(length, string.Length()));
+               _DoAssign(string.String(), min_clamp0(length, string.Length()));
        return *this;
 }
 
@@ -326,16 +420,18 @@
 {
        if (&from == this) // Avoid auto-adoption
                return *this;
-               
+
+       int32 len = min_clamp0(length, from.Length());
+
        if (_privateData)
                free(_privateData - sizeof(int32));
 
        /* "steal" the data from the given BString */
        _privateData = from._privateData;
        from._privateData = NULL;
-
-       if (length < Length())
-               _privateData = _GrowBy(length - Length()); //Negative
+       
+       if (len < Length())
+               _GrowBy(len - Length()); // Negative, we truncate
 
        return *this;
 }
@@ -351,13 +447,12 @@
 BString&
 BString::SetTo(char c, int32 count)
 {
+       if (count<0)
+               count = 0;
        int32 curLen = Length();
        
-       if (curLen != count)
-               _GrowBy(count - curLen);
-               
-       memset(_privateData, c, count);
-       
+       if (curLen == count || _GrowBy(count - curLen)) 
+               memset(_privateData, c, count);
        return *this;   
 }
 
@@ -374,8 +469,13 @@
 BString &
 BString::CopyInto(BString &into, int32 fromOffset, int32 length) const
 {
-       if (&into != this)
-               into.SetTo(String() + fromOffset, length);
+       if (&into != this) {
+               CHECK_PARAM_RET( fromOffset>=0, "'fromOffset' must not be 
negative!", 
+                                                         into);
+               CHECK_PARAM_RET( fromOffset<=Length(), "'fromOffset' exceeds 
length!", 
+                                                         into);
+               into.SetTo( String() + fromOffset, length);
+       }
        return into;
 }
 
@@ -391,8 +491,9 @@
 {
        if (into != NULL)
        {
-               int32 len = Length() - fromOffset;
-               len = min_c(len, length);
+               CHECK_PARAM_VOID( fromOffset>=0, "'fromOffset' must not be 
negative!");
+               CHECK_PARAM_VOID( fromOffset<=Length(), "'fromOffset' exceeds 
length!");
+               int32 len = min_clamp0( length, Length() - fromOffset);
                memcpy(into, _privateData + fromOffset, len);
        }
 }
@@ -421,9 +522,7 @@
 BString&
 BString::operator+=(char c)
 {
-       _GrowBy(1);
-       _privateData[Length() - 1] = c;
-       
+       _DoAppend(&c, 1);
        return *this;
 }
 
@@ -437,7 +536,7 @@
 BString&
 BString::Append(const BString &string, int32 length)
 {
-       _DoAppend(string.String(), min_c(length, string.Length()));
+       _DoAppend(string.String(), min_clamp0(length, string.Length()));
        return *this;
 }
 
@@ -453,8 +552,8 @@
 {
        if (str != NULL) 
        {
-               int32 len = (int32)strlen(str);
-               _DoAppend(str, min_c(len, length));
+               int32 len = strlen_clamp( str, length);
+               _DoAppend(str, len);
        }       
        return *this;
 }
@@ -470,8 +569,8 @@
 BString::Append(char c, int32 count)
 {
        int32 len = Length();
-       _GrowBy(count);
-       memset(_privateData + len, c, count);
+       if (count>0 && _GrowBy(count))
+               memset(_privateData + len, c, count);
 
        return *this;
 }
@@ -517,8 +616,8 @@
 {
        if (str != NULL) 
        {
-               int32 len = (int32)strlen(str);
-               _DoPrepend(str, min_c(len, length));            
+               int32 len = strlen_clamp( str, length);
+               _DoPrepend(str, len);
        }
        return *this;
 }
@@ -534,7 +633,7 @@
 BString::Prepend(const BString &string, int32 len)
 {
        if (&string != this)
-               _DoPrepend(string.String(), min_c(len, string.Length()));
+               _DoPrepend(string.String(), min_clamp0(len, string.Length()));
        return *this;
 }
 
@@ -548,8 +647,8 @@
 BString&
 BString::Prepend(char c, int32 count)
 {
-       _OpenAtBy(0, count);
-       memset(_privateData, c, count);
+       if (count>0 && _OpenAtBy(0, count))
+               memset(_privateData, c, count);
        
        return *this;
 }
@@ -567,13 +666,18 @@
 {
        if (str != NULL)
        {
+               CHECK_PARAM( pos<=Length(), "'pos' exceeds length!");
+               int32 len = (int32)strlen(str);
                if (pos < 0)
                {
-                       str -= pos;
+                       int32 skipLen = min_clamp0( -1*pos, len);
+                       str += skipLen;
+                       len -= skipLen;
                        pos = 0;
-               }
-               _privateData = _OpenAtBy(pos, strlen(str));
-               memcpy(_privateData + pos, str, strlen(str));
+               } else
+                       pos = min_clamp0( pos, Length());
+               if (_OpenAtBy(pos, len))
+                       memcpy(_privateData + pos, str, len);
        }
        return *this;
 }
@@ -591,15 +695,18 @@
 {
        if (str != NULL)
        {
-               if (pos < 0) 
+               CHECK_PARAM( pos<=Length(), "'pos' exceeds length!");
+               int32 len = strlen_clamp(str, length);
+               if (pos < 0)
                {
-                       str -= pos;
+                       int32 skipLen = min_clamp0( -1*pos, len);
+                       str += skipLen;
+                       len -= skipLen;
                        pos = 0;
-               }
-               int32 len = (int32)strlen(str);
-               len = min_c(len, length);
-               _OpenAtBy(pos, len);
-               memcpy(_privateData + pos, str, len);
+               } else
+                       pos = min_clamp0( pos, Length());
+               if (_OpenAtBy(pos, len))
+                       memcpy(_privateData + pos, str, len);
        }
        return *this;
 }
@@ -616,14 +723,8 @@
 BString&
 BString::Insert(const char *str, int32 fromOffset, int32 length, int32 pos)
 {
-       if (str != NULL) 
-       {
-               int32 len = (int32)strlen(str);
-               len = min_c(len - fromOffset, length);
-               _privateData = _OpenAtBy(pos, len);
-               memcpy(_privateData + pos, str + fromOffset, len);
-       }
-       return *this;
+       CHECK_PARAM( fromOffset>=0, "'fromOffset' must not be negative!");
+       return Insert( str+fromOffset, length, pos);
 }
 
 
@@ -669,8 +770,10 @@
 BString&
 BString::Insert(const BString &string, int32 fromOffset, int32 length, int32 
pos)
 {
-       if (&string != this)
-               Insert(string.String(), fromOffset, length, pos); //TODO: 
Optimize
+       if (&string != this) {
+               CHECK_PARAM( fromOffset>=0, "'fromOffset' must not be 
negative!");
+               Insert( string.String()+fromOffset, length, pos);
+       }
        return *this;
 }
 
@@ -685,9 +788,15 @@
 BString&
 BString::Insert(char c, int32 count, int32 pos)
 {
-       _OpenAtBy(pos, count);
-       memset(_privateData + pos, c, count);
-       
+       CHECK_PARAM( pos<=Length(), "'pos' exceeds length!");
+       if (pos < 0)
+       {
+               count = max_c( count + pos, 0);
+               pos = 0;
+       } else
+               pos = min_clamp0( pos, Length());
+       if (count > 0 && _OpenAtBy(pos, count))
+               memset(_privateData + pos, c, count);
        return *this;
 }
 
@@ -696,7 +805,7 @@
 // Truncate
 /*! \brief Truncate the string to the new length.
        \param newLength The new lenght of the string.
-       \param lazy Currently unused. (?)
+       \param lazy If true, the memory-optimisation is postponed to later
        \return This function always returns *this .
 */
 BString&
@@ -707,13 +816,13 @@
        
        int32 curLen = Length();
                
-       if (newLength < curLen) 
-       {
-
-       //TODO: Implement lazy truncate?
-       
-               _GrowBy(newLength - curLen); //Negative 
-               _privateData[newLength] = '\0';
+       if (newLength < curLen) {
+               if (lazy) {
+                       // don't free memory yet, just set new length:
+                       _SetLength( newLength);
+                       _privateData[newLength] = '\0';
+               } else
+                       _GrowBy(newLength - curLen); //Negative 
        }
        return *this;
 }
@@ -728,7 +837,15 @@
 BString&
 BString::Remove(int32 from, int32 length)
 {
-       _ShrinkAtBy(from, length);
+       int32 len = Length();
+       if (from < 0)
+       {
+               int32 skipLen = min_clamp0( from, len);
+               len -= skipLen;
+               from = 0;
+       } else
+               from = min_clamp0( from, len);
+       _ShrinkAtBy( from, min_clamp0(length, len-from));
        return *this;
 }
 
@@ -775,7 +892,7 @@
 BString&
 BString::RemoveAll(const BString &string)
 {
-       return _DoReplace( string.String(), "", 0x7FFFFFFF, 0, KEEP_CASE);
+       return _DoReplace( string.String(), "", REPLACE_ALL, 0, KEEP_CASE);
 }
 
 
@@ -824,7 +941,7 @@
 BString&
 BString::RemoveAll(const char *str)
 {
-       return _DoReplace( str, "", 0x7FFFFFFF, 0, KEEP_CASE);
+       return _DoReplace( str, "", REPLACE_ALL, 0, KEEP_CASE);
 }
 
 
@@ -836,26 +953,7 @@
 BString&
 BString::RemoveSet(const char *setOfCharsToRemove)
 {
-       char* buf;
-       if (setOfCharsToRemove && (buf=LockBuffer( Length())) != NULL) {
-               char* oldPos = buf;
-               char* newPos = buf;
-               int32 len;
-               int32 lenToGo = Length();
-               while( (len = strcspn( oldPos, setOfCharsToRemove)) < lenToGo) {
-                       if (oldPos>newPos)
-                               memmove( newPos, oldPos, len);
-                       newPos += len++;
-                       oldPos += len;
-                       lenToGo -= len;
-               }
-               if (oldPos>newPos)
-                       memmove( newPos, oldPos, lenToGo);
-               newPos += lenToGo;
-               *newPos = 0;
-               UnlockBuffer( newPos-buf);
-       }               
-       return *this;
+       return ReplaceSet( setOfCharsToRemove, "");
 }
 
 
@@ -864,24 +962,27 @@
        \param into The BString where to move the object.
        \param from The offset (zero based) where to begin the move
        \param length The amount of bytes to move.
-       \return This function always returns *this .
+       \return This function always returns into.
 */
 BString&
 BString::MoveInto(BString &into, int32 from, int32 length)
 {
-       if (&into == this)
+       CHECK_PARAM_RET( from>=0, "'from' must not be negative!", into);
+       CHECK_PARAM_RET( from<=Length(), "'from' exceeds length!", into);
+       int32 len = min_clamp0( length, Length() - from);
+       if (&into == this) {
+               /* [zooey]: to be activated later (>R1):
+               // strings are identical, just move the data:
+               if (from>0 && _privateData)
+                       memmove( _privateData, _privateData+from, len);
+               Truncate( len);
+               */
                return *this;
-       
-       int32 len = Length() - from;
-       
-       len = min_c(len, length);
-       
-       into.SetTo(String() + from, length);
-       
-       if (from + length <= Length())
-               _privateData = _ShrinkAtBy(from, len);
+       }
+       into.SetTo(String() + from, len);
+       _ShrinkAtBy(from, len);
 
-       return *this;
+       return into;
 }
 
 
@@ -896,11 +997,13 @@
 {
        if (into != NULL)
        {
-               memcpy(into, String() + from, length);
-               if (from + length <= Length())
-                       _privateData = _ShrinkAtBy(from, length);
-               into[length] = '\0';
-       }                
+               CHECK_PARAM_VOID( from>=0, "'from' must not be negative!");
+               CHECK_PARAM_VOID( from<=Length(), "'from' exceeds length!");
+               int32 len = min_clamp0(length, Length() - from);
+               memcpy(into, String() + from, len);
+               into[len] = '\0';
+               _ShrinkAtBy(from, len);
+       }
 }
 
 
@@ -908,35 +1011,35 @@
 bool
 BString::operator<(const char *string) const
 {
-       return strcmp(String(), string) < 0;
+       return strcmp(String(), safestr(string)) < 0;
 }
 
 
 bool
 BString::operator<=(const char *string) const
 {
-       return strcmp(String(), string) <= 0;
+       return strcmp(String(), safestr(string)) <= 0;
 }
 
 
 bool
 BString::operator==(const char *string) const
 {
-       return strcmp(String(), string) == 0;
+       return strcmp(String(), safestr(string)) == 0;
 }
 
 
 bool
 BString::operator>=(const char *string) const
 {
-       return strcmp(String(), string) >= 0;
+       return strcmp(String(), safestr(string)) >= 0;
 }
 
 
 bool
 BString::operator>(const char *string) const
 {
-       return strcmp(String(), string) > 0;
+       return strcmp(String(), safestr(string)) > 0;
 }
 
 
@@ -951,7 +1054,7 @@
 int
 BString::Compare(const char *string) const
 {
-       return strcmp(String(), string);
+       return strcmp(String(), safestr(string));
 }
 
 
@@ -965,7 +1068,7 @@
 int
 BString::Compare(const char *string, int32 n) const
 {
-       return strncmp(String(), string, n);
+       return strncmp(String(), safestr(string), n);
 }
 
 
@@ -979,7 +1082,7 @@
 int
 BString::ICompare(const char *str) const
 {
-       return strcasecmp(String(), str);
+       return strcasecmp(String(), safestr(str));
 }
 
 
@@ -993,7 +1096,7 @@
 int
 BString::ICompare(const char *str, int32 n) const
 {
-       return strncasecmp(String(), str, n);
+       return strncasecmp(String(), safestr(str), n);
 }
 
 
@@ -1021,7 +1124,7 @@
 BString::FindFirst(const char *string) const
 {
        if (string == NULL)
-               return B_BAD_VALUE;
+               return B_ERROR;
        return _ShortFindAfter(string, strlen(string));
 }
 
@@ -1037,7 +1140,10 @@
 int32
 BString::FindFirst(const BString &string, int32 fromOffset) const
 {
-       return _FindAfter(string.String(), fromOffset, string.Length());
+       if (fromOffset<0)
+               return B_ERROR;
+       return _FindAfter( string.String(), min_clamp0(fromOffset, Length()),
+                                                   string.Length());
 }
 
 
@@ -1052,9 +1158,9 @@
 int32
 BString::FindFirst(const char *string, int32 fromOffset) const
 {
-       if (string == NULL)
-               return B_BAD_VALUE;
-       return _FindAfter(string, fromOffset, strlen(string));
+       if (string == NULL || fromOffset<0)
+               return B_ERROR;
+       return _FindAfter(string, min_clamp0(fromOffset, Length()), 
strlen(string));
 }
 
 
@@ -1067,10 +1173,10 @@
 int32
 BString::FindFirst(char c) const
 {      
-       char *start = _privateData;
-       char *end = _privateData + Length(); /* String's end */
+       const char *start = String();
+       const char *end = String() + Length(); /* String's end */
        
-       /* Scans the string until we found the character, */
+       /* Scans the string until we find the character, */
        /* or we hit the string's end */
        while(start != end && *start != c)
                start++;
@@ -1078,7 +1184,7 @@
        if (start == end)
                return B_ERROR;
                        
-       return start - _privateData;
+       return start - String();
 }
 
 
@@ -1093,8 +1199,10 @@
 int32
 BString::FindFirst(char c, int32 fromOffset) const
 {
-       char *start = _privateData + fromOffset;
-       char *end = _privateData + Length(); /* String's end */
+       if (fromOffset<0)
+               return B_ERROR;
+       const char *start = String() + min_clamp0( fromOffset, Length());
+       const char *end = String() + Length(); /* String's end */
        
        /* Scans the string until we found the character, */
        /* or we hit the string's end */
@@ -1104,7 +1212,7 @@
        if (start >= end)
                return B_ERROR;
                        
-       return start - _privateData;
+       return start - String();
 }
 
 
@@ -1131,7 +1239,7 @@
 BString::FindLast(const char *string) const
 {
        if (string == NULL)
-               return B_BAD_VALUE;
+               return B_ERROR;
        return _FindBefore(string, Length(), strlen(string));
 }
 
@@ -1147,7 +1255,10 @@
 int32
 BString::FindLast(const BString &string, int32 beforeOffset) const
 {
-       return _FindBefore(string.String(), beforeOffset, string.Length()); 
+       if (beforeOffset<0)
+               return B_ERROR;
+       return _FindBefore(string.String(), min_clamp0(beforeOffset,Length()), 
+                                                        string.Length()); 
 }
 
 
@@ -1161,9 +1272,9 @@
 int32
 BString::FindLast(const char *string, int32 beforeOffset) const
 {
-       if (string == NULL)
-               return B_BAD_VALUE;
-       return _FindBefore(string, beforeOffset, strlen(string));
+       if (string == NULL || beforeOffset<0)
+               return B_ERROR;
+       return _FindBefore(string, min_clamp0(beforeOffset,Length()), 
strlen(string));
 }
 
 
@@ -1176,8 +1287,8 @@
 int32
 BString::FindLast(char c) const
 {
-       char *start = _privateData;
-       char *end = _privateData + Length(); /* String's end */
+       const char *start = String();
+       const char *end = String() + Length(); /* String's end */
        
        /* Scans the string backwards until we found the character, */
        /* or we reach the string's start */
@@ -1187,7 +1298,7 @@
        if (end == start)
                return B_ERROR;
                        
-       return end - _privateData;
+       return end - String();
 }
 
 
@@ -1202,8 +1313,10 @@
 int32
 BString::FindLast(char c, int32 beforeOffset) const
 {
-       char *start = _privateData;
-       char *end = _privateData + Length() - beforeOffset;
+       if (beforeOffset < 0)
+               return B_ERROR;
+       const char *start = String();
+       const char *end = String() + Length() - beforeOffset;
        
        /* Scans the string backwards until we found the character, */
        /* or we reach the string's start */
@@ -1213,7 +1326,7 @@
        if (end <= start)
                return B_ERROR;
                        
-       return end - _privateData;
+       return end - String();
 }
 
 
@@ -1228,7 +1341,7 @@
 BString::IFindFirst(const char *string) const
 {
        if (string == NULL)
-               return B_BAD_VALUE;
+               return B_ERROR;
        return _IFindAfter(string, 0, strlen(string));
 }
 
@@ -1236,16 +1349,19 @@
 int32
 BString::IFindFirst(const BString &string, int32 fromOffset) const
 {
-       return _IFindAfter(string.String(), fromOffset, string.Length());
+       if (fromOffset < 0)
+               return B_ERROR;
+       return _IFindAfter(string.String(), min_clamp0(fromOffset,Length()), 
+                                                   string.Length());
 }
 
 
 int32
 BString::IFindFirst(const char *string, int32 fromOffset) const
 {
-       if (string == NULL)
-               return B_BAD_VALUE;
-       return _IFindAfter(string, fromOffset, strlen(string));
+       if (string == NULL || fromOffset < 0)
+               return B_ERROR;
+       return _IFindAfter(string, min_clamp0(fromOffset,Length()), 
strlen(string));
 }
 
 
@@ -1260,7 +1376,7 @@
 BString::IFindLast(const char *string) const
 {
        if (string == NULL)
-               return B_BAD_VALUE;
+               return B_ERROR;
        return _IFindBefore(string, Length(), strlen(string));
 }
 
@@ -1268,16 +1384,20 @@
 int32
 BString::IFindLast(const BString &string, int32 beforeOffset) const
 {
-       return _IFindBefore(string.String(), beforeOffset, string.Length());
+       if (beforeOffset < 0)
+               return B_ERROR;
+       return _IFindBefore(string.String(), min_clamp0(beforeOffset,Length()), 
+                                                         string.Length());
 }
 
 
 int32
 BString::IFindLast(const char *string, int32 beforeOffset) const
 {
-       if (string == NULL)
-               return B_BAD_VALUE;
-       return _IFindBefore(string, beforeOffset, strlen(string));
+       if (string == NULL || beforeOffset < 0)
+               return B_ERROR;
+       return _IFindBefore(string, min_clamp0(beforeOffset,Length()), 
+                                                         strlen(string));
 }
 
 
@@ -1309,13 +1429,13 @@
 BString&
 BString::ReplaceAll(char replaceThis, char withThis, int32 fromOffset)
 {
-       for (int32 pos;;) 
+       CHECK_PARAM( fromOffset>=0, "'fromOffset' must not be negative!");
+       for (int32 pos = min_clamp0(fromOffset,Length());;) 
        {
-               pos = FindFirst(replaceThis, fromOffset);
+               pos = FindFirst(replaceThis, pos);
                if (pos < 0)
                        break;
                _privateData[pos] = withThis;
-               fromOffset = pos;
        }
        
        return *this;
@@ -1325,15 +1445,17 @@
 BString&
 BString::Replace(char replaceThis, char withThis, int32 maxReplaceCount, int32 
fromOffset)
 {
-       for (int32 pos ; maxReplaceCount > 0 ; maxReplaceCount--) 
+       CHECK_PARAM( fromOffset>=0, "'fromOffset' must not be negative!");
+       if (maxReplaceCount > 0) 
        {
-               pos = FindFirst(replaceThis, fromOffset);
-               if (pos < 0)
-                       break;
-               
-               _privateData[pos] = withThis;
-               fromOffset = pos;
-               
+               for (int32 pos=min_clamp0(fromOffset,Length()); 
+                         maxReplaceCount > 0;   maxReplaceCount--) 
+               {
+                       pos = FindFirst(replaceThis, pos);
+                       if (pos < 0)
+                               break;
+                       _privateData[pos] = withThis;
+               }
        }
        return *this;
 }
@@ -1360,11 +1482,16 @@
                int32 len = (withThis ? strlen(withThis) : 0);
                int32 difference = len - firstStringLength;
                
-               if (difference > 0)
-                       _OpenAtBy(pos, difference);
+               if (difference > 0) 
+               {
+                       if (!_OpenAtBy(pos, difference))
+                               return *this;
+               }
                else if (difference < 0)
-                       _ShrinkAtBy(pos, -difference);
-               
+               {
+                       if (!_ShrinkAtBy(pos, -difference))
+                               return *this;
+               }
                memcpy(_privateData + pos, withThis, len);
        }
                
@@ -1375,14 +1502,18 @@
 BString&
 BString::ReplaceAll(const char *replaceThis, const char *withThis, int32 
fromOffset)
 {
-       return _DoReplace( replaceThis, withThis, 0x7FFFFFFF, fromOffset, 
KEEP_CASE);
+       CHECK_PARAM( fromOffset>=0, "'fromOffset' must not be negative!");
+       return _DoReplace( replaceThis, withThis, REPLACE_ALL, 
+                                                        
min_clamp0(fromOffset,Length()), KEEP_CASE);
 }
 
 
 BString&
 BString::Replace(const char *replaceThis, const char *withThis, int32 
maxReplaceCount, int32 fromOffset)
 {
-       return _DoReplace( replaceThis, withThis, maxReplaceCount, fromOffset, 
KEEP_CASE);
+       CHECK_PARAM( fromOffset>=0, "'fromOffset' must not be negative!");
+       return _DoReplace( replaceThis, withThis, maxReplaceCount, 
+                                                        
min_clamp0(fromOffset,Length()), KEEP_CASE);
 }
 
 
@@ -1415,15 +1546,16 @@
 BString&
 BString::IReplaceAll(char replaceThis, char withThis, int32 fromOffset)
 {
+       CHECK_PARAM( fromOffset>=0, "'fromOffset' must not be negative!");
+
        char tmp[2] = { replaceThis, '\0' };
-       
-       for (int32 pos;;) 
+
+       for (int32 pos=min_clamp0(fromOffset,Length());;) 
        {
-               pos = _IFindAfter(tmp, fromOffset, 1);
+               pos = _IFindAfter(tmp, pos, 1);
                if (pos < 0)
                        break;
                _privateData[pos] = withThis;
-               fromOffset = pos;
        }
        return *this;
 }
@@ -1432,19 +1564,20 @@
 BString&
 BString::IReplace(char replaceThis, char withThis, int32 maxReplaceCount, 
int32 fromOffset)
 {
+       CHECK_PARAM( fromOffset>=0, "'fromOffset' must not be negative!");
+
        char tmp[2] = { replaceThis, '\0' };
        
        if (_privateData == NULL)
                return *this;
                
-       for (int32 pos ; maxReplaceCount > 0 ; maxReplaceCount--) 
+       for (int32 pos=min_clamp0(fromOffset,Length()); 
+                 maxReplaceCount > 0;   maxReplaceCount--) 
        {       
-               pos = _IFindAfter(tmp, fromOffset, 1);
+               pos = _IFindAfter(tmp, pos, 1);
                if (pos < 0)
                        break;
-               
                _privateData[pos] = withThis;
-               fromOffset = pos;
        }
        return *this;
 }
@@ -1472,11 +1605,17 @@
                int32 difference = len - firstStringLength;
                
                if (difference > 0)
-                       _OpenAtBy(pos, difference);
+               {
+                       if (!_OpenAtBy(pos, difference))
+                               return *this;
+               }
                else if (difference < 0)
-                       _ShrinkAtBy(pos, -difference);
-               
+               {
+                       if (!_ShrinkAtBy(pos, -difference))
+                               return *this;
+               }
                memcpy(_privateData + pos, withThis, len);
+               
        }
                
        return *this;
@@ -1486,31 +1625,35 @@
 BString&
 BString::IReplaceAll(const char *replaceThis, const char *withThis, int32 
fromOffset)
 {
-       return _DoReplace( replaceThis, withThis, 0x7FFFFFFF, fromOffset, 
IGNORE_CASE);
+       CHECK_PARAM( fromOffset>=0, "'fromOffset' must not be negative!");
+       return _DoReplace( replaceThis, withThis, REPLACE_ALL, 
+                                                        
min_clamp0(fromOffset,Length()), IGNORE_CASE);
 }
 
 
 BString&
 BString::IReplace(const char *replaceThis, const char *withThis, int32 
maxReplaceCount, int32 fromOffset)
 {
-       return _DoReplace( replaceThis, withThis, maxReplaceCount, fromOffset, 
IGNORE_CASE);
+       CHECK_PARAM( fromOffset>=0, "'fromOffset' must not be negative!");
+       return _DoReplace( replaceThis, withThis, maxReplaceCount, 
+                                                        
min_clamp0(fromOffset,Length()), IGNORE_CASE);
 }
 
 
 BString&
 BString::ReplaceSet(const char *setOfChars, char with)
 {
+       if (setOfChars == NULL)
+               return *this;
+
        int32 offset = 0;
        int32 length = Length();
        
        for (int32 pos;;) 
        {
                pos = strcspn(String() + offset, setOfChars);
-               if (pos >= length)
-                       break;
-               
+
                offset += pos;
-               
                if (offset >= length)
                        break;
 
@@ -1521,32 +1664,32 @@
        return *this;
 }
 
-
 BString&
 BString::ReplaceSet(const char *setOfChars, const char *with)
 {
-       if (with == NULL)
-               return *this; //TODO: do something smart
-       
-       int32 offset = 0;
-       int32 withLen = strlen(with);
+       int32 withLen = with ? strlen(with) : 0;
+       if (withLen == 1)
+               // delegate simple case:
+               return ReplaceSet( setOfChars, *with);
+
+       if (setOfChars == NULL || _privateData == NULL)
+               return *this;
        
-       for (int32 pos;;) 
+       PosVect positions;
+
+       int32 searchLen = 1;
+       int32 len = Length();
+       int32 pos = 0;
+       for (int32 offset = 0; offset < len; offset += (pos+searchLen)) 
        {
-               pos = strcspn(String() + offset, setOfChars);
-               if (pos >= Length())
+               pos = strcspn(_privateData + offset, setOfChars);
+               if (pos+offset>=len)
                        break;
-               
-               offset += pos;
-               
-               if (offset >= Length())
-                       break;
-               
-               _OpenAtBy(offset, withLen - 1);
-               memcpy(_privateData + offset, with, withLen);
-               offset += withLen;
+               if (!positions.Add( offset+pos))
+                       return *this;
        }
-       
+
+       _ReplaceAtPositions( &positions, searchLen, with, withLen);     
        return *this;
 }
 
@@ -1576,7 +1719,18 @@
        int32 len = Length();
        
        if (maxLength > len)
-               _GrowBy(maxLength - len);
+       {
+               if (!_GrowBy(maxLength - len))
+                       return NULL;
+               if (!len && _privateData)
+                       // if string was empty before call to LockBuffer(), we 
make sure the
+                       // buffer represents an empty c-string:
+                       *_privateData = '\0';
+       }
+       else if (!maxLength && !len)
+       {       // special case for unallocated string, we return an empty 
c-string:
+               return const_cast<char*>( String());
+       }
 
        return _privateData;
 }
@@ -1710,19 +1864,51 @@
        if (setOfCharsToEscape == NULL || _privateData == NULL)
                return *this;
        
-       int32 offset = 0;
-       
-       for(int32 pos;;) 
+       PosVect positions;
+       int32 len = Length();
+       int32 pos = 0;
+       for (int32 offset = 0; offset < len; offset += pos+1) 
        {
-               pos = strcspn(_privateData + offset, setOfCharsToEscape);
-               offset += pos;
-               if (offset >= Length())
-                       break;
-               _OpenAtBy(offset, 1);
-               memset(_privateData + offset, escapeWith, 1);
-               offset += 2;
+               if ((pos = strcspn(_privateData + offset, setOfCharsToEscape)) 
< len-offset)
+                       if (!positions.Add( offset+pos))
+                               return *this;
        }
-               
+
+       uint32 count = positions.CountItems();
+       int32 newLength = len + count;
+       if (!newLength) {
+               _GrowBy( -len);
+               return *this;
+       }
+       int32 lastPos = 0;
+       char* oldAdr = _privateData;
+       char* newData = (char*)malloc( newLength+sizeof(int32)+1);
+       if (newData) {
+               newData += sizeof(int32);
+               char* newAdr = newData;
+               for( uint32 i=0; i<count; ++i)
+               {
+                       pos = positions.ItemAt( i);
+                       len = pos-lastPos;
+                       if (len>0) {
+                               memcpy(newAdr, oldAdr, len);
+                               oldAdr += len;
+                               newAdr += len;
+                       }
+                       *newAdr++ = escapeWith;
+                       *newAdr++ = *oldAdr++;
+                       lastPos = pos+1;
+               }
+               len = Length()+1-lastPos;
+               if (len>0) {
+                       memcpy(newAdr, oldAdr, len);
+               }
+               free( _privateData-sizeof(int32));
+               _privateData = newData;
+               _privateData[newLength] = 0;
+               _SetLength( newLength);
+       }
+
        return *this;
 }
 
@@ -1740,11 +1926,8 @@
 BString&
 BString::CharacterDeescape(char escapeChar)
 {
-       int32 pos;      
-       while ((pos = FindFirst(escapeChar)) >= 0)
-               _ShrinkAtBy(pos, 1);
-                       
-       return *this;
+       const char temp[2] = {escapeChar, 0};
+       return _DoReplace( temp, "", REPLACE_ALL, 0, KEEP_CASE);
 }
 
 
@@ -1846,19 +2029,35 @@
 
 
 /*---- Private or Reserved ------------------------------------------------*/
+char*
+BString::_Alloc( int32 dataLen)
+{
+       char* dataPtr = _privateData ? _privateData-sizeof(int32) : NULL;
+       if (dataLen<=0)
+       {       // release buffer if requested size is 0:
+               if (dataPtr)
+                       free( dataPtr);
+               _privateData = NULL;
+               return NULL;
+       }
+       int32 allocLen = dataLen + sizeof(int32) + 1;
+       dataPtr = (char*)realloc( dataPtr, allocLen);
+       if (dataPtr) 
+       {
+               dataPtr += sizeof(int32);
+               _privateData = dataPtr;
+               int32 newLen = allocLen - sizeof(int32) - 1;
+               _SetLength( newLen);
+               _privateData[newLen] = '\0';
+       }
+       return dataPtr;
+}      
+
 void
 BString::_Init(const char* str, int32 len)
 {
-       ASSERT(str != NULL);
-       ASSERT(_privateData == NULL);
-
-       _privateData = (char*)malloc(len + sizeof(int32) + 1);  
-       _privateData += sizeof(int32);
-       
-       memcpy(_privateData, str, len);
-       
-       _SetLength(len);
-       _privateData[len] = '\0';       
+       if (_Alloc( len))
+               memcpy(_privateData, str, len);
 }
 
 
@@ -1868,13 +2067,10 @@
 void
 BString::_DoAssign(const char *str, int32 len)
 {
-       ASSERT(str != NULL);    
        int32 curLen = Length();
        
-       if (len != curLen)
-               _GrowBy(len - curLen);
-       
-       memcpy(_privateData, str, len);
+       if (len == curLen ||    _GrowBy(len - curLen))
+               memcpy(_privateData, str, len);
 }
 
 
@@ -1884,11 +2080,9 @@
 void
 BString::_DoAppend(const char *str, int32 len)
 {
-       ASSERT(str != NULL);
-       
        int32 length = Length();
-       _GrowBy(len);
-       memcpy(_privateData + length, str, len);
+       if (_GrowBy(len))
+               memcpy(_privateData + length, str, len);
 }
 
 
@@ -1896,65 +2090,36 @@
 BString::_GrowBy(int32 size)
 {              
        int32 newLen = Length() + size;         
-       ASSERT(newLen >= 0);
-       
-       if (_privateData != NULL)
-               _privateData -= sizeof(int32);
-               
-       _privateData = (char*)realloc(_privateData, 
-               newLen + sizeof(int32) + 1);
-               
-       _privateData += sizeof(int32);
-       
-       _SetLength(newLen);     
-       _privateData[newLen] = '\0';
-       
-       return _privateData;
+       return _Alloc( newLen);
 }
 
 
 char *
 BString::_OpenAtBy(int32 offset, int32 length)
 {
-       ASSERT(offset >= 0);
-                       
        int32 oldLength = Length();
        
-       if (_privateData != NULL)
-               _privateData -= sizeof(int32);
+       char* newData = _Alloc( oldLength + length);
+       if (newData != NULL)
+               memmove(_privateData + offset + length, _privateData + offset,
+                                 oldLength - offset);
        
-       _privateData = (char*)realloc(_privateData, oldLength + length + 
sizeof(int32) + 1);
-       _privateData += sizeof(int32);
-       
-       memmove(_privateData + offset + length, _privateData + offset,
-                       oldLength - offset);
-       
-       _SetLength(oldLength + length);
-       _privateData[Length()] = '\0';
-       
-       return _privateData;    
+       return newData;
 }
 
 
 char*
 BString::_ShrinkAtBy(int32 offset, int32 length)
 {      
+       if (!_privateData)
+               return NULL;
        int32 oldLength = Length();
-       
-       if (offset > oldLength || offset + length > oldLength)
-               return _privateData;
 
        memmove(_privateData + offset, _privateData + offset + length,
-               oldLength - offset - length);
-       
-       _privateData -= sizeof(int32);  
-       _privateData = (char*)realloc(_privateData, oldLength - length + 
sizeof(int32) + 1);
-       _privateData += sizeof(int32);
-       
-       _SetLength(oldLength - length); 
-       _privateData[Length()] = '\0';
-       
-       return _privateData;
+                    oldLength - offset - length);
+
+       // the following actually should never fail, since we are reducing the 
size...
+       return _Alloc( oldLength - length);
 }
 
 
@@ -1964,9 +2129,8 @@
 void
 BString::_DoPrepend(const char *str, int32 count)
 {
-       ASSERT(str != NULL);
-       _OpenAtBy(0, count);
-       memcpy(_privateData, str, count);
+       if (_OpenAtBy(0, count))
+               memcpy(_privateData, str, count);
 }
 
 
@@ -1974,11 +2138,6 @@
 int32
 BString::_FindAfter(const char *str, int32 offset, int32 strlen) const
 {      
-       ASSERT(str != NULL);
-       
-       if (offset > Length())
-               return B_ERROR;
-
        char *ptr = strstr(String() + offset, str);
 
        if (ptr != NULL)
@@ -1991,11 +2150,6 @@
 int32
 BString::_IFindAfter(const char *str, int32 offset, int32 strlen) const
 {
-       ASSERT(str != NULL);
-
-       if (offset > Length())
-               return B_ERROR;
-
        char *ptr = strcasestr(String() + offset, str);
 
        if (ptr != NULL)
@@ -2008,8 +2162,6 @@
 int32
 BString::_ShortFindAfter(const char *str, int32 len) const
 {
-       ASSERT(str != NULL);
-       
        char *ptr = strstr(String(), str);
        
        if (ptr != NULL)
@@ -2022,20 +2174,17 @@
 int32
 BString::_FindBefore(const char *str, int32 offset, int32 strlen) const
 {
-       ASSERT(str != NULL);
-       
-       if (offset <= 0)
-               return B_ERROR;
-       
-       const char *ptr = _privateData + offset - strlen;
-       
-       while (ptr >= _privateData)
-       {       
-               if (!memcmp(ptr, str, strlen))
-                       return ptr - _privateData; 
-               ptr--;
+       if (_privateData)
+       {
+               const char *ptr = _privateData + offset - strlen;
+               
+               while (ptr >= _privateData)
+               {       
+                       if (!memcmp(ptr, str, strlen))
+                               return ptr - _privateData; 
+                       ptr--;
+               }
        }
-       
        return B_ERROR;
 }
 
@@ -2043,56 +2192,20 @@
 int32
 BString::_IFindBefore(const char *str, int32 offset, int32 strlen) const
 {
-       ASSERT(str != NULL);
-       
-       if (offset <= 0)
-               return B_ERROR;
-                       
-       char *ptr1 = _privateData + offset - strlen;
-       
-       while (ptr1 >= _privateData) 
+       if (_privateData)
        {
-               if (!strncasecmp(ptr1, str, strlen))
-                       return ptr1 - _privateData; 
-               ptr1--;
+               char *ptr1 = _privateData + offset - strlen;
+               
+               while (ptr1 >= _privateData) 
+               {
+                       if (!strncasecmp(ptr1, str, strlen))
+                               return ptr1 - _privateData; 
+                       ptr1--;
+               }
        }
-       
        return B_ERROR;
 }
 
-
-/*!
-       BStringOBuf is helper classed used by _DoReplace()
-*/
-class BStringOBuf
-{
-
-public:
-       BStringOBuf( uint32 startLen, float growFactor=1.5);
-       ~BStringOBuf();
-
-       // methods to write into buffer (always append):
-       uint32 Write( const char* data, uint32 len);
-       
-       // getters:
-       BString& TheString();
-       inline bool HasData() const                     { return mBuf!=NULL; }
-
-private:
-       bool GrowBufferToFit( uint32 len);
-
-       uint32 mBufLen;
-       float mGrowFactor;
-       char* mBuf;
-       uint32 mCurrPos;
-       BString mStr;
-
-       // Hide copy-constructor and assignment:
-       BStringOBuf( const BStringOBuf&);
-       BStringOBuf operator=( const BStringOBuf&);
-};
-
-
 BString&
 BString::_DoReplace(const char *findThis, const char *replaceWith, int32 
maxReplaceCount, 
                                                  int32 fromOffset,     bool 
ignoreCase)
@@ -2105,30 +2218,63 @@
                ? &BString::_IFindAfter
                : &BString::_FindAfter;
        int32 findLen = strlen( findThis);
-       int32 replaceLen = replaceWith ? strlen( replaceWith) : 0;
-       BStringOBuf tempIO( (int32)max_c( max_c( findLen, 128), Length()*1.2), 
1.2);
+       if (!replaceWith)
+               replaceWith = "";
+       int32 replaceLen = strlen( replaceWith);
        int32 lastSrcPos = fromOffset;
-       int32 len;
+       PosVect positions;
        for(  int32 srcPos=0; 
-                       maxReplaceCount>0 && (srcPos = (this->*findMethod)( 
findThis, lastSrcPos, findLen))!=B_ERROR; 
+                       maxReplaceCount > 0 
+                       && (srcPos = (this->*findMethod)( findThis, lastSrcPos, 
findLen)) >= 0; 
                        maxReplaceCount-- ) {
-               len = srcPos-lastSrcPos;
-               if (fromOffset && !tempIO.HasData())
-                       tempIO.Write( String(), fromOffset);
-               tempIO.Write( String()+lastSrcPos, len);
-               tempIO.Write( replaceWith, replaceLen);
+               positions.Add( srcPos);
                lastSrcPos = srcPos+findLen;
        }
-       if (tempIO.HasData()) {
-               // only copy remainder if we have actually changed anything
-               if ((len = Length()-lastSrcPos)!=0) {
-                       if (fromOffset && !tempIO.HasData())
-                               tempIO.Write( String(), fromOffset);
-                       tempIO.Write( String()+lastSrcPos, len);
+       _ReplaceAtPositions( &positions, findLen, replaceWith, replaceLen);
+       return *this;
+}
+
+void BString::_ReplaceAtPositions( const PosVect* positions,
+                                                                               
          int32 searchLen, const char* with,
+                                                                               
          int32 withLen)
+{
+       int32 len = Length();
+       uint32 count = positions->CountItems();
+       int32 newLength = len + count*(withLen-searchLen);
+       if (!newLength) {
+               _GrowBy( -len);
+               return;
+       }
+       int32 pos;
+       int32 lastPos = 0;
+       char* oldAdr = _privateData;
+       char* newData = (char*)malloc( newLength+sizeof(int32)+1);
+       if (newData) {
+               newData += sizeof(int32);
+               char* newAdr = newData;
+               for( uint32 i=0; i<count; ++i)
+               {
+                       pos = positions->ItemAt( i);
+                       len = pos-lastPos;
+                       if (len>0) {
+                               memcpy(newAdr, oldAdr, len);
+                               oldAdr += len;
+                               newAdr += len;
+                       }
+                       memcpy(newAdr, with, withLen);
+                       oldAdr += searchLen;
+                       newAdr += withLen;
+                       lastPos = pos+searchLen;
                }
-               Adopt( tempIO.TheString());
+               len = Length()+1-lastPos;
+               if (len>0) {
+                       memcpy(newAdr, oldAdr, len);
+               }
+               free( _privateData-sizeof(int32));
+               _privateData = newData;
+               _privateData[newLength] = 0;
+               _SetLength( newLength);
        }
-       return *this;
 }
 
 #if ENABLE_INLINES
@@ -2185,62 +2331,5 @@
 ICompare(const BString *string1, const BString *string2)
 {
        return strcasecmp(string1->String(), string2->String());
-}
-
-
-/*----- Implementation of BStringOBuf ------------------------------*/
-BStringOBuf::BStringOBuf(uint32 startLen, float growFactor)
-       :mBufLen(startLen),
-       mGrowFactor(max_c((float)1.0, growFactor)),
-       mBuf(NULL),
-       mCurrPos(0)
-{
-}
-
-
-BStringOBuf::~BStringOBuf() 
-{
-       if (mBuf)
-               mStr.UnlockBuffer(mCurrPos);
-}
-
-
-bool
-BStringOBuf::GrowBufferToFit(uint32 len) 
-{
-       if (!mBuf || mCurrPos+len > mBufLen) {
-               if (mBuf) {
-                       mStr.UnlockBuffer(mBufLen);
-                       mBufLen = (uint32)max_c(mGrowFactor*mBufLen, 
mGrowFactor*(mCurrPos+len));
-               } else
-                       mBufLen = (uint32)max_c(mBufLen, mCurrPos+len);
-               mBuf = mStr.LockBuffer(mBufLen);
-               if (!mBuf)
-                       return false;
-       }
-       return true;
-}
-
-
-uint32
-BStringOBuf::Write(const char* data, uint32 len)
-{
-       if (!len || !GrowBufferToFit( len))
-               return 0;
-       memcpy( mBuf+mCurrPos, data, len);
-       mCurrPos += len;
-       return len;
-}
-
-
-BString&
-BStringOBuf::TheString()
-{
-       if (mBuf) {
-               mBuf[mCurrPos] = '\0';
-               mStr.UnlockBuffer( mCurrPos);
-               mBuf = NULL;
-       }
-       return mStr;
 }
 
Index: src/tests/kits/support/bstring/StringAppendTest.cpp
===================================================================
RCS file: 
/cvsroot/open-beos/current/src/tests/kits/support/bstring/StringAppendTest.cpp,v
retrieving revision 1.2
diff -u -r1.2 StringAppendTest.cpp
--- src/tests/kits/support/bstring/StringAppendTest.cpp 15 Oct 2002 05:30:29 
-0000      1.2
+++ src/tests/kits/support/bstring/StringAppendTest.cpp 6 Nov 2003 09:29:19 
-0000
@@ -99,7 +99,7 @@
        str1 = new BString("Base");
        str1->Append("APPENDED", 40);
        CPPUNIT_ASSERT(strcmp(str1->String(), "BaseAPPENDED") == 0);
-       CPPUNIT_ASSERT(str1->Length() == strlen("BaseAPPENDED"));
+       CPPUNIT_ASSERT(str1->Length() == (int32)strlen("BaseAPPENDED"));
        delete str1;
        
        //char ptr is NULL
@@ -115,6 +115,25 @@
        str1->Append('C', 5);
        CPPUNIT_ASSERT(strcmp(str1->String(), "BaseCCCCC") == 0);
        delete str1;
+
+       const int32 OUT_OF_MEM_VAL = 2*1000*1000*1000;
+#ifndef TEST_R5
+       //Append(char, int32) with excessive length:
+       NextSubTest();
+       str1 = new BString("Base");
+       str1->Append('C', OUT_OF_MEM_VAL);
+       CPPUNIT_ASSERT(strcmp(str1->String(), "Base") == 0);
+       delete str1;
+#endif
+
+#ifndef TEST_R5
+       //Append(char*, int32) with excessive length:
+       NextSubTest();
+       str1 = new BString("Base");
+       str1->Append("some more text", OUT_OF_MEM_VAL);
+       CPPUNIT_ASSERT(strcmp(str1->String(), "Basesome more text") == 0);
+       delete str1;
+#endif
 }
 
 
Index: src/tests/kits/support/bstring/StringAssignTest.cpp
===================================================================
RCS file: 
/cvsroot/open-beos/current/src/tests/kits/support/bstring/StringAssignTest.cpp,v
retrieving revision 1.2
diff -u -r1.2 StringAssignTest.cpp
--- src/tests/kits/support/bstring/StringAssignTest.cpp 15 Oct 2002 05:30:29 
-0000      1.2
+++ src/tests/kits/support/bstring/StringAssignTest.cpp 6 Nov 2003 09:29:19 
-0000
@@ -99,6 +99,25 @@
        CPPUNIT_ASSERT(str->Length() == 2);
        CPPUNIT_ASSERT(strcmp(newstring.String(), "") == 0);
        delete str;
+
+       const int32 OUT_OF_MEM_VAL = 2*1000*1000*1000;
+#ifndef TEST_R5
+       //SetTo(char, int32) with excessive length:
+       NextSubTest();
+       str = new BString("dummy");
+       str->SetTo('C', OUT_OF_MEM_VAL);
+       CPPUNIT_ASSERT(strcmp(str->String(), "dummy") == 0);
+       delete str;
+#endif
+
+#ifndef TEST_R5
+       //SetTo(char*, int32) with excessive length:
+       NextSubTest();
+       str = new BString("dummy");
+       str->SetTo("some more text", OUT_OF_MEM_VAL);
+       CPPUNIT_ASSERT(strcmp(str->String(), "some more text") == 0);
+       delete str;
+#endif
 }
 
 
Index: src/tests/kits/support/bstring/StringEscapeTest.cpp
===================================================================
RCS file: 
/cvsroot/open-beos/current/src/tests/kits/support/bstring/StringEscapeTest.cpp,v
retrieving revision 1.3
diff -u -r1.3 StringEscapeTest.cpp
--- src/tests/kits/support/bstring/StringEscapeTest.cpp 19 Nov 2002 08:24:13 
-0000      1.3
+++ src/tests/kits/support/bstring/StringEscapeTest.cpp 6 Nov 2003 09:29:19 
-0000
@@ -17,7 +17,7 @@
 void 
 StringEscapeTest::PerformTest(void)
 {
-       BString *string1, *string2;
+       BString *string1;
        
        //CharacterEscape(char*, char)
        NextSubTest();
Index: src/tests/kits/support/bstring/StringInsertTest.cpp
===================================================================
RCS file: 
/cvsroot/open-beos/current/src/tests/kits/support/bstring/StringInsertTest.cpp,v
retrieving revision 1.3
diff -u -r1.3 StringInsertTest.cpp
--- src/tests/kits/support/bstring/StringInsertTest.cpp 15 Oct 2002 19:32:11 
-0000      1.3
+++ src/tests/kits/support/bstring/StringInsertTest.cpp 6 Nov 2003 09:29:19 
-0000
@@ -26,12 +26,15 @@
        CPPUNIT_ASSERT(strcmp(str1->String(), "StrINSERTEDing") == 0);
        delete str1;
        
-       //This test crashes both implementations
-       //NextSubTest();
-       //str1 = new BString("String");
-       //str1->Insert("INSERTED", 10);
-       //CPPUNIT_ASSERT(strcmp(str1->String(), "string") == 0);
-       //delete str1;
+#ifndef TEST_R5
+       // This test crashes R5 and should drop into the debugger in OpenBeOS
+       // (if compiled with DEBUG):
+       NextSubTest();
+       str1 = new BString("String");
+       str1->Insert("INSERTED", 10);
+       CPPUNIT_ASSERT(strcmp(str1->String(), "String") == 0);
+       delete str1;
+#endif
        
        NextSubTest();
        str1 = new BString;
@@ -39,6 +42,15 @@
        CPPUNIT_ASSERT(strcmp(str1->String(), "NSERTED") == 0);
        delete str1;
        
+#ifndef TEST_R5
+       // check limitation of negative values (R5 doesn't):
+       NextSubTest();
+       str1 = new BString;
+       str1->Insert("INSERTED", -142364253);
+       CPPUNIT_ASSERT(strcmp(str1->String(), "") == 0);
+       delete str1;
+#endif
+       
        //&Insert(const char *, int32 length, int32 pos);
        NextSubTest();
        str1 = new BString("string");
@@ -46,12 +58,15 @@
        CPPUNIT_ASSERT(strcmp(str1->String(), "stINring") == 0);
        delete str1;
        
-       //This test crashes both implementations
-       //NextSubTest();
-       //str1 = new BString("string");
-       //str1->Insert("INSERTED", 2, 30);
-       //CPPUNIT_ASSERT(strcmp(str1->String(), "stINring") == 0);
-       //delete str1;
+#ifndef TEST_R5
+       // This test crashes R5 and should drop into the debugger in OpenBeOS
+       // (if compiled with DEBUG):
+       NextSubTest();
+       str1 = new BString("string");
+       str1->Insert("INSERTED", 2, 30);
+       CPPUNIT_ASSERT(strcmp(str1->String(), "string") == 0);
+       delete str1;
+#endif
        
        NextSubTest();
        str1 = new BString("string");
@@ -71,6 +86,13 @@
        str1 = new BString("string");
        str1->Insert('P', 5, 3);
        CPPUNIT_ASSERT(strcmp(str1->String(), "strPPPPPing") == 0);
+       delete str1;
+       
+       //Insert(char c, int32 count, int32 pos)
+       NextSubTest();
+       str1 = new BString("string");
+       str1->Insert('P', 5, -2);
+       CPPUNIT_ASSERT(strcmp(str1->String(), "PPPstring") == 0);
        delete str1;
        
        //Insert(BString&)
Index: src/tests/kits/support/bstring/StringRemoveTest.cpp
===================================================================
RCS file: 
/cvsroot/open-beos/current/src/tests/kits/support/bstring/StringRemoveTest.cpp,v
retrieving revision 1.4
diff -u -r1.4 StringRemoveTest.cpp
--- src/tests/kits/support/bstring/StringRemoveTest.cpp 10 Jan 2003 16:10:00 
-0000      1.4
+++ src/tests/kits/support/bstring/StringRemoveTest.cpp 6 Nov 2003 09:29:20 
-0000
@@ -86,11 +86,11 @@
        CPPUNIT_ASSERT(strcmp(string1->String(), "a String") == 0);
        delete string1;
        
-       //from + length is > Length()
+       //from + length exceeds Length() (R5 fails)
        NextSubTest();
        string1 = new BString("a String");
        string1->Remove(4, 30);
-       CPPUNIT_ASSERT(strcmp(string1->String(), "a String") == 0);
+       CPPUNIT_ASSERT(strcmp(string1->String(), "a St") == 0);
        delete string1;
        
        NextSubTest();
@@ -223,7 +223,7 @@
        string2 = new BString("string");
        string2->MoveInto(*string1, 0, 200);
        CPPUNIT_ASSERT(strcmp(string1->String(), "string") == 0);
-       CPPUNIT_ASSERT(strcmp(string2->String(), "string") == 0);
+       CPPUNIT_ASSERT(strcmp(string2->String(), "") == 0);
        delete string1;
        delete string2;
        
@@ -242,7 +242,7 @@
        memset(dest, 0, 100);
        string1->MoveInto(dest, 0, 50);
        CPPUNIT_ASSERT(strcmp(dest, "some text") == 0);
-       CPPUNIT_ASSERT(strcmp(string1->String(), "some text") == 0);
+       CPPUNIT_ASSERT(strcmp(string1->String(), "") == 0);
        delete string1;
 }
 
Index: src/tests/kits/support/bstring/StringReplaceTest.cpp
===================================================================
RCS file: 
/cvsroot/open-beos/current/src/tests/kits/support/bstring/StringReplaceTest.cpp,v
retrieving revision 1.6
diff -u -r1.6 StringReplaceTest.cpp
--- src/tests/kits/support/bstring/StringReplaceTest.cpp        11 Feb 2003 
19:14:04 -0000      1.6
+++ src/tests/kits/support/bstring/StringReplaceTest.cpp        6 Nov 2003 
09:29:20 -0000
@@ -17,7 +17,9 @@
 void 
 StringReplaceTest::PerformTest(void)
 {
-       BString *str1, *str2;
+       BString *str1;
+       const int32 sz = 1024*50;
+       char* buf;
        
        //&ReplaceFirst(char, char);
        NextSubTest();
@@ -257,10 +259,79 @@
        //ReplaceSet(const char*, const char*)
        NextSubTest();
        str1 = new BString("abcd abcd abcd");
+       str1->ReplaceSet("abcd ", "");
+       CPPUNIT_ASSERT(strcmp(str1->String(), "") == 0);
+       delete str1;
+#endif
+
+#ifndef TEST_R5
+       //ReplaceSet(const char*, const char*)
+       NextSubTest();
+       str1 = new BString("abcd abcd abcd");
        str1->ReplaceSet("ad", "da");
        CPPUNIT_ASSERT(strcmp(str1->String(), "dabcda dabcda dabcda") == 0);
        delete str1;
 #endif
+
+#ifndef TEST_R5
+       //ReplaceSet(const char*, const char*)
+       NextSubTest();
+       str1 = new BString("abcd abcd abcd");
+       str1->ReplaceSet("ad", "");
+       CPPUNIT_ASSERT(strcmp(str1->String(), "bc bc bc") == 0);
+       delete str1;
+#endif
+
+       // we repeat some test, but this time with a bit of data 
+       // to test the performance:
+
+       // ReplaceSet(const char*, const char*)
+       NextSubTest();
+       str1 = new BString();
+       buf = str1->LockBuffer(sz);
+       memset( buf, 'x', sz);
+       str1->UnlockBuffer( sz);
+       str1->ReplaceSet("x", "y");
+       CPPUNIT_ASSERT(str1->Length() == sz);
+       delete str1;
+
+       NextSubTest();
+       str1 = new BString();
+       buf = str1->LockBuffer(sz);
+       memset( buf, 'x', sz);
+       str1->UnlockBuffer( sz);
+       str1->ReplaceSet("x", "");
+       CPPUNIT_ASSERT(str1->Length() == 0);
+       delete str1;
+
+       // ReplaceAll(const char*, const char*)
+       NextSubTest();
+       str1 = new BString();
+       buf = str1->LockBuffer(sz);
+       memset( buf, 'x', sz);
+       str1->UnlockBuffer( sz);
+       str1->ReplaceAll("x", "y");
+       CPPUNIT_ASSERT(str1->Length() == sz);
+       delete str1;
+
+       NextSubTest();
+       str1 = new BString();
+       buf = str1->LockBuffer(sz);
+       memset( buf, 'x', sz);
+       str1->UnlockBuffer( sz);
+       str1->ReplaceAll("xx", "y");
+       CPPUNIT_ASSERT(str1->Length() == sz/2);
+       delete str1;
+
+       NextSubTest();
+       str1 = new BString();
+       buf = str1->LockBuffer(sz);
+       memset( buf, 'x', sz);
+       str1->UnlockBuffer( sz);
+       str1->ReplaceSet("xx", "");
+       CPPUNIT_ASSERT(str1->Length() == 0);
+       delete str1;
+
 }
 
 
Index: src/tests/kits/support/bstring/StringSearchTest.cpp
===================================================================
RCS file: 
/cvsroot/open-beos/current/src/tests/kits/support/bstring/StringSearchTest.cpp,v
retrieving revision 1.8
diff -u -r1.8 StringSearchTest.cpp
--- src/tests/kits/support/bstring/StringSearchTest.cpp 9 Aug 2003 04:30:43 
-0000       1.8
+++ src/tests/kits/support/bstring/StringSearchTest.cpp 6 Nov 2003 09:29:20 
-0000
@@ -56,7 +56,7 @@
        NextSubTest();
        string1 = new BString("string");
        i = string1->FindFirst((char*)NULL);
-       CPPUNIT_ASSERT(i == B_BAD_VALUE);
+       CPPUNIT_ASSERT(i == B_ERROR);
        delete string1;
 #endif
 
@@ -109,7 +109,7 @@
        NextSubTest();
        string1 = new BString("abc abc abc");
        i = string1->FindFirst((char*)NULL, 3);
-       CPPUNIT_ASSERT(i == B_BAD_VALUE);
+       CPPUNIT_ASSERT(i == B_ERROR);
        delete string1;
 #endif
 
@@ -180,7 +180,7 @@
        NextSubTest();
        string1 = new BString("string");
        i = string1->FindLast((char*)NULL);
-       CPPUNIT_ASSERT(i == B_BAD_VALUE);
+       CPPUNIT_ASSERT(i == B_ERROR);
        delete string1;
 #endif
 
@@ -219,7 +219,7 @@
        NextSubTest();
        string1 = new BString("abc abc abc");
        i = string1->FindLast((char*)NULL, 3);
-       CPPUNIT_ASSERT(i == B_BAD_VALUE);
+       CPPUNIT_ASSERT(i == B_ERROR);
        delete string1;
 #endif
 
@@ -312,7 +312,7 @@
        NextSubTest();
        string1 = new BString("string");
        i = string1->IFindFirst((char*)NULL);
-       CPPUNIT_ASSERT(i == B_BAD_VALUE);
+       CPPUNIT_ASSERT(i == B_ERROR);
        delete string1;
 #endif
 
@@ -427,7 +427,7 @@
        NextSubTest();
        string1 = new BString("string");
        i = string1->IFindLast((char*)NULL);
-       CPPUNIT_ASSERT(i == B_BAD_VALUE);
+       CPPUNIT_ASSERT(i == B_ERROR);
        delete string1;
 #endif
 

Other related posts: