diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index 3becc7e39febf2c280f1133292cac1df0c2ac79e..c4383d754276dd39ba2b47c34bf218af9674690e 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -86,10 +86,10 @@ static jlong latinime_BinaryDictionary_open(JNIEnv *env, jclass clazz, jstring s
     char sourceDirChars[sourceDirUtf8Length + 1];
     env->GetStringUTFRegion(sourceDir, 0, env->GetStringLength(sourceDir), sourceDirChars);
     sourceDirChars[sourceDirUtf8Length] = '\0';
-    DictionaryStructureWithBufferPolicy::StructurePoilcyPtr dictionaryStructureWithBufferPolicy(
+    DictionaryStructureWithBufferPolicy::StructurePoilcyPtr dictionaryStructureWithBufferPolicy =
             DictionaryStructureWithBufferPolicyFactory::newDictionaryStructureWithBufferPolicy(
                     sourceDirChars, static_cast<int>(dictOffset), static_cast<int>(dictSize),
-                    isUpdatable == JNI_TRUE));
+                    isUpdatable == JNI_TRUE);
     if (!dictionaryStructureWithBufferPolicy.get()) {
         return 0;
     }
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp
index 3ab6a8e21769ba1736482b997136c8f4feb0ee15..063b84cbf0699cd093e84f70ebbc4d3e036a57ff 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp
@@ -35,8 +35,8 @@ namespace latinime {
                         const int bufOffset, const int size, const bool isUpdatable) {
     // Allocated buffer in MmapedBuffer::newBuffer() will be freed in the destructor of
     // MmappedBufferWrapper if the instance has the responsibility.
-    MmappedBuffer::MmappedBufferPtr mmappedBuffer(MmappedBuffer::openBuffer(path, bufOffset, size,
-            isUpdatable));
+    MmappedBuffer::MmappedBufferPtr mmappedBuffer = MmappedBuffer::openBuffer(path, bufOffset, size,
+            isUpdatable);
     if (!mmappedBuffer.get()) {
         return DictionaryStructureWithBufferPolicy::StructurePoilcyPtr(0);
     }
@@ -58,8 +58,8 @@ namespace latinime {
             }
             // Removing extension to get the base path.
             dictDirPath.erase(pos);
-            const Ver4DictBuffers::Ver4DictBuffersPtr dictBuffers(
-                    Ver4DictBuffers::openVer4DictBuffers(dictDirPath.c_str(), mmappedBuffer));
+            const Ver4DictBuffers::Ver4DictBuffersPtr dictBuffers =
+                    Ver4DictBuffers::openVer4DictBuffers(dictDirPath.c_str(), mmappedBuffer);
             if (!dictBuffers.get()->isValid()) {
                 AKLOGE("DICT: The dictionary doesn't satisfy ver4 format requirements.");
                 ASSERT(false);
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h
index a112667e2f3aea9c023c4d24dc369a3265564123..bc9e4b6193dad243e882a3853d48ca00dcbab32c 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h
@@ -33,6 +33,10 @@ class BigramDictContent : public SparseTableDictContent {
                       Ver4DictConstants::BIGRAM_ADDRESS_TABLE_BLOCK_SIZE,
                       Ver4DictConstants::BIGRAM_ADDRESS_TABLE_DATA_SIZE) {}
 
+    BigramDictContent()
+            : SparseTableDictContent(Ver4DictConstants::BIGRAM_ADDRESS_TABLE_BLOCK_SIZE,
+                      Ver4DictConstants::BIGRAM_ADDRESS_TABLE_DATA_SIZE) {}
+
     void getBigramEntryAndAdvancePosition(int *const outProbability, bool *const outHasNext,
             int *const outTargetTerminalId, int *const bigramEntryPos) const;
 
@@ -56,7 +60,7 @@ class BigramDictContent : public SparseTableDictContent {
     bool copyBigramList(const int bigramListPos, const int toPos);
 
  private:
-    DISALLOW_IMPLICIT_CONSTRUCTORS(BigramDictContent);
+    DISALLOW_COPY_AND_ASSIGN(BigramDictContent);
 
     int createAndGetBigramFlags(const int probability, const bool hasNext) const {
         return (probability & Ver4DictConstants::BIGRAM_PROBABILITY_MASK)
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.h
index e85bbe18ead23e59b0ab956d693750d89865913c..c109cbf51dcd12139a96c3af2a81c39700c8506c 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.h
@@ -31,6 +31,8 @@ class ProbabilityDictContent : public SingleDictContent {
             : SingleDictContent(dictDirPath, Ver4DictConstants::FREQ_FILE_EXTENSION,
                     isUpdatable) {}
 
+    ProbabilityDictContent() {}
+
     int getProbability(const int terminalId) const {
         if (terminalId < 0 || terminalId >= getSize()) {
             return NOT_A_PROBABILITY;
@@ -61,7 +63,7 @@ class ProbabilityDictContent : public SingleDictContent {
     }
 
  private:
-    DISALLOW_IMPLICIT_CONSTRUCTORS(ProbabilityDictContent);
+    DISALLOW_COPY_AND_ASSIGN(ProbabilityDictContent);
 
     int getSize() const {
         return getBuffer()->getTailPosition() / (Ver4DictConstants::PROBABILITY_SIZE
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.h
index c10fbcb2ab51a2a64d1ff72dcdacac5f059f907b..8463a1753ca2cb593abe0434178053110956b70d 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.h
@@ -33,6 +33,10 @@ class ShortcutDictContent : public SparseTableDictContent {
                       Ver4DictConstants::SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE,
                       Ver4DictConstants::SHORTCUT_ADDRESS_TABLE_DATA_SIZE) {}
 
+    ShortcutDictContent()
+            : SparseTableDictContent(Ver4DictConstants::SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE,
+                      Ver4DictConstants::SHORTCUT_ADDRESS_TABLE_DATA_SIZE) {}
+
     void getShortcutEntryAndAdvancePosition(const int maxCodePointCount,
             int *const outCodePoint, int *const outCodePointCount, int *const outShortcutFlags,
             int *const shortcutEntryPos) const {
@@ -57,7 +61,7 @@ class ShortcutDictContent : public SparseTableDictContent {
     }
 
  private:
-    DISALLOW_IMPLICIT_CONSTRUCTORS(ShortcutDictContent);
+    DISALLOW_COPY_AND_ASSIGN(ShortcutDictContent);
 };
 } // namespace latinime
 #endif /* LATINIME_SHORTCUT_DICT_CONTENT_H */
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/single_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/single_dict_content.h
index 4cb96da6a43de4e295974e3d191c1df9a36abaef..7669c1ecada2d0326e71b832d84ed2359bd03c31 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/single_dict_content.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/single_dict_content.h
@@ -19,6 +19,7 @@
 
 #include "defines.h"
 #include "suggest/policyimpl/dictionary/structure/v4/content/dict_content.h"
+#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h"
 #include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h"
 #include "suggest/policyimpl/dictionary/utils/mmapped_buffer.h"
 
@@ -31,12 +32,17 @@ class SingleDictContent : public DictContent {
             : mMmappedBuffer(MmappedBuffer::openBuffer(dictDirPath, contentFileName, isUpdatable)),
               mExpandableContentBuffer(mMmappedBuffer.get() ? mMmappedBuffer.get()->getBuffer() : 0,
                       mMmappedBuffer.get() ? mMmappedBuffer.get()->getBufferSize() : 0,
-                      BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE) {}
+                      BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE),
+              mIsValid(mMmappedBuffer.get() != 0) {}
+
+    SingleDictContent()
+            : mMmappedBuffer(0), mExpandableContentBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE),
+              mIsValid(true) {}
 
     virtual ~SingleDictContent() {}
 
     virtual bool isValid() const {
-        return mMmappedBuffer.get() != 0;
+        return mIsValid;
     }
 
  protected:
@@ -49,10 +55,11 @@ class SingleDictContent : public DictContent {
     }
 
  private:
-    DISALLOW_IMPLICIT_CONSTRUCTORS(SingleDictContent);
+    DISALLOW_COPY_AND_ASSIGN(SingleDictContent);
 
     const MmappedBuffer::MmappedBufferPtr mMmappedBuffer;
     BufferWithExtendableBuffer mExpandableContentBuffer;
+    const bool mIsValid;
 };
 } // namespace latinime
 #endif /* LATINIME_SINGLE_DICT_CONTENT_H */
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/sparse_table_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/sparse_table_dict_content.h
index fe9efe874aee206a66f875ce40bb1acbd35e9678..5ae5f0ff1346872544ad7bd26b7a930fb4072106 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/sparse_table_dict_content.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/sparse_table_dict_content.h
@@ -19,6 +19,7 @@
 
 #include "defines.h"
 #include "suggest/policyimpl/dictionary/structure/v4/content/dict_content.h"
+#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h"
 #include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h"
 #include "suggest/policyimpl/dictionary/utils/mmapped_buffer.h"
 #include "suggest/policyimpl/dictionary/utils/sparse_table.h"
@@ -49,13 +50,22 @@ class SparseTableDictContent : public DictContent {
                       mContentBuffer.get() ? mContentBuffer.get()->getBufferSize() : 0,
                       BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE),
               mAddressLookupTable(&mExpandableLookupTableBuffer, &mExpandableAddressTableBuffer,
-                      sparseTableBlockSize, sparseTableDataSize) {}
+                      sparseTableBlockSize, sparseTableDataSize),
+              mIsValid(mLookupTableBuffer.get() != 0 && mAddressTableBuffer.get() != 0
+                      && mContentBuffer.get() != 0) {}
+
+    SparseTableDictContent(const int sparseTableBlockSize, const int sparseTableDataSize)
+            : mLookupTableBuffer(0), mAddressTableBuffer(0), mContentBuffer(0),
+              mExpandableLookupTableBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE),
+              mExpandableAddressTableBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE),
+              mExpandableContentBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE),
+              mAddressLookupTable(&mExpandableLookupTableBuffer, &mExpandableAddressTableBuffer,
+                      sparseTableBlockSize, sparseTableDataSize), mIsValid(true) {}
 
     virtual ~SparseTableDictContent() {}
 
     virtual bool isValid() const {
-        return mLookupTableBuffer.get() != 0 && mAddressTableBuffer.get() != 0
-                && mContentBuffer.get() != 0;
+        return mIsValid;
     }
 
  protected:
@@ -78,7 +88,6 @@ class SparseTableDictContent : public DictContent {
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(SparseTableDictContent);
 
-    // TODO: Have sparse table.
     const MmappedBuffer::MmappedBufferPtr mLookupTableBuffer;
     const MmappedBuffer::MmappedBufferPtr mAddressTableBuffer;
     const MmappedBuffer::MmappedBufferPtr mContentBuffer;
@@ -86,6 +95,7 @@ class SparseTableDictContent : public DictContent {
     BufferWithExtendableBuffer mExpandableAddressTableBuffer;
     BufferWithExtendableBuffer mExpandableContentBuffer;
     SparseTable mAddressLookupTable;
+    const bool mIsValid;
 };
 } // namespace latinime
 #endif /* LATINIME_SPARSE_TABLE_DICT_CONTENT_H */
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h
index f6ced31b432a9b945bcaba4683f62aac0422dfda..e016a2b5f9ad2dab4bf3611d6f23bb8fe2c2681a 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h
@@ -38,6 +38,8 @@ class TerminalPositionLookupTable : public SingleDictContent {
                       / Ver4DictConstants::TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE),
               mHeaderRegionSize(headerRegionSize) {}
 
+    TerminalPositionLookupTable() : mSize(0), mHeaderRegionSize(0) {}
+
     int getTerminalPtNodePosition(const int terminalId) const {
         if (terminalId < 0 || terminalId >= mSize) {
             return NOT_A_DICT_POS;
@@ -66,7 +68,7 @@ class TerminalPositionLookupTable : public SingleDictContent {
     }
 
  private:
-    DISALLOW_IMPLICIT_CONSTRUCTORS(TerminalPositionLookupTable);
+    DISALLOW_COPY_AND_ASSIGN(TerminalPositionLookupTable);
 
     int mSize;
     const int mHeaderRegionSize;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h
index b9e56a254b186d53195e9e87f7fce6725896e6c3..e468be5918d08770f0454daca22102819e63f83e 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h
@@ -33,27 +33,30 @@ class Ver4DictBuffers {
  public:
     typedef ExclusiveOwnershipPointer<Ver4DictBuffers> Ver4DictBuffersPtr;
 
-    static Ver4DictBuffersPtr openVer4DictBuffers(const char *const dictDirPath,
+    static AK_FORCE_INLINE Ver4DictBuffersPtr openVer4DictBuffers(const char *const dictDirPath,
             const MmappedBuffer::MmappedBufferPtr &dictBuffer) {
         const bool isUpdatable = dictBuffer.get() ? dictBuffer.get()->isUpdatable() : false;
         return Ver4DictBuffersPtr(new Ver4DictBuffers(dictDirPath, dictBuffer, isUpdatable));
     }
 
+    static AK_FORCE_INLINE Ver4DictBuffersPtr createVer4DictBuffers() {
+        return Ver4DictBuffersPtr(new Ver4DictBuffers());
+    }
+
     AK_FORCE_INLINE bool isValid() const {
         return mDictBuffer.get() != 0 && mProbabilityDictContent.isValid()
                 && mTerminalPositionLookupTable.isValid() && mBigramDictContent.isValid()
                 && mShortcutDictContent.isValid();
     }
 
-    AK_FORCE_INLINE uint8_t *getRawDictBuffer() const {
-        return mDictBuffer.get()->getBuffer();
+    AK_FORCE_INLINE BufferWithExtendableBuffer *getWritableHeaderBuffer() {
+        return &mExpandableHeaderBuffer;
     }
 
-    AK_FORCE_INLINE int getRawDictBufferSize() const {
-        return mDictBuffer.get()->getBufferSize();
+    AK_FORCE_INLINE BufferWithExtendableBuffer *getWritableTrieBuffer() {
+        return &mExpandableTrieBuffer;
     }
 
-
     AK_FORCE_INLINE TerminalPositionLookupTable *getUpdatableTerminalPositionLookupTable() {
         return &mTerminalPositionLookupTable;
     }
@@ -86,21 +89,41 @@ class Ver4DictBuffers {
         return mIsUpdatable;
     }
 
+    bool flush(const char *const dictDirPath) {
+        // TODO: Implement.
+        return false;
+    }
+
  private:
-    DISALLOW_IMPLICIT_CONSTRUCTORS(Ver4DictBuffers);
+    DISALLOW_COPY_AND_ASSIGN(Ver4DictBuffers);
 
     AK_FORCE_INLINE Ver4DictBuffers(const char *const dictDirPath,
             const MmappedBuffer::MmappedBufferPtr &dictBuffer, const bool isUpdatable)
             : mDictBuffer(dictBuffer),
-              // TODO: Quit using getHeaderSize.
-              mTerminalPositionLookupTable(dictDirPath, isUpdatable,
-                      HeaderReadWriteUtils::getHeaderSize(mDictBuffer.get()->getBuffer())),
+              mHeaderSize(HeaderReadWriteUtils::getHeaderSize(mDictBuffer.get()->getBuffer())),
+              mExpandableHeaderBuffer(dictBuffer.get()->getBuffer(), mHeaderSize,
+                      BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE),
+              mExpandableTrieBuffer(dictBuffer.get()->getBuffer() + mHeaderSize,
+                      dictBuffer.get()->getBufferSize() - mHeaderSize,
+                      BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE),
+              // TODO: Quit using header size.
+              mTerminalPositionLookupTable(dictDirPath, isUpdatable, mHeaderSize),
               mProbabilityDictContent(dictDirPath, isUpdatable),
               mBigramDictContent(dictDirPath, isUpdatable),
               mShortcutDictContent(dictDirPath, isUpdatable),
               mIsUpdatable(isUpdatable) {}
 
+    AK_FORCE_INLINE Ver4DictBuffers()
+            : mDictBuffer(0), mHeaderSize(0),
+              mExpandableHeaderBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE),
+              mExpandableTrieBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE),
+              mTerminalPositionLookupTable(), mProbabilityDictContent(),
+              mBigramDictContent(), mShortcutDictContent(), mIsUpdatable(true) {}
+
     const MmappedBuffer::MmappedBufferPtr mDictBuffer;
+    const int mHeaderSize;
+    BufferWithExtendableBuffer mExpandableHeaderBuffer;
+    BufferWithExtendableBuffer mExpandableTrieBuffer;
     TerminalPositionLookupTable mTerminalPositionLookupTable;
     ProbabilityDictContent mProbabilityDictContent;
     BigramDictContent mBigramDictContent;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp
index ced76d25d0c241db65f5585542aaf15b42259c17..af13a374ab4c65293e59669379cf06029d0d7b27 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp
@@ -30,6 +30,10 @@ const char *const Ver4DictConstants::SHORTCUT_LOOKUP_TABLE_FILE_EXTENSION = ".sh
 const char *const Ver4DictConstants::SHORTCUT_CONTENT_TABLE_FILE_EXTENSION =
         ".shortcut_index_shortcut";
 
+// Version 4 dictionary size is implicitly limited to 8MB due to 3-byte offsets.
+// TODO: Make MAX_DICTIONARY_SIZE 8MB.
+const int Ver4DictConstants::MAX_DICTIONARY_SIZE = 2 * 1024 * 1024;
+
 const int Ver4DictConstants::NOT_A_TERMINAL_ID = -1;
 const int Ver4DictConstants::PROBABILITY_SIZE = 1;
 const int Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE = 1;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h
index b008213730e8809a6e966ed90c686884394eac9a..cfb7740beb92b645d5dee8ff637393ba0bfff18e 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h
@@ -34,6 +34,8 @@ class Ver4DictConstants {
     static const char *const SHORTCUT_LOOKUP_TABLE_FILE_EXTENSION;
     static const char *const SHORTCUT_CONTENT_TABLE_FILE_EXTENSION;
 
+    static const int MAX_DICTIONARY_SIZE;
+
     static const int NOT_A_TERMINAL_ID;
     static const int PROBABILITY_SIZE;
     static const int FLAGS_IN_PROBABILITY_FILE_SIZE;
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 40cc3d00b0533e9f9493acbb0f070f59163a8132..698483a7964b53885f81be273baeb0c24683bc6d 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
@@ -28,14 +28,14 @@ namespace latinime {
 
 const int Ver4PatriciaTriePolicy::MARGIN_TO_REFUSE_DYNAMIC_OPERATIONS = 1024;
 const int Ver4PatriciaTriePolicy::MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS =
-        DynamicPatriciaTrieWritingHelper::MAX_DICTIONARY_SIZE - MARGIN_TO_REFUSE_DYNAMIC_OPERATIONS;
+        Ver4DictConstants::MAX_DICTIONARY_SIZE - MARGIN_TO_REFUSE_DYNAMIC_OPERATIONS;
 
 void Ver4PatriciaTriePolicy::createAndGetAllChildDicNodes(const DicNode *const dicNode,
         DicNodeVector *const childDicNodes) const {
     if (!dicNode->hasChildren()) {
         return;
     }
-    DynamicPatriciaTrieReadingHelper readingHelper(&mDictBuffer, &mNodeReader);
+    DynamicPatriciaTrieReadingHelper readingHelper(mDictBuffer, &mNodeReader);
     readingHelper.initWithPtNodeArrayPos(dicNode->getChildrenPtNodeArrayPos());
     while (!readingHelper.isEnd()) {
         const PtNodeParams ptNodeParams = readingHelper.getPtNodeParams();
@@ -63,7 +63,7 @@ void Ver4PatriciaTriePolicy::createAndGetAllChildDicNodes(const DicNode *const d
 int Ver4PatriciaTriePolicy::getCodePointsAndProbabilityAndReturnCodePointCount(
         const int ptNodePos, const int maxCodePointCount, int *const outCodePoints,
         int *const outUnigramProbability) const {
-    DynamicPatriciaTrieReadingHelper readingHelper(&mDictBuffer, &mNodeReader);
+    DynamicPatriciaTrieReadingHelper readingHelper(mDictBuffer, &mNodeReader);
     readingHelper.initWithPtNodePos(ptNodePos);
     return readingHelper.getCodePointsAndProbabilityAndReturnCodePointCount(
             maxCodePointCount, outCodePoints, outUnigramProbability);
@@ -71,7 +71,7 @@ int Ver4PatriciaTriePolicy::getCodePointsAndProbabilityAndReturnCodePointCount(
 
 int Ver4PatriciaTriePolicy::getTerminalPtNodePositionOfWord(const int *const inWord,
         const int length, const bool forceLowerCaseSearch) const {
-    DynamicPatriciaTrieReadingHelper readingHelper(&mDictBuffer, &mNodeReader);
+    DynamicPatriciaTrieReadingHelper readingHelper(mDictBuffer, &mNodeReader);
     readingHelper.initWithPtNodeArrayPos(getRootPosition());
     return readingHelper.getTerminalPtNodePositionOfWord(inWord, length, forceLowerCaseSearch);
 }
@@ -135,12 +135,12 @@ bool Ver4PatriciaTriePolicy::addUnigramWord(const int *const word, const int len
         AKLOGI("Warning: addUnigramWord() is called for non-updatable dictionary.");
         return false;
     }
-    if (mDictBuffer.getTailPosition() >= MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS) {
+    if (mDictBuffer->getTailPosition() >= MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS) {
         AKLOGE("The dictionary is too large to dynamically update. Dictionary size: %d",
-                mDictBuffer.getTailPosition());
+                mDictBuffer->getTailPosition());
         return false;
     }
-    DynamicPatriciaTrieReadingHelper readingHelper(&mDictBuffer, &mNodeReader);
+    DynamicPatriciaTrieReadingHelper readingHelper(mDictBuffer, &mNodeReader);
     readingHelper.initWithPtNodeArrayPos(getRootPosition());
     bool addedNewUnigram = false;
     if (mUpdatingHelper.addUnigramWord(&readingHelper, word, length, probability,
@@ -160,9 +160,9 @@ bool Ver4PatriciaTriePolicy::addBigramWords(const int *const word0, const int le
         AKLOGI("Warning: addBigramWords() is called for non-updatable dictionary.");
         return false;
     }
-    if (mDictBuffer.getTailPosition() >= MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS) {
+    if (mDictBuffer->getTailPosition() >= MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS) {
         AKLOGE("The dictionary is too large to dynamically update. Dictionary size: %d",
-                mDictBuffer.getTailPosition());
+                mDictBuffer->getTailPosition());
         return false;
     }
     const int word0Pos = getTerminalPtNodePositionOfWord(word0, length0,
@@ -192,9 +192,9 @@ bool Ver4PatriciaTriePolicy::removeBigramWords(const int *const word0, const int
         AKLOGI("Warning: addBigramWords() is called for non-updatable dictionary.");
         return false;
     }
-    if (mDictBuffer.getTailPosition() >= MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS) {
+    if (mDictBuffer->getTailPosition() >= MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS) {
         AKLOGE("The dictionary is too large to dynamically update. Dictionary size: %d",
-                mDictBuffer.getTailPosition());
+                mDictBuffer->getTailPosition());
         return false;
     }
     const int word0Pos = getTerminalPtNodePositionOfWord(word0, length0,
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 93cc4b7a238da65f4bf95be7a4480d3110bc0622..e8fdf55136ebf5cb5fa7432b3b69c8203c7a2dd0 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
@@ -38,18 +38,17 @@ class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy {
  public:
     Ver4PatriciaTriePolicy(const Ver4DictBuffers::Ver4DictBuffersPtr &buffers)
             : mBuffers(buffers),
-              mHeaderPolicy(mBuffers.get()->getRawDictBuffer(), FormatUtils::VERSION_4),
-              mDictBuffer(mBuffers.get()->getRawDictBuffer() + mHeaderPolicy.getSize(),
-                      mBuffers.get()->getRawDictBufferSize() - mHeaderPolicy.getSize(),
-                      BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE),
+              mHeaderPolicy(mBuffers.get()->getWritableHeaderBuffer()->getBuffer(
+                      false /* usesAdditionalBuffer*/), FormatUtils::VERSION_4),
+              mDictBuffer(mBuffers.get()->getWritableTrieBuffer()),
               mBigramPolicy(mBuffers.get()->getUpdatableBigramDictContent(),
                       mBuffers.get()->getTerminalPositionLookupTable()),
               mShortcutPolicy(mBuffers.get()->getShortcutDictContent(),
                       mBuffers.get()->getTerminalPositionLookupTable()),
-              mNodeReader(&mDictBuffer, mBuffers.get()->getProbabilityDictContent()),
-              mNodeWriter(&mDictBuffer, mBuffers.get(), &mNodeReader, &mBigramPolicy,
+              mNodeReader(mDictBuffer, mBuffers.get()->getProbabilityDictContent()),
+              mNodeWriter(mDictBuffer, mBuffers.get(), &mNodeReader, &mBigramPolicy,
                       &mShortcutPolicy),
-              mUpdatingHelper(&mDictBuffer, &mNodeReader, &mNodeWriter,
+              mUpdatingHelper(mDictBuffer, &mNodeReader, &mNodeWriter,
                       mHeaderPolicy.isDecayingDict()),
               mUnigramCount(mHeaderPolicy.getUnigramCount()),
               mBigramCount(mHeaderPolicy.getBigramCount()) {};
@@ -115,7 +114,7 @@ class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy {
 
     Ver4DictBuffers::Ver4DictBuffersPtr mBuffers;
     const HeaderPolicy mHeaderPolicy;
-    BufferWithExtendableBuffer mDictBuffer;
+    BufferWithExtendableBuffer *const mDictBuffer;
     Ver4BigramListPolicy mBigramPolicy;
     Ver4ShortcutListPolicy mShortcutPolicy;
     Ver4PatriciaTrieNodeReader mNodeReader;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp
index b48e5b00570468ea750ec25b6c2a24716412a400..40f7d1f5ce657bf2f100ecd7b7d0678fb82f7e07 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp
@@ -21,6 +21,7 @@
 
 #include "suggest/policyimpl/dictionary/header/header_policy.h"
 #include "suggest/policyimpl/dictionary/structure/v3/dynamic_patricia_trie_writing_utils.h"
+#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h"
 #include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h"
 #include "suggest/policyimpl/dictionary/utils/format_utils.h"
 
@@ -34,7 +35,7 @@ const char *const DictFileWritingUtils::TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE =
         case 3:
             return createEmptyV3DictFile(filePath, attributeMap);
         case 4:
-            // TODO: Support version 4 dictionary format.
+            return createEmptyV4DictFile(filePath, attributeMap);
             return false;
         default:
             // Only version 3 dictionary is supported for now.
@@ -58,6 +59,20 @@ const char *const DictFileWritingUtils::TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE =
     return flushAllHeaderAndBodyToFile(filePath, &headerBuffer, &bodyBuffer);
 }
 
+/* static */ bool DictFileWritingUtils::createEmptyV4DictFile(const char *const filePath,
+        const HeaderReadWriteUtils::AttributeMap *const attributeMap) {
+    Ver4DictBuffers::Ver4DictBuffersPtr dictBuffers = Ver4DictBuffers::createVer4DictBuffers();
+    HeaderPolicy headerPolicy(FormatUtils::VERSION_4, attributeMap);
+    headerPolicy.writeHeaderToBuffer(dictBuffers.get()->getWritableHeaderBuffer(),
+            true /* updatesLastUpdatedTime */, true /* updatesLastDecayedTime */,
+            0 /* unigramCount */, 0 /* bigramCount */, 0 /* extendedRegionSize */);
+    if (!DynamicPatriciaTrieWritingUtils::writeEmptyDictionary(
+            dictBuffers.get()->getWritableTrieBuffer(), 0 /* rootPos */)) {
+        return false;
+    }
+    return dictBuffers.get()->flush(filePath);
+}
+
 /* static */ bool DictFileWritingUtils::flushAllHeaderAndBodyToFile(const char *const filePath,
         BufferWithExtendableBuffer *const dictHeader, BufferWithExtendableBuffer *const dictBody) {
     const int tmpFileNameBufSize = strlen(filePath)
@@ -69,21 +84,21 @@ const char *const DictFileWritingUtils::TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE =
             TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE);
     FILE *const file = fopen(tmpFileName, "wb");
     if (!file) {
-        AKLOGE("Dictionary file %s cannnot be opened.", tmpFileName);
+        AKLOGE("Dictionary file %s cannot be opened.", tmpFileName);
         ASSERT(false);
         return false;
     }
     // Write the dictionary header.
     if (!writeBufferToFile(file, dictHeader)) {
         remove(tmpFileName);
-        AKLOGE("Dictionary header cannnot be written. size: %d", dictHeader->getTailPosition());
+        AKLOGE("Dictionary header cannot be written. size: %d", dictHeader->getTailPosition());
         ASSERT(false);
         return false;
     }
     // Write the dictionary body.
     if (!writeBufferToFile(file, dictBody)) {
         remove(tmpFileName);
-        AKLOGE("Dictionary body cannnot be written. size: %d", dictBody->getTailPosition());
+        AKLOGE("Dictionary body cannot be written. size: %d", dictBody->getTailPosition());
         ASSERT(false);
         return false;
     }
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h
index bd4ac66fd480752a6d6cc97554581a9aeb467d44..3291f98c7b414b04e2818a392d5b4c387a75688d 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h
@@ -43,6 +43,9 @@ class DictFileWritingUtils {
     static bool createEmptyV3DictFile(const char *const filePath,
             const HeaderReadWriteUtils::AttributeMap *const attributeMap);
 
+    static bool createEmptyV4DictFile(const char *const filePath,
+            const HeaderReadWriteUtils::AttributeMap *const attributeMap);
+
     static bool writeBufferToFile(FILE *const file,
             const BufferWithExtendableBuffer *const buffer);
 };