use error::UTF8Validation;
#[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]>, UTF8Validation>;
fn first_two_bits_arent_10(byte: u8) -> bool {
byte & 0xc0 != 0x80
}
fn validate_four_byte(i: &[u8]) -> SequenceValidationResult {
if i.len() < 4 {
Ok(Sequence::Partial(4 - i.len()))
} else {
let first = i[0];
let second = i[1];
let third = i[2];
let fourth = i[3];
if first == 0xf4 && second > 0x8f {
Err(UTF8Validation::MaximumCodePoint)
}
else if first_two_bits_arent_10(second) {
Err(UTF8Validation::FourByteContinuation(2))
}
else if first_two_bits_arent_10(third) {
Err(UTF8Validation::FourByteContinuation(3))
}
else if first_two_bits_arent_10(fourth) {
Err(UTF8Validation::FourByteContinuation(4))
}
else if first == 0xf0 && (second & 0xf0 == 0x80) {
Err(UTF8Validation::FourByteOverlong)
} else {
Ok(Sequence::Valid(&i[4..], &i[0..4]))
}
}
}
fn validate_three_byte(i: &[u8]) -> SequenceValidationResult {
if i.len() < 3 {
Ok(Sequence::Partial(3 - i.len()))
} else {
let first = i[0];
let second = i[1];
let third = i[2];
if first_two_bits_arent_10(second) {
Err(UTF8Validation::ThreeByteContinuation(2))
}
else if first_two_bits_arent_10(third) {
Err(UTF8Validation::ThreeByteContinuation(3))
}
else if first == 0xe0 && (second & 0xe0 == 0x80) {
Err(UTF8Validation::ThreeByteOverlong)
} else if first == 0xed && (second & 0xe0 == 0xa0) {
Err(UTF8Validation::UTF16Surrogate)
} else {
Ok(Sequence::Valid(&i[3..], &i[0..3]))
}
}
}
fn validate_two_byte(i: &[u8]) -> SequenceValidationResult {
if i.len() < 2 {
Ok(Sequence::Partial(2 - i.len()))
} else {
let first = i[0];
let second = i[1];
if first_two_bits_arent_10(second) {
Err(UTF8Validation::TwoByteContinuation)
}
else if first & 0xfe == 0xc0 {
Err(UTF8Validation::TwoByteOverlong)
}
else {
Ok(Sequence::Valid(&i[2..], &i[0..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(UTF8Validation::MaximumCodePoint)
}
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(UTF8Validation::InvalidFirstByte(first))
}
}
}
pub fn validate(buf: &[u8]) -> Result<Success, UTF8Validation> {
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) => return Err(e),
}
}
}
Ok(Success::Complete(pos))
}