vatfluid 0.3.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::{Error, ErrorKind, Result, ResultExt};

#[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]>>;

/// Check for a 2-byte overlong sequence.
fn two_byte_overlong(b: u8) -> Result<()> {
    ensure!(!(b & 0xfe == 0xc0), ErrorKind::TwoByteOverlong);
    Ok(())
}

/// Check for a 3-byte overlong sequence.
fn three_byte_overlong(f: u8, s: u8) -> Result<()> {
    ensure!(!(f == 0xe0 && (s & 0xe0 == 0x80)),
            ErrorKind::ThreeByteOverlong);
    Ok(())
}

/// Check for a 4-byte overlong sequence.
fn four_byte_overlong(f: u8, s: u8) -> Result<()> {
    ensure!(!(f == 0xf0 && (s & 0xf0 == 0x80)),
            ErrorKind::FourByteOverlong);
    Ok(())
}

/// Check for 3-byte UTF-16 surrogates.
fn three_byte_utf16_surrogate(f: u8, s: u8) -> Result<()> {
    ensure!(!(f == 0xed && (s & 0xe0 == 0xa0)),
            ErrorKind::UTF16Surrogate);
    Ok(())
}

/// Check for an invalid continuation byte.  Throw the given error if the check is true.
fn invalid_continuation(b: u8, e: ErrorKind) -> Result<()> {
    ensure!(!(b & 0xc0 != 0x80), e);
    Ok(())
}

/// Check for a value that is outside the maximum allowed codepoint range (U+10FFFF).
fn check_max_code_point(f: u8, s: u8) -> Result<()> {
    ensure!(!(f == 0xf4 && s > 0x8f), ErrorKind::BeyondMaximumCodePoint);
    Ok(())
}

/// Validate a 4 byte UTF-8 sequence
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];

        // Is the 4-byte sequence out of maximum codepoint range? (U+10FFFF)
        // 11110 100 10 001111 10 111111 10 111111
        check_max_code_point(first, second)?;
        // Check for valid continuation in second byte.
        invalid_continuation(second, ErrorKind::FourByteContinuation(2))?;
        // Check for valid continuation in third byte.
        invalid_continuation(third, ErrorKind::FourByteContinuation(3))?;
        // Check for valid continuation in fourth byte.
        invalid_continuation(fourth, ErrorKind::FourByteContinuation(4))?;
        // Check for 4-byte overlong condition
        four_byte_overlong(first, second)?;
        // Keep on truckin
        Ok(Sequence::Valid(&i[4..], &i[0..4]))
    } else if len == 3 {
        let first = i[0];
        let second = i[1];
        let third = i[2];

        // Is the 4-byte sequence out of maximum codepoint range? (U+10FFFF)
        // 11110 100 10 001111 10 111111 10 111111
        check_max_code_point(first, second)?;
        // Check for valid continuation in second byte.
        invalid_continuation(second, ErrorKind::FourByteContinuation(2))?;
        // Check for valid continuation in third byte.
        invalid_continuation(third, ErrorKind::FourByteContinuation(3))?;
        // Check for 4-byte overlong condition
        four_byte_overlong(first, second)?;
        // Need 1 more byte.
        Ok(Sequence::Partial(1))
    } else if len == 2 {
        let first = i[0];
        let second = i[1];

        // Is the 4-byte sequence out of maximum codepoint range? (U+10FFFF)
        // 11110 100 10 001111 10 111111 10 111111
        check_max_code_point(first, second)?;
        // Check for valid continuation in second byte.
        invalid_continuation(second, ErrorKind::FourByteContinuation(2).into())?;
        // Check for 4-byte overlong condition
        four_byte_overlong(first, second)?;
        // Need two more bytes.
        Ok(Sequence::Partial(2))
    } else if len == 1 {
        // Need 3 more bytes.
        Ok(Sequence::Partial(3))
    } else {
        // Need 4 more bytes.
        Ok(Sequence::Partial(4))
    }
}

/// Validate a 3 byte UTF-8 sequence
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];

        // Check for valid continuation in second byte.
        invalid_continuation(second, ErrorKind::ThreeByteContinuation(2).into())?;
        // Check for valid continuation in third byte.
        invalid_continuation(third, ErrorKind::ThreeByteContinuation(3).into())?;
        // Check for a 3-byte overlong sequence.
        three_byte_overlong(first, second)?;
        // Check for a 3-byte UTF-16 surrogate.
        three_byte_utf16_surrogate(first, second)?;
        // Keep on truckin
        Ok(Sequence::Valid(&i[3..], &i[0..3]))
    } else if len == 2 {
        let first = i[0];
        let second = i[1];

        // Check for valid continuation in second byte.
        invalid_continuation(second, ErrorKind::ThreeByteContinuation(2).into())?;
        // Check for a 3-byte overlong sequence.
        three_byte_overlong(first, second)?;
        // Check for a 3-byte UTF-16 surrogate.
        three_byte_utf16_surrogate(first, second)?;
        // Need 1 more byte.
        Ok(Sequence::Partial(1))
    } else if len == 1 {
        // No additional checks can be performed when we only have 1 byte of a 3-byte sequence.
        // Need 2 more bytes.
        Ok(Sequence::Partial(2))
    } else {
        // Need 3 more bytes.
        Ok(Sequence::Partial(3))
    }
}

/// Validate a 2 byte UTF-8 sequence
fn validate_two_byte(i: &[u8]) -> SequenceValidationResult {
    let len = i.len();

    if len >= 2 {
        let first = i[0];
        let second = i[1];

        // Check for valid continuation in second byte.
        invalid_continuation(second, ErrorKind::TwoByteContinuation.into())?;
        // Check for 2-byte overlong sequence.
        two_byte_overlong(first)?;
        // Keep on truckin
        Ok(Sequence::Valid(&i[2..], &i[0..2]))
    } else if len == 1 {
        let first = i[0];
        // We can still check the 1st byte for a 2-byte overlong sequence.
        two_byte_overlong(first)?;
        // Need 1 more byte.
        Ok(Sequence::Partial(1))
    } else {
        Ok(Sequence::Partial(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(ErrorKind::BeyondMaximumCodePoint.into())
        }
        // 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(ErrorKind::InvalidFirstByte(first).into())
        }
    }
}

/// Validate a UTF-8 sequence
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))
}