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