aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--java/res/values/attrs.xml8
-rw-r--r--java/res/values/dimens.xml8
-rw-r--r--java/res/values/styles.xml4
-rw-r--r--java/src/com/android/inputmethod/keyboard/PointerTracker.java6
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java92
-rw-r--r--java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java19
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/AccountUtils.java47
-rw-r--r--native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp3
-rw-r--r--native/jni/src/binary_format.h53
-rw-r--r--native/jni/src/dictionary.cpp6
-rw-r--r--native/jni/src/suggest/core/session/dic_traverse_session.cpp3
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;