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