1use serde::Serialize;
4#[cfg(feature = "unstable-v2")]
5use usize_cast::IntoUsize as _;
6
7use super::model::{BlobInfo, DecodeHint};
8use crate::decoder::RawStream;
9use crate::{Decoder, GeometryType, MltResult};
10
11#[derive(Debug, Clone, PartialEq, Serialize)]
16#[serde(
17 tag = "kind",
18 rename_all = "camelCase",
19 rename_all_fields = "camelCase"
20)]
21pub enum DecodedBlob {
22 Numbers {
24 values: Vec<f64>,
25 truncated_from: Option<u32>,
27 },
28 #[serde(rename = "bigints")]
30 BigInts {
31 values: Vec<i128>,
32 truncated_from: Option<u32>,
33 },
34 Bools {
35 values: Vec<bool>,
36 truncated_from: Option<u32>,
37 },
38 Enum {
40 values: Vec<u32>,
41 names: Vec<String>,
42 truncated_from: Option<u32>,
43 },
44 Text { value: String },
46 Binary { len: u32 },
48 Error { message: String },
50}
51
52pub fn decode_blob(
58 info: BlobInfo,
59 data: &[u8],
60 max_values: usize,
61 dec: &mut Decoder,
62) -> DecodedBlob {
63 dec.reset_budget();
65 let meta = info.meta;
66 match info.hint {
67 DecodeHint::Presence => match RawStream::new(meta, data).decode_bitvec(dec) {
68 Ok(bits) => bools(bits.iter().by_vals().collect(), max_values),
69 Err(e) => error(&e),
70 },
71 #[cfg(feature = "unstable-v2")]
72 DecodeHint::PresenceCoded(coding) => {
73 use crate::codecs::presence_coding::{Unmetered, read};
74 match read(data, meta.num_values, coding, &mut Unmetered) {
75 Ok((_, bits)) => bools(bits.iter().by_vals().collect(), max_values),
76 Err(e) => DecodedBlob::Error {
77 message: e.to_string(),
78 },
79 }
80 }
81 #[cfg(feature = "unstable-v2")]
82 DecodeHint::PackedBits => {
83 let n = meta.num_values.into_usize();
84 let available = data.len() * 8;
85 if available < n {
86 return DecodedBlob::Error {
87 message: format!("needs {n} bits, got {available}"),
88 };
89 }
90 bools(
91 (0..n).map(|i| data[i / 8] >> (i % 8) & 1 == 1).collect(),
92 max_values,
93 )
94 }
95 DecodeHint::Bool => match RawStream::new(meta, data).decode_bools(dec) {
96 Ok(v) => bools(v, max_values),
97 Err(e) => error(&e),
98 },
99 DecodeHint::GeometryType => geometry_types(
100 RawStream::new(meta, data).decode_ints::<u32>(dec),
101 max_values,
102 ),
103 DecodeHint::I32 => numbers(
104 RawStream::new(meta, data).decode_ints::<i32>(dec),
105 max_values,
106 ),
107 DecodeHint::U32 => numbers(
108 RawStream::new(meta, data).decode_ints::<u32>(dec),
109 max_values,
110 ),
111 DecodeHint::F32 => numbers(
112 RawStream::new(meta, data).decode_floats::<f32>(dec),
113 max_values,
114 ),
115 DecodeHint::F64 => numbers(
116 RawStream::new(meta, data).decode_floats::<f64>(dec),
117 max_values,
118 ),
119 DecodeHint::I64 => bigints(
120 RawStream::new(meta, data).decode_ints::<i64>(dec),
121 max_values,
122 ),
123 DecodeHint::U64 => bigints(
124 RawStream::new(meta, data).decode_ints::<u64>(dec),
125 max_values,
126 ),
127 #[cfg(feature = "unstable-v2")]
128 DecodeHint::Alp(params) => bigints(
129 RawStream::new(meta, data).decode_alp_codes(params, dec),
130 max_values,
131 ),
132 DecodeHint::Bytes => match std::str::from_utf8(data) {
133 Ok(s) => DecodedBlob::Text {
134 value: s.to_string(),
135 },
136 Err(_) => DecodedBlob::Binary {
137 len: count(data.len()),
138 },
139 },
140 }
141}
142
143fn numbers<T: Into<f64>>(res: MltResult<Vec<T>>, max_values: usize) -> DecodedBlob {
144 match res {
145 Ok(v) => {
146 let (v, truncated_from) = cap(v, max_values);
147 DecodedBlob::Numbers {
148 values: v.into_iter().map(Into::into).collect(),
149 truncated_from,
150 }
151 }
152 Err(e) => error(&e),
153 }
154}
155
156fn geometry_types(res: MltResult<Vec<u32>>, max_values: usize) -> DecodedBlob {
157 match res {
158 Ok(v) => {
159 let (values, truncated_from) = cap(v, max_values);
160 let names = values
161 .iter()
162 .map(|&v| {
163 u8::try_from(v)
164 .ok()
165 .and_then(|b| GeometryType::try_from(b).ok())
166 .map_or_else(|| format!("unknown({v})"), |t| t.to_string())
167 })
168 .collect();
169 DecodedBlob::Enum {
170 values,
171 names,
172 truncated_from,
173 }
174 }
175 Err(e) => error(&e),
176 }
177}
178
179fn bigints<T: Into<i128>>(res: MltResult<Vec<T>>, max_values: usize) -> DecodedBlob {
180 match res {
181 Ok(v) => {
182 let (v, truncated_from) = cap(v, max_values);
183 DecodedBlob::BigInts {
184 values: v.into_iter().map(Into::into).collect(),
185 truncated_from,
186 }
187 }
188 Err(e) => error(&e),
189 }
190}
191
192fn bools(v: Vec<bool>, max_values: usize) -> DecodedBlob {
193 let (values, truncated_from) = cap(v, max_values);
194 DecodedBlob::Bools {
195 values,
196 truncated_from,
197 }
198}
199
200fn error(e: &crate::MltError) -> DecodedBlob {
201 DecodedBlob::Error {
202 message: e.to_string(),
203 }
204}
205
206fn cap<T>(mut values: Vec<T>, max_values: usize) -> (Vec<T>, Option<u32>) {
208 let total = values.len();
209 if max_values == 0 || total <= max_values {
210 return (values, None);
211 }
212 values.truncate(max_values);
213 (values, Some(count(total)))
214}
215
216fn count(n: usize) -> u32 {
217 u32::try_from(n).unwrap_or(u32::MAX)
218}