1 // natString.cc - Implementation of java.lang.String native methods.
3 /* Copyright (C) 1998, 1999 Cygnus Solutions
5 This file is part of libgcj.
7 This software is copyrighted work licensed under the terms of the
8 Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
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>
29 static jstring
* strhash
= NULL
;
30 static int strhash_count
= 0; /* Number of slots used in strhash. */
31 static int strhash_size
= 0; /* Number of slots available in strhash.
32 * Assumed be power of 2! */
34 #define DELETED_STRING ((jstring)(~0))
35 #define SET_STRING_IS_INTERNED(STR) /* nothing */
37 /* Find a slot where the string with elements DATA, length LEN,
38 and hash HASH should go in the strhash table of interned strings. */
40 _Jv_StringFindSlot (jchar
* data
, jint len
, jint hash
)
42 JvSynchronize
sync (&StringClass
);
44 int start_index
= hash
& (strhash_size
- 1);
45 int deleted_index
= -1;
47 register int index
= start_index
;
48 /* step must be non-zero, and relatively prime with strhash_size. */
49 int step
= 8 * hash
+ 7;
52 register jstring
* ptr
= &strhash
[index
];
55 if (deleted_index
>= 0)
56 return (&strhash
[deleted_index
]);
60 else if (*ptr
== DELETED_STRING
)
61 deleted_index
= index
;
62 else if ((*ptr
)->length() == len
63 && memcmp(JvGetStringChars(*ptr
), data
, 2*len
) == 0)
65 index
= (index
+ step
) & (strhash_size
- 1);
66 JvAssert (index
!= start_index
);
70 /* Calculate a hash code for the string starting at PTR at given LENGTH.
71 This uses the same formula as specified for java.lang.String.hash. */
74 hashChars (jchar
* ptr
, jint length
)
76 register jchar
* limit
= ptr
+ length
;
78 // Updated specification from
79 // http://www.javasoft.com/docs/books/jls/clarify.html.
81 hash
= (31 * hash
) + *ptr
++;
86 java::lang::String::hashCode()
88 return hashChars(JvGetStringChars(this), length());
92 _Jv_StringGetSlot (jstring str
)
94 jchar
* data
= JvGetStringChars(str
);
95 int length
= str
->length();
96 return _Jv_StringFindSlot(data
, length
, hashChars (data
, length
));
100 java::lang::String::rehash()
102 JvSynchronize
sync (&StringClass
);
107 strhash
= (jstring
*) _Jv_AllocBytes (strhash_size
* sizeof (jstring
));
108 memset (strhash
, 0, strhash_size
* sizeof (jstring
));
112 register int i
= strhash_size
;
113 register jstring
* ptr
= strhash
+ i
;
115 strhash
= (jstring
*) _Jv_AllocBytes (strhash_size
* sizeof (jstring
));
116 memset (strhash
, 0, strhash_size
* sizeof (jstring
));
121 if (*ptr
== NULL
|| *ptr
== DELETED_STRING
)
124 /* This is faster equivalent of
125 * *__JvGetInternSlot(*ptr) = *ptr; */
126 jint hash
= (*ptr
)->hashCode();
127 jint index
= hash
& (strhash_size
- 1);
128 jint step
= 8 * hash
+ 7;
131 if (strhash
[index
] == NULL
)
133 strhash
[index
] = *ptr
;
136 index
= (index
+ step
) & (strhash_size
- 1);
143 java::lang::String::intern()
145 JvSynchronize
sync (&StringClass
);
146 if (4 * strhash_count
>= 3 * strhash_size
)
148 jstring
* ptr
= _Jv_StringGetSlot(this);
149 if (*ptr
!= NULL
&& *ptr
!= DELETED_STRING
)
151 SET_STRING_IS_INTERNED(this);
157 /* Called by String fake finalizer. */
159 java::lang::String::unintern()
161 JvSynchronize
sync (&StringClass
);
162 jstring
* ptr
= _Jv_StringGetSlot(this);
163 if (*ptr
== NULL
|| *ptr
== DELETED_STRING
)
165 *ptr
= DELETED_STRING
;
170 _Jv_NewStringUTF (const char *bytes
)
172 int size
= strlen (bytes
);
173 unsigned char *p
= (unsigned char *) bytes
;
175 int length
= _Jv_strLengthUtf8 ((char *) p
, size
);
179 jstring jstr
= JvAllocString (length
);
180 jchar
*chrs
= JvGetStringChars (jstr
);
182 p
= (unsigned char *) bytes
;
183 unsigned char *limit
= p
+ size
;
185 *chrs
++ = UTF8_GET (p
, limit
);
191 _Jv_NewStringUtf8Const (Utf8Const
* str
)
196 register unsigned char* data
= (unsigned char*) str
->data
;
197 register unsigned char* limit
= data
+ str
->length
;
198 int length
= _Jv_strLengthUtf8(str
->data
, str
->length
);
200 if (length
<= (int) (sizeof(buffer
) / sizeof(jchar
)))
207 jstr
= JvAllocString(length
);
208 chrs
= JvGetStringChars(jstr
);
212 *chrs
++ = UTF8_GET(data
, limit
);
215 JvSynchronize
sync (&StringClass
);
216 if (4 * strhash_count
>= 3 * strhash_size
)
217 java::lang::String::rehash();
218 int hash
= str
->hash
;
219 jstring
* ptr
= _Jv_StringFindSlot (chrs
, length
, hash
);
220 if (*ptr
!= NULL
&& *ptr
!= DELETED_STRING
)
225 jstr
= JvAllocString(length
);
226 chrs
= JvGetStringChars(jstr
);
227 memcpy (chrs
, buffer
, sizeof(jchar
)*length
);
230 SET_STRING_IS_INTERNED(jstr
);
235 _Jv_GetStringUTFLength (jstring string
)
237 register jsize len
= 0;
238 register jchar
*ptr
= JvGetStringChars (string
);
239 register jsize i
= string
->length();
242 register jchar ch
= *ptr
++;
243 if (ch
> 0 && ch
<= 0x7F)
245 else if (ch
<= 0x7FF)
253 // Not sure this quite matches GetStringUTFRegion.
254 // null-termination of result? len? throw exception?
256 _Jv_GetStringUTFRegion (jstring str
, jsize start
, jsize len
, char *buf
)
258 register jchar
*sptr
= JvGetStringChars (str
) + start
;
259 register jsize i
= len
;
260 register char *dptr
= buf
;
264 if (ch
> 0 && ch
<= 0x7F)
266 else if (ch
<= 0x7FF)
268 *dptr
++ = (char) (0xC0 + ((ch
>> 6) & 0x1F));
269 *dptr
++ = (char) (0x80 + (ch
& 0x3F));
273 *dptr
++ = (char) (0xE0 + ((ch
>> 12) & 0xF));
274 *dptr
++ = (char) (0x80 + ((ch
>> 6) & 0x3F));
275 *dptr
++ = (char) (0x80 + (ch
& 0x3F));
282 _Jv_AllocString(jsize len
)
284 jsize sz
= sizeof(java::lang::String
) + len
* sizeof(jchar
);
286 jstring obj
= (jstring
) JvAllocObject(&StringClass
, sz
);
289 obj
->boffset
= sizeof(java::lang::String
);
295 _Jv_NewString(const jchar
*chars
, jsize len
)
297 jstring str
= _Jv_AllocString(len
);
298 jchar
* data
= JvGetStringChars (str
);
305 _Jv_NewStringLatin1(const char *bytes
, jsize len
)
307 jstring str
= JvAllocString(len
);
308 jchar
* data
= JvGetStringChars (str
);
310 *data
++ = *(unsigned char*)bytes
++;
315 java::lang::String::init ()
318 boffset
= sizeof(java::lang::String
);
323 java::lang::String::init(jcharArray chars
, jint offset
, jint count
,
327 JvThrow (new NullPointerException
);
328 jsize data_size
= JvGetArrayLength (chars
);
329 if (offset
< 0 || count
< 0 || offset
+ count
> data_size
)
330 JvThrow (new StringIndexOutOfBoundsException());
335 array
= JvNewCharArray(count
);
336 pdst
= elements (array
);
337 memcpy (pdst
, elements (chars
) + offset
, count
* sizeof (jchar
));
341 JvAssert (offset
== 0);
343 pdst
= elements (array
);
347 boffset
= (char *) pdst
- (char *) array
;
352 java::lang::String::init(jbyteArray ascii
, jint hibyte
, jint offset
,
356 JvThrow (new NullPointerException
);
357 jsize data_size
= JvGetArrayLength (ascii
);
358 if (offset
< 0 || count
< 0 || offset
+ count
< 0
359 || offset
+ count
> data_size
)
360 JvThrow (new java::lang::StringIndexOutOfBoundsException());
361 jcharArray array
= JvNewCharArray(count
);
362 jbyte
*psrc
= elements (ascii
) + offset
;
363 jchar
*pdst
= elements (array
);
365 boffset
= (char *) pdst
- (char *) array
;
367 hibyte
= (hibyte
& 0xff) << 8;
368 while (-- count
>= 0)
370 *pdst
++ = hibyte
| (*psrc
++ & 0xff);
375 java::lang::String::init (jbyteArray bytes
, jint offset
, jint count
,
379 JvThrow (new NullPointerException
);
380 jsize data_size
= JvGetArrayLength (bytes
);
381 if (offset
< 0 || count
< 0 || offset
+ count
< 0
382 || offset
+ count
> data_size
)
383 JvThrow (new StringIndexOutOfBoundsException
);
385 java::io::ByteArrayInputStream
*b
386 = new java::io::ByteArrayInputStream (bytes
, offset
, count
);
387 java::io::InputStreamReader
*ir
388 = new java::io::InputStreamReader (b
, encoding
);
389 // FIXME: we allocate too much here in some cases.
390 jcharArray array
= JvNewCharArray (count
);
392 boffset
= (char *) elements (array
) - (char *) array
;
393 // FIXME: this can throw IOException.
394 this->count
= ir
->read(array
, 0, count
);
398 java::lang::String::equals(jobject anObject
)
400 if (anObject
== NULL
)
402 if (anObject
== this)
404 if (anObject
->getClass() != &StringClass
)
406 jstring other
= (jstring
) anObject
;
407 if (count
!= other
->count
)
409 /* if both are interned, return false. */
410 register jint i
= count
;
411 register jchar
*xptr
= JvGetStringChars (this);
412 register jchar
*yptr
= JvGetStringChars (other
);
415 if (*xptr
++ != *yptr
++)
422 java::lang::String::charAt(jint i
)
424 if (i
< 0 || i
>= count
)
425 JvThrow (new java::lang::StringIndexOutOfBoundsException());
426 return JvGetStringChars(this)[i
];
430 java::lang::String::getChars(jint srcBegin
, jint srcEnd
,
431 jcharArray dst
, jint dstBegin
)
433 jint dst_length
= JvGetArrayLength (dst
);
434 if (srcBegin
< 0 || srcBegin
> srcEnd
|| srcEnd
> count
435 || dstBegin
< 0 || dstBegin
+ (srcEnd
-srcBegin
) > dst_length
)
436 JvThrow (new java::lang::ArrayIndexOutOfBoundsException());
437 register jchar
*dPtr
= elements (dst
) + dstBegin
;
438 register jchar
*sPtr
= JvGetStringChars (this) + srcBegin
;
439 register jint i
= srcEnd
-srcBegin
;
445 java::lang::String::getBytes (jstring enc
)
447 java::io::ByteArrayOutputStream
*os
448 = new java::io::ByteArrayOutputStream(length ());
449 java::io::OutputStreamWriter
*ow
450 = new java::io::OutputStreamWriter(os
, enc
);
452 ow
->write(this, 0, length ());
455 return os
->toByteArray();
459 java::lang::String::getBytes(jint srcBegin
, jint srcEnd
,
460 jbyteArray dst
, jint dstBegin
)
462 jint dst_length
= JvGetArrayLength (dst
);
463 if (srcBegin
< 0 || srcBegin
> srcEnd
|| srcEnd
> count
464 || dstBegin
< 0 || dstBegin
+ (srcEnd
-srcBegin
) > dst_length
)
465 JvThrow (new java::lang::ArrayIndexOutOfBoundsException());
466 register jbyte
*dPtr
= elements (dst
) + dstBegin
;
467 register jchar
*sPtr
= JvGetStringChars (this) + srcBegin
;
468 register jint i
= srcEnd
-srcBegin
;
470 *dPtr
++ = (jbyte
) *sPtr
++;
474 java::lang::String::toCharArray()
476 jcharArray array
= JvNewCharArray(count
);
477 register jchar
*dPtr
= elements (array
);
478 register jchar
*sPtr
= JvGetStringChars (this);
479 register jint i
= count
;
486 java::lang::String::equalsIgnoreCase (jstring anotherString
)
488 if (count
!= anotherString
->count
)
490 register jchar
*tptr
= JvGetStringChars (this);
491 register jchar
*optr
= JvGetStringChars (anotherString
);
492 register jint i
= count
;
498 && (java::lang::Character::toLowerCase (tch
)
499 != java::lang::Character::toLowerCase (och
))
500 && (java::lang::Character::toUpperCase (tch
)
501 != java::lang::Character::toUpperCase (och
)))
508 java::lang::String::regionMatches (jint toffset
,
509 jstring other
, jint ooffset
, jint len
)
511 if (toffset
< 0 || ooffset
< 0
512 || toffset
+ len
> count
513 || ooffset
+ len
> other
->count
)
515 register jchar
*tptr
= JvGetStringChars (this) + toffset
;
516 register jchar
*optr
= JvGetStringChars (other
) + ooffset
;
517 register jint i
= len
;
520 if (*tptr
++ != *optr
++)
527 java::lang::String::compareTo (jstring anotherString
)
529 register jchar
*tptr
= JvGetStringChars (this);
530 register jchar
*optr
= JvGetStringChars (anotherString
);
531 jint tlen
= this->count
;
532 jint olen
= anotherString
->count
;
533 register jint i
= tlen
> olen
? olen
: tlen
;
539 return (jint
) tch
- (jint
) och
;
545 java::lang::String::regionMatches (jboolean ignoreCase
, jint toffset
,
546 jstring other
, jint ooffset
, jint len
)
548 if (toffset
< 0 || ooffset
< 0
549 || toffset
+ len
> count
550 || ooffset
+ len
> other
->count
)
552 register jchar
*tptr
= JvGetStringChars (this) + toffset
;
553 register jchar
*optr
= JvGetStringChars (other
) + ooffset
;
554 register jint i
= len
;
562 && (java::lang::Character::toLowerCase (tch
)
563 != java::lang::Character::toLowerCase (och
))
564 && (java::lang::Character::toUpperCase (tch
)
565 != java::lang::Character::toUpperCase (och
)))
572 java::lang::String::startsWith (jstring prefix
, jint toffset
)
574 register jint i
= prefix
->count
;
575 if (toffset
< 0 || toffset
+ i
> count
)
577 register jchar
*xptr
= JvGetStringChars (this) + toffset
;
578 register jchar
*yptr
= JvGetStringChars (prefix
);
581 if (*xptr
++ != *yptr
++)
588 java::lang::String::indexOf (jint ch
, jint fromIndex
)
592 register jchar
*ptr
= JvGetStringChars(this);
595 if (fromIndex
>= count
)
597 if (ptr
[fromIndex
] == ch
)
603 java::lang::String::indexOf (jstring s
, jint fromIndex
)
605 const jchar
*const xchars
= JvGetStringChars(s
);
606 const jchar
*const ychars
= JvGetStringChars(this) + fromIndex
;
608 const int xlength
= s
->length ();
609 const int ylength
= length () - fromIndex
;
614 while (i
< ylength
&& j
< xlength
)
616 if (xchars
[j
] != ychars
[i
])
626 return fromIndex
+ i
- xlength
;
632 java::lang::String::lastIndexOf (jint ch
, jint fromIndex
)
634 if (fromIndex
>= count
)
635 fromIndex
= count
- 1;
636 register jchar
*ptr
= JvGetStringChars(this);
641 if (ptr
[fromIndex
] == ch
)
647 java::lang::String::substring (jint beginIndex
, jint endIndex
)
649 if (beginIndex
< 0 || endIndex
> count
|| beginIndex
> endIndex
)
650 JvThrow (new StringIndexOutOfBoundsException());
651 jint newCount
= endIndex
- beginIndex
;
652 if (newCount
<= 8) // Optimization, mainly for GC.
653 return JvNewString(JvGetStringChars(this) + beginIndex
, newCount
);
654 jstring s
= new String();
657 s
->boffset
= boffset
+ sizeof(jchar
) * beginIndex
;
662 java::lang::String::concat(jstring str
)
664 jint str_count
= str
->count
;
667 jstring result
= JvAllocString(count
+ str_count
);
668 register jchar
*dstPtr
= JvGetStringChars(result
);
669 register jchar
*srcPtr
= JvGetStringChars(this);
670 register jint i
= count
;
672 *dstPtr
++ = *srcPtr
++;
673 srcPtr
= JvGetStringChars(str
);
676 *dstPtr
++ = *srcPtr
++;
681 java::lang::String::replace (jchar oldChar
, jchar newChar
)
684 jchar
* chrs
= JvGetStringChars (this);
689 if (chrs
[i
] == oldChar
)
692 jstring result
= JvAllocString (count
);
693 jchar
*dPtr
= JvGetStringChars (result
);
694 for (int j
= 0; j
< i
; j
++)
696 for (; i
< count
; i
++)
707 java::lang::String::toLowerCase ()
710 jchar
* chrs
= JvGetStringChars(this);
716 jchar origChar
= chrs
[i
];
717 ch
= java::lang::Character::toLowerCase(origChar
);
721 jstring result
= JvAllocString(count
);
722 jchar
*dPtr
= JvGetStringChars (result
);
723 for (int j
= 0; j
< i
; j
++)
726 for (; i
< count
; i
++)
728 *dPtr
++ = java::lang::Character::toLowerCase(chrs
[i
]);
734 java::lang::String::toUpperCase ()
737 jchar
* chrs
= JvGetStringChars(this);
743 jchar origChar
= chrs
[i
];
744 ch
= java::lang::Character::toUpperCase(origChar
);
748 jstring result
= JvAllocString(count
);
749 jchar
*dPtr
= JvGetStringChars (result
);
750 for (int j
= 0; j
< i
; j
++)
753 for (; i
< count
; i
++)
755 *dPtr
++ = java::lang::Character::toUpperCase(chrs
[i
]);
761 java::lang::String::trim ()
763 jchar
* chrs
= JvGetStringChars(this);
764 if (count
== 0 || (chrs
[0] > ' ' && chrs
[count
-1] > ' '))
769 if (preTrim
== count
)
771 if (chrs
[preTrim
] > ' ')
774 jint endTrim
= count
;
775 while (chrs
[endTrim
-1] <= ' ')
777 return substring(preTrim
, endTrim
);
781 java::lang::String::valueOf(jcharArray data
, jint offset
, jint count
)
783 jint data_length
= JvGetArrayLength (data
);
784 if (offset
< 0 || count
< 0 || offset
+count
> data_length
)
785 JvThrow (new java::lang::IndexOutOfBoundsException());
786 register jstring result
= JvAllocString(count
);
787 register jchar
*sPtr
= elements (data
) + offset
;
788 register jchar
*dPtr
= JvGetStringChars(result
);
795 java::lang::String::valueOf(jchar c
)
797 register jstring result
= JvAllocString(1);
798 JvGetStringChars (result
)[0] = c
;