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