Merge from mainline.
[official-gcc.git] / libjava / java / lang / natString.cc
blobf3f3e4d567b50d6ebb1999a0c3e7c286dd57d5dd
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 memcpy (data, chars, len * sizeof (jchar));
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;
537 // If both have cached hash codes, check that. If the cached hash
538 // codes are zero, don't bother trying to compute them.
539 int myHash = cachedHashCode;
540 int otherHash = other->cachedHashCode;
541 if (myHash && otherHash && myHash != otherHash)
542 return false;
544 // We could see if both are interned, and return false. But that
545 // seems too expensive.
547 jchar *xptr = JvGetStringChars (this);
548 jchar *yptr = JvGetStringChars (other);
549 return ! memcmp (xptr, yptr, count * sizeof (jchar));
552 jboolean
553 java::lang::String::contentEquals(java::lang::StringBuffer* buffer)
555 if (buffer == NULL)
556 throw new NullPointerException;
557 JvSynchronize sync(buffer);
558 if (count != buffer->count)
559 return false;
560 if (data == buffer->value)
561 return true; // Possible if shared.
562 jchar *xptr = JvGetStringChars(this);
563 jchar *yptr = elements(buffer->value);
564 return ! memcmp (xptr, yptr, count * sizeof (jchar));
567 jboolean
568 java::lang::String::contentEquals(java::lang::CharSequence *seq)
570 if (seq->length() != count)
571 return false;
572 jchar *value = JvGetStringChars(this);
573 for (int i = 0; i < count; ++i)
574 if (value[i] != seq->charAt(i))
575 return false;
576 return true;
579 jchar
580 java::lang::String::charAt(jint i)
582 if (i < 0 || i >= count)
583 throw new java::lang::StringIndexOutOfBoundsException(i);
584 return JvGetStringChars(this)[i];
587 void
588 java::lang::String::getChars(jint srcBegin, jint srcEnd,
589 jcharArray dst, jint dstBegin)
591 jint dst_length = JvGetArrayLength (dst);
592 if (srcBegin < 0 || srcBegin > srcEnd || srcEnd > count)
593 throw new java::lang::StringIndexOutOfBoundsException;
594 // The 2nd part of the test below is equivalent to
595 // dstBegin + (srcEnd-srcBegin) > dst_length
596 // except that it does not overflow.
597 if (dstBegin < 0 || dstBegin > dst_length - (srcEnd-srcBegin))
598 throw new ArrayIndexOutOfBoundsException;
599 jchar *dPtr = elements (dst) + dstBegin;
600 jchar *sPtr = JvGetStringChars (this) + srcBegin;
601 jint i = srcEnd - srcBegin;
602 memcpy (dPtr, sPtr, i * sizeof (jchar));
605 jbyteArray
606 java::lang::String::getBytes (jstring enc)
608 jint todo = length();
609 jint buflen = todo;
610 jbyteArray buffer = JvNewByteArray(todo);
611 jint bufpos = 0;
612 jint offset = 0;
613 gnu::gcj::convert::UnicodeToBytes *converter
614 = gnu::gcj::convert::UnicodeToBytes::getEncoder(enc);
615 while (todo > 0 || converter->havePendingBytes())
617 converter->setOutput(buffer, bufpos);
618 int converted = converter->write(this, offset, todo, NULL);
619 bufpos = converter->count;
620 if (converted == 0 && bufpos == converter->count)
622 buflen *= 2;
623 jbyteArray newbuffer = JvNewByteArray(buflen);
624 memcpy (elements (newbuffer), elements (buffer), bufpos);
625 buffer = newbuffer;
627 else
628 bufpos = converter->count;
630 offset += converted;
631 todo -= converted;
633 converter->done ();
634 if (bufpos == buflen)
635 return buffer;
636 jbyteArray result = JvNewByteArray(bufpos);
637 memcpy (elements (result), elements (buffer), bufpos);
638 return result;
641 void
642 java::lang::String::getBytes(jint srcBegin, jint srcEnd,
643 jbyteArray dst, jint dstBegin)
645 jint dst_length = JvGetArrayLength (dst);
646 if (srcBegin < 0 || srcBegin > srcEnd || srcEnd > count)
647 throw new java::lang::StringIndexOutOfBoundsException;
648 // The 2nd part of the test below is equivalent to
649 // dstBegin + (srcEnd-srcBegin) > dst_length
650 // except that it does not overflow.
651 if (dstBegin < 0 || dstBegin > dst_length - (srcEnd-srcBegin))
652 throw new ArrayIndexOutOfBoundsException;
653 jbyte *dPtr = elements (dst) + dstBegin;
654 jchar *sPtr = JvGetStringChars (this) + srcBegin;
655 jint i = srcEnd-srcBegin;
656 while (--i >= 0)
657 *dPtr++ = (jbyte) *sPtr++;
660 jcharArray
661 java::lang::String::toCharArray()
663 jcharArray array = JvNewCharArray(count);
664 jchar *dPtr = elements (array);
665 jchar *sPtr = JvGetStringChars (this);
666 jint i = count;
667 memcpy (dPtr, sPtr, i * sizeof (jchar));
668 return array;
671 jboolean
672 java::lang::String::equalsIgnoreCase (jstring anotherString)
674 if (anotherString == NULL || count != anotherString->count)
675 return false;
676 jchar *tptr = JvGetStringChars (this);
677 jchar *optr = JvGetStringChars (anotherString);
678 jint i = count;
679 while (--i >= 0)
681 jchar tch = *tptr++;
682 jchar och = *optr++;
683 if (tch != och
684 && (java::lang::Character::toLowerCase (tch)
685 != java::lang::Character::toLowerCase (och))
686 && (java::lang::Character::toUpperCase (tch)
687 != java::lang::Character::toUpperCase (och)))
688 return false;
690 return true;
693 jboolean
694 java::lang::String::regionMatches (jint toffset,
695 jstring other, jint ooffset, jint len)
697 if (toffset < 0 || ooffset < 0 || len < 0
698 || toffset > count - len
699 || ooffset > other->count - len)
700 return false;
701 jchar *tptr = JvGetStringChars (this) + toffset;
702 jchar *optr = JvGetStringChars (other) + ooffset;
703 jint i = len;
704 return ! memcmp (tptr, optr, i * sizeof (jchar));
707 jint
708 java::lang::String::compareTo (jstring anotherString)
710 jchar *tptr = JvGetStringChars (this);
711 jchar *optr = JvGetStringChars (anotherString);
712 jint tlen = this->count;
713 jint olen = anotherString->count;
714 jint i = tlen > olen ? olen : tlen;
715 while (--i >= 0)
717 jchar tch = *tptr++;
718 jchar och = *optr++;
719 if (tch != och)
720 return (jint) tch - (jint) och;
722 return tlen - olen;
725 jboolean
726 java::lang::String::regionMatches (jboolean ignoreCase, jint toffset,
727 jstring other, jint ooffset, jint len)
729 if (toffset < 0 || ooffset < 0 || len < 0
730 || toffset > count - len
731 || ooffset > other->count - len)
732 return false;
733 jchar *tptr = JvGetStringChars (this) + toffset;
734 jchar *optr = JvGetStringChars (other) + ooffset;
735 jint i = len;
736 if (ignoreCase)
738 while (--i >= 0)
740 jchar tch = *tptr++;
741 jchar och = *optr++;
742 if ((java::lang::Character::toLowerCase (tch)
743 != java::lang::Character::toLowerCase (och))
744 && (java::lang::Character::toUpperCase (tch)
745 != java::lang::Character::toUpperCase (och)))
746 return false;
748 return true;
750 return ! memcmp (tptr, optr, i * sizeof (jchar));
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 return ! memcmp (xptr, yptr, i * sizeof (jchar));
764 jint
765 java::lang::String::indexOf (jint ch, jint fromIndex)
767 if (fromIndex < 0)
768 fromIndex = 0;
769 jchar *ptr = JvGetStringChars(this);
770 for (;; ++fromIndex)
772 if (fromIndex >= count)
773 return -1;
774 if (ptr[fromIndex] == ch)
775 return fromIndex;
779 jint
780 java::lang::String::indexOf (jstring s, jint fromIndex)
782 const jchar *const xchars = JvGetStringChars(s);
783 const jchar *const ychars = JvGetStringChars(this) + fromIndex;
785 const int xlength = s->length ();
786 const int ylength = length () - fromIndex;
788 int i = 0;
789 int j = 0;
791 while (i < ylength && j < xlength)
793 if (xchars[j] != ychars[i])
795 i = i - j + 1;
796 j = 0;
798 else
799 i++, j++;
802 if (j >= xlength)
803 return fromIndex + i - xlength;
804 else
805 return -1;
808 jint
809 java::lang::String::lastIndexOf (jint ch, jint fromIndex)
811 if (fromIndex >= count)
812 fromIndex = count - 1;
813 jchar *ptr = JvGetStringChars(this);
814 for (;; --fromIndex)
816 if (fromIndex < 0)
817 return -1;
818 if (ptr[fromIndex] == ch)
819 return fromIndex;
823 jstring
824 java::lang::String::substring (jint beginIndex, jint endIndex)
826 if (beginIndex < 0 || endIndex > count || beginIndex > endIndex)
827 throw new StringIndexOutOfBoundsException;
828 if (beginIndex == 0 && endIndex == count)
829 return this;
830 jint newCount = endIndex - beginIndex;
831 // For very small strings, just allocate a new one. For other
832 // substrings, allocate a new one unless the substring is over half
833 // of the original string.
834 if (newCount <= 8 || newCount < (count >> 1))
835 return JvNewString(JvGetStringChars(this) + beginIndex, newCount);
836 jstring s = new String();
837 s->data = data;
838 s->count = newCount;
839 s->boffset = boffset + sizeof(jchar) * beginIndex;
840 return s;
843 jstring
844 java::lang::String::concat(jstring str)
846 jint str_count = str->count;
847 if (str_count == 0)
848 return this;
849 jstring result = JvAllocString(count + str_count);
850 jchar *dstPtr = JvGetStringChars(result);
851 jchar *srcPtr = JvGetStringChars(this);
852 jint i = count;
853 memcpy (dstPtr, srcPtr, i * sizeof (jchar));
854 dstPtr += i;
855 srcPtr = JvGetStringChars(str);
856 i = str->count;
857 memcpy (dstPtr, srcPtr, i * sizeof (jchar));
858 return result;
861 jstring
862 java::lang::String::replace (jchar oldChar, jchar newChar)
864 jint i;
865 jchar* chrs = JvGetStringChars (this);
866 for (i = 0; ; i++)
868 if (i == count)
869 return this;
870 if (chrs[i] == oldChar)
871 break;
873 jstring result = JvAllocString (count);
874 jchar *dPtr = JvGetStringChars (result);
875 for (int j = 0; j < i; j++)
876 *dPtr++ = chrs[j];
877 for (; i < count; i++)
879 jchar ch = chrs[i];
880 if (ch == oldChar)
881 ch = newChar;
882 *dPtr++ = ch;
884 return result;
887 jstring
888 java::lang::String::toLowerCase (java::util::Locale *locale)
890 jint i;
891 jchar* chrs = JvGetStringChars(this);
892 jchar ch = 0;
894 bool handle_tr = false;
895 if (locale != NULL)
897 String *lang = locale->getLanguage ();
898 if (lang->length () == 2
899 && lang->charAt (0) == 't'
900 && lang->charAt (1) == 'r')
901 handle_tr = true;
904 for (i = 0; ; i++)
906 if (i == count)
907 return this;
908 jchar origChar = chrs[i];
910 if (handle_tr && (origChar == CAPITAL_I
911 || origChar == CAPITAL_I_WITH_DOT))
912 break;
914 ch = java::lang::Character::toLowerCase(origChar);
915 if (ch != origChar)
916 break;
918 jstring result = JvAllocString(count);
919 jchar *dPtr = JvGetStringChars (result);
920 for (int j = 0; j < i; j++)
921 *dPtr++ = chrs[j];
922 *dPtr++ = ch; i++;
923 for (; i < count; i++)
925 if (handle_tr && chrs[i] == CAPITAL_I)
926 *dPtr++ = SMALL_DOTLESS_I;
927 else if (handle_tr && chrs[i] == CAPITAL_I_WITH_DOT)
928 *dPtr++ = SMALL_I;
929 else
930 *dPtr++ = java::lang::Character::toLowerCase(chrs[i]);
932 return result;
935 jstring
936 java::lang::String::toUpperCase (java::util::Locale *locale)
938 jint i;
939 jchar* chrs = JvGetStringChars(this);
940 jchar ch;
942 // When handling a specific locale there might be special rules.
943 // Currently all existing rules are simply handled inline, as there
944 // are only two and they are documented in the online 1.2 docs.
945 bool handle_esset = locale != NULL;
946 bool handle_tr = false;
947 if (locale != NULL)
949 String *lang = locale->getLanguage ();
950 if (lang->length () == 2
951 && lang->charAt (0) == 't'
952 && lang->charAt (1) == 'r')
953 handle_tr = true;
956 int new_count = count;
957 bool new_string = false;
958 for (i = 0; ; i++)
960 if (i == count)
961 break;
962 jchar origChar = chrs[i];
964 if (handle_esset && origChar == ESSET)
966 ++new_count;
967 new_string = true;
969 else if (handle_tr && (origChar == SMALL_I
970 || origChar == SMALL_DOTLESS_I))
971 new_string = true;
972 else
974 ch = java::lang::Character::toUpperCase(origChar);
975 if (ch != origChar)
976 new_string = true;
979 if (new_string && ! handle_esset)
980 break;
982 if (! new_string)
983 return this;
984 jstring result = JvAllocString(new_count);
985 jchar *dPtr = JvGetStringChars (result);
986 for (i = 0; i < count; i++)
988 if (handle_esset && chrs[i] == ESSET)
990 *dPtr++ = CAPITAL_S;
991 *dPtr++ = CAPITAL_S;
993 else if (handle_tr && chrs[i] == SMALL_I)
994 *dPtr++ = CAPITAL_I_WITH_DOT;
995 else if (handle_tr && chrs[i] == SMALL_DOTLESS_I)
996 *dPtr++ = CAPITAL_I;
997 else
998 *dPtr++ = java::lang::Character::toUpperCase(chrs[i]);
1000 return result;
1003 jstring
1004 java::lang::String::trim ()
1006 jchar* chrs = JvGetStringChars(this);
1007 if (count == 0 || (chrs[0] > ' ' && chrs[count-1] > ' '))
1008 return this;
1009 jint preTrim = 0;
1010 for (;; preTrim++)
1012 if (preTrim == count)
1013 return new String();
1014 if (chrs[preTrim] > ' ')
1015 break;
1017 jint endTrim = count;
1018 while (chrs[endTrim-1] <= ' ')
1019 endTrim--;
1020 return substring(preTrim, endTrim);
1023 jstring
1024 java::lang::String::valueOf(jcharArray data, jint offset, jint count)
1026 jint data_length = JvGetArrayLength (data);
1027 if (offset < 0 || count < 0 || offset > data_length - count)
1028 throw new ArrayIndexOutOfBoundsException;
1029 jstring result = JvAllocString(count);
1030 jchar *sPtr = elements (data) + offset;
1031 jchar *dPtr = JvGetStringChars(result);
1032 memcpy (dPtr, sPtr, count * sizeof (jchar));
1033 return result;
1036 jstring
1037 java::lang::String::valueOf(jchar c)
1039 jstring result = JvAllocString(1);
1040 JvGetStringChars (result)[0] = c;
1041 return result;