ferritin_core/columns/
buffer.rs1use std::borrow::Cow;
4
5#[repr(u8)]
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
10pub enum ScalarType {
11 U8 = 1,
12 I8 = 2,
13 U32 = 3,
14 I32 = 4,
15 F32 = 5,
16}
17
18impl ScalarType {
19 pub const fn size_bytes(self) -> usize {
21 match self {
22 ScalarType::U8 | ScalarType::I8 => 1,
23 ScalarType::U32 | ScalarType::I32 | ScalarType::F32 => 4,
24 }
25 }
26
27 pub const fn code(self) -> u8 {
29 self as u8
30 }
31}
32
33pub(crate) trait Scalar: Copy + 'static {
37 fn extend_le(self, out: &mut Vec<u8>);
38}
39
40macro_rules! impl_scalar {
41 ($($t:ty),*) => {$(
42 impl Scalar for $t {
43 fn extend_le(self, out: &mut Vec<u8>) {
44 out.extend_from_slice(&self.to_le_bytes());
45 }
46 }
47 )*};
48}
49impl_scalar!(u8, i8, u32, i32, f32);
50
51pub(crate) fn le_bytes<T: Scalar>(values: &[T]) -> Cow<'_, [u8]> {
54 if cfg!(target_endian = "little") {
55 Cow::Borrowed(unsafe {
59 std::slice::from_raw_parts(values.as_ptr().cast::<u8>(), std::mem::size_of_val(values))
60 })
61 } else {
62 let mut out = Vec::with_capacity(std::mem::size_of_val(values));
63 for &value in values {
64 value.extend_le(&mut out);
65 }
66 Cow::Owned(out)
67 }
68}
69
70#[derive(Clone, Copy, Debug, PartialEq)]
77pub enum ColumnData<'a> {
78 U8(&'a [u8]),
79 I8(&'a [i8]),
80 U32(&'a [u32]),
81 I32(&'a [i32]),
82 F32(&'a [f32]),
83}
84
85impl<'a> ColumnData<'a> {
86 pub fn scalar_type(&self) -> ScalarType {
88 match self {
89 ColumnData::U8(_) => ScalarType::U8,
90 ColumnData::I8(_) => ScalarType::I8,
91 ColumnData::U32(_) => ScalarType::U32,
92 ColumnData::I32(_) => ScalarType::I32,
93 ColumnData::F32(_) => ScalarType::F32,
94 }
95 }
96
97 pub fn len(&self) -> usize {
99 match self {
100 ColumnData::U8(v) => v.len(),
101 ColumnData::I8(v) => v.len(),
102 ColumnData::U32(v) => v.len(),
103 ColumnData::I32(v) => v.len(),
104 ColumnData::F32(v) => v.len(),
105 }
106 }
107
108 pub fn is_empty(&self) -> bool {
110 self.len() == 0
111 }
112
113 pub fn byte_len(&self) -> usize {
115 self.len() * self.scalar_type().size_bytes()
116 }
117
118 pub fn as_ptr(&self) -> *const u8 {
122 match self {
123 ColumnData::U8(v) => v.as_ptr(),
124 ColumnData::I8(v) => v.as_ptr().cast(),
125 ColumnData::U32(v) => v.as_ptr().cast(),
126 ColumnData::I32(v) => v.as_ptr().cast(),
127 ColumnData::F32(v) => v.as_ptr().cast(),
128 }
129 }
130
131 pub fn le_bytes(&self) -> Cow<'a, [u8]> {
133 match *self {
134 ColumnData::U8(v) => Cow::Borrowed(v),
135 ColumnData::I8(v) => le_bytes(v),
136 ColumnData::U32(v) => le_bytes(v),
137 ColumnData::I32(v) => le_bytes(v),
138 ColumnData::F32(v) => le_bytes(v),
139 }
140 }
141}
142
143#[derive(Clone, Copy, Debug, PartialEq)]
146pub struct Column<'a> {
147 pub name: &'static str,
149 pub data: ColumnData<'a>,
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156
157 #[test]
158 fn test_le_bytes_matches_to_le_bytes() {
159 let values = [1.5f32, -2.0, 3.25];
160 let expected: Vec<u8> = values.iter().flat_map(|v| v.to_le_bytes()).collect();
161 assert_eq!(&*ColumnData::F32(&values).le_bytes(), expected.as_slice());
162
163 let ints = [-1i32, 7, i32::MIN];
164 let expected: Vec<u8> = ints.iter().flat_map(|v| v.to_le_bytes()).collect();
165 assert_eq!(&*ColumnData::I32(&ints).le_bytes(), expected.as_slice());
166 }
167
168 #[test]
169 fn test_le_bytes_is_zero_copy_on_little_endian() {
170 let values = [1u32, 2, 3];
171 let bytes = ColumnData::U32(&values).le_bytes();
172 if cfg!(target_endian = "little") {
173 assert!(matches!(bytes, Cow::Borrowed(_)));
174 assert_eq!(bytes.as_ptr(), values.as_ptr().cast());
175 }
176 assert_eq!(bytes.len(), 12);
177 }
178
179 #[test]
180 fn test_column_data_metadata() {
181 let values = [0i8, -1, 2, 3];
182 let column = ColumnData::I8(&values);
183 assert_eq!(column.scalar_type(), ScalarType::I8);
184 assert_eq!(column.len(), 4);
185 assert_eq!(column.byte_len(), 4);
186 assert_eq!(column.as_ptr(), values.as_ptr().cast());
187 assert!(ColumnData::F32(&[]).is_empty());
188 }
189}