use std::{str, char, mem};
use std::marker::PhantomData;
use std::convert::Into;
use std::default::Default;
use types;
pub fn as_char(ch: u32) -> char {
debug_assert!(char::from_u32(ch).is_some());
unsafe { mem::transmute(ch) }
}
pub struct StrCharIndexIterator<'r> {
index: usize,
chars: str::Chars<'r>,
}
impl<'r> Iterator for StrCharIndexIterator<'r> {
type Item = ((usize,usize), char);
#[inline]
fn next(&mut self) -> Option<((usize,usize), char)> {
if let Some(ch) = self.chars.next() {
let prev = self.index;
let next = prev + ch.len_utf8();
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, chars: self.chars() }
}
}
pub struct StatefulDecoderHelper<'a, St> {
pub buf: &'a [u8],
pub pos: usize,
pub output: &'a mut (types::StringWriter + 'a),
pub err: Option<types::CodecError>,
_marker: PhantomData<St>,
}
impl<'a, St: Default> StatefulDecoderHelper<'a, St> {
#[inline(always)]
pub fn new(buf: &'a [u8],
output: &'a mut (types::StringWriter + 'a)) -> StatefulDecoderHelper<'a, St> {
StatefulDecoderHelper { buf: buf, pos: 0, output: output, err: None, _marker: PhantomData }
}
#[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 isize, cause: msg.into() });
Default::default()
}
#[inline(always)]
pub fn backup_and_err(&mut self, backup: usize, msg: &'static str) -> St {
let upto = self.pos as isize - backup as isize;
self.err = Some(types::CodecError { upto: upto, cause: msg.into() });
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: Context) {
$(case $($inilhs:pat),+ => $($inirhs:expr),+;)+
final => $($inifin:expr),+;
}
checkpoint:
$(state $ckst:ident($ckctx:ident: Context $(, $ckarg:ident: $ckty:ty)*) {
$(case $($cklhs:pat),+ => $($ckrhs:expr),+;)+
final => $($ckfin:expr),+;
})*
transient:
$(state $st:ident($ctx:ident: Context $(, $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::new($dec { st: $stmod::$inist })
}
}
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) -> (usize, Option<CodecError>) {
use self::$stmod::{start, transient};
output.writer_hint(input.len());
let mut ctx = ::util::StatefulDecoderHelper::new(input, output);
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::new(&[], output);
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: Context) {
$(case $($inilhs:pat),+ => $($inirhs:expr),+;)+
}
transient:
$(state $st:ident($ctx:ident: Context $(, $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: Context) {
$(case $($inilhs),+ => $($inirhs),+;)+
final => $inictx.reset();
}
checkpoint:
transient:
$(state $st($ctx: Context $(, $arg: $ty)*) {
$(case $($lhs),+ => $($rhs),+;)+
final => $ctx.err("incomplete sequence");
})*
);
)
);