source: trunk/gcc/libjava/java/lang/natString.cc

Last change on this file was 1392, checked in by bird, 21 years ago

This commit was generated by cvs2svn to compensate for changes in r1391,
which included commits to RCS files with non-trunk default branches.

  • Property cvs2svn:cvs-rev set to 1.1.1.2
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 25.4 KB
Line 
1// natString.cc - Implementation of java.lang.String native methods.
2
3/* Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation
4
5 This file is part of libgcj.
6
7This software is copyrighted work licensed under the terms of the
8Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
9details. */
10
11#include <config.h>
12
13#include <string.h>
14#include <stdlib.h>
15
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/io/ByteArrayOutputStream.h>
24#include <java/io/OutputStreamWriter.h>
25#include <java/io/ByteArrayInputStream.h>
26#include <java/io/InputStreamReader.h>
27#include <java/util/Locale.h>
28#include <gnu/gcj/convert/UnicodeToBytes.h>
29#include <gnu/gcj/convert/BytesToUnicode.h>
30#include <jvm.h>
31
32static void unintern (jobject);
33static jstring* strhash = NULL;
34static int strhash_count = 0; /* Number of slots used in strhash. */
35static int strhash_size = 0; /* Number of slots available in strhash.
36 * Assumed be power of 2! */
37
38// Some defines used by toUpperCase / toLowerCase.
39#define ESSET 0x00df
40#define CAPITAL_S 0x0053
41#define SMALL_I 0x0069
42#define CAPITAL_I_WITH_DOT 0x0130
43#define SMALL_DOTLESS_I 0x0131
44#define CAPITAL_I 0x0049
45
46#define DELETED_STRING ((jstring)(~0))
47#define SET_STRING_IS_INTERNED(STR) /* nothing */
48
49#define UNMASK_PTR(Ptr) (((unsigned long) (Ptr)) & ~0x01)
50#define MASK_PTR(Ptr) (((unsigned long) (Ptr)) | 0x01)
51#define PTR_MASKED(Ptr) (((unsigned long) (Ptr)) & 0x01)
52
53/* Find a slot where the string with elements DATA, length LEN,
54 and hash HASH should go in the strhash table of interned strings. */
55jstring*
56_Jv_StringFindSlot (jchar* data, jint len, jint hash)
57{
58 JvSynchronize sync (&StringClass);
59
60 int start_index = hash & (strhash_size - 1);
61 int deleted_index = -1;
62
63 int index = start_index;
64 /* step must be non-zero, and relatively prime with strhash_size. */
65 jint step = (hash ^ (hash >> 16)) | 1;
66 for (;;)
67 {
68 jstring* ptr = &strhash[index];
69 jstring value = (jstring) UNMASK_PTR (*ptr);
70 if (value == NULL)
71 {
72 if (deleted_index >= 0)
73 return (&strhash[deleted_index]);
74 else
75 return ptr;
76 }
77 else if (*ptr == DELETED_STRING)
78 deleted_index = index;
79 else if (value->length() == len
80 && memcmp(JvGetStringChars(value), data, 2*len) == 0)
81 return (ptr);
82 index = (index + step) & (strhash_size - 1);
83 JvAssert (index != start_index);
84 }
85}
86
87/* Calculate a hash code for the string starting at PTR at given LENGTH.
88 This uses the same formula as specified for java.lang.String.hash. */
89
90static jint
91hashChars (jchar* ptr, jint length)
92{
93 jchar* limit = ptr + length;
94 jint hash = 0;
95 // Updated specification from
96 // http://www.javasoft.com/docs/books/jls/clarify.html.
97 while (ptr < limit)
98 hash = (31 * hash) + *ptr++;
99 return hash;
100}
101
102jint
103java::lang::String::hashCode()
104{
105 return hashChars(JvGetStringChars(this), length());
106}
107
108jstring*
109_Jv_StringGetSlot (jstring str)
110{
111 jchar* data = JvGetStringChars(str);
112 int length = str->length();
113 return _Jv_StringFindSlot(data, length, hashChars (data, length));
114}
115
116void
117java::lang::String::rehash()
118{
119 JvSynchronize sync (&StringClass);
120
121 if (strhash == NULL)
122 {
123 strhash_size = 1024;
124 strhash = (jstring *) _Jv_AllocBytes (strhash_size * sizeof (jstring));
125 memset (strhash, 0, strhash_size * sizeof (jstring));
126 }
127 else
128 {
129 int i = strhash_size;
130 jstring* ptr = strhash + i;
131 int nsize = strhash_size * 2;
132 jstring *next = (jstring *) _Jv_AllocBytes (nsize * sizeof (jstring));
133 memset (next, 0, nsize * sizeof (jstring));
134
135 while (--i >= 0)
136 {
137 --ptr;
138 if (*ptr == NULL || *ptr == DELETED_STRING)
139 continue;
140
141 /* This is faster equivalent of
142 * *__JvGetInternSlot(*ptr) = *ptr; */
143 jstring val = (jstring) UNMASK_PTR (*ptr);
144 jint hash = val->hashCode();
145 jint index = hash & (nsize - 1);
146 jint step = (hash ^ (hash >> 16)) | 1;
147 for (;;)
148 {
149 if (next[index] == NULL)
150 {
151 next[index] = *ptr;
152 break;
153 }
154 index = (index + step) & (nsize - 1);
155 }
156 }
157
158 strhash_size = nsize;
159 strhash = next;
160 }
161}
162
163jstring
164java::lang::String::intern()
165{
166 JvSynchronize sync (&StringClass);
167 if (3 * strhash_count >= 2 * strhash_size)
168 rehash();
169 jstring* ptr = _Jv_StringGetSlot(this);
170 if (*ptr != NULL && *ptr != DELETED_STRING)
171 {
172 // See description in unintern() to understand this.
173 *ptr = (jstring) MASK_PTR (*ptr);
174 return (jstring) UNMASK_PTR (*ptr);
175 }
176 jstring str = this->data == this ? this
177 : _Jv_NewString(JvGetStringChars(this), this->length());
178 SET_STRING_IS_INTERNED(str);
179 strhash_count++;
180 *ptr = str;
181 // When string is GC'd, clear the slot in the hash table.
182 _Jv_RegisterFinalizer ((void *) str, unintern);
183 return str;
184}
185
186/* Called by String fake finalizer. */
187static void
188unintern (jobject obj)
189{
190 JvSynchronize sync (&StringClass);
191 jstring str = reinterpret_cast<jstring> (obj);
192 jstring* ptr = _Jv_StringGetSlot(str);
193 if (*ptr == NULL || *ptr == DELETED_STRING)
194 return;
195
196 // We assume the lowest bit of the pointer is free for our nefarious
197 // manipulations. What we do is set it to `0' (implicitly) when
198 // interning the String. If we subsequently re-intern the same
199 // String, then we set the bit. When finalizing, if the bit is set
200 // then we clear it and re-register the finalizer. We know this is
201 // a safe approach because both intern() and unintern() acquire
202 // the class lock; this bit can't be manipulated when the lock is
203 // not held. So if we are finalizing and the bit is clear then we
204 // know all references are gone and we can clear the entry in the
205 // hash table. The naive approach of simply clearing the pointer
206 // here fails in the case where a request to intern a new string
207 // with the same contents is made between the time the intern()d
208 // string is found to be unreachable and when the finalizer is
209 // actually run. In this case we could clear a pointer to a valid
210 // string, and future intern() calls for that particular value would
211 // spuriously fail.
212 if (PTR_MASKED (*ptr))
213 {
214 *ptr = (jstring) UNMASK_PTR (*ptr);
215 _Jv_RegisterFinalizer ((void *) obj, unintern);
216 }
217 else
218 {
219 *ptr = DELETED_STRING;
220 strhash_count--;
221 }
222}
223
224jstring
225_Jv_NewStringUTF (const char *bytes)
226{
227 int size = strlen (bytes);
228 unsigned char *p = (unsigned char *) bytes;
229
230 int length = _Jv_strLengthUtf8 ((char *) p, size);
231 if (length < 0)
232 return NULL;
233
234 jstring jstr = JvAllocString (length);
235 jchar *chrs = JvGetStringChars (jstr);
236
237 p = (unsigned char *) bytes;
238 unsigned char *limit = p + size;
239 while (p < limit)
240 *chrs++ = UTF8_GET (p, limit);
241
242 return jstr;
243}
244
245jstring
246_Jv_NewStringUtf8Const (Utf8Const* str)
247{
248 jchar *chrs;
249 jchar buffer[100];
250 jstring jstr;
251 unsigned char* data = (unsigned char*) str->data;
252 unsigned char* limit = data + str->length;
253 int length = _Jv_strLengthUtf8(str->data, str->length);
254
255 if (length <= (int) (sizeof(buffer) / sizeof(jchar)))
256 {
257 jstr = NULL;
258 chrs = buffer;
259 }
260 else
261 {
262 jstr = JvAllocString(length);
263 chrs = JvGetStringChars(jstr);
264 }
265
266 jint hash = 0;
267 while (data < limit)
268 {
269 jchar ch = UTF8_GET(data, limit);
270 hash = (31 * hash) + ch;
271 *chrs++ = ch;
272 }
273 chrs -= length;
274
275 JvSynchronize sync (&StringClass);
276 if (3 * strhash_count >= 2 * strhash_size)
277 java::lang::String::rehash();
278 jstring* ptr = _Jv_StringFindSlot (chrs, length, hash);
279 if (*ptr != NULL && *ptr != DELETED_STRING)
280 return (jstring) UNMASK_PTR (*ptr);
281 strhash_count++;
282 if (jstr == NULL)
283 {
284 jstr = JvAllocString(length);
285 chrs = JvGetStringChars(jstr);
286 memcpy (chrs, buffer, sizeof(jchar)*length);
287 }
288 *ptr = jstr;
289 SET_STRING_IS_INTERNED(jstr);
290 // When string is GC'd, clear the slot in the hash table.
291 _Jv_RegisterFinalizer ((void *) jstr, unintern);
292 return jstr;
293}
294
295jsize
296_Jv_GetStringUTFLength (jstring string)
297{
298 jsize len = 0;
299 jchar *ptr = JvGetStringChars (string);
300 jsize i = string->length();
301 while (--i >= 0)
302 {
303 jchar ch = *ptr++;
304 if (ch > 0 && ch <= 0x7F)
305 len += 1;
306 else if (ch <= 0x7FF)
307 len += 2;
308 else
309 len += 3;
310 }
311 return len;
312}
313
314// Not sure this quite matches GetStringUTFRegion.
315// null-termination of result? len? throw exception?
316jsize
317_Jv_GetStringUTFRegion (jstring str, jsize start, jsize len, char *buf)
318{
319 jchar *sptr = JvGetStringChars (str) + start;
320 jsize i = len;
321 char *dptr = buf;
322 while (--i >= 0)
323 {
324 jchar ch = *sptr++;
325 if (ch > 0 && ch <= 0x7F)
326 *dptr++ = (char) ch;
327 else if (ch <= 0x7FF)
328 {
329 *dptr++ = (char) (0xC0 + ((ch >> 6) & 0x1F));
330 *dptr++ = (char) (0x80 + (ch & 0x3F));
331 }
332 else
333 {
334 *dptr++ = (char) (0xE0 + ((ch >> 12) & 0xF));
335 *dptr++ = (char) (0x80 + ((ch >> 6) & 0x3F));
336 *dptr++ = (char) (0x80 + (ch & 0x3F));
337 }
338 }
339 return dptr - buf;
340}
341
342/* Put printed (decimal) representation of NUM in a buffer.
343 BUFEND marks the end of the buffer, which must be at least 11 jchars long.
344 Returns the COUNT of jchars written. The result is in
345 (BUFEND - COUNT) (inclusive) upto (BUFEND) (exclusive). */
346
347jint
348_Jv_FormatInt (jchar* bufend, jint num)
349{
350 register jchar* ptr = bufend;
351 jboolean isNeg;
352 if (num < 0)
353 {
354 isNeg = true;
355 num = -(num);
356 if (num < 0)
357 {
358 // Must be MIN_VALUE, so handle this special case.
359 // FIXME use 'unsigned jint' for num.
360 *--ptr = '8';
361 num = 214748364;
362 }
363 }
364 else
365 isNeg = false;
366
367 do
368 {
369 *--ptr = (jchar) ((int) '0' + (num % 10));
370 num /= 10;
371 }
372 while (num > 0);
373
374 if (isNeg)
375 *--ptr = '-';
376 return bufend - ptr;
377}
378
379jstring
380java::lang::String::valueOf (jint num)
381{
382 // Use an array large enough for "-2147483648"; i.e. 11 chars.
383 jchar buffer[11];
384 int i = _Jv_FormatInt (buffer+11, num);
385 return _Jv_NewString (buffer+11-i, i);
386}
387
388jstring
389_Jv_AllocString(jsize len)
390{
391 jsize sz = sizeof(java::lang::String) + len * sizeof(jchar);
392
393 // We assert that for strings allocated this way, the data field
394 // will always point to the object itself. Thus there is no reason
395 // for the garbage collector to scan any of it.
396 // Furthermore, we're about to overwrite the string data, so
397 // initialization of the object is not an issue.
398#ifdef ENABLE_JVMPI
399 jstring obj = (jstring) _Jv_AllocPtrFreeObject(&StringClass, sz);
400#else
401 // Class needs no initialization, and there is no finalizer, so
402 // we can go directly to the collector's allocator interface.
403 jstring obj = (jstring) _Jv_AllocPtrFreeObj(sz, &StringClass);
404#endif
405 obj->data = obj;
406 obj->boffset = sizeof(java::lang::String);
407 obj->count = len;
408 return obj;
409}
410
411jstring
412_Jv_NewString(const jchar *chars, jsize len)
413{
414 jstring str = _Jv_AllocString(len);
415 jchar* data = JvGetStringChars (str);
416 while (--len >= 0)
417 *data++ = *chars++;
418 return str;
419}
420
421jstring
422_Jv_NewStringLatin1(const char *bytes, jsize len)
423{
424 jstring str = JvAllocString(len);
425 jchar* data = JvGetStringChars (str);
426 while (--len >= 0)
427 *data++ = *(unsigned char*)bytes++;
428 return str;
429}
430
431void
432java::lang::String::init ()
433{
434 count = 0;
435 boffset = sizeof(java::lang::String);
436 data = this;
437}
438
439void
440java::lang::String::init(jcharArray chars, jint offset, jint count,
441 jboolean dont_copy)
442{
443 if (! chars)
444 throw new NullPointerException;
445 jsize data_size = JvGetArrayLength (chars);
446 if (offset < 0 || count < 0 || offset + count < 0
447 || offset + count > data_size)
448 throw new ArrayIndexOutOfBoundsException;
449 jcharArray array;
450 jchar *pdst;
451 if (! dont_copy)
452 {
453 array = JvNewCharArray(count);
454 pdst = elements (array);
455 memcpy (pdst, elements (chars) + offset, count * sizeof (jchar));
456 }
457 else
458 {
459 array = chars;
460 pdst = &(elements(array)[offset]);
461 }
462
463 data = array;
464 boffset = (char *) pdst - (char *) array;
465 this->count = count;
466}
467
468void
469java::lang::String::init(jbyteArray ascii, jint hibyte, jint offset,
470 jint count)
471{
472 if (! ascii)
473 throw new NullPointerException;
474 jsize data_size = JvGetArrayLength (ascii);
475 if (offset < 0 || count < 0 || offset + count < 0
476 || offset + count > data_size)
477 throw new ArrayIndexOutOfBoundsException;
478 jcharArray array = JvNewCharArray(count);
479 jbyte *psrc = elements (ascii) + offset;
480 jchar *pdst = elements (array);
481 data = array;
482 boffset = (char *) pdst - (char *) array;
483 this->count = count;
484 hibyte = (hibyte & 0xff) << 8;
485 while (-- count >= 0)
486 {
487 *pdst++ = hibyte | (*psrc++ & 0xff);
488 }
489}
490
491void
492java::lang::String::init (jbyteArray bytes, jint offset, jint count,
493 jstring encoding)
494{
495 if (! bytes)
496 throw new NullPointerException;
497 jsize data_size = JvGetArrayLength (bytes);
498 if (offset < 0 || count < 0 || offset + count < 0
499 || offset + count > data_size)
500 throw new ArrayIndexOutOfBoundsException;
501 jcharArray array = JvNewCharArray (count);
502 gnu::gcj::convert::BytesToUnicode *converter
503 = gnu::gcj::convert::BytesToUnicode::getDecoder(encoding);
504 jint outpos = 0;
505 int avail = count;
506 converter->setInput(bytes, offset, offset+count);
507 while (converter->inpos < converter->inlength)
508 {
509 int done = converter->read(array, outpos, avail);
510 if (done == 0)
511 {
512 jint new_size = 2 * (outpos + avail);
513 jcharArray new_array = JvNewCharArray (new_size);
514 memcpy (elements (new_array), elements (array),
515 outpos * sizeof(jchar));
516 array = new_array;
517 avail = new_size - outpos;
518 }
519 else
520 {
521 outpos += done;
522 avail -= done;
523 }
524 }
525 converter->done ();
526 this->data = array;
527 this->boffset = (char *) elements (array) - (char *) array;
528 this->count = outpos;
529}
530
531jboolean
532java::lang::String::equals(jobject anObject)
533{
534 if (anObject == NULL)
535 return false;
536 if (anObject == this)
537 return true;
538 if (anObject->getClass() != &StringClass)
539 return false;
540 jstring other = (jstring) anObject;
541 if (count != other->count)
542 return false;
543 /* if both are interned, return false. */
544 jint i = count;
545 jchar *xptr = JvGetStringChars (this);
546 jchar *yptr = JvGetStringChars (other);
547 while (--i >= 0)
548 {
549 if (*xptr++ != *yptr++)
550 return false;
551 }
552 return true;
553}
554
555jchar
556java::lang::String::charAt(jint i)
557{
558 if (i < 0 || i >= count)
559 throw new java::lang::StringIndexOutOfBoundsException;
560 return JvGetStringChars(this)[i];
561}
562
563void
564java::lang::String::getChars(jint srcBegin, jint srcEnd,
565 jcharArray dst, jint dstBegin)
566{
567 jint dst_length = JvGetArrayLength (dst);
568 if (srcBegin < 0 || srcBegin > srcEnd || srcEnd > count)
569 throw new java::lang::StringIndexOutOfBoundsException;
570 if (dstBegin < 0 || dstBegin + (srcEnd-srcBegin) > dst_length)
571 throw new ArrayIndexOutOfBoundsException;
572 jchar *dPtr = elements (dst) + dstBegin;
573 jchar *sPtr = JvGetStringChars (this) + srcBegin;
574 jint i = srcEnd-srcBegin;
575 while (--i >= 0)
576 *dPtr++ = *sPtr++;
577}
578
579jbyteArray
580java::lang::String::getBytes (jstring enc)
581{
582 jint todo = length();
583 jint buflen = todo;
584 jbyteArray buffer = JvNewByteArray(todo);
585 jint bufpos = 0;
586 jint offset = 0;
587 gnu::gcj::convert::UnicodeToBytes *converter
588 = gnu::gcj::convert::UnicodeToBytes::getEncoder(enc);
589 while (todo > 0 || converter->havePendingBytes())
590 {
591 converter->setOutput(buffer, bufpos);
592 int converted = converter->write(this, offset, todo, NULL);
593 bufpos = converter->count;
594 if (converted == 0 && bufpos == converter->count)
595 {
596 buflen *= 2;
597 jbyteArray newbuffer = JvNewByteArray(buflen);
598 memcpy (elements (newbuffer), elements (buffer), bufpos);
599 buffer = newbuffer;
600 }
601 else
602 bufpos = converter->count;
603
604 offset += converted;
605 todo -= converted;
606 }
607 converter->done ();
608 if (bufpos == buflen)
609 return buffer;
610 jbyteArray result = JvNewByteArray(bufpos);
611 memcpy (elements (result), elements (buffer), bufpos);
612 return result;
613}
614
615void
616java::lang::String::getBytes(jint srcBegin, jint srcEnd,
617 jbyteArray dst, jint dstBegin)
618{
619 jint dst_length = JvGetArrayLength (dst);
620 if (srcBegin < 0 || srcBegin > srcEnd || srcEnd > count)
621 throw new java::lang::StringIndexOutOfBoundsException;
622 if (dstBegin < 0 || dstBegin + (srcEnd-srcBegin) > dst_length)
623 throw new ArrayIndexOutOfBoundsException;
624 jbyte *dPtr = elements (dst) + dstBegin;
625 jchar *sPtr = JvGetStringChars (this) + srcBegin;
626 jint i = srcEnd-srcBegin;
627 while (--i >= 0)
628 *dPtr++ = (jbyte) *sPtr++;
629}
630
631jcharArray
632java::lang::String::toCharArray()
633{
634 jcharArray array = JvNewCharArray(count);
635 jchar *dPtr = elements (array);
636 jchar *sPtr = JvGetStringChars (this);
637 jint i = count;
638 while (--i >= 0)
639 *dPtr++ = *sPtr++;
640 return array;
641}
642
643jboolean
644java::lang::String::equalsIgnoreCase (jstring anotherString)
645{
646 if (anotherString == NULL || count != anotherString->count)
647 return false;
648 jchar *tptr = JvGetStringChars (this);
649 jchar *optr = JvGetStringChars (anotherString);
650 jint i = count;
651 while (--i >= 0)
652 {
653 jchar tch = *tptr++;
654 jchar och = *optr++;
655 if (tch != och
656 && (java::lang::Character::toLowerCase (tch)
657 != java::lang::Character::toLowerCase (och))
658 && (java::lang::Character::toUpperCase (tch)
659 != java::lang::Character::toUpperCase (och)))
660 return false;
661 }
662 return true;
663}
664
665jboolean
666java::lang::String::regionMatches (jint toffset,
667 jstring other, jint ooffset, jint len)
668{
669 if (toffset < 0 || ooffset < 0
670 || toffset + len > count
671 || ooffset + len > other->count)
672 return false;
673 jchar *tptr = JvGetStringChars (this) + toffset;
674 jchar *optr = JvGetStringChars (other) + ooffset;
675 jint i = len;
676 while (--i >= 0)
677 {
678 if (*tptr++ != *optr++)
679 return false;
680 }
681 return true;
682}
683
684jint
685java::lang::String::compareTo (jstring anotherString)
686{
687 jchar *tptr = JvGetStringChars (this);
688 jchar *optr = JvGetStringChars (anotherString);
689 jint tlen = this->count;
690 jint olen = anotherString->count;
691 jint i = tlen > olen ? olen : tlen;
692 while (--i >= 0)
693 {
694 jchar tch = *tptr++;
695 jchar och = *optr++;
696 if (tch != och)
697 return (jint) tch - (jint) och;
698 }
699 return tlen - olen;
700}
701
702jboolean
703java::lang::String::regionMatches (jboolean ignoreCase, jint toffset,
704 jstring other, jint ooffset, jint len)
705{
706 if (toffset < 0 || ooffset < 0
707 || toffset + len > count
708 || ooffset + len > other->count)
709 return false;
710 jchar *tptr = JvGetStringChars (this) + toffset;
711 jchar *optr = JvGetStringChars (other) + ooffset;
712 jint i = len;
713 if (ignoreCase)
714 while (--i >= 0)
715 {
716 jchar tch = *tptr++;
717 jchar och = *optr++;
718 if ((java::lang::Character::toLowerCase (tch)
719 != java::lang::Character::toLowerCase (och))
720 && (java::lang::Character::toUpperCase (tch)
721 != java::lang::Character::toUpperCase (och)))
722 return false;
723 }
724 else
725 while (--i >= 0)
726 {
727 jchar tch = *tptr++;
728 jchar och = *optr++;
729 if (tch != och)
730 return false;
731 }
732 return true;
733}
734
735jboolean
736java::lang::String::startsWith (jstring prefix, jint toffset)
737{
738 jint i = prefix->count;
739 if (toffset < 0 || toffset + i > count)
740 return false;
741 jchar *xptr = JvGetStringChars (this) + toffset;
742 jchar *yptr = JvGetStringChars (prefix);
743 while (--i >= 0)
744 {
745 if (*xptr++ != *yptr++)
746 return false;
747 }
748 return true;
749}
750
751jint
752java::lang::String::indexOf (jint ch, jint fromIndex)
753{
754 if (fromIndex < 0)
755 fromIndex = 0;
756 jchar *ptr = JvGetStringChars(this);
757 for (;; ++fromIndex)
758 {
759 if (fromIndex >= count)
760 return -1;
761 if (ptr[fromIndex] == ch)
762 return fromIndex;
763 }
764}
765
766jint
767java::lang::String::indexOf (jstring s, jint fromIndex)
768{
769 const jchar *const xchars = JvGetStringChars(s);
770 const jchar *const ychars = JvGetStringChars(this) + fromIndex;
771
772 const int xlength = s->length ();
773 const int ylength = length () - fromIndex;
774
775 int i = 0;
776 int j = 0;
777
778 while (i < ylength && j < xlength)
779 {
780 if (xchars[j] != ychars[i])
781 {
782 i = i - j + 1;
783 j = 0;
784 }
785 else
786 i++, j++;
787 }
788
789 if (j >= xlength)
790 return fromIndex + i - xlength;
791 else
792 return -1;
793}
794
795jint
796java::lang::String::lastIndexOf (jint ch, jint fromIndex)
797{
798 if (fromIndex >= count)
799 fromIndex = count - 1;
800 jchar *ptr = JvGetStringChars(this);
801 for (;; --fromIndex)
802 {
803 if (fromIndex < 0)
804 return -1;
805 if (ptr[fromIndex] == ch)
806 return fromIndex;
807 }
808}
809
810jstring
811java::lang::String::substring (jint beginIndex, jint endIndex)
812{
813 if (beginIndex < 0 || endIndex > count || beginIndex > endIndex)
814 throw new StringIndexOutOfBoundsException;
815 if (beginIndex == 0 && endIndex == count)
816 return this;
817 jint newCount = endIndex - beginIndex;
818 if (newCount <= 8) // Optimization, mainly for GC.
819 return JvNewString(JvGetStringChars(this) + beginIndex, newCount);
820 jstring s = new String();
821 s->data = data;
822 s->count = newCount;
823 s->boffset = boffset + sizeof(jchar) * beginIndex;
824 return s;
825}
826
827jstring
828java::lang::String::concat(jstring str)
829{
830 jint str_count = str->count;
831 if (str_count == 0)
832 return this;
833 jstring result = JvAllocString(count + str_count);
834 jchar *dstPtr = JvGetStringChars(result);
835 jchar *srcPtr = JvGetStringChars(this);
836 jint i = count;
837 while (--i >= 0)
838 *dstPtr++ = *srcPtr++;
839 srcPtr = JvGetStringChars(str);
840 i = str->count;
841 while (--i >= 0)
842 *dstPtr++ = *srcPtr++;
843 return result;
844}
845
846jstring
847java::lang::String::replace (jchar oldChar, jchar newChar)
848{
849 jint i;
850 jchar* chrs = JvGetStringChars (this);
851 for (i = 0; ; i++)
852 {
853 if (i == count)
854 return this;
855 if (chrs[i] == oldChar)
856 break;
857 }
858 jstring result = JvAllocString (count);
859 jchar *dPtr = JvGetStringChars (result);
860 for (int j = 0; j < i; j++)
861 *dPtr++ = chrs[j];
862 for (; i < count; i++)
863 {
864 jchar ch = chrs[i];
865 if (ch == oldChar)
866 ch = newChar;
867 *dPtr++ = ch;
868 }
869 return result;
870}
871
872jstring
873java::lang::String::toLowerCase (java::util::Locale *locale)
874{
875 jint i;
876 jchar* chrs = JvGetStringChars(this);
877 jchar ch = 0;
878
879 bool handle_tr = false;
880 if (locale != NULL)
881 {
882 String *lang = locale->getLanguage ();
883 if (lang->length () == 2
884 && lang->charAt (0) == 't'
885 && lang->charAt (1) == 'r')
886 handle_tr = true;
887 }
888
889 for (i = 0; ; i++)
890 {
891 if (i == count)
892 return this;
893 jchar origChar = chrs[i];
894
895 if (handle_tr && (origChar == CAPITAL_I
896 || origChar == CAPITAL_I_WITH_DOT))
897 break;
898
899 ch = java::lang::Character::toLowerCase(origChar);
900 if (ch != origChar)
901 break;
902 }
903 jstring result = JvAllocString(count);
904 jchar *dPtr = JvGetStringChars (result);
905 for (int j = 0; j < i; j++)
906 *dPtr++ = chrs[j];
907 *dPtr++ = ch; i++;
908 for (; i < count; i++)
909 {
910 if (handle_tr && chrs[i] == CAPITAL_I)
911 *dPtr++ = SMALL_DOTLESS_I;
912 else if (handle_tr && chrs[i] == CAPITAL_I_WITH_DOT)
913 *dPtr++ = SMALL_I;
914 else
915 *dPtr++ = java::lang::Character::toLowerCase(chrs[i]);
916 }
917 return result;
918}
919
920jstring
921java::lang::String::toUpperCase (java::util::Locale *locale)
922{
923 jint i;
924 jchar* chrs = JvGetStringChars(this);
925 jchar ch;
926
927 // When handling a specific locale there might be special rules.
928 // Currently all existing rules are simply handled inline, as there
929 // are only two and they are documented in the online 1.2 docs.
930 bool handle_esset = locale != NULL;
931 bool handle_tr = false;
932 if (locale != NULL)
933 {
934 String *lang = locale->getLanguage ();
935 if (lang->length () == 2
936 && lang->charAt (0) == 't'
937 && lang->charAt (1) == 'r')
938 handle_tr = true;
939 }
940
941 int new_count = count;
942 bool new_string = false;
943 for (i = 0; ; i++)
944 {
945 if (i == count)
946 break;
947 jchar origChar = chrs[i];
948
949 if (handle_esset && origChar == ESSET)
950 {
951 ++new_count;
952 new_string = true;
953 }
954 else if (handle_tr && (origChar == SMALL_I
955 || origChar == SMALL_DOTLESS_I))
956 new_string = true;
957 else
958 {
959 ch = java::lang::Character::toUpperCase(origChar);
960 if (ch != origChar)
961 new_string = true;
962 }
963
964 if (new_string && ! handle_esset)
965 break;
966 }
967 if (! new_string)
968 return this;
969 jstring result = JvAllocString(new_count);
970 jchar *dPtr = JvGetStringChars (result);
971 for (i = 0; i < count; i++)
972 {
973 if (handle_esset && chrs[i] == ESSET)
974 {
975 *dPtr++ = CAPITAL_S;
976 *dPtr++ = CAPITAL_S;
977 }
978 else if (handle_tr && chrs[i] == SMALL_I)
979 *dPtr++ = CAPITAL_I_WITH_DOT;
980 else if (handle_tr && chrs[i] == SMALL_DOTLESS_I)
981 *dPtr++ = CAPITAL_I;
982 else
983 *dPtr++ = java::lang::Character::toUpperCase(chrs[i]);
984 }
985 return result;
986}
987
988jstring
989java::lang::String::trim ()
990{
991 jchar* chrs = JvGetStringChars(this);
992 if (count == 0 || (chrs[0] > ' ' && chrs[count-1] > ' '))
993 return this;
994 jint preTrim = 0;
995 for (;; preTrim++)
996 {
997 if (preTrim == count)
998 return new String();
999 if (chrs[preTrim] > ' ')
1000 break;
1001 }
1002 jint endTrim = count;
1003 while (chrs[endTrim-1] <= ' ')
1004 endTrim--;
1005 return substring(preTrim, endTrim);
1006}
1007
1008jstring
1009java::lang::String::valueOf(jcharArray data, jint offset, jint count)
1010{
1011 jint data_length = JvGetArrayLength (data);
1012 if (offset < 0 || count < 0 || offset+count > data_length)
1013 throw new ArrayIndexOutOfBoundsException;
1014 jstring result = JvAllocString(count);
1015 jchar *sPtr = elements (data) + offset;
1016 jchar *dPtr = JvGetStringChars(result);
1017 while (--count >= 0)
1018 *dPtr++ = *sPtr++;
1019 return result;
1020}
1021
1022jstring
1023java::lang::String::valueOf(jchar c)
1024{
1025 jstring result = JvAllocString(1);
1026 JvGetStringChars (result)[0] = c;
1027 return result;
1028}
Note: See TracBrowser for help on using the repository browser.