• Main Page
  • Related Pages
  • Modules
  • Namespaces
  • Classes
  • Files
  • File List
  • File Members

src/String.cpp

00001 /*
00002  *   This file is part of the Standard Portable Library (SPL).
00003  *
00004  *   SPL is free software: you can redistribute it and/or modify
00005  *   it under the terms of the GNU General Public License as published by
00006  *   the Free Software Foundation, either version 3 of the License, or
00007  *   (at your option) any later version.
00008  *
00009  *   SPL is distributed in the hope that it will be useful,
00010  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *   GNU General Public License for more details.
00013  *
00014  *   You should have received a copy of the GNU General Public License
00015  *   along with SPL.  If not, see <http://www.gnu.org/licenses/>.
00016  */
00017 #include <ctype.h>
00018 #include <stdio.h>
00019 #include <spl/Double.h>
00020 #include <spl/Exception.h>
00021 #include <spl/Int32.h>
00022 #include <spl/math/Math.h>
00023 #include <spl/String.h>
00024 #include <spl/text/StringBuffer.h>
00025 
00026 using namespace spl;
00027 
00028 const char eos = '\0';
00029 
00030 char String::m_empty[2] = {'\0', 0xC};
00031 
00032 namespace spl
00033 {
00034         int IndexofchfromWithLen( const char *str, const char ch, int start, const int len )
00035         {
00036                 int pos;
00037 
00038                 for ( pos = start; pos < len; pos++ )
00039                 {
00040                         if ( str[pos] == ch )
00041                         {
00042                                 return pos;
00043                         }
00044                 }
00045                 return -1;
00046         }
00047 
00048         char *StrCat(const char *cstr, const int cstrlen, const char *arg, const int arglen)
00049         {
00050                 int len = cstrlen + arglen + 1;
00051                 char *buf = (char *)malloc(len);
00052                 if ( NULL == buf )
00053                 {
00054                         throw OutOfMemoryException();
00055                 }
00056                 StrCpyLen(buf, cstr, len);
00057                 strcat(buf, arg);
00058                 return buf;
00059         }
00060 
00061         int StrCmpNoCase( const char *str1, int str1len, const char *str2 )
00062         {
00063                 while ( *str1 != '\0' && *str2  != '\0' && str1len-- >= 0 )
00064                 {
00065                         if ( toupper(*str1) != toupper(*str2) )
00066                         {
00067                                 if ( *str1 > *str2 )
00068                                 {
00069                                         return 1;
00070                                 }
00071                                 return -1;
00072                         }
00073                         str1++;
00074                         str2++;
00075                 }
00076                 if ( *str1 != '\0' )
00077                 {
00078                         return 1;
00079                 }
00080                 if ( *str2 != '\0' )
00081                 {
00082                         return -1;
00083                 }
00084                 return 0;
00085         }
00086 
00087         int StrCmp( const char *str1, const char *str2, int maxlen )
00088         {
00089                 while ( *str1 != '\0' && *str2  != '\0' && maxlen-- >= 0 )
00090                 {
00091                         if ( *str1 != *str2 )
00092                         {
00093                                 if ( *str1 > *str2 )
00094                                 {
00095                                         return 1;
00096                                 }
00097                                 return -1;
00098                         }
00099                         str1++;
00100                         str2++;
00101                 }
00102                 if ( *str1 != '\0' )
00103                 {
00104                         return 1;
00105                 }
00106                 if ( *str2 != '\0' )
00107                 {
00108                         return -1;
00109                 }
00110                 return 0;
00111         }
00112 
00113         int StrCpyLen( char *str1, const char *str2, const int maxlen )
00114         {
00115                 int len = maxlen;
00116                 while ( *str2 != '\0' && len-- > 0 )
00117                 {
00118                         *(str1++) = *(str2++);
00119                 }
00120                 *str1 = '\0';
00121                 return -(len - maxlen);
00122         }
00123 
00124         int StrCpy( char *str1, const char *str2 )
00125         {
00126                 return StrCpyLen(str1, str2, Int32::MaxValue());
00127         }
00128 };
00129 
00130 String::String()
00131 :       m_cstr(m_empty),
00132         m_isintern(true),
00133         m_len(0)
00134 
00135 {
00136 }
00137 
00138 void String::InitWith( const char *str, const int offset, const int len )
00139 {
00140         m_len = len;
00141         char *cp;
00142         if ( NULL == (cp = (char *)malloc(m_len+1)) )
00143         {
00144                 throw OutOfMemoryException();
00145         }
00146         m_isintern = false;
00147         StrCpyLen(cp, &str[offset], len);
00148         m_cstr = cp;
00149 }
00150 
00151 String::String( const String& str )
00152 {
00153         m_len = str.Length();
00154         if ( str.m_isintern )
00155         {
00156                 m_cstr = str.m_cstr;
00157         }
00158         else
00159         {
00160                 char *cp;
00161                 if ( NULL == (cp = (char *)malloc(m_len+1)) )
00162                 {
00163                         throw OutOfMemoryException();
00164                 }
00165                 StrCpyLen(cp, str.m_cstr, m_len);
00166                 m_cstr = cp;
00167         }
00168         m_isintern = str.m_isintern;
00169 }
00170 
00171 String::~String()
00172 {
00173         if ( !m_isintern )
00174         {
00175                 free((char *)m_cstr);
00176         }
00177 }
00178 
00179 void String::Set( const String &str )
00180 {
00181         ValidateMem();
00182         if ( m_isintern )
00183         {
00184                 m_isintern = false;
00185         }
00186         else
00187         {
00188                 free( (char *)m_cstr );
00189         }
00190         
00191         m_len = str.Length();
00192         char *cp;
00193         cp = (char *)malloc( m_len + 1 );
00194         if ( NULL == cp )
00195         {
00196                 throw OutOfMemoryException();
00197         }
00198         StrCpyLen(cp, str.m_cstr, m_len);
00199         m_cstr = cp;
00200 }
00201 
00202 StringPtr String::PadRight( char ch, int count )
00203 {
00204         char *buf = (char *)malloc(count + 1);
00205         if ( NULL == buf )
00206         {
00207                 throw OutOfMemoryException();
00208         }
00209         for ( int x = 0; x < count; x++ )
00210         {
00211                 buf[x] = ch;
00212         }
00213         buf[count] = '\0';
00214         StringPtr ret = Cat(buf);
00215         free( buf );
00216         return ret;
00217 }
00218 
00219 StringPtr String::Fill( char ch, int length )
00220 {
00221         StringBuffer buf;
00222         buf.Fill(ch, length);
00223         return buf.ToString();
00224 }
00225 
00226 char String::CharAt( const int idx ) const
00227 {
00228         if ( idx > m_len )
00229         {
00230                 throw new IndexOutOfBoundsException();
00231         }
00232         return m_cstr[idx];
00233 }
00234 
00235 char String::operator[] (const int idx) const
00236 {
00237         if ( idx > m_len )
00238         {
00239                 throw new IndexOutOfBoundsException();
00240         }
00241         return m_cstr[idx];
00242 }
00243 
00244 StringPtr String::Substring( int start, int len ) const
00245 {
00246         char *ret;
00247         const char *cpstart;
00248 
00249         if ( len == 0 )
00250         {
00251                 return StringPtr(new String());
00252         }
00253 
00254         ASSERT ( len > 0 );
00255 
00256         if( start > m_len )
00257         {
00258                 start = m_len;
00259         }
00260         if ( start + len > m_len )
00261         {
00262                 len = m_len - start;
00263         }
00264         if ( len < 0 )
00265         {
00266                 len = 0;
00267         }
00268         cpstart = &m_cstr[start];
00269         ASSERT_PTR( cpstart );
00270 
00271         if ( NULL == (ret = (char *)malloc( len+1 )) )
00272         {
00273                 throw OutOfMemoryException();
00274         }
00275         StrCpyLen( ret, cpstart, len );
00276         ret[len] = eos;
00277         ASSERT_MEM( ret, len+1 );
00278 
00279         StringPtr strret = StringPtr(new String(ret));
00280         free(ret);
00281         return strret;
00282 }
00283 
00284 RefCountPtr<Vector<RefCountPtr<String> > > String::Split( const String& cp ) const
00285 {
00286         RefCountPtr<Vector<RefCountPtr<String> > > tvec(new Vector<StringPtr>);
00287         int delimlen = cp.Length();
00288         int delimpos = IndexOf( cp );
00289         int pos = 0;
00290 
00291         StringPtr span;
00292 
00293         if ( delimpos < 0 )
00294         {
00295                 tvec->Add( StringPtr(new String(m_cstr)) );
00296                 return tvec;
00297         }
00298         while ( pos < m_len )
00299         {
00300                 span = Mid( pos, delimpos );
00301                 pos = delimpos + delimlen;
00302                 tvec->Add( span );
00303 
00304                 delimpos = IndexOf( cp, delimpos+1 );
00305                 if ( delimpos < 0 )
00306                 {
00307                         break;
00308                 }
00309         }
00310         pos = m_len - pos;
00311         ASSERT( pos >- 0 );
00312         if ( pos > 0 )
00313         {
00314                 span = Right( pos );
00315                 tvec->Add( span );
00316         }
00317 
00318         return tvec;
00319 }
00320 
00321 int String::IndexOfAny( const String& cp ) const
00322 {
00323         int ret;
00324         int cplen = cp.Length();
00325         for ( int x = 0; x < m_len; x++ )
00326         {
00327                 for ( int y = 0; y < cplen; y++ )
00328                 {
00329                         if ( 0 <= (ret = IndexofchfromWithLen(m_cstr, cp[y], 0, m_len)) )
00330                         {
00331                                 return ret;
00332                         }
00333                 }
00334         }
00335         return -1;
00336 }
00337 
00338 StringPtr String::Replace(const char from, const char to) const
00339 {
00340         StringBuffer buf(m_cstr);
00341         buf.Replace(from, to);
00342         return buf.ToString();
00343 }
00344 
00345 StringPtr String::Replace(const char *from, const char *to) const
00346 {
00347         int start = 0;
00348         int pos = IndexOf( from );
00349         if ( 0 > pos )
00350         {
00351                 return StringPtr(new String(*this));
00352         }
00353 
00354         int fromlen = (int)strlen(from);
00355         StringBuffer buf(m_len + 10);
00356 
00357         while (start < m_len)
00358         {
00359                 buf.Append(m_cstr, start, pos-start);
00360                 start = pos;
00361                 buf.Append(to);
00362                 start += fromlen;
00363 
00364                 if ( 0 > (pos = IndexOf( from, start )) )
00365                 {
00366                         buf.Append(m_cstr, start, m_len - start);
00367                         start = m_len;
00368                 }
00369         }
00370         return buf.ToString();
00371 }
00372 
00373 StringPtr String::Cat( const String& cp, const int len ) const
00374 {
00375         char *buf = StrCat(m_cstr, m_len, cp.m_cstr, len);
00376         StringPtr ret = StringPtr(new String(buf));
00377         free(buf);
00378         return ret;
00379 }
00380 
00381 StringPtr String::Right( int len ) const
00382 {
00383         if ( len > m_len )
00384         {
00385                 len = m_len;
00386         }
00387         return Substring( m_len - len );
00388 }
00389 
00390 StringPtr String::Left( int len ) const
00391 {
00392         if ( len > m_len )
00393         {
00394                 len = m_len;
00395         }
00396         return Substring(0, len);
00397 }
00398 
00399 StringPtr String::Mid( int start, int stop ) const
00400 {
00401         return Substring( start, stop - start );
00402 }
00403 
00404 StringPtr String::RTrim(char ch) const
00405 {
00406         StringBuffer buf(m_len+1);
00407 
00408         int endpos;
00409         for ( endpos = m_len-1; m_cstr[endpos] == ch && endpos >= 0; endpos-- )
00410         {
00411         }
00412         for ( int x = 0; x <= endpos; x++ )
00413         {
00414                 ASSERT(m_cstr[x] != '\0');
00415                 buf.Append(m_cstr[x]);
00416         }
00417         return buf.ToString();
00418 }
00419 
00420 StringPtr String::LTrim(char ch) const
00421 {
00422         if (m_cstr[0] != ch)
00423         {
00424                 return StringPtr(new String(*this));
00425         }
00426         
00427         int pos;
00428         StringBuffer buf(m_len);
00429         for ( pos = 0; pos < m_len; pos++ )
00430         {
00431                 if (m_cstr[pos] != ch)
00432                 {
00433                         break;
00434                 }
00435         }
00436         for ( int x = pos; x < m_len; x++ )
00437         {
00438                 buf.Append(m_cstr[x]);
00439         }
00440 
00441         return buf.ToString();
00442 }
00443 
00444 StringPtr String::Trim() const
00445 {
00446         StringBuffer buf(m_len);
00447 
00448         int pos;
00449         for ( pos = 0; m_cstr[pos] == ' ' && pos < m_len; pos++ )
00450         {
00451         }
00452         int endpos;
00453         for ( endpos = m_len-1; m_cstr[endpos] == ' ' && endpos >= 0; endpos-- )
00454         {
00455         }
00456         for ( int x = pos; x <= endpos; x++ )
00457         {
00458                 buf.Append(m_cstr[x]);
00459         }
00460         return buf.ToString();
00461 }
00462 
00463 void String::UnIntern()
00464 {
00465         char *buf = (char *)malloc(m_len+1);
00466         StrCpyLen(buf, m_cstr, m_len);  
00467         m_isintern = false;
00468         m_cstr = buf;
00469 }
00470 
00471 //void String::InsertCharAt(char ch, int idx)
00472 //{
00473 //      ASSERT_MEM(m_cstr, m_len);
00474 //      
00475 //      if ( m_isintern )
00476 //      {
00477 //              UnIntern();
00478 //      }
00479 //
00480 //      if ( idx < 0 )
00481 //      {
00482 //              throw new InvalidArgumentException("Index cannot be less than zero.");
00483 //      }
00484 //      int tmplen;
00485 //      if ( idx > m_len )
00486 //      {
00487 //              tmplen = idx + 1;
00488 //      }
00489 //      else
00490 //      {
00491 //              tmplen = m_len + 1;
00492 //      }
00493 //      char *tmp = (char *)malloc(tmplen+1);
00494 //      if ( NULL == tmp )
00495 //      {
00496 //              throw OutOfMemoryException();
00497 //      }
00498 //      if( idx < m_len )
00499 //      {
00500 //              memcpy(tmp, m_cstr, idx);
00501 //              ASSERT_MEM(m_cstr, m_len);
00502 //              ASSERT_MEM(tmp, tmplen);
00503 //              memcpy(&tmp[idx+1], &m_cstr[idx], m_len - (idx) );
00504 //      }
00505 //      else
00506 //      {
00507 //              memcpy(tmp, m_cstr, m_len);
00508 //      }
00509 //      tmp[idx] = ch;
00510 //      tmp[tmplen] = '\0';
00511 //      
00512 //      free(m_cstr);
00513 //      m_cstr = tmp;
00514 //      m_len = tmplen;
00515 //}
00516 
00517 int String::IndexOf( const String& cp, const int start ) const
00518 {
00519         for ( int pos = start; pos < m_len; pos++ )
00520         {
00521                 int end;
00522                 int cppos = 1;
00523 
00524                 if ( m_cstr[pos] == cp[0] )
00525                 {
00526                         for( end = pos+1; end < m_len && cp[cppos] != '\0'; end++ )
00527                         {
00528                                 if ( m_cstr[end] != cp[cppos] )
00529                                 {
00530                                         break;
00531                                 }
00532                                 cppos++;
00533                         }
00534                         if ( cp[cppos] == '\0' )
00535                         {
00536                                 return pos;
00537                         }
00538                 }
00539         }
00540         return -1;
00541 }
00542 
00543 StringPtr String::Intern( const char *str )
00544 {
00545         String *s = new String();
00546         ASSERT(s->m_isintern);
00547 
00548         s->m_cstr = str;
00549         s->m_isintern = true;
00550         s->m_len = (int)strlen(str);
00551         return StringPtr(s);
00552 }
00553 
00554 int32 String::HashCode() const
00555 {
00556         return Math::Hash( m_cstr );
00557 }
00558 
00559 int String::Compare( const IComparable *istr ) const
00560 {
00561         if (istr->MajicNumber() != MajicNumber())
00562         {
00563                 return 1;
00564         }
00565         const String *str = static_cast<const String *>(istr);
00566         str->ValidateMem();
00567         return Compare( str );
00568 }
00569 
00570 bool String::Equals( const IComparable *istr ) const
00571 {
00572         if (istr->MajicNumber() != MajicNumber())
00573         {
00574                 return false;
00575         }
00576         const String *str = static_cast<const String *>(istr);
00577         str->ValidateMem();
00578         return Equals( str );
00579 }
00580 
00581 int String::Compare( const IComparable& istr ) const
00582 {
00583         if (istr.MajicNumber() != MajicNumber())
00584         {
00585                 return 1;
00586         }
00587         const String& str = static_cast<const String&>(istr);
00588         str.ValidateMem();
00589         return Compare( str );
00590 }
00591 
00592 bool String::Equals( const IComparable& istr ) const
00593 {
00594         if (istr.MajicNumber() != MajicNumber())
00595         {
00596                 return false;
00597         }
00598         const String& str = static_cast<const String&>(istr);
00599         str.ValidateMem();
00600         return Equals( str );
00601 }
00602 
00603 int32 String::MajicNumber() const
00604 {
00605         return STRING_MAJIC;
00606 }
00607 
00608 static const char *base64_chars = 
00609              "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
00610              "abcdefghijklmnopqrstuvwxyz"
00611              "0123456789+/";
00612 
00613 #define is_base64(c) ((isalnum(c) || (c == '+') || (c == '/')))
00614 
00615 RefCountPtr<String> String::Base64Encode( const char *cp, int len )
00616 {
00617         return Base64Encode(RefCountPtr<Array<byte> >(new Array<byte>((const byte *)cp, len)));
00618 }
00619 
00620 RefCountPtr<String> String::Base64Encode( RefCountPtr<Array<byte> > rcp )
00621 {
00622         StringBuffer ret(256);
00623         int i = 0;
00624         int j = 0;
00625         int in_len = rcp->Length();
00626         byte *cp = rcp->Data();
00627         unsigned char char_array_3[3];
00628         unsigned char char_array_4[4];
00629 
00630         while (in_len--) 
00631         {
00632                 char_array_3[i++] = *(cp++);
00633                 if (i == 3) 
00634                 {
00635                         char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
00636                         char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
00637                         char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
00638                         char_array_4[3] = char_array_3[2] & 0x3f;
00639 
00640                         for(i = 0; (i <4) ; i++)
00641                         {
00642                                 ret.Append(base64_chars[char_array_4[i]]);
00643                         }
00644                         i = 0;
00645                 }
00646         }
00647         if (i)
00648         {
00649                 for(j = i; j < 3; j++)
00650                 {
00651                         char_array_3[j] = '\0';
00652                 }
00653                 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
00654                 char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
00655                 char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
00656                 char_array_4[3] = char_array_3[2] & 0x3f;
00657 
00658                 for (j = 0; (j < i + 1); j++)
00659                 {
00660                         ret.Append(base64_chars[char_array_4[j]]);
00661                 }
00662                 while((i++ < 3))
00663                 {
00664                         ret.Append('=');
00665                 }
00666         }
00667         return ret.ToString();
00668 }
00669 
00670 RefCountPtr<Array<byte> > String::Base64Decode( const String& s )
00671 {
00672         int in_len = s.Length();
00673         int i = 0;
00674         int j = 0;
00675         int in_ = 0;
00676         unsigned char char_array_4[4], char_array_3[3];
00677         Vector<byte> ret(256);
00678 
00679         while (in_len-- && ( s[in_] != '=') && is_base64(s[in_])) 
00680         {
00681                 char_array_4[i++] = s[in_]; 
00682                 in_++;
00683                 if (i ==4) 
00684                 {
00685                         for (i = 0; i <4; i++)
00686                         {
00687                                 char_array_4[i] = IndexOfCh(base64_chars, char_array_4[i]);
00688                         }
00689                         char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
00690                         char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
00691                         char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
00692 
00693                         for (i = 0; (i < 3); i++)
00694                         {
00695                                 ret.Add(char_array_3[i]);
00696                         }
00697                         i = 0;
00698                 }
00699         }
00700 
00701         if (i) 
00702         {
00703                 for (j = i; j <4; j++)
00704                 {
00705                         char_array_4[j] = 0;
00706                 }
00707                 for (j = 0; j <4; j++)
00708                 {
00709                         char_array_4[j] = IndexOfCh(base64_chars, char_array_4[j]);
00710                 }
00711 
00712                 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
00713                 char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
00714                 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
00715 
00716                 for (j = 0; (j < i - 1); j++) 
00717                 {
00718                         ret.Add(char_array_3[j]);
00719                 }
00720         }
00721         return ret.ToArray();
00722 }
00723 
00724 #define PAD_RIGHT 1
00725 #define PAD_ZERO 2
00726 
00727 static void _prints(StringBuffer *out, const char *string, int width, int pad)
00728 {
00729         int padchar = ' ';
00730 
00731         if (width > 0) 
00732         {
00733                 register int len = 0;
00734                 register const char *ptr;
00735                 for (ptr = string; *ptr; ++ptr) 
00736                         ++len;
00737                 if (len >= width) 
00738                         width = 0;
00739                 else 
00740                         width -= len;
00741                 if (pad & PAD_ZERO) 
00742                         padchar = '0';
00743         }
00744         if (!(pad & PAD_RIGHT)) 
00745         {
00746                 for ( ; width > 0; --width) 
00747                 {
00748                         out->Append((char) padchar);
00749                 }
00750         }
00751         for ( ; *string ; ++string) 
00752         {
00753                 out->Append((char) *string);
00754         }
00755         for ( ; width > 0; --width) 
00756         {
00757                 out->Append((char) padchar);
00758         }
00759 }
00760 
00761 /* the following should be enough for 32 bit int */
00762 #define PRINT_BUF_LEN 12
00763 
00764 static void _printi(StringBuffer *out, int i, int b, int sg, int width, int pad, int letbase)
00765 {
00766         char print_buf[PRINT_BUF_LEN];
00767         register char *s;
00768         register int t, neg = 0;
00769         register unsigned int u = i;
00770 
00771         if (i == 0) 
00772         {
00773                 print_buf[0] = '0';
00774                 print_buf[1] = '\0';
00775                 _prints (out, print_buf, width, pad);
00776                 return;
00777         }
00778 
00779         if (sg && b == 10 && i < 0) 
00780         {
00781                 neg = 1;
00782                 u = -i;
00783         }
00784 
00785         s = print_buf + PRINT_BUF_LEN-1;
00786         *s = '\0';
00787 
00788         while (u) 
00789         {
00790                 t = u % b;
00791                 if( t >= 10 )
00792                         t += letbase - '0' - 10;
00793                 *--s = t + '0';
00794                 u /= b;
00795         }
00796 
00797         if (neg) 
00798         {
00799                 if( width && (pad & PAD_ZERO) ) 
00800                 {
00801                         out->Append((char) '-');
00802                         --width;
00803                 }
00804                 else 
00805                 {
00806                         *--s = '-';
00807                 }
00808         }
00809 
00810         _prints (out, s, width, pad);
00811 }
00812 
00813 static int _round_(char *bytes, int nDigits, int roundPos, bool roundDown) 
00814 {
00815         int next = roundPos + 1;
00816         if (next >= nDigits || bytes[next] < '5' ||
00817                 // MRI rounds up on nnn5nnn, but not nnn5 --
00818                 // except for when they do
00819                 (roundDown && bytes[next] == '5' && next == nDigits - 1)) 
00820         {
00821                 return nDigits;
00822         }
00823         if (roundPos < 0) 
00824         { // "%.0f" % 0.99
00825                 memcpy(bytes, &bytes[1], sizeof(char) * nDigits);
00826                 bytes[0] = '1';
00827                 return nDigits + 1;
00828         }
00829         bytes[roundPos] += 1;
00830         while (bytes[roundPos] > '9') 
00831         {
00832                 bytes[roundPos] = '0';
00833                 roundPos--;
00834                 if (roundPos >= 0) 
00835                 {
00836                         bytes[roundPos] += 1;
00837                 } 
00838                 else 
00839                 {
00840                         memcpy(bytes, &bytes[1], sizeof(char) * nDigits);
00841                         bytes[0] = '1';
00842                         return nDigits + 1;
00843                 }
00844         }
00845         return nDigits;
00846 }
00847 
00848 
00849 static void _printdf(StringBuffer *out, char fchar, double d, int width, int pad)
00850 {
00851 //
00852 //http://www.java2s.com/Open-Source/Java-Document/Open-Source-Script/jruby/org/jruby/util/Sprintf.java.htm
00853 //
00854         int precision = 0;
00855         double dval = d;
00856         bool nan = Math::IsNaN(dval);
00857         bool inf = Math::IsINF(dval);
00858 
00859         bool negative = dval < 0.0;
00860         int nDigits = 0;
00861         int exponent = 0;
00862         bool expForm = false;
00863 
00864         int len = 0;
00865         char signChar;
00866 
00867     if (nan || inf) 
00868         {
00869                 const char *cdigits = NULL;
00870         if (nan) 
00871                 {
00872             cdigits = "NaN";
00873             len = 3;
00874         } 
00875                 else 
00876                 {
00877             cdigits = "Inf";
00878             len = 3;
00879        }
00880         if (negative) 
00881                 {
00882             signChar = '-';
00883             width--;
00884         } 
00885                 //else if (pad & PAD_PLUS) 
00886                 //{
00887         //    signChar = '+';
00888         //    width--;
00889         //} 
00890                 else 
00891                 {
00892             signChar = 0;
00893         }
00894         width -= len;
00895 
00896                 if (width > 0 && (pad & PAD_RIGHT) == 0) 
00897                 {
00898                     out->Fill(' ', width);
00899                     width = 0;
00900                 }
00901                 if (signChar != 0)
00902                 {
00903                         out->Append(signChar);
00904                 }
00905 
00906         if (width > 0 && (pad & PAD_ZERO) == 0) 
00907                 {
00908             out->Fill('0', width);
00909                         width = 0;
00910         }
00911         
00912                 out->Append(cdigits);
00913 
00914         if (width > 0)
00915                 {
00916             out->Fill(' ', width);
00917                 }
00918 
00919         return;
00920     }
00921 
00922         StringPtr str = Double::ToString(dval);
00923         int strlen = str->Length();
00924         char *digits = new char[strlen];
00925         int nTrailingZeroes = 0;
00926         int i = negative ? 1 : 0;
00927         int decPos = 0;
00928         char ival;
00929         bool decfound = false;
00930         while( i < strlen && !decfound ) 
00931         {
00932                 switch (ival = (byte) str->CharAt(i++)) 
00933                 {
00934                 case '0':
00935                         if (nDigits > 0)
00936                                 nTrailingZeroes++;
00937 
00938                         break; // switch
00939                 case '1':
00940                 case '2':
00941                 case '3':
00942                 case '4':
00943                 case '5':
00944                 case '6':
00945                 case '7':
00946                 case '8':
00947                 case '9':
00948                         if (nTrailingZeroes > 0) 
00949                         {
00950                                 for (; nTrailingZeroes > 0; nTrailingZeroes--) 
00951                                 {
00952                                         digits[nDigits++] = '0';
00953                                 }
00954                         }
00955                         digits[nDigits++] = ival;
00956                         break; // switch
00957                 case '.':
00958                         decfound = true;
00959                         break;
00960                 }
00961         }
00962         decPos = nDigits + nTrailingZeroes;
00963         decfound = false;
00964         while(i < strlen && !decfound) 
00965         {
00966                 switch (ival = (byte) str->CharAt(i++)) 
00967                 {
00968                 case '0':
00969                         if (nDigits > 0) 
00970                         {
00971                                 nTrailingZeroes++;
00972                         } 
00973                         else 
00974                         {
00975                                 exponent--;
00976                         }
00977                         break; // switch
00978                 case '1':
00979                 case '2':
00980                 case '3':
00981                 case '4':
00982                 case '5':
00983                 case '6':
00984                 case '7':
00985                 case '8':
00986                 case '9':
00987                         if (nTrailingZeroes > 0) 
00988                         {
00989                                 for (; nTrailingZeroes > 0; nTrailingZeroes--) 
00990                                 {
00991                                         digits[nDigits++] = '0';
00992                                 }
00993                         }
00994                         digits[nDigits++] = ival;
00995                         break; // switch
00996                 case 'E':
00997                         decfound = true;
00998                         break;
00999                 }
01000         }
01001         if (i < strlen) 
01002         {
01003                 int expSign;
01004                 int expVal = 0;
01005                 if (str->CharAt(i) == '-') 
01006                 {
01007                         expSign = -1;
01008                         i++;
01009                 } 
01010                 else 
01011                 {
01012                         expSign = 1;
01013                 }
01014                 for (; i < strlen;) 
01015                 {
01016                         expVal = expVal
01017                                         * 10
01018                                         + ((int) str->CharAt(i++) - (int) '0');
01019                 }
01020                 exponent += expVal * expSign;
01021         }
01022         exponent += decPos - nDigits;
01023 
01024         // gotta have at least a zero...
01025         if (nDigits == 0) 
01026         {
01027                 digits[0] = '0';
01028                 nDigits = 1;
01029                 exponent = 0;
01030         }
01031 
01032         // OK, we now have the significand in digits[0...nDigits]
01033         // and the exponent in exponent.  We're ready to format.
01034 
01035         int intDigits, intZeroes, intLength;
01036         int decDigits, decZeroes, decLength;
01037         char expChar;
01038 
01039         if (negative) 
01040         {
01041                 signChar = '-';
01042                 width--;
01043         } 
01044         //else if (pad & PAD_PLUS) 
01045         //{
01046         //      signChar = '+';
01047         //      width--;
01048         //} 
01049         //else if ((flags & FLAG_SPACE) != 0) 
01050         //{
01051         //      signChar = ' ';
01052         //      width--;
01053         //} 
01054         else 
01055         {
01056                 signChar = 0;
01057         }
01058         //if ((flags & FLAG_PRECISION) == 0) 
01059         //{
01060                 precision = 6;
01061         //}
01062 
01063         switch (fchar)
01064         {
01065         case 'E':
01066         case 'G':
01067                 expChar = 'E';
01068                 break;
01069         case 'e':
01070         case 'g':
01071                 expChar = 'e';
01072                 break;
01073         default:
01074                 expChar = 0;
01075         }
01076 
01077         switch (fchar) 
01078         {
01079         case 'g':
01080         case 'G':
01081                 // an empirically derived rule: precision applies to
01082                 // significand length, irrespective of exponent
01083 
01084                 // an official rule, clarified: if the exponent
01085                 // <clarif>after adjusting for exponent form</clarif>
01086                 // is < -4,  or the exponent <clarif>after adjusting 
01087                 // for exponent form</clarif> is greater than the
01088                 // precision, use exponent form
01089                 expForm = (exponent + nDigits - 1 < -4 || exponent
01090                            + nDigits > (precision == 0 ? 1
01091                                 : precision));
01092                 // it would be nice (and logical!) if exponent form 
01093                 // behaved like E/e, and decimal form behaved like f,
01094                 // but no such luck. hence: 
01095                 if (expForm) 
01096                 {
01097                         // intDigits isn't used here, but if it were, it would be 1
01098                         // intDigits = 1; 
01099                         decDigits = nDigits - 1;
01100                         // precision for G/g includes integer digits
01101                         precision = Math::Max(0, precision - 1);
01102 
01103                         if (precision < decDigits) 
01104                         {
01105                                 int n = _round_(digits, nDigits, precision, precision != 0);
01106                                 ASSERT_MEM(digits, strlen*sizeof(char));
01107                                 if (n > nDigits) 
01108                                 {
01109                                         nDigits = n;
01110                                 }
01111                                 decDigits = Math::Min(nDigits - 1, precision);
01112                         }
01113                         exponent += nDigits - 1;
01114                         if (precision > 0) 
01115                         {
01116                                 len += 1 + precision; // n.prec
01117                         } 
01118                         else 
01119                         {
01120                                 len += 1; // n
01121                                 //if ((flags & FLAG_SHARP) != 0) 
01122                                 //{
01123                                 //      len++; // will have a trailing '.'
01124                                 //}
01125                    }
01126 
01127                    width -= len + 5; // 5 -> e+nnn / e-nnn
01128 
01129                         if (width > 0 && (pad & (PAD_ZERO/* | FLAG_MINUS*/)) == 0) 
01130                         {
01131                                 out->Fill(' ', width);
01132                                 width = 0;
01133                         }
01134                         if (signChar != 0) 
01135                         {
01136                                 out->Append(signChar);
01137                         }
01138                         //if (width > 0 && (flags & FLAG_MINUS) == 0) 
01139                         //{
01140                         //      buf.fill('0', width);
01141                         //      width = 0;
01142                         //}
01143                         // now some data...
01144                         out->Append(digits[0]);
01145                         if (precision > 0) 
01146                         {
01147                                 out->Append('.'); // '.'
01148                                 if (decDigits > 0) 
01149                                 {
01150                                         out->Append(digits, 1, decDigits);
01151                                         precision -= decDigits;
01152                                 }
01153                         } 
01154                         //else if ((flags & FLAG_SHARP) != 0) 
01155                         //{
01156                         //      out->Append('.');
01157                         //}
01158                         out->Append(expChar); // E or e
01159                         if ( exponent >= 0 )
01160                                 out->Append('-');
01161                         if (exponent < 0) 
01162                         {
01163                                 exponent = -exponent;
01164                         }
01165                         out->Append((char)(exponent / 100 + '0'));
01166                         out->Append((char)(exponent % 100 / 10 + '0'));
01167                         out->Append((char)(exponent % 10 + '0'));
01168                         if (width > 0) 
01169                         {
01170                                 out->Fill(' ', width);
01171                         }
01172                 } 
01173                 else  // decimal form, like (but not *just* like!) 'f'
01174                 {
01175                         intDigits = Math::Max(0, Math::Min(nDigits + exponent, nDigits));
01176                         intZeroes = Math::Max(0, exponent);
01177                         intLength = intDigits + intZeroes;
01178                         decDigits = nDigits - intDigits; 
01179                         decZeroes = Math::Max(0, -(decDigits + exponent));
01180                         decLength = decZeroes + decDigits;
01181                         precision = Math::Max(0, precision - intLength);
01182 
01183                         if (precision < decDigits) 
01184                         {
01185                                 int n = _round_(digits, nDigits,
01186                                                 intDigits + precision - 1,
01187                                                 precision != 0);
01188                                 ASSERT_MEM(digits, strlen*sizeof(char));
01189                                 if (n > nDigits) 
01190                                 {
01191                                         // digits array shifted, update all
01192                                         nDigits = n;
01193                                         intDigits = Math::Max(0, Math::Min(nDigits + exponent, nDigits));
01194                                         intLength = intDigits + intZeroes;
01195                                         decDigits = nDigits - intDigits;
01196                                         decZeroes = Math::Max(0, -(decDigits + exponent));
01197                                         precision = Math::Max(0, precision - 1);
01198                                 }
01199                                 decDigits = precision;
01200                                 decLength = decZeroes + decDigits;
01201                         }
01202                         len += intLength;
01203                         if (decLength > 0) 
01204                         {
01205                                 len += decLength + 1;
01206                         } 
01207                         else 
01208                         {
01209                                 //if ((flags & FLAG_SHARP) != 0) 
01210                                 //{
01211                                 //      len++; // will have a trailing '.'
01212                                 //      if (precision > 0) // g fills trailing zeroes if #
01213                                 //      { 
01214                                 //              len += precision;
01215                                 //      }
01216                                 //}
01217                         }
01218 
01219                         width -= len;
01220 
01221                         if (width > 0 && (pad & (PAD_ZERO/* | FLAG_MINUS*/)) == 0) 
01222                         {
01223                                 out->Fill(' ', width);
01224                                 width = 0;
01225                         }
01226                         if (signChar != 0) 
01227                         {
01228                                 out->Append(signChar);
01229                         }
01230                         //if (width > 0 && (flags & FLAG_MINUS) == 0) 
01231                         //{
01232                         //      out->Fill('0', width);
01233                         //      width = 0;
01234                         //}
01235                         // now some data...
01236                         if (intLength > 0) 
01237                         {
01238                                 if (intDigits > 0)  // s/b true, since intLength > 0
01239                                 {
01240                                         out->Append(digits, 0, intDigits);
01241                                 }
01242                                 if (intZeroes > 0) 
01243                                 {
01244                                         out->Fill('0', intZeroes);
01245                                 }
01246                         } 
01247                         else 
01248                         {
01249                                 // always need at least a 0
01250                                 out->Append('0');
01251                         }
01252                         if (decLength > 0 /*|| (flags & FLAG_SHARP) != 0*/) 
01253                         {
01254                                 out->Append('.');
01255                         }
01256                         if (decLength > 0) 
01257                         {
01258                                 if (decZeroes > 0) 
01259                                 {
01260                                         out->Fill('0', decZeroes);
01261                                         precision -= decZeroes;
01262                                 }
01263                                 if (decDigits > 0) 
01264                                 {
01265                                         out->Append(digits, intDigits, decDigits);
01266                                         precision -= decDigits;
01267                                 }
01268                                 //if ((flags & FLAG_SHARP) != 0 && precision > 0) 
01269                                 //{
01270                                 //      out->Fill('0', precision);
01271                                 //}
01272                         }
01273                         //if ((flags & FLAG_SHARP) != 0 && precision > 0) 
01274                         //{
01275                         //      out->Fill('0', precision);
01276                         //}
01277                         if (width > 0) 
01278                         {
01279                                 out->Fill(' ', width);
01280                         }
01281                 }
01282                 break;
01283 
01284         case 'f':
01285                 intDigits = Math::Max(0, Math::Min(nDigits + exponent, nDigits));
01286                 intZeroes = Math::Max(0, exponent);
01287                 intLength = intDigits + intZeroes;
01288                 decDigits = nDigits - intDigits;
01289                 decZeroes = Math::Max(0, -(decDigits + exponent));
01290                 decLength = decZeroes + decDigits;
01291 
01292                 if (precision < decLength) 
01293                 {
01294                         if (precision < decZeroes) 
01295                         {
01296                                 decDigits = 0;
01297                                 decZeroes = precision;
01298                         } 
01299                         else 
01300                         {
01301                                 int n = _round_(digits, nDigits, intDigits + precision  - decZeroes - 1, precision != 0);
01302                                 ASSERT_MEM(digits, strlen*sizeof(char));
01303                                 if (n > nDigits) 
01304                                 {
01305                                         // digits arr shifted, update all
01306                                         nDigits = n;
01307                                         intDigits = Math::Max(0, Math::Min(nDigits + exponent, nDigits));
01308                                         intLength = intDigits + intZeroes;
01309                                         decDigits = nDigits - intDigits;
01310                                         decZeroes = Math::Max(0, -(decDigits + exponent));
01311                                         decLength = decZeroes + decDigits;
01312                                 }
01313                                 decDigits = precision - decZeroes;
01314                         }
01315                         decLength = decZeroes + decDigits;
01316                 }
01317                 if (precision > 0) 
01318                 {
01319                         len += Math::Max(1, intLength) + 1 + precision;
01320                         // (1|intlen).prec
01321                 } 
01322                 else 
01323                 {
01324                         len += Math::Max(1, intLength);
01325                         // (1|intlen)
01326                         //if ((flags & FLAG_SHARP) != 0) 
01327                         //{
01328                         //      len++; // will have a trailing '.'
01329                         //}
01330                 }
01331 
01332                 width -= len;
01333 
01334                 if (width > 0 && (pad & (PAD_ZERO/* | FLAG_MINUS*/)) == 0) 
01335                 {
01336                         out->Fill(' ', width);
01337                         width = 0;
01338                 }
01339                 if (signChar != 0) 
01340                 {
01341                         out->Append(signChar);
01342                 }
01343                 //if (width > 0 && (flags & FLAG_MINUS) == 0) 
01344                 //{
01345                 //      out->Fill('0', width);
01346                 //      width = 0;
01347                 //}
01348                 // now some data...
01349                 if (intLength > 0) 
01350                 {
01351                         if (intDigits > 0) // s/b true, since intLength > 0
01352                         { 
01353                                 out->Append(digits, 0, intDigits);
01354                         }
01355                         if (intZeroes > 0) 
01356                         {
01357                                 out->Fill('0', intZeroes);
01358                         }
01359                 } 
01360                 else 
01361                 {
01362                         // always need at least a 0
01363                         out->Append('0');
01364                 }
01365                 if (precision > 0 /*|| (flags & FLAG_SHARP) != 0*/) 
01366                 {
01367                         out->Append('.');
01368                 }
01369                 if (precision > 0) 
01370                 {
01371                         if (decZeroes > 0) 
01372                         {
01373                                 out->Fill('0', decZeroes);
01374                                 precision -= decZeroes;
01375                         }
01376                         if (decDigits > 0) 
01377                         {
01378                                 out->Append(digits, intDigits, decDigits);
01379                                 precision -= decDigits;
01380                         }
01381                         // fill up the rest with zeroes
01382                         if (precision > 0) 
01383                         {
01384                                 out->Fill('0', precision);
01385                         }
01386                 }
01387                 if (width > 0) 
01388                 {
01389                         out->Fill(' ', width);
01390                 }
01391                 break;
01392         case 'E':
01393         case 'e':
01394                 // intDigits isn't used here, but if it were, it would be 1
01395                 // intDigits = 1; 
01396                 decDigits = nDigits - 1;
01397 
01398                 if (precision < decDigits) 
01399                 {
01400                         int n = _round_(digits, nDigits, precision, precision != 0);
01401                         if (n > nDigits) 
01402                         {
01403                                 nDigits = n;
01404                         }
01405                         decDigits = Math::Min(nDigits - 1, precision);
01406                 }
01407                 exponent += nDigits - 1;
01408                 if (precision > 0) 
01409                 {
01410                         len += 2 + precision; // n.prec
01411                 } 
01412                 else 
01413                 {
01414                         len += 1; // n
01415                         //if ((pad & FLAG_SHARP) != 0) 
01416                         //{
01417                         //      len++; // will have a trailing '.'
01418                         //}
01419                 }
01420 
01421                 width -= len + 5; // 5 -> e+nnn / e-nnn
01422 
01423                 if (width > 0 && (pad & (PAD_ZERO/* | FLAG_MINUS*/)) == 0) 
01424                 {
01425                         out->Fill(' ', width);
01426                         width = 0;
01427                 }
01428                 if (signChar != 0) 
01429                 {
01430                         out->Append(signChar);
01431                 }
01432                 //if (width > 0 && (flags & FLAG_MINUS) == 0) 
01433                 //{
01434                 //      out->Fill('0', width);
01435                 //   width = 0;
01436                 //}
01437                 // now some data...
01438                 out->Append(digits[0]);
01439                 if (precision > 0) 
01440                 {
01441                         out->Append('.'); // '.'
01442                         if (decDigits > 0) 
01443                         {
01444                                 out->Append(digits, 1, decDigits);
01445                                 precision -= decDigits;
01446                         }
01447                         if (precision > 0) 
01448                         {
01449                                 out->Fill('0', precision);
01450                         }
01451 
01452                 } 
01453                 //else if ((flags & FLAG_SHARP) != 0) 
01454                 //{
01455                 //      out->Append(args.getDecimalSeparator());
01456                 //}
01457                 out->Append(expChar); // E or e
01458                 out->Append(exponent >= 0 ? '+' : '-');
01459                 if (exponent < 0) 
01460                 {
01461                         exponent = -exponent;
01462                 }
01463                 out->Append((char)(exponent / 100 + '0'));
01464                 out->Append((char)(exponent % 100 / 10 + '0'));
01465                 out->Append((char)(exponent % 10 + '0'));
01466                 if (width > 0) 
01467                 {
01468                         out->Fill(' ', width);
01469                 }
01470                 break;
01471         } // switch (format char E,e,f,G,g)
01472 
01473         delete [] digits;
01474 }
01475 
01476 // Do not use a reference for sformat -- va args don't work with reference.
01477 StringPtr String::FormatVA(const String sformat, va_list args)
01478 {
01479         StringBuffer out;
01480         const char *format = sformat.GetChars();
01481 
01482         register int width = 0, pad = 0;
01483         char scr[2];
01484 
01485         for (; *format != 0; ++format) 
01486         {
01487                 if (*format == '%') 
01488                 {
01489                         ++format;
01490                         width = pad = 0;
01491                         if (*format == '\0') 
01492                                 break;
01493                         if (*format == '%') 
01494                         {
01495                                 out.Append((char)*format);
01496                                 continue;
01497                         }
01498                         if (*format == '-') 
01499                         {
01500                                 ++format;
01501                                 pad = PAD_RIGHT;
01502                         }
01503                         while (*format == '0') 
01504                         {
01505                                 ++format;
01506                                 pad |= PAD_ZERO;
01507                         }
01508                         for ( ; *format >= '0' && *format <= '9'; ++format) 
01509                         {
01510                                 width *= 10;
01511                                 width += *format - '0';
01512                         }
01513                         if( *format == 's' ) 
01514                         {
01515                                 register char *s = (char *)va_arg( args, char * );
01516                                 _prints (&out, s?s:"(null)", width, pad);
01517                                 continue;
01518                         }
01519                         if( *format == 'd' ) 
01520                         {
01521                                 _printi (&out, va_arg( args, int ), 10, 1, width, pad, 'a');
01522                                 continue;
01523                         }
01524                         if( *format == 'x' ) 
01525                         {
01526                                 _printi (&out, va_arg( args, int ), 16, 0, width, pad, 'a');
01527                                 continue;
01528                         }
01529                         if( *format == 'X' ) 
01530                         {
01531                                 _printi (&out, va_arg( args, int ), 16, 0, width, pad, 'A');
01532                                 continue;
01533                         }
01534                         if( *format == 'u' ) 
01535                         {
01536                                 _printi (&out, va_arg( args, int ), 10, 0, width, pad, 'a');
01537                                 continue;
01538                         }
01539                         if( *format == 'c' ) 
01540                         {
01541                                 /* char are converted to int then pushed on the stack */
01542                                 scr[0] = (char)va_arg( args, int );
01543                                 scr[1] = '\0';
01544                                 _prints (&out, scr, width, pad);
01545                                 continue;
01546                         }
01547                         if ( *format == 'f' )
01548                         {
01549                                 _printdf(&out, *format, va_arg(args, double), width, pad);
01550                                 continue;
01551                         }
01552                         if ( *format == 'e' || *format == 'E')
01553                         {
01554                                 _printdf(&out, *format, va_arg(args, double), width, pad);
01555                                 continue;
01556                         }
01557                         if ( *format == 'g' || *format == 'G' )
01558                         {
01559                                 _printdf(&out, *format, va_arg(args, double), width, pad);
01560                                 continue;
01561                         }
01562                 }
01563                 else 
01564                 {
01565                         out.Append((char) *format);
01566                 }
01567         }
01568         return out.ToString();
01569 }
01570 
01571 // Do not use reference for format -- breaks va args.
01572 StringPtr String::Format(const String format, ...)
01573 {
01574         va_list args;
01575         va_start(args, format);
01576         
01577         StringPtr ret = FormatVA(format, args);
01578 
01579         va_end( args );
01580         return ret;
01581 }
01582 
01583 StringPtr String::StripQuotes()
01584 {
01585         if ( CharAt(0) == '"' && CharAt( Length() -1 ) == '"' )
01586         {
01587                 StringBuffer buf(m_cstr);
01588                 buf.RemoveCharAt(0);
01589                 buf.RemoveCharAt(buf.Length() - 1);
01590                 return buf.ToString();
01591         }
01592         else
01593         {
01594                 return StringPtr(new String(*this));
01595         }
01596 }
01597 
01598 StringPtr String::ToUpper() const
01599 {
01600         StringBuffer buf(m_len+1);
01601         for (int x = 0; x < m_len; x++)
01602         {
01603                 buf.Append((char)toupper(m_cstr[x]));
01604         }
01605         return buf.ToString();
01606 }
01607 
01608 StringPtr String::ToLower() const
01609 {
01610         StringBuffer buf(m_len+1);
01611         for (int x = 0; x < m_len; x++)
01612         {
01613                 buf.Append((char)tolower(m_cstr[x]));
01614         }
01615         return buf.ToString();
01616 }
01617 
01618 bool String::EndsWith( const String& scp ) const
01619 {
01620         int pos = m_len - scp.Length();
01621         const char *cp = scp.m_cstr;
01622         int x;
01623 
01624         if ( pos <= 0 )
01625         {
01626                 return false;
01627         }
01628         for ( x = pos; x < m_len; x++ )
01629         {
01630                 if ( m_cstr[x] != *cp++ )
01631                 {
01632                         return false;
01633                 }
01634         }
01635         return true;
01636 }
01637 
01638 bool String::StartsWith( const String& cp ) const
01639 {
01640         int len = cp.Length();
01641         int x;
01642 
01643         if ( len > m_len )
01644         {
01645                 return false;
01646         }
01647         for ( x = 0; x < len; x++ )
01648         {
01649                 if ( m_cstr[x] != cp[x] )
01650                 {
01651                         return false;
01652                 }
01653         }
01654         return true;
01655 }
01656 
01657 int String::LastIndexOf( const char ch ) const
01658 {
01659         for ( int pos = m_len-1; pos > -1; pos-- )
01660         {
01661                 if ( m_cstr[pos] == ch )
01662                 {
01663                         return pos;
01664                 }
01665         }
01666         return -1;
01667 }
01668 
01669 int String::CountChar( const char *str, const int len, const char ch )
01670 {
01671         int count = 0;
01672         for ( int x = 0; x < len; x++ )
01673         {
01674                 if ( ch == str[x] )
01675                 {
01676                         count++;
01677                 }
01678         }
01679         return count;
01680 }
01681 
01682 RefCountPtr<Array<byte> > String::ToByteArray() const
01683 {
01684         RefCountPtr<Array<byte> > bytes(new Array<byte>(m_len));
01685         for ( int x = 0; x < m_len; x++ )
01686         {
01687                 (*bytes)[x] = m_cstr[x];
01688         }
01689 
01690         return bytes;
01691 }
01692 
01693 String operator +(const char *cp, const String& str)
01694 {
01695         return *String(cp).Cat(str);
01696 }
01697 
01698 #if defined(DEBUG) || defined(_DEBUG)
01699 void String::CheckMem() const
01700 {
01701         if ( ! m_isintern )
01702         {
01703                 DEBUG_NOTE_MEM_ALLOCATION( m_cstr );
01704         }
01705 }
01706 
01707 void String::ValidateMem() const
01708 {
01709         if ( ! m_isintern )
01710         {
01711                 ASSERT_MEM( m_cstr, m_len );
01712         }
01713 }
01714 #endif