Merge from mainline
[official-gcc.git] / libjava / java / lang / natString.cc
blob3f630812d5ef478db610f70ed568cbbbf68b4244
1 // natString.cc - Implementation of java.lang.String native methods.
3 /* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 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/CharSequence.h>
19 #include <java/lang/String.h>
20 #include <java/lang/IndexOutOfBoundsException.h>
21 #include <java/lang/ArrayIndexOutOfBoundsException.h>
22 #include <java/lang/StringIndexOutOfBoundsException.h>
23 #include <java/lang/NullPointerException.h>
24 #include <java/lang/StringBuffer.h>
25 #include <java/io/ByteArrayOutputStream.h>
26 #include <java/io/OutputStreamWriter.h>
27 #include <java/io/ByteArrayInputStream.h>
28 #include <java/io/InputStreamReader.h>
29 #include <java/util/Locale.h>
30 #include <gnu/gcj/convert/UnicodeToBytes.h>
31 #include <gnu/gcj/convert/BytesToUnicode.h>
32 #include <gnu/gcj/runtime/StringBuffer.h>
33 #include <jvm.h>
35 static jstring* strhash = NULL;
36 static int strhash_count = 0; /* Number of slots used in strhash. */
37 static int strhash_size = 0; /* Number of slots available in strhash.
38 * Assumed be power of 2! */
40 // Some defines used by toUpperCase / toLowerCase.
41 #define ESSET 0x00df
42 #define CAPITAL_S 0x0053
43 #define SMALL_I 0x0069
44 #define CAPITAL_I_WITH_DOT 0x0130
45 #define SMALL_DOTLESS_I 0x0131
46 #define CAPITAL_I 0x0049
48 #define DELETED_STRING ((jstring)(~0))
49 #define SET_STRING_IS_INTERNED(STR) /* nothing */
51 #define UNMASK_PTR(Ptr) (((unsigned long) (Ptr)) & ~0x01)
52 #define MASK_PTR(Ptr) (((unsigned long) (Ptr)) | 0x01)
53 #define PTR_MASKED(Ptr) (((unsigned long) (Ptr)) & 0x01)
55 /* Find a slot where the string with elements DATA, length LEN,
56 and hash HASH should go in the strhash table of interned strings. */
57 jstring*
58 _Jv_StringFindSlot (jchar* data, jint len, jint hash)
60 JvSynchronize sync (&java::lang::String::class$);
62 int start_index = hash & (strhash_size - 1);
63 int deleted_index = -1;
65 int index = start_index;
66 /* step must be non-zero, and relatively prime with strhash_size. */
67 jint step = (hash ^ (hash >> 16)) | 1;
70 jstring* ptr = &strhash[index];
71 jstring value = (jstring) UNMASK_PTR (*ptr);
72 if (value == NULL)
74 if (deleted_index >= 0)
75 return (&strhash[deleted_index]);
76 else
77 return ptr;
79 else if (*ptr == DELETED_STRING)
80 deleted_index = index;
81 else if (value->length() == len
82 && memcmp(JvGetStringChars(value), data, 2*len) == 0)
83 return (ptr);
84 index = (index + step) & (strhash_size - 1);
86 while (index != start_index);
87 // Note that we can have INDEX == START_INDEX if the table has no
88 // NULL entries but does have DELETED_STRING entries.
89 JvAssert (deleted_index >= 0);
90 return &strhash[deleted_index];
93 /* Calculate a hash code for the string starting at PTR at given LENGTH.
94 This uses the same formula as specified for java.lang.String.hash. */
96 static jint
97 hashChars (jchar* ptr, jint length)
99 jchar* limit = ptr + length;
100 jint hash = 0;
101 // Updated specification from
102 // http://www.javasoft.com/docs/books/jls/clarify.html.
103 while (ptr < limit)
104 hash = (31 * hash) + *ptr++;
105 return hash;
108 jint
109 java::lang::String::hashCode()
111 if (cachedHashCode == 0)
112 cachedHashCode = hashChars(JvGetStringChars(this), length());
113 return cachedHashCode;
116 jstring*
117 _Jv_StringGetSlot (jstring str)
119 jchar* data = JvGetStringChars(str);
120 int length = str->length();
121 return _Jv_StringFindSlot(data, length, hashChars (data, length));
124 static void
125 rehash ()
127 JvSynchronize sync (&java::lang::String::class$);
129 if (strhash == NULL)
131 strhash_size = 1024;
132 strhash = (jstring *) _Jv_AllocBytes (strhash_size * sizeof (jstring));
134 else
136 int i = strhash_size;
137 jstring* ptr = strhash + i;
138 int nsize = strhash_size * 2;
139 jstring *next = (jstring *) _Jv_AllocBytes (nsize * sizeof (jstring));
141 while (--i >= 0)
143 --ptr;
144 if (*ptr == NULL || *ptr == DELETED_STRING)
145 continue;
147 /* This is faster equivalent of
148 * *__JvGetInternSlot(*ptr) = *ptr; */
149 jstring val = (jstring) UNMASK_PTR (*ptr);
150 jint hash = val->hashCode();
151 jint index = hash & (nsize - 1);
152 jint step = (hash ^ (hash >> 16)) | 1;
153 for (;;)
155 if (next[index] == NULL)
157 next[index] = *ptr;
158 break;
160 index = (index + step) & (nsize - 1);
164 strhash_size = nsize;
165 strhash = next;
169 jstring
170 java::lang::String::intern()
172 JvSynchronize sync (&java::lang::String::class$);
173 if (3 * strhash_count >= 2 * strhash_size)
174 rehash();
175 jstring* ptr = _Jv_StringGetSlot(this);
176 if (*ptr != NULL && *ptr != DELETED_STRING)
178 // See description in _Jv_FinalizeString() to understand this.
179 *ptr = (jstring) MASK_PTR (*ptr);
180 return (jstring) UNMASK_PTR (*ptr);
182 jstring str = (this->data == this
183 ? this
184 : _Jv_NewString(JvGetStringChars(this), this->length()));
185 SET_STRING_IS_INTERNED(str);
186 strhash_count++;
187 *ptr = str;
188 // When string is GC'd, clear the slot in the hash table.
189 _Jv_RegisterStringFinalizer (str);
190 return str;
193 // The fake String finalizer. This is only used when the String has
194 // been intern()d. However, we must check this case, as it might be
195 // called by the Reference code for any String.
196 void
197 _Jv_FinalizeString (jobject obj)
199 JvSynchronize sync (&java::lang::String::class$);
201 // We might not actually have intern()d any strings at all, if
202 // we're being called from Reference.
203 if (! strhash)
204 return;
206 jstring str = reinterpret_cast<jstring> (obj);
207 jstring *ptr = _Jv_StringGetSlot(str);
208 if (*ptr == NULL || *ptr == DELETED_STRING
209 || (jobject) UNMASK_PTR (*ptr) != obj)
210 return;
212 // We assume the lowest bit of the pointer is free for our nefarious
213 // manipulations. What we do is set it to `0' (implicitly) when
214 // interning the String. If we subsequently re-intern the same
215 // String, then we set the bit. When finalizing, if the bit is set
216 // then we clear it and re-register the finalizer. We know this is
217 // a safe approach because both intern() and _Jv_FinalizeString()
218 // acquire the class lock; this bit can't be manipulated when the
219 // lock is not held. So if we are finalizing and the bit is clear
220 // then we know all references are gone and we can clear the entry
221 // in the hash table. The naive approach of simply clearing the
222 // pointer here fails in the case where a request to intern a new
223 // string with the same contents is made between the time the
224 // intern()d string is found to be unreachable and when the
225 // finalizer is actually run. In this case we could clear a pointer
226 // to a valid string, and future intern() calls for that particular
227 // value would spuriously fail.
228 if (PTR_MASKED (*ptr))
230 *ptr = (jstring) UNMASK_PTR (*ptr);
231 _Jv_RegisterStringFinalizer (obj);
233 else
235 *ptr = DELETED_STRING;
236 strhash_count--;
240 jstring
241 _Jv_NewStringUTF (const char *bytes)
243 int size = strlen (bytes);
244 unsigned char *p = (unsigned char *) bytes;
246 int length = _Jv_strLengthUtf8 ((char *) p, size);
247 if (length < 0)
248 return NULL;
250 jstring jstr = JvAllocString (length);
251 jchar *chrs = JvGetStringChars (jstr);
253 p = (unsigned char *) bytes;
254 unsigned char *limit = p + size;
255 while (p < limit)
256 *chrs++ = UTF8_GET (p, limit);
258 return jstr;
261 jstring
262 _Jv_NewStringUtf8Const (Utf8Const* str)
264 jchar *chrs;
265 jchar buffer[100];
266 jstring jstr;
267 unsigned char* data = (unsigned char*) str->data;
268 unsigned char* limit = data + str->length;
269 int length = _Jv_strLengthUtf8(str->data, str->length);
271 if (length <= (int) (sizeof(buffer) / sizeof(jchar)))
273 jstr = NULL;
274 chrs = buffer;
276 else
278 jstr = JvAllocString(length);
279 chrs = JvGetStringChars(jstr);
282 jint hash = 0;
283 while (data < limit)
285 jchar ch = UTF8_GET(data, limit);
286 hash = (31 * hash) + ch;
287 *chrs++ = ch;
289 chrs -= length;
291 JvSynchronize sync (&java::lang::String::class$);
292 if (3 * strhash_count >= 2 * strhash_size)
293 rehash();
294 jstring* ptr = _Jv_StringFindSlot (chrs, length, hash);
295 if (*ptr != NULL && *ptr != DELETED_STRING)
296 return (jstring) UNMASK_PTR (*ptr);
297 strhash_count++;
298 if (jstr == NULL)
300 jstr = JvAllocString(length);
301 chrs = JvGetStringChars(jstr);
302 memcpy (chrs, buffer, sizeof(jchar)*length);
304 jstr->cachedHashCode = hash;
305 *ptr = jstr;
306 SET_STRING_IS_INTERNED(jstr);
307 // When string is GC'd, clear the slot in the hash table. Note that
308 // we don't have to call _Jv_RegisterStringFinalizer here, as we
309 // know the new object cannot be referred to by a Reference.
310 _Jv_RegisterFinalizer ((void *) jstr, _Jv_FinalizeString);
311 return jstr;
314 jsize
315 _Jv_GetStringUTFLength (jstring string)
317 jsize len = 0;
318 jchar *ptr = JvGetStringChars (string);
319 jsize i = string->length();
320 while (--i >= 0)
322 jchar ch = *ptr++;
323 if (ch > 0 && ch <= 0x7F)
324 len += 1;
325 else if (ch <= 0x7FF)
326 len += 2;
327 else
328 len += 3;
330 return len;
333 // Not sure this quite matches GetStringUTFRegion.
334 // null-termination of result? len? throw exception?
335 jsize
336 _Jv_GetStringUTFRegion (jstring str, jsize start, jsize len, char *buf)
338 jchar *sptr = JvGetStringChars (str) + start;
339 jsize i = len;
340 char *dptr = buf;
341 while (--i >= 0)
343 jchar ch = *sptr++;
344 if (ch > 0 && ch <= 0x7F)
345 *dptr++ = (char) ch;
346 else if (ch <= 0x7FF)
348 *dptr++ = (char) (0xC0 + ((ch >> 6) & 0x1F));
349 *dptr++ = (char) (0x80 + (ch & 0x3F));
351 else
353 *dptr++ = (char) (0xE0 + ((ch >> 12) & 0xF));
354 *dptr++ = (char) (0x80 + ((ch >> 6) & 0x3F));
355 *dptr++ = (char) (0x80 + (ch & 0x3F));
358 return dptr - buf;
361 /* Put printed (decimal) representation of NUM in a buffer.
362 BUFEND marks the end of the buffer, which must be at least 11 jchars long.
363 Returns the COUNT of jchars written. The result is in
364 (BUFEND - COUNT) (inclusive) upto (BUFEND) (exclusive). */
366 jint
367 _Jv_FormatInt (jchar* bufend, jint num)
369 register jchar* ptr = bufend;
370 jboolean isNeg;
371 if (num < 0)
373 isNeg = true;
374 num = -(num);
375 if (num < 0)
377 // Must be MIN_VALUE, so handle this special case.
378 // FIXME use 'unsigned jint' for num.
379 *--ptr = '8';
380 num = 214748364;
383 else
384 isNeg = false;
388 *--ptr = (jchar) ((int) '0' + (num % 10));
389 num /= 10;
391 while (num > 0);
393 if (isNeg)
394 *--ptr = '-';
395 return bufend - ptr;
398 jstring
399 java::lang::String::valueOf (jint num)
401 // Use an array large enough for "-2147483648"; i.e. 11 chars.
402 jchar buffer[11];
403 int i = _Jv_FormatInt (buffer+11, num);
404 return _Jv_NewString (buffer+11-i, i);
407 jstring
408 _Jv_NewString(const jchar *chars, jsize len)
410 jstring str = _Jv_AllocString(len);
411 jchar* data = JvGetStringChars (str);
412 while (--len >= 0)
413 *data++ = *chars++;
414 return str;
417 jstring
418 _Jv_NewStringLatin1(const char *bytes, jsize len)
420 jstring str = JvAllocString(len);
421 jchar* data = JvGetStringChars (str);
422 while (--len >= 0)
423 *data++ = *(unsigned char*)bytes++;
424 return str;
427 void
428 java::lang::String::init(jcharArray chars, jint offset, jint count,
429 jboolean dont_copy)
431 if (! chars)
432 throw new NullPointerException;
433 jsize data_size = JvGetArrayLength (chars);
434 if (offset < 0 || count < 0 || offset + count < 0
435 || offset + count > data_size)
436 throw new ArrayIndexOutOfBoundsException;
437 jcharArray array;
438 jchar *pdst;
439 if (! dont_copy)
441 array = JvNewCharArray(count);
442 pdst = elements (array);
443 memcpy (pdst, elements (chars) + offset, count * sizeof (jchar));
445 else
447 array = chars;
448 pdst = &(elements(array)[offset]);
451 data = array;
452 boffset = (char *) pdst - (char *) array;
453 this->count = count;
456 void
457 java::lang::String::init(jbyteArray ascii, jint hibyte, jint offset,
458 jint count)
460 if (! ascii)
461 throw new NullPointerException;
462 jsize data_size = JvGetArrayLength (ascii);
463 if (offset < 0 || count < 0 || offset + count < 0
464 || offset + count > data_size)
465 throw new ArrayIndexOutOfBoundsException;
466 jcharArray array = JvNewCharArray(count);
467 jbyte *psrc = elements (ascii) + offset;
468 jchar *pdst = elements (array);
469 data = array;
470 boffset = (char *) pdst - (char *) array;
471 this->count = count;
472 hibyte = (hibyte & 0xff) << 8;
473 while (-- count >= 0)
475 *pdst++ = hibyte | (*psrc++ & 0xff);
479 void
480 java::lang::String::init (jbyteArray bytes, jint offset, jint count,
481 jstring encoding)
483 if (! bytes)
484 throw new NullPointerException;
485 jsize data_size = JvGetArrayLength (bytes);
486 if (offset < 0 || count < 0 || offset + count < 0
487 || offset + count > data_size)
488 throw new ArrayIndexOutOfBoundsException;
489 jcharArray array = JvNewCharArray (count);
490 gnu::gcj::convert::BytesToUnicode *converter
491 = gnu::gcj::convert::BytesToUnicode::getDecoder(encoding);
492 jint outpos = 0;
493 int avail = count;
494 converter->setInput(bytes, offset, offset+count);
495 while (converter->inpos < converter->inlength)
497 int done = converter->read(array, outpos, avail);
498 if (done == 0)
500 jint new_size = 2 * (outpos + avail);
501 jcharArray new_array = JvNewCharArray (new_size);
502 memcpy (elements (new_array), elements (array),
503 outpos * sizeof(jchar));
504 array = new_array;
505 avail = new_size - outpos;
507 else
509 outpos += done;
510 avail -= done;
513 converter->done ();
514 this->data = array;
515 this->boffset = (char *) elements (array) - (char *) array;
516 this->count = outpos;
519 void
520 java::lang::String::init (gnu::gcj::runtime::StringBuffer *buffer)
522 init (buffer->value, 0, buffer->count, true);
525 jboolean
526 java::lang::String::equals(jobject anObject)
528 if (anObject == NULL)
529 return false;
530 if (anObject == this)
531 return true;
532 if (anObject->getClass() != &java::lang::String::class$)
533 return false;
534 jstring other = (jstring) anObject;
535 if (count != other->count)
536 return false;
537 /* if both are interned, return false. */
538 jint i = count;
539 jchar *xptr = JvGetStringChars (this);
540 jchar *yptr = JvGetStringChars (other);
541 while (--i >= 0)
543 if (*xptr++ != *yptr++)
544 return false;
546 return true;
549 jboolean
550 java::lang::String::contentEquals(java::lang::StringBuffer* buffer)
552 if (buffer == NULL)
553 throw new NullPointerException;
554 JvSynchronize sync(buffer);
555 if (count != buffer->count)
556 return false;
557 if (data == buffer->value)
558 return true; // Possible if shared.
559 jint i = count;
560 jchar *xptr = JvGetStringChars(this);
561 jchar *yptr = elements(buffer->value);
562 while (--i >= 0)
563 if (*xptr++ != *yptr++)
564 return false;
565 return true;
568 jboolean
569 java::lang::String::contentEquals(java::lang::CharSequence *seq)
571 if (seq->length() != count)
572 return false;
573 jchar *value = JvGetStringChars(this);
574 for (int i = 0; i < count; ++i)
575 if (value[i] != seq->charAt(i))
576 return false;
577 return true;
580 jchar
581 java::lang::String::charAt(jint i)
583 if (i < 0 || i >= count)
584 throw new java::lang::StringIndexOutOfBoundsException(i);
585 return JvGetStringChars(this)[i];
588 void
589 java::lang::String::getChars(jint srcBegin, jint srcEnd,
590 jcharArray dst, jint dstBegin)
592 jint dst_length = JvGetArrayLength (dst);
593 if (srcBegin < 0 || srcBegin > srcEnd || srcEnd > count)
594 throw new java::lang::StringIndexOutOfBoundsException;
595 // The 2nd part of the test below is equivalent to
596 // dstBegin + (srcEnd-srcBegin) > dst_length
597 // except that it does not overflow.
598 if (dstBegin < 0 || dstBegin > dst_length - (srcEnd-srcBegin))
599 throw new ArrayIndexOutOfBoundsException;
600 jchar *dPtr = elements (dst) + dstBegin;
601 jchar *sPtr = JvGetStringChars (this) + srcBegin;
602 jint i = srcEnd-srcBegin;
603 while (--i >= 0)
604 *dPtr++ = *sPtr++;
607 jbyteArray
608 java::lang::String::getBytes (jstring enc)
610 jint todo = length();
611 jint buflen = todo;
612 jbyteArray buffer = JvNewByteArray(todo);
613 jint bufpos = 0;
614 jint offset = 0;
615 gnu::gcj::convert::UnicodeToBytes *converter
616 = gnu::gcj::convert::UnicodeToBytes::getEncoder(enc);
617 while (todo > 0 || converter->havePendingBytes())
619 converter->setOutput(buffer, bufpos);
620 int converted = converter->write(this, offset, todo, NULL);
621 bufpos = converter->count;
622 if (converted == 0 && bufpos == converter->count)
624 buflen *= 2;
625 jbyteArray newbuffer = JvNewByteArray(buflen);
626 memcpy (elements (newbuffer), elements (buffer), bufpos);
627 buffer = newbuffer;
629 else
630 bufpos = converter->count;
632 offset += converted;
633 todo -= converted;
635 converter->done ();
636 if (bufpos == buflen)
637 return buffer;
638 jbyteArray result = JvNewByteArray(bufpos);
639 memcpy (elements (result), elements (buffer), bufpos);
640 return result;
643 void
644 java::lang::String::getBytes(jint srcBegin, jint srcEnd,
645 jbyteArray dst, jint dstBegin)
647 jint dst_length = JvGetArrayLength (dst);
648 if (srcBegin < 0 || srcBegin > srcEnd || srcEnd > count)
649 throw new java::lang::StringIndexOutOfBoundsException;
650 // The 2nd part of the test below is equivalent to
651 // dstBegin + (srcEnd-srcBegin) > dst_length
652 // except that it does not overflow.
653 if (dstBegin < 0 || dstBegin > dst_length - (srcEnd-srcBegin))
654 throw new ArrayIndexOutOfBoundsException;
655 jbyte *dPtr = elements (dst) + dstBegin;
656 jchar *sPtr = JvGetStringChars (this) + srcBegin;
657 jint i = srcEnd-srcBegin;
658 while (--i >= 0)
659 *dPtr++ = (jbyte) *sPtr++;
662 jcharArray
663 java::lang::String::toCharArray()
665 jcharArray array = JvNewCharArray(count);
666 jchar *dPtr = elements (array);
667 jchar *sPtr = JvGetStringChars (this);
668 jint i = count;
669 while (--i >= 0)
670 *dPtr++ = *sPtr++;
671 return array;
674 jboolean
675 java::lang::String::equalsIgnoreCase (jstring anotherString)
677 if (anotherString == NULL || count != anotherString->count)
678 return false;
679 jchar *tptr = JvGetStringChars (this);
680 jchar *optr = JvGetStringChars (anotherString);
681 jint i = count;
682 while (--i >= 0)
684 jchar tch = *tptr++;
685 jchar och = *optr++;
686 if (tch != och
687 && (java::lang::Character::toLowerCase (tch)
688 != java::lang::Character::toLowerCase (och))
689 && (java::lang::Character::toUpperCase (tch)
690 != java::lang::Character::toUpperCase (och)))
691 return false;
693 return true;
696 jboolean
697 java::lang::String::regionMatches (jint toffset,
698 jstring other, jint ooffset, jint len)
700 if (toffset < 0 || ooffset < 0 || len < 0
701 || toffset > count - len
702 || ooffset > other->count - len)
703 return false;
704 jchar *tptr = JvGetStringChars (this) + toffset;
705 jchar *optr = JvGetStringChars (other) + ooffset;
706 jint i = len;
707 while (--i >= 0)
709 if (*tptr++ != *optr++)
710 return false;
712 return true;
715 jint
716 java::lang::String::compareTo (jstring anotherString)
718 jchar *tptr = JvGetStringChars (this);
719 jchar *optr = JvGetStringChars (anotherString);
720 jint tlen = this->count;
721 jint olen = anotherString->count;
722 jint i = tlen > olen ? olen : tlen;
723 while (--i >= 0)
725 jchar tch = *tptr++;
726 jchar och = *optr++;
727 if (tch != och)
728 return (jint) tch - (jint) och;
730 return tlen - olen;
733 jboolean
734 java::lang::String::regionMatches (jboolean ignoreCase, jint toffset,
735 jstring other, jint ooffset, jint len)
737 if (toffset < 0 || ooffset < 0 || len < 0
738 || toffset > count - len
739 || ooffset > other->count - len)
740 return false;
741 jchar *tptr = JvGetStringChars (this) + toffset;
742 jchar *optr = JvGetStringChars (other) + ooffset;
743 jint i = len;
744 if (ignoreCase)
745 while (--i >= 0)
747 jchar tch = *tptr++;
748 jchar och = *optr++;
749 if ((java::lang::Character::toLowerCase (tch)
750 != java::lang::Character::toLowerCase (och))
751 && (java::lang::Character::toUpperCase (tch)
752 != java::lang::Character::toUpperCase (och)))
753 return false;
755 else
756 while (--i >= 0)
758 jchar tch = *tptr++;
759 jchar och = *optr++;
760 if (tch != och)
761 return false;
763 return true;
766 jboolean
767 java::lang::String::startsWith (jstring prefix, jint toffset)
769 jint i = prefix->count;
770 if (toffset < 0 || toffset > count - i)
771 return false;
772 jchar *xptr = JvGetStringChars (this) + toffset;
773 jchar *yptr = JvGetStringChars (prefix);
774 while (--i >= 0)
776 if (*xptr++ != *yptr++)
777 return false;
779 return true;
782 jint
783 java::lang::String::indexOf (jint ch, jint fromIndex)
785 if (fromIndex < 0)
786 fromIndex = 0;
787 jchar *ptr = JvGetStringChars(this);
788 for (;; ++fromIndex)
790 if (fromIndex >= count)
791 return -1;
792 if (ptr[fromIndex] == ch)
793 return fromIndex;
797 jint
798 java::lang::String::indexOf (jstring s, jint fromIndex)
800 const jchar *const xchars = JvGetStringChars(s);
801 const jchar *const ychars = JvGetStringChars(this) + fromIndex;
803 const int xlength = s->length ();
804 const int ylength = length () - fromIndex;
806 int i = 0;
807 int j = 0;
809 while (i < ylength && j < xlength)
811 if (xchars[j] != ychars[i])
813 i = i - j + 1;
814 j = 0;
816 else
817 i++, j++;
820 if (j >= xlength)
821 return fromIndex + i - xlength;
822 else
823 return -1;
826 jint
827 java::lang::String::lastIndexOf (jint ch, jint fromIndex)
829 if (fromIndex >= count)
830 fromIndex = count - 1;
831 jchar *ptr = JvGetStringChars(this);
832 for (;; --fromIndex)
834 if (fromIndex < 0)
835 return -1;
836 if (ptr[fromIndex] == ch)
837 return fromIndex;
841 jstring
842 java::lang::String::substring (jint beginIndex, jint endIndex)
844 if (beginIndex < 0 || endIndex > count || beginIndex > endIndex)
845 throw new StringIndexOutOfBoundsException;
846 if (beginIndex == 0 && endIndex == count)
847 return this;
848 jint newCount = endIndex - beginIndex;
849 // For very small strings, just allocate a new one. For other
850 // substrings, allocate a new one unless the substring is over half
851 // of the original string.
852 if (newCount <= 8 || newCount < (count >> 1))
853 return JvNewString(JvGetStringChars(this) + beginIndex, newCount);
854 jstring s = new String();
855 s->data = data;
856 s->count = newCount;
857 s->boffset = boffset + sizeof(jchar) * beginIndex;
858 return s;
861 jstring
862 java::lang::String::concat(jstring str)
864 jint str_count = str->count;
865 if (str_count == 0)
866 return this;
867 jstring result = JvAllocString(count + str_count);
868 jchar *dstPtr = JvGetStringChars(result);
869 jchar *srcPtr = JvGetStringChars(this);
870 jint i = count;
871 while (--i >= 0)
872 *dstPtr++ = *srcPtr++;
873 srcPtr = JvGetStringChars(str);
874 i = str->count;
875 while (--i >= 0)
876 *dstPtr++ = *srcPtr++;
877 return result;
880 jstring
881 java::lang::String::replace (jchar oldChar, jchar newChar)
883 jint i;
884 jchar* chrs = JvGetStringChars (this);
885 for (i = 0; ; i++)
887 if (i == count)
888 return this;
889 if (chrs[i] == oldChar)
890 break;
892 jstring result = JvAllocString (count);
893 jchar *dPtr = JvGetStringChars (result);
894 for (int j = 0; j < i; j++)
895 *dPtr++ = chrs[j];
896 for (; i < count; i++)
898 jchar ch = chrs[i];
899 if (ch == oldChar)
900 ch = newChar;
901 *dPtr++ = ch;
903 return result;
906 jstring
907 java::lang::String::toLowerCase (java::util::Locale *locale)
909 jint i;
910 jchar* chrs = JvGetStringChars(this);
911 jchar ch = 0;
913 bool handle_tr = false;
914 if (locale != NULL)
916 String *lang = locale->getLanguage ();
917 if (lang->length () == 2
918 && lang->charAt (0) == 't'
919 && lang->charAt (1) == 'r')
920 handle_tr = true;
923 for (i = 0; ; i++)
925 if (i == count)
926 return this;
927 jchar origChar = chrs[i];
929 if (handle_tr && (origChar == CAPITAL_I
930 || origChar == CAPITAL_I_WITH_DOT))
931 break;
933 ch = java::lang::Character::toLowerCase(origChar);
934 if (ch != origChar)
935 break;
937 jstring result = JvAllocString(count);
938 jchar *dPtr = JvGetStringChars (result);
939 for (int j = 0; j < i; j++)
940 *dPtr++ = chrs[j];
941 *dPtr++ = ch; i++;
942 for (; i < count; i++)
944 if (handle_tr && chrs[i] == CAPITAL_I)
945 *dPtr++ = SMALL_DOTLESS_I;
946 else if (handle_tr && chrs[i] == CAPITAL_I_WITH_DOT)
947 *dPtr++ = SMALL_I;
948 else
949 *dPtr++ = java::lang::Character::toLowerCase(chrs[i]);
951 return result;
954 jstring
955 java::lang::String::toUpperCase (java::util::Locale *locale)
957 jint i;
958 jchar* chrs = JvGetStringChars(this);
959 jchar ch;
961 // When handling a specific locale there might be special rules.
962 // Currently all existing rules are simply handled inline, as there
963 // are only two and they are documented in the online 1.2 docs.
964 bool handle_esset = locale != NULL;
965 bool handle_tr = false;
966 if (locale != NULL)
968 String *lang = locale->getLanguage ();
969 if (lang->length () == 2
970 && lang->charAt (0) == 't'
971 && lang->charAt (1) == 'r')
972 handle_tr = true;
975 int new_count = count;
976 bool new_string = false;
977 for (i = 0; ; i++)
979 if (i == count)
980 break;
981 jchar origChar = chrs[i];
983 if (handle_esset && origChar == ESSET)
985 ++new_count;
986 new_string = true;
988 else if (handle_tr && (origChar == SMALL_I
989 || origChar == SMALL_DOTLESS_I))
990 new_string = true;
991 else
993 ch = java::lang::Character::toUpperCase(origChar);
994 if (ch != origChar)
995 new_string = true;
998 if (new_string && ! handle_esset)
999 break;
1001 if (! new_string)
1002 return this;
1003 jstring result = JvAllocString(new_count);
1004 jchar *dPtr = JvGetStringChars (result);
1005 for (i = 0; i < count; i++)
1007 if (handle_esset && chrs[i] == ESSET)
1009 *dPtr++ = CAPITAL_S;
1010 *dPtr++ = CAPITAL_S;
1012 else if (handle_tr && chrs[i] == SMALL_I)
1013 *dPtr++ = CAPITAL_I_WITH_DOT;
1014 else if (handle_tr && chrs[i] == SMALL_DOTLESS_I)
1015 *dPtr++ = CAPITAL_I;
1016 else
1017 *dPtr++ = java::lang::Character::toUpperCase(chrs[i]);
1019 return result;
1022 jstring
1023 java::lang::String::trim ()
1025 jchar* chrs = JvGetStringChars(this);
1026 if (count == 0 || (chrs[0] > ' ' && chrs[count-1] > ' '))
1027 return this;
1028 jint preTrim = 0;
1029 for (;; preTrim++)
1031 if (preTrim == count)
1032 return new String();
1033 if (chrs[preTrim] > ' ')
1034 break;
1036 jint endTrim = count;
1037 while (chrs[endTrim-1] <= ' ')
1038 endTrim--;
1039 return substring(preTrim, endTrim);
1042 jstring
1043 java::lang::String::valueOf(jcharArray data, jint offset, jint count)
1045 jint data_length = JvGetArrayLength (data);
1046 if (offset < 0 || count < 0 || offset > data_length - count)
1047 throw new ArrayIndexOutOfBoundsException;
1048 jstring result = JvAllocString(count);
1049 jchar *sPtr = elements (data) + offset;
1050 jchar *dPtr = JvGetStringChars(result);
1051 while (--count >= 0)
1052 *dPtr++ = *sPtr++;
1053 return result;
1056 jstring
1057 java::lang::String::valueOf(jchar c)
1059 jstring result = JvAllocString(1);
1060 JvGetStringChars (result)[0] = c;
1061 return result;