#![macro_escape]
use std::{str, mem};
use std::borrow::IntoCow;
use std::default::Default;
use std::num::ToPrimitive;
use types;
pub fn as_char<T:ToPrimitive>(ch: T) -> char {
unsafe { mem::transmute(ch.to_u32().unwrap()) }
}
pub struct StrCharIndexIterator<'r> {
index: uint,
string: &'r str,
}
impl<'r> Iterator for StrCharIndexIterator<'r> {
type Item = ((uint,uint), char);
#[inline]
fn next(&mut self) -> Option<((uint,uint), char)> {
if self.index < self.string.len() {
let str::CharRange {ch, next} = self.string.char_range_at(self.index);
let prev = self.index;
self.index = next;
Some(((prev, next), ch))
} else {
None
}
}
}
pub trait StrCharIndex<'r> {
fn index_iter(&self) -> StrCharIndexIterator<'r>;
}
impl<'r> StrCharIndex<'r> for &'r str {
fn index_iter(&self) -> StrCharIndexIterator<'r> {
StrCharIndexIterator { index: 0, string: *self }
}
}
pub struct StatefulDecoderHelper<'a, St> {
pub buf: &'a [u8],
pub pos: uint,
pub output: &'a mut (types::StringWriter + 'a),
pub err: Option<types::CodecError>,
}
impl<'a, St:Default> StatefulDecoderHelper<'a, St> {
#[inline(always)]
pub fn read(&mut self) -> Option<u8> {
match self.buf.get(self.pos) {
Some(&c) => { self.pos += 1; Some(c) }
None => None
}
}
#[inline(always)]
pub fn reset(&self) -> St {
Default::default()
}
#[inline(always)]
pub fn emit(&mut self, c: u32) -> St {
self.output.write_char(unsafe {mem::transmute(c)});
Default::default()
}
#[inline(always)]
pub fn emit_str(&mut self, s: &str) -> St {
self.output.write_str(s);
Default::default()
}
#[inline(always)]
pub fn err(&mut self, msg: &'static str) -> St {
self.err = Some(types::CodecError { upto: self.pos as int, cause: msg.into_cow() });
Default::default()
}
#[inline(always)]
pub fn backup_and_err(&mut self, backup: uint, msg: &'static str) -> St {
let upto = self.pos as int - backup as int;
self.err = Some(types::CodecError { upto: upto, cause: msg.into_cow() });
Default::default()
}
}
macro_rules! stateful_decoder(
(
$(#[$decmeta:meta])*
struct $dec:ident;
module $stmod:ident; // should be unique from other existing identifiers
ascii_compatible $asciicompat:expr;
$(internal $item:item)* // will only be visible from state functions
initial state $inist:ident($inictx:ident) {
$(case $($inilhs:pat)|+ => $($inirhs:expr),+;)+
final => $($inifin:expr),+;
}
$(checkpoint state $ckst:ident($ckctx:ident $(, $ckarg:ident: $ckty:ty)*) {
$(case $($cklhs:pat)|+ => $($ckrhs:expr),+;)+
final => $($ckfin:expr),+;
})*
$(state $st:ident($ctx:ident $(, $arg:ident: $ty:ty)*) {
$(case $($lhs:pat)|+ => $($rhs:expr),+;)+
final => $($fin:expr),+;
})*
) => (
$(#[$decmeta])*
pub struct $dec {
st: $stmod::State
}
#[allow(non_snake_case)]
mod $stmod {
pub use self::State::*;
#[derive(PartialEq,Clone,Copy)]
pub enum State {
$inist,
$(
$ckst(() $(, $ckty)*),
)*
$(
$st(() $(, $ty)*),
)*
}
impl ::std::default::Default for State {
#[inline(always)] fn default() -> State { $inist }
}
pub mod internal {
pub type Context<'a> = ::util::StatefulDecoderHelper<'a, super::State>;
$($item)*
}
pub mod start {
use super::internal::*;
#[inline(always)]
pub fn $inist($inictx: &mut Context) -> super::State {
#[allow(unused_imports)] use super::transient::*;
match $inictx.read() {
None => super::$inist,
Some(c) => match c { $($($inilhs)|+ => { $($inirhs);+ })+ },
}
}
$(
#[inline(always)]
pub fn $ckst($ckctx: &mut Context $(, $ckarg: $ckty)*) -> super::State {
#[allow(unused_imports)] use super::transient::*;
match $ckctx.read() {
None => super::$ckst(() $(, $ckarg)*),
Some(c) => match c { $($($cklhs)|+ => { $($ckrhs);+ })+ },
}
}
)*
}
pub mod transient {
use super::internal::*;
#[inline(always)]
#[allow(dead_code)]
pub fn $inist(_: &mut Context) -> super::State {
super::$inist }
$(
#[inline(always)]
#[allow(dead_code)]
pub fn $ckst(_: &mut Context $(, $ckarg: $ckty)*) -> super::State {
super::$ckst(() $(, $ckarg)*) }
)*
$(
#[inline(always)]
pub fn $st($ctx: &mut Context $(, $arg: $ty)*) -> super::State {
match $inictx.read() {
None => super::$st(() $(, $arg)*),
Some(c) => match c { $($($lhs)|+ => { $($rhs);+ })+ },
}
}
)*
}
}
impl $dec {
pub fn new() -> Box<RawDecoder> {
box $dec { st: $stmod::$inist } as Box<RawDecoder>
}
}
impl RawDecoder for $dec {
fn from_self(&self) -> Box<RawDecoder> { $dec::new() }
fn is_ascii_compatible(&self) -> bool { $asciicompat }
fn raw_feed(&mut self, input: &[u8],
output: &mut StringWriter) -> (uint, Option<CodecError>) {
use self::$stmod::{start, transient};
output.writer_hint(input.len());
let mut ctx = ::util::StatefulDecoderHelper {
buf: input, pos: 0, output: output, err: None
};
let mut processed = 0;
let mut st = self.st;
let st_ = match st {
$stmod::$inist => $stmod::$inist,
$(
$stmod::$ckst(() $(, $ckarg)*) => start::$ckst(&mut ctx $(, $ckarg)*),
)*
$(
$stmod::$st(() $(, $arg)*) => transient::$st(&mut ctx $(, $arg)*),
)*
};
match (ctx.err.take(), st_) {
(None, $stmod::$inist) $(| (None, $stmod::$ckst(..)))* =>
{ st = st_; processed = ctx.pos; }
(None, _) => { self.st = st_; return (processed, None); }
(Some(err), _) => { self.st = st_; return (processed, Some(err)); }
}
while ctx.pos < ctx.buf.len() {
let st_ = match st {
$stmod::$inist => start::$inist(&mut ctx),
$(
$stmod::$ckst(() $(, $ckarg)*) => start::$ckst(&mut ctx $(, $ckarg)*),
)*
_ => unreachable!(),
};
match (ctx.err.take(), st_) {
(None, $stmod::$inist) $(| (None, $stmod::$ckst(..)))* =>
{ st = st_; processed = ctx.pos; }
(None, _) => { self.st = st_; return (processed, None); }
(Some(err), _) => { self.st = st_; return (processed, Some(err)); }
}
}
self.st = st;
(processed, None)
}
fn raw_finish(&mut self, output: &mut StringWriter) -> Option<CodecError> {
#![allow(unused_mut, unused_variables)]
let mut ctx = ::util::StatefulDecoderHelper {
buf: &[], pos: 0, output: output, err: None
};
self.st = match ::std::mem::replace(&mut self.st, $stmod::$inist) {
$stmod::$inist => { let $inictx = &mut ctx; $($inifin);+ },
$(
$stmod::$ckst(() $(, $ckarg)*) => { let $ckctx = &mut ctx; $($ckfin);+ },
)*
$(
$stmod::$st(() $(, $arg)*) => { let $ctx = &mut ctx; $($fin);+ },
)*
};
ctx.err.take()
}
}
)
);
macro_rules! ascii_compatible_stateful_decoder(
(
$(#[$decmeta:meta])*
struct $dec:ident;
module $stmod:ident; // should be unique from other existing identifiers
$(internal $item:item)* // will only be visible from state functions
initial state $inist:ident($inictx:ident) {
$(case $($inilhs:pat)|+ => $($inirhs:expr),+;)+
}
$(state $st:ident($ctx:ident $(, $arg:ident: $ty:ty)*) {
$(case $($lhs:pat)|+ => $($rhs:expr),+;)+
})*
) => (
stateful_decoder!(
$(#[$decmeta])*
struct $dec;
module $stmod;
ascii_compatible true;
$(internal $item)*
initial state $inist($inictx) {
$(case $($inilhs)|+ => $($inirhs),+;)+
final => $inictx.reset();
}
$(state $st($ctx $(, $arg: $ty)*) {
$(case $($lhs)|+ => $($rhs),+;)+
final => $ctx.err("incomplete sequence");
})*
);
)
);