com_android_inputmethod_latin_BinaryDictionary.cpp revision f1008c550168e50f930ea1e043000b395ce0f129
1/*
2 * Copyright (C) 2009, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "LatinIME: jni: BinaryDictionary"
18
19#include "binary_format.h"
20#include "correction.h"
21#include "com_android_inputmethod_latin_BinaryDictionary.h"
22#include "defines.h"
23#include "dictionary.h"
24#include "jni.h"
25#include "jni_common.h"
26#include "proximity_info.h"
27
28#include <cassert>
29#include <cerrno>
30#include <cstdio>
31
32#ifdef USE_MMAP_FOR_DICTIONARY
33#include <sys/mman.h>
34#include <sys/types.h>
35#include <sys/stat.h>
36#include <fcntl.h>
37#include <unistd.h>
38#else // USE_MMAP_FOR_DICTIONARY
39#include <cstdlib>
40#endif // USE_MMAP_FOR_DICTIONARY
41
42namespace latinime {
43
44void releaseDictBuf(void *dictBuf, const size_t length, int fd);
45
46static jlong latinime_BinaryDictionary_open(JNIEnv *env, jobject object,
47        jstring sourceDir, jlong dictOffset, jlong dictSize,
48        jint typedLetterMultiplier, jint fullWordMultiplier, jint maxWordLength, jint maxWords,
49        jint maxPredictions) {
50    PROF_OPEN;
51    PROF_START(66);
52    const char *sourceDirChars = env->GetStringUTFChars(sourceDir, 0);
53    if (sourceDirChars == 0) {
54        AKLOGE("DICT: Can't get sourceDir string");
55        return 0;
56    }
57    int fd = 0;
58    void *dictBuf = 0;
59    int adjust = 0;
60#ifdef USE_MMAP_FOR_DICTIONARY
61    /* mmap version */
62    fd = open(sourceDirChars, O_RDONLY);
63    if (fd < 0) {
64        AKLOGE("DICT: Can't open sourceDir. sourceDirChars=%s errno=%d", sourceDirChars, errno);
65        return 0;
66    }
67    int pagesize = getpagesize();
68    adjust = dictOffset % pagesize;
69    int adjDictOffset = dictOffset - adjust;
70    int adjDictSize = dictSize + adjust;
71    dictBuf = mmap(0, sizeof(char) * adjDictSize, PROT_READ, MAP_PRIVATE, fd, adjDictOffset);
72    if (dictBuf == MAP_FAILED) {
73        AKLOGE("DICT: Can't mmap dictionary. errno=%d", errno);
74        return 0;
75    }
76    dictBuf = (void *)((char *)dictBuf + adjust);
77#else // USE_MMAP_FOR_DICTIONARY
78    /* malloc version */
79    FILE *file = 0;
80    file = fopen(sourceDirChars, "rb");
81    if (file == 0) {
82        AKLOGE("DICT: Can't fopen sourceDir. sourceDirChars=%s errno=%d", sourceDirChars, errno);
83        return 0;
84    }
85    dictBuf = malloc(sizeof(char) * dictSize);
86    if (!dictBuf) {
87        AKLOGE("DICT: Can't allocate memory region for dictionary. errno=%d", errno);
88        return 0;
89    }
90    int ret = fseek(file, (long)dictOffset, SEEK_SET);
91    if (ret != 0) {
92        AKLOGE("DICT: Failure in fseek. ret=%d errno=%d", ret, errno);
93        return 0;
94    }
95    ret = fread(dictBuf, sizeof(char) * dictSize, 1, file);
96    if (ret != 1) {
97        AKLOGE("DICT: Failure in fread. ret=%d errno=%d", ret, errno);
98        return 0;
99    }
100    ret = fclose(file);
101    if (ret != 0) {
102        AKLOGE("DICT: Failure in fclose. ret=%d errno=%d", ret, errno);
103        return 0;
104    }
105#endif // USE_MMAP_FOR_DICTIONARY
106    env->ReleaseStringUTFChars(sourceDir, sourceDirChars);
107
108    if (!dictBuf) {
109        AKLOGE("DICT: dictBuf is null");
110        return 0;
111    }
112    Dictionary *dictionary = 0;
113    if (BinaryFormat::UNKNOWN_FORMAT == BinaryFormat::detectFormat((uint8_t*)dictBuf)) {
114        AKLOGE("DICT: dictionary format is unknown, bad magic number");
115#ifdef USE_MMAP_FOR_DICTIONARY
116        releaseDictBuf(((char*)dictBuf) - adjust, adjDictSize, fd);
117#else // USE_MMAP_FOR_DICTIONARY
118        releaseDictBuf(dictBuf, 0, 0);
119#endif // USE_MMAP_FOR_DICTIONARY
120    } else {
121        dictionary = new Dictionary(dictBuf, dictSize, fd, adjust, typedLetterMultiplier,
122                fullWordMultiplier, maxWordLength, maxWords, maxPredictions);
123    }
124    PROF_END(66);
125    PROF_CLOSE;
126    return (jlong)dictionary;
127}
128
129static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jobject object, jlong dict,
130        jlong proximityInfo, jintArray xCoordinatesArray, jintArray yCoordinatesArray,
131        jintArray timesArray, jintArray pointerIdArray, jintArray inputArray, jint arraySize,
132        jint commitPoint, jboolean isGesture,
133        jintArray prevWordForBigrams, jboolean useFullEditDistance, jcharArray outputArray,
134        jintArray frequencyArray, jintArray spaceIndexArray, jintArray outputTypesArray) {
135    Dictionary *dictionary = (Dictionary*) dict;
136    if (!dictionary) return 0;
137    ProximityInfo *pInfo = (ProximityInfo*)proximityInfo;
138    int *xCoordinates = env->GetIntArrayElements(xCoordinatesArray, 0);
139    int *yCoordinates = env->GetIntArrayElements(yCoordinatesArray, 0);
140    int *times = env->GetIntArrayElements(timesArray, 0);
141    int *pointerIds = env->GetIntArrayElements(pointerIdArray, 0);
142    int *frequencies = env->GetIntArrayElements(frequencyArray, 0);
143    int *inputCodes = env->GetIntArrayElements(inputArray, 0);
144    jchar *outputChars = env->GetCharArrayElements(outputArray, 0);
145    int *spaceIndices = env->GetIntArrayElements(spaceIndexArray, 0);
146    int *outputTypes = env->GetIntArrayElements(outputTypesArray, 0);
147    jint *prevWordChars = prevWordForBigrams
148            ? env->GetIntArrayElements(prevWordForBigrams, 0) : 0;
149    jsize prevWordLength = prevWordChars ? env->GetArrayLength(prevWordForBigrams) : 0;
150
151    int count;
152    if (isGesture || arraySize > 1) {
153        count = dictionary->getSuggestions(pInfo, xCoordinates, yCoordinates, times, pointerIds,
154                inputCodes, arraySize, prevWordChars, prevWordLength, commitPoint, isGesture,
155                useFullEditDistance, (unsigned short*) outputChars, frequencies, spaceIndices,
156                outputTypes);
157    } else {
158        count = dictionary->getBigrams(prevWordChars, prevWordLength, inputCodes,
159                arraySize, (unsigned short*) outputChars, frequencies, outputTypes);
160    }
161
162    if (prevWordChars) {
163        env->ReleaseIntArrayElements(prevWordForBigrams, prevWordChars, JNI_ABORT);
164    }
165    env->ReleaseIntArrayElements(outputTypesArray, outputTypes, 0);
166    env->ReleaseIntArrayElements(spaceIndexArray, spaceIndices, 0);
167    env->ReleaseCharArrayElements(outputArray, outputChars, 0);
168    env->ReleaseIntArrayElements(inputArray, inputCodes, JNI_ABORT);
169    env->ReleaseIntArrayElements(frequencyArray, frequencies, 0);
170    env->ReleaseIntArrayElements(pointerIdArray, pointerIds, 0);
171    env->ReleaseIntArrayElements(timesArray, times, 0);
172    env->ReleaseIntArrayElements(yCoordinatesArray, yCoordinates, 0);
173    env->ReleaseIntArrayElements(xCoordinatesArray, xCoordinates, 0);
174    return count;
175}
176
177static jint latinime_BinaryDictionary_getFrequency(JNIEnv *env, jobject object, jlong dict,
178        jintArray wordArray, jint wordLength) {
179    Dictionary *dictionary = (Dictionary*)dict;
180    if (!dictionary) return (jboolean) false;
181    jint *word = env->GetIntArrayElements(wordArray, 0);
182    jint result = dictionary->getFrequency(word, wordLength);
183    env->ReleaseIntArrayElements(wordArray, word, JNI_ABORT);
184    return result;
185}
186
187static jboolean latinime_BinaryDictionary_isValidBigram(JNIEnv *env, jobject object, jlong dict,
188        jintArray wordArray1, jintArray wordArray2) {
189    Dictionary *dictionary = (Dictionary*)dict;
190    if (!dictionary) return (jboolean) false;
191    jint *word1 = env->GetIntArrayElements(wordArray1, 0);
192    jint *word2 = env->GetIntArrayElements(wordArray2, 0);
193    jsize length1 = word1 ? env->GetArrayLength(wordArray1) : 0;
194    jsize length2 = word2 ? env->GetArrayLength(wordArray2) : 0;
195    jboolean result = dictionary->isValidBigram(word1, length1, word2, length2);
196    env->ReleaseIntArrayElements(wordArray2, word2, JNI_ABORT);
197    env->ReleaseIntArrayElements(wordArray1, word1, JNI_ABORT);
198    return result;
199}
200
201static jfloat latinime_BinaryDictionary_calcNormalizedScore(JNIEnv *env, jobject object,
202        jcharArray before, jint beforeLength, jcharArray after, jint afterLength, jint score) {
203    jchar *beforeChars = env->GetCharArrayElements(before, 0);
204    jchar *afterChars = env->GetCharArrayElements(after, 0);
205    jfloat result = Correction::RankingAlgorithm::calcNormalizedScore((unsigned short*)beforeChars,
206            beforeLength, (unsigned short*)afterChars, afterLength, score);
207    env->ReleaseCharArrayElements(after, afterChars, JNI_ABORT);
208    env->ReleaseCharArrayElements(before, beforeChars, JNI_ABORT);
209    return result;
210}
211
212static jint latinime_BinaryDictionary_editDistance(JNIEnv *env, jobject object,
213        jcharArray before, jint beforeLength, jcharArray after, jint afterLength) {
214    jchar *beforeChars = env->GetCharArrayElements(before, 0);
215    jchar *afterChars = env->GetCharArrayElements(after, 0);
216    jint result = Correction::RankingAlgorithm::editDistance(
217            (unsigned short*)beforeChars, beforeLength, (unsigned short*)afterChars, afterLength);
218    env->ReleaseCharArrayElements(after, afterChars, JNI_ABORT);
219    env->ReleaseCharArrayElements(before, beforeChars, JNI_ABORT);
220    return result;
221}
222
223static void latinime_BinaryDictionary_close(JNIEnv *env, jobject object, jlong dict) {
224    Dictionary *dictionary = (Dictionary*)dict;
225    if (!dictionary) return;
226    void *dictBuf = dictionary->getDict();
227    if (!dictBuf) return;
228#ifdef USE_MMAP_FOR_DICTIONARY
229    releaseDictBuf((void *)((char *)dictBuf - dictionary->getDictBufAdjust()),
230            dictionary->getDictSize() + dictionary->getDictBufAdjust(), dictionary->getMmapFd());
231#else // USE_MMAP_FOR_DICTIONARY
232    releaseDictBuf(dictBuf, 0, 0);
233#endif // USE_MMAP_FOR_DICTIONARY
234    delete dictionary;
235}
236
237void releaseDictBuf(void *dictBuf, const size_t length, int fd) {
238#ifdef USE_MMAP_FOR_DICTIONARY
239    int ret = munmap(dictBuf, length);
240    if (ret != 0) {
241        AKLOGE("DICT: Failure in munmap. ret=%d errno=%d", ret, errno);
242    }
243    ret = close(fd);
244    if (ret != 0) {
245        AKLOGE("DICT: Failure in close. ret=%d errno=%d", ret, errno);
246    }
247#else // USE_MMAP_FOR_DICTIONARY
248    free(dictBuf);
249#endif // USE_MMAP_FOR_DICTIONARY
250}
251
252static JNINativeMethod sMethods[] = {
253    {"openNative", "(Ljava/lang/String;JJIIIII)J", (void*)latinime_BinaryDictionary_open},
254    {"closeNative", "(J)V", (void*)latinime_BinaryDictionary_close},
255    {"getSuggestionsNative", "(JJ[I[I[I[I[IIIZ[IZ[C[I[I[I)I",
256            (void*) latinime_BinaryDictionary_getSuggestions},
257    {"getFrequencyNative", "(J[II)I", (void*)latinime_BinaryDictionary_getFrequency},
258    {"isValidBigramNative", "(J[I[I)Z", (void*)latinime_BinaryDictionary_isValidBigram},
259    {"calcNormalizedScoreNative", "([CI[CII)F",
260            (void*)latinime_BinaryDictionary_calcNormalizedScore},
261    {"editDistanceNative", "([CI[CI)I", (void*)latinime_BinaryDictionary_editDistance}
262};
263
264int register_BinaryDictionary(JNIEnv *env) {
265    const char *const kClassPathName = "com/android/inputmethod/latin/BinaryDictionary";
266    return registerNativeMethods(env, kClassPathName, sMethods,
267            sizeof(sMethods) / sizeof(sMethods[0]));
268}
269} // namespace latinime
270