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 "defines.h" 24 #include "dictionary.h" 25 #include "jni.h" 26 #include "jni_common.h" 27 #include "proximity_info.h" 28 29 #include <assert.h> 30 #include <errno.h> 31 #include <stdio.h> 32 33 #ifdef USE_MMAP_FOR_DICTIONARY 34 #include <sys/mman.h> 35 #include <sys/types.h> 36 #include <sys/stat.h> 37 #include <fcntl.h> 38 #include <unistd.h> 39 #else // USE_MMAP_FOR_DICTIONARY 40 #include <stdlib.h> 41 #endif // USE_MMAP_FOR_DICTIONARY 42 43 namespace latinime { 44 45 void releaseDictBuf(void* dictBuf, const size_t length, int fd); 46 47 static jlong latinime_BinaryDictionary_open(JNIEnv *env, jobject object, 48 jstring sourceDir, jlong dictOffset, jlong dictSize, 49 jint typedLetterMultiplier, jint fullWordMultiplier, jint maxWordLength, jint maxWords) { 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); 123 } 124 PROF_END(66); 125 PROF_CLOSE; 126 return (jlong)dictionary; 127 } 128 129 static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jobject object, jlong dict, 130 jlong proximityInfo, jintArray xCoordinatesArray, jintArray yCoordinatesArray, 131 jintArray inputArray, jint arraySize, jintArray prevWordForBigrams, 132 jboolean useFullEditDistance, jcharArray outputArray, jintArray frequencyArray) { 133 Dictionary *dictionary = (Dictionary*)dict; 134 if (!dictionary) return 0; 135 ProximityInfo *pInfo = (ProximityInfo*)proximityInfo; 136 int *xCoordinates = env->GetIntArrayElements(xCoordinatesArray, 0); 137 int *yCoordinates = env->GetIntArrayElements(yCoordinatesArray, 0); 138 int *frequencies = env->GetIntArrayElements(frequencyArray, 0); 139 int *inputCodes = env->GetIntArrayElements(inputArray, 0); 140 jchar *outputChars = env->GetCharArrayElements(outputArray, 0); 141 jint *prevWordChars = prevWordForBigrams 142 ? env->GetIntArrayElements(prevWordForBigrams, 0) : 0; 143 jsize prevWordLength = prevWordChars ? env->GetArrayLength(prevWordForBigrams) : 0; 144 int count = dictionary->getSuggestions(pInfo, xCoordinates, yCoordinates, inputCodes, 145 arraySize, prevWordChars, prevWordLength, useFullEditDistance, 146 (unsigned short*) outputChars, frequencies); 147 if (prevWordChars) { 148 env->ReleaseIntArrayElements(prevWordForBigrams, prevWordChars, JNI_ABORT); 149 } 150 env->ReleaseCharArrayElements(outputArray, outputChars, 0); 151 env->ReleaseIntArrayElements(inputArray, inputCodes, JNI_ABORT); 152 env->ReleaseIntArrayElements(frequencyArray, frequencies, 0); 153 env->ReleaseIntArrayElements(yCoordinatesArray, yCoordinates, 0); 154 env->ReleaseIntArrayElements(xCoordinatesArray, xCoordinates, 0); 155 return count; 156 } 157 158 static int latinime_BinaryDictionary_getBigrams(JNIEnv *env, jobject object, jlong dict, 159 jintArray prevWordArray, jint prevWordLength, jintArray inputArray, jint inputArraySize, 160 jcharArray outputArray, jintArray frequencyArray, jint maxWordLength, jint maxBigrams) { 161 Dictionary *dictionary = (Dictionary*)dict; 162 if (!dictionary) return 0; 163 jint *prevWord = env->GetIntArrayElements(prevWordArray, 0); 164 int *inputCodes = env->GetIntArrayElements(inputArray, 0); 165 jchar *outputChars = env->GetCharArrayElements(outputArray, 0); 166 int *frequencies = env->GetIntArrayElements(frequencyArray, 0); 167 int count = dictionary->getBigrams(prevWord, prevWordLength, inputCodes, 168 inputArraySize, (unsigned short*) outputChars, frequencies, maxWordLength, maxBigrams); 169 env->ReleaseIntArrayElements(frequencyArray, frequencies, 0); 170 env->ReleaseCharArrayElements(outputArray, outputChars, 0); 171 env->ReleaseIntArrayElements(inputArray, inputCodes, JNI_ABORT); 172 env->ReleaseIntArrayElements(prevWordArray, prevWord, JNI_ABORT); 173 return count; 174 } 175 176 static jint latinime_BinaryDictionary_getFrequency(JNIEnv *env, jobject object, jlong dict, 177 jintArray wordArray, jint wordLength) { 178 Dictionary *dictionary = (Dictionary*)dict; 179 if (!dictionary) return (jboolean) false; 180 jint *word = env->GetIntArrayElements(wordArray, 0); 181 jint result = dictionary->getFrequency(word, wordLength); 182 env->ReleaseIntArrayElements(wordArray, word, JNI_ABORT); 183 return result; 184 } 185 186 static jboolean latinime_BinaryDictionary_isValidBigram(JNIEnv *env, jobject object, jlong dict, 187 jintArray wordArray1, jintArray wordArray2) { 188 Dictionary *dictionary = (Dictionary*)dict; 189 if (!dictionary) return (jboolean) false; 190 jint *word1 = env->GetIntArrayElements(wordArray1, 0); 191 jint *word2 = env->GetIntArrayElements(wordArray2, 0); 192 jsize length1 = word1 ? env->GetArrayLength(wordArray1) : 0; 193 jsize length2 = word2 ? env->GetArrayLength(wordArray2) : 0; 194 jboolean result = dictionary->isValidBigram(word1, length1, word2, length2); 195 env->ReleaseIntArrayElements(wordArray2, word2, JNI_ABORT); 196 env->ReleaseIntArrayElements(wordArray1, word1, JNI_ABORT); 197 return result; 198 } 199 200 static jfloat latinime_BinaryDictionary_calcNormalizedScore(JNIEnv *env, jobject object, 201 jcharArray before, jint beforeLength, jcharArray after, jint afterLength, jint score) { 202 jchar *beforeChars = env->GetCharArrayElements(before, 0); 203 jchar *afterChars = env->GetCharArrayElements(after, 0); 204 jfloat result = Correction::RankingAlgorithm::calcNormalizedScore((unsigned short*)beforeChars, 205 beforeLength, (unsigned short*)afterChars, afterLength, score); 206 env->ReleaseCharArrayElements(after, afterChars, JNI_ABORT); 207 env->ReleaseCharArrayElements(before, beforeChars, JNI_ABORT); 208 return result; 209 } 210 211 static jint latinime_BinaryDictionary_editDistance(JNIEnv *env, jobject object, 212 jcharArray before, jint beforeLength, jcharArray after, jint afterLength) { 213 jchar *beforeChars = env->GetCharArrayElements(before, 0); 214 jchar *afterChars = env->GetCharArrayElements(after, 0); 215 jint result = Correction::RankingAlgorithm::editDistance( 216 (unsigned short*)beforeChars, beforeLength, (unsigned short*)afterChars, afterLength); 217 env->ReleaseCharArrayElements(after, afterChars, JNI_ABORT); 218 env->ReleaseCharArrayElements(before, beforeChars, JNI_ABORT); 219 return result; 220 } 221 222 static void latinime_BinaryDictionary_close(JNIEnv *env, jobject object, jlong dict) { 223 Dictionary *dictionary = (Dictionary*)dict; 224 if (!dictionary) return; 225 void *dictBuf = dictionary->getDict(); 226 if (!dictBuf) return; 227 #ifdef USE_MMAP_FOR_DICTIONARY 228 releaseDictBuf((void *)((char *)dictBuf - dictionary->getDictBufAdjust()), 229 dictionary->getDictSize() + dictionary->getDictBufAdjust(), dictionary->getMmapFd()); 230 #else // USE_MMAP_FOR_DICTIONARY 231 releaseDictBuf(dictBuf, 0, 0); 232 #endif // USE_MMAP_FOR_DICTIONARY 233 delete dictionary; 234 } 235 236 void releaseDictBuf(void* dictBuf, const size_t length, int fd) { 237 #ifdef USE_MMAP_FOR_DICTIONARY 238 int ret = munmap(dictBuf, length); 239 if (ret != 0) { 240 AKLOGE("DICT: Failure in munmap. ret=%d errno=%d", ret, errno); 241 } 242 ret = close(fd); 243 if (ret != 0) { 244 AKLOGE("DICT: Failure in close. ret=%d errno=%d", ret, errno); 245 } 246 #else // USE_MMAP_FOR_DICTIONARY 247 free(dictBuf); 248 #endif // USE_MMAP_FOR_DICTIONARY 249 } 250 251 static JNINativeMethod sMethods[] = { 252 {"openNative", "(Ljava/lang/String;JJIIII)J", (void*)latinime_BinaryDictionary_open}, 253 {"closeNative", "(J)V", (void*)latinime_BinaryDictionary_close}, 254 {"getSuggestionsNative", "(JJ[I[I[II[IZ[C[I)I", 255 (void*)latinime_BinaryDictionary_getSuggestions}, 256 {"getFrequencyNative", "(J[II)I", (void*)latinime_BinaryDictionary_getFrequency}, 257 {"isValidBigramNative", "(J[I[I)Z", (void*)latinime_BinaryDictionary_isValidBigram}, 258 {"getBigramsNative", "(J[II[II[C[III)I", (void*)latinime_BinaryDictionary_getBigrams}, 259 {"calcNormalizedScoreNative", "([CI[CII)F", 260 (void*)latinime_BinaryDictionary_calcNormalizedScore}, 261 {"editDistanceNative", "([CI[CI)I", (void*)latinime_BinaryDictionary_editDistance} 262 }; 263 264 int 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 270 } // namespace latinime 271