blkar_lib/sbx_block/
metadata.rs

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