From 1d6afa179cd31010efe28f1c3e17698d6be79cab Mon Sep 17 00:00:00 2001
From: Keisuke Kuroyanagi <ksk@google.com>
Date: Mon, 10 Feb 2014 21:06:07 +0900
Subject: [PATCH] Refactoring: extract PtNode array reading logic form helper.

Bug: 12810574
Change-Id: I2d2660871862f11630c3ad7bf04bb49ade57c1e7
---
 native/jni/NativeFileList.mk                  |  3 +-
 .../pt_common/dynamic_pt_reading_helper.cpp   | 48 +++--------
 .../pt_common/dynamic_pt_reading_helper.h     |  8 +-
 .../pt_common/pt_node_array_reader.h          | 45 +++++++++++
 .../structure/pt_common/pt_node_params.h      |  4 +-
 .../v4/ver4_patricia_trie_node_writer.h       | 11 +--
 .../v4/ver4_patricia_trie_policy.cpp          | 10 +--
 .../structure/v4/ver4_patricia_trie_policy.h  |  7 +-
 .../v4/ver4_patricia_trie_writing_helper.cpp  | 15 ++--
 .../v4/ver4_pt_node_array_reader.cpp          | 79 +++++++++++++++++++
 .../structure/v4/ver4_pt_node_array_reader.h  | 42 ++++++++++
 11 files changed, 213 insertions(+), 59 deletions(-)
 create mode 100644 native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_array_reader.h
 create mode 100644 native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_pt_node_array_reader.cpp
 create mode 100644 native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_pt_node_array_reader.h

diff --git a/native/jni/NativeFileList.mk b/native/jni/NativeFileList.mk
index c7061c8c3e..e9efde9fde 100644
--- a/native/jni/NativeFileList.mk
+++ b/native/jni/NativeFileList.mk
@@ -65,7 +65,8 @@ LATIN_IME_CORE_SRC_FILES := \
         ver4_patricia_trie_node_writer.cpp \
         ver4_patricia_trie_policy.cpp \
         ver4_patricia_trie_reading_utils.cpp \
-        ver4_patricia_trie_writing_helper.cpp) \
+        ver4_patricia_trie_writing_helper.cpp \
+        ver4_pt_node_array_reader.cpp) \
     $(addprefix suggest/policyimpl/dictionary/structure/v4/content/, \
         bigram_dict_content.cpp \
         probability_dict_content.cpp \
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.cpp
index 824d442e4e..086d98b4af 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.cpp
@@ -16,9 +16,7 @@
 
 #include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h"
 
-#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h"
-#include "suggest/policyimpl/dictionary/structure/v2/patricia_trie_reading_utils.h"
-#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_utils.h"
+#include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_array_reader.h"
 #include "utils/char_utils.h"
 
 namespace latinime {
@@ -266,27 +264,17 @@ int DynamicPtReadingHelper::getTerminalPtNodePositionOfWord(const int *const inW
 // Read node array size and process empty node arrays. Nodes and arrays are counted up in this
 // method to avoid an infinite loop.
 void DynamicPtReadingHelper::nextPtNodeArray() {
-    if (mReadingState.mPos < 0 || mReadingState.mPos >= mBuffer->getTailPosition()) {
-        // Reading invalid position because of a bug or a broken dictionary.
-        AKLOGE("Reading PtNode array info from invalid dictionary position: %d, dict size: %d",
-                mReadingState.mPos, mBuffer->getTailPosition());
-        ASSERT(false);
+    int ptNodeCountInArray = 0;
+    int firstPtNodePos = NOT_A_DICT_POS;
+    if (!mPtNodeArrayReader->readPtNodeArrayInfoAndReturnIfValid(
+            mReadingState.mPos, &ptNodeCountInArray, &firstPtNodePos)) {
         mIsError = true;
         mReadingState.mPos = NOT_A_DICT_POS;
         return;
     }
     mReadingState.mPosOfThisPtNodeArrayHead = mReadingState.mPos;
-    const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(mReadingState.mPos);
-    const uint8_t *const dictBuf = mBuffer->getBuffer(usesAdditionalBuffer);
-    if (usesAdditionalBuffer) {
-        mReadingState.mPos -= mBuffer->getOriginalBufferSize();
-    }
-    mReadingState.mRemainingPtNodeCountInThisArray =
-            PatriciaTrieReadingUtils::getPtNodeArraySizeAndAdvancePosition(dictBuf,
-                    &mReadingState.mPos);
-    if (usesAdditionalBuffer) {
-        mReadingState.mPos += mBuffer->getOriginalBufferSize();
-    }
+    mReadingState.mRemainingPtNodeCountInThisArray = ptNodeCountInArray;
+    mReadingState.mPos = firstPtNodePos;
     // Count up nodes and node arrays to avoid infinite loop.
     mReadingState.mTotalPtNodeIndexInThisArrayChain +=
             mReadingState.mRemainingPtNodeCountInThisArray;
@@ -317,29 +305,17 @@ void DynamicPtReadingHelper::nextPtNodeArray() {
 
 // Follow the forward link and read the next node array if exists.
 void DynamicPtReadingHelper::followForwardLink() {
-    if (mReadingState.mPos < 0 || mReadingState.mPos >= mBuffer->getTailPosition()) {
-        // Reading invalid position because of bug or broken dictionary.
-        AKLOGE("Reading forward link from invalid dictionary position: %d, dict size: %d",
-                mReadingState.mPos, mBuffer->getTailPosition());
-        ASSERT(false);
+    int nextPtNodeArrayPos = NOT_A_DICT_POS;
+    if (!mPtNodeArrayReader->readForwardLinkAndReturnIfValid(
+            mReadingState.mPos, &nextPtNodeArrayPos)) {
         mIsError = true;
         mReadingState.mPos = NOT_A_DICT_POS;
         return;
     }
-    const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(mReadingState.mPos);
-    const uint8_t *const dictBuf = mBuffer->getBuffer(usesAdditionalBuffer);
-    if (usesAdditionalBuffer) {
-        mReadingState.mPos -= mBuffer->getOriginalBufferSize();
-    }
-    const int forwardLinkPosition =
-            DynamicPtReadingUtils::getForwardLinkPosition(dictBuf, mReadingState.mPos);
-    if (usesAdditionalBuffer) {
-        mReadingState.mPos += mBuffer->getOriginalBufferSize();
-    }
     mReadingState.mPosOfLastForwardLinkField = mReadingState.mPos;
-    if (DynamicPtReadingUtils::isValidForwardLinkPosition(forwardLinkPosition)) {
+    if (nextPtNodeArrayPos != NOT_A_DICT_POS) {
         // Follow the forward link.
-        mReadingState.mPos += forwardLinkPosition;
+        mReadingState.mPos = nextPtNodeArrayPos;
         nextPtNodeArray();
     } else {
         // All node arrays have been read.
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h
index bcc5c78574..f6ee0fe076 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h
@@ -29,6 +29,7 @@ namespace latinime {
 class BufferWithExtendableBuffer;
 class DictionaryBigramsStructurePolicy;
 class DictionaryShortcutsStructurePolicy;
+class PtNodeArrayReader;
 
 /*
  * This class is used for traversing dynamic patricia trie. This class supports iterating nodes and
@@ -75,9 +76,11 @@ class DynamicPtReadingHelper {
     };
 
     DynamicPtReadingHelper(const BufferWithExtendableBuffer *const buffer,
-            const PtNodeReader *const ptNodeReader)
+            const PtNodeReader *const ptNodeReader,
+            const PtNodeArrayReader *const ptNodeArrayReader)
             : mIsError(false), mReadingState(), mBuffer(buffer),
-              mPtNodeReader(ptNodeReader), mReadingStateStack() {}
+              mPtNodeReader(ptNodeReader), mPtNodeArrayReader(ptNodeArrayReader),
+              mReadingStateStack() {}
 
     ~DynamicPtReadingHelper() {}
 
@@ -254,6 +257,7 @@ class DynamicPtReadingHelper {
     PtNodeReadingState mReadingState;
     const BufferWithExtendableBuffer *const mBuffer;
     const PtNodeReader *const mPtNodeReader;
+    const PtNodeArrayReader *const mPtNodeArrayReader;
     std::vector<PtNodeReadingState> mReadingStateStack;
 
     void nextPtNodeArray();
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_array_reader.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_array_reader.h
new file mode 100644
index 0000000000..6078d82853
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_array_reader.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2014, 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.
+ */
+
+#ifndef LATINIME_PT_NODE_ARRAY_READER_H
+#define LATINIME_PT_NODE_ARRAY_READER_H
+
+#include "defines.h"
+
+namespace latinime {
+
+// Interface class used to read PtNode array information.
+class PtNodeArrayReader {
+ public:
+    virtual ~PtNodeArrayReader() {}
+
+    // Returns if the position is valid or not.
+    virtual bool readPtNodeArrayInfoAndReturnIfValid(const int ptNodeArrayPos,
+            int *const outPtNodeCount, int *const outFirstPtNodePos) const = 0;
+
+    // Returns if the position is valid or not. NOT_A_DICT_POS is set to outNextPtNodeArrayPos when
+    // the next array doesn't exist.
+    virtual bool readForwardLinkAndReturnIfValid(const int forwordLinkPos,
+            int *const outNextPtNodeArrayPos) const = 0;
+
+ protected:
+    PtNodeArrayReader() {};
+
+ private:
+    DISALLOW_COPY_AND_ASSIGN(PtNodeArrayReader);
+};
+} // namespace latinime
+#endif /* LATINIME_PT_NODE_READER_H */
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h
index 84731eb178..a4a53a80c9 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h
@@ -205,9 +205,7 @@ class PtNodeParams {
 
  private:
     // This class have a public copy constructor to be used as a return value.
-
-    // Disallowing the assignment operator.
-    PtNodeParams &operator=(PtNodeParams &ptNodeParams);
+    DISALLOW_ASSIGNMENT_OPERATOR(PtNodeParams);
 
     const int mHeadPos;
     const PatriciaTrieReadingUtils::NodeFlags mFlags;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h
index 69576d8e50..bf06576202 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h
@@ -24,13 +24,14 @@
 #include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h"
 #include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_writer.h"
 #include "suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h"
-#include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h"
 
 namespace latinime {
 
 class BufferWithExtendableBuffer;
 class Ver4BigramListPolicy;
 class Ver4DictBuffers;
+class Ver4PatriciaTrieNodeReader;
+class Ver4PtNodeArrayReader;
 class Ver4ShortcutListPolicy;
 
 /*
@@ -39,10 +40,11 @@ class Ver4ShortcutListPolicy;
 class Ver4PatriciaTrieNodeWriter : public PtNodeWriter {
  public:
     Ver4PatriciaTrieNodeWriter(BufferWithExtendableBuffer *const trieBuffer,
-            Ver4DictBuffers *const buffers, const Ver4PatriciaTrieNodeReader *const ptNodeReader,
+            Ver4DictBuffers *const buffers, const PtNodeReader *const ptNodeReader,
+            const PtNodeArrayReader *const ptNodeArrayReader,
             Ver4BigramListPolicy *const bigramPolicy, Ver4ShortcutListPolicy *const shortcutPolicy)
-            : mTrieBuffer(trieBuffer), mBuffers(buffers), mPtNodeReader(ptNodeReader),
-              mReadingHelper(mTrieBuffer, mPtNodeReader),
+            : mTrieBuffer(trieBuffer), mBuffers(buffers),
+              mReadingHelper(mTrieBuffer, ptNodeReader, ptNodeArrayReader),
               mBigramPolicy(bigramPolicy), mShortcutPolicy(shortcutPolicy) {}
 
     virtual ~Ver4PatriciaTrieNodeWriter() {}
@@ -114,7 +116,6 @@ class Ver4PatriciaTrieNodeWriter : public PtNodeWriter {
 
     BufferWithExtendableBuffer *const mTrieBuffer;
     Ver4DictBuffers *const mBuffers;
-    const Ver4PatriciaTrieNodeReader *const mPtNodeReader;
     DynamicPtReadingHelper mReadingHelper;
     Ver4BigramListPolicy *const mBigramPolicy;
     Ver4ShortcutListPolicy *const mShortcutPolicy;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp
index 75d85988c5..980193d42c 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp
@@ -43,7 +43,7 @@ void Ver4PatriciaTriePolicy::createAndGetAllChildDicNodes(const DicNode *const d
     if (!dicNode->hasChildren()) {
         return;
     }
-    DynamicPtReadingHelper readingHelper(mDictBuffer, &mNodeReader);
+    DynamicPtReadingHelper readingHelper(mDictBuffer, &mNodeReader, &mPtNodeArrayReader);
     readingHelper.initWithPtNodeArrayPos(dicNode->getChildrenPtNodeArrayPos());
     while (!readingHelper.isEnd()) {
         const PtNodeParams ptNodeParams = readingHelper.getPtNodeParams();
@@ -70,7 +70,7 @@ void Ver4PatriciaTriePolicy::createAndGetAllChildDicNodes(const DicNode *const d
 int Ver4PatriciaTriePolicy::getCodePointsAndProbabilityAndReturnCodePointCount(
         const int ptNodePos, const int maxCodePointCount, int *const outCodePoints,
         int *const outUnigramProbability) const {
-    DynamicPtReadingHelper readingHelper(mDictBuffer, &mNodeReader);
+    DynamicPtReadingHelper readingHelper(mDictBuffer, &mNodeReader, &mPtNodeArrayReader);
     readingHelper.initWithPtNodePos(ptNodePos);
     return readingHelper.getCodePointsAndProbabilityAndReturnCodePointCount(
             maxCodePointCount, outCodePoints, outUnigramProbability);
@@ -78,7 +78,7 @@ int Ver4PatriciaTriePolicy::getCodePointsAndProbabilityAndReturnCodePointCount(
 
 int Ver4PatriciaTriePolicy::getTerminalPtNodePositionOfWord(const int *const inWord,
         const int length, const bool forceLowerCaseSearch) const {
-    DynamicPtReadingHelper readingHelper(mDictBuffer, &mNodeReader);
+    DynamicPtReadingHelper readingHelper(mDictBuffer, &mNodeReader, &mPtNodeArrayReader);
     readingHelper.initWithPtNodeArrayPos(getRootPosition());
     return readingHelper.getTerminalPtNodePositionOfWord(inWord, length, forceLowerCaseSearch);
 }
@@ -158,7 +158,7 @@ bool Ver4PatriciaTriePolicy::addUnigramWord(const int *const word, const int len
                 shortcutLength);
         return false;
     }
-    DynamicPtReadingHelper readingHelper(mDictBuffer, &mNodeReader);
+    DynamicPtReadingHelper readingHelper(mDictBuffer, &mNodeReader, &mPtNodeArrayReader);
     readingHelper.initWithPtNodeArrayPos(getRootPosition());
     bool addedNewUnigram = false;
     if (mUpdatingHelper.addUnigramWord(&readingHelper, word, length, probability, isNotAWord,
@@ -397,7 +397,7 @@ int Ver4PatriciaTriePolicy::getNextWordAndNextToken(const int token, int *const
         mTerminalPtNodePositionsForIteratingWords.clear();
         DynamicPtReadingHelper::TraversePolicyToGetAllTerminalPtNodePositions traversePolicy(
                 &mTerminalPtNodePositionsForIteratingWords);
-        DynamicPtReadingHelper readingHelper(mDictBuffer, &mNodeReader);
+        DynamicPtReadingHelper readingHelper(mDictBuffer, &mNodeReader, &mPtNodeArrayReader);
         readingHelper.initWithPtNodeArrayPos(getRootPosition());
         readingHelper.traverseAllPtNodesInPostorderDepthFirstManner(&traversePolicy);
     }
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h
index 9ba5be0c32..6921630583 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h
@@ -29,6 +29,7 @@
 #include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h"
 #include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h"
 #include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.h"
+#include "suggest/policyimpl/dictionary/structure/v4/ver4_pt_node_array_reader.h"
 #include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h"
 
 namespace latinime {
@@ -47,8 +48,9 @@ class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy {
               mShortcutPolicy(mBuffers.get()->getMutableShortcutDictContent(),
                       mBuffers.get()->getTerminalPositionLookupTable()),
               mNodeReader(mDictBuffer, mBuffers.get()->getProbabilityDictContent()),
-              mNodeWriter(mDictBuffer, mBuffers.get(), &mNodeReader, &mBigramPolicy,
-                      &mShortcutPolicy),
+              mPtNodeArrayReader(mDictBuffer),
+              mNodeWriter(mDictBuffer, mBuffers.get(), &mNodeReader, &mPtNodeArrayReader,
+                      &mBigramPolicy, &mShortcutPolicy),
               mUpdatingHelper(mDictBuffer, &mNodeReader, &mNodeWriter),
               mWritingHelper(mBuffers.get()),
               mUnigramCount(mHeaderPolicy->getUnigramCount()),
@@ -132,6 +134,7 @@ class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy {
     Ver4BigramListPolicy mBigramPolicy;
     Ver4ShortcutListPolicy mShortcutPolicy;
     Ver4PatriciaTrieNodeReader mNodeReader;
+    Ver4PtNodeArrayReader mPtNodeArrayReader;
     Ver4PatriciaTrieNodeWriter mNodeWriter;
     DynamicPtUpdatingHelper mUpdatingHelper;
     Ver4PatriciaTrieWritingHelper mWritingHelper;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp
index 672097455c..f26c430d8e 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp
@@ -26,6 +26,7 @@
 #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h"
 #include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h"
 #include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h"
+#include "suggest/policyimpl/dictionary/structure/v4/ver4_pt_node_array_reader.h"
 #include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h"
 #include "suggest/policyimpl/dictionary/utils/file_utils.h"
 #include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h"
@@ -74,14 +75,16 @@ bool Ver4PatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos,
         int *const outUnigramCount, int *const outBigramCount) {
     Ver4PatriciaTrieNodeReader ptNodeReader(mBuffers->getTrieBuffer(),
             mBuffers->getProbabilityDictContent());
+    Ver4PtNodeArrayReader ptNodeArrayReader(mBuffers->getTrieBuffer());
     Ver4BigramListPolicy bigramPolicy(mBuffers->getMutableBigramDictContent(),
             mBuffers->getTerminalPositionLookupTable(), headerPolicy);
     Ver4ShortcutListPolicy shortcutPolicy(mBuffers->getMutableShortcutDictContent(),
             mBuffers->getTerminalPositionLookupTable());
     Ver4PatriciaTrieNodeWriter ptNodeWriter(mBuffers->getWritableTrieBuffer(),
-            mBuffers, &ptNodeReader, &bigramPolicy, &shortcutPolicy);
+            mBuffers, &ptNodeReader, &ptNodeArrayReader, &bigramPolicy, &shortcutPolicy);
 
-    DynamicPtReadingHelper readingHelper(mBuffers->getTrieBuffer(), &ptNodeReader);
+    DynamicPtReadingHelper readingHelper(mBuffers->getTrieBuffer(), &ptNodeReader,
+            &ptNodeArrayReader);
     readingHelper.initWithPtNodeArrayPos(rootPtNodeArrayPos);
     DynamicPtGcEventListeners
             ::TraversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted
@@ -124,7 +127,7 @@ bool Ver4PatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos,
     PtNodeWriter::DictPositionRelocationMap dictPositionRelocationMap;
     readingHelper.initWithPtNodeArrayPos(rootPtNodeArrayPos);
     Ver4PatriciaTrieNodeWriter ptNodeWriterForNewBuffers(buffersToWrite->getWritableTrieBuffer(),
-            buffersToWrite, &ptNodeReader, &bigramPolicy, &shortcutPolicy);
+            buffersToWrite, &ptNodeReader, &ptNodeArrayReader, &bigramPolicy, &shortcutPolicy);
     DynamicPtGcEventListeners::TraversePolicyToPlaceAndWriteValidPtNodesToBuffer
             traversePolicyToPlaceAndWriteValidPtNodesToBuffer(&ptNodeWriterForNewBuffers,
                     buffersToWrite->getWritableTrieBuffer(), &dictPositionRelocationMap);
@@ -136,12 +139,14 @@ bool Ver4PatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos,
     // Create policy instances for the GCed dictionary.
     Ver4PatriciaTrieNodeReader newPtNodeReader(buffersToWrite->getTrieBuffer(),
             buffersToWrite->getProbabilityDictContent());
+    Ver4PtNodeArrayReader newPtNodeArrayreader(buffersToWrite->getTrieBuffer());
     Ver4BigramListPolicy newBigramPolicy(buffersToWrite->getMutableBigramDictContent(),
             buffersToWrite->getTerminalPositionLookupTable(), headerPolicy);
     Ver4ShortcutListPolicy newShortcutPolicy(buffersToWrite->getMutableShortcutDictContent(),
             buffersToWrite->getTerminalPositionLookupTable());
     Ver4PatriciaTrieNodeWriter newPtNodeWriter(buffersToWrite->getWritableTrieBuffer(),
-            buffersToWrite, &newPtNodeReader, &newBigramPolicy, &newShortcutPolicy);
+            buffersToWrite, &newPtNodeReader, &newPtNodeArrayreader, &newBigramPolicy,
+            &newShortcutPolicy);
     // Re-assign terminal IDs for valid terminal PtNodes.
     TerminalPositionLookupTable::TerminalIdMap terminalIdMap;
     if(!buffersToWrite->getMutableTerminalPositionLookupTable()->runGCTerminalIds(
@@ -164,7 +169,7 @@ bool Ver4PatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos,
         return false;
     }
     DynamicPtReadingHelper newDictReadingHelper(buffersToWrite->getTrieBuffer(),
-            &newPtNodeReader);
+            &newPtNodeReader, &newPtNodeArrayreader);
     newDictReadingHelper.initWithPtNodeArrayPos(rootPtNodeArrayPos);
     DynamicPtGcEventListeners::TraversePolicyToUpdateAllPositionFields
             traversePolicyToUpdateAllPositionFields(&newPtNodeWriter, &dictPositionRelocationMap);
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_pt_node_array_reader.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_pt_node_array_reader.cpp
new file mode 100644
index 0000000000..bbdf40cdd2
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_pt_node_array_reader.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2014, 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.
+ */
+
+#include "suggest/policyimpl/dictionary/structure/v4/ver4_pt_node_array_reader.h"
+
+#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_utils.h"
+#include "suggest/policyimpl/dictionary/structure/v2/patricia_trie_reading_utils.h"
+#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h"
+
+namespace latinime {
+
+bool Ver4PtNodeArrayReader::readPtNodeArrayInfoAndReturnIfValid(const int ptNodeArrayPos,
+        int *const outPtNodeCount, int *const outFirstPtNodePos) const {
+    if (ptNodeArrayPos < 0 || ptNodeArrayPos >= mBuffer->getTailPosition()) {
+        // Reading invalid position because of a bug or a broken dictionary.
+        AKLOGE("Reading PtNode array info from invalid dictionary position: %d, dict size: %d",
+                ptNodeArrayPos, mBuffer->getTailPosition());
+        ASSERT(false);
+        return false;
+    }
+    const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(ptNodeArrayPos);
+    const uint8_t *const dictBuf = mBuffer->getBuffer(usesAdditionalBuffer);
+    int readingPos = ptNodeArrayPos;
+    if (usesAdditionalBuffer) {
+        readingPos -= mBuffer->getOriginalBufferSize();
+    }
+    const int ptNodeCountInArray = PatriciaTrieReadingUtils::getPtNodeArraySizeAndAdvancePosition(
+            dictBuf, &readingPos);
+    if (usesAdditionalBuffer) {
+        readingPos += mBuffer->getOriginalBufferSize();
+    }
+    if (ptNodeCountInArray < 0) {
+        AKLOGE("Invalid PtNode count in an array: %d.", ptNodeCountInArray);
+        return false;
+    }
+    *outPtNodeCount = ptNodeCountInArray;
+    *outFirstPtNodePos = readingPos;
+    return true;
+}
+
+bool Ver4PtNodeArrayReader::readForwardLinkAndReturnIfValid(const int forwordLinkPos,
+        int *const outNextPtNodeArrayPos) const {
+    if (forwordLinkPos < 0 || forwordLinkPos >= mBuffer->getTailPosition()) {
+        // Reading invalid position because of bug or broken dictionary.
+        AKLOGE("Reading forward link from invalid dictionary position: %d, dict size: %d",
+                forwordLinkPos, mBuffer->getTailPosition());
+        ASSERT(false);
+        return false;
+    }
+    const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(forwordLinkPos);
+    const uint8_t *const dictBuf = mBuffer->getBuffer(usesAdditionalBuffer);
+    int readingPos = forwordLinkPos;
+    if (usesAdditionalBuffer) {
+        readingPos -= mBuffer->getOriginalBufferSize();
+    }
+    const int nextPtNodeArrayOffset =
+            DynamicPtReadingUtils::getForwardLinkPosition(dictBuf, readingPos);
+    if (DynamicPtReadingUtils::isValidForwardLinkPosition(nextPtNodeArrayOffset)) {
+        *outNextPtNodeArrayPos = forwordLinkPos + nextPtNodeArrayOffset;
+    } else {
+        *outNextPtNodeArrayPos = NOT_A_DICT_POS;
+    }
+    return true;
+}
+
+} // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_pt_node_array_reader.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_pt_node_array_reader.h
new file mode 100644
index 0000000000..d81808efc2
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_pt_node_array_reader.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014, 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.
+ */
+
+#ifndef LATINIME_VER4_PT_NODE_ARRAY_READER_H
+#define LATINIME_VER4_PT_NODE_ARRAY_READER_H
+
+#include "defines.h"
+#include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_array_reader.h"
+
+namespace latinime {
+
+class BufferWithExtendableBuffer;
+
+class Ver4PtNodeArrayReader : public PtNodeArrayReader {
+ public:
+    Ver4PtNodeArrayReader(const BufferWithExtendableBuffer *const buffer) : mBuffer(buffer) {};
+
+    virtual bool readPtNodeArrayInfoAndReturnIfValid(const int ptNodeArrayPos,
+            int *const outPtNodeCount, int *const outFirstPtNodePos) const;
+    virtual bool readForwardLinkAndReturnIfValid(const int forwordLinkPos,
+            int *const outNextPtNodeArrayPos) const;
+
+ private:
+    DISALLOW_COPY_AND_ASSIGN(Ver4PtNodeArrayReader);
+
+    const BufferWithExtendableBuffer *const mBuffer;
+};
+} // namespace latinime
+#endif /* LATINIME_VER4_PT_NODE_ARRAY_READER_H */
-- 
GitLab