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

leaves12138 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/paimon-cpp.git


The following commit(s) were added to refs/heads/main by this push:
     new bc00e64  feat: add common utilities (#5)
bc00e64 is described below

commit bc00e64609d266eda5ea70c78d1e79b18f133e3d
Author: dalingmeng <[email protected]>
AuthorDate: Mon May 25 08:54:50 2026 +0800

    feat: add common utilities (#5)
---
 LICENSE                                           |  44 +++++
 NOTICE                                            |   4 +
 src/paimon/common/utils/bin_packing.h             |  59 ++++++
 src/paimon/common/utils/bin_packing_test.cpp      |  40 ++++
 src/paimon/common/utils/linked_hash_map.h         | 156 +++++++++++++++
 src/paimon/common/utils/linked_hash_map_test.cpp  | 220 ++++++++++++++++++++++
 src/paimon/common/utils/long_counter.h            |  59 ++++++
 src/paimon/common/utils/long_counter_test.cpp     |  76 ++++++++
 src/paimon/common/utils/math.h                    | 118 ++++++++++++
 src/paimon/common/utils/math_test.cpp             |  79 ++++++++
 src/paimon/common/utils/scope_guard.h             |  46 +++++
 src/paimon/common/utils/scope_guard_test.cpp      |  80 ++++++++
 src/paimon/common/utils/threadsafe_queue.h        |  82 ++++++++
 src/paimon/common/utils/threadsafe_queue_test.cpp | 107 +++++++++++
 src/paimon/common/utils/uuid.h                    |  70 +++++++
 src/paimon/common/utils/uuid_test.cpp             |  44 +++++
 16 files changed, 1284 insertions(+)

diff --git a/LICENSE b/LICENSE
index 261eeb9..6d17236 100644
--- a/LICENSE
+++ b/LICENSE
@@ -199,3 +199,47 @@
    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.
+
+--------------------------------------------------------------------------------
+
+This product includes code from RocksDB.
+
+* endian utility in src/paimon/common/utils/math.h
+* uuid utility in src/paimon/common/utils/uuid.h
+
+Copyright: 2011-present, Facebook, Inc.  All rights reserved.
+Copyright: 2011 The LevelDB Authors. All rights reserved.
+Home page: https://rocksdb.org/
+License: https://www.apache.org/licenses/LICENSE-2.0 and 
https://opensource.org/license/bsd-3-clause
+
+This contains code that is from LevelDB, and that code is under the following 
license:
+
+Copyright (c) 2011 The LevelDB Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+   * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+--------------------------------------------------------------------------------
diff --git a/NOTICE b/NOTICE
index 85fe26f..bf5efb9 100644
--- a/NOTICE
+++ b/NOTICE
@@ -3,3 +3,7 @@ Copyright 2024-2026 The Apache Software Foundation
 
 This product includes software developed at
 The Apache Software Foundation (http://www.apache.org/).
+
+This product includes software from RocksDB project (Apache 2.0 and BSD 
3-clause)
+Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
+Copyright (c) 2011 The LevelDB Authors. All rights reserved.
diff --git a/src/paimon/common/utils/bin_packing.h 
b/src/paimon/common/utils/bin_packing.h
new file mode 100644
index 0000000..06d1868
--- /dev/null
+++ b/src/paimon/common/utils/bin_packing.h
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <functional>
+#include <utility>
+#include <vector>
+
+namespace paimon {
+/// Contains bin packing implementations.
+class BinPacking {
+ public:
+    BinPacking() = delete;
+    ~BinPacking() = delete;
+
+    template <typename T>
+    static std::vector<std::vector<T>> PackForOrdered(
+        std::vector<T>&& items, const std::function<int64_t(const T&)>& 
weight_func,
+        int64_t target_weight) {
+        std::vector<std::vector<T>> packed;
+        std::vector<T> bin_items;
+        int64_t bin_weight = 0;
+        for (auto& item : items) {
+            int64_t weight = weight_func(item);
+            // when get a much big item or total weight enough, we check the 
binItems size. If
+            // greater than zero, we pack it
+            if (bin_weight + weight > target_weight && bin_items.size() > 0) {
+                packed.emplace_back(std::move(bin_items));
+                bin_items.clear();
+                bin_weight = 0;
+            }
+            bin_weight += weight;
+            bin_items.push_back(std::move(item));
+        }
+        if (bin_items.size() > 0) {
+            packed.push_back(std::move(bin_items));
+        }
+        return packed;
+    }
+};
+}  // namespace paimon
diff --git a/src/paimon/common/utils/bin_packing_test.cpp 
b/src/paimon/common/utils/bin_packing_test.cpp
new file mode 100644
index 0000000..2b6e673
--- /dev/null
+++ b/src/paimon/common/utils/bin_packing_test.cpp
@@ -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.
+ */
+
+#include "paimon/common/utils/bin_packing.h"
+
+#include <map>
+#include <string>
+#include <utility>
+
+#include "gtest/gtest.h"
+
+namespace paimon::test {
+
+TEST(BinPackingTest, TestExactlyPack) {
+    std::map<std::string, int64_t> items_map = {{"a", 1000ll}, {"b", 20ll}, 
{"c", 200ll}};
+    auto weight_func = [&](const std::string& item) -> int64_t { return 
items_map[item]; };
+    std::vector<std::string> items = {"a", "b", "c"};
+    std::vector<std::vector<std::string>> expect_packed = {{"a"}, {"b", "c"}};
+    auto packed = BinPacking::PackForOrdered<std::string>(std::move(items), 
weight_func,
+                                                          
/*target_weight=*/800);
+    ASSERT_EQ(packed, expect_packed);
+}
+
+}  // namespace paimon::test
diff --git a/src/paimon/common/utils/linked_hash_map.h 
b/src/paimon/common/utils/linked_hash_map.h
new file mode 100644
index 0000000..f831756
--- /dev/null
+++ b/src/paimon/common/utils/linked_hash_map.h
@@ -0,0 +1,156 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <cstddef>
+#include <iterator>
+#include <list>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+namespace paimon {
+
+template <typename K, typename V>
+class LinkedHashMap {
+ public:
+    using IteratorType = typename std::list<std::pair<K, V>>::iterator;
+    using ConstIteratorType = typename std::list<std::pair<K, 
V>>::const_iterator;
+
+    LinkedHashMap() = default;
+    LinkedHashMap(const LinkedHashMap& other) {
+        order_ = other.order_;
+        map_.clear();
+        for (auto iter = order_.begin(); iter != order_.end(); ++iter) {
+            map_[iter->first] = iter;
+        }
+    }
+
+    LinkedHashMap& operator=(const LinkedHashMap& other) {
+        order_ = other.order_;
+        map_.clear();
+        for (auto iter = order_.begin(); iter != order_.end(); ++iter) {
+            map_[iter->first] = iter;
+        }
+        return *this;
+    }
+
+    bool operator==(const LinkedHashMap& other) const {
+        if (this == &other) {
+            return true;
+        }
+        return map_.size() == other.map_.size() && order_ == other.order_;
+    }
+
+    bool operator!=(const LinkedHashMap& other) const {
+        return !(*this == other);
+    }
+
+    size_t size() const {
+        return order_.size();
+    }
+
+    bool empty() const {
+        return order_.empty();
+    }
+
+    IteratorType erase(const K& key) {
+        if (!map_.count(key)) {
+            return order_.end();
+        }
+        auto iter = order_.erase(map_[key]);
+        map_.erase(key);
+        return iter;
+    }
+
+    IteratorType insert(const K& key, const V& value) {
+        auto iter = map_.find(key);
+        if (iter != map_.end()) {
+            return iter->second;
+        }
+        return unsafe_insert(key, value);
+    }
+
+    IteratorType insert_or_assign(const K& key, const V& value) {
+        auto iter = map_.find(key);
+        if (iter != map_.end()) {
+            // if key exists, update its value
+            iter->second->second = value;
+            return iter->second;
+        }
+        return unsafe_insert(key, value);
+    }
+
+    ConstIteratorType find(const K& key) const {
+        auto iter = map_.find(key);
+        if (iter != map_.end()) {
+            return iter->second;
+        }
+        return order_.end();
+    }
+
+    ConstIteratorType begin() const {
+        return order_.begin();
+    }
+
+    ConstIteratorType end() const {
+        return order_.end();
+    }
+
+    V& operator[](const K& key) {
+        auto iter = map_.find(key);
+        if (iter != map_.end()) {
+            // if key exists, return its value reference
+            return iter->second->second;
+        }
+        return unsafe_insert(key, V())->second;
+    }
+
+    std::unordered_set<K> key_set() const {
+        std::unordered_set<K> key_set;
+        for (const auto& [key, _] : order_) {
+            key_set.insert(key);
+        }
+        return key_set;
+    }
+
+    std::vector<K> key_vec() const {
+        std::vector<K> key_vec;
+        key_vec.reserve(order_.size());
+        for (const auto& [key, _] : order_) {
+            key_vec.push_back(key);
+        }
+        return key_vec;
+    }
+
+ private:
+    IteratorType unsafe_insert(const K& key, const V& value) {
+        auto new_iter = order_.insert(order_.end(), {key, value});
+        map_[key] = new_iter;
+        return new_iter;
+    }
+
+    // map_ {key, iterator in order}
+    std::unordered_map<K, IteratorType> map_;
+    std::list<std::pair<K, V>> order_;
+};
+
+}  // namespace paimon
diff --git a/src/paimon/common/utils/linked_hash_map_test.cpp 
b/src/paimon/common/utils/linked_hash_map_test.cpp
new file mode 100644
index 0000000..6548cb3
--- /dev/null
+++ b/src/paimon/common/utils/linked_hash_map_test.cpp
@@ -0,0 +1,220 @@
+/*
+ * 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.
+ */
+
+#include "paimon/common/utils/linked_hash_map.h"
+
+#include <cstdint>
+#include <string>
+
+#include "gtest/gtest.h"
+
+namespace paimon::test {
+
+TEST(LinkedHashMapTest, TestCopy) {
+    LinkedHashMap<int32_t, std::string> hash_map;
+    hash_map.insert(3, "c");
+    hash_map.insert(1, "a");
+    hash_map.insert(2, "b");
+    ASSERT_EQ(hash_map.size(), 3);
+
+    LinkedHashMap<int32_t, std::string> hash_map_2;
+    hash_map_2 = hash_map;
+    ASSERT_EQ(hash_map_2, hash_map);
+    LinkedHashMap<int32_t, std::string> hash_map_3(hash_map);
+    ASSERT_EQ(hash_map_3, hash_map);
+
+    auto check = [](const LinkedHashMap<int32_t, std::string>& hash_map) {
+        std::vector<std::pair<int32_t, std::string>> expected = {{3, "c"}, {1, 
"a"}, {2, "b"}};
+        std::vector<std::pair<int32_t, std::string>> result;
+        for (const auto& iter : hash_map) {
+            result.push_back(iter);
+        }
+        ASSERT_EQ(result, expected);
+        ASSERT_EQ(std::unordered_set<int32_t>({3, 1, 2}), hash_map.key_set());
+        ASSERT_EQ(std::vector<int32_t>({3, 1, 2}), hash_map.key_vec());
+    };
+    check(hash_map);
+    check(hash_map_2);
+    check(hash_map_3);
+}
+
+TEST(LinkedHashMapTest, TestInsert) {
+    LinkedHashMap<int32_t, std::string> hash_map;
+    hash_map.insert(3, "c");
+    hash_map.insert(1, "a");
+    hash_map.insert(2, "b");
+    ASSERT_EQ(hash_map.size(), 3);
+    // test key exist
+    auto iter = hash_map.find(1);
+    ASSERT_TRUE(iter != hash_map.end());
+    ASSERT_EQ(iter->first, 1);
+    ASSERT_EQ(iter->second, "a");
+
+    // insert the same key (will not modify)
+    hash_map.insert(1, "bb");
+    iter = hash_map.find(1);
+    ASSERT_TRUE(iter != hash_map.end());
+    ASSERT_EQ(iter->first, 1);
+    ASSERT_EQ(iter->second, "a");
+
+    // test iterator
+    std::vector<std::pair<int32_t, std::string>> expected = {{3, "c"}, {1, 
"a"}, {2, "b"}};
+    std::vector<std::pair<int32_t, std::string>> result;
+    for (const auto& iter2 : hash_map) {
+        result.push_back(iter2);
+    }
+    ASSERT_EQ(result, expected);
+    ASSERT_EQ(std::unordered_set<int32_t>({3, 1, 2}), hash_map.key_set());
+    ASSERT_EQ(std::vector<int32_t>({3, 1, 2}), hash_map.key_vec());
+}
+
+TEST(LinkedHashMapTest, TestErase) {
+    LinkedHashMap<int32_t, std::string> hash_map;
+    hash_map.insert(3, "c");
+    hash_map.insert(1, "a");
+    hash_map.insert(2, "b");
+    ASSERT_EQ(hash_map.size(), 3);
+
+    auto iter = hash_map.erase(1);
+    ASSERT_TRUE(iter != hash_map.end());
+    ASSERT_EQ(iter->first, 2);
+    ASSERT_EQ(iter->second, "b");
+    ASSERT_EQ(hash_map.size(), 2);
+
+    iter = hash_map.erase(1);
+    ASSERT_TRUE(iter == hash_map.end());
+
+    iter = hash_map.erase(2);
+    ASSERT_TRUE(iter == hash_map.end());
+    ASSERT_EQ(hash_map.size(), 1);
+}
+
+TEST(LinkedHashMapTest, TestSimple) {
+    LinkedHashMap<int32_t, std::string> hash_map;
+    hash_map.insert_or_assign(3, "c");
+    hash_map.insert_or_assign(1, "a");
+    hash_map.insert_or_assign(2, "b");
+    ASSERT_EQ(hash_map.size(), 3);
+    // test key exist
+    auto iter = hash_map.find(1);
+    ASSERT_TRUE(iter != hash_map.end());
+    ASSERT_EQ(iter->first, 1);
+    ASSERT_EQ(iter->second, "a");
+
+    // test key not exist
+    iter = hash_map.find(100);
+    ASSERT_TRUE(iter == hash_map.end());
+
+    // test insert the same key with insert_or_assign()
+    hash_map.insert_or_assign(1, "aa");
+    ASSERT_EQ(hash_map.size(), 3);
+    iter = hash_map.find(1);
+    ASSERT_TRUE(iter != hash_map.end());
+    ASSERT_EQ(iter->first, 1);
+    ASSERT_EQ(iter->second, "aa");
+
+    iter = hash_map.find(2);
+    ASSERT_TRUE(iter != hash_map.end());
+    ASSERT_EQ(iter->first, 2);
+    ASSERT_EQ(iter->second, "b");
+
+    hash_map.insert_or_assign(0, "d");
+    ASSERT_EQ(hash_map.size(), 4);
+    iter = hash_map.find(0);
+    ASSERT_TRUE(iter != hash_map.end());
+    ASSERT_EQ(iter->first, 0);
+    ASSERT_EQ(iter->second, "d");
+
+    // test iterator
+    std::vector<std::pair<int32_t, std::string>> expected = {
+        {3, "c"}, {1, "aa"}, {2, "b"}, {0, "d"}};
+    std::vector<std::pair<int32_t, std::string>> result;
+    for (const auto& iter2 : hash_map) {
+        result.push_back(iter2);
+    }
+    ASSERT_EQ(result, expected);
+    ASSERT_EQ(std::unordered_set<int32_t>({3, 1, 2, 0}), hash_map.key_set());
+    ASSERT_EQ(std::vector<int32_t>({3, 1, 2, 0}), hash_map.key_vec());
+}
+
+TEST(LinkedHashMapTest, TestOperator) {
+    LinkedHashMap<int32_t, std::string> hash_map;
+    hash_map[10] = "abc";
+    ASSERT_EQ(hash_map.size(), 1);
+    // test key 10 exist
+    auto iter = hash_map.find(10);
+    ASSERT_TRUE(iter != hash_map.end());
+    ASSERT_EQ(iter->first, 10);
+    ASSERT_EQ(iter->second, "abc");
+
+    // test key not exist
+    iter = hash_map.find(100);
+    ASSERT_TRUE(iter == hash_map.end());
+
+    hash_map[20] = "aaa";
+    ASSERT_EQ(hash_map.size(), 2);
+    // test key 20 exist
+    iter = hash_map.find(20);
+    ASSERT_TRUE(iter != hash_map.end());
+    ASSERT_EQ(iter->first, 20);
+    ASSERT_EQ(iter->second, "aaa");
+
+    hash_map[10] = "bbb";
+    ASSERT_EQ(hash_map.size(), 2);
+    iter = hash_map.find(10);
+    ASSERT_TRUE(iter != hash_map.end());
+    ASSERT_EQ(iter->first, 10);
+    ASSERT_EQ(iter->second, "bbb");
+
+    auto non_exist = hash_map[30];
+    ASSERT_EQ(non_exist, "");
+    ASSERT_EQ(hash_map.size(), 3);
+
+    ASSERT_EQ(std::unordered_set<int32_t>({10, 20, 30}), hash_map.key_set());
+    ASSERT_EQ(std::vector<int32_t>({10, 20, 30}), hash_map.key_vec());
+}
+
+TEST(LinkedHashMapTest, TestEqual) {
+    LinkedHashMap<int32_t, std::string> hash_map1;
+    hash_map1.insert(3, "c");
+    hash_map1.insert(1, "a");
+    hash_map1.insert(2, "b");
+    ASSERT_EQ(hash_map1, hash_map1);
+
+    LinkedHashMap<int32_t, std::string> hash_map2;
+    hash_map2[3] = "c";
+    hash_map2[1] = "a";
+    hash_map2[2] = "b";
+    ASSERT_EQ(hash_map1, hash_map2);
+
+    LinkedHashMap<int32_t, std::string> hash_map3;
+    hash_map3[1] = "a";
+    hash_map3[2] = "b";
+    hash_map3[3] = "c";
+    ASSERT_NE(hash_map1, hash_map3);
+
+    LinkedHashMap<int32_t, std::string> hash_map4;
+    hash_map4[3] = "c";
+    hash_map4[1] = "a";
+    hash_map4[2] = "b";
+    hash_map4[4] = "d";
+    ASSERT_NE(hash_map1, hash_map4);
+}
+
+}  // namespace paimon::test
diff --git a/src/paimon/common/utils/long_counter.h 
b/src/paimon/common/utils/long_counter.h
new file mode 100644
index 0000000..c356fdd
--- /dev/null
+++ b/src/paimon/common/utils/long_counter.h
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <string>
+
+namespace paimon {
+
+/// An counter that sums up `long` values.
+class LongCounter {
+ public:
+    LongCounter() = default;
+    explicit LongCounter(int64_t value) {
+        value_ = value;
+    }
+
+    void Add(int64_t value) {
+        value_ += value;
+    }
+
+    int64_t GetValue() const {
+        return value_;
+    }
+
+    void Merge(const LongCounter& other) {
+        value_ += other.GetValue();
+    }
+
+    void Reset() {
+        value_ = 0;
+    }
+
+    std::string ToString() const {
+        return "LongCounter " + std::to_string(value_);
+    }
+
+ private:
+    int64_t value_ = 0;
+};
+
+}  // namespace paimon
diff --git a/src/paimon/common/utils/long_counter_test.cpp 
b/src/paimon/common/utils/long_counter_test.cpp
new file mode 100644
index 0000000..0b594e1
--- /dev/null
+++ b/src/paimon/common/utils/long_counter_test.cpp
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+#include "paimon/common/utils/long_counter.h"
+
+#include "gtest/gtest.h"
+
+namespace paimon::test {
+
+// Test case: Test default constructor and initial value
+TEST(LongCounterTest, DefaultConstructor) {
+    LongCounter counter;
+    EXPECT_EQ(counter.GetValue(), 0);
+}
+
+// Test case: Test constructor with initial value
+TEST(LongCounterTest, ConstructorWithInitialValue) {
+    LongCounter counter(10);
+    EXPECT_EQ(counter.GetValue(), 10);
+}
+
+// Test case: Test Add method
+TEST(LongCounterTest, Add) {
+    LongCounter counter(10);
+    counter.Add(5);
+    EXPECT_EQ(counter.GetValue(), 15);
+
+    counter.Add(-3);
+    EXPECT_EQ(counter.GetValue(), 12);
+}
+
+// Test case: Test Reset method
+TEST(LongCounterTest, Reset) {
+    LongCounter counter(10);
+    counter.Add(5);
+    EXPECT_EQ(counter.GetValue(), 15);
+
+    counter.Reset();
+    EXPECT_EQ(counter.GetValue(), 0);
+}
+
+// Test case: Test Merge method
+TEST(LongCounterTest, Merge) {
+    LongCounter counter1(10);
+    LongCounter counter2(20);
+
+    counter1.Merge(counter2);
+    EXPECT_EQ(counter1.GetValue(), 30);
+}
+
+// Test case: Test ToString method
+TEST(LongCounterTest, ToString) {
+    LongCounter counter(42);
+    EXPECT_EQ(counter.ToString(), "LongCounter 42");
+
+    counter.Reset();
+    EXPECT_EQ(counter.ToString(), "LongCounter 0");
+}
+
+}  // namespace paimon::test
diff --git a/src/paimon/common/utils/math.h b/src/paimon/common/utils/math.h
new file mode 100644
index 0000000..575ed41
--- /dev/null
+++ b/src/paimon/common/utils/math.h
@@ -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.
+ */
+
+//  Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
+//  This source code is licensed under both the GPLv2 (found in the
+//  COPYING file in the root directory) and Apache 2.0 License
+//  (found in the LICENSE.Apache file in the root directory).
+
+// Adapted from RocksDB
+// https://github.com/facebook/rocksdb/blob/main/util/math.h
+
+#pragma once
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <limits>
+#include <type_traits>
+
+namespace paimon {
+
+template <typename To, typename From>
+constexpr bool InRange(From value) {
+    static_assert(std::is_integral_v<To> && std::is_integral_v<From>,
+                  "InRange only supports integral types");
+
+    if constexpr (std::is_signed_v<From> && std::is_unsigned_v<To>) {
+        if (value < 0) {
+            return false;
+        }
+        using CompareType = std::common_type_t<std::make_unsigned_t<From>, To>;
+        return static_cast<CompareType>(value) <=
+               static_cast<CompareType>(std::numeric_limits<To>::max());
+    } else if constexpr (std::is_unsigned_v<From> && std::is_signed_v<To>) {
+        using CompareType = std::common_type_t<From, std::make_unsigned_t<To>>;
+        return static_cast<CompareType>(value) <=
+               static_cast<CompareType>(std::numeric_limits<To>::max());
+    } else {
+        using CompareType = std::common_type_t<To, From>;
+        const auto numeric_value = static_cast<CompareType>(value);
+        const auto min_value = 
static_cast<CompareType>(std::numeric_limits<To>::min());
+        const auto max_value = 
static_cast<CompareType>(std::numeric_limits<To>::max());
+        return numeric_value >= min_value && numeric_value <= max_value;
+    }
+}
+
+// Swaps between big and little endian. Can be used in combination with the
+// little-endian encoding/decoding functions in coding_lean.h and coding.h to
+// encode/decode big endian.
+template <typename T>
+inline T EndianSwapValue(T v) {
+    static_assert(std::is_standard_layout_v<T> && 
std::is_trivially_copyable_v<T>,
+                  "Type must be standard-layout and trivially copyable (e.g., 
integral or "
+                  "floating-point types).");
+    if constexpr (sizeof(T) == 1) {
+        return v;
+    } else if constexpr (std::is_same_v<T, float> ||  // 
NOLINT(readability/braces)
+                         std::is_same_v<T, double> || std::is_integral_v<T>) {
+        using UintType = std::conditional_t<
+            sizeof(T) == 2, uint16_t,
+            std::conditional_t<sizeof(T) == 4, uint32_t,
+                               std::conditional_t<sizeof(T) == 8, uint64_t, 
void>>>;
+
+        static_assert(!std::is_same_v<UintType, void>,
+                      "Unsupported size: only 2-byte, 4-byte, and 8-byte types 
are supported.");
+
+        UintType int_repr;
+        std::memcpy(&int_repr, &v, sizeof(T));
+
+#ifdef _MSC_VER
+        if constexpr (sizeof(T) == 2) {
+            int_repr = _byteswap_ushort(static_cast<uint16_t>(int_repr));
+        } else if constexpr (sizeof(T) == 4) {
+            int_repr = _byteswap_ulong(static_cast<uint32_t>(int_repr));
+        } else if constexpr (sizeof(T) == 8) {
+            int_repr = _byteswap_uint64(static_cast<uint64_t>(int_repr));
+        }
+#else
+        if constexpr (sizeof(T) == 2) {
+            int_repr = __builtin_bswap16(static_cast<uint16_t>(int_repr));
+        } else if constexpr (sizeof(T) == 4) {
+            int_repr = __builtin_bswap32(static_cast<uint32_t>(int_repr));
+        } else if constexpr (sizeof(T) == 8) {
+            int_repr = __builtin_bswap64(static_cast<uint64_t>(int_repr));
+        }
+#endif
+        T result;
+        std::memcpy(&result, &int_repr, sizeof(T));
+        return result;
+    } else {
+        // Fallback for unsupported sizes (e.g., 16-bit integers on some 
platforms)
+        T ret_val{};
+        for (std::size_t i = 0; i < sizeof(T); ++i) {
+            reinterpret_cast<unsigned char*>(&ret_val)[sizeof(T) - 1 - i] =
+                reinterpret_cast<unsigned char*>(&v)[i];
+        }
+        return ret_val;
+    }
+}
+
+}  // namespace paimon
diff --git a/src/paimon/common/utils/math_test.cpp 
b/src/paimon/common/utils/math_test.cpp
new file mode 100644
index 0000000..ae4a503
--- /dev/null
+++ b/src/paimon/common/utils/math_test.cpp
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+#include "paimon/common/utils/math.h"
+
+#include <limits>
+
+#include "gtest/gtest.h"
+
+namespace paimon::test {
+
+// Test case: Test EndianSwapValue for different integral types
+TEST(MathTest, EndianSwapValue) {
+    // Test 16-bit value
+    uint16_t value16 = 0x1234;
+    uint16_t swapped16 = EndianSwapValue(value16);
+    ASSERT_EQ(swapped16, 0x3412);
+
+    // Test 32-bit value
+    uint32_t value32 = 0x12345678;
+    uint32_t swapped32 = EndianSwapValue(value32);
+    ASSERT_EQ(swapped32, 0x78563412);
+
+    // Test 64-bit value
+    uint64_t value64 = 0x123456789ABCDEF0;
+    uint64_t swapped64 = EndianSwapValue(value64);
+    ASSERT_EQ(swapped64, 0xF0DEBC9A78563412);
+}
+
+TEST(MathTest, InRange) {
+    // signed -> unsigned: negative values out of range, boundary values in 
range
+    ASSERT_TRUE(InRange<uint32_t>(0));
+    ASSERT_TRUE(InRange<uint32_t>(std::numeric_limits<int32_t>::max()));
+    ASSERT_FALSE(InRange<uint32_t>(std::numeric_limits<int32_t>::lowest()));
+    ASSERT_FALSE(InRange<uint32_t>(-1));
+
+    // unsigned -> signed: values beyond signed max are out of range
+    ASSERT_TRUE(InRange<int32_t>(static_cast<uint32_t>(0)));
+    
ASSERT_TRUE(InRange<int32_t>(static_cast<uint32_t>(std::numeric_limits<int32_t>::max())));
+    
ASSERT_FALSE(InRange<int32_t>(static_cast<uint32_t>(std::numeric_limits<int32_t>::max())
 + 1U));
+    ASSERT_FALSE(InRange<int32_t>(std::numeric_limits<uint32_t>::max()));
+
+    // wider signed -> narrower signed: overflow detection
+    
ASSERT_TRUE(InRange<int32_t>(static_cast<int64_t>(std::numeric_limits<int32_t>::lowest())));
+    
ASSERT_TRUE(InRange<int32_t>(static_cast<int64_t>(std::numeric_limits<int32_t>::max())));
+    ASSERT_FALSE(
+        
InRange<int32_t>(static_cast<int64_t>(std::numeric_limits<int32_t>::lowest()) - 
1));
+    
ASSERT_FALSE(InRange<int32_t>(static_cast<int64_t>(std::numeric_limits<int32_t>::max())
 + 1));
+
+    // wider unsigned -> narrower unsigned: overflow detection
+    ASSERT_TRUE(InRange<uint32_t>(static_cast<uint64_t>(0)));
+    
ASSERT_TRUE(InRange<uint32_t>(static_cast<uint64_t>(std::numeric_limits<uint32_t>::max())));
+    ASSERT_FALSE(
+        
InRange<uint32_t>(static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()) + 
1ULL));
+
+    // mixed width: narrower -> wider always in range, wider -> narrower may 
overflow
+    ASSERT_TRUE(InRange<int32_t>(static_cast<int16_t>(12)));
+    ASSERT_FALSE(InRange<int16_t>(std::numeric_limits<int32_t>::max()));
+    ASSERT_TRUE(InRange<uint32_t>(std::numeric_limits<uint32_t>::max()));
+    
ASSERT_FALSE(InRange<uint32_t>(static_cast<int64_t>(std::numeric_limits<uint32_t>::max())
 + 1));
+}
+
+}  // namespace paimon::test
diff --git a/src/paimon/common/utils/scope_guard.h 
b/src/paimon/common/utils/scope_guard.h
new file mode 100644
index 0000000..8ff6187
--- /dev/null
+++ b/src/paimon/common/utils/scope_guard.h
@@ -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.
+ */
+
+#pragma once
+
+#include <functional>
+#include <utility>
+
+namespace paimon {
+
+class ScopeGuard {
+ public:
+    template <typename F>
+    explicit ScopeGuard(F&& f) : _exit_fn(std::forward<F>(f)) {}
+
+    ~ScopeGuard() {
+        if (_exit_fn) {
+            _exit_fn();
+        }
+    }
+
+    void Release() {
+        _exit_fn = std::function<void()>();
+    }
+
+ private:
+    std::function<void()> _exit_fn;
+};
+
+}  // namespace paimon
diff --git a/src/paimon/common/utils/scope_guard_test.cpp 
b/src/paimon/common/utils/scope_guard_test.cpp
new file mode 100644
index 0000000..eb4d1de
--- /dev/null
+++ b/src/paimon/common/utils/scope_guard_test.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+#include "paimon/common/utils/scope_guard.h"
+
+#include <memory>
+
+#include "gtest/gtest.h"
+
+namespace paimon::test {
+
+// Test case: ScopeGuard triggers the function when going out of scope
+TEST(ScopeGuardTest, ScopeGuardTriggersOnDestruction) {
+    bool triggered = false;
+
+    {
+        ScopeGuard guard([&triggered]() { triggered = true; });
+    }
+
+    // At this point, the scope guard should have triggered the lambda
+    EXPECT_TRUE(triggered);
+}
+
+// Test case: ScopeGuard does not trigger after calling Release()
+TEST(ScopeGuardTest, ScopeGuardDoesNotTriggerAfterRelease) {
+    bool triggered = false;
+
+    {
+        ScopeGuard guard([&triggered]() { triggered = true; });
+        guard.Release();  // Release the guard, it should not trigger
+    }
+
+    // The scope guard should not trigger the lambda after release
+    EXPECT_FALSE(triggered);
+}
+
+// Test case: ScopeGuard without any function (empty guard)
+TEST(ScopeGuardTest, ScopeGuardEmptyGuardDoesNothing) {
+    bool triggered = false;
+
+    {
+        ScopeGuard guard([]() {});  // Empty function
+    }
+
+    // Nothing should happen
+    EXPECT_FALSE(triggered);
+}
+
+// Test case: ScopeGuard with a complex action
+TEST(ScopeGuardTest, ScopeGuardComplexAction) {
+    bool triggered = false;
+
+    {
+        ScopeGuard guard([&triggered]() {
+            triggered = true;
+            // Simulate some complex cleanup or action
+        });
+    }
+
+    // The scope guard should trigger the complex action
+    EXPECT_TRUE(triggered);
+}
+
+}  // namespace paimon::test
diff --git a/src/paimon/common/utils/threadsafe_queue.h 
b/src/paimon/common/utils/threadsafe_queue.h
new file mode 100644
index 0000000..0566679
--- /dev/null
+++ b/src/paimon/common/utils/threadsafe_queue.h
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <cassert>
+#include <chrono>
+#include <condition_variable>
+#include <cstddef>
+#include <functional>
+#include <mutex>
+#include <optional>
+#include <queue>
+#include <thread>
+#include <utility>
+#include <vector>
+
+namespace paimon {
+
+template <typename T>
+class ThreadsafeQueue {
+ public:
+    void push(const T& value) {
+        std::unique_lock<std::mutex> lock(mtx_);
+        queue_.push(value);
+    }
+
+    void push(T&& value) {
+        std::unique_lock<std::mutex> lock(mtx_);
+        queue_.push(std::move(value));
+    }
+
+    std::optional<T> try_pop() {
+        std::unique_lock<std::mutex> lock(mtx_);
+        if (queue_.empty()) {
+            return std::nullopt;
+        }
+        T front = std::move(queue_.front());
+        queue_.pop();
+        return front;
+    }
+
+    const T* try_front() const {
+        std::unique_lock<std::mutex> lock(mtx_);
+        if (queue_.empty()) {
+            return nullptr;
+        }
+        return &queue_.front();
+    }
+
+    size_t size() const {
+        std::unique_lock<std::mutex> lock(mtx_);
+        return queue_.size();
+    }
+
+    bool empty() const {
+        std::unique_lock<std::mutex> lock(mtx_);
+        return queue_.empty();
+    }
+
+ private:
+    mutable std::queue<T> queue_;
+    mutable std::mutex mtx_;
+};
+
+}  // namespace paimon
diff --git a/src/paimon/common/utils/threadsafe_queue_test.cpp 
b/src/paimon/common/utils/threadsafe_queue_test.cpp
new file mode 100644
index 0000000..f2271f0
--- /dev/null
+++ b/src/paimon/common/utils/threadsafe_queue_test.cpp
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+#include "paimon/common/utils/threadsafe_queue.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "gtest/gtest.h"
+
+namespace paimon::test {
+
+TEST(ThreadsafeQueueTest, BasicPushPop) {
+    ThreadsafeQueue<int> q;
+    q.push(1);
+    q.push(2);
+
+    auto val1 = q.try_pop();
+    ASSERT_TRUE(val1);
+    EXPECT_EQ(*val1, 1);
+
+    auto val2 = q.try_pop();
+    ASSERT_TRUE(val2);
+    EXPECT_EQ(*val2, 2);
+
+    auto val3 = q.try_pop();
+    EXPECT_FALSE(val3);
+}
+
+TEST(ThreadsafeQueueTest, PushRValue) {
+    ThreadsafeQueue<std::string> q;
+    std::string s1 = "hello";
+    q.push(std::move(s1));
+
+    auto val = q.try_pop();
+    ASSERT_TRUE(val);
+    EXPECT_EQ(*val, "hello");
+    EXPECT_TRUE(s1.empty());  // NOLINT(bugprone-use-after-move)
+}
+
+TEST(ThreadsafeQueueTest, EmptyMethod) {
+    ThreadsafeQueue<int> q;
+    EXPECT_TRUE(q.empty());
+
+    q.push(1);
+    EXPECT_FALSE(q.empty());
+
+    q.try_pop();
+    EXPECT_TRUE(q.empty());
+}
+
+TEST(ThreadsafeQueueTest, SizeMethod) {
+    ThreadsafeQueue<int> q;
+    EXPECT_EQ(q.size(), 0);
+
+    q.push(1);
+    EXPECT_EQ(q.size(), 1);
+
+    q.push(2);
+    EXPECT_EQ(q.size(), 2);
+
+    q.try_pop();
+    EXPECT_EQ(q.size(), 1);
+
+    q.try_pop();
+    EXPECT_EQ(q.size(), 0);
+}
+
+TEST(ThreadsafeQueueTest, TryFront) {
+    ThreadsafeQueue<int> q;
+    EXPECT_EQ(q.try_front(), nullptr);
+
+    q.push(10);
+    const int* front_val = q.try_front();
+    ASSERT_NE(front_val, nullptr);
+    EXPECT_EQ(*front_val, 10);
+    EXPECT_EQ(q.size(), 1);
+
+    q.push(20);
+    front_val = q.try_front();
+    ASSERT_NE(front_val, nullptr);
+    EXPECT_EQ(*front_val, 10);
+
+    q.try_pop();
+    front_val = q.try_front();
+    ASSERT_NE(front_val, nullptr);
+    EXPECT_EQ(*front_val, 20);
+}
+
+}  // namespace paimon::test
diff --git a/src/paimon/common/utils/uuid.h b/src/paimon/common/utils/uuid.h
new file mode 100644
index 0000000..23cf24e
--- /dev/null
+++ b/src/paimon/common/utils/uuid.h
@@ -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.
+ */
+
+//  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
+//  This source code is licensed under both the GPLv2 (found in the
+//  COPYING file in the root directory) and Apache 2.0 License
+//  (found in the LICENSE.Apache file in the root directory).
+//
+// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file. See the AUTHORS file for names of contributors.
+
+// Adapted from RocksDB
+// https://github.com/facebook/rocksdb/blob/main/port/port_posix.cc
+
+#pragma once
+
+#include <fstream>
+#include <string>
+
+#if defined(__APPLE__)
+#include <uuid/uuid.h>
+
+#include <array>
+#endif
+
+namespace paimon {
+
+class UUID {
+ public:
+    static bool Generate(std::string* output) {
+#if defined(__APPLE__)
+        output->clear();
+        std::array<char, 37> buffer{};
+        uuid_t uuid;
+        uuid_generate_random(uuid);
+        uuid_unparse_lower(uuid, buffer.data());
+        *output = buffer.data();
+        if (output->size() == 36) {
+            return true;
+        }
+#else
+        output->clear();
+        std::ifstream f("/proc/sys/kernel/random/uuid");
+        if (std::getline(f, /*&*/ *output) && output->size() == 36) {
+            return true;
+        }
+#endif
+        output->clear();
+        return false;
+    }
+};
+
+}  // namespace paimon
diff --git a/src/paimon/common/utils/uuid_test.cpp 
b/src/paimon/common/utils/uuid_test.cpp
new file mode 100644
index 0000000..1e0a6e7
--- /dev/null
+++ b/src/paimon/common/utils/uuid_test.cpp
@@ -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.
+ */
+
+#include "paimon/common/utils/uuid.h"
+
+#include <string>
+
+#include "gtest/gtest.h"
+
+namespace paimon::test {
+
+// Test case: Successfully generate a valid UUID
+TEST(UUIDTest, GenerateValidUUID) {
+    std::string output;
+    bool result = UUID::Generate(&output);
+
+    // Ensure that the result is true (UUID is valid)
+    EXPECT_TRUE(result);
+
+    // Ensure that the generated UUID has 36 characters
+    EXPECT_EQ(output.size(), 36);
+
+    // Ensure the UUID contains dashes in the correct positions
+    EXPECT_TRUE(output.find('-') == 8 || output.find('-') == 13 || 
output.find('-') == 18 ||
+                output.find('-') == 23);
+}
+
+}  // namespace paimon::test


Reply via email to