| /* |
| * Copyright (C) 2008 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "java_lang_StringFactory.h" |
| |
| #include "common_throws.h" |
| #include "handle_scope-inl.h" |
| #include "jni/jni_internal.h" |
| #include "mirror/object-inl.h" |
| #include "mirror/string-alloc-inl.h" |
| #include "native_util.h" |
| #include "nativehelper/jni_macros.h" |
| #include "nativehelper/scoped_local_ref.h" |
| #include "nativehelper/scoped_primitive_array.h" |
| #include "scoped_fast_native_object_access-inl.h" |
| #include "scoped_thread_state_change-inl.h" |
| |
| namespace art HIDDEN { |
| |
| static jstring StringFactory_newStringFromBytes(JNIEnv* env, jclass, jbyteArray java_data, |
| jint high, jint offset, jint byte_count) { |
| ScopedFastNativeObjectAccess soa(env); |
| if (UNLIKELY(java_data == nullptr)) { |
| ThrowNullPointerException("data == null"); |
| return nullptr; |
| } |
| StackHandleScope<1> hs(soa.Self()); |
| Handle<mirror::ByteArray> byte_array(hs.NewHandle(soa.Decode<mirror::ByteArray>(java_data))); |
| int32_t data_size = byte_array->GetLength(); |
| if ((offset | byte_count) < 0 || byte_count > data_size - offset) { |
| soa.Self()->ThrowNewExceptionF("Ljava/lang/StringIndexOutOfBoundsException;", |
| "length=%d; regionStart=%d; regionLength=%d", data_size, |
| offset, byte_count); |
| return nullptr; |
| } |
| gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator(); |
| ObjPtr<mirror::String> result = mirror::String::AllocFromByteArray(soa.Self(), |
| byte_count, |
| byte_array, |
| offset, |
| high, |
| allocator_type); |
| return soa.AddLocalReference<jstring>(result); |
| } |
| |
| static jstring StringFactory_newStringFromUtf16Bytes( |
| JNIEnv* env, jclass, jbyteArray java_data, jint offset, jint char_count) { |
| ScopedFastNativeObjectAccess soa(env); |
| if (UNLIKELY(java_data == nullptr)) { |
| ThrowNullPointerException("data == null"); |
| return nullptr; |
| } |
| StackHandleScope<1> hs(soa.Self()); |
| Handle<mirror::ByteArray> byte_array(hs.NewHandle(soa.Decode<mirror::ByteArray>(java_data))); |
| int32_t data_size = byte_array->GetLength(); |
| DCHECK_GE(data_size, 0); |
| if (offset < 0 || |
| offset > data_size || |
| static_cast<uint32_t>(char_count) > (static_cast<uint32_t>(data_size - offset) >> 1)) { |
| soa.Self()->ThrowNewExceptionF("Ljava/lang/StringIndexOutOfBoundsException;", |
| "length=%d; regionStart=%d; bytePairLength=%d", |
| data_size, |
| offset, |
| char_count); |
| return nullptr; |
| } |
| gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator(); |
| ObjPtr<mirror::String> result = mirror::String::AllocFromUtf16ByteArray(soa.Self(), |
| char_count, |
| byte_array, |
| offset, |
| allocator_type); |
| return soa.AddLocalReference<jstring>(result); |
| } |
| |
| // The char array passed as `java_data` must not be a null reference. |
| static jstring StringFactory_newStringFromChars(JNIEnv* env, jclass, jint offset, |
| jint char_count, jcharArray java_data) { |
| DCHECK(java_data != nullptr); |
| ScopedFastNativeObjectAccess soa(env); |
| StackHandleScope<1> hs(soa.Self()); |
| Handle<mirror::CharArray> char_array(hs.NewHandle(soa.Decode<mirror::CharArray>(java_data))); |
| gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator(); |
| ObjPtr<mirror::String> result = mirror::String::AllocFromCharArray(soa.Self(), |
| char_count, |
| char_array, |
| offset, |
| allocator_type); |
| return soa.AddLocalReference<jstring>(result); |
| } |
| |
| static jstring StringFactory_newStringFromString(JNIEnv* env, jclass, jstring to_copy) { |
| ScopedFastNativeObjectAccess soa(env); |
| if (UNLIKELY(to_copy == nullptr)) { |
| ThrowNullPointerException("toCopy == null"); |
| return nullptr; |
| } |
| StackHandleScope<1> hs(soa.Self()); |
| Handle<mirror::String> string(hs.NewHandle(soa.Decode<mirror::String>(to_copy))); |
| gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator(); |
| ObjPtr<mirror::String> result = mirror::String::AllocFromString(soa.Self(), |
| string->GetLength(), |
| string, |
| /*offset=*/ 0, |
| allocator_type); |
| return soa.AddLocalReference<jstring>(result); |
| } |
| |
| static jstring StringFactory_newStringFromUtf8Bytes(JNIEnv* env, jclass, jbyteArray java_data, |
| jint offset, jint byte_count) { |
| // Local Define in here |
| static const jchar kReplacementChar = 0xfffd; |
| static const int kDefaultBufferSize = 256; |
| static const int kTableUtf8Needed[] = { |
| // 0 1 2 3 4 5 6 7 8 9 a b c d e f |
| 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xc0 - 0xcf |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xd0 - 0xdf |
| 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xe0 - 0xef |
| 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xf0 - 0xff |
| }; |
| |
| ScopedFastNativeObjectAccess soa(env); |
| if (UNLIKELY(java_data == nullptr)) { |
| ThrowNullPointerException("data == null"); |
| return nullptr; |
| } |
| |
| StackHandleScope<1> hs(soa.Self()); |
| Handle<mirror::ByteArray> byte_array(hs.NewHandle(soa.Decode<mirror::ByteArray>(java_data))); |
| int32_t data_size = byte_array->GetLength(); |
| if ((offset | byte_count) < 0 || byte_count > data_size - offset) { |
| soa.Self()->ThrowNewExceptionF("Ljava/lang/StringIndexOutOfBoundsException;", |
| "length=%d; regionStart=%d; regionLength=%d", data_size, |
| offset, byte_count); |
| return nullptr; |
| } |
| |
| /* |
| * This code converts a UTF-8 byte sequence to a Java String (UTF-16). |
| * It implements the W3C recommended UTF-8 decoder. |
| * https://www.w3.org/TR/encoding/#utf-8-decoder |
| * |
| * Unicode 3.2 Well-Formed UTF-8 Byte Sequences |
| * Code Points First Second Third Fourth |
| * U+0000..U+007F 00..7F |
| * U+0080..U+07FF C2..DF 80..BF |
| * U+0800..U+0FFF E0 A0..BF 80..BF |
| * U+1000..U+CFFF E1..EC 80..BF 80..BF |
| * U+D000..U+D7FF ED 80..9F 80..BF |
| * U+E000..U+FFFF EE..EF 80..BF 80..BF |
| * U+10000..U+3FFFF F0 90..BF 80..BF 80..BF |
| * U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF |
| * U+100000..U+10FFFF F4 80..8F 80..BF 80..BF |
| * |
| * Please refer to Unicode as the authority. |
| * p.126 Table 3-7 in http://www.unicode.org/versions/Unicode10.0.0/ch03.pdf |
| * |
| * Handling Malformed Input |
| * The maximal subpart should be replaced by a single U+FFFD. Maximal subpart is |
| * the longest code unit subsequence starting at an unconvertible offset that is either |
| * 1) the initial subsequence of a well-formed code unit sequence, or |
| * 2) a subsequence of length one: |
| * One U+FFFD should be emitted for every sequence of bytes that is an incomplete prefix |
| * of a valid sequence, and with the conversion to restart after the incomplete sequence. |
| * |
| * For example, in byte sequence "41 C0 AF 41 F4 80 80 41", the maximal subparts are |
| * "C0", "AF", and "F4 80 80". "F4 80 80" can be the initial subsequence of "F4 80 80 80", |
| * but "C0" can't be the initial subsequence of any well-formed code unit sequence. |
| * Thus, the output should be "A\ufffd\ufffdA\ufffdA". |
| * |
| * Please refer to section "Best Practices for Using U+FFFD." in |
| * http://www.unicode.org/versions/Unicode10.0.0/ch03.pdf |
| */ |
| |
| // Initial value |
| jchar stack_buffer[kDefaultBufferSize]; |
| std::unique_ptr<jchar[]> allocated_buffer; |
| jchar* v; |
| if (byte_count <= kDefaultBufferSize) { |
| v = stack_buffer; |
| } else { |
| allocated_buffer.reset(new jchar[byte_count]); |
| v = allocated_buffer.get(); |
| } |
| |
| jbyte* d = byte_array->GetData(); |
| DCHECK(d != nullptr); |
| |
| int idx = offset; |
| int last = offset + byte_count; |
| int s = 0; |
| |
| int code_point = 0; |
| int utf8_bytes_seen = 0; |
| int utf8_bytes_needed = 0; |
| int lower_bound = 0x80; |
| int upper_bound = 0xbf; |
| while (idx < last) { |
| int b = d[idx++] & 0xff; |
| if (utf8_bytes_needed == 0) { |
| if ((b & 0x80) == 0) { // ASCII char. 0xxxxxxx |
| v[s++] = (jchar) b; |
| continue; |
| } |
| |
| if ((b & 0x40) == 0) { // 10xxxxxx is illegal as first byte |
| v[s++] = kReplacementChar; |
| continue; |
| } |
| |
| // 11xxxxxx |
| int tableLookupIndex = b & 0x3f; |
| utf8_bytes_needed = kTableUtf8Needed[tableLookupIndex]; |
| if (utf8_bytes_needed == 0) { |
| v[s++] = kReplacementChar; |
| continue; |
| } |
| |
| // utf8_bytes_needed |
| // 1: b & 0x1f |
| // 2: b & 0x0f |
| // 3: b & 0x07 |
| code_point = b & (0x3f >> utf8_bytes_needed); |
| if (b == 0xe0) { |
| lower_bound = 0xa0; |
| } else if (b == 0xed) { |
| upper_bound = 0x9f; |
| } else if (b == 0xf0) { |
| lower_bound = 0x90; |
| } else if (b == 0xf4) { |
| upper_bound = 0x8f; |
| } |
| } else { |
| if (b < lower_bound || b > upper_bound) { |
| // The bytes seen are ill-formed. Substitute them with U+FFFD |
| v[s++] = kReplacementChar; |
| code_point = 0; |
| utf8_bytes_needed = 0; |
| utf8_bytes_seen = 0; |
| lower_bound = 0x80; |
| upper_bound = 0xbf; |
| /* |
| * According to the Unicode Standard, |
| * "a UTF-8 conversion process is required to never consume well-formed |
| * subsequences as part of its error handling for ill-formed subsequences" |
| * The current byte could be part of well-formed subsequences. Reduce the |
| * index by 1 to parse it in next loop. |
| */ |
| idx--; |
| continue; |
| } |
| |
| lower_bound = 0x80; |
| upper_bound = 0xbf; |
| code_point = (code_point << 6) | (b & 0x3f); |
| utf8_bytes_seen++; |
| if (utf8_bytes_needed != utf8_bytes_seen) { |
| continue; |
| } |
| |
| // Encode chars from U+10000 up as surrogate pairs |
| if (code_point < 0x10000) { |
| v[s++] = (jchar) code_point; |
| } else { |
| v[s++] = (jchar) ((code_point >> 10) + 0xd7c0); |
| v[s++] = (jchar) ((code_point & 0x3ff) + 0xdc00); |
| } |
| |
| utf8_bytes_seen = 0; |
| utf8_bytes_needed = 0; |
| code_point = 0; |
| } |
| } |
| |
| // The bytes seen are ill-formed. Substitute them by U+FFFD |
| if (utf8_bytes_needed != 0) { |
| v[s++] = kReplacementChar; |
| } |
| |
| ObjPtr<mirror::String> result = mirror::String::AllocFromUtf16(soa.Self(), s, v); |
| return soa.AddLocalReference<jstring>(result); |
| } |
| |
| static JNINativeMethod gMethods[] = { |
| FAST_NATIVE_METHOD(StringFactory, newStringFromBytes, "([BIII)Ljava/lang/String;"), |
| FAST_NATIVE_METHOD(StringFactory, newStringFromChars, "(II[C)Ljava/lang/String;"), |
| FAST_NATIVE_METHOD(StringFactory, newStringFromString, "(Ljava/lang/String;)Ljava/lang/String;"), |
| FAST_NATIVE_METHOD(StringFactory, newStringFromUtf8Bytes, "([BII)Ljava/lang/String;"), |
| FAST_NATIVE_METHOD(StringFactory, newStringFromUtf16Bytes, "([BII)Ljava/lang/String;"), |
| }; |
| |
| void register_java_lang_StringFactory(JNIEnv* env) { |
| REGISTER_NATIVE_METHODS("java/lang/StringFactory"); |
| } |
| |
| } // namespace art |