blob: abfc1ca873c789e38405487e878d25168b9f4d3c [file]
/*
* 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
/// \file iceberg/util/partition_value_util.h
/// Wrapper classes for partition value related data structures.
#include <functional>
#include <unordered_map>
#include <unordered_set>
#include "iceberg/row/partition_values.h"
namespace iceberg {
constexpr size_t kHashPrime = 0x9e3779b9;
using PartitionKey = std::pair<int32_t, PartitionValues>;
/// \brief Lightweight lookup key for heterogeneous lookup without copying
/// PartitionValues.
struct PartitionKeyRef {
int32_t spec_id;
const PartitionValues& values;
PartitionKeyRef(int32_t id, const PartitionValues& vals) : spec_id(id), values(vals) {}
explicit PartitionKeyRef(const PartitionKey& key)
: spec_id(key.first), values(key.second) {}
};
/// \brief Hash functor for PartitionValues.
struct PartitionValuesHash {
std::size_t operator()(const PartitionValues& partition) const noexcept {
std::size_t hash = 0;
LiteralHash literal_hash;
for (const auto& literal : partition.values()) {
hash ^= literal_hash(literal) + kHashPrime + (hash << 6) + (hash >> 2);
}
return hash;
}
};
/// \brief Equality functor for PartitionValues.
struct PartitionValuesEqual {
bool operator()(const PartitionValues& lhs, const PartitionValues& rhs) const {
return lhs == rhs;
}
};
/// \brief Transparent hash functor for PartitionKey with heterogeneous lookup support.
struct PartitionKeyHash {
using is_transparent = void;
std::size_t operator()(const PartitionKey& key) const noexcept {
std::size_t hash = std::hash<int32_t>{}(key.first);
hash ^= PartitionValuesHash{}(key.second) + kHashPrime + (hash << 6) + (hash >> 2);
return hash;
}
std::size_t operator()(const PartitionKeyRef& key) const noexcept {
std::size_t hash = std::hash<int32_t>{}(key.spec_id);
hash ^= PartitionValuesHash{}(key.values) + kHashPrime + (hash << 6) + (hash >> 2);
return hash;
}
};
/// \brief Transparent equality functor for PartitionKey with heterogeneous lookup
/// support.
struct PartitionKeyEqual {
using is_transparent = void;
// Equality for PartitionKey vs PartitionKey
bool operator()(const PartitionKey& lhs, const PartitionKey& rhs) const {
return lhs.first == rhs.first && lhs.second == rhs.second;
}
// Equality for PartitionKey vs PartitionKeyRef (heterogeneous lookup)
bool operator()(const PartitionKey& lhs, const PartitionKeyRef& rhs) const {
return lhs.first == rhs.spec_id && lhs.second == rhs.values;
}
// Equality for PartitionKeyRef vs PartitionKey (heterogeneous lookup)
bool operator()(const PartitionKeyRef& lhs, const PartitionKey& rhs) const {
return lhs.spec_id == rhs.first && lhs.values == rhs.second;
}
// Equality for PartitionKeyRef vs PartitionKeyRef (heterogeneous lookup)
bool operator()(const PartitionKeyRef& lhs, const PartitionKeyRef& rhs) const {
return lhs.spec_id == rhs.spec_id && lhs.values == rhs.values;
}
};
/// \brief A map that uses a pair of spec ID and partition tuple as keys.
///
/// \tparam V the type of values
template <typename V>
class PartitionMap {
public:
using map_type =
std::unordered_map<PartitionKey, V, PartitionKeyHash, PartitionKeyEqual>;
using iterator = typename map_type::iterator;
using const_iterator = typename map_type::const_iterator;
PartitionMap() = default;
/// \brief Get the number of entries in the map.
size_t size() const { return map_.size(); }
/// \brief Check if the map is empty.
bool empty() const { return map_.empty(); }
/// \brief Clear all entries from the map.
void clear() { map_.clear(); }
/// \brief Check if the map contains a key.
/// \param spec_id The partition spec ID.
/// \param values The partition values.
/// \return true if the key exists, false otherwise.
bool contains(int32_t spec_id, const PartitionValues& values) const {
return map_.contains(PartitionKeyRef{spec_id, values});
}
/// \brief Get the value associated with a key.
/// \param spec_id The partition spec ID.
/// \param values The partition values.
/// \return Reference to the value if found, std::nullopt otherwise.
std::optional<std::reference_wrapper<V>> get(int32_t spec_id,
const PartitionValues& values) {
auto it = map_.find(PartitionKeyRef{spec_id, values});
return it != map_.end() ? std::make_optional(std::ref(it->second)) : std::nullopt;
}
/// \brief Get the value associated with a key (const version).
/// \param spec_id The partition spec ID.
/// \param values The partition values.
/// \return Reference to the value if found, std::nullopt otherwise.
std::optional<std::reference_wrapper<const V>> get(
int32_t spec_id, const PartitionValues& values) const {
auto it = map_.find(PartitionKeyRef{spec_id, values});
return it != map_.end() ? std::make_optional(std::cref(it->second)) : std::nullopt;
}
/// \brief Insert or update a value in the map.
/// \param spec_id The partition spec ID.
/// \param values The partition values.
/// \param value The value to insert.
/// \return true if the entry was updated, false if it was inserted.
bool put(int32_t spec_id, PartitionValues values, V value) {
auto it = map_.find(PartitionKeyRef{spec_id, values});
if (it != map_.end()) {
it->second = std::move(value);
return true;
}
map_.emplace(PartitionKey{spec_id, std::move(values)}, std::move(value));
return false;
}
/// \brief Remove an entry from the map.
/// \param spec_id The partition spec ID.
/// \param values The partition values.
/// \return true if the entry was removed, false if it didn't exist.
bool remove(int32_t spec_id, const PartitionValues& values) {
auto it = map_.find(PartitionKeyRef{spec_id, values});
if (it != map_.end()) {
map_.erase(it);
return true;
}
return false;
}
/// \brief Get iterator to the beginning.
iterator begin() { return map_.begin(); }
const_iterator begin() const { return map_.begin(); }
const_iterator cbegin() const { return map_.cbegin(); }
/// \brief Get iterator to the end.
iterator end() { return map_.end(); }
const_iterator end() const { return map_.end(); }
const_iterator cend() const { return map_.cend(); }
private:
map_type map_;
};
/// \brief A set that uses a pair of spec ID and partition tuple as elements.
class PartitionSet {
public:
using set_type = std::unordered_set<PartitionKey, PartitionKeyHash, PartitionKeyEqual>;
using iterator = typename set_type::iterator;
using const_iterator = typename set_type::const_iterator;
PartitionSet() = default;
/// \brief Get the number of elements in the set.
size_t size() const { return set_.size(); }
/// \brief Check if the set is empty.
bool empty() const { return set_.empty(); }
/// \brief Clear all elements from the set.
void clear() { set_.clear(); }
/// \brief Check if the set contains an element.
/// \param spec_id The partition spec ID.
/// \param values The partition values.
/// \return true if the element exists, false otherwise.
bool contains(int32_t spec_id, const PartitionValues& values) const {
return set_.contains(PartitionKeyRef{spec_id, values});
}
/// \brief Add an element to the set.
/// \param spec_id The partition spec ID.
/// \param values The partition values.
/// \return true if the element was added, false if it already existed.
bool add(int32_t spec_id, PartitionValues values) {
auto [_, inserted] = set_.emplace(spec_id, std::move(values));
return inserted;
}
/// \brief Remove an element from the set.
/// \param spec_id The partition spec ID.
/// \param values The partition values.
/// \return true if the element was removed, false if it didn't exist.
bool remove(int32_t spec_id, const PartitionValues& values) {
auto it = set_.find(PartitionKeyRef{spec_id, values});
if (it != set_.end()) {
set_.erase(it);
return true;
}
return false;
}
/// \brief Get iterator to the beginning.
iterator begin() { return set_.begin(); }
const_iterator begin() const { return set_.begin(); }
const_iterator cbegin() const { return set_.cbegin(); }
/// \brief Get iterator to the end.
iterator end() { return set_.end(); }
const_iterator end() const { return set_.end(); }
const_iterator cend() const { return set_.cend(); }
private:
set_type set_;
};
} // namespace iceberg