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