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 #include <cstring> // for memset() 18 19 #define LOG_TAG "LatinIME: jni: BinaryDictionary" 20 21 #include "defines.h" // for macros below 22 23 #ifdef USE_MMAP_FOR_DICTIONARY 24 #include <cerrno> 25 #include <fcntl.h> 26 #include <sys/mman.h> 27 #else // USE_MMAP_FOR_DICTIONARY 28 #include <cstdlib> 29 #include <cstdio> // for fopen() etc. 30 #endif // USE_MMAP_FOR_DICTIONARY 31 32 #include "binary_format.h" 33 #include "com_android_inputmethod_latin_BinaryDictionary.h" 34 #include "correction.h" 35 #include "dictionary.h" 36 #include "jni.h" 37 #include "jni_common.h" 38 39 namespace latinime { 40 41 class ProximityInfo; 42 43 static void releaseDictBuf(const void *dictBuf, const size_t length, const int fd); 44 45 static jlong latinime_BinaryDictionary_open(JNIEnv *env, jclass clazz, jstring sourceDir, 46 jlong dictOffset, jlong dictSize) { 47 PROF_OPEN; 48 PROF_START(66); 49 const jsize sourceDirUtf8Length = env->GetStringUTFLength(sourceDir); 50 if (sourceDirUtf8Length <= 0) { 51 AKLOGE("DICT: Can't get sourceDir string"); 52 return 0; 53 } 54 char sourceDirChars[sourceDirUtf8Length + 1]; 55 env->GetStringUTFRegion(sourceDir, 0, env->GetStringLength(sourceDir), sourceDirChars); 56 sourceDirChars[sourceDirUtf8Length] = '\0'; 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 = static_cast<int>(dictOffset) % pagesize; 69 int adjDictOffset = static_cast<int>(dictOffset) - adjust; 70 int adjDictSize = static_cast<int>(dictSize) + adjust; 71 dictBuf = mmap(0, 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 = static_cast<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(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, static_cast<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, 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 if (!dictBuf) { 107 AKLOGE("DICT: dictBuf is null"); 108 return 0; 109 } 110 Dictionary *dictionary = 0; 111 if (BinaryFormat::UNKNOWN_FORMAT 112 == BinaryFormat::detectFormat(static_cast<uint8_t *>(dictBuf), 113 static_cast<int>(dictSize))) { 114 AKLOGE("DICT: dictionary format is unknown, bad magic number"); 115 #ifdef USE_MMAP_FOR_DICTIONARY 116 releaseDictBuf(static_cast<const 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, static_cast<int>(dictSize), fd, adjust); 122 } 123 PROF_END(66); 124 PROF_CLOSE; 125 return reinterpret_cast<jlong>(dictionary); 126 } 127 128 static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jclass clazz, jlong dict, 129 jlong proximityInfo, jlong dicTraverseSession, jintArray xCoordinatesArray, 130 jintArray yCoordinatesArray, jintArray timesArray, jintArray pointerIdsArray, 131 jintArray inputCodePointsArray, jint inputSize, jint commitPoint, jboolean isGesture, 132 jintArray prevWordCodePointsForBigrams, jboolean useFullEditDistance, 133 jintArray outputCodePointsArray, jintArray scoresArray, jintArray spaceIndicesArray, 134 jintArray outputTypesArray) { 135 Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); 136 if (!dictionary) return 0; 137 ProximityInfo *pInfo = reinterpret_cast<ProximityInfo *>(proximityInfo); 138 void *traverseSession = reinterpret_cast<void *>(dicTraverseSession); 139 140 // Input values 141 int xCoordinates[inputSize]; 142 int yCoordinates[inputSize]; 143 int times[inputSize]; 144 int pointerIds[inputSize]; 145 const jsize inputCodePointsLength = env->GetArrayLength(inputCodePointsArray); 146 int inputCodePoints[inputCodePointsLength]; 147 const jsize prevWordCodePointsLength = 148 prevWordCodePointsForBigrams ? env->GetArrayLength(prevWordCodePointsForBigrams) : 0; 149 int prevWordCodePointsInternal[prevWordCodePointsLength]; 150 int *prevWordCodePoints = 0; 151 env->GetIntArrayRegion(xCoordinatesArray, 0, inputSize, xCoordinates); 152 env->GetIntArrayRegion(yCoordinatesArray, 0, inputSize, yCoordinates); 153 env->GetIntArrayRegion(timesArray, 0, inputSize, times); 154 env->GetIntArrayRegion(pointerIdsArray, 0, inputSize, pointerIds); 155 env->GetIntArrayRegion(inputCodePointsArray, 0, inputCodePointsLength, inputCodePoints); 156 if (prevWordCodePointsForBigrams) { 157 env->GetIntArrayRegion(prevWordCodePointsForBigrams, 0, prevWordCodePointsLength, 158 prevWordCodePointsInternal); 159 prevWordCodePoints = prevWordCodePointsInternal; 160 } 161 162 // Output values 163 /* By the way, let's check the output array length here to make sure */ 164 const jsize outputCodePointsLength = env->GetArrayLength(outputCodePointsArray); 165 if (outputCodePointsLength != (MAX_WORD_LENGTH * MAX_RESULTS)) { 166 AKLOGE("Invalid outputCodePointsLength: %d", outputCodePointsLength); 167 ASSERT(false); 168 return 0; 169 } 170 const jsize scoresLength = env->GetArrayLength(scoresArray); 171 if (scoresLength != MAX_RESULTS) { 172 AKLOGE("Invalid scoresLength: %d", scoresLength); 173 ASSERT(false); 174 return 0; 175 } 176 int outputCodePoints[outputCodePointsLength]; 177 int scores[scoresLength]; 178 const jsize spaceIndicesLength = env->GetArrayLength(spaceIndicesArray); 179 int spaceIndices[spaceIndicesLength]; 180 const jsize outputTypesLength = env->GetArrayLength(outputTypesArray); 181 int outputTypes[outputTypesLength]; 182 memset(outputCodePoints, 0, sizeof(outputCodePoints)); 183 memset(scores, 0, sizeof(scores)); 184 memset(spaceIndices, 0, sizeof(spaceIndices)); 185 memset(outputTypes, 0, sizeof(outputTypes)); 186 187 int count; 188 if (isGesture || inputSize > 0) { 189 count = dictionary->getSuggestions(pInfo, traverseSession, xCoordinates, yCoordinates, 190 times, pointerIds, inputCodePoints, inputSize, prevWordCodePoints, 191 prevWordCodePointsLength, commitPoint, isGesture, useFullEditDistance, 192 outputCodePoints, scores, spaceIndices, outputTypes); 193 } else { 194 count = dictionary->getBigrams(prevWordCodePoints, prevWordCodePointsLength, 195 inputCodePoints, inputSize, outputCodePoints, scores, outputTypes); 196 } 197 198 // Copy back the output values 199 env->SetIntArrayRegion(outputCodePointsArray, 0, outputCodePointsLength, outputCodePoints); 200 env->SetIntArrayRegion(scoresArray, 0, scoresLength, scores); 201 env->SetIntArrayRegion(spaceIndicesArray, 0, spaceIndicesLength, spaceIndices); 202 env->SetIntArrayRegion(outputTypesArray, 0, outputTypesLength, outputTypes); 203 204 return count; 205 } 206 207 static jint latinime_BinaryDictionary_getProbability(JNIEnv *env, jclass clazz, jlong dict, 208 jintArray wordArray) { 209 Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); 210 if (!dictionary) return 0; 211 const jsize codePointLength = env->GetArrayLength(wordArray); 212 int codePoints[codePointLength]; 213 env->GetIntArrayRegion(wordArray, 0, codePointLength, codePoints); 214 return dictionary->getProbability(codePoints, codePointLength); 215 } 216 217 static jboolean latinime_BinaryDictionary_isValidBigram(JNIEnv *env, jclass clazz, jlong dict, 218 jintArray wordArray1, jintArray wordArray2) { 219 Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); 220 if (!dictionary) return JNI_FALSE; 221 const jsize codePointLength1 = env->GetArrayLength(wordArray1); 222 const jsize codePointLength2 = env->GetArrayLength(wordArray2); 223 int codePoints1[codePointLength1]; 224 int codePoints2[codePointLength2]; 225 env->GetIntArrayRegion(wordArray1, 0, codePointLength1, codePoints1); 226 env->GetIntArrayRegion(wordArray2, 0, codePointLength2, codePoints2); 227 return dictionary->isValidBigram(codePoints1, codePointLength1, codePoints2, codePointLength2); 228 } 229 230 static jfloat latinime_BinaryDictionary_calcNormalizedScore(JNIEnv *env, jclass clazz, 231 jintArray before, jintArray after, jint score) { 232 jsize beforeLength = env->GetArrayLength(before); 233 jsize afterLength = env->GetArrayLength(after); 234 int beforeCodePoints[beforeLength]; 235 int afterCodePoints[afterLength]; 236 env->GetIntArrayRegion(before, 0, beforeLength, beforeCodePoints); 237 env->GetIntArrayRegion(after, 0, afterLength, afterCodePoints); 238 return Correction::RankingAlgorithm::calcNormalizedScore(beforeCodePoints, beforeLength, 239 afterCodePoints, afterLength, score); 240 } 241 242 static jint latinime_BinaryDictionary_editDistance(JNIEnv *env, jclass clazz, jintArray before, 243 jintArray after) { 244 jsize beforeLength = env->GetArrayLength(before); 245 jsize afterLength = env->GetArrayLength(after); 246 int beforeCodePoints[beforeLength]; 247 int afterCodePoints[afterLength]; 248 env->GetIntArrayRegion(before, 0, beforeLength, beforeCodePoints); 249 env->GetIntArrayRegion(after, 0, afterLength, afterCodePoints); 250 return Correction::RankingAlgorithm::editDistance(beforeCodePoints, beforeLength, 251 afterCodePoints, afterLength); 252 } 253 254 static void latinime_BinaryDictionary_close(JNIEnv *env, jclass clazz, jlong dict) { 255 Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); 256 if (!dictionary) return; 257 const void *dictBuf = dictionary->getDict(); 258 if (!dictBuf) return; 259 #ifdef USE_MMAP_FOR_DICTIONARY 260 releaseDictBuf(static_cast<const char *>(dictBuf) - dictionary->getDictBufAdjust(), 261 dictionary->getDictSize() + dictionary->getDictBufAdjust(), dictionary->getMmapFd()); 262 #else // USE_MMAP_FOR_DICTIONARY 263 releaseDictBuf(dictBuf, 0, 0); 264 #endif // USE_MMAP_FOR_DICTIONARY 265 delete dictionary; 266 } 267 268 static void releaseDictBuf(const void *dictBuf, const size_t length, const int fd) { 269 #ifdef USE_MMAP_FOR_DICTIONARY 270 int ret = munmap(const_cast<void *>(dictBuf), length); 271 if (ret != 0) { 272 AKLOGE("DICT: Failure in munmap. ret=%d errno=%d", ret, errno); 273 } 274 ret = close(fd); 275 if (ret != 0) { 276 AKLOGE("DICT: Failure in close. ret=%d errno=%d", ret, errno); 277 } 278 #else // USE_MMAP_FOR_DICTIONARY 279 free(const_cast<void *>(dictBuf)); 280 #endif // USE_MMAP_FOR_DICTIONARY 281 } 282 283 static JNINativeMethod sMethods[] = { 284 {const_cast<char *>("openNative"), 285 const_cast<char *>("(Ljava/lang/String;JJ)J"), 286 reinterpret_cast<void *>(latinime_BinaryDictionary_open)}, 287 {const_cast<char *>("closeNative"), 288 const_cast<char *>("(J)V"), 289 reinterpret_cast<void *>(latinime_BinaryDictionary_close)}, 290 {const_cast<char *>("getSuggestionsNative"), 291 const_cast<char *>("(JJJ[I[I[I[I[IIIZ[IZ[I[I[I[I)I"), 292 reinterpret_cast<void *>(latinime_BinaryDictionary_getSuggestions)}, 293 {const_cast<char *>("getProbabilityNative"), 294 const_cast<char *>("(J[I)I"), 295 reinterpret_cast<void *>(latinime_BinaryDictionary_getProbability)}, 296 {const_cast<char *>("isValidBigramNative"), 297 const_cast<char *>("(J[I[I)Z"), 298 reinterpret_cast<void *>(latinime_BinaryDictionary_isValidBigram)}, 299 {const_cast<char *>("calcNormalizedScoreNative"), 300 const_cast<char *>("([I[II)F"), 301 reinterpret_cast<void *>(latinime_BinaryDictionary_calcNormalizedScore)}, 302 {const_cast<char *>("editDistanceNative"), 303 const_cast<char *>("([I[I)I"), 304 reinterpret_cast<void *>(latinime_BinaryDictionary_editDistance)} 305 }; 306 307 int register_BinaryDictionary(JNIEnv *env) { 308 const char *const kClassPathName = "com/android/inputmethod/latin/BinaryDictionary"; 309 return registerNativeMethods(env, kClassPathName, sMethods, NELEMS(sMethods)); 310 } 311 } // namespace latinime 312