forked from vortex-data/vortex
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdivisible_int.rs
More file actions
130 lines (109 loc) · 3.75 KB
/
divisible_int.rs
File metadata and controls
130 lines (109 loc) · 3.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: Copyright the Vortex contributors
//! A test extension type representing unsigned integers divisible by a given divisor.
use std::fmt;
use vortex_error::VortexResult;
use vortex_error::vortex_bail;
use vortex_error::vortex_ensure;
use crate::dtype::DType;
use crate::dtype::PType;
use crate::dtype::extension::ExtDType;
use crate::dtype::extension::ExtId;
use crate::dtype::extension::ExtVTable;
use crate::scalar::ScalarValue;
/// The divisor stored as extension metadata.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Divisor(pub u64);
impl fmt::Display for Divisor {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "divisible by {}", self.0)
}
}
/// Extension type for unsigned integers that must be divisible by the metadata divisor.
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct DivisibleInt;
impl ExtVTable for DivisibleInt {
type Metadata = Divisor;
type NativeValue<'a> = u64;
fn id(&self) -> ExtId {
ExtId::new("test.divisible_int")
}
fn serialize_metadata(&self, metadata: &Self::Metadata) -> VortexResult<Vec<u8>> {
Ok(metadata.0.to_le_bytes().to_vec())
}
fn deserialize_metadata(&self, data: &[u8]) -> VortexResult<Self::Metadata> {
vortex_ensure!(data.len() == 8, "divisible int metadata must be 8 bytes");
let bytes: [u8; 8] = data
.try_into()
.map_err(|_| vortex_error::vortex_err!("divisible int metadata must be 8 bytes"))?;
let n = u64::from_le_bytes(bytes);
vortex_ensure!(n > 0, "divisor must be greater than 0");
Ok(Divisor(n))
}
fn validate_dtype(ext_dtype: &ExtDType<Self>) -> VortexResult<()> {
vortex_ensure!(
matches!(ext_dtype.storage_dtype(), DType::Primitive(PType::U64, _)),
"divisible int storage dtype must be u64"
);
Ok(())
}
fn unpack_native<'a>(
ext_dtype: &'a ExtDType<Self>,
storage_value: &'a ScalarValue,
) -> VortexResult<Self::NativeValue<'a>> {
let value = storage_value.as_primitive().cast::<u64>()?;
let metadata = ext_dtype.metadata();
if value % metadata.0 != 0 {
vortex_bail!("{} is not divisible by {}", value, metadata.0);
}
Ok(value)
}
}
#[cfg(test)]
mod tests {
use vortex_error::VortexResult;
use super::DivisibleInt;
use super::Divisor;
use crate::dtype::DType;
use crate::dtype::Nullability;
use crate::dtype::PType;
use crate::dtype::extension::ExtDType;
use crate::dtype::extension::ExtVTable;
#[test]
fn metadata_roundtrip() -> VortexResult<()> {
let vtable = DivisibleInt;
let divisor = Divisor(42);
let bytes = vtable.serialize_metadata(&divisor)?;
let decoded = vtable.deserialize_metadata(&bytes)?;
assert_eq!(decoded, divisor);
Ok(())
}
#[test]
fn rejects_zero_divisor() {
let vtable = DivisibleInt;
let bytes = 0u64.to_le_bytes();
assert!(vtable.deserialize_metadata(&bytes).is_err());
}
#[test]
fn rejects_wrong_storage_dtype() {
let divisor = Divisor(10);
assert!(
ExtDType::<DivisibleInt>::try_new(
divisor,
DType::Primitive(PType::I32, Nullability::NonNullable)
)
.is_err()
);
assert!(
ExtDType::<DivisibleInt>::try_new(divisor, DType::Utf8(Nullability::NonNullable))
.is_err()
);
assert!(
ExtDType::<DivisibleInt>::try_new(
divisor,
DType::Primitive(PType::U64, Nullability::NonNullable)
)
.is_ok()
);
}
}