diff options
11 files changed, 193 insertions, 56 deletions
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml index a71e7cc11..8a6733634 100644 --- a/java/res/values/attrs.xml +++ b/java/res/values/attrs.xml @@ -108,6 +108,14 @@ <attr name="backgroundDimAlpha" format="integer" /> <!-- More keys keyboard will shown at touched point. --> <attr name="showMoreKeysKeyboardAtTouchedPoint" format="boolean" /> + <!-- Minimum distance between gesture preview trail sampling points. --> + <attr name="gesturePreviewTrailMinSamplingDistance" format="dimension" /> + <!-- Maximum angular threshold between gesture preview trail interpolation segments in degree. --> + <attr name="gesturePreviewTrailMaxInterpolationAngularThreshold" format="integer" /> + <!-- Maximum distance threshold between gesture preview trail interpolation segments. --> + <attr name="gesturePreviewTrailMaxInterpolationDistanceThreshold" format="dimension" /> + <!-- Maximum number of gesture preview trail interpolation segments. --> + <attr name="gesturePreviewTrailMaxInterpolationSegments" format="integer" /> <!-- Delay after gesture trail starts fading out in millisecond. --> <attr name="gesturePreviewTrailFadeoutStartDelay" format="integer" /> <!-- Duration while gesture preview trail is fading out in millisecond. --> diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml index da735cf5a..5c33275b3 100644 --- a/java/res/values/dimens.xml +++ b/java/res/values/dimens.xml @@ -101,6 +101,14 @@ <fraction name="center_suggestion_percentile">36%</fraction> <!-- Gesture preview trail parameters --> + <!-- Minimum distance between gesture preview trail sampling points. --> + <dimen name="gesture_preview_trail_min_sampling_distance">6.4dp</dimen> + <!-- Maximum angular threshold between gesture preview trails interpolation segments in degree. --> + <integer name="gesture_preview_trail_max_interpolation_angular_threshold">15</integer> + <!-- Maximum distance threshold between gesture preview trails interpolation segments. --> + <dimen name="gesture_preview_trail_max_interpolation_distance_threshold">16.0dp</dimen> + <!-- Maximum number of gesture preview trail interpolation segments. --> + <integer name="gesture_preview_trail_max_interpolation_segments">6</integer> <dimen name="gesture_preview_trail_start_width">10.0dp</dimen> <dimen name="gesture_preview_trail_end_width">2.5dp</dimen> <!-- Percentages of gesture preview taril body and shadow, in proportion to the trail width. diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml index dad7e2064..fa40e5196 100644 --- a/java/res/values/styles.xml +++ b/java/res/values/styles.xml @@ -64,6 +64,10 @@ <item name="gestureFloatingPreviewHorizontalPadding">@dimen/gesture_floating_preview_horizontal_padding</item> <item name="gestureFloatingPreviewVerticalPadding">@dimen/gesture_floating_preview_vertical_padding</item> <item name="gestureFloatingPreviewRoundRadius">@dimen/gesture_floating_preview_round_radius</item> + <item name="gesturePreviewTrailMinSamplingDistance">@dimen/gesture_preview_trail_min_sampling_distance</item> + <item name="gesturePreviewTrailMaxInterpolationAngularThreshold">@integer/gesture_preview_trail_max_interpolation_angular_threshold</item> + <item name="gesturePreviewTrailMaxInterpolationDistanceThreshold">@dimen/gesture_preview_trail_max_interpolation_distance_threshold</item> + <item name="gesturePreviewTrailMaxInterpolationSegments">@integer/gesture_preview_trail_max_interpolation_segments</item> <item name="gesturePreviewTrailFadeoutStartDelay">@integer/config_gesture_preview_trail_fadeout_start_delay</item> <item name="gesturePreviewTrailFadeoutDuration">@integer/config_gesture_preview_trail_fadeout_duration</item> <item name="gesturePreviewTrailUpdateInterval">@integer/config_gesture_preview_trail_update_interval</item> diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 0556fddd3..2d791648e 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -25,6 +25,7 @@ import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.keyboard.internal.GestureStroke; import com.android.inputmethod.keyboard.internal.GestureStroke.GestureStrokeParams; import com.android.inputmethod.keyboard.internal.GestureStrokeWithPreviewPoints; +import com.android.inputmethod.keyboard.internal.GestureStrokeWithPreviewPoints.GestureStrokePreviewParams; import com.android.inputmethod.keyboard.internal.PointerTrackerQueue; import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.Constants; @@ -161,6 +162,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { // Parameters for pointer handling. private static PointerTrackerParams sParams; private static GestureStrokeParams sGestureStrokeParams; + private static GestureStrokePreviewParams sGesturePreviewParams; private static boolean sNeedsPhantomSuddenMoveEventHack; // Move this threshold to resource. // TODO: Device specific parameter would be better for device specific hack? @@ -339,12 +341,14 @@ public final class PointerTracker implements PointerTrackerQueue.Element { sNeedsPhantomSuddenMoveEventHack = needsPhantomSuddenMoveEventHack; sParams = PointerTrackerParams.DEFAULT; sGestureStrokeParams = GestureStrokeParams.DEFAULT; + sGesturePreviewParams = GestureStrokePreviewParams.DEFAULT; sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams); } public static void setParameters(final TypedArray mainKeyboardViewAttr) { sParams = new PointerTrackerParams(mainKeyboardViewAttr); sGestureStrokeParams = new GestureStrokeParams(mainKeyboardViewAttr); + sGesturePreviewParams = new GestureStrokePreviewParams(mainKeyboardViewAttr); sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams); } @@ -428,7 +432,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } mPointerId = id; mGestureStrokeWithPreviewPoints = new GestureStrokeWithPreviewPoints( - id, sGestureStrokeParams); + id, sGestureStrokeParams, sGesturePreviewParams); setKeyDetectorInner(handler.getKeyDetector()); mListener = handler.getKeyboardActionListener(); mDrawingProxy = handler.getDrawingProxy(); diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java index 7a51e2568..312fd2160 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java @@ -16,6 +16,9 @@ package com.android.inputmethod.keyboard.internal; +import android.content.res.TypedArray; + +import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.ResizableIntArray; public final class GestureStrokeWithPreviewPoints extends GestureStroke { @@ -25,6 +28,8 @@ public final class GestureStrokeWithPreviewPoints extends GestureStroke { private final ResizableIntArray mPreviewXCoordinates = new ResizableIntArray(PREVIEW_CAPACITY); private final ResizableIntArray mPreviewYCoordinates = new ResizableIntArray(PREVIEW_CAPACITY); + private final GestureStrokePreviewParams mPreviewParams; + private int mStrokeId; private int mLastPreviewSize; private final HermiteInterpolator mInterpolator = new HermiteInterpolator(); @@ -32,23 +37,53 @@ public final class GestureStrokeWithPreviewPoints extends GestureStroke { private int mLastX; private int mLastY; - private double mMinPreviewSamplingDistance; private double mDistanceFromLastSample; - private double mInterpolationDistanceThreshold; - - // TODO: Move these constants to resource. - // TODO: Use "dp" instead of ratio to the keyWidth because table has rather large keys. - // The minimum trail distance between sample points for preview in keyWidth unit when using - // interpolation. - private static final float MIN_PREVIEW_SAMPLING_RATIO_TO_KEY_WIDTH = 0.2f; - // The angular threshold to use interpolation in radian. PI/12 is 15 degree. - private static final double INTERPOLATION_ANGULAR_THRESHOLD = Math.PI / 12.0d; - // The distance threshold to use interpolation in keyWidth unit. - private static final float INTERPOLATION_DISTANCE_THRESHOLD_TO_KEY_WIDTH = 0.5f; - private static final int MAX_INTERPOLATION_PARTITIONS = 6; - - public GestureStrokeWithPreviewPoints(final int pointerId, final GestureStrokeParams params) { - super(pointerId, params); + + public static final class GestureStrokePreviewParams { + public final double mMinSamplingDistance; // in pixel + public final double mMaxInterpolationAngularThreshold; // in radian + public final double mMaxInterpolationDistanceThreshold; // in pixel + public final int mMaxInterpolationSegments; + + public static final GestureStrokePreviewParams DEFAULT = new GestureStrokePreviewParams(); + + private static final int DEFAULT_MAX_INTERPOLATION_ANGULAR_THRESHOLD = 15; // in degree + + private GestureStrokePreviewParams() { + mMinSamplingDistance = 0.0d; + mMaxInterpolationAngularThreshold = + degreeToRadian(DEFAULT_MAX_INTERPOLATION_ANGULAR_THRESHOLD); + mMaxInterpolationDistanceThreshold = mMinSamplingDistance; + mMaxInterpolationSegments = 4; + } + + private static double degreeToRadian(final int degree) { + return (double)degree / 180.0d * Math.PI; + } + + public GestureStrokePreviewParams(final TypedArray mainKeyboardViewAttr) { + mMinSamplingDistance = mainKeyboardViewAttr.getDimension( + R.styleable.MainKeyboardView_gesturePreviewTrailMinSamplingDistance, + (float)DEFAULT.mMinSamplingDistance); + final int interpolationAngularDegree = mainKeyboardViewAttr.getInteger(R.styleable + .MainKeyboardView_gesturePreviewTrailMaxInterpolationAngularThreshold, 0); + mMaxInterpolationAngularThreshold = (interpolationAngularDegree <= 0) + ? DEFAULT.mMaxInterpolationAngularThreshold + : degreeToRadian(interpolationAngularDegree); + mMaxInterpolationDistanceThreshold = mainKeyboardViewAttr.getDimension(R.styleable + .MainKeyboardView_gesturePreviewTrailMaxInterpolationDistanceThreshold, + (float)DEFAULT.mMaxInterpolationDistanceThreshold); + mMaxInterpolationSegments = mainKeyboardViewAttr.getInteger( + R.styleable.MainKeyboardView_gesturePreviewTrailMaxInterpolationSegments, + DEFAULT.mMaxInterpolationSegments); + } + } + + public GestureStrokeWithPreviewPoints(final int pointerId, + final GestureStrokeParams strokeParams, + final GestureStrokePreviewParams previewParams) { + super(pointerId, strokeParams); + mPreviewParams = previewParams; } @Override @@ -66,19 +101,12 @@ public final class GestureStrokeWithPreviewPoints extends GestureStroke { return mStrokeId; } - @Override - public void setKeyboardGeometry(final int keyWidth, final int keyboardHeight) { - super.setKeyboardGeometry(keyWidth, keyboardHeight); - mMinPreviewSamplingDistance = keyWidth * MIN_PREVIEW_SAMPLING_RATIO_TO_KEY_WIDTH; - mInterpolationDistanceThreshold = keyWidth * INTERPOLATION_DISTANCE_THRESHOLD_TO_KEY_WIDTH; - } - private boolean needsSampling(final int x, final int y) { mDistanceFromLastSample += Math.hypot(x - mLastX, y - mLastY); mLastX = x; mLastY = y; final boolean isDownEvent = (mPreviewEventTimes.getLength() == 0); - if (mDistanceFromLastSample >= mMinPreviewSamplingDistance || isDownEvent) { + if (mDistanceFromLastSample >= mPreviewParams.mMinSamplingDistance || isDownEvent) { mDistanceFromLastSample = 0.0d; return true; } @@ -144,19 +172,19 @@ public final class GestureStrokeWithPreviewPoints extends GestureStroke { final double m1 = Math.atan2(mInterpolator.mSlope1Y, mInterpolator.mSlope1X); final double m2 = Math.atan2(mInterpolator.mSlope2Y, mInterpolator.mSlope2X); final double deltaAngle = Math.abs(angularDiff(m2, m1)); - final int partitionsByAngle = (int)Math.ceil( - deltaAngle / INTERPOLATION_ANGULAR_THRESHOLD); + final int segmentsByAngle = (int)Math.ceil( + deltaAngle / mPreviewParams.mMaxInterpolationAngularThreshold); final double deltaDistance = Math.hypot(mInterpolator.mP1X - mInterpolator.mP2X, mInterpolator.mP1Y - mInterpolator.mP2Y); - final int partitionsByDistance = (int)Math.ceil(deltaDistance - / mInterpolationDistanceThreshold); - final int partitions = Math.min(MAX_INTERPOLATION_PARTITIONS, - Math.max(partitionsByAngle, partitionsByDistance)); + final int segmentsByDistance = (int)Math.ceil(deltaDistance + / mPreviewParams.mMaxInterpolationDistanceThreshold); + final int segments = Math.min(mPreviewParams.mMaxInterpolationSegments, + Math.max(segmentsByAngle, segmentsByDistance)); final int t1 = eventTimes.get(d1); final int dt = pt[p2] - pt[p1]; d1++; - for (int i = 1; i < partitions; i++) { - final float t = i / (float)partitions; + for (int i = 1; i < segments; i++) { + final float t = i / (float)segments; mInterpolator.interpolate(t); eventTimes.add(d1, (int)(dt * t) + t1); xCoords.add(d1, (int)mInterpolator.mInterpolatedX); diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java index d660f70cc..b9db9a092 100644 --- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java @@ -16,6 +16,8 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.latin.personalization.AccountUtils; + import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; @@ -28,6 +30,7 @@ import android.provider.ContactsContract.Contacts; import android.text.TextUtils; import android.util.Log; +import java.util.List; import java.util.Locale; public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { @@ -105,11 +108,27 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { @Override public void loadDictionaryAsync() { clearFusionDictionary(); + loadDeviceAccountsEmailAddresses(); loadDictionaryAsyncForUri(ContactsContract.Profile.CONTENT_URI); // TODO: Switch this URL to the newer ContactsContract too loadDictionaryAsyncForUri(Contacts.CONTENT_URI); } + private void loadDeviceAccountsEmailAddresses() { + final List<String> accountVocabulary = + AccountUtils.getDeviceAccountsEmailAddresses(mContext); + if (accountVocabulary == null || accountVocabulary.isEmpty()) { + return; + } + for (String word : accountVocabulary) { + if (DEBUG) { + Log.d(TAG, "loadAccountVocabulary: " + word); + } + super.addWord(word, null /* shortcut */, FREQUENCY_FOR_CONTACTS, + false /* isNotAWord */); + } + } + private void loadDictionaryAsyncForUri(final Uri uri) { try { Cursor cursor = mContext.getContentResolver() diff --git a/java/src/com/android/inputmethod/latin/personalization/AccountUtils.java b/java/src/com/android/inputmethod/latin/personalization/AccountUtils.java new file mode 100644 index 000000000..93687e193 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/personalization/AccountUtils.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.personalization; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.content.Context; +import android.util.Patterns; + +import java.util.ArrayList; +import java.util.List; + +public class AccountUtils { + private AccountUtils() { + // This utility class is not publicly instantiable. + } + + private static Account[] getAccounts(final Context context) { + return AccountManager.get(context).getAccounts(); + } + + public static List<String> getDeviceAccountsEmailAddresses(final Context context) { + final ArrayList<String> retval = new ArrayList<String>(); + for (final Account account : getAccounts(context)) { + final String name = account.name; + if (Patterns.EMAIL_ADDRESS.matcher(name).matches()) { + retval.add(name); + retval.add(name.split("@")[0]); + } + } + return retval; + } +} diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp index 11fa3da3a..1dd68ea8b 100644 --- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp +++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp @@ -109,7 +109,8 @@ static jlong latinime_BinaryDictionary_open(JNIEnv *env, jclass clazz, jstring s } Dictionary *dictionary = 0; if (BinaryFormat::UNKNOWN_FORMAT - == BinaryFormat::detectFormat(static_cast<uint8_t *>(dictBuf))) { + == BinaryFormat::detectFormat(static_cast<uint8_t *>(dictBuf), + static_cast<int>(dictSize))) { AKLOGE("DICT: dictionary format is unknown, bad magic number"); #ifdef USE_MMAP_FOR_DICTIONARY releaseDictBuf(static_cast<const char *>(dictBuf) - adjust, adjDictSize, fd); diff --git a/native/jni/src/binary_format.h b/native/jni/src/binary_format.h index 06f50dc7f..98241532f 100644 --- a/native/jni/src/binary_format.h +++ b/native/jni/src/binary_format.h @@ -64,13 +64,14 @@ class BinaryFormat { static const int UNKNOWN_FORMAT = -1; static const int SHORTCUT_LIST_SIZE_SIZE = 2; - static int detectFormat(const uint8_t *const dict); - static int getHeaderSize(const uint8_t *const dict); - static int getFlags(const uint8_t *const dict); + static int detectFormat(const uint8_t *const dict, const int dictSize); + static int getHeaderSize(const uint8_t *const dict, const int dictSize); + static int getFlags(const uint8_t *const dict, const int dictSize); static bool hasBlacklistedOrNotAWordFlag(const int flags); - static void readHeaderValue(const uint8_t *const dict, const char *const key, int *outValue, - const int outValueSize); - static int readHeaderValueInt(const uint8_t *const dict, const char *const key); + static void readHeaderValue(const uint8_t *const dict, const int dictSize, + const char *const key, int *outValue, const int outValueSize); + static int readHeaderValueInt(const uint8_t *const dict, const int dictSize, + const char *const key); static int getGroupCountAndForwardPointer(const uint8_t *const dict, int *pos); static uint8_t getFlagsAndForwardPointer(const uint8_t *const dict, int *pos); static int getCodePointAndForwardPointer(const uint8_t *const dict, int *pos); @@ -96,7 +97,7 @@ class BinaryFormat { const uint8_t *bigramFilter, const int unigramProbability); static int getBigramProbabilityFromHashMap(const int position, const hash_map_compat<int, int> *bigramMap, const int unigramProbability); - static float getMultiWordCostMultiplier(const uint8_t *const dict); + static float getMultiWordCostMultiplier(const uint8_t *const dict, const int dictSize); static void fillBigramProbabilityToHashMap(const uint8_t *const root, int position, hash_map_compat<int, int> *bigramMap); static int getBigramProbability(const uint8_t *const root, int position, @@ -122,6 +123,8 @@ class BinaryFormat { static const int FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES = 0x20; static const int FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES = 0x30; + // Any file smaller than this is not a dictionary. + static const int DICTIONARY_MINIMUM_SIZE = 4; // Originally, format version 1 had a 16-bit magic number, then the version number `01' // then options that must be 0. Hence the first 32-bits of the format are always as follow // and it's okay to consider them a magic number as a whole. @@ -131,6 +134,8 @@ class BinaryFormat { // number, so we had to change it so that version 2 files would be rejected by older // implementations. On this occasion, we made the magic number 32 bits long. static const int FORMAT_VERSION_2_MAGIC_NUMBER = -1681835266; // 0x9BC13AFE + // Magic number (4 bytes), version (2 bytes), options (2 bytes), header size (4 bytes) = 12 + static const int FORMAT_VERSION_2_MINIMUM_SIZE = 12; static const int CHARACTER_ARRAY_TERMINATOR_SIZE = 1; static const int MINIMAL_ONE_BYTE_CHARACTER_VALUE = 0x20; @@ -141,8 +146,11 @@ class BinaryFormat { static int skipBigrams(const uint8_t *const dict, const uint8_t flags, const int pos); }; -AK_FORCE_INLINE int BinaryFormat::detectFormat(const uint8_t *const dict) { +AK_FORCE_INLINE int BinaryFormat::detectFormat(const uint8_t *const dict, const int dictSize) { // The magic number is stored big-endian. + // If the dictionary is less than 4 bytes, we can't even read the magic number, so we don't + // understand this format. + if (dictSize < DICTIONARY_MINIMUM_SIZE) return UNKNOWN_FORMAT; const int magicNumber = (dict[0] << 24) + (dict[1] << 16) + (dict[2] << 8) + dict[3]; switch (magicNumber) { case FORMAT_VERSION_1_MAGIC_NUMBER: @@ -152,6 +160,10 @@ AK_FORCE_INLINE int BinaryFormat::detectFormat(const uint8_t *const dict) { // Options (2 bytes) must be 0x00 0x00 return 1; case FORMAT_VERSION_2_MAGIC_NUMBER: + // Version 2 dictionaries are at least 12 bytes long (see below details for the header). + // If this dictionary has the version 2 magic number but is less than 12 bytes long, then + // it's an unknown format and we need to avoid confidently reading the next bytes. + if (dictSize < FORMAT_VERSION_2_MINIMUM_SIZE) return UNKNOWN_FORMAT; // Format 2 header is as follows: // Magic number (4 bytes) 0x9B 0xC1 0x3A 0xFE // Version number (2 bytes) 0x00 0x02 @@ -163,8 +175,8 @@ AK_FORCE_INLINE int BinaryFormat::detectFormat(const uint8_t *const dict) { } } -inline int BinaryFormat::getFlags(const uint8_t *const dict) { - switch (detectFormat(dict)) { +inline int BinaryFormat::getFlags(const uint8_t *const dict, const int dictSize) { + switch (detectFormat(dict, dictSize)) { case 1: return NO_FLAGS; // TODO: NO_FLAGS is unused anywhere else? default: @@ -176,8 +188,8 @@ inline bool BinaryFormat::hasBlacklistedOrNotAWordFlag(const int flags) { return (flags & (FLAG_IS_BLACKLISTED | FLAG_IS_NOT_A_WORD)) != 0; } -inline int BinaryFormat::getHeaderSize(const uint8_t *const dict) { - switch (detectFormat(dict)) { +inline int BinaryFormat::getHeaderSize(const uint8_t *const dict, const int dictSize) { + switch (detectFormat(dict, dictSize)) { case 1: return FORMAT_VERSION_1_HEADER_SIZE; case 2: @@ -188,12 +200,12 @@ inline int BinaryFormat::getHeaderSize(const uint8_t *const dict) { } } -inline void BinaryFormat::readHeaderValue(const uint8_t *const dict, const char *const key, - int *outValue, const int outValueSize) { +inline void BinaryFormat::readHeaderValue(const uint8_t *const dict, const int dictSize, + const char *const key, int *outValue, const int outValueSize) { int outValueIndex = 0; // Only format 2 and above have header attributes as {key,value} string pairs. For prior // formats, we just return an empty string, as if the key wasn't found. - if (2 <= detectFormat(dict)) { + if (2 <= detectFormat(dict, dictSize)) { const int headerOptionsOffset = 4 /* magic number */ + 2 /* dictionary version */ + 2 /* flags */; const int headerSize = @@ -236,11 +248,12 @@ inline void BinaryFormat::readHeaderValue(const uint8_t *const dict, const char if (outValueIndex >= 0) outValue[outValueIndex] = 0; } -inline int BinaryFormat::readHeaderValueInt(const uint8_t *const dict, const char *const key) { +inline int BinaryFormat::readHeaderValueInt(const uint8_t *const dict, const int dictSize, + const char *const key) { const int bufferSize = LARGEST_INT_DIGIT_COUNT; int intBuffer[bufferSize]; char charBuffer[bufferSize]; - BinaryFormat::readHeaderValue(dict, key, intBuffer, bufferSize); + BinaryFormat::readHeaderValue(dict, dictSize, key, intBuffer, bufferSize); for (int i = 0; i < bufferSize; ++i) { charBuffer[i] = intBuffer[i]; } @@ -256,8 +269,10 @@ AK_FORCE_INLINE int BinaryFormat::getGroupCountAndForwardPointer(const uint8_t * return ((msb & 0x7F) << 8) | dict[(*pos)++]; } -inline float BinaryFormat::getMultiWordCostMultiplier(const uint8_t *const dict) { - const int headerValue = readHeaderValueInt(dict, "MULTIPLE_WORDS_DEMOTION_RATE"); +inline float BinaryFormat::getMultiWordCostMultiplier(const uint8_t *const dict, + const int dictSize) { + const int headerValue = readHeaderValueInt(dict, dictSize, + "MULTIPLE_WORDS_DEMOTION_RATE"); if (headerValue == S_INT_MIN) { return 1.0f; } diff --git a/native/jni/src/dictionary.cpp b/native/jni/src/dictionary.cpp index c998c0676..dadb2bab2 100644 --- a/native/jni/src/dictionary.cpp +++ b/native/jni/src/dictionary.cpp @@ -34,9 +34,11 @@ namespace latinime { Dictionary::Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust) : mDict(static_cast<unsigned char *>(dict)), - mOffsetDict((static_cast<unsigned char *>(dict)) + BinaryFormat::getHeaderSize(mDict)), + mOffsetDict((static_cast<unsigned char *>(dict)) + + BinaryFormat::getHeaderSize(mDict, dictSize)), mDictSize(dictSize), mMmapFd(mmapFd), mDictBufAdjust(dictBufAdjust), - mUnigramDictionary(new UnigramDictionary(mOffsetDict, BinaryFormat::getFlags(mDict))), + mUnigramDictionary(new UnigramDictionary(mOffsetDict, + BinaryFormat::getFlags(mDict, dictSize))), mBigramDictionary(new BigramDictionary(mOffsetDict)), mGestureSuggest(new Suggest(GestureSuggestPolicyFactory::getGestureSuggestPolicy())), mTypingSuggest(new Suggest(TypingSuggestPolicyFactory::getTypingSuggestPolicy())) { diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.cpp b/native/jni/src/suggest/core/session/dic_traverse_session.cpp index 51165858b..6408f0163 100644 --- a/native/jni/src/suggest/core/session/dic_traverse_session.cpp +++ b/native/jni/src/suggest/core/session/dic_traverse_session.cpp @@ -64,7 +64,8 @@ static TraverseSessionFactoryRegisterer traverseSessionFactoryRegisterer; void DicTraverseSession::init(const Dictionary *const dictionary, const int *prevWord, int prevWordLength) { mDictionary = dictionary; - mMultiWordCostMultiplier = BinaryFormat::getMultiWordCostMultiplier(mDictionary->getDict()); + mMultiWordCostMultiplier = BinaryFormat::getMultiWordCostMultiplier(mDictionary->getDict(), + mDictionary->getDictSize()); if (!prevWord) { mPrevWordPos = NOT_VALID_WORD; return; |