idx_lib/
lib.rs

1pub use ndarray::ArrayD;
2use ndarray::IxDyn;
3use std::io::{Read, Seek};
4
5#[derive(Debug, Default, Clone, PartialEq)]
6pub enum IdxData {
7    #[default]
8    None,
9    UnsignedByte(u8),
10    SignedByte(i8),
11    Short(i16),
12    Int(i32),
13    Float(f32),
14    Double(f64),
15}
16
17impl std::ops::Add for IdxData {
18    type Output = Self;
19
20    fn add(self, rhs: Self) -> Self::Output {
21        if self != rhs {
22            return Self::None;
23        }
24        match self {
25            Self::None => Self::None,
26            Self::UnsignedByte(val1) => {
27                if let Self::UnsignedByte(val2) = rhs {
28                    Self::UnsignedByte(val1 + val2)
29                } else {
30                    unreachable!();
31                }
32            }
33            Self::SignedByte(val1) => {
34                if let Self::SignedByte(val2) = rhs {
35                    Self::SignedByte(val1 + val2)
36                } else {
37                    unreachable!();
38                }
39            }
40            Self::Short(val1) => {
41                if let Self::Short(val2) = rhs {
42                    Self::Short(val1 + val2)
43                } else {
44                    unreachable!();
45                }
46            }
47            Self::Int(val1) => {
48                if let Self::Int(val2) = rhs {
49                    Self::Int(val1 + val2)
50                } else {
51                    unreachable!();
52                }
53            }
54            Self::Float(val1) => {
55                if let Self::Float(val2) = rhs {
56                    Self::Float(val1 + val2)
57                } else {
58                    unreachable!();
59                }
60            }
61            Self::Double(val1) => {
62                if let Self::Double(val2) = rhs {
63                    Self::Double(val1 + val2)
64                } else {
65                    unreachable!();
66                }
67            }
68        }
69    }
70}
71
72impl num_traits::identities::Zero for IdxData {
73    fn zero() -> Self {
74        Self::None
75    }
76
77    fn is_zero(&self) -> bool {
78        self == &Self::None
79    }
80}
81
82#[derive(Debug)]
83enum IdxType {
84    UnsignedByte,
85    SignedByte,
86    Short,
87    Int,
88    Float,
89    Double,
90}
91
92#[derive(Debug, Clone)]
93struct IdxError;
94
95impl std::fmt::Display for IdxError {
96    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
97        write!(f, "Error")
98    }
99}
100
101impl std::error::Error for IdxError {}
102
103fn recurser(
104    idx_source: &mut (impl Read + Seek),
105    data: &mut ArrayD<IdxData>,
106    dimension_sizes: &[usize],
107    data_type: &IdxType,
108    past_idxes: &mut Vec<usize>,
109) -> Result<(), Box<dyn std::error::Error>> {
110    let current_idx = past_idxes.len();
111    // If we are on the last dimension
112    if current_idx == dimension_sizes.len() {
113        // Add the data
114        match data_type {
115            IdxType::UnsignedByte => {
116                let mut buf = [0u8; 1];
117                idx_source.read_exact(&mut buf)?;
118                data[&past_idxes[..]] = IdxData::UnsignedByte(u8::from_be_bytes(buf));
119            }
120            IdxType::SignedByte => {
121                let mut buf = [0u8; 1];
122                idx_source.read_exact(&mut buf)?;
123                data[&past_idxes[..]] = IdxData::SignedByte(i8::from_be_bytes(buf));
124            }
125            IdxType::Short => {
126                let mut buf = [0u8; 2];
127                idx_source.read_exact(&mut buf)?;
128                data[&past_idxes[..]] = IdxData::Short(i16::from_be_bytes(buf));
129            }
130            IdxType::Int => {
131                let mut buf = [0u8; 4];
132                idx_source.read_exact(&mut buf)?;
133                data[&past_idxes[..]] = IdxData::Int(i32::from_be_bytes(buf));
134            }
135            IdxType::Float => {
136                let mut buf = [0u8; 4];
137                idx_source.read_exact(&mut buf)?;
138                data[&past_idxes[..]] = IdxData::Float(f32::from_be_bytes(buf));
139            }
140            IdxType::Double => {
141                let mut buf = [0u8; 8];
142                idx_source.read_exact(&mut buf)?;
143                data[&past_idxes[..]] = IdxData::Double(f64::from_be_bytes(buf));
144            }
145        }
146        Ok(())
147    } else {
148        let my_idx = past_idxes.len();
149        past_idxes.push(0);
150        // Not in the final dimension
151        for i in 0..dimension_sizes[current_idx] {
152            past_idxes[my_idx] = i;
153            recurser(idx_source, data, dimension_sizes, data_type, past_idxes)?;
154        }
155        // Remember to remove our index
156        past_idxes.pop();
157        Ok(())
158    }
159}
160
161fn process_dimensions(
162    idx_source: &mut (impl Read + Seek),
163    data: &mut ArrayD<IdxData>,
164    dimension_sizes: &[usize],
165    data_type: &IdxType,
166) -> Result<(), Box<dyn std::error::Error>> {
167    recurser(idx_source, data, dimension_sizes, data_type, &mut vec![])
168}
169
170pub fn read_idx(
171    idx_source: &mut (impl Read + Seek),
172) -> Result<ArrayD<IdxData>, Box<dyn std::error::Error>> {
173    // First 2 bytes are always 0
174    idx_source.seek_relative(2)?;
175
176    // Data in idx is stored in big endian format
177    let mut data_type_buf = [0u8; 1];
178    let mut dimension_count_buf = [0u8; 1];
179
180    idx_source.read_exact(&mut data_type_buf)?;
181    idx_source.read_exact(&mut dimension_count_buf)?;
182
183    let data_type = match u8::from_be_bytes(data_type_buf) {
184        0x08u8 => IdxType::UnsignedByte,
185        0x09u8 => IdxType::SignedByte,
186        0x0Bu8 => IdxType::Short,
187        0x0Cu8 => IdxType::Int,
188        0x0Du8 => IdxType::Float,
189        0x0Eu8 => IdxType::Double,
190        _ => return Err(IdxError.into()),
191    };
192    let dimension_count = u8::from_be_bytes(dimension_count_buf);
193
194    let mut dimension_sizes = Vec::with_capacity(dimension_count as usize);
195
196    for _ in 0..dimension_count {
197        let mut dimension_size_buf = [0u8; 4];
198        idx_source.read_exact(&mut dimension_size_buf)?;
199        dimension_sizes.push(i32::from_be_bytes(dimension_size_buf) as usize);
200    }
201
202    //println!("Data Type: {:#?}\nDimension Count: {}\nDimension Sizes: {:#?}", data_type, dimension_count, dimension_sizes);
203
204    let mut data: ArrayD<IdxData> = ArrayD::zeros(IxDyn(&dimension_sizes));
205
206    process_dimensions(idx_source, &mut data, &dimension_sizes, &data_type)?;
207
208    Ok(data)
209}