Home | History | Annotate | Download | only in jni
      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