#![feature(test)]
extern crate encoding;
#[macro_use] extern crate gstuff;
#[macro_use] extern crate lazy_static;
extern crate libc;
extern crate memmap;
extern crate parking_lot;
extern crate test;
use gstuff::any_to_str;
use libc::c_void;
use memmap::Mmap;
use parking_lot::Mutex;
use std::error::Error;
use std::ffi::CStr;
use std::fs;
use std::fmt::{self, Display};
use std::mem::transmute;
use std::panic::catch_unwind;
use std::path::Path;
use std::ptr::null;
use std::rc::Rc;
pub mod sys {
use libc::{c_int, c_char, c_void, wchar_t, size_t};
use std::default::Default;
use std::mem::zeroed;
pub enum VTDGen {}
pub enum VTDNav {}
pub enum AutoPilot {}
pub type UByte = u8;
pub type UCSChar = wchar_t;
#[derive(Copy, Clone, PartialEq)]
#[repr(u32)]
#[derive(Debug)]
pub enum Bool {FALSE = 0, TRUE = 1}
pub type Boolean = Bool;
#[derive(Copy, Clone)]
#[repr(u32)]
#[derive(Debug)]
pub enum Direction {
Root = 0,
Parent = 1,
FirstChild = 2,
LastChild = 3,
NextSibling = 4,
PrevSibling = 5}
#[derive(Copy, Clone)]
#[repr(u32)]
#[derive(Debug)]
pub enum ExceptionType {
OutOfMem = 0,
InvalidArgument = 1,
ArrayOutOfBound = 2,
ParseException = 3,
NavException = 4,
PilotException = 5,
NumberFormatException = 6,
XPathParseException = 7,
XPathEvalException = 8,
ModifyException = 9,
IndexWriteException = 10,
IndexReadException = 11,
IoException = 12,
TranscodeException = 13,
OtherException = 14}
#[repr(C)]
#[derive(Copy, Clone)]
#[derive(Debug)]
pub struct VtdException {
pub et: ExceptionType,
pub subtype: c_int,
pub msg: *const c_char,
pub sub_msg: *const c_char}
impl Default for VtdException {
fn default() -> Self {unsafe {zeroed()}}}
#[link(name="vtdxml")] extern {
pub fn createVTDGen() -> *mut VTDGen;
pub fn freeVTDGen (vg: *mut VTDGen);
pub fn setDoc (vg: *mut VTDGen, byteArray: *const UByte, arrayLen: c_int);
pub fn parse (vg: *mut VTDGen, ns: Boolean);
pub fn parseFile (vg: *mut VTDGen, ns: Boolean, fileName: *const u8) -> Boolean;
pub fn selectLcDepth (vg: *mut VTDGen, d: c_int);
pub fn getNav (vg: *mut VTDGen) -> *mut VTDNav;
pub fn cloneNav_shim (vn: *mut VTDNav) -> *mut VTDNav;
pub fn duplicateNav_shim (vn: *mut VTDNav) -> *mut VTDNav;
pub fn recoverNode_shim (vn: *mut VTDNav, index: c_int);
pub fn freeVTDNav_shim (vn: *mut VTDNav);
pub fn getCurrentDepth (vn: *mut VTDNav) -> c_int;
pub fn getCurrentIndex_shim (vn: *mut VTDNav) -> c_int;
pub fn toElement_shim (vn: *mut VTDNav, direction: Direction) -> Boolean;
pub fn toElement2_shim (vn: *mut VTDNav, direction: Direction, en: *const UCSChar) -> Boolean;
pub fn matchElement (vn: *mut VTDNav, en: *const UCSChar) -> Boolean;
pub fn getAttrCount (vn: *mut VTDNav) -> c_int;
pub fn hasAttr (vn: *mut VTDNav, attrName: *const UCSChar) -> Boolean;
pub fn getAttrVal (vn: *mut VTDNav, attrName: *const UCSChar) -> c_int;
pub fn createAutoPilot (v: *mut VTDNav) -> *mut AutoPilot;
pub fn createAutoPilot2() -> *mut AutoPilot;
pub fn freeAutoPilot (ap: *mut AutoPilot);
pub fn declareXPathNameSpace (ap: *mut AutoPilot, prefix: *const UCSChar, url: *const UCSChar);
pub fn selectXPath (ap: *mut AutoPilot, s: *const UCSChar) -> Boolean;
pub fn apBind (ap: *mut AutoPilot, vn: *mut VTDNav);
pub fn evalXPath (ap: *mut AutoPilot) -> c_int;
pub fn toString (vn: *const VTDNav, index: c_int) -> *mut UCSChar;
pub fn toRawString (vn: *const VTDNav, index: c_int) -> *mut UCSChar;
pub fn getText (vn: *const VTDNav) -> c_int;
pub fn toNormalizedString (vn: *const VTDNav, index: c_int) -> *mut UCSChar;
pub fn vtd_try_catch_shim (cb: extern fn (*mut c_void), dugout: *mut c_void, ex: *mut VtdException) -> c_int;}
pub type Iconv = *mut c_void;
extern {
pub fn iconv_open_shim (tocode: *const u8, fromcode: *const u8) -> Iconv;
pub fn iconv_shim (cd: Iconv, inbuf: *mut *const u8, inbytesleft: *mut size_t,
outbuf: *mut *mut u8, outbytesleft: *mut size_t) -> size_t;
pub fn iconv_close_shim (cd: Iconv) -> c_int;
pub fn is_errno_einval() -> c_int;
pub fn is_errno_e2big() -> c_int;}}
pub mod helpers {
use ::sys::{iconv_open_shim, iconv_shim, iconv_close_shim, is_errno_e2big, UCSChar, Iconv};
use libc::{self, size_t, c_void};
use parking_lot::Mutex;
use std::mem::{size_of, MaybeUninit};
use std::ptr::null;
use std::str::from_utf8_unchecked;
struct IconvSync (Mutex<Iconv>);
unsafe impl Sync for IconvSync {}
impl Drop for IconvSync {
fn drop (&mut self) {
if unsafe {iconv_close_shim (*self.0.lock())} != 0 {panic! ("!iconv_close")}}}
lazy_static! {static ref WCHAR_T_TO_UTF_8: IconvSync = {
let cd = unsafe {iconv_open_shim ("UTF-8\0".as_ptr(), "WCHAR_T\0".as_ptr())};
if cd as size_t == size_t::max_value() {panic! ("!iconv_open")}
IconvSync (Mutex::new (cd))};}
pub fn ucs2string<'a> (sbuf: &'a mut String, ucs: *const UCSChar, free: bool) -> &'a mut String {
sbuf.clear();
if ucs == null() {return sbuf}
let mut wide_len = 0;
loop {
let ch = unsafe {*ucs.offset (wide_len)};
if ch == 0 {break}
wide_len += 1;}
let cd = WCHAR_T_TO_UTF_8.0.lock();
let mut buf: [u8; 1024] = unsafe {MaybeUninit::uninit().assume_init()};
let mut ucs_p: *const u8 = ucs as *const u8;
let mut ucs_len = wide_len as usize * size_of::<UCSChar>();
loop {
let mut buf_p: *mut u8 = buf.as_mut_ptr();
let mut buf_len = 1024;
let rc = unsafe {iconv_shim (*cd, &mut ucs_p, &mut ucs_len, &mut buf_p, &mut buf_len)};
let mut finished = true;
if rc == size_t::max_value() {
if unsafe {is_errno_e2big()} == 1 { finished = false;
} else {panic! ("!iconv")}}
let encoded_len = buf_p as usize - buf.as_ptr() as usize;
let encoded = unsafe {from_utf8_unchecked (&buf[0..encoded_len])};
sbuf.reserve (encoded_len);
sbuf.push_str (encoded);
if finished {break}}
if free {unsafe {libc::free (ucs as *mut c_void)}}
sbuf}
pub fn str2ucs<'a> (buf: &'a mut Vec<UCSChar>, s: &str) -> &'a Vec<UCSChar> {
buf.clear();
buf.reserve (s.len() + 1);
for ch in s.chars() {buf.push (ch as UCSChar)}
buf.push (0); buf}}
#[derive(Debug)]
pub struct VtdError {
pub et: sys::ExceptionType,
pub subtype: i32,
pub msg: String,
pub sub_msg: String}
impl Display for VtdError {
fn fmt (&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {write! (fmt, "{:?}", *self)}}
impl Error for VtdError {
fn description (&self) -> &str {&self.msg}}
lazy_static! {static ref LOCK: Mutex<()> = Mutex::new (());}
struct Dugout<'a> {
closure: &'a mut dyn FnMut() -> Result<(), Box<dyn Error>>,
panic_message: String,
rc: Result<(), Box<dyn Error>>}
pub fn vtd_catch (cb: &mut dyn FnMut() -> Result<(), Box<dyn Error>>) -> Result<(), Box<dyn Error>> {
let mut dugout = Dugout {
closure: cb,
panic_message: String::new(),
rc: Ok(())};
let dugout_p: *mut c_void = unsafe {transmute (&mut dugout)};
let mut ex = sys::VtdException::default();
let lock = LOCK.lock();
let rc = unsafe {::sys::vtd_try_catch_shim (::vtd_xml_try_catch_rust_shim, dugout_p, &mut ex)};
test::black_box (&lock);
if !dugout.panic_message.is_empty() {panic! ("vtd_catch] {}", dugout.panic_message)}
if rc == 0 {
dugout.rc
} else {
let err = VtdError {
et: ex.et,
subtype: ex.subtype as i32,
msg: if ex.msg == null() {String::new()} else {
unsafe {CStr::from_ptr (ex.msg as *mut i8)} .to_string_lossy().into_owned()},
sub_msg: if ex.sub_msg == null() {String::new()} else {
unsafe {CStr::from_ptr (ex.sub_msg as *mut i8)} .to_string_lossy().into_owned()}};
test::black_box (&lock);
Err (Box::new (err))}}
#[derive(Clone)]
pub enum VtdMem {
None,
Vec (Rc<Vec<u8>>),
Mmap (Rc<memmap::Mmap>)}
pub struct VtdGen {
pub vtd_gen: *mut sys::VTDGen,
vtd_mem: VtdMem}
impl VtdGen {
pub fn new() -> VtdGen {
VtdGen {
vtd_gen: unsafe {sys::createVTDGen()},
vtd_mem: VtdMem::None}}
pub fn parse_file<P: AsRef<Path>> (&mut self, ns: bool, path: P) -> Result<(), String> {
use sys::Bool;
let path: &Path = path.as_ref();
let path = try_s! (path.to_str().ok_or (format! ("VtdGen::parse_file] Can't make a str out of {:?}", path)));
let path = try_s! (std::ffi::CString::new (path));
if unsafe {sys::parseFile (self.vtd_gen, if ns {Bool::TRUE} else {Bool::FALSE}, path.as_ptr() as *const u8)} != Bool::TRUE {
return Err (format! ("VtdGen::parse_file] Error parsing {:?}.", path))}
Ok(())}
pub fn parse_mmap<P: AsRef<Path>> (&mut self, ns: bool, path: P) -> Result<(), String> {
use sys::Bool;
let file = try_s! (fs::File::open (path));
let bytes = Rc::new (try_s! (unsafe {Mmap::map (&file)}));
if bytes.len() > i32::max_value() as usize {return ERR! ("Large files ({}) are not supported yet", bytes.len())}
unsafe {sys::setDoc (self.vtd_gen, bytes.as_ptr(), bytes.len() as i32)};
self.vtd_mem = VtdMem::Mmap (bytes);
unsafe {sys::parse (self.vtd_gen, if ns {Bool::TRUE} else {Bool::FALSE})};
Ok(())}
pub fn parse_vec (&mut self, ns: bool, vec: Rc<Vec<u8>>, offset: usize, len: usize) -> Result<(), String> {
use sys::Bool;
{ let slice = &vec[offset .. offset + len];
unsafe {sys::setDoc (self.vtd_gen, slice.as_ptr(), len as i32)}; }
self.vtd_mem = VtdMem::Vec (vec);
unsafe {sys::parse (self.vtd_gen, if ns {Bool::TRUE} else {Bool::FALSE})};
Ok(())}}
impl Drop for VtdGen {
fn drop (self: &mut VtdGen) {
unsafe {sys::freeVTDGen (self.vtd_gen)};
self.vtd_gen = std::ptr::null_mut();}}
#[derive(Debug)]
pub struct VtdNavError<'a> {
direction: sys::Direction,
tag: Option<&'a str>}
impl<'a> std::fmt::Display for VtdNavError<'a> {
fn fmt (&self, fm: &mut std::fmt::Formatter) -> std::fmt::Result {
write! (fm, "Failed to move the VtdNav cursor to {:?}", self.direction)?;
if let Some (tag) = self.tag {write! (fm, " tag '{}'", tag)?}
write! (fm, ".")}}
impl<'a> Error for VtdNavError<'a> {
fn description (&self) -> &str {"Failed to move the VtdNav cursor in the direction specified"}}
#[derive (Copy, Clone, Debug, Eq, PartialEq)]
pub struct VtdNavPosition (libc::c_int);
pub struct VtdChildIter {
pub vtd_nav: *mut sys::VTDNav,
pub tag: Vec<sys::UCSChar>,
pub first: bool}
impl Iterator for VtdChildIter {
type Item = VtdNavPosition;
fn next (&mut self) -> Option<VtdNavPosition> {
let direction = if self.first {self.first = false; sys::Direction::FirstChild} else {sys::Direction::NextSibling};
match unsafe {sys::toElement2_shim (self.vtd_nav, direction, self.tag.as_ptr())} {
sys::Bool::TRUE => Some (VtdNavPosition (unsafe {sys::getCurrentIndex_shim (self.vtd_nav)})),
sys::Bool::FALSE => None}}}
impl Drop for VtdChildIter {
fn drop (&mut self) {
unsafe {sys::freeVTDNav_shim (self.vtd_nav)};
self.vtd_nav = std::ptr::null_mut()}}
pub struct VtdNav {
pub vtd_nav: *mut sys::VTDNav,
pub to_vtd: Vec<sys::UCSChar>,
pub from_vtd: String,
_vtd_mem: VtdMem}
impl VtdNav {
pub fn new (vg: &mut VtdGen) -> VtdNav {
VtdNav {
vtd_nav: unsafe {sys::getNav (vg.vtd_gen)},
to_vtd: Vec::new(),
from_vtd: String::new(),
_vtd_mem: vg.vtd_mem.clone()}}
pub fn raw<'a> (&'a mut self) -> &'a String {
let idx = unsafe {sys::getCurrentIndex_shim (self.vtd_nav)};
helpers::ucs2string (&mut self.from_vtd, unsafe {sys::toRawString (self.vtd_nav, idx)}, true)}
pub fn to_element<'t> (&mut self, direction: sys::Direction) -> Result<&mut VtdNav, VtdNavError<'t>> {
if unsafe {sys::toElement_shim (self.vtd_nav, direction)} != sys::Bool::TRUE {
return Err (VtdNavError {direction: direction, tag: None})}
Ok (self)}
pub fn to_named_element<'t> (&mut self, direction: sys::Direction, tag: &'t str) -> Result<&mut VtdNav, VtdNavError<'t>> {
if unsafe {sys::toElement2_shim (
self.vtd_nav, direction, helpers::str2ucs (&mut self.to_vtd, tag) .as_ptr())} != sys::Bool::TRUE {
return Err (VtdNavError {direction: direction, tag: Some (tag)})}
Ok (self)}
pub fn up (&mut self) -> Result<&mut VtdNav, VtdNavError> {
if unsafe {sys::toElement_shim (self.vtd_nav, sys::Direction::Parent)} != sys::Bool::TRUE {
return Err (VtdNavError {direction: sys::Direction::Parent, tag: None})}
Ok (self)}
pub fn first_child<'t> (&mut self, tag: &'t str) -> Result<&mut VtdNav, VtdNavError<'t>> {
self.to_named_element (sys::Direction::FirstChild, tag)}
pub fn next_sibling<'t> (&mut self, tag: &'t str) -> Result<&mut VtdNav, VtdNavError<'t>> {
self.to_named_element (sys::Direction::NextSibling, tag)}
pub fn root<'t> (&mut self) -> &mut VtdNav {
self.to_element (sys::Direction::Root) .expect ("!root")}
pub fn child_iter (&mut self, tag: &str) -> VtdChildIter {
let idx = self.idx();
let vtd_nav = unsafe {sys::duplicateNav_shim (self.vtd_nav)};
unsafe {sys::recoverNode_shim (vtd_nav, idx.0)};
VtdChildIter {
vtd_nav: vtd_nav,
tag: helpers::str2ucs (&mut self.to_vtd, tag) .clone(),
first: true}}
pub fn raw_attr<'a> (&'a mut self, attr: &str) -> Option<&'a mut String> {
let idx = unsafe {sys::getAttrVal (self.vtd_nav, helpers::str2ucs (&mut self.to_vtd, attr) .as_ptr())};
if idx == -1 {return None}
helpers::ucs2string (&mut self.from_vtd, unsafe {sys::toRawString (self.vtd_nav, idx)}, true);
Some (&mut self.from_vtd)}
pub fn attr<'a> (&'a mut self, attr: &str) -> Option<&'a mut String> {
let idx = unsafe {sys::getAttrVal (self.vtd_nav, helpers::str2ucs (&mut self.to_vtd, attr) .as_ptr())};
if idx == -1 {return None}
helpers::ucs2string (&mut self.from_vtd, unsafe {sys::toString (self.vtd_nav, idx)}, true);
Some (&mut self.from_vtd)}
pub fn text<'a> (&'a mut self) -> Option<&'a mut String> {
let idx = unsafe {sys::getText (self.vtd_nav)};
if idx == -1 {return None}
helpers::ucs2string (&mut self.from_vtd, unsafe {sys::toString (self.vtd_nav, idx)}, true);
Some (&mut self.from_vtd)}
pub fn idx (&self) -> VtdNavPosition {
VtdNavPosition (unsafe {sys::getCurrentIndex_shim (self.vtd_nav)})}
pub fn set_idx (&mut self, idx: VtdNavPosition) -> &mut VtdNav {
unsafe {sys::recoverNode_shim (self.vtd_nav, idx.0)};
self}}
impl std::fmt::Display for VtdNav {
fn fmt (&self, fm: &mut std::fmt::Formatter) -> std::fmt::Result {
let idx = unsafe {sys::getCurrentIndex_shim (self.vtd_nav)};
let mut buf = String::with_capacity (128);
let s = helpers::ucs2string (&mut buf, unsafe {sys::toString (self.vtd_nav, idx)}, true);
write! (fm, "{}", s)}}
impl Drop for VtdNav {
fn drop (self: &mut VtdNav) {
unsafe {sys::freeVTDNav_shim (self.vtd_nav)};
self.vtd_nav = std::ptr::null_mut()}}
#[no_mangle] #[doc(hidden)]
pub extern "C" fn vtd_xml_try_catch_rust_shim (dugout: *mut c_void) {
let catch_rc = catch_unwind (|| {
let dugout: &mut Dugout = unsafe {transmute (dugout)};
dugout.rc = (dugout.closure)();});
if let Err (panic) = catch_rc {
let message = match any_to_str (&*panic) {Some (s) => s, None => "panic in vtd_catch"};
let dugout: &mut Dugout = unsafe {transmute (dugout)};
dugout.panic_message.push_str (message);}}
#[macro_export] macro_rules! vtd_for_children {
($vn: ident, $tag: expr, $default: expr, $code: block) => {{
let mut ret = $default;
let original_position = $vn.idx();
if $vn.first_child ($tag) .is_ok() {
let mut child_position = None;
ret = loop {
if let Some (pos) = child_position {if $vn.set_idx (pos) .next_sibling ($tag) .is_err() {break $default}}
child_position = Some ($vn.idx());
assert! (child_position.is_some()); $code;};}
$vn.set_idx (original_position);
ret}};
($vn: ident, $tag: expr, $code: block) => (vtd_for_children! ($vn, $tag, (), $code))}
#[cfg(test)] mod tests {
use ::sys::*;
use ::helpers::*;
use ::{vtd_catch, VtdGen, VtdNav};
use libc::{self, c_int};
use std;
use std::panic::catch_unwind;
use std::rc::Rc;
use test::Bencher;
#[test] fn str2ucs_test() {
let mut to_ucs = Vec::new();
let mut from_ucs = String::new();
assert_eq! (unsafe {libc::wcslen (str2ucs (&mut to_ucs, "foo") .as_ptr())}, 3);
assert_eq! (ucs2string (&mut from_ucs, str2ucs (&mut to_ucs, "foo") .as_ptr(), false), "foo");}
#[test] fn rss_reader() { vtd_catch (&mut || {
let vg = unsafe {createVTDGen()};
let xml = "\
<?xml version=\"1.0\" encoding=\"UTF-8\"?><rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" \
xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\" \
xmlns:rss=\"http://purl.org/rss/1.0/\" xmlns=\"http://purl.org/rss/1.0/\">\
<item rdf:about=\"http://www.nwfusion.com/news/2004/0614amd.html\">\
<title>AMD readies dual-core Opteron</title>\
<link>http://www.nwfusion.com/news/2004/0614amd.html</link>\
<description>AMD Monday reiterated plans to ship dual-core Opteron processors</description>\
<dc:creator>Jennifer Mears</dc:creator>\
<dc:date>2004-06-14T00:00:00Z</dc:date>\
</item>\
</rdf:RDF>";
unsafe {setDoc (vg, xml.as_ptr(), xml.len() as c_int)};
unsafe {parse (vg, Bool::TRUE)};
let vn = unsafe {getNav (vg)};
unsafe {freeVTDGen (vg)};
let ap = unsafe {createAutoPilot2()};
let mut to_ucs = Vec::new();
let mut from_ucs = String::new();
let ns1 = str2ucs (&mut to_ucs, "ns1") .clone();
let url = str2ucs (&mut to_ucs, "http://purl.org/dc/elements/1.1/") .clone();
unsafe {declareXPathNameSpace (ap, ns1.as_ptr(), url.as_ptr())};
let mut num = 0;
if unsafe {selectXPath (ap, str2ucs (&mut to_ucs, "//ns1:*") .as_ptr())} == Bool::TRUE {
unsafe {apBind (ap, vn)};
let mut result; while {result = unsafe {evalXPath (ap)}; result} != -1 {
let tmp_string = unsafe {toString (vn, result)};
let name = ucs2string (&mut from_ucs, tmp_string, true) .clone();
let t = unsafe {getText (vn)};
let mut text = String::new();
if t != -1 {
let tmp_string = unsafe {toNormalizedString (vn, t)};
text.push_str (&ucs2string (&mut from_ucs, tmp_string, true));}
match {num += 1; num} {
1 => {assert_eq! (name, "dc:creator"); assert_eq! (text, "Jennifer Mears")},
2 => {assert_eq! (name, "dc:date"); assert_eq! (text, "2004-06-14T00:00:00Z")},
x => panic! ("num is {}", x)}}}
assert_eq! (num, 2);
unsafe {freeVTDNav_shim (vn)};
unsafe {freeAutoPilot (ap)};
Ok(())}) .expect ("!rss_reader");}
#[test] fn walk() {
vtd_catch (&mut || {
let xml = "<foo><bar surname=\"Stover\">Smokey</bar></foo>";
let vg = unsafe {createVTDGen()};
unsafe {setDoc (vg, xml.as_ptr(), xml.len() as c_int)};
unsafe {parse (vg, Bool::FALSE)};
let vn = unsafe {getNav (vg)};
unsafe {freeVTDGen (vg)};
let mut to_ucs = Vec::new();
let mut from_ucs = String::new();
assert! (unsafe {toElement2_shim (vn, Direction::FirstChild, str2ucs (&mut to_ucs, "bar") .as_ptr())} == Bool::TRUE);
assert_eq! (ucs2string (&mut from_ucs, unsafe {toString (vn, getCurrentIndex_shim (vn))}, true), "bar");
assert_eq! (ucs2string (&mut from_ucs, unsafe {toRawString (vn, getCurrentIndex_shim (vn))}, true), "bar");
let surname = unsafe {getAttrVal (vn, str2ucs (&mut to_ucs, "surname") .as_ptr())};
assert! (surname != -1);
assert_eq! (ucs2string (&mut from_ucs, unsafe {toString (vn, surname)}, true), "Stover");
assert_eq! (ucs2string (&mut from_ucs, unsafe {toRawString (vn, surname)}, true), "Stover");
let text = unsafe {getText (vn)};
assert! (text != -1);
assert_eq! (ucs2string (&mut from_ucs, unsafe {toString (vn, text)}, true), "Smokey");
unsafe {freeVTDNav_shim (vn)};
Ok(())}) .expect ("!walk");}
#[bench] fn panic (bencher: &mut Bencher) { std::panic::set_hook (Box::new (|_| ())); bencher.iter (|| {
match catch_unwind (|| {
vtd_catch (&mut || panic! ("woot")) .expect ("!vtd_catch");}) {
Ok (_) => panic! ("No panic!"),
Err (panic) => {
let message = ::any_to_str (&*panic) .expect ("!message");
assert_eq! (message, "vtd_catch] woot");}}});
let _ = std::panic::take_hook();}
#[bench] fn error (bencher: &mut Bencher) { bencher.iter (|| {
let rc = vtd_catch (&mut || Err (From::from ("woot")));
let err = rc.err().expect ("!err");
assert_eq! (err.to_string(), "woot");})}
#[bench] fn unicode (bencher: &mut Bencher) {
let xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><foo arg=\"Рок\">Стар</foo>";
vtd_catch (&mut || {
let vg = unsafe {createVTDGen()};
unsafe {setDoc (vg, xml.as_ptr(), xml.len() as c_int)};
unsafe {parse (vg, Bool::FALSE)};
let vn = unsafe {getNav (vg)};
unsafe {freeVTDGen (vg)};
let mut to_ucs = Vec::new();
let mut from_ucs = String::new();
bencher.iter (|| {
let arg = unsafe {getAttrVal (vn, str2ucs (&mut to_ucs, "arg") .as_ptr())};
assert! (arg != -1);
assert_eq! (ucs2string (&mut from_ucs, unsafe {toString (vn, arg)}, true), "Рок");
let text = unsafe {getText (vn)};
assert! (text != -1);
assert_eq! (ucs2string (&mut from_ucs, unsafe {toString (vn, text)}, true), "Стар");});
unsafe {freeVTDNav_shim (vn)};
Ok(())}) .expect ("!unicode");}
#[bench] fn attr (bencher: &mut Bencher) {
let xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Product Title=\"Bowers & Wilkins SA250 Mk2 Wired Black audio amplifier\" />";
vtd_catch (&mut || {
let mut vg = VtdGen::new();
vg.parse_vec (false, Rc::new (Vec::from (xml.as_bytes())), 0, xml.len()) .expect ("!parse_vec");
let mut vn = VtdNav::new (&mut vg);
assert_eq! (vn.raw_attr ("Title") .expect ("!Title"), "Bowers & Wilkins SA250 Mk2 Wired Black audio amplifier");
bencher.iter (|| {
assert_eq! (vn.attr ("Title") .expect ("!Title"), "Bowers & Wilkins SA250 Mk2 Wired Black audio amplifier")});
Ok(())}) .expect ("!character_entities");}
#[bench] fn raw_attr (bencher: &mut Bencher) {
let xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Product Title=\"Bowers & Wilkins SA250 Mk2 Wired Black audio amplifier\" />";
vtd_catch (&mut || {
let mut vg = VtdGen::new();
vg.parse_vec (false, Rc::new (Vec::from (xml.as_bytes())), 0, xml.len()) .expect ("!parse_vec");
let mut vn = VtdNav::new (&mut vg);
assert_eq! (vn.attr ("Title") .expect ("!Title"), "Bowers & Wilkins SA250 Mk2 Wired Black audio amplifier");
bencher.iter (|| {
assert_eq! (vn.raw_attr ("Title") .expect ("!Title"), "Bowers & Wilkins SA250 Mk2 Wired Black audio amplifier")});
Ok(())}) .expect ("!character_entities");}
#[test] fn vtd_for_children() {
let xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><foo>\
<bar i=\"1\" />\
<bar i=\"2\">\
<baz>q</baz>\
</bar>\
<bar i=\"3\">\
<baz>w</baz>\
<baz>e</baz>\
</bar>\
<bar i=\"4\" />\
<bar i=\"5\" />\
</foo>";
vtd_catch (&mut || {
let mut vg = VtdGen::new();
vg.parse_vec (false, Rc::new (xml.into()), 0, xml.len()) .expect ("!parse_vec");
let mut vn = VtdNav::new (&mut vg);
let mut i = 0u32;
vtd_for_children! (vn, "bar", { i += 1;
assert_eq! (i, vn.raw_attr ("i") .expect ("!i") .parse::<u32>() .expect ("!i"));
if i == 1 {continue} if i == 4 {break}
let baz = vtd_for_children! (vn, "baz", String::new(), {break vn.text().unwrap().clone()}); if i == 2 {assert_eq! (baz, "q")}
else if i == 3 {assert_eq! (baz, "w")}
else {panic! ("Unexpected i: {}", i)}});
Ok(())
}) .unwrap();}}