vatfluid 0.1.0

UTF-8 slice validation, with incomplete results to support byte streams.
Documentation
// Copyright (c) 2016 vatfluid developers
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

//! nom UTF-8 validation implementation
use error::UTF8Validation;

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
/// A successful validation result.
pub enum Success {
    /// The validation is complete.
    /// The argument is the final position in the input.
    Complete(usize),
    /// The validation was successful, but incomplete.
    /// The first argument is the number of expected bytes.
    /// The second argument is the position in the input buffer that has been validated so far.
    Incomplete(usize, usize),
}

/// Used to indicate the current status of UTF-8 sequence validation.
enum Sequence<S, T> {
    /// A UTF-8 sequence has been validated and there is remaining input to validate.
    /// S is the remaining input, T is the sequence that is valid.
    Valid(S, T),
    /// The UTF-8 sequence is valid so far, but we need more input to complete validation.
    Partial(usize),
}

/// The result a UTF-8 sequence validation attempt.
type SequenceValidationResult<'a> = Result<Sequence<&'a [u8], &'a [u8]>, UTF8Validation>;

/// Continuation error check
fn first_two_bits_arent_10(byte: u8) -> bool {
    byte & 0xc0 != 0x80
}

/// Validate a 4 byte UTF-8 sequence
fn validate_four_byte(i: &[u8]) -> SequenceValidationResult {
    if i.len() < 4 {
        Ok(Sequence::Partial(4))
    } else {
        let first = i[0];
        let second = i[1];
        let third = i[2];
        let fourth = i[3];

        // If the second byte doesn't start with 10 error out.
        if first_two_bits_arent_10(second) {
            Err(UTF8Validation::FourByteContinuation(2))
        }
        // If the third byte doesn't start with 10 error out.
        else if first_two_bits_arent_10(third) {
            Err(UTF8Validation::FourByteContinuation(3))
        }
        // If the fourth byte doesn't start with 10 error out.
        else if first_two_bits_arent_10(fourth) {
            Err(UTF8Validation::FourByteContinuation(4))
        }
        // Check for 4-byte overlong condition
        else if first == 0xf0 && (second & 0xf0 == 0x80) {
            Err(UTF8Validation::FourByteOverlong)
        } else {
            Ok(Sequence::Valid(&i[4..], &i[0..4]))
        }
    }
}

/// Validate a 3 byte UTF-8 sequence
fn validate_three_byte(i: &[u8]) -> SequenceValidationResult {
    if i.len() < 3 {
        Ok(Sequence::Partial(3))
    } else {
        let first = i[0];
        let second = i[1];
        let third = i[2];

        // If the second byte doesn't start with 10 error out.
        if first_two_bits_arent_10(second) {
            Err(UTF8Validation::ThreeByteContinuation(2))
        }
        // If the third byte doesn't start with 10 error out.
        else if first_two_bits_arent_10(third) {
            Err(UTF8Validation::ThreeByteContinuation(3))
        }
        // Check for 3-byte overlong condition
        // UTF-16 surrogates
        else if (first == 0xe0 && (second & 0xe0 == 0x80)) ||
                  (first == 0xed && (second & 0xe0 == 0xa0)) {
            Err(UTF8Validation::ThreeByteOverlong)
        } else {
            Ok(Sequence::Valid(&i[3..], &i[0..3]))
        }
    }
}

/// Validate a 2 byte UTF-8 sequence
fn validate_two_byte(i: &[u8]) -> SequenceValidationResult {
    if i.len() < 2 {
        Ok(Sequence::Partial(2))
    } else {
        let first = i[0];
        let second = i[1];
        // Check for valid continuation in second byte.
        if first_two_bits_arent_10(second) {
            Err(UTF8Validation::TwoByteContinuation)
        }
        // Check for 2-byte overlong sequence.
        else if first & 0xfe == 0xc0 {
            Err(UTF8Validation::TwoByteOverlong)
        }
        // Keep on truckin
        else {
            Ok(Sequence::Valid(&i[2..], &i[0..2]))
        }
    }
}

/// Validate a 1 byte UTF-8 sequence
fn validate_one_byte(i: &[u8]) -> SequenceValidationResult {
    if i.is_empty() {
        Ok(Sequence::Partial(1))
    } else {
        let first = i[0];

        // This indicates a 4-byte sequence out of maximum code point range.
        if first > 0xf4 {
            Err(UTF8Validation::MaximumCodePoint)
        }
        // Eat up 1-byte codes 0xxxxxxx
        // 7-bit code points
        else if first < 0x80 {
            Ok(Sequence::Valid(&i[1..], &i[0..1]))
        }
        // Handle the 2-byte codes 110 xxxxx 10 xxxxxx
        // 11-bit code points
        else if first & 0xe0 == 0xc0 {
            validate_two_byte(i)
        }
        // Handle the 3-byte codes 1110 xxxx 10 xxxxxx 10 xxxxxx
        // 16-bit code points
        else if first & 0xf0 == 0xe0 {
            validate_three_byte(i)
        }
        // Handle the 4-bytes codes 11110 xxx 10 xxxxxx 10 xxxxxx 10 xxxxxx
        // 21-bit code points
        else if first & 0xf8 == 0xf0 {
            validate_four_byte(i)
        }
        // This covers 1-byte 0x80 - 0xbf continuation bytes.
        else {
            Err(UTF8Validation::InvalidFirstByte(first))
        }
    }
}

/// Validate a UTF-8 sequence
pub fn validate(buf: &[u8]) -> Result<Success, UTF8Validation> {
    let mut input = buf;
    let mut pos = 0;
    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))
}