You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@doris.apache.org by ai...@apache.org on 2025/04/17 03:07:11 UTC

(doris-thirdparty) branch clucene-3.0 updated: [fix](for) fix PFOR x86 mix with arm problem (#304)

This is an automated email from the ASF dual-hosted git repository.

airborne pushed a commit to branch clucene-3.0
in repository https://gitbox.apache.org/repos/asf/doris-thirdparty.git


The following commit(s) were added to refs/heads/clucene-3.0 by this push:
     new 317e5071488 [fix](for) fix PFOR x86 mix with arm problem (#304)
317e5071488 is described below

commit 317e50714884fe338e0f44e870d841df3a91689f
Author: airborne12 <ji...@selectdb.com>
AuthorDate: Thu Apr 17 11:07:06 2025 +0800

    [fix](for) fix PFOR x86 mix with arm problem (#304)
    
    * [fix](for) fix PFOR x86 mix with arm problem (#299)
    
    * [refact](reader) refact reader's compatible option (#300)
    
    * [refact](reader) refact reader's compatible option
    
    * [fix](reader) remove IndexReader::setCompatibleRead acquireWriteLock
    
    * add cmake min version
    
    * add cmake min version
    
    * fix compatible read for new version
    
    * fix cmake
    
    * fix compile
    
    * update noavx2
    
    * fix v3
    
    * fix compile
    
    * fix compile
    
    * update cmake
    
    * update cmake
    
    * update cmake
---
 CMakeLists.txt                                     |   29 +-
 cmake/Toolchain-aarch64.cmake                      |    5 +
 src/core/CLucene/index/CodeMode.h                  |    4 +-
 src/core/CLucene/index/FieldInfos.cpp              |  118 +-
 src/core/CLucene/index/IndexReader.cpp             |   10 +
 src/core/CLucene/index/IndexReader.h               |    5 +
 src/core/CLucene/index/IndexWriter.cpp             |   24 +-
 src/core/CLucene/index/SDocumentWriter.cpp         |   26 +-
 src/core/CLucene/index/SegmentTermDocs.cpp         |   37 +-
 src/core/CLucene/index/_SegmentHeader.h            |    8 +-
 src/core/CLucene/util/PFORUtil.cpp                 |  290 ++++-
 src/core/CLucene/util/PFORUtil.h                   |    7 +-
 src/ext/for/CMakeLists.txt                         |   22 +-
 src/ext/for/bitpack.c                              |    7 +-
 src/ext/for/bitpack.h                              |   17 +
 src/ext/for/bitunpack.c                            | 1208 +++++++++++++++++++-
 src/ext/for/test_bitd1unpack.cpp                   |  399 +++++++
 src/ext/for/transpose.c                            |    4 +-
 src/ext/for/vp4.h                                  |    2 +
 src/ext/for/vp4c.c                                 |    5 +-
 src/ext/for/vp4d.c                                 |   33 +-
 src/test/CMakeLists.txt                            |    3 +-
 .../pfor_p4ndx_compat_gen_by_old_version_arm.dat   |  Bin 0 -> 1168 bytes
 ...pfor_p4ndx_compat_gen_by_old_version_x86_64.dat |  Bin 0 -> 1164 bytes
 src/test/store/testPFOR.cpp                        |  652 +++++++++++
 src/test/test.h                                    |    2 +-
 src/test/tests.cpp                                 |    1 +
 27 files changed, 2708 insertions(+), 210 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index c3b77861857..aaed4b94a46 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,5 +1,4 @@
 PROJECT (clucene)
-
 #Rules for version:
 #MAJOR and MINOR versions are purely political (tracks JLucene compatibility)
 #REVISION version MUST be revised if the headers or compatibility change
@@ -17,9 +16,7 @@ MATH(EXPR CLUCENE_INT_VERSION "(${CLUCENE_VERSION_MAJOR} * 1000000) + (${CLUCENE
 SET(CLUCENE_VERSION "${CLUCENE_VERSION_MAJOR}.${CLUCENE_VERSION_MINOR}.${CLUCENE_VERSION_REVISION}.${CLUCENE_VERSION_PATCH}")
 
 #CMake 2.6+ is recommended to an improved Boost module
-CMAKE_MINIMUM_REQUIRED(VERSION 2.4.0 FATAL_ERROR)
-
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
+CMAKE_MINIMUM_REQUIRED(VERSION 3.5 FATAL_ERROR)
 
 if(COMMAND cmake_policy)
   cmake_policy(SET CMP0003 NEW)
@@ -135,9 +132,29 @@ elseif ("${CMAKE_BUILD_TYPE}" STREQUAL "LSAN")
     SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_FLAGS_LSAN}")
 endif()
 
-if (USE_AVX2)
-	SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUSE_AVX2")
+if (CMAKE_SYSTEM_PROCESSOR MATCHES "amd64|x86_64")
+    set (ARCH_AMD64 1)
+endif ()
+if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64.*|AARCH64.*|arm64.*)")
+    set (ARCH_AARCH64 1)
+endif ()
+if (ARCH_AARCH64 OR CMAKE_SYSTEM_PROCESSOR MATCHES "arm")
+    set (ARCH_ARM 1)
+endif ()
+if (ARCH_AMD64)
+    add_compile_options(-msse4.2)
+    if (USE_AVX2)
+        add_compile_options(-mavx2)
+        add_definitions(-DUSE_AVX2)
+    endif()
+    message(STATUS "CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}")
+endif()
+
+if (ARCH_ARM)
+    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv8-a+crc")
 endif()
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
+
 if (__COMPILER_CLANG)
 	SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-c++11-narrowing -g -fno-omit-frame-pointer")
 else ()
diff --git a/cmake/Toolchain-aarch64.cmake b/cmake/Toolchain-aarch64.cmake
new file mode 100644
index 00000000000..948164d513f
--- /dev/null
+++ b/cmake/Toolchain-aarch64.cmake
@@ -0,0 +1,5 @@
+set(CMAKE_SYSTEM_NAME Linux)
+set(CMAKE_SYSTEM_PROCESSOR aarch64)
+
+set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
+set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)
diff --git a/src/core/CLucene/index/CodeMode.h b/src/core/CLucene/index/CodeMode.h
index 3c39e94ecb6..05dd8b82649 100644
--- a/src/core/CLucene/index/CodeMode.h
+++ b/src/core/CLucene/index/CodeMode.h
@@ -5,7 +5,9 @@ CL_NS_DEF(index)
 enum class CodeMode { 
   kDefault = 0,
   kPfor = 1,
-  kRange = 2
+  kRange = 2,
+  kPfor256 = 3,
+  kPfor128 = 4
 };
 
 CL_NS_END
\ No newline at end of file
diff --git a/src/core/CLucene/index/FieldInfos.cpp b/src/core/CLucene/index/FieldInfos.cpp
index 3fa035661d4..f53ed892a5b 100644
--- a/src/core/CLucene/index/FieldInfos.cpp
+++ b/src/core/CLucene/index/FieldInfos.cpp
@@ -21,32 +21,28 @@ CL_NS_USE(document)
 CL_NS_USE(util)
 CL_NS_DEF(index)
 
-
-FieldInfo::FieldInfo(const TCHAR *_fieldName,
-                     const bool _isIndexed,
-                     const int32_t _fieldNumber,
-                     const bool _storeTermVector,
-                     const bool _storeOffsetWithTermVector,
-                     const bool _storePositionWithTermVector,
-                     const bool _omitNorms,
-										 const bool _hasProx,
-                     const bool _storePayloads) : name(CLStringIntern::intern(_fieldName )),
-                                                  isIndexed(_isIndexed),
-                                                  number(_fieldNumber),
-                                                  storeTermVector(_storeTermVector),
-                                                  storeOffsetWithTermVector(_storeOffsetWithTermVector),
-                                                  storePositionWithTermVector(_storePositionWithTermVector),
-                                                  omitNorms(_omitNorms), hasProx(_hasProx),
-																									storePayloads(_storePayloads) {
-}
+FieldInfo::FieldInfo(const TCHAR* _fieldName, const bool _isIndexed, const int32_t _fieldNumber,
+                     const bool _storeTermVector, const bool _storeOffsetWithTermVector,
+                     const bool _storePositionWithTermVector, const bool _omitNorms,
+                     const bool _hasProx, const bool _storePayloads)
+        : name(CLStringIntern::intern(_fieldName)),
+          isIndexed(_isIndexed),
+          number(_fieldNumber),
+          storeTermVector(_storeTermVector),
+          storeOffsetWithTermVector(_storeOffsetWithTermVector),
+          storePositionWithTermVector(_storePositionWithTermVector),
+          omitNorms(_omitNorms),
+          hasProx(_hasProx),
+          storePayloads(_storePayloads) {}
 
 FieldInfo::~FieldInfo(){
 	CL_NS(util)::CLStringIntern::unintern(name);
 }
 
 FieldInfo* FieldInfo::clone() {
-	return _CLNEW FieldInfo(name, isIndexed, number, storeTermVector, storePositionWithTermVector,
-		storeOffsetWithTermVector, omitNorms, hasProx, storePayloads);
+        return _CLNEW FieldInfo(name, isIndexed, number, storeTermVector,
+                                storePositionWithTermVector, storeOffsetWithTermVector, omitNorms,
+                                hasProx, storePayloads);
 }
 
 FieldInfos::FieldInfos():
@@ -139,39 +135,39 @@ FieldInfo* FieldInfos::add(const TCHAR* name, const bool isIndexed, const bool s
                            const bool storePositionWithTermVector,
                            const bool storeOffsetWithTermVector, const bool omitNorms,
                            const bool hasProx, const bool storePayloads,
-													 IndexVersion indexVersion) {
-  FieldInfo* fi = fieldInfo(name);
-	if (fi == NULL) {
-		return addInternal(name, isIndexed, storeTermVector, storePositionWithTermVector,
-												storeOffsetWithTermVector, omitNorms, hasProx, storePayloads,
-												indexVersion);
-  } else {
-		if (fi->isIndexed != isIndexed) {
-			fi->isIndexed = true;                      // once indexed, always index
-		}
-		if (fi->storeTermVector != storeTermVector) {
-			fi->storeTermVector = true;                // once vector, always vector
-		}
-		if (fi->storePositionWithTermVector != storePositionWithTermVector) {
-	        fi->storePositionWithTermVector = true;                // once vector, always vector
-	    }
-	    if (fi->storeOffsetWithTermVector != storeOffsetWithTermVector) {
-	        fi->storeOffsetWithTermVector = true;                // once vector, always vector
-	    }
-	    if (fi->omitNorms != omitNorms) {
-	        fi->omitNorms = false;                // once norms are stored, always store
-	    }
-			if (fi->hasProx != hasProx) {
-				fi->hasProx = true;
-			}
-		if (fi->storePayloads != storePayloads) {
-			fi->storePayloads = true;
-		}
-		if (fi->indexVersion_ != indexVersion) {
-			fi->indexVersion_ = indexVersion;
-		}
-	}
-	return fi;
+                           IndexVersion indexVersion) {
+        FieldInfo* fi = fieldInfo(name);
+        if (fi == NULL) {
+                return addInternal(name, isIndexed, storeTermVector, storePositionWithTermVector,
+                                   storeOffsetWithTermVector, omitNorms, hasProx, storePayloads,
+                                   indexVersion);
+        } else {
+            if (fi->isIndexed != isIndexed) {
+                fi->isIndexed = true; // once indexed, always index
+            }
+            if (fi->storeTermVector != storeTermVector) {
+                fi->storeTermVector = true; // once vector, always vector
+            }
+            if (fi->storePositionWithTermVector != storePositionWithTermVector) {
+                fi->storePositionWithTermVector = true; // once vector, always vector
+            }
+            if (fi->storeOffsetWithTermVector != storeOffsetWithTermVector) {
+                fi->storeOffsetWithTermVector = true; // once vector, always vector
+            }
+            if (fi->omitNorms != omitNorms) {
+                fi->omitNorms = false; // once norms are stored, always store
+            }
+            if (fi->hasProx != hasProx) {
+                fi->hasProx = true;
+            }
+            if (fi->storePayloads != storePayloads) {
+                fi->storePayloads = true;
+            }
+            if (fi->indexVersion_ != indexVersion) {
+                fi->indexVersion_ = indexVersion;
+            }
+        }
+        return fi;
 }
 
 FieldInfo* FieldInfos::addInternal(const TCHAR* name, const bool isIndexed,
@@ -179,14 +175,14 @@ FieldInfo* FieldInfos::addInternal(const TCHAR* name, const bool isIndexed,
                                    const bool storePositionWithTermVector,
                                    const bool storeOffsetWithTermVector, const bool omitNorms,
                                    const bool hasProx, const bool storePayloads,
-																	 IndexVersion indexVersion) {
-	FieldInfo* fi = _CLNEW FieldInfo(name, isIndexed, byNumber.size(), storeTermVector,
-																		storePositionWithTermVector, storeOffsetWithTermVector,
-																		omitNorms, hasProx, storePayloads);
-	fi->setIndexVersion(indexVersion);
-  byNumber.push_back(fi);
-	byName.put( fi->name, fi);
-	return fi;
+                                   IndexVersion indexVersion) {
+        FieldInfo* fi = _CLNEW FieldInfo(name, isIndexed, byNumber.size(), storeTermVector,
+                                         storePositionWithTermVector, storeOffsetWithTermVector,
+                                         omitNorms, hasProx, storePayloads);
+        fi->setIndexVersion(indexVersion);
+        byNumber.push_back(fi);
+        byName.put(fi->name, fi);
+        return fi;
 }
 
 int32_t FieldInfos::fieldNumber(const TCHAR* fieldName)const {
diff --git a/src/core/CLucene/index/IndexReader.cpp b/src/core/CLucene/index/IndexReader.cpp
index 584957ee3b1..69c66b4f735 100644
--- a/src/core/CLucene/index/IndexReader.cpp
+++ b/src/core/CLucene/index/IndexReader.cpp
@@ -222,6 +222,16 @@ CL_NS_DEF(index)
     this->doSetNorm(doc, field, value);
   }
 
+  void IndexReader::setCompatibleRead(bool compatibleRead) {
+    SCOPED_LOCK_MUTEX(THIS_LOCK)
+    this->ensureOpen();
+    this->compatibleRead = compatibleRead;
+  }
+
+  bool IndexReader::getCompatibleRead() const {
+    return this->compatibleRead;
+  }
+
 
   void IndexReader::setNorm(int32_t doc, const TCHAR* field, float_t value){
      ensureOpen();
diff --git a/src/core/CLucene/index/IndexReader.h b/src/core/CLucene/index/IndexReader.h
index 8f78ca53b07..13403963076 100644
--- a/src/core/CLucene/index/IndexReader.h
+++ b/src/core/CLucene/index/IndexReader.h
@@ -112,6 +112,8 @@ public:
   /** Internal use. */
 	Internal* _internal;
 
+  bool compatibleRead = false;
+
 
   /**
    * Constants describing field properties, for example used for
@@ -689,6 +691,9 @@ public:
 
   virtual IndexVersion getIndexVersion();
 
+  void setCompatibleRead(bool compatibleRead);
+  bool getCompatibleRead() const;
+
   friend class SegmentReader;
   friend class MultiReader;
   friend class IndexWriter;
diff --git a/src/core/CLucene/index/IndexWriter.cpp b/src/core/CLucene/index/IndexWriter.cpp
index d86299acc83..ae03dcb8fed 100644
--- a/src/core/CLucene/index/IndexWriter.cpp
+++ b/src/core/CLucene/index/IndexWriter.cpp
@@ -1730,19 +1730,6 @@ void IndexWriter::mergeTerms(bool hasProx, IndexVersion indexVersion) {
             processPostings(postings, &destDocs[i], i);
         }
 
-        auto encode = [](IndexOutput* out, std::vector<uint32_t>& buffer, bool isDoc) {
-            std::vector<uint8_t> compress(4 * buffer.size() + PFOR_BLOCK_SIZE);
-            size_t size = 0;
-            if (isDoc) {
-                size = P4ENC(buffer.data(), buffer.size(), compress.data());
-            } else {
-                size = P4NZENC(buffer.data(), buffer.size(), compress.data());
-            }
-            out->writeVInt(size);
-            out->writeBytes(reinterpret_cast<const uint8_t*>(compress.data()), size);
-            buffer.resize(0);
-        };
-
         std::vector<int32_t> dfs(numDestIndexes, 0);
         std::vector<int32_t> lastDocs(numDestIndexes, 0);
         while (destPostingQueues->size()) {
@@ -1790,14 +1777,9 @@ void IndexWriter::mergeTerms(bool hasProx, IndexVersion indexVersion) {
                 }
 
                 if ((++df % skipInterval) == 0) {
-                    freqOut->writeByte((char)CodeMode::kPfor);
-                    freqOut->writeVInt(docDeltaBuffer.size());
-                    encode(freqOut, docDeltaBuffer, true);
-                    if (hasProx) {
-                        encode(freqOut, freqBuffer, false);
-                        if (indexVersion >= IndexVersion::kV2) {
-                            PforUtil::encodePos(proxOut, posBuffer);
-                        }
+                    PforUtil::pfor_encode(freqOut, docDeltaBuffer, freqBuffer, hasProx);
+                    if (hasProx && indexVersion >= IndexVersion::kV2) {
+                        PforUtil::encodePos(proxOut, posBuffer);
                     }
 
                     skipWriter->setSkipData(lastDoc, false, -1);
diff --git a/src/core/CLucene/index/SDocumentWriter.cpp b/src/core/CLucene/index/SDocumentWriter.cpp
index 2e3f778d4ac..17202cada5e 100644
--- a/src/core/CLucene/index/SDocumentWriter.cpp
+++ b/src/core/CLucene/index/SDocumentWriter.cpp
@@ -1198,33 +1198,15 @@ void SDocumentsWriter<T>::appendPostings(ArrayBase<typename ThreadState::FieldDa
 
         skipListWriter->resetSkip();
 
-        auto encode = [](IndexOutput* out, std::vector<uint32_t>& buffer, bool isDoc) {
-            std::vector<uint8_t> compress(4 * buffer.size() + PFOR_BLOCK_SIZE);
-            size_t size = 0;
-            if (isDoc) {
-                size = P4ENC(buffer.data(), buffer.size(), compress.data());
-            } else {
-                size = P4NZENC(buffer.data(), buffer.size(), compress.data());
-            }
-            out->writeVInt(size);
-            out->writeBytes(reinterpret_cast<const uint8_t*>(compress.data()), size);
-            buffer.resize(0);
-        };
-
         // Now termStates has numToMerge FieldMergeStates
         // which all share the same term.  Now we must
         // interleave the docID streams.
         while (numToMerge > 0) {
 
             if ((++df % skipInterval) == 0) {
-                freqOut->writeByte((char)CodeMode::kPfor);
-                freqOut->writeVInt(docDeltaBuffer.size());
-                encode(freqOut, docDeltaBuffer, true);
-                if (hasProx_) {
-                    encode(freqOut, freqBuffer, false);
-                    if (indexVersion_ >= IndexVersion::kV2) {
-                        PforUtil::encodePos(proxOut, posBuffer);
-                    }
+                PforUtil::pfor_encode(freqOut, docDeltaBuffer, freqBuffer, hasProx_);
+                if (hasProx_ && indexVersion_ >= IndexVersion::kV2) {
+                    PforUtil::encodePos(proxOut, posBuffer);
                 }
 
                 skipListWriter->setSkipData(lastDoc, currentFieldStorePayloads, lastPayloadLength);
@@ -1317,7 +1299,7 @@ void SDocumentsWriter<T>::appendPostings(ArrayBase<typename ThreadState::FieldDa
             }
             docDeltaBuffer.resize(0);
             freqBuffer.resize(0);
-            if (indexVersion_ >= IndexVersion::kV2) {
+            if (hasProx_ && indexVersion_ >= IndexVersion::kV2) {
                 PforUtil::encodePos(proxOut, posBuffer);
             }
         }
diff --git a/src/core/CLucene/index/SegmentTermDocs.cpp b/src/core/CLucene/index/SegmentTermDocs.cpp
index bc2c2e3e93d..5f5df366f91 100644
--- a/src/core/CLucene/index/SegmentTermDocs.cpp
+++ b/src/core/CLucene/index/SegmentTermDocs.cpp
@@ -22,7 +22,7 @@ SegmentTermDocs::SegmentTermDocs(const SegmentReader *_parent) : parent(_parent)
                                                                  count(0), df(0), deletedDocs(_parent->deletedDocs), _doc(-1), _freq(0), skipInterval(_parent->tis->getSkipInterval()),
                                                                  maxSkipLevels(_parent->tis->getMaxSkipLevels()), skipListReader(NULL), freqBasePointer(0), proxBasePointer(0),
                                                                  skipPointer(0), haveSkipped(false), pointer(0), pointerMax(0), indexVersion_(_parent->_fieldInfos->getIndexVersion()),
-                                                                 hasProx(_parent->_fieldInfos->hasProx()), buffer_(freqStream, hasProx, indexVersion_) {
+                                                                 hasProx(_parent->_fieldInfos->hasProx()), buffer_(freqStream, hasProx, indexVersion_, _parent->getCompatibleRead()) {
     CND_CONDITION(_parent != NULL, "Parent is NULL");
     memset(docs,0,PFOR_BLOCK_SIZE*sizeof(int32_t));
     memset(freqs,0,PFOR_BLOCK_SIZE*sizeof(int32_t));
@@ -275,40 +275,7 @@ int32_t TermDocsBuffer::refillV0() {
 }
 
 int32_t TermDocsBuffer::refillV1() {
-    char mode = freqStream_->readByte();
-    uint32_t arraySize = freqStream_->readVInt();
-    if (mode == (char)CodeMode::kPfor) {
-        {
-            uint32_t SerializedSize = freqStream_->readVInt();
-            std::vector<uint8_t> buf(SerializedSize + PFOR_BLOCK_SIZE);
-            freqStream_->readBytes(buf.data(), SerializedSize);
-            P4DEC(buf.data(), arraySize, docs_.data());
-        }
-        if (hasProx_) {
-            uint32_t SerializedSize = freqStream_->readVInt();
-            std::vector<uint8_t> buf(SerializedSize + PFOR_BLOCK_SIZE);
-            freqStream_->readBytes(buf.data(), SerializedSize);
-            P4NZDEC(buf.data(), arraySize, freqs_.data());
-        }
-    } else if (mode == (char)CodeMode::kDefault) {
-        uint32_t docDelta = 0;
-        for (uint32_t i = 0; i < arraySize; i++) {
-            uint32_t docCode = freqStream_->readVInt();
-            if (hasProx_) {
-                docDelta += (docCode >> 1);
-                docs_[i] = docDelta;
-                if ((docCode & 1) != 0) {
-                    freqs_[i] = 1;
-                } else {
-                    freqs_[i] = freqStream_->readVInt();
-                }
-            } else {
-                docDelta += docCode;
-                docs_[i] = docDelta;
-            }            
-        }
-    }
-    return arraySize;
+    return PforUtil::pfor_decode(freqStream_, docs_, freqs_, hasProx_, compatibleRead_);
 }
 
 CL_NS_END
diff --git a/src/core/CLucene/index/_SegmentHeader.h b/src/core/CLucene/index/_SegmentHeader.h
index 740023200c5..739836c361e 100644
--- a/src/core/CLucene/index/_SegmentHeader.h
+++ b/src/core/CLucene/index/_SegmentHeader.h
@@ -33,12 +33,13 @@ class SegmentReader;
 
 class TermDocsBuffer {
 public:
-  TermDocsBuffer(CL_NS(store)::IndexInput* freqStream, bool hasProx, IndexVersion indexVersion)
+  TermDocsBuffer(CL_NS(store)::IndexInput* freqStream, bool hasProx, IndexVersion indexVersion, bool compatibleRead)
       : docs_(PFOR_BLOCK_SIZE + 3),
         freqs_(PFOR_BLOCK_SIZE + 3),
         freqStream_(freqStream),
         hasProx_(hasProx),
-        indexVersion_(indexVersion) {
+        indexVersion_(indexVersion),
+        compatibleRead_(compatibleRead) {
   }
 
   ~TermDocsBuffer() {
@@ -84,13 +85,14 @@ private:
   CL_NS(store)::IndexInput* freqStream_ = nullptr;
 
   bool hasProx_ = false;
+  bool compatibleRead_ = false;
   IndexVersion indexVersion_ = IndexVersion::kV0; 
 };
 
 class TermPostingsBuffer {
 public:
   TermPostingsBuffer(CL_NS(store)::IndexInput* proxStream, IndexVersion indexVersion)
-    : poss_(PforUtil::blockSize + 3)
+    : poss_(0)
     , proxStream_(proxStream)
     , indexVersion_(indexVersion) {
   }
diff --git a/src/core/CLucene/util/PFORUtil.cpp b/src/core/CLucene/util/PFORUtil.cpp
index 8ebad306a4e..7cc836514ac 100644
--- a/src/core/CLucene/util/PFORUtil.cpp
+++ b/src/core/CLucene/util/PFORUtil.cpp
@@ -15,6 +15,9 @@
 // specific language governing permissions and limitations
 // under the License.
 #include "PFORUtil.h"
+
+#include "CLucene/debug/error.h"
+#include "CLucene/index/CodeMode.h"
 #include "vp4.h"
 #if (defined(__i386) || defined(__x86_64__))
 #include <cpuid.h>
@@ -26,15 +29,15 @@
 CL_NS_USE(index)
 
 namespace {
-using DEC_FUNC = size_t (*)(unsigned char *__restrict, size_t, uint32_t *__restrict);
-using ENC_FUNC = size_t (*)(uint32_t *__restrict in, size_t n, unsigned char *__restrict out);
+using DEC_FUNC = size_t (*)(unsigned char* __restrict, size_t, uint32_t* __restrict);
+using ENC_FUNC = size_t (*)(uint32_t* __restrict in, size_t n, unsigned char* __restrict out);
 DEC_FUNC g_p4nd1dec;
 DEC_FUNC g_p4nzdec;
 ENC_FUNC g_p4nd1enc;
 ENC_FUNC g_p4nzenc;
-} // anonymous namespace
+} // namespace
 
-size_t DefaultDEC(unsigned char *__restrict in, size_t n, uint32_t *__restrict out) {
+size_t DefaultDEC(unsigned char* __restrict in, size_t n, uint32_t* __restrict out) {
     size_t bufferSize = 0;
     for (uint32_t i = 0; i < n; i++) {
         uint8_t b = in[bufferSize++];
@@ -48,7 +51,7 @@ size_t DefaultDEC(unsigned char *__restrict in, size_t n, uint32_t *__restrict o
     return n;
 }
 
-size_t DefaultDDEC(unsigned char *__restrict in, size_t n, uint32_t *__restrict out) {
+size_t DefaultDDEC(unsigned char* __restrict in, size_t n, uint32_t* __restrict out) {
     uint32_t docDelta = 0;
     size_t bufferSize = 0;
     for (uint32_t i = 0; i < n; i++) {
@@ -58,13 +61,13 @@ size_t DefaultDDEC(unsigned char *__restrict in, size_t n, uint32_t *__restrict
             b = in[bufferSize++];
             docCode |= (b & 0x7F) << shift;
         }
-        docDelta += docCode;  // Corrected line: Removed right shift
+        docDelta += docCode; // Corrected line: Removed right shift
         out[i] = docDelta;
     }
     return n;
 }
 
-size_t DefaultDENC(uint32_t *__restrict in, size_t n, unsigned char *__restrict out) {
+size_t DefaultDENC(uint32_t* __restrict in, size_t n, unsigned char* __restrict out) {
     int outIndex = 0;
     uint32_t lastDoc = 0;
     for (int32_t i = 0; i < n; i++) {
@@ -80,7 +83,7 @@ size_t DefaultDENC(uint32_t *__restrict in, size_t n, unsigned char *__restrict
     return outIndex;
 }
 
-size_t DefaultENC(uint32_t *__restrict in, size_t n, unsigned char *__restrict out) {
+size_t DefaultENC(uint32_t* __restrict in, size_t n, unsigned char* __restrict out) {
     int outIndex = 0;
     for (int32_t i = 0; i < n; i++) {
         uint32_t curDoc = in[i];
@@ -119,66 +122,269 @@ __attribute__((constructor)) void SelectPFORFunctions() {
 #endif
 }
 
-size_t P4DEC(unsigned char *__restrict in, size_t n, uint32_t *__restrict out) {
+size_t P4DEC(unsigned char* __restrict in, size_t n, uint32_t* __restrict out) {
     return g_p4nd1dec(in, n, out);
 }
 
-size_t P4NZDEC(unsigned char *__restrict in, size_t n, uint32_t *__restrict out) {
+size_t P4NZDEC(unsigned char* __restrict in, size_t n, uint32_t* __restrict out) {
     return g_p4nzdec(in, n, out);
 }
 
-size_t P4ENC(uint32_t *__restrict in, size_t n, unsigned char *__restrict out) {
+size_t P4ENC(uint32_t* __restrict in, size_t n, unsigned char* __restrict out) {
     return g_p4nd1enc(in, n, out);
 }
 
-size_t P4NZENC(uint32_t *__restrict in, size_t n, unsigned char *__restrict out) {
+size_t P4NZENC(uint32_t* __restrict in, size_t n, unsigned char* __restrict out) {
     return g_p4nzenc(in, n, out);
 }
 
 void PforUtil::encodePos(IndexOutput* out, std::vector<uint32_t>& buffer) {
-    auto encode = [&out, &buffer](size_t offset, size_t size, CodeMode mode) {
-        out->writeByte((char)mode);
-        out->writeVInt(size);
-        if (mode == CodeMode::kPfor) {
-            std::vector<uint8_t> compress(4 * size + PFOR_BLOCK_SIZE);
-            size_t compressSize = P4NZENC(buffer.data() + offset, size, compress.data());
-            out->writeVInt(compressSize);
-            out->writeBytes(reinterpret_cast<const uint8_t*>(compress.data()), compressSize);
-        } else if (mode == CodeMode::kDefault) {
-            for (size_t i = 0; i < size; i++) {
-                out->writeVInt(buffer[offset + i]);
-            }
-        }
-    };
-
-    size_t i = 0;
-    size_t totalSize = buffer.size();
-    while (i < totalSize) {
-        size_t remainingElements = totalSize - i;
-        if (remainingElements >= blockSize) {
-            encode(i, blockSize, CodeMode::kPfor);
-            i += blockSize;
-        } else {
-            encode(i, remainingElements, CodeMode::kDefault);
-            break;
-        }
+#ifdef __AVX2__
+    out->writeByte((char)CodeMode::kPfor256);
+    out->writeVInt(buffer.size());
+    std::vector<uint8_t> compress(4 * buffer.size() + PFOR_BLOCK_SIZE);
+    size_t compressSize = p4nzenc256v32(buffer.data(), buffer.size(), compress.data());
+    out->writeVInt(compressSize);
+    out->writeBytes(reinterpret_cast<const uint8_t*>(compress.data()), compressSize);
+#elif defined(__ARM_NEON)
+    out->writeByte((char)CodeMode::kPfor128);
+    out->writeVInt(buffer.size());
+    std::vector<uint8_t> compress(4 * buffer.size() + PFOR_BLOCK_SIZE);
+    size_t compressSize = p4nzenc32(buffer.data(), buffer.size(), compress.data());
+    out->writeVInt(compressSize);
+    out->writeBytes(reinterpret_cast<const uint8_t*>(compress.data()), compressSize);
+#else
+    out->writeByte((char)CodeMode::kDefault);
+    out->writeVInt(buffer.size());
+    for (size_t i = 0; i < buffer.size(); i++) {
+        out->writeVInt(buffer[i]);
     }
-
+#endif
     buffer.resize(0);
 }
 
 uint32_t PforUtil::decodePos(IndexInput* in, std::vector<uint32_t>& buffer) {
     CodeMode mode = static_cast<CodeMode>(in->readByte());
     uint32_t size = in->readVInt();
+    buffer.resize((size + PforUtil::blockSize - 1) / PforUtil::blockSize * PforUtil::blockSize + 3);
     if (mode == CodeMode::kPfor) {
+        _CLTHROWA(CL_ERR_CorruptIndex, "PFOR old version is not supported for decodePos");
+    } else if (mode == CodeMode::kPfor256) {
+        uint32_t serializedSize = in->readVInt();
+        std::vector<uint8_t> buf(serializedSize + PFOR_BLOCK_SIZE);
+        in->readBytes(buf.data(), serializedSize);
+#if defined(USE_AVX2) && defined(__AVX2__)
+        p4nzdec256v32(buf.data(), size, buffer.data());
+#else
+        _CLTHROWA(CL_ERR_CorruptIndex, "PFOR256 is not supported on this platform");
+#endif
+    } else if (mode == CodeMode::kPfor128) {
         uint32_t serializedSize = in->readVInt();
         std::vector<uint8_t> buf(serializedSize + PFOR_BLOCK_SIZE);
         in->readBytes(buf.data(), serializedSize);
-        P4NZDEC(buf.data(), size, buffer.data());
-    } else {
+#if defined(USE_AVX2) && defined(__AVX2__)
+        p4nzdec32(buf.data(), size, buffer.data());
+#elif defined(__ARM_NEON)
+        p4nzdec32(buf.data(), size, buffer.data());
+#else
+        _CLTHROWA(CL_ERR_CorruptIndex, "PFOR128 is not supported on this platform");
+#endif
+    } else if (mode == CodeMode::kDefault) {
         for (uint32_t i = 0; i < size; i++) {
             buffer[i] = in->readVInt();
         }
     }
     return size;
-}
\ No newline at end of file
+}
+
+void PforUtil::pfor_encode(IndexOutput* out, std::vector<uint32_t>& docDeltaBuffer,
+                           std::vector<uint32_t>& freqBuffer, bool has_prox) {
+#ifdef __AVX2__
+    out->writeByte((char)CodeMode::kPfor256);
+    out->writeVInt(docDeltaBuffer.size());
+    std::vector<uint8_t> compress(4 * docDeltaBuffer.size() + PFOR_BLOCK_SIZE);
+    size_t size = 0;
+    size = p4nd1enc256v32(docDeltaBuffer.data(), docDeltaBuffer.size(), compress.data());
+    out->writeVInt(size);
+    out->writeBytes(reinterpret_cast<const uint8_t*>(compress.data()), size);
+    if (has_prox) {
+        size = p4nzenc256v32(freqBuffer.data(), freqBuffer.size(), compress.data());
+        out->writeVInt(size);
+        out->writeBytes(reinterpret_cast<const uint8_t*>(compress.data()), size);
+    }
+#elif defined(__ARM_NEON)
+    out->writeByte((char)CodeMode::kPfor128);
+    out->writeVInt(docDeltaBuffer.size());
+    std::vector<uint8_t> compress(4 * docDeltaBuffer.size() + PFOR_BLOCK_SIZE);
+    size_t size = 0;
+    size = p4nd1enc32(docDeltaBuffer.data(), docDeltaBuffer.size(), compress.data());
+    out->writeVInt(size);
+    out->writeBytes(reinterpret_cast<const uint8_t*>(compress.data()), size);
+    if (has_prox) {
+        size = p4nzenc32(freqBuffer.data(), freqBuffer.size(), compress.data());
+        out->writeVInt(size);
+        out->writeBytes(reinterpret_cast<const uint8_t*>(compress.data()), size);
+    }
+#else
+    out->writeByte((char)CodeMode::kDefault);
+    out->writeVInt(docDeltaBuffer.size());
+    uint32_t lastDoc = 0;
+    for (int32_t i = 0; i < docDeltaBuffer.size(); i++) {
+        uint32_t curDoc = docDeltaBuffer[i];
+        if (has_prox) {
+            uint32_t newDocCode = (curDoc - lastDoc) << 1;
+            lastDoc = curDoc;
+            uint32_t freq = freqBuffer[i];
+            if (1 == freq) {
+                out->writeVInt(newDocCode | 1);
+            } else {
+                out->writeVInt(newDocCode);
+                out->writeVInt(freq);
+            }
+        } else {
+            out->writeVInt(curDoc - lastDoc);
+            lastDoc = curDoc;
+        }
+    }
+#endif
+    docDeltaBuffer.resize(0);
+    freqBuffer.resize(0);
+}
+
+uint32_t PforUtil::pfor_decode(IndexInput* in, std::vector<uint32_t>& docs,
+                               std::vector<uint32_t>& freqs, bool has_prox, bool compatibleRead) {
+    char mode = in->readByte();
+    uint32_t arraySize = in->readVInt();
+    // old version, need to separate read based on compatibleRead
+    if (mode == (char)CodeMode::kPfor) {
+        {
+            uint32_t SerializedSize = in->readVInt();
+            std::vector<uint8_t> buf(SerializedSize + PFOR_BLOCK_SIZE);
+            in->readBytes(buf.data(), SerializedSize);
+#if defined(USE_AVX2) && defined(__AVX2__)
+            // if compatibleRead is true, means we are reading old version arm64 index in x86_64 platform.
+            if (compatibleRead) {
+                p4nd1dec32(buf.data(), arraySize, docs.data());
+            } else {
+                p4nd1dec256v32(buf.data(), arraySize, docs.data());
+            }
+#elif (defined(__ARM_NEON))
+            // if compatibleRead is true, means we are reading old version x86_64 index in arm64 platform.
+            if (compatibleRead) {
+                p4nd1dec256scalarv32(buf.data(), arraySize, docs.data());
+            } else {
+                p4nd1dec32(buf.data(), arraySize, docs.data());
+            }
+#elif (defined(__SSSE3__))
+            // if compatibleRead is true, means we are reading old version x86_64 index in x86_64 which does not support avx2.
+            if (compatibleRead) {
+                p4nd1dec256scalarv32(buf.data(), arraySize, docs.data());
+            } else {
+                DefaultDDEC(buf.data(), arraySize, docs.data());
+            }
+#else
+            DefaultDDEC(buf.data(), arraySize, docs.data());
+#endif
+        }
+        if (has_prox) {
+            uint32_t SerializedSize = in->readVInt();
+            std::vector<uint8_t> buf(SerializedSize + PFOR_BLOCK_SIZE);
+            in->readBytes(buf.data(), SerializedSize);
+#if defined(USE_AVX2) && defined(__AVX2__)
+            // if compatibleRead is true, means we are reading old version arm64 index in x86_64 platform.
+            if (compatibleRead) {
+                p4nzdec32(buf.data(), arraySize, freqs.data());
+            } else {
+                p4nzdec256v32(buf.data(), arraySize, freqs.data());
+            }
+#elif (defined(__ARM_NEON))
+            // if compatibleRead is true, means we are reading old version x86_64 index in arm64 platform.
+            if (compatibleRead) {
+                p4nzdec256scalarv32(buf.data(), arraySize, freqs.data());
+            } else {
+                p4nzdec32(buf.data(), arraySize, freqs.data());
+            }
+#elif (defined(__SSSE3__))
+            // if compatibleRead is true, means we are reading old version x86_64 index in x86_64 which does not support avx2.
+            if (compatibleRead) {
+                p4nzdec256scalarv32(buf.data(), arraySize, freqs.data());
+            } else {
+                DefaultDEC(buf.data(), arraySize, freqs.data());
+            }
+#else
+            DefaultDEC(buf.data(), arraySize, freqs.data());
+#endif
+        }
+    } else if (mode == (char)CodeMode::kDefault) {
+        uint32_t docDelta = 0;
+        for (uint32_t i = 0; i < arraySize; i++) {
+            uint32_t docCode = in->readVInt();
+            if (has_prox) {
+                docDelta += (docCode >> 1);
+                docs[i] = docDelta;
+                if ((docCode & 1) != 0) {
+                    freqs[i] = 1;
+                } else {
+                    freqs[i] = in->readVInt();
+                }
+            } else {
+                docDelta += docCode;
+                docs[i] = docDelta;
+            }
+        }
+    } else if (mode == (char)CodeMode::kPfor256) {
+        {
+            uint32_t SerializedSize = in->readVInt();
+            std::vector<uint8_t> buf(SerializedSize + PFOR_BLOCK_SIZE);
+            in->readBytes(buf.data(), SerializedSize);
+#if defined(USE_AVX2) && defined(__AVX2__)
+            // new version, read cross platform, x86_64 read arm64 index
+            p4nd1dec256v32(buf.data(), arraySize, docs.data());
+#elif (defined(__SSSE3__) || defined(__ARM_NEON))
+            p4nd1dec256scalarv32(buf.data(), arraySize, docs.data());
+#else
+            _CLTHROWA(CL_ERR_CorruptIndex, "PFOR256 is not supported on this platform");
+#endif
+        }
+        if (has_prox) {
+            uint32_t SerializedSize = in->readVInt();
+            std::vector<uint8_t> buf(SerializedSize + PFOR_BLOCK_SIZE);
+            in->readBytes(buf.data(), SerializedSize);
+#if defined(USE_AVX2) && defined(__AVX2__)
+            p4nzdec256v32(buf.data(), arraySize, freqs.data());
+#elif (defined(__SSSE3__) || defined(__ARM_NEON))
+            // new version, read cross platform, arm64 read x86_64 index
+            p4nzdec256scalarv32(buf.data(), arraySize, freqs.data());
+#else
+            _CLTHROWA(CL_ERR_CorruptIndex, "PFOR256 is not supported on this platform");
+#endif
+        }
+    } else if (mode == (char)CodeMode::kPfor128) {
+        {
+            uint32_t SerializedSize = in->readVInt();
+            std::vector<uint8_t> buf(SerializedSize + PFOR_BLOCK_SIZE);
+            in->readBytes(buf.data(), SerializedSize);
+#if defined(USE_AVX2) && defined(__AVX2__)
+            // new version, read cross platform, x86_64 read arm64 index
+            p4nd1dec32(buf.data(), arraySize, docs.data());
+#elif (defined(__SSSE3__) || defined(__ARM_NEON))
+            p4nd1dec32(buf.data(), arraySize, docs.data());
+#else
+            _CLTHROWA(CL_ERR_CorruptIndex, "PFOR128 is not supported on this platform");
+#endif
+        }
+        if (has_prox) {
+            uint32_t SerializedSize = in->readVInt();
+            std::vector<uint8_t> buf(SerializedSize + PFOR_BLOCK_SIZE);
+            in->readBytes(buf.data(), SerializedSize);
+#if defined(USE_AVX2) && defined(__AVX2__)
+            p4nzdec32(buf.data(), arraySize, freqs.data());
+#elif (defined(__SSSE3__) || defined(__ARM_NEON))
+            p4nzdec32(buf.data(), arraySize, freqs.data());
+#else
+            _CLTHROWA(CL_ERR_CorruptIndex, "PFOR128 is not supported on this platform");
+#endif
+        }
+    }
+    return arraySize;
+}
diff --git a/src/core/CLucene/util/PFORUtil.h b/src/core/CLucene/util/PFORUtil.h
index 583f5c9a25e..f76ebf9b970 100644
--- a/src/core/CLucene/util/PFORUtil.h
+++ b/src/core/CLucene/util/PFORUtil.h
@@ -22,7 +22,7 @@
 
 #include "CLucene.h"
 #include "CLucene/store/IndexOutput.h"
-
+#include "CLucene/store/IndexInput.h"
 CL_NS_USE(store)
 
 size_t P4DEC(unsigned char *__restrict in, size_t n, uint32_t *__restrict out);
@@ -36,4 +36,7 @@ public:
 
     static void encodePos(IndexOutput* out, std::vector<uint32_t>& buffer);
     static uint32_t decodePos(IndexInput* out, std::vector<uint32_t>& buffer);
-};
\ No newline at end of file
+    static void pfor_encode(IndexOutput* out, std::vector<uint32_t>& docDeltaBuffer, std::vector<uint32_t>& freqBuffer, bool has_prox);
+    static uint32_t pfor_decode(IndexInput* in, std::vector<uint32_t>& docs, std::vector<uint32_t>& freqs, bool has_prox, bool compatibleRead);
+};
+
diff --git a/src/ext/for/CMakeLists.txt b/src/ext/for/CMakeLists.txt
index 3b14781f69e..24384297462 100644
--- a/src/ext/for/CMakeLists.txt
+++ b/src/ext/for/CMakeLists.txt
@@ -12,8 +12,8 @@ set(DEBUG "-DNDEBUG -g -O3")
 set(OPT "-w -Wall -fstrict-aliasing -falign-loops -Wno-int-conversion")
 
 if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
-    set(SSE "-march=corei7-avx -mtune=corei7-avx")
-    set(AVX2 "-march=haswell")
+    set(SSE "-msse4.2")
+    set(AVX2 "-mavx2")
     set(CMAKE_C_FLAGS ${SSE})
 elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
     set(SSE "-march=armv8-a")
@@ -67,7 +67,7 @@ foreach(SRC_FILE ${SRC_FILES})
 
     add_custom_command(
         OUTPUT ${SSE_OUTPUT}
-        COMMAND ${CMAKE_C_COMPILER} -DSSE2_ON ${c_flags_list} -c -o ${SSE_OUTPUT} ${SRC_FILE}
+        COMMAND ${CMAKE_C_COMPILER} ${c_flags_list} -c -o ${SSE_OUTPUT} ${SRC_FILE}
         DEPENDS ${SRC_FILE}
     )
 
@@ -84,8 +84,24 @@ foreach(SRC_FILE ${SRC_FILES})
     endif()
 endforeach()
 
+add_executable(test_bitd1unpack test_bitd1unpack.cpp)
+
+target_link_libraries(test_bitd1unpack ic)
+
+if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
+    target_compile_options(test_bitd1unpack PRIVATE ${AVX2} -DAVX2_ON ${DEBUG})
+elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
+    target_compile_options(test_bitd1unpack PRIVATE -march=armv8-a ${DEBUG})
+elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64le")
+    target_compile_options(test_bitd1unpack PRIVATE -mcpu=power9 -mtune=power9 -D__SSSE3__ ${DEBUG})
+endif()
+
 set(LIB_DESTINATION ../)
 
 install(TARGETS ic
       DESTINATION ${LIB_DESTINATION}
       COMPONENT ext)
+
+install(TARGETS test_bitd1unpack
+      DESTINATION ${CMAKE_INSTALL_PREFIX}/bin
+      COMPONENT tests)
diff --git a/src/ext/for/bitpack.c b/src/ext/for/bitpack.c
index 666b1030e51..f7115dbc340 100644
--- a/src/ext/for/bitpack.c
+++ b/src/ext/for/bitpack.c
@@ -44,7 +44,7 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wunsequenced"
 
-#if !defined(SSE2_ON) && !defined(AVX2_ON) //----------------------------------- Plain -----------------------------------------------------------------------
+#ifndef __AVX2__ //----------------------------------- Plain -----------------------------------------------------------------------
 typedef unsigned char *(*BITPACK_F8)( uint8_t  *__restrict out, unsigned n, const unsigned char *__restrict in);
 typedef unsigned char *(*BITPACK_D8)( uint8_t  *__restrict out, unsigned n, const unsigned char *__restrict in, uint8_t start);
 typedef unsigned char *(*BITPACK_F16)(uint16_t *__restrict out, unsigned n, const unsigned char *__restrict in);
@@ -227,7 +227,9 @@ size_t bitnfpack16( uint16_t *__restrict in, size_t n, unsigned char *__restrict
 size_t bitnfpack32( uint32_t *__restrict in, size_t n, unsigned char *__restrict out) { uint32_t *ip,start; BITNDPACK(in, n, out, 128, 32, bitf, bitfpacka); }
 size_t bitnfpack64( uint64_t *__restrict in, size_t n, unsigned char *__restrict out) { uint64_t *ip,start; BITNDPACK(in, n, out, 128, 64, bitf, bitfpacka); }
 
-#else //--------------------------------------- SIMD ----------------------------------------------------------------------------------------------
+#endif // ifndef AVX2
+
+//--------------------------------------- SIMD ----------------------------------------------------------------------------------------------
 
 #define _BITNPACKV(in, n, out, _csize_, _usize_, _bitpackv_) {\
   unsigned char *op = out; TEMPLATE3(uint, _usize_, _t) _o,_x;\
@@ -422,6 +424,5 @@ size_t bitnzpack128v32( uint32_t *__restrict in, size_t n, unsigned char *__rest
 size_t bitnfpack128v16( uint16_t *__restrict in, size_t n, unsigned char *__restrict out) { uint16_t *ip,start; _BITNDPACKV(in, n, out, 128, 16, bitf,  bitfpack128v, bitf, bitfpack); }
 size_t bitnfpack128v32( uint32_t *__restrict in, size_t n, unsigned char *__restrict out) { uint32_t *ip,start; _BITNDPACKV(in, n, out, 128, 32, bitf,  bitfpack128v, bitf, bitfpack); }
   #endif // SSE
-#endif // Plain
 
 #pragma clang diagnostic pop
diff --git a/src/ext/for/bitpack.h b/src/ext/for/bitpack.h
index b0b9e02275a..b8f91ad5690 100644
--- a/src/ext/for/bitpack.h
+++ b/src/ext/for/bitpack.h
@@ -30,6 +30,7 @@
 #include <stdint.h>
 #endif
 #include <stddef.h>
+#include <stdbool.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -271,6 +272,7 @@ unsigned char *bitunpack128v64(  const unsigned char *__restrict in, unsigned n,
 
 unsigned char *bitunpack256v32(  const unsigned char *__restrict in, unsigned n, unsigned *__restrict out,                 unsigned b);
 unsigned char *bitzunpack256v32( const unsigned char *__restrict in, unsigned n, unsigned *__restrict out, unsigned start, unsigned b);
+unsigned char *bitzunpack256scalarv32( const unsigned char *__restrict in, unsigned n, unsigned *__restrict out, unsigned start, unsigned b);
 unsigned char *bitdunpack256v32( const unsigned char *__restrict in, unsigned n, unsigned *__restrict out, unsigned start, unsigned b);
 unsigned char *bitd1unpack256v32(const unsigned char *__restrict in, unsigned n, unsigned *__restrict out, unsigned start, unsigned b);
 unsigned char *bitfunpack256v32( const unsigned char *__restrict in, unsigned n, unsigned *__restrict out, unsigned start, unsigned b);
@@ -299,10 +301,25 @@ unsigned char *_bitd1unpack128h32(const unsigned char *__restrict in, unsigned n
 unsigned char *_bitunpack256w32(  const unsigned char *__restrict in, unsigned n, unsigned *__restrict out, unsigned b, unsigned *__restrict pex, unsigned char *bb);
 unsigned char *_bitunpack128v64(  const unsigned char *__restrict in, unsigned n, uint64_t *__restrict out, unsigned b, uint32_t *__restrict pex, unsigned char *bb);
 
+unsigned char* bitd1unpack256scalarv32(const unsigned char* __restrict in, unsigned n,
+                                 unsigned* __restrict out, unsigned start, unsigned b);
+unsigned char* _bitd1unpack256scalarv32(const unsigned char* __restrict in, unsigned n,
+                                  unsigned* __restrict out, unsigned start, unsigned b,
+                                  unsigned* __restrict pex, unsigned char* bb);
+unsigned char *bitunpack256scalarv32(const unsigned char *__restrict in, unsigned n, 
+                                     unsigned *__restrict out, unsigned b);
+unsigned char *_bitunpack256scalarv32(const unsigned char *__restrict in,
+                                unsigned n,
+                                unsigned *__restrict out,
+                                unsigned b,
+                                unsigned *__restrict pex,
+                                unsigned char *bb,
+                                bool isZigZag);
 unsigned char *_bitunpack256v32(  const unsigned char *__restrict in, unsigned n, unsigned *__restrict out, unsigned b, unsigned *__restrict pex, unsigned char *bb);
 unsigned char *_bitdunpack256v32( const unsigned char *__restrict in, unsigned n, unsigned *__restrict out, unsigned start, unsigned b, unsigned *__restrict pex, unsigned char *bb);
 unsigned char *_bitd1unpack256v32(const unsigned char *__restrict in, unsigned n, unsigned *__restrict out, unsigned start, unsigned b, unsigned *__restrict pex, unsigned char *bb);
 unsigned char *_bitzunpack256v32( const unsigned char *__restrict in, unsigned n, unsigned *__restrict out, unsigned start, unsigned b, unsigned *__restrict pex, unsigned char *bb);
+unsigned char *_bitzunpack256scalarv32( const unsigned char *__restrict in, unsigned n, unsigned *__restrict out, unsigned start, unsigned b, unsigned *__restrict pex, unsigned char *bb);
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/ext/for/bitunpack.c b/src/ext/for/bitunpack.c
index 1dd78003ada..802df40caad 100644
--- a/src/ext/for/bitunpack.c
+++ b/src/ext/for/bitunpack.c
@@ -28,6 +28,9 @@
 #include "bitutil.h"
 #include "bitpack.h"
 #include "vint.h"
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
 
 #define PAD8(_x_) (((_x_)+7)/8)
 
@@ -40,7 +43,7 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wunsequenced"
 
-#if !defined(SSE2_ON) && !defined(AVX2_ON) //----------------------------------- Plain -------------------------------------------------------------------------------------------
+#ifndef __AVX2__ //----------------------------------- Plain -------------------------------------------------------------------------------------------
 typedef unsigned char *(*BITUNPACK_F8)( const unsigned char *__restrict in, unsigned n, uint8_t  *__restrict out);
 typedef unsigned char *(*BITUNPACK_D8)( const unsigned char *__restrict in, unsigned n, uint8_t  *__restrict out, uint8_t start);
 typedef unsigned char *(*BITUNPACK_F16)(const unsigned char *__restrict in, unsigned n, uint16_t *__restrict out);
@@ -130,7 +133,8 @@ size_t bitnfunpack16( unsigned char *__restrict in, size_t n, uint16_t *__restri
 size_t bitnfunpack32( unsigned char *__restrict in, size_t n, uint32_t *__restrict out) { uint32_t *op,start; BITNDUNPACK(in, n, out, 128, 32, bitfunpacka); }
 size_t bitnfunpack64( unsigned char *__restrict in, size_t n, uint64_t *__restrict out) { uint64_t *op,start; BITNDUNPACK(in, n, out, 128, 64, bitfunpacka); }
 
-#else //-------------------------------------------- SSE/AVX2 ---------------------------------------------------------------------------------------
+#endif
+//-------------------------------------------- SSE/AVX2 ---------------------------------------------------------------------------------------
 
 #define _BITNUNPACKV(in, n, out, _csize_, _usize_, _bitunpackv_) {\
   unsigned char *ip = in;\
@@ -690,6 +694,1205 @@ unsigned char *bitunpack256w32( const unsigned char *__restrict in, unsigned n,
   BITUNPACK128V32(in, b, out, sv);
   return (unsigned char *)_in+PAD8(256*b);
 }
+static void applyException_8bits(uint8_t xm8, uint32_t** pPEX, int nb, uint32_t ov[8]) {
+  uint32_t* ex = *pPEX;
+  for (int j = 0; j < 8; j++) {
+      if ((xm8 >> j) & 1) {
+          ov[j] += (ex[0] << nb);
+          ex++;
+      }
+  }
+  *pPEX = ex;
+}
+static inline uint32_t zigzagDecode_scalar(uint32_t x) {
+  // (x>>1) ^ -((x & 1) )
+  return (x >> 1) ^ -(x & 1);
+}
+static void bitunblk256v32_scalar_template(uint32_t** pIn, uint32_t** pOut, int expansions_count,
+                                           const uint8_t* SHIFT_HI, const uint8_t* SHIFT_LO,
+                                           const uint8_t* READ_FLAG, uint32_t mask, int nb,
+                                           bool isZigZag) {
+  const uint32_t* oldp = NULL; // pointer to current block data
+  uint32_t ov[8], tmp[8];
+
+  for (int k = 0; k < expansions_count; k++) {
+      if (k == 0) {
+          // Step 0: Load input block and directly take the lower nb bits
+          oldp = *pIn;
+          *pIn += 8;
+          for (int j = 0; j < 8; j++) {
+              ov[j] = oldp[j] & mask;
+          }
+      } else {
+          // First right shift the current block data by SHIFT_HI[k]
+          for (int j = 0; j < 8; j++) {
+              ov[j] = oldp[j] >> SHIFT_HI[k];
+          }
+          if (READ_FLAG[k]) {
+              // Need to load a new block: left shift the new block data by SHIFT_LO[k], then merge with ov
+              const uint32_t* newp = *pIn;
+              *pIn += 8;
+              for (int j = 0; j < 8; j++) {
+                  uint32_t part_lo = (newp[j] << SHIFT_LO[k]) & mask;
+                  ov[j] |= part_lo;
+              }
+              // Update current block pointer
+              oldp = newp;
+          } else {
+              // No need to load a new block, ensure the result is within mask range
+              for (int j = 0; j < 8; j++) {
+                  ov[j] &= mask;
+              }
+          }
+      }
+      // Write out the current 8 results
+      uint32_t* outp = *pOut;
+      for (int j = 0; j < 8; j++) {
+          if (isZigZag) {
+              outp[j] = zigzagDecode_scalar(ov[j]);
+          } else {
+              outp[j] = ov[j];
+          }
+      }
+      *pOut += 8;
+  }
+}
+/**
+ * Generic template: supports "some expansions don't need to read new blocks".
+ *
+ * Parameters:
+ *  - expansions_count: total number of expansions (for 29-bit, it might be 32 times)
+ *  - SHIFT_HI[k], SHIFT_LO[k]: right shift for leftover, left shift for new block in k-th expansion
+ *  - READ_FLAG[k]: whether k-th expansion needs to read a new block (1 means yes, 0 means no)
+ *  - mask: for 29-bit = (1u << 29) - 1
+ *  - nb: base bits (29)
+ */
+static void bitunblk256v32_scalarBlock_ex_template(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                                   unsigned char** pBB, int expansions_count,
+                                                   const uint8_t* SHIFT_HI, const uint8_t* SHIFT_LO,
+                                                   const uint8_t* READ_FLAG, uint32_t mask, int nb,
+                                                   bool isZigZag) {
+  const uint32_t* oldp = NULL; // leftover block (previous batch)
+
+  for (int k = 0; k < expansions_count; k++) {
+      uint32_t ov[8];
+
+      if (k == 0) {
+          // First time: directly read 8×32-bit and apply mask
+          oldp = *pIn;
+          *pIn += 8;
+          for (int j = 0; j < 8; j++) {
+              ov[j] = oldp[j] & mask;
+          }
+      } else {
+          // Subsequent expansions
+          uint8_t hi = SHIFT_HI[k];
+          uint8_t lo = SHIFT_LO[k];
+
+          // First shift leftover >> hi
+          for (int j = 0; j < 8; j++) {
+              ov[j] = (oldp[j] >> hi);
+          }
+
+          // If this expansion needs to read a new block, append newp << lo
+          if (READ_FLAG[k]) {
+              const uint32_t* newp = *pIn;
+              *pIn += 8;
+              for (int j = 0; j < 8; j++) {
+                  uint32_t part_lo = (newp[j] << lo) & mask;
+                  ov[j] |= part_lo;
+              }
+              // After reading, newp becomes the leftover for next time
+              oldp = newp;
+          } else {
+              // No need to read new block => just apply mask to leftover >> hi
+              for (int j = 0; j < 8; j++) {
+                  ov[j] &= mask;
+              }
+              // leftover remains unchanged, continue using oldp
+          }
+      }
+
+      // Apply exceptions
+      uint8_t xm8 = **pBB;
+      (*pBB)++;
+      applyException_8bits(xm8, pPEX, nb, ov);
+
+      // Write out this batch of 8 results
+      uint32_t* outp = *pOut;
+      for (int j = 0; j < 8; j++) {
+          if (isZigZag) {
+              outp[j] = zigzagDecode_scalar(ov[j]);
+          } else {
+              outp[j] = ov[j];
+          }
+      }
+      *pOut += 8;
+  }
+}
+static void bitunpack256v32_0_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                     unsigned char** pBB, bool isZigZag) {
+  uint32_t* op = *pOut;
+  for (int i = 0; i < 32; i++) {
+      // Read bitmap if exists, otherwise default to 0
+      uint8_t xm8 = (pBB != NULL) ? **pBB : 0;
+      if (pBB != NULL) {
+          (*pBB)++;
+      }
+      // Initialize output array (all zeros by default)
+      uint32_t ov[8] = {0};
+      if (xm8 != 0 && pPEX != NULL) {
+          applyException_8bits(xm8, pPEX, 0, ov);
+      }
+
+      // Directly write 8 values using a loop to avoid repeated memory copy calls
+      for (int j = 0; j < 8; j++) {
+          if (isZigZag) {
+              op[j] = zigzagDecode_scalar(ov[j]);
+          } else {
+              op[j] = ov[j];
+          }
+      }
+      op += 8;
+  }
+  *pOut = op;
+}
+
+static void bitunpack256v32_1_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                     unsigned char** pBB, bool isZigZag) {
+  const int nb1 = 1;
+  const uint32_t mask1 = 1; // 0x1
+  const int expansions_count_1 = 32;
+  static const uint8_t SHIFT_HI_1[32] = {0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10,
+                                         11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+                                         22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
+  static const uint8_t SHIFT_LO_1[32] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+  static const uint8_t READ_FLAG_1[32] = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+  if (pPEX != NULL && pBB != NULL) {
+      bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count_1, SHIFT_HI_1,
+                                             SHIFT_LO_1, READ_FLAG_1, mask1, nb1, isZigZag);
+  } else {
+      bitunblk256v32_scalar_template(pIn, pOut, expansions_count_1, SHIFT_HI_1, SHIFT_LO_1,
+                                     READ_FLAG_1, mask1, nb1, isZigZag);
+  }
+}
+
+static void bitunpack256v32_2_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                     unsigned char** pBB, bool isZigZag) {
+  const int nb2 = 2;
+  const uint32_t mask2 = (1u << nb2) - 1; // 0x3
+  const int expansions_count_2 = 16;
+  static const uint8_t SHIFT_HI_2[16] = {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30};
+  static const uint8_t SHIFT_LO_2[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+  static const uint8_t READ_FLAG_2[16] = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+  if (pPEX != NULL && pBB != NULL) {
+      for (int i = 0; i < 2; i++) {
+          bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count_2,
+                                                 SHIFT_HI_2, SHIFT_LO_2, READ_FLAG_2, mask2, nb2,
+                                                 isZigZag);
+      }
+  } else {
+      for (int i = 0; i < 2; i++) {
+          bitunblk256v32_scalar_template(pIn, pOut, expansions_count_2, SHIFT_HI_2, SHIFT_LO_2,
+                                         READ_FLAG_2, mask2, nb2, isZigZag);
+      }
+  }
+}
+
+static void bitunpack256v32_3_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                     unsigned char** pBB, bool isZigZag) {
+  const int nb3 = 3;
+  const uint32_t mask3 = (1u << nb3) - 1; // 0x7
+  const int expansions_count_3 = 32;
+  static const uint8_t SHIFT_HI_3[32] = {0, 3, 6, 9,  12, 15, 18, 21, 24, 27, 30,
+                                         1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31,
+                                         2, 5, 8, 11, 14, 17, 20, 23, 26, 29};
+  static const uint8_t SHIFT_LO_3[32] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0,
+                                         0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+  static const uint8_t READ_FLAG_3[32] = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
+                                          0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+  if (pPEX != NULL && pBB != NULL) {
+      bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count_3, SHIFT_HI_3,
+                                             SHIFT_LO_3, READ_FLAG_3, mask3, nb3, isZigZag);
+  } else {
+      bitunblk256v32_scalar_template(pIn, pOut, expansions_count_3, SHIFT_HI_3, SHIFT_LO_3,
+                                     READ_FLAG_3, mask3, nb3, isZigZag);
+  }
+}
+
+static void bitunpack256v32_4_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                     unsigned char** pBB, bool isZigZag) {
+  const uint32_t mask4 = (1u << 4) - 1; // 0xF
+  const int nb = 4;                     // base bits
+  const int expansions_count = 8;
+  static const uint8_t SHIFT_HI_4[8] = {0, 4, 8, 12, 16, 20, 24, 28};
+  static const uint8_t SHIFT_LO_4[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+  static const uint8_t READ_FLAG_4[8] = {1, 0, 0, 0, 0, 0, 0, 0};
+
+  if (pPEX != NULL && pBB != NULL) {
+      for (int i = 0; i < 4; i++) {
+          bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count, SHIFT_HI_4,
+                                                 SHIFT_LO_4, READ_FLAG_4, mask4, nb, isZigZag);
+      }
+  } else {
+      for (int i = 0; i < 4; i++) {
+          bitunblk256v32_scalar_template(pIn, pOut, expansions_count, SHIFT_HI_4, SHIFT_LO_4,
+                                         READ_FLAG_4, mask4, nb, isZigZag);
+      }
+  }
+}
+
+static void bitunpack256v32_5_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                     unsigned char** pBB, bool isZigZag) {
+  const int nb5 = 5;
+  const uint32_t mask5 = (1u << nb5) - 1; // 0x1F
+  const int expansions_count_5 = 32;
+  static const uint8_t SHIFT_HI_5[32] = {0,  5,  10, 15, 20, 25, 30, 3,  8,  13, 18,
+                                         23, 28, 1,  6,  11, 16, 21, 26, 31, 4,  9,
+                                         14, 19, 24, 29, 2,  7,  12, 17, 22, 27};
+  static const uint8_t SHIFT_LO_5[32] = {0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 4, 0, 0, 0,
+                                         0, 0, 0, 1, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0};
+  static const uint8_t READ_FLAG_5[32] = {1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0,
+                                          0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0};
+  if (pPEX != NULL && pBB != NULL) {
+      bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count_5, SHIFT_HI_5,
+                                             SHIFT_LO_5, READ_FLAG_5, mask5, nb5, isZigZag);
+  } else {
+      bitunblk256v32_scalar_template(pIn, pOut, expansions_count_5, SHIFT_HI_5, SHIFT_LO_5,
+                                     READ_FLAG_5, mask5, nb5, isZigZag);
+  }
+}
+static void bitunpack256v32_6_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                     unsigned char** pBB, bool isZigZag) {
+  const int nb6 = 6;
+  const uint32_t mask6 = (1u << nb6) - 1; // 0x3F
+  const int expansions_count_6 = 16;
+  static const uint8_t SHIFT_HI_6[16] = {0, 6, 12, 18, 24, 30, 4, 10, 16, 22, 28, 2, 8, 14, 20, 26};
+  static const uint8_t SHIFT_LO_6[16] = {0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0};
+  static const uint8_t READ_FLAG_6[16] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0};
+
+  if (pPEX != NULL && pBB != NULL) {
+      for (int i = 0; i < 2; i++) {
+          bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count_6,
+                                                 SHIFT_HI_6, SHIFT_LO_6, READ_FLAG_6, mask6, nb6,
+                                                 isZigZag);
+      }
+  } else {
+      for (int i = 0; i < 2; i++) {
+          bitunblk256v32_scalar_template(pIn, pOut, expansions_count_6, SHIFT_HI_6, SHIFT_LO_6,
+                                         READ_FLAG_6, mask6, nb6, isZigZag);
+      }
+  }
+}
+static void bitunpack256v32_7_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                     unsigned char** pBB, bool isZigZag) {
+  const int nb7 = 7;
+  const uint32_t mask7 = (1u << nb7) - 1; // 0x7F
+  const int expansions_count = 32;
+  static const uint8_t SHIFT_HI_7[32] = {0,  7,  14, 21, 28, 3,  10, 17, 24, 31, 6,
+                                         13, 20, 27, 2,  9,  16, 23, 30, 5,  12, 19,
+                                         26, 1,  8,  15, 22, 29, 4,  11, 18, 25};
+  static const uint8_t SHIFT_LO_7[32] = {0, 0, 0, 0, 4, 0, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0,
+                                         0, 0, 2, 0, 0, 0, 6, 0, 0, 0, 0, 3, 0, 0, 0, 0};
+  static const uint8_t READ_FLAG_7[32] = {1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0,
+                                          0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0};
+  if (pPEX != NULL && pBB != NULL) {
+      bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count, SHIFT_HI_7,
+                                             SHIFT_LO_7, READ_FLAG_7, mask7, nb7, isZigZag);
+  } else {
+      bitunblk256v32_scalar_template(pIn, pOut, expansions_count, SHIFT_HI_7, SHIFT_LO_7,
+                                     READ_FLAG_7, mask7, nb7, isZigZag);
+  }
+}
+static void bitunpack256v32_8_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                     unsigned char** pBB, bool isZigZag) {
+  const int nb8 = 8;
+  const uint32_t mask8 = (1u << nb8) - 1; // 0xFF
+  const int expansions_count_8 = 4;
+  static const uint8_t SHIFT_HI_8[4] = {0, 8, 16, 24};
+  static const uint8_t SHIFT_LO_8[4] = {0, 0, 0, 0};
+  static const uint8_t READ_FLAG_8[4] = {1, 0, 0, 0};
+
+  if (pPEX != NULL && pBB != NULL) {
+      for (int i = 0; i < 8; i++) {
+          bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count_8,
+                                                 SHIFT_HI_8, SHIFT_LO_8, READ_FLAG_8, mask8, nb8,
+                                                 isZigZag);
+      }
+  } else {
+      for (int i = 0; i < 8; i++) {
+          bitunblk256v32_scalar_template(pIn, pOut, expansions_count_8, SHIFT_HI_8, SHIFT_LO_8,
+                                         READ_FLAG_8, mask8, nb8, isZigZag);
+      }
+  }
+}
+
+static void bitunpack256v32_9_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                     unsigned char** pBB, bool isZigZag) {
+  const int nb9 = 9;
+  const uint32_t mask9 = (1u << nb9) - 1; // 0x1FF
+  const int expansions_count_9 = 32;
+  static const uint8_t SHIFT_HI_9[32] = {0, 9,  18, 27, 4,  13, 22, 31, 8,  17, 26,
+                                         3, 12, 21, 30, 7,  16, 25, 2,  11, 20, 29,
+                                         6, 15, 24, 1,  10, 19, 28, 5,  14, 23};
+  static const uint8_t SHIFT_LO_9[32] = {0, 0, 0, 5, 0, 0, 0, 1, 0, 0, 6, 0, 0, 0, 2, 0,
+                                         0, 7, 0, 0, 0, 3, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0};
+  static const uint8_t READ_FLAG_9[32] = {1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0,
+                                          0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0};
+
+  if (pPEX != NULL && pBB != NULL) {
+      bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count_9, SHIFT_HI_9,
+                                             SHIFT_LO_9, READ_FLAG_9, mask9, nb9, isZigZag);
+  } else {
+      bitunblk256v32_scalar_template(pIn, pOut, expansions_count_9, SHIFT_HI_9, SHIFT_LO_9,
+                                     READ_FLAG_9, mask9, nb9, isZigZag);
+  }
+}
+static void bitunpack256v32_10_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                      unsigned char** pBB, bool isZigZag) {
+  const int nb10 = 10;
+  const uint32_t mask10 = (1u << nb10) - 1; // 0x3FF
+  const int expansions_count_10 = 16;
+  static const uint8_t SHIFT_HI_10[16] = {0,  10, 20, 30, 8,  18, 28, 6,
+                                          16, 26, 4,  14, 24, 2,  12, 22};
+  static const uint8_t SHIFT_LO_10[16] = {0, 0, 0, 2, 0, 0, 4, 0, 0, 6, 0, 0, 8, 0, 0, 0};
+  static const uint8_t READ_FLAG_10[16] = {1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0};
+
+  if (pPEX != NULL && pBB != NULL) {
+      for (int i = 0; i < 2; i++) {
+          bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count_10,
+                                                 SHIFT_HI_10, SHIFT_LO_10, READ_FLAG_10, mask10,
+                                                 nb10, isZigZag);
+      }
+  } else {
+      for (int i = 0; i < 2; i++) {
+          bitunblk256v32_scalar_template(pIn, pOut, expansions_count_10, SHIFT_HI_10, SHIFT_LO_10,
+                                         READ_FLAG_10, mask10, nb10, isZigZag);
+      }
+  }
+}
+
+static void bitunpack256v32_11_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                      unsigned char** pBB, bool isZigZag) {
+  const int nb11 = 11;
+  const uint32_t mask11 = (1u << nb11) - 1; // 0x7FF
+  const int expansions_count_11 = 32;
+  static const uint8_t SHIFT_HI_11[32] = {0,  11, 22, 1,  12, 23, 2,  13, 24, 3,  14,
+                                          25, 4,  15, 26, 5,  16, 27, 6,  17, 28, 7,
+                                          18, 29, 8,  19, 30, 9,  20, 31, 10, 21};
+  static const uint8_t SHIFT_LO_11[32] = {0, 0, 10, 0, 0, 9, 0, 0, 8, 0, 0, 7, 0, 0, 6, 0,
+                                          0, 5, 0,  0, 4, 0, 0, 3, 0, 0, 2, 0, 0, 1, 0, 0};
+  static const uint8_t READ_FLAG_11[32] = {1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0,
+                                           0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0};
+
+  if (pPEX != NULL && pBB != NULL) {
+      bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count_11, SHIFT_HI_11,
+                                             SHIFT_LO_11, READ_FLAG_11, mask11, nb11, isZigZag);
+  } else {
+      bitunblk256v32_scalar_template(pIn, pOut, expansions_count_11, SHIFT_HI_11, SHIFT_LO_11,
+                                     READ_FLAG_11, mask11, nb11, isZigZag);
+  }
+}
+
+static void bitunpack256v32_12_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                      unsigned char** pBB, bool isZigZag) {
+  const int nb12 = 12;
+  const uint32_t mask12 = (1u << nb12) - 1; // 0xFFF
+  const int expansions_count_12 = 8;
+  static const uint8_t SHIFT_HI_12[8] = {0, 12, 24, 4, 16, 28, 8, 20};
+  static const uint8_t SHIFT_LO_12[8] = {0, 0, 8, 0, 0, 4, 0, 0};
+  static const uint8_t READ_FLAG_12[8] = {1, 0, 1, 0, 0, 1, 0, 0};
+
+  if (pPEX != NULL && pBB != NULL) {
+      for (int i = 0; i < 4; i++) {
+          bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count_12,
+                                                 SHIFT_HI_12, SHIFT_LO_12, READ_FLAG_12, mask12,
+                                                 nb12, isZigZag);
+      }
+  } else {
+      for (int i = 0; i < 4; i++) {
+          bitunblk256v32_scalar_template(pIn, pOut, expansions_count_12, SHIFT_HI_12, SHIFT_LO_12,
+                                         READ_FLAG_12, mask12, nb12, isZigZag);
+      }
+  }
+}
+
+static void bitunpack256v32_13_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                      unsigned char** pBB, bool isZigZag) {
+  const int nb13 = 13;
+  const uint32_t mask13 = (1u << nb13) - 1; // 0x1FFF
+  const int expansions_count_13 = 32;
+  static const uint8_t SHIFT_HI_13[32] = {0,  13, 26, 7,  20, 1,  14, 27, 8,  21, 2,
+                                          15, 28, 9,  22, 3,  16, 29, 10, 23, 4,  17,
+                                          30, 11, 24, 5,  18, 31, 12, 25, 6,  19};
+  static const uint8_t SHIFT_LO_13[32] = {0, 0, 6, 0, 12, 0, 0, 5, 0, 11, 0, 0, 4, 0, 10, 0,
+                                          0, 3, 0, 9, 0,  0, 2, 0, 8, 0,  0, 1, 0, 7, 0,  0};
+  static const uint8_t READ_FLAG_13[32] = {1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0,
+                                           0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0};
+  if (pPEX != NULL && pBB != NULL) {
+      bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count_13, SHIFT_HI_13,
+                                             SHIFT_LO_13, READ_FLAG_13, mask13, nb13, isZigZag);
+  } else {
+      bitunblk256v32_scalar_template(pIn, pOut, expansions_count_13, SHIFT_HI_13, SHIFT_LO_13,
+                                     READ_FLAG_13, mask13, nb13, isZigZag);
+  }
+}
+
+static void bitunpack256v32_14_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                      unsigned char** pBB, bool isZigZag) {
+  const int nb14 = 14;
+  const uint32_t mask14 = (1u << nb14) - 1; // 0x3FFF
+  const int expansions_count_14 = 16;
+  static const uint8_t SHIFT_HI_14[16] = {0,  14, 28, 10, 24, 6,  20, 2,
+                                          16, 30, 12, 26, 8,  22, 4,  18};
+  static const uint8_t SHIFT_LO_14[16] = {0, 0, 4, 0, 8, 0, 12, 0, 0, 2, 0, 6, 0, 10, 0, 0};
+  static const uint8_t READ_FLAG_14[16] = {1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0};
+  if (pPEX != NULL && pBB != NULL) {
+      for (int i = 0; i < 2; i++) {
+          bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count_14,
+                                                 SHIFT_HI_14, SHIFT_LO_14, READ_FLAG_14, mask14,
+                                                 nb14, isZigZag);
+      }
+  } else {
+      for (int i = 0; i < 2; i++) {
+          bitunblk256v32_scalar_template(pIn, pOut, expansions_count_14, SHIFT_HI_14, SHIFT_LO_14,
+                                         READ_FLAG_14, mask14, nb14, isZigZag);
+      }
+  }
+}
+
+static void bitunpack256v32_15_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                      unsigned char** pBB, bool isZigZag) {
+  const int nb15 = 15;
+  const uint32_t mask15 = (1u << 15) - 1; // 0x7FFF
+
+  // expansions=32 => unpacks 256 values at once
+  const int expansions_count_15 = 32;
+
+  static const uint8_t SHIFT_HI_15[32] = {0,  15, 30, 13, 28, 11, 26, 9,  24, 7,  22,
+                                          5,  20, 3,  18, 1,  16, 31, 14, 29, 12, 27,
+                                          10, 25, 8,  23, 6,  21, 4,  19, 2,  17};
+
+  static const uint8_t SHIFT_LO_15[32] = {0, 0, 2, 0, 4, 0, 6, 0, 8, 0, 10, 0,  12, 0,  14, 0,
+                                          0, 1, 0, 3, 0, 5, 0, 7, 0, 9, 0,  11, 0,  13, 0,  0};
+
+  static const uint8_t READ_FLAG_15[32] = {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
+                                           0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0};
+  if (pPEX != NULL && pBB != NULL) {
+      bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count_15, SHIFT_HI_15,
+                                             SHIFT_LO_15, READ_FLAG_15, mask15, nb15, isZigZag);
+  } else {
+      bitunblk256v32_scalar_template(pIn, pOut, expansions_count_15, SHIFT_HI_15, SHIFT_LO_15,
+                                     READ_FLAG_15, mask15, nb15, isZigZag);
+  }
+}
+
+static void bitunpack256v32_16_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                      unsigned char** pBB, bool isZigZag) {
+  const int nb16 = 16;
+  const uint32_t mask16 = (1u << 16) - 1; // 0xFFFF
+
+  const int expansions_count = 2;
+  // Iteration 0: directly read 8×32-bit; Iteration 1: only right shift 16 bits, no new data read
+  static const uint8_t SHIFT_HI_16[2] = {0, 16};
+  static const uint8_t SHIFT_LO_16[2] = {0, 0};
+  static const uint8_t READ_FLAG_16[2] = {1, 0};
+
+  if (pPEX != NULL && pBB != NULL) {
+      for (int i = 0; i < 16; i++) {
+          bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count,
+                                                 SHIFT_HI_16, SHIFT_LO_16, READ_FLAG_16, mask16,
+                                                 nb16, isZigZag);
+      }
+  } else {
+      for (int i = 0; i < 16; i++) {
+          bitunblk256v32_scalar_template(pIn, pOut, expansions_count, SHIFT_HI_16, SHIFT_LO_16,
+                                         READ_FLAG_16, mask16, nb16, isZigZag);
+      }
+  }
+}
+
+static void bitunpack256v32_17_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                      unsigned char** pBB, bool isZigZag) {
+  const int nb17 = 17;
+  const uint32_t mask17 = (1u << 17) - 1; // 0x1FFFF
+
+  // expansions=32 => unpacks 256 values
+  const int expansions_count_17 = 32;
+
+  static const uint8_t SHIFT_HI_17[32] = {0,  17, 2,  19, 4,  21, 6,  23, 8,  25, 10,
+                                          27, 12, 29, 14, 31, 16, 1,  18, 3,  20, 5,
+                                          22, 7,  24, 9,  26, 11, 28, 13, 30, 15};
+
+  static const uint8_t SHIFT_LO_17[32] = {0,  15, 0,  13, 0,  11, 0,  9, 0, 7, 0, 5, 0, 3, 0, 1,
+                                          16, 0,  14, 0,  12, 0,  10, 0, 8, 0, 6, 0, 4, 0, 2, 0};
+
+  static const uint8_t READ_FLAG_17[32] = {1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+                                           1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0};
+  if (pPEX != NULL && pBB != NULL) {
+      bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count_17, SHIFT_HI_17,
+                                             SHIFT_LO_17, READ_FLAG_17, mask17, nb17, isZigZag);
+  } else {
+      bitunblk256v32_scalar_template(pIn, pOut, expansions_count_17, SHIFT_HI_17, SHIFT_LO_17,
+                                     READ_FLAG_17, mask17, nb17, isZigZag);
+  }
+}
+
+static void bitunpack256v32_18_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                      unsigned char** pBB, bool isZigZag) {
+  // base bits & mask
+  const int nb18 = 18;
+  const uint32_t mask18 = (1u << 18) - 1; // 0x3FFFF
+
+  // expansions=16 => 128 values
+  const int expansions_count_18 = 16;
+
+  static const uint8_t SHIFT_HI_18[16] = {0,  18, 4,  22, 8,  26, 12, 30,
+                                          16, 2,  20, 6,  24, 10, 28, 14};
+  static const uint8_t SHIFT_LO_18[16] = {0, 14, 0, 10, 0, 6, 0, 2, 16, 0, 12, 0, 8, 0, 4, 0};
+  static const uint8_t READ_FLAG_18[16] = {// #0 =>1, #1 =>1, #2=>0, #3=>1,
+                                           // #4 =>0, #5 =>1, #6=>0, #7=>1,
+                                           // #8 =>1, #9 =>0, #10=>1, #11=>0,
+                                           // #12=>1, #13=>0, #14=>1, #15=>0
+                                           1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0};
+
+  if (pPEX != NULL && pBB != NULL) {
+      for (int i = 0; i < 2; i++) {
+          bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count_18,
+                                                 SHIFT_HI_18, SHIFT_LO_18, READ_FLAG_18, mask18,
+                                                 nb18, isZigZag);
+      }
+  } else {
+      for (int i = 0; i < 2; i++) {
+          bitunblk256v32_scalar_template(pIn, pOut, expansions_count_18, SHIFT_HI_18, SHIFT_LO_18,
+                                         READ_FLAG_18, mask18, nb18, isZigZag);
+      }
+  }
+}
+
+static void bitunpack256v32_19_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                      unsigned char** pBB, bool isZigZag) {
+  // base bits & mask
+  const int nb19 = 19;
+  const uint32_t mask19 = (1u << 19) - 1; // 0x7FFFF
+
+  // expansions=32 => unpacks 256 values at once
+  const int expansions_count_19 = 32;
+
+  static const uint8_t SHIFT_HI_19[32] = {0,  19, 6,  25, 12, 31, 18, 5,  24, 11, 30,
+                                          17, 4,  23, 10, 29, 16, 3,  22, 9,  28, 15,
+                                          2,  21, 8,  27, 14, 1,  20, 7,  26, 13};
+  static const uint8_t SHIFT_LO_19[32] = {0,  13, 0,  7, 0, 1,  14, 0,  8, 0, 2,  15, 0,  9, 0, 3,
+                                          16, 0,  10, 0, 4, 17, 0,  11, 0, 5, 18, 0,  12, 0, 6, 0};
+  static const uint8_t READ_FLAG_19[32] = {1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1,
+                                           1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0};
+  if (pPEX != NULL && pBB != NULL) {
+      bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count_19, SHIFT_HI_19,
+                                             SHIFT_LO_19, READ_FLAG_19, mask19, nb19, isZigZag);
+  } else {
+      bitunblk256v32_scalar_template(pIn, pOut, expansions_count_19, SHIFT_HI_19, SHIFT_LO_19,
+                                     READ_FLAG_19, mask19, nb19, isZigZag);
+  }
+}
+
+static void bitunpack256v32_20_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                      unsigned char** pBB, bool isZigZag) {
+  // base bits & mask
+  const int nb20 = 20;
+  const uint32_t mask20 = (1u << 20) - 1; // 0xFFFFF
+
+  // expansions=8 => process 64 values at once
+  const int expansions_count_20 = 8;
+
+  // shift tables for k=0..7
+  static const uint8_t SHIFT_HI_20[8] = {0, 20, 8, 28, 16, 4, 24, 12};
+  static const uint8_t SHIFT_LO_20[8] = {0, 12, 0, 4, 16, 0, 8, 0};
+  static const uint8_t READ_FLAG_20[8] = {1, 1, 0, 1, 1, 0, 1, 0};
+
+  if (pPEX != NULL && pBB != NULL) {
+      for (int i = 0; i < 4; i++) {
+          bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count_20,
+                                                 SHIFT_HI_20, SHIFT_LO_20, READ_FLAG_20, mask20,
+                                                 nb20, isZigZag);
+      }
+  } else {
+      for (int i = 0; i < 4; i++) {
+          bitunblk256v32_scalar_template(pIn, pOut, expansions_count_20, SHIFT_HI_20, SHIFT_LO_20,
+                                         READ_FLAG_20, mask20, nb20, isZigZag);
+      }
+  }
+}
+
+static void bitunpack256v32_21_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                      unsigned char** pBB, bool isZigZag) {
+  // base bits & mask
+  const uint32_t mask21 = (1u << 21) - 1; // 0x1FFFFF
+  const int nb21 = 21;
+
+  // expansions=32 => unpacks 256 values at once
+  const int expansions_count_21 = 32;
+
+  static const uint8_t SHIFT_HI_21[32] = {0,  21, 10, 31, 20, 9,  30, 19, 8,  29, 18,
+                                          7,  28, 17, 6,  27, 16, 5,  26, 15, 4,  25,
+                                          14, 3,  24, 13, 2,  23, 12, 1,  22, 11};
+  static const uint8_t SHIFT_LO_21[32] = {0,  11, 0,  1,  12, 0,  2,  13, 0,  3, 14,
+                                          0,  4,  15, 0,  5,  16, 0,  6,  17, 0, 7,
+                                          18, 0,  8,  19, 0,  9,  20, 0,  10, 0};
+  static const uint8_t READ_FLAG_21[32] = {
+          // Check original expansions #k if there's a "load #X" => 1 if yes, 0 if no
+          1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1,
+          1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0};
+
+  if (pPEX != NULL && pBB != NULL) {
+      bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count_21, SHIFT_HI_21,
+                                             SHIFT_LO_21, READ_FLAG_21, mask21, nb21, isZigZag);
+  } else {
+      bitunblk256v32_scalar_template(pIn, pOut, expansions_count_21, SHIFT_HI_21, SHIFT_LO_21,
+                                     READ_FLAG_21, mask21, nb21, isZigZag);
+  }
+}
+
+static void bitunpack256v32_22_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                      unsigned char** pBB, bool isZigZag) {
+  // base bits & mask
+  const uint32_t mask22 = (1u << 22) - 1; // 0x3FFFFF
+  const int nb22 = 22;
+
+  // b=22 => one block function with expansions=16 => outputs 128 values
+  // need to call it twice to get 256 values
+  const int expansions_count_22 = 16;
+
+  static const uint8_t SHIFT_HI_22[16] = {/* 0 */ 0,  /* 1 */ 22, /* 2 */ 12, /* 3 */ 2,
+                                          /* 4 */ 24, /* 5 */ 14, /* 6 */ 4,  /* 7 */ 26,
+                                          /* 8 */ 16, /* 9 */ 6,  /*10 */ 28, /*11 */ 18,
+                                          /*12 */ 8,  /*13 */ 30, /*14 */ 20, /*15 */ 10};
+
+  static const uint8_t SHIFT_LO_22[16] = {/* 0 */ 0,  /* 1 */ 10, /* 2 */ 20, /* 3 */ 0,
+                                          /* 4 */ 8,  /* 5 */ 18, /* 6 */ 0,  /* 7 */ 6,
+                                          /* 8 */ 16, /* 9 */ 0,  /*10 */ 4,  /*11 */ 14,
+                                          /*12 */ 0,  /*13 */ 2,  /*14 */ 12, /*15 */ 0};
+
+  static const uint8_t READ_FLAG_22[16] = {
+          // From original code: expansions #3, #6, #9, #12, #15 don't read, others do
+          1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0};
+
+  if (pPEX != NULL && pBB != NULL) {
+      for (int i = 0; i < 2; i++) {
+          bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count_22,
+                                                 SHIFT_HI_22, SHIFT_LO_22, READ_FLAG_22, mask22,
+                                                 nb22, isZigZag);
+      }
+  } else {
+      for (int i = 0; i < 2; i++) {
+          bitunblk256v32_scalar_template(pIn, pOut, expansions_count_22, SHIFT_HI_22, SHIFT_LO_22,
+                                         READ_FLAG_22, mask22, nb22, isZigZag);
+      }
+  }
+}
+
+static void bitunpack256v32_23_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                      unsigned char** pBB, bool isZigZag) {
+  // base bits & mask
+  const int nb23 = 23;
+  const uint32_t mask23 = (1u << 23) - 1; // 0x7FFFFF
+
+  // expansions_count=32
+  const int expansions_count_23 = 32;
+
+  // Predefined SHIFT_HI_23, SHIFT_LO_23, READ_FLAG_23
+  static const uint8_t SHIFT_HI_23[32] = {0,  23, 14, 5,  28, 19, 10, 1,  24, 15, 6,
+                                          29, 20, 11, 2,  25, 16, 7,  30, 21, 12, 3,
+                                          26, 17, 8,  31, 22, 13, 4,  27, 18, 9};
+
+  static const uint8_t SHIFT_LO_23[32] = {0, 9,  18, 0, 4,  13, 22, 0, 8,  17, 0,
+                                          3, 12, 21, 0, 7,  16, 0,  2, 11, 20, 0,
+                                          6, 15, 0,  1, 10, 19, 0,  5, 14, 0};
+
+  static const uint8_t READ_FLAG_23[32] = {1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1,
+                                           1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0};
+
+  if (pPEX != NULL && pBB != NULL) {
+      bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count_23, SHIFT_HI_23,
+                                             SHIFT_LO_23, READ_FLAG_23, mask23, nb23, isZigZag);
+  } else {
+      bitunblk256v32_scalar_template(pIn, pOut, expansions_count_23, SHIFT_HI_23, SHIFT_LO_23,
+                                     READ_FLAG_23, mask23, nb23, isZigZag);
+  }
+}
+static void bitunpack256v32_24_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                      unsigned char** pBB, bool isZigZag) {
+  // base bits & mask
+  const int nb24 = 24;
+  const uint32_t mask24 = (1u << 24) - 1; // 0xFFFFFF
+
+  // expansions_count=4 (corresponds to 4 expansions => outputs 32 values)
+  const int expansions_count_24 = 4;
+
+  // k=0 => leftover>>0, new<<0
+  // k=1 => leftover>>24, new<<8
+  // k=2 => leftover>>16, new<<16
+  // k=3 => leftover>>8,  no new block read
+  static const uint8_t SHIFT_HI_24[4] = {0, 24, 16, 8};
+  static const uint8_t SHIFT_LO_24[4] = {0, 8, 16, 0};
+
+  // Only read new blocks for steps 0,1,2, not for step 3
+  static const uint8_t READ_FLAG_24[4] = {1, 1, 1, 0};
+
+  if (pPEX != NULL && pBB != NULL) {
+      for (int i = 0; i < 8; i++) {
+          bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count_24,
+                                                 SHIFT_HI_24, SHIFT_LO_24, READ_FLAG_24, mask24,
+                                                 nb24, isZigZag);
+      }
+  } else {
+      for (int i = 0; i < 8; i++) {
+          bitunblk256v32_scalar_template(pIn, pOut, expansions_count_24, SHIFT_HI_24, SHIFT_LO_24,
+                                         READ_FLAG_24, mask24, nb24, isZigZag);
+      }
+  }
+}
+
+static void bitunpack256v32_25_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                      unsigned char** pBB, bool isZigZag) {
+  // mask & base bits
+  const uint32_t mask25 = (1u << 25) - 1; // 0x1FFFFFF
+  const int nb25 = 25;
+
+  // 32 expansions total
+  const int expansions_count_25 = 32;
+
+  // Extract high and low shift amounts from original implementation
+  static const uint8_t SHIFT_HI_25[32] = {
+          /* #0  */ 0,  /* #1  */ 25, /* #2  */ 18, /* #3  */ 11,
+          /* #4  */ 4,  /* #5  */ 29, /* #6  */ 22, /* #7  */ 15,
+          /* #8  */ 8,  /* #9  */ 1,  /* #10 */ 26, /* #11 */ 19,
+          /* #12 */ 12, /* #13 */ 5,  /* #14 */ 30, /* #15 */ 23,
+          /* #16 */ 16, /* #17 */ 9,  /* #18 */ 2,  /* #19 */ 27,
+          /* #20 */ 20, /* #21 */ 13, /* #22 */ 6,  /* #23 */ 31,
+          /* #24 */ 24, /* #25 */ 17, /* #26 */ 10, /* #27 */ 3,
+          /* #28 */ 28, /* #29 */ 21, /* #30 */ 14, /* #31 */ 7};
+
+  static const uint8_t SHIFT_LO_25[32] = {
+          /* #0  */ 0,  /* #1  */ 7,  /* #2  */ 14, /* #3  */ 21,
+          /* #4  */ 0,  /* #5  */ 3,  /* #6  */ 10, /* #7  */ 17,
+          /* #8  */ 24, /* #9  */ 0,  /* #10 */ 6,  /* #11 */ 13,
+          /* #12 */ 20, /* #13 */ 0,  /* #14 */ 2,  /* #15 */ 9,
+          /* #16 */ 16, /* #17 */ 23, /* #18 */ 0,  /* #19 */ 5,
+          /* #20 */ 12, /* #21 */ 19, /* #22 */ 0,  /* #23 */ 1,
+          /* #24 */ 8,  /* #25 */ 15, /* #26 */ 22, /* #27 */ 0,
+          /* #28 */ 4,  /* #29 */ 11, /* #30 */ 18, /* #31 */ 0};
+
+  // Mark which steps don't need to read new data
+  // Based on original code, expansions #4, #9, #13, #18, #22, #27, #31 don't need to read new data
+  static const uint8_t READ_FLAG_25[32] = {
+          /* #0  */ 1, /* #1  */ 1, /* #2  */ 1, /* #3  */ 1,
+          /* #4  */ 0, /* #5  */ 1, /* #6  */ 1, /* #7  */ 1,
+          /* #8  */ 1, /* #9  */ 0, /* #10 */ 1, /* #11 */ 1,
+          /* #12 */ 1, /* #13 */ 0, /* #14 */ 1, /* #15 */ 1,
+          /* #16 */ 1, /* #17 */ 1, /* #18 */ 0, /* #19 */ 1,
+          /* #20 */ 1, /* #21 */ 1, /* #22 */ 0, /* #23 */ 1,
+          /* #24 */ 1, /* #25 */ 1, /* #26 */ 1, /* #27 */ 0,
+          /* #28 */ 1, /* #29 */ 1, /* #30 */ 1, /* #31 */ 0};
+  if (pPEX != NULL && pBB != NULL) {
+      for (int i = 0; i < 2; i++) {
+          bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count_25,
+                                                 SHIFT_HI_25, SHIFT_LO_25, READ_FLAG_25, mask25,
+                                                 nb25, isZigZag);
+      }
+  } else {
+      bitunblk256v32_scalar_template(pIn, pOut, expansions_count_25, SHIFT_HI_25, SHIFT_LO_25,
+                                     READ_FLAG_25, mask25, nb25, isZigZag);
+  }
+}
+
+static void bitunpack256v32_26_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                      unsigned char** pBB, bool isZigZag) {
+  // mask & base bits
+  const uint32_t mask26 = (1u << 26) - 1; // 0x3FFFFFF
+  const int nb26 = 26;
+
+  // 16 expansions total
+  const int expansions_count_26 = 16;
+
+  // Extract high and low shift amounts from original implementation
+  static const uint8_t SHIFT_HI_26[16] = {
+          /* #0  */ 0,  /* #1  */ 26, /* #2  */ 20, /* #3  */ 14,
+          /* #4  */ 8,  /* #5  */ 2,  /* #6  */ 28, /* #7  */ 22,
+          /* #8  */ 16, /* #9  */ 10, /* #10 */ 4,  /* #11 */ 30,
+          /* #12 */ 24, /* #13 */ 18, /* #14 */ 12, /* #15 */ 6};
+
+  static const uint8_t SHIFT_LO_26[16] = {
+          /* #0  */ 0,  /* #1  */ 6,  /* #2  */ 12, /* #3  */ 18,
+          /* #4  */ 24, /* #5  */ 0,  /* #6  */ 4,  /* #7  */ 10,
+          /* #8  */ 16, /* #9  */ 22, /* #10 */ 0,  /* #11 */ 2,
+          /* #12 */ 8,  /* #13 */ 14, /* #14 */ 20, /* #15 */ 0};
+
+  // Mark which steps don't need to read new data
+  // Based on original code, expansions #5, #10, #15 don't need to read new data
+  static const uint8_t READ_FLAG_26[16] = {
+          /* #0  */ 1, /* #1  */ 1, /* #2  */ 1, /* #3  */ 1,
+          /* #4  */ 1, /* #5  */ 0, /* #6  */ 1, /* #7  */ 1,
+          /* #8  */ 1, /* #9  */ 1, /* #10 */ 0, /* #11 */ 1,
+          /* #12 */ 1, /* #13 */ 1, /* #14 */ 1, /* #15 */ 0};
+
+  if (pPEX != NULL && pBB != NULL) {
+      for (int i = 0; i < 2; i++) {
+          bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count_26,
+                                                 SHIFT_HI_26, SHIFT_LO_26, READ_FLAG_26, mask26,
+                                                 nb26, isZigZag);
+      }
+  } else {
+      for (int i = 0; i < 2; i++) {
+          bitunblk256v32_scalar_template(pIn, pOut, expansions_count_26, SHIFT_HI_26, SHIFT_LO_26,
+                                         READ_FLAG_26, mask26, nb26, isZigZag);
+      }
+  }
+}
+
+static void bitunpack256v32_27_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                      unsigned char** pBB, bool isZigZag) {
+  // mask & base bits
+  const uint32_t mask27 = (1u << 27) - 1; // 0x7FFFFFF
+  const int nb27 = 27;
+
+  // 32 expansions total
+  const int expansions_count_27 = 32;
+
+  // Extract high and low shift amounts from original implementation
+  static const uint8_t SHIFT_HI_27[32] = {
+          /* #0  */ 0,  /* #1  */ 27, /* #2  */ 22, /* #3  */ 17,
+          /* #4  */ 12, /* #5  */ 7,  /* #6  */ 2,  /* #7  */ 29,
+          /* #8  */ 24, /* #9  */ 19, /* #10 */ 14, /* #11 */ 9,
+          /* #12 */ 4,  /* #13 */ 31, /* #14 */ 26, /* #15 */ 21,
+          /* #16 */ 16, /* #17 */ 11, /* #18 */ 6,  /* #19 */ 1,
+          /* #20 */ 28, /* #21 */ 23, /* #22 */ 18, /* #23 */ 13,
+          /* #24 */ 8,  /* #25 */ 3,  /* #26 */ 30, /* #27 */ 25,
+          /* #28 */ 20, /* #29 */ 15, /* #30 */ 10, /* #31 */ 5};
+
+  static const uint8_t SHIFT_LO_27[32] = {
+          /* #0  */ 0,  /* #1  */ 5,  /* #2  */ 10, /* #3  */ 15,
+          /* #4  */ 20, /* #5  */ 25, /* #6  */ 0,  /* #7  */ 3,
+          /* #8  */ 8,  /* #9  */ 13, /* #10 */ 18, /* #11 */ 23,
+          /* #12 */ 0,  /* #13 */ 1,  /* #14 */ 6,  /* #15 */ 11,
+          /* #16 */ 16, /* #17 */ 21, /* #18 */ 26, /* #19 */ 0,
+          /* #20 */ 4,  /* #21 */ 9,  /* #22 */ 14, /* #23 */ 19,
+          /* #24 */ 24, /* #25 */ 0,  /* #26 */ 2,  /* #27 */ 7,
+          /* #28 */ 12, /* #29 */ 17, /* #30 */ 22, /* #31 */ 0};
+
+  // Mark which steps don't need to read new data
+  // From original code, steps #6, #12, #19, #25, #31 don't have CPY8(iv, *pIn)
+  static const uint8_t READ_FLAG_27[32] = {
+          /* #0  */ 1, /* #1  */ 1, /* #2  */ 1, /* #3  */ 1,
+          /* #4  */ 1, /* #5  */ 1, /* #6  */ 0, /* #7  */ 1,
+          /* #8  */ 1, /* #9  */ 1, /* #10 */ 1, /* #11 */ 1,
+          /* #12 */ 0, /* #13 */ 1, /* #14 */ 1, /* #15 */ 1,
+          /* #16 */ 1, /* #17 */ 1, /* #18 */ 1, /* #19 */ 0,
+          /* #20 */ 1, /* #21 */ 1, /* #22 */ 1, /* #23 */ 1,
+          /* #24 */ 1, /* #25 */ 0, /* #26 */ 1, /* #27 */ 1,
+          /* #28 */ 1, /* #29 */ 1, /* #30 */ 1, /* #31 */ 0};
+  if (pPEX != NULL && pBB != NULL) {
+      bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count_27, SHIFT_HI_27,
+                                             SHIFT_LO_27, READ_FLAG_27, mask27, nb27, isZigZag);
+  } else {
+      bitunblk256v32_scalar_template(pIn, pOut, expansions_count_27, SHIFT_HI_27, SHIFT_LO_27,
+                                     READ_FLAG_27, mask27, nb27, isZigZag);
+  }
+}
+static void bitunpack256v32_28_scalar(
+        uint32_t** pIn, uint32_t** pOut,
+        uint32_t** pPEX,                    // Optional parameter, non-NULL for extended version
+        unsigned char** pBB, bool isZigZag) // Optional parameter, non-NULL for extended version
+{
+  // Common constant definitions
+  const uint32_t mask28 = (1u << 28) - 1; // 0xFFFFFFF
+  const int nb28 = 28;
+  const int expansions_count_28 = 8;
+  static const uint8_t SHIFT_HI_28[8] = {0, 28, 24, 20, 16, 12, 8, 4};
+  static const uint8_t SHIFT_LO_28[8] = {0, 4, 8, 12, 16, 20, 24, 0};
+  static const uint8_t READ_FLAG_28[8] = {1, 1, 1, 1, 1, 1, 1, 0};
+
+  // Choose template based on whether extension parameters are provided
+  if (pPEX != NULL && pBB != NULL) {
+      // Call extended template, each call outputs 64 values, loop 4 times to get 256
+      for (int i = 0; i < 4; i++) {
+          bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count_28,
+                                                 SHIFT_HI_28, SHIFT_LO_28, READ_FLAG_28, mask28,
+                                                 nb28, isZigZag);
+      }
+  } else {
+      // Call non-extended template, also each call outputs 64 values, loop 4 times to get 256
+      for (int i = 0; i < 4; i++) {
+          bitunblk256v32_scalar_template(pIn, pOut, expansions_count_28, SHIFT_HI_28, SHIFT_LO_28,
+                                         READ_FLAG_28, mask28, nb28, isZigZag);
+      }
+  }
+}
+
+static void bitunpack256v32_29_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                      unsigned char** pBB, bool isZigZag) {
+  const uint32_t mask29 = (1U << 29) - 1; // 0x1FFFFFFF
+  const int expansions_count = 32;
+  static const uint8_t SHIFT_HI_29[32] = {0,  29, 26, 23, 20, 17, 14, 11, 8, 5, 2,
+                                          31, 28, 25, 22, 19, 16, 13, 10, 7, 4, 1,
+                                          30, 27, 24, 21, 18, 15, 12, 9,  6, 3};
+  static const uint8_t SHIFT_LO_29[32] = {0, 3, 6, 9,  12, 15, 18, 21, 24, 27, 0,
+                                          1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 0,
+                                          2, 5, 8, 11, 14, 17, 20, 23, 26, 0};
+  static const uint8_t READ_FLAG_29[32] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1,
+                                           1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0};
+  if (pPEX != NULL && pBB != NULL) {
+      bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count, SHIFT_HI_29,
+                                             SHIFT_LO_29, READ_FLAG_29, mask29, 29, isZigZag);
+  } else {
+      bitunblk256v32_scalar_template(pIn, pOut, expansions_count, SHIFT_HI_29, SHIFT_LO_29,
+                                     READ_FLAG_29, mask29, 29, isZigZag);
+  }
+}
+
+static void bitunpack256v32_30_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                      unsigned char** pBB, bool isZigZag) {
+  const uint32_t mask30 = (1U << 30) - 1; // 0x3FFFFFFF
+  const int expansions_count = 16;
+  static const uint8_t SHIFT_HI_30[16] = {0,  30, 28, 26, 24, 22, 20, 18,
+                                          16, 14, 12, 10, 8,  6,  4,  2};
+  static const uint8_t SHIFT_LO_30[16] = {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 0};
+  static const uint8_t READ_FLAG_30[16] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0};
+
+  if (pPEX != NULL && pBB != NULL) {
+      for (int i = 0; i < 2; i++) {
+          bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count,
+                                                 SHIFT_HI_30, SHIFT_LO_30, READ_FLAG_30, mask30, 30,
+                                                 isZigZag);
+      }
+  } else {
+      for (int i = 0; i < 2; i++) {
+          bitunblk256v32_scalar_template(pIn, pOut, expansions_count, SHIFT_HI_30, SHIFT_LO_30,
+                                         READ_FLAG_30, mask30, 30, isZigZag);
+      }
+  }
+}
+
+static void bitunpack256v32_31_scalar(uint32_t** pIn, uint32_t** pOut, uint32_t** pPEX,
+                                      unsigned char** pBB, bool isZigZag) {
+  const uint32_t mask31 = (1U << 31) - 1; // 0x7FFFFFFF
+  const int expansions_count = 32;
+  // Construct parameter arrays:
+  // For k==0: SHIFT_HI = 0, SHIFT_LO = 0, READ_FLAG = 1
+  // For k = 1 .. 30: SHIFT_HI = 32 - k, SHIFT_LO = k, READ_FLAG = 1
+  // For k==31: SHIFT_HI = 1, SHIFT_LO = 0, READ_FLAG = 0
+  static const uint8_t SHIFT_HI[32] = {0,  31, 30, 29, 28, 27, 26, 25, 24, 23, 22,
+                                       21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11,
+                                       10, 9,  8,  7,  6,  5,  4,  3,  2,  1};
+  static const uint8_t SHIFT_LO[32] = {0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10,
+                                       11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+                                       22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0};
+  static const uint8_t READ_FLAG[32] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0};
+  if (pPEX != NULL && pBB != NULL) {
+      bitunblk256v32_scalarBlock_ex_template(pIn, pOut, pPEX, pBB, expansions_count, SHIFT_HI,
+                                             SHIFT_LO, READ_FLAG, mask31, 31, isZigZag);
+  } else {
+      bitunblk256v32_scalar_template(pIn, pOut, expansions_count, SHIFT_HI, SHIFT_LO, READ_FLAG,
+                                     mask31, 31, isZigZag);
+  }
+}
+
+static void bitunpack256v32_32_scalar(
+        uint32_t** pIn, uint32_t** pOut,
+        uint32_t** pPEX,                    // Optional parameter, non-NULL for extended version
+        unsigned char** pBB, bool isZigZag) // Optional parameter, non-NULL for extended version
+{
+  uint32_t* ip = *pIn;
+  uint32_t* op = *pOut;
+  const int nb = 32; // When b=32, each 32-bit integer stores a value directly
+
+  // There are 32 groups, each group has 8 numbers, totaling 256 numbers
+  for (int i = 0; i < 32; i++) {
+      // Copy 8 input values directly to output (avoid calling CPY8)
+      for (int j = 0; j < 8; j++) {
+          op[j] = ip[j];
+      }
+      ip += 8;
+
+      if (pPEX != NULL && pBB != NULL) {
+          uint8_t xm8 = **pBB;
+          (*pBB)++;
+          if (xm8 != 0) {
+              applyException_8bits(xm8, pPEX, nb, op);
+          }
+      }
+      if (isZigZag) {
+          for (int j = 0; j < 8; j++) {
+              op[j] = zigzagDecode_scalar(op[j]);
+          }
+      }
+      op += 8;
+  }
+  *pIn = ip;
+  *pOut = op;
+}
+
+// Define function pointer type for unpacking functions
+typedef void (*unpack_func_t)(uint32_t**, uint32_t**, unsigned**, unsigned char**, bool);
+
+// Array of function pointers for each bit width (0 to 32)
+static unpack_func_t unpack_funcs[33] = {
+        bitunpack256v32_0_scalar,  bitunpack256v32_1_scalar,  bitunpack256v32_2_scalar,
+        bitunpack256v32_3_scalar,  bitunpack256v32_4_scalar,  bitunpack256v32_5_scalar,
+        bitunpack256v32_6_scalar,  bitunpack256v32_7_scalar,  bitunpack256v32_8_scalar,
+        bitunpack256v32_9_scalar,  bitunpack256v32_10_scalar, bitunpack256v32_11_scalar,
+        bitunpack256v32_12_scalar, bitunpack256v32_13_scalar, bitunpack256v32_14_scalar,
+        bitunpack256v32_15_scalar, bitunpack256v32_16_scalar, bitunpack256v32_17_scalar,
+        bitunpack256v32_18_scalar, bitunpack256v32_19_scalar, bitunpack256v32_20_scalar,
+        bitunpack256v32_21_scalar, bitunpack256v32_22_scalar, bitunpack256v32_23_scalar,
+        bitunpack256v32_24_scalar, bitunpack256v32_25_scalar, bitunpack256v32_26_scalar,
+        bitunpack256v32_27_scalar, bitunpack256v32_28_scalar, bitunpack256v32_29_scalar,
+        bitunpack256v32_30_scalar, bitunpack256v32_31_scalar, bitunpack256v32_32_scalar};
+/**
+ *
+ * @param in   Compressed data input stream
+ * @param n    Currently unused, can be processed according to actual needs
+ * @param out  Output buffer for decompressed 32-bit integers (must accommodate at least 256 32-bit integers)
+ * @param b    Bit width for each integer, this example only demonstrates the b=8 branch
+ * @return     Returns the next readable input position after decompression (consistent with original logic)
+ */
+unsigned char* bitunpack256scalarv32_withzigzag(const unsigned char* __restrict in, unsigned n,
+                                                unsigned* __restrict out, unsigned b,
+                                                bool isZigZag) {
+  // Debug output (optional, can be removed in production)
+  //printf("bitunpack256scalarv32_withzigzag b=%d bits=%d isZigZag=%d\n", b, b & 0x3f, isZigZag);
+
+  // Calculate input pointer offset
+  unsigned char* ip = (unsigned char*)(in + PAD8(256 * b));
+
+  // Initialize pointers
+  uint32_t* pIn32 = (uint32_t*)in;
+  uint32_t* pOut32 = (uint32_t*)out;
+
+  unsigned bits = b & 0x3f;
+  // Execute unpacking if b is in valid range
+  if (bits <= 32) {
+      unpack_funcs[bits](&pIn32, &pOut32, NULL, NULL, isZigZag);
+  }
+
+  return ip;
+}
+unsigned char* bitunpack256scalarv32(const unsigned char* __restrict in, unsigned n,
+                                     unsigned* __restrict out, unsigned b) {
+  // Debug output (optional, can be removed in production)
+  //printf("bitunpack256scalarv32 b=%d bits=%d\n", b, b & 0x3f);
+
+  // Calculate input pointer offset
+  unsigned char* ip = (unsigned char*)(in + PAD8(256 * b));
+
+  bitunpack256scalarv32_withzigzag(in, n, out, b, false);
+
+  return ip;
+}
+unsigned char* _bitd1unpack256scalarv32(const unsigned char* __restrict in, unsigned n,
+                                        unsigned* __restrict out, unsigned start, unsigned b,
+                                        unsigned* __restrict pex, unsigned char* bb) {
+  //printf("_bitd1unpack256scalarv32, b=%d\n", b & 0x3f);
+  unsigned* deltas = (unsigned*)malloc(n * sizeof(unsigned));
+  if (!deltas) return NULL;
+
+  const unsigned char* orig_in = in;
+  in = _bitunpack256scalarv32(in, n, deltas, b, pex, bb, false);
+
+  unsigned running_sum = start;
+  for (unsigned i = 0; i < n; ++i) {
+      running_sum += deltas[i] + 1;
+      out[i] = running_sum;
+  }
+
+  free(deltas);
+  return (unsigned char*)in;
+}
+
+// Add this after the definition of _bitunpack256w32 in the SSE2/SSSE3 section
+
+// Delta1 unpacking for 256 32-bit integers (no exceptions)
+unsigned char* bitd1unpack256scalarv32(const unsigned char* __restrict in, unsigned n,
+                                       unsigned* __restrict out, unsigned start, unsigned b) {
+  //printf("bitd1unpack256scalarv32, b=%d\n", b & 0x3f);
+  const unsigned char* _in = in;
+  unsigned deltas[n];
+
+  in = bitunpack256scalarv32(in, n, deltas, b);
+
+  unsigned running_sum = start;
+  for (unsigned i = 0; i < n; ++i) {
+      running_sum += deltas[i] + 1;
+      out[i] = running_sum;
+  }
+
+  return (unsigned char*)in;
+}
+
+unsigned char* _bitunpack256scalarv32(const unsigned char* __restrict in, unsigned n,
+                                      unsigned* __restrict out, unsigned b,
+                                      unsigned* __restrict pex, unsigned char* bb, bool isZigZag) {
+  // Debug output (optional, can be removed in production)
+  //printf("_bitunpack256scalarv32 bits=%d isZigZag=%d\n", b & 0x3f, isZigZag);
+
+  // Calculate input pointer offset
+  unsigned char* ip = (unsigned char*)(in + PAD8(256 * b));
+
+  // Initialize pointers
+  unsigned* pPEX = pex;
+  unsigned char* pBB = bb;
+  uint32_t* pIn32 = (uint32_t*)in;
+  uint32_t* pOut32 = (uint32_t*)out;
+
+  unsigned bits = b & 0x3f;
+  // Execute unpacking if b is in valid range
+  if (bits <= 32) {
+      unpack_funcs[bits](&pIn32, &pOut32, &pPEX, &pBB, isZigZag);
+  }
+
+  return ip;
+}
+
+unsigned char* bitzunpack256scalarv32(const unsigned char* __restrict in, unsigned n,
+                                      unsigned* __restrict out, unsigned start, unsigned b) {
+  // Debug output (optional, can be removed in production)
+  //printf("bitzunpack256scalarv32 b=%d bits=%d\n", b, b & 0x3f);
+  const unsigned char* _in = in;
+  unsigned deltas[n];
+
+  in = bitunpack256scalarv32_withzigzag(in, n, deltas, b, true);
+
+  unsigned running_sum = start;
+  for (unsigned i = 0; i < n; ++i) {
+      running_sum += deltas[i];
+      out[i] = running_sum;
+  }
+
+  return (unsigned char*)in;
+}
+unsigned char* _bitzunpack256scalarv32(const unsigned char* __restrict in, unsigned n,
+                                       unsigned* __restrict out, unsigned start, unsigned b,
+                                       unsigned* __restrict pex, unsigned char* bb) {
+  // Debug output (optional, can be removed in production)
+  //printf("_bitzunpack256scalarv32 bits=%d\n", b & 0x3f);
+
+  unsigned* deltas = (unsigned*)malloc(n * sizeof(unsigned));
+  if (!deltas) return NULL;
+
+  const unsigned char* orig_in = in;
+  in = _bitunpack256scalarv32(in, n, deltas, b, pex, bb, true);
+
+  unsigned running_sum = start;
+  for (unsigned i = 0; i < n; ++i) {
+      running_sum += deltas[i];
+      out[i] = running_sum;
+  }
+
+  free(deltas);
+  return (unsigned char*)in;
+}
 
 #define STOZ64(_op_, _ov_) _mm_storeu_si128(_op_++, _ov_); _mm_storeu_si128(_op_++, _ov_)
 #define STO64( _op_, _ov_, _zv_) _mm_storeu_si128(_op_++, _mm_unpacklo_epi32(_ov_,_zv_));_mm_storeu_si128(_op_++, _mm_unpacklo_epi32(_mm_srli_si128(_ov_,8),_zv_))
@@ -1224,7 +2427,6 @@ size_t bitnzunpack128v32( unsigned char *__restrict in, size_t n, uint32_t *__re
 size_t bitnfunpack128v16( unsigned char *__restrict in, size_t n, uint16_t *__restrict out) { uint16_t *op,start; _BITNDUNPACKV(in, n, out, 128, 16, bitfunpack128v, bitfunpack); }
 size_t bitnfunpack128v32( unsigned char *__restrict in, size_t n, uint32_t *__restrict out) { uint32_t *op,start; _BITNDUNPACKV(in, n, out, 128, 32, bitfunpack128v, bitfunpack); }
 
-#endif
 #endif
 
 #pragma clang diagnostic pop
diff --git a/src/ext/for/test_bitd1unpack.cpp b/src/ext/for/test_bitd1unpack.cpp
new file mode 100644
index 00000000000..23484a7f50e
--- /dev/null
+++ b/src/ext/for/test_bitd1unpack.cpp
@@ -0,0 +1,399 @@
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <vp4.h>
+#include <vint.h>
+#include "conf.h"
+
+// 定义PAD8宏
+#ifndef PAD8
+#define PAD8(_x_) (((_x_) + 7) / 8)
+#endif
+
+const unsigned TEST_SIZE = 512;
+
+#ifdef __AVX2__
+void generate_test_data(unsigned* raw_values, unsigned n, unsigned char* encoded_data,
+                        unsigned* out_size) {
+    // 使用p4nd1enc256v32编码原始数据
+    size_t end_ptr = p4nd1enc256v32(raw_values, n, encoded_data);
+
+    // 计算编码后数据大小
+    *out_size = end_ptr;
+}
+#endif
+#define _1vbxget32(_ip_, _x_, _act_) do { _x_ = (unsigned)(*_ip_++);\
+       if(!(_x_ & 0x80u)) {                                                                      _act_;}\
+  else if(!(_x_ & 0x40u)) { _x_ = bswap16(ctou16(_ip_ - 1) & 0xff3fu); _ip_++;                       _act_;}\
+  else if(!(_x_ & 0x20u)) { _x_ = (_x_ & 0x1f)<<16 | ctou16(_ip_);                    _ip_ += 2; _act_;}\
+  else if(!(_x_ & 0x10u)) { _x_ = bswap32(ctou32(_ip_-1) & 0xffffff0fu);                  _ip_ += 3; _act_;}\
+  else                    { _x_ = (unsigned long long)((_x_) & 0x07)<<32 | ctou32(_ip_); _ip_ += 4; _act_;}\
+} while(0)
+#define xvbxget32(_ip_, _x_) _1vbxget32(_ip_, _x_, ;)
+
+// 用于快速得到 10^k 的一个表,避免多次调用 pow
+// 注意 10^10=10000000000 需要 64 位才能存
+static const uint64_t g_pow10[] = {
+    1ULL,         // 10^0
+    10ULL,        // 10^1
+    100ULL,       // 10^2
+    1000ULL,      // 10^3
+    10000ULL,     // 10^4
+    100000ULL,    // 10^5
+    1000000ULL,   // 10^6
+    10000000ULL,  // 10^7
+    100000000ULL, // 10^8
+    1000000000ULL,// 10^9
+    10000000000ULL// 10^10
+};
+
+// 计算 10^(floor(b/3)),若超出 g_pow10 范围可再加判断
+static inline uint64_t get_pow10_for_b(unsigned b) {
+    // floor(b/3)
+    unsigned idx = b / 3;
+    if (idx >= sizeof(g_pow10)/sizeof(g_pow10[0])) {
+        // 超过预置表最大 10^10,就固定返回 10^10 或自行处理
+        return g_pow10[10];
+    }
+    return g_pow10[idx];
+}
+
+// 计算 2^b 的函数
+static inline unsigned power2(unsigned b) {
+    // (1U << b) 当 b=32 时也可能溢出,你可自行判断
+    return (1U << b);
+}
+
+
+/**
+ * @param values        输出数组
+ * @param n             要生成的数据个数
+ * @param b             当前位宽
+ * @param with_exception 0=无异常,1=有异常
+ */
+void generate_raw_data_for_bitwidth(unsigned* values, unsigned n,
+                                    unsigned b, int with_exception)
+{
+    if (n == 0) return;
+
+    if (!with_exception) {
+        // =====================================
+        //         无异常模式:递增序列
+        // =====================================
+        // 1) 先给一个随机初始值 base (你也可随意决定)
+        unsigned base = rand() % 1000;
+        values[0] = base;
+
+        // 2) 根据 b 分段决定“增量最大范围”
+        unsigned inc_range;
+        if (b < 4) {
+            // b=0 => 2^0=1, b=1 =>2, b=2 =>4, b=3=>8
+            inc_range = power2(b);
+        } else {
+            // b>=4 => 用10^(floor(b/3)) => 10,100,1000,...
+            uint64_t r = get_pow10_for_b(b);
+            // 这里最好判断 r 是否超出 unsigned 范围
+            // 若测试场景不会特别大,可以直接转为 unsigned
+            if (r > 0xFFFFFFFFULL) {
+                r = 0xFFFFFFFFULL; // 避免溢出
+            }
+            inc_range = (unsigned)r;
+        }
+
+        // 3) 生成递增序列
+        for (unsigned i = 1; i < n; i++) {
+            // +1 是为了避免 0 增量的情况
+            unsigned inc = 1 + rand() % inc_range;
+            base += inc;
+            values[i] = base;
+        }
+
+    } else {
+        // =====================================
+        //         有异常模式:直接随机
+        // =====================================
+
+        // 观察示例得知:
+        //  - b=0 => rand()%2
+        //  - b=2 => rand()%4
+        //  - b=3 => rand()%10
+        //  - b=7 => rand()%100
+        //  - b=10 => rand()%1000
+        //  - b=13 => rand()%10000
+        //  => 规律:当 b >= 3 用 10^(floor(b/3));当 b < 3 用特殊处理
+
+        uint64_t val_range = 0; // 用 64 位临时存,最后再转回 unsigned
+
+        if (b == 0) {
+            val_range = 2; // 0..1
+        } 
+        else if (b == 1) {
+            // 你没给 b=1 的具体例子,这里假设跟 b=0 一样 => range=2
+            val_range = 2; // 0..1
+        } 
+        else if (b == 2) {
+            val_range = 4; // 0..3
+        }
+        else {
+            // b>=3 => 用 10^(floor(b/3))
+            val_range = get_pow10_for_b(b);
+            // 同样检查一下是否超过 unsigned
+            if (val_range > 0xFFFFFFFFULL) {
+                val_range = 0xFFFFFFFFULL;
+            }
+        }
+
+        // 直接随机
+        for (unsigned i = 0; i < n; i++) {
+            unsigned x = (unsigned)(rand() % (unsigned)val_range);
+            values[i] = x;
+        }
+    }
+}
+
+/**
+ * 生成 n 个有符号数:
+ *   - b<3: 范围很小(±(1<<b) 之类)
+ *   - b>=3: 直接从 ±(10^(floor(b/3))) 随机, 并包含一定的负值
+ *
+ * with_exception=0 => 生成一个“有序/有限范围”
+ * with_exception=1 => 生成一个“更大随机范围” (你可自定义)
+ */
+static void generate_raw_signed_data_for_zigzag(unsigned *values,
+                                                unsigned n,
+                                                unsigned b,
+                                                int with_exception)
+{
+    if (n == 0) return;
+
+    // srand(...) 在外部一次初始化
+    uint64_t val_range = 1;
+    if (b < 3) {
+        // 例如 b=0 =>±1, b=1=>±2, b=2=>±4
+        val_range = (1ULL << b);
+    } else {
+        // b>=3 => use get_pow10_for_b(b) => 10^(floor(b/3))
+        val_range = get_pow10_for_b(b); // 参考你贴的 delta pfor
+        if(val_range > 0x7fffffffULL) {
+            val_range = 0x7fffffffULL; // 避免溢出 32-bit
+        }
+    }
+
+    for(unsigned i=0; i<n; i++){
+        // 先产生 0..val_range-1
+        int32_t x = (int32_t)(rand() % (unsigned)val_range);
+        // 随机决定正负
+        if(with_exception) {
+            // 例如 50% 概率取反
+            if((rand() & 1) == 1) x = -x;
+        } else {
+            // 不带异常 => 大部分正, 也可以小概率负
+            if((rand()%10)==0) x = -x; 
+        }
+        values[i] = x;
+    }
+}
+#ifdef __AVX2__
+void run_testZigzag(unsigned b,
+                    int with_exception,
+                    unsigned TEST_SIZE,
+                    unsigned *raw_values,
+                    unsigned char *encoded_data,
+                    unsigned *decoded1,
+                    unsigned *decoded2)
+{
+    printf("Zigzag 测试: 位宽 b=%u, with_exception=%d\n", b, with_exception);
+
+    // 1) 生成带正负 raw data
+    generate_raw_signed_data_for_zigzag(raw_values, TEST_SIZE, b, with_exception);
+    unsigned encoded_size = p4nzenc256v32(raw_values, TEST_SIZE, encoded_data);
+
+    // 获取编码头部信息(例如起始值等)
+    unsigned start;
+    unsigned char* copy = encoded_data;
+    xvbxget32(copy, start);
+    unsigned char encoded_b = copy[0];  // 编码后的第一个字节为位宽
+    if((encoded_b & 0x40)) {
+        encoded_b &= 0x3f;
+    } else {
+        if(encoded_b & 0x80) {
+            encoded_b &= 0x7f;
+        }
+    }
+    printf("  编码参数: 位宽 b=%u, 起始值 start=%u, 编码大小=%u字节\n", encoded_b, start, encoded_size);
+
+    // 3) decode => two versions for cross-check 
+    //   (here we define "decoded1" from "bitzunpack256v32...??" and "decoded2" from "bitzunpack256scalarv32Zigzag"??)
+    memset(decoded1,0,TEST_SIZE*sizeof(unsigned));
+    memset(decoded2,0,TEST_SIZE*sizeof(unsigned));
+
+    // "decoded1" => maybe  vector version if you have it? e.g. "bitzunpack256v32(in,b, out,??)" 
+    // "decoded2" => scalar version ?
+
+    // for demonstration, we do the same decode to compare:
+    p4nzdec256v32(encoded_data, TEST_SIZE, decoded1);
+    p4nzdec256scalarv32(encoded_data, TEST_SIZE, decoded2);
+
+    // 4) compare mismatch
+    int mismatch=0;
+    for(unsigned i=0;i<TEST_SIZE;i++){
+        if(decoded1[i] != decoded2[i]){
+            if(mismatch<10)
+                printf(" mismatch at i=%u: dec1=%d, dec2=%d\n", i, decoded1[i], decoded2[i]);
+            mismatch++;
+        }
+    }
+    if(mismatch==0){
+        printf(" decode1 & decode2 match!\n");
+        // verify with original
+        int error=0;
+        for(unsigned i=0;i<TEST_SIZE;i++){
+            if(decoded1[i] != raw_values[i]){
+                if(error<10)
+                  printf(" raw mismatch at i=%u: raw=%d, dec=%d\n", i,raw_values[i], decoded1[i]);
+                error++;
+            }
+        }
+        if(error==0) printf(" and match raw data!\n");
+        else printf(" total %d raw mismatch\n", error);
+    } else {
+        printf(" total mismatch=%d\n", mismatch);
+    }
+    printf("\n");
+}
+
+void run_test(unsigned b, int with_exception, unsigned TEST_SIZE,
+              unsigned* raw_values, unsigned char* encoded_data,
+              unsigned* decoded1, unsigned* decoded2) {
+    printf("测试: 位宽 b=%u, 异常%s\n", b, (with_exception ? "有" : "无"));
+
+    // 生成符合当前 b 与异常模式的原始数据
+    generate_raw_data_for_bitwidth(raw_values, TEST_SIZE, b, with_exception);
+
+    unsigned encoded_size;
+    generate_test_data(raw_values, TEST_SIZE, encoded_data, &encoded_size);
+
+    // 获取编码头部信息(例如起始值等)
+    unsigned start;
+    unsigned char* copy = encoded_data;
+    xvbxget32(copy, start);
+    unsigned char encoded_b = copy[0];  // 编码后的第一个字节为位宽
+    if((encoded_b & 0x40)) {
+        encoded_b &= 0x3f;
+    } else {
+        if(encoded_b & 0x80) {
+            encoded_b &= 0x7f;
+        }
+    }
+    printf("  编码参数: 位宽 b=%u, 起始值 start=%u, 编码大小=%u字节\n", encoded_b, start, encoded_size);
+
+    // 清空解码缓冲区
+    memset(decoded1, 0, TEST_SIZE * sizeof(unsigned));
+    memset(decoded2, 0, TEST_SIZE * sizeof(unsigned));
+
+    // 调用两种解码方式
+    p4nd1dec256v32(encoded_data, TEST_SIZE, decoded1);
+    p4nd1dec256scalarv32(encoded_data, TEST_SIZE, decoded2);
+
+    // 比较两个解码结果是否匹配
+    int mismatch = 0;
+    for (unsigned i = 0; i < TEST_SIZE; i++) {
+        if (decoded1[i] != decoded2[i]) {
+            if (mismatch < 10)
+                printf("  不匹配: 索引 %u, 原始值=%u, 原始解码=%u, 标量解码=%u\n",
+                       i, raw_values[i], decoded1[i], decoded2[i]);
+            mismatch++;
+        }
+    }
+    if (mismatch == 0) {
+        printf("  通过: 所有解码值匹配!\n");
+        // 验证解码值与原始数据是否一致
+        int error = 0;
+        for (unsigned i = 0; i < TEST_SIZE && error < 10; i++) {
+            if (decoded1[i] != raw_values[i]) {
+                printf("  编码/解码错误: 索引 %u, 原始值=%u, 解码值=%u\n",
+                       i, raw_values[i], decoded1[i]);
+                error++;
+            }
+        }
+        if (error == 0)
+            printf("  验证通过: 解码结果与原始数据一致\n");
+    } else {
+        printf("  失败: 有 %d 个值不匹配\n", mismatch);
+        printf("  原始数据 (前16个): ");
+        for (unsigned i = 0; i < 16 && i < TEST_SIZE; i++)
+            printf("%u ", raw_values[i]);
+        printf("...\n");
+        printf("  原始解码 (前16个): ");
+        for (unsigned i = 0; i < 16 && i < TEST_SIZE; i++)
+            printf("%u ", decoded1[i]);
+        printf("...\n");
+        printf("  标量解码 (前16个): ");
+        for (unsigned i = 0; i < 16 && i < TEST_SIZE; i++)
+            printf("%u ", decoded2[i]);
+        printf("...\n");
+    }
+    printf("\n");
+}
+
+void testZigZag()
+{
+    const unsigned TEST_SIZE=512; //or512
+    unsigned *raw_values= (unsigned*) malloc(TEST_SIZE*sizeof(unsigned));
+    unsigned *decoded1=  (unsigned*) malloc(TEST_SIZE*sizeof(unsigned));
+    unsigned *decoded2=  (unsigned*) malloc(TEST_SIZE*sizeof(unsigned));
+    unsigned char* encoded_data= (unsigned char*) malloc(TEST_SIZE*4+ 10); //maybe
+
+    srand((unsigned)time(NULL));
+    printf("开始测试 p4nzdec256v32...\n");
+
+    for(unsigned b=0; b<=32; b++){
+        run_testZigzag(b,0, TEST_SIZE, raw_values, encoded_data, decoded1, decoded2);
+        run_testZigzag(b,1, TEST_SIZE, raw_values, encoded_data, decoded1, decoded2);
+    }
+
+    free(raw_values);
+    free(decoded1);
+    free(decoded2);
+    free(encoded_data);
+}
+
+void test_p4nd1dec256v32() {
+    const unsigned TEST_SIZE = 512;
+
+    // 分配缓冲区
+    unsigned* raw_values   = (unsigned*)malloc(TEST_SIZE * sizeof(unsigned));
+    unsigned char* encoded_data = (unsigned char*)malloc(TEST_SIZE * sizeof(unsigned) * 2);
+    unsigned* decoded1     = (unsigned*)malloc(TEST_SIZE * sizeof(unsigned));
+    unsigned* decoded2     = (unsigned*)malloc(TEST_SIZE * sizeof(unsigned));
+
+    srand((unsigned)time(NULL));
+    printf("开始测试 p4nd1dec256v32...\n");
+
+    // 对 b = 0 到 31 测试两种模式:无异常和有异常
+    for (unsigned b = 0; b < 32; b++) {
+        run_test(b, 0, TEST_SIZE, raw_values, encoded_data, decoded1, decoded2);
+        run_test(b, 1, TEST_SIZE, raw_values, encoded_data, decoded1, decoded2);
+    }
+    // 对 b == 32 只测试无异常情况
+    run_test(32, 0, TEST_SIZE, raw_values, encoded_data, decoded1, decoded2);
+
+    free(raw_values);
+    free(encoded_data);
+    free(decoded1);
+    free(decoded2);
+
+    printf("测试完成!\n");
+}
+#endif
+int main() {
+#ifdef __AVX2__
+    test_p4nd1dec256v32();
+    testZigZag();
+    //test_until_b1_achieved_improved();
+#endif
+    return 0;
+}
diff --git a/src/ext/for/transpose.c b/src/ext/for/transpose.c
index 5e50c80e48a..4402ea2b00e 100644
--- a/src/ext/for/transpose.c
+++ b/src/ext/for/transpose.c
@@ -130,7 +130,7 @@
 #define TPDEC256V   tp4dec256v
 #include "transpose.c"
 
-#if !defined(SSE2_ON) && !defined(AVX2_ON)
+#if !defined(__AVX2__) && !defined(__SSE3__) 
 //--------------------- CPU detection -------------------------------------------
   #if _MSC_VER >=1300
 #include <intrin.h>
@@ -431,7 +431,7 @@ void tp4dec(unsigned char *in, unsigned n, unsigned char *out, unsigned esize) {
 #define SID(p,i)  (p+=stride)
 //#define SID(_p_,_i_)  (_p_+ _i_*stride)
 
-#if !defined(SSE2_ON) && !defined(AVX2_ON) //--------------------------------------- plain -------------------------------------------------------------------
+#if !defined(__AVX2__) && !defined(__SSE3__) //--------------------------------------- plain -------------------------------------------------------------------
 
   #if STRIDE == ESIZE
 void TEMPLATE2(TPENC, ESIZE)(unsigned char *in, unsigned n, unsigned char *out) {
diff --git a/src/ext/for/vp4.h b/src/ext/for/vp4.h
index fae28df8d45..39460b614c4 100644
--- a/src/ext/for/vp4.h
+++ b/src/ext/for/vp4.h
@@ -99,6 +99,7 @@ size_t p4nd1dec32(    unsigned char *__restrict in, size_t n, uint32_t *__restri
 size_t p4nd1dec128v16(unsigned char *__restrict in, size_t n, uint16_t *__restrict out);
 size_t p4nd1dec128v32(unsigned char *__restrict in, size_t n, uint32_t *__restrict out);
 size_t p4nd1dec256v32(unsigned char *__restrict in, size_t n, uint32_t *__restrict out);
+size_t p4nd1dec256scalarv32(unsigned char* __restrict in, size_t n, uint32_t* __restrict out);
 size_t p4nd1dec64(    unsigned char *__restrict in, size_t n, uint64_t *__restrict out);
 //Zigzag
 size_t p4nzdec8(      unsigned char *__restrict in, size_t n, uint8_t  *__restrict out);
@@ -107,6 +108,7 @@ size_t p4nzdec32(     unsigned char *__restrict in, size_t n, uint32_t *__restri
 size_t p4nzdec128v16( unsigned char *__restrict in, size_t n, uint16_t *__restrict out);
 size_t p4nzdec128v32( unsigned char *__restrict in, size_t n, uint32_t *__restrict out);
 size_t p4nzdec256v32( unsigned char *__restrict in, size_t n, uint32_t *__restrict out);
+size_t p4nzdec256scalarv32(unsigned char* __restrict in, size_t n, uint32_t* __restrict out);
 size_t p4nzdec64(     unsigned char *__restrict in, size_t n, uint64_t *__restrict out);
 
 //************** Low level API - n limited to 128/256 ***************************************
diff --git a/src/ext/for/vp4c.c b/src/ext/for/vp4c.c
index f9237f252de..fc230150431 100644
--- a/src/ext/for/vp4c.c
+++ b/src/ext/for/vp4c.c
@@ -41,7 +41,7 @@
 
 #define HYBRID 1 // Hybrid TurboPFor : 0=fixed bit packing, 1=fixed BP+Variable byte
 
-  #if !defined(SSE2_ON) && !defined(AVX2_ON)
+  #ifndef __AVX2__
 #define _P4BITS _p4bits
 #define  P4BITS _p4bits
 #define _P4ENC   _p4enc
@@ -155,7 +155,8 @@ size_t p4nsdec16(unsigned char *in, size_t n, uint16_t *out) { uint16_t  *op,sta
 size_t p4nsdec32(unsigned char *in, size_t n, uint32_t *out) { uint32_t  *op,start; P4NDDEC(in, n, out, 128, 32, p4sdec); }
 size_t p4nsdec64(unsigned char *in, size_t n, uint64_t *out) { uint64_t  *op,start; P4NDDEC(in, n, out, 128, 64, p4sdec); }
 #undef _P4BITS
-  #elif defined(__AVX2__)
+#endif
+#if defined(__AVX2__)
 #define  BITDELTA bitdienc
 #define HYBRID 1
 #define P4BITS _p4bits
diff --git a/src/ext/for/vp4d.c b/src/ext/for/vp4d.c
index a255fc2a2f0..5566073fd66 100644
--- a/src/ext/for/vp4d.c
+++ b/src/ext/for/vp4d.c
@@ -95,7 +95,7 @@ extern char _shuffle_16[256][16];
 #include "vp4d.c"
 
 #undef  BITUNDD
-  #elif !defined(SSE2_ON) && !defined(AVX2_ON)
+  #elif defined(__SSSE3__) || defined(__ARM_NEON)
 
 #define _P4DEC      _p4dec
 #define  P4DEC       p4dec
@@ -177,7 +177,6 @@ extern char _shuffle_16[256][16];
 #undef USIZE
 #undef DELTA
 
-  #elif defined(__SSSE3__) || defined(__ARM_NEON)
 #define VSIZE 128
 #define P4DELTA(a)
 #define P4DELTA_(a)
@@ -253,6 +252,36 @@ extern char _shuffle_16[256][16];
 #define  BITUNPACK     bitunpack256w
 #define  BITUNPACKD    bitunpack256w
 #define  _BITUNPACKD  _bitunpack256w
+#include "vp4d.c"
+#define P4DELTA(a) ,a
+#define P4DELTA_(a) a
+#define DELTA
+
+#undef _P4DEC
+#undef  P4DEC
+#undef  P4NDEC
+#undef  BITUNPACKD
+#undef _BITUNPACKD
+
+
+#define _P4DEC        _p4d1dec256scalarv
+#define  P4DEC         p4d1dec256scalarv
+#define  P4NDEC        p4nd1dec256scalarv
+#define  P4NDECS       p4d1dec
+#define  BITUNPACK     bitunpack256scalarv
+#define  BITUNPACKD    bitd1unpack256scalarv
+#define  _BITUNPACKD  _bitd1unpack256scalarv
+#define  BITUNDD       bitd1dec
+#include "vp4d.c"
+
+#define _P4DEC        _p4zdec256scalarv
+#define  P4DEC         p4zdec256scalarv
+#define  P4NDEC        p4nzdec256scalarv
+#define  P4NDECS       p4zdec
+#define  BITUNPACKD    bitzunpack256scalarv
+#define  _BITUNPACKD  _bitzunpack256scalarv
+#define  BITUNDD       bitzdec
+#define USIZE 32
 #include "vp4d.c"
   #endif
 #undef  DELTA
diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt
index 8fe1e9b7162..6b6239dfaf1 100644
--- a/src/test/CMakeLists.txt
+++ b/src/test/CMakeLists.txt
@@ -16,7 +16,7 @@ SOURCE_GROUP("search" ./search/*)
 SOURCE_GROUP("search-spans" ./search/spans/*)
 SOURCE_GROUP("store" ./store/*)
 SOURCE_GROUP("util" ./util/*)
-
+INCLUDE_DIRECTORIES( ${clucene_SOURCE_DIR}/src/ext/* )
 IF (BUILD_CONTRIBS_LIB)
     SET(test_contribs_lib_files ./contribs-lib/analysis/testChinese.cpp)
     SET(EXTRA_LIBS ${EXTRA_LIBS} clucene-contribs-lib)
@@ -107,6 +107,7 @@ SET(test_files ./tests.cpp
         ./util/TestStrConvert.cpp
         ./query/TestMultiPhraseQuery.cpp
         ./store/TestUTF8Chars.cpp
+        ./store/testPFOR.cpp
         ${test_HEADERS})
 IF (USE_SHARED_OBJECT_FILES)
     GET_SHARED_FILES(clucene_shared_Files)
diff --git a/src/test/data/pfor_p4ndx_compat_gen_by_old_version_arm.dat b/src/test/data/pfor_p4ndx_compat_gen_by_old_version_arm.dat
new file mode 100644
index 00000000000..e823aef9968
Binary files /dev/null and b/src/test/data/pfor_p4ndx_compat_gen_by_old_version_arm.dat differ
diff --git a/src/test/data/pfor_p4ndx_compat_gen_by_old_version_x86_64.dat b/src/test/data/pfor_p4ndx_compat_gen_by_old_version_x86_64.dat
new file mode 100644
index 00000000000..a27177e3090
Binary files /dev/null and b/src/test/data/pfor_p4ndx_compat_gen_by_old_version_x86_64.dat differ
diff --git a/src/test/store/testPFOR.cpp b/src/test/store/testPFOR.cpp
new file mode 100644
index 00000000000..f02cfd9be4e
--- /dev/null
+++ b/src/test/store/testPFOR.cpp
@@ -0,0 +1,652 @@
+#include "test.h"
+
+#include <memory.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <cstdint>
+#include <cstdio>
+#include <vector>
+
+#include "CLucene/index/CodeMode.h"
+#include "CLucene/store/FSDirectory.h"
+#include "CLucene/store/IndexInput.h"
+#include "CLucene/store/IndexOutput.h"
+#include "CLucene/util/PFORUtil.h"
+#include "CuTest.h"
+#include "for/vp4.h"
+
+using namespace lucene::store;
+using namespace lucene::util;
+// Add a helper macro for printing more detailed error messages when assertions fail
+#define CuAssertTrueWithMessage(tc, message, condition) \
+    do {                                                \
+        if (!(condition)) {                             \
+            printf("Assertion failed: %s\n", message);  \
+        }                                               \
+        CuAssertTrue(tc, condition);                    \
+    } while (0)
+
+static const uint64_t g_pow10[] = {
+        1ULL,          // 10^0
+        10ULL,         // 10^1
+        100ULL,        // 10^2
+        1000ULL,       // 10^3
+        10000ULL,      // 10^4
+        100000ULL,     // 10^5
+        1000000ULL,    // 10^6
+        10000000ULL,   // 10^7
+        100000000ULL,  // 10^8
+        1000000000ULL, // 10^9
+        10000000000ULL // 10^10
+};
+
+// Calculate 10^(floor(b/3)), if exceeds g_pow10 range, additional check can be added
+static inline uint64_t get_pow10_for_b(unsigned b) {
+    // floor(b/3)
+    unsigned idx = b / 3;
+    if (idx >= sizeof(g_pow10) / sizeof(g_pow10[0])) {
+        // Exceeds the maximum 10^10 in the preset table, return 10^10 or handle accordingly
+        return g_pow10[10];
+    }
+    return g_pow10[idx];
+}
+
+// Calculate 2^b
+static inline unsigned power2(unsigned b) {
+    // (1U << b) might overflow when b=32, you can add additional check if needed
+    return (1U << b);
+}
+
+/**
+ * @param values        Output array
+ * @param n             Number of data points to generate
+ * @param b             Current bit width
+ * @param with_exception 0=no exceptions, 1=with exceptions
+ */
+void generate_raw_data_for_bitwidth(unsigned* values, unsigned n, unsigned b, int with_exception) {
+    if (n == 0) return;
+
+    if (!with_exception) {
+        // =====================================
+        //         No exception mode: Increasing sequence
+        // =====================================
+        // 1) First generate a random base value
+        unsigned base = rand() % 1000;
+        values[0] = base;
+
+        // 2) Determine the "maximum increment range" based on b
+        unsigned inc_range;
+        if (b < 4) {
+            // b=0 => 2^0=1, b=1 =>2, b=2 =>4, b=3=>8
+            inc_range = power2(b);
+        } else {
+            // b>=4 => use 10^(floor(b/3)) => 10,100,1000,...
+            uint64_t r = get_pow10_for_b(b);
+            // Check if r exceeds unsigned range
+            // If test scenarios won't be too large, can directly cast to unsigned
+            if (r > 0xFFFFFFFFULL) {
+                r = 0xFFFFFFFFULL; // Avoid overflow
+            }
+            inc_range = (unsigned)r;
+        }
+
+        // 3) Generate increasing sequence
+        for (unsigned i = 1; i < n; i++) {
+            // +1 to avoid 0 increment
+            unsigned inc = 1 + rand() % inc_range;
+            base += inc;
+            values[i] = base;
+        }
+
+    } else {
+        // =====================================
+        //         Exception mode: Direct random
+        // =====================================
+
+        // Observed pattern:
+        //  - b=0 => rand()%2
+        //  - b=2 => rand()%4
+        //  - b=3 => rand()%10
+        //  - b=7 => rand()%100
+        //  - b=10 => rand()%1000
+        //  - b=13 => rand()%10000
+        //  => Pattern: when b >= 3 use 10^(floor(b/3)); when b < 3 use special handling
+
+        uint64_t val_range = 0; // Use 64-bit temporarily, convert back to unsigned later
+
+        if (b == 0) {
+            val_range = 2; // 0..1
+        } else if (b == 1) {
+            // No specific example for b=1, assuming same as b=0 => range=2
+            val_range = 2; // 0..1
+        } else if (b == 2) {
+            val_range = 4; // 0..3
+        } else {
+            // b>=3 => use 10^(floor(b/3))
+            val_range = get_pow10_for_b(b);
+            // Check if exceeds unsigned range
+            if (val_range > 0xFFFFFFFFULL) {
+                val_range = 0xFFFFFFFFULL;
+            }
+        }
+
+        // Direct random generation
+        for (unsigned i = 0; i < n; i++) {
+            unsigned x = (unsigned)(rand() % (unsigned)val_range);
+            values[i] = x;
+        }
+    }
+}
+
+void test_pfor_has_prox(CuTest* tc) {
+    const unsigned TEST_SIZE = 512;
+    const char* testFileName = "pfor.dat";
+
+    // Allocate buffers
+    std::vector<unsigned> docDeltaBuffer(TEST_SIZE);
+    std::vector<unsigned> freqBuffer(TEST_SIZE);
+    std::vector<unsigned> encoded_data(TEST_SIZE * 2);
+    std::vector<unsigned> decoded1(TEST_SIZE);
+    std::vector<unsigned> decoded2(TEST_SIZE);
+
+    srand((unsigned)time(NULL));
+    printf("Starting test p4nd1dec256v32...\n");
+
+    {
+        generate_raw_data_for_bitwidth(docDeltaBuffer.data(), TEST_SIZE, 32, 0);
+        auto* dir = lucene::store::FSDirectory::getDirectory("./");
+
+        auto* output = dir->createOutput(testFileName);
+
+        PforUtil::pfor_encode(output, docDeltaBuffer, freqBuffer, true);
+        output->close();
+        _CLDELETE(output);
+        dir->close();
+        _CLDELETE(dir);
+    }
+    {
+        IndexInput* input = nullptr;
+        CLuceneError error;
+        auto* dir = lucene::store::FSDirectory::getDirectory("./");
+        bool result = dir->openInput(testFileName, input, error);
+        PforUtil::pfor_decode(input, decoded1, decoded2, true, false);
+        for (size_t i = 0; i < TEST_SIZE; i++) {
+            CuAssertIntEquals(tc, _T("docDeltaBuffer[%zu] != decoded1[%zu]"), docDeltaBuffer[i],
+                              decoded1[i]);
+            CuAssertIntEquals(tc, _T("freqBuffer[%zu] != decoded2[%zu]"), freqBuffer[i],
+                              decoded2[i]);
+        }
+        input->close();
+        _CLDELETE(input);
+        dir->close();
+        _CLDELETE(dir);
+    }
+    printf("Test completed!\n");
+}
+
+void test_pfor_no_prox(CuTest* tc) {
+    const unsigned TEST_SIZE = 512;
+    const char* testFileName = "pfor.dat";
+
+    // Allocate buffers
+    std::vector<unsigned> docDeltaBuffer(TEST_SIZE);
+    std::vector<unsigned> freqBuffer(TEST_SIZE);
+    std::vector<unsigned> encoded_data(TEST_SIZE * 2);
+    std::vector<unsigned> decoded1(TEST_SIZE);
+    std::vector<unsigned> decoded2(TEST_SIZE);
+
+    srand((unsigned)time(NULL));
+    printf("Starting test p4nd1dec256v32...\n");
+
+    {
+        generate_raw_data_for_bitwidth(docDeltaBuffer.data(), TEST_SIZE, 32, 0);
+        auto* dir = lucene::store::FSDirectory::getDirectory("./");
+
+        auto* output = dir->createOutput(testFileName);
+
+        PforUtil::pfor_encode(output, docDeltaBuffer, freqBuffer, false);
+        output->close();
+        _CLDELETE(output);
+        dir->close();
+        _CLDELETE(dir);
+    }
+    {
+        IndexInput* input = nullptr;
+        CLuceneError error;
+        auto* dir = lucene::store::FSDirectory::getDirectory("./");
+        bool result = dir->openInput(testFileName, input, error);
+        PforUtil::pfor_decode(input, decoded1, decoded2, false, false);
+        for (size_t i = 0; i < TEST_SIZE; i++) {
+            CuAssertIntEquals(tc, _T("docDeltaBuffer[%zu] != decoded1[%zu]"), docDeltaBuffer[i],
+                              decoded1[i]);
+            CuAssertIntEquals(tc, _T("freqBuffer[%zu] != decoded2[%zu]"), freqBuffer[i],
+                              decoded2[i]);
+        }
+        input->close();
+        _CLDELETE(input);
+        dir->close();
+        _CLDELETE(dir);
+    }
+    printf("Test completed!\n");
+}
+
+// Test the compatibility of P4DEC and P4ENC
+void test_p4dec_p4enc_compat(CuTest* tc) {
+    const unsigned TEST_SIZE = 512;
+    const char* testFileName = "pfor_p4enc.dat";
+
+    // Allocate buffers
+    std::vector<uint32_t> originalData(TEST_SIZE);
+    std::vector<uint32_t> decodedData(TEST_SIZE);
+    std::vector<uint32_t> freqs(TEST_SIZE);
+    std::vector<uint32_t> decodedFreqs(TEST_SIZE);
+
+    srand((unsigned)time(NULL));
+    printf("Testing P4ENC and pfor_decode compatibility...\n");
+
+    // Generate test data with delta encoding pattern (increasing values)
+    generate_raw_data_for_bitwidth(originalData.data(), TEST_SIZE, 32, 0);
+    generate_raw_data_for_bitwidth(freqs.data(), TEST_SIZE, 32, 1);
+
+    auto encode = [](IndexOutput* out, std::vector<uint32_t>& buffer, bool isDoc) {
+        std::vector<uint8_t> compress(4 * buffer.size() + PFOR_BLOCK_SIZE);
+        size_t size = 0;
+        if (isDoc) {
+            size = P4ENC(buffer.data(), buffer.size(), compress.data());
+        } else {
+            size = P4NZENC(buffer.data(), buffer.size(), compress.data());
+        }
+        out->writeVInt(size);
+        out->writeBytes(reinterpret_cast<const uint8_t*>(compress.data()), size);
+    };
+    // Step 1: Encode data using P4ENC and write to file
+    {
+        auto* dir = lucene::store::FSDirectory::getDirectory("./");
+        auto* output = dir->createOutput(testFileName);
+
+        // Write encoding mode and size
+        output->writeByte((char)lucene::index::CodeMode::kPfor);
+        output->writeVInt(TEST_SIZE);
+
+        // Encode and write data
+        encode(output, originalData, true);
+        encode(output, freqs, false);
+
+        output->close();
+        _CLDELETE(output);
+        dir->close();
+        _CLDELETE(dir);
+    }
+
+    // Step 2: Decode data using pfor_decode
+    {
+        IndexInput* input = nullptr;
+        CLuceneError error;
+        auto* dir = lucene::store::FSDirectory::getDirectory("./");
+        bool result = dir->openInput(testFileName, input, error);
+
+        // Use pfor_decode to decode data (no proxy has_prox=false, compatibleRead=false)
+        uint32_t decoded_size =
+                PforUtil::pfor_decode(input, decodedData, decodedFreqs, true, false);
+
+        // Verify decoded size
+        CuAssertIntEquals(tc, _T("Decoded size mismatch"), TEST_SIZE, decoded_size);
+
+        // Verify decoded data matches original data
+        for (size_t i = 0; i < TEST_SIZE; i++) {
+            //printf("freqs[%zu] = %u, decodedFreqs[%zu] = %u\n", i, freqs[i], i, decodedFreqs[i]);
+            //printf("originalData[%zu] = %u, decodedData[%zu] = %u\n", i, originalData[i], i, decodedData[i]);
+            CuAssertTrueWithMessage(tc, "Decoded doc doesn't match original",
+                                    originalData[i] == decodedData[i]);
+            CuAssertTrueWithMessage(tc, "Decoded freq doesn't match original",
+                                    freqs[i] == decodedFreqs[i]);
+        }
+
+        input->close();
+        _CLDELETE(input);
+        dir->close();
+        _CLDELETE(dir);
+    }
+
+    printf("P4ENC/pfor_decode compatibility test completed successfully!\n");
+}
+
+// Test cross-platform compatibility for P4DEC/P4ENC
+void test_cross_platform_compat(CuTest* tc) {
+    const unsigned TEST_SIZE = 512;
+    const char* testFileName = "pfor_cross_platform.dat";
+
+    // Allocate buffers
+    std::vector<uint32_t> originalData(TEST_SIZE);
+    std::vector<uint32_t> decodedData(TEST_SIZE);
+
+    srand((unsigned)time(NULL));
+    printf("Testing cross-platform compatibility...\n");
+
+    // Generate test data with different patterns
+    for (unsigned i = 0; i < TEST_SIZE; i++) {
+        // Mix of small and large values to test different bit widths
+        if (i % 10 == 0) {
+            originalData[i] = rand() % 1000000; // Occasional large value
+        } else {
+            originalData[i] = rand() % 100; // Mostly small values
+        }
+    }
+
+    // Part 1: Write encoded data to file using PFOR encoding
+    {
+        auto* dir = lucene::store::FSDirectory::getDirectory("./");
+        auto* output = dir->createOutput(testFileName);
+
+        // Write encoding mode and size
+        output->writeByte((char)lucene::index::CodeMode::kPfor);
+        output->writeVInt(TEST_SIZE);
+
+        // Encode and write the data
+        std::vector<uint8_t> compress(4 * TEST_SIZE + PFOR_BLOCK_SIZE);
+        size_t size = ::P4ENC(originalData.data(), TEST_SIZE, compress.data());
+        output->writeVInt(size);
+        output->writeBytes(reinterpret_cast<const uint8_t*>(compress.data()), size);
+
+        output->close();
+        _CLDELETE(output);
+        dir->close();
+        _CLDELETE(dir);
+    }
+
+    // Part 2: Read encoded data from file and decode it with compatibleRead=true
+    {
+        IndexInput* input = nullptr;
+        CLuceneError error;
+        auto* dir = lucene::store::FSDirectory::getDirectory("./");
+        bool result = dir->openInput(testFileName, input, error);
+
+        // Verify the encoded format
+        char mode = input->readByte();
+        uint32_t arraySize = input->readVInt();
+        CuAssertIntEquals(tc, _T("Array size mismatch"), TEST_SIZE, arraySize);
+
+        // Read, decode and verify
+        uint32_t SerializedSize = input->readVInt();
+        std::vector<uint8_t> buf(SerializedSize + PFOR_BLOCK_SIZE);
+        input->readBytes(buf.data(), SerializedSize);
+
+        // Use P4DEC for decoding, simulating cross-platform read
+        size_t decoded_size = ::P4DEC(buf.data(), arraySize, decodedData.data());
+
+        // Verify decoded data matches original
+        for (size_t i = 0; i < TEST_SIZE; i++) {
+            CuAssertIntEquals(tc, _T("Cross-platform decoded data mismatch at %zu"),
+                              originalData[i], decodedData[i]);
+        }
+
+        input->close();
+        _CLDELETE(input);
+        dir->close();
+        _CLDELETE(dir);
+    }
+
+    printf("Cross-platform compatibility test completed successfully!\n");
+}
+
+// Test compatibility between encoded by ARM old version data and decoded by x86 new version with compatible mode
+void test_p4ndx_compatibility(CuTest* tc) {
+    const unsigned TEST_SIZE = 512;
+    const char* testFileName1 = "pfor_p4ndx_compat_gen_by_old_version_x86_64.dat";
+    const char* testFileName2 = "pfor_p4ndx_compat_gen_by_old_version_arm.dat";
+
+    // Allocate buffers
+    std::vector<uint32_t> docDeltaBuffer = {
+            635,    1188,   1795,   2109,   2694,   3612,   3714,   4511,   5072,   5352,   5526,
+            5894,   6706,   6891,   6979,   7080,   7586,   7789,   8530,   9065,   9704,   9949,
+            10377,  10678,  11516,  11921,  12226,  13133,  13417,  13854,  14215,  14486,  15476,
+            16444,  17380,  18306,  19191,  19580,  20302,  21099,  21119,  22014,  22178,  22361,
+            22440,  23043,  23326,  24262,  25067,  25443,  26265,  27061,  27681,  27931,  28027,
+            28837,  28843,  29595,  29663,  29953,  30494,  30922,  31834,  32364,  33111,  33311,
+            33766,  34749,  35689,  36217,  36348,  36660,  37083,  37378,  37872,  38725,  39622,
+            40399,  40540,  40594,  41098,  42060,  42909,  43032,  43243,  43539,  43823,  44040,
+            44439,  44790,  45648,  46587,  46718,  46839,  47307,  47536,  48208,  48482,  49046,
+            49658,  50460,  51154,  51429,  52005,  52993,  53761,  54541,  54778,  55674,  56594,
+            57236,  57635,  58517,  59007,  59881,  60325,  60462,  60971,  60983,  61518,  62378,
+            63247,  64073,  64415,  64757,  65050,  65620,  65633,  66552,  66685,  67661,  67733,
+            67912,  68514,  69161,  69327,  69697,  70475,  71229,  71846,  71896,  72291,  72659,
+            72942,  73178,  73419,  74145,  74517,  75266,  75356,  75615,  76575,  77533,  77617,
+            77918,  78569,  79297,  79520,  79536,  80534,  81241,  81584,  81653,  82538,  83483,
+            83550,  83601,  84267,  85112,  85268,  85550,  86444,  87347,  87996,  88172,  88310,
+            88551,  88804,  89666,  90008,  90350,  90822,  91475,  92127,  93034,  93340,  93994,
+            94628,  95156,  95825,  96457,  96691,  96703,  96755,  96874,  97182,  97301,  97822,
+            98795,  99758,  100434, 101040, 101248, 101826, 102081, 102816, 102884, 103731, 104070,
+            104999, 105539, 106220, 106972, 107165, 107849, 108507, 109005, 109342, 109633, 109658,
+            110016, 110290, 110900, 111621, 111947, 112675, 112703, 113499, 114099, 114451, 115209,
+            115837, 116794, 117111, 117668, 118231, 118634, 119258, 119668, 120409, 121313, 122262,
+            123035, 123690, 124183, 124991, 125303, 126293, 126790, 127745, 128111, 128965, 129545,
+            129873, 130447, 130704, 130759, 131712, 131764, 131771, 132075, 132236, 132870, 133482,
+            134311, 134501, 134676, 134907, 135073, 136009, 136333, 136402, 137286, 137734, 137810,
+            138539, 138795, 139534, 139604, 140356, 140401, 141189, 142146, 142771, 142886, 143416,
+            143649, 144170, 145004, 145289, 145816, 146305, 146750, 146910, 147010, 147636, 147986,
+            148612, 149468, 150335, 150896, 151427, 152362, 153159, 154138, 154500, 155025, 155259,
+            155360, 155954, 156291, 156436, 157169, 157462, 157583, 158430, 158604, 158958, 159326,
+            159333, 159971, 160865, 161712, 162146, 162552, 162850, 162909, 163016, 163940, 164207,
+            165180, 165664, 166461, 167368, 167648, 168423, 168692, 168848, 169208, 169929, 170679,
+            171375, 172240, 172722, 173710, 174696, 175377, 175890, 176581, 176629, 177148, 177476,
+            177769, 178486, 178599, 179297, 179312, 179836, 180640, 180930, 181072, 181848, 182621,
+            183559, 183594, 183999, 184064, 184719, 185279, 186055, 186430, 187091, 187915, 188154,
+            188649, 188812, 189388, 189563, 190239, 190505, 190727, 190921, 191866, 192732, 192995,
+            193405, 193969, 194246, 195179, 195546, 196464, 196890, 197385, 198075, 198790, 199319,
+            199765, 199896, 200079, 200085, 200992, 201549, 202215, 202945, 203092, 203252, 204144,
+            204219, 204905, 205472, 205812, 206071, 206184, 206821, 206946, 207673, 207719, 208407,
+            208762, 209092, 209498, 209770, 209877, 210129, 210442, 211263, 212043, 212802, 213754,
+            214068, 214832, 215690, 215912, 216693, 217632, 218001, 218942, 219124, 219567, 220545,
+            220646, 220780, 221017, 221582, 222352, 223065, 223356, 223523, 224275, 224920, 225768,
+            225925, 226841, 227795, 228556, 228784, 229559, 230099, 231085, 231163, 231369, 231470,
+            231757, 232184, 233066, 233291, 234086, 234260, 235018, 235607, 235758, 236616, 237339,
+            238078, 238500, 239344, 239795, 239859, 240222, 240424, 241132, 241694, 242405, 243380,
+            243896, 244367, 244922, 245564, 245926, 246818, 247537, 248104, 249097, 249102, 249448,
+            249674, 250255, 250395, 250794, 251132, 251213, 252114, 252662, 252817, 253457, 253778,
+            254128, 254570, 254955, 255667, 256311, 256755};
+    std::vector<uint32_t> freqBuffer = {
+            73, 5,  18, 40, 27, 24, 33, 88, 15, 51, 7,  59, 7,  4,  84, 39, 43, 34, 28, 75, 35, 75,
+            29, 26, 48, 79, 67, 32, 42, 10, 75, 67, 67, 45, 7,  94, 21, 40, 35, 37, 43, 94, 48, 2,
+            98, 85, 41, 93, 19, 22, 69, 54, 49, 50, 32, 97, 81, 0,  29, 24, 62, 57, 91, 30, 54, 51,
+            76, 76, 91, 63, 65, 87, 57, 13, 89, 7,  98, 31, 1,  70, 5,  22, 76, 54, 24, 9,  52, 6,
+            9,  33, 82, 71, 4,  2,  25, 53, 97, 76, 30, 25, 20, 93, 90, 7,  3,  55, 96, 10, 6,  79,
+            63, 76, 84, 85, 52, 39, 10, 13, 91, 68, 22, 76, 50, 46, 19, 75, 99, 16, 4,  81, 41, 24,
+            27, 83, 31, 30, 38, 27, 92, 44, 59, 56, 20, 43, 93, 25, 82, 55, 38, 25, 23, 13, 2,  25,
+            59, 21, 53, 10, 89, 57, 44, 82, 81, 71, 17, 64, 53, 55, 43, 97, 0,  2,  53, 72, 46, 99,
+            49, 28, 54, 40, 6,  30, 53, 8,  5,  5,  64, 81, 60, 74, 22, 17, 18, 4,  50, 41, 21, 14,
+            94, 28, 58, 92, 80, 60, 97, 5,  58, 96, 54, 87, 3,  46, 45, 33, 99, 53, 40, 15, 86, 1,
+            90, 8,  18, 60, 64, 21, 54, 37, 87, 48, 65, 45, 92, 98, 58, 42, 3,  16, 90, 9,  55, 93,
+            56, 0,  26, 7,  5,  67, 23, 91, 20, 65, 99, 38, 77, 15, 11, 31, 52, 99, 32, 18, 96, 76,
+            68, 54, 18, 71, 23, 9,  32, 78, 2,  88, 31, 81, 48, 88, 0,  23, 80, 20, 40, 31, 10, 17,
+            47, 22, 49, 99, 21, 81, 69, 17, 57, 37, 24, 28, 60, 47, 37, 93, 77, 91, 33, 8,  72, 33,
+            97, 72, 56, 29, 92, 96, 60, 55, 14, 59, 77, 15, 11, 98, 48, 32, 67, 57, 70, 91, 85, 82,
+            90, 74, 75, 68, 66, 61, 28, 90, 94, 77, 15, 3,  6,  59, 99, 19, 14, 65, 30, 91, 32, 41,
+            41, 80, 74, 9,  38, 96, 52, 75, 78, 43, 2,  6,  63, 68, 19, 91, 10, 13, 69, 25, 16, 27,
+            85, 68, 98, 99, 33, 81, 91, 66, 74, 84, 98, 48, 93, 88, 96, 98, 16, 27, 93, 18, 85, 56,
+            38, 4,  47, 48, 69, 68, 74, 38, 48, 11, 6,  98, 10, 91, 31, 53, 9,  6,  38, 60, 54, 83,
+            48, 3,  33, 64, 30, 26, 34, 67, 82, 72, 71, 82, 21, 92, 2,  47, 30, 50, 58, 88, 1,  20,
+            32, 32, 74, 93, 38, 64, 53, 45, 99, 54, 48, 33, 70, 30, 59, 5,  97, 94, 29, 20, 76, 50,
+            12, 78, 49, 95, 81, 7,  83, 34, 80, 67, 18, 6,  13, 57, 70, 18, 54, 69, 72, 54, 2,  95,
+            36, 14, 52, 33, 8,  81, 5,  36, 84, 17, 14, 33, 12, 47, 93, 48, 81, 25, 67, 52, 31, 80,
+            9,  1,  99, 15, 22, 23, 69, 25};
+    std::vector<uint32_t> decodedDocs(TEST_SIZE);
+    std::vector<uint32_t> decodedFreqs(TEST_SIZE);
+
+    srand((unsigned)time(NULL));
+    printf("Testing pfor_decode compatibility...\n");
+
+#if defined(__AVX2__)
+    // Part 2: Decode data using pfor_decode with compatible mode (compatibleRead=true)
+    {
+        IndexInput* input = nullptr;
+        CLuceneError error;
+        auto* dir = lucene::store::FSDirectory::getDirectory(clucene_data_location);
+        bool result = dir->openInput(testFileName2, input, error);
+
+        // Use pfor_decode with compatibleRead=true
+        uint32_t decoded_size = PforUtil::pfor_decode(input, decodedDocs, decodedFreqs, true, true);
+
+        // Verify decoded size
+        CuAssertIntEquals(tc, _T("Decoded size mismatch"), TEST_SIZE, decoded_size);
+
+        // Verify decoded data matches original
+        for (size_t i = 0; i < TEST_SIZE; i++) {
+            CuAssertTrueWithMessage(tc, "Decoded doc doesn't match original",
+                                    docDeltaBuffer[i] == decodedDocs[i]);
+            CuAssertTrueWithMessage(tc, "Decoded freq doesn't match original",
+                                    freqBuffer[i] == decodedFreqs[i]);
+        }
+
+        input->close();
+        _CLDELETE(input);
+        dir->close();
+        _CLDELETE(dir);
+    }
+#elif defined(__ARM_NEON) || defined(__SSSE3__)
+    {
+        IndexInput* input = nullptr;
+        CLuceneError error;
+        auto* dir = lucene::store::FSDirectory::getDirectory(clucene_data_location);
+        bool result = dir->openInput(testFileName1, input, error);
+
+        // Use pfor_decode with compatibleRead=true
+        uint32_t decoded_size = PforUtil::pfor_decode(input, decodedDocs, decodedFreqs, true, true);
+
+        // Verify decoded size
+        CuAssertIntEquals(tc, _T("Decoded size mismatch"), TEST_SIZE, decoded_size);
+
+        // Verify decoded data matches original
+        for (size_t i = 0; i < TEST_SIZE; i++) {
+            CuAssertTrueWithMessage(tc, "Decoded doc doesn't match original",
+                                    docDeltaBuffer[i] == decodedDocs[i]);
+            CuAssertTrueWithMessage(tc, "Decoded freq doesn't match original",
+                                    freqBuffer[i] == decodedFreqs[i]);
+        }
+
+        input->close();
+        _CLDELETE(input);
+        dir->close();
+        _CLDELETE(dir);
+    }
+#endif
+    printf("compatibility test completed successfully!\n");
+}
+
+//generate random data within 10
+void generate_random_data(std::vector<uint32_t>& data, unsigned size) {
+    srand((unsigned)time(NULL));
+    data[0] = 1;
+    for (unsigned i = 1; i < size; i++) {
+        data[i] = rand() % 10;
+    }
+}
+
+// Helper function for testing encodePos and decodePos functionality for specific data size
+void test_encodePos_with_size(CuTest* tc, unsigned TEST_SIZE, std::vector<uint32_t>& inputData) {
+    printf("Testing data size: %u\n", TEST_SIZE);
+
+    const char* testFileName = "encodePos.dat";
+
+    // Allocate buffers
+    std::vector<uint32_t> decodedData;
+
+    // Use provided test data
+    printf("Using provided test data\n");
+
+    {
+        auto* dir = lucene::store::FSDirectory::getDirectory("./");
+        auto* output = dir->createOutput(testFileName);
+
+        // Test encodePos function
+        PforUtil::encodePos(output, inputData);
+        output->close();
+        _CLDELETE(output);
+        dir->close();
+        _CLDELETE(dir);
+    }
+
+    {
+        IndexInput* input = nullptr;
+        CLuceneError error;
+        auto* dir = lucene::store::FSDirectory::getDirectory("./");
+        bool result = dir->openInput(testFileName, input, error);
+        CuAssertTrue(tc, result);
+
+        // Test decodePos function
+        uint32_t size = PforUtil::decodePos(input, decodedData);
+        printf("size = %u, TEST_SIZE = %u\n", size, TEST_SIZE);
+        CuAssertIntEquals(tc, _T("Size mismatch"), TEST_SIZE, size);
+
+        // Verify decoded data matches original data
+        for (size_t i = 0; i < TEST_SIZE; i++) {
+            CuAssertIntEquals(tc, _T("Data mismatch at index %zu"), inputData[i], decodedData[i]);
+        }
+
+        input->close();
+        _CLDELETE(input);
+        dir->close();
+        _CLDELETE(dir);
+    }
+
+    printf("Test completed for data size %u!\n", TEST_SIZE);
+}
+
+void test_encodePos(CuTest* tc) {
+    // Test different data size
+    std::vector<unsigned> test_sizes = {159, // Original data size
+                                        128, // SIMD processing boundary
+                                        256, // Standard PFOR_BLOCK_SIZE
+                                        512, // Larger dataset
+                                        384, 215, 40, 32, 16, 8, 4, 2, 1};
+
+    const std::vector<uint32_t> sample_data = {
+            3, 0, 2, 1, 1, 1, 3, 1, 2, 1, 1, 0, 1, 2, 1, 3, 0, 0, 3, 0, 0, 3, 2, 1, 3, 0, 3,
+            3, 2, 3, 2, 1, 0, 0, 2, 0, 1, 1, 0, 0, 0, 2, 0, 0, 3, 0, 2, 3, 1, 3, 0, 1, 3, 2,
+            3, 2, 2, 3, 1, 2, 3, 3, 2, 1, 1, 1, 1, 0, 3, 2, 2, 0, 3, 2, 0, 0, 1, 1, 3, 1, 2,
+            2, 2, 3, 0, 3, 0, 1, 0, 3, 2, 0, 3, 0, 3, 3, 0, 0, 2, 1, 3, 0, 2, 3, 0, 0, 3, 3,
+            1, 1, 1, 3, 0, 0, 3, 3, 2, 0, 3, 1, 2, 1, 3, 2, 3, 0, 1, 2, 1, 3, 0, 2, 2, 2, 2,
+            3, 3, 3, 2, 2, 0, 3, 2, 2, 1, 1, 1, 2, 1, 1, 2, 2, 2, 0, 0, 2, 0, 1, 1};
+
+    srand((unsigned)time(NULL));
+    printf("Starting encodePos and decodePos function tests...\n");
+
+    // First test with predefined original data
+    {
+        std::vector<uint32_t> source_data = sample_data;
+        unsigned TEST_SIZE = source_data.size();
+        printf("Testing with the predefined sample data of %u elements\n", TEST_SIZE);
+        test_encodePos_with_size(tc, TEST_SIZE, source_data);
+    }
+
+    // Then test different data sizes
+    for (unsigned TEST_SIZE : test_sizes) {
+        if (TEST_SIZE == sample_data.size()) continue; // Already tested
+
+        printf("Testing data size: %u\n", TEST_SIZE);
+        std::vector<uint32_t> testData(TEST_SIZE);
+
+        // Generate data based on source_data pattern
+        for (unsigned i = 0; i < TEST_SIZE; i++) {
+            testData[i] = sample_data[i % sample_data.size()];
+        }
+
+        // Use existing test function
+        test_encodePos_with_size(tc, TEST_SIZE, testData);
+    }
+
+    printf("All encodePos and decodePos tests completed successfully!\n");
+}
+
+CuSuite* testPFORSuite() {
+    CuSuite* suite = CuSuiteNew(_T("PFOR Test Suite"));
+
+    SUITE_ADD_TEST(suite, test_pfor_has_prox);
+    SUITE_ADD_TEST(suite, test_pfor_no_prox);
+    SUITE_ADD_TEST(suite, test_p4dec_p4enc_compat);
+    SUITE_ADD_TEST(suite, test_cross_platform_compat);
+    SUITE_ADD_TEST(suite, test_p4ndx_compatibility);
+    SUITE_ADD_TEST(suite, test_encodePos);
+
+    return suite;
+}
\ No newline at end of file
diff --git a/src/test/test.h b/src/test/test.h
index f4b091e4337..5ac8e98a64b 100644
--- a/src/test/test.h
+++ b/src/test/test.h
@@ -87,7 +87,7 @@ CuSuite *testIndexCompaction(void);
 CuSuite *testStringReader(void);
 CuSuite *testIndexCompress(void);
 CuSuite *testUTF8CharsSuite(void);
-
+CuSuite *testPFORSuite(void);
 #ifdef TEST_CONTRIB_LIBS
 //CuSuite *testGermanAnalyzer(void);
 CuSuite *testchinese(void);
diff --git a/src/test/tests.cpp b/src/test/tests.cpp
index fa40a8c78ea..4b99b94d396 100644
--- a/src/test/tests.cpp
+++ b/src/test/tests.cpp
@@ -21,6 +21,7 @@ unittest tests[] = {
         {"testStringReader", testStringReader},
         {"IndexCompress", testIndexCompress},
         {"TestUTF8Chars", testUTF8CharsSuite},
+        {"testPFOR", testPFORSuite},
 #ifdef TEST_CONTRIB_LIBS
         {"chinese", testchinese},
 #endif


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org