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
base / android / jni_string.cc [blame]
// 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 <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) {
// 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) {
return ScopedJavaLocalRef<jstring>(env,
ConvertUTF16ToJavaStringImpl(env, str));
}
} // namespace android
} // namespace base