OSDN Git Service

am 548d70b0: am 23966773: Rename absoluteMaxSize to maximumSize.
[android-x86/dalvik.git] / vm / UtfString.c
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /*
18  * UTF-8 and Unicode string manipulation, plus java/lang/String convenience
19  * functions.
20  *
21  * In most cases we populate the fields in the String object directly,
22  * rather than going through an instance field lookup.
23  */
24 #include "Dalvik.h"
25 #include <stdlib.h>
26
27 /*
28  * Initialize string globals.
29  *
30  * This isn't part of the VM init sequence because it's hard to get the
31  * timing right -- we need it to happen after java/lang/String has been
32  * loaded, but before anybody wants to use a string.  It's easiest to
33  * just initialize it on first use.
34  *
35  * In some unusual circumstances (e.g. trying to throw an exception because
36  * String implements java/lang/CharSequence, but CharSequence doesn't exist)
37  * we can try to create an exception string internally before anything has
38  * really tried to use String.  In that case we basically self-destruct.
39  *
40  * We're expecting to be essentially single-threaded at this point.
41  * We employ atomics to ensure everything is observed correctly, and also
42  * to guarantee that we do detect a problem if our assumption is wrong.
43  */
44 static bool stringStartup()
45 {
46     if (gDvm.javaLangStringReady < 0) {
47         LOGE("ERROR: reentrant string initialization\n");
48         assert(false);
49         return false;
50     }
51
52     if (android_atomic_acquire_cas(0, -1, &gDvm.javaLangStringReady) != 0) {
53         LOGE("ERROR: initial string-ready state not 0 (%d)\n",
54             gDvm.javaLangStringReady);
55         return false;
56     }
57
58     if (gDvm.classJavaLangString == NULL)
59         gDvm.classJavaLangString =
60             dvmFindSystemClassNoInit("Ljava/lang/String;");
61
62     gDvm.offJavaLangString_value =
63         dvmFindFieldOffset(gDvm.classJavaLangString, "value", "[C");
64     gDvm.offJavaLangString_count =
65         dvmFindFieldOffset(gDvm.classJavaLangString, "count", "I");
66     gDvm.offJavaLangString_offset =
67         dvmFindFieldOffset(gDvm.classJavaLangString, "offset", "I");
68     gDvm.offJavaLangString_hashCode =
69         dvmFindFieldOffset(gDvm.classJavaLangString, "hashCode", "I");
70
71     if (gDvm.offJavaLangString_value < 0 ||
72         gDvm.offJavaLangString_count < 0 ||
73         gDvm.offJavaLangString_offset < 0 ||
74         gDvm.offJavaLangString_hashCode < 0)
75     {
76         LOGE("VM-required field missing from java/lang/String\n");
77         return false;
78     }
79
80     bool badValue = false;
81     if (gDvm.offJavaLangString_value != STRING_FIELDOFF_VALUE) {
82         LOGE("InlineNative: String.value offset = %d, expected %d\n",
83             gDvm.offJavaLangString_value, STRING_FIELDOFF_VALUE);
84         badValue = true;
85     }
86     if (gDvm.offJavaLangString_count != STRING_FIELDOFF_COUNT) {
87         LOGE("InlineNative: String.count offset = %d, expected %d\n",
88             gDvm.offJavaLangString_count, STRING_FIELDOFF_COUNT);
89         badValue = true;
90     }
91     if (gDvm.offJavaLangString_offset != STRING_FIELDOFF_OFFSET) {
92         LOGE("InlineNative: String.offset offset = %d, expected %d\n",
93             gDvm.offJavaLangString_offset, STRING_FIELDOFF_OFFSET);
94         badValue = true;
95     }
96     if (gDvm.offJavaLangString_hashCode != STRING_FIELDOFF_HASHCODE) {
97         LOGE("InlineNative: String.hashCode offset = %d, expected %d\n",
98             gDvm.offJavaLangString_hashCode, STRING_FIELDOFF_HASHCODE);
99         badValue = true;
100     }
101     if (badValue)
102         return false;
103
104     android_atomic_release_store(1, &gDvm.javaLangStringReady);
105
106     return true;
107 }
108
109 /*
110  * Discard heap-allocated storage.
111  */
112 void dvmStringShutdown()
113 {
114     // currently unused
115 }
116
117 /*
118  * Compute a hash code on a UTF-8 string, for use with internal hash tables.
119  *
120  * This may or may not yield the same results as the java/lang/String
121  * computeHashCode() function.  (To make sure this doesn't get abused,
122  * I'm initializing the hash code to 1 so they *don't* match up.)
123  *
124  * It would be more correct to invoke dexGetUtf16FromUtf8() here and compute
125  * the hash with the result.  That way, if something encoded the same
126  * character in two different ways, the hash value would be the same.  For
127  * our purposes that isn't necessary.
128  */
129 u4 dvmComputeUtf8Hash(const char* utf8Str)
130 {
131     u4 hash = 1;
132
133     while (*utf8Str != '\0')
134         hash = hash * 31 + *utf8Str++;
135
136     return hash;
137 }
138
139 /*
140  * Like "strlen", but for strings encoded with "modified" UTF-8.
141  *
142  * The value returned is the number of characters, which may or may not
143  * be the same as the number of bytes.
144  *
145  * (If this needs optimizing, try: mask against 0xa0, shift right 5,
146  * get increment {1-3} from table of 8 values.)
147  */
148 int dvmUtf8Len(const char* utf8Str)
149 {
150     int ic, len = 0;
151
152     while ((ic = *utf8Str++) != '\0') {
153         len++;
154         if ((ic & 0x80) != 0) {
155             /* two- or three-byte encoding */
156             utf8Str++;
157             if ((ic & 0x20) != 0) {
158                 /* three-byte encoding */
159                 utf8Str++;
160             }
161         }
162     }
163
164     return len;
165 }
166
167 /*
168  * Convert a "modified" UTF-8 string to UTF-16.
169  */
170 void dvmConvertUtf8ToUtf16(u2* utf16Str, const char* utf8Str)
171 {
172     while (*utf8Str != '\0')
173         *utf16Str++ = dexGetUtf16FromUtf8(&utf8Str);
174 }
175
176 /*
177  * Given a UTF-16 string, compute the length of the corresponding UTF-8
178  * string in bytes.
179  */
180 static int utf16_utf8ByteLen(const u2* utf16Str, int len)
181 {
182     int utf8Len = 0;
183
184     while (len--) {
185         unsigned int uic = *utf16Str++;
186
187         /*
188          * The most common case is (uic > 0 && uic <= 0x7f).
189          */
190         if (uic == 0 || uic > 0x7f) {
191             if (uic > 0x07ff)
192                 utf8Len += 3;
193             else /*(uic > 0x7f || uic == 0) */
194                 utf8Len += 2;
195         } else
196             utf8Len++;
197     }
198     return utf8Len;
199 }
200
201 /*
202  * Convert a UTF-16 string to UTF-8.
203  *
204  * Make sure you allocate "utf8Str" with the result of utf16_utf8ByteLen(),
205  * not just "len".
206  */
207 static void convertUtf16ToUtf8(char* utf8Str, const u2* utf16Str, int len)
208 {
209     assert(len >= 0);
210
211     while (len--) {
212         unsigned int uic = *utf16Str++;
213
214         /*
215          * The most common case is (uic > 0 && uic <= 0x7f).
216          */
217         if (uic == 0 || uic > 0x7f) {
218             if (uic > 0x07ff) {
219                 *utf8Str++ = (uic >> 12) | 0xe0;
220                 *utf8Str++ = ((uic >> 6) & 0x3f) | 0x80;
221                 *utf8Str++ = (uic & 0x3f) | 0x80;
222             } else /*(uic > 0x7f || uic == 0)*/ {
223                 *utf8Str++ = (uic >> 6) | 0xc0;
224                 *utf8Str++ = (uic & 0x3f) | 0x80;
225             }
226         } else {
227             *utf8Str++ = uic;
228         }
229     }
230
231     *utf8Str = '\0';
232 }
233
234 /*
235  * Use the java/lang/String.computeHashCode() algorithm.
236  */
237 static inline u4 dvmComputeUtf16Hash(const u2* utf16Str, int len)
238 {
239     u4 hash = 0;
240
241     while (len--)
242         hash = hash * 31 + *utf16Str++;
243
244     return hash;
245 }
246 u4 dvmComputeStringHash(const StringObject* strObj) {
247     ArrayObject* chars = (ArrayObject*) dvmGetFieldObject((Object*) strObj,
248                                 STRING_FIELDOFF_VALUE);
249     int offset, len;
250
251     len = dvmGetFieldInt((Object*) strObj, STRING_FIELDOFF_COUNT);
252     offset = dvmGetFieldInt((Object*) strObj, STRING_FIELDOFF_OFFSET);
253
254     return dvmComputeUtf16Hash((u2*) chars->contents + offset, len);
255 }
256
257 /*
258  * Create a new java/lang/String object, using the string data in "utf8Str".
259  *
260  * The caller must call dvmReleaseTrackedAlloc() on the return value.
261  *
262  * Returns NULL and throws an exception on failure.
263  */
264 StringObject* dvmCreateStringFromCstr(const char* utf8Str)
265 {
266     assert(utf8Str != NULL);
267     return dvmCreateStringFromCstrAndLength(utf8Str, dvmUtf8Len(utf8Str));
268 }
269
270 /*
271  * Create a java/lang/String from a C string, given its UTF-16 length
272  * (number of UTF-16 code points).
273  *
274  * The caller must call dvmReleaseTrackedAlloc() on the return value.
275  *
276  * Returns NULL and throws an exception on failure.
277  */
278 StringObject* dvmCreateStringFromCstrAndLength(const char* utf8Str,
279     u4 utf16Length)
280 {
281     StringObject* newObj;
282     ArrayObject* chars;
283     u4 hashCode = 0;
284
285     //LOGV("Creating String from '%s'\n", utf8Str);
286     assert(utf8Str != NULL);
287
288     if (gDvm.javaLangStringReady <= 0) {
289         if (!stringStartup())
290             return NULL;
291     }
292
293     /* init before alloc */
294     if (!dvmIsClassInitialized(gDvm.classJavaLangString) &&
295         !dvmInitClass(gDvm.classJavaLangString))
296     {
297         return NULL;
298     }
299
300     newObj = (StringObject*) dvmAllocObject(gDvm.classJavaLangString,
301                 ALLOC_DEFAULT);
302     if (newObj == NULL)
303         return NULL;
304
305     chars = dvmAllocPrimitiveArray('C', utf16Length, ALLOC_DEFAULT);
306     if (chars == NULL) {
307         dvmReleaseTrackedAlloc((Object*) newObj, NULL);
308         return NULL;
309     }
310     dvmConvertUtf8ToUtf16((u2*)chars->contents, utf8Str);
311     hashCode = dvmComputeUtf16Hash((u2*) chars->contents, utf16Length);
312
313     dvmSetFieldObject((Object*)newObj, STRING_FIELDOFF_VALUE,
314         (Object*)chars);
315     dvmReleaseTrackedAlloc((Object*) chars, NULL);
316     dvmSetFieldInt((Object*)newObj, STRING_FIELDOFF_COUNT, utf16Length);
317     dvmSetFieldInt((Object*)newObj, STRING_FIELDOFF_HASHCODE, hashCode);
318     /* leave offset set to zero */
319
320     /* debugging stuff */
321     //dvmDumpObject((Object*)newObj);
322     //printHexDumpEx(ANDROID_LOG_DEBUG, chars->contents, utf16Length * 2,
323     //    kHexDumpMem);
324
325     /* caller may need to dvmReleaseTrackedAlloc(newObj) */
326     return newObj;
327 }
328
329 /*
330  * Create a new java/lang/String object, using the Unicode data.
331  */
332 StringObject* dvmCreateStringFromUnicode(const u2* unichars, int len)
333 {
334     StringObject* newObj;
335     ArrayObject* chars;
336     u4 hashCode = 0;
337
338     /* we allow a null pointer if the length is zero */
339     assert(len == 0 || unichars != NULL);
340
341     if (gDvm.javaLangStringReady <= 0) {
342         if (!stringStartup())
343             return NULL;
344     }
345
346     /* init before alloc */
347     if (!dvmIsClassInitialized(gDvm.classJavaLangString) &&
348         !dvmInitClass(gDvm.classJavaLangString))
349     {
350         return NULL;
351     }
352
353     newObj = (StringObject*) dvmAllocObject(gDvm.classJavaLangString,
354         ALLOC_DEFAULT);
355     if (newObj == NULL)
356         return NULL;
357
358     chars = dvmAllocPrimitiveArray('C', len, ALLOC_DEFAULT);
359     if (chars == NULL) {
360         dvmReleaseTrackedAlloc((Object*) newObj, NULL);
361         return NULL;
362     }
363     if (len > 0)
364         memcpy(chars->contents, unichars, len * sizeof(u2));
365     hashCode = dvmComputeUtf16Hash((u2*) chars->contents, len);
366
367     dvmSetFieldObject((Object*)newObj, STRING_FIELDOFF_VALUE,
368         (Object*)chars);
369     dvmReleaseTrackedAlloc((Object*) chars, NULL);
370     dvmSetFieldInt((Object*)newObj, STRING_FIELDOFF_COUNT, len);
371     dvmSetFieldInt((Object*)newObj, STRING_FIELDOFF_HASHCODE, hashCode);
372     /* leave offset set to zero */
373
374     /* debugging stuff */
375     //dvmDumpObject((Object*)newObj);
376     //printHexDumpEx(ANDROID_LOG_DEBUG, chars->contents, len*2, kHexDumpMem);
377
378     /* caller must dvmReleaseTrackedAlloc(newObj) */
379     return newObj;
380 }
381
382 /*
383  * Create a new C string from a java/lang/String object.
384  *
385  * Returns NULL if the object is NULL.
386  */
387 char* dvmCreateCstrFromString(StringObject* jstr)
388 {
389     char* newStr;
390     ArrayObject* chars;
391     int len, byteLen, offset;
392     const u2* data;
393
394     assert(gDvm.javaLangStringReady > 0);
395
396     if (jstr == NULL)
397         return NULL;
398
399     len = dvmGetFieldInt((Object*) jstr, STRING_FIELDOFF_COUNT);
400     offset = dvmGetFieldInt((Object*) jstr, STRING_FIELDOFF_OFFSET);
401     chars = (ArrayObject*) dvmGetFieldObject((Object*) jstr,
402                                 STRING_FIELDOFF_VALUE);
403     data = (const u2*) chars->contents + offset;
404     assert(offset + len <= (int) chars->length);
405
406     byteLen = utf16_utf8ByteLen(data, len);
407     newStr = (char*) malloc(byteLen+1);
408     if (newStr == NULL)
409         return NULL;
410     convertUtf16ToUtf8(newStr, data, len);
411
412     return newStr;
413 }
414
415 /*
416  * Create a UTF-8 C string from a region of a java/lang/String.  (Used by
417  * the JNI GetStringUTFRegion call.)
418  */
419 void dvmCreateCstrFromStringRegion(StringObject* jstr, int start, int len,
420     char* buf)
421 {
422     const u2* data;
423
424     data = dvmStringChars(jstr) + start;
425     convertUtf16ToUtf8(buf, data, len);
426 }
427
428 /*
429  * Compute the length, in modified UTF-8, of a java/lang/String object.
430  *
431  * Does not include the terminating null byte.
432  */
433 int dvmStringUtf8ByteLen(StringObject* jstr)
434 {
435     ArrayObject* chars;
436     int len, offset;
437     const u2* data;
438
439     assert(gDvm.javaLangStringReady > 0);
440
441     if (jstr == NULL)
442         return 0;       // should we throw something?  assert?
443
444     len = dvmGetFieldInt((Object*) jstr, STRING_FIELDOFF_COUNT);
445     offset = dvmGetFieldInt((Object*) jstr, STRING_FIELDOFF_OFFSET);
446     chars = (ArrayObject*) dvmGetFieldObject((Object*) jstr,
447                                 STRING_FIELDOFF_VALUE);
448     data = (const u2*) chars->contents + offset;
449     assert(offset + len <= (int) chars->length);
450
451     return utf16_utf8ByteLen(data, len);
452 }
453
454 /*
455  * Get the string's length.
456  */
457 int dvmStringLen(StringObject* jstr)
458 {
459     return dvmGetFieldInt((Object*) jstr, STRING_FIELDOFF_COUNT);
460 }
461
462 /*
463  * Get the char[] object from the String.
464  */
465 ArrayObject* dvmStringCharArray(StringObject* jstr)
466 {
467     return (ArrayObject*) dvmGetFieldObject((Object*) jstr,
468                                 STRING_FIELDOFF_VALUE);
469 }
470
471 /*
472  * Get the string's data.
473  */
474 const u2* dvmStringChars(StringObject* jstr)
475 {
476     ArrayObject* chars;
477     int offset;
478
479     offset = dvmGetFieldInt((Object*) jstr, STRING_FIELDOFF_OFFSET);
480     chars = (ArrayObject*) dvmGetFieldObject((Object*) jstr,
481                                 STRING_FIELDOFF_VALUE);
482     return (const u2*) chars->contents + offset;
483 }
484
485
486 /*
487  * Compare two String objects.
488  *
489  * This is a dvmHashTableLookup() callback.  The function has already
490  * compared their hash values; we need to do a full compare to ensure
491  * that the strings really match.
492  */
493 int dvmHashcmpStrings(const void* vstrObj1, const void* vstrObj2)
494 {
495     const StringObject* strObj1 = (const StringObject*) vstrObj1;
496     const StringObject* strObj2 = (const StringObject*) vstrObj2;
497     ArrayObject* chars1;
498     ArrayObject* chars2;
499     int len1, len2, offset1, offset2;
500
501     assert(gDvm.javaLangStringReady > 0);
502
503     /* get offset and length into char array; all values are in 16-bit units */
504     len1 = dvmGetFieldInt((Object*) strObj1, STRING_FIELDOFF_COUNT);
505     offset1 = dvmGetFieldInt((Object*) strObj1, STRING_FIELDOFF_OFFSET);
506     len2 = dvmGetFieldInt((Object*) strObj2, STRING_FIELDOFF_COUNT);
507     offset2 = dvmGetFieldInt((Object*) strObj2, STRING_FIELDOFF_OFFSET);
508     if (len1 != len2)
509         return len1 - len2;
510
511     chars1 = (ArrayObject*) dvmGetFieldObject((Object*) strObj1,
512                                 STRING_FIELDOFF_VALUE);
513     chars2 = (ArrayObject*) dvmGetFieldObject((Object*) strObj2,
514                                 STRING_FIELDOFF_VALUE);
515
516     /* damage here actually indicates a broken java/lang/String */
517     assert(offset1 + len1 <= (int) chars1->length);
518     assert(offset2 + len2 <= (int) chars2->length);
519
520     return memcmp((const u2*) chars1->contents + offset1,
521                   (const u2*) chars2->contents + offset2,
522                   len1 * sizeof(u2));
523 }