1f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/* 2f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project 3f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * 4f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 5f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * you may not use this file except in compliance with the License. 6f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * You may obtain a copy of the License at 7f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * 8f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 9f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * 10f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 11f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 12f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * See the License for the specific language governing permissions and 14f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * limitations under the License. 15f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */ 1659a434629ba06d4decf7bc88a62ae370a1935f0eAndy McFadden 17f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/* 18f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * UTF-8 and Unicode string manipulation, plus java/lang/String convenience 19f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * functions. 20f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * 21f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * In most cases we populate the fields in the String object directly, 22f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * rather than going through an instance field lookup. 23f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */ 24f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project#include "Dalvik.h" 25f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project#include <stdlib.h> 26f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 27f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/* 28cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein * Allocate a new instance of the class String, performing first-use 29cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein * initialization of the class if necessary. Upon success, the 30cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein * returned value will have all its fields except hashCode already 31cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein * filled in, including a reference to a newly-allocated char[] for 32cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein * the contents, sized as given. Additionally, a reference to the 33cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein * chars array is stored to the pChars pointer. Callers must 34cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein * subsequently call dvmReleaseTrackedAlloc() on the result pointer. 35cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein * This function returns NULL on failure. 36f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */ 37cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornsteinstatic StringObject* makeStringObject(u4 charsLength, ArrayObject** pChars) 38f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project{ 39cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein /* 40cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein * The String class should have already gotten found (but not 41cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein * necessarily initialized) before making it here. We assert it 42cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein * explicitly, since historically speaking, we have had bugs with 43cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein * regard to when the class String gets set up. The assert helps 44cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein * make any regressions easier to diagnose. 45cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein */ 46cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein assert(gDvm.classJavaLangString != NULL); 47cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein 48cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein if (!dvmIsClassInitialized(gDvm.classJavaLangString)) { 49cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein /* Perform first-time use initialization of the class. */ 50cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein if (!dvmInitClass(gDvm.classJavaLangString)) { 51c1a4ab9c313d8a3d12007f2dbef7b5a6fa4ac2efSteve Block ALOGE("FATAL: Could not initialize class String"); 52cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein dvmAbort(); 53cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein } 54deeeeb264fc6f4ab7f5cb6e01b9dd76f487ff914Andy McFadden } 55f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 56cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein Object* result = dvmAllocObject(gDvm.classJavaLangString, ALLOC_DEFAULT); 57cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein if (result == NULL) { 58cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein return NULL; 59f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project } 60f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 61cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein ArrayObject* chars = dvmAllocPrimitiveArray('C', charsLength, ALLOC_DEFAULT); 62cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein if (chars == NULL) { 63cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein dvmReleaseTrackedAlloc(result, NULL); 64cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein return NULL; 6559a434629ba06d4decf7bc88a62ae370a1935f0eAndy McFadden } 6659a434629ba06d4decf7bc88a62ae370a1935f0eAndy McFadden 67cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein dvmSetFieldInt(result, STRING_FIELDOFF_COUNT, charsLength); 68cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein dvmSetFieldObject(result, STRING_FIELDOFF_VALUE, (Object*) chars); 69cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein dvmReleaseTrackedAlloc((Object*) chars, NULL); 70cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein /* Leave offset and hashCode set to zero. */ 71f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 72cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein *pChars = chars; 73cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein return (StringObject*) result; 74f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project} 75f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 76f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/* 77f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Compute a hash code on a UTF-8 string, for use with internal hash tables. 78f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * 79f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * This may or may not yield the same results as the java/lang/String 80f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * computeHashCode() function. (To make sure this doesn't get abused, 81f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * I'm initializing the hash code to 1 so they *don't* match up.) 82f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * 83f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * It would be more correct to invoke dexGetUtf16FromUtf8() here and compute 84f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * the hash with the result. That way, if something encoded the same 85f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * character in two different ways, the hash value would be the same. For 86f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * our purposes that isn't necessary. 87f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */ 88f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectu4 dvmComputeUtf8Hash(const char* utf8Str) 89f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project{ 90f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project u4 hash = 1; 91f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 92f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project while (*utf8Str != '\0') 93f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project hash = hash * 31 + *utf8Str++; 94f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 95f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return hash; 96f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project} 97f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 98f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/* 99f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Like "strlen", but for strings encoded with "modified" UTF-8. 100f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * 101f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * The value returned is the number of characters, which may or may not 102f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * be the same as the number of bytes. 103f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * 104f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * (If this needs optimizing, try: mask against 0xa0, shift right 5, 105f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * get increment {1-3} from table of 8 values.) 106f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */ 107d5c36b9040bd26a81219a7f399513526f9b46324Carl Shapirosize_t dvmUtf8Len(const char* utf8Str) 108f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project{ 109d5c36b9040bd26a81219a7f399513526f9b46324Carl Shapiro size_t len = 0; 110d5c36b9040bd26a81219a7f399513526f9b46324Carl Shapiro int ic; 111f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 112f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project while ((ic = *utf8Str++) != '\0') { 113f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project len++; 114f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project if ((ic & 0x80) != 0) { 115f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project /* two- or three-byte encoding */ 116f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project utf8Str++; 117f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project if ((ic & 0x20) != 0) { 118f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project /* three-byte encoding */ 119f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project utf8Str++; 120f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project } 121f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project } 122f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project } 123f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 124f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return len; 125f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project} 126f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 127f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/* 128f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Convert a "modified" UTF-8 string to UTF-16. 129f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */ 130f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectvoid dvmConvertUtf8ToUtf16(u2* utf16Str, const char* utf8Str) 131f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project{ 132f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project while (*utf8Str != '\0') 133f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *utf16Str++ = dexGetUtf16FromUtf8(&utf8Str); 134f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project} 135f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 136f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/* 137f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Given a UTF-16 string, compute the length of the corresponding UTF-8 138f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * string in bytes. 139f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */ 140f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectstatic int utf16_utf8ByteLen(const u2* utf16Str, int len) 141f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project{ 142f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project int utf8Len = 0; 143f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 144f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project while (len--) { 145f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project unsigned int uic = *utf16Str++; 146f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 147f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project /* 148f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * The most common case is (uic > 0 && uic <= 0x7f). 149f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */ 150f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project if (uic == 0 || uic > 0x7f) { 151f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project if (uic > 0x07ff) 152f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project utf8Len += 3; 153f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project else /*(uic > 0x7f || uic == 0) */ 154f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project utf8Len += 2; 155f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project } else 156f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project utf8Len++; 157f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project } 158f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return utf8Len; 159f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project} 160f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 161f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/* 162f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Convert a UTF-16 string to UTF-8. 163f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * 164f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Make sure you allocate "utf8Str" with the result of utf16_utf8ByteLen(), 165f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * not just "len". 166f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */ 167f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectstatic void convertUtf16ToUtf8(char* utf8Str, const u2* utf16Str, int len) 168f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project{ 169f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project assert(len >= 0); 170f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 171f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project while (len--) { 172f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project unsigned int uic = *utf16Str++; 173f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 174f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project /* 175f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * The most common case is (uic > 0 && uic <= 0x7f). 176f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */ 177f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project if (uic == 0 || uic > 0x7f) { 178f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project if (uic > 0x07ff) { 179f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *utf8Str++ = (uic >> 12) | 0xe0; 180f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *utf8Str++ = ((uic >> 6) & 0x3f) | 0x80; 181f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *utf8Str++ = (uic & 0x3f) | 0x80; 182f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project } else /*(uic > 0x7f || uic == 0)*/ { 183f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *utf8Str++ = (uic >> 6) | 0xc0; 184f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *utf8Str++ = (uic & 0x3f) | 0x80; 185f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project } 186f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project } else { 187f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *utf8Str++ = uic; 188f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project } 189f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project } 190f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 191f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *utf8Str = '\0'; 192f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project} 193f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 194f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/* 195f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Use the java/lang/String.computeHashCode() algorithm. 196f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */ 197d5c36b9040bd26a81219a7f399513526f9b46324Carl Shapirostatic inline u4 computeUtf16Hash(const u2* utf16Str, size_t len) 198f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project{ 199f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project u4 hash = 0; 200f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 201f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project while (len--) 202f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project hash = hash * 31 + *utf16Str++; 203f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 204f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return hash; 205f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project} 206d5c36b9040bd26a81219a7f399513526f9b46324Carl Shapiro 207dc9e44cc0af797679822484d88ef76bff15ffc98Carl Shapirou4 dvmComputeStringHash(StringObject* strObj) { 208dc9e44cc0af797679822484d88ef76bff15ffc98Carl Shapiro int hashCode = dvmGetFieldInt(strObj, STRING_FIELDOFF_HASHCODE); 209dc9e44cc0af797679822484d88ef76bff15ffc98Carl Shapiro if (hashCode != 0) { 210dc9e44cc0af797679822484d88ef76bff15ffc98Carl Shapiro return hashCode; 211dc9e44cc0af797679822484d88ef76bff15ffc98Carl Shapiro } 212dc9e44cc0af797679822484d88ef76bff15ffc98Carl Shapiro int len = dvmGetFieldInt(strObj, STRING_FIELDOFF_COUNT); 213dc9e44cc0af797679822484d88ef76bff15ffc98Carl Shapiro int offset = dvmGetFieldInt(strObj, STRING_FIELDOFF_OFFSET); 214fe7f2b3920bf5d66eda262e643245b03df3e57c8Elliott Hughes ArrayObject* chars = 215fe7f2b3920bf5d66eda262e643245b03df3e57c8Elliott Hughes (ArrayObject*) dvmGetFieldObject(strObj, STRING_FIELDOFF_VALUE); 216dc9e44cc0af797679822484d88ef76bff15ffc98Carl Shapiro hashCode = computeUtf16Hash((u2*)(void*)chars->contents + offset, len); 217dc9e44cc0af797679822484d88ef76bff15ffc98Carl Shapiro dvmSetFieldInt(strObj, STRING_FIELDOFF_HASHCODE, hashCode); 218dc9e44cc0af797679822484d88ef76bff15ffc98Carl Shapiro return hashCode; 219f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project} 220f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 2210fbb7030fff58e25718291811394487d95d95a3eElliott HughesStringObject* dvmCreateStringFromCstr(const char* utf8Str) { 222f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project assert(utf8Str != NULL); 22381f3ebe03cd33c9003641084bece0604ee68bf88Barry Hayes return dvmCreateStringFromCstrAndLength(utf8Str, dvmUtf8Len(utf8Str)); 224f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project} 225f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 2260fbb7030fff58e25718291811394487d95d95a3eElliott HughesStringObject* dvmCreateStringFromCstr(const std::string& utf8Str) { 2270fbb7030fff58e25718291811394487d95d95a3eElliott Hughes return dvmCreateStringFromCstr(utf8Str.c_str()); 2280fbb7030fff58e25718291811394487d95d95a3eElliott Hughes} 2290fbb7030fff58e25718291811394487d95d95a3eElliott Hughes 230f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/* 231f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Create a java/lang/String from a C string, given its UTF-16 length 232f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * (number of UTF-16 code points). 233f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * 23481f3ebe03cd33c9003641084bece0604ee68bf88Barry Hayes * The caller must call dvmReleaseTrackedAlloc() on the return value. 235f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * 236f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Returns NULL and throws an exception on failure. 237f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */ 238f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source ProjectStringObject* dvmCreateStringFromCstrAndLength(const char* utf8Str, 239d5c36b9040bd26a81219a7f399513526f9b46324Carl Shapiro size_t utf16Length) 240f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project{ 241f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project assert(utf8Str != NULL); 242f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 243cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein ArrayObject* chars; 244cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein StringObject* newObj = makeStringObject(utf16Length, &chars); 245cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein if (newObj == NULL) { 246f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return NULL; 247f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project } 248f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 249d5c36b9040bd26a81219a7f399513526f9b46324Carl Shapiro dvmConvertUtf8ToUtf16((u2*)(void*)chars->contents, utf8Str); 250f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 251d5c36b9040bd26a81219a7f399513526f9b46324Carl Shapiro u4 hashCode = computeUtf16Hash((u2*)(void*)chars->contents, utf16Length); 252cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein dvmSetFieldInt((Object*) newObj, STRING_FIELDOFF_HASHCODE, hashCode); 253f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 254f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return newObj; 255f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project} 256f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 257f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/* 258cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein * Create a new java/lang/String object, using the given Unicode data. 259f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */ 260f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source ProjectStringObject* dvmCreateStringFromUnicode(const u2* unichars, int len) 261f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project{ 262cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein /* We allow a NULL pointer if the length is zero. */ 263f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project assert(len == 0 || unichars != NULL); 264f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 265cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein ArrayObject* chars; 266cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein StringObject* newObj = makeStringObject(len, &chars); 267cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein if (newObj == NULL) { 268f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return NULL; 269f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project } 270f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 271cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein if (len > 0) memcpy(chars->contents, unichars, len * sizeof(u2)); 272f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 273d5c36b9040bd26a81219a7f399513526f9b46324Carl Shapiro u4 hashCode = computeUtf16Hash((u2*)(void*)chars->contents, len); 27459a434629ba06d4decf7bc88a62ae370a1935f0eAndy McFadden dvmSetFieldInt((Object*)newObj, STRING_FIELDOFF_HASHCODE, hashCode); 275f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 276f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return newObj; 277f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project} 278f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 279f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/* 280f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Create a new C string from a java/lang/String object. 281f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * 282f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Returns NULL if the object is NULL. 283f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */ 284fe7f2b3920bf5d66eda262e643245b03df3e57c8Elliott Hugheschar* dvmCreateCstrFromString(const StringObject* jstr) 285f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project{ 286cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein assert(gDvm.classJavaLangString != NULL); 287fe7f2b3920bf5d66eda262e643245b03df3e57c8Elliott Hughes if (jstr == NULL) { 288f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return NULL; 289fe7f2b3920bf5d66eda262e643245b03df3e57c8Elliott Hughes } 290f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 291fe7f2b3920bf5d66eda262e643245b03df3e57c8Elliott Hughes int len = dvmGetFieldInt(jstr, STRING_FIELDOFF_COUNT); 292fe7f2b3920bf5d66eda262e643245b03df3e57c8Elliott Hughes int offset = dvmGetFieldInt(jstr, STRING_FIELDOFF_OFFSET); 293fe7f2b3920bf5d66eda262e643245b03df3e57c8Elliott Hughes ArrayObject* chars = 294fe7f2b3920bf5d66eda262e643245b03df3e57c8Elliott Hughes (ArrayObject*) dvmGetFieldObject(jstr, STRING_FIELDOFF_VALUE); 295fe7f2b3920bf5d66eda262e643245b03df3e57c8Elliott Hughes const u2* data = (const u2*)(void*)chars->contents + offset; 296f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project assert(offset + len <= (int) chars->length); 297f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 298fe7f2b3920bf5d66eda262e643245b03df3e57c8Elliott Hughes int byteLen = utf16_utf8ByteLen(data, len); 299fe7f2b3920bf5d66eda262e643245b03df3e57c8Elliott Hughes char* newStr = (char*) malloc(byteLen+1); 300fe7f2b3920bf5d66eda262e643245b03df3e57c8Elliott Hughes if (newStr == NULL) { 301f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return NULL; 302fe7f2b3920bf5d66eda262e643245b03df3e57c8Elliott Hughes } 303f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project convertUtf16ToUtf8(newStr, data, len); 304f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 305f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return newStr; 306f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project} 307f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 308d8a3f9fa1951e552f5f65c2914689083cc0c46c2Elliott Hughesvoid dvmGetStringUtfRegion(const StringObject* jstr, 309fe7f2b3920bf5d66eda262e643245b03df3e57c8Elliott Hughes int start, int len, char* buf) 310f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project{ 311d8a3f9fa1951e552f5f65c2914689083cc0c46c2Elliott Hughes const u2* data = jstr->chars() + start; 312f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project convertUtf16ToUtf8(buf, data, len); 313f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project} 314f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 315d8a3f9fa1951e552f5f65c2914689083cc0c46c2Elliott Hughesint StringObject::utfLength() const 316f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project{ 317cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein assert(gDvm.classJavaLangString != NULL); 318f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 319d8a3f9fa1951e552f5f65c2914689083cc0c46c2Elliott Hughes int len = dvmGetFieldInt(this, STRING_FIELDOFF_COUNT); 320d8a3f9fa1951e552f5f65c2914689083cc0c46c2Elliott Hughes int offset = dvmGetFieldInt(this, STRING_FIELDOFF_OFFSET); 321fe7f2b3920bf5d66eda262e643245b03df3e57c8Elliott Hughes ArrayObject* chars = 322d8a3f9fa1951e552f5f65c2914689083cc0c46c2Elliott Hughes (ArrayObject*) dvmGetFieldObject(this, STRING_FIELDOFF_VALUE); 323fe7f2b3920bf5d66eda262e643245b03df3e57c8Elliott Hughes const u2* data = (const u2*)(void*)chars->contents + offset; 324f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project assert(offset + len <= (int) chars->length); 325f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 326f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return utf16_utf8ByteLen(data, len); 327f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project} 328f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 329d8a3f9fa1951e552f5f65c2914689083cc0c46c2Elliott Hughesint StringObject::length() const 330f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project{ 331d8a3f9fa1951e552f5f65c2914689083cc0c46c2Elliott Hughes return dvmGetFieldInt(this, STRING_FIELDOFF_COUNT); 332f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project} 333f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 334d8a3f9fa1951e552f5f65c2914689083cc0c46c2Elliott HughesArrayObject* StringObject::array() const 335ab00d455ea67fbf4090567bb09ead8017896ea61Andy McFadden{ 336d8a3f9fa1951e552f5f65c2914689083cc0c46c2Elliott Hughes return (ArrayObject*) dvmGetFieldObject(this, STRING_FIELDOFF_VALUE); 337ab00d455ea67fbf4090567bb09ead8017896ea61Andy McFadden} 338ab00d455ea67fbf4090567bb09ead8017896ea61Andy McFadden 339d8a3f9fa1951e552f5f65c2914689083cc0c46c2Elliott Hughesconst u2* StringObject::chars() const 340f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project{ 341d8a3f9fa1951e552f5f65c2914689083cc0c46c2Elliott Hughes int offset = dvmGetFieldInt(this, STRING_FIELDOFF_OFFSET); 342fe7f2b3920bf5d66eda262e643245b03df3e57c8Elliott Hughes ArrayObject* chars = 343d8a3f9fa1951e552f5f65c2914689083cc0c46c2Elliott Hughes (ArrayObject*) dvmGetFieldObject(this, STRING_FIELDOFF_VALUE); 344d5c36b9040bd26a81219a7f399513526f9b46324Carl Shapiro return (const u2*)(void*)chars->contents + offset; 345f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project} 346f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 347f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 348f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/* 349f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Compare two String objects. 350f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * 351f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * This is a dvmHashTableLookup() callback. The function has already 352f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * compared their hash values; we need to do a full compare to ensure 353f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * that the strings really match. 354f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */ 355f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectint dvmHashcmpStrings(const void* vstrObj1, const void* vstrObj2) 356f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project{ 357f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project const StringObject* strObj1 = (const StringObject*) vstrObj1; 358f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project const StringObject* strObj2 = (const StringObject*) vstrObj2; 359f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 360cd380420ec1b52c3d6f0c7cc54b46ec29bb27dc0Dan Bornstein assert(gDvm.classJavaLangString != NULL); 361f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 362f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project /* get offset and length into char array; all values are in 16-bit units */ 363fe7f2b3920bf5d66eda262e643245b03df3e57c8Elliott Hughes int len1 = dvmGetFieldInt(strObj1, STRING_FIELDOFF_COUNT); 364fe7f2b3920bf5d66eda262e643245b03df3e57c8Elliott Hughes int offset1 = dvmGetFieldInt(strObj1, STRING_FIELDOFF_OFFSET); 365fe7f2b3920bf5d66eda262e643245b03df3e57c8Elliott Hughes int len2 = dvmGetFieldInt(strObj2, STRING_FIELDOFF_COUNT); 366fe7f2b3920bf5d66eda262e643245b03df3e57c8Elliott Hughes int offset2 = dvmGetFieldInt(strObj2, STRING_FIELDOFF_OFFSET); 367fe7f2b3920bf5d66eda262e643245b03df3e57c8Elliott Hughes if (len1 != len2) { 368f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project return len1 - len2; 369fe7f2b3920bf5d66eda262e643245b03df3e57c8Elliott Hughes } 370f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 371fe7f2b3920bf5d66eda262e643245b03df3e57c8Elliott Hughes ArrayObject* chars1 = 372fe7f2b3920bf5d66eda262e643245b03df3e57c8Elliott Hughes (ArrayObject*) dvmGetFieldObject(strObj1, STRING_FIELDOFF_VALUE); 373fe7f2b3920bf5d66eda262e643245b03df3e57c8Elliott Hughes ArrayObject* chars2 = 374fe7f2b3920bf5d66eda262e643245b03df3e57c8Elliott Hughes (ArrayObject*) dvmGetFieldObject(strObj2, STRING_FIELDOFF_VALUE); 375f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 376f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project /* damage here actually indicates a broken java/lang/String */ 377f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project assert(offset1 + len1 <= (int) chars1->length); 378f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project assert(offset2 + len2 <= (int) chars2->length); 379f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project 380d5c36b9040bd26a81219a7f399513526f9b46324Carl Shapiro return memcmp((const u2*)(void*)chars1->contents + offset1, 381d5c36b9040bd26a81219a7f399513526f9b46324Carl Shapiro (const u2*)(void*)chars2->contents + offset2, 382f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project len1 * sizeof(u2)); 383f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project} 38449dc060d5ae9bbcc78e570ec0dd244973e920fb6Elliott Hughes 3850fbb7030fff58e25718291811394487d95d95a3eElliott HughesArrayObject* dvmCreateStringArray(const std::vector<std::string>& strings) { 38649dc060d5ae9bbcc78e570ec0dd244973e920fb6Elliott Hughes Thread* self = dvmThreadSelf(); 38749dc060d5ae9bbcc78e570ec0dd244973e920fb6Elliott Hughes 3880fbb7030fff58e25718291811394487d95d95a3eElliott Hughes // Allocate an array to hold the String objects. 3890fbb7030fff58e25718291811394487d95d95a3eElliott Hughes ClassObject* elementClass = dvmFindArrayClassForElement(gDvm.classJavaLangString); 3900fbb7030fff58e25718291811394487d95d95a3eElliott Hughes ArrayObject* stringArray = dvmAllocArrayByClass(elementClass, strings.size(), ALLOC_DEFAULT); 39149dc060d5ae9bbcc78e570ec0dd244973e920fb6Elliott Hughes if (stringArray == NULL) { 3920fbb7030fff58e25718291811394487d95d95a3eElliott Hughes // Probably OOM. 39349dc060d5ae9bbcc78e570ec0dd244973e920fb6Elliott Hughes assert(dvmCheckException(self)); 39449dc060d5ae9bbcc78e570ec0dd244973e920fb6Elliott Hughes return NULL; 39549dc060d5ae9bbcc78e570ec0dd244973e920fb6Elliott Hughes } 39649dc060d5ae9bbcc78e570ec0dd244973e920fb6Elliott Hughes 3970fbb7030fff58e25718291811394487d95d95a3eElliott Hughes // Create the individual String objects and add them to the array. 3980fbb7030fff58e25718291811394487d95d95a3eElliott Hughes for (size_t i = 0; i < strings.size(); i++) { 3990fbb7030fff58e25718291811394487d95d95a3eElliott Hughes Object* str = (Object*) dvmCreateStringFromCstr(strings[i]); 40049dc060d5ae9bbcc78e570ec0dd244973e920fb6Elliott Hughes if (str == NULL) { 4010fbb7030fff58e25718291811394487d95d95a3eElliott Hughes // Probably OOM; drop out now. 40249dc060d5ae9bbcc78e570ec0dd244973e920fb6Elliott Hughes assert(dvmCheckException(self)); 40349dc060d5ae9bbcc78e570ec0dd244973e920fb6Elliott Hughes dvmReleaseTrackedAlloc((Object*) stringArray, self); 40449dc060d5ae9bbcc78e570ec0dd244973e920fb6Elliott Hughes return NULL; 40549dc060d5ae9bbcc78e570ec0dd244973e920fb6Elliott Hughes } 40649dc060d5ae9bbcc78e570ec0dd244973e920fb6Elliott Hughes dvmSetObjectArrayElement(stringArray, i, str); 40749dc060d5ae9bbcc78e570ec0dd244973e920fb6Elliott Hughes /* stored in tracked array, okay to release */ 40849dc060d5ae9bbcc78e570ec0dd244973e920fb6Elliott Hughes dvmReleaseTrackedAlloc(str, self); 40949dc060d5ae9bbcc78e570ec0dd244973e920fb6Elliott Hughes } 41049dc060d5ae9bbcc78e570ec0dd244973e920fb6Elliott Hughes 41149dc060d5ae9bbcc78e570ec0dd244973e920fb6Elliott Hughes return stringArray; 41249dc060d5ae9bbcc78e570ec0dd244973e920fb6Elliott Hughes} 413