1 // natString.cc - Implementation of java.lang.String native methods.
2
3 /* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
4 2007, 2008 Free Software Foundation
5
6 This file is part of libgcj.
7
8 This software is copyrighted work licensed under the terms of the
9 Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
10 details. */
11
12 #include <config.h>
13
14 #include <string.h>
15 #include <stdlib.h>
16
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>
36
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! */
41
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
49
50 #define DELETED_STRING ((jstring)(~0))
51 #define SET_STRING_IS_INTERNED(STR) /* nothing */
52
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)
56
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*
_Jv_StringFindSlot(jchar * data,jint len,jint hash)60 _Jv_StringFindSlot (jchar* data, jint len, jint hash)
61 {
62 JvSynchronize sync (&java::lang::String::class$);
63
64 int start_index = hash & (strhash_size - 1);
65 int deleted_index = -1;
66
67 int index = start_index;
68 /* step must be non-zero, and relatively prime with strhash_size. */
69 jint step = (hash ^ (hash >> 16)) | 1;
70 do
71 {
72 jstring* ptr = &strhash[index];
73 jstring value = (jstring) UNMASK_PTR (*ptr);
74 if (value == NULL)
75 {
76 if (deleted_index >= 0)
77 return (&strhash[deleted_index]);
78 else
79 return ptr;
80 }
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);
87 }
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];
93 }
94
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. */
97
98 static jint
hashChars(jchar * ptr,jint length)99 hashChars (jchar* ptr, jint length)
100 {
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;
108 }
109
110 jint
hashCode()111 java::lang::String::hashCode()
112 {
113 if (cachedHashCode == 0)
114 cachedHashCode = hashChars(JvGetStringChars(this), length());
115 return cachedHashCode;
116 }
117
118 jstring*
_Jv_StringGetSlot(jstring str)119 _Jv_StringGetSlot (jstring str)
120 {
121 jchar* data = JvGetStringChars(str);
122 int length = str->length();
123 return _Jv_StringFindSlot(data, length, hashChars (data, length));
124 }
125
126 static void
rehash()127 rehash ()
128 {
129 JvSynchronize sync (&java::lang::String::class$);
130
131 if (strhash == NULL)
132 {
133 strhash_size = 1024;
134 strhash = (jstring *) _Jv_AllocBytes (strhash_size * sizeof (jstring));
135 }
136 else
137 {
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));
142
143 while (--i >= 0)
144 {
145 --ptr;
146 if (*ptr == NULL || *ptr == DELETED_STRING)
147 continue;
148
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 (;;)
156 {
157 if (next[index] == NULL)
158 {
159 next[index] = *ptr;
160 break;
161 }
162 index = (index + step) & (nsize - 1);
163 }
164 }
165
166 strhash_size = nsize;
167 strhash = next;
168 }
169 }
170
171 jstring
intern()172 java::lang::String::intern()
173 {
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)
179 {
180 // See description in _Jv_FinalizeString() to understand this.
181 *ptr = (jstring) MASK_PTR (*ptr);
182 return (jstring) UNMASK_PTR (*ptr);
183 }
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;
193 }
194
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
_Jv_FinalizeString(jobject obj)199 _Jv_FinalizeString (jobject obj)
200 {
201 JvSynchronize sync (&java::lang::String::class$);
202
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;
207
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;
213
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))
231 {
232 *ptr = (jstring) UNMASK_PTR (*ptr);
233 _Jv_RegisterStringFinalizer (obj);
234 }
235 else
236 {
237 *ptr = DELETED_STRING;
238 strhash_count--;
239 }
240 }
241
242 jstring
_Jv_NewStringUTF(const char * bytes)243 _Jv_NewStringUTF (const char *bytes)
244 {
245 int size = strlen (bytes);
246 unsigned char *p = (unsigned char *) bytes;
247
248 int length = _Jv_strLengthUtf8 ((char *) p, size);
249 if (length < 0)
250 return NULL;
251
252 jstring jstr = JvAllocString (length);
253 jchar *chrs = JvGetStringChars (jstr);
254
255 p = (unsigned char *) bytes;
256 unsigned char *limit = p + size;
257 while (p < limit)
258 *chrs++ = UTF8_GET (p, limit);
259
260 return jstr;
261 }
262
263 jstring
_Jv_NewStringUtf8Const(Utf8Const * str)264 _Jv_NewStringUtf8Const (Utf8Const* str)
265 {
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);
272
273 if (length <= (int) (sizeof(buffer) / sizeof(jchar)))
274 {
275 jstr = NULL;
276 chrs = buffer;
277 }
278 else
279 {
280 jstr = JvAllocString(length);
281 chrs = JvGetStringChars(jstr);
282 }
283
284 jint hash = 0;
285 while (data < limit)
286 {
287 jchar ch = UTF8_GET(data, limit);
288 hash = (31 * hash) + ch;
289 *chrs++ = ch;
290 }
291 chrs -= length;
292
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)
301 {
302 jstr = JvAllocString(length);
303 chrs = JvGetStringChars(jstr);
304 memcpy (chrs, buffer, sizeof(jchar)*length);
305 }
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;
314 }
315
316 jsize
_Jv_GetStringUTFLength(jstring string)317 _Jv_GetStringUTFLength (jstring string)
318 {
319 jsize len = 0;
320 jchar *ptr = JvGetStringChars (string);
321 jsize i = string->length();
322 while (--i >= 0)
323 {
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;
331 }
332 return len;
333 }
334
335 // Not sure this quite matches GetStringUTFRegion.
336 // null-termination of result? len? throw exception?
337 jsize
_Jv_GetStringUTFRegion(jstring str,jsize start,jsize len,char * buf)338 _Jv_GetStringUTFRegion (jstring str, jsize start, jsize len, char *buf)
339 {
340 jchar *sptr = JvGetStringChars (str) + start;
341 jsize i = len;
342 char *dptr = buf;
343 while (--i >= 0)
344 {
345 jchar ch = *sptr++;
346 if (ch > 0 && ch <= 0x7F)
347 *dptr++ = (char) ch;
348 else if (ch <= 0x7FF)
349 {
350 *dptr++ = (char) (0xC0 + ((ch >> 6) & 0x1F));
351 *dptr++ = (char) (0x80 + (ch & 0x3F));
352 }
353 else
354 {
355 *dptr++ = (char) (0xE0 + ((ch >> 12) & 0xF));
356 *dptr++ = (char) (0x80 + ((ch >> 6) & 0x3F));
357 *dptr++ = (char) (0x80 + (ch & 0x3F));
358 }
359 }
360 return dptr - buf;
361 }
362
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). */
367
368 jint
_Jv_FormatInt(jchar * bufend,jint num)369 _Jv_FormatInt (jchar* bufend, jint num)
370 {
371 register jchar* ptr = bufend;
372 jboolean isNeg;
373 if (num < 0)
374 {
375 isNeg = true;
376 if (num != (jint) -2147483648U)
377 num = -(num);
378 else
379 {
380 // Handle special case of MIN_VALUE.
381 *--ptr = '8';
382 num = 214748364;
383 }
384 }
385 else
386 isNeg = false;
387
388 do
389 {
390 *--ptr = (jchar) ((int) '0' + (num % 10));
391 num /= 10;
392 }
393 while (num > 0);
394
395 if (isNeg)
396 *--ptr = '-';
397 return bufend - ptr;
398 }
399
400 jstring
valueOf(jint num)401 java::lang::String::valueOf (jint num)
402 {
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);
407 }
408
409 jstring
_Jv_NewString(const jchar * chars,jsize len)410 _Jv_NewString(const jchar *chars, jsize len)
411 {
412 jstring str = _Jv_AllocString(len);
413 jchar* data = JvGetStringChars (str);
414 memcpy (data, chars, len * sizeof (jchar));
415 return str;
416 }
417
418 jstring
_Jv_NewStringLatin1(const char * bytes,jsize len)419 _Jv_NewStringLatin1(const char *bytes, jsize len)
420 {
421 jstring str = JvAllocString(len);
422 jchar* data = JvGetStringChars (str);
423 while (--len >= 0)
424 *data++ = *(unsigned char*)bytes++;
425 return str;
426 }
427
428 void
init(jcharArray chars,jint offset,jint count,jboolean dont_copy)429 java::lang::String::init(jcharArray chars, jint offset, jint count,
430 jboolean dont_copy)
431 {
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)
441 {
442 array = JvNewCharArray(count);
443 pdst = elements (array);
444 memcpy (pdst, elements (chars) + offset, count * sizeof (jchar));
445 }
446 else
447 {
448 array = chars;
449 pdst = &(elements(array)[offset]);
450 }
451
452 data = array;
453 boffset = (char *) pdst - (char *) array;
454 this->count = count;
455 }
456
457 void
init(jbyteArray ascii,jint hibyte,jint offset,jint count)458 java::lang::String::init(jbyteArray ascii, jint hibyte, jint offset,
459 jint count)
460 {
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)
475 {
476 *pdst++ = hibyte | (*psrc++ & 0xff);
477 }
478 }
479
480 void
init(jbyteArray bytes,jint offset,jint count,jstring encoding)481 java::lang::String::init (jbyteArray bytes, jint offset, jint count,
482 jstring encoding)
483 {
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)
497 {
498 int done;
499 try
500 {
501 done = converter->read(array, outpos, avail);
502 }
503 catch (::java::io::CharConversionException *e)
504 {
505 // Ignore it and silently throw away the offending data.
506 break;
507 }
508 if (done == 0)
509 {
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;
519
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;
526 }
527 else
528 {
529 outpos += done;
530 avail -= done;
531 }
532 }
533 converter->done ();
534 this->data = array;
535 this->boffset = (char *) elements (array) - (char *) array;
536 this->count = outpos;
537 }
538
539 void
init(gnu::gcj::runtime::StringBuffer * buffer)540 java::lang::String::init (gnu::gcj::runtime::StringBuffer *buffer)
541 {
542 init (buffer->value, 0, buffer->count, true);
543 }
544
545 jboolean
equals(jobject anObject)546 java::lang::String::equals(jobject anObject)
547 {
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;
557
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;
564
565 // We could see if both are interned, and return false. But that
566 // seems too expensive.
567
568 jchar *xptr = JvGetStringChars (this);
569 jchar *yptr = JvGetStringChars (other);
570 return ! memcmp (xptr, yptr, count * sizeof (jchar));
571 }
572
573 jboolean
contentEquals(java::lang::StringBuffer * buffer)574 java::lang::String::contentEquals(java::lang::StringBuffer* buffer)
575 {
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));
586 }
587
588 jboolean
contentEquals(java::lang::CharSequence * seq)589 java::lang::String::contentEquals(java::lang::CharSequence *seq)
590 {
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;
598 }
599
600 jchar
charAt(jint i)601 java::lang::String::charAt(jint i)
602 {
603 if (i < 0 || i >= count)
604 throw new java::lang::StringIndexOutOfBoundsException(i);
605 return JvGetStringChars(this)[i];
606 }
607
608 void
getChars(jint srcBegin,jint srcEnd,jcharArray dst,jint dstBegin)609 java::lang::String::getChars(jint srcBegin, jint srcEnd,
610 jcharArray dst, jint dstBegin)
611 {
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));
624 }
625
626 jbyteArray
getBytes(jstring enc)627 java::lang::String::getBytes (jstring enc)
628 {
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())
637 {
638 converter->setOutput(buffer, bufpos);
639 int converted = converter->write(this, offset, todo, NULL);
640 bufpos = converter->count;
641 if (converted == 0)
642 {
643 buflen *= 2;
644 jbyteArray newbuffer = JvNewByteArray(buflen);
645 memcpy (elements (newbuffer), elements (buffer), bufpos);
646 buffer = newbuffer;
647 }
648 else
649 {
650 offset += converted;
651 todo -= converted;
652 }
653 }
654 if (length() > 0)
655 {
656 converter->setFinished();
657 converter->write(this, 0, 0, NULL);
658 }
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;
665 }
666
667 void
getBytes(jint srcBegin,jint srcEnd,jbyteArray dst,jint dstBegin)668 java::lang::String::getBytes(jint srcBegin, jint srcEnd,
669 jbyteArray dst, jint dstBegin)
670 {
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++;
684 }
685
686 jcharArray
toCharArray()687 java::lang::String::toCharArray()
688 {
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;
695 }
696
697 jboolean
equalsIgnoreCase(jstring anotherString)698 java::lang::String::equalsIgnoreCase (jstring anotherString)
699 {
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)
706 {
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;
715 }
716 return true;
717 }
718
719 jboolean
regionMatches(jint toffset,jstring other,jint ooffset,jint len)720 java::lang::String::regionMatches (jint toffset,
721 jstring other, jint ooffset, jint len)
722 {
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));
731 }
732
733 jint
nativeCompareTo(jstring anotherString)734 java::lang::String::nativeCompareTo (jstring anotherString)
735 {
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)
742 {
743 jchar tch = *tptr++;
744 jchar och = *optr++;
745 if (tch != och)
746 return (jint) tch - (jint) och;
747 }
748 return tlen - olen;
749 }
750
751 jboolean
regionMatches(jboolean ignoreCase,jint toffset,jstring other,jint ooffset,jint len)752 java::lang::String::regionMatches (jboolean ignoreCase, jint toffset,
753 jstring other, jint ooffset, jint len)
754 {
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)
763 {
764 while (--i >= 0)
765 {
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;
773 }
774 return true;
775 }
776 return ! memcmp (tptr, optr, i * sizeof (jchar));
777 }
778
779 jboolean
startsWith(jstring prefix,jint toffset)780 java::lang::String::startsWith (jstring prefix, jint toffset)
781 {
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));
788 }
789
790 jint
indexOf(jint ch,jint fromIndex)791 java::lang::String::indexOf (jint ch, jint fromIndex)
792 {
793 if (fromIndex < 0)
794 fromIndex = 0;
795 jchar *ptr = JvGetStringChars(this);
796 for (;; ++fromIndex)
797 {
798 if (fromIndex >= count)
799 return -1;
800 if (ptr[fromIndex] == ch)
801 return fromIndex;
802 }
803 }
804
805 jint
indexOf(jstring s,jint fromIndex)806 java::lang::String::indexOf (jstring s, jint fromIndex)
807 {
808 const jchar *const xchars = JvGetStringChars(s);
809 const jchar *const ychars = JvGetStringChars(this) + fromIndex;
810
811 const int xlength = s->length ();
812 const int ylength = length () - fromIndex;
813
814 int i = 0;
815 int j = 0;
816
817 while (i < ylength && j < xlength)
818 {
819 if (xchars[j] != ychars[i])
820 {
821 i = i - j + 1;
822 j = 0;
823 }
824 else
825 i++, j++;
826 }
827
828 if (j >= xlength)
829 return fromIndex + i - xlength;
830 else
831 return -1;
832 }
833
834 jint
lastIndexOf(jint ch,jint fromIndex)835 java::lang::String::lastIndexOf (jint ch, jint fromIndex)
836 {
837 if (fromIndex >= count)
838 fromIndex = count - 1;
839 jchar *ptr = JvGetStringChars(this);
840 for (;; --fromIndex)
841 {
842 if (fromIndex < 0)
843 return -1;
844 if (ptr[fromIndex] == ch)
845 return fromIndex;
846 }
847 }
848
849 jstring
substring(jint beginIndex,jint endIndex)850 java::lang::String::substring (jint beginIndex, jint endIndex)
851 {
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;
867 }
868
869 jstring
concat(jstring str)870 java::lang::String::concat(jstring str)
871 {
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;
885 }
886
887 jstring
replace(jchar oldChar,jchar newChar)888 java::lang::String::replace (jchar oldChar, jchar newChar)
889 {
890 jint i;
891 jchar* chrs = JvGetStringChars (this);
892 for (i = 0; ; i++)
893 {
894 if (i == count)
895 return this;
896 if (chrs[i] == oldChar)
897 break;
898 }
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++)
904 {
905 jchar ch = chrs[i];
906 if (ch == oldChar)
907 ch = newChar;
908 *dPtr++ = ch;
909 }
910 return result;
911 }
912
913 jstring
toLowerCase(java::util::Locale * locale)914 java::lang::String::toLowerCase (java::util::Locale *locale)
915 {
916 jint i;
917 jchar* chrs = JvGetStringChars(this);
918 jchar ch = 0;
919
920 bool handle_tr = false;
921 if (locale != NULL)
922 {
923 String *lang = locale->getLanguage ();
924 if (lang->length () == 2
925 && lang->charAt (0) == 't'
926 && lang->charAt (1) == 'r')
927 handle_tr = true;
928 }
929
930 for (i = 0; ; i++)
931 {
932 if (i == count)
933 return this;
934 jchar origChar = chrs[i];
935
936 if (handle_tr && (origChar == CAPITAL_I
937 || origChar == CAPITAL_I_WITH_DOT))
938 break;
939
940 ch = java::lang::Character::toLowerCase(origChar);
941 if (ch != origChar)
942 break;
943 }
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++)
950 {
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]);
957 }
958 return result;
959 }
960
961 jstring
toUpperCase(java::util::Locale * locale)962 java::lang::String::toUpperCase (java::util::Locale *locale)
963 {
964 jint i;
965 jchar* chrs = JvGetStringChars(this);
966 jchar ch;
967
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)
974 {
975 String *lang = locale->getLanguage ();
976 if (lang->length () == 2
977 && lang->charAt (0) == 't'
978 && lang->charAt (1) == 'r')
979 handle_tr = true;
980 }
981
982 int new_count = count;
983 bool new_string = false;
984 for (i = 0; ; i++)
985 {
986 if (i == count)
987 break;
988 jchar origChar = chrs[i];
989
990 if (handle_esset && origChar == ESSET)
991 {
992 ++new_count;
993 new_string = true;
994 }
995 else if (handle_tr && (origChar == SMALL_I
996 || origChar == SMALL_DOTLESS_I))
997 new_string = true;
998 else
999 {
1000 ch = java::lang::Character::toUpperCase(origChar);
1001 if (ch != origChar)
1002 new_string = true;
1003 }
1004
1005 if (new_string && ! handle_esset)
1006 break;
1007 }
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++)
1013 {
1014 if (handle_esset && chrs[i] == ESSET)
1015 {
1016 *dPtr++ = CAPITAL_S;
1017 *dPtr++ = CAPITAL_S;
1018 }
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]);
1025 }
1026 return result;
1027 }
1028
1029 jstring
trim()1030 java::lang::String::trim ()
1031 {
1032 jchar* chrs = JvGetStringChars(this);
1033 if (count == 0 || (chrs[0] > ' ' && chrs[count-1] > ' '))
1034 return this;
1035 jint preTrim = 0;
1036 for (;; preTrim++)
1037 {
1038 if (preTrim == count)
1039 return new String();
1040 if (chrs[preTrim] > ' ')
1041 break;
1042 }
1043 jint endTrim = count;
1044 while (chrs[endTrim-1] <= ' ')
1045 endTrim--;
1046 return substring(preTrim, endTrim);
1047 }
1048
1049 jstring
valueOf(jcharArray data,jint offset,jint count)1050 java::lang::String::valueOf(jcharArray data, jint offset, jint count)
1051 {
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;
1060 }
1061
1062 jstring
valueOf(jchar c)1063 java::lang::String::valueOf(jchar c)
1064 {
1065 jstring result = JvAllocString(1);
1066 JvGetStringChars (result)[0] = c;
1067 return result;
1068 }
1069