You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tsfile.apache.org by ja...@apache.org on 2025/10/22 07:57:29 UTC

(tsfile) 01/02: Refactored some dependencies

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

jackietien pushed a commit to branch snapshot/2.2.0-251022
in repository https://gitbox.apache.org/repos/asf/tsfile.git

commit f5c0682b8ad26d9bd6f28e11f97a0e5f755f5291
Author: Caideyipi <87...@users.noreply.github.com>
AuthorDate: Wed Oct 22 15:54:15 2025 +0800

    Refactored some dependencies
---
 LICENSE                                            |   42 +
 java/pom.xml                                       |   10 -
 java/tools/pom.xml                                 |    8 -
 .../java/org/apache/tsfile/tools/TsFileTool.java   |    5 +-
 .../org/apache/tsfile/tools/TsfiletoolsTest.java   |    2 +-
 java/tsfile/pom.xml                                |    8 -
 .../tsfile/external/commons/codec/binary/Hex.java  |  118 ++
 .../codec/binary/MessageDigestAlgorithms.java      |   52 +
 .../external/commons/codec/binary/StringUtils.java |   54 +
 .../external/commons/codec/digest/DigestUtils.java |  141 ++
 .../external/commons/collections4/BoundedMap.java  |   44 +
 .../commons/collections4/CollectionUtils.java      |   52 +
 .../tsfile/external/commons/collections4/Get.java  |   91 ++
 .../external/commons/collections4/IterableGet.java |   47 +
 .../external/commons/collections4/IterableMap.java |   43 +
 .../external/commons/collections4/KeyValue.java    |   44 +
 .../external/commons/collections4/MapIterator.java |  110 ++
 .../external/commons/collections4/MapUtils.java    |   39 +
 .../commons/collections4/OrderedIterator.java      |   46 +
 .../external/commons/collections4/OrderedMap.java  |   70 +
 .../commons/collections4/OrderedMapIterator.java   |   46 +
 .../tsfile/external/commons/collections4/Put.java  |   60 +
 .../commons/collections4/ResettableIterator.java   |   33 +
 .../collections4/comparators/ComparatorChain.java  |  197 +++
 .../iterators/AbstractEmptyIterator.java           |   56 +
 .../iterators/AbstractEmptyMapIterator.java        |   44 +
 .../collections4/iterators/EmptyIterator.java      |   74 ++
 .../collections4/iterators/EmptyMapIterator.java   |   55 +
 .../iterators/EmptyOrderedIterator.java            |   53 +
 .../iterators/EmptyOrderedMapIterator.java         |   55 +
 .../collections4/map/AbstractHashedMap.java        | 1394 ++++++++++++++++++++
 .../collections4/map/AbstractLinkedMap.java        |  610 +++++++++
 .../external/commons/collections4/map/LRUMap.java  |  573 ++++++++
 .../tsfile/external/commons/io/Charsets.java       |  211 +++
 .../external/commons/io/FileExistsException.java   |   40 +
 .../tsfile/external/commons/io/FileUtils.java      |  928 +++++++++++++
 .../tsfile/external/commons/io/FilenameUtils.java  |  290 ++++
 .../apache/tsfile/external/commons/io/IOCase.java  |  247 ++++
 .../external/commons/io/IOExceptionList.java       |  140 ++
 .../external/commons/io/IOIndexedException.java    |   68 +
 .../apache/tsfile/external/commons/io/IOUtils.java |  558 ++++++++
 .../external/commons/io/RandomAccessFileMode.java  |   96 ++
 .../external/commons/io/RandomAccessFiles.java     |   46 +
 .../external/commons/io/build/AbstractOrigin.java  |  484 +++++++
 .../commons/io/build/AbstractOriginSupplier.java   |  276 ++++
 .../commons/io/build/AbstractStreamBuilder.java    |  177 +++
 .../commons/io/build/AbstractSupplier.java         |   41 +
 .../commons/io/charset/CharsetDecoders.java        |   44 +
 .../commons/io/charset/CharsetEncoders.java        |   58 +
 .../tsfile/external/commons/io/file/Counters.java  |  419 ++++++
 .../commons/io/file/CountingPathVisitor.java       |  155 +++
 .../external/commons/io/file/DeleteOption.java     |   30 +
 .../commons/io/file/DeletingPathVisitor.java       |  157 +++
 .../external/commons/io/file/PathFilter.java       |   40 +
 .../tsfile/external/commons/io/file/PathUtils.java |  532 ++++++++
 .../external/commons/io/file/PathVisitor.java      |   30 +
 .../commons/io/file/SimplePathVisitor.java         |   46 +
 .../commons/io/file/StandardDeleteOption.java      |   51 +
 .../commons/io/filefilter/AbstractFileFilter.java  |  177 +++
 .../commons/io/filefilter/AndFileFilter.java       |  131 ++
 .../io/filefilter/ConditionalFileFilter.java       |   33 +
 .../commons/io/filefilter/FalseFileFilter.java     |  109 ++
 .../commons/io/filefilter/FileFileFilter.java      |  111 ++
 .../commons/io/filefilter/IOFileFilter.java        |  109 ++
 .../commons/io/filefilter/NotFileFilter.java       |  104 ++
 .../commons/io/filefilter/OrFileFilter.java        |  162 +++
 .../commons/io/filefilter/SuffixFileFilter.java    |  163 +++
 .../io/filefilter/SymbolicLinkFileFilter.java      |  141 ++
 .../commons/io/filefilter/TrueFileFilter.java      |  109 ++
 .../external/commons/io/function/Constants.java    |   60 +
 .../tsfile/external/commons/io/function/Erase.java |  191 +++
 .../external/commons/io/function/IOBaseStream.java |  141 ++
 .../commons/io/function/IOBaseStreamAdapter.java   |   50 +
 .../external/commons/io/function/IOBiConsumer.java |   55 +
 .../external/commons/io/function/IOBiFunction.java |   65 +
 .../commons/io/function/IOBinaryOperator.java      |   32 +
 .../external/commons/io/function/IOComparator.java |   58 +
 .../external/commons/io/function/IOConsumer.java   |  145 ++
 .../external/commons/io/function/IOFunction.java   |   41 +
 .../commons/io/function/IOIntSupplier.java         |   38 +
 .../external/commons/io/function/IOIterator.java   |   78 ++
 .../commons/io/function/IOIteratorAdapter.java     |   55 +
 .../external/commons/io/function/IOPredicate.java  |   79 ++
 .../commons/io/function/IOQuadFunction.java        |   51 +
 .../external/commons/io/function/IORunnable.java   |   36 +
 .../commons/io/function/IOSpliterator.java         |  135 ++
 .../commons/io/function/IOSpliteratorAdapter.java  |   44 +
 .../external/commons/io/function/IOStream.java     |  633 +++++++++
 .../commons/io/function/IOStreamAdapter.java       |   44 +
 .../external/commons/io/function/IOStreams.java    |   91 ++
 .../external/commons/io/function/IOSupplier.java   |   54 +
 .../commons/io/function/IOTriConsumer.java         |   44 +
 .../commons/io/function/IOTriFunction.java         |   49 +
 .../commons/io/function/IOUnaryOperator.java       |   32 +
 .../external/commons/io/function/Uncheck.java      |  310 +++++
 .../commons/io/function/UncheckedIOIterator.java   |   60 +
 .../commons/io/input/ClosedInputStream.java        |   58 +
 .../commons/io/input/ReaderInputStream.java        |  427 ++++++
 .../input/UnsynchronizedByteArrayInputStream.java  |  276 ++++
 .../io/output/AbstractByteArrayOutputStream.java   |  394 ++++++
 .../commons/io/output/ByteArrayOutputStream.java   |  104 ++
 .../commons/io/output/NullOutputStream.java        |   86 ++
 .../commons/io/output/StringBuilderWriter.java     |  140 ++
 .../io/output/ThresholdingOutputStream.java        |  210 +++
 .../UnsynchronizedByteArrayOutputStream.java       |  172 +++
 .../commons/io/output/WriterOutputStream.java      |  479 +++++++
 .../tsfile/external/commons/lang3/ArrayFill.java   |   40 +
 .../tsfile/external/commons/lang3/ArrayUtils.java  |  477 +++++++
 .../external/commons/lang3/CharSequenceUtils.java  |  163 +++
 .../tsfile/external/commons/lang3/ClassUtils.java  |   34 +
 .../commons/lang3/NotImplementedException.java     |  143 ++
 .../tsfile/external/commons/lang3/ObjectUtils.java |  172 +++
 .../tsfile/external/commons/lang3/StringUtils.java |  679 ++++++++++
 .../tsfile/external/commons/lang3/Strings.java     |  464 +++++++
 .../external/commons/lang3/SystemProperties.java   |   94 ++
 .../tsfile/external/commons/lang3/SystemUtils.java |  144 ++
 .../tsfile/external/commons/lang3/Validate.java    |  150 +++
 .../commons/lang3/builder/HashCodeBuilder.java     |  109 ++
 .../commons/lang3/exception/ExceptionUtils.java    |   70 +
 .../commons/lang3/function/FailableBiConsumer.java |   78 ++
 .../commons/lang3/function/FailableBiFunction.java |   77 ++
 .../commons/lang3/function/FailableFunction.java   |  114 ++
 .../external/commons/lang3/function/Suppliers.java |   71 +
 .../commons/lang3/function/TriFunction.java        |   65 +
 .../external/commons/lang3/math/NumberUtils.java   |  153 +++
 .../commons/lang3/stream/LangCollectors.java       |  115 ++
 .../external/commons/lang3/stream/Streams.java     |   56 +
 .../commons/lang3/tuple/ImmutablePair.java         |  204 +++
 .../commons/lang3/tuple/ImmutableTriple.java       |  168 +++
 .../tsfile/external/commons/lang3/tuple/Pair.java  |  264 ++++
 .../external/commons/lang3/tuple/Triple.java       |  205 +++
 .../fileSystem/fsFactory/LocalFSFactory.java       |    2 +-
 .../java/org/apache/tsfile/read/common/Path.java   |    5 +-
 .../read/common/block/column/DictionaryColumn.java |    6 +-
 .../tsfile/read/common/parser/PathVisitor.java     |    3 +-
 .../main/java/org/apache/tsfile/utils/Pair.java    |    1 +
 .../tsfile/write/v4/TsFileWriterBuilder.java       |    3 +-
 .../apache/tsfile/write/writer/TsFileIOWriter.java |    2 +-
 .../TsFileGeneratorForSeriesReaderByTimestamp.java |   10 +-
 .../read/reader/chunk/TableChunkReaderTest.java    |    2 +-
 .../org/apache/tsfile/tableview/TableViewTest.java |    2 +-
 .../org/apache/tsfile/utils/FilePathUtilsTest.java |    6 +-
 .../utils/{FileUtils.java => FileTestUtils.java}   |    2 +-
 .../{FileUtilsTest.java => FileTestUtilsTest.java} |   26 +-
 .../writer/TsFileIOWriterMemoryControlTest.java    |    2 +-
 145 files changed, 20126 insertions(+), 61 deletions(-)

diff --git a/LICENSE b/LICENSE
index 7b741bb5..014d343f 100644
--- a/LICENSE
+++ b/LICENSE
@@ -295,3 +295,45 @@ The following files include code is copied from lzokay project.
 Copyright: (c) 2018 Jack Andersen
 Project page: https://github.com/AxioDL/lzokay
 License: https://github.com/AxioDL/lzokay/blob/master/LICENSE
+
+--------------------------------------------------------------------------------
+
+The following files include code modified from Apache Commons Lang project.
+
+./java/tsfile/src/main/java/org/apache/tsfile/external/commons/lang3/*
+
+Apache Commons Lang is open source software licensed under the Apache License 2.0 and supported by the Apache Software Foundation.
+Project page: https://github.com/apache/commons-lang
+License: https://github.com/apache/commons-lang/blob/master/LICENSE.txt
+
+--------------------------------------------------------------------------------
+
+The following files include code modified from Apache Commons IO project.
+
+./java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/*
+
+Apache Commons IO is open source software licensed under the Apache License 2.0 and supported by the Apache Software Foundation.
+Project page: https://github.com/apache/commons-io
+License: https://github.com/apache/commons-io/blob/master/LICENSE.txt
+
+--------------------------------------------------------------------------------
+
+The following files include code modified from Apache Commons CodeC project.
+
+./java/tsfile/src/main/java/org/apache/tsfile/external/commons/codec/*
+
+Apache Commons CodeC is open source software licensed under the Apache License 2.0 and supported by the Apache Software Foundation.
+Project page: https://github.com/apache/commons-codec
+License: https://github.com/apache/commons-codec/blob/master/LICENSE.txt
+
+--------------------------------------------------------------------------------
+
+The following files include code modified from Apache Commons Collections project.
+
+./java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/*
+
+Apache Commons Collections is open source software licensed under the Apache License 2.0 and supported by the Apache Software Foundation.
+Project page: https://github.com/apache/commons-collections
+License: https://github.com/apache/commons-collections/blob/master/LICENSE.txt
+
+--------------------------------------------------------------------------------
diff --git a/java/pom.xml b/java/pom.xml
index a380d4a4..b8e5a551 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -48,16 +48,6 @@
                 <artifactId>snappy-java</artifactId>
                 <version>1.1.10.5</version>
             </dependency>
-            <dependency>
-                <groupId>commons-io</groupId>
-                <artifactId>commons-io</artifactId>
-                <version>2.17.0</version>
-            </dependency>
-            <dependency>
-                <groupId>org.apache.commons</groupId>
-                <artifactId>commons-lang3</artifactId>
-                <version>3.18.0</version>
-            </dependency>
             <dependency>
                 <groupId>org.lz4</groupId>
                 <artifactId>lz4-java</artifactId>
diff --git a/java/tools/pom.xml b/java/tools/pom.xml
index c8a76f97..80549e7c 100644
--- a/java/tools/pom.xml
+++ b/java/tools/pom.xml
@@ -39,14 +39,6 @@
             <artifactId>commons-cli</artifactId>
             <version>1.9.0</version>
         </dependency>
-        <dependency>
-            <groupId>commons-io</groupId>
-            <artifactId>commons-io</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.commons</groupId>
-            <artifactId>commons-lang3</artifactId>
-        </dependency>
         <dependency>
             <groupId>org.apache.tsfile</groupId>
             <artifactId>tsfile</artifactId>
diff --git a/java/tools/src/main/java/org/apache/tsfile/tools/TsFileTool.java b/java/tools/src/main/java/org/apache/tsfile/tools/TsFileTool.java
index 5dd2afe5..26b69916 100644
--- a/java/tools/src/main/java/org/apache/tsfile/tools/TsFileTool.java
+++ b/java/tools/src/main/java/org/apache/tsfile/tools/TsFileTool.java
@@ -16,10 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 package org.apache.tsfile.tools;
 
 import org.apache.tsfile.enums.ColumnCategory;
 import org.apache.tsfile.enums.TSDataType;
+import org.apache.tsfile.external.commons.io.FilenameUtils;
+import org.apache.tsfile.external.commons.lang3.StringUtils;
 import org.apache.tsfile.file.metadata.TableSchema;
 import org.apache.tsfile.file.metadata.enums.CompressionType;
 import org.apache.tsfile.file.metadata.enums.TSEncoding;
@@ -35,8 +38,6 @@ import org.apache.commons.cli.DefaultParser;
 import org.apache.commons.cli.HelpFormatter;
 import org.apache.commons.cli.Options;
 import org.apache.commons.cli.ParseException;
-import org.apache.commons.io.FilenameUtils;
-import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/java/tools/src/test/java/org/apache/tsfile/tools/TsfiletoolsTest.java b/java/tools/src/test/java/org/apache/tsfile/tools/TsfiletoolsTest.java
index ddbe32e8..5fb09c75 100644
--- a/java/tools/src/test/java/org/apache/tsfile/tools/TsfiletoolsTest.java
+++ b/java/tools/src/test/java/org/apache/tsfile/tools/TsfiletoolsTest.java
@@ -19,6 +19,7 @@
 
 package org.apache.tsfile.tools;
 
+import org.apache.tsfile.external.commons.io.FileUtils;
 import org.apache.tsfile.read.TsFileSequenceReader;
 import org.apache.tsfile.read.common.block.TsBlock;
 import org.apache.tsfile.read.controller.CachedChunkLoaderImpl;
@@ -26,7 +27,6 @@ import org.apache.tsfile.read.controller.MetadataQuerierByFileImpl;
 import org.apache.tsfile.read.query.executor.TableQueryExecutor;
 import org.apache.tsfile.read.reader.block.TsBlockReader;
 
-import org.apache.commons.io.FileUtils;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
diff --git a/java/tsfile/pom.xml b/java/tsfile/pom.xml
index 9187b60e..6d0d41b0 100644
--- a/java/tsfile/pom.xml
+++ b/java/tsfile/pom.xml
@@ -48,14 +48,6 @@
             <groupId>org.xerial.snappy</groupId>
             <artifactId>snappy-java</artifactId>
         </dependency>
-        <dependency>
-            <groupId>commons-io</groupId>
-            <artifactId>commons-io</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.commons</groupId>
-            <artifactId>commons-lang3</artifactId>
-        </dependency>
         <dependency>
             <groupId>org.lz4</groupId>
             <artifactId>lz4-java</artifactId>
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/codec/binary/Hex.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/codec/binary/Hex.java
new file mode 100644
index 00000000..982b519c
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/codec/binary/Hex.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tsfile.external.commons.codec.binary;
+
+/////////////////////////////////////////////////////////////////////////////////////////////////
+// IoTDB
+/////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class Hex {
+
+  /** Used to build output as hex. */
+  private static final char[] DIGITS_LOWER = {
+    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+  };
+
+  /** Used to build output as hex. */
+  private static final char[] DIGITS_UPPER = {
+    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+  };
+
+  /**
+   * Converts an array of bytes into a String representing the hexadecimal values of each byte in
+   * order. The returned String will be double the length of the passed array, as it takes two
+   * characters to represent any given byte.
+   *
+   * @param data a byte[] to convert to hexadecimal characters
+   * @return A String containing lower-case hexadecimal characters
+   * @since 1.4
+   */
+  public static String encodeHexString(final byte[] data) {
+    return new String(encodeHex(data));
+  }
+
+  /**
+   * Converts an array of bytes into an array of characters representing the hexadecimal values of
+   * each byte in order. The returned array will be double the length of the passed array, as it
+   * takes two characters to represent any given byte.
+   *
+   * @param data a byte[] to convert to hexadecimal characters
+   * @return A char[] containing lower-case hexadecimal characters
+   */
+  public static char[] encodeHex(final byte[] data) {
+    return encodeHex(data, true);
+  }
+
+  /**
+   * Converts an array of bytes into an array of characters representing the hexadecimal values of
+   * each byte in order. The returned array will be double the length of the passed array, as it
+   * takes two characters to represent any given byte.
+   *
+   * @param data a byte[] to convert to Hex characters
+   * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase
+   * @return A char[] containing hexadecimal characters in the selected case
+   * @since 1.4
+   */
+  public static char[] encodeHex(final byte[] data, final boolean toLowerCase) {
+    return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
+  }
+
+  /**
+   * Converts an array of bytes into an array of characters representing the hexadecimal values of
+   * each byte in order. The returned array will be double the length of the passed array, as it
+   * takes two characters to represent any given byte.
+   *
+   * @param data a byte[] to convert to hexadecimal characters
+   * @param toDigits the output alphabet (must contain at least 16 chars)
+   * @return A char[] containing the appropriate characters from the alphabet For best results, this
+   *     should be either upper- or lower-case hex.
+   * @since 1.4
+   */
+  protected static char[] encodeHex(final byte[] data, final char[] toDigits) {
+    final int dataLength = data.length;
+    final char[] out = new char[dataLength << 1];
+    encodeHex(data, 0, dataLength, toDigits, out, 0);
+    return out;
+  }
+
+  /**
+   * Converts an array of bytes into an array of characters representing the hexadecimal values of
+   * each byte in order.
+   *
+   * @param data a byte[] to convert to hexadecimal characters
+   * @param dataOffset the position in {@code data} to start encoding from
+   * @param dataLen the number of bytes from {@code dataOffset} to encode
+   * @param toDigits the output alphabet (must contain at least 16 chars)
+   * @param out a char[] which will hold the resultant appropriate characters from the alphabet.
+   * @param outOffset the position within {@code out} at which to start writing the encoded
+   *     characters.
+   */
+  private static void encodeHex(
+      final byte[] data,
+      final int dataOffset,
+      final int dataLen,
+      final char[] toDigits,
+      final char[] out,
+      final int outOffset) {
+    // two characters form the hex value.
+    for (int i = dataOffset, j = outOffset; i < dataOffset + dataLen; i++) {
+      out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
+      out[j++] = toDigits[0x0F & data[i]];
+    }
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/codec/binary/MessageDigestAlgorithms.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/codec/binary/MessageDigestAlgorithms.java
new file mode 100644
index 00000000..738af14d
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/codec/binary/MessageDigestAlgorithms.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tsfile.external.commons.codec.binary;
+
+import java.security.MessageDigest;
+
+/**
+ * Standard {@link MessageDigest} algorithm names from the <cite>Java Cryptography Architecture
+ * Standard Algorithm Name Documentation</cite>.
+ *
+ * <p>This class is immutable and thread-safe.
+ *
+ * <ul>
+ *   <li>Java 8 and up: SHA-224.
+ *   <li>Java 9 and up: SHA3-224, SHA3-256, SHA3-384, SHA3-512.
+ * </ul>
+ *
+ * @see <a
+ *     href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#MessageDigest">
+ *     Java 8 Cryptography Architecture Standard Algorithm Name Documentation</a>
+ * @see <a
+ *     href="https://docs.oracle.com/en/java/javase/11/docs/specs/security/standard-names.html#messagedigest-algorithms">
+ *     Java 11 Cryptography Architecture Standard Algorithm Name Documentation</a>
+ * @see <a
+ *     href="https://docs.oracle.com/en/java/javase/17/docs/specs/security/standard-names.html#messagedigest-algorithms">
+ *     Java 17 Cryptography Architecture Standard Algorithm Name Documentation</a>
+ * @see <a
+ *     href="https://docs.oracle.com/en/java/javase/21/docs/specs/security/standard-names.html#messagedigest-algorithms">
+ *     Java 21 Cryptography Architecture Standard Algorithm Name Documentation</a>
+ * @see <a href="https://dx.doi.org/10.6028/NIST.FIPS.180-4">FIPS PUB 180-4</a>
+ * @see <a href="https://dx.doi.org/10.6028/NIST.FIPS.202">FIPS PUB 202</a>
+ * @since 1.7
+ */
+public class MessageDigestAlgorithms {
+  /** The MD2 message digest algorithm defined in RFC 1319. */
+  public static final String MD2 = "MD2";
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/codec/binary/StringUtils.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/codec/binary/StringUtils.java
new file mode 100644
index 00000000..5106d8b4
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/codec/binary/StringUtils.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tsfile.external.commons.codec.binary;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+/////////////////////////////////////////////////////////////////////////////////////////////////
+// IoTDB
+/////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class StringUtils {
+  /**
+   * Encodes the given string into a sequence of bytes using the UTF-8 charset, storing the result
+   * into a new byte array.
+   *
+   * @param string the String to encode, may be {@code null}
+   * @return encoded bytes, or {@code null} if the input string was {@code null}
+   * @throws NullPointerException Thrown if {@link StandardCharsets#UTF_8} is not initialized, which
+   *     should never happen since it is required by the Java platform specification.
+   * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException
+   * @see Charset
+   * @see #getBytesUnchecked(String, String)
+   */
+  public static byte[] getBytesUtf8(final String string) {
+    return getBytes(string, StandardCharsets.UTF_8);
+  }
+
+  /**
+   * Calls {@link String#getBytes(Charset)}
+   *
+   * @param string The string to encode (if null, return null).
+   * @param charset The {@link Charset} to encode the {@code String}
+   * @return the encoded bytes
+   */
+  private static byte[] getBytes(final String string, final Charset charset) {
+    return string == null ? null : string.getBytes(charset);
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/codec/digest/DigestUtils.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/codec/digest/DigestUtils.java
new file mode 100644
index 00000000..8f3fc127
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/codec/digest/DigestUtils.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tsfile.external.commons.codec.digest;
+
+import org.apache.tsfile.external.commons.codec.binary.Hex;
+import org.apache.tsfile.external.commons.codec.binary.MessageDigestAlgorithms;
+import org.apache.tsfile.external.commons.codec.binary.StringUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/////////////////////////////////////////////////////////////////////////////////////////////////
+// IoTDB
+/////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class DigestUtils {
+
+  /**
+   * Calculates the MD2 digest and returns the value as a 32 character hexadecimal string.
+   *
+   * @param data Data to digest
+   * @return MD2 digest as a hexadecimal string
+   * @since 1.7
+   */
+  public static String md2Hex(final String data) {
+    return Hex.encodeHexString(md2(data));
+  }
+
+  /**
+   * Calculates the MD2 digest and returns the value as a 16 element {@code byte[]}.
+   *
+   * @param data Data to digest; converted to bytes using {@link StringUtils#getBytesUtf8(String)}
+   * @return MD2 digest
+   * @since 1.7
+   */
+  public static byte[] md2(final String data) {
+    return md2(StringUtils.getBytesUtf8(data));
+  }
+
+  /**
+   * Calculates the MD2 digest and returns the value as a 16 element {@code byte[]}.
+   *
+   * @param data Data to digest
+   * @return MD2 digest
+   * @since 1.7
+   */
+  public static byte[] md2(final byte[] data) {
+    return getMd2Digest().digest(data);
+  }
+
+  /**
+   * Gets an MD2 MessageDigest.
+   *
+   * @return An MD2 digest instance.
+   * @throws IllegalArgumentException when a {@link NoSuchAlgorithmException} is caught, which
+   *     should never happen because MD2 is a built-in algorithm
+   * @see MessageDigestAlgorithms#MD2
+   * @since 1.7
+   */
+  public static MessageDigest getMd2Digest() {
+    return getDigest(MessageDigestAlgorithms.MD2);
+  }
+
+  public static String md5Hex(InputStream data) throws IOException {
+    return Hex.encodeHexString(md5(data));
+  }
+
+  public static byte[] md5(InputStream data) throws IOException {
+    return digest(getMd5Digest(), data);
+  }
+
+  /**
+   * Calculates the MD5 digest and returns the value as a 32 character hexadecimal string.
+   *
+   * @param data Data to digest
+   * @return MD5 digest as a hexadecimal string
+   */
+  public static String md5Hex(final byte[] data) {
+    return Hex.encodeHexString(md5(data));
+  }
+
+  /**
+   * Calculates the MD5 digest and returns the value as a 16 element {@code byte[]}.
+   *
+   * @param data Data to digest
+   * @return MD5 digest
+   */
+  public static byte[] md5(final byte[] data) {
+    return getMd5Digest().digest(data);
+  }
+
+  public static byte[] digest(MessageDigest messageDigest, InputStream data) throws IOException {
+    return updateDigest(messageDigest, data).digest();
+  }
+
+  public static MessageDigest updateDigest(MessageDigest digest, InputStream inputStream)
+      throws IOException {
+    byte[] buffer = new byte[1024];
+
+    for (int read = inputStream.read(buffer, 0, 1024);
+        read > -1;
+        read = inputStream.read(buffer, 0, 1024)) {
+      digest.update(buffer, 0, read);
+    }
+
+    return digest;
+  }
+
+  public static MessageDigest getMd5Digest() {
+    return getDigest("MD5");
+  }
+
+  public static MessageDigest getDigest(String algorithm) {
+    try {
+      return getMessageDigest(algorithm);
+    } catch (NoSuchAlgorithmException var2) {
+      throw new IllegalArgumentException(var2);
+    }
+  }
+
+  private static MessageDigest getMessageDigest(String algorithm) throws NoSuchAlgorithmException {
+    return MessageDigest.getInstance(algorithm);
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/BoundedMap.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/BoundedMap.java
new file mode 100644
index 00000000..d0a5ab16
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/BoundedMap.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tsfile.external.commons.collections4;
+
+/**
+ * Defines a map that is bounded in size.
+ *
+ * <p>The size of the map can vary, but it can never exceed a preset maximum number of elements.
+ * This interface allows the querying of details associated with the maximum number of elements.
+ *
+ * @param <K> the type of the keys in this map
+ * @param <V> the type of the values in this map
+ * @since 3.0
+ */
+public interface BoundedMap<K, V> extends IterableMap<K, V> {
+
+  /**
+   * Returns true if this map is full and no new elements can be added.
+   *
+   * @return <code>true</code> if the map is full
+   */
+  boolean isFull();
+
+  /**
+   * Gets the maximum size of the map (the bound).
+   *
+   * @return the maximum number of elements the map can hold
+   */
+  int maxSize();
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/CollectionUtils.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/CollectionUtils.java
new file mode 100644
index 00000000..db35216d
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/CollectionUtils.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tsfile.external.commons.collections4;
+
+import java.util.Collection;
+
+/////////////////////////////////////////////////////////////////////////////////////////////////
+// IoTDB
+/////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class CollectionUtils {
+  /**
+   * Null-safe check if the specified collection is empty.
+   *
+   * <p>Null returns true.
+   *
+   * @param coll the collection to check, may be null
+   * @return true if empty or null
+   * @since 3.2
+   */
+  public static boolean isEmpty(final Collection<?> coll) {
+    return coll == null || coll.isEmpty();
+  }
+
+  /**
+   * Null-safe check if the specified collection is not empty.
+   *
+   * <p>Null returns false.
+   *
+   * @param coll the collection to check, may be null
+   * @return true if non-null and non-empty
+   * @since 3.2
+   */
+  public static boolean isNotEmpty(final Collection<?> coll) {
+    return !isEmpty(coll);
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/Get.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/Get.java
new file mode 100644
index 00000000..adb43c4e
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/Get.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tsfile.external.commons.collections4;
+
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * The "read" subset of the {@link java.util.Map} interface.
+ *
+ * @param <K> the type of the keys in this map
+ * @param <V> the type of the values in this map
+ * @since 4.0
+ * @see Put
+ */
+public interface Get<K, V> {
+
+  /**
+   * @param key key whose presence in this map is to be tested
+   * @return <code>true</code> if this map contains a mapping for the specified key
+   * @see java.util.Map#containsKey(Object)
+   */
+  boolean containsKey(Object key);
+
+  /**
+   * @param value value whose presence in this map is to be tested
+   * @return <code>true</code> if this map maps one or more keys to the specified value
+   * @see java.util.Map#containsValue(Object)
+   */
+  boolean containsValue(Object value);
+
+  /**
+   * @return a set view of the mappings contained in this map
+   * @see java.util.Map#entrySet()
+   */
+  Set<java.util.Map.Entry<K, V>> entrySet();
+
+  /**
+   * @param key the key whose associated value is to be returned
+   * @return the value to which the specified key is mapped, or {@code null} if this map contains no
+   *     mapping for the key
+   * @see java.util.Map#get(Object)
+   */
+  V get(Object key);
+
+  /**
+   * @param key key whose mapping is to be removed from the map
+   * @return the previous value associated with <code>key</code>, or <code>null</code> if there was
+   *     no mapping for <code>key</code>.
+   * @see java.util.Map#remove(Object)
+   */
+  V remove(Object key);
+
+  /**
+   * @return <code>true</code> if this map contains no key-value mappings
+   * @see java.util.Map#isEmpty()
+   */
+  boolean isEmpty();
+
+  /**
+   * @return a set view of the keys contained in this map
+   * @see java.util.Map#keySet()
+   */
+  Set<K> keySet();
+
+  /**
+   * @return the number of key-value mappings in this map
+   * @see java.util.Map#size()
+   */
+  int size();
+
+  /**
+   * @return a collection view of the values contained in this map
+   * @see java.util.Map#values()
+   */
+  Collection<V> values();
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/IterableGet.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/IterableGet.java
new file mode 100644
index 00000000..5767c749
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/IterableGet.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tsfile.external.commons.collections4;
+
+/**
+ * The "read" subset of the {@link java.util.Map} interface.
+ *
+ * @param <K> the type of the keys in this map
+ * @param <V> the type of the values in this map
+ * @since 4.0
+ * @see Put
+ */
+public interface IterableGet<K, V> extends Get<K, V> {
+  /**
+   * Obtains a <code>MapIterator</code> over the map.
+   *
+   * <p>A map iterator is an efficient way of iterating over maps. There is no need to access the
+   * entry set or use Map Entry objects.
+   *
+   * <pre>
+   * IterableMap&lt;String,Integer&gt; map = new HashedMap&lt;String,Integer&gt;();
+   * MapIterator&lt;String,Integer&gt; it = map.mapIterator();
+   * while (it.hasNext()) {
+   *   String key = it.next();
+   *   Integer value = it.getValue();
+   *   it.setValue(value + 1);
+   * }
+   * </pre>
+   *
+   * @return a map iterator
+   */
+  MapIterator<K, V> mapIterator();
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/IterableMap.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/IterableMap.java
new file mode 100644
index 00000000..e7ad28ca
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/IterableMap.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tsfile.external.commons.collections4;
+
+import java.util.Map;
+
+/**
+ * Defines a map that can be iterated directly without needing to create an entry set.
+ *
+ * <p>A map iterator is an efficient way of iterating over maps. There is no need to access the
+ * entry set or use Map Entry objects.
+ *
+ * <pre>
+ * IterableMap&lt;String,Integer&gt; map = new HashedMap&lt;String,Integer&gt;();
+ * MapIterator&lt;String,Integer&gt; it = map.mapIterator();
+ * while (it.hasNext()) {
+ *   String key = it.next();
+ *   Integer value = it.getValue();
+ *   it.setValue(value + 1);
+ * }
+ * </pre>
+ *
+ * @param <K> the type of the keys in this map
+ * @param <V> the type of the values in this map
+ * @since 3.0
+ */
+public interface IterableMap<K, V> extends Map<K, V>, Put<K, V>, IterableGet<K, V> {
+  // empty
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/KeyValue.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/KeyValue.java
new file mode 100644
index 00000000..d173b68e
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/KeyValue.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tsfile.external.commons.collections4;
+
+/**
+ * Defines a simple key value pair.
+ *
+ * <p>A Map Entry has considerable additional semantics over and above a simple key-value pair. This
+ * interface defines the minimum key value, with just the two get methods.
+ *
+ * @param <K> the type of the key
+ * @param <V> the type of the value
+ * @since 3.0
+ */
+public interface KeyValue<K, V> {
+
+  /**
+   * Gets the key from the pair.
+   *
+   * @return the key
+   */
+  K getKey();
+
+  /**
+   * Gets the value from the pair.
+   *
+   * @return the value
+   */
+  V getValue();
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/MapIterator.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/MapIterator.java
new file mode 100644
index 00000000..9f9de0a7
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/MapIterator.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tsfile.external.commons.collections4;
+
+import java.util.Iterator;
+
+/**
+ * Defines an iterator that operates over a <code>Map</code>.
+ *
+ * <p>This iterator is a special version designed for maps. It can be more efficient to use this
+ * rather than an entry set iterator where the option is available, and it is certainly more
+ * convenient.
+ *
+ * <p>A map that provides this interface may not hold the data internally using Map Entry objects,
+ * thus this interface can avoid lots of object creation.
+ *
+ * <p>In use, this iterator iterates through the keys in the map. After each call to <code>next()
+ * </code>, the <code>getValue()</code> method provides direct access to the value. The value can
+ * also be set using <code>setValue()</code>.
+ *
+ * <pre>{@code
+ * MapIterator<String,Integer> it = map.mapIterator();
+ * while (it.hasNext()) {
+ *   String key = it.next();
+ *   Integer value = it.getValue();
+ *   it.setValue(value + 1);
+ * }
+ * }</pre>
+ *
+ * @param <K> the type of the keys in the map
+ * @param <V> the type of the values in the map
+ * @since 3.0
+ */
+public interface MapIterator<K, V> extends Iterator<K> {
+
+  /**
+   * Checks to see if there are more entries still to be iterated.
+   *
+   * @return <code>true</code> if the iterator has more elements
+   */
+  @Override
+  boolean hasNext();
+
+  /**
+   * Gets the next <em>key</em> from the <code>Map</code>.
+   *
+   * @return the next key in the iteration
+   * @throws java.util.NoSuchElementException if the iteration is finished
+   */
+  @Override
+  K next();
+
+  // -----------------------------------------------------------------------
+  /**
+   * Gets the current key, which is the key returned by the last call to <code>next()</code>.
+   *
+   * @return the current key
+   * @throws IllegalStateException if <code>next()</code> has not yet been called
+   */
+  K getKey();
+
+  /**
+   * Gets the current value, which is the value associated with the last key returned by <code>
+   * next()</code>.
+   *
+   * @return the current value
+   * @throws IllegalStateException if <code>next()</code> has not yet been called
+   */
+  V getValue();
+
+  // -----------------------------------------------------------------------
+  /**
+   * Removes the last returned key from the underlying <code>Map</code> (optional operation).
+   *
+   * <p>This method can be called once per call to <code>next()</code>.
+   *
+   * @throws UnsupportedOperationException if remove is not supported by the map
+   * @throws IllegalStateException if <code>next()</code> has not yet been called
+   * @throws IllegalStateException if <code>remove()</code> has already been called since the last
+   *     call to <code>next()</code>
+   */
+  @Override
+  void remove();
+
+  /**
+   * Sets the value associated with the current key (optional operation).
+   *
+   * @param value the new value
+   * @return the previous value
+   * @throws UnsupportedOperationException if setValue is not supported by the map
+   * @throws IllegalStateException if <code>next()</code> has not yet been called
+   * @throws IllegalStateException if <code>remove()</code> has been called since the last call to
+   *     <code>next()</code>
+   */
+  V setValue(V value);
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/MapUtils.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/MapUtils.java
new file mode 100644
index 00000000..d8e94118
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/MapUtils.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tsfile.external.commons.collections4;
+
+/////////////////////////////////////////////////////////////////////////////////////////////////
+// IoTDB
+/////////////////////////////////////////////////////////////////////////////////////////////////
+
+import java.util.Map;
+
+public class MapUtils {
+  /**
+   * Null-safe check if the specified map is empty.
+   *
+   * <p>Null returns true.
+   *
+   * @param map the map to check, may be null
+   * @return true if empty or null
+   * @since 3.2
+   */
+  public static boolean isEmpty(final Map<?, ?> map) {
+    return map == null || map.isEmpty();
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/OrderedIterator.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/OrderedIterator.java
new file mode 100644
index 00000000..7f81134e
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/OrderedIterator.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tsfile.external.commons.collections4;
+
+import java.util.Iterator;
+
+/**
+ * Defines an iterator that operates over an ordered container. Subset of {@link
+ * java.util.ListIterator}.
+ *
+ * <p>This iterator allows both forward and reverse iteration through the container.
+ *
+ * @param <E> the type of elements returned by this iterator
+ * @since 3.0
+ */
+public interface OrderedIterator<E> extends Iterator<E> {
+
+  /**
+   * Checks to see if there is a previous element that can be iterated to.
+   *
+   * @return <code>true</code> if the iterator has a previous element
+   */
+  boolean hasPrevious();
+
+  /**
+   * Gets the previous element from the container.
+   *
+   * @return the previous element in the iteration
+   * @throws java.util.NoSuchElementException if the iteration is finished
+   */
+  E previous();
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/OrderedMap.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/OrderedMap.java
new file mode 100644
index 00000000..f87f0057
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/OrderedMap.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tsfile.external.commons.collections4;
+
+/**
+ * Defines a map that maintains order and allows both forward and backward iteration through that
+ * order.
+ *
+ * @param <K> the type of the keys in the map
+ * @param <V> the type of the values in the map
+ * @since 3.0
+ */
+public interface OrderedMap<K, V> extends IterableMap<K, V> {
+
+  /**
+   * Obtains an <code>OrderedMapIterator</code> over the map.
+   *
+   * <p>A ordered map iterator is an efficient way of iterating over maps in both directions.
+   *
+   * @return a map iterator
+   */
+  @Override
+  OrderedMapIterator<K, V> mapIterator();
+
+  /**
+   * Gets the first key currently in this map.
+   *
+   * @return the first key currently in this map
+   * @throws java.util.NoSuchElementException if this map is empty
+   */
+  K firstKey();
+
+  /**
+   * Gets the last key currently in this map.
+   *
+   * @return the last key currently in this map
+   * @throws java.util.NoSuchElementException if this map is empty
+   */
+  K lastKey();
+
+  /**
+   * Gets the next key after the one specified.
+   *
+   * @param key the key to search for next from
+   * @return the next key, null if no match or at end
+   */
+  K nextKey(K key);
+
+  /**
+   * Gets the previous key before the one specified.
+   *
+   * @param key the key to search for previous from
+   * @return the previous key, null if no match or at start
+   */
+  K previousKey(K key);
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/OrderedMapIterator.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/OrderedMapIterator.java
new file mode 100644
index 00000000..6f3b4368
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/OrderedMapIterator.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tsfile.external.commons.collections4;
+
+/**
+ * Defines an iterator that operates over an ordered <code>Map</code>.
+ *
+ * <p>This iterator allows both forward and reverse iteration through the map.
+ *
+ * @param <K> the type of the keys in the map
+ * @param <V> the type of the values in the map
+ * @since 3.0
+ */
+public interface OrderedMapIterator<K, V> extends MapIterator<K, V>, OrderedIterator<K> {
+
+  /**
+   * Checks to see if there is a previous entry that can be iterated to.
+   *
+   * @return <code>true</code> if the iterator has a previous element
+   */
+  @Override
+  boolean hasPrevious();
+
+  /**
+   * Gets the previous <em>key</em> from the <code>Map</code>.
+   *
+   * @return the previous key in the iteration
+   * @throws java.util.NoSuchElementException if the iteration is finished
+   */
+  @Override
+  K previous();
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/Put.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/Put.java
new file mode 100644
index 00000000..9bab9773
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/Put.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tsfile.external.commons.collections4;
+
+import java.util.Map;
+
+/**
+ * The "write" subset of the {@link Map} interface.
+ *
+ * <p>NOTE: in the original {@link Map} interface, {@link Map#put(Object, Object)} is known to have
+ * the same return type as {@link Map#get(Object)}, namely {@code V}. {@link Put} makes no
+ * assumptions in this regard (there is no association with, nor even knowledge of, a "reading"
+ * interface) and thus defines {@link #put(Object, Object)} as returning {@link Object}.
+ *
+ * @param <K> the type of the keys in this map
+ * @param <V> the type of the values in this map
+ * @since 4.0
+ * @see Get
+ */
+public interface Put<K, V> {
+
+  /**
+   * @see Map#clear()
+   */
+  void clear();
+
+  /**
+   * Note that the return type is Object, rather than V as in the Map interface. See the class
+   * Javadoc for further info.
+   *
+   * @param key key with which the specified value is to be associated
+   * @param value value to be associated with the specified key
+   * @return the previous value associated with <code>key</code>, or <code>null</code> if there was
+   *     no mapping for <code>key</code>. (A <code>null</code> return can also indicate that the map
+   *     previously associated <code>null</code> with <code>key</code>, if the implementation
+   *     supports <code>null</code> values.)
+   * @see Map#put(Object, Object)
+   */
+  Object put(K key, V value);
+
+  /**
+   * @param t mappings to be stored in this map
+   * @see Map#putAll(Map)
+   */
+  void putAll(Map<? extends K, ? extends V> t);
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/ResettableIterator.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/ResettableIterator.java
new file mode 100644
index 00000000..2b8b2ec1
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/ResettableIterator.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tsfile.external.commons.collections4;
+
+import java.util.Iterator;
+
+/**
+ * Defines an iterator that can be reset back to an initial state.
+ *
+ * <p>This interface allows an iterator to be repeatedly reused.
+ *
+ * @param <E> the type of elements returned by this iterator
+ * @since 3.0
+ */
+public interface ResettableIterator<E> extends Iterator<E> {
+
+  /** Resets the iterator back to the position at which the iterator was created. */
+  void reset();
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/comparators/ComparatorChain.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/comparators/ComparatorChain.java
new file mode 100644
index 00000000..2acefe07
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/comparators/ComparatorChain.java
@@ -0,0 +1,197 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tsfile.external.commons.collections4.comparators;
+
+import java.io.Serializable;
+import java.util.BitSet;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+/////////////////////////////////////////////////////////////////////////////////////////////////
+// IoTDB
+/////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * A ComparatorChain is a Comparator that wraps one or more Comparators in sequence. The
+ * ComparatorChain calls each Comparator in sequence until either 1) any single Comparator returns a
+ * non-zero result (and that result is then returned), or 2) the ComparatorChain is exhausted (and
+ * zero is returned). This type of sorting is very similar to multi-column sorting in SQL, and this
+ * class allows Java classes to emulate that kind of behaviour when sorting a List.
+ *
+ * <p>To further facilitate SQL-like sorting, the order of any single Comparator in the list can be
+ * reversed.
+ *
+ * <p>Calling a method that adds new Comparators or changes the ascend/descend sort <i>after
+ * compare(Object, Object) has been called</i> will result in an UnsupportedOperationException.
+ * However, <i>take care</i> to not alter the underlying List of Comparators or the BitSet that
+ * defines the sort order.
+ *
+ * <p>Instances of ComparatorChain are not synchronized. The class is not thread-safe at
+ * construction time, but it <i>is</i> thread-safe to perform multiple comparisons after all the
+ * setup operations are complete.
+ *
+ * @param <E> the type of objects compared by this comparator
+ * @since 2.0
+ */
+public class ComparatorChain<E> implements Comparator<E>, Serializable {
+
+  /** Serialization version from Collections 2.0. */
+  private static final long serialVersionUID = -721644942746081630L;
+
+  /** The list of comparators in the chain. */
+  private final List<Comparator<E>> comparatorChain;
+
+  /** Order - false (clear) = ascend; true (set) = descend. */
+  private BitSet orderingBits = null;
+
+  /** Whether the chain has been "locked". */
+  private boolean isLocked = false;
+
+  /**
+   * Construct a ComparatorChain from the Comparators in the List. All Comparators will default to
+   * the forward sort order.
+   *
+   * @param list List of Comparators
+   * @see #ComparatorChain(List,BitSet)
+   */
+  public ComparatorChain(final List<Comparator<E>> list) {
+    this(list, new BitSet(list.size()));
+  }
+
+  /**
+   * Construct a ComparatorChain from the Comparators in the given List. The sort order of each
+   * column will be drawn from the given BitSet. When determining the sort order for Comparator at
+   * index <i>i</i> in the List, the ComparatorChain will call BitSet.get(<i>i</i>). If that method
+   * returns <i>false</i>, the forward sort order is used; a return value of <i>true</i> indicates
+   * reverse sort order.
+   *
+   * @param list List of Comparators. NOTE: This constructor does not perform a defensive copy of
+   *     the list
+   * @param bits Sort order for each Comparator. Extra bits are ignored, unless extra Comparators
+   *     are added by another method.
+   */
+  public ComparatorChain(final List<Comparator<E>> list, final BitSet bits) {
+    comparatorChain = list;
+    orderingBits = bits;
+  }
+
+  /**
+   * Throws an exception if the {@link ComparatorChain} is empty.
+   *
+   * @throws UnsupportedOperationException if the {@link ComparatorChain} is empty
+   */
+  private void checkChainIntegrity() {
+    if (comparatorChain.size() == 0) {
+      throw new UnsupportedOperationException(
+          "ComparatorChains must contain at least one Comparator");
+    }
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Perform comparisons on the Objects as per Comparator.compare(o1,o2).
+   *
+   * @param o1 the first object to compare
+   * @param o2 the second object to compare
+   * @return -1, 0, or 1
+   * @throws UnsupportedOperationException if the ComparatorChain does not contain at least one
+   *     Comparator
+   */
+  @Override
+  public int compare(final E o1, final E o2) throws UnsupportedOperationException {
+    if (isLocked == false) {
+      checkChainIntegrity();
+      isLocked = true;
+    }
+
+    // iterate over all comparators in the chain
+    final Iterator<Comparator<E>> comparators = comparatorChain.iterator();
+    for (int comparatorIndex = 0; comparators.hasNext(); ++comparatorIndex) {
+
+      final Comparator<? super E> comparator = comparators.next();
+      int retval = comparator.compare(o1, o2);
+      if (retval != 0) {
+        // invert the order if it is a reverse sort
+        if (orderingBits.get(comparatorIndex) == true) {
+          if (retval > 0) {
+            retval = -1;
+          } else {
+            retval = 1;
+          }
+        }
+        return retval;
+      }
+    }
+
+    // if comparators are exhausted, return 0
+    return 0;
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Implement a hash code for this comparator that is consistent with {@link #equals(Object)
+   * equals}.
+   *
+   * @return a suitable hash code
+   * @since 3.0
+   */
+  @Override
+  public int hashCode() {
+    int hash = 0;
+    if (null != comparatorChain) {
+      hash ^= comparatorChain.hashCode();
+    }
+    if (null != orderingBits) {
+      hash ^= orderingBits.hashCode();
+    }
+    return hash;
+  }
+
+  /**
+   * Returns <code>true</code> iff <i>that</i> Object is is a {@link Comparator} whose ordering is
+   * known to be equivalent to mine.
+   *
+   * <p>This implementation returns <code>true</code> iff <code>
+   * <i>object</i>.{@link Object#getClass() getClass()}</code> equals <code>this.getClass()</code>,
+   * and the underlying comparators and order bits are equal. Subclasses may want to override this
+   * behavior to remain consistent with the {@link Comparator#equals(Object)} contract.
+   *
+   * @param object the object to compare with
+   * @return true if equal
+   * @since 3.0
+   */
+  @Override
+  public boolean equals(final Object object) {
+    if (this == object) {
+      return true;
+    }
+    if (null == object) {
+      return false;
+    }
+    if (object.getClass().equals(this.getClass())) {
+      final ComparatorChain<?> chain = (ComparatorChain<?>) object;
+      return (null == orderingBits
+              ? null == chain.orderingBits
+              : orderingBits.equals(chain.orderingBits))
+          && (null == comparatorChain
+              ? null == chain.comparatorChain
+              : comparatorChain.equals(chain.comparatorChain));
+    }
+    return false;
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/iterators/AbstractEmptyIterator.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/iterators/AbstractEmptyIterator.java
new file mode 100644
index 00000000..01085769
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/iterators/AbstractEmptyIterator.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tsfile.external.commons.collections4.iterators;
+
+import java.util.NoSuchElementException;
+
+/**
+ * Provides an implementation of an empty iterator.
+ *
+ * @since 3.1
+ */
+abstract class AbstractEmptyIterator<E> {
+
+  /** Constructor. */
+  protected AbstractEmptyIterator() {
+    super();
+  }
+
+  public boolean hasNext() {
+    return false;
+  }
+
+  public E next() {
+    throw new NoSuchElementException("Iterator contains no elements");
+  }
+
+  public boolean hasPrevious() {
+    return false;
+  }
+
+  public E previous() {
+    throw new NoSuchElementException("Iterator contains no elements");
+  }
+
+  public void remove() {
+    throw new IllegalStateException("Iterator contains no elements");
+  }
+
+  public void reset() {
+    // do nothing
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/iterators/AbstractEmptyMapIterator.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/iterators/AbstractEmptyMapIterator.java
new file mode 100644
index 00000000..ae45cf5c
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/iterators/AbstractEmptyMapIterator.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tsfile.external.commons.collections4.iterators;
+
+/**
+ * Provides an implementation of an empty map iterator.
+ *
+ * @param <K> the type of keys
+ * @param <V> the type of mapped values
+ * @since 4.0
+ */
+public abstract class AbstractEmptyMapIterator<K, V> extends AbstractEmptyIterator<K> {
+
+  /** Create a new AbstractEmptyMapIterator. */
+  public AbstractEmptyMapIterator() {
+    super();
+  }
+
+  public K getKey() {
+    throw new IllegalStateException("Iterator contains no elements");
+  }
+
+  public V getValue() {
+    throw new IllegalStateException("Iterator contains no elements");
+  }
+
+  public V setValue(final V value) {
+    throw new IllegalStateException("Iterator contains no elements");
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/iterators/EmptyIterator.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/iterators/EmptyIterator.java
new file mode 100644
index 00000000..f108487c
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/iterators/EmptyIterator.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tsfile.external.commons.collections4.iterators;
+
+import org.apache.tsfile.external.commons.collections4.ResettableIterator;
+
+import java.util.Iterator;
+
+/**
+ * Provides an implementation of an empty iterator.
+ *
+ * <p>This class provides an implementation of an empty iterator. This class provides for binary
+ * compatibility between Commons Collections 2.1.1 and 3.1 due to issues with <code>IteratorUtils
+ * </code>.
+ *
+ * @since 2.1.1 and 3.1
+ */
+public class EmptyIterator<E> extends AbstractEmptyIterator<E> implements ResettableIterator<E> {
+
+  /**
+   * Singleton instance of the iterator.
+   *
+   * @since 3.1
+   */
+  @SuppressWarnings("rawtypes")
+  public static final ResettableIterator RESETTABLE_INSTANCE = new EmptyIterator<>();
+
+  /**
+   * Singleton instance of the iterator.
+   *
+   * @since 2.1.1 and 3.1
+   */
+  @SuppressWarnings("rawtypes")
+  public static final Iterator INSTANCE = RESETTABLE_INSTANCE;
+
+  /**
+   * Get a typed resettable empty iterator instance.
+   *
+   * @param <E> the element type
+   * @return ResettableIterator&lt;E&gt;
+   */
+  public static <E> ResettableIterator<E> resettableEmptyIterator() {
+    return RESETTABLE_INSTANCE;
+  }
+
+  /**
+   * Get a typed empty iterator instance.
+   *
+   * @param <E> the element type
+   * @return Iterator&lt;E&gt;
+   */
+  public static <E> Iterator<E> emptyIterator() {
+    return INSTANCE;
+  }
+
+  /** Constructor. */
+  protected EmptyIterator() {
+    super();
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/iterators/EmptyMapIterator.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/iterators/EmptyMapIterator.java
new file mode 100644
index 00000000..3d27b12a
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/iterators/EmptyMapIterator.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tsfile.external.commons.collections4.iterators;
+
+import org.apache.tsfile.external.commons.collections4.MapIterator;
+import org.apache.tsfile.external.commons.collections4.ResettableIterator;
+
+/**
+ * Provides an implementation of an empty map iterator.
+ *
+ * @param <K> the type of keys
+ * @param <V> the type of mapped values
+ * @since 3.1
+ */
+public class EmptyMapIterator<K, V> extends AbstractEmptyMapIterator<K, V>
+    implements MapIterator<K, V>, ResettableIterator<K> {
+
+  /**
+   * Singleton instance of the iterator.
+   *
+   * @since 3.1
+   */
+  @SuppressWarnings("rawtypes")
+  public static final MapIterator INSTANCE = new EmptyMapIterator<>();
+
+  /**
+   * Get a typed instance of the iterator.
+   *
+   * @param <K> the key type
+   * @param <V> the value type
+   * @return {@link MapIterator}&lt;K, V&gt;
+   */
+  public static <K, V> MapIterator<K, V> emptyMapIterator() {
+    return INSTANCE;
+  }
+
+  /** Constructor. */
+  protected EmptyMapIterator() {
+    super();
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/iterators/EmptyOrderedIterator.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/iterators/EmptyOrderedIterator.java
new file mode 100644
index 00000000..ea144b26
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/iterators/EmptyOrderedIterator.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tsfile.external.commons.collections4.iterators;
+
+import org.apache.tsfile.external.commons.collections4.OrderedIterator;
+import org.apache.tsfile.external.commons.collections4.ResettableIterator;
+
+/**
+ * Provides an implementation of an empty ordered iterator.
+ *
+ * @param <E> the type to iterate.
+ * @since 3.1
+ */
+public class EmptyOrderedIterator<E> extends AbstractEmptyIterator<E>
+    implements OrderedIterator<E>, ResettableIterator<E> {
+
+  /**
+   * Singleton instance of the iterator.
+   *
+   * @since 3.1
+   */
+  @SuppressWarnings("rawtypes")
+  public static final OrderedIterator INSTANCE = new EmptyOrderedIterator<>();
+
+  /**
+   * Typed instance of the iterator.
+   *
+   * @param <E> the element type
+   * @return OrderedIterator&lt;E&gt;
+   */
+  public static <E> OrderedIterator<E> emptyOrderedIterator() {
+    return INSTANCE;
+  }
+
+  /** Constructor. */
+  protected EmptyOrderedIterator() {
+    super();
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/iterators/EmptyOrderedMapIterator.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/iterators/EmptyOrderedMapIterator.java
new file mode 100644
index 00000000..8a1b502c
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/iterators/EmptyOrderedMapIterator.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tsfile.external.commons.collections4.iterators;
+
+import org.apache.tsfile.external.commons.collections4.OrderedMapIterator;
+import org.apache.tsfile.external.commons.collections4.ResettableIterator;
+
+/**
+ * Provides an implementation of an empty ordered map iterator.
+ *
+ * @param <K> the type of keys
+ * @param <V> the type of mapped values
+ * @since 3.1
+ */
+public class EmptyOrderedMapIterator<K, V> extends AbstractEmptyMapIterator<K, V>
+    implements OrderedMapIterator<K, V>, ResettableIterator<K> {
+
+  /**
+   * Singleton instance of the iterator.
+   *
+   * @since 3.1
+   */
+  @SuppressWarnings("rawtypes")
+  public static final OrderedMapIterator INSTANCE = new EmptyOrderedMapIterator<>();
+
+  /**
+   * Get a typed instance of the iterator.
+   *
+   * @param <K> the key type
+   * @param <V> the value type
+   * @return {@link OrderedMapIterator}&lt;K, V&gt;
+   */
+  public static <K, V> OrderedMapIterator<K, V> emptyOrderedMapIterator() {
+    return INSTANCE;
+  }
+
+  /** Constructor. */
+  protected EmptyOrderedMapIterator() {
+    super();
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/map/AbstractHashedMap.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/map/AbstractHashedMap.java
new file mode 100644
index 00000000..47718bbb
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/map/AbstractHashedMap.java
@@ -0,0 +1,1394 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tsfile.external.commons.collections4.map;
+
+import org.apache.tsfile.external.commons.collections4.IterableMap;
+import org.apache.tsfile.external.commons.collections4.KeyValue;
+import org.apache.tsfile.external.commons.collections4.MapIterator;
+import org.apache.tsfile.external.commons.collections4.iterators.EmptyIterator;
+import org.apache.tsfile.external.commons.collections4.iterators.EmptyMapIterator;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.AbstractCollection;
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+/**
+ * An abstract implementation of a hash-based map which provides numerous points for subclasses to
+ * override.
+ *
+ * <p>This class implements all the features necessary for a subclass hash-based map. Key-value
+ * entries are stored in instances of the <code>HashEntry</code> class, which can be overridden and
+ * replaced. The iterators can similarly be replaced, without the need to replace the KeySet,
+ * EntrySet and Values view classes.
+ *
+ * <p>Overridable methods are provided to change the default hashing behaviour, and to change how
+ * entries are added to and removed from the map. Hopefully, all you need for unusual subclasses is
+ * here.
+ *
+ * <p>NOTE: From Commons Collections 3.1 this class extends AbstractMap. This is to provide
+ * backwards compatibility for ReferenceMap between v3.0 and v3.1. This extends clause will be
+ * removed in v5.0.
+ *
+ * @param <K> the type of the keys in this map
+ * @param <V> the type of the values in this map
+ * @since 3.0
+ */
+public class AbstractHashedMap<K, V> extends AbstractMap<K, V> implements IterableMap<K, V> {
+
+  protected static final String NO_NEXT_ENTRY = "No next() entry in the iteration";
+  protected static final String NO_PREVIOUS_ENTRY = "No previous() entry in the iteration";
+  protected static final String REMOVE_INVALID = "remove() can only be called once after next()";
+  protected static final String GETKEY_INVALID =
+      "getKey() can only be called after next() and before remove()";
+  protected static final String GETVALUE_INVALID =
+      "getValue() can only be called after next() and before remove()";
+  protected static final String SETVALUE_INVALID =
+      "setValue() can only be called after next() and before remove()";
+
+  /** The default capacity to use */
+  protected static final int DEFAULT_CAPACITY = 16;
+
+  /** The default threshold to use */
+  protected static final int DEFAULT_THRESHOLD = 12;
+
+  /** The default load factor to use */
+  protected static final float DEFAULT_LOAD_FACTOR = 0.75f;
+
+  /** The maximum capacity allowed */
+  protected static final int MAXIMUM_CAPACITY = 1 << 30;
+
+  /** An object for masking null */
+  protected static final Object NULL = new Object();
+
+  /** Load factor, normally 0.75 */
+  transient float loadFactor;
+
+  /** The size of the map */
+  transient int size;
+
+  /** Map entries */
+  transient HashEntry<K, V>[] data;
+
+  /** Size at which to rehash */
+  transient int threshold;
+
+  /** Modification count for iterators */
+  transient int modCount;
+
+  /** Entry set */
+  transient EntrySet<K, V> entrySet;
+
+  /** Key set */
+  transient KeySet<K> keySet;
+
+  /** Values */
+  transient Values<V> values;
+
+  /** Constructor only used in deserialization, do not use otherwise. */
+  protected AbstractHashedMap() {
+    super();
+  }
+
+  /**
+   * Constructor which performs no validation on the passed in parameters.
+   *
+   * @param initialCapacity the initial capacity, must be a power of two
+   * @param loadFactor the load factor, must be &gt; 0.0f and generally &lt; 1.0f
+   * @param threshold the threshold, must be sensible
+   */
+  @SuppressWarnings("unchecked")
+  protected AbstractHashedMap(
+      final int initialCapacity, final float loadFactor, final int threshold) {
+    super();
+    this.loadFactor = loadFactor;
+    this.data = new HashEntry[initialCapacity];
+    this.threshold = threshold;
+    init();
+  }
+
+  /**
+   * Constructs a new, empty map with the specified initial capacity and default load factor.
+   *
+   * @param initialCapacity the initial capacity
+   * @throws IllegalArgumentException if the initial capacity is negative
+   */
+  protected AbstractHashedMap(final int initialCapacity) {
+    this(initialCapacity, DEFAULT_LOAD_FACTOR);
+  }
+
+  /**
+   * Constructs a new, empty map with the specified initial capacity and load factor.
+   *
+   * @param initialCapacity the initial capacity
+   * @param loadFactor the load factor
+   * @throws IllegalArgumentException if the initial capacity is negative
+   * @throws IllegalArgumentException if the load factor is less than or equal to zero
+   */
+  @SuppressWarnings("unchecked")
+  protected AbstractHashedMap(int initialCapacity, final float loadFactor) {
+    super();
+    if (initialCapacity < 0) {
+      throw new IllegalArgumentException("Initial capacity must be a non negative number");
+    }
+    if (loadFactor <= 0.0f || Float.isNaN(loadFactor)) {
+      throw new IllegalArgumentException("Load factor must be greater than 0");
+    }
+    this.loadFactor = loadFactor;
+    initialCapacity = calculateNewCapacity(initialCapacity);
+    this.threshold = calculateThreshold(initialCapacity, loadFactor);
+    this.data = new HashEntry[initialCapacity];
+    init();
+  }
+
+  /**
+   * Constructor copying elements from another map.
+   *
+   * @param map the map to copy
+   * @throws NullPointerException if the map is null
+   */
+  protected AbstractHashedMap(final Map<? extends K, ? extends V> map) {
+    this(Math.max(2 * map.size(), DEFAULT_CAPACITY), DEFAULT_LOAD_FACTOR);
+    _putAll(map);
+  }
+
+  /** Initialise subclasses during construction, cloning or deserialization. */
+  protected void init() {}
+
+  // -----------------------------------------------------------------------
+  /**
+   * Gets the value mapped to the key specified.
+   *
+   * @param key the key
+   * @return the mapped value, null if no match
+   */
+  @Override
+  public V get(Object key) {
+    key = convertKey(key);
+    final int hashCode = hash(key);
+    HashEntry<K, V> entry = data[hashIndex(hashCode, data.length)]; // no local for hash index
+    while (entry != null) {
+      if (entry.hashCode == hashCode && isEqualKey(key, entry.key)) {
+        return entry.getValue();
+      }
+      entry = entry.next;
+    }
+    return null;
+  }
+
+  /**
+   * Gets the size of the map.
+   *
+   * @return the size
+   */
+  @Override
+  public int size() {
+    return size;
+  }
+
+  /**
+   * Checks whether the map is currently empty.
+   *
+   * @return true if the map is currently size zero
+   */
+  @Override
+  public boolean isEmpty() {
+    return size == 0;
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Checks whether the map contains the specified key.
+   *
+   * @param key the key to search for
+   * @return true if the map contains the key
+   */
+  @Override
+  public boolean containsKey(Object key) {
+    key = convertKey(key);
+    final int hashCode = hash(key);
+    HashEntry<K, V> entry = data[hashIndex(hashCode, data.length)]; // no local for hash index
+    while (entry != null) {
+      if (entry.hashCode == hashCode && isEqualKey(key, entry.key)) {
+        return true;
+      }
+      entry = entry.next;
+    }
+    return false;
+  }
+
+  /**
+   * Checks whether the map contains the specified value.
+   *
+   * @param value the value to search for
+   * @return true if the map contains the value
+   */
+  @Override
+  public boolean containsValue(final Object value) {
+    if (value == null) {
+      for (final HashEntry<K, V> element : data) {
+        HashEntry<K, V> entry = element;
+        while (entry != null) {
+          if (entry.getValue() == null) {
+            return true;
+          }
+          entry = entry.next;
+        }
+      }
+    } else {
+      for (final HashEntry<K, V> element : data) {
+        HashEntry<K, V> entry = element;
+        while (entry != null) {
+          if (isEqualValue(value, entry.getValue())) {
+            return true;
+          }
+          entry = entry.next;
+        }
+      }
+    }
+    return false;
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Puts a key-value mapping into this map.
+   *
+   * @param key the key to add
+   * @param value the value to add
+   * @return the value previously mapped to this key, null if none
+   */
+  @Override
+  public V put(final K key, final V value) {
+    final Object convertedKey = convertKey(key);
+    final int hashCode = hash(convertedKey);
+    final int index = hashIndex(hashCode, data.length);
+    HashEntry<K, V> entry = data[index];
+    while (entry != null) {
+      if (entry.hashCode == hashCode && isEqualKey(convertedKey, entry.key)) {
+        final V oldValue = entry.getValue();
+        updateEntry(entry, value);
+        return oldValue;
+      }
+      entry = entry.next;
+    }
+
+    addMapping(index, hashCode, key, value);
+    return null;
+  }
+
+  /**
+   * Puts all the values from the specified map into this map.
+   *
+   * <p>This implementation iterates around the specified map and uses {@link #put(Object, Object)}.
+   *
+   * @param map the map to add
+   * @throws NullPointerException if the map is null
+   */
+  @Override
+  public void putAll(final Map<? extends K, ? extends V> map) {
+    _putAll(map);
+  }
+
+  /**
+   * Puts all the values from the specified map into this map.
+   *
+   * <p>This implementation iterates around the specified map and uses {@link #put(Object, Object)}.
+   *
+   * <p>It is private to allow the constructor to still call it even when putAll is overriden.
+   *
+   * @param map the map to add
+   * @throws NullPointerException if the map is null
+   */
+  private void _putAll(final Map<? extends K, ? extends V> map) {
+    final int mapSize = map.size();
+    if (mapSize == 0) {
+      return;
+    }
+    final int newSize = (int) ((size + mapSize) / loadFactor + 1);
+    ensureCapacity(calculateNewCapacity(newSize));
+    for (final Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
+      put(entry.getKey(), entry.getValue());
+    }
+  }
+
+  /**
+   * Removes the specified mapping from this map.
+   *
+   * @param key the mapping to remove
+   * @return the value mapped to the removed key, null if key not in map
+   */
+  @Override
+  public V remove(Object key) {
+    key = convertKey(key);
+    final int hashCode = hash(key);
+    final int index = hashIndex(hashCode, data.length);
+    HashEntry<K, V> entry = data[index];
+    HashEntry<K, V> previous = null;
+    while (entry != null) {
+      if (entry.hashCode == hashCode && isEqualKey(key, entry.key)) {
+        final V oldValue = entry.getValue();
+        removeMapping(entry, index, previous);
+        return oldValue;
+      }
+      previous = entry;
+      entry = entry.next;
+    }
+    return null;
+  }
+
+  /**
+   * Clears the map, resetting the size to zero and nullifying references to avoid garbage
+   * collection issues.
+   */
+  @Override
+  public void clear() {
+    modCount++;
+    final HashEntry<K, V>[] data = this.data;
+    for (int i = data.length - 1; i >= 0; i--) {
+      data[i] = null;
+    }
+    size = 0;
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Converts input keys to another object for storage in the map. This implementation masks nulls.
+   * Subclasses can override this to perform alternate key conversions.
+   *
+   * <p>The reverse conversion can be changed, if required, by overriding the getKey() method in the
+   * hash entry.
+   *
+   * @param key the key convert
+   * @return the converted key
+   */
+  protected Object convertKey(final Object key) {
+    return key == null ? NULL : key;
+  }
+
+  /**
+   * Gets the hash code for the key specified. This implementation uses the additional hashing
+   * routine from JDK1.4. Subclasses can override this to return alternate hash codes.
+   *
+   * @param key the key to get a hash code for
+   * @return the hash code
+   */
+  protected int hash(final Object key) {
+    // same as JDK 1.4
+    int h = key.hashCode();
+    h += ~(h << 9);
+    h ^= h >>> 14;
+    h += h << 4;
+    h ^= h >>> 10;
+    return h;
+  }
+
+  /**
+   * Compares two keys, in internal converted form, to see if they are equal. This implementation
+   * uses the equals method and assumes neither key is null. Subclasses can override this to match
+   * differently.
+   *
+   * @param key1 the first key to compare passed in from outside
+   * @param key2 the second key extracted from the entry via <code>entry.key</code>
+   * @return true if equal
+   */
+  protected boolean isEqualKey(final Object key1, final Object key2) {
+    return key1 == key2 || key1.equals(key2);
+  }
+
+  /**
+   * Compares two values, in external form, to see if they are equal. This implementation uses the
+   * equals method and assumes neither value is null. Subclasses can override this to match
+   * differently.
+   *
+   * @param value1 the first value to compare passed in from outside
+   * @param value2 the second value extracted from the entry via <code>getValue()</code>
+   * @return true if equal
+   */
+  protected boolean isEqualValue(final Object value1, final Object value2) {
+    return value1 == value2 || value1.equals(value2);
+  }
+
+  /**
+   * Gets the index into the data storage for the hashCode specified. This implementation uses the
+   * least significant bits of the hashCode. Subclasses can override this to return alternate
+   * bucketing.
+   *
+   * @param hashCode the hash code to use
+   * @param dataSize the size of the data to pick a bucket from
+   * @return the bucket index
+   */
+  protected int hashIndex(final int hashCode, final int dataSize) {
+    return hashCode & dataSize - 1;
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Gets the entry mapped to the key specified.
+   *
+   * <p>This method exists for subclasses that may need to perform a multi-step process accessing
+   * the entry. The public methods in this class don't use this method to gain a small performance
+   * boost.
+   *
+   * @param key the key
+   * @return the entry, null if no match
+   */
+  protected HashEntry<K, V> getEntry(Object key) {
+    key = convertKey(key);
+    final int hashCode = hash(key);
+    HashEntry<K, V> entry = data[hashIndex(hashCode, data.length)]; // no local for hash index
+    while (entry != null) {
+      if (entry.hashCode == hashCode && isEqualKey(key, entry.key)) {
+        return entry;
+      }
+      entry = entry.next;
+    }
+    return null;
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Updates an existing key-value mapping to change the value.
+   *
+   * <p>This implementation calls <code>setValue()</code> on the entry. Subclasses could override to
+   * handle changes to the map.
+   *
+   * @param entry the entry to update
+   * @param newValue the new value to store
+   */
+  protected void updateEntry(final HashEntry<K, V> entry, final V newValue) {
+    entry.setValue(newValue);
+  }
+
+  /**
+   * Reuses an existing key-value mapping, storing completely new data.
+   *
+   * <p>This implementation sets all the data fields on the entry. Subclasses could populate
+   * additional entry fields.
+   *
+   * @param entry the entry to update, not null
+   * @param hashIndex the index in the data array
+   * @param hashCode the hash code of the key to add
+   * @param key the key to add
+   * @param value the value to add
+   */
+  protected void reuseEntry(
+      final HashEntry<K, V> entry,
+      final int hashIndex,
+      final int hashCode,
+      final K key,
+      final V value) {
+    entry.next = data[hashIndex];
+    entry.hashCode = hashCode;
+    entry.key = key;
+    entry.value = value;
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Adds a new key-value mapping into this map.
+   *
+   * <p>This implementation calls <code>createEntry()</code>, <code>addEntry()</code> and <code>
+   * checkCapacity()</code>. It also handles changes to <code>modCount</code> and <code>size</code>.
+   * Subclasses could override to fully control adds to the map.
+   *
+   * @param hashIndex the index into the data array to store at
+   * @param hashCode the hash code of the key to add
+   * @param key the key to add
+   * @param value the value to add
+   */
+  protected void addMapping(final int hashIndex, final int hashCode, final K key, final V value) {
+    modCount++;
+    final HashEntry<K, V> entry = createEntry(data[hashIndex], hashCode, key, value);
+    addEntry(entry, hashIndex);
+    size++;
+    checkCapacity();
+  }
+
+  /**
+   * Creates an entry to store the key-value data.
+   *
+   * <p>This implementation creates a new HashEntry instance. Subclasses can override this to return
+   * a different storage class, or implement caching.
+   *
+   * @param next the next entry in sequence
+   * @param hashCode the hash code to use
+   * @param key the key to store
+   * @param value the value to store
+   * @return the newly created entry
+   */
+  protected HashEntry<K, V> createEntry(
+      final HashEntry<K, V> next, final int hashCode, final K key, final V value) {
+    return new HashEntry<>(next, hashCode, convertKey(key), value);
+  }
+
+  /**
+   * Adds an entry into this map.
+   *
+   * <p>This implementation adds the entry to the data storage table. Subclasses could override to
+   * handle changes to the map.
+   *
+   * @param entry the entry to add
+   * @param hashIndex the index into the data array to store at
+   */
+  protected void addEntry(final HashEntry<K, V> entry, final int hashIndex) {
+    data[hashIndex] = entry;
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Removes a mapping from the map.
+   *
+   * <p>This implementation calls <code>removeEntry()</code> and <code>destroyEntry()</code>. It
+   * also handles changes to <code>modCount</code> and <code>size</code>. Subclasses could override
+   * to fully control removals from the map.
+   *
+   * @param entry the entry to remove
+   * @param hashIndex the index into the data structure
+   * @param previous the previous entry in the chain
+   */
+  protected void removeMapping(
+      final HashEntry<K, V> entry, final int hashIndex, final HashEntry<K, V> previous) {
+    modCount++;
+    removeEntry(entry, hashIndex, previous);
+    size--;
+    destroyEntry(entry);
+  }
+
+  /**
+   * Removes an entry from the chain stored in a particular index.
+   *
+   * <p>This implementation removes the entry from the data storage table. The size is not updated.
+   * Subclasses could override to handle changes to the map.
+   *
+   * @param entry the entry to remove
+   * @param hashIndex the index into the data structure
+   * @param previous the previous entry in the chain
+   */
+  protected void removeEntry(
+      final HashEntry<K, V> entry, final int hashIndex, final HashEntry<K, V> previous) {
+    if (previous == null) {
+      data[hashIndex] = entry.next;
+    } else {
+      previous.next = entry.next;
+    }
+  }
+
+  /**
+   * Kills an entry ready for the garbage collector.
+   *
+   * <p>This implementation prepares the HashEntry for garbage collection. Subclasses can override
+   * this to implement caching (override clear as well).
+   *
+   * @param entry the entry to destroy
+   */
+  protected void destroyEntry(final HashEntry<K, V> entry) {
+    entry.next = null;
+    entry.key = null;
+    entry.value = null;
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Checks the capacity of the map and enlarges it if necessary.
+   *
+   * <p>This implementation uses the threshold to check if the map needs enlarging
+   */
+  protected void checkCapacity() {
+    if (size >= threshold) {
+      final int newCapacity = data.length * 2;
+      if (newCapacity <= MAXIMUM_CAPACITY) {
+        ensureCapacity(newCapacity);
+      }
+    }
+  }
+
+  /**
+   * Changes the size of the data structure to the capacity proposed.
+   *
+   * @param newCapacity the new capacity of the array (a power of two, less or equal to max)
+   */
+  @SuppressWarnings("unchecked")
+  protected void ensureCapacity(final int newCapacity) {
+    final int oldCapacity = data.length;
+    if (newCapacity <= oldCapacity) {
+      return;
+    }
+    if (size == 0) {
+      threshold = calculateThreshold(newCapacity, loadFactor);
+      data = new HashEntry[newCapacity];
+    } else {
+      final HashEntry<K, V> oldEntries[] = data;
+      final HashEntry<K, V> newEntries[] = new HashEntry[newCapacity];
+
+      modCount++;
+      for (int i = oldCapacity - 1; i >= 0; i--) {
+        HashEntry<K, V> entry = oldEntries[i];
+        if (entry != null) {
+          oldEntries[i] = null; // gc
+          do {
+            final HashEntry<K, V> next = entry.next;
+            final int index = hashIndex(entry.hashCode, newCapacity);
+            entry.next = newEntries[index];
+            newEntries[index] = entry;
+            entry = next;
+          } while (entry != null);
+        }
+      }
+      threshold = calculateThreshold(newCapacity, loadFactor);
+      data = newEntries;
+    }
+  }
+
+  /**
+   * Calculates the new capacity of the map. This implementation normalizes the capacity to a power
+   * of two.
+   *
+   * @param proposedCapacity the proposed capacity
+   * @return the normalized new capacity
+   */
+  protected int calculateNewCapacity(final int proposedCapacity) {
+    int newCapacity = 1;
+    if (proposedCapacity > MAXIMUM_CAPACITY) {
+      newCapacity = MAXIMUM_CAPACITY;
+    } else {
+      while (newCapacity < proposedCapacity) {
+        newCapacity <<= 1; // multiply by two
+      }
+      if (newCapacity > MAXIMUM_CAPACITY) {
+        newCapacity = MAXIMUM_CAPACITY;
+      }
+    }
+    return newCapacity;
+  }
+
+  /**
+   * Calculates the new threshold of the map, where it will be resized. This implementation uses the
+   * load factor.
+   *
+   * @param newCapacity the new capacity
+   * @param factor the load factor
+   * @return the new resize threshold
+   */
+  protected int calculateThreshold(final int newCapacity, final float factor) {
+    return (int) (newCapacity * factor);
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Gets the <code>next</code> field from a <code>HashEntry</code>. Used in subclasses that have no
+   * visibility of the field.
+   *
+   * @param entry the entry to query, must not be null
+   * @return the <code>next</code> field of the entry
+   * @throws NullPointerException if the entry is null
+   * @since 3.1
+   */
+  protected HashEntry<K, V> entryNext(final HashEntry<K, V> entry) {
+    return entry.next;
+  }
+
+  /**
+   * Gets the <code>hashCode</code> field from a <code>HashEntry</code>. Used in subclasses that
+   * have no visibility of the field.
+   *
+   * @param entry the entry to query, must not be null
+   * @return the <code>hashCode</code> field of the entry
+   * @throws NullPointerException if the entry is null
+   * @since 3.1
+   */
+  protected int entryHashCode(final HashEntry<K, V> entry) {
+    return entry.hashCode;
+  }
+
+  /**
+   * Gets the <code>key</code> field from a <code>HashEntry</code>. Used in subclasses that have no
+   * visibility of the field.
+   *
+   * @param entry the entry to query, must not be null
+   * @return the <code>key</code> field of the entry
+   * @throws NullPointerException if the entry is null
+   * @since 3.1
+   */
+  protected K entryKey(final HashEntry<K, V> entry) {
+    return entry.getKey();
+  }
+
+  /**
+   * Gets the <code>value</code> field from a <code>HashEntry</code>. Used in subclasses that have
+   * no visibility of the field.
+   *
+   * @param entry the entry to query, must not be null
+   * @return the <code>value</code> field of the entry
+   * @throws NullPointerException if the entry is null
+   * @since 3.1
+   */
+  protected V entryValue(final HashEntry<K, V> entry) {
+    return entry.getValue();
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Gets an iterator over the map. Changes made to the iterator affect this map.
+   *
+   * <p>A MapIterator returns the keys in the map. It also provides convenient methods to get the
+   * key and value, and set the value. It avoids the need to create an entrySet/keySet/values
+   * object. It also avoids creating the Map.Entry object.
+   *
+   * @return the map iterator
+   */
+  @Override
+  public MapIterator<K, V> mapIterator() {
+    if (size == 0) {
+      return EmptyMapIterator.<K, V>emptyMapIterator();
+    }
+    return new HashMapIterator<>(this);
+  }
+
+  /** MapIterator implementation. */
+  protected static class HashMapIterator<K, V> extends HashIterator<K, V>
+      implements MapIterator<K, V> {
+
+    protected HashMapIterator(final AbstractHashedMap<K, V> parent) {
+      super(parent);
+    }
+
+    @Override
+    public K next() {
+      return super.nextEntry().getKey();
+    }
+
+    @Override
+    public K getKey() {
+      final HashEntry<K, V> current = currentEntry();
+      if (current == null) {
+        throw new IllegalStateException(AbstractHashedMap.GETKEY_INVALID);
+      }
+      return current.getKey();
+    }
+
+    @Override
+    public V getValue() {
+      final HashEntry<K, V> current = currentEntry();
+      if (current == null) {
+        throw new IllegalStateException(AbstractHashedMap.GETVALUE_INVALID);
+      }
+      return current.getValue();
+    }
+
+    @Override
+    public V setValue(final V value) {
+      final HashEntry<K, V> current = currentEntry();
+      if (current == null) {
+        throw new IllegalStateException(AbstractHashedMap.SETVALUE_INVALID);
+      }
+      return current.setValue(value);
+    }
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Gets the entrySet view of the map. Changes made to the view affect this map. To simply iterate
+   * through the entries, use {@link #mapIterator()}.
+   *
+   * @return the entrySet view
+   */
+  @Override
+  public Set<Map.Entry<K, V>> entrySet() {
+    if (entrySet == null) {
+      entrySet = new EntrySet<>(this);
+    }
+    return entrySet;
+  }
+
+  /**
+   * Creates an entry set iterator. Subclasses can override this to return iterators with different
+   * properties.
+   *
+   * @return the entrySet iterator
+   */
+  protected Iterator<Map.Entry<K, V>> createEntrySetIterator() {
+    if (size() == 0) {
+      return EmptyIterator.<Map.Entry<K, V>>emptyIterator();
+    }
+    return new EntrySetIterator<>(this);
+  }
+
+  /** EntrySet implementation. */
+  protected static class EntrySet<K, V> extends AbstractSet<Map.Entry<K, V>> {
+    /** The parent map */
+    private final AbstractHashedMap<K, V> parent;
+
+    protected EntrySet(final AbstractHashedMap<K, V> parent) {
+      super();
+      this.parent = parent;
+    }
+
+    @Override
+    public int size() {
+      return parent.size();
+    }
+
+    @Override
+    public void clear() {
+      parent.clear();
+    }
+
+    @Override
+    public boolean contains(final Object entry) {
+      if (entry instanceof Map.Entry) {
+        final Map.Entry<?, ?> e = (Map.Entry<?, ?>) entry;
+        final Entry<K, V> match = parent.getEntry(e.getKey());
+        return match != null && match.equals(e);
+      }
+      return false;
+    }
+
+    @Override
+    public boolean remove(final Object obj) {
+      if (obj instanceof Map.Entry == false) {
+        return false;
+      }
+      if (contains(obj) == false) {
+        return false;
+      }
+      final Map.Entry<?, ?> entry = (Map.Entry<?, ?>) obj;
+      parent.remove(entry.getKey());
+      return true;
+    }
+
+    @Override
+    public Iterator<Map.Entry<K, V>> iterator() {
+      return parent.createEntrySetIterator();
+    }
+  }
+
+  /** EntrySet iterator. */
+  protected static class EntrySetIterator<K, V> extends HashIterator<K, V>
+      implements Iterator<Map.Entry<K, V>> {
+
+    protected EntrySetIterator(final AbstractHashedMap<K, V> parent) {
+      super(parent);
+    }
+
+    @Override
+    public Map.Entry<K, V> next() {
+      return super.nextEntry();
+    }
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Gets the keySet view of the map. Changes made to the view affect this map. To simply iterate
+   * through the keys, use {@link #mapIterator()}.
+   *
+   * @return the keySet view
+   */
+  @Override
+  public Set<K> keySet() {
+    if (keySet == null) {
+      keySet = new KeySet<>(this);
+    }
+    return keySet;
+  }
+
+  /**
+   * Creates a key set iterator. Subclasses can override this to return iterators with different
+   * properties.
+   *
+   * @return the keySet iterator
+   */
+  protected Iterator<K> createKeySetIterator() {
+    if (size() == 0) {
+      return EmptyIterator.<K>emptyIterator();
+    }
+    return new KeySetIterator<>(this);
+  }
+
+  /** KeySet implementation. */
+  protected static class KeySet<K> extends AbstractSet<K> {
+    /** The parent map */
+    private final AbstractHashedMap<K, ?> parent;
+
+    protected KeySet(final AbstractHashedMap<K, ?> parent) {
+      super();
+      this.parent = parent;
+    }
+
+    @Override
+    public int size() {
+      return parent.size();
+    }
+
+    @Override
+    public void clear() {
+      parent.clear();
+    }
+
+    @Override
+    public boolean contains(final Object key) {
+      return parent.containsKey(key);
+    }
+
+    @Override
+    public boolean remove(final Object key) {
+      final boolean result = parent.containsKey(key);
+      parent.remove(key);
+      return result;
+    }
+
+    @Override
+    public Iterator<K> iterator() {
+      return parent.createKeySetIterator();
+    }
+  }
+
+  /** KeySet iterator. */
+  protected static class KeySetIterator<K> extends HashIterator<K, Object> implements Iterator<K> {
+
+    @SuppressWarnings("unchecked")
+    protected KeySetIterator(final AbstractHashedMap<K, ?> parent) {
+      super((AbstractHashedMap<K, Object>) parent);
+    }
+
+    @Override
+    public K next() {
+      return super.nextEntry().getKey();
+    }
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Gets the values view of the map. Changes made to the view affect this map. To simply iterate
+   * through the values, use {@link #mapIterator()}.
+   *
+   * @return the values view
+   */
+  @Override
+  public Collection<V> values() {
+    if (values == null) {
+      values = new Values<>(this);
+    }
+    return values;
+  }
+
+  /**
+   * Creates a values iterator. Subclasses can override this to return iterators with different
+   * properties.
+   *
+   * @return the values iterator
+   */
+  protected Iterator<V> createValuesIterator() {
+    if (size() == 0) {
+      return EmptyIterator.<V>emptyIterator();
+    }
+    return new ValuesIterator<>(this);
+  }
+
+  /** Values implementation. */
+  protected static class Values<V> extends AbstractCollection<V> {
+    /** The parent map */
+    private final AbstractHashedMap<?, V> parent;
+
+    protected Values(final AbstractHashedMap<?, V> parent) {
+      super();
+      this.parent = parent;
+    }
+
+    @Override
+    public int size() {
+      return parent.size();
+    }
+
+    @Override
+    public void clear() {
+      parent.clear();
+    }
+
+    @Override
+    public boolean contains(final Object value) {
+      return parent.containsValue(value);
+    }
+
+    @Override
+    public Iterator<V> iterator() {
+      return parent.createValuesIterator();
+    }
+  }
+
+  /** Values iterator. */
+  protected static class ValuesIterator<V> extends HashIterator<Object, V> implements Iterator<V> {
+
+    @SuppressWarnings("unchecked")
+    protected ValuesIterator(final AbstractHashedMap<?, V> parent) {
+      super((AbstractHashedMap<Object, V>) parent);
+    }
+
+    @Override
+    public V next() {
+      return super.nextEntry().getValue();
+    }
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * HashEntry used to store the data.
+   *
+   * <p>If you subclass <code>AbstractHashedMap</code> but not <code>HashEntry</code> then you will
+   * not be able to access the protected fields. The <code>entryXxx()</code> methods on <code>
+   * AbstractHashedMap</code> exist to provide the necessary access.
+   */
+  protected static class HashEntry<K, V> implements Map.Entry<K, V>, KeyValue<K, V> {
+    /** The next entry in the hash chain */
+    protected HashEntry<K, V> next;
+
+    /** The hash code of the key */
+    protected int hashCode;
+
+    /** The key */
+    protected Object key;
+
+    /** The value */
+    protected Object value;
+
+    protected HashEntry(
+        final HashEntry<K, V> next, final int hashCode, final Object key, final V value) {
+      super();
+      this.next = next;
+      this.hashCode = hashCode;
+      this.key = key;
+      this.value = value;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public K getKey() {
+      if (key == NULL) {
+        return null;
+      }
+      return (K) key;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public V getValue() {
+      return (V) value;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public V setValue(final V value) {
+      final Object old = this.value;
+      this.value = value;
+      return (V) old;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+      if (obj == this) {
+        return true;
+      }
+      if (obj instanceof Map.Entry == false) {
+        return false;
+      }
+      final Map.Entry<?, ?> other = (Map.Entry<?, ?>) obj;
+      return (getKey() == null ? other.getKey() == null : getKey().equals(other.getKey()))
+          && (getValue() == null ? other.getValue() == null : getValue().equals(other.getValue()));
+    }
+
+    @Override
+    public int hashCode() {
+      return (getKey() == null ? 0 : getKey().hashCode())
+          ^ (getValue() == null ? 0 : getValue().hashCode());
+    }
+
+    @Override
+    public String toString() {
+      return new StringBuilder().append(getKey()).append('=').append(getValue()).toString();
+    }
+  }
+
+  /** Base Iterator */
+  protected abstract static class HashIterator<K, V> {
+
+    /** The parent map */
+    private final AbstractHashedMap<K, V> parent;
+
+    /** The current index into the array of buckets */
+    private int hashIndex;
+
+    /** The last returned entry */
+    private HashEntry<K, V> last;
+
+    /** The next entry */
+    private HashEntry<K, V> next;
+
+    /** The modification count expected */
+    private int expectedModCount;
+
+    protected HashIterator(final AbstractHashedMap<K, V> parent) {
+      super();
+      this.parent = parent;
+      final HashEntry<K, V>[] data = parent.data;
+      int i = data.length;
+      HashEntry<K, V> next = null;
+      while (i > 0 && next == null) {
+        next = data[--i];
+      }
+      this.next = next;
+      this.hashIndex = i;
+      this.expectedModCount = parent.modCount;
+    }
+
+    public boolean hasNext() {
+      return next != null;
+    }
+
+    protected HashEntry<K, V> nextEntry() {
+      if (parent.modCount != expectedModCount) {
+        throw new ConcurrentModificationException();
+      }
+      final HashEntry<K, V> newCurrent = next;
+      if (newCurrent == null) {
+        throw new NoSuchElementException(AbstractHashedMap.NO_NEXT_ENTRY);
+      }
+      final HashEntry<K, V>[] data = parent.data;
+      int i = hashIndex;
+      HashEntry<K, V> n = newCurrent.next;
+      while (n == null && i > 0) {
+        n = data[--i];
+      }
+      next = n;
+      hashIndex = i;
+      last = newCurrent;
+      return newCurrent;
+    }
+
+    protected HashEntry<K, V> currentEntry() {
+      return last;
+    }
+
+    public void remove() {
+      if (last == null) {
+        throw new IllegalStateException(AbstractHashedMap.REMOVE_INVALID);
+      }
+      if (parent.modCount != expectedModCount) {
+        throw new ConcurrentModificationException();
+      }
+      parent.remove(last.getKey());
+      last = null;
+      expectedModCount = parent.modCount;
+    }
+
+    @Override
+    public String toString() {
+      if (last != null) {
+        return "Iterator[" + last.getKey() + "=" + last.getValue() + "]";
+      }
+      return "Iterator[]";
+    }
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Writes the map data to the stream. This method must be overridden if a subclass must be setup
+   * before <code>put()</code> is used.
+   *
+   * <p>Serialization is not one of the JDK's nicest topics. Normal serialization will initialise
+   * the superclass before the subclass. Sometimes however, this isn't what you want, as in this
+   * case the <code>put()</code> method on read can be affected by subclass state.
+   *
+   * <p>The solution adopted here is to serialize the state data of this class in this protected
+   * method. This method must be called by the <code>writeObject()</code> of the first serializable
+   * subclass.
+   *
+   * <p>Subclasses may override if they have a specific field that must be present on read before
+   * this implementation will work. Generally, the read determines what must be serialized here, if
+   * anything.
+   *
+   * @param out the output stream
+   * @throws IOException if an error occurs while writing tothe stream
+   */
+  protected void doWriteObject(final ObjectOutputStream out) throws IOException {
+    out.writeFloat(loadFactor);
+    out.writeInt(data.length);
+    out.writeInt(size);
+    for (final MapIterator<K, V> it = mapIterator(); it.hasNext(); ) {
+      out.writeObject(it.next());
+      out.writeObject(it.getValue());
+    }
+  }
+
+  /**
+   * Reads the map data from the stream. This method must be overridden if a subclass must be setup
+   * before <code>put()</code> is used.
+   *
+   * <p>Serialization is not one of the JDK's nicest topics. Normal serialization will initialise
+   * the superclass before the subclass. Sometimes however, this isn't what you want, as in this
+   * case the <code>put()</code> method on read can be affected by subclass state.
+   *
+   * <p>The solution adopted here is to deserialize the state data of this class in this protected
+   * method. This method must be called by the <code>readObject()</code> of the first serializable
+   * subclass.
+   *
+   * <p>Subclasses may override if the subclass has a specific field that must be present before
+   * <code>put()</code> or <code>calculateThreshold()</code> will work correctly.
+   *
+   * @param in the input stream
+   * @throws IOException if an error occurs while reading from the stream
+   * @throws ClassNotFoundException if an object read from the stream can not be loaded
+   */
+  @SuppressWarnings("unchecked")
+  protected void doReadObject(final ObjectInputStream in)
+      throws IOException, ClassNotFoundException {
+    loadFactor = in.readFloat();
+    final int capacity = in.readInt();
+    final int size = in.readInt();
+    init();
+    threshold = calculateThreshold(capacity, loadFactor);
+    data = new HashEntry[capacity];
+    for (int i = 0; i < size; i++) {
+      final K key = (K) in.readObject();
+      final V value = (V) in.readObject();
+      put(key, value);
+    }
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Clones the map without cloning the keys or values.
+   *
+   * <p>To implement <code>clone()</code>, a subclass must implement the <code>Cloneable</code>
+   * interface and make this method public.
+   *
+   * @return a shallow clone
+   * @throws InternalError if {@link AbstractMap#clone()} failed
+   */
+  @Override
+  @SuppressWarnings("unchecked")
+  protected AbstractHashedMap<K, V> clone() {
+    try {
+      final AbstractHashedMap<K, V> cloned = (AbstractHashedMap<K, V>) super.clone();
+      cloned.data = new HashEntry[data.length];
+      cloned.entrySet = null;
+      cloned.keySet = null;
+      cloned.values = null;
+      cloned.modCount = 0;
+      cloned.size = 0;
+      cloned.init();
+      cloned.putAll(this);
+      return cloned;
+    } catch (final CloneNotSupportedException ex) {
+      throw new InternalError();
+    }
+  }
+
+  /**
+   * Compares this map with another.
+   *
+   * @param obj the object to compare to
+   * @return true if equal
+   */
+  @Override
+  public boolean equals(final Object obj) {
+    if (obj == this) {
+      return true;
+    }
+    if (obj instanceof Map == false) {
+      return false;
+    }
+    final Map<?, ?> map = (Map<?, ?>) obj;
+    if (map.size() != size()) {
+      return false;
+    }
+    final MapIterator<?, ?> it = mapIterator();
+    try {
+      while (it.hasNext()) {
+        final Object key = it.next();
+        final Object value = it.getValue();
+        if (value == null) {
+          if (map.get(key) != null || map.containsKey(key) == false) {
+            return false;
+          }
+        } else {
+          if (value.equals(map.get(key)) == false) {
+            return false;
+          }
+        }
+      }
+    } catch (final ClassCastException ignored) {
+      return false;
+    } catch (final NullPointerException ignored) {
+      return false;
+    }
+    return true;
+  }
+
+  /**
+   * Gets the standard Map hashCode.
+   *
+   * @return the hash code defined in the Map interface
+   */
+  @Override
+  public int hashCode() {
+    int total = 0;
+    final Iterator<Map.Entry<K, V>> it = createEntrySetIterator();
+    while (it.hasNext()) {
+      total += it.next().hashCode();
+    }
+    return total;
+  }
+
+  /**
+   * Gets the map as a String.
+   *
+   * @return a string version of the map
+   */
+  @Override
+  public String toString() {
+    if (size() == 0) {
+      return "{}";
+    }
+    final StringBuilder buf = new StringBuilder(32 * size());
+    buf.append('{');
+
+    final MapIterator<K, V> it = mapIterator();
+    boolean hasNext = it.hasNext();
+    while (hasNext) {
+      final K key = it.next();
+      final V value = it.getValue();
+      buf.append(key == this ? "(this Map)" : key)
+          .append('=')
+          .append(value == this ? "(this Map)" : value);
+
+      hasNext = it.hasNext();
+      if (hasNext) {
+        buf.append(',').append(' ');
+      }
+    }
+
+    buf.append('}');
+    return buf.toString();
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/map/AbstractLinkedMap.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/map/AbstractLinkedMap.java
new file mode 100644
index 00000000..a60e670b
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/map/AbstractLinkedMap.java
@@ -0,0 +1,610 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tsfile.external.commons.collections4.map;
+
+import org.apache.tsfile.external.commons.collections4.OrderedIterator;
+import org.apache.tsfile.external.commons.collections4.OrderedMap;
+import org.apache.tsfile.external.commons.collections4.OrderedMapIterator;
+import org.apache.tsfile.external.commons.collections4.ResettableIterator;
+import org.apache.tsfile.external.commons.collections4.iterators.EmptyOrderedIterator;
+import org.apache.tsfile.external.commons.collections4.iterators.EmptyOrderedMapIterator;
+
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+/**
+ * An abstract implementation of a hash-based map that links entries to create an ordered map and
+ * which provides numerous points for subclasses to override.
+ *
+ * <p>This class implements all the features necessary for a subclass linked hash-based map.
+ * Key-value entries are stored in instances of the <code>LinkEntry</code> class which can be
+ * overridden and replaced. The iterators can similarly be replaced, without the need to replace the
+ * KeySet, EntrySet and Values view classes.
+ *
+ * <p>Overridable methods are provided to change the default hashing behaviour, and to change how
+ * entries are added to and removed from the map. Hopefully, all you need for unusual subclasses is
+ * here.
+ *
+ * <p>This implementation maintains order by original insertion, but subclasses may work
+ * differently. The <code>OrderedMap</code> interface is implemented to provide access to
+ * bidirectional iteration and extra convenience methods.
+ *
+ * <p>The <code>orderedMapIterator()</code> method provides direct access to a bidirectional
+ * iterator. The iterators from the other views can also be cast to <code>OrderedIterator</code> if
+ * required.
+ *
+ * <p>All the available iterators can be reset back to the start by casting to <code>
+ * ResettableIterator</code> and calling <code>reset()</code>.
+ *
+ * <p>The implementation is also designed to be subclassed, with lots of useful methods exposed.
+ *
+ * @param <K> the type of the keys in this map
+ * @param <V> the type of the values in this map
+ * @since 3.0
+ */
+public abstract class AbstractLinkedMap<K, V> extends AbstractHashedMap<K, V>
+    implements OrderedMap<K, V> {
+
+  /** Header in the linked list */
+  transient LinkEntry<K, V> header;
+
+  /** Constructor only used in deserialization, do not use otherwise. */
+  protected AbstractLinkedMap() {
+    super();
+  }
+
+  /**
+   * Constructor which performs no validation on the passed in parameters.
+   *
+   * @param initialCapacity the initial capacity, must be a power of two
+   * @param loadFactor the load factor, must be &gt; 0.0f and generally &lt; 1.0f
+   * @param threshold the threshold, must be sensible
+   */
+  protected AbstractLinkedMap(
+      final int initialCapacity, final float loadFactor, final int threshold) {
+    super(initialCapacity, loadFactor, threshold);
+  }
+
+  /**
+   * Constructs a new, empty map with the specified initial capacity.
+   *
+   * @param initialCapacity the initial capacity
+   * @throws IllegalArgumentException if the initial capacity is negative
+   */
+  protected AbstractLinkedMap(final int initialCapacity) {
+    super(initialCapacity);
+  }
+
+  /**
+   * Constructs a new, empty map with the specified initial capacity and load factor.
+   *
+   * @param initialCapacity the initial capacity
+   * @param loadFactor the load factor
+   * @throws IllegalArgumentException if the initial capacity is negative
+   * @throws IllegalArgumentException if the load factor is less than zero
+   */
+  protected AbstractLinkedMap(final int initialCapacity, final float loadFactor) {
+    super(initialCapacity, loadFactor);
+  }
+
+  /**
+   * Constructor copying elements from another map.
+   *
+   * @param map the map to copy
+   * @throws NullPointerException if the map is null
+   */
+  protected AbstractLinkedMap(final Map<? extends K, ? extends V> map) {
+    super(map);
+  }
+
+  /**
+   * Initialise this subclass during construction.
+   *
+   * <p>NOTE: As from v3.2 this method calls {@link #createEntry(HashEntry, int, Object, Object)} to
+   * create the map entry object.
+   */
+  @Override
+  protected void init() {
+    header = createEntry(null, -1, null, null);
+    header.before = header.after = header;
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Checks whether the map contains the specified value.
+   *
+   * @param value the value to search for
+   * @return true if the map contains the value
+   */
+  @Override
+  public boolean containsValue(final Object value) {
+    // override uses faster iterator
+    if (value == null) {
+      for (LinkEntry<K, V> entry = header.after; entry != header; entry = entry.after) {
+        if (entry.getValue() == null) {
+          return true;
+        }
+      }
+    } else {
+      for (LinkEntry<K, V> entry = header.after; entry != header; entry = entry.after) {
+        if (isEqualValue(value, entry.getValue())) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Clears the map, resetting the size to zero and nullifying references to avoid garbage
+   * collection issues.
+   */
+  @Override
+  public void clear() {
+    // override to reset the linked list
+    super.clear();
+    header.before = header.after = header;
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Gets the first key in the map, which is the first inserted.
+   *
+   * @return the eldest key
+   */
+  @Override
+  public K firstKey() {
+    if (size == 0) {
+      throw new NoSuchElementException("Map is empty");
+    }
+    return header.after.getKey();
+  }
+
+  /**
+   * Gets the last key in the map, which is the most recently inserted.
+   *
+   * @return the most recently inserted key
+   */
+  @Override
+  public K lastKey() {
+    if (size == 0) {
+      throw new NoSuchElementException("Map is empty");
+    }
+    return header.before.getKey();
+  }
+
+  /**
+   * Gets the next key in sequence.
+   *
+   * @param key the key to get after
+   * @return the next key
+   */
+  @Override
+  public K nextKey(final Object key) {
+    final LinkEntry<K, V> entry = getEntry(key);
+    return entry == null || entry.after == header ? null : entry.after.getKey();
+  }
+
+  @Override
+  protected LinkEntry<K, V> getEntry(final Object key) {
+    return (LinkEntry<K, V>) super.getEntry(key);
+  }
+
+  /**
+   * Gets the previous key in sequence.
+   *
+   * @param key the key to get before
+   * @return the previous key
+   */
+  @Override
+  public K previousKey(final Object key) {
+    final LinkEntry<K, V> entry = getEntry(key);
+    return entry == null || entry.before == header ? null : entry.before.getKey();
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Gets the key at the specified index.
+   *
+   * @param index the index to retrieve
+   * @return the key at the specified index
+   * @throws IndexOutOfBoundsException if the index is invalid
+   */
+  protected LinkEntry<K, V> getEntry(final int index) {
+    if (index < 0) {
+      throw new IndexOutOfBoundsException("Index " + index + " is less than zero");
+    }
+    if (index >= size) {
+      throw new IndexOutOfBoundsException("Index " + index + " is invalid for size " + size);
+    }
+    LinkEntry<K, V> entry;
+    if (index < size / 2) {
+      // Search forwards
+      entry = header.after;
+      for (int currentIndex = 0; currentIndex < index; currentIndex++) {
+        entry = entry.after;
+      }
+    } else {
+      // Search backwards
+      entry = header;
+      for (int currentIndex = size; currentIndex > index; currentIndex--) {
+        entry = entry.before;
+      }
+    }
+    return entry;
+  }
+
+  /**
+   * Adds an entry into this map, maintaining insertion order.
+   *
+   * <p>This implementation adds the entry to the data storage table and to the end of the linked
+   * list.
+   *
+   * @param entry the entry to add
+   * @param hashIndex the index into the data array to store at
+   */
+  @Override
+  protected void addEntry(final HashEntry<K, V> entry, final int hashIndex) {
+    final LinkEntry<K, V> link = (LinkEntry<K, V>) entry;
+    link.after = header;
+    link.before = header.before;
+    header.before.after = link;
+    header.before = link;
+    data[hashIndex] = link;
+  }
+
+  /**
+   * Creates an entry to store the data.
+   *
+   * <p>This implementation creates a new LinkEntry instance.
+   *
+   * @param next the next entry in sequence
+   * @param hashCode the hash code to use
+   * @param key the key to store
+   * @param value the value to store
+   * @return the newly created entry
+   */
+  @Override
+  protected LinkEntry<K, V> createEntry(
+      final HashEntry<K, V> next, final int hashCode, final K key, final V value) {
+    return new LinkEntry<>(next, hashCode, convertKey(key), value);
+  }
+
+  /**
+   * Removes an entry from the map and the linked list.
+   *
+   * <p>This implementation removes the entry from the linked list chain, then calls the superclass
+   * implementation.
+   *
+   * @param entry the entry to remove
+   * @param hashIndex the index into the data structure
+   * @param previous the previous entry in the chain
+   */
+  @Override
+  protected void removeEntry(
+      final HashEntry<K, V> entry, final int hashIndex, final HashEntry<K, V> previous) {
+    final LinkEntry<K, V> link = (LinkEntry<K, V>) entry;
+    link.before.after = link.after;
+    link.after.before = link.before;
+    link.after = null;
+    link.before = null;
+    super.removeEntry(entry, hashIndex, previous);
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Gets the <code>before</code> field from a <code>LinkEntry</code>. Used in subclasses that have
+   * no visibility of the field.
+   *
+   * @param entry the entry to query, must not be null
+   * @return the <code>before</code> field of the entry
+   * @throws NullPointerException if the entry is null
+   * @since 3.1
+   */
+  protected LinkEntry<K, V> entryBefore(final LinkEntry<K, V> entry) {
+    return entry.before;
+  }
+
+  /**
+   * Gets the <code>after</code> field from a <code>LinkEntry</code>. Used in subclasses that have
+   * no visibility of the field.
+   *
+   * @param entry the entry to query, must not be null
+   * @return the <code>after</code> field of the entry
+   * @throws NullPointerException if the entry is null
+   * @since 3.1
+   */
+  protected LinkEntry<K, V> entryAfter(final LinkEntry<K, V> entry) {
+    return entry.after;
+  }
+
+  // -----------------------------------------------------------------------
+  /** {@inheritDoc} */
+  @Override
+  public OrderedMapIterator<K, V> mapIterator() {
+    if (size == 0) {
+      return EmptyOrderedMapIterator.<K, V>emptyOrderedMapIterator();
+    }
+    return new LinkMapIterator<>(this);
+  }
+
+  /** MapIterator implementation. */
+  protected static class LinkMapIterator<K, V> extends LinkIterator<K, V>
+      implements OrderedMapIterator<K, V>, ResettableIterator<K> {
+
+    protected LinkMapIterator(final AbstractLinkedMap<K, V> parent) {
+      super(parent);
+    }
+
+    @Override
+    public K next() {
+      return super.nextEntry().getKey();
+    }
+
+    @Override
+    public K previous() {
+      return super.previousEntry().getKey();
+    }
+
+    @Override
+    public K getKey() {
+      final LinkEntry<K, V> current = currentEntry();
+      if (current == null) {
+        throw new IllegalStateException(AbstractHashedMap.GETKEY_INVALID);
+      }
+      return current.getKey();
+    }
+
+    @Override
+    public V getValue() {
+      final LinkEntry<K, V> current = currentEntry();
+      if (current == null) {
+        throw new IllegalStateException(AbstractHashedMap.GETVALUE_INVALID);
+      }
+      return current.getValue();
+    }
+
+    @Override
+    public V setValue(final V value) {
+      final LinkEntry<K, V> current = currentEntry();
+      if (current == null) {
+        throw new IllegalStateException(AbstractHashedMap.SETVALUE_INVALID);
+      }
+      return current.setValue(value);
+    }
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Creates an entry set iterator. Subclasses can override this to return iterators with different
+   * properties.
+   *
+   * @return the entrySet iterator
+   */
+  @Override
+  protected Iterator<Map.Entry<K, V>> createEntrySetIterator() {
+    if (size() == 0) {
+      return EmptyOrderedIterator.<Map.Entry<K, V>>emptyOrderedIterator();
+    }
+    return new EntrySetIterator<>(this);
+  }
+
+  /** EntrySet iterator. */
+  protected static class EntrySetIterator<K, V> extends LinkIterator<K, V>
+      implements OrderedIterator<Map.Entry<K, V>>, ResettableIterator<Map.Entry<K, V>> {
+
+    protected EntrySetIterator(final AbstractLinkedMap<K, V> parent) {
+      super(parent);
+    }
+
+    @Override
+    public Map.Entry<K, V> next() {
+      return super.nextEntry();
+    }
+
+    @Override
+    public Map.Entry<K, V> previous() {
+      return super.previousEntry();
+    }
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Creates a key set iterator. Subclasses can override this to return iterators with different
+   * properties.
+   *
+   * @return the keySet iterator
+   */
+  @Override
+  protected Iterator<K> createKeySetIterator() {
+    if (size() == 0) {
+      return EmptyOrderedIterator.<K>emptyOrderedIterator();
+    }
+    return new KeySetIterator<>(this);
+  }
+
+  /** KeySet iterator. */
+  protected static class KeySetIterator<K> extends LinkIterator<K, Object>
+      implements OrderedIterator<K>, ResettableIterator<K> {
+
+    @SuppressWarnings("unchecked")
+    protected KeySetIterator(final AbstractLinkedMap<K, ?> parent) {
+      super((AbstractLinkedMap<K, Object>) parent);
+    }
+
+    @Override
+    public K next() {
+      return super.nextEntry().getKey();
+    }
+
+    @Override
+    public K previous() {
+      return super.previousEntry().getKey();
+    }
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Creates a values iterator. Subclasses can override this to return iterators with different
+   * properties.
+   *
+   * @return the values iterator
+   */
+  @Override
+  protected Iterator<V> createValuesIterator() {
+    if (size() == 0) {
+      return EmptyOrderedIterator.<V>emptyOrderedIterator();
+    }
+    return new ValuesIterator<>(this);
+  }
+
+  /** Values iterator. */
+  protected static class ValuesIterator<V> extends LinkIterator<Object, V>
+      implements OrderedIterator<V>, ResettableIterator<V> {
+
+    @SuppressWarnings("unchecked")
+    protected ValuesIterator(final AbstractLinkedMap<?, V> parent) {
+      super((AbstractLinkedMap<Object, V>) parent);
+    }
+
+    @Override
+    public V next() {
+      return super.nextEntry().getValue();
+    }
+
+    @Override
+    public V previous() {
+      return super.previousEntry().getValue();
+    }
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * LinkEntry that stores the data.
+   *
+   * <p>If you subclass <code>AbstractLinkedMap</code> but not <code>LinkEntry</code> then you will
+   * not be able to access the protected fields. The <code>entryXxx()</code> methods on <code>
+   * AbstractLinkedMap</code> exist to provide the necessary access.
+   */
+  protected static class LinkEntry<K, V> extends HashEntry<K, V> {
+    /** The entry before this one in the order */
+    protected LinkEntry<K, V> before;
+
+    /** The entry after this one in the order */
+    protected LinkEntry<K, V> after;
+
+    /**
+     * Constructs a new entry.
+     *
+     * @param next the next entry in the hash bucket sequence
+     * @param hashCode the hash code
+     * @param key the key
+     * @param value the value
+     */
+    protected LinkEntry(
+        final HashEntry<K, V> next, final int hashCode, final Object key, final V value) {
+      super(next, hashCode, key, value);
+    }
+  }
+
+  /** Base Iterator that iterates in link order. */
+  protected abstract static class LinkIterator<K, V> {
+
+    /** The parent map */
+    protected final AbstractLinkedMap<K, V> parent;
+
+    /** The current (last returned) entry */
+    protected LinkEntry<K, V> last;
+
+    /** The next entry */
+    protected LinkEntry<K, V> next;
+
+    /** The modification count expected */
+    protected int expectedModCount;
+
+    protected LinkIterator(final AbstractLinkedMap<K, V> parent) {
+      super();
+      this.parent = parent;
+      this.next = parent.header.after;
+      this.expectedModCount = parent.modCount;
+    }
+
+    public boolean hasNext() {
+      return next != parent.header;
+    }
+
+    public boolean hasPrevious() {
+      return next.before != parent.header;
+    }
+
+    protected LinkEntry<K, V> nextEntry() {
+      if (parent.modCount != expectedModCount) {
+        throw new ConcurrentModificationException();
+      }
+      if (next == parent.header) {
+        throw new NoSuchElementException(AbstractHashedMap.NO_NEXT_ENTRY);
+      }
+      last = next;
+      next = next.after;
+      return last;
+    }
+
+    protected LinkEntry<K, V> previousEntry() {
+      if (parent.modCount != expectedModCount) {
+        throw new ConcurrentModificationException();
+      }
+      final LinkEntry<K, V> previous = next.before;
+      if (previous == parent.header) {
+        throw new NoSuchElementException(AbstractHashedMap.NO_PREVIOUS_ENTRY);
+      }
+      next = previous;
+      last = previous;
+      return last;
+    }
+
+    protected LinkEntry<K, V> currentEntry() {
+      return last;
+    }
+
+    public void remove() {
+      if (last == null) {
+        throw new IllegalStateException(AbstractHashedMap.REMOVE_INVALID);
+      }
+      if (parent.modCount != expectedModCount) {
+        throw new ConcurrentModificationException();
+      }
+      parent.remove(last.getKey());
+      last = null;
+      expectedModCount = parent.modCount;
+    }
+
+    public void reset() {
+      last = null;
+      next = parent.header.after;
+    }
+
+    @Override
+    public String toString() {
+      if (last != null) {
+        return "Iterator[" + last.getKey() + "=" + last.getValue() + "]";
+      }
+      return "Iterator[]";
+    }
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/map/LRUMap.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/map/LRUMap.java
new file mode 100644
index 00000000..4ae916a6
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/collections4/map/LRUMap.java
@@ -0,0 +1,573 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tsfile.external.commons.collections4.map;
+
+import org.apache.tsfile.external.commons.collections4.BoundedMap;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * A <code>Map</code> implementation with a fixed maximum size which removes the least recently used
+ * entry if an entry is added when full.
+ *
+ * <p>The least recently used algorithm works on the get and put operations only. Iteration of any
+ * kind, including setting the value by iteration, does not change the order. Queries such as
+ * containsKey and containsValue or access via views also do not change the order.
+ *
+ * <p>A somewhat subtle ramification of the least recently used algorithm is that calls to {@link
+ * #get(Object)} stand a very good chance of modifying the map's iteration order and thus
+ * invalidating any iterators currently in use. It is therefore suggested that iterations over an
+ * {@link LRUMap} instance access entry values only through a {@link
+ * org.apache.tsfile.external.commons.collections4.MapIterator MapIterator} or {@link #entrySet()}
+ * iterator.
+ *
+ * <p>The map implements <code>OrderedMap</code> and entries may be queried using the bidirectional
+ * <code>OrderedMapIterator</code>. The order returned is least recently used to most recently used.
+ * Iterators from map views can also be cast to <code>OrderedIterator</code> if required.
+ *
+ * <p>All the available iterators can be reset back to the start by casting to <code>
+ * ResettableIterator</code> and calling <code>reset()</code>.
+ *
+ * <p><strong>Note that LRUMap is not synchronized and is not thread-safe.</strong> If you wish to
+ * use this map from multiple threads concurrently, you must use appropriate synchronization. The
+ * simplest approach is to wrap this map using {@link java.util.Collections#synchronizedMap(Map)}.
+ * This class may throw <code>NullPointerException</code>'s when accessed by concurrent threads.
+ *
+ * @param <K> the type of the keys in this map
+ * @param <V> the type of the values in this map
+ * @since 3.0 (previously in main package v1.0)
+ */
+public class LRUMap<K, V> extends AbstractLinkedMap<K, V>
+    implements BoundedMap<K, V>, Serializable, Cloneable {
+
+  /** Serialisation version */
+  private static final long serialVersionUID = -612114643488955218L;
+
+  /** Default maximum size */
+  protected static final int DEFAULT_MAX_SIZE = 100;
+
+  /** Maximum size */
+  private transient int maxSize;
+
+  /** Scan behaviour */
+  private boolean scanUntilRemovable;
+
+  /** Constructs a new empty map with a maximum size of 100. */
+  public LRUMap() {
+    this(DEFAULT_MAX_SIZE, DEFAULT_LOAD_FACTOR, false);
+  }
+
+  /**
+   * Constructs a new, empty map with the specified maximum size.
+   *
+   * @param maxSize the maximum size of the map
+   * @throws IllegalArgumentException if the maximum size is less than one
+   */
+  public LRUMap(final int maxSize) {
+    this(maxSize, DEFAULT_LOAD_FACTOR);
+  }
+
+  /**
+   * Constructs a new, empty map with the specified maximum size.
+   *
+   * @param maxSize the maximum size of the map
+   * @param initialSize the initial size of the map
+   * @throws IllegalArgumentException if the maximum size is less than one
+   * @throws IllegalArgumentException if the initial size is negative or larger than the maximum
+   *     size
+   * @since 4.1
+   */
+  public LRUMap(final int maxSize, final int initialSize) {
+    this(maxSize, initialSize, DEFAULT_LOAD_FACTOR);
+  }
+
+  /**
+   * Constructs a new, empty map with the specified maximum size.
+   *
+   * @param maxSize the maximum size of the map
+   * @param scanUntilRemovable scan until a removeable entry is found, default false
+   * @throws IllegalArgumentException if the maximum size is less than one
+   * @since 3.1
+   */
+  public LRUMap(final int maxSize, final boolean scanUntilRemovable) {
+    this(maxSize, DEFAULT_LOAD_FACTOR, scanUntilRemovable);
+  }
+
+  /**
+   * Constructs a new, empty map with the specified max capacity and load factor.
+   *
+   * @param maxSize the maximum size of the map
+   * @param loadFactor the load factor
+   * @throws IllegalArgumentException if the maximum size is less than one
+   * @throws IllegalArgumentException if the load factor is less than zero
+   */
+  public LRUMap(final int maxSize, final float loadFactor) {
+    this(maxSize, loadFactor, false);
+  }
+
+  /**
+   * Constructs a new, empty map with the specified max / initial capacity and load factor.
+   *
+   * @param maxSize the maximum size of the map
+   * @param initialSize the initial size of the map
+   * @param loadFactor the load factor
+   * @throws IllegalArgumentException if the maximum size is less than one
+   * @throws IllegalArgumentException if the initial size is negative or larger than the maximum
+   *     size
+   * @throws IllegalArgumentException if the load factor is less than zero
+   * @since 4.1
+   */
+  public LRUMap(final int maxSize, final int initialSize, final float loadFactor) {
+    this(maxSize, initialSize, loadFactor, false);
+  }
+
+  /**
+   * Constructs a new, empty map with the specified max capacity and load factor.
+   *
+   * @param maxSize the maximum size of the map
+   * @param loadFactor the load factor
+   * @param scanUntilRemovable scan until a removeable entry is found, default false
+   * @throws IllegalArgumentException if the maximum size is less than one
+   * @throws IllegalArgumentException if the load factor is less than zero
+   * @since 3.1
+   */
+  public LRUMap(final int maxSize, final float loadFactor, final boolean scanUntilRemovable) {
+    this(maxSize, maxSize, loadFactor, scanUntilRemovable);
+  }
+
+  /**
+   * Constructs a new, empty map with the specified max / initial capacity and load factor.
+   *
+   * @param maxSize the maximum size of the map
+   * @param initialSize the initial size of the map
+   * @param loadFactor the load factor
+   * @param scanUntilRemovable scan until a removeable entry is found, default false
+   * @throws IllegalArgumentException if the maximum size is less than one
+   * @throws IllegalArgumentException if the initial size is negative or larger than the maximum
+   *     size
+   * @throws IllegalArgumentException if the load factor is less than zero
+   * @since 4.1
+   */
+  public LRUMap(
+      final int maxSize,
+      final int initialSize,
+      final float loadFactor,
+      final boolean scanUntilRemovable) {
+
+    super(initialSize, loadFactor);
+    if (maxSize < 1) {
+      throw new IllegalArgumentException("LRUMap max size must be greater than 0");
+    }
+    if (initialSize > maxSize) {
+      throw new IllegalArgumentException("LRUMap initial size must not be greather than max size");
+    }
+    this.maxSize = maxSize;
+    this.scanUntilRemovable = scanUntilRemovable;
+  }
+
+  /**
+   * Constructor copying elements from another map.
+   *
+   * <p>The maximum size is set from the map's size.
+   *
+   * @param map the map to copy
+   * @throws NullPointerException if the map is null
+   * @throws IllegalArgumentException if the map is empty
+   */
+  public LRUMap(final Map<? extends K, ? extends V> map) {
+    this(map, false);
+  }
+
+  /**
+   * Constructor copying elements from another map.
+   *
+   * <p>The maximum size is set from the map's size.
+   *
+   * @param map the map to copy
+   * @param scanUntilRemovable scan until a removeable entry is found, default false
+   * @throws NullPointerException if the map is null
+   * @throws IllegalArgumentException if the map is empty
+   * @since 3.1
+   */
+  public LRUMap(final Map<? extends K, ? extends V> map, final boolean scanUntilRemovable) {
+    this(map.size(), DEFAULT_LOAD_FACTOR, scanUntilRemovable);
+    putAll(map);
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Gets the value mapped to the key specified.
+   *
+   * <p>This operation changes the position of the key in the map to the most recently used position
+   * (last).
+   *
+   * @param key the key
+   * @return the mapped value, null if no match
+   */
+  @Override
+  public V get(final Object key) {
+    return get(key, true);
+  }
+
+  /**
+   * Gets the value mapped to the key specified.
+   *
+   * <p>If {@code updateToMRU} is {@code true}, the position of the key in the map is changed to the
+   * most recently used position (last), otherwise the iteration order is not changed by this
+   * operation.
+   *
+   * @param key the key
+   * @param updateToMRU whether the key shall be updated to the most recently used position
+   * @return the mapped value, null if no match
+   * @since 4.1
+   */
+  public V get(final Object key, final boolean updateToMRU) {
+    final LinkEntry<K, V> entry = getEntry(key);
+    if (entry == null) {
+      return null;
+    }
+    if (updateToMRU) {
+      moveToMRU(entry);
+    }
+    return entry.getValue();
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Moves an entry to the MRU position at the end of the list.
+   *
+   * <p>This implementation moves the updated entry to the end of the list.
+   *
+   * @param entry the entry to update
+   */
+  protected void moveToMRU(final LinkEntry<K, V> entry) {
+    if (entry.after != header) {
+      modCount++;
+      // remove
+      if (entry.before == null) {
+        throw new IllegalStateException(
+            "Entry.before is null."
+                + " This should not occur if your keys are immutable, and you have used synchronization properly.");
+      }
+      entry.before.after = entry.after;
+      entry.after.before = entry.before;
+      // add first
+      entry.after = header;
+      entry.before = header.before;
+      header.before.after = entry;
+      header.before = entry;
+    } else if (entry == header) {
+      throw new IllegalStateException(
+          "Can't move header to MRU"
+              + " This should not occur if your keys are immutable, and you have used synchronization properly.");
+    }
+  }
+
+  /**
+   * Updates an existing key-value mapping.
+   *
+   * <p>This implementation moves the updated entry to the end of the list using {@link
+   * #moveToMRU(AbstractLinkedMap.LinkEntry)}.
+   *
+   * @param entry the entry to update
+   * @param newValue the new value to store
+   */
+  @Override
+  protected void updateEntry(final HashEntry<K, V> entry, final V newValue) {
+    moveToMRU((LinkEntry<K, V>) entry); // handles modCount
+    entry.setValue(newValue);
+  }
+
+  /**
+   * Adds a new key-value mapping into this map.
+   *
+   * <p>This implementation checks the LRU size and determines whether to discard an entry or not
+   * using {@link #removeLRU(AbstractLinkedMap.LinkEntry)}.
+   *
+   * <p>From Commons Collections 3.1 this method uses {@link #isFull()} rather than accessing <code>
+   * size</code> and <code>maxSize</code> directly. It also handles the scanUntilRemovable
+   * functionality.
+   *
+   * @param hashIndex the index into the data array to store at
+   * @param hashCode the hash code of the key to add
+   * @param key the key to add
+   * @param value the value to add
+   */
+  @Override
+  protected void addMapping(final int hashIndex, final int hashCode, final K key, final V value) {
+    if (isFull()) {
+      LinkEntry<K, V> reuse = header.after;
+      boolean removeLRUEntry = false;
+      if (scanUntilRemovable) {
+        while (reuse != header && reuse != null) {
+          if (removeLRU(reuse)) {
+            removeLRUEntry = true;
+            break;
+          }
+          reuse = reuse.after;
+        }
+        if (reuse == null) {
+          throw new IllegalStateException(
+              "Entry.after=null, header.after="
+                  + header.after
+                  + " header.before="
+                  + header.before
+                  + " key="
+                  + key
+                  + " value="
+                  + value
+                  + " size="
+                  + size
+                  + " maxSize="
+                  + maxSize
+                  + " This should not occur if your keys are immutable, and you have used synchronization properly.");
+        }
+      } else {
+        removeLRUEntry = removeLRU(reuse);
+      }
+
+      if (removeLRUEntry) {
+        if (reuse == null) {
+          throw new IllegalStateException(
+              "reuse=null, header.after="
+                  + header.after
+                  + " header.before="
+                  + header.before
+                  + " key="
+                  + key
+                  + " value="
+                  + value
+                  + " size="
+                  + size
+                  + " maxSize="
+                  + maxSize
+                  + " This should not occur if your keys are immutable, and you have used synchronization properly.");
+        }
+        reuseMapping(reuse, hashIndex, hashCode, key, value);
+      } else {
+        super.addMapping(hashIndex, hashCode, key, value);
+      }
+    } else {
+      super.addMapping(hashIndex, hashCode, key, value);
+    }
+  }
+
+  /**
+   * Reuses an entry by removing it and moving it to a new place in the map.
+   *
+   * <p>This method uses {@link #removeEntry}, {@link #reuseEntry} and {@link #addEntry}.
+   *
+   * @param entry the entry to reuse
+   * @param hashIndex the index into the data array to store at
+   * @param hashCode the hash code of the key to add
+   * @param key the key to add
+   * @param value the value to add
+   */
+  protected void reuseMapping(
+      final LinkEntry<K, V> entry,
+      final int hashIndex,
+      final int hashCode,
+      final K key,
+      final V value) {
+    // find the entry before the entry specified in the hash table
+    // remember that the parameters (except the first) refer to the new entry,
+    // not the old one
+    try {
+      final int removeIndex = hashIndex(entry.hashCode, data.length);
+      final HashEntry<K, V>[] tmp = data; // may protect against some sync issues
+      HashEntry<K, V> loop = tmp[removeIndex];
+      HashEntry<K, V> previous = null;
+      while (loop != entry && loop != null) {
+        previous = loop;
+        loop = loop.next;
+      }
+      if (loop == null) {
+        throw new IllegalStateException(
+            "Entry.next=null, data[removeIndex]="
+                + data[removeIndex]
+                + " previous="
+                + previous
+                + " key="
+                + key
+                + " value="
+                + value
+                + " size="
+                + size
+                + " maxSize="
+                + maxSize
+                + " This should not occur if your keys are immutable, and you have used synchronization properly.");
+      }
+
+      // reuse the entry
+      modCount++;
+      removeEntry(entry, removeIndex, previous);
+      reuseEntry(entry, hashIndex, hashCode, key, value);
+      addEntry(entry, hashIndex);
+    } catch (final NullPointerException ex) {
+      throw new IllegalStateException(
+          "NPE, entry="
+              + entry
+              + " entryIsHeader="
+              + (entry == header)
+              + " key="
+              + key
+              + " value="
+              + value
+              + " size="
+              + size
+              + " maxSize="
+              + maxSize
+              + " This should not occur if your keys are immutable, and you have used synchronization properly.");
+    }
+  }
+
+  /**
+   * Subclass method to control removal of the least recently used entry from the map.
+   *
+   * <p>This method exists for subclasses to override. A subclass may wish to provide cleanup of
+   * resources when an entry is removed. For example:
+   *
+   * <pre>
+   * protected boolean removeLRU(LinkEntry entry) {
+   *   releaseResources(entry.getValue());  // release resources held by entry
+   *   return true;  // actually delete entry
+   * }
+   * </pre>
+   *
+   * <p>Alternatively, a subclass may choose to not remove the entry or selectively keep certain LRU
+   * entries. For example:
+   *
+   * <pre>
+   * protected boolean removeLRU(LinkEntry entry) {
+   *   if (entry.getKey().toString().startsWith("System.")) {
+   *     return false;  // entry not removed from LRUMap
+   *   } else {
+   *     return true;  // actually delete entry
+   *   }
+   * }
+   * </pre>
+   *
+   * The effect of returning false is dependent on the scanUntilRemovable flag. If the flag is true,
+   * the next LRU entry will be passed to this method and so on until one returns false and is
+   * removed, or every entry in the map has been passed. If the scanUntilRemovable flag is false,
+   * the map will exceed the maximum size.
+   *
+   * <p>NOTE: Commons Collections 3.0 passed the wrong entry to this method. This is fixed in
+   * version 3.1 onwards.
+   *
+   * @param entry the entry to be removed
+   * @return {@code true}
+   */
+  protected boolean removeLRU(final LinkEntry<K, V> entry) {
+    return true;
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Returns true if this map is full and no new mappings can be added.
+   *
+   * @return <code>true</code> if the map is full
+   */
+  @Override
+  public boolean isFull() {
+    return size >= maxSize;
+  }
+
+  /**
+   * Gets the maximum size of the map (the bound).
+   *
+   * @return the maximum number of elements the map can hold
+   */
+  @Override
+  public int maxSize() {
+    return maxSize;
+  }
+
+  /**
+   * Whether this LRUMap will scan until a removable entry is found when the map is full.
+   *
+   * @return true if this map scans
+   * @since 3.1
+   */
+  public boolean isScanUntilRemovable() {
+    return scanUntilRemovable;
+  }
+
+  // -----------------------------------------------------------------------
+  /**
+   * Clones the map without cloning the keys or values.
+   *
+   * @return a shallow clone
+   */
+  @Override
+  public LRUMap<K, V> clone() {
+    return (LRUMap<K, V>) super.clone();
+  }
+
+  /**
+   * Write the map out using a custom routine.
+   *
+   * @param out the output stream
+   * @throws IOException if an error occurs while writing to the stream
+   */
+  private void writeObject(final ObjectOutputStream out) throws IOException {
+    out.defaultWriteObject();
+    doWriteObject(out);
+  }
+
+  /**
+   * Read the map in using a custom routine.
+   *
+   * @param in the input stream
+   * @throws IOException if an error occurs while reading from the stream
+   * @throws ClassNotFoundException if an object read from the stream can not be loaded
+   */
+  private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
+    in.defaultReadObject();
+    doReadObject(in);
+  }
+
+  /**
+   * Writes the data necessary for <code>put()</code> to work in deserialization.
+   *
+   * @param out the output stream
+   * @throws IOException if an error occurs while writing to the stream
+   */
+  @Override
+  protected void doWriteObject(final ObjectOutputStream out) throws IOException {
+    out.writeInt(maxSize);
+    super.doWriteObject(out);
+  }
+
+  /**
+   * Reads the data necessary for <code>put()</code> to work in the superclass.
+   *
+   * @param in the input stream
+   * @throws IOException if an error occurs while reading from the stream
+   * @throws ClassNotFoundException if an object read from the stream can not be loaded
+   */
+  @Override
+  protected void doReadObject(final ObjectInputStream in)
+      throws IOException, ClassNotFoundException {
+    maxSize = in.readInt();
+    super.doReadObject(in);
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/Charsets.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/Charsets.java
new file mode 100644
index 00000000..2429385f
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/Charsets.java
@@ -0,0 +1,211 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tsfile.external.commons.io;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.charset.UnsupportedCharsetException;
+import java.util.Collections;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * Charsets required of every implementation of the Java platform.
+ *
+ * <p>From the Java documentation <a
+ * href="https://docs.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html">Standard
+ * charsets</a>:
+ *
+ * <p><cite>Every implementation of the Java platform is required to support the following character
+ * encodings. Consult the release documentation for your implementation to see if any other
+ * encodings are supported. Consult the release documentation for your implementation to see if any
+ * other encodings are supported. </cite>
+ *
+ * <ul>
+ *   <li>{@code US-ASCII}<br>
+ *       Seven-bit ASCII, a.k.a. ISO646-US, a.k.a. the Basic Latin block of the Unicode character
+ *       set.
+ *   <li>{@code ISO-8859-1}<br>
+ *       ISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1.
+ *   <li>{@code UTF-8}<br>
+ *       Eight-bit Unicode Transformation Format.
+ *   <li>{@code UTF-16BE}<br>
+ *       Sixteen-bit Unicode Transformation Format, big-endian byte order.
+ *   <li>{@code UTF-16LE}<br>
+ *       Sixteen-bit Unicode Transformation Format, little-endian byte order.
+ *   <li>{@code UTF-16}<br>
+ *       Sixteen-bit Unicode Transformation Format, byte order specified by a mandatory initial
+ *       byte-order mark (either order accepted on input, big-endian used on output.)
+ * </ul>
+ *
+ * @see <a href="https://docs.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html">Standard
+ *     charsets</a>
+ * @since 2.3
+ */
+public class Charsets {
+
+  //
+  // This class should only contain Charset instances for required encodings. This guarantees that
+  // it will load
+  // correctly and without delay on all Java platforms.
+  //
+
+  private static final SortedMap<String, Charset> STANDARD_CHARSET_MAP;
+
+  static {
+    final SortedMap<String, Charset> standardCharsetMap =
+        new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+    standardCharsetMap.put(StandardCharsets.ISO_8859_1.name(), StandardCharsets.ISO_8859_1);
+    standardCharsetMap.put(StandardCharsets.US_ASCII.name(), StandardCharsets.US_ASCII);
+    standardCharsetMap.put(StandardCharsets.UTF_16.name(), StandardCharsets.UTF_16);
+    standardCharsetMap.put(StandardCharsets.UTF_16BE.name(), StandardCharsets.UTF_16BE);
+    standardCharsetMap.put(StandardCharsets.UTF_16LE.name(), StandardCharsets.UTF_16LE);
+    standardCharsetMap.put(StandardCharsets.UTF_8.name(), StandardCharsets.UTF_8);
+    STANDARD_CHARSET_MAP = Collections.unmodifiableSortedMap(standardCharsetMap);
+  }
+
+  /**
+   * CharEncodingISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1.
+   *
+   * <p>Every implementation of the Java platform is required to support this character encoding.
+   *
+   * @see <a href="https://docs.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html">Standard
+   *     charsets</a>
+   * @deprecated Use Java 7's {@link java.nio.charset.StandardCharsets}
+   */
+  @Deprecated public static final Charset ISO_8859_1 = StandardCharsets.ISO_8859_1;
+
+  /**
+   * Seven-bit ASCII, also known as ISO646-US, also known as the Basic Latin block of the Unicode
+   * character set.
+   *
+   * <p>Every implementation of the Java platform is required to support this character encoding.
+   *
+   * @see <a href="https://docs.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html">Standard
+   *     charsets</a>
+   * @deprecated Use Java 7's {@link java.nio.charset.StandardCharsets}
+   */
+  @Deprecated public static final Charset US_ASCII = StandardCharsets.US_ASCII;
+
+  /**
+   * Sixteen-bit Unicode Transformation Format, The byte order specified by a mandatory initial
+   * byte-order mark (either order accepted on input, big-endian used on output)
+   *
+   * <p>Every implementation of the Java platform is required to support this character encoding.
+   *
+   * @see <a href="https://docs.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html">Standard
+   *     charsets</a>
+   * @deprecated Use Java 7's {@link java.nio.charset.StandardCharsets}
+   */
+  @Deprecated public static final Charset UTF_16 = StandardCharsets.UTF_16;
+
+  /**
+   * Sixteen-bit Unicode Transformation Format, big-endian byte order.
+   *
+   * <p>Every implementation of the Java platform is required to support this character encoding.
+   *
+   * @see <a href="https://docs.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html">Standard
+   *     charsets</a>
+   * @deprecated Use Java 7's {@link java.nio.charset.StandardCharsets}
+   */
+  @Deprecated public static final Charset UTF_16BE = StandardCharsets.UTF_16BE;
+
+  /**
+   * Sixteen-bit Unicode Transformation Format, little-endian byte order.
+   *
+   * <p>Every implementation of the Java platform is required to support this character encoding.
+   *
+   * @see <a href="https://docs.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html">Standard
+   *     charsets</a>
+   * @deprecated Use Java 7's {@link java.nio.charset.StandardCharsets}
+   */
+  @Deprecated public static final Charset UTF_16LE = StandardCharsets.UTF_16LE;
+
+  /**
+   * Eight-bit Unicode Transformation Format.
+   *
+   * <p>Every implementation of the Java platform is required to support this character encoding.
+   *
+   * @see <a href="https://docs.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html">Standard
+   *     charsets</a>
+   * @deprecated Use Java 7's {@link java.nio.charset.StandardCharsets}
+   */
+  @Deprecated public static final Charset UTF_8 = StandardCharsets.UTF_8;
+
+  /**
+   * Constructs a sorted map from canonical charset names to charset objects required of every
+   * implementation of the Java platform.
+   *
+   * <p>From the Java documentation <a
+   * href="https://docs.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html">Standard
+   * charsets</a>:
+   *
+   * @return An immutable, case-insensitive map from canonical charset names to charset objects.
+   * @see Charset#availableCharsets()
+   * @since 2.5
+   */
+  public static SortedMap<String, Charset> requiredCharsets() {
+    return STANDARD_CHARSET_MAP;
+  }
+
+  /**
+   * Returns the given Charset or the default Charset if the given Charset is null.
+   *
+   * @param charset A charset or null.
+   * @return the given Charset or the default Charset if the given Charset is null
+   */
+  public static Charset toCharset(final Charset charset) {
+    return charset == null ? Charset.defaultCharset() : charset;
+  }
+
+  /**
+   * Returns the given charset if non-null, otherwise return defaultCharset.
+   *
+   * @param charset The charset to test, may be null.
+   * @param defaultCharset The charset to return if charset is null, may be null.
+   * @return a Charset .
+   * @since 2.12.0
+   */
+  public static Charset toCharset(final Charset charset, final Charset defaultCharset) {
+    return charset == null ? defaultCharset : charset;
+  }
+
+  /**
+   * Returns a Charset for the named charset. If the name is null, return the default Charset.
+   *
+   * @param charsetName The name of the requested charset, may be null.
+   * @return a Charset for the named charset.
+   * @throws UnsupportedCharsetException If the named charset is unavailable (unchecked exception).
+   */
+  public static Charset toCharset(final String charsetName) throws UnsupportedCharsetException {
+    return toCharset(charsetName, Charset.defaultCharset());
+  }
+
+  /**
+   * Returns a Charset for the named charset. If the name is null, return the given default Charset.
+   *
+   * @param charsetName The name of the requested charset, may be null.
+   * @param defaultCharset The name charset to return if charsetName is null, may be null.
+   * @return a Charset for the named charset.
+   * @throws UnsupportedCharsetException If the named charset is unavailable (unchecked exception).
+   * @since 2.12.0
+   */
+  public static Charset toCharset(final String charsetName, final Charset defaultCharset)
+      throws UnsupportedCharsetException {
+    return charsetName == null ? defaultCharset : Charset.forName(charsetName);
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/FileExistsException.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/FileExistsException.java
new file mode 100644
index 00000000..b1cb2537
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/FileExistsException.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tsfile.external.commons.io;
+
+import java.io.IOException;
+
+/**
+ * Indicates that a file already exists.
+ *
+ * @since 2.0
+ */
+public class FileExistsException extends IOException {
+
+  /** Defines the serial version UID. */
+  private static final long serialVersionUID = 1L;
+
+  /**
+   * Constructs an instance with the specified message.
+   *
+   * @param message The error message
+   */
+  public FileExistsException(final String message) {
+    super(message);
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/FileUtils.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/FileUtils.java
new file mode 100644
index 00000000..97f22787
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/FileUtils.java
@@ -0,0 +1,928 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tsfile.external.commons.io;
+
+import org.apache.tsfile.external.commons.io.file.Counters;
+import org.apache.tsfile.external.commons.io.file.PathUtils;
+import org.apache.tsfile.external.commons.io.file.StandardDeleteOption;
+import org.apache.tsfile.external.commons.io.filefilter.FileFileFilter;
+import org.apache.tsfile.external.commons.io.filefilter.IOFileFilter;
+import org.apache.tsfile.external.commons.io.filefilter.SuffixFileFilter;
+import org.apache.tsfile.external.commons.io.function.IOConsumer;
+import org.apache.tsfile.external.commons.io.function.Uncheck;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.nio.file.CopyOption;
+import java.nio.file.FileVisitOption;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.BasicFileAttributeView;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileTime;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class FileUtils {
+
+  private static final String PROTOCOL_FILE = "file";
+
+  public static void deleteDirectory(final File directory) throws IOException {
+    Objects.requireNonNull(directory, "directory");
+    if (!directory.exists()) {
+      return;
+    }
+    if (!isSymlink(directory)) {
+      cleanDirectory(directory);
+    }
+    delete(directory);
+  }
+
+  public static File delete(final File file) throws IOException {
+    Objects.requireNonNull(file, PROTOCOL_FILE);
+    Files.delete(file.toPath());
+    return file;
+  }
+
+  public static boolean isSymlink(final File file) {
+    return file != null && Files.isSymbolicLink(file.toPath());
+  }
+
+  private static void requireDirectoryExists(final File directory, final String name)
+      throws FileNotFoundException {
+    Objects.requireNonNull(directory, name);
+    if (!directory.isDirectory()) {
+      if (directory.exists()) {
+        throw new IllegalArgumentException(
+            "Parameter '" + name + "' is not a directory: '" + directory + "'");
+      }
+      throw new FileNotFoundException("Directory '" + directory + "' does not exist.");
+    }
+  }
+
+  private static File[] listFiles(final File directory, final FileFilter fileFilter)
+      throws IOException {
+    requireDirectoryExists(directory, "directory");
+    final File[] files =
+        fileFilter == null ? directory.listFiles() : directory.listFiles(fileFilter);
+    if (files == null) {
+      // null if the directory does not denote a directory, or if an I/O error occurs.
+      throw new IOException("Unknown I/O error listing contents of directory: " + directory);
+    }
+    return files;
+  }
+
+  /**
+   * Creates all directories for a File object, including any necessary but non-existent parent
+   * directories. If the {@code directory} already exists or is null, nothing happens.
+   *
+   * <p>Calls {@link File#mkdirs()} and throws an {@link IOException} on failure.
+   *
+   * @param directory the receiver for {@code mkdirs()}. If the {@code directory} already exists or
+   *     is null, nothing happens.
+   * @throws IOException if the directory was not created along with all its parent directories.
+   * @throws IOException if the given file object is not a directory.
+   * @throws SecurityException See {@link File#mkdirs()}.
+   * @see File#mkdirs()
+   */
+  public static void forceMkdir(final File directory) throws IOException {
+    mkdirs(directory);
+  }
+
+  /**
+   * Creates all directories for a File object, including any necessary but non-existent parent
+   * directories. If the parent directory already exists or is null, nothing happens.
+   *
+   * <p>Calls {@link File#mkdirs()} for the parent of @{code file}.
+   *
+   * @param file file with parents to create, must not be {@code null}.
+   * @throws NullPointerException if the file is {@code null}.
+   * @throws IOException if the directory was not created along with all its parent directories.
+   * @throws SecurityException See {@link File#mkdirs()}.
+   * @see File#mkdirs()
+   * @since 2.5
+   */
+  public static void forceMkdirParent(File file) throws IOException {
+    forceMkdir(getParentFile(Objects.requireNonNull(file, "file")));
+  }
+
+  public static void forceDelete(final File file) throws IOException {
+    Objects.requireNonNull(file, PROTOCOL_FILE);
+
+    final Counters.PathCounters deleteCounters;
+    try {
+      deleteCounters =
+          PathUtils.delete(
+              file.toPath(),
+              PathUtils.EMPTY_LINK_OPTION_ARRAY,
+              StandardDeleteOption.OVERRIDE_READ_ONLY);
+    } catch (final IOException ex) {
+      throw new IOException("Cannot delete file: " + file, ex);
+    }
+    if (deleteCounters.getFileCounter().get() < 1
+        && deleteCounters.getDirectoryCounter().get() < 1) {
+      // didn't find a file to delete.
+      throw new FileNotFoundException("File does not exist: " + file);
+    }
+  }
+
+  public static void cleanDirectory(final File directory) throws IOException {
+    IOConsumer.forAll(FileUtils::forceDelete, listFiles(directory, null));
+  }
+
+  public static void moveFile(
+      final File srcFile, final File destFile, final CopyOption... copyOptions) throws IOException {
+    validateMoveParameters(srcFile, destFile);
+    checkFileExists(srcFile, "srcFile");
+    requireAbsent(destFile, "destFile");
+    final boolean rename = srcFile.renameTo(destFile);
+    if (!rename) {
+      // Don't interfere with file date on move, handled by StandardCopyOption.COPY_ATTRIBUTES
+      copyFile(srcFile, destFile, false, copyOptions);
+      if (!srcFile.delete()) {
+        deleteQuietly(destFile);
+        throw new IOException(
+            "Failed to delete original file '" + srcFile + "' after copy to '" + destFile + "'");
+      }
+    }
+  }
+
+  private static void checkFileExists(final File file, final String name)
+      throws FileNotFoundException {
+    Objects.requireNonNull(file, name);
+    if (!file.isFile()) {
+      if (file.exists()) {
+        throw new IllegalArgumentException("Parameter '" + name + "' is not a file: " + file);
+      }
+      if (!Files.isSymbolicLink(file.toPath())) {
+        throw new FileNotFoundException("Source '" + file + "' does not exist");
+      }
+    }
+  }
+
+  /**
+   * Requires that the given {@link File} is a file.
+   *
+   * @param file The {@link File} to check.
+   * @param name The parameter name to use in the exception message.
+   * @return the given file.
+   * @throws NullPointerException if the given {@link File} is {@code null}.
+   * @throws IllegalArgumentException if the given {@link File} does not exist or is not a file.
+   */
+  private static File requireFile(final File file, final String name) {
+    Objects.requireNonNull(file, name);
+    if (!file.isFile()) {
+      throw new IllegalArgumentException("Parameter '" + name + "' is not a file: " + file);
+    }
+    return file;
+  }
+
+  private static void requireAbsent(final File file, final String name) throws FileExistsException {
+    if (file.exists()) {
+      throw new FileExistsException(
+          String.format("File element in parameter '%s' already exists: '%s'", name, file));
+    }
+  }
+
+  public static void copyFile(final File srcFile, final File destFile) throws IOException {
+    copyFile(srcFile, destFile, StandardCopyOption.REPLACE_EXISTING);
+  }
+
+  public static void copyFile(
+      final File srcFile, final File destFile, final CopyOption... copyOptions) throws IOException {
+    copyFile(srcFile, destFile, true, copyOptions);
+  }
+
+  public static void copyFile(
+      final File srcFile,
+      final File destFile,
+      final boolean preserveFileDate,
+      final CopyOption... copyOptions)
+      throws IOException {
+    Objects.requireNonNull(destFile, "destination");
+    checkFileExists(srcFile, "srcFile");
+    requireCanonicalPathsNotEquals(srcFile, destFile);
+    createParentDirectories(destFile);
+    if (destFile.exists()) {
+      checkFileExists(destFile, "destFile");
+    }
+
+    final Path srcPath = srcFile.toPath();
+
+    Files.copy(srcPath, destFile.toPath(), copyOptions);
+
+    // On Windows, the last modified time is copied by default.
+    if (preserveFileDate && !Files.isSymbolicLink(srcPath) && !setTimes(srcFile, destFile)) {
+      throw new IOException("Cannot set the file time.");
+    }
+  }
+
+  private static void requireCanonicalPathsNotEquals(final File file1, final File file2)
+      throws IOException {
+    final String canonicalPath = file1.getCanonicalPath();
+    if (canonicalPath.equals(file2.getCanonicalPath())) {
+      throw new IllegalArgumentException(
+          String.format(
+              "File canonical paths are equal: '%s' (file1='%s', file2='%s')",
+              canonicalPath, file1, file2));
+    }
+  }
+
+  public static File createParentDirectories(final File file) throws IOException {
+    return mkdirs(getParentFile(file));
+  }
+
+  private static File mkdirs(final File directory) throws IOException {
+    if (directory != null && !directory.mkdirs() && !directory.isDirectory()) {
+      throw new IOException("Cannot create directory '" + directory + "'.");
+    }
+    return directory;
+  }
+
+  private static File getParentFile(final File file) {
+    return file == null ? null : file.getParentFile();
+  }
+
+  private static boolean setTimes(final File sourceFile, final File targetFile) {
+    Objects.requireNonNull(sourceFile, "sourceFile");
+    Objects.requireNonNull(targetFile, "targetFile");
+    try {
+      // Set creation, modified, last accessed to match source file
+      final BasicFileAttributes srcAttr =
+          Files.readAttributes(sourceFile.toPath(), BasicFileAttributes.class);
+      final BasicFileAttributeView destAttrView =
+          Files.getFileAttributeView(targetFile.toPath(), BasicFileAttributeView.class);
+      // null guards are not needed; BasicFileAttributes.setTimes(...) is null safe
+      destAttrView.setTimes(
+          srcAttr.lastModifiedTime(), srcAttr.lastAccessTime(), srcAttr.creationTime());
+      return true;
+    } catch (final IOException ignored) {
+      // Fallback: Only set modified time to match source file
+      return targetFile.setLastModified(sourceFile.lastModified());
+    }
+  }
+
+  public static boolean deleteQuietly(final File file) {
+    if (file == null) {
+      return false;
+    }
+    try {
+      if (file.isDirectory()) {
+        cleanDirectory(file);
+      }
+    } catch (final Exception ignored) {
+      // ignore
+    }
+
+    try {
+      return file.delete();
+    } catch (final Exception ignored) {
+      return false;
+    }
+  }
+
+  /////////////////////////////////////////////////////////////////////////////////////////////////
+  // IoTDB
+  /////////////////////////////////////////////////////////////////////////////////////////////////
+
+  /**
+   * The empty String {@code ""}.
+   *
+   * @since 2.0
+   */
+  public static final String EMPTY = "";
+
+  /**
+   * Writes a CharSequence to a file creating the file if it does not exist.
+   *
+   * @param file the file to write
+   * @param data the content to write to the file
+   * @param charset the charset to use, {@code null} means platform default
+   * @param append if {@code true}, then the data will be added to the end of the file rather than
+   *     overwriting
+   * @throws IOException in case of an I/O error
+   * @since 2.3
+   */
+  public static void write(
+      final File file, final CharSequence data, final Charset charset, final boolean append)
+      throws IOException {
+    writeStringToFile(file, Objects.toString(data, null), charset, append);
+  }
+
+  /**
+   * Writes a String to a file creating the file if it does not exist.
+   *
+   * @param file the file to write
+   * @param data the content to write to the file
+   * @param charset the charset to use, {@code null} means platform default
+   * @param append if {@code true}, then the String will be added to the end of the file rather than
+   *     overwriting
+   * @throws IOException in case of an I/O error
+   * @since 2.3
+   */
+  public static void writeStringToFile(
+      final File file, final String data, final Charset charset, final boolean append)
+      throws IOException {
+    try (OutputStream out = newOutputStream(file, append)) {
+      IOUtils.write(data, out, charset);
+    }
+  }
+
+  /**
+   * Creates a new OutputStream by opening or creating a file, returning an output stream that may
+   * be used to write bytes to the file.
+   *
+   * @param append Whether or not to append.
+   * @param file the File.
+   * @return a new OutputStream.
+   * @throws IOException if an I/O error occurs.
+   * @see PathUtils#newOutputStream(Path, boolean)
+   * @since 2.12.0
+   */
+  public static OutputStream newOutputStream(final File file, final boolean append)
+      throws IOException {
+    return PathUtils.newOutputStream(Objects.requireNonNull(file, "file").toPath(), append);
+  }
+
+  /**
+   * Moves a file preserving attributes.
+   *
+   * <p>Shorthand for {@code moveFile(srcFile, destFile, StandardCopyOption.COPY_ATTRIBUTES)}.
+   *
+   * <p>When the destination file is on another file system, do a "copy and delete".
+   *
+   * @param srcFile the file to be moved.
+   * @param destFile the destination file.
+   * @throws NullPointerException if any of the given {@link File}s are {@code null}.
+   * @throws FileExistsException if the destination file exists.
+   * @throws FileNotFoundException if the source file does not exist.
+   * @throws IOException if source or destination is invalid.
+   * @throws IOException if an error occurs.
+   * @since 1.4
+   */
+  public static void moveFile(final File srcFile, final File destFile) throws IOException {
+    moveFile(srcFile, destFile, StandardCopyOption.COPY_ATTRIBUTES);
+  }
+
+  /**
+   * Copies bytes from the URL {@code source} to a file {@code destination}. The directories up to
+   * {@code destination} will be created if they don't already exist. {@code destination} will be
+   * overwritten if it already exists.
+   *
+   * <p>Warning: this method does not set a connection or read timeout and thus might block forever.
+   * Use {@link #copyURLToFile(URL, File, int, int)} with reasonable timeouts to prevent this.
+   *
+   * @param source the {@link URL} to copy bytes from, must not be {@code null}
+   * @param destination the non-directory {@link File} to write bytes to (possibly overwriting),
+   *     must not be {@code null}
+   * @throws IOException if {@code source} URL cannot be opened
+   * @throws IOException if {@code destination} is a directory
+   * @throws IOException if {@code destination} cannot be written
+   * @throws IOException if {@code destination} needs creating but can't be
+   * @throws IOException if an IO error occurs during copying
+   */
+  public static void copyURLToFile(final URL source, final File destination) throws IOException {
+    final Path path = destination.toPath();
+    PathUtils.createParentDirectories(path);
+    PathUtils.copy(source::openStream, path, StandardCopyOption.REPLACE_EXISTING);
+  }
+
+  /**
+   * Moves a file to a directory.
+   *
+   * <p>If {@code createDestDir} is true, creates all destination parent directories, including any
+   * necessary but non-existent parent directories.
+   *
+   * @param srcFile the file to be moved.
+   * @param destDir the destination file.
+   * @param createDestDir If {@code true} create the destination directory, otherwise if {@code
+   *     false} throw an IOException.
+   * @throws NullPointerException if any of the given {@link File}s are {@code null}.
+   * @throws FileExistsException if the destination file exists.
+   * @throws FileNotFoundException if the source file does not exist.
+   * @throws IOException if source or destination is invalid.
+   * @throws IOException if the directory was not created along with all its parent directories, if
+   *     enabled.
+   * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
+   * @throws SecurityException See {@link File#mkdirs()}.
+   * @since 1.4
+   */
+  public static void moveFileToDirectory(
+      final File srcFile, final File destDir, final boolean createDestDir) throws IOException {
+    validateMoveParameters(srcFile, destDir);
+    if (!destDir.exists() && createDestDir) {
+      mkdirs(destDir);
+    }
+    requireExistsChecked(destDir, "destDir");
+    requireDirectory(destDir, "destDir");
+    moveFile(srcFile, new File(destDir, srcFile.getName()));
+  }
+
+  /**
+   * Validates the given arguments.
+   *
+   * <ul>
+   *   <li>Throws {@link NullPointerException} if {@code source} is null
+   *   <li>Throws {@link NullPointerException} if {@code destination} is null
+   *   <li>Throws {@link FileNotFoundException} if {@code source} does not exist
+   * </ul>
+   *
+   * @param source the file or directory to be moved.
+   * @param destination the destination file or directory.
+   * @throws NullPointerException if any of the given {@link File}s are {@code null}.
+   * @throws FileNotFoundException if the source file does not exist.
+   */
+  private static void validateMoveParameters(final File source, final File destination)
+      throws FileNotFoundException {
+    Objects.requireNonNull(source, "source");
+    Objects.requireNonNull(destination, "destination");
+    if (!source.exists()) {
+      throw new FileNotFoundException("Source '" + source + "' does not exist");
+    }
+  }
+
+  /**
+   * Requires that the given {@link File} exists and throws an {@link FileNotFoundException} if it
+   * doesn't.
+   *
+   * @param file The {@link File} to check.
+   * @param fileParamName The parameter name to use in the exception message in case of {@code null}
+   *     input.
+   * @return the given file.
+   * @throws NullPointerException if the given {@link File} is {@code null}.
+   * @throws FileNotFoundException if the given {@link File} does not exist.
+   */
+  private static File requireExistsChecked(final File file, final String fileParamName)
+      throws FileNotFoundException {
+    Objects.requireNonNull(file, fileParamName);
+    if (!file.exists()) {
+      throw new FileNotFoundException(
+          "File system element for parameter '"
+              + fileParamName
+              + "' does not exist: '"
+              + file
+              + "'");
+    }
+    return file;
+  }
+
+  /**
+   * Requires that the given {@link File} is a directory.
+   *
+   * @param directory The {@link File} to check.
+   * @param name The parameter name to use in the exception message in case of null input or if the
+   *     file is not a directory.
+   * @return the given directory.
+   * @throws NullPointerException if the given {@link File} is {@code null}.
+   * @throws IllegalArgumentException if the given {@link File} does not exist or is not a
+   *     directory.
+   */
+  private static File requireDirectory(final File directory, final String name) {
+    Objects.requireNonNull(directory, name);
+    if (!directory.isDirectory()) {
+      throw new IllegalArgumentException(
+          "Parameter '" + name + "' is not a directory: '" + directory + "'");
+    }
+    return directory;
+  }
+
+  /**
+   * Finds files within a given directory (and optionally its subdirectories) which match an array
+   * of extensions.
+   *
+   * @param directory the directory to search in
+   * @param extensions an array of extensions, ex. {"java","xml"}. If this parameter is {@code
+   *     null}, all files are returned.
+   * @param recursive if true all subdirectories are searched as well
+   * @return a collection of java.io.File with the matching files
+   */
+  public static Collection<File> listFiles(
+      final File directory, final String[] extensions, final boolean recursive) {
+    return Uncheck.apply(d -> toList(streamFiles(d, recursive, extensions)), directory);
+  }
+
+  private static List<File> toList(final Stream<File> stream) {
+    return stream.collect(Collectors.toList());
+  }
+
+  /**
+   * Streams over the files in a given directory (and optionally its subdirectories) which match an
+   * array of extensions.
+   *
+   * @param directory the directory to search in
+   * @param recursive if true all subdirectories are searched as well
+   * @param extensions an array of extensions, ex. {"java","xml"}. If this parameter is {@code
+   *     null}, all files are returned.
+   * @return an iterator of java.io.File with the matching files
+   * @since 2.9.0
+   */
+  public static Stream<File> streamFiles(
+      final File directory, final boolean recursive, final String... extensions)
+      throws IOException {
+    // @formatter:off
+    final IOFileFilter filter =
+        extensions == null
+            ? FileFileFilter.INSTANCE
+            : FileFileFilter.INSTANCE.and(new SuffixFileFilter(toSuffixes(extensions)));
+    // @formatter:on
+    return PathUtils.walk(
+            directory.toPath(), filter, toMaxDepth(recursive), false, FileVisitOption.FOLLOW_LINKS)
+        .map(Path::toFile);
+  }
+
+  /**
+   * Converts an array of file extensions to suffixes.
+   *
+   * @param extensions an array of extensions. Format: {"java", "xml"}
+   * @return an array of suffixes. Format: {".java", ".xml"}
+   * @throws NullPointerException if the parameter is null
+   */
+  private static String[] toSuffixes(final String... extensions) {
+    return Stream.of(Objects.requireNonNull(extensions, "extensions"))
+        .map(e -> "." + e)
+        .toArray(String[]::new);
+  }
+
+  /**
+   * Converts whether or not to recurse into a recursion max depth.
+   *
+   * @param recursive whether or not to recurse
+   * @return the recursion depth
+   */
+  private static int toMaxDepth(final boolean recursive) {
+    return recursive ? Integer.MAX_VALUE : 1;
+  }
+
+  /**
+   * Converts each of an array of {@link File} to a {@link URL}.
+   *
+   * <p>Returns an array of the same size as the input.
+   *
+   * @param files the files to convert, must not be {@code null}
+   * @return an array of URLs matching the input
+   * @throws IOException if a file cannot be converted
+   * @throws NullPointerException if the parameter is null
+   */
+  public static URL[] toURLs(final File... files) throws IOException {
+    Objects.requireNonNull(files, "files");
+    final URL[] urls = new URL[files.length];
+    for (int i = 0; i < urls.length; i++) {
+      urls[i] = files[i].toURI().toURL();
+    }
+    return urls;
+  }
+
+  public static void moveDirectory(final File srcDir, final File destDir) throws IOException {
+    validateMoveParameters(srcDir, destDir);
+    requireDirectory(srcDir, "srcDir");
+    requireAbsent(destDir, "destDir");
+    if (!srcDir.renameTo(destDir)) {
+      if (destDir.getCanonicalPath().startsWith(srcDir.getCanonicalPath() + File.separator)) {
+        throw new IOException(
+            "Cannot move directory: " + srcDir + " to a subdirectory of itself: " + destDir);
+      }
+      copyDirectory(srcDir, destDir);
+      deleteDirectory(srcDir);
+      if (srcDir.exists()) {
+        throw new IOException(
+            "Failed to delete original directory '" + srcDir + "' after copy to '" + destDir + "'");
+      }
+    }
+  }
+
+  /**
+   * Copies a whole directory to a new location preserving the file dates.
+   *
+   * <p>This method copies the specified directory and all its child directories and files to the
+   * specified destination. The destination is the new location and name of the directory.
+   *
+   * <p>The destination directory is created if it does not exist. If the destination directory did
+   * exist, then this method merges the source with the destination, with the source taking
+   * precedence.
+   *
+   * <p><strong>Note:</strong> Setting {@code preserveFileDate} to {@code true} tries to preserve
+   * the file's last modified date/times using {@link BasicFileAttributeView#setTimes(FileTime,
+   * FileTime, FileTime)}, however it is not guaranteed that the operation will succeed. If the
+   * modification operation fails it will fallback to {@link File#setLastModified(long)} and if that
+   * fails, the methods throws IOException.
+   *
+   * @param srcDir an existing directory to copy, must not be {@code null}.
+   * @param destDir the new directory, must not be {@code null}.
+   * @throws NullPointerException if any of the given {@link File}s are {@code null}.
+   * @throws IllegalArgumentException if the source or destination is invalid.
+   * @throws FileNotFoundException if the source does not exist.
+   * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
+   * @since 1.1
+   */
+  public static void copyDirectory(final File srcDir, final File destDir) throws IOException {
+    copyDirectory(srcDir, destDir, true);
+  }
+
+  /**
+   * Copies a whole directory to a new location.
+   *
+   * <p>This method copies the contents of the specified source directory to within the specified
+   * destination directory.
+   *
+   * <p>The destination directory is created if it does not exist. If the destination directory did
+   * exist, then this method merges the source with the destination, with the source taking
+   * precedence.
+   *
+   * <p><strong>Note:</strong> Setting {@code preserveFileDate} to {@code true} tries to preserve
+   * the files' last modified date/times using {@link File#setLastModified(long)}, however it is not
+   * guaranteed that those operations will succeed. If the modification operation fails, the methods
+   * throws IOException.
+   *
+   * @param srcDir an existing directory to copy, must not be {@code null}.
+   * @param destDir the new directory, must not be {@code null}.
+   * @param preserveFileDate true if the file date of the copy should be the same as the original.
+   * @throws NullPointerException if any of the given {@link File}s are {@code null}.
+   * @throws IllegalArgumentException if the source or destination is invalid.
+   * @throws FileNotFoundException if the source does not exist.
+   * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
+   * @since 1.1
+   */
+  public static void copyDirectory(
+      final File srcDir, final File destDir, final boolean preserveFileDate) throws IOException {
+    copyDirectory(srcDir, destDir, null, preserveFileDate);
+  }
+
+  /**
+   * Copies a filtered directory to a new location.
+   *
+   * <p>This method copies the contents of the specified source directory to within the specified
+   * destination directory.
+   *
+   * <p>The destination directory is created if it does not exist. If the destination directory did
+   * exist, then this method merges the source with the destination, with the source taking
+   * precedence.
+   *
+   * <p><strong>Note:</strong> Setting {@code preserveFileDate} to {@code true} tries to preserve
+   * the file's last modified date/times using {@link BasicFileAttributeView#setTimes(FileTime,
+   * FileTime, FileTime)}, however it is not guaranteed that the operation will succeed. If the
+   * modification operation fails it will fallback to {@link File#setLastModified(long)} and if that
+   * fails, the methods throws IOException. <b>Example: Copy directories only</b>
+   *
+   * <pre>
+   * // only copy the directory structure
+   * FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY, false);
+   * </pre>
+   *
+   * <b>Example: Copy directories and txt files</b>
+   *
+   * <pre>
+   * // Create a filter for ".txt" files
+   * IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt");
+   * IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter);
+   *
+   * // Create a filter for either directories or ".txt" files
+   * FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles);
+   *
+   * // Copy using the filter
+   * FileUtils.copyDirectory(srcDir, destDir, filter, false);
+   * </pre>
+   *
+   * @param srcDir an existing directory to copy, must not be {@code null}.
+   * @param destDir the new directory, must not be {@code null}.
+   * @param filter the filter to apply, null means copy all directories and files.
+   * @param preserveFileDate true if the file date of the copy should be the same as the original.
+   * @throws NullPointerException if any of the given {@link File}s are {@code null}.
+   * @throws IllegalArgumentException if the source or destination is invalid.
+   * @throws FileNotFoundException if the source does not exist.
+   * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
+   * @since 1.4
+   */
+  public static void copyDirectory(
+      final File srcDir,
+      final File destDir,
+      final FileFilter filter,
+      final boolean preserveFileDate)
+      throws IOException {
+    copyDirectory(srcDir, destDir, filter, preserveFileDate, StandardCopyOption.REPLACE_EXISTING);
+  }
+
+  /**
+   * Copies a filtered directory to a new location.
+   *
+   * <p>This method copies the contents of the specified source directory to within the specified
+   * destination directory.
+   *
+   * <p>The destination directory is created if it does not exist. If the destination directory did
+   * exist, then this method merges the source with the destination, with the source taking
+   * precedence.
+   *
+   * <p><strong>Note:</strong> Setting {@code preserveFileDate} to {@code true} tries to preserve
+   * the file's last modified date/times using {@link BasicFileAttributeView#setTimes(FileTime,
+   * FileTime, FileTime)}, however it is not guaranteed that the operation will succeed. If the
+   * modification operation fails it will fallback to {@link File#setLastModified(long)} and if that
+   * fails, the methods throws IOException. <b>Example: Copy directories only</b>
+   *
+   * <pre>
+   * // only copy the directory structure
+   * FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY, false);
+   * </pre>
+   *
+   * <b>Example: Copy directories and txt files</b>
+   *
+   * <pre>
+   * // Create a filter for ".txt" files
+   * IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt");
+   * IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter);
+   *
+   * // Create a filter for either directories or ".txt" files
+   * FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles);
+   *
+   * // Copy using the filter
+   * FileUtils.copyDirectory(srcDir, destDir, filter, false);
+   * </pre>
+   *
+   * @param srcDir an existing directory to copy, must not be {@code null}
+   * @param destDir the new directory, must not be {@code null}
+   * @param fileFilter the filter to apply, null means copy all directories and files
+   * @param preserveFileDate true if the file date of the copy should be the same as the original
+   * @param copyOptions options specifying how the copy should be done, for example {@link
+   *     StandardCopyOption}.
+   * @throws NullPointerException if any of the given {@link File}s are {@code null}.
+   * @throws IllegalArgumentException if the source or destination is invalid.
+   * @throws FileNotFoundException if the source does not exist.
+   * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
+   * @since 2.8.0
+   */
+  public static void copyDirectory(
+      final File srcDir,
+      final File destDir,
+      final FileFilter fileFilter,
+      final boolean preserveFileDate,
+      final CopyOption... copyOptions)
+      throws IOException {
+    requireFileCopy(srcDir, destDir);
+    requireDirectory(srcDir, "srcDir");
+    requireCanonicalPathsNotEquals(srcDir, destDir);
+
+    // Cater for destination being directory within the source directory (see IO-141)
+    List<String> exclusionList = null;
+    final String srcDirCanonicalPath = srcDir.getCanonicalPath();
+    final String destDirCanonicalPath = destDir.getCanonicalPath();
+    if (destDirCanonicalPath.startsWith(srcDirCanonicalPath)) {
+      final File[] srcFiles = listFiles(srcDir, fileFilter);
+      if (srcFiles.length > 0) {
+        exclusionList = new ArrayList<>(srcFiles.length);
+        for (final File srcFile : srcFiles) {
+          exclusionList.add(new File(destDir, srcFile.getName()).getCanonicalPath());
+        }
+      }
+    }
+    doCopyDirectory(srcDir, destDir, fileFilter, exclusionList, preserveFileDate, copyOptions);
+  }
+
+  /**
+   * Requires parameter attributes for a file copy operation.
+   *
+   * @param source the source file
+   * @param destination the destination
+   * @throws NullPointerException if any of the given {@link File}s are {@code null}.
+   * @throws FileNotFoundException if the source does not exist.
+   */
+  private static void requireFileCopy(final File source, final File destination)
+      throws FileNotFoundException {
+    requireExistsChecked(source, "source");
+    Objects.requireNonNull(destination, "destination");
+  }
+
+  /**
+   * Internal copy directory method. Creates all destination parent directories, including any
+   * necessary but non-existent parent directories.
+   *
+   * @param srcDir the validated source directory, must not be {@code null}.
+   * @param destDir the validated destination directory, must not be {@code null}.
+   * @param fileFilter the filter to apply, null means copy all directories and files.
+   * @param exclusionList List of files and directories to exclude from the copy, may be null.
+   * @param preserveDirDate preserve the directories last modified dates.
+   * @param copyOptions options specifying how the copy should be done, see {@link
+   *     StandardCopyOption}.
+   * @throws IOException if the directory was not created along with all its parent directories.
+   * @throws SecurityException See {@link File#mkdirs()}.
+   */
+  private static void doCopyDirectory(
+      final File srcDir,
+      final File destDir,
+      final FileFilter fileFilter,
+      final List<String> exclusionList,
+      final boolean preserveDirDate,
+      final CopyOption... copyOptions)
+      throws IOException {
+    // recurse dirs, copy files.
+    final File[] srcFiles = listFiles(srcDir, fileFilter);
+    requireDirectoryIfExists(destDir, "destDir");
+    mkdirs(destDir);
+    requireCanWrite(destDir, "destDir");
+    for (final File srcFile : srcFiles) {
+      final File dstFile = new File(destDir, srcFile.getName());
+      if (exclusionList == null || !exclusionList.contains(srcFile.getCanonicalPath())) {
+        if (srcFile.isDirectory()) {
+          doCopyDirectory(
+              srcFile, dstFile, fileFilter, exclusionList, preserveDirDate, copyOptions);
+        } else {
+          copyFile(srcFile, dstFile, preserveDirDate, copyOptions);
+        }
+      }
+    }
+    // Do this last, as the above has probably affected directory metadata
+    if (preserveDirDate) {
+      setTimes(srcDir, destDir);
+    }
+  }
+
+  /**
+   * Throws an {@link IllegalArgumentException} if the file is not writable. This provides a more
+   * precise exception message than a plain access denied.
+   *
+   * @param file The file to test.
+   * @param name The parameter name to use in the exception message.
+   * @throws NullPointerException if the given {@link File} is {@code null}.
+   * @throws IllegalArgumentException if the file is not writable.
+   */
+  private static void requireCanWrite(final File file, final String name) {
+    Objects.requireNonNull(file, "file");
+    if (!file.canWrite()) {
+      throw new IllegalArgumentException(
+          "File parameter '" + name + " is not writable: '" + file + "'");
+    }
+  }
+
+  /**
+   * Requires that the given {@link File} is a directory if it exists.
+   *
+   * @param directory The {@link File} to check.
+   * @param name The parameter name to use in the exception message in case of null input.
+   * @return the given directory.
+   * @throws NullPointerException if the given {@link File} is {@code null}.
+   * @throws IllegalArgumentException if the given {@link File} exists but is not a directory.
+   */
+  private static File requireDirectoryIfExists(final File directory, final String name) {
+    Objects.requireNonNull(directory, name);
+    if (directory.exists()) {
+      requireDirectory(directory, name);
+    }
+    return directory;
+  }
+
+  /**
+   * Tests whether the specified {@link File} is a directory or not. Implemented as a null-safe
+   * delegate to {@link Files#isDirectory(Path path, LinkOption... options)}.
+   *
+   * @param file the path to the file.
+   * @param options options indicating how symbolic links are handled
+   * @return {@code true} if the file is a directory; {@code false} if the path is null, the file
+   *     does not exist, is not a directory, or it cannot be determined if the file is a directory
+   *     or not.
+   * @throws SecurityException In the case of the default provider, and a security manager is
+   *     installed, the {@link SecurityManager#checkRead(String) checkRead} method is invoked to
+   *     check read access to the directory.
+   * @since 2.9.0
+   */
+  public static boolean isDirectory(final File file, final LinkOption... options) {
+    return file != null && Files.isDirectory(file.toPath(), options);
+  }
+
+  /**
+   * Reads the contents of a file into a String. The file is always closed.
+   *
+   * @param file the file to read, must not be {@code null}
+   * @param charsetName the name of the requested charset, {@code null} means platform default
+   * @return the file contents, never {@code null}
+   * @throws NullPointerException if file is {@code null}.
+   * @throws FileNotFoundException if the file does not exist, is a directory rather than a regular
+   *     file, or for some other reason cannot be opened for reading.
+   * @throws IOException if an I/O error occurs.
+   * @since 2.3
+   */
+  public static String readFileToString(final File file, final Charset charsetName)
+      throws IOException {
+    return IOUtils.toString(
+        () -> Files.newInputStream(file.toPath()), Charsets.toCharset(charsetName));
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/FilenameUtils.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/FilenameUtils.java
new file mode 100644
index 00000000..fb327944
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/FilenameUtils.java
@@ -0,0 +1,290 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tsfile.external.commons.io;
+
+import java.io.File;
+
+public class FilenameUtils {
+  /**
+   * Gets the base name, minus the full path and extension, from a full fileName.
+   *
+   * <p>This method will handle a file in either Unix or Windows format. The text after the last
+   * forward or backslash and before the last dot is returned.
+   *
+   * <pre>
+   * a/b/c.txt --&gt; c
+   * a.txt     --&gt; a
+   * a/b/c     --&gt; c
+   * a/b/c/    --&gt; ""
+   * </pre>
+   *
+   * <p>The output will be the same irrespective of the machine that the code is running on.
+   *
+   * @param fileName the fileName to query, null returns null
+   * @return the name of the file without the path, or an empty string if none exists
+   * @throws IllegalArgumentException if the fileName contains the null character ({@code U+0000})
+   */
+  public static String getBaseName(final String fileName) {
+    return removeExtension(getName(fileName));
+  }
+
+  /**
+   * Removes the extension from a fileName.
+   *
+   * <p>This method returns the textual part of the fileName before the last dot. There must be no
+   * directory separator after the dot.
+   *
+   * <pre>
+   * foo.txt    --&gt; foo
+   * a\b\c.jpg  --&gt; a\b\c
+   * a\b\c      --&gt; a\b\c
+   * a.b\c      --&gt; a.b\c
+   * </pre>
+   *
+   * <p>The output will be the same irrespective of the machine that the code is running on.
+   *
+   * @param fileName the fileName to query, null returns null
+   * @return the fileName minus the extension
+   * @throws IllegalArgumentException if the fileName contains the null character ({@code U+0000})
+   */
+  public static String removeExtension(final String fileName) {
+    if (fileName == null) {
+      return null;
+    }
+    requireNonNullChars(fileName);
+
+    final int index = indexOfExtension(fileName);
+    if (index == NOT_FOUND) {
+      return fileName;
+    }
+    return fileName.substring(0, index);
+  }
+
+  /**
+   * Gets the name minus the path from a full fileName.
+   *
+   * <p>This method will handle a file in either Unix or Windows format. The text after the last
+   * forward or backslash is returned.
+   *
+   * <pre>
+   * a/b/c.txt --&gt; c.txt
+   * a.txt     --&gt; a.txt
+   * a/b/c     --&gt; c
+   * a/b/c/    --&gt; ""
+   * </pre>
+   *
+   * <p>The output will be the same irrespective of the machine that the code is running on.
+   *
+   * @param fileName the fileName to query, null returns null
+   * @return the name of the file without the path, or an empty string if none exists
+   * @throws IllegalArgumentException if the fileName contains the null character ({@code U+0000})
+   */
+  public static String getName(final String fileName) {
+    if (fileName == null) {
+      return null;
+    }
+    return requireNonNullChars(fileName).substring(indexOfLastSeparator(fileName) + 1);
+  }
+
+  /**
+   * Checks the input for null characters ({@code U+0000}), a sign of unsanitized data being passed
+   * to file level functions.
+   *
+   * <p>This may be used for poison byte attacks.
+   *
+   * @param path the path to check
+   * @return The input
+   * @throws IllegalArgumentException if path contains the null character ({@code U+0000})
+   */
+  private static String requireNonNullChars(final String path) {
+    if (path.indexOf(0) >= 0) {
+      throw new IllegalArgumentException(
+          "Null character present in file/path name. There are no known legitimate use cases for such data, but several injection attacks may use it");
+    }
+    return path;
+  }
+
+  /////////////////////////////////////////////////////////////////////////////////////////////////
+  // IoTDB
+  /////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static final int NOT_FOUND = -1;
+  private static final String EMPTY_STRING = "";
+
+  /**
+   * The extension separator character.
+   *
+   * @since 1.4
+   */
+  public static final char EXTENSION_SEPARATOR = '.';
+
+  /** The system separator character. */
+  private static final char SYSTEM_NAME_SEPARATOR = File.separatorChar;
+
+  /** The separator character that is the opposite of the system separator. */
+  private static final char OTHER_SEPARATOR = flipSeparator(SYSTEM_NAME_SEPARATOR);
+
+  /** The Unix separator character. */
+  private static final char UNIX_NAME_SEPARATOR = '/';
+
+  /** The Windows separator character. */
+  private static final char WINDOWS_NAME_SEPARATOR = '\\';
+
+  /**
+   * Gets the extension of a fileName.
+   *
+   * <p>This method returns the textual part of the fileName after the last dot. There must be no
+   * directory separator after the dot.
+   *
+   * <pre>
+   * foo.txt      --&gt; "txt"
+   * a/b/c.jpg    --&gt; "jpg"
+   * a/b.txt/c    --&gt; ""
+   * a/b/c        --&gt; ""
+   * </pre>
+   *
+   * <p>The output will be the same irrespective of the machine that the code is running on, with
+   * the exception of a possible {@link IllegalArgumentException} on Windows (see below).
+   *
+   * <p><b>Note:</b> This method used to have a hidden problem for names like "foo.exe:bar.txt". In
+   * this case, the name wouldn't be the name of a file, but the identifier of an alternate data
+   * stream (bar.txt) on the file foo.exe. The method used to return ".txt" here, which would be
+   * misleading. Commons IO 2.7, and later versions, are throwing an {@link
+   * IllegalArgumentException} for names like this.
+   *
+   * @param fileName the fileName to retrieve the extension of.
+   * @return the extension of the file or an empty string if none exists or {@code null} if the
+   *     fileName is {@code null}.
+   * @throws IllegalArgumentException <b>Windows only:</b> The fileName parameter is, in fact, the
+   *     identifier of an Alternate Data Stream, for example "foo.exe:bar.txt".
+   */
+  public static String getExtension(final String fileName) throws IllegalArgumentException {
+    if (fileName == null) {
+      return null;
+    }
+    final int index = indexOfExtension(fileName);
+    if (index == NOT_FOUND) {
+      return EMPTY_STRING;
+    }
+    return fileName.substring(index + 1);
+  }
+
+  /**
+   * Returns the index of the last extension separator character, which is a dot.
+   *
+   * <p>This method also checks that there is no directory separator after the last dot. To do this
+   * it uses {@link #indexOfLastSeparator(String)} which will handle a file in either Unix or
+   * Windows format.
+   *
+   * <p>The output will be the same irrespective of the machine that the code is running on, with
+   * the exception of a possible {@link IllegalArgumentException} on Windows (see below).
+   * <b>Note:</b> This method used to have a hidden problem for names like "foo.exe:bar.txt". In
+   * this case, the name wouldn't be the name of a file, but the identifier of an alternate data
+   * stream (bar.txt) on the file foo.exe. The method used to return ".txt" here, which would be
+   * misleading. Commons IO 2.7, and later versions, are throwing an {@link
+   * IllegalArgumentException} for names like this.
+   *
+   * @param fileName the fileName to find the last extension separator in, null returns -1
+   * @return the index of the last extension separator character, or -1 if there is no such
+   *     character
+   * @throws IllegalArgumentException <b>Windows only:</b> The fileName parameter is, in fact, the
+   *     identifier of an Alternate Data Stream, for example "foo.exe:bar.txt".
+   */
+  public static int indexOfExtension(final String fileName) throws IllegalArgumentException {
+    if (fileName == null) {
+      return NOT_FOUND;
+    }
+    if (isSystemWindows()) {
+      // Special handling for NTFS ADS: Don't accept colon in the fileName.
+      final int offset = fileName.indexOf(':', getAdsCriticalOffset(fileName));
+      if (offset != -1) {
+        throw new IllegalArgumentException("NTFS ADS separator (':') in file name is forbidden.");
+      }
+    }
+    final int extensionPos = fileName.lastIndexOf(EXTENSION_SEPARATOR);
+    final int lastSeparator = indexOfLastSeparator(fileName);
+    return lastSeparator > extensionPos ? NOT_FOUND : extensionPos;
+  }
+
+  /**
+   * Determines if Windows file system is in use.
+   *
+   * @return true if the system is Windows
+   */
+  static boolean isSystemWindows() {
+    return SYSTEM_NAME_SEPARATOR == WINDOWS_NAME_SEPARATOR;
+  }
+
+  /**
+   * Flips the Windows name separator to Linux and vice-versa.
+   *
+   * @param ch The Windows or Linux name separator.
+   * @return The Windows or Linux name separator.
+   */
+  static char flipSeparator(final char ch) {
+    if (ch == UNIX_NAME_SEPARATOR) {
+      return WINDOWS_NAME_SEPARATOR;
+    }
+    if (ch == WINDOWS_NAME_SEPARATOR) {
+      return UNIX_NAME_SEPARATOR;
+    }
+    throw new IllegalArgumentException(String.valueOf(ch));
+  }
+
+  /**
+   * Special handling for NTFS ADS: Don't accept colon in the fileName.
+   *
+   * @param fileName a file name
+   * @return ADS offsets.
+   */
+  private static int getAdsCriticalOffset(final String fileName) {
+    // Step 1: Remove leading path segments.
+    final int offset1 = fileName.lastIndexOf(SYSTEM_NAME_SEPARATOR);
+    final int offset2 = fileName.lastIndexOf(OTHER_SEPARATOR);
+    if (offset1 == -1) {
+      if (offset2 == -1) {
+        return 0;
+      }
+      return offset2 + 1;
+    }
+    if (offset2 == -1) {
+      return offset1 + 1;
+    }
+    return Math.max(offset1, offset2) + 1;
+  }
+
+  /**
+   * Returns the index of the last directory separator character.
+   *
+   * <p>This method will handle a file in either Unix or Windows format. The position of the last
+   * forward or backslash is returned.
+   *
+   * <p>The output will be the same irrespective of the machine that the code is running on.
+   *
+   * @param fileName the fileName to find the last path separator in, null returns -1
+   * @return the index of the last separator character, or -1 if there is no such character
+   */
+  public static int indexOfLastSeparator(final String fileName) {
+    if (fileName == null) {
+      return NOT_FOUND;
+    }
+    final int lastUnixPos = fileName.lastIndexOf(UNIX_NAME_SEPARATOR);
+    final int lastWindowsPos = fileName.lastIndexOf(WINDOWS_NAME_SEPARATOR);
+    return Math.max(lastUnixPos, lastWindowsPos);
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/IOCase.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/IOCase.java
new file mode 100644
index 00000000..c3e5a042
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/IOCase.java
@@ -0,0 +1,247 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tsfile.external.commons.io;
+
+import java.util.Objects;
+import java.util.stream.Stream;
+
+/**
+ * Enumeration of IO case sensitivity.
+ *
+ * <p>Different filing systems have different rules for case-sensitivity. Windows is
+ * case-insensitive, Unix is case-sensitive.
+ *
+ * <p>This class captures that difference, providing an enumeration to control how file name
+ * comparisons should be performed. It also provides methods that use the enumeration to perform
+ * comparisons.
+ *
+ * <p>Wherever possible, you should use the {@code check} methods in this class to compare file
+ * names.
+ *
+ * @since 1.3
+ */
+public enum IOCase {
+
+  /** The constant for case-sensitive regardless of operating system. */
+  SENSITIVE("Sensitive", true),
+
+  /** The constant for case-insensitive regardless of operating system. */
+  INSENSITIVE("Insensitive", false);
+
+  /** Serialization version. */
+  private static final long serialVersionUID = -6343169151696340687L;
+
+  /**
+   * Factory method to create an IOCase from a name.
+   *
+   * @param name the name to find
+   * @return the IOCase object
+   * @throws IllegalArgumentException if the name is invalid
+   */
+  public static IOCase forName(final String name) {
+    return Stream.of(IOCase.values())
+        .filter(ioCase -> ioCase.getName().equals(name))
+        .findFirst()
+        .orElseThrow(() -> new IllegalArgumentException("Illegal IOCase name: " + name));
+  }
+
+  /**
+   * Tests for cases sensitivity in a null-safe manner.
+   *
+   * @param ioCase an IOCase.
+   * @return true if the input is non-null and {@link #isCaseSensitive()}.
+   * @since 2.10.0
+   */
+  public static boolean isCaseSensitive(final IOCase ioCase) {
+    return ioCase != null && ioCase.isCaseSensitive();
+  }
+
+  /**
+   * Returns the given value if not-null, the defaultValue if null.
+   *
+   * @param value the value to test.
+   * @param defaultValue the default value.
+   * @return the given value if not-null, the defaultValue if null.
+   * @since 2.12.0
+   */
+  public static IOCase value(final IOCase value, final IOCase defaultValue) {
+    return value != null ? value : defaultValue;
+  }
+
+  /** The enumeration name. */
+  private final String name;
+
+  /** The sensitivity flag. */
+  private final transient boolean sensitive;
+
+  /**
+   * Constructs a new instance.
+   *
+   * @param name the name
+   * @param sensitive the sensitivity
+   */
+  IOCase(final String name, final boolean sensitive) {
+    this.name = name;
+    this.sensitive = sensitive;
+  }
+
+  /**
+   * Compares two strings using the case-sensitivity rule.
+   *
+   * <p>This method mimics {@link String#compareTo} but takes case-sensitivity into account.
+   *
+   * @param str1 the first string to compare, not null
+   * @param str2 the second string to compare, not null
+   * @return true if equal using the case rules
+   * @throws NullPointerException if either string is null
+   */
+  public int checkCompareTo(final String str1, final String str2) {
+    Objects.requireNonNull(str1, "str1");
+    Objects.requireNonNull(str2, "str2");
+    return sensitive ? str1.compareTo(str2) : str1.compareToIgnoreCase(str2);
+  }
+
+  /**
+   * Checks if one string ends with another using the case-sensitivity rule.
+   *
+   * <p>This method mimics {@link String#endsWith} but takes case-sensitivity into account.
+   *
+   * @param str the string to check
+   * @param end the end to compare against
+   * @return true if equal using the case rules, false if either input is null
+   */
+  public boolean checkEndsWith(final String str, final String end) {
+    if (str == null || end == null) {
+      return false;
+    }
+    final int endLen = end.length();
+    return str.regionMatches(!sensitive, str.length() - endLen, end, 0, endLen);
+  }
+
+  /**
+   * Compares two strings using the case-sensitivity rule.
+   *
+   * <p>This method mimics {@link String#equals} but takes case-sensitivity into account.
+   *
+   * @param str1 the first string to compare, not null
+   * @param str2 the second string to compare, not null
+   * @return true if equal using the case rules
+   * @throws NullPointerException if either string is null
+   */
+  public boolean checkEquals(final String str1, final String str2) {
+    Objects.requireNonNull(str1, "str1");
+    Objects.requireNonNull(str2, "str2");
+    return sensitive ? str1.equals(str2) : str1.equalsIgnoreCase(str2);
+  }
+
+  /**
+   * Checks if one string contains another starting at a specific index using the case-sensitivity
+   * rule.
+   *
+   * <p>This method mimics parts of {@link String#indexOf(String, int)} but takes case-sensitivity
+   * into account.
+   *
+   * @param str the string to check, not null
+   * @param strStartIndex the index to start at in str
+   * @param search the start to search for, not null
+   * @return the first index of the search String, -1 if no match or {@code null} string input
+   * @throws NullPointerException if either string is null
+   * @since 2.0
+   */
+  public int checkIndexOf(final String str, final int strStartIndex, final String search) {
+    final int endIndex = str.length() - search.length();
+    if (endIndex >= strStartIndex) {
+      for (int i = strStartIndex; i <= endIndex; i++) {
+        if (checkRegionMatches(str, i, search)) {
+          return i;
+        }
+      }
+    }
+    return -1;
+  }
+
+  /**
+   * Checks if one string contains another at a specific index using the case-sensitivity rule.
+   *
+   * <p>This method mimics parts of {@link String#regionMatches(boolean, int, String, int, int)} but
+   * takes case-sensitivity into account.
+   *
+   * @param str the string to check, not null
+   * @param strStartIndex the index to start at in str
+   * @param search the start to search for, not null
+   * @return true if equal using the case rules
+   * @throws NullPointerException if either string is null
+   */
+  public boolean checkRegionMatches(
+      final String str, final int strStartIndex, final String search) {
+    return str.regionMatches(!sensitive, strStartIndex, search, 0, search.length());
+  }
+
+  /**
+   * Checks if one string starts with another using the case-sensitivity rule.
+   *
+   * <p>This method mimics {@link String#startsWith(String)} but takes case-sensitivity into
+   * account.
+   *
+   * @param str the string to check
+   * @param start the start to compare against
+   * @return true if equal using the case rules, false if either input is null
+   */
+  public boolean checkStartsWith(final String str, final String start) {
+    return str != null
+        && start != null
+        && str.regionMatches(!sensitive, 0, start, 0, start.length());
+  }
+
+  /**
+   * Gets the name of the constant.
+   *
+   * @return the name of the constant
+   */
+  public String getName() {
+    return name;
+  }
+
+  /**
+   * Does the object represent case-sensitive comparison.
+   *
+   * @return true if case-sensitive
+   */
+  public boolean isCaseSensitive() {
+    return sensitive;
+  }
+
+  /**
+   * Replaces the enumeration from the stream with a real one. This ensures that the correct flag is
+   * set for SYSTEM.
+   *
+   * @return the resolved object
+   */
+  private Object readResolve() {
+    return forName(name);
+  }
+
+  /**
+   * Gets a string describing the sensitivity.
+   *
+   * @return a string describing the sensitivity
+   */
+  @Override
+  public String toString() {
+    return name;
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/IOExceptionList.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/IOExceptionList.java
new file mode 100644
index 00000000..30d8e35c
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/IOExceptionList.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tsfile.external.commons.io;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * An IOException based on a list of Throwable causes.
+ *
+ * <p>The first exception in the list is used as this exception's cause and is accessible with the
+ * usual {@link #getCause()} while the complete list is accessible with {@link #getCauseList()}.
+ *
+ * @since 2.7
+ */
+public class IOExceptionList extends IOException implements Iterable<Throwable> {
+
+  private static final long serialVersionUID = 1L;
+
+  /**
+   * Throws this exception if the list is not null or empty.
+   *
+   * @param causeList The list to test.
+   * @param message The detail message, see {@link #getMessage()}.
+   * @throws IOExceptionList if the list is not null or empty.
+   * @since 2.12.0
+   */
+  public static void checkEmpty(final List<? extends Throwable> causeList, final Object message)
+      throws IOExceptionList {
+    if (!isEmpty(causeList)) {
+      throw new IOExceptionList(Objects.toString(message, null), causeList);
+    }
+  }
+
+  private static boolean isEmpty(final List<? extends Throwable> causeList) {
+    return size(causeList) == 0;
+  }
+
+  private static int size(final List<? extends Throwable> causeList) {
+    return causeList != null ? causeList.size() : 0;
+  }
+
+  private static String toMessage(final List<? extends Throwable> causeList) {
+    return String.format("%,d exception(s): %s", size(causeList), causeList);
+  }
+
+  /** List of causes. */
+  private final List<? extends Throwable> causeList;
+
+  /**
+   * Constructs a new exception caused by a list of exceptions.
+   *
+   * @param causeList a list of cause exceptions.
+   */
+  public IOExceptionList(final List<? extends Throwable> causeList) {
+    this(toMessage(causeList), causeList);
+  }
+
+  /**
+   * Constructs a new exception caused by a list of exceptions.
+   *
+   * @param message The detail message, see {@link #getMessage()}.
+   * @param causeList a list of cause exceptions.
+   * @since 2.9.0
+   */
+  public IOExceptionList(final String message, final List<? extends Throwable> causeList) {
+    super(
+        message != null ? message : toMessage(causeList),
+        isEmpty(causeList) ? null : causeList.get(0));
+    this.causeList = causeList == null ? Collections.emptyList() : causeList;
+  }
+
+  /**
+   * Gets the cause exception at the given index.
+   *
+   * @param <T> type of exception to return.
+   * @param index index in the cause list.
+   * @return The list of causes.
+   */
+  public <T extends Throwable> T getCause(final int index) {
+    return (T) causeList.get(index);
+  }
+
+  /**
+   * Gets the cause exception at the given index.
+   *
+   * @param <T> type of exception to return.
+   * @param index index in the cause list.
+   * @param clazz type of exception to return.
+   * @return The list of causes.
+   */
+  public <T extends Throwable> T getCause(final int index, final Class<T> clazz) {
+    return clazz.cast(getCause(index));
+  }
+
+  /**
+   * Gets the cause list.
+   *
+   * @param <T> type of exception to return.
+   * @return The list of causes.
+   */
+  public <T extends Throwable> List<T> getCauseList() {
+    return (List<T>) new ArrayList<>(causeList);
+  }
+
+  /**
+   * Works around Throwable and Generics, may fail at runtime depending on the argument value.
+   *
+   * @param <T> type of exception to return.
+   * @param clazz the target type
+   * @return The list of causes.
+   */
+  public <T extends Throwable> List<T> getCauseList(final Class<T> clazz) {
+    return (List<T>) new ArrayList<>(causeList);
+  }
+
+  @Override
+  public Iterator<Throwable> iterator() {
+    return getCauseList().iterator();
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/IOIndexedException.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/IOIndexedException.java
new file mode 100644
index 00000000..64a319ea
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/IOIndexedException.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tsfile.external.commons.io;
+
+import java.io.IOException;
+
+/**
+ * A IOException associated with a source index.
+ *
+ * @since 2.7
+ */
+public class IOIndexedException extends IOException {
+
+  private static final long serialVersionUID = 1L;
+
+  /**
+   * Converts input to a suitable String for exception message.
+   *
+   * @param index An index into a source collection.
+   * @param cause A cause.
+   * @return A message.
+   */
+  protected static String toMessage(final int index, final Throwable cause) {
+    // Letting index be any int
+    final String unspecified = "Null";
+    final String name = cause == null ? unspecified : cause.getClass().getSimpleName();
+    final String msg = cause == null ? unspecified : cause.getMessage();
+    return String.format("%s #%,d: %s", name, index, msg);
+  }
+
+  /** Index. */
+  private final int index;
+
+  /**
+   * Constructs a new exception.
+   *
+   * @param index index of this exception.
+   * @param cause cause exceptions.
+   */
+  public IOIndexedException(final int index, final Throwable cause) {
+    super(toMessage(index, cause), cause);
+    this.index = index;
+  }
+
+  /**
+   * The index of this exception.
+   *
+   * @return index of this exception.
+   */
+  public int getIndex() {
+    return index;
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/IOUtils.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/IOUtils.java
new file mode 100644
index 00000000..1642a652
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/IOUtils.java
@@ -0,0 +1,558 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tsfile.external.commons.io;
+
+import org.apache.tsfile.external.commons.io.function.IOSupplier;
+import org.apache.tsfile.external.commons.io.function.IOTriFunction;
+import org.apache.tsfile.external.commons.io.output.ByteArrayOutputStream;
+import org.apache.tsfile.external.commons.io.output.StringBuilderWriter;
+import org.apache.tsfile.external.commons.io.output.ThresholdingOutputStream;
+import org.apache.tsfile.external.commons.io.output.UnsynchronizedByteArrayOutputStream;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.nio.channels.Channels;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.Objects;
+
+public class IOUtils {
+
+  /**
+   * A singleton empty byte array.
+   *
+   * @since 2.9.0
+   */
+  public static final byte[] EMPTY_BYTE_ARRAY = {};
+
+  /**
+   * Represents the end-of-file (or stream).
+   *
+   * @since 2.5 (made public)
+   */
+  public static final int EOF = -1;
+
+  /** The default buffer size ({@value}) to use in copy methods. */
+  public static final int DEFAULT_BUFFER_SIZE = 8192;
+
+  public static int length(final Object[] array) {
+    return array == null ? 0 : array.length;
+  }
+
+  /////////////////////////////////////////////////////////////////////////////////////////////////
+  // IoTDB
+  /////////////////////////////////////////////////////////////////////////////////////////////////
+
+  /** Internal char array buffer, intended for both reading and writing. */
+  private static final ThreadLocal<char[]> SCRATCH_CHAR_BUFFER_RW =
+      ThreadLocal.withInitial(IOUtils::charArray);
+
+  /**
+   * Returns a new char array of size {@link #DEFAULT_BUFFER_SIZE}.
+   *
+   * @return a new char array of size {@link #DEFAULT_BUFFER_SIZE}.
+   * @since 2.9.0
+   */
+  private static char[] charArray() {
+    return charArray(DEFAULT_BUFFER_SIZE);
+  }
+
+  /**
+   * Returns a new char array of the given size.
+   *
+   * <p>TODO Consider guarding or warning against large allocations...
+   *
+   * @param size array size.
+   * @return a new char array of the given size.
+   * @since 2.9.0
+   */
+  private static char[] charArray(final int size) {
+    return new char[size];
+  }
+
+  /**
+   * Gets the contents of a {@link Reader} as a {@code byte[]} using the specified character
+   * encoding.
+   *
+   * <p>This method buffers the input internally, so there is no need to use a {@link
+   * BufferedReader}.
+   *
+   * @param reader the {@link Reader} to read
+   * @param charset the charset to use, null means platform default
+   * @return the requested byte array
+   * @throws NullPointerException if the input is null
+   * @throws IOException if an I/O error occurs
+   * @since 2.3
+   */
+  public static byte[] toByteArray(final Reader reader, final Charset charset) throws IOException {
+    try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
+      copy(reader, output, charset);
+      return output.toByteArray();
+    }
+  }
+
+  /**
+   * Copies chars from a {@link Reader} to bytes on an {@link OutputStream} using the specified
+   * character encoding, and calling flush.
+   *
+   * <p>This method buffers the input internally, so there is no need to use a {@link
+   * BufferedReader}.
+   *
+   * <p>Due to the implementation of OutputStreamWriter, this method performs a flush.
+   *
+   * <p>This method uses {@link OutputStreamWriter}.
+   *
+   * @param reader the {@link Reader} to read
+   * @param output the {@link OutputStream} to write to
+   * @param outputCharset the charset to use for the OutputStream, null means platform default
+   * @throws NullPointerException if the input or output is null
+   * @throws IOException if an I/O error occurs
+   * @since 2.3
+   */
+  public static void copy(
+      final Reader reader, final OutputStream output, final Charset outputCharset)
+      throws IOException {
+    final OutputStreamWriter writer =
+        new OutputStreamWriter(output, Charsets.toCharset(outputCharset));
+    copy(reader, writer);
+    // XXX Unless anyone is planning on rewriting OutputStreamWriter,
+    // we have to flush here.
+    writer.flush();
+  }
+
+  /**
+   * Gets the contents of an {@link InputStream} as a {@code byte[]}.
+   *
+   * <p>This method buffers the input internally, so there is no need to use a {@link
+   * BufferedInputStream}.
+   *
+   * @param inputStream the {@link InputStream} to read.
+   * @return the requested byte array.
+   * @throws NullPointerException if the InputStream is {@code null}.
+   * @throws IOException if an I/O error occurs or reading more than {@link Integer#MAX_VALUE}
+   *     occurs.
+   */
+  public static byte[] toByteArray(final InputStream inputStream) throws IOException {
+    // We use a ThresholdingOutputStream to avoid reading AND writing more than Integer.MAX_VALUE.
+    try (UnsynchronizedByteArrayOutputStream ubaOutput =
+            UnsynchronizedByteArrayOutputStream.builder().get();
+        ThresholdingOutputStream thresholdOutput =
+            new ThresholdingOutputStream(
+                Integer.MAX_VALUE,
+                os -> {
+                  throw new IllegalArgumentException(
+                      String.format(
+                          "Cannot read more than %,d into a byte array", Integer.MAX_VALUE));
+                },
+                os -> ubaOutput)) {
+      copy(inputStream, thresholdOutput);
+      return ubaOutput.toByteArray();
+    }
+  }
+
+  /**
+   * Returns a new byte array of size {@link #DEFAULT_BUFFER_SIZE}.
+   *
+   * @return a new byte array of size {@link #DEFAULT_BUFFER_SIZE}.
+   * @since 2.9.0
+   */
+  public static byte[] byteArray() {
+    return byteArray(DEFAULT_BUFFER_SIZE);
+  }
+
+  /**
+   * Returns a new byte array of the given size.
+   *
+   * <p>TODO Consider guarding or warning against large allocations...
+   *
+   * @param size array size.
+   * @return a new byte array of the given size.
+   * @throws NegativeArraySizeException if the size is negative.
+   * @since 2.9.0
+   */
+  public static byte[] byteArray(final int size) {
+    return new byte[size];
+  }
+
+  /**
+   * Copies bytes from an {@link InputStream} to an {@link OutputStream}.
+   *
+   * <p>This method buffers the input internally, so there is no need to use a {@link
+   * BufferedInputStream}.
+   *
+   * <p>Large streams (over 2GB) will return a bytes copied value of {@code -1} after the copy has
+   * completed since the correct number of bytes cannot be returned as an int. For large streams use
+   * the {@link #copyLarge(InputStream, OutputStream)} method.
+   *
+   * @param inputStream the {@link InputStream} to read.
+   * @param outputStream the {@link OutputStream} to write.
+   * @return the number of bytes copied, or -1 if greater than {@link Integer#MAX_VALUE}.
+   * @throws NullPointerException if the InputStream is {@code null}.
+   * @throws NullPointerException if the OutputStream is {@code null}.
+   * @throws IOException if an I/O error occurs.
+   * @since 1.1
+   */
+  public static int copy(final InputStream inputStream, final OutputStream outputStream)
+      throws IOException {
+    final long count = copyLarge(inputStream, outputStream);
+    return count > Integer.MAX_VALUE ? EOF : (int) count;
+  }
+
+  /**
+   * Copies bytes from an {@link InputStream} to an {@link OutputStream} using an internal buffer of
+   * the given size.
+   *
+   * <p>This method buffers the input internally, so there is no need to use a {@link
+   * BufferedInputStream}.
+   *
+   * @param inputStream the {@link InputStream} to read.
+   * @param outputStream the {@link OutputStream} to write to
+   * @param bufferSize the bufferSize used to copy from the input to the output
+   * @return the number of bytes copied.
+   * @throws NullPointerException if the InputStream is {@code null}.
+   * @throws NullPointerException if the OutputStream is {@code null}.
+   * @throws IOException if an I/O error occurs.
+   * @since 2.5
+   */
+  public static long copy(
+      final InputStream inputStream, final OutputStream outputStream, final int bufferSize)
+      throws IOException {
+    return copyLarge(inputStream, outputStream, IOUtils.byteArray(bufferSize));
+  }
+
+  /**
+   * Copies bytes from a large (over 2GB) {@link InputStream} to an {@link OutputStream}.
+   *
+   * <p>This method buffers the input internally, so there is no need to use a {@link
+   * BufferedInputStream}.
+   *
+   * <p>The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}.
+   *
+   * @param inputStream the {@link InputStream} to read.
+   * @param outputStream the {@link OutputStream} to write.
+   * @return the number of bytes copied.
+   * @throws NullPointerException if the InputStream is {@code null}.
+   * @throws NullPointerException if the OutputStream is {@code null}.
+   * @throws IOException if an I/O error occurs.
+   * @since 1.3
+   */
+  public static long copyLarge(final InputStream inputStream, final OutputStream outputStream)
+      throws IOException {
+    return copy(inputStream, outputStream, DEFAULT_BUFFER_SIZE);
+  }
+
+  /**
+   * Copies bytes from a large (over 2GB) {@link InputStream} to an {@link OutputStream}.
+   *
+   * <p>This method uses the provided buffer, so there is no need to use a {@link
+   * BufferedInputStream}.
+   *
+   * @param inputStream the {@link InputStream} to read.
+   * @param outputStream the {@link OutputStream} to write.
+   * @param buffer the buffer to use for the copy
+   * @return the number of bytes copied.
+   * @throws NullPointerException if the InputStream is {@code null}.
+   * @throws NullPointerException if the OutputStream is {@code null}.
+   * @throws IOException if an I/O error occurs.
+   * @since 2.2
+   */
+  @SuppressWarnings("resource") // streams are closed by the caller.
+  public static long copyLarge(
+      final InputStream inputStream, final OutputStream outputStream, final byte[] buffer)
+      throws IOException {
+    Objects.requireNonNull(inputStream, "inputStream");
+    Objects.requireNonNull(outputStream, "outputStream");
+    long count = 0;
+    int n;
+    while (EOF != (n = inputStream.read(buffer))) {
+      outputStream.write(buffer, 0, n);
+      count += n;
+    }
+    return count;
+  }
+
+  /**
+   * Gets the contents of a {@link Reader} as a String.
+   *
+   * <p>This method buffers the input internally, so there is no need to use a {@link
+   * BufferedReader}.
+   *
+   * @param reader the {@link Reader} to read
+   * @return the requested String
+   * @throws NullPointerException if the input is null
+   * @throws IOException if an I/O error occurs
+   */
+  public static String toString(final Reader reader) throws IOException {
+    try (StringBuilderWriter sw = new StringBuilderWriter()) {
+      copy(reader, sw);
+      return sw.toString();
+    }
+  }
+
+  /**
+   * Copies chars from a {@link Reader} to a {@link Writer}.
+   *
+   * <p>This method buffers the input internally, so there is no need to use a {@link
+   * BufferedReader}.
+   *
+   * <p>Large streams (over 2GB) will return a chars copied value of {@code -1} after the copy has
+   * completed since the correct number of chars cannot be returned as an int. For large streams use
+   * the {@link #copyLarge(Reader, Writer)} method.
+   *
+   * @param reader the {@link Reader} to read.
+   * @param writer the {@link Writer} to write.
+   * @return the number of characters copied, or -1 if &gt; Integer.MAX_VALUE
+   * @throws NullPointerException if the input or output is null
+   * @throws IOException if an I/O error occurs
+   * @since 1.1
+   */
+  public static int copy(final Reader reader, final Writer writer) throws IOException {
+    final long count = copyLarge(reader, writer);
+    if (count > Integer.MAX_VALUE) {
+      return EOF;
+    }
+    return (int) count;
+  }
+
+  /**
+   * Copies chars from a large (over 2GB) {@link Reader} to a {@link Writer}.
+   *
+   * <p>This method buffers the input internally, so there is no need to use a {@link
+   * BufferedReader}.
+   *
+   * <p>The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}.
+   *
+   * @param reader the {@link Reader} to source.
+   * @param writer the {@link Writer} to target.
+   * @return the number of characters copied
+   * @throws NullPointerException if the input or output is null
+   * @throws IOException if an I/O error occurs
+   * @since 1.3
+   */
+  public static long copyLarge(final Reader reader, final Writer writer) throws IOException {
+    return copyLarge(reader, writer, getScratchCharArray());
+  }
+
+  /**
+   * Copies chars from a large (over 2GB) {@link Reader} to a {@link Writer}.
+   *
+   * <p>This method uses the provided buffer, so there is no need to use a {@link BufferedReader}.
+   *
+   * @param reader the {@link Reader} to source.
+   * @param writer the {@link Writer} to target.
+   * @param buffer the buffer to be used for the copy
+   * @return the number of characters copied
+   * @throws NullPointerException if the input or output is null
+   * @throws IOException if an I/O error occurs
+   * @since 2.2
+   */
+  public static long copyLarge(final Reader reader, final Writer writer, final char[] buffer)
+      throws IOException {
+    long count = 0;
+    int n;
+    while (EOF != (n = reader.read(buffer))) {
+      writer.write(buffer, 0, n);
+      count += n;
+    }
+    return count;
+  }
+
+  /**
+   * Gets the char byte array buffer, intended for both reading and writing.
+   *
+   * @return the char byte array buffer, intended for both reading and writing.
+   */
+  static char[] getScratchCharArray() {
+    return fill0(SCRATCH_CHAR_BUFFER_RW.get());
+  }
+
+  /**
+   * Fills the given array with 0s.
+   *
+   * @param arr The array to fill.
+   * @return The given array.
+   */
+  private static char[] fill0(final char[] arr) {
+    Arrays.fill(arr, (char) 0);
+    return arr;
+  }
+
+  /**
+   * Gets the contents of an input as a {@code byte[]}.
+   *
+   * @param input the input to read.
+   * @param size the size of the input to read, where 0 &lt; {@code size} &lt;= length of input.
+   * @return byte [] of length {@code size}.
+   * @throws IOException if an I/O error occurs or input length is smaller than parameter {@code
+   *     size}.
+   * @throws IllegalArgumentException if {@code size} is less than zero.
+   */
+  static byte[] toByteArray(
+      final IOTriFunction<byte[], Integer, Integer, Integer> input, final int size)
+      throws IOException {
+
+    if (size < 0) {
+      throw new IllegalArgumentException("Size must be equal or greater than zero: " + size);
+    }
+
+    if (size == 0) {
+      return EMPTY_BYTE_ARRAY;
+    }
+
+    final byte[] data = byteArray(size);
+    int offset = 0;
+    int read;
+
+    while (offset < size && (read = input.apply(data, offset, size - offset)) != EOF) {
+      offset += read;
+    }
+
+    if (offset != size) {
+      throw new IOException("Unexpected read size, current: " + offset + ", expected: " + size);
+    }
+
+    return data;
+  }
+
+  /**
+   * Writes chars from a {@link String} to bytes on an {@link OutputStream} using the specified
+   * character encoding.
+   *
+   * <p>This method uses {@link String#getBytes(String)}.
+   *
+   * @param data the {@link String} to write, null ignored
+   * @param output the {@link OutputStream} to write to
+   * @param charset the charset to use, null means platform default
+   * @throws NullPointerException if output is null
+   * @throws IOException if an I/O error occurs
+   * @since 2.3
+   */
+  @SuppressWarnings("resource")
+  public static void write(final String data, final OutputStream output, final Charset charset)
+      throws IOException {
+    if (data != null) {
+      // Use Charset#encode(String), since calling String#getBytes(Charset) might result in
+      // NegativeArraySizeException or OutOfMemoryError.
+      // The underlying OutputStream should not be closed, so the channel is not closed.
+      Channels.newChannel(output).write(Charsets.toCharset(charset).encode(data));
+    }
+  }
+
+  /**
+   * Gets the contents of an {@link InputStream} from a supplier as a String using the specified
+   * character encoding.
+   *
+   * <p>This method buffers the input internally, so there is no need to use a {@link
+   * BufferedInputStream}.
+   *
+   * @param input supplies the {@link InputStream} to read
+   * @param charset the charset to use, null means platform default
+   * @return the requested String
+   * @throws NullPointerException if the input is null
+   * @throws IOException if an I/O error occurs
+   * @since 2.12.0
+   */
+  public static String toString(final IOSupplier<InputStream> input, final Charset charset)
+      throws IOException {
+    return toString(
+        input,
+        charset,
+        () -> {
+          throw new NullPointerException("input");
+        });
+  }
+
+  /**
+   * Gets the contents of an {@link InputStream} from a supplier as a String using the specified
+   * character encoding.
+   *
+   * <p>This method buffers the input internally, so there is no need to use a {@link
+   * BufferedInputStream}.
+   *
+   * @param input supplies the {@link InputStream} to read
+   * @param charset the charset to use, null means platform default
+   * @param defaultString the default return value if the supplier or its value is null.
+   * @return the requested String
+   * @throws NullPointerException if the input is null
+   * @throws IOException if an I/O error occurs
+   * @since 2.12.0
+   */
+  public static String toString(
+      final IOSupplier<InputStream> input,
+      final Charset charset,
+      final IOSupplier<String> defaultString)
+      throws IOException {
+    if (input == null) {
+      return defaultString.get();
+    }
+    try (InputStream inputStream = input.get()) {
+      return inputStream != null ? toString(inputStream, charset) : defaultString.get();
+    }
+  }
+
+  /**
+   * Gets the contents of an {@link InputStream} as a String using the specified character encoding.
+   *
+   * <p>This method buffers the input internally, so there is no need to use a {@link
+   * BufferedInputStream}.
+   *
+   * @param input the {@link InputStream} to read
+   * @param charset the charset to use, null means platform default
+   * @return the requested String
+   * @throws NullPointerException if the input is null
+   * @throws IOException if an I/O error occurs
+   * @since 2.3
+   */
+  public static String toString(final InputStream input, final Charset charset) throws IOException {
+    try (StringBuilderWriter sw = new StringBuilderWriter()) {
+      copy(input, sw, charset);
+      return sw.toString();
+    }
+  }
+
+  /**
+   * Copies bytes from an {@link InputStream} to chars on a {@link Writer} using the specified
+   * character encoding.
+   *
+   * <p>This method buffers the input internally, so there is no need to use a {@link
+   * BufferedInputStream}.
+   *
+   * <p>This method uses {@link InputStreamReader}.
+   *
+   * @param input the {@link InputStream} to read
+   * @param writer the {@link Writer} to write to
+   * @param inputCharset the charset to use for the input stream, null means platform default
+   * @throws NullPointerException if the input or output is null
+   * @throws IOException if an I/O error occurs
+   * @since 2.3
+   */
+  public static void copy(final InputStream input, final Writer writer, final Charset inputCharset)
+      throws IOException {
+    final InputStreamReader reader = new InputStreamReader(input, Charsets.toCharset(inputCharset));
+    copy(reader, writer);
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/RandomAccessFileMode.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/RandomAccessFileMode.java
new file mode 100644
index 00000000..13a1b314
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/RandomAccessFileMode.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tsfile.external.commons.io;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.RandomAccessFile;
+import java.nio.file.Path;
+
+/**
+ * Access modes and factory methods for {@link RandomAccessFile}.
+ *
+ * @since 2.12.0
+ */
+public enum RandomAccessFileMode {
+
+  /** Mode {@code "r"} opens for reading only. */
+  READ_ONLY("r"),
+
+  /** Mode {@code "rw"} opens for reading and writing. */
+  READ_WRITE("rw"),
+
+  /**
+   * Mode {@code "rws"} opens for reading and writing, as with {@code "rw"}, and also require that
+   * every update to the file's content or metadata be written synchronously to the underlying
+   * storage device.
+   */
+  READ_WRITE_SYNC_ALL("rws"),
+
+  /**
+   * Mode {@code "rwd"} open for reading and writing, as with {@code "rw"}, and also require that
+   * every update to the file's content be written synchronously to the underlying storage device.
+   */
+  READ_WRITE_SYNC_CONTENT("rwd");
+
+  private final String mode;
+
+  RandomAccessFileMode(final String mode) {
+    this.mode = mode;
+  }
+
+  /**
+   * Constructs a random access file stream to read from, and optionally to write to, the file
+   * specified by the {@link File} argument.
+   *
+   * @param file the file object
+   * @return a random access file stream
+   * @throws FileNotFoundException See {@link RandomAccessFile#RandomAccessFile(File, String)}.
+   */
+  public RandomAccessFile create(final File file) throws FileNotFoundException {
+    return new RandomAccessFile(file, mode);
+  }
+
+  /**
+   * Constructs a random access file stream to read from, and optionally to write to, the file
+   * specified by the {@link File} argument.
+   *
+   * @param file the file object
+   * @return a random access file stream
+   * @throws FileNotFoundException See {@link RandomAccessFile#RandomAccessFile(File, String)}.
+   */
+  public RandomAccessFile create(final Path file) throws FileNotFoundException {
+    return create(file.toFile());
+  }
+
+  /**
+   * Constructs a random access file stream to read from, and optionally to write to, the file
+   * specified by the {@link File} argument.
+   *
+   * @param file the file object
+   * @return a random access file stream
+   * @throws FileNotFoundException See {@link RandomAccessFile#RandomAccessFile(File, String)}.
+   */
+  public RandomAccessFile create(final String file) throws FileNotFoundException {
+    return new RandomAccessFile(file, mode);
+  }
+
+  @Override
+  public String toString() {
+    return mode;
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/RandomAccessFiles.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/RandomAccessFiles.java
new file mode 100644
index 00000000..e69f79a1
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/RandomAccessFiles.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tsfile.external.commons.io;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+/**
+ * Works on RandomAccessFile.
+ *
+ * @since 2.13.0
+ */
+public class RandomAccessFiles {
+
+  /**
+   * Reads a byte array starting at "position" for "length" bytes.
+   *
+   * @param input The source RandomAccessFile.
+   * @param position The offset position, measured in bytes from the beginning of the file, at which
+   *     to set the file pointer.
+   * @param length How many bytes to read.
+   * @return a new byte array.
+   * @throws IOException If the first byte cannot be read for any reason other than end of file, or
+   *     if the random access file has been closed, or if some other I/O error occurs.
+   */
+  public static byte[] read(final RandomAccessFile input, final long position, final int length)
+      throws IOException {
+    input.seek(position);
+    return IOUtils.toByteArray(input::read, length);
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/build/AbstractOrigin.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/build/AbstractOrigin.java
new file mode 100644
index 00000000..5189dfa8
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/build/AbstractOrigin.java
@@ -0,0 +1,484 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tsfile.external.commons.io.build;
+
+import org.apache.tsfile.external.commons.io.IOUtils;
+import org.apache.tsfile.external.commons.io.input.ReaderInputStream;
+import org.apache.tsfile.external.commons.io.output.WriterOutputStream;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.net.URI;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Objects;
+
+/**
+ * Abstracts the origin of data for builders like a {@link File}, {@link Path}, {@link Reader},
+ * {@link Writer}, {@link InputStream}, {@link OutputStream}, and {@link URI}.
+ *
+ * <p>Some methods may throw {@link UnsupportedOperationException} if that method is not implemented
+ * in a concrete subclass, see {@link #getFile()} and {@link #getPath()}.
+ *
+ * @param <T> the type of instances to build.
+ * @param <B> the type of builder subclass.
+ * @since 2.12.0
+ */
+public abstract class AbstractOrigin<T, B extends AbstractOrigin<T, B>>
+    extends AbstractSupplier<T, B> {
+
+  /** A {@code byte[]} origin. */
+  public static class ByteArrayOrigin extends AbstractOrigin<byte[], ByteArrayOrigin> {
+
+    /**
+     * Constructs a new instance for the given origin.
+     *
+     * @param origin The origin.
+     */
+    public ByteArrayOrigin(final byte[] origin) {
+      super(origin);
+    }
+
+    @Override
+    public byte[] getByteArray() {
+      // No conversion
+      return get();
+    }
+
+    @Override
+    public InputStream getInputStream(final OpenOption... options) throws IOException {
+      return new ByteArrayInputStream(origin);
+    }
+
+    @Override
+    public Reader getReader(final Charset charset) throws IOException {
+      return new InputStreamReader(getInputStream(), charset);
+    }
+
+    @Override
+    public long size() throws IOException {
+      return origin.length;
+    }
+  }
+
+  /** A {@link CharSequence} origin. */
+  public static class CharSequenceOrigin extends AbstractOrigin<CharSequence, CharSequenceOrigin> {
+
+    /**
+     * Constructs a new instance for the given origin.
+     *
+     * @param origin The origin.
+     */
+    public CharSequenceOrigin(final CharSequence origin) {
+      super(origin);
+    }
+
+    @Override
+    public byte[] getByteArray() {
+      // TODO Pass in a Charset? Consider if call sites actually need this.
+      return origin.toString().getBytes(Charset.defaultCharset());
+    }
+
+    @Override
+    public CharSequence getCharSequence(final Charset charset) {
+      // No conversion
+      return get();
+    }
+
+    @Override
+    public InputStream getInputStream(final OpenOption... options) throws IOException {
+      // TODO Pass in a Charset? Consider if call sites actually need this.
+      return new ByteArrayInputStream(origin.toString().getBytes(Charset.defaultCharset()));
+      // Needs [IO-795] CharSequenceInputStream.reset() only works once.
+      // return
+      // CharSequenceInputStream.builder().setCharSequence(getCharSequence(Charset.defaultCharset())).get();
+    }
+
+    @Override
+    public Reader getReader(final Charset charset) throws IOException {
+      return new InputStreamReader(getInputStream(), charset);
+    }
+
+    @Override
+    public long size() throws IOException {
+      return origin.length();
+    }
+  }
+
+  /**
+   * A {@link File} origin.
+   *
+   * <p>Starting from this origin, you can get a byte array, a file, an input stream, an output
+   * stream, a path, a reader, and a writer.
+   */
+  public static class FileOrigin extends AbstractOrigin<File, FileOrigin> {
+
+    /**
+     * Constructs a new instance for the given origin.
+     *
+     * @param origin The origin.
+     */
+    public FileOrigin(final File origin) {
+      super(origin);
+    }
+
+    @Override
+    public File getFile() {
+      // No conversion
+      return get();
+    }
+
+    @Override
+    public Path getPath() {
+      return get().toPath();
+    }
+  }
+
+  /**
+   * An {@link InputStream} origin.
+   *
+   * <p>This origin cannot provide some of the other aspects.
+   */
+  public static class InputStreamOrigin extends AbstractOrigin<InputStream, InputStreamOrigin> {
+
+    /**
+     * Constructs a new instance for the given origin.
+     *
+     * @param origin The origin.
+     */
+    public InputStreamOrigin(final InputStream origin) {
+      super(origin);
+    }
+
+    @Override
+    public byte[] getByteArray() throws IOException {
+      return IOUtils.toByteArray(origin);
+    }
+
+    @Override
+    public InputStream getInputStream(final OpenOption... options) {
+      // No conversion
+      return get();
+    }
+
+    @Override
+    public Reader getReader(final Charset charset) throws IOException {
+      return new InputStreamReader(getInputStream(), charset);
+    }
+  }
+
+  /**
+   * An {@link OutputStream} origin.
+   *
+   * <p>This origin cannot provide some of the other aspects.
+   */
+  public static class OutputStreamOrigin extends AbstractOrigin<OutputStream, OutputStreamOrigin> {
+
+    /**
+     * Constructs a new instance for the given origin.
+     *
+     * @param origin The origin.
+     */
+    public OutputStreamOrigin(final OutputStream origin) {
+      super(origin);
+    }
+
+    @Override
+    public OutputStream getOutputStream(final OpenOption... options) {
+      // No conversion
+      return get();
+    }
+
+    @Override
+    public Writer getWriter(final Charset charset, final OpenOption... options) throws IOException {
+      return new OutputStreamWriter(origin, charset);
+    }
+  }
+
+  /**
+   * A {@link Path} origin.
+   *
+   * <p>Starting from this origin, you can get a byte array, a file, an input stream, an output
+   * stream, a path, a reader, and a writer.
+   */
+  public static class PathOrigin extends AbstractOrigin<Path, PathOrigin> {
+
+    /**
+     * Constructs a new instance for the given origin.
+     *
+     * @param origin The origin.
+     */
+    public PathOrigin(final Path origin) {
+      super(origin);
+    }
+
+    @Override
+    public File getFile() {
+      return get().toFile();
+    }
+
+    @Override
+    public Path getPath() {
+      // No conversion
+      return get();
+    }
+  }
+
+  /**
+   * An {@link Reader} origin.
+   *
+   * <p>This origin cannot provide other aspects.
+   */
+  public static class ReaderOrigin extends AbstractOrigin<Reader, ReaderOrigin> {
+
+    /**
+     * Constructs a new instance for the given origin.
+     *
+     * @param origin The origin.
+     */
+    public ReaderOrigin(final Reader origin) {
+      super(origin);
+    }
+
+    @Override
+    public byte[] getByteArray() throws IOException {
+      // TODO Pass in a Charset? Consider if call sites actually need this.
+      return IOUtils.toByteArray(origin, Charset.defaultCharset());
+    }
+
+    @Override
+    public CharSequence getCharSequence(final Charset charset) throws IOException {
+      return IOUtils.toString(origin);
+    }
+
+    @Override
+    public InputStream getInputStream(final OpenOption... options) throws IOException {
+      // TODO Pass in a Charset? Consider if call sites actually need this.
+      return ReaderInputStream.builder()
+          .setReader(origin)
+          .setCharset(Charset.defaultCharset())
+          .get();
+    }
+
+    @Override
+    public Reader getReader(final Charset charset) throws IOException {
+      // No conversion
+      return get();
+    }
+  }
+
+  /** A {@link URI} origin. */
+  public static class URIOrigin extends AbstractOrigin<URI, URIOrigin> {
+
+    /**
+     * Constructs a new instance for the given origin.
+     *
+     * @param origin The origin.
+     */
+    public URIOrigin(final URI origin) {
+      super(origin);
+    }
+
+    @Override
+    public File getFile() {
+      return getPath().toFile();
+    }
+
+    @Override
+    public Path getPath() {
+      return Paths.get(get());
+    }
+  }
+
+  /**
+   * An {@link Writer} origin.
+   *
+   * <p>This origin cannot provide other aspects.
+   */
+  public static class WriterOrigin extends AbstractOrigin<Writer, WriterOrigin> {
+
+    /**
+     * Constructs a new instance for the given origin.
+     *
+     * @param origin The origin.
+     */
+    public WriterOrigin(final Writer origin) {
+      super(origin);
+    }
+
+    @Override
+    public OutputStream getOutputStream(final OpenOption... options) throws IOException {
+      // TODO Pass in a Charset? Consider if call sites actually need this.
+      return WriterOutputStream.builder()
+          .setWriter(origin)
+          .setCharset(Charset.defaultCharset())
+          .get();
+    }
+
+    @Override
+    public Writer getWriter(final Charset charset, final OpenOption... options) throws IOException {
+      // No conversion
+      return get();
+    }
+  }
+
+  /** The non-null origin. */
+  final T origin;
+
+  /**
+   * Constructs a new instance for a subclass.
+   *
+   * @param origin The origin.
+   */
+  protected AbstractOrigin(final T origin) {
+    this.origin = Objects.requireNonNull(origin, "origin");
+  }
+
+  /**
+   * Gets the origin.
+   *
+   * @return the origin.
+   */
+  @Override
+  public T get() {
+    return origin;
+  }
+
+  /**
+   * Gets this origin as a byte array, if possible.
+   *
+   * @return this origin as a byte array, if possible.
+   * @throws IOException if an I/O error occurs.
+   * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
+   */
+  public byte[] getByteArray() throws IOException {
+    return Files.readAllBytes(getPath());
+  }
+
+  /**
+   * Gets this origin as a byte array, if possible.
+   *
+   * @param charset The charset to use if conversion from bytes is needed.
+   * @return this origin as a byte array, if possible.
+   * @throws IOException if an I/O error occurs.
+   * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
+   */
+  public CharSequence getCharSequence(final Charset charset) throws IOException {
+    return new String(getByteArray(), charset);
+  }
+
+  /**
+   * Gets this origin as a Path, if possible.
+   *
+   * @return this origin as a Path, if possible.
+   * @throws UnsupportedOperationException if this method is not implemented in a concrete subclass.
+   */
+  public File getFile() {
+    throw new UnsupportedOperationException(
+        String.format(
+            "%s#getFile() for %s origin %s",
+            getClass().getSimpleName(), origin.getClass().getSimpleName(), origin));
+  }
+
+  /**
+   * Gets this origin as an InputStream, if possible.
+   *
+   * @param options options specifying how the file is opened
+   * @return this origin as an InputStream, if possible.
+   * @throws IOException if an I/O error occurs.
+   * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
+   */
+  public InputStream getInputStream(final OpenOption... options) throws IOException {
+    return Files.newInputStream(getPath(), options);
+  }
+
+  /**
+   * Gets this origin as an OutputStream, if possible.
+   *
+   * @param options options specifying how the file is opened
+   * @return this origin as an OutputStream, if possible.
+   * @throws IOException if an I/O error occurs.
+   * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
+   */
+  public OutputStream getOutputStream(final OpenOption... options) throws IOException {
+    return Files.newOutputStream(getPath(), options);
+  }
+
+  /**
+   * Gets this origin as a Path, if possible.
+   *
+   * @return this origin as a Path, if possible.
+   * @throws UnsupportedOperationException if this method is not implemented in a concrete subclass.
+   */
+  public Path getPath() {
+    throw new UnsupportedOperationException(
+        String.format(
+            "%s#getPath() for %s origin %s",
+            getClass().getSimpleName(), origin.getClass().getSimpleName(), origin));
+  }
+
+  /**
+   * Gets a new Reader on the origin, buffered by default.
+   *
+   * @param charset the charset to use for decoding
+   * @return a new Reader on the origin.
+   * @throws IOException if an I/O error occurs opening the file.
+   */
+  public Reader getReader(final Charset charset) throws IOException {
+    return Files.newBufferedReader(getPath(), charset);
+  }
+
+  /**
+   * Gets a new Writer on the origin, buffered by default.
+   *
+   * @param charset the charset to use for encoding
+   * @param options options specifying how the file is opened
+   * @return a new Writer on the origin.
+   * @throws IOException if an I/O error occurs opening or creating the file.
+   * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
+   */
+  public Writer getWriter(final Charset charset, final OpenOption... options) throws IOException {
+    return Files.newBufferedWriter(getPath(), charset, options);
+  }
+
+  /**
+   * Gets the size of the origin, if possible.
+   *
+   * @return the size of the origin in bytes or characters.
+   * @throws IOException if an I/O error occurs.
+   * @since 2.13.0
+   */
+  public long size() throws IOException {
+    return Files.size(getPath());
+  }
+
+  @Override
+  public String toString() {
+    return getClass().getSimpleName() + "[" + origin.toString() + "]";
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/build/AbstractOriginSupplier.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/build/AbstractOriginSupplier.java
new file mode 100644
index 00000000..60ff5569
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/build/AbstractOriginSupplier.java
@@ -0,0 +1,276 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tsfile.external.commons.io.build;
+
+import org.apache.tsfile.external.commons.io.build.AbstractOrigin.ByteArrayOrigin;
+import org.apache.tsfile.external.commons.io.build.AbstractOrigin.CharSequenceOrigin;
+import org.apache.tsfile.external.commons.io.build.AbstractOrigin.FileOrigin;
+import org.apache.tsfile.external.commons.io.build.AbstractOrigin.InputStreamOrigin;
+import org.apache.tsfile.external.commons.io.build.AbstractOrigin.OutputStreamOrigin;
+import org.apache.tsfile.external.commons.io.build.AbstractOrigin.PathOrigin;
+import org.apache.tsfile.external.commons.io.build.AbstractOrigin.ReaderOrigin;
+import org.apache.tsfile.external.commons.io.build.AbstractOrigin.URIOrigin;
+import org.apache.tsfile.external.commons.io.build.AbstractOrigin.WriterOrigin;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.net.URI;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * Abstracts building an instance of {@code T}.
+ *
+ * @param <T> the type of instances to build.
+ * @param <B> the type of builder subclass.
+ * @since 2.12.0
+ */
+public abstract class AbstractOriginSupplier<T, B extends AbstractOriginSupplier<T, B>>
+    extends AbstractSupplier<T, B> {
+
+  /**
+   * Constructs a new byte array origin for a byte array.
+   *
+   * @param origin the byte array.
+   * @return a new byte array origin.
+   */
+  protected static ByteArrayOrigin newByteArrayOrigin(final byte[] origin) {
+    return new ByteArrayOrigin(origin);
+  }
+
+  /**
+   * Constructs a new CharSequence origin for a CharSequence.
+   *
+   * @param origin the CharSequence.
+   * @return a new file origin.
+   * @since 2.13.0
+   */
+  protected static CharSequenceOrigin newCharSequenceOrigin(final CharSequence origin) {
+    return new CharSequenceOrigin(origin);
+  }
+
+  /**
+   * Constructs a new file origin for a file.
+   *
+   * @param origin the file.
+   * @return a new file origin.
+   */
+  protected static FileOrigin newFileOrigin(final File origin) {
+    return new FileOrigin(origin);
+  }
+
+  /**
+   * Constructs a new file origin for a file path.
+   *
+   * @param origin the file path.
+   * @return a new file origin.
+   */
+  protected static FileOrigin newFileOrigin(final String origin) {
+    return new FileOrigin(new File(origin));
+  }
+
+  /**
+   * Constructs a new input stream origin for a file.
+   *
+   * @param origin the input stream.
+   * @return a new input stream origin.
+   */
+  protected static InputStreamOrigin newInputStreamOrigin(final InputStream origin) {
+    return new InputStreamOrigin(origin);
+  }
+
+  /**
+   * Constructs a new output stream origin for a file.
+   *
+   * @param origin the output stream.
+   * @return a new output stream origin.
+   */
+  protected static OutputStreamOrigin newOutputStreamOrigin(final OutputStream origin) {
+    return new OutputStreamOrigin(origin);
+  }
+
+  /**
+   * Constructs a new path origin for a file.
+   *
+   * @param origin the path.
+   * @return a new path origin.
+   */
+  protected static PathOrigin newPathOrigin(final Path origin) {
+    return new PathOrigin(origin);
+  }
+
+  /**
+   * Constructs a new path name origin for a path name.
+   *
+   * @param origin the path name.
+   * @return a new path name origin.
+   */
+  protected static PathOrigin newPathOrigin(final String origin) {
+    return new PathOrigin(Paths.get(origin));
+  }
+
+  /**
+   * Constructs a new reader origin for a reader.
+   *
+   * @param origin the reader.
+   * @return a new reader origin.
+   */
+  protected static ReaderOrigin newReaderOrigin(final Reader origin) {
+    return new ReaderOrigin(origin);
+  }
+
+  /**
+   * Constructs a new reader origin for a URI.
+   *
+   * @param origin the URI.
+   * @return a new URI origin.
+   */
+  protected static URIOrigin newURIOrigin(final URI origin) {
+    return new URIOrigin(origin);
+  }
+
+  /**
+   * Constructs a new writer origin for a file.
+   *
+   * @param origin the writer.
+   * @return a new writer .
+   */
+  protected static WriterOrigin newWriterOrigin(final Writer origin) {
+    return new WriterOrigin(origin);
+  }
+
+  /** The underlying origin. */
+  private AbstractOrigin<?, ?> origin;
+
+  /**
+   * Checks whether the origin is null.
+   *
+   * @return the origin.
+   * @throws IllegalStateException if the {@code origin} is {@code null}.
+   */
+  protected AbstractOrigin<?, ?> checkOrigin() {
+    if (origin == null) {
+      throw new IllegalStateException("origin == null");
+    }
+    return origin;
+  }
+
+  /**
+   * Sets a new origin.
+   *
+   * @param origin the new origin.
+   * @return this
+   */
+  public B setByteArray(final byte[] origin) {
+    return setOrigin(newByteArrayOrigin(origin));
+  }
+
+  /**
+   * Sets a new origin.
+   *
+   * @param origin the new origin.
+   * @return this
+   */
+  public B setFile(final File origin) {
+    return setOrigin(newFileOrigin(origin));
+  }
+
+  /**
+   * Sets a new origin.
+   *
+   * @param origin the new origin.
+   * @return this
+   */
+  public B setFile(final String origin) {
+    return setOrigin(newFileOrigin(origin));
+  }
+
+  /**
+   * Sets a new origin.
+   *
+   * @param origin the new origin.
+   * @return this
+   */
+  public B setInputStream(final InputStream origin) {
+    return setOrigin(newInputStreamOrigin(origin));
+  }
+
+  /**
+   * Sets a new origin.
+   *
+   * @param origin the new origin.
+   * @return this
+   */
+  protected B setOrigin(final AbstractOrigin<?, ?> origin) {
+    this.origin = origin;
+    return asThis();
+  }
+
+  /**
+   * Sets a new origin.
+   *
+   * @param origin the new origin.
+   * @return this
+   */
+  public B setOutputStream(final OutputStream origin) {
+    return setOrigin(newOutputStreamOrigin(origin));
+  }
+
+  /**
+   * Sets a new origin.
+   *
+   * @param origin the new origin.
+   * @return this
+   */
+  public B setPath(final Path origin) {
+    return setOrigin(newPathOrigin(origin));
+  }
+
+  /**
+   * Sets a new origin.
+   *
+   * @param origin the new origin.
+   * @return this
+   */
+  public B setPath(final String origin) {
+    return setOrigin(newPathOrigin(origin));
+  }
+
+  /**
+   * Sets a new origin.
+   *
+   * @param origin the new origin.
+   * @return this
+   */
+  public B setReader(final Reader origin) {
+    return setOrigin(newReaderOrigin(origin));
+  }
+
+  /**
+   * Sets a new origin.
+   *
+   * @param origin the new origin.
+   * @return this
+   */
+  public B setWriter(final Writer origin) {
+    return setOrigin(newWriterOrigin(origin));
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/build/AbstractStreamBuilder.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/build/AbstractStreamBuilder.java
new file mode 100644
index 00000000..cd5d7513
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/build/AbstractStreamBuilder.java
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tsfile.external.commons.io.build;
+
+import org.apache.tsfile.external.commons.io.Charsets;
+import org.apache.tsfile.external.commons.io.IOUtils;
+import org.apache.tsfile.external.commons.io.file.PathUtils;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.util.function.IntUnaryOperator;
+
+/**
+ * Abstracts building a typed instance of {@code T}.
+ *
+ * @param <T> the type of instances to build.
+ * @param <B> the type of builder subclass.
+ * @since 2.12.0
+ */
+public abstract class AbstractStreamBuilder<T, B extends AbstractStreamBuilder<T, B>>
+    extends AbstractOriginSupplier<T, B> {
+
+  private static final int DEFAULT_MAX_VALUE = Integer.MAX_VALUE;
+
+  private static final OpenOption[] DEFAULT_OPEN_OPTIONS = PathUtils.EMPTY_OPEN_OPTION_ARRAY;
+
+  /**
+   * The buffer size, defaults to {@link IOUtils#DEFAULT_BUFFER_SIZE} ({@value
+   * IOUtils#DEFAULT_BUFFER_SIZE}).
+   */
+  private int bufferSize = IOUtils.DEFAULT_BUFFER_SIZE;
+
+  /**
+   * The buffer size, defaults to {@link IOUtils#DEFAULT_BUFFER_SIZE} ({@value
+   * IOUtils#DEFAULT_BUFFER_SIZE}).
+   */
+  private int bufferSizeDefault = IOUtils.DEFAULT_BUFFER_SIZE;
+
+  /** The maximum buffer size. */
+  private int bufferSizeMax = DEFAULT_MAX_VALUE;
+
+  /** The Charset, defaults to {@link Charset#defaultCharset()}. */
+  private Charset charset = Charset.defaultCharset();
+
+  /** The Charset, defaults to {@link Charset#defaultCharset()}. */
+  private Charset charsetDefault = Charset.defaultCharset();
+
+  private OpenOption[] openOptions = DEFAULT_OPEN_OPTIONS;
+
+  /**
+   * The default checking behavior for a buffer size request. Throws a {@link
+   * IllegalArgumentException} by default.
+   */
+  private final IntUnaryOperator defaultSizeChecker =
+      size -> size > bufferSizeMax ? throwIae(size, bufferSizeMax) : size;
+
+  /** The checking behavior for a buffer size request. */
+  private IntUnaryOperator bufferSizeChecker = defaultSizeChecker;
+
+  /**
+   * Applies the buffer size request.
+   *
+   * @param size the size request.
+   * @return the size to use, usually the input, or can throw an unchecked exception, like {@link
+   *     IllegalArgumentException}.
+   */
+  private int checkBufferSize(final int size) {
+    return bufferSizeChecker.applyAsInt(size);
+  }
+
+  /**
+   * Gets the buffer size, defaults to {@link IOUtils#DEFAULT_BUFFER_SIZE} ({@value
+   * IOUtils#DEFAULT_BUFFER_SIZE}).
+   *
+   * @return the buffer size, defaults to {@link IOUtils#DEFAULT_BUFFER_SIZE} ({@value
+   *     IOUtils#DEFAULT_BUFFER_SIZE}).
+   */
+  protected int getBufferSize() {
+    return bufferSize;
+  }
+
+  /**
+   * Gets the Charset, defaults to {@link Charset#defaultCharset()}.
+   *
+   * @return the Charset, defaults to {@link Charset#defaultCharset()}.
+   */
+  public Charset getCharset() {
+    return charset;
+  }
+
+  /**
+   * Gets the Charset default, defaults to {@link Charset#defaultCharset()}.
+   *
+   * @return the Charset default, defaults to {@link Charset#defaultCharset()}.
+   */
+  protected Charset getCharsetDefault() {
+    return charsetDefault;
+  }
+
+  protected OpenOption[] getOpenOptions() {
+    return openOptions;
+  }
+
+  /**
+   * Gets a Path from the origin.
+   *
+   * @return A Path
+   * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
+   * @throws IllegalStateException if the {@code origin} is {@code null}.
+   * @see AbstractOrigin#getPath()
+   * @since 2.13.0
+   */
+  protected Path getPath() {
+    return checkOrigin().getPath();
+  }
+
+  /**
+   * Gets an writer from the origin with open options.
+   *
+   * @return An writer.
+   * @throws IOException if an I/O error occurs.
+   * @throws UnsupportedOperationException if the origin cannot be converted to a Writer.
+   * @throws IllegalStateException if the {@code origin} is {@code null}.
+   * @see AbstractOrigin#getOutputStream(OpenOption...)
+   * @since 2.13.0
+   */
+  protected Writer getWriter() throws IOException {
+    return checkOrigin().getWriter(getCharset(), getOpenOptions());
+  }
+
+  /**
+   * Sets the Charset.
+   *
+   * <p>Subclasses may ignore this setting.
+   *
+   * @param charset the Charset, null resets to the default.
+   * @return this.
+   */
+  public B setCharset(final Charset charset) {
+    this.charset = Charsets.toCharset(charset, charsetDefault);
+    return asThis();
+  }
+
+  /**
+   * Sets the Charset.
+   *
+   * <p>Subclasses may ignore this setting.
+   *
+   * @param charset the Charset name, null resets to the default.
+   * @return this.
+   */
+  public B setCharset(final String charset) {
+    return setCharset(Charsets.toCharset(charset, charsetDefault));
+  }
+
+  private int throwIae(final int size, final int max) {
+    throw new IllegalArgumentException(String.format("Request %,d exceeds maximum %,d", size, max));
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/build/AbstractSupplier.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/build/AbstractSupplier.java
new file mode 100644
index 00000000..fdb42d6a
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/build/AbstractSupplier.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tsfile.external.commons.io.build;
+
+import org.apache.tsfile.external.commons.io.function.IOSupplier;
+
+/**
+ * Abstracts supplying an instance of {@code T}. Use to implement the builder pattern.
+ *
+ * @param <T> the type of instances to build.
+ * @param <B> the type of builder subclass.
+ * @since 2.12.0
+ */
+public abstract class AbstractSupplier<T, B extends AbstractSupplier<T, B>>
+    implements IOSupplier<T> {
+
+  /**
+   * Returns this instance typed as the proper subclass type.
+   *
+   * @return this instance typed as the proper subclass type.
+   */
+  @SuppressWarnings("unchecked")
+  protected B asThis() {
+    return (B) this;
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/charset/CharsetDecoders.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/charset/CharsetDecoders.java
new file mode 100644
index 00000000..b7c356c5
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/charset/CharsetDecoders.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tsfile.external.commons.io.charset;
+
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+
+/**
+ * Works with {@link CharsetDecoder}.
+ *
+ * @since 2.12.0
+ */
+public final class CharsetDecoders {
+
+  /**
+   * Returns the given non-null CharsetDecoder or a new default CharsetDecoder.
+   *
+   * @param charsetDecoder The CharsetDecoder to test.
+   * @return the given non-null CharsetDecoder or a new default CharsetDecoder.
+   */
+  public static CharsetDecoder toCharsetDecoder(final CharsetDecoder charsetDecoder) {
+    return charsetDecoder != null ? charsetDecoder : Charset.defaultCharset().newDecoder();
+  }
+
+  /** No instances. */
+  private CharsetDecoders() {
+    // No instances.
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/charset/CharsetEncoders.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/charset/CharsetEncoders.java
new file mode 100644
index 00000000..902ba284
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/charset/CharsetEncoders.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tsfile.external.commons.io.charset;
+
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.util.function.Supplier;
+
+/**
+ * Works with {@link CharsetEncoder}.
+ *
+ * @since 2.12.0
+ */
+public final class CharsetEncoders {
+
+  /**
+   * Returns the given non-null CharsetEncoder or a new default CharsetEncoder.
+   *
+   * @param charsetEncoder The CharsetEncoder to test.
+   * @return the given non-null CharsetEncoder or a new default CharsetEncoder.
+   */
+  public static CharsetEncoder toCharsetEncoder(final CharsetEncoder charsetEncoder) {
+    return toCharsetEncoder(charsetEncoder, () -> Charset.defaultCharset().newEncoder());
+  }
+
+  /**
+   * Returns the given non-null CharsetEncoder or a new default CharsetEncoder.
+   *
+   * @param charsetEncoder The CharsetEncoder to test.
+   * @param defaultSupplier The CharsetEncoder supplier to get when charsetEncoder is null.
+   * @return the given non-null CharsetEncoder or a new default CharsetEncoder.
+   * @since 2.13.0
+   */
+  public static CharsetEncoder toCharsetEncoder(
+      final CharsetEncoder charsetEncoder, final Supplier<CharsetEncoder> defaultSupplier) {
+    return charsetEncoder != null ? charsetEncoder : defaultSupplier.get();
+  }
+
+  /** No instances. */
+  private CharsetEncoders() {
+    // No instances.
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/file/Counters.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/file/Counters.java
new file mode 100644
index 00000000..3c2fcf60
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/file/Counters.java
@@ -0,0 +1,419 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tsfile.external.commons.io.file;
+
+import java.math.BigInteger;
+import java.util.Objects;
+
+/**
+ * Provides counters for files, directories, and sizes, as a visit proceeds.
+ *
+ * @since 2.7
+ */
+public class Counters {
+
+  /** Counts files, directories, and sizes, as a visit proceeds. */
+  private static class AbstractPathCounters implements PathCounters {
+
+    private final Counter byteCounter;
+    private final Counter directoryCounter;
+    private final Counter fileCounter;
+
+    /**
+     * Constructs a new instance.
+     *
+     * @param byteCounter the byte counter.
+     * @param directoryCounter the directory counter.
+     * @param fileCounter the file counter.
+     */
+    protected AbstractPathCounters(
+        final Counter byteCounter, final Counter directoryCounter, final Counter fileCounter) {
+      this.byteCounter = byteCounter;
+      this.directoryCounter = directoryCounter;
+      this.fileCounter = fileCounter;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+      if (this == obj) {
+        return true;
+      }
+      if (!(obj instanceof AbstractPathCounters)) {
+        return false;
+      }
+      final AbstractPathCounters other = (AbstractPathCounters) obj;
+      return Objects.equals(byteCounter, other.byteCounter)
+          && Objects.equals(directoryCounter, other.directoryCounter)
+          && Objects.equals(fileCounter, other.fileCounter);
+    }
+
+    @Override
+    public Counter getByteCounter() {
+      return byteCounter;
+    }
+
+    @Override
+    public Counter getDirectoryCounter() {
+      return directoryCounter;
+    }
+
+    /**
+     * Gets the count of visited files.
+     *
+     * @return the byte count of visited files.
+     */
+    @Override
+    public Counter getFileCounter() {
+      return this.fileCounter;
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(byteCounter, directoryCounter, fileCounter);
+    }
+
+    @Override
+    public void reset() {
+      byteCounter.reset();
+      directoryCounter.reset();
+      fileCounter.reset();
+    }
+
+    @Override
+    public String toString() {
+      return String.format(
+          "%,d files, %,d directories, %,d bytes",
+          Long.valueOf(fileCounter.get()),
+          Long.valueOf(directoryCounter.get()),
+          Long.valueOf(byteCounter.get()));
+    }
+  }
+
+  /** Counts using a {@link BigInteger} number. */
+  private static final class BigIntegerCounter implements Counter {
+
+    private BigInteger value = BigInteger.ZERO;
+
+    @Override
+    public void add(final long val) {
+      value = value.add(BigInteger.valueOf(val));
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+      if (this == obj) {
+        return true;
+      }
+      if (!(obj instanceof Counter)) {
+        return false;
+      }
+      final Counter other = (Counter) obj;
+      return Objects.equals(value, other.getBigInteger());
+    }
+
+    @Override
+    public long get() {
+      return value.longValueExact();
+    }
+
+    @Override
+    public BigInteger getBigInteger() {
+      return value;
+    }
+
+    @Override
+    public Long getLong() {
+      return Long.valueOf(value.longValueExact());
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(value);
+    }
+
+    @Override
+    public void increment() {
+      value = value.add(BigInteger.ONE);
+    }
+
+    @Override
+    public void reset() {
+      value = BigInteger.ZERO;
+    }
+
+    @Override
+    public String toString() {
+      return value.toString();
+    }
+  }
+
+  /** Counts files, directories, and sizes, as a visit proceeds, using BigInteger numbers. */
+  private static final class BigIntegerPathCounters extends AbstractPathCounters {
+
+    /** Constructs a new initialized instance. */
+    protected BigIntegerPathCounters() {
+      super(bigIntegerCounter(), bigIntegerCounter(), bigIntegerCounter());
+    }
+  }
+
+  /** Counts using a number. */
+  public interface Counter {
+
+    /**
+     * Adds the given number to this counter.
+     *
+     * @param val the value to add.
+     */
+    void add(long val);
+
+    /**
+     * Gets the counter as a long.
+     *
+     * @return the counter as a long.
+     */
+    long get();
+
+    /**
+     * Gets the counter as a BigInteger.
+     *
+     * @return the counter as a BigInteger.
+     */
+    BigInteger getBigInteger();
+
+    /**
+     * Gets the counter as a Long.
+     *
+     * @return the counter as a Long.
+     */
+    Long getLong();
+
+    /** Adds one to this counter. */
+    void increment();
+
+    /** Resets this count to 0. */
+    default void reset() {
+      // binary compat, do nothing
+    }
+  }
+
+  /** Counts using a {@code long} number. */
+  private static final class LongCounter implements Counter {
+
+    private long value;
+
+    @Override
+    public void add(final long add) {
+      value += add;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+      if (this == obj) {
+        return true;
+      }
+      if (!(obj instanceof Counter)) {
+        return false;
+      }
+      final Counter other = (Counter) obj;
+      return value == other.get();
+    }
+
+    @Override
+    public long get() {
+      return value;
+    }
+
+    @Override
+    public BigInteger getBigInteger() {
+      return BigInteger.valueOf(value);
+    }
+
+    @Override
+    public Long getLong() {
+      return Long.valueOf(value);
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(value);
+    }
+
+    @Override
+    public void increment() {
+      value++;
+    }
+
+    @Override
+    public void reset() {
+      value = 0L;
+    }
+
+    @Override
+    public String toString() {
+      return Long.toString(value);
+    }
+  }
+
+  /** Counts files, directories, and sizes, as a visit proceeds, using long numbers. */
+  private static final class LongPathCounters extends AbstractPathCounters {
+
+    /** Constructs a new initialized instance. */
+    protected LongPathCounters() {
+      super(longCounter(), longCounter(), longCounter());
+    }
+  }
+
+  /** Counts nothing. */
+  private static final class NoopCounter implements Counter {
+
+    static final NoopCounter INSTANCE = new NoopCounter();
+
+    @Override
+    public void add(final long add) {
+      // noop
+    }
+
+    @Override
+    public long get() {
+      return 0;
+    }
+
+    @Override
+    public BigInteger getBigInteger() {
+      return BigInteger.ZERO;
+    }
+
+    @Override
+    public Long getLong() {
+      return 0L;
+    }
+
+    @Override
+    public void increment() {
+      // noop
+    }
+
+    /**
+     * Returns {@code "0"}, always.
+     *
+     * @return {@code "0"}, always.
+     * @since 2.12.0
+     */
+    @Override
+    public String toString() {
+      return "0";
+    }
+  }
+
+  /** Counts nothing. */
+  private static final class NoopPathCounters extends AbstractPathCounters {
+
+    static final NoopPathCounters INSTANCE = new NoopPathCounters();
+
+    /** Constructs a new initialized instance. */
+    private NoopPathCounters() {
+      super(noopCounter(), noopCounter(), noopCounter());
+    }
+  }
+
+  /** Counts files, directories, and sizes, as a visit proceeds. */
+  public interface PathCounters {
+
+    /**
+     * Gets the byte counter.
+     *
+     * @return the byte counter.
+     */
+    Counter getByteCounter();
+
+    /**
+     * Gets the directory counter.
+     *
+     * @return the directory counter.
+     */
+    Counter getDirectoryCounter();
+
+    /**
+     * Gets the file counter.
+     *
+     * @return the file counter.
+     */
+    Counter getFileCounter();
+
+    /** Resets the counts to 0. */
+    default void reset() {
+      // binary compat, do nothing
+    }
+  }
+
+  /**
+   * Returns a new BigInteger Counter.
+   *
+   * @return a new BigInteger Counter.
+   */
+  public static Counter bigIntegerCounter() {
+    return new BigIntegerCounter();
+  }
+
+  /**
+   * Returns a new BigInteger PathCounters.
+   *
+   * @return a new BigInteger PathCounters.
+   */
+  public static PathCounters bigIntegerPathCounters() {
+    return new BigIntegerPathCounters();
+  }
+
+  /**
+   * Returns a new long Counter.
+   *
+   * @return a new long Counter.
+   */
+  public static Counter longCounter() {
+    return new LongCounter();
+  }
+
+  /**
+   * Returns a new BigInteger PathCounters.
+   *
+   * @return a new BigInteger PathCounters.
+   */
+  public static PathCounters longPathCounters() {
+    return new LongPathCounters();
+  }
+
+  /**
+   * Returns the no-op Counter.
+   *
+   * @return the no-op Counter.
+   * @since 2.9.0
+   */
+  public static Counter noopCounter() {
+    return NoopCounter.INSTANCE;
+  }
+
+  /**
+   * Returns the no-op PathCounters.
+   *
+   * @return the no-op PathCounters.
+   * @since 2.9.0
+   */
+  public static PathCounters noopPathCounters() {
+    return NoopPathCounters.INSTANCE;
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/file/CountingPathVisitor.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/file/CountingPathVisitor.java
new file mode 100644
index 00000000..2caba293
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/file/CountingPathVisitor.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tsfile.external.commons.io.file;
+
+import org.apache.tsfile.external.commons.io.file.Counters.PathCounters;
+import org.apache.tsfile.external.commons.io.filefilter.IOFileFilter;
+import org.apache.tsfile.external.commons.io.filefilter.SymbolicLinkFileFilter;
+import org.apache.tsfile.external.commons.io.filefilter.TrueFileFilter;
+
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Objects;
+
+/**
+ * Counts files, directories, and sizes, as a visit proceeds.
+ *
+ * @since 2.7
+ */
+public class CountingPathVisitor extends SimplePathVisitor {
+
+  static final String[] EMPTY_STRING_ARRAY = {};
+
+  static IOFileFilter defaultDirFilter() {
+    return TrueFileFilter.INSTANCE;
+  }
+
+  static IOFileFilter defaultFileFilter() {
+    return new SymbolicLinkFileFilter(FileVisitResult.TERMINATE, FileVisitResult.CONTINUE);
+  }
+
+  private final PathCounters pathCounters;
+  private final PathFilter fileFilter;
+  private final PathFilter dirFilter;
+
+  /**
+   * Constructs a new instance.
+   *
+   * @param pathCounter How to count path visits.
+   */
+  public CountingPathVisitor(final PathCounters pathCounter) {
+    this(pathCounter, defaultFileFilter(), defaultDirFilter());
+  }
+
+  /**
+   * Constructs a new instance.
+   *
+   * @param pathCounter How to count path visits.
+   * @param fileFilter Filters which files to count.
+   * @param dirFilter Filters which directories to count.
+   * @since 2.9.0
+   */
+  public CountingPathVisitor(
+      final PathCounters pathCounter, final PathFilter fileFilter, final PathFilter dirFilter) {
+    this.pathCounters = Objects.requireNonNull(pathCounter, "pathCounter");
+    this.fileFilter = Objects.requireNonNull(fileFilter, "fileFilter");
+    this.dirFilter = Objects.requireNonNull(dirFilter, "dirFilter");
+  }
+
+  @Override
+  public boolean equals(final Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if (!(obj instanceof CountingPathVisitor)) {
+      return false;
+    }
+    final CountingPathVisitor other = (CountingPathVisitor) obj;
+    return Objects.equals(pathCounters, other.pathCounters);
+  }
+
+  /**
+   * Gets the visitation counts.
+   *
+   * @return the visitation counts.
+   */
+  public PathCounters getPathCounters() {
+    return pathCounters;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(pathCounters);
+  }
+
+  @Override
+  public FileVisitResult postVisitDirectory(final Path dir, final IOException exc)
+      throws IOException {
+    updateDirCounter(dir, exc);
+    return FileVisitResult.CONTINUE;
+  }
+
+  @Override
+  public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attributes)
+      throws IOException {
+    final FileVisitResult accept = dirFilter.accept(dir, attributes);
+    return accept != FileVisitResult.CONTINUE
+        ? FileVisitResult.SKIP_SUBTREE
+        : FileVisitResult.CONTINUE;
+  }
+
+  @Override
+  public String toString() {
+    return pathCounters.toString();
+  }
+
+  /**
+   * Updates the counter for visiting the given directory.
+   *
+   * @param dir the visited directory.
+   * @param exc Encountered exception.
+   * @since 2.9.0
+   */
+  protected void updateDirCounter(final Path dir, final IOException exc) {
+    pathCounters.getDirectoryCounter().increment();
+  }
+
+  /**
+   * Updates the counters for visiting the given file.
+   *
+   * @param file the visited file.
+   * @param attributes the visited file attributes.
+   */
+  protected void updateFileCounters(final Path file, final BasicFileAttributes attributes) {
+    pathCounters.getFileCounter().increment();
+    pathCounters.getByteCounter().add(attributes.size());
+  }
+
+  @Override
+  public FileVisitResult visitFile(final Path file, final BasicFileAttributes attributes)
+      throws IOException {
+    // Note: A file can be a symbolic link to a directory.
+    if (Files.exists(file) && fileFilter.accept(file, attributes) == FileVisitResult.CONTINUE) {
+      updateFileCounters(file, attributes);
+    }
+    return FileVisitResult.CONTINUE;
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/file/DeleteOption.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/file/DeleteOption.java
new file mode 100644
index 00000000..ce0aee7f
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/file/DeleteOption.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tsfile.external.commons.io.file;
+
+/**
+ * An object that configures how to delete a file.
+ *
+ * <p>The {@link StandardDeleteOption} enumeration type defines our standard options.
+ *
+ * @see StandardDeleteOption
+ * @since 2.8.0
+ */
+public interface DeleteOption {
+  // empty
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/file/DeletingPathVisitor.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/file/DeletingPathVisitor.java
new file mode 100644
index 00000000..dcf56933
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/file/DeletingPathVisitor.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tsfile.external.commons.io.file;
+
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Deletes files and directories as a visit proceeds.
+ *
+ * @since 2.7
+ */
+public class DeletingPathVisitor extends CountingPathVisitor {
+
+  private final String[] skip;
+  private final boolean overrideReadOnly;
+  private final LinkOption[] linkOptions;
+
+  /**
+   * Constructs a new visitor that deletes files except for the files and directories explicitly
+   * given.
+   *
+   * @param pathCounter How to count visits.
+   * @param deleteOption How deletion is handled.
+   * @param skip The files to skip deleting.
+   * @since 2.8.0
+   */
+  public DeletingPathVisitor(
+      final Counters.PathCounters pathCounter,
+      final DeleteOption[] deleteOption,
+      final String... skip) {
+    this(pathCounter, PathUtils.noFollowLinkOptionArray(), deleteOption, skip);
+  }
+
+  /**
+   * Constructs a new visitor that deletes files except for the files and directories explicitly
+   * given.
+   *
+   * @param pathCounter How to count visits.
+   * @param linkOptions How symbolic links are handled.
+   * @param deleteOption How deletion is handled.
+   * @param skip The files to skip deleting.
+   * @since 2.9.0
+   */
+  public DeletingPathVisitor(
+      final Counters.PathCounters pathCounter,
+      final LinkOption[] linkOptions,
+      final DeleteOption[] deleteOption,
+      final String... skip) {
+    super(pathCounter);
+    final String[] temp = skip != null ? skip.clone() : EMPTY_STRING_ARRAY;
+    Arrays.sort(temp);
+    this.skip = temp;
+    this.overrideReadOnly = StandardDeleteOption.overrideReadOnly(deleteOption);
+    // TODO Files.deleteIfExists() never follows links, so use LinkOption.NOFOLLOW_LINKS in other
+    // calls to Files.
+    this.linkOptions =
+        linkOptions == null ? PathUtils.noFollowLinkOptionArray() : linkOptions.clone();
+  }
+
+  /**
+   * Returns true to process the given path, false if not.
+   *
+   * @param path the path to test.
+   * @return true to process the given path, false if not.
+   */
+  private boolean accept(final Path path) {
+    return Arrays.binarySearch(skip, PathUtils.getFileNameString(path)) < 0;
+  }
+
+  @Override
+  public boolean equals(final Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if (!super.equals(obj)) {
+      return false;
+    }
+    if (getClass() != obj.getClass()) {
+      return false;
+    }
+    final DeletingPathVisitor other = (DeletingPathVisitor) obj;
+    return overrideReadOnly == other.overrideReadOnly && Arrays.equals(skip, other.skip);
+  }
+
+  @Override
+  public int hashCode() {
+    final int prime = 31;
+    int result = super.hashCode();
+    result = prime * result + Arrays.hashCode(skip);
+    result = prime * result + Objects.hash(overrideReadOnly);
+    return result;
+  }
+
+  @Override
+  public FileVisitResult postVisitDirectory(final Path dir, final IOException exc)
+      throws IOException {
+    if (PathUtils.isEmptyDirectory(dir)) {
+      Files.deleteIfExists(dir);
+    }
+    return super.postVisitDirectory(dir, exc);
+  }
+
+  @Override
+  public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs)
+      throws IOException {
+    super.preVisitDirectory(dir, attrs);
+    return accept(dir) ? FileVisitResult.CONTINUE : FileVisitResult.SKIP_SUBTREE;
+  }
+
+  @Override
+  public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs)
+      throws IOException {
+    if (accept(file)) {
+      // delete files and valid links, respecting linkOptions
+      if (Files.exists(file, linkOptions)) {
+        if (overrideReadOnly) {
+          PathUtils.setReadOnly(file, false, linkOptions);
+        }
+        Files.deleteIfExists(file);
+      }
+      // invalid links will survive previous delete, different approach needed:
+      if (Files.isSymbolicLink(file)) {
+        try {
+          // deleteIfExists does not work for this case
+          Files.delete(file);
+        } catch (final NoSuchFileException ignored) {
+          // ignore
+        }
+      }
+    }
+    updateFileCounters(file, attrs);
+    return FileVisitResult.CONTINUE;
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/file/PathFilter.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/file/PathFilter.java
new file mode 100644
index 00000000..5123b846
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/file/PathFilter.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tsfile.external.commons.io.file;
+
+import java.nio.file.FileVisitResult;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+
+/**
+ * A filter for {@link Path}s.
+ *
+ * @since 2.9.0
+ */
+@FunctionalInterface
+public interface PathFilter {
+
+  /**
+   * Tests whether or not to include the specified Path in a result.
+   *
+   * @param path The Path to test.
+   * @param attributes the file's basic attributes (TODO may be null).
+   * @return a FileVisitResult
+   */
+  FileVisitResult accept(Path path, BasicFileAttributes attributes);
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/file/PathUtils.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/file/PathUtils.java
new file mode 100644
index 00000000..59d3356d
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/file/PathUtils.java
@@ -0,0 +1,532 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tsfile.external.commons.io.file;
+
+import org.apache.tsfile.external.commons.io.function.IOFunction;
+import org.apache.tsfile.external.commons.io.function.IOSupplier;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UncheckedIOException;
+import java.nio.file.AccessDeniedException;
+import java.nio.file.CopyOption;
+import java.nio.file.DirectoryStream;
+import java.nio.file.FileVisitOption;
+import java.nio.file.FileVisitResult;
+import java.nio.file.FileVisitor;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.DosFileAttributeView;
+import java.nio.file.attribute.FileAttribute;
+import java.nio.file.attribute.PosixFileAttributes;
+import java.nio.file.attribute.PosixFilePermission;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+public class PathUtils {
+  public static final LinkOption[] NOFOLLOW_LINK_OPTION_ARRAY = {LinkOption.NOFOLLOW_LINKS};
+
+  /** Empty {@link OpenOption} array. */
+  public static final OpenOption[] EMPTY_OPEN_OPTION_ARRAY = {};
+
+  public static final LinkOption[] EMPTY_LINK_OPTION_ARRAY = {};
+  public static final DeleteOption[] EMPTY_DELETE_OPTION_ARRAY = {};
+
+  public static Counters.PathCounters delete(
+      final Path path, final LinkOption[] linkOptions, final DeleteOption... deleteOptions)
+      throws IOException {
+    // File deletion through Files deletes links, not targets, so use LinkOption.NOFOLLOW_LINKS.
+    return Files.isDirectory(path, linkOptions)
+        ? deleteDirectory(path, linkOptions, deleteOptions)
+        : deleteFile(path, linkOptions, deleteOptions);
+  }
+
+  public static LinkOption[] noFollowLinkOptionArray() {
+    return NOFOLLOW_LINK_OPTION_ARRAY.clone();
+  }
+
+  public static Counters.PathCounters deleteFile(
+      final Path file, final LinkOption[] linkOptions, final DeleteOption... deleteOptions)
+      throws NoSuchFileException, IOException {
+    //
+    // TODO Needs clean up
+    //
+    if (Files.isDirectory(file, linkOptions)) {
+      throw new NoSuchFileException(file.toString());
+    }
+    final Counters.PathCounters pathCounts = Counters.longPathCounters();
+    boolean exists = exists(file, linkOptions);
+    long size = exists && !Files.isSymbolicLink(file) ? Files.size(file) : 0;
+    try {
+      if (Files.deleteIfExists(file)) {
+        pathCounts.getFileCounter().increment();
+        pathCounts.getByteCounter().add(size);
+        return pathCounts;
+      }
+    } catch (final AccessDeniedException ignored) {
+      // Ignore and try again below.
+    }
+    final Path parent = getParent(file);
+    PosixFileAttributes posixFileAttributes = null;
+    try {
+      if (overrideReadOnly(deleteOptions)) {
+        posixFileAttributes = readPosixFileAttributes(parent, linkOptions);
+        setReadOnly(file, false, linkOptions);
+      }
+      // Read size _after_ having read/execute access on POSIX.
+      exists = exists(file, linkOptions);
+      size = exists && !Files.isSymbolicLink(file) ? Files.size(file) : 0;
+      if (Files.deleteIfExists(file)) {
+        pathCounts.getFileCounter().increment();
+        pathCounts.getByteCounter().add(size);
+      }
+    } finally {
+      if (posixFileAttributes != null) {
+        Files.setPosixFilePermissions(parent, posixFileAttributes.permissions());
+      }
+    }
+    return pathCounts;
+  }
+
+  public static Path setReadOnly(
+      final Path path, final boolean readOnly, final LinkOption... linkOptions) throws IOException {
+    try {
+      // Windows is simplest
+      if (setDosReadOnly(path, readOnly, linkOptions)) {
+        return path;
+      }
+    } catch (final IOException ignored) {
+      // Retry with POSIX below.
+    }
+    final Path parent = getParent(path);
+    if (!isPosix(
+        parent, linkOptions)) { // Test parent because we may not the permissions to test the file.
+      throw new IOException(
+          String.format(
+              "DOS or POSIX file operations not available for '%s', linkOptions %s",
+              path, Arrays.toString(linkOptions)));
+    }
+    // POSIX
+    if (readOnly) {
+      // RO
+      // File, then parent dir (if any).
+      setPosixReadOnlyFile(path, readOnly, linkOptions);
+      setPosixDeletePermissions(parent, false, linkOptions);
+    } else {
+      // RE
+      // Parent dir (if any), then file.
+      setPosixDeletePermissions(parent, true, linkOptions);
+    }
+    return path;
+  }
+
+  private static boolean setPosixDeletePermissions(
+      final Path parent, final boolean enableDeleteChildren, final LinkOption... linkOptions)
+      throws IOException {
+    // To delete a file in POSIX, you need write and execute permissions on its parent directory.
+    // @formatter:off
+    return setPosixPermissions(
+        parent,
+        enableDeleteChildren,
+        Arrays.asList(
+            PosixFilePermission.OWNER_WRITE,
+            // PosixFilePermission.GROUP_WRITE,
+            // PosixFilePermission.OTHERS_WRITE,
+            PosixFilePermission.OWNER_EXECUTE
+            // PosixFilePermission.GROUP_EXECUTE,
+            // PosixFilePermission.OTHERS_EXECUTE
+            ),
+        linkOptions);
+    // @formatter:on
+  }
+
+  private static boolean setPosixPermissions(
+      final Path path,
+      final boolean addPermissions,
+      final List<PosixFilePermission> updatePermissions,
+      final LinkOption... linkOptions)
+      throws IOException {
+    if (path != null) {
+      final Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(path, linkOptions);
+      final Set<PosixFilePermission> newPermissions = new HashSet<>(permissions);
+      if (addPermissions) {
+        newPermissions.addAll(updatePermissions);
+      } else {
+        newPermissions.removeAll(updatePermissions);
+      }
+      if (!newPermissions.equals(permissions)) {
+        Files.setPosixFilePermissions(path, newPermissions);
+      }
+      return true;
+    }
+    return false;
+  }
+
+  private static void setPosixReadOnlyFile(
+      final Path path, final boolean readOnly, final LinkOption... linkOptions) throws IOException {
+    // Not Windows 10
+    final Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(path, linkOptions);
+    // @formatter:off
+    final List<PosixFilePermission> readPermissions =
+        Arrays.asList(
+            PosixFilePermission.OWNER_READ
+            // PosixFilePermission.GROUP_READ,
+            // PosixFilePermission.OTHERS_READ
+            );
+    final List<PosixFilePermission> writePermissions =
+        Arrays.asList(
+            PosixFilePermission.OWNER_WRITE
+            // PosixFilePermission.GROUP_WRITE,
+            // PosixFilePermission.OTHERS_WRITE
+            );
+    // @formatter:on
+    if (readOnly) {
+      // RO: We can read, we cannot write.
+      permissions.addAll(readPermissions);
+      permissions.removeAll(writePermissions);
+    } else {
+      // Not RO: We can read, we can write.
+      permissions.addAll(readPermissions);
+      permissions.addAll(writePermissions);
+    }
+    Files.setPosixFilePermissions(path, permissions);
+  }
+
+  public static boolean isPosix(final Path test, final LinkOption... options) {
+    return exists(test, options) && readPosixFileAttributes(test, options) != null;
+  }
+
+  private static boolean exists(final Path path, final LinkOption... options) {
+    return path != null && (options != null ? Files.exists(path, options) : Files.exists(path));
+  }
+
+  public static PosixFileAttributes readPosixFileAttributes(
+      final Path path, final LinkOption... options) {
+    return readAttributes(path, PosixFileAttributes.class, options);
+  }
+
+  private static boolean setDosReadOnly(
+      final Path path, final boolean readOnly, final LinkOption... linkOptions) throws IOException {
+    final DosFileAttributeView dosFileAttributeView = getDosFileAttributeView(path, linkOptions);
+    if (dosFileAttributeView != null) {
+      dosFileAttributeView.setReadOnly(readOnly);
+      return true;
+    }
+    return false;
+  }
+
+  public static <A extends BasicFileAttributes> A readAttributes(
+      final Path path, final Class<A> type, final LinkOption... options) {
+    try {
+      return path == null ? null : Files.readAttributes(path, type, options);
+    } catch (final UnsupportedOperationException | IOException e) {
+      // For example, on Windows.
+      return null;
+    }
+  }
+
+  public static DosFileAttributeView getDosFileAttributeView(
+      final Path path, final LinkOption... options) {
+    return Files.getFileAttributeView(path, DosFileAttributeView.class, options);
+  }
+
+  public static Counters.PathCounters deleteDirectory(
+      final Path directory, final LinkOption[] linkOptions, final DeleteOption... deleteOptions)
+      throws IOException {
+    return visitFileTree(
+            new DeletingPathVisitor(Counters.longPathCounters(), linkOptions, deleteOptions),
+            directory)
+        .getPathCounters();
+  }
+
+  public static <T extends FileVisitor<? super Path>> T visitFileTree(
+      final T visitor, final Path directory) throws IOException {
+    Files.walkFileTree(directory, visitor);
+    return visitor;
+  }
+
+  private static Path getParent(final Path path) {
+    return path == null ? null : path.getParent();
+  }
+
+  private static boolean overrideReadOnly(final DeleteOption... deleteOptions) {
+    if (deleteOptions == null) {
+      return false;
+    }
+    return Stream.of(deleteOptions).anyMatch(e -> e == StandardDeleteOption.OVERRIDE_READ_ONLY);
+  }
+
+  public static String getFileNameString(final Path path) {
+    return getFileName(path, Path::toString);
+  }
+
+  public static <R> R getFileName(final Path path, final Function<Path, R> function) {
+    final Path fileName = path != null ? path.getFileName() : null;
+    return fileName != null ? function.apply(fileName) : null;
+  }
+
+  public static boolean isEmptyDirectory(final Path directory) throws IOException {
+    try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(directory)) {
+      return !directoryStream.iterator().hasNext();
+    }
+  }
+
+  /////////////////////////////////////////////////////////////////////////////////////////////////
+  // IoTDB
+  /////////////////////////////////////////////////////////////////////////////////////////////////
+
+  /**
+   * A LinkOption used to follow link in this class, the inverse of {@link
+   * LinkOption#NOFOLLOW_LINKS}.
+   *
+   * @since 2.12.0
+   */
+  static final LinkOption NULL_LINK_OPTION = null;
+
+  private static final OpenOption[] OPEN_OPTIONS_TRUNCATE = {
+    StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING
+  };
+
+  private static final OpenOption[] OPEN_OPTIONS_APPEND = {
+    StandardOpenOption.CREATE, StandardOpenOption.APPEND
+  };
+
+  /**
+   * Deletes a directory including subdirectories.
+   *
+   * @param directory directory to delete.
+   * @return The visitor used to delete the given directory.
+   * @throws IOException if an I/O error is thrown by a visitor method.
+   */
+  public static Counters.PathCounters deleteDirectory(final Path directory) throws IOException {
+    return deleteDirectory(directory, EMPTY_DELETE_OPTION_ARRAY);
+  }
+
+  /**
+   * Deletes a directory including subdirectories.
+   *
+   * @param directory directory to delete.
+   * @param deleteOptions How to handle deletion.
+   * @return The visitor used to delete the given directory.
+   * @throws IOException if an I/O error is thrown by a visitor method.
+   * @since 2.8.0
+   */
+  public static Counters.PathCounters deleteDirectory(
+      final Path directory, final DeleteOption... deleteOptions) throws IOException {
+    final LinkOption[] linkOptions = PathUtils.noFollowLinkOptionArray();
+    // POSIX ops will noop on non-POSIX.
+    return withPosixFileAttributes(
+        getParent(directory),
+        linkOptions,
+        overrideReadOnly(deleteOptions),
+        pfa ->
+            visitFileTree(
+                    new DeletingPathVisitor(
+                        Counters.longPathCounters(), linkOptions, deleteOptions),
+                    directory)
+                .getPathCounters());
+  }
+
+  private static <R> R withPosixFileAttributes(
+      final Path path,
+      final LinkOption[] linkOptions,
+      final boolean overrideReadOnly,
+      final IOFunction<PosixFileAttributes, R> function)
+      throws IOException {
+    final PosixFileAttributes posixFileAttributes =
+        overrideReadOnly ? readPosixFileAttributes(path, linkOptions) : null;
+    try {
+      return function.apply(posixFileAttributes);
+    } finally {
+      if (posixFileAttributes != null && path != null && Files.exists(path, linkOptions)) {
+        Files.setPosixFilePermissions(path, posixFileAttributes.permissions());
+      }
+    }
+  }
+
+  /**
+   * Creates a new OutputStream by opening or creating a file, returning an output stream that may
+   * be used to write bytes to the file.
+   *
+   * @param path the Path.
+   * @param append Whether or not to append.
+   * @return a new OutputStream.
+   * @throws IOException if an I/O error occurs.
+   * @see Files#newOutputStream(Path, OpenOption...)
+   * @since 2.12.0
+   */
+  public static OutputStream newOutputStream(final Path path, final boolean append)
+      throws IOException {
+    return newOutputStream(
+        path, EMPTY_LINK_OPTION_ARRAY, append ? OPEN_OPTIONS_APPEND : OPEN_OPTIONS_TRUNCATE);
+  }
+
+  static OutputStream newOutputStream(
+      final Path path, final LinkOption[] linkOptions, final OpenOption... openOptions)
+      throws IOException {
+    if (!exists(path, linkOptions)) {
+      createParentDirectories(
+          path, linkOptions != null && linkOptions.length > 0 ? linkOptions[0] : NULL_LINK_OPTION);
+    }
+    final List<OpenOption> list =
+        new ArrayList<>(Arrays.asList(openOptions != null ? openOptions : EMPTY_OPEN_OPTION_ARRAY));
+    list.addAll(Arrays.asList(linkOptions != null ? linkOptions : EMPTY_LINK_OPTION_ARRAY));
+    return Files.newOutputStream(path, list.toArray(EMPTY_OPEN_OPTION_ARRAY));
+  }
+
+  /**
+   * Creates the parent directories for the given {@code path}.
+   *
+   * <p>If the parent directory already exists, then return it.
+   *
+   * <p>
+   *
+   * @param path The path to a file (or directory).
+   * @param attrs An optional list of file attributes to set atomically when creating the
+   *     directories.
+   * @return The Path for the {@code path}'s parent directory or null if the given path has no
+   *     parent.
+   * @throws IOException if an I/O error occurs.
+   * @since 2.9.0
+   */
+  public static Path createParentDirectories(final Path path, final FileAttribute<?>... attrs)
+      throws IOException {
+    return createParentDirectories(path, LinkOption.NOFOLLOW_LINKS, attrs);
+  }
+
+  /**
+   * Creates the parent directories for the given {@code path}.
+   *
+   * <p>If the parent directory already exists, then return it.
+   *
+   * <p>
+   *
+   * @param path The path to a file (or directory).
+   * @param linkOption A {@link LinkOption} or null.
+   * @param attrs An optional list of file attributes to set atomically when creating the
+   *     directories.
+   * @return The Path for the {@code path}'s parent directory or null if the given path has no
+   *     parent.
+   * @throws IOException if an I/O error occurs.
+   * @since 2.12.0
+   */
+  public static Path createParentDirectories(
+      final Path path, final LinkOption linkOption, final FileAttribute<?>... attrs)
+      throws IOException {
+    Path parent = getParent(path);
+    parent = linkOption == LinkOption.NOFOLLOW_LINKS ? parent : readIfSymbolicLink(parent);
+    if (parent == null) {
+      return null;
+    }
+    final boolean exists =
+        linkOption == null ? Files.exists(parent) : Files.exists(parent, linkOption);
+    return exists ? parent : Files.createDirectories(parent, attrs);
+  }
+
+  private static Path readIfSymbolicLink(final Path path) throws IOException {
+    return path != null ? Files.isSymbolicLink(path) ? Files.readSymbolicLink(path) : path : null;
+  }
+
+  /**
+   * Copies the InputStream from the supplier with {@link Files#copy(InputStream, Path,
+   * CopyOption...)}.
+   *
+   * @param in Supplies the InputStream.
+   * @param target See {@link Files#copy(InputStream, Path, CopyOption...)}.
+   * @param copyOptions See {@link Files#copy(InputStream, Path, CopyOption...)}.
+   * @return See {@link Files#copy(InputStream, Path, CopyOption...)}
+   * @throws IOException See {@link Files#copy(InputStream, Path, CopyOption...)}
+   * @since 2.12.0
+   */
+  public static long copy(
+      final IOSupplier<InputStream> in, final Path target, final CopyOption... copyOptions)
+      throws IOException {
+    try (InputStream inputStream = in.get()) {
+      return Files.copy(inputStream, target, copyOptions);
+    }
+  }
+
+  /**
+   * Returns a stream of filtered paths.
+   *
+   * @param start the start path
+   * @param pathFilter the path filter
+   * @param maxDepth the maximum depth of directories to walk.
+   * @param readAttributes whether to call the filters with file attributes (false passes null).
+   * @param options the options to configure the walk.
+   * @return a filtered stream of paths.
+   * @throws IOException if an I/O error is thrown when accessing the starting file.
+   * @since 2.9.0
+   */
+  public static Stream<Path> walk(
+      final Path start,
+      final PathFilter pathFilter,
+      final int maxDepth,
+      final boolean readAttributes,
+      final FileVisitOption... options)
+      throws IOException {
+    return Files.walk(start, maxDepth, options)
+        .filter(
+            path ->
+                pathFilter.accept(
+                        path, readAttributes ? readBasicFileAttributesUnchecked(path) : null)
+                    == FileVisitResult.CONTINUE);
+  }
+
+  /**
+   * Reads the BasicFileAttributes from the given path. Returns null instead of throwing {@link
+   * UnsupportedOperationException}.
+   *
+   * @param path the path to read.
+   * @return the path attributes.
+   * @throws UncheckedIOException if an I/O error occurs
+   * @since 2.9.0
+   * @deprecated Use {@link #readBasicFileAttributes(Path, LinkOption...)}.
+   */
+  @Deprecated
+  public static BasicFileAttributes readBasicFileAttributesUnchecked(final Path path) {
+    return readBasicFileAttributes(path, EMPTY_LINK_OPTION_ARRAY);
+  }
+
+  /**
+   * Reads the BasicFileAttributes from the given path. Returns null instead of throwing {@link
+   * UnsupportedOperationException}.
+   *
+   * @param path the path to read.
+   * @param options options indicating how to handle symbolic links.
+   * @return the path attributes.
+   * @since 2.12.0
+   */
+  public static BasicFileAttributes readBasicFileAttributes(
+      final Path path, final LinkOption... options) {
+    return readAttributes(path, BasicFileAttributes.class, options);
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/file/PathVisitor.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/file/PathVisitor.java
new file mode 100644
index 00000000..931d630a
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/file/PathVisitor.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tsfile.external.commons.io.file;
+
+import java.nio.file.FileVisitor;
+import java.nio.file.Path;
+
+/**
+ * A {@link FileVisitor} typed to a {@link Path}.
+ *
+ * @since 2.9.0
+ */
+public interface PathVisitor extends FileVisitor<Path> {
+  // empty
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/file/SimplePathVisitor.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/file/SimplePathVisitor.java
new file mode 100644
index 00000000..46a1ad66
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/file/SimplePathVisitor.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tsfile.external.commons.io.file;
+
+import org.apache.tsfile.external.commons.io.function.IOBiFunction;
+
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+
+/**
+ * A {@link SimpleFileVisitor} typed to a {@link Path}.
+ *
+ * @since 2.7
+ */
+public abstract class SimplePathVisitor extends SimpleFileVisitor<Path> implements PathVisitor {
+
+  private final IOBiFunction<Path, IOException, FileVisitResult> visitFileFailedFunction;
+
+  /** Constructs a new instance. */
+  protected SimplePathVisitor() {
+    this.visitFileFailedFunction = super::visitFileFailed;
+  }
+
+  @Override
+  public FileVisitResult visitFileFailed(final Path file, final IOException exc)
+      throws IOException {
+    return visitFileFailedFunction.apply(file, exc);
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/file/StandardDeleteOption.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/file/StandardDeleteOption.java
new file mode 100644
index 00000000..9c619d7a
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/file/StandardDeleteOption.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tsfile.external.commons.io.file;
+
+import org.apache.tsfile.external.commons.io.IOUtils;
+
+import java.util.stream.Stream;
+
+/**
+ * Defines the standard delete options.
+ *
+ * @since 2.8.0
+ */
+public enum StandardDeleteOption implements DeleteOption {
+
+  /**
+   * Overrides the read-only attribute to allow deletion, on POSIX, this means Write and Execute on
+   * the parent.
+   */
+  OVERRIDE_READ_ONLY;
+
+  /**
+   * Returns true if the given options contain {@link StandardDeleteOption#OVERRIDE_READ_ONLY}.
+   *
+   * <p>For now, assume the array is not sorted.
+   *
+   * @param options the array to test
+   * @return true if the given options contain {@link StandardDeleteOption#OVERRIDE_READ_ONLY}.
+   */
+  public static boolean overrideReadOnly(final DeleteOption[] options) {
+    if (IOUtils.length(options) == 0) {
+      return false;
+    }
+    return Stream.of(options).anyMatch(e -> OVERRIDE_READ_ONLY == e);
+  }
+}
diff --git a/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/filefilter/AbstractFileFilter.java b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/filefilter/AbstractFileFilter.java
new file mode 100644
index 00000000..9b184a91
--- /dev/null
+++ b/java/tsfile/src/main/java/org/apache/tsfile/external/commons/io/filefilter/AbstractFileFilter.java
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
... 11433 lines suppressed ...