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