svn merge -r108665:108708 svn+ssh://gcc.gnu.org/svn/gcc/trunk
[official-gcc.git] / libjava / java / lang / natString.cc
blobc8f3129a2129a77d2dbc44a7b4948af905ddbed1
1 // natString.cc - Implementation of java.lang.String native methods.
3 /* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation
5 This file is part of libgcj.
7 This software is copyrighted work licensed under the terms of the
8 Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
9 details. */
11 #include <config.h>
13 #include <string.h>
14 #include <stdlib.h>
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>
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! */
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
47 #define DELETED_STRING ((jstring)(~0))
48 #define SET_STRING_IS_INTERNED(STR) /* nothing */
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)
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*
57 _Jv_StringFindSlot (jchar* data, jint len, jint hash)
59 JvSynchronize sync (&java::lang::String::class$);
61 int start_index = hash & (strhash_size - 1);
62 int deleted_index = -1;
64 int index = start_index;
65 /* step must be non-zero, and relatively prime with strhash_size. */
66 jint step = (hash ^ (hash >> 16)) | 1;
69 jstring* ptr = &strhash[index];
70 jstring value = (jstring) UNMASK_PTR (*ptr);
71 if (value == NULL)
73 if (deleted_index >= 0)
74 return (&strhash[deleted_index]);
75 else
76 return ptr;
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);
85 while (index != start_index);
86 // Note that we can have INDEX == START_INDEX if the table has no
87 // NULL entries but does have DELETED_STRING entries.
88 JvAssert (deleted_index >= 0);
89 return &strhash[deleted_index];
92 /* Calculate a hash code for the string starting at PTR at given LENGTH.
93 This uses the same formula as specified for java.lang.String.hash. */
95 static jint
96 hashChars (jchar* ptr, jint length)
98 jchar* limit = ptr + length;
99 jint hash = 0;
100 // Updated specification from
101 // http://www.javasoft.com/docs/books/jls/clarify.html.
102 while (ptr < limit)
103 hash = (31 * hash) + *ptr++;
104 return hash;
107 jint
108 java::lang::String::hashCode()
110 if (cachedHashCode == 0)
111 cachedHashCode = hashChars(JvGetStringChars(this), length());
112 return cachedHashCode;
115 jstring*
116 _Jv_StringGetSlot (jstring str)
118 jchar* data = JvGetStringChars(str);
119 int length = str->length();
120 return _Jv_StringFindSlot(data, length, hashChars (data, length));
123 static void
124 rehash ()
126 JvSynchronize sync (&java::lang::String::class$);
128 if (strhash == NULL)
130 strhash_size = 1024;
131 strhash = (jstring *) _Jv_AllocBytes (strhash_size * sizeof (jstring));
133 else
135 int i = strhash_size;
136 jstring* ptr = strhash + i;
137 int nsize = strhash_size * 2;
138 jstring *next = (jstring *) _Jv_AllocBytes (nsize * sizeof (jstring));
140 while (--i >= 0)
142 --ptr;
143 if (*ptr == NULL || *ptr == DELETED_STRING)
144 continue;
146 /* This is faster equivalent of
147 * *__JvGetInternSlot(*ptr) = *ptr; */
148 jstring val = (jstring) UNMASK_PTR (*ptr);
149 jint hash = val->hashCode();
150 jint index = hash & (nsize - 1);
151 jint step = (hash ^ (hash >> 16)) | 1;
152 for (;;)
154 if (next[index] == NULL)
156 next[index] = *ptr;
157 break;
159 index = (index + step) & (nsize - 1);
163 strhash_size = nsize;
164 strhash = next;
168 jstring
169 java::lang::String::intern()
171 JvSynchronize sync (&java::lang::String::class$);
172 if (3 * strhash_count >= 2 * strhash_size)
173 rehash();
174 jstring* ptr = _Jv_StringGetSlot(this);
175 if (*ptr != NULL && *ptr != DELETED_STRING)
177 // See description in _Jv_FinalizeString() to understand this.
178 *ptr = (jstring) MASK_PTR (*ptr);
179 return (jstring) UNMASK_PTR (*ptr);
181 jstring str = (this->data == this
182 ? this
183 : _Jv_NewString(JvGetStringChars(this), this->length()));
184 SET_STRING_IS_INTERNED(str);
185 strhash_count++;
186 *ptr = str;
187 // When string is GC'd, clear the slot in the hash table.
188 _Jv_RegisterStringFinalizer (str);
189 return str;
192 // The fake String finalizer. This is only used when the String has
193 // been intern()d. However, we must check this case, as it might be
194 // called by the Reference code for any String.
195 void
196 _Jv_FinalizeString (jobject obj)
198 JvSynchronize sync (&java::lang::String::class$);
200 // We might not actually have intern()d any strings at all, if
201 // we're being called from Reference.
202 if (! strhash)
203 return;
205 jstring str = reinterpret_cast<jstring> (obj);
206 jstring *ptr = _Jv_StringGetSlot(str);
207 if (*ptr == NULL || *ptr == DELETED_STRING
208 || (jobject) UNMASK_PTR (*ptr) != obj)
209 return;
211 // We assume the lowest bit of the pointer is free for our nefarious
212 // manipulations. What we do is set it to `0' (implicitly) when
213 // interning the String. If we subsequently re-intern the same
214 // String, then we set the bit. When finalizing, if the bit is set
215 // then we clear it and re-register the finalizer. We know this is
216 // a safe approach because both intern() and _Jv_FinalizeString()
217 // acquire the class lock; this bit can't be manipulated when the
218 // lock is not held. So if we are finalizing and the bit is clear
219 // then we know all references are gone and we can clear the entry
220 // in the hash table. The naive approach of simply clearing the
221 // pointer here fails in the case where a request to intern a new
222 // string with the same contents is made between the time the
223 // intern()d string is found to be unreachable and when the
224 // finalizer is actually run. In this case we could clear a pointer
225 // to a valid string, and future intern() calls for that particular
226 // value would spuriously fail.
227 if (PTR_MASKED (*ptr))
229 *ptr = (jstring) UNMASK_PTR (*ptr);
230 _Jv_RegisterStringFinalizer (obj);
232 else
234 *ptr = DELETED_STRING;
235 strhash_count--;
239 jstring
240 _Jv_NewStringUTF (const char *bytes)
242 int size = strlen (bytes);
243 unsigned char *p = (unsigned char *) bytes;
245 int length = _Jv_strLengthUtf8 ((char *) p, size);
246 if (length < 0)
247 return NULL;
249 jstring jstr = JvAllocString (length);
250 jchar *chrs = JvGetStringChars (jstr);
252 p = (unsigned char *) bytes;
253 unsigned char *limit = p + size;
254 while (p < limit)
255 *chrs++ = UTF8_GET (p, limit);
257 return jstr;
260 jstring
261 _Jv_NewStringUtf8Const (Utf8Const* str)
263 jchar *chrs;
264 jchar buffer[100];
265 jstring jstr;
266 unsigned char* data = (unsigned char*) str->data;
267 unsigned char* limit = data + str->length;
268 int length = _Jv_strLengthUtf8(str->data, str->length);
270 if (length <= (int) (sizeof(buffer) / sizeof(jchar)))
272 jstr = NULL;
273 chrs = buffer;
275 else
277 jstr = JvAllocString(length);
278 chrs = JvGetStringChars(jstr);
281 jint hash = 0;
282 while (data < limit)
284 jchar ch = UTF8_GET(data, limit);
285 hash = (31 * hash) + ch;
286 *chrs++ = ch;
288 chrs -= length;
290 JvSynchronize sync (&java::lang::String::class$);
291 if (3 * strhash_count >= 2 * strhash_size)
292 rehash();
293 jstring* ptr = _Jv_StringFindSlot (chrs, length, hash);
294 if (*ptr != NULL && *ptr != DELETED_STRING)
295 return (jstring) UNMASK_PTR (*ptr);
296 strhash_count++;
297 if (jstr == NULL)
299 jstr = JvAllocString(length);
300 chrs = JvGetStringChars(jstr);
301 memcpy (chrs, buffer, sizeof(jchar)*length);
303 jstr->cachedHashCode = hash;
304 *ptr = jstr;
305 SET_STRING_IS_INTERNED(jstr);
306 // When string is GC'd, clear the slot in the hash table. Note that
307 // we don't have to call _Jv_RegisterStringFinalizer here, as we
308 // know the new object cannot be referred to by a Reference.
309 _Jv_RegisterFinalizer ((void *) jstr, _Jv_FinalizeString);
310 return jstr;
313 jsize
314 _Jv_GetStringUTFLength (jstring string)
316 jsize len = 0;
317 jchar *ptr = JvGetStringChars (string);
318 jsize i = string->length();
319 while (--i >= 0)
321 jchar ch = *ptr++;
322 if (ch > 0 && ch <= 0x7F)
323 len += 1;
324 else if (ch <= 0x7FF)
325 len += 2;
326 else
327 len += 3;
329 return len;
332 // Not sure this quite matches GetStringUTFRegion.
333 // null-termination of result? len? throw exception?
334 jsize
335 _Jv_GetStringUTFRegion (jstring str, jsize start, jsize len, char *buf)
337 jchar *sptr = JvGetStringChars (str) + start;
338 jsize i = len;
339 char *dptr = buf;
340 while (--i >= 0)
342 jchar ch = *sptr++;
343 if (ch > 0 && ch <= 0x7F)
344 *dptr++ = (char) ch;
345 else if (ch <= 0x7FF)
347 *dptr++ = (char) (0xC0 + ((ch >> 6) & 0x1F));
348 *dptr++ = (char) (0x80 + (ch & 0x3F));
350 else
352 *dptr++ = (char) (0xE0 + ((ch >> 12) & 0xF));
353 *dptr++ = (char) (0x80 + ((ch >> 6) & 0x3F));
354 *dptr++ = (char) (0x80 + (ch & 0x3F));
357 return dptr - buf;
360 /* Put printed (decimal) representation of NUM in a buffer.
361 BUFEND marks the end of the buffer, which must be at least 11 jchars long.
362 Returns the COUNT of jchars written. The result is in
363 (BUFEND - COUNT) (inclusive) upto (BUFEND) (exclusive). */
365 jint
366 _Jv_FormatInt (jchar* bufend, jint num)
368 register jchar* ptr = bufend;
369 jboolean isNeg;
370 if (num < 0)
372 isNeg = true;
373 num = -(num);
374 if (num < 0)
376 // Must be MIN_VALUE, so handle this special case.
377 // FIXME use 'unsigned jint' for num.
378 *--ptr = '8';
379 num = 214748364;
382 else
383 isNeg = false;
387 *--ptr = (jchar) ((int) '0' + (num % 10));
388 num /= 10;
390 while (num > 0);
392 if (isNeg)
393 *--ptr = '-';
394 return bufend - ptr;
397 jstring
398 java::lang::String::valueOf (jint num)
400 // Use an array large enough for "-2147483648"; i.e. 11 chars.
401 jchar buffer[11];
402 int i = _Jv_FormatInt (buffer+11, num);
403 return _Jv_NewString (buffer+11-i, i);
406 jstring
407 _Jv_NewString(const jchar *chars, jsize len)
409 jstring str = _Jv_AllocString(len);
410 jchar* data = JvGetStringChars (str);
411 while (--len >= 0)
412 *data++ = *chars++;
413 return str;
416 jstring
417 _Jv_NewStringLatin1(const char *bytes, jsize len)
419 jstring str = JvAllocString(len);
420 jchar* data = JvGetStringChars (str);
421 while (--len >= 0)
422 *data++ = *(unsigned char*)bytes++;
423 return str;
426 void
427 java::lang::String::init(jcharArray chars, jint offset, jint count,
428 jboolean dont_copy)
430 if (! chars)
431 throw new NullPointerException;
432 jsize data_size = JvGetArrayLength (chars);
433 if (offset < 0 || count < 0 || offset + count < 0
434 || offset + count > data_size)
435 throw new ArrayIndexOutOfBoundsException;
436 jcharArray array;
437 jchar *pdst;
438 if (! dont_copy)
440 array = JvNewCharArray(count);
441 pdst = elements (array);
442 memcpy (pdst, elements (chars) + offset, count * sizeof (jchar));
444 else
446 array = chars;
447 pdst = &(elements(array)[offset]);
450 data = array;
451 boffset = (char *) pdst - (char *) array;
452 this->count = count;
455 void
456 java::lang::String::init(jbyteArray ascii, jint hibyte, jint offset,
457 jint count)
459 if (! ascii)
460 throw new NullPointerException;
461 jsize data_size = JvGetArrayLength (ascii);
462 if (offset < 0 || count < 0 || offset + count < 0
463 || offset + count > data_size)
464 throw new ArrayIndexOutOfBoundsException;
465 jcharArray array = JvNewCharArray(count);
466 jbyte *psrc = elements (ascii) + offset;
467 jchar *pdst = elements (array);
468 data = array;
469 boffset = (char *) pdst - (char *) array;
470 this->count = count;
471 hibyte = (hibyte & 0xff) << 8;
472 while (-- count >= 0)
474 *pdst++ = hibyte | (*psrc++ & 0xff);
478 void
479 java::lang::String::init (jbyteArray bytes, jint offset, jint count,
480 jstring encoding)
482 if (! bytes)
483 throw new NullPointerException;
484 jsize data_size = JvGetArrayLength (bytes);
485 if (offset < 0 || count < 0 || offset + count < 0
486 || offset + count > data_size)
487 throw new ArrayIndexOutOfBoundsException;
488 jcharArray array = JvNewCharArray (count);
489 gnu::gcj::convert::BytesToUnicode *converter
490 = gnu::gcj::convert::BytesToUnicode::getDecoder(encoding);
491 jint outpos = 0;
492 int avail = count;
493 converter->setInput(bytes, offset, offset+count);
494 while (converter->inpos < converter->inlength)
496 int done = converter->read(array, outpos, avail);
497 if (done == 0)
499 jint new_size = 2 * (outpos + avail);
500 jcharArray new_array = JvNewCharArray (new_size);
501 memcpy (elements (new_array), elements (array),
502 outpos * sizeof(jchar));
503 array = new_array;
504 avail = new_size - outpos;
506 else
508 outpos += done;
509 avail -= done;
512 converter->done ();
513 this->data = array;
514 this->boffset = (char *) elements (array) - (char *) array;
515 this->count = outpos;
518 void
519 java::lang::String::init (gnu::gcj::runtime::StringBuffer *buffer)
521 init (buffer->value, 0, buffer->count, true);
524 jboolean
525 java::lang::String::equals(jobject anObject)
527 if (anObject == NULL)
528 return false;
529 if (anObject == this)
530 return true;
531 if (anObject->getClass() != &java::lang::String::class$)
532 return false;
533 jstring other = (jstring) anObject;
534 if (count != other->count)
535 return false;
536 /* if both are interned, return false. */
537 jint i = count;
538 jchar *xptr = JvGetStringChars (this);
539 jchar *yptr = JvGetStringChars (other);
540 while (--i >= 0)
542 if (*xptr++ != *yptr++)
543 return false;
545 return true;
548 jboolean
549 java::lang::String::contentEquals(java::lang::StringBuffer* buffer)
551 if (buffer == NULL)
552 throw new NullPointerException;
553 JvSynchronize sync(buffer);
554 if (count != buffer->count)
555 return false;
556 if (data == buffer->value)
557 return true; // Possible if shared.
558 jint i = count;
559 jchar *xptr = JvGetStringChars(this);
560 jchar *yptr = elements(buffer->value);
561 while (--i >= 0)
562 if (*xptr++ != *yptr++)
563 return false;
564 return true;
567 jchar
568 java::lang::String::charAt(jint i)
570 if (i < 0 || i >= count)
571 throw new java::lang::StringIndexOutOfBoundsException(i);
572 return JvGetStringChars(this)[i];
575 void
576 java::lang::String::getChars(jint srcBegin, jint srcEnd,
577 jcharArray dst, jint dstBegin)
579 jint dst_length = JvGetArrayLength (dst);
580 if (srcBegin < 0 || srcBegin > srcEnd || srcEnd > count)
581 throw new java::lang::StringIndexOutOfBoundsException;
582 // The 2nd part of the test below is equivalent to
583 // dstBegin + (srcEnd-srcBegin) > dst_length
584 // except that it does not overflow.
585 if (dstBegin < 0 || dstBegin > dst_length - (srcEnd-srcBegin))
586 throw new ArrayIndexOutOfBoundsException;
587 jchar *dPtr = elements (dst) + dstBegin;
588 jchar *sPtr = JvGetStringChars (this) + srcBegin;
589 jint i = srcEnd-srcBegin;
590 while (--i >= 0)
591 *dPtr++ = *sPtr++;
594 jbyteArray
595 java::lang::String::getBytes (jstring enc)
597 jint todo = length();
598 jint buflen = todo;
599 jbyteArray buffer = JvNewByteArray(todo);
600 jint bufpos = 0;
601 jint offset = 0;
602 gnu::gcj::convert::UnicodeToBytes *converter
603 = gnu::gcj::convert::UnicodeToBytes::getEncoder(enc);
604 while (todo > 0 || converter->havePendingBytes())
606 converter->setOutput(buffer, bufpos);
607 int converted = converter->write(this, offset, todo, NULL);
608 bufpos = converter->count;
609 if (converted == 0 && bufpos == converter->count)
611 buflen *= 2;
612 jbyteArray newbuffer = JvNewByteArray(buflen);
613 memcpy (elements (newbuffer), elements (buffer), bufpos);
614 buffer = newbuffer;
616 else
617 bufpos = converter->count;
619 offset += converted;
620 todo -= converted;
622 converter->done ();
623 if (bufpos == buflen)
624 return buffer;
625 jbyteArray result = JvNewByteArray(bufpos);
626 memcpy (elements (result), elements (buffer), bufpos);
627 return result;
630 void
631 java::lang::String::getBytes(jint srcBegin, jint srcEnd,
632 jbyteArray dst, jint dstBegin)
634 jint dst_length = JvGetArrayLength (dst);
635 if (srcBegin < 0 || srcBegin > srcEnd || srcEnd > count)
636 throw new java::lang::StringIndexOutOfBoundsException;
637 // The 2nd part of the test below is equivalent to
638 // dstBegin + (srcEnd-srcBegin) > dst_length
639 // except that it does not overflow.
640 if (dstBegin < 0 || dstBegin > dst_length - (srcEnd-srcBegin))
641 throw new ArrayIndexOutOfBoundsException;
642 jbyte *dPtr = elements (dst) + dstBegin;
643 jchar *sPtr = JvGetStringChars (this) + srcBegin;
644 jint i = srcEnd-srcBegin;
645 while (--i >= 0)
646 *dPtr++ = (jbyte) *sPtr++;
649 jcharArray
650 java::lang::String::toCharArray()
652 jcharArray array = JvNewCharArray(count);
653 jchar *dPtr = elements (array);
654 jchar *sPtr = JvGetStringChars (this);
655 jint i = count;
656 while (--i >= 0)
657 *dPtr++ = *sPtr++;
658 return array;
661 jboolean
662 java::lang::String::equalsIgnoreCase (jstring anotherString)
664 if (anotherString == NULL || count != anotherString->count)
665 return false;
666 jchar *tptr = JvGetStringChars (this);
667 jchar *optr = JvGetStringChars (anotherString);
668 jint i = count;
669 while (--i >= 0)
671 jchar tch = *tptr++;
672 jchar och = *optr++;
673 if (tch != och
674 && (java::lang::Character::toLowerCase (tch)
675 != java::lang::Character::toLowerCase (och))
676 && (java::lang::Character::toUpperCase (tch)
677 != java::lang::Character::toUpperCase (och)))
678 return false;
680 return true;
683 jboolean
684 java::lang::String::regionMatches (jint toffset,
685 jstring other, jint ooffset, jint len)
687 if (toffset < 0 || ooffset < 0 || len < 0
688 || toffset > count - len
689 || ooffset > other->count - len)
690 return false;
691 jchar *tptr = JvGetStringChars (this) + toffset;
692 jchar *optr = JvGetStringChars (other) + ooffset;
693 jint i = len;
694 while (--i >= 0)
696 if (*tptr++ != *optr++)
697 return false;
699 return true;
702 jint
703 java::lang::String::compareTo (jstring anotherString)
705 jchar *tptr = JvGetStringChars (this);
706 jchar *optr = JvGetStringChars (anotherString);
707 jint tlen = this->count;
708 jint olen = anotherString->count;
709 jint i = tlen > olen ? olen : tlen;
710 while (--i >= 0)
712 jchar tch = *tptr++;
713 jchar och = *optr++;
714 if (tch != och)
715 return (jint) tch - (jint) och;
717 return tlen - olen;
720 jboolean
721 java::lang::String::regionMatches (jboolean ignoreCase, jint toffset,
722 jstring other, jint ooffset, jint len)
724 if (toffset < 0 || ooffset < 0 || len < 0
725 || toffset > count - len
726 || ooffset > other->count - len)
727 return false;
728 jchar *tptr = JvGetStringChars (this) + toffset;
729 jchar *optr = JvGetStringChars (other) + ooffset;
730 jint i = len;
731 if (ignoreCase)
732 while (--i >= 0)
734 jchar tch = *tptr++;
735 jchar och = *optr++;
736 if ((java::lang::Character::toLowerCase (tch)
737 != java::lang::Character::toLowerCase (och))
738 && (java::lang::Character::toUpperCase (tch)
739 != java::lang::Character::toUpperCase (och)))
740 return false;
742 else
743 while (--i >= 0)
745 jchar tch = *tptr++;
746 jchar och = *optr++;
747 if (tch != och)
748 return false;
750 return true;
753 jboolean
754 java::lang::String::startsWith (jstring prefix, jint toffset)
756 jint i = prefix->count;
757 if (toffset < 0 || toffset > count - i)
758 return false;
759 jchar *xptr = JvGetStringChars (this) + toffset;
760 jchar *yptr = JvGetStringChars (prefix);
761 while (--i >= 0)
763 if (*xptr++ != *yptr++)
764 return false;
766 return true;
769 jint
770 java::lang::String::indexOf (jint ch, jint fromIndex)
772 if (fromIndex < 0)
773 fromIndex = 0;
774 jchar *ptr = JvGetStringChars(this);
775 for (;; ++fromIndex)
777 if (fromIndex >= count)
778 return -1;
779 if (ptr[fromIndex] == ch)
780 return fromIndex;
784 jint
785 java::lang::String::indexOf (jstring s, jint fromIndex)
787 const jchar *const xchars = JvGetStringChars(s);
788 const jchar *const ychars = JvGetStringChars(this) + fromIndex;
790 const int xlength = s->length ();
791 const int ylength = length () - fromIndex;
793 int i = 0;
794 int j = 0;
796 while (i < ylength && j < xlength)
798 if (xchars[j] != ychars[i])
800 i = i - j + 1;
801 j = 0;
803 else
804 i++, j++;
807 if (j >= xlength)
808 return fromIndex + i - xlength;
809 else
810 return -1;
813 jint
814 java::lang::String::lastIndexOf (jint ch, jint fromIndex)
816 if (fromIndex >= count)
817 fromIndex = count - 1;
818 jchar *ptr = JvGetStringChars(this);
819 for (;; --fromIndex)
821 if (fromIndex < 0)
822 return -1;
823 if (ptr[fromIndex] == ch)
824 return fromIndex;
828 jstring
829 java::lang::String::substring (jint beginIndex, jint endIndex)
831 if (beginIndex < 0 || endIndex > count || beginIndex > endIndex)
832 throw new StringIndexOutOfBoundsException;
833 if (beginIndex == 0 && endIndex == count)
834 return this;
835 jint newCount = endIndex - beginIndex;
836 // For very small strings, just allocate a new one. For other
837 // substrings, allocate a new one unless the substring is over half
838 // of the original string.
839 if (newCount <= 8 || newCount < (count >> 1))
840 return JvNewString(JvGetStringChars(this) + beginIndex, newCount);
841 jstring s = new String();
842 s->data = data;
843 s->count = newCount;
844 s->boffset = boffset + sizeof(jchar) * beginIndex;
845 return s;
848 jstring
849 java::lang::String::concat(jstring str)
851 jint str_count = str->count;
852 if (str_count == 0)
853 return this;
854 jstring result = JvAllocString(count + str_count);
855 jchar *dstPtr = JvGetStringChars(result);
856 jchar *srcPtr = JvGetStringChars(this);
857 jint i = count;
858 while (--i >= 0)
859 *dstPtr++ = *srcPtr++;
860 srcPtr = JvGetStringChars(str);
861 i = str->count;
862 while (--i >= 0)
863 *dstPtr++ = *srcPtr++;
864 return result;
867 jstring
868 java::lang::String::replace (jchar oldChar, jchar newChar)
870 jint i;
871 jchar* chrs = JvGetStringChars (this);
872 for (i = 0; ; i++)
874 if (i == count)
875 return this;
876 if (chrs[i] == oldChar)
877 break;
879 jstring result = JvAllocString (count);
880 jchar *dPtr = JvGetStringChars (result);
881 for (int j = 0; j < i; j++)
882 *dPtr++ = chrs[j];
883 for (; i < count; i++)
885 jchar ch = chrs[i];
886 if (ch == oldChar)
887 ch = newChar;
888 *dPtr++ = ch;
890 return result;
893 jstring
894 java::lang::String::toLowerCase (java::util::Locale *locale)
896 jint i;
897 jchar* chrs = JvGetStringChars(this);
898 jchar ch = 0;
900 bool handle_tr = false;
901 if (locale != NULL)
903 String *lang = locale->getLanguage ();
904 if (lang->length () == 2
905 && lang->charAt (0) == 't'
906 && lang->charAt (1) == 'r')
907 handle_tr = true;
910 for (i = 0; ; i++)
912 if (i == count)
913 return this;
914 jchar origChar = chrs[i];
916 if (handle_tr && (origChar == CAPITAL_I
917 || origChar == CAPITAL_I_WITH_DOT))
918 break;
920 ch = java::lang::Character::toLowerCase(origChar);
921 if (ch != origChar)
922 break;
924 jstring result = JvAllocString(count);
925 jchar *dPtr = JvGetStringChars (result);
926 for (int j = 0; j < i; j++)
927 *dPtr++ = chrs[j];
928 *dPtr++ = ch; i++;
929 for (; i < count; i++)
931 if (handle_tr && chrs[i] == CAPITAL_I)
932 *dPtr++ = SMALL_DOTLESS_I;
933 else if (handle_tr && chrs[i] == CAPITAL_I_WITH_DOT)
934 *dPtr++ = SMALL_I;
935 else
936 *dPtr++ = java::lang::Character::toLowerCase(chrs[i]);
938 return result;
941 jstring
942 java::lang::String::toUpperCase (java::util::Locale *locale)
944 jint i;
945 jchar* chrs = JvGetStringChars(this);
946 jchar ch;
948 // When handling a specific locale there might be special rules.
949 // Currently all existing rules are simply handled inline, as there
950 // are only two and they are documented in the online 1.2 docs.
951 bool handle_esset = locale != NULL;
952 bool handle_tr = false;
953 if (locale != NULL)
955 String *lang = locale->getLanguage ();
956 if (lang->length () == 2
957 && lang->charAt (0) == 't'
958 && lang->charAt (1) == 'r')
959 handle_tr = true;
962 int new_count = count;
963 bool new_string = false;
964 for (i = 0; ; i++)
966 if (i == count)
967 break;
968 jchar origChar = chrs[i];
970 if (handle_esset && origChar == ESSET)
972 ++new_count;
973 new_string = true;
975 else if (handle_tr && (origChar == SMALL_I
976 || origChar == SMALL_DOTLESS_I))
977 new_string = true;
978 else
980 ch = java::lang::Character::toUpperCase(origChar);
981 if (ch != origChar)
982 new_string = true;
985 if (new_string && ! handle_esset)
986 break;
988 if (! new_string)
989 return this;
990 jstring result = JvAllocString(new_count);
991 jchar *dPtr = JvGetStringChars (result);
992 for (i = 0; i < count; i++)
994 if (handle_esset && chrs[i] == ESSET)
996 *dPtr++ = CAPITAL_S;
997 *dPtr++ = CAPITAL_S;
999 else if (handle_tr && chrs[i] == SMALL_I)
1000 *dPtr++ = CAPITAL_I_WITH_DOT;
1001 else if (handle_tr && chrs[i] == SMALL_DOTLESS_I)
1002 *dPtr++ = CAPITAL_I;
1003 else
1004 *dPtr++ = java::lang::Character::toUpperCase(chrs[i]);
1006 return result;
1009 jstring
1010 java::lang::String::trim ()
1012 jchar* chrs = JvGetStringChars(this);
1013 if (count == 0 || (chrs[0] > ' ' && chrs[count-1] > ' '))
1014 return this;
1015 jint preTrim = 0;
1016 for (;; preTrim++)
1018 if (preTrim == count)
1019 return new String();
1020 if (chrs[preTrim] > ' ')
1021 break;
1023 jint endTrim = count;
1024 while (chrs[endTrim-1] <= ' ')
1025 endTrim--;
1026 return substring(preTrim, endTrim);
1029 jstring
1030 java::lang::String::valueOf(jcharArray data, jint offset, jint count)
1032 jint data_length = JvGetArrayLength (data);
1033 if (offset < 0 || count < 0 || offset > data_length - count)
1034 throw new ArrayIndexOutOfBoundsException;
1035 jstring result = JvAllocString(count);
1036 jchar *sPtr = elements (data) + offset;
1037 jchar *dPtr = JvGetStringChars(result);
1038 while (--count >= 0)
1039 *dPtr++ = *sPtr++;
1040 return result;
1043 jstring
1044 java::lang::String::valueOf(jchar c)
1046 jstring result = JvAllocString(1);
1047 JvGetStringChars (result)[0] = c;
1048 return result;