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