blob: e2315764fe68c6c40cb04fe4f0cd577c0347acdd [file]
//
// Created by 李烁麟 on 25-8-3.
//
#ifndef TS_2DIFF_H
#define TS_2DIFF_H
#include <cstdint>
#include <cstring>
#include <iostream>
#include "utils.h"
template <typename>
struct always_false : std::false_type {};
template <typename T,
typename =
typename std::enable_if<std::is_same<T, int32_t>::value ||
std::is_same<T, int64_t>::value>::type>
class ts_2diff_encoder {
public:
ts_2diff_encoder() {
block_size_ = 128;
delta_array_ = new T[block_size_];
write_index_ = -1;
bits_left_ = 8;
buffer_ = 0;
delta_array_min_ = 0;
delta_array_max_ = 0;
first_value_ = 0;
previous_value_ = 0;
cap_ = 1024;
cur_position_ = 0;
data_array_ = (uint8_t *)malloc(cap_);
}
~ts_2diff_encoder() { free(data_array_); }
void do_encode(T value) {
// Record first value.
if (write_index_ == -1) {
first_value_ = value;
previous_value_ = first_value_;
write_index_++;
return;
}
T delta = value - previous_value_;
previous_value_ = value;
if (write_index_ == 0) {
delta_array_min_ = delta;
delta_array_max_ = delta;
}
delta_array_min_ = std::min(delta, delta_array_min_);
delta_array_max_ = std::max(delta, delta_array_max_);
delta_array_[write_index_++] = delta;
if (write_index_ >= block_size_) {
flush_internal();
}
}
int32_t flush(uint8_t *&data) {
if (write_index_ != -1) {
flush_internal();
}
data = data_array_;
return cur_position_;
}
// Flush data into local data array.
void flush_internal() {
uint8_t *data = nullptr;
int data_size = write_to_binary(data);
if (data_size == 0) {
return;
}
if (cap_ - cur_position_ < data_size) {
cap_ *= 2;
data_array_ = (uint8_t *)realloc(data_array_, cap_);
}
memcpy(data_array_ + cur_position_, data, data_size);
free(data);
cur_position_ += data_size;
}
int write_to_binary(uint8_t *&data) {
uint8_t *value = nullptr;
if (write_index_ == -1) {
return 0;
}
for (int i = 0; i < write_index_; ++i) {
delta_array_[i] -= delta_array_min_;
}
int bit_width = 0;
T delta_max = delta_array_max_ - delta_array_min_;
while (delta_max > 0) {
bit_width++;
delta_max >>= 1;
}
int size = 4 * 4 + (bit_width * write_index_ + 7)/8;
value = (uint8_t *)malloc(size);
data = value;
write_uint32(write_index_, value);
write_uint32(bit_width, value);
if constexpr (std::is_same<T, int32_t>::value) {
write_uint32(static_cast<uint32_t>(delta_array_min_), value);
write_uint32(static_cast<uint32_t>(first_value_), value);
} else if constexpr (std::is_same<T, int64_t>::value) {
write_uint64(static_cast<uint64_t>(delta_array_min_), value);
write_uint64(static_cast<uint64_t>(first_value_), value);
} else {
static_assert(always_false<T>::value, "Unsupport type T");
}
for (int i = 0; i < write_index_; i++) {
write_bits(delta_array_[i], bit_width, value);
}
if (bits_left_ != 0 && bits_left_ != 8) {
bits_left_ = 0;
flush_byte_if_full(value);
}
write_index_ = -1;
return size;
}
void write_bits(int64_t value, int bits, uint8_t *&out_stream) {
while (bits > 0) {
int shift = bits - bits_left_;
if (shift >= 0) {
buffer_ |=
(uint8_t)((value >> shift) & ((1 << bits_left_) - 1));
bits -= bits_left_;
bits_left_ = 0;
} else {
shift = bits_left_ - bits;
buffer_ |= (uint8_t)(value << shift);
bits_left_ -= bits;
bits = 0;
}
flush_byte_if_full(out_stream);
}
}
void flush_byte_if_full(uint8_t *&out_stream) {
if (bits_left_ == 0) {
memcpy(out_stream, &buffer_, 1);
out_stream++;
buffer_ = 0;
bits_left_ = 8;
}
}
private:
int block_size_;
T *delta_array_;
T first_value_;
T previous_value_;
T delta_array_min_;
T delta_array_max_;
uint8_t buffer_;
uint8_t *data_array_ = nullptr;
int32_t cap_;
int32_t cur_position_;
int bits_left_;
int write_index_;
};
template <typename T,
typename =
typename std::enable_if<std::is_same<T, int32_t>::value ||
std::is_same<T, int64_t>::value>::type>
class ts_2diff_decoder {
public:
explicit ts_2diff_decoder(uint8_t *data_array) {
write_index_ = -1;
bits_left_ = 0;
stored_value_ = 0;
buffer_ = 0;
delta_min_ = 0;
previous_value_ = 0;
bit_width_ = 0;
current_index_ = 0;
data_array_ = data_array;
}
T decode() {
T ret_value = 0;
if (current_index_ == 0) {
write_index_ = read_ui32(data_array_);
bit_width_ = read_ui32(data_array_);
if constexpr (std::is_same<T, int32_t>::value) {
delta_min_ = read_ui32(data_array_);
previous_value_ = read_ui32(data_array_);
} else if constexpr (std::is_same<T, int64_t>::value) {
delta_min_ = read_ui64(data_array_);
previous_value_ = read_ui64(data_array_);
} else {
static_assert(always_false<T>::value, "Unsupport type T");
}
ret_value = previous_value_;
if (write_index_ == 0) {
current_index_ = 0;
} else {
current_index_ = 1;
}
return ret_value;
}
if (current_index_++ >= write_index_) {
current_index_ = 0;
}
stored_value_ = read_long(bit_width_, data_array_);
ret_value = stored_value_ + previous_value_ + delta_min_;
previous_value_ = ret_value;
return ret_value;
}
void read_byte_if_empty(uint8_t *&in) {
if (bits_left_ == 0) {
memcpy(&buffer_, in, 1);
bits_left_ = 8;
in += 1;
}
}
int64_t read_long(int bits, uint8_t *&in) {
int64_t value = 0;
while (bits > 0) {
read_byte_if_empty(in);
if (bits > bits_left_ || bits == 8) {
auto d = (uint8_t)(buffer_ & ((1 << bits_left_) - 1));
value = (value << bits_left_) + (d & 0xFF);
bits -= bits_left_;
bits_left_ = 0;
} else {
auto d = (uint8_t)((((uint8_t)buffer_) >> (bits_left_ - bits)) &
((1 << bits) - 1));
value = (value << bits) + (d & 0xFF);
bits_left_ -= bits;
bits = 0;
}
if (bits <= 0 && current_index_ == 0) {
break;
}
}
return value;
}
private:
T stored_value_;
T delta_min_;
T previous_value_;
uint8_t buffer_;
int bits_left_;
int bit_width_;
int write_index_;
int current_index_;
uint8_t *data_array_;
};
#endif // TS_2DIFF_H