| /* |
| * 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. |
| */ |
| |
| #ifndef ENCODING_CHIMP_ENCODER_H |
| #define ENCODING_CHIMP_ENCODER_H |
| |
| #include <stdint.h> |
| |
| #include <climits> |
| |
| #include "bit_packing_utils.h" |
| #include "common/allocator/byte_stream.h" |
| #include "common/db_common.h" |
| #include "encode_utils.h" |
| #include "encoder.h" |
| #include "utils/errno_define.h" |
| #include "utils/util_define.h" |
| |
| namespace storage { |
| |
| static constexpr int8_t CHIMP_LEADING_REPRESENTATION[64] = { |
| 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 4, 4, 5, 5, |
| 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, |
| 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}; |
| |
| static constexpr int8_t CHIMP_LEADING_ROUND[64] = { |
| 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 12, 12, 12, 12, |
| 16, 16, 18, 18, 20, 20, 22, 22, 24, 24, 24, 24, 24, 24, 24, 24, |
| 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, |
| 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24}; |
| |
| static constexpr int32_t CHIMP_ENCODING_ENDING_INTEGER = INT32_MIN; |
| static constexpr int64_t CHIMP_ENCODING_ENDING_LONG = INT64_MIN; |
| static constexpr int32_t CHIMP_ENCODING_ENDING_FLOAT = 0x7fc00000; |
| static constexpr int64_t CHIMP_ENCODING_ENDING_DOUBLE = |
| static_cast<int64_t>(0x7ff8000000000000ULL); |
| |
| template <typename T> |
| struct ChimpTraits; |
| |
| template <> |
| struct ChimpTraits<int32_t> { |
| using Unsigned = uint32_t; |
| static constexpr int VALUE_BITS = 32; |
| static constexpr int PREVIOUS_VALUES = 64; |
| static constexpr int PREVIOUS_VALUES_LOG2 = 6; |
| static constexpr int THRESHOLD = 5 + PREVIOUS_VALUES_LOG2; |
| static constexpr int SET_LSB = (1 << (THRESHOLD + 1)) - 1; |
| static constexpr int CASE_ZERO_METADATA_LENGTH = PREVIOUS_VALUES_LOG2 + 2; |
| static constexpr int CASE_ONE_METADATA_LENGTH = PREVIOUS_VALUES_LOG2 + 10; |
| static constexpr int32_t ENDING = CHIMP_ENCODING_ENDING_INTEGER; |
| }; |
| |
| template <> |
| struct ChimpTraits<int64_t> { |
| using Unsigned = uint64_t; |
| static constexpr int VALUE_BITS = 64; |
| static constexpr int PREVIOUS_VALUES = 128; |
| static constexpr int PREVIOUS_VALUES_LOG2 = 7; |
| static constexpr int THRESHOLD = 6 + PREVIOUS_VALUES_LOG2; |
| static constexpr int SET_LSB = (1 << (THRESHOLD + 1)) - 1; |
| static constexpr int CASE_ZERO_METADATA_LENGTH = PREVIOUS_VALUES_LOG2 + 2; |
| static constexpr int CASE_ONE_METADATA_LENGTH = PREVIOUS_VALUES_LOG2 + 11; |
| static constexpr int64_t ENDING = CHIMP_ENCODING_ENDING_LONG; |
| }; |
| |
| template <typename T> |
| class ChimpEncoder : public Encoder { |
| public: |
| using Traits = ChimpTraits<T>; |
| using Unsigned = typename Traits::Unsigned; |
| |
| ChimpEncoder() { reset(); } |
| ~ChimpEncoder() override = default; |
| |
| void destroy() override {} |
| |
| void reset() override { |
| type_ = common::CHIMP; |
| first_value_was_written_ = false; |
| stored_leading_zeros_ = INT32_MAX; |
| writer_.reset(); |
| index_ = 0; |
| current_ = 0; |
| for (int i = 0; i < Traits::PREVIOUS_VALUES; ++i) { |
| stored_values_[i] = 0; |
| } |
| for (int i = 0; i < (1 << (Traits::THRESHOLD + 1)); ++i) { |
| indices_[i] = 0; |
| } |
| } |
| |
| int flush(common::ByteStream& out) override { |
| int ret = encode_value(Traits::ENDING, out); |
| if (RET_FAIL(ret)) { |
| return ret; |
| } |
| ret = writer_.flush_forced(out); |
| reset(); |
| return ret; |
| } |
| |
| int get_max_byte_size() override { |
| return (2 + (Traits::VALUE_BITS == 32 ? 5 : 6) + |
| (Traits::VALUE_BITS == 32 ? 5 : 6) + Traits::VALUE_BITS) / |
| 8 + |
| 1; |
| } |
| |
| int encode(bool, common::ByteStream&) override { |
| return common::E_TYPE_NOT_MATCH; |
| } |
| int encode(int32_t, common::ByteStream&) override { |
| return common::E_TYPE_NOT_MATCH; |
| } |
| int encode(int64_t, common::ByteStream&) override { |
| return common::E_TYPE_NOT_MATCH; |
| } |
| int encode(float, common::ByteStream&) override { |
| return common::E_TYPE_NOT_MATCH; |
| } |
| int encode(double, common::ByteStream&) override { |
| return common::E_TYPE_NOT_MATCH; |
| } |
| int encode(common::String, common::ByteStream&) override { |
| return common::E_TYPE_NOT_MATCH; |
| } |
| |
| protected: |
| int encode_value(T value, common::ByteStream& out) { |
| if (LIKELY(first_value_was_written_)) { |
| return compress_value(value, out); |
| } |
| int ret = write_first(value, out); |
| if (ret == common::E_OK) { |
| first_value_was_written_ = true; |
| } |
| return ret; |
| } |
| |
| private: |
| int key_of(T value) const { |
| return static_cast<int>(static_cast<uint32_t>(value) & Traits::SET_LSB); |
| } |
| |
| int write_first(T value, common::ByteStream& out) { |
| stored_values_[current_] = value; |
| indices_[key_of(value)] = index_; |
| return writer_.write_bits(static_cast<Unsigned>(value), |
| Traits::VALUE_BITS, out); |
| } |
| |
| int compress_value(T value, common::ByteStream& out) { |
| int ret = common::E_OK; |
| int key = key_of(value); |
| T xor_value = 0; |
| int previous_index = 0; |
| int trailing_zeros = 0; |
| int curr_index = indices_[key]; |
| |
| if ((index_ - curr_index) < Traits::PREVIOUS_VALUES) { |
| T temp_xor = |
| value ^ stored_values_[curr_index % Traits::PREVIOUS_VALUES]; |
| trailing_zeros = number_of_trailing_zeros(temp_xor); |
| if (trailing_zeros > Traits::THRESHOLD) { |
| previous_index = curr_index % Traits::PREVIOUS_VALUES; |
| xor_value = temp_xor; |
| } else { |
| previous_index = index_ % Traits::PREVIOUS_VALUES; |
| xor_value = stored_values_[previous_index] ^ value; |
| } |
| } else { |
| previous_index = index_ % Traits::PREVIOUS_VALUES; |
| xor_value = stored_values_[previous_index] ^ value; |
| } |
| |
| if (xor_value == 0) { |
| ret = writer_.write_bits(static_cast<uint64_t>(previous_index), |
| Traits::CASE_ZERO_METADATA_LENGTH, out); |
| stored_leading_zeros_ = Traits::VALUE_BITS + 1; |
| } else { |
| int leading_zeros = |
| CHIMP_LEADING_ROUND[number_of_leading_zeros(xor_value)]; |
| if (trailing_zeros > Traits::THRESHOLD) { |
| int significant_bits = |
| Traits::VALUE_BITS - leading_zeros - trailing_zeros; |
| uint64_t metadata = |
| (Traits::VALUE_BITS == 32 |
| ? 256ULL * (Traits::PREVIOUS_VALUES + previous_index) |
| : 512ULL * |
| (Traits::PREVIOUS_VALUES + previous_index)) + |
| (Traits::VALUE_BITS == 32 |
| ? 32ULL * CHIMP_LEADING_REPRESENTATION[leading_zeros] |
| : 64ULL * |
| CHIMP_LEADING_REPRESENTATION[leading_zeros]) + |
| static_cast<uint64_t>(significant_bits); |
| if (RET_FAIL(writer_.write_bits( |
| metadata, Traits::CASE_ONE_METADATA_LENGTH, out))) { |
| return ret; |
| } |
| Unsigned meaningful = |
| static_cast<Unsigned>(xor_value) >> trailing_zeros; |
| ret = writer_.write_bits(meaningful, significant_bits, out); |
| stored_leading_zeros_ = Traits::VALUE_BITS + 1; |
| } else if (leading_zeros == stored_leading_zeros_) { |
| if (RET_FAIL(writer_.write_bit(true, out))) { |
| return ret; |
| } |
| if (RET_FAIL(writer_.write_bit(false, out))) { |
| return ret; |
| } |
| int significant_bits = Traits::VALUE_BITS - leading_zeros; |
| ret = writer_.write_bits(static_cast<Unsigned>(xor_value), |
| significant_bits, out); |
| } else { |
| stored_leading_zeros_ = leading_zeros; |
| int significant_bits = Traits::VALUE_BITS - leading_zeros; |
| if (RET_FAIL(writer_.write_bits( |
| 24ULL + CHIMP_LEADING_REPRESENTATION[leading_zeros], 5, |
| out))) { |
| return ret; |
| } |
| ret = writer_.write_bits(static_cast<Unsigned>(xor_value), |
| significant_bits, out); |
| } |
| } |
| |
| if (ret == common::E_OK) { |
| current_ = (current_ + 1) % Traits::PREVIOUS_VALUES; |
| stored_values_[current_] = value; |
| ++index_; |
| indices_[key] = index_; |
| } |
| return ret; |
| } |
| |
| protected: |
| common::TSEncoding type_; |
| bool first_value_was_written_; |
| int stored_leading_zeros_; |
| MsbBitWriter writer_; |
| T stored_values_[Traits::PREVIOUS_VALUES]; |
| int indices_[1 << (Traits::THRESHOLD + 1)]; |
| int index_; |
| int current_; |
| }; |
| |
| class IntChimpEncoder : public ChimpEncoder<int32_t> { |
| public: |
| int encode(int32_t value, common::ByteStream& out) override { |
| return encode_value(value, out); |
| } |
| }; |
| |
| class LongChimpEncoder : public ChimpEncoder<int64_t> { |
| public: |
| int encode(int64_t value, common::ByteStream& out) override { |
| return encode_value(value, out); |
| } |
| }; |
| |
| class FloatChimpEncoder : public ChimpEncoder<int32_t> { |
| public: |
| int encode(float value, common::ByteStream& out) override { |
| return encode_value(common::float_to_int(value), out); |
| } |
| |
| int flush(common::ByteStream& out) override { |
| int ret = encode_value(CHIMP_ENCODING_ENDING_FLOAT, out); |
| if (RET_FAIL(ret)) { |
| return ret; |
| } |
| ret = writer_.flush_forced(out); |
| reset(); |
| return ret; |
| } |
| }; |
| |
| class DoubleChimpEncoder : public ChimpEncoder<int64_t> { |
| public: |
| int encode(double value, common::ByteStream& out) override { |
| return encode_value(common::double_to_long(value), out); |
| } |
| |
| int flush(common::ByteStream& out) override { |
| int ret = encode_value(CHIMP_ENCODING_ENDING_DOUBLE, out); |
| if (RET_FAIL(ret)) { |
| return ret; |
| } |
| ret = writer_.flush_forced(out); |
| reset(); |
| return ret; |
| } |
| }; |
| |
| } // namespace storage |
| |
| #endif // ENCODING_CHIMP_ENCODER_H |