1 // natString.cc - Implementation of java.lang.String native methods.
2 
3 /* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003  Free Software Foundation
4 
5    This file is part of libgcj.
6 
7 This software is copyrighted work licensed under the terms of the
8 Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
9 details.  */
10 
11 #include <config.h>
12 
13 #include <string.h>
14 #include <stdlib.h>
15 
16 #include <gcj/cni.h>
17 #include <java/lang/Character.h>
18 #include <java/lang/String.h>
19 #include <java/lang/IndexOutOfBoundsException.h>
20 #include <java/lang/ArrayIndexOutOfBoundsException.h>
21 #include <java/lang/StringIndexOutOfBoundsException.h>
22 #include <java/lang/NullPointerException.h>
23 #include <java/lang/StringBuffer.h>
24 #include <java/io/ByteArrayOutputStream.h>
25 #include <java/io/OutputStreamWriter.h>
26 #include <java/io/ByteArrayInputStream.h>
27 #include <java/io/InputStreamReader.h>
28 #include <java/util/Locale.h>
29 #include <gnu/gcj/convert/UnicodeToBytes.h>
30 #include <gnu/gcj/convert/BytesToUnicode.h>
31 #include <gnu/gcj/runtime/StringBuffer.h>
32 #include <jvm.h>
33 
34 static jstring* strhash = NULL;
35 static int strhash_count = 0;  /* Number of slots used in strhash. */
36 static int strhash_size = 0;  /* Number of slots available in strhash.
37                                * Assumed be power of 2! */
38 
39 // Some defines used by toUpperCase / toLowerCase.
40 #define ESSET     0x00df
41 #define CAPITAL_S 0x0053
42 #define SMALL_I   0x0069
43 #define CAPITAL_I_WITH_DOT 0x0130
44 #define SMALL_DOTLESS_I    0x0131
45 #define CAPITAL_I 0x0049
46 
47 #define DELETED_STRING ((jstring)(~0))
48 #define SET_STRING_IS_INTERNED(STR) /* nothing */
49 
50 #define UNMASK_PTR(Ptr) (((unsigned long) (Ptr)) & ~0x01)
51 #define MASK_PTR(Ptr) (((unsigned long) (Ptr)) | 0x01)
52 #define PTR_MASKED(Ptr) (((unsigned long) (Ptr)) & 0x01)
53 
54 /* Find a slot where the string with elements DATA, length LEN,
55    and hash HASH should go in the strhash table of interned strings. */
56 jstring*
_Jv_StringFindSlot(jchar * data,jint len,jint hash)57 _Jv_StringFindSlot (jchar* data, jint len, jint hash)
58 {
59   JvSynchronize sync (&StringClass);
60 
61   int start_index = hash & (strhash_size - 1);
62   int deleted_index = -1;
63 
64   int index = start_index;
65   /* step must be non-zero, and relatively prime with strhash_size. */
66   jint step = (hash ^ (hash >> 16)) | 1;
67   for (;;)
68     {
69       jstring* ptr = &strhash[index];
70       jstring value = (jstring) UNMASK_PTR (*ptr);
71       if (value == NULL)
72 	{
73 	  if (deleted_index >= 0)
74 	    return (&strhash[deleted_index]);
75 	  else
76 	    return ptr;
77 	}
78       else if (*ptr == DELETED_STRING)
79 	deleted_index = index;
80       else if (value->length() == len
81 	       && memcmp(JvGetStringChars(value), data, 2*len) == 0)
82 	return (ptr);
83       index = (index + step) & (strhash_size - 1);
84       JvAssert (index != start_index);
85     }
86 }
87 
88 /* Calculate a hash code for the string starting at PTR at given LENGTH.
89    This uses the same formula as specified for java.lang.String.hash. */
90 
91 static jint
hashChars(jchar * ptr,jint length)92 hashChars (jchar* ptr, jint length)
93 {
94   jchar* limit = ptr + length;
95   jint hash = 0;
96   // Updated specification from
97   // http://www.javasoft.com/docs/books/jls/clarify.html.
98   while (ptr < limit)
99     hash = (31 * hash) + *ptr++;
100   return hash;
101 }
102 
103 jint
hashCode()104 java::lang::String::hashCode()
105 {
106   if (cachedHashCode == 0)
107     cachedHashCode = hashChars(JvGetStringChars(this), length());
108   return cachedHashCode;
109 }
110 
111 jstring*
_Jv_StringGetSlot(jstring str)112 _Jv_StringGetSlot (jstring str)
113 {
114   jchar* data = JvGetStringChars(str);
115   int length = str->length();
116   return _Jv_StringFindSlot(data, length, hashChars (data, length));
117 }
118 
119 void
rehash()120 java::lang::String::rehash()
121 {
122   JvSynchronize sync (&StringClass);
123 
124   if (strhash == NULL)
125     {
126       strhash_size = 1024;
127       strhash = (jstring *) _Jv_AllocBytes (strhash_size * sizeof (jstring));
128       memset (strhash, 0, strhash_size * sizeof (jstring));
129     }
130   else
131     {
132       int i = strhash_size;
133       jstring* ptr = strhash + i;
134       int nsize = strhash_size * 2;
135       jstring *next = (jstring *) _Jv_AllocBytes (nsize * sizeof (jstring));
136       memset (next, 0, nsize * sizeof (jstring));
137 
138       while (--i >= 0)
139 	{
140 	  --ptr;
141 	  if (*ptr == NULL || *ptr == DELETED_STRING)
142 	    continue;
143 
144 	  /* This is faster equivalent of
145 	   * *__JvGetInternSlot(*ptr) = *ptr; */
146 	  jstring val = (jstring) UNMASK_PTR (*ptr);
147 	  jint hash = val->hashCode();
148 	  jint index = hash & (nsize - 1);
149 	  jint step = (hash ^ (hash >> 16)) | 1;
150 	  for (;;)
151 	    {
152 	      if (next[index] == NULL)
153 		{
154 		  next[index] = *ptr;
155 		  break;
156 		}
157 	      index = (index + step) & (nsize - 1);
158 	    }
159 	}
160 
161       strhash_size = nsize;
162       strhash = next;
163     }
164 }
165 
166 jstring
intern()167 java::lang::String::intern()
168 {
169   JvSynchronize sync (&StringClass);
170   if (3 * strhash_count >= 2 * strhash_size)
171     rehash();
172   jstring* ptr = _Jv_StringGetSlot(this);
173   if (*ptr != NULL && *ptr != DELETED_STRING)
174     {
175       // See description in _Jv_FinalizeString() to understand this.
176       *ptr = (jstring) MASK_PTR (*ptr);
177       return (jstring) UNMASK_PTR (*ptr);
178     }
179   jstring str = (this->data == this
180 		 ? this
181 		 : _Jv_NewString(JvGetStringChars(this), this->length()));
182   SET_STRING_IS_INTERNED(str);
183   strhash_count++;
184   *ptr = str;
185   // When string is GC'd, clear the slot in the hash table.
186   _Jv_RegisterStringFinalizer (str);
187   return str;
188 }
189 
190 // The fake String finalizer.  This is only used when the String has
191 // been intern()d.  However, we must check this case, as it might be
192 // called by the Reference code for any String.
193 void
_Jv_FinalizeString(jobject obj)194 _Jv_FinalizeString (jobject obj)
195 {
196   JvSynchronize sync (&StringClass);
197 
198   // We might not actually have intern()d any strings at all, if
199   // we're being called from Reference.
200   if (! strhash)
201     return;
202 
203   jstring str = reinterpret_cast<jstring> (obj);
204   jstring *ptr = _Jv_StringGetSlot(str);
205   if (*ptr == NULL || *ptr == DELETED_STRING
206       || (jobject) UNMASK_PTR (*ptr) != obj)
207     return;
208 
209   // We assume the lowest bit of the pointer is free for our nefarious
210   // manipulations.  What we do is set it to `0' (implicitly) when
211   // interning the String.  If we subsequently re-intern the same
212   // String, then we set the bit.  When finalizing, if the bit is set
213   // then we clear it and re-register the finalizer.  We know this is
214   // a safe approach because both intern() and _Jv_FinalizeString()
215   // acquire the class lock; this bit can't be manipulated when the
216   // lock is not held.  So if we are finalizing and the bit is clear
217   // then we know all references are gone and we can clear the entry
218   // in the hash table.  The naive approach of simply clearing the
219   // pointer here fails in the case where a request to intern a new
220   // string with the same contents is made between the time the
221   // intern()d string is found to be unreachable and when the
222   // finalizer is actually run.  In this case we could clear a pointer
223   // to a valid string, and future intern() calls for that particular
224   // value would spuriously fail.
225   if (PTR_MASKED (*ptr))
226     {
227       *ptr = (jstring) UNMASK_PTR (*ptr);
228       _Jv_RegisterStringFinalizer (obj);
229     }
230   else
231     {
232       *ptr = DELETED_STRING;
233       strhash_count--;
234     }
235 }
236 
237 jstring
_Jv_NewStringUTF(const char * bytes)238 _Jv_NewStringUTF (const char *bytes)
239 {
240   int size = strlen (bytes);
241   unsigned char *p = (unsigned char *) bytes;
242 
243   int length = _Jv_strLengthUtf8 ((char *) p, size);
244   if (length < 0)
245     return NULL;
246 
247   jstring jstr = JvAllocString (length);
248   jchar *chrs = JvGetStringChars (jstr);
249 
250   p = (unsigned char *) bytes;
251   unsigned char *limit = p + size;
252   while (p < limit)
253     *chrs++ = UTF8_GET (p, limit);
254 
255   return jstr;
256 }
257 
258 jstring
_Jv_NewStringUtf8Const(Utf8Const * str)259 _Jv_NewStringUtf8Const (Utf8Const* str)
260 {
261   jchar *chrs;
262   jchar buffer[100];
263   jstring jstr;
264   unsigned char* data = (unsigned char*) str->data;
265   unsigned char* limit = data + str->length;
266   int length = _Jv_strLengthUtf8(str->data, str->length);
267 
268   if (length <= (int) (sizeof(buffer) / sizeof(jchar)))
269     {
270       jstr = NULL;
271       chrs = buffer;
272     }
273   else
274     {
275       jstr = JvAllocString(length);
276       chrs = JvGetStringChars(jstr);
277     }
278 
279   jint hash = 0;
280   while (data < limit)
281     {
282       jchar ch = UTF8_GET(data, limit);
283       hash = (31 * hash) + ch;
284       *chrs++ = ch;
285     }
286   chrs -= length;
287 
288   JvSynchronize sync (&StringClass);
289   if (3 * strhash_count >= 2 * strhash_size)
290     java::lang::String::rehash();
291   jstring* ptr = _Jv_StringFindSlot (chrs, length, hash);
292   if (*ptr != NULL && *ptr != DELETED_STRING)
293     return (jstring) UNMASK_PTR (*ptr);
294   strhash_count++;
295   if (jstr == NULL)
296     {
297       jstr = JvAllocString(length);
298       chrs = JvGetStringChars(jstr);
299       memcpy (chrs, buffer, sizeof(jchar)*length);
300     }
301   jstr->cachedHashCode = hash;
302   *ptr = jstr;
303   SET_STRING_IS_INTERNED(jstr);
304   // When string is GC'd, clear the slot in the hash table.  Note that
305   // we don't have to call _Jv_RegisterStringFinalizer here, as we
306   // know the new object cannot be referred to by a Reference.
307   _Jv_RegisterFinalizer ((void *) jstr, _Jv_FinalizeString);
308   return jstr;
309 }
310 
311 jsize
_Jv_GetStringUTFLength(jstring string)312 _Jv_GetStringUTFLength (jstring string)
313 {
314   jsize len = 0;
315   jchar *ptr = JvGetStringChars (string);
316   jsize i = string->length();
317   while (--i >= 0)
318     {
319       jchar ch = *ptr++;
320       if (ch > 0 && ch <= 0x7F)
321 	len += 1;
322       else if (ch <= 0x7FF)
323 	len += 2;
324       else
325 	len += 3;
326     }
327   return len;
328 }
329 
330 // Not sure this quite matches GetStringUTFRegion.
331 // null-termination of result?  len?  throw exception?
332 jsize
_Jv_GetStringUTFRegion(jstring str,jsize start,jsize len,char * buf)333 _Jv_GetStringUTFRegion (jstring str, jsize start, jsize len, char *buf)
334 {
335   jchar *sptr = JvGetStringChars (str) + start;
336   jsize i = len;
337   char *dptr = buf;
338   while (--i >= 0)
339     {
340       jchar ch = *sptr++;
341       if (ch > 0 && ch <= 0x7F)
342 	*dptr++ = (char) ch;
343       else if (ch <= 0x7FF)
344 	{
345 	  *dptr++ = (char) (0xC0 + ((ch >> 6) & 0x1F));
346 	  *dptr++ = (char) (0x80 + (ch & 0x3F));
347 	}
348       else
349 	{
350 	  *dptr++ = (char) (0xE0 + ((ch >> 12) & 0xF));
351 	  *dptr++ = (char) (0x80 + ((ch >> 6) & 0x3F));
352 	  *dptr++ = (char) (0x80 + (ch & 0x3F));
353 	}
354     }
355   return dptr - buf;
356 }
357 
358 /* Put printed (decimal) representation of NUM in a buffer.
359    BUFEND marks the end of the buffer, which must be at least 11 jchars long.
360    Returns the COUNT of jchars written.  The result is in
361    (BUFEND - COUNT) (inclusive) upto (BUFEND) (exclusive). */
362 
363 jint
_Jv_FormatInt(jchar * bufend,jint num)364 _Jv_FormatInt (jchar* bufend, jint num)
365 {
366   register jchar* ptr = bufend;
367   jboolean isNeg;
368   if (num < 0)
369     {
370       isNeg = true;
371       num = -(num);
372       if (num < 0)
373 	{
374 	  // Must be MIN_VALUE, so handle this special case.
375 	  // FIXME use 'unsigned jint' for num.
376 	  *--ptr = '8';
377 	  num = 214748364;
378 	}
379       }
380     else
381       isNeg = false;
382 
383     do
384       {
385         *--ptr = (jchar) ((int) '0' + (num % 10));
386         num /= 10;
387       }
388     while (num > 0);
389 
390     if (isNeg)
391       *--ptr = '-';
392     return bufend - ptr;
393 }
394 
395 jstring
valueOf(jint num)396 java::lang::String::valueOf (jint num)
397 {
398   // Use an array large enough for "-2147483648"; i.e. 11 chars.
399   jchar buffer[11];
400   int i = _Jv_FormatInt (buffer+11, num);
401   return _Jv_NewString (buffer+11-i, i);
402 }
403 
404 jstring
_Jv_AllocString(jsize len)405 _Jv_AllocString(jsize len)
406 {
407   jsize sz = sizeof(java::lang::String) + len * sizeof(jchar);
408 
409   // We assert that for strings allocated this way, the data field
410   // will always point to the object itself.  Thus there is no reason
411   // for the garbage collector to scan any of it.
412   // Furthermore, we're about to overwrite the string data, so
413   // initialization of the object is not an issue.
414 #ifdef ENABLE_JVMPI
415   jstring obj = (jstring) _Jv_AllocPtrFreeObject(&StringClass, sz);
416 #else
417   // Class needs no initialization, and there is no finalizer, so
418   // we can go directly to the collector's allocator interface.
419   jstring obj = (jstring) _Jv_AllocPtrFreeObj(sz, &StringClass);
420 #endif
421   obj->data = obj;
422   obj->boffset = sizeof(java::lang::String);
423   obj->count = len;
424   obj->cachedHashCode = 0;
425   return obj;
426 }
427 
428 jstring
_Jv_NewString(const jchar * chars,jsize len)429 _Jv_NewString(const jchar *chars, jsize len)
430 {
431   jstring str = _Jv_AllocString(len);
432   jchar* data = JvGetStringChars (str);
433   while (--len >= 0)
434     *data++ = *chars++;
435   return str;
436 }
437 
438 jstring
_Jv_NewStringLatin1(const char * bytes,jsize len)439 _Jv_NewStringLatin1(const char *bytes, jsize len)
440 {
441   jstring str = JvAllocString(len);
442   jchar* data = JvGetStringChars (str);
443   while (--len >= 0)
444     *data++ = *(unsigned char*)bytes++;
445   return str;
446 }
447 
448 void
init(jcharArray chars,jint offset,jint count,jboolean dont_copy)449 java::lang::String::init(jcharArray chars, jint offset, jint count,
450 			 jboolean dont_copy)
451 {
452   if (! chars)
453     throw new NullPointerException;
454   jsize data_size = JvGetArrayLength (chars);
455   if (offset < 0 || count < 0 || offset + count < 0
456       || offset + count > data_size)
457     throw new ArrayIndexOutOfBoundsException;
458   jcharArray array;
459   jchar *pdst;
460   if (! dont_copy)
461     {
462       array = JvNewCharArray(count);
463       pdst = elements (array);
464       memcpy (pdst, elements (chars) + offset, count * sizeof (jchar));
465     }
466   else
467     {
468       array = chars;
469       pdst = &(elements(array)[offset]);
470     }
471 
472   data = array;
473   boffset = (char *) pdst - (char *) array;
474   this->count = count;
475 }
476 
477 void
init(jbyteArray ascii,jint hibyte,jint offset,jint count)478 java::lang::String::init(jbyteArray ascii, jint hibyte, jint offset,
479 			 jint count)
480 {
481   if (! ascii)
482     throw new NullPointerException;
483   jsize data_size = JvGetArrayLength (ascii);
484   if (offset < 0 || count < 0 || offset + count < 0
485       || offset + count > data_size)
486     throw new ArrayIndexOutOfBoundsException;
487   jcharArray array = JvNewCharArray(count);
488   jbyte *psrc = elements (ascii) + offset;
489   jchar *pdst = elements (array);
490   data = array;
491   boffset = (char *) pdst - (char *) array;
492   this->count = count;
493   hibyte = (hibyte & 0xff) << 8;
494   while (-- count >= 0)
495     {
496       *pdst++ = hibyte | (*psrc++ & 0xff);
497     }
498 }
499 
500 void
init(jbyteArray bytes,jint offset,jint count,jstring encoding)501 java::lang::String::init (jbyteArray bytes, jint offset, jint count,
502 			  jstring encoding)
503 {
504   if (! bytes)
505     throw new NullPointerException;
506   jsize data_size = JvGetArrayLength (bytes);
507   if (offset < 0 || count < 0 || offset + count < 0
508       || offset + count > data_size)
509     throw new ArrayIndexOutOfBoundsException;
510   jcharArray array = JvNewCharArray (count);
511   gnu::gcj::convert::BytesToUnicode *converter
512     = gnu::gcj::convert::BytesToUnicode::getDecoder(encoding);
513   jint outpos = 0;
514   int avail = count;
515   converter->setInput(bytes, offset, offset+count);
516   while (converter->inpos < converter->inlength)
517     {
518       int done = converter->read(array, outpos, avail);
519       if (done == 0)
520 	{
521 	  jint new_size = 2 * (outpos + avail);
522 	  jcharArray new_array = JvNewCharArray (new_size);
523 	  memcpy (elements (new_array), elements (array),
524 		  outpos * sizeof(jchar));
525 	  array = new_array;
526 	  avail = new_size - outpos;
527 	}
528       else
529 	{
530 	  outpos += done;
531 	  avail -= done;
532 	}
533     }
534   converter->done ();
535   this->data = array;
536   this->boffset = (char *) elements (array) - (char *) array;
537   this->count = outpos;
538 }
539 
540 void
init(gnu::gcj::runtime::StringBuffer * buffer)541 java::lang::String::init (gnu::gcj::runtime::StringBuffer *buffer)
542 {
543   init (buffer->value, 0, buffer->count, true);
544 }
545 
546 jboolean
equals(jobject anObject)547 java::lang::String::equals(jobject anObject)
548 {
549   if (anObject == NULL)
550     return false;
551   if (anObject == this)
552     return true;
553   if (anObject->getClass() != &StringClass)
554     return false;
555   jstring other = (jstring) anObject;
556   if (count != other->count)
557     return false;
558   /* if both are interned, return false. */
559   jint i = count;
560   jchar *xptr = JvGetStringChars (this);
561   jchar *yptr = JvGetStringChars (other);
562   while (--i >= 0)
563     {
564       if (*xptr++ != *yptr++)
565 	return false;
566     }
567   return true;
568 }
569 
570 jboolean
contentEquals(java::lang::StringBuffer * buffer)571 java::lang::String::contentEquals(java::lang::StringBuffer* buffer)
572 {
573   if (buffer == NULL)
574     throw new NullPointerException;
575   JvSynchronize sync(buffer);
576   if (count != buffer->count)
577     return false;
578   if (data == buffer->value)
579     return true; // Possible if shared.
580   jint i = count;
581   jchar *xptr = JvGetStringChars(this);
582   jchar *yptr = elements(buffer->value);
583   while (--i >= 0)
584     if (*xptr++ != *yptr++)
585       return false;
586   return true;
587 }
588 
589 jchar
charAt(jint i)590 java::lang::String::charAt(jint i)
591 {
592   if (i < 0 || i >= count)
593     throw new java::lang::StringIndexOutOfBoundsException(i);
594   return JvGetStringChars(this)[i];
595 }
596 
597 void
getChars(jint srcBegin,jint srcEnd,jcharArray dst,jint dstBegin)598 java::lang::String::getChars(jint srcBegin, jint srcEnd,
599 			     jcharArray dst, jint dstBegin)
600 {
601   jint dst_length = JvGetArrayLength (dst);
602   if (srcBegin < 0 || srcBegin > srcEnd || srcEnd > count)
603     throw new java::lang::StringIndexOutOfBoundsException;
604   // The 2nd part of the test below is equivalent to
605   // dstBegin + (srcEnd-srcBegin) > dst_length
606   // except that it does not overflow.
607   if (dstBegin < 0 || dstBegin > dst_length - (srcEnd-srcBegin))
608     throw new ArrayIndexOutOfBoundsException;
609   jchar *dPtr = elements (dst) + dstBegin;
610   jchar *sPtr = JvGetStringChars (this) + srcBegin;
611   jint i = srcEnd-srcBegin;
612   while (--i >= 0)
613     *dPtr++ = *sPtr++;
614 }
615 
616 jbyteArray
getBytes(jstring enc)617 java::lang::String::getBytes (jstring enc)
618 {
619   jint todo = length();
620   jint buflen = todo;
621   jbyteArray buffer = JvNewByteArray(todo);
622   jint bufpos = 0;
623   jint offset = 0;
624   gnu::gcj::convert::UnicodeToBytes *converter
625     = gnu::gcj::convert::UnicodeToBytes::getEncoder(enc);
626   while (todo > 0 || converter->havePendingBytes())
627     {
628       converter->setOutput(buffer, bufpos);
629       int converted = converter->write(this, offset, todo, NULL);
630       bufpos = converter->count;
631       if (converted == 0 && bufpos == converter->count)
632 	{
633 	  buflen *= 2;
634 	  jbyteArray newbuffer = JvNewByteArray(buflen);
635 	  memcpy (elements (newbuffer), elements (buffer), bufpos);
636 	  buffer = newbuffer;
637 	}
638       else
639 	bufpos = converter->count;
640 
641       offset += converted;
642       todo -= converted;
643     }
644   converter->done ();
645   if (bufpos == buflen)
646     return buffer;
647   jbyteArray result = JvNewByteArray(bufpos);
648   memcpy (elements (result), elements (buffer), bufpos);
649   return result;
650 }
651 
652 void
getBytes(jint srcBegin,jint srcEnd,jbyteArray dst,jint dstBegin)653 java::lang::String::getBytes(jint srcBegin, jint srcEnd,
654 			     jbyteArray dst, jint dstBegin)
655 {
656   jint dst_length = JvGetArrayLength (dst);
657   if (srcBegin < 0 || srcBegin > srcEnd || srcEnd > count)
658     throw new java::lang::StringIndexOutOfBoundsException;
659   // The 2nd part of the test below is equivalent to
660   // dstBegin + (srcEnd-srcBegin) > dst_length
661   // except that it does not overflow.
662   if (dstBegin < 0 || dstBegin > dst_length - (srcEnd-srcBegin))
663     throw new ArrayIndexOutOfBoundsException;
664   jbyte *dPtr = elements (dst) + dstBegin;
665   jchar *sPtr = JvGetStringChars (this) + srcBegin;
666   jint i = srcEnd-srcBegin;
667   while (--i >= 0)
668     *dPtr++ = (jbyte) *sPtr++;
669 }
670 
671 jcharArray
toCharArray()672 java::lang::String::toCharArray()
673 {
674   jcharArray array = JvNewCharArray(count);
675   jchar *dPtr = elements (array);
676   jchar *sPtr = JvGetStringChars (this);
677   jint i = count;
678   while (--i >= 0)
679     *dPtr++ = *sPtr++;
680   return array;
681 }
682 
683 jboolean
equalsIgnoreCase(jstring anotherString)684 java::lang::String::equalsIgnoreCase (jstring anotherString)
685 {
686   if (anotherString == NULL || count != anotherString->count)
687     return false;
688   jchar *tptr = JvGetStringChars (this);
689   jchar *optr = JvGetStringChars (anotherString);
690   jint i = count;
691   while (--i >= 0)
692     {
693       jchar tch = *tptr++;
694       jchar och = *optr++;
695       if (tch != och
696 	  && (java::lang::Character::toLowerCase (tch)
697 	      != java::lang::Character::toLowerCase (och))
698 	  && (java::lang::Character::toUpperCase (tch)
699 	      != java::lang::Character::toUpperCase (och)))
700 	return false;
701     }
702   return true;
703 }
704 
705 jboolean
regionMatches(jint toffset,jstring other,jint ooffset,jint len)706 java::lang::String::regionMatches (jint toffset,
707 				   jstring other, jint ooffset, jint len)
708 {
709   if (toffset < 0 || ooffset < 0 || len < 0
710       || toffset > count - len
711       || ooffset > other->count - len)
712     return false;
713   jchar *tptr = JvGetStringChars (this) + toffset;
714   jchar *optr = JvGetStringChars (other) + ooffset;
715   jint i = len;
716   while (--i >= 0)
717     {
718       if (*tptr++ != *optr++)
719 	return false;
720     }
721   return true;
722 }
723 
724 jint
compareTo(jstring anotherString)725 java::lang::String::compareTo (jstring anotherString)
726 {
727   jchar *tptr = JvGetStringChars (this);
728   jchar *optr = JvGetStringChars (anotherString);
729   jint tlen = this->count;
730   jint olen = anotherString->count;
731   jint i = tlen > olen ? olen : tlen;
732   while (--i >= 0)
733     {
734       jchar tch = *tptr++;
735       jchar och = *optr++;
736       if (tch != och)
737 	return (jint) tch - (jint) och;
738     }
739   return tlen - olen;
740 }
741 
742 jboolean
regionMatches(jboolean ignoreCase,jint toffset,jstring other,jint ooffset,jint len)743 java::lang::String::regionMatches (jboolean ignoreCase, jint toffset,
744 				   jstring other, jint ooffset, jint len)
745 {
746   if (toffset < 0 || ooffset < 0 || len < 0
747       || toffset > count - len
748       || ooffset > other->count - len)
749     return false;
750   jchar *tptr = JvGetStringChars (this) + toffset;
751   jchar *optr = JvGetStringChars (other) + ooffset;
752   jint i = len;
753   if (ignoreCase)
754     while (--i >= 0)
755       {
756 	jchar tch = *tptr++;
757 	jchar och = *optr++;
758 	if ((java::lang::Character::toLowerCase (tch)
759 	     != java::lang::Character::toLowerCase (och))
760 	    && (java::lang::Character::toUpperCase (tch)
761 		!= java::lang::Character::toUpperCase (och)))
762 	  return false;
763       }
764   else
765     while (--i >= 0)
766       {
767 	jchar tch = *tptr++;
768 	jchar och = *optr++;
769 	if (tch != och)
770 	  return false;
771       }
772   return true;
773 }
774 
775 jboolean
startsWith(jstring prefix,jint toffset)776 java::lang::String::startsWith (jstring prefix, jint toffset)
777 {
778   jint i = prefix->count;
779   if (toffset < 0 || toffset > count - i)
780     return false;
781   jchar *xptr = JvGetStringChars (this) + toffset;
782   jchar *yptr = JvGetStringChars (prefix);
783   while (--i >= 0)
784     {
785       if (*xptr++ != *yptr++)
786 	return false;
787     }
788   return true;
789 }
790 
791 jint
indexOf(jint ch,jint fromIndex)792 java::lang::String::indexOf (jint ch, jint fromIndex)
793 {
794   if (fromIndex < 0)
795     fromIndex = 0;
796   jchar *ptr = JvGetStringChars(this);
797   for (;; ++fromIndex)
798     {
799       if (fromIndex >= count)
800 	return -1;
801       if (ptr[fromIndex] == ch)
802 	return fromIndex;
803     }
804 }
805 
806 jint
indexOf(jstring s,jint fromIndex)807 java::lang::String::indexOf (jstring s, jint fromIndex)
808 {
809   const jchar *const xchars = JvGetStringChars(s);
810   const jchar *const ychars = JvGetStringChars(this) + fromIndex;
811 
812   const int xlength = s->length ();
813   const int ylength = length () - fromIndex;
814 
815   int i = 0;
816   int j = 0;
817 
818   while (i < ylength && j < xlength)
819     {
820       if (xchars[j] != ychars[i])
821 	{
822 	  i = i - j + 1;
823 	  j = 0;
824 	}
825       else
826 	i++, j++;
827     }
828 
829   if (j >= xlength)
830     return fromIndex + i - xlength;
831   else
832     return -1;
833 }
834 
835 jint
lastIndexOf(jint ch,jint fromIndex)836 java::lang::String::lastIndexOf (jint ch, jint fromIndex)
837 {
838   if (fromIndex >= count)
839     fromIndex = count - 1;
840   jchar *ptr = JvGetStringChars(this);
841   for (;; --fromIndex)
842     {
843       if (fromIndex < 0)
844 	return -1;
845       if (ptr[fromIndex] == ch)
846 	return fromIndex;
847     }
848 }
849 
850 jstring
substring(jint beginIndex,jint endIndex)851 java::lang::String::substring (jint beginIndex, jint endIndex)
852 {
853   if (beginIndex < 0 || endIndex > count || beginIndex > endIndex)
854     throw new StringIndexOutOfBoundsException;
855   if (beginIndex == 0 && endIndex == count)
856     return this;
857   jint newCount = endIndex - beginIndex;
858   if (newCount <= 8)  // Optimization, mainly for GC.
859     return JvNewString(JvGetStringChars(this) + beginIndex, newCount);
860   jstring s = new String();
861   s->data = data;
862   s->count = newCount;
863   s->boffset = boffset + sizeof(jchar) * beginIndex;
864   return s;
865 }
866 
867 jstring
concat(jstring str)868 java::lang::String::concat(jstring str)
869 {
870   jint str_count = str->count;
871   if (str_count == 0)
872     return this;
873   jstring result = JvAllocString(count + str_count);
874   jchar *dstPtr = JvGetStringChars(result);
875   jchar *srcPtr = JvGetStringChars(this);
876   jint i = count;
877   while (--i >= 0)
878     *dstPtr++ = *srcPtr++;
879   srcPtr = JvGetStringChars(str);
880   i = str->count;
881   while (--i >= 0)
882     *dstPtr++ = *srcPtr++;
883   return result;
884 }
885 
886 jstring
replace(jchar oldChar,jchar newChar)887 java::lang::String::replace (jchar oldChar, jchar newChar)
888 {
889   jint i;
890   jchar* chrs = JvGetStringChars (this);
891   for (i = 0;  ;  i++)
892     {
893       if (i == count)
894 	return this;
895       if (chrs[i] == oldChar)
896 	break;
897     }
898   jstring result = JvAllocString (count);
899   jchar *dPtr = JvGetStringChars (result);
900   for (int j = 0;  j < i;  j++)
901     *dPtr++ = chrs[j];
902   for (; i < count;  i++)
903     {
904       jchar ch = chrs[i];
905       if (ch == oldChar)
906 	ch = newChar;
907       *dPtr++ = ch;
908     }
909   return result;
910 }
911 
912 jstring
toLowerCase(java::util::Locale * locale)913 java::lang::String::toLowerCase (java::util::Locale *locale)
914 {
915   jint i;
916   jchar* chrs = JvGetStringChars(this);
917   jchar ch = 0;
918 
919   bool handle_tr = false;
920   if (locale != NULL)
921     {
922       String *lang = locale->getLanguage ();
923       if (lang->length () == 2
924 	  && lang->charAt (0) == 't'
925 	  && lang->charAt (1) == 'r')
926 	handle_tr = true;
927     }
928 
929   for (i = 0;  ;  i++)
930     {
931       if (i == count)
932 	return this;
933       jchar origChar = chrs[i];
934 
935       if (handle_tr && (origChar == CAPITAL_I
936 			|| origChar == CAPITAL_I_WITH_DOT))
937 	break;
938 
939       ch = java::lang::Character::toLowerCase(origChar);
940       if (ch != origChar)
941 	break;
942     }
943   jstring result = JvAllocString(count);
944   jchar *dPtr = JvGetStringChars (result);
945   for (int j = 0;  j < i;  j++)
946     *dPtr++ = chrs[j];
947   *dPtr++ = ch;  i++;
948   for (; i < count;  i++)
949     {
950       if (handle_tr && chrs[i] == CAPITAL_I)
951 	*dPtr++ = SMALL_DOTLESS_I;
952       else if (handle_tr && chrs[i] == CAPITAL_I_WITH_DOT)
953 	*dPtr++ = SMALL_I;
954       else
955 	*dPtr++ = java::lang::Character::toLowerCase(chrs[i]);
956     }
957   return result;
958 }
959 
960 jstring
toUpperCase(java::util::Locale * locale)961 java::lang::String::toUpperCase (java::util::Locale *locale)
962 {
963   jint i;
964   jchar* chrs = JvGetStringChars(this);
965   jchar ch;
966 
967   // When handling a specific locale there might be special rules.
968   // Currently all existing rules are simply handled inline, as there
969   // are only two and they are documented in the online 1.2 docs.
970   bool handle_esset = locale != NULL;
971   bool handle_tr = false;
972   if (locale != NULL)
973     {
974       String *lang = locale->getLanguage ();
975       if (lang->length () == 2
976 	  && lang->charAt (0) == 't'
977 	  && lang->charAt (1) == 'r')
978 	handle_tr = true;
979     }
980 
981   int new_count = count;
982   bool new_string = false;
983   for (i = 0;  ;  i++)
984     {
985       if (i == count)
986 	break;
987       jchar origChar = chrs[i];
988 
989       if (handle_esset && origChar == ESSET)
990 	{
991 	  ++new_count;
992 	  new_string = true;
993 	}
994       else if (handle_tr && (origChar == SMALL_I
995 			     || origChar == SMALL_DOTLESS_I))
996 	new_string = true;
997       else
998 	{
999 	  ch = java::lang::Character::toUpperCase(origChar);
1000 	  if (ch != origChar)
1001 	    new_string = true;
1002 	}
1003 
1004       if (new_string && ! handle_esset)
1005 	break;
1006     }
1007   if (! new_string)
1008     return this;
1009   jstring result = JvAllocString(new_count);
1010   jchar *dPtr = JvGetStringChars (result);
1011   for (i = 0; i < count;  i++)
1012     {
1013       if (handle_esset && chrs[i] == ESSET)
1014 	{
1015 	  *dPtr++ = CAPITAL_S;
1016 	  *dPtr++ = CAPITAL_S;
1017 	}
1018       else if (handle_tr && chrs[i] == SMALL_I)
1019 	*dPtr++ = CAPITAL_I_WITH_DOT;
1020       else if (handle_tr && chrs[i] == SMALL_DOTLESS_I)
1021 	*dPtr++ = CAPITAL_I;
1022       else
1023 	*dPtr++ = java::lang::Character::toUpperCase(chrs[i]);
1024     }
1025   return result;
1026 }
1027 
1028 jstring
trim()1029 java::lang::String::trim ()
1030 {
1031   jchar* chrs = JvGetStringChars(this);
1032   if (count == 0 || (chrs[0] > ' ' && chrs[count-1] > ' '))
1033     return this;
1034   jint preTrim = 0;
1035   for (;; preTrim++)
1036     {
1037       if (preTrim == count)
1038 	return new String();
1039       if (chrs[preTrim] > ' ')
1040 	break;
1041     }
1042   jint endTrim = count;
1043   while (chrs[endTrim-1] <= ' ')
1044     endTrim--;
1045   return substring(preTrim, endTrim);
1046 }
1047 
1048 jstring
valueOf(jcharArray data,jint offset,jint count)1049 java::lang::String::valueOf(jcharArray data, jint offset, jint count)
1050 {
1051   jint data_length = JvGetArrayLength (data);
1052   if (offset < 0 || count < 0 || offset > data_length - count)
1053     throw new ArrayIndexOutOfBoundsException;
1054   jstring result = JvAllocString(count);
1055   jchar *sPtr = elements (data) + offset;
1056   jchar *dPtr = JvGetStringChars(result);
1057   while (--count >= 0)
1058     *dPtr++ = *sPtr++;
1059   return result;
1060 }
1061 
1062 jstring
valueOf(jchar c)1063 java::lang::String::valueOf(jchar c)
1064 {
1065   jstring result = JvAllocString(1);
1066   JvGetStringChars (result)[0] = c;
1067   return result;
1068 }
1069