diff --git a/src/cache/xml.rs b/src/cache/xml.rs index cf5fa86..6a05744 100644 --- a/src/cache/xml.rs +++ b/src/cache/xml.rs @@ -1,10 +1,12 @@ -use anyhow::Result; -use base64::encode; -use std::{borrow::Cow, fmt::Display, io::Write}; +use anyhow::{anyhow, Result}; +use base64::{decode, encode}; +use std::io::{BufRead, BufReader}; +use std::io::{Read, Write}; -use quick_xml::events::attributes::Attribute; use quick_xml::events::{BytesEnd, BytesStart, Event}; -use quick_xml::Writer; +use quick_xml::{Reader, Writer}; + +use crate::xml::*; //--------------------------------------- @@ -73,18 +75,6 @@ impl XmlWriter { } } -fn mk_attr_<'a, T: Display>(n: T) -> Cow<'a, [u8]> { - let str = format!("{}", n); - Cow::Owned(str.into_bytes()) -} - -fn mk_attr(key: &[u8], value: T) -> Attribute { - Attribute { - key, - value: mk_attr_(value), - } -} - impl MetadataVisitor for XmlWriter { fn superblock_b(&mut self, sb: &Superblock) -> Result { let tag = b"superblock"; @@ -176,3 +166,129 @@ impl MetadataVisitor for XmlWriter { Ok(Visit::Continue) } } + +//------------------------------------------ + +fn parse_superblock(e: &BytesStart) -> Result { + let mut uuid: Option = None; + let mut block_size: Option = None; + let mut nr_cache_blocks: Option = None; + let mut policy: Option = None; + let mut hint_width: Option = None; + + for a in e.attributes() { + let kv = a.unwrap(); + match kv.key { + b"uuid" => uuid = Some(string_val(&kv)), + b"block_size" => block_size = Some(u32_val(&kv)?), + b"nr_cache_blocks" => nr_cache_blocks = Some(u32_val(&kv)?), + b"policy" => policy = Some(string_val(&kv)), + b"hint_width" => hint_width = Some(u32_val(&kv)?), + _ => return bad_attr("superblock", kv.key), + } + } + + let tag = "cache"; + + Ok(Superblock { + uuid: check_attr(tag, "uuid", uuid)?, + block_size: check_attr(tag, "block_size", block_size)?, + nr_cache_blocks: check_attr(tag, "nr_cache_blocks", nr_cache_blocks)?, + policy: check_attr(tag, "policy", policy)?, + hint_width: check_attr(tag, "hint_width", hint_width)?, + }) +} + +fn parse_mapping(e: &BytesStart) -> Result { + let mut cblock: Option = None; + let mut oblock: Option = None; + let mut dirty: Option = None; + + for a in e.attributes() { + let kv = a.unwrap(); + match kv.key { + b"cache_block" => cblock = Some(u32_val(&kv)?), + b"origin_block" => oblock = Some(u64_val(&kv)?), + b"dirty" => dirty = Some(bool_val(&kv)?), + _ => return bad_attr("mapping", kv.key), + } + } + + let tag = "mapping"; + + Ok(Map { + cblock: check_attr(tag, "cache_block", cblock)?, + oblock: check_attr(tag, "origin_block", oblock)?, + dirty: check_attr(tag, "dirty", dirty)?, + }) +} + +fn parse_hint(e: &BytesStart) -> Result { + let mut cblock: Option = None; + let mut data: Option> = None; + + for a in e.attributes() { + let kv = a.unwrap(); + match kv.key { + b"cache_block" => cblock = Some(u32_val(&kv)?), + b"data" => data = Some(decode(bytes_val(&kv))?), + _ => return bad_attr("mapping", kv.key), + } + } + + let tag = "hint"; + + Ok(Hint { + cblock: check_attr(tag, "cache_block", cblock)?, + data: check_attr(tag, "data", data)?, + }) +} + +fn handle_event(reader: &mut Reader, buf: &mut Vec, visitor: &mut M) -> Result +where + R: Read + BufRead, + M: MetadataVisitor, +{ + match reader.read_event(buf) { + Ok(Event::Start(ref e)) => match e.name() { + b"superblock" => visitor.superblock_b(&parse_superblock(e)?), + b"mappings" => visitor.mappings_b(), + b"hints" => visitor.hints_b(), + _ => todo!(), + }, + Ok(Event::End(ref e)) => match e.name() { + b"superblock" => visitor.superblock_e(), + b"mappings" => visitor.mappings_e(), + b"hints" => visitor.hints_e(), + _ => todo!(), + }, + Ok(Event::Empty(ref e)) => match e.name() { + b"mapping" => visitor.mapping(&parse_mapping(e)?), + b"hint" => visitor.hint(&parse_hint(e)?), + _ => todo!(), + }, + Ok(Event::Text(_)) => Ok(Visit::Continue), + Ok(Event::Comment(_)) => Ok(Visit::Continue), + Ok(Event::Eof) => { + visitor.eof()?; + Ok(Visit::Stop) + } + Ok(_) => todo!(), + Err(e) => Err(anyhow!("{:?}", e)), + } +} + +pub fn read(input: R, visitor: &mut M) -> Result<()> +where + R: Read, + M: MetadataVisitor, +{ + let input = BufReader::new(input); + let mut reader = Reader::from_reader(input); + + reader.trim_text(true); + let mut buf = Vec::new(); + + while let Visit::Continue = handle_event(&mut reader, &mut buf, visitor)? {} + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index 0ff3841..8398379 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,3 +27,4 @@ pub mod shrink; pub mod thin; pub mod version; pub mod write_batcher; +pub mod xml; diff --git a/src/thin/xml.rs b/src/thin/xml.rs index 0cdf7b4..a5c35a9 100644 --- a/src/thin/xml.rs +++ b/src/thin/xml.rs @@ -1,10 +1,11 @@ -use anyhow::{anyhow, Result}; -use std::{borrow::Cow, fmt::Display, io::prelude::*, io::BufReader, io::Write}; +use anyhow::Result; +use std::{io::prelude::*, io::BufReader, io::Write}; -use quick_xml::events::attributes::Attribute; use quick_xml::events::{BytesEnd, BytesStart, Event}; use quick_xml::{Reader, Writer}; +use crate::xml::*; + //--------------------------------------- #[derive(Clone)] @@ -72,18 +73,6 @@ impl XmlWriter { } } -fn mk_attr_<'a, T: Display>(n: T) -> Cow<'a, [u8]> { - let str = format!("{}", n); - Cow::Owned(str.into_bytes()) -} - -fn mk_attr(key: &[u8], value: T) -> Attribute { - Attribute { - key, - value: mk_attr_(value), - } -} - const XML_VERSION: u32 = 2; impl MetadataVisitor for XmlWriter { @@ -187,40 +176,6 @@ impl MetadataVisitor for XmlWriter { //--------------------------------------- -// FIXME: nasty unwraps -fn string_val(kv: &Attribute) -> String { - let v = kv.unescaped_value().unwrap(); - let bytes = v.to_vec(); - String::from_utf8(bytes).unwrap() -} - -// FIXME: there's got to be a way of doing this without copying the string -fn u64_val(kv: &Attribute) -> Result { - let n = string_val(kv).parse::()?; - Ok(n) -} - -fn u32_val(kv: &Attribute) -> Result { - let n = string_val(kv).parse::()?; - Ok(n) -} - -fn bad_attr(_tag: &str, _attr: &[u8]) -> Result { - todo!(); -} - -fn missing_attr(tag: &str, attr: &str) -> Result { - let msg = format!("missing attribute '{}' for tag '{}", attr, tag); - Err(anyhow!(msg)) -} - -fn check_attr(tag: &str, name: &str, maybe_v: Option) -> Result { - match maybe_v { - None => missing_attr(tag, name), - Some(v) => Ok(v), - } -} - fn parse_superblock(e: &BytesStart) -> Result { let mut uuid: Option = None; let mut time: Option = None; diff --git a/src/xml.rs b/src/xml.rs new file mode 100644 index 0000000..577c0e3 --- /dev/null +++ b/src/xml.rs @@ -0,0 +1,63 @@ +use anyhow::anyhow; +use quick_xml::events::attributes::Attribute; +use std::borrow::Cow; +use std::fmt::Display; + +//------------------------------------------ + +pub fn bytes_val<'a>(kv: &'a Attribute) -> Cow<'a, [u8]> { + kv.unescaped_value().unwrap() +} + +// FIXME: nasty unwraps +pub fn string_val(kv: &Attribute) -> String { + let v = kv.unescaped_value().unwrap(); + let bytes = v.to_vec(); + String::from_utf8(bytes).unwrap() +} + +// FIXME: there's got to be a way of doing this without copying the string +pub fn u64_val(kv: &Attribute) -> anyhow::Result { + let n = string_val(kv).parse::()?; + Ok(n) +} + +pub fn u32_val(kv: &Attribute) -> anyhow::Result { + let n = string_val(kv).parse::()?; + Ok(n) +} + +pub fn bool_val(kv: &Attribute) -> anyhow::Result { + let n = string_val(kv).parse::()?; + Ok(n) +} + +pub fn bad_attr(_tag: &str, _attr: &[u8]) -> anyhow::Result { + todo!(); +} + +pub fn check_attr(tag: &str, name: &str, maybe_v: Option) -> anyhow::Result { + match maybe_v { + None => missing_attr(tag, name), + Some(v) => Ok(v), + } +} + +fn missing_attr(tag: &str, attr: &str) -> anyhow::Result { + let msg = format!("missing attribute '{}' for tag '{}", attr, tag); + Err(anyhow!(msg)) +} + +pub fn mk_attr(key: &[u8], value: T) -> Attribute { + Attribute { + key, + value: mk_attr_(value), + } +} + +fn mk_attr_<'a, T: Display>(n: T) -> Cow<'a, [u8]> { + let str = format!("{}", n); + Cow::Owned(str.into_bytes()) +} + +//------------------------------------------