// Copyright 2024 PingCAP, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #pragma once #include #include #include #include #include #include namespace DB::ErrorCodes { extern const int CANNOT_COMPRESS; extern const int CANNOT_DECOMPRESS; } // namespace DB::ErrorCodes namespace DB::Compression { template char * writeSameValueMultipleTime(T value, UInt32 count, char * dest); /// Constant encoding template inline size_t constantEncoding(T constant, char * dest) { unalignedStore(dest, constant); return sizeof(T); } template void constantDecoding(const char * src, UInt32 source_size, char * dest, UInt32 dest_size); /// Constant delta encoding template inline size_t constantDeltaEncoding(T first_value, T constant_delta, char * dest) { unalignedStore(dest, first_value); dest += sizeof(T); unalignedStore(dest, constant_delta); return sizeof(T) + sizeof(T); } template void constantDeltaDecoding(const char * src, UInt32 source_size, char * dest, UInt32 dest_size); /// Run-length encoding // template using RunLengthPair = std::pair; template using RunLengthPairs = std::vector>; template static constexpr size_t RunLengthPairLength = sizeof(T) + sizeof(UInt8); // Return the approximate size of the run-length encoded data. The actual size may be larger. template size_t runLengthEncodedApproximateSize(const T * source, UInt32 source_size); // After run-length encoding, the values stored in `dest` are: // [val1, val2, val3, ..., valn, cnt1, cnt2, ..., cntn] template size_t runLengthEncoding(const T * source, UInt32 source_size, char * dest); template void runLengthDecoding(const char * src, UInt32 source_size, char * dest, UInt32 dest_size); /// Frame of Reference encoding template void subtractFrameOfReference(T * dst, T frame_of_reference, UInt32 count); template size_t FOREncoding(T * values, UInt32 count, T frame_of_reference, UInt8 width, char * dest) { assert(count != 0); // caller must ensure input is not empty subtractFrameOfReference(values, frame_of_reference, count); // store frame of reference unalignedStore(dest, frame_of_reference); dest += sizeof(T); // store width unalignedStore(dest, width); dest += sizeof(UInt8); // if width == 0, skip bitpacking if (width == 0) return sizeof(T) + sizeof(UInt8); auto required_size = BitpackingPrimitives::getRequiredSize(count, width); // after applying frame of reference, all values are bigger than 0. BitpackingPrimitives::packBuffer(reinterpret_cast(dest), values, count, width); return sizeof(T) + sizeof(UInt8) + required_size; } template void applyFrameOfReference(T * dst, T frame_of_reference, UInt32 count); template void FORDecoding(const char * src, UInt32 source_size, char * dest, UInt32 dest_size) { static constexpr UInt8 BYTES_SIZE = sizeof(T); if unlikely (dest_size % BYTES_SIZE != 0) throw Exception( ErrorCodes::CANNOT_DECOMPRESS, "uncompressed size {} is not aligned to {}", dest_size, BYTES_SIZE); const auto count = dest_size / BYTES_SIZE; T frame_of_reference = unalignedLoad(src); src += BYTES_SIZE; auto width = unalignedLoad(src); src += sizeof(UInt8); const auto required_size = source_size - BYTES_SIZE - sizeof(UInt8); RUNTIME_CHECK(BitpackingPrimitives::getRequiredSize(count, width) == required_size); auto round_size = BitpackingPrimitives::roundUpToAlgorithmGroupSize(count); if (round_size != count) { // Reserve enough space for the temporary buffer. unsigned char tmp_buffer[round_size * BYTES_SIZE]; BitpackingPrimitives::unPackBuffer(tmp_buffer, reinterpret_cast(src), count, width); applyFrameOfReference(reinterpret_cast(tmp_buffer), frame_of_reference, count); memcpy(dest, tmp_buffer, dest_size); return; } BitpackingPrimitives::unPackBuffer( reinterpret_cast(dest), reinterpret_cast(src), count, width); applyFrameOfReference(reinterpret_cast(dest), frame_of_reference, count); } /// ZigZag encoding template void zigZagEncoding(const T * source, UInt32 count, T * dest); template void zigZagDecoding(const T * source, UInt32 count, T * dest); /// Delta encoding template void deltaEncoding(const T * source, UInt32 count, T * dest); template void ordinaryDeltaDecoding(const char * source, UInt32 source_size, char * dest) { T accumulator{}; const char * const source_end = source + source_size; while (source < source_end) { accumulator += unalignedLoad(source); unalignedStore(dest, accumulator); source += sizeof(T); dest += sizeof(T); } } template void deltaDecoding(const char * source, UInt32 source_size, char * dest); /// Delta + Frame of Reference encoding template void ordinaryDeltaFORDecoding(const char * src, UInt32 source_size, char * dest, UInt32 dest_size) { // caller should ensure these size assert(source_size >= sizeof(T)); assert(dest_size >= sizeof(T)); // copy first value to dest memcpy(dest, src, sizeof(T)); if (unlikely(source_size == sizeof(T))) return; // FOR decode deltas FORDecoding(src + sizeof(T), source_size - sizeof(T), dest + sizeof(T), dest_size - sizeof(T)); // ZigZag decode deltas auto * deltas = reinterpret_cast(dest + sizeof(T)); zigZagDecoding(deltas, (dest_size - sizeof(T)) / sizeof(T), deltas); // delta decode using TS = typename std::make_signed::type; ordinaryDeltaDecoding(dest, dest_size, dest); } template void deltaFORDecoding(const char * src, UInt32 source_size, char * dest, UInt32 dest_size); } // namespace DB::Compression