blob: dcfde6d514932f0af5eb338f8b94a548130debde [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.
*/
/* This file is based on source code from the Iceberg Project (http://iceberg.apache.org/),
* licensed by the Apache Software Foundation (ASF) under the Apache License, Version 2.0. See the
* NOTICE file distributed with this work for additional information regarding copyright
* ownership. */
#include "paimon/common/utils/optimized_roaring_bitmap64.h"
#include <cstring>
#include <utility>
#include <vector>
#include "fmt/format.h"
#include "paimon/common/utils/math.h"
#include "paimon/result.h"
#include "roaring.hh" // NOLINT(build/include_subdir)
namespace paimon {
namespace {
constexpr size_t kBitmapCountSizeBytes = sizeof(int64_t);
constexpr size_t kBitmapKeySizeBytes = sizeof(int32_t);
template <typename T>
void WriteLittleEndian(T value, char** output) {
T little_endian = ToLittleEndian(value);
std::memcpy(*output, &little_endian, sizeof(T));
*output += sizeof(T);
}
template <typename T>
Result<T> ReadLittleEndian(ByteArrayInputStream* input_stream) {
T little_endian = 0;
PAIMON_ASSIGN_OR_RAISE(int64_t read_size,
input_stream->Read(reinterpret_cast<char*>(&little_endian), sizeof(T)));
if (read_size != sizeof(T)) {
return Status::Invalid(
fmt::format("Failed to read {} bytes, only read {}", sizeof(T), read_size));
}
return FromLittleEndian(little_endian);
}
} // namespace
OptimizedRoaringBitmap64 OptimizedRoaringBitmap64::FromRoaringBitmap32(
const RoaringBitmap32& bitmap) {
OptimizedRoaringBitmap64 result;
const auto* roaring_bitmap = static_cast<const roaring::Roaring*>(bitmap.roaring_bitmap_);
result.bitmaps_.push_back(*roaring_bitmap);
return result;
}
Status OptimizedRoaringBitmap64::Add(int64_t position) {
PAIMON_RETURN_NOT_OK(CheckPosition(position));
const auto key = static_cast<int32_t>(position >> 32);
const auto position32 = static_cast<uint32_t>(position);
AllocateBitmapsIfNeeded(static_cast<size_t>(key) + 1);
bitmaps_[key].add(position32);
return Status::OK();
}
Status OptimizedRoaringBitmap64::AddRange(int64_t start, int64_t end) {
for (int64_t position = start; position < end; ++position) {
PAIMON_RETURN_NOT_OK(Add(position));
}
return Status::OK();
}
OptimizedRoaringBitmap64& OptimizedRoaringBitmap64::operator|=(
const OptimizedRoaringBitmap64& other) {
AllocateBitmapsIfNeeded(other.bitmaps_.size());
for (size_t key = 0; key < other.bitmaps_.size(); ++key) {
bitmaps_[key] |= other.bitmaps_[key];
}
return *this;
}
Result<bool> OptimizedRoaringBitmap64::Contains(int64_t position) const {
PAIMON_RETURN_NOT_OK(CheckPosition(position));
const auto key = static_cast<int32_t>(position >> 32);
const auto position32 = static_cast<uint32_t>(position);
return static_cast<size_t>(key) < bitmaps_.size() && bitmaps_[key].contains(position32);
}
bool OptimizedRoaringBitmap64::IsEmpty() const {
return Cardinality() == 0;
}
int64_t OptimizedRoaringBitmap64::Cardinality() const {
int64_t cardinality = 0;
for (const roaring::Roaring& bitmap : bitmaps_) {
cardinality += static_cast<int64_t>(bitmap.cardinality());
}
return cardinality;
}
bool OptimizedRoaringBitmap64::RunLengthEncode() {
bool changed = false;
for (roaring::Roaring& bitmap : bitmaps_) {
changed |= bitmap.runOptimize();
}
return changed;
}
void OptimizedRoaringBitmap64::ForEach(const std::function<void(int64_t)>& consumer) const {
for (size_t key = 0; key < bitmaps_.size(); ++key) {
for (uint32_t position32 : bitmaps_[key]) {
const uint64_t position = (static_cast<uint64_t>(key) << 32) | position32;
consumer(static_cast<int64_t>(position));
}
}
}
size_t OptimizedRoaringBitmap64::GetAllocatedBitmapCount() const {
return bitmaps_.size();
}
size_t OptimizedRoaringBitmap64::GetSizeInBytes() const {
size_t size = kBitmapCountSizeBytes;
for (const roaring::Roaring& bitmap : bitmaps_) {
size += kBitmapKeySizeBytes + bitmap.getSizeInBytes();
}
return size;
}
PAIMON_UNIQUE_PTR<Bytes> OptimizedRoaringBitmap64::Serialize(MemoryPool* pool) const {
if (pool == nullptr) {
pool = GetDefaultPool().get();
}
PAIMON_UNIQUE_PTR<Bytes> bytes = Bytes::AllocateBytes(GetSizeInBytes(), pool);
char* output = bytes->data();
WriteLittleEndian(static_cast<int64_t>(bitmaps_.size()), &output);
for (size_t key = 0; key < bitmaps_.size(); ++key) {
WriteLittleEndian(static_cast<int32_t>(key), &output);
output += bitmaps_[key].write(output);
}
return bytes;
}
Status OptimizedRoaringBitmap64::Deserialize(ByteArrayInputStream* input_stream) {
if (input_stream == nullptr) {
return Status::Invalid("Cannot deserialize OptimizedRoaringBitmap64 from a null stream");
}
PAIMON_ASSIGN_OR_RAISE(int64_t bitmap_count, ReadLittleEndian<int64_t>(input_stream));
if (bitmap_count < 0) {
return Status::Invalid(fmt::format("Invalid bitmap count: {}", bitmap_count));
}
PAIMON_RETURN_NOT_OK(ValidateValueInRange<int32_t>(bitmap_count, "bitmap count"));
std::vector<roaring::Roaring> bitmaps;
bitmaps.reserve(static_cast<size_t>(bitmap_count));
int32_t last_key = -1;
for (int64_t i = 0; i < bitmap_count; ++i) {
PAIMON_ASSIGN_OR_RAISE(int32_t key, ReadLittleEndian<int32_t>(input_stream));
if (key < 0) {
return Status::Invalid(fmt::format("Invalid unsigned key: {}", key));
}
if (key > std::numeric_limits<int32_t>::max() - 1) {
return Status::Invalid(fmt::format("Key is too large: {}", key));
}
if (key <= last_key) {
return Status::Invalid("Keys must be sorted in ascending order");
}
bitmaps.resize(static_cast<size_t>(key));
PAIMON_ASSIGN_OR_RAISE(int64_t position, input_stream->GetPos());
PAIMON_ASSIGN_OR_RAISE(int64_t length, input_stream->Length());
roaring::Roaring bitmap;
try {
bitmap = roaring::Roaring::readSafe(input_stream->GetRawData(), length - position);
} catch (...) {
return Status::Invalid("Failed to deserialize inner RoaringBitmap32");
}
const size_t bitmap_size = bitmap.getSizeInBytes();
PAIMON_RETURN_NOT_OK(
input_stream->Seek(static_cast<int64_t>(bitmap_size), SeekOrigin::FS_SEEK_CUR));
bitmaps.push_back(std::move(bitmap));
last_key = key;
}
bitmaps_ = std::move(bitmaps);
return Status::OK();
}
Status OptimizedRoaringBitmap64::Deserialize(const char* begin, size_t length) {
if (begin == nullptr) {
return Status::Invalid("Cannot deserialize OptimizedRoaringBitmap64 from a null buffer");
}
ByteArrayInputStream input_stream(begin, static_cast<int64_t>(length));
return Deserialize(&input_stream);
}
bool OptimizedRoaringBitmap64::operator==(const OptimizedRoaringBitmap64& other) const noexcept {
if (this == &other) {
return true;
}
return bitmaps_ == other.bitmaps_;
}
Status OptimizedRoaringBitmap64::CheckPosition(int64_t position) {
if (position < 0 || position > kMaxValue) {
return Status::Invalid(
fmt::format("OptimizedRoaringBitmap64 supports positions that are >= 0 and <= {}: {}",
kMaxValue, position));
}
return Status::OK();
}
void OptimizedRoaringBitmap64::AllocateBitmapsIfNeeded(size_t required_length) {
if (bitmaps_.size() < required_length) {
bitmaps_.resize(required_length);
}
}
} // namespace paimon