blob: 165f8c87cca6ad04d2d0e878d043502cf7d79976 [file] [log] [blame]
/* Copyright 2016 The encode_unicode Developers
*
* Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
* http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
* http://opensource.org/licenses/MIT>, at your option. This file may not be
* copied, modified, or distributed except according to those terms.
*/
#![allow(unused_unsafe)]// explicit unsafe{} blocks in unsafe functions are a good thing.
use utf8_char::Utf8Char;
use utf16_char::Utf16Char;
use utf8_iterators::*;
use utf16_iterators::*;
use decoding_iterators::*;
use error::*;
extern crate core;
use self::core::{char, u32, mem};
use self::core::ops::{Not, Index, RangeFull};
use self::core::borrow::Borrow;
#[cfg(feature="ascii")]
extern crate ascii;
#[cfg(feature="ascii")]
use self::ascii::AsciiStr;
// TODO better docs and tests
/// Methods for working with `u8`s as UTF-8 bytes.
pub trait U8UtfExt {
/// How many more bytes will you need to complete this codepoint?
///
/// # Errors
///
/// An error is returned if the byte is not a valid start of an UTF-8
/// codepoint:
///
/// * `128..192`: ContinuationByte
/// * `248..`: TooLongSequence
///
/// Values in 244..248 represent a too high codepoint, but do not cause an
/// error.
fn extra_utf8_bytes(self) -> Result<usize,InvalidUtf8FirstByte>;
/// How many more bytes will you need to complete this codepoint?
///
/// This function assumes that the byte is a valid UTF-8 start, and might
/// return any value otherwise. (but the function is pure and safe to call
/// with any value).
fn extra_utf8_bytes_unchecked(self) -> usize;
}
impl U8UtfExt for u8 {
#[inline]
fn extra_utf8_bytes(self) -> Result<usize,InvalidUtf8FirstByte> {
use error::InvalidUtf8FirstByte::{ContinuationByte,TooLongSeqence};
// the bit twiddling is explained in extra_utf8_bytes_unchecked()
if self < 128 {
return Ok(0);
}
match ((self as u32)<<25).not().leading_zeros() {
n @ 1...3 => Ok(n as usize),
0 => Err(ContinuationByte),
_ => Err(TooLongSeqence),
}
}
#[inline]
fn extra_utf8_bytes_unchecked(self) -> usize {
// For fun I've optimized this function (for x86 instruction count):
// The most straightforward implementation, that lets the compiler do
// the optimizing:
//match self {
// 0b0000_0000...0b0111_1111 => 0,
// 0b1100_0010...0b1101_1111 => 1,
// 0b1110_0000...0b1110_1111 => 2,
// 0b1111_0000...0b1111_0100 => 3,
// _ => whatever()
//}
// Using `unsafe{self::core::hint::unreachable_unchecked()}` for the
// "don't care" case is a terrible idea: while having the function
// non-deterministically return whatever happens to be in a register
// MIGHT be acceptable, it permits the function to not `ret`urn at all,
// but let execution fall through to whatever comes after it in the
// binary! (in other words completely UB).
// Currently unreachable_unchecked() might trap too,
// which is certainly not what we want.
// I also think `unsafe{mem::unitialized()}` is much more likely to
// explicitly produce whatever happens to be in a register than tell
// the compiler it can ignore this branch but needs to produce a value.
//
// From the bit patterns we see that for non-ASCII values the result is
// (number of leading set bits) - 1
// The standard library doesn't have a method for counting leading ones,
// but it has leading_zeros(), which can be used after inverting.
// This function can therefore be reduced to the one-liner
//`self.not().leading_zeros().saturating_sub(1) as usize`, which would
// be branchless for architectures with instructions for
// leading_zeros() and saturating_sub().
// Shortest version as long as ASCII-ness can be predicted: (especially
// if the BSR instruction which leading_zeros() uses is microcoded or
// doesn't exist)
// u8.leading_zeros() would cast to a bigger type internally, so that's
// free. compensating by shifting left by 24 before inverting lets the
// compiler know that the value passed to leading_zeros() is not zero,
// for which BSR's output is undefined and leading_zeros() normally has
// special case with a branch.
// Shifting one bit too many left acts as a saturating_sub(1).
if self<128 {0} else {((self as u32)<<25).not().leading_zeros() as usize}
// Branchless but longer version: (9 instructions)
// It's tempting to try (self|0x80).not().leading_zeros().wrapping_sub(1),
// but that produces high lengths for ASCII values 0b01xx_xxxx.
// If we could somehow (branchlessy) clear that bit for ASCII values...
// We can by masking with the value shifted right with sign extension!
// (any nonzero number of bits in range works)
//let extended = self as i8 as i32;
//let ascii_cleared = (extended<<25) & (extended>>25);
//ascii_cleared.not().leading_zeros() as usize
// cmov version: (7 instructions)
//(((self as u32)<<24).not().leading_zeros() as usize).saturating_sub(1)
}
}
/// Methods for working with `u16`s as UTF-16 units.
pub trait U16UtfExt {
/// Will you need an extra unit to complete this codepoint?
///
/// Returns `Err` for trailing surrogates, `Ok(true)` for leading surrogates,
/// and `Ok(false)` for others.
fn utf16_needs_extra_unit(self) -> Result<bool,InvalidUtf16FirstUnit>;
/// Does this `u16` need another `u16` to complete a codepoint?
/// Returns `(self & 0xfc00) == 0xd800`
///
/// Is basically an unchecked variant of `utf16_needs_extra_unit()`.
fn is_utf16_leading_surrogate(self) -> bool;
}
impl U16UtfExt for u16 {
#[inline]
fn utf16_needs_extra_unit(self) -> Result<bool,InvalidUtf16FirstUnit> {
match self {
// https://en.wikipedia.org/wiki/UTF-16#U.2B10000_to_U.2B10FFFF
0x00_00...0xd7_ff | 0xe0_00...0xff_ff => Ok(false),
0xd8_00...0xdb_ff => Ok(true),
_ => Err(InvalidUtf16FirstUnit)
}
}
#[inline]
fn is_utf16_leading_surrogate(self) -> bool {
(self & 0xfc00) == 0xd800// Clear the ten content bytes of a surrogate,
// and see if it's a leading surrogate.
}
}
/// Extension trait for `char` that adds methods for converting to and from UTF-8 or UTF-16.
pub trait CharExt: Sized {
/// Get the UTF-8 representation of this codepoint.
///
/// `Utf8Char` is to `[u8;4]` what `char` is to `u32`:
/// a restricted type that cannot be mutated internally.
fn to_utf8(self) -> Utf8Char;
/// Get the UTF-16 representation of this codepoint.
///
/// `Utf16Char` is to `[u16;2]` what `char` is to `u32`:
/// a restricted type that cannot be mutated internally.
fn to_utf16(self) -> Utf16Char;
/// Iterate over or [read](https://doc.rust-lang.org/std/io/trait.Read.html)
/// the one to four bytes in the UTF-8 representation of this codepoint.
///
/// An identical alternative to the unstable `char.encode_utf8()`.
/// That method somehow still exist on stable, so I have to use a different name.
fn iter_utf8_bytes(self) -> Utf8Iterator;
/// Iterate over the one or two units in the UTF-16 representation of this codepoint.
///
/// An identical alternative to the unstable `char.encode_utf16()`.
/// That method somehow still exist on stable, so I have to use a different name.
fn iter_utf16_units(self) -> Utf16Iterator;
/// Convert this char to an UTF-8 array, and also return how many bytes of
/// the array are used,
///
/// The returned array is left-aligned with unused bytes set to zero.
fn to_utf8_array(self) -> ([u8; 4], usize);
/// Convert this `char` to UTF-16.
/// The second `u16` is `Some` if a surrogate pair is required.
fn to_utf16_tuple(self) -> (u16, Option<u16>);
/// Create a `char` from the start of an UTF-8 slice,
/// and also return how many bytes were used.
///
/// # Errors
///
/// Returns an `Err` if the slice is empty, doesn't start with a valid
/// UTF-8 sequence or is too short for the sequence.
///
/// # Examples
///
/// ```
/// use encode_unicode::CharExt;
/// use encode_unicode::error::InvalidUtf8Slice::*;
/// use encode_unicode::error::InvalidUtf8::*;
///
/// assert_eq!(char::from_utf8_slice_start(&[b'A', b'B', b'C']), Ok(('A',1)));
/// assert_eq!(char::from_utf8_slice_start(&[0xdd, 0xbb]), Ok(('\u{77b}',2)));
///
/// assert_eq!(char::from_utf8_slice_start(&[]), Err(TooShort(1)));
/// assert_eq!(char::from_utf8_slice_start(&[0xf0, 0x99]), Err(TooShort(4)));
/// assert_eq!(char::from_utf8_slice_start(&[0xee, b'F', 0x80]), Err(Utf8(NotAContinuationByte(1))));
/// assert_eq!(char::from_utf8_slice_start(&[0xee, 0x99, 0x0f]), Err(Utf8(NotAContinuationByte(2))));
/// ```
fn from_utf8_slice_start(src: &[u8]) -> Result<(Self,usize),InvalidUtf8Slice>;
/// Create a `char` from the start of an UTF-16 slice,
/// and also return how many units were used.
///
/// If you want to continue after an error, continue with the next `u16` unit.
fn from_utf16_slice_start(src: &[u16]) -> Result<(Self,usize), InvalidUtf16Slice>;
/// Convert an UTF-8 sequence as returned from `.to_utf8_array()` into a `char`
///
/// The codepoint must start at the first byte, and leftover bytes are ignored.
///
/// # Errors
///
/// Returns an `Err` if the array doesn't start with a valid UTF-8 sequence.
///
/// # Examples
///
/// ```
/// use encode_unicode::CharExt;
/// use encode_unicode::error::InvalidUtf8Array::*;
/// use encode_unicode::error::InvalidUtf8::*;
/// use encode_unicode::error::InvalidCodepoint::*;
///
/// assert_eq!(char::from_utf8_array([b'A', 0, 0, 0]), Ok('A'));
/// assert_eq!(char::from_utf8_array([0xf4, 0x8b, 0xbb, 0xbb]), Ok('\u{10befb}'));
/// assert_eq!(char::from_utf8_array([b'A', b'B', b'C', b'D']), Ok('A'));
/// assert_eq!(char::from_utf8_array([0, 0, 0xcc, 0xbb]), Ok('\0'));
///
/// assert_eq!(char::from_utf8_array([0xef, b'F', 0x80, 0x80]), Err(Utf8(NotAContinuationByte(1))));
/// assert_eq!(char::from_utf8_array([0xc1, 0x80, 0, 0]), Err(Utf8(OverLong)));
/// assert_eq!(char::from_utf8_array([0xf7, 0xaa, 0x99, 0x88]), Err(Codepoint(TooHigh)));
/// ```
fn from_utf8_array(utf8: [u8; 4]) -> Result<Self,InvalidUtf8Array>;
/// Convert a UTF-16 pair as returned from `.to_utf16_tuple()` into a `char`.
fn from_utf16_tuple(utf16: (u16, Option<u16>)) -> Result<Self, InvalidUtf16Tuple>;
/// Convert an UTF-8 sequence into a char.
///
/// The length of the slice is taken as length of the sequence;
/// it should be 1,2,3 or 4.
///
/// # Safety
///
/// The slice must contain exactly one, valid, UTF-8 sequence.
///
/// Passing a slice that produces an invalid codepoint is always undefined
/// behavior; Later checks that the codepoint is valid can be removed
/// by the compiler.
///
/// # Panics
///
/// If the slice is empty
unsafe fn from_utf8_exact_slice_unchecked(src: &[u8]) -> Self;
/// Convert a UTF-16 tuple as returned from `.to_utf16_tuple()` into a `char`.
unsafe fn from_utf16_tuple_unchecked(utf16: (u16, Option<u16>)) -> Self;
/// Perform some extra validations compared to `char::from_u32_unchecked()`
///
/// # Errors
///
/// This function will return an error if
///
/// * the value is greater than 0x10ffff
/// * the value is between 0xd800 and 0xdfff (inclusive)
fn from_u32_detailed(c: u32) -> Result<Self,InvalidCodepoint>;
}
impl CharExt for char {
/////////
//UTF-8//
/////////
fn to_utf8(self) -> Utf8Char {
self.into()
}
fn iter_utf8_bytes(self) -> Utf8Iterator {
self.to_utf8().into_iter()
}
fn to_utf8_array(self) -> ([u8; 4], usize) {
let len = self.len_utf8();
let mut c = self as u32;
if len == 1 {// ASCII, the common case
([c as u8, 0, 0, 0], 1)
} else {
let mut parts = 0;// convert to 6-bit bytes
parts |= c & 0x3f; c>>=6;
parts<<=8; parts |= c & 0x3f; c>>=6;
parts<<=8; parts |= c & 0x3f; c>>=6;
parts<<=8; parts |= c & 0x3f;
parts |= 0x80_80_80_80;// set the most significant bit
parts >>= 8*(4-len);// right-align bytes
// Now, unused bytes are zero, (which matters for Utf8Char.eq())
// and the rest are 0b10xx_xxxx
// set header on first byte
parts |= (0xff_00u32 >> len) & 0xff;// store length
parts &= Not::not(1u32 << 7-len);// clear the next bit after it
let bytes: [u8; 4] = unsafe{ mem::transmute(u32::from_le(parts)) };
(bytes, len)
}
}
fn from_utf8_slice_start(src: &[u8]) -> Result<(Self,usize),InvalidUtf8Slice> {
use errors::InvalidUtf8::*;
use errors::InvalidUtf8Slice::*;
let first = match src.first() {
Some(first) => *first,
None => return Err(TooShort(1)),
};
let bytes = match first.extra_utf8_bytes() {
Err(e) => return Err(Utf8(FirstByte(e))),
Ok(0) => return Ok((first as char, 1)),
Ok(extra) if extra >= src.len()
=> return Err(TooShort(extra+1)),
Ok(extra) => &src[..extra+1],
};
if let Some(i) = bytes.iter().skip(1).position(|&b| (b >> 6) != 0b10 ) {
Err(Utf8(NotAContinuationByte(i+1)))
} else if overlong(bytes[0], bytes[1]) {
Err(Utf8(OverLong))
} else {
match char::from_u32_detailed(merge_nonascii_unchecked_utf8(bytes)) {
Ok(c) => Ok((c, bytes.len())),
Err(e) => Err(Codepoint(e)),
}
}
}
fn from_utf8_array(utf8: [u8; 4]) -> Result<Self,InvalidUtf8Array> {
use errors::InvalidUtf8::*;
use errors::InvalidUtf8Array::*;
let src = match utf8[0].extra_utf8_bytes() {
Err(error) => return Err(Utf8(FirstByte(error))),
Ok(0) => return Ok(utf8[0] as char),
Ok(extra) => &utf8[..extra+1],
};
if let Some(i) = src[1..].iter().position(|&b| (b >> 6) != 0b10 ) {
Err(Utf8(NotAContinuationByte(i+1)))
} else if overlong(utf8[0], utf8[1]) {
Err(Utf8(OverLong))
} else {
char::from_u32_detailed(merge_nonascii_unchecked_utf8(src))
.map_err(|e| Codepoint(e) )
}
}
unsafe fn from_utf8_exact_slice_unchecked(src: &[u8]) -> Self {
if src.len() == 1 {
src[0] as char
} else {
char::from_u32_unchecked(merge_nonascii_unchecked_utf8(src))
}
}
//////////
//UTF-16//
//////////
fn to_utf16(self) -> Utf16Char {
Utf16Char::from(self)
}
fn iter_utf16_units(self) -> Utf16Iterator {
self.to_utf16().into_iter()
}
fn to_utf16_tuple(self) -> (u16, Option<u16>) {
let c = self as u32;
if c <= 0x_ff_ff {// single (or reserved, which we ignore)
(c as u16, None)
} else {// double (or too high, which we ignore)
let c = c - 0x_01_00_00;
let high = 0x_d8_00 + (c >> 10);
let low = 0x_dc_00 + (c & 0x_03_ff);
(high as u16, Some(low as u16))
}
}
fn from_utf16_slice_start(src: &[u16]) -> Result<(Self,usize), InvalidUtf16Slice> {
use errors::InvalidUtf16Slice::*;
unsafe {match (src.get(0), src.get(1)) {
(Some(&u @ 0x00_00...0xd7_ff), _) |
(Some(&u @ 0xe0_00...0xff_ff), _)
=> Ok((char::from_u32_unchecked(u as u32), 1)),
(Some(&0xdc_00...0xdf_ff), _) => Err(FirstLowSurrogate),
(None, _) => Err(EmptySlice),
(Some(&f @ 0xd8_00...0xdb_ff), Some(&s @ 0xdc_00...0xdf_ff))
=> Ok((char::from_utf16_tuple_unchecked((f, Some(s))), 2)),
(Some(&0xd8_00...0xdb_ff), Some(_)) => Err(SecondNotLowSurrogate),
(Some(&0xd8_00...0xdb_ff), None) => Err(MissingSecond),
(Some(_), _) => unreachable!()
}}
}
fn from_utf16_tuple(utf16: (u16, Option<u16>)) -> Result<Self, InvalidUtf16Tuple> {
use errors::InvalidUtf16Tuple::*;
unsafe{ match utf16 {
(0x00_00...0xd7_ff, None) | // single
(0xe0_00...0xff_ff, None) | // single
(0xd8_00...0xdb_ff, Some(0xdc_00...0xdf_ff)) // correct surrogate
=> Ok(char::from_utf16_tuple_unchecked(utf16)),
(0xd8_00...0xdb_ff, Some(_)) => Err(InvalidSecond),
(0xd8_00...0xdb_ff, None ) => Err(MissingSecond),
(0xdc_00...0xdf_ff, _ ) => Err(FirstIsTrailingSurrogate),
( _ , Some(_)) => Err(SuperfluousSecond),
( _ , None ) => unreachable!()
}}
}
unsafe fn from_utf16_tuple_unchecked(utf16: (u16, Option<u16>)) -> Self {
match utf16.1 {
Some(second) => combine_surrogates(utf16.0, second),
None => char::from_u32_unchecked(utf16.0 as u32)
}
}
fn from_u32_detailed(c: u32) -> Result<Self,InvalidCodepoint> {
match char::from_u32(c) {
Some(c) => Ok(c),
None if c > 0x10_ff_ff => Err(InvalidCodepoint::TooHigh),
None => Err(InvalidCodepoint::Utf16Reserved),
}
}
}
// Adapted from https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c
fn overlong(first: u8, second: u8) -> bool {
if first < 0x80 {
false
} else if (first & 0xe0) == 0xc0 {
(first & 0xfe) == 0xc0
} else if (first & 0xf0) == 0xe0 {
first == 0xe0 && (second & 0xe0) == 0x80
} else {
first == 0xf0 && (second & 0xf0) == 0x80
}
}
/// Decodes the codepoint represented by a multi-byte UTF-8 sequence.
///
/// Does not check that the codepoint is valid,
/// and returns `u32` because casting invalid codepoints to `char` is insta UB.
fn merge_nonascii_unchecked_utf8(src: &[u8]) -> u32 {
let mut c = src[0] as u32 & (0x7f >> src.len());
for b in &src[1..] {
c = (c << 6) | (b & 0b0011_1111) as u32;
}
c
}
// Create a `char` from a leading and a trailing surrogate.
unsafe fn combine_surrogates(first: u16, second: u16) -> char {
let high = (first & 0x_03_ff) as u32;
let low = (second & 0x_03_ff) as u32;
let c = ((high << 10) | low) + 0x_01_00_00; // no, the constant can't be or'd in
char::from_u32_unchecked(c)
}
/// Adds `.utf8chars()` and `.utf16chars()` iterator constructors to `&str`.
pub trait StrExt: AsRef<str> {
/// Equivalent to `.chars()` but produces `Utf8Char`s.
fn utf8chars(&self) -> Utf8Chars;
/// Equivalent to `.chars()` but produces `Utf16Char`s.
fn utf16chars(&self) -> Utf16Chars;
/// Equivalent to `.char_indices()` but produces `Utf8Char`s.
fn utf8char_indices(&self) -> Utf8CharIndices;
/// Equivalent to `.char_indices()` but produces `Utf16Char`s.
fn utf16char_indices(&self) -> Utf16CharIndices;
}
impl StrExt for str {
fn utf8chars(&self) -> Utf8Chars {
Utf8Chars::from(self)
}
fn utf16chars(&self) -> Utf16Chars {
Utf16Chars::from(self)
}
fn utf8char_indices(&self) -> Utf8CharIndices {
Utf8CharIndices::from(self)
}
fn utf16char_indices(&self) -> Utf16CharIndices {
Utf16CharIndices::from(self)
}
}
#[cfg(feature="ascii")]
impl StrExt for AsciiStr {
fn utf8chars(&self) -> Utf8Chars {
Utf8Chars::from(self.as_str())
}
fn utf16chars(&self) -> Utf16Chars {
Utf16Chars::from(self.as_str())
}
fn utf8char_indices(&self) -> Utf8CharIndices {
Utf8CharIndices::from(self.as_str())
}
fn utf16char_indices(&self) -> Utf16CharIndices {
Utf16CharIndices::from(self.as_str())
}
}
/// Iterator methods that convert between `u8`s and `Utf8Char` or `u16`s and `Utf16Char`
///
/// All the iterator adapters also accept iterators that produce references of
/// the type they convert from.
pub trait IterExt: Iterator+Sized {
/// Converts an iterator of `Utf8Char`s or `&Utf8Char`s to an iterator of
/// `u8`s.
///
/// Has the same effect as `.flat_map()` or `.flatten()`, but the returned
/// iterator is ~40% faster.
///
/// The iterator also implements `Read`
/// (when the `std` feature isn't disabled).
/// Reading will never produce an error, and calls to `.read()` and `.next()`
/// can be mixed.
///
/// The exact number of bytes cannot be known in advance, but `size_hint()`
/// gives the possible range.
/// (min: all remaining characters are ASCII, max: all require four bytes)
///
/// # Examples
///
/// From iterator of values:
///
/// ```
/// use encode_unicode::{IterExt, StrExt};
///
/// let iterator = "foo".utf8chars();
/// let mut bytes = [0; 4];
/// for (u,dst) in iterator.to_bytes().zip(&mut bytes) {*dst=u;}
/// assert_eq!(&bytes, b"foo\0");
/// ```
///
/// From iterator of references:
///
#[cfg_attr(feature="std", doc=" ```")]
#[cfg_attr(not(feature="std"), doc=" ```no_compile")]
/// use encode_unicode::{IterExt, StrExt, Utf8Char};
///
/// let chars: Vec<Utf8Char> = "💣 bomb 💣".utf8chars().collect();
/// let bytes: Vec<u8> = chars.iter().to_bytes().collect();
/// let flat_map: Vec<u8> = chars.iter().flat_map(|u8c| *u8c ).collect();
/// assert_eq!(bytes, flat_map);
/// ```
///
/// `Read`ing from it:
///
#[cfg_attr(feature="std", doc=" ```")]
#[cfg_attr(not(feature="std"), doc=" ```no_compile")]
/// use encode_unicode::{IterExt, StrExt};
/// use std::io::Read;
///
/// let s = "Ååh‽";
/// assert_eq!(s.len(), 8);
/// let mut buf = [b'E'; 9];
/// let mut reader = s.utf8chars().to_bytes();
/// assert_eq!(reader.read(&mut buf[..]).unwrap(), 8);
/// assert_eq!(reader.read(&mut buf[..]).unwrap(), 0);
/// assert_eq!(&buf[..8], s.as_bytes());
/// assert_eq!(buf[8], b'E');
/// ```
fn to_bytes(self) -> Utf8CharSplitter<Self::Item,Self> where Self::Item: Borrow<Utf8Char>;
/// Converts an iterator of `Utf16Char` (or `&Utf16Char`) to an iterator of
/// `u16`s.
///
/// Has the same effect as `.flat_map()` or `.flatten()`, but the returned
/// iterator is about twice as fast.
///
/// The exact number of units cannot be known in advance, but `size_hint()`
/// gives the possible range.
///
/// # Examples
///
/// From iterator of values:
///
/// ```
/// use encode_unicode::{IterExt, StrExt};
///
/// let iterator = "foo".utf16chars();
/// let mut units = [0; 4];
/// for (u,dst) in iterator.to_units().zip(&mut units) {*dst=u;}
///
/// assert_eq!(units, ['f' as u16, 'o' as u16, 'o' as u16, 0]);
/// ```
///
/// From iterator of references:
///
#[cfg_attr(feature="std", doc=" ```")]
#[cfg_attr(not(feature="std"), doc=" ```no_compile")]
/// use encode_unicode::{IterExt, StrExt, Utf16Char};
///
/// // (💣 takes two units)
/// let chars: Vec<Utf16Char> = "💣 bomb 💣".utf16chars().collect();
/// let units: Vec<u16> = chars.iter().to_units().collect();
/// let flat_map: Vec<u16> = chars.iter().flat_map(|u16c| *u16c ).collect();
///
/// assert_eq!(units, flat_map);
/// ```
fn to_units(self) -> Utf16CharSplitter<Self::Item,Self> where Self::Item: Borrow<Utf16Char>;
/// Decodes bytes as UTF-8 and groups them into `Utf8Char`s
///
/// When errors (invalid values or sequences) are encountered,
/// it continues with the byte right after the start of the error sequence.
/// This is neither the most intelligent choiche (sometimes it is guaranteed to
/// produce another error), nor the easiest to implement, but I believe it to
/// be the most predictable.
/// It also means that ASCII characters are never hidden by errors.
///
/// # Examples
///
/// Replace all errors with u+FFFD REPLACEMENT_CHARACTER:
/// ```
/// use encode_unicode::{Utf8Char, IterExt};
///
/// let mut buf = [b'\0'; 255];
/// let len = b"foo\xCFbar".iter()
/// .to_utf8chars()
/// .flat_map(|r| r.unwrap_or(Utf8Char::from('\u{FFFD}')).into_iter() )
/// .zip(&mut buf[..])
/// .map(|(byte, dst)| *dst = byte )
/// .count();
///
/// assert_eq!(&buf[..len], "foo\u{FFFD}bar".as_bytes());
/// ```
///
/// Collect everything up until the first error into a string:
#[cfg_attr(feature="std", doc=" ```")]
#[cfg_attr(not(feature="std"), doc=" ```no_compile")]
/// use encode_unicode::iterator::Utf8CharMerger;
/// let mut good = String::new();
/// for r in Utf8CharMerger::from(b"foo\xcc\xbbbar\xcc\xddbaz") {
/// if let Ok(uc) = r {
/// good.push_str(uc.as_str());
/// } else {
/// break;
/// }
/// }
/// assert_eq!(good, "foo̻bar");
/// ```
///
/// Abort decoding on error:
#[cfg_attr(feature="std", doc=" ```")]
#[cfg_attr(not(feature="std"), doc=" ```no_compile")]
/// use encode_unicode::{IterExt, Utf8Char};
/// use encode_unicode::error::{InvalidUtf8Slice, InvalidUtf8};
///
/// let result = b"ab\0\xe0\xbc\xa9 \xf3\x80\x77".iter()
/// .to_utf8chars()
/// .collect::<Result<String,InvalidUtf8Slice>>();
///
/// assert_eq!(result, Err(InvalidUtf8Slice::Utf8(InvalidUtf8::NotAContinuationByte(2))));
/// ```
fn to_utf8chars(self) -> Utf8CharMerger<Self::Item,Self> where Self::Item: Borrow<u8>;
/// Decodes bytes as UTF-16 and groups them into `Utf16Char`s
///
/// When errors (unmatched leading surrogates or unexpected trailing surrogates)
/// are encountered, an error is produced for every unit.
///
/// # Examples
///
/// Replace errors with '�':
#[cfg_attr(feature="std", doc=" ```")]
#[cfg_attr(not(feature="std"), doc=" ```no_compile")]
/// use encode_unicode::{IterExt, Utf16Char};
///
/// let slice = &['a' as u16, 0xdf00, 0xd83c, 0xdca0][..];
/// let string = slice.iter()
/// .to_utf16chars()
/// .map(|r| r.unwrap_or(Utf16Char::from('\u{fffd}')) ) // REPLACEMENT_CHARACTER
/// .collect::<String>();
///
/// assert_eq!(string, "a�🂠");
/// ```
///
/// ```
/// use encode_unicode::{IterExt, Utf16Char};
/// use encode_unicode::error::Utf16PairError::*;
///
/// let slice = [0xdcba, 0xdeff, 0xd8be, 0xdeee, 'Y' as u16, 0xdab1, 0xdab1];
/// let mut iter = slice.iter().to_utf16chars();
/// assert_eq!(iter.size_hint(), (3, Some(7)));
/// assert_eq!(iter.next(), Some(Err(UnexpectedTrailingSurrogate)));
/// assert_eq!(iter.next(), Some(Err(UnexpectedTrailingSurrogate)));
/// assert_eq!(iter.next(), Some(Ok(Utf16Char::from('\u{3faee}'))));
/// assert_eq!(iter.next(), Some(Ok(Utf16Char::from('Y'))));
/// assert_eq!(iter.next(), Some(Err(UnmatchedLeadingSurrogate)));
/// assert_eq!(iter.next(), Some(Err(Incomplete)));
/// assert_eq!(iter.into_remaining_units().next(), None);
/// ```
///
/// Search for a codepoint and return the codepoint index of the first match:
/// ```
/// use encode_unicode::{IterExt, Utf16Char};
///
/// let position = [0xd875, 0xdd4f, '≈' as u16, '2' as u16].iter()
/// .to_utf16chars()
/// .position(|r| r == Ok(Utf16Char::from('≈')) );
///
/// assert_eq!(position, Some(1));
/// ```
fn to_utf16chars(self) -> Utf16CharMerger<Self::Item,Self> where Self::Item: Borrow<u16>;
}
impl<I:Iterator> IterExt for I {
fn to_bytes(self) -> Utf8CharSplitter<Self::Item,Self> where Self::Item: Borrow<Utf8Char> {
iter_bytes(self)
}
fn to_units(self) -> Utf16CharSplitter<Self::Item,Self> where Self::Item: Borrow<Utf16Char> {
iter_units(self)
}
fn to_utf8chars(self) -> Utf8CharMerger<Self::Item,Self> where Self::Item: Borrow<u8> {
Utf8CharMerger::from(self)
}
fn to_utf16chars(self) -> Utf16CharMerger<Self::Item,Self> where Self::Item: Borrow<u16> {
Utf16CharMerger::from(self)
}
}
/// Methods for iterating over `u8` and `u16` slices as UTF-8 or UTF-16 characters.
///
/// The iterators are slightly faster than the similar methods in [`IterExt`](trait.IterExt.html)
/// because they con "push back" items for free after errors and don't need a
/// separate buffer that must be checked on every call to `.next()`.
pub trait SliceExt: Index<RangeFull> {
/// Decode `u8` slices as UTF-8 and iterate over the codepoints as `Utf8Char`s,
///
/// # Examples
///
/// Get the index and error type of the first error:
#[cfg_attr(feature="std", doc=" ```")]
#[cfg_attr(not(feature="std"), doc=" ```no_compile")]
/// use encode_unicode::{SliceExt, Utf8Char};
/// use encode_unicode::error::InvalidUtf8Slice;
///
/// let slice = b"ab\0\xe0\xbc\xa9 \xf3\x80\x77";
/// let result = slice.utf8char_indices()
/// .map(|(offset,r,length)| r.map_err(|e| (offset,e,length) ) )
/// .collect::<Result<String,(usize,InvalidUtf8Slice,usize)>>();
///
/// assert_eq!(result, Err((7, InvalidUtf8Slice::TooShort(4), 1)));
/// ```
///
/// ```
/// use encode_unicode::{SliceExt, Utf8Char};
/// use std::error::Error;
///
/// let slice = b"\xf0\xbf\xbf\xbfXY\xdd\xbb\xe1\x80\x99quux123";
/// let mut fixed_size = [Utf8Char::default(); 8];
/// for (cp_i, (byte_index, r, _)) in slice.utf8char_indices().enumerate().take(8) {
/// match r {
/// Ok(u8c) => fixed_size[cp_i] = u8c,
/// Err(e) => panic!("Invalid codepoint at index {} ({})", cp_i, e.description()),
/// }
/// }
/// let chars = ['\u{3ffff}', 'X', 'Y', '\u{77b}', '\u{1019}', 'q', 'u', 'u'];
/// assert_eq!(fixed_size, chars);
/// ```
///
#[cfg_attr(feature="std", doc=" ```")]
#[cfg_attr(not(feature="std"), doc=" ```no_compile")]
/// use encode_unicode::{SliceExt, Utf8Char};
/// use encode_unicode::error::InvalidUtf8Slice::*;
/// use encode_unicode::error::{InvalidUtf8, InvalidUtf8FirstByte, InvalidCodepoint};
///
/// let bytes = b"\xfa-\xf4\x8f\xee\xa1\x8f-\xed\xa9\x87\xf0\xcc\xbb";
/// let mut errors = Vec::new();
/// let mut lengths = Vec::new();
/// let mut string = String::new();
/// for (offset,result,length) in bytes.utf8char_indices() {
/// lengths.push((offset,length));
/// let c = result.unwrap_or_else(|error| {
/// errors.push((offset,error));
/// Utf8Char::from('\u{fffd}') // replacement character
/// });
/// string.push_str(c.as_str());
/// }
///
/// assert_eq!(string, "�-��\u{e84f}-����\u{33b}");
/// assert_eq!(lengths, [(0,1), (1,1), (2,1), (3,1), (4,3), (7,1),
/// (8,1), (9,1), (10,1), (11,1), (12,2)]);
/// assert_eq!(errors, [
/// ( 0, Utf8(InvalidUtf8::FirstByte(InvalidUtf8FirstByte::TooLongSeqence))),
/// ( 2, Utf8(InvalidUtf8::NotAContinuationByte(2))),
/// ( 3, Utf8(InvalidUtf8::FirstByte(InvalidUtf8FirstByte::ContinuationByte))),
/// ( 8, Codepoint(InvalidCodepoint::Utf16Reserved)),
/// ( 9, Utf8(InvalidUtf8::FirstByte(InvalidUtf8FirstByte::ContinuationByte))),
/// (10, Utf8(InvalidUtf8::FirstByte(InvalidUtf8FirstByte::ContinuationByte))),
/// (11, TooShort(4)), // (but it was not the last element returned!)
/// ]);
/// ```
fn utf8char_indices(&self) -> Utf8CharDecoder where Self::Output: Borrow<[u8]>;
/// Decode `u16` slices as UTF-16 and iterate over the codepoints as `Utf16Char`s,
///
/// The iterator produces `(usize,Result<Utf16Char,Utf16Error>,usize)`,
/// and the slice is validated as you go.
///
/// The first `usize` contains the offset from the start of the slice and
/// the last `usize` contains the length of the codepoint or error.
/// The length is either 1 or 2, and always 1 for errors.
///
/// # Examples
///
#[cfg_attr(feature="std", doc=" ```")]
#[cfg_attr(not(feature="std"), doc=" ```no_compile")]
/// use encode_unicode::{SliceExt, Utf8Char};
///
/// let slice = &['a' as u16, 0xdf00, 0xd83c, 0xdca0][..];
/// let mut errors = Vec::new();
/// let string = slice.utf16char_indices().map(|(offset,r,_)| match r {
/// Ok(u16c) => Utf8Char::from(u16c),
/// Err(_) => {
/// errors.push(offset);
/// Utf8Char::from('\u{fffd}') // REPLACEMENT_CHARACTER
/// }
/// }).collect::<String>();
///
/// assert_eq!(string, "a�🂠");
/// assert_eq!(errors, [1]);
/// ```
///
/// Search for a codepoint and return its unit and codepoint index.
/// ```
/// use encode_unicode::{SliceExt, Utf16Char};
///
/// let slice = [0xd875,/*'𝕏'*/ 0xdd4f, '≈' as u16, '2' as u16];
/// let position = slice.utf16char_indices()
/// .enumerate()
/// .find(|&(_,(_,r,_))| r == Ok(Utf16Char::from('≈')) )
/// .map(|(codepoint, (offset, _, _))| (codepoint, offset) );
///
/// assert_eq!(position, Some((1,2)));
/// ```
///
/// Error types:
/// ```
/// use encode_unicode::{SliceExt, Utf16Char};
/// use encode_unicode::error::Utf16PairError::*;
///
/// let slice = [0xdcba, 0xdeff, 0xd8be, 0xdeee, 'λ' as u16, 0xdab1, 0xdab1];
/// let mut iter = slice.utf16char_indices();
/// assert_eq!(iter.next(), Some((0, Err(UnexpectedTrailingSurrogate), 1)));
/// assert_eq!(iter.next(), Some((1, Err(UnexpectedTrailingSurrogate), 1)));
/// assert_eq!(iter.next(), Some((2, Ok(Utf16Char::from('\u{3faee}')), 2)));
/// assert_eq!(iter.next(), Some((4, Ok(Utf16Char::from('λ')), 1)));
/// assert_eq!(iter.next(), Some((5, Err(UnmatchedLeadingSurrogate), 1)));
/// assert_eq!(iter.next(), Some((6, Err(Incomplete), 1)));
/// assert_eq!(iter.next(), None);
/// assert_eq!(iter.as_slice(), [])
/// ```
fn utf16char_indices(&self) -> Utf16CharDecoder where Self::Output: Borrow<[u16]>;
}
impl<S: ?Sized+Index<RangeFull>> SliceExt for S {
fn utf8char_indices(&self) -> Utf8CharDecoder where Self::Output: Borrow<[u8]> {
Utf8CharDecoder::from(self[..].borrow())
}
fn utf16char_indices(&self) -> Utf16CharDecoder where Self::Output: Borrow<[u16]> {
Utf16CharDecoder::from(self[..].borrow())
}
}