Merge "Add memory view class for int and uint8_t." into lmp-dev
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index f2a6f3b..e2feb7c 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -405,10 +405,11 @@
             final int keyboardShiftMode,
             // TODO: remove these arguments
             final int currentKeyboardScriptId, final LatinIME.UIHandler handler) {
-        final InputTransaction inputTransaction = new InputTransaction(settingsValues, event,
-                SystemClock.uptimeMillis(), mSpaceState,
+        final Event processedEvent = mWordComposer.processEvent(event);
+        final InputTransaction inputTransaction = new InputTransaction(settingsValues,
+                processedEvent, SystemClock.uptimeMillis(), mSpaceState,
                 getActualCapsMode(settingsValues, keyboardShiftMode));
-        if (event.mKeyCode != Constants.CODE_DELETE
+        if (processedEvent.mKeyCode != Constants.CODE_DELETE
                 || inputTransaction.mTimestamp > mLastKeyTime + Constants.LONG_PRESS_MILLISECONDS) {
             mDeleteCount = 0;
         }
@@ -419,14 +420,14 @@
         }
 
         // TODO: Consolidate the double-space period timer, mLastKeyTime, and the space state.
-        if (event.mCodePoint != Constants.CODE_SPACE) {
+        if (processedEvent.mCodePoint != Constants.CODE_SPACE) {
             cancelDoubleSpacePeriodCountdown();
         }
 
         boolean didAutoCorrect = false;
-        if (event.isFunctionalKeyEvent()) {
+        if (processedEvent.isFunctionalKeyEvent()) {
             // A special key, like delete, shift, emoji, or the settings key.
-            switch (event.mKeyCode) {
+            switch (processedEvent.mKeyCode) {
             case Constants.CODE_DELETE:
                 handleBackspace(inputTransaction, currentKeyboardScriptId);
                 // Backspace is a functional key, but it affects the contents of the editor.
@@ -478,7 +479,8 @@
             case Constants.CODE_SHIFT_ENTER:
                 // TODO: remove this object
                 final Event tmpEvent = Event.createSoftwareKeypressEvent(Constants.CODE_ENTER,
-                        event.mKeyCode, event.mX, event.mY, event.isKeyRepeat());
+                        processedEvent.mKeyCode, processedEvent.mX, processedEvent.mY,
+                        processedEvent.isKeyRepeat());
                 final InputTransaction tmpTransaction = new InputTransaction(
                         inputTransaction.mSettingsValues, tmpEvent,
                         inputTransaction.mTimestamp, inputTransaction.mSpaceState,
@@ -489,11 +491,11 @@
                 inputTransaction.setDidAffectContents();
                 break;
             default:
-                throw new RuntimeException("Unknown key code : " + event.mKeyCode);
+                throw new RuntimeException("Unknown key code : " + processedEvent.mKeyCode);
             }
         } else {
             inputTransaction.setDidAffectContents();
-            switch (event.mCodePoint) {
+            switch (processedEvent.mCodePoint) {
             case Constants.CODE_ENTER:
                 final EditorInfo editorInfo = getCurrentInputEditorInfo();
                 final int imeOptionsActionId =
@@ -522,11 +524,11 @@
                 break;
             }
         }
-        if (!didAutoCorrect && event.mKeyCode != Constants.CODE_SHIFT
-                && event.mKeyCode != Constants.CODE_CAPSLOCK
-                && event.mKeyCode != Constants.CODE_SWITCH_ALPHA_SYMBOL)
+        if (!didAutoCorrect && processedEvent.mKeyCode != Constants.CODE_SHIFT
+                && processedEvent.mKeyCode != Constants.CODE_CAPSLOCK
+                && processedEvent.mKeyCode != Constants.CODE_SWITCH_ALPHA_SYMBOL)
             mLastComposedWord.deactivate();
-        if (Constants.CODE_DELETE != event.mKeyCode) {
+        if (Constants.CODE_DELETE != processedEvent.mKeyCode) {
             mEnteredText = null;
         }
         mConnection.endBatchEdit();
@@ -712,8 +714,7 @@
      */
     private void handleNonSeparator(final SettingsValues settingsValues,
             final InputTransaction inputTransaction) {
-        final Event processedEvent = mWordComposer.processEvent(inputTransaction.mEvent);
-        final int codePoint = processedEvent.mCodePoint;
+        final int codePoint = inputTransaction.mEvent.mCodePoint;
         // TODO: refactor this method to stop flipping isComposingWord around all the time, and
         // make it shorter (possibly cut into several pieces). Also factor handleNonSpecialCharacter
         // which has the same name as other handle* methods but is not the same.
@@ -763,7 +764,7 @@
             resetComposingState(false /* alsoResetLastComposedWord */);
         }
         if (isComposingWord) {
-            mWordComposer.applyProcessedEvent(processedEvent);
+            mWordComposer.applyProcessedEvent(inputTransaction.mEvent);
             // If it's the first letter, make note of auto-caps state
             if (mWordComposer.isSingleLetter()) {
                 mWordComposer.setCapitalizedModeAtStartComposingTime(inputTransaction.mShiftState);
@@ -772,7 +773,7 @@
                     mWordComposer.getTypedWord()), 1);
         } else {
             final boolean swapWeakSpace = tryStripSpaceAndReturnWhetherShouldSwapInstead(
-                    inputTransaction, processedEvent.isSuggestionStripPress());
+                    inputTransaction);
 
             if (swapWeakSpace && trySwapSwapperAndSpace(inputTransaction)) {
                 mSpaceState = SpaceState.WEAK;
@@ -823,7 +824,7 @@
         }
 
         final boolean swapWeakSpace = tryStripSpaceAndReturnWhetherShouldSwapInstead(
-                inputTransaction, isFromSuggestionStrip);
+                inputTransaction);
 
         final boolean isInsideDoubleQuoteOrAfterDigit = Constants.CODE_DOUBLE_QUOTE == codePoint
                 && mConnection.isInsideDoubleQuoteOrAfterDigit();
@@ -903,7 +904,7 @@
     private void handleBackspace(final InputTransaction inputTransaction,
             // TODO: remove this argument, put it into settingsValues
             final int currentKeyboardScriptId) {
-        final Event processedEvent = mWordComposer.processEvent(inputTransaction.mEvent);
+        final Event event = inputTransaction.mEvent;
         mSpaceState = SpaceState.NONE;
         mDeleteCount++;
 
@@ -915,7 +916,7 @@
         // Then again, even in the case of a key repeat, if the cursor is at start of text, it
         // can't go any further back, so we can update right away even if it's a key repeat.
         final int shiftUpdateKind =
-                processedEvent.isKeyRepeat() && mConnection.getExpectedSelectionStart() > 0
+                event.isKeyRepeat() && mConnection.getExpectedSelectionStart() > 0
                 ? InputTransaction.SHIFT_UPDATE_LATER : InputTransaction.SHIFT_UPDATE_NOW;
         inputTransaction.requireShiftUpdate(shiftUpdateKind);
 
@@ -935,7 +936,7 @@
                     mDictionaryFacilitator.removeWordFromPersonalizedDicts(rejectedSuggestion);
                 }
             } else {
-                mWordComposer.applyProcessedEvent(processedEvent);
+                mWordComposer.applyProcessedEvent(event);
             }
             if (mWordComposer.isComposingWord()) {
                 mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
@@ -1072,12 +1073,12 @@
     /*
      * Strip a trailing space if necessary and returns whether it's a swap weak space situation.
      * @param inputTransaction The transaction in progress.
-     * @param isFromSuggestionStrip Whether this code point is coming from the suggestion strip.
      * @return whether we should swap the space instead of removing it.
      */
     private boolean tryStripSpaceAndReturnWhetherShouldSwapInstead(
-            final InputTransaction inputTransaction, final boolean isFromSuggestionStrip) {
+            final InputTransaction inputTransaction) {
         final int codePoint = inputTransaction.mEvent.mCodePoint;
+        final boolean isFromSuggestionStrip = inputTransaction.mEvent.isSuggestionStripPress();
         if (Constants.CODE_ENTER == codePoint &&
                 SpaceState.SWAP_PUNCTUATION == inputTransaction.mSpaceState) {
             mConnection.removeTrailingSpace();
diff --git a/native/jni/src/suggest/core/dicnode/dic_node.h b/native/jni/src/suggest/core/dicnode/dic_node.h
index 92f39ea..d1b2c87 100644
--- a/native/jni/src/suggest/core/dicnode/dic_node.h
+++ b/native/jni/src/suggest/core/dicnode/dic_node.h
@@ -117,7 +117,7 @@
         int newPrevWordsPtNodePos[MAX_PREV_WORD_COUNT_FOR_N_GRAM];
         newPrevWordsPtNodePos[0] = dicNode->mDicNodeProperties.getPtNodePos();
         for (size_t i = 1; i < NELEMS(newPrevWordsPtNodePos); ++i) {
-            newPrevWordsPtNodePos[i] = dicNode->getNthPrevWordTerminalPtNodePos(i);
+            newPrevWordsPtNodePos[i] = dicNode->getPrevWordsTerminalPtNodePos()[i - 1];
         }
         mDicNodeProperties.init(rootPtNodeArrayPos, newPrevWordsPtNodePos);
         mDicNodeState.initAsRootWithPreviousWord(&dicNode->mDicNodeState,
@@ -208,12 +208,9 @@
         return mDicNodeProperties.getPtNodePos();
     }
 
-    // Used to get n-gram probability in DicNodeUtils. n is 1-indexed.
-    int getNthPrevWordTerminalPtNodePos(const int n) const {
-        if (n <= 0 || n > MAX_PREV_WORD_COUNT_FOR_N_GRAM) {
-            return NOT_A_DICT_POS;
-        }
-        return mDicNodeProperties.getPrevWordsTerminalPtNodePos()[n - 1];
+    // TODO: Use view class to return PtNodePos array.
+    const int *getPrevWordsTerminalPtNodePos() const {
+        return mDicNodeProperties.getPrevWordsTerminalPtNodePos();
     }
 
     // Used in DicNodeUtils
diff --git a/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp b/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp
index 4445f4a..69ea674 100644
--- a/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp
+++ b/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp
@@ -85,17 +85,10 @@
         const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy,
         const DicNode *const dicNode, MultiBigramMap *const multiBigramMap) {
     const int unigramProbability = dicNode->getProbability();
-    const int ptNodePos = dicNode->getPtNodePos();
-    const int prevWordTerminalPtNodePos = dicNode->getNthPrevWordTerminalPtNodePos(1 /* n */);
-    if (NOT_A_DICT_POS == ptNodePos || NOT_A_DICT_POS == prevWordTerminalPtNodePos) {
-        // Note: Normally wordPos comes from the dictionary and should never equal
-        // NOT_A_VALID_WORD_POS.
-        return dictionaryStructurePolicy->getProbability(unigramProbability,
-                NOT_A_PROBABILITY);
-    }
     if (multiBigramMap) {
+        const int *const prevWordsPtNodePos = dicNode->getPrevWordsTerminalPtNodePos();
         return multiBigramMap->getBigramProbability(dictionaryStructurePolicy,
-                prevWordTerminalPtNodePos, ptNodePos, unigramProbability);
+                prevWordsPtNodePos, dicNode->getPtNodePos(), unigramProbability);
     }
     return dictionaryStructurePolicy->getProbability(unigramProbability,
             NOT_A_PROBABILITY);
diff --git a/native/jni/src/suggest/core/dictionary/multi_bigram_map.cpp b/native/jni/src/suggest/core/dictionary/multi_bigram_map.cpp
index 012e4dc..91f33a8 100644
--- a/native/jni/src/suggest/core/dictionary/multi_bigram_map.cpp
+++ b/native/jni/src/suggest/core/dictionary/multi_bigram_map.cpp
@@ -35,34 +35,30 @@
 // Also caches the bigrams if there is space remaining and they have not been cached already.
 int MultiBigramMap::getBigramProbability(
         const DictionaryStructureWithBufferPolicy *const structurePolicy,
-        const int wordPosition, const int nextWordPosition, const int unigramProbability) {
+        const int *const prevWordsPtNodePos, const int nextWordPosition,
+        const int unigramProbability) {
+    if (!prevWordsPtNodePos || prevWordsPtNodePos[0] == NOT_A_DICT_POS) {
+        return structurePolicy->getProbability(unigramProbability, NOT_A_PROBABILITY);
+    }
     std::unordered_map<int, BigramMap>::const_iterator mapPosition =
-            mBigramMaps.find(wordPosition);
+            mBigramMaps.find(prevWordsPtNodePos[0]);
     if (mapPosition != mBigramMaps.end()) {
         return mapPosition->second.getBigramProbability(structurePolicy, nextWordPosition,
                 unigramProbability);
     }
     if (mBigramMaps.size() < MAX_CACHED_PREV_WORDS_IN_BIGRAM_MAP) {
-        addBigramsForWordPosition(structurePolicy, wordPosition);
-        return mBigramMaps[wordPosition].getBigramProbability(structurePolicy,
+        addBigramsForWordPosition(structurePolicy, prevWordsPtNodePos);
+        return mBigramMaps[prevWordsPtNodePos[0]].getBigramProbability(structurePolicy,
                 nextWordPosition, unigramProbability);
     }
-    return readBigramProbabilityFromBinaryDictionary(structurePolicy, wordPosition,
+    return readBigramProbabilityFromBinaryDictionary(structurePolicy, prevWordsPtNodePos,
             nextWordPosition, unigramProbability);
 }
 
 void MultiBigramMap::BigramMap::init(
-        const DictionaryStructureWithBufferPolicy *const structurePolicy, const int nodePos) {
-    BinaryDictionaryBigramsIterator bigramsIt =
-            structurePolicy->getBigramsIteratorOfPtNode(nodePos);
-    while (bigramsIt.hasNext()) {
-        bigramsIt.next();
-        if (bigramsIt.getBigramPos() == NOT_A_DICT_POS) {
-            continue;
-        }
-        mBigramMap[bigramsIt.getBigramPos()] = bigramsIt.getProbability();
-        mBloomFilter.setInFilter(bigramsIt.getBigramPos());
-    }
+        const DictionaryStructureWithBufferPolicy *const structurePolicy,
+        const int *const prevWordsPtNodePos) {
+    structurePolicy->iterateNgramEntries(prevWordsPtNodePos, this /* listener */);
 }
 
 int MultiBigramMap::BigramMap::getBigramProbability(
@@ -79,25 +75,33 @@
     return structurePolicy->getProbability(unigramProbability, bigramProbability);
 }
 
+void MultiBigramMap::BigramMap::onVisitEntry(const int ngramProbability,
+        const int targetPtNodePos) {
+    if (targetPtNodePos == NOT_A_DICT_POS) {
+        return;
+    }
+    mBigramMap[targetPtNodePos] = ngramProbability;
+    mBloomFilter.setInFilter(targetPtNodePos);
+}
+
 void MultiBigramMap::addBigramsForWordPosition(
-        const DictionaryStructureWithBufferPolicy *const structurePolicy, const int position) {
-    mBigramMaps[position].init(structurePolicy, position);
+        const DictionaryStructureWithBufferPolicy *const structurePolicy,
+        const int *const prevWordsPtNodePos) {
+    if (prevWordsPtNodePos) {
+        mBigramMaps[prevWordsPtNodePos[0]].init(structurePolicy, prevWordsPtNodePos);
+    }
 }
 
 int MultiBigramMap::readBigramProbabilityFromBinaryDictionary(
-        const DictionaryStructureWithBufferPolicy *const structurePolicy, const int nodePos,
-        const int nextWordPosition, const int unigramProbability) {
-    int bigramProbability = NOT_A_PROBABILITY;
-    BinaryDictionaryBigramsIterator bigramsIt =
-            structurePolicy->getBigramsIteratorOfPtNode(nodePos);
-    while (bigramsIt.hasNext()) {
-        bigramsIt.next();
-        if (bigramsIt.getBigramPos() == nextWordPosition) {
-            bigramProbability = bigramsIt.getProbability();
-            break;
-        }
+        const DictionaryStructureWithBufferPolicy *const structurePolicy,
+        const int *const prevWordsPtNodePos, const int nextWordPosition,
+        const int unigramProbability) {
+    const int bigramProbability = structurePolicy->getProbabilityOfPtNode(prevWordsPtNodePos,
+            nextWordPosition);
+    if (bigramProbability != NOT_A_PROBABILITY) {
+        return bigramProbability;
     }
-    return structurePolicy->getProbability(unigramProbability, bigramProbability);
+    return structurePolicy->getProbability(unigramProbability, NOT_A_PROBABILITY);
 }
 
 } // namespace latinime
diff --git a/native/jni/src/suggest/core/dictionary/multi_bigram_map.h b/native/jni/src/suggest/core/dictionary/multi_bigram_map.h
index 195b5e2..ad36dde 100644
--- a/native/jni/src/suggest/core/dictionary/multi_bigram_map.h
+++ b/native/jni/src/suggest/core/dictionary/multi_bigram_map.h
@@ -23,6 +23,7 @@
 #include "defines.h"
 #include "suggest/core/dictionary/binary_dictionary_bigrams_iterator.h"
 #include "suggest/core/dictionary/bloom_filter.h"
+#include "suggest/core/dictionary/ngram_listener.h"
 #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h"
 
 namespace latinime {
@@ -38,7 +39,8 @@
     // Look up the bigram probability for the given word pair from the cached bigram maps.
     // Also caches the bigrams if there is space remaining and they have not been cached already.
     int getBigramProbability(const DictionaryStructureWithBufferPolicy *const structurePolicy,
-            const int wordPosition, const int nextWordPosition, const int unigramProbability);
+            const int *const prevWordsPtNodePos, const int nextWordPosition,
+            const int unigramProbability);
 
     void clear() {
         mBigramMaps.clear();
@@ -47,32 +49,35 @@
  private:
     DISALLOW_COPY_AND_ASSIGN(MultiBigramMap);
 
-    class BigramMap {
+    class BigramMap : public NgramListener {
      public:
         BigramMap() : mBigramMap(DEFAULT_HASH_MAP_SIZE_FOR_EACH_BIGRAM_MAP), mBloomFilter() {}
-        ~BigramMap() {}
+        // Copy constructor needed for std::unordered_map.
+        BigramMap(const BigramMap &bigramMap)
+                : mBigramMap(bigramMap.mBigramMap), mBloomFilter(bigramMap.mBloomFilter) {}
+        virtual ~BigramMap() {}
 
         void init(const DictionaryStructureWithBufferPolicy *const structurePolicy,
-                const int nodePos);
-
+                const int *const prevWordsPtNodePos);
         int getBigramProbability(
                 const DictionaryStructureWithBufferPolicy *const structurePolicy,
                 const int nextWordPosition, const int unigramProbability) const;
+        virtual void onVisitEntry(const int ngramProbability, const int targetPtNodePos);
 
      private:
-        // NOTE: The BigramMap class doesn't use DISALLOW_COPY_AND_ASSIGN() because its default
-        // copy constructor is needed for use in hash_map.
         static const int DEFAULT_HASH_MAP_SIZE_FOR_EACH_BIGRAM_MAP;
         std::unordered_map<int, int> mBigramMap;
         BloomFilter mBloomFilter;
     };
 
     void addBigramsForWordPosition(
-            const DictionaryStructureWithBufferPolicy *const structurePolicy, const int position);
+            const DictionaryStructureWithBufferPolicy *const structurePolicy,
+            const int *const prevWordsPtNodePos);
 
     int readBigramProbabilityFromBinaryDictionary(
-            const DictionaryStructureWithBufferPolicy *const structurePolicy, const int nodePos,
-            const int nextWordPosition, const int unigramProbability);
+            const DictionaryStructureWithBufferPolicy *const structurePolicy,
+            const int *const prevWordsPtNodePos, const int nextWordPosition,
+            const int unigramProbability);
 
     static const size_t MAX_CACHED_PREV_WORDS_IN_BIGRAM_MAP;
     std::unordered_map<int, BigramMap> mBigramMaps;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.cpp
index ca79c18..2904b1e 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.cpp
@@ -43,6 +43,10 @@
     writeEntry(EMPTY_BITMAP_ENTRY, ROOT_BITMAP_ENTRY_INDEX);
 }
 
+TrieMap::TrieMap(uint8_t *const buffer, const int bufferSize)
+        : mBuffer(buffer, bufferSize,
+                BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE) {}
+
 void TrieMap::dump(const int from, const int to) const {
     AKLOGI("BufSize: %d", mBuffer.getTailPosition());
     for (int i = from; i < to; ++i) {
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.h b/native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.h
index db278f5..8b33346 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.h
@@ -160,6 +160,8 @@
     static const uint64_t MAX_VALUE;
 
     TrieMap();
+    // Construct TrieMap using existing data in the memory region written by save().
+    TrieMap(uint8_t *const buffer, const int bufferSize);
     void dump(const int from = 0, const int to = 0) const;
 
     bool isNearSizeLimit() const {