2003-12-26 Guilhem Lavaux <guilhem@kaffe.org>
[official-gcc.git] / libjava / java / lang / natString.cc
blob8217f70399541a0aabfbc6828335b43c2414da2e
1 // natString.cc - Implementation of java.lang.String native methods.
3 /* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 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 (&StringClass);
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;
67 for (;;)
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);
84 JvAssert (index != start_index);
88 /* Calculate a hash code for the string starting at PTR at given LENGTH.
89 This uses the same formula as specified for java.lang.String.hash. */
91 static jint
92 hashChars (jchar* ptr, jint length)
94 jchar* limit = ptr + length;
95 jint hash = 0;
96 // Updated specification from
97 // http://www.javasoft.com/docs/books/jls/clarify.html.
98 while (ptr < limit)
99 hash = (31 * hash) + *ptr++;
100 return hash;
103 jint
104 java::lang::String::hashCode()
106 if (cachedHashCode == 0)
107 cachedHashCode = hashChars(JvGetStringChars(this), length());
108 return cachedHashCode;
111 jstring*
112 _Jv_StringGetSlot (jstring str)
114 jchar* data = JvGetStringChars(str);
115 int length = str->length();
116 return _Jv_StringFindSlot(data, length, hashChars (data, length));
119 void
120 java::lang::String::rehash()
122 JvSynchronize sync (&StringClass);
124 if (strhash == NULL)
126 strhash_size = 1024;
127 strhash = (jstring *) _Jv_AllocBytes (strhash_size * sizeof (jstring));
128 memset (strhash, 0, strhash_size * sizeof (jstring));
130 else
132 int i = strhash_size;
133 jstring* ptr = strhash + i;
134 int nsize = strhash_size * 2;
135 jstring *next = (jstring *) _Jv_AllocBytes (nsize * sizeof (jstring));
136 memset (next, 0, nsize * sizeof (jstring));
138 while (--i >= 0)
140 --ptr;
141 if (*ptr == NULL || *ptr == DELETED_STRING)
142 continue;
144 /* This is faster equivalent of
145 * *__JvGetInternSlot(*ptr) = *ptr; */
146 jstring val = (jstring) UNMASK_PTR (*ptr);
147 jint hash = val->hashCode();
148 jint index = hash & (nsize - 1);
149 jint step = (hash ^ (hash >> 16)) | 1;
150 for (;;)
152 if (next[index] == NULL)
154 next[index] = *ptr;
155 break;
157 index = (index + step) & (nsize - 1);
161 strhash_size = nsize;
162 strhash = next;
166 jstring
167 java::lang::String::intern()
169 JvSynchronize sync (&StringClass);
170 if (3 * strhash_count >= 2 * strhash_size)
171 rehash();
172 jstring* ptr = _Jv_StringGetSlot(this);
173 if (*ptr != NULL && *ptr != DELETED_STRING)
175 // See description in _Jv_FinalizeString() to understand this.
176 *ptr = (jstring) MASK_PTR (*ptr);
177 return (jstring) UNMASK_PTR (*ptr);
179 jstring str = (this->data == this
180 ? this
181 : _Jv_NewString(JvGetStringChars(this), this->length()));
182 SET_STRING_IS_INTERNED(str);
183 strhash_count++;
184 *ptr = str;
185 // When string is GC'd, clear the slot in the hash table.
186 _Jv_RegisterStringFinalizer (str);
187 return str;
190 // The fake String finalizer. This is only used when the String has
191 // been intern()d. However, we must check this case, as it might be
192 // called by the Reference code for any String.
193 void
194 _Jv_FinalizeString (jobject obj)
196 JvSynchronize sync (&StringClass);
198 // We might not actually have intern()d any strings at all, if
199 // we're being called from Reference.
200 if (! strhash)
201 return;
203 jstring str = reinterpret_cast<jstring> (obj);
204 jstring *ptr = _Jv_StringGetSlot(str);
205 if (*ptr == NULL || *ptr == DELETED_STRING
206 || (jobject) UNMASK_PTR (*ptr) != obj)
207 return;
209 // We assume the lowest bit of the pointer is free for our nefarious
210 // manipulations. What we do is set it to `0' (implicitly) when
211 // interning the String. If we subsequently re-intern the same
212 // String, then we set the bit. When finalizing, if the bit is set
213 // then we clear it and re-register the finalizer. We know this is
214 // a safe approach because both intern() and _Jv_FinalizeString()
215 // acquire the class lock; this bit can't be manipulated when the
216 // lock is not held. So if we are finalizing and the bit is clear
217 // then we know all references are gone and we can clear the entry
218 // in the hash table. The naive approach of simply clearing the
219 // pointer here fails in the case where a request to intern a new
220 // string with the same contents is made between the time the
221 // intern()d string is found to be unreachable and when the
222 // finalizer is actually run. In this case we could clear a pointer
223 // to a valid string, and future intern() calls for that particular
224 // value would spuriously fail.
225 if (PTR_MASKED (*ptr))
227 *ptr = (jstring) UNMASK_PTR (*ptr);
228 _Jv_RegisterStringFinalizer (obj);
230 else
232 *ptr = DELETED_STRING;
233 strhash_count--;
237 jstring
238 _Jv_NewStringUTF (const char *bytes)
240 int size = strlen (bytes);
241 unsigned char *p = (unsigned char *) bytes;
243 int length = _Jv_strLengthUtf8 ((char *) p, size);
244 if (length < 0)
245 return NULL;
247 jstring jstr = JvAllocString (length);
248 jchar *chrs = JvGetStringChars (jstr);
250 p = (unsigned char *) bytes;
251 unsigned char *limit = p + size;
252 while (p < limit)
253 *chrs++ = UTF8_GET (p, limit);
255 return jstr;
258 jstring
259 _Jv_NewStringUtf8Const (Utf8Const* str)
261 jchar *chrs;
262 jchar buffer[100];
263 jstring jstr;
264 unsigned char* data = (unsigned char*) str->data;
265 unsigned char* limit = data + str->length;
266 int length = _Jv_strLengthUtf8(str->data, str->length);
268 if (length <= (int) (sizeof(buffer) / sizeof(jchar)))
270 jstr = NULL;
271 chrs = buffer;
273 else
275 jstr = JvAllocString(length);
276 chrs = JvGetStringChars(jstr);
279 jint hash = 0;
280 while (data < limit)
282 jchar ch = UTF8_GET(data, limit);
283 hash = (31 * hash) + ch;
284 *chrs++ = ch;
286 chrs -= length;
288 JvSynchronize sync (&StringClass);
289 if (3 * strhash_count >= 2 * strhash_size)
290 java::lang::String::rehash();
291 jstring* ptr = _Jv_StringFindSlot (chrs, length, hash);
292 if (*ptr != NULL && *ptr != DELETED_STRING)
293 return (jstring) UNMASK_PTR (*ptr);
294 strhash_count++;
295 if (jstr == NULL)
297 jstr = JvAllocString(length);
298 chrs = JvGetStringChars(jstr);
299 memcpy (chrs, buffer, sizeof(jchar)*length);
301 jstr->cachedHashCode = hash;
302 *ptr = jstr;
303 SET_STRING_IS_INTERNED(jstr);
304 // When string is GC'd, clear the slot in the hash table. Note that
305 // we don't have to call _Jv_RegisterStringFinalizer here, as we
306 // know the new object cannot be referred to by a Reference.
307 _Jv_RegisterFinalizer ((void *) jstr, _Jv_FinalizeString);
308 return jstr;
311 jsize
312 _Jv_GetStringUTFLength (jstring string)
314 jsize len = 0;
315 jchar *ptr = JvGetStringChars (string);
316 jsize i = string->length();
317 while (--i >= 0)
319 jchar ch = *ptr++;
320 if (ch > 0 && ch <= 0x7F)
321 len += 1;
322 else if (ch <= 0x7FF)
323 len += 2;
324 else
325 len += 3;
327 return len;
330 // Not sure this quite matches GetStringUTFRegion.
331 // null-termination of result? len? throw exception?
332 jsize
333 _Jv_GetStringUTFRegion (jstring str, jsize start, jsize len, char *buf)
335 jchar *sptr = JvGetStringChars (str) + start;
336 jsize i = len;
337 char *dptr = buf;
338 while (--i >= 0)
340 jchar ch = *sptr++;
341 if (ch > 0 && ch <= 0x7F)
342 *dptr++ = (char) ch;
343 else if (ch <= 0x7FF)
345 *dptr++ = (char) (0xC0 + ((ch >> 6) & 0x1F));
346 *dptr++ = (char) (0x80 + (ch & 0x3F));
348 else
350 *dptr++ = (char) (0xE0 + ((ch >> 12) & 0xF));
351 *dptr++ = (char) (0x80 + ((ch >> 6) & 0x3F));
352 *dptr++ = (char) (0x80 + (ch & 0x3F));
355 return dptr - buf;
358 /* Put printed (decimal) representation of NUM in a buffer.
359 BUFEND marks the end of the buffer, which must be at least 11 jchars long.
360 Returns the COUNT of jchars written. The result is in
361 (BUFEND - COUNT) (inclusive) upto (BUFEND) (exclusive). */
363 jint
364 _Jv_FormatInt (jchar* bufend, jint num)
366 register jchar* ptr = bufend;
367 jboolean isNeg;
368 if (num < 0)
370 isNeg = true;
371 num = -(num);
372 if (num < 0)
374 // Must be MIN_VALUE, so handle this special case.
375 // FIXME use 'unsigned jint' for num.
376 *--ptr = '8';
377 num = 214748364;
380 else
381 isNeg = false;
385 *--ptr = (jchar) ((int) '0' + (num % 10));
386 num /= 10;
388 while (num > 0);
390 if (isNeg)
391 *--ptr = '-';
392 return bufend - ptr;
395 jstring
396 java::lang::String::valueOf (jint num)
398 // Use an array large enough for "-2147483648"; i.e. 11 chars.
399 jchar buffer[11];
400 int i = _Jv_FormatInt (buffer+11, num);
401 return _Jv_NewString (buffer+11-i, i);
404 jstring
405 _Jv_AllocString(jsize len)
407 jsize sz = sizeof(java::lang::String) + len * sizeof(jchar);
409 // We assert that for strings allocated this way, the data field
410 // will always point to the object itself. Thus there is no reason
411 // for the garbage collector to scan any of it.
412 // Furthermore, we're about to overwrite the string data, so
413 // initialization of the object is not an issue.
414 #ifdef ENABLE_JVMPI
415 jstring obj = (jstring) _Jv_AllocPtrFreeObject(&StringClass, sz);
416 #else
417 // Class needs no initialization, and there is no finalizer, so
418 // we can go directly to the collector's allocator interface.
419 jstring obj = (jstring) _Jv_AllocPtrFreeObj(sz, &StringClass);
420 #endif
421 obj->data = obj;
422 obj->boffset = sizeof(java::lang::String);
423 obj->count = len;
424 obj->cachedHashCode = 0;
425 return obj;
428 jstring
429 _Jv_NewString(const jchar *chars, jsize len)
431 jstring str = _Jv_AllocString(len);
432 jchar* data = JvGetStringChars (str);
433 while (--len >= 0)
434 *data++ = *chars++;
435 return str;
438 jstring
439 _Jv_NewStringLatin1(const char *bytes, jsize len)
441 jstring str = JvAllocString(len);
442 jchar* data = JvGetStringChars (str);
443 while (--len >= 0)
444 *data++ = *(unsigned char*)bytes++;
445 return str;
448 void
449 java::lang::String::init(jcharArray chars, jint offset, jint count,
450 jboolean dont_copy)
452 if (! chars)
453 throw new NullPointerException;
454 jsize data_size = JvGetArrayLength (chars);
455 if (offset < 0 || count < 0 || offset + count < 0
456 || offset + count > data_size)
457 throw new ArrayIndexOutOfBoundsException;
458 jcharArray array;
459 jchar *pdst;
460 if (! dont_copy)
462 array = JvNewCharArray(count);
463 pdst = elements (array);
464 memcpy (pdst, elements (chars) + offset, count * sizeof (jchar));
466 else
468 array = chars;
469 pdst = &(elements(array)[offset]);
472 data = array;
473 boffset = (char *) pdst - (char *) array;
474 this->count = count;
477 void
478 java::lang::String::init(jbyteArray ascii, jint hibyte, jint offset,
479 jint count)
481 if (! ascii)
482 throw new NullPointerException;
483 jsize data_size = JvGetArrayLength (ascii);
484 if (offset < 0 || count < 0 || offset + count < 0
485 || offset + count > data_size)
486 throw new ArrayIndexOutOfBoundsException;
487 jcharArray array = JvNewCharArray(count);
488 jbyte *psrc = elements (ascii) + offset;
489 jchar *pdst = elements (array);
490 data = array;
491 boffset = (char *) pdst - (char *) array;
492 this->count = count;
493 hibyte = (hibyte & 0xff) << 8;
494 while (-- count >= 0)
496 *pdst++ = hibyte | (*psrc++ & 0xff);
500 void
501 java::lang::String::init (jbyteArray bytes, jint offset, jint count,
502 jstring encoding)
504 if (! bytes)
505 throw new NullPointerException;
506 jsize data_size = JvGetArrayLength (bytes);
507 if (offset < 0 || count < 0 || offset + count < 0
508 || offset + count > data_size)
509 throw new ArrayIndexOutOfBoundsException;
510 jcharArray array = JvNewCharArray (count);
511 gnu::gcj::convert::BytesToUnicode *converter
512 = gnu::gcj::convert::BytesToUnicode::getDecoder(encoding);
513 jint outpos = 0;
514 int avail = count;
515 converter->setInput(bytes, offset, offset+count);
516 while (converter->inpos < converter->inlength)
518 int done = converter->read(array, outpos, avail);
519 if (done == 0)
521 jint new_size = 2 * (outpos + avail);
522 jcharArray new_array = JvNewCharArray (new_size);
523 memcpy (elements (new_array), elements (array),
524 outpos * sizeof(jchar));
525 array = new_array;
526 avail = new_size - outpos;
528 else
530 outpos += done;
531 avail -= done;
534 converter->done ();
535 this->data = array;
536 this->boffset = (char *) elements (array) - (char *) array;
537 this->count = outpos;
540 void
541 java::lang::String::init (gnu::gcj::runtime::StringBuffer *buffer)
543 init (buffer->value, 0, buffer->count, true);
546 jboolean
547 java::lang::String::equals(jobject anObject)
549 if (anObject == NULL)
550 return false;
551 if (anObject == this)
552 return true;
553 if (anObject->getClass() != &StringClass)
554 return false;
555 jstring other = (jstring) anObject;
556 if (count != other->count)
557 return false;
558 /* if both are interned, return false. */
559 jint i = count;
560 jchar *xptr = JvGetStringChars (this);
561 jchar *yptr = JvGetStringChars (other);
562 while (--i >= 0)
564 if (*xptr++ != *yptr++)
565 return false;
567 return true;
570 jboolean
571 java::lang::String::contentEquals(java::lang::StringBuffer* buffer)
573 if (buffer == NULL)
574 throw new NullPointerException;
575 JvSynchronize sync(buffer);
576 if (count != buffer->count)
577 return false;
578 if (data == buffer->value)
579 return true; // Possible if shared.
580 jint i = count;
581 jchar *xptr = JvGetStringChars(this);
582 jchar *yptr = elements(buffer->value);
583 while (--i >= 0)
584 if (*xptr++ != *yptr++)
585 return false;
586 return true;
589 jchar
590 java::lang::String::charAt(jint i)
592 if (i < 0 || i >= count)
593 throw new java::lang::StringIndexOutOfBoundsException(i);
594 return JvGetStringChars(this)[i];
597 void
598 java::lang::String::getChars(jint srcBegin, jint srcEnd,
599 jcharArray dst, jint dstBegin)
601 jint dst_length = JvGetArrayLength (dst);
602 if (srcBegin < 0 || srcBegin > srcEnd || srcEnd > count)
603 throw new java::lang::StringIndexOutOfBoundsException;
604 // The 2nd part of the test below is equivalent to
605 // dstBegin + (srcEnd-srcBegin) > dst_length
606 // except that it does not overflow.
607 if (dstBegin < 0 || dstBegin > dst_length - (srcEnd-srcBegin))
608 throw new ArrayIndexOutOfBoundsException;
609 jchar *dPtr = elements (dst) + dstBegin;
610 jchar *sPtr = JvGetStringChars (this) + srcBegin;
611 jint i = srcEnd-srcBegin;
612 while (--i >= 0)
613 *dPtr++ = *sPtr++;
616 jbyteArray
617 java::lang::String::getBytes (jstring enc)
619 jint todo = length();
620 jint buflen = todo;
621 jbyteArray buffer = JvNewByteArray(todo);
622 jint bufpos = 0;
623 jint offset = 0;
624 gnu::gcj::convert::UnicodeToBytes *converter
625 = gnu::gcj::convert::UnicodeToBytes::getEncoder(enc);
626 while (todo > 0 || converter->havePendingBytes())
628 converter->setOutput(buffer, bufpos);
629 int converted = converter->write(this, offset, todo, NULL);
630 bufpos = converter->count;
631 if (converted == 0 && bufpos == converter->count)
633 buflen *= 2;
634 jbyteArray newbuffer = JvNewByteArray(buflen);
635 memcpy (elements (newbuffer), elements (buffer), bufpos);
636 buffer = newbuffer;
638 else
639 bufpos = converter->count;
641 offset += converted;
642 todo -= converted;
644 converter->done ();
645 if (bufpos == buflen)
646 return buffer;
647 jbyteArray result = JvNewByteArray(bufpos);
648 memcpy (elements (result), elements (buffer), bufpos);
649 return result;
652 void
653 java::lang::String::getBytes(jint srcBegin, jint srcEnd,
654 jbyteArray dst, jint dstBegin)
656 jint dst_length = JvGetArrayLength (dst);
657 if (srcBegin < 0 || srcBegin > srcEnd || srcEnd > count)
658 throw new java::lang::StringIndexOutOfBoundsException;
659 // The 2nd part of the test below is equivalent to
660 // dstBegin + (srcEnd-srcBegin) > dst_length
661 // except that it does not overflow.
662 if (dstBegin < 0 || dstBegin > dst_length - (srcEnd-srcBegin))
663 throw new ArrayIndexOutOfBoundsException;
664 jbyte *dPtr = elements (dst) + dstBegin;
665 jchar *sPtr = JvGetStringChars (this) + srcBegin;
666 jint i = srcEnd-srcBegin;
667 while (--i >= 0)
668 *dPtr++ = (jbyte) *sPtr++;
671 jcharArray
672 java::lang::String::toCharArray()
674 jcharArray array = JvNewCharArray(count);
675 jchar *dPtr = elements (array);
676 jchar *sPtr = JvGetStringChars (this);
677 jint i = count;
678 while (--i >= 0)
679 *dPtr++ = *sPtr++;
680 return array;
683 jboolean
684 java::lang::String::equalsIgnoreCase (jstring anotherString)
686 if (anotherString == NULL || count != anotherString->count)
687 return false;
688 jchar *tptr = JvGetStringChars (this);
689 jchar *optr = JvGetStringChars (anotherString);
690 jint i = count;
691 while (--i >= 0)
693 jchar tch = *tptr++;
694 jchar och = *optr++;
695 if (tch != och
696 && (java::lang::Character::toLowerCase (tch)
697 != java::lang::Character::toLowerCase (och))
698 && (java::lang::Character::toUpperCase (tch)
699 != java::lang::Character::toUpperCase (och)))
700 return false;
702 return true;
705 jboolean
706 java::lang::String::regionMatches (jint toffset,
707 jstring other, jint ooffset, jint len)
709 if (toffset < 0 || ooffset < 0 || len < 0
710 || toffset > count - len
711 || ooffset > other->count - len)
712 return false;
713 jchar *tptr = JvGetStringChars (this) + toffset;
714 jchar *optr = JvGetStringChars (other) + ooffset;
715 jint i = len;
716 while (--i >= 0)
718 if (*tptr++ != *optr++)
719 return false;
721 return true;
724 jint
725 java::lang::String::compareTo (jstring anotherString)
727 jchar *tptr = JvGetStringChars (this);
728 jchar *optr = JvGetStringChars (anotherString);
729 jint tlen = this->count;
730 jint olen = anotherString->count;
731 jint i = tlen > olen ? olen : tlen;
732 while (--i >= 0)
734 jchar tch = *tptr++;
735 jchar och = *optr++;
736 if (tch != och)
737 return (jint) tch - (jint) och;
739 return tlen - olen;
742 jboolean
743 java::lang::String::regionMatches (jboolean ignoreCase, jint toffset,
744 jstring other, jint ooffset, jint len)
746 if (toffset < 0 || ooffset < 0 || len < 0
747 || toffset > count - len
748 || ooffset > other->count - len)
749 return false;
750 jchar *tptr = JvGetStringChars (this) + toffset;
751 jchar *optr = JvGetStringChars (other) + ooffset;
752 jint i = len;
753 if (ignoreCase)
754 while (--i >= 0)
756 jchar tch = *tptr++;
757 jchar och = *optr++;
758 if ((java::lang::Character::toLowerCase (tch)
759 != java::lang::Character::toLowerCase (och))
760 && (java::lang::Character::toUpperCase (tch)
761 != java::lang::Character::toUpperCase (och)))
762 return false;
764 else
765 while (--i >= 0)
767 jchar tch = *tptr++;
768 jchar och = *optr++;
769 if (tch != och)
770 return false;
772 return true;
775 jboolean
776 java::lang::String::startsWith (jstring prefix, jint toffset)
778 jint i = prefix->count;
779 if (toffset < 0 || toffset > count - i)
780 return false;
781 jchar *xptr = JvGetStringChars (this) + toffset;
782 jchar *yptr = JvGetStringChars (prefix);
783 while (--i >= 0)
785 if (*xptr++ != *yptr++)
786 return false;
788 return true;
791 jint
792 java::lang::String::indexOf (jint ch, jint fromIndex)
794 if (fromIndex < 0)
795 fromIndex = 0;
796 jchar *ptr = JvGetStringChars(this);
797 for (;; ++fromIndex)
799 if (fromIndex >= count)
800 return -1;
801 if (ptr[fromIndex] == ch)
802 return fromIndex;
806 jint
807 java::lang::String::indexOf (jstring s, jint fromIndex)
809 const jchar *const xchars = JvGetStringChars(s);
810 const jchar *const ychars = JvGetStringChars(this) + fromIndex;
812 const int xlength = s->length ();
813 const int ylength = length () - fromIndex;
815 int i = 0;
816 int j = 0;
818 while (i < ylength && j < xlength)
820 if (xchars[j] != ychars[i])
822 i = i - j + 1;
823 j = 0;
825 else
826 i++, j++;
829 if (j >= xlength)
830 return fromIndex + i - xlength;
831 else
832 return -1;
835 jint
836 java::lang::String::lastIndexOf (jint ch, jint fromIndex)
838 if (fromIndex >= count)
839 fromIndex = count - 1;
840 jchar *ptr = JvGetStringChars(this);
841 for (;; --fromIndex)
843 if (fromIndex < 0)
844 return -1;
845 if (ptr[fromIndex] == ch)
846 return fromIndex;
850 jstring
851 java::lang::String::substring (jint beginIndex, jint endIndex)
853 if (beginIndex < 0 || endIndex > count || beginIndex > endIndex)
854 throw new StringIndexOutOfBoundsException;
855 if (beginIndex == 0 && endIndex == count)
856 return this;
857 jint newCount = endIndex - beginIndex;
858 if (newCount <= 8) // Optimization, mainly for GC.
859 return JvNewString(JvGetStringChars(this) + beginIndex, newCount);
860 jstring s = new String();
861 s->data = data;
862 s->count = newCount;
863 s->boffset = boffset + sizeof(jchar) * beginIndex;
864 return s;
867 jstring
868 java::lang::String::concat(jstring str)
870 jint str_count = str->count;
871 if (str_count == 0)
872 return this;
873 jstring result = JvAllocString(count + str_count);
874 jchar *dstPtr = JvGetStringChars(result);
875 jchar *srcPtr = JvGetStringChars(this);
876 jint i = count;
877 while (--i >= 0)
878 *dstPtr++ = *srcPtr++;
879 srcPtr = JvGetStringChars(str);
880 i = str->count;
881 while (--i >= 0)
882 *dstPtr++ = *srcPtr++;
883 return result;
886 jstring
887 java::lang::String::replace (jchar oldChar, jchar newChar)
889 jint i;
890 jchar* chrs = JvGetStringChars (this);
891 for (i = 0; ; i++)
893 if (i == count)
894 return this;
895 if (chrs[i] == oldChar)
896 break;
898 jstring result = JvAllocString (count);
899 jchar *dPtr = JvGetStringChars (result);
900 for (int j = 0; j < i; j++)
901 *dPtr++ = chrs[j];
902 for (; i < count; i++)
904 jchar ch = chrs[i];
905 if (ch == oldChar)
906 ch = newChar;
907 *dPtr++ = ch;
909 return result;
912 jstring
913 java::lang::String::toLowerCase (java::util::Locale *locale)
915 jint i;
916 jchar* chrs = JvGetStringChars(this);
917 jchar ch = 0;
919 bool handle_tr = false;
920 if (locale != NULL)
922 String *lang = locale->getLanguage ();
923 if (lang->length () == 2
924 && lang->charAt (0) == 't'
925 && lang->charAt (1) == 'r')
926 handle_tr = true;
929 for (i = 0; ; i++)
931 if (i == count)
932 return this;
933 jchar origChar = chrs[i];
935 if (handle_tr && (origChar == CAPITAL_I
936 || origChar == CAPITAL_I_WITH_DOT))
937 break;
939 ch = java::lang::Character::toLowerCase(origChar);
940 if (ch != origChar)
941 break;
943 jstring result = JvAllocString(count);
944 jchar *dPtr = JvGetStringChars (result);
945 for (int j = 0; j < i; j++)
946 *dPtr++ = chrs[j];
947 *dPtr++ = ch; i++;
948 for (; i < count; i++)
950 if (handle_tr && chrs[i] == CAPITAL_I)
951 *dPtr++ = SMALL_DOTLESS_I;
952 else if (handle_tr && chrs[i] == CAPITAL_I_WITH_DOT)
953 *dPtr++ = SMALL_I;
954 else
955 *dPtr++ = java::lang::Character::toLowerCase(chrs[i]);
957 return result;
960 jstring
961 java::lang::String::toUpperCase (java::util::Locale *locale)
963 jint i;
964 jchar* chrs = JvGetStringChars(this);
965 jchar ch;
967 // When handling a specific locale there might be special rules.
968 // Currently all existing rules are simply handled inline, as there
969 // are only two and they are documented in the online 1.2 docs.
970 bool handle_esset = locale != NULL;
971 bool handle_tr = false;
972 if (locale != NULL)
974 String *lang = locale->getLanguage ();
975 if (lang->length () == 2
976 && lang->charAt (0) == 't'
977 && lang->charAt (1) == 'r')
978 handle_tr = true;
981 int new_count = count;
982 bool new_string = false;
983 for (i = 0; ; i++)
985 if (i == count)
986 break;
987 jchar origChar = chrs[i];
989 if (handle_esset && origChar == ESSET)
991 ++new_count;
992 new_string = true;
994 else if (handle_tr && (origChar == SMALL_I
995 || origChar == SMALL_DOTLESS_I))
996 new_string = true;
997 else
999 ch = java::lang::Character::toUpperCase(origChar);
1000 if (ch != origChar)
1001 new_string = true;
1004 if (new_string && ! handle_esset)
1005 break;
1007 if (! new_string)
1008 return this;
1009 jstring result = JvAllocString(new_count);
1010 jchar *dPtr = JvGetStringChars (result);
1011 for (i = 0; i < count; i++)
1013 if (handle_esset && chrs[i] == ESSET)
1015 *dPtr++ = CAPITAL_S;
1016 *dPtr++ = CAPITAL_S;
1018 else if (handle_tr && chrs[i] == SMALL_I)
1019 *dPtr++ = CAPITAL_I_WITH_DOT;
1020 else if (handle_tr && chrs[i] == SMALL_DOTLESS_I)
1021 *dPtr++ = CAPITAL_I;
1022 else
1023 *dPtr++ = java::lang::Character::toUpperCase(chrs[i]);
1025 return result;
1028 jstring
1029 java::lang::String::trim ()
1031 jchar* chrs = JvGetStringChars(this);
1032 if (count == 0 || (chrs[0] > ' ' && chrs[count-1] > ' '))
1033 return this;
1034 jint preTrim = 0;
1035 for (;; preTrim++)
1037 if (preTrim == count)
1038 return new String();
1039 if (chrs[preTrim] > ' ')
1040 break;
1042 jint endTrim = count;
1043 while (chrs[endTrim-1] <= ' ')
1044 endTrim--;
1045 return substring(preTrim, endTrim);
1048 jstring
1049 java::lang::String::valueOf(jcharArray data, jint offset, jint count)
1051 jint data_length = JvGetArrayLength (data);
1052 if (offset < 0 || count < 0 || offset > data_length - count)
1053 throw new ArrayIndexOutOfBoundsException;
1054 jstring result = JvAllocString(count);
1055 jchar *sPtr = elements (data) + offset;
1056 jchar *dPtr = JvGetStringChars(result);
1057 while (--count >= 0)
1058 *dPtr++ = *sPtr++;
1059 return result;
1062 jstring
1063 java::lang::String::valueOf(jchar c)
1065 jstring result = JvAllocString(1);
1066 JvGetStringChars (result)[0] = c;
1067 return result;