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