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