bb_bmap_parser/
xml.rs

1use crate::bmap::{BmapBuilder, BmapBuilderError, HashValue};
2use quick_xml::de::{DeError, from_str};
3use serde::Deserialize;
4use thiserror::Error;
5
6#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
7#[serde(rename_all = "lowercase")]
8#[non_exhaustive]
9pub enum HashType {
10    Sha256,
11}
12
13#[derive(Debug, Deserialize)]
14struct Range {
15    #[serde(rename = "@chksum")]
16    chksum: String,
17    #[serde(rename = "$value")]
18    range: String,
19}
20
21#[derive(Debug, Deserialize)]
22struct BlockMap {
23    #[serde(rename = "Range")]
24    ranges: Vec<Range>,
25}
26
27#[allow(dead_code)]
28#[derive(Debug, Deserialize)]
29struct Bmap {
30    #[serde(rename = "@version")]
31    version: String,
32    #[serde(rename = "ImageSize")]
33    image_size: u64,
34    #[serde(rename = "BlockSize")]
35    block_size: u64,
36    #[serde(rename = "BlocksCount")]
37    blocks_count: u64,
38    #[serde(rename = "MappedBlocksCount")]
39    mapped_blocks_count: u64,
40    #[serde(rename = "ChecksumType")]
41    checksum_type: HashType,
42    #[serde(rename = "BmapFileChecksum")]
43    bmap_file_checksum: String,
44    #[serde(rename = "BlockMap")]
45    block_map: BlockMap,
46}
47
48#[derive(Debug, Error)]
49pub enum XmlError {
50    #[error("Failed to parse bmap XML: {0}")]
51    XmlParsError(#[from] DeError),
52    #[error("Invalid bmap file: {0}")]
53    InvalidFIleError(#[from] BmapBuilderError),
54    #[error("Unknown checksum type: {0}")]
55    UnknownChecksumType(String),
56    #[error("Invalid checksum: {0}")]
57    InvalidChecksum(String),
58}
59
60const fn hexdigit_to_u8(c: u8) -> Option<u8> {
61    match c {
62        b'a'..=b'f' => Some(c - b'a' + 0xa),
63        b'A'..=b'F' => Some(c - b'A' + 0xa),
64        b'0'..=b'9' => Some(c - b'0'),
65        _ => None,
66    }
67}
68
69fn str_to_digest(s: String, digest: &mut [u8]) -> Result<(), XmlError> {
70    let l = digest.len();
71    if s.len() != l * 2 {
72        return Err(XmlError::InvalidChecksum(format!(
73            "No enough chars: {} {}",
74            s,
75            s.len()
76        )));
77    }
78
79    for (i, chunk) in s.as_bytes().chunks(2).enumerate() {
80        let hi = match hexdigit_to_u8(chunk[0]) {
81            Some(v) => v,
82            None => return Err(XmlError::InvalidChecksum(s)),
83        };
84        let lo = match hexdigit_to_u8(chunk[1]) {
85            Some(v) => v,
86            None => return Err(XmlError::InvalidChecksum(s)),
87        };
88        digest[i] = hi << 4 | lo;
89    }
90
91    Ok(())
92}
93
94pub(crate) fn from_xml(xml: &str) -> Result<crate::bmap::Bmap, XmlError> {
95    let b: Bmap = from_str(xml)?;
96    let mut builder = BmapBuilder::default();
97    let hash_type = b.checksum_type;
98    builder
99        .image_size(b.image_size)
100        .block_size(b.block_size)
101        .blocks(b.blocks_count)
102        .checksum_type(hash_type)
103        .mapped_blocks(b.mapped_blocks_count);
104
105    for range in b.block_map.ranges {
106        let mut split = range.range.trim().splitn(2, '-');
107        let start = match split.next() {
108            Some(s) => s.parse().unwrap(),
109            None => unimplemented!("woops"),
110        };
111        let end = match split.next() {
112            Some(s) => s.parse().unwrap(),
113            None => start,
114        };
115
116        let checksum = match hash_type {
117            HashType::Sha256 => {
118                let mut v = [0; 32];
119                str_to_digest(range.chksum, &mut v)?;
120                HashValue::Sha256(v)
121            }
122        };
123        builder.add_block_range(start, end, checksum);
124    }
125
126    builder.build().map_err(std::convert::Into::into)
127}