1use super::Error;
2use crate::misc_utils;
3use crate::multihash;
4use crate::sbx_specs::{ver_to_data_size, Version};
5use crate::time_utils;
6use std;
7use std::fmt;
8
9#[derive(Clone, Debug, PartialEq)]
10pub enum Metadata {
11 FNM(String),
12 SNM(String),
13 FSZ(u64),
14 FDT(i64),
15 SDT(i64),
16 HSH(multihash::HashBytes),
17 RSD(u8),
18 RSP(u8),
19}
20
21impl fmt::Display for Metadata {
22 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
23 use Metadata::*;
24
25 match self {
26 FNM(s) => write!(f, "{}", s),
27 SNM(s) => write!(f, "{}", s),
28 FSZ(x) => write!(f, "{}", *x),
29 FDT(x) | SDT(x) => {
30 match (
31 time_utils::i64_secs_to_date_time_string(*x, time_utils::TimeMode::UTC),
32 time_utils::i64_secs_to_date_time_string(*x, time_utils::TimeMode::Local),
33 ) {
34 (Some(u), Some(l)) => write!(f, "{} (UTC) {} (Local)", u, l),
35 _ => write!(f, "Invalid recorded date time"),
36 }
37 }
38 HSH(h) => write!(
39 f,
40 "{} - {}",
41 multihash::hash_type_to_string(h.0),
42 misc_utils::bytes_to_lower_hex_string(&h.1)
43 ),
44 RSD(x) => write!(f, "{}", *x),
45 RSP(x) => write!(f, "{}", *x),
46 }
47 }
48}
49
50pub enum UncheckedMetadata {
51 FNM(Vec<u8>),
52 SNM(Vec<u8>),
53 FSZ(u64),
54 FDT(i64),
55 SDT(i64),
56 HSH(multihash::HashBytes),
57 RSD(u8),
58 RSP(u8),
59}
60
61#[derive(Clone, Copy, Debug, PartialEq)]
62pub enum MetadataID {
63 FNM,
64 SNM,
65 FSZ,
66 FDT,
67 SDT,
68 HSH,
69 RSD,
70 RSP,
71}
72
73static PREAMBLE_LEN: usize = 3 + 1;
74
75fn single_info_size(meta: &Metadata) -> usize {
76 use self::Metadata::*;
77 use std::mem;
78 match *meta {
79 FNM(ref x) | SNM(ref x) => x.len(),
80 FSZ(_) | FDT(_) | SDT(_) => mem::size_of::<u64>(),
81 HSH(ref x) => multihash::specs::Param::new(x.0).total_length(),
82 RSD(_) | RSP(_) => mem::size_of::<u8>(),
83 }
84}
85
86fn single_meta_size(meta: &Metadata) -> usize {
87 PREAMBLE_LEN + single_info_size(meta)
88}
89
90pub fn id_to_bytes(id: MetadataID) -> [u8; 3] {
91 use self::MetadataID::*;
92 match id {
93 FNM => [b'F', b'N', b'M'],
94 SNM => [b'S', b'N', b'M'],
95 FSZ => [b'F', b'S', b'Z'],
96 FDT => [b'F', b'D', b'T'],
97 SDT => [b'S', b'D', b'T'],
98 HSH => [b'H', b'S', b'H'],
99 RSD => [b'R', b'S', b'D'],
100 RSP => [b'R', b'S', b'P'],
101 }
102}
103
104pub fn id_to_str(id: MetadataID) -> &'static str {
105 use self::MetadataID::*;
106 match id {
107 FNM => "FNM",
108 SNM => "SNM",
109 FSZ => "FSZ",
110 FDT => "FDT",
111 SDT => "SDT",
112 HSH => "HSH",
113 RSD => "RSD",
114 RSP => "RSP",
115 }
116}
117
118pub fn meta_to_id(meta: &Metadata) -> MetadataID {
119 match *meta {
120 Metadata::FNM(_) => MetadataID::FNM,
121 Metadata::SNM(_) => MetadataID::SNM,
122 Metadata::FSZ(_) => MetadataID::FSZ,
123 Metadata::FDT(_) => MetadataID::FDT,
124 Metadata::SDT(_) => MetadataID::SDT,
125 Metadata::HSH(_) => MetadataID::HSH,
126 Metadata::RSD(_) => MetadataID::RSD,
127 Metadata::RSP(_) => MetadataID::RSP,
128 }
129}
130
131fn single_to_bytes(meta: &Metadata, buffer: &mut [u8]) -> Result<usize, ()> {
132 let total_size = single_meta_size(meta);
133 let info_size = single_info_size(meta);
134
135 if buffer.len() < total_size {
136 return Err(());
137 }
138
139 use self::Metadata::*;
140
141 let id = id_to_bytes(meta_to_id(meta));
143 for i in 0..id.len() {
144 buffer[i] = id[i];
145 }
146
147 buffer[3] = info_size as u8;
149
150 let dst = &mut buffer[PREAMBLE_LEN..PREAMBLE_LEN + info_size];
151
152 match *meta {
154 FNM(ref x) | SNM(ref x) => {
155 dst.copy_from_slice(x.as_bytes());
156 }
157 FSZ(x) => {
158 let be_bytes: [u8; 8] = unsafe { std::mem::transmute::<u64, [u8; 8]>(x.to_be()) };
159 dst.copy_from_slice(&be_bytes);
160 }
161 FDT(x) | SDT(x) => {
162 let be_bytes: [u8; 8] = unsafe { std::mem::transmute::<i64, [u8; 8]>(x.to_be()) };
163 dst.copy_from_slice(&be_bytes);
164 }
165 HSH(ref x) => {
166 multihash::hash_bytes_to_bytes(x, dst);
167 }
168 RSD(x) | RSP(x) => {
169 dst[0] = x;
170 }
171 }
172
173 Ok(total_size)
174}
175
176pub fn to_bytes(meta: &[Metadata], buffer: &mut [u8]) -> Result<(), Error> {
177 let mut cur_pos = 0;
178 for m in meta.iter() {
179 let size_written = match single_to_bytes(m, &mut buffer[cur_pos..]) {
180 Ok(x) => x,
181 Err(()) => {
182 return Err(Error::TooMuchMetadata(meta.to_vec()));
183 }
184 };
185
186 cur_pos += size_written;
187 }
188
189 for i in cur_pos..buffer.len() {
191 buffer[i] = 0x1A;
192 }
193
194 Ok(())
195}
196
197pub fn make_too_much_meta_err_string(version: Version, meta: &[Metadata]) -> String {
198 let msg = make_distribution_string(version, meta);
199
200 format!("Too much metadata, distribution :\n{}", &msg)
201}
202
203pub fn make_distribution_string(version: Version, metas: &[Metadata]) -> String {
204 let mut string = String::with_capacity(1000);
205 string.push_str("| ID | Info length | Total length |\n");
206
207 let mut overall_total = 0;
208 let max_size = ver_to_data_size(version);
209
210 for i in 0..metas.len() {
211 let id_str = id_to_str(meta_to_id(&metas[i]));
212 let total_size = single_meta_size(&metas[i]);
213 let info_size = single_info_size(&metas[i]);
214
215 overall_total += total_size;
216
217 string.push_str(&format!(
218 "| {} | {:6} | {:6} |\n",
219 id_str, info_size, total_size
220 ));
221 }
222 string.push_str("\n");
223 string.push_str(&format!("Overall total length : {:6}\n", overall_total));
224 string.push_str(&format!("Maximum total length : {:6}", max_size));
225 string
226}
227
228mod parsers {
229 use super::super::super::misc_utils;
230 use super::super::super::multihash::parsers::multihash_w_len_p;
231 use super::UncheckedMetadata;
232 use super::UncheckedMetadata::*;
233
234 use nom::number::complete::be_i64;
235 use nom::number::complete::be_u64;
236 use nom::number::complete::be_u8;
237
238 macro_rules! make_meta_parser {
239 (
240 $name:ident, $id:expr, $constructor:path
241 => num, $n_must_be:expr, $res_parser:ident
242 ) => {
243 named!(
244 $name<UncheckedMetadata>,
245 do_parse!(
246 _id: tag!($id)
247 >> n: be_u8
248 >> res: map_opt!(cond!(n >= 1 && n == $n_must_be, $res_parser), move |x| x)
249 >> ($constructor(res))
250 )
251 );
252 };
253 (
254 $name:ident, $id:expr, $constructor:path => str
255 ) => {
256 named!(
257 $name<UncheckedMetadata>,
258 do_parse!(
259 tag!($id)
260 >> n: be_u8
261 >> res: map_opt!(cond!(n >= 1, take!(n)), move |x| x)
262 >> ($constructor(misc_utils::slice_to_vec(res)))
263 )
264 );
265 };
266 }
267
268 make_meta_parser!(fnm_p, b"FNM", FNM => str);
269 make_meta_parser!(snm_p, b"SNM", SNM => str);
270 make_meta_parser!(fsz_p, b"FSZ", FSZ => num, 8, be_u64);
271 make_meta_parser!(fdt_p, b"FDT", FDT => num, 8, be_i64);
272 make_meta_parser!(sdt_p, b"SDT", SDT => num, 8, be_i64);
273 make_meta_parser!(rsd_p, b"RSD", RSD => num, 1, be_u8);
274 make_meta_parser!(rsp_p, b"RSP", RSP => num, 1, be_u8);
275
276 named!(
277 hsh_p<UncheckedMetadata>,
278 do_parse!(_id: tag!(b"HSH") >> res: multihash_w_len_p >> (HSH(res)))
279 );
280
281 named!(pub meta_p <Vec<UncheckedMetadata>>,
282 many0!(
283 alt!(
284 complete!(fnm_p)
285 | complete!(snm_p)
286 | complete!(fsz_p)
287 | complete!(fdt_p)
288 | complete!(sdt_p)
289 | complete!(hsh_p)
290 | complete!(rsd_p)
291 | complete!(rsp_p)
292 )
293 )
294 );
295}
296
297pub fn filter_invalid_metadata(input: Vec<UncheckedMetadata>) -> Vec<Metadata> {
298 use self::UncheckedMetadata::*;
299 let mut res = Vec::with_capacity(input.len());
300
301 let mut rsd: Option<usize> = None;
302 let mut rsp: Option<usize> = None;
303
304 for meta in input.into_iter() {
305 let possibly_push: Option<Metadata> = match meta {
306 FNM(x) => match String::from_utf8(x) {
307 Ok(x) => Some(Metadata::FNM(x)),
308 Err(_) => None,
309 },
310 SNM(x) => match String::from_utf8(x) {
311 Ok(x) => Some(Metadata::SNM(x)),
312 Err(_) => None,
313 },
314 FSZ(x) => Some(Metadata::FSZ(x)),
315 FDT(x) => Some(Metadata::FDT(x)),
316 SDT(x) => Some(Metadata::SDT(x)),
317 HSH(h) => Some(Metadata::HSH(h)),
318 RSD(d) => {
319 if 1 <= d {
320 if let None = rsd {
322 rsd = Some(d as usize);
323 }
324 Some(Metadata::RSD(d))
325 } else {
326 None
327 }
328 }
329 RSP(p) => {
330 if 1 <= p {
331 if let None = rsp {
333 rsp = Some(p as usize);
334 }
335 Some(Metadata::RSP(p))
336 } else {
337 None
338 }
339 }
340 };
341
342 match possibly_push {
343 None => {}
344 Some(x) => res.push(x),
345 }
346 }
347
348 let res = match (rsd, rsp) {
349 (Some(d), Some(p)) if d + p > 256 => {
350 res.into_iter()
352 .filter(|x| meta_to_id(x) != MetadataID::RSD && meta_to_id(x) != MetadataID::RSP)
353 .collect()
354 }
355 (..) => res,
356 };
357
358 res
359}
360
361pub fn from_bytes(bytes: &[u8]) -> Result<Vec<Metadata>, Error> {
362 match parsers::meta_p(bytes) {
363 Ok((_, res)) => Ok(filter_invalid_metadata(res)),
364 _ => Err(Error::ParseError),
365 }
366}
367
368pub fn get_meta_ref_by_id(metas: &[Metadata], id: MetadataID) -> Option<&Metadata> {
369 for m in metas.iter() {
370 if meta_to_id(m) == id {
371 return Some(m);
372 }
373 }
374 None
375}
376
377pub fn get_meta_ref_mut_by_id(metas: &mut [Metadata], id: MetadataID) -> Option<&mut Metadata> {
378 for m in metas.iter_mut() {
379 if meta_to_id(m) == id {
380 return Some(m);
381 }
382 }
383
384 None
385}