use error::{Error, ErrorKind, Result, ResultExt};
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Success {
Complete(usize),
Incomplete(usize, usize),
}
enum Sequence<S, T> {
Valid(S, T),
Partial(usize),
}
type SequenceValidationResult<'a> = Result<Sequence<&'a [u8], &'a [u8]>>;
fn two_byte_overlong(b: u8) -> Result<()> {
ensure!(!(b & 0xfe == 0xc0), ErrorKind::TwoByteOverlong);
Ok(())
}
fn three_byte_overlong(f: u8, s: u8) -> Result<()> {
ensure!(!(f == 0xe0 && (s & 0xe0 == 0x80)),
ErrorKind::ThreeByteOverlong);
Ok(())
}
fn four_byte_overlong(f: u8, s: u8) -> Result<()> {
ensure!(!(f == 0xf0 && (s & 0xf0 == 0x80)),
ErrorKind::FourByteOverlong);
Ok(())
}
fn three_byte_utf16_surrogate(f: u8, s: u8) -> Result<()> {
ensure!(!(f == 0xed && (s & 0xe0 == 0xa0)),
ErrorKind::UTF16Surrogate);
Ok(())
}
fn invalid_continuation(b: u8, e: ErrorKind) -> Result<()> {
ensure!(!(b & 0xc0 != 0x80), e);
Ok(())
}
fn check_max_code_point(f: u8, s: u8) -> Result<()> {
ensure!(!(f == 0xf4 && s > 0x8f), ErrorKind::BeyondMaximumCodePoint);
Ok(())
}
fn validate_four_byte(i: &[u8]) -> SequenceValidationResult {
let len = i.len();
if len >= 4 {
let first = i[0];
let second = i[1];
let third = i[2];
let fourth = i[3];
check_max_code_point(first, second)?;
invalid_continuation(second, ErrorKind::FourByteContinuation(2))?;
invalid_continuation(third, ErrorKind::FourByteContinuation(3))?;
invalid_continuation(fourth, ErrorKind::FourByteContinuation(4))?;
four_byte_overlong(first, second)?;
Ok(Sequence::Valid(&i[4..], &i[0..4]))
} else if len == 3 {
let first = i[0];
let second = i[1];
let third = i[2];
check_max_code_point(first, second)?;
invalid_continuation(second, ErrorKind::FourByteContinuation(2))?;
invalid_continuation(third, ErrorKind::FourByteContinuation(3))?;
four_byte_overlong(first, second)?;
Ok(Sequence::Partial(1))
} else if len == 2 {
let first = i[0];
let second = i[1];
check_max_code_point(first, second)?;
invalid_continuation(second, ErrorKind::FourByteContinuation(2).into())?;
four_byte_overlong(first, second)?;
Ok(Sequence::Partial(2))
} else if len == 1 {
Ok(Sequence::Partial(3))
} else {
Ok(Sequence::Partial(4))
}
}
fn validate_three_byte(i: &[u8]) -> SequenceValidationResult {
let len = i.len();
if len >= 3 {
let first = i[0];
let second = i[1];
let third = i[2];
invalid_continuation(second, ErrorKind::ThreeByteContinuation(2).into())?;
invalid_continuation(third, ErrorKind::ThreeByteContinuation(3).into())?;
three_byte_overlong(first, second)?;
three_byte_utf16_surrogate(first, second)?;
Ok(Sequence::Valid(&i[3..], &i[0..3]))
} else if len == 2 {
let first = i[0];
let second = i[1];
invalid_continuation(second, ErrorKind::ThreeByteContinuation(2).into())?;
three_byte_overlong(first, second)?;
three_byte_utf16_surrogate(first, second)?;
Ok(Sequence::Partial(1))
} else if len == 1 {
Ok(Sequence::Partial(2))
} else {
Ok(Sequence::Partial(3))
}
}
fn validate_two_byte(i: &[u8]) -> SequenceValidationResult {
let len = i.len();
if len >= 2 {
let first = i[0];
let second = i[1];
invalid_continuation(second, ErrorKind::TwoByteContinuation.into())?;
two_byte_overlong(first)?;
Ok(Sequence::Valid(&i[2..], &i[0..2]))
} else if len == 1 {
let first = i[0];
two_byte_overlong(first)?;
Ok(Sequence::Partial(1))
} else {
Ok(Sequence::Partial(2))
}
}
fn validate_one_byte(i: &[u8]) -> SequenceValidationResult {
if i.is_empty() {
Ok(Sequence::Partial(1))
} else {
let first = i[0];
if first > 0xf4 {
Err(ErrorKind::BeyondMaximumCodePoint.into())
}
else if first < 0x80 {
Ok(Sequence::Valid(&i[1..], &i[0..1]))
}
else if first & 0xe0 == 0xc0 {
validate_two_byte(i)
}
else if first & 0xf0 == 0xe0 {
validate_three_byte(i)
}
else if first & 0xf8 == 0xf0 {
validate_four_byte(i)
}
else {
Err(ErrorKind::InvalidFirstByte(first).into())
}
}
}
pub fn validate(buf: &[u8]) -> Result<Success> {
let mut input = buf;
let mut pos = 0;
if !buf.is_empty() {
loop {
match validate_one_byte(input) {
Ok(Sequence::Valid(rest, parsed)) => {
pos += parsed.len();
if rest.is_empty() {
break;
} else {
input = rest;
}
}
Ok(Sequence::Partial(needed)) => return Ok(Success::Incomplete(needed, pos)),
Err(e) => {
let pos: Error = ErrorKind::Position(pos).into();
return Err(pos).chain_err(|| e);
}
}
}
}
Ok(Success::Complete(pos))
}