use std::marker::PhantomData;
use arrow::datatypes::{ArrowTimestampType, TimeUnit};
use bytes::Bytes;
use snafu::ensure;
use crate::{
encoding::{PrimitiveValueDecoder, PrimitiveValueEncoder},
error::{DecodeTimestampSnafu, EncodeTimestampSnafu, Result},
memory::EstimateMemory,
};
const NANOSECONDS_IN_SECOND: i64 = 1_000_000_000;
pub struct TimestampEncoder<T: ArrowTimestampType> {
base_from_epoch: i64,
data: Box<dyn PrimitiveValueEncoder<i64>>,
secondary: Box<dyn PrimitiveValueEncoder<i64>>,
_marker: PhantomData<T>,
}
impl<T: ArrowTimestampType> TimestampEncoder<T> {
pub fn new(
base_from_epoch: i64,
data: Box<dyn PrimitiveValueEncoder<i64>>,
secondary: Box<dyn PrimitiveValueEncoder<i64>>,
) -> Self {
Self {
base_from_epoch,
data,
secondary,
_marker: PhantomData,
}
}
pub fn encode(&mut self, values: &[T::Native]) -> Result<()> {
let (units_per_second, nanoseconds_per_unit) = match T::UNIT {
TimeUnit::Second => (1, NANOSECONDS_IN_SECOND),
TimeUnit::Millisecond => (1_000, 1_000_000),
TimeUnit::Microsecond => (1_000_000, 1_000),
TimeUnit::Nanosecond => (NANOSECONDS_IN_SECOND, 1),
};
let mut data = Vec::with_capacity(values.len());
let mut secondary = Vec::with_capacity(values.len());
for &value in values {
let mut seconds = value.div_euclid(units_per_second);
let nanoseconds = value.rem_euclid(units_per_second) * nanoseconds_per_unit;
ensure!(
seconds != -1 || nanoseconds <= 999_999,
EncodeTimestampSnafu {
value,
time_unit: T::UNIT,
}
);
if seconds < 0 && nanoseconds > 999_999 {
seconds += 1;
}
let Some(seconds) = seconds.checked_sub(self.base_from_epoch) else {
return EncodeTimestampSnafu {
value,
time_unit: T::UNIT,
}
.fail();
};
let encoded_nanoseconds = if nanoseconds == 0 {
0
} else if nanoseconds % 100 != 0 {
nanoseconds << 3
} else {
let mut nanoseconds = nanoseconds / 100;
let mut trailing = 1;
while nanoseconds % 10 == 0 && trailing < 7 {
nanoseconds /= 10;
trailing += 1;
}
(nanoseconds << 3) | trailing
};
data.push(seconds);
secondary.push(encoded_nanoseconds);
}
self.data.write_slice(&data);
self.secondary.write_slice(&secondary);
Ok(())
}
pub fn take_inner(&mut self) -> (Bytes, Bytes) {
(self.data.take_inner(), self.secondary.take_inner())
}
}
impl<T: ArrowTimestampType> EstimateMemory for TimestampEncoder<T> {
fn estimate_memory_size(&self) -> usize {
self.data.estimate_memory_size() + self.secondary.estimate_memory_size()
}
}
pub struct TimestampDecoder<T: ArrowTimestampType> {
base_from_epoch: i64,
data: Box<dyn PrimitiveValueDecoder<i64> + Send>,
secondary: Box<dyn PrimitiveValueDecoder<i64> + Send>,
_marker: PhantomData<T>,
}
impl<T: ArrowTimestampType> TimestampDecoder<T> {
pub fn new(
base_from_epoch: i64,
data: Box<dyn PrimitiveValueDecoder<i64> + Send>,
secondary: Box<dyn PrimitiveValueDecoder<i64> + Send>,
) -> Self {
Self {
base_from_epoch,
data,
secondary,
_marker: PhantomData,
}
}
}
impl<T: ArrowTimestampType> PrimitiveValueDecoder<T::Native> for TimestampDecoder<T> {
fn skip(&mut self, n: usize) -> Result<()> {
self.data.skip(n)?;
self.secondary.skip(n)?;
Ok(())
}
fn decode(&mut self, out: &mut [T::Native]) -> Result<()> {
let mut data = vec![0; out.len()];
let mut secondary = vec![0; out.len()];
self.data.decode(&mut data)?;
self.secondary.decode(&mut secondary)?;
for (index, (&seconds_since_orc_base, &nanoseconds)) in
data.iter().zip(secondary.iter()).enumerate()
{
out[index] =
decode_timestamp::<T>(self.base_from_epoch, seconds_since_orc_base, nanoseconds)?;
}
Ok(())
}
}
pub struct TimestampNanosecondAsDecimalDecoder {
base_from_epoch: i64,
data: Box<dyn PrimitiveValueDecoder<i64> + Send>,
secondary: Box<dyn PrimitiveValueDecoder<i64> + Send>,
}
impl TimestampNanosecondAsDecimalDecoder {
pub fn new(
base_from_epoch: i64,
data: Box<dyn PrimitiveValueDecoder<i64> + Send>,
secondary: Box<dyn PrimitiveValueDecoder<i64> + Send>,
) -> Self {
Self {
base_from_epoch,
data,
secondary,
}
}
}
impl PrimitiveValueDecoder<i128> for TimestampNanosecondAsDecimalDecoder {
fn skip(&mut self, n: usize) -> Result<()> {
self.data.skip(n)?;
self.secondary.skip(n)?;
Ok(())
}
fn decode(&mut self, out: &mut [i128]) -> Result<()> {
let mut data = vec![0; out.len()];
let mut secondary = vec![0; out.len()];
self.data.decode(&mut data)?;
self.secondary.decode(&mut secondary)?;
for (index, (&seconds_since_orc_base, &nanoseconds)) in
data.iter().zip(secondary.iter()).enumerate()
{
out[index] =
decode_timestamp_as_i128(self.base_from_epoch, seconds_since_orc_base, nanoseconds);
}
Ok(())
}
}
fn decode(base: i64, seconds_since_orc_base: i64, nanoseconds: i64) -> (i128, i64, u64) {
let data = seconds_since_orc_base;
let mut nanoseconds = nanoseconds as u64;
let zeros = nanoseconds & 0x7;
nanoseconds >>= 3;
if zeros != 0 {
nanoseconds *= 10_u64.pow(zeros as u32 + 1);
}
let seconds_since_epoch = data + base;
let seconds = if seconds_since_epoch < 0 && nanoseconds > 999_999 {
seconds_since_epoch - 1
} else {
seconds_since_epoch
};
let nanoseconds_since_epoch =
(seconds as i128 * NANOSECONDS_IN_SECOND as i128) + (nanoseconds as i128);
(nanoseconds_since_epoch, seconds, nanoseconds)
}
fn decode_timestamp<T: ArrowTimestampType>(
base: i64,
seconds_since_orc_base: i64,
nanoseconds: i64,
) -> Result<i64> {
let (nanoseconds_since_epoch, seconds, nanoseconds) =
decode(base, seconds_since_orc_base, nanoseconds);
let nanoseconds_in_timeunit = match T::UNIT {
TimeUnit::Second => 1_000_000_000,
TimeUnit::Millisecond => 1_000_000,
TimeUnit::Microsecond => 1_000,
TimeUnit::Nanosecond => 1,
};
ensure!(
nanoseconds_since_epoch % nanoseconds_in_timeunit == 0,
DecodeTimestampSnafu {
seconds,
nanoseconds,
to_time_unit: T::UNIT,
}
);
let num_since_epoch = (nanoseconds_since_epoch / nanoseconds_in_timeunit)
.try_into()
.or_else(|_| {
DecodeTimestampSnafu {
seconds,
nanoseconds,
to_time_unit: T::UNIT,
}
.fail()
})?;
Ok(num_since_epoch)
}
fn decode_timestamp_as_i128(base: i64, seconds_since_orc_base: i64, nanoseconds: i64) -> i128 {
let (nanoseconds_since_epoch, _, _) = decode(base, seconds_since_orc_base, nanoseconds);
nanoseconds_since_epoch
}
#[cfg(test)]
mod tests {
use std::io::Cursor;
use arrow::datatypes::{
TimestampMicrosecondType, TimestampMillisecondType, TimestampNanosecondType,
TimestampSecondType,
};
use crate::encoding::integer::{
rle_v2::{RleV2Decoder, RleV2Encoder},
SignedEncoding, UnsignedEncoding,
};
use super::*;
const ORC_EPOCH_SECONDS: i64 = 1_420_070_400;
fn encoder<T: ArrowTimestampType>() -> TimestampEncoder<T> {
TimestampEncoder::new(
ORC_EPOCH_SECONDS,
Box::new(RleV2Encoder::<i64, SignedEncoding>::new()),
Box::new(RleV2Encoder::<i64, UnsignedEncoding>::new()),
)
}
fn roundtrip<T: ArrowTimestampType>(values: &[i64]) {
let mut encoder = encoder::<T>();
encoder.encode(values).unwrap();
let (data, secondary) = encoder.take_inner();
let data = Box::new(RleV2Decoder::<i64, _, SignedEncoding>::new(Cursor::new(
data,
)));
let secondary = Box::new(RleV2Decoder::<i64, _, UnsignedEncoding>::new(Cursor::new(
secondary,
)));
let mut decoder = TimestampDecoder::<T>::new(ORC_EPOCH_SECONDS, data, secondary);
let mut actual = vec![0; values.len()];
decoder.decode(&mut actual).unwrap();
assert_eq!(values, actual);
}
#[test]
fn test_timestamp_encoder_roundtrip() {
roundtrip::<TimestampSecondType>(&[-2, -1, 0, 1, 2, 1_420_070_400]);
roundtrip::<TimestampMillisecondType>(&[-2_001, -2_000, -1_999, -1_000, 0, 1_001, 1_234]);
roundtrip::<TimestampMicrosecondType>(&[
-2_000_001, -1_001_001, -1_000_000, -999_999, 0, 1_001_001, 1_234_567,
]);
roundtrip::<TimestampNanosecondType>(&[
-2_000_000_001,
-1_001_001_001,
-1_000_000_000,
-999_999_999,
0,
1_001_001_001,
1_234_567_890,
]);
}
#[test]
fn test_timestamp_encoder_rejects_orc_epoch_overflow() {
let mut encoder = encoder::<TimestampSecondType>();
assert!(encoder.encode(&[i64::MIN]).is_err());
}
#[test]
fn test_timestamp_encoder_rejects_orc_763_gap() {
let mut milliseconds = encoder::<TimestampMillisecondType>();
assert!(milliseconds.encode(&[-1_000]).is_ok());
assert!(milliseconds.encode(&[-999]).is_err());
assert!(milliseconds.encode(&[-1]).is_err());
assert!(milliseconds.encode(&[0]).is_ok());
let mut microseconds = encoder::<TimestampMicrosecondType>();
assert!(microseconds.encode(&[-999_001]).is_ok());
assert!(microseconds.encode(&[-999_000]).is_err());
let mut nanoseconds = encoder::<TimestampNanosecondType>();
assert!(nanoseconds.encode(&[-999_000_001]).is_ok());
assert!(nanoseconds.encode(&[-999_000_000]).is_err());
}
}