1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 // This file provides a Java Native Interface (JNI) version of the Detokenizer
16 // class. This facilitates using the tokenizer library from Java or other JVM
17 // languages. A corresponding Java class is provided in Detokenizer.java.
18 
19 #include <jni.h>
20 
21 #include <cstring>
22 #include <span>
23 
24 #include "pw_preprocessor/concat.h"
25 #include "pw_tokenizer/detokenize.h"
26 #include "pw_tokenizer/token_database.h"
27 
28 #define DETOKENIZER_METHOD(method) \
29   JNICALL PW_CONCAT(Java_dev_pigweed_tokenizer_, Detokenizer_, method)
30 
31 namespace pw::tokenizer {
32 namespace {
33 
HandleToPointer(jlong handle)34 Detokenizer* HandleToPointer(jlong handle) {
35   Detokenizer* detokenizer = nullptr;
36   std::memcpy(&detokenizer, &handle, sizeof(detokenizer));
37   static_assert(sizeof(detokenizer) <= sizeof(handle));
38   return detokenizer;
39 }
40 
PointerToHandle(Detokenizer * detokenizer)41 jlong PointerToHandle(Detokenizer* detokenizer) {
42   jlong handle = 0;
43   std::memcpy(&handle, &detokenizer, sizeof(detokenizer));
44   static_assert(sizeof(handle) >= sizeof(detokenizer));
45   return handle;
46 }
47 
48 }  // namespace
49 
50 extern "C" {
51 
52 static_assert(sizeof(jbyte) == 1u);
53 
DETOKENIZER_METHOD(newNativeDetokenizer)54 JNIEXPORT jlong DETOKENIZER_METHOD(newNativeDetokenizer)(JNIEnv* env,
55                                                          jclass,
56                                                          jbyteArray array) {
57   jbyte* const data = env->GetByteArrayElements(array, nullptr);
58   const jsize size = env->GetArrayLength(array);
59 
60   TokenDatabase tokens = TokenDatabase::Create(std::span(data, size));
61   const jlong handle =
62       PointerToHandle(new Detokenizer(tokens.ok() ? tokens : TokenDatabase()));
63 
64   env->ReleaseByteArrayElements(array, data, 0);
65   return handle;
66 }
67 
DETOKENIZER_METHOD(deleteNativeDetokenizer)68 JNIEXPORT void DETOKENIZER_METHOD(deleteNativeDetokenizer)(JNIEnv*,
69                                                            jclass,
70                                                            jlong handle) {
71   delete HandleToPointer(handle);
72 }
73 
DETOKENIZER_METHOD(detokenizeNative)74 JNIEXPORT jstring DETOKENIZER_METHOD(detokenizeNative)(JNIEnv* env,
75                                                        jobject,
76                                                        jlong handle,
77                                                        jbyteArray array) {
78   jbyte* const data = env->GetByteArrayElements(array, nullptr);
79   const jsize size = env->GetArrayLength(array);
80 
81   DetokenizedString result = HandleToPointer(handle)->Detokenize(data, size);
82 
83   env->ReleaseByteArrayElements(array, data, 0);
84 
85   return result.matches().empty()
86              ? nullptr
87              : env->NewStringUTF(result.BestString().c_str());
88 }
89 
90 }  // extern "C"
91 
92 }  // namespace pw::tokenizer
93