libjava/ChangeLog:
[official-gcc.git] / libjava / java / lang / natString.cc
blob75006a7c9a7aa955b91226754a498b2956f3b41a
1 // natString.cc - Implementation of java.lang.String native methods.
3 /* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
4 2007, 2008 Free Software Foundation
6 This file is part of libgcj.
8 This software is copyrighted work licensed under the terms of the
9 Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
10 details. */
12 #include <config.h>
14 #include <string.h>
15 #include <stdlib.h>
17 #include <gcj/cni.h>
18 #include <java/lang/Character.h>
19 #include <java/lang/CharSequence.h>
20 #include <java/lang/String.h>
21 #include <java/lang/IndexOutOfBoundsException.h>
22 #include <java/lang/ArrayIndexOutOfBoundsException.h>
23 #include <java/lang/StringIndexOutOfBoundsException.h>
24 #include <java/lang/NullPointerException.h>
25 #include <java/lang/StringBuffer.h>
26 #include <java/io/ByteArrayOutputStream.h>
27 #include <java/io/CharConversionException.h>
28 #include <java/io/OutputStreamWriter.h>
29 #include <java/io/ByteArrayInputStream.h>
30 #include <java/io/InputStreamReader.h>
31 #include <java/util/Locale.h>
32 #include <gnu/gcj/convert/UnicodeToBytes.h>
33 #include <gnu/gcj/convert/BytesToUnicode.h>
34 #include <gnu/gcj/runtime/StringBuffer.h>
35 #include <jvm.h>
37 static jstring* strhash = NULL;
38 static int strhash_count = 0; /* Number of slots used in strhash. */
39 static int strhash_size = 0; /* Number of slots available in strhash.
40 * Assumed be power of 2! */
42 // Some defines used by toUpperCase / toLowerCase.
43 #define ESSET 0x00df
44 #define CAPITAL_S 0x0053
45 #define SMALL_I 0x0069
46 #define CAPITAL_I_WITH_DOT 0x0130
47 #define SMALL_DOTLESS_I 0x0131
48 #define CAPITAL_I 0x0049
50 #define DELETED_STRING ((jstring)(~0))
51 #define SET_STRING_IS_INTERNED(STR) /* nothing */
53 #define UNMASK_PTR(Ptr) (((unsigned long) (Ptr)) & ~0x01)
54 #define MASK_PTR(Ptr) (((unsigned long) (Ptr)) | 0x01)
55 #define PTR_MASKED(Ptr) (((unsigned long) (Ptr)) & 0x01)
57 /* Find a slot where the string with elements DATA, length LEN,
58 and hash HASH should go in the strhash table of interned strings. */
59 jstring*
60 _Jv_StringFindSlot (jchar* data, jint len, jint hash)
62 JvSynchronize sync (&java::lang::String::class$);
64 int start_index = hash & (strhash_size - 1);
65 int deleted_index = -1;
67 int index = start_index;
68 /* step must be non-zero, and relatively prime with strhash_size. */
69 jint step = (hash ^ (hash >> 16)) | 1;
72 jstring* ptr = &strhash[index];
73 jstring value = (jstring) UNMASK_PTR (*ptr);
74 if (value == NULL)
76 if (deleted_index >= 0)
77 return (&strhash[deleted_index]);
78 else
79 return ptr;
81 else if (*ptr == DELETED_STRING)
82 deleted_index = index;
83 else if (value->length() == len
84 && memcmp(JvGetStringChars(value), data, 2*len) == 0)
85 return (ptr);
86 index = (index + step) & (strhash_size - 1);
88 while (index != start_index);
89 // Note that we can have INDEX == START_INDEX if the table has no
90 // NULL entries but does have DELETED_STRING entries.
91 JvAssert (deleted_index >= 0);
92 return &strhash[deleted_index];
95 /* Calculate a hash code for the string starting at PTR at given LENGTH.
96 This uses the same formula as specified for java.lang.String.hash. */
98 static jint
99 hashChars (jchar* ptr, jint length)
101 jchar* limit = ptr + length;
102 jint hash = 0;
103 // Updated specification from
104 // http://www.javasoft.com/docs/books/jls/clarify.html.
105 while (ptr < limit)
106 hash = (31 * hash) + *ptr++;
107 return hash;
110 jint
111 java::lang::String::hashCode()
113 if (cachedHashCode == 0)
114 cachedHashCode = hashChars(JvGetStringChars(this), length());
115 return cachedHashCode;
118 jstring*
119 _Jv_StringGetSlot (jstring str)
121 jchar* data = JvGetStringChars(str);
122 int length = str->length();
123 return _Jv_StringFindSlot(data, length, hashChars (data, length));
126 static void
127 rehash ()
129 JvSynchronize sync (&java::lang::String::class$);
131 if (strhash == NULL)
133 strhash_size = 1024;
134 strhash = (jstring *) _Jv_AllocBytes (strhash_size * sizeof (jstring));
136 else
138 int i = strhash_size;
139 jstring* ptr = strhash + i;
140 int nsize = strhash_size * 2;
141 jstring *next = (jstring *) _Jv_AllocBytes (nsize * sizeof (jstring));
143 while (--i >= 0)
145 --ptr;
146 if (*ptr == NULL || *ptr == DELETED_STRING)
147 continue;
149 /* This is faster equivalent of
150 * *__JvGetInternSlot(*ptr) = *ptr; */
151 jstring val = (jstring) UNMASK_PTR (*ptr);
152 jint hash = val->hashCode();
153 jint index = hash & (nsize - 1);
154 jint step = (hash ^ (hash >> 16)) | 1;
155 for (;;)
157 if (next[index] == NULL)
159 next[index] = *ptr;
160 break;
162 index = (index + step) & (nsize - 1);
166 strhash_size = nsize;
167 strhash = next;
171 jstring
172 java::lang::String::intern()
174 JvSynchronize sync (&java::lang::String::class$);
175 if (3 * strhash_count >= 2 * strhash_size)
176 rehash();
177 jstring* ptr = _Jv_StringGetSlot(this);
178 if (*ptr != NULL && *ptr != DELETED_STRING)
180 // See description in _Jv_FinalizeString() to understand this.
181 *ptr = (jstring) MASK_PTR (*ptr);
182 return (jstring) UNMASK_PTR (*ptr);
184 jstring str = (this->data == this
185 ? this
186 : _Jv_NewString(JvGetStringChars(this), this->length()));
187 SET_STRING_IS_INTERNED(str);
188 strhash_count++;
189 *ptr = str;
190 // When string is GC'd, clear the slot in the hash table.
191 _Jv_RegisterStringFinalizer (str);
192 return str;
195 // The fake String finalizer. This is only used when the String has
196 // been intern()d. However, we must check this case, as it might be
197 // called by the Reference code for any String.
198 void
199 _Jv_FinalizeString (jobject obj)
201 JvSynchronize sync (&java::lang::String::class$);
203 // We might not actually have intern()d any strings at all, if
204 // we're being called from Reference.
205 if (! strhash)
206 return;
208 jstring str = reinterpret_cast<jstring> (obj);
209 jstring *ptr = _Jv_StringGetSlot(str);
210 if (*ptr == NULL || *ptr == DELETED_STRING
211 || (jobject) UNMASK_PTR (*ptr) != obj)
212 return;
214 // We assume the lowest bit of the pointer is free for our nefarious
215 // manipulations. What we do is set it to `0' (implicitly) when
216 // interning the String. If we subsequently re-intern the same
217 // String, then we set the bit. When finalizing, if the bit is set
218 // then we clear it and re-register the finalizer. We know this is
219 // a safe approach because both intern() and _Jv_FinalizeString()
220 // acquire the class lock; this bit can't be manipulated when the
221 // lock is not held. So if we are finalizing and the bit is clear
222 // then we know all references are gone and we can clear the entry
223 // in the hash table. The naive approach of simply clearing the
224 // pointer here fails in the case where a request to intern a new
225 // string with the same contents is made between the time the
226 // intern()d string is found to be unreachable and when the
227 // finalizer is actually run. In this case we could clear a pointer
228 // to a valid string, and future intern() calls for that particular
229 // value would spuriously fail.
230 if (PTR_MASKED (*ptr))
232 *ptr = (jstring) UNMASK_PTR (*ptr);
233 _Jv_RegisterStringFinalizer (obj);
235 else
237 *ptr = DELETED_STRING;
238 strhash_count--;
242 jstring
243 _Jv_NewStringUTF (const char *bytes)
245 int size = strlen (bytes);
246 unsigned char *p = (unsigned char *) bytes;
248 int length = _Jv_strLengthUtf8 ((char *) p, size);
249 if (length < 0)
250 return NULL;
252 jstring jstr = JvAllocString (length);
253 jchar *chrs = JvGetStringChars (jstr);
255 p = (unsigned char *) bytes;
256 unsigned char *limit = p + size;
257 while (p < limit)
258 *chrs++ = UTF8_GET (p, limit);
260 return jstr;
263 jstring
264 _Jv_NewStringUtf8Const (Utf8Const* str)
266 jchar *chrs;
267 jchar buffer[100];
268 jstring jstr;
269 unsigned char* data = (unsigned char*) str->data;
270 unsigned char* limit = data + str->length;
271 int length = _Jv_strLengthUtf8(str->data, str->length);
273 if (length <= (int) (sizeof(buffer) / sizeof(jchar)))
275 jstr = NULL;
276 chrs = buffer;
278 else
280 jstr = JvAllocString(length);
281 chrs = JvGetStringChars(jstr);
284 jint hash = 0;
285 while (data < limit)
287 jchar ch = UTF8_GET(data, limit);
288 hash = (31 * hash) + ch;
289 *chrs++ = ch;
291 chrs -= length;
293 JvSynchronize sync (&java::lang::String::class$);
294 if (3 * strhash_count >= 2 * strhash_size)
295 rehash();
296 jstring* ptr = _Jv_StringFindSlot (chrs, length, hash);
297 if (*ptr != NULL && *ptr != DELETED_STRING)
298 return (jstring) UNMASK_PTR (*ptr);
299 strhash_count++;
300 if (jstr == NULL)
302 jstr = JvAllocString(length);
303 chrs = JvGetStringChars(jstr);
304 memcpy (chrs, buffer, sizeof(jchar)*length);
306 jstr->cachedHashCode = hash;
307 *ptr = jstr;
308 SET_STRING_IS_INTERNED(jstr);
309 // When string is GC'd, clear the slot in the hash table. Note that
310 // we don't have to call _Jv_RegisterStringFinalizer here, as we
311 // know the new object cannot be referred to by a Reference.
312 _Jv_RegisterFinalizer ((void *) jstr, _Jv_FinalizeString);
313 return jstr;
316 jsize
317 _Jv_GetStringUTFLength (jstring string)
319 jsize len = 0;
320 jchar *ptr = JvGetStringChars (string);
321 jsize i = string->length();
322 while (--i >= 0)
324 jchar ch = *ptr++;
325 if (ch > 0 && ch <= 0x7F)
326 len += 1;
327 else if (ch <= 0x7FF)
328 len += 2;
329 else
330 len += 3;
332 return len;
335 // Not sure this quite matches GetStringUTFRegion.
336 // null-termination of result? len? throw exception?
337 jsize
338 _Jv_GetStringUTFRegion (jstring str, jsize start, jsize len, char *buf)
340 jchar *sptr = JvGetStringChars (str) + start;
341 jsize i = len;
342 char *dptr = buf;
343 while (--i >= 0)
345 jchar ch = *sptr++;
346 if (ch > 0 && ch <= 0x7F)
347 *dptr++ = (char) ch;
348 else if (ch <= 0x7FF)
350 *dptr++ = (char) (0xC0 + ((ch >> 6) & 0x1F));
351 *dptr++ = (char) (0x80 + (ch & 0x3F));
353 else
355 *dptr++ = (char) (0xE0 + ((ch >> 12) & 0xF));
356 *dptr++ = (char) (0x80 + ((ch >> 6) & 0x3F));
357 *dptr++ = (char) (0x80 + (ch & 0x3F));
360 return dptr - buf;
363 /* Put printed (decimal) representation of NUM in a buffer.
364 BUFEND marks the end of the buffer, which must be at least 11 jchars long.
365 Returns the COUNT of jchars written. The result is in
366 (BUFEND - COUNT) (inclusive) upto (BUFEND) (exclusive). */
368 jint
369 _Jv_FormatInt (jchar* bufend, jint num)
371 register jchar* ptr = bufend;
372 jboolean isNeg;
373 if (num < 0)
375 isNeg = true;
376 if (num != (jint) -2147483648U)
377 num = -(num);
378 else
380 // Handle special case of MIN_VALUE.
381 *--ptr = '8';
382 num = 214748364;
385 else
386 isNeg = false;
390 *--ptr = (jchar) ((int) '0' + (num % 10));
391 num /= 10;
393 while (num > 0);
395 if (isNeg)
396 *--ptr = '-';
397 return bufend - ptr;
400 jstring
401 java::lang::String::valueOf (jint num)
403 // Use an array large enough for "-2147483648"; i.e. 11 chars.
404 jchar buffer[11];
405 int i = _Jv_FormatInt (buffer+11, num);
406 return _Jv_NewString (buffer+11-i, i);
409 jstring
410 _Jv_NewString(const jchar *chars, jsize len)
412 jstring str = _Jv_AllocString(len);
413 jchar* data = JvGetStringChars (str);
414 memcpy (data, chars, len * sizeof (jchar));
415 return str;
418 jstring
419 _Jv_NewStringLatin1(const char *bytes, jsize len)
421 jstring str = JvAllocString(len);
422 jchar* data = JvGetStringChars (str);
423 while (--len >= 0)
424 *data++ = *(unsigned char*)bytes++;
425 return str;
428 void
429 java::lang::String::init(jcharArray chars, jint offset, jint count,
430 jboolean dont_copy)
432 if (! chars)
433 throw new NullPointerException;
434 jsize data_size = JvGetArrayLength (chars);
435 if (offset < 0 || count < 0 || offset + count < 0
436 || offset + count > data_size)
437 throw new ArrayIndexOutOfBoundsException;
438 jcharArray array;
439 jchar *pdst;
440 if (! dont_copy)
442 array = JvNewCharArray(count);
443 pdst = elements (array);
444 memcpy (pdst, elements (chars) + offset, count * sizeof (jchar));
446 else
448 array = chars;
449 pdst = &(elements(array)[offset]);
452 data = array;
453 boffset = (char *) pdst - (char *) array;
454 this->count = count;
457 void
458 java::lang::String::init(jbyteArray ascii, jint hibyte, jint offset,
459 jint count)
461 if (! ascii)
462 throw new NullPointerException;
463 jsize data_size = JvGetArrayLength (ascii);
464 if (offset < 0 || count < 0 || offset + count < 0
465 || offset + count > data_size)
466 throw new ArrayIndexOutOfBoundsException;
467 jcharArray array = JvNewCharArray(count);
468 jbyte *psrc = elements (ascii) + offset;
469 jchar *pdst = elements (array);
470 data = array;
471 boffset = (char *) pdst - (char *) array;
472 this->count = count;
473 hibyte = (hibyte & 0xff) << 8;
474 while (-- count >= 0)
476 *pdst++ = hibyte | (*psrc++ & 0xff);
480 void
481 java::lang::String::init (jbyteArray bytes, jint offset, jint count,
482 jstring encoding)
484 if (! bytes)
485 throw new NullPointerException;
486 jsize data_size = JvGetArrayLength (bytes);
487 if (offset < 0 || count < 0 || offset + count < 0
488 || offset + count > data_size)
489 throw new ArrayIndexOutOfBoundsException;
490 jcharArray array = JvNewCharArray (count);
491 gnu::gcj::convert::BytesToUnicode *converter
492 = gnu::gcj::convert::BytesToUnicode::getDecoder(encoding);
493 jint outpos = 0;
494 int avail = count;
495 converter->setInput(bytes, offset, offset+count);
496 while (converter->inpos < converter->inlength)
498 int done;
501 done = converter->read(array, outpos, avail);
503 catch (::java::io::CharConversionException *e)
505 // Ignore it and silently throw away the offending data.
506 break;
508 if (done == 0)
510 // done is zero if either there is no space available in the
511 // output *or* the input is incomplete. We assume that if
512 // there are 20 characters available in the output, the
513 // input must be incomplete and there is no more work to do.
514 // This means we may skip several bytes of input, but that
515 // is OK as the behavior is explicitly unspecified in this
516 // case.
517 if (avail - outpos > 20)
518 break;
520 jint new_size = 2 * (outpos + avail);
521 jcharArray new_array = JvNewCharArray (new_size);
522 memcpy (elements (new_array), elements (array),
523 outpos * sizeof(jchar));
524 array = new_array;
525 avail = new_size - outpos;
527 else
529 outpos += done;
530 avail -= done;
533 converter->done ();
534 this->data = array;
535 this->boffset = (char *) elements (array) - (char *) array;
536 this->count = outpos;
539 void
540 java::lang::String::init (gnu::gcj::runtime::StringBuffer *buffer)
542 init (buffer->value, 0, buffer->count, true);
545 jboolean
546 java::lang::String::equals(jobject anObject)
548 if (anObject == NULL)
549 return false;
550 if (anObject == this)
551 return true;
552 if (anObject->getClass() != &java::lang::String::class$)
553 return false;
554 jstring other = (jstring) anObject;
555 if (count != other->count)
556 return false;
558 // If both have cached hash codes, check that. If the cached hash
559 // codes are zero, don't bother trying to compute them.
560 int myHash = cachedHashCode;
561 int otherHash = other->cachedHashCode;
562 if (myHash && otherHash && myHash != otherHash)
563 return false;
565 // We could see if both are interned, and return false. But that
566 // seems too expensive.
568 jchar *xptr = JvGetStringChars (this);
569 jchar *yptr = JvGetStringChars (other);
570 return ! memcmp (xptr, yptr, count * sizeof (jchar));
573 jboolean
574 java::lang::String::contentEquals(java::lang::StringBuffer* buffer)
576 if (buffer == NULL)
577 throw new NullPointerException;
578 JvSynchronize sync(buffer);
579 if (count != buffer->count)
580 return false;
581 if (data == buffer->value)
582 return true; // Possible if shared.
583 jchar *xptr = JvGetStringChars(this);
584 jchar *yptr = elements(buffer->value);
585 return ! memcmp (xptr, yptr, count * sizeof (jchar));
588 jboolean
589 java::lang::String::contentEquals(java::lang::CharSequence *seq)
591 if (seq->length() != count)
592 return false;
593 jchar *value = JvGetStringChars(this);
594 for (int i = 0; i < count; ++i)
595 if (value[i] != seq->charAt(i))
596 return false;
597 return true;
600 jchar
601 java::lang::String::charAt(jint i)
603 if (i < 0 || i >= count)
604 throw new java::lang::StringIndexOutOfBoundsException(i);
605 return JvGetStringChars(this)[i];
608 void
609 java::lang::String::getChars(jint srcBegin, jint srcEnd,
610 jcharArray dst, jint dstBegin)
612 jint dst_length = JvGetArrayLength (dst);
613 if (srcBegin < 0 || srcBegin > srcEnd || srcEnd > count)
614 throw new java::lang::StringIndexOutOfBoundsException;
615 // The 2nd part of the test below is equivalent to
616 // dstBegin + (srcEnd-srcBegin) > dst_length
617 // except that it does not overflow.
618 if (dstBegin < 0 || dstBegin > dst_length - (srcEnd-srcBegin))
619 throw new ArrayIndexOutOfBoundsException;
620 jchar *dPtr = elements (dst) + dstBegin;
621 jchar *sPtr = JvGetStringChars (this) + srcBegin;
622 jint i = srcEnd - srcBegin;
623 memcpy (dPtr, sPtr, i * sizeof (jchar));
626 jbyteArray
627 java::lang::String::getBytes (jstring enc)
629 jint todo = length();
630 jint buflen = todo;
631 jbyteArray buffer = JvNewByteArray(todo);
632 jint bufpos = 0;
633 jint offset = 0;
634 gnu::gcj::convert::UnicodeToBytes *converter
635 = gnu::gcj::convert::UnicodeToBytes::getEncoder(enc);
636 while (todo > 0 || converter->havePendingBytes())
638 converter->setOutput(buffer, bufpos);
639 int converted = converter->write(this, offset, todo, NULL);
640 bufpos = converter->count;
641 if (converted == 0)
643 buflen *= 2;
644 jbyteArray newbuffer = JvNewByteArray(buflen);
645 memcpy (elements (newbuffer), elements (buffer), bufpos);
646 buffer = newbuffer;
648 else
650 offset += converted;
651 todo -= converted;
654 if (length() > 0)
656 converter->setFinished();
657 converter->write(this, 0, 0, NULL);
659 converter->done ();
660 if (bufpos == buflen)
661 return buffer;
662 jbyteArray result = JvNewByteArray(bufpos);
663 memcpy (elements (result), elements (buffer), bufpos);
664 return result;
667 void
668 java::lang::String::getBytes(jint srcBegin, jint srcEnd,
669 jbyteArray dst, jint dstBegin)
671 jint dst_length = JvGetArrayLength (dst);
672 if (srcBegin < 0 || srcBegin > srcEnd || srcEnd > count)
673 throw new java::lang::StringIndexOutOfBoundsException;
674 // The 2nd part of the test below is equivalent to
675 // dstBegin + (srcEnd-srcBegin) > dst_length
676 // except that it does not overflow.
677 if (dstBegin < 0 || dstBegin > dst_length - (srcEnd-srcBegin))
678 throw new ArrayIndexOutOfBoundsException;
679 jbyte *dPtr = elements (dst) + dstBegin;
680 jchar *sPtr = JvGetStringChars (this) + srcBegin;
681 jint i = srcEnd-srcBegin;
682 while (--i >= 0)
683 *dPtr++ = (jbyte) *sPtr++;
686 jcharArray
687 java::lang::String::toCharArray()
689 jcharArray array = JvNewCharArray(count);
690 jchar *dPtr = elements (array);
691 jchar *sPtr = JvGetStringChars (this);
692 jint i = count;
693 memcpy (dPtr, sPtr, i * sizeof (jchar));
694 return array;
697 jboolean
698 java::lang::String::equalsIgnoreCase (jstring anotherString)
700 if (anotherString == NULL || count != anotherString->count)
701 return false;
702 jchar *tptr = JvGetStringChars (this);
703 jchar *optr = JvGetStringChars (anotherString);
704 jint i = count;
705 while (--i >= 0)
707 jchar tch = *tptr++;
708 jchar och = *optr++;
709 if (tch != och
710 && (java::lang::Character::toLowerCase (tch)
711 != java::lang::Character::toLowerCase (och))
712 && (java::lang::Character::toUpperCase (tch)
713 != java::lang::Character::toUpperCase (och)))
714 return false;
716 return true;
719 jboolean
720 java::lang::String::regionMatches (jint toffset,
721 jstring other, jint ooffset, jint len)
723 if (toffset < 0 || ooffset < 0 || len < 0
724 || toffset > count - len
725 || ooffset > other->count - len)
726 return false;
727 jchar *tptr = JvGetStringChars (this) + toffset;
728 jchar *optr = JvGetStringChars (other) + ooffset;
729 jint i = len;
730 return ! memcmp (tptr, optr, i * sizeof (jchar));
733 jint
734 java::lang::String::nativeCompareTo (jstring anotherString)
736 jchar *tptr = JvGetStringChars (this);
737 jchar *optr = JvGetStringChars (anotherString);
738 jint tlen = this->count;
739 jint olen = anotherString->count;
740 jint i = tlen > olen ? olen : tlen;
741 while (--i >= 0)
743 jchar tch = *tptr++;
744 jchar och = *optr++;
745 if (tch != och)
746 return (jint) tch - (jint) och;
748 return tlen - olen;
751 jboolean
752 java::lang::String::regionMatches (jboolean ignoreCase, jint toffset,
753 jstring other, jint ooffset, jint len)
755 if (toffset < 0 || ooffset < 0 || len < 0
756 || toffset > count - len
757 || ooffset > other->count - len)
758 return false;
759 jchar *tptr = JvGetStringChars (this) + toffset;
760 jchar *optr = JvGetStringChars (other) + ooffset;
761 jint i = len;
762 if (ignoreCase)
764 while (--i >= 0)
766 jchar tch = *tptr++;
767 jchar och = *optr++;
768 if ((java::lang::Character::toLowerCase (tch)
769 != java::lang::Character::toLowerCase (och))
770 && (java::lang::Character::toUpperCase (tch)
771 != java::lang::Character::toUpperCase (och)))
772 return false;
774 return true;
776 return ! memcmp (tptr, optr, i * sizeof (jchar));
779 jboolean
780 java::lang::String::startsWith (jstring prefix, jint toffset)
782 jint i = prefix->count;
783 if (toffset < 0 || toffset > count - i)
784 return false;
785 jchar *xptr = JvGetStringChars (this) + toffset;
786 jchar *yptr = JvGetStringChars (prefix);
787 return ! memcmp (xptr, yptr, i * sizeof (jchar));
790 jint
791 java::lang::String::indexOf (jint ch, jint fromIndex)
793 if (fromIndex < 0)
794 fromIndex = 0;
795 jchar *ptr = JvGetStringChars(this);
796 for (;; ++fromIndex)
798 if (fromIndex >= count)
799 return -1;
800 if (ptr[fromIndex] == ch)
801 return fromIndex;
805 jint
806 java::lang::String::indexOf (jstring s, jint fromIndex)
808 const jchar *const xchars = JvGetStringChars(s);
809 const jchar *const ychars = JvGetStringChars(this) + fromIndex;
811 const int xlength = s->length ();
812 const int ylength = length () - fromIndex;
814 int i = 0;
815 int j = 0;
817 while (i < ylength && j < xlength)
819 if (xchars[j] != ychars[i])
821 i = i - j + 1;
822 j = 0;
824 else
825 i++, j++;
828 if (j >= xlength)
829 return fromIndex + i - xlength;
830 else
831 return -1;
834 jint
835 java::lang::String::lastIndexOf (jint ch, jint fromIndex)
837 if (fromIndex >= count)
838 fromIndex = count - 1;
839 jchar *ptr = JvGetStringChars(this);
840 for (;; --fromIndex)
842 if (fromIndex < 0)
843 return -1;
844 if (ptr[fromIndex] == ch)
845 return fromIndex;
849 jstring
850 java::lang::String::substring (jint beginIndex, jint endIndex)
852 if (beginIndex < 0 || endIndex > count || beginIndex > endIndex)
853 throw new StringIndexOutOfBoundsException;
854 if (beginIndex == 0 && endIndex == count)
855 return this;
856 jint newCount = endIndex - beginIndex;
857 // For very small strings, just allocate a new one. For other
858 // substrings, allocate a new one unless the substring is over half
859 // of the original string.
860 if (newCount <= 8 || newCount < (count >> 1))
861 return JvNewString(JvGetStringChars(this) + beginIndex, newCount);
862 jstring s = new String();
863 s->data = data;
864 s->count = newCount;
865 s->boffset = boffset + sizeof(jchar) * beginIndex;
866 return s;
869 jstring
870 java::lang::String::concat(jstring str)
872 jint str_count = str->count;
873 if (str_count == 0)
874 return this;
875 jstring result = JvAllocString(count + str_count);
876 jchar *dstPtr = JvGetStringChars(result);
877 jchar *srcPtr = JvGetStringChars(this);
878 jint i = count;
879 memcpy (dstPtr, srcPtr, i * sizeof (jchar));
880 dstPtr += i;
881 srcPtr = JvGetStringChars(str);
882 i = str->count;
883 memcpy (dstPtr, srcPtr, i * sizeof (jchar));
884 return result;
887 jstring
888 java::lang::String::replace (jchar oldChar, jchar newChar)
890 jint i;
891 jchar* chrs = JvGetStringChars (this);
892 for (i = 0; ; i++)
894 if (i == count)
895 return this;
896 if (chrs[i] == oldChar)
897 break;
899 jstring result = JvAllocString (count);
900 jchar *dPtr = JvGetStringChars (result);
901 for (int j = 0; j < i; j++)
902 *dPtr++ = chrs[j];
903 for (; i < count; i++)
905 jchar ch = chrs[i];
906 if (ch == oldChar)
907 ch = newChar;
908 *dPtr++ = ch;
910 return result;
913 jstring
914 java::lang::String::toLowerCase (java::util::Locale *locale)
916 jint i;
917 jchar* chrs = JvGetStringChars(this);
918 jchar ch = 0;
920 bool handle_tr = false;
921 if (locale != NULL)
923 String *lang = locale->getLanguage ();
924 if (lang->length () == 2
925 && lang->charAt (0) == 't'
926 && lang->charAt (1) == 'r')
927 handle_tr = true;
930 for (i = 0; ; i++)
932 if (i == count)
933 return this;
934 jchar origChar = chrs[i];
936 if (handle_tr && (origChar == CAPITAL_I
937 || origChar == CAPITAL_I_WITH_DOT))
938 break;
940 ch = java::lang::Character::toLowerCase(origChar);
941 if (ch != origChar)
942 break;
944 jstring result = JvAllocString(count);
945 jchar *dPtr = JvGetStringChars (result);
946 for (int j = 0; j < i; j++)
947 *dPtr++ = chrs[j];
948 *dPtr++ = ch; i++;
949 for (; i < count; i++)
951 if (handle_tr && chrs[i] == CAPITAL_I)
952 *dPtr++ = SMALL_DOTLESS_I;
953 else if (handle_tr && chrs[i] == CAPITAL_I_WITH_DOT)
954 *dPtr++ = SMALL_I;
955 else
956 *dPtr++ = java::lang::Character::toLowerCase(chrs[i]);
958 return result;
961 jstring
962 java::lang::String::toUpperCase (java::util::Locale *locale)
964 jint i;
965 jchar* chrs = JvGetStringChars(this);
966 jchar ch;
968 // When handling a specific locale there might be special rules.
969 // Currently all existing rules are simply handled inline, as there
970 // are only two and they are documented in the online 1.2 docs.
971 bool handle_esset = locale != NULL;
972 bool handle_tr = false;
973 if (locale != NULL)
975 String *lang = locale->getLanguage ();
976 if (lang->length () == 2
977 && lang->charAt (0) == 't'
978 && lang->charAt (1) == 'r')
979 handle_tr = true;
982 int new_count = count;
983 bool new_string = false;
984 for (i = 0; ; i++)
986 if (i == count)
987 break;
988 jchar origChar = chrs[i];
990 if (handle_esset && origChar == ESSET)
992 ++new_count;
993 new_string = true;
995 else if (handle_tr && (origChar == SMALL_I
996 || origChar == SMALL_DOTLESS_I))
997 new_string = true;
998 else
1000 ch = java::lang::Character::toUpperCase(origChar);
1001 if (ch != origChar)
1002 new_string = true;
1005 if (new_string && ! handle_esset)
1006 break;
1008 if (! new_string)
1009 return this;
1010 jstring result = JvAllocString(new_count);
1011 jchar *dPtr = JvGetStringChars (result);
1012 for (i = 0; i < count; i++)
1014 if (handle_esset && chrs[i] == ESSET)
1016 *dPtr++ = CAPITAL_S;
1017 *dPtr++ = CAPITAL_S;
1019 else if (handle_tr && chrs[i] == SMALL_I)
1020 *dPtr++ = CAPITAL_I_WITH_DOT;
1021 else if (handle_tr && chrs[i] == SMALL_DOTLESS_I)
1022 *dPtr++ = CAPITAL_I;
1023 else
1024 *dPtr++ = java::lang::Character::toUpperCase(chrs[i]);
1026 return result;
1029 jstring
1030 java::lang::String::trim ()
1032 jchar* chrs = JvGetStringChars(this);
1033 if (count == 0 || (chrs[0] > ' ' && chrs[count-1] > ' '))
1034 return this;
1035 jint preTrim = 0;
1036 for (;; preTrim++)
1038 if (preTrim == count)
1039 return new String();
1040 if (chrs[preTrim] > ' ')
1041 break;
1043 jint endTrim = count;
1044 while (chrs[endTrim-1] <= ' ')
1045 endTrim--;
1046 return substring(preTrim, endTrim);
1049 jstring
1050 java::lang::String::valueOf(jcharArray data, jint offset, jint count)
1052 jint data_length = JvGetArrayLength (data);
1053 if (offset < 0 || count < 0 || offset > data_length - count)
1054 throw new ArrayIndexOutOfBoundsException;
1055 jstring result = JvAllocString(count);
1056 jchar *sPtr = elements (data) + offset;
1057 jchar *dPtr = JvGetStringChars(result);
1058 memcpy (dPtr, sPtr, count * sizeof (jchar));
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;