1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/android/jni_string.h"
#include <array>
#include <string_view>
#include "base/android/jni_android.h"
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/utf_string_conversions.h"
// Size of buffer to allocate on the stack for string conversion.
#define BUFFER_SIZE 1024
namespace {
// Internal version that does not use a scoped local pointer.
jstring ConvertUTF16ToJavaStringImpl(JNIEnv* env, std::u16string_view str) {
jstring result = env->NewString(reinterpret_cast<const jchar*>(str.data()),
base::checked_cast<jsize>(str.length()));
base::android::CheckException(env);
return result;
}
} // namespace
namespace base {
namespace android {
void ConvertJavaStringToUTF8(JNIEnv* env, jstring str, std::string* result) {
if (!str) {
result->clear();
return;
}
const jsize length = env->GetStringLength(str);
if (length <= 0) {
result->clear();
CheckException(env);
return;
}
// JNI's GetStringUTFChars() and GetStringUTFRegion returns strings in Java
// "modified" UTF8, so instead get the String in UTF16 and convert using
// chromium's conversion function that yields plain (non Java-modified) UTF8.
if (length <= BUFFER_SIZE) {
// fast path, allocate temporary buffer on the stack and use GetStringRegion
// to copy the utf-16 characters into it with no heap allocation.
// https://developer.android.com/training/articles/perf-jni#utf-8-and-utf-16-strings:~:text=stack%2Dallocated%20buffer
std::array<jchar, BUFFER_SIZE> chars;
// GetStringRegion does not copy a null terminated string so the length must
// be explicitly passed to UTF16ToUTF8.
env->GetStringRegion(str, 0, length, chars.data());
UTF16ToUTF8(reinterpret_cast<const char16_t*>(chars.data()),
static_cast<size_t>(length), result);
} else {
// slow path
// GetStringChars doesn't NULL-terminate the strings it returns, so the
// length must be explicitly passed to UTF16ToUTF8.
const jchar* chars = env->GetStringChars(str, NULL);
DCHECK(chars);
UTF16ToUTF8(reinterpret_cast<const char16_t*>(chars),
static_cast<size_t>(length), result);
env->ReleaseStringChars(str, chars);
}
CheckException(env);
}
std::string ConvertJavaStringToUTF8(JNIEnv* env, jstring str) {
std::string result;
ConvertJavaStringToUTF8(env, str, &result);
return result;
}
std::string ConvertJavaStringToUTF8(const JavaRef<jstring>& str) {
return ConvertJavaStringToUTF8(AttachCurrentThread(), str.obj());
}
std::string ConvertJavaStringToUTF8(JNIEnv* env, const JavaRef<jstring>& str) {
return ConvertJavaStringToUTF8(env, str.obj());
}
ScopedJavaLocalRef<jstring> ConvertUTF8ToJavaString(JNIEnv* env,
std::string_view str) {
// ART allocates new empty strings, so use a singleton when applicable.
if (str.empty()) {
return jni_zero::g_empty_string.AsLocalRef(env);
}
// JNI's NewStringUTF expects "modified" UTF8 so instead create the string
// via our own UTF16 conversion utility.
// Further, Dalvik requires the string passed into NewStringUTF() to come from
// a trusted source. We can't guarantee that all UTF8 will be sanitized before
// it gets here, so constructing via UTF16 side-steps this issue.
// (Dalvik stores strings internally as UTF16 anyway, so there shouldn't be
// a significant performance hit by doing it this way).
return ScopedJavaLocalRef<jstring>(
env, ConvertUTF16ToJavaStringImpl(env, UTF8ToUTF16(str)));
}
void ConvertJavaStringToUTF16(JNIEnv* env,
jstring str,
std::u16string* result) {
if (!str) {
result->clear();
return;
}
const jsize length = env->GetStringLength(str);
if (length <= 0) {
result->clear();
CheckException(env);
return;
}
if (length <= BUFFER_SIZE) {
// fast path, allocate temporary buffer on the stack and use GetStringRegion
// to copy the utf-16 characters into it with no heap allocation.
// https://developer.android.com/training/articles/perf-jni#utf-8-and-utf-16-strings:~:text=stack%2Dallocated%20buffer
std::array<jchar, BUFFER_SIZE> chars;
env->GetStringRegion(str, 0, length, chars.data());
// GetStringRegion does not copy a null terminated string so the length must
// be explicitly passed to assign.
result->assign(reinterpret_cast<const char16_t*>(chars.data()),
static_cast<size_t>(length));
} else {
// slow path
const jchar* chars = env->GetStringChars(str, NULL);
DCHECK(chars);
// GetStringChars doesn't NULL-terminate the strings it returns, so the
// length must be explicitly passed to assign.
result->assign(reinterpret_cast<const char16_t*>(chars),
static_cast<size_t>(length));
env->ReleaseStringChars(str, chars);
}
CheckException(env);
}
std::u16string ConvertJavaStringToUTF16(JNIEnv* env, jstring str) {
std::u16string result;
ConvertJavaStringToUTF16(env, str, &result);
return result;
}
std::u16string ConvertJavaStringToUTF16(const JavaRef<jstring>& str) {
return ConvertJavaStringToUTF16(AttachCurrentThread(), str.obj());
}
std::u16string ConvertJavaStringToUTF16(JNIEnv* env,
const JavaRef<jstring>& str) {
return ConvertJavaStringToUTF16(env, str.obj());
}
ScopedJavaLocalRef<jstring> ConvertUTF16ToJavaString(JNIEnv* env,
std::u16string_view str) {
// ART allocates new empty strings, so use a singleton when applicable.
if (str.empty()) {
return jni_zero::g_empty_string.AsLocalRef(env);
}
return ScopedJavaLocalRef<jstring>(env,
ConvertUTF16ToJavaStringImpl(env, str));
}
} // namespace android
} // namespace base
|