OSDN Git Service

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