存储对生存时间不够长的结构中的基础缓冲区的引用



我正试图使用quick_XML在Rust中编写一个增量XML解析器。

有些XML文件无法放入内存(在我的笔记本电脑上(,所以我试图只将每个文件的相关块存储在Vec<u8>的缓冲区中。

Vec<u8>的每个文件块中,我想将借用存储到某个结构Data中的切片

quick_xml提供了一个read_event方法,该方法附加到缓冲区并返回quick_xml::events::Event(一个包含从缓冲区借用的buf: Cow<'a, [u8]>字段的结构的枚举(

本质上,我想获取Event引用的数据,并将其存储在我的Data结构中。

然而,借用检查器有心脏病发作,因为Event只适用于对read_event的调用,而我正试图保留一个与缓冲区中的数据一样长的引用。

下面的代码是我试图在上面描述的内容的实现。我可以从Event获得一些帮助来存储对底层buf的借用吗?

use quick_xml::events::Event;
use quick_xml::Reader;
const XML: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
<RUN_SET xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<RUN xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" alias="HAP1 gene trap unselected control dataset" accession="SRR2034585" center_name="Stanford University">
<IDENTIFIERS>
<PRIMARY_ID>SRR2034585</PRIMARY_ID>
<SUBMITTER_ID namespace="Stanford University">HAP1 gene trap unselected control dataset</SUBMITTER_ID>
</IDENTIFIERS>
<EXPERIMENT_REF accession="SRX1034759"/>
</RUN>
</RUN_SET>
"#;
#[derive(Debug)]
struct Data<'a> {
primary_id: Option<&'a [u8]>,
experiment_ref: Option<&'a [u8]>,
}

fn main() {
let mut buf: Vec<u8> = vec![];
let mut reader = Reader::from_str(XML);
let mut depth = 0;
let mut path: Vec<u8> = vec![];
reader.expand_empty_elements(true);
let mut data = Data { primary_id: None, experiment_ref: None };
loop {
match reader.read_event(&mut buf) {
Ok(Event::Start(ref e)) => {
depth += 1;
path.push(b"/"[0]);
path.append(&mut e.name().to_vec());
if path == "/RUN_SET/RUN/EXPERIMENT_REF".as_bytes() {
let experiment_ref = // What to put here?
data = Data { experiment_ref, ..data };
}
}
Ok(Event::End(ref e)) => {
depth -= 1;
path.truncate(path.len() - e.name().len() - 1);
}
Ok(Event::Eof) => { break; }
_ => {}
}
if depth == 1 {
println!("{:?}", data);
buf.clear();
path.clear();
}
}

}

调用read_event将导致缓冲区在必要时扩展,这可能会更改其地址,因此任何引用都将无效。具体来说,您正在尝试调用read_event,存储指向缓冲区的引用(data(,然后再次调用read_event,它可以移动缓冲区。

解决这个问题的最好方法似乎是移动/克隆事件名称,这样它的生存期就不会绑定到缓冲区。令人沮丧的是,quick_xml::events::BytesStart<'a>似乎无法直接移动底层Cow<'a, [u8]>,因此我们必须存储BytesStart对象本身,以避免潜在的不必要克隆。

这里有一种方法。我对代码进行了重大更改,以便更准确/有效地完成我认为您的意图:

use quick_xml::events::Event;
use quick_xml::Reader;
const XML: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
<RUN_SET xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<RUN xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" alias="HAP1 gene trap unselected control dataset" accession="SRR2034585" center_name="Stanford University">
<IDENTIFIERS>
<PRIMARY_ID>SRR2034585</PRIMARY_ID>
<SUBMITTER_ID namespace="Stanford University">HAP1 gene trap unselected control dataset</SUBMITTER_ID>
</IDENTIFIERS>
<EXPERIMENT_REF accession="SRX1034759"/>
</RUN>
</RUN_SET>
"#;
#[derive(Debug)]
struct Data<'a> {
primary_id: Option<&'a [u8]>,
experiment_ref: Option<quick_xml::events::BytesStart<'static>>,
}
fn main() {
let target: &[&[u8]] = &[b"RUN_SET", b"RUN", b"EXPERIMENT_REF"];
let mut buf: Vec<u8> = vec![];
let mut reader = Reader::from_str(XML);
let mut depth = 0;
let mut good = 0;
reader.expand_empty_elements(true);
let mut data = Data {
primary_id: None,
experiment_ref: None,
};
loop {
match reader.read_event(&mut buf) {
Ok(Event::Start(e)) => {
if depth == good && target.get(depth) == Some(&e.name()) {
good += 1;
if good == target.len() {
data = Data {
experiment_ref: Some(e.into_owned()),
..data
};
}
}
depth += 1;
}
Ok(Event::End(_)) => {
depth -= 1;
good = good.min(depth);
}
Ok(Event::Eof) => {
buf.clear();
break;
}
_ => {}
}
buf.clear();
}
println!("{:?}", data);
}

最新更新