Utf8Lossy type with chunks iterator and impl Display and Debug
This commit is contained in:
@@ -61,8 +61,8 @@ use core::hash;
|
|||||||
use core::iter::{FromIterator, FusedIterator};
|
use core::iter::{FromIterator, FusedIterator};
|
||||||
use core::ops::{self, Add, AddAssign, Index, IndexMut};
|
use core::ops::{self, Add, AddAssign, Index, IndexMut};
|
||||||
use core::ptr;
|
use core::ptr;
|
||||||
use core::str as core_str;
|
|
||||||
use core::str::pattern::Pattern;
|
use core::str::pattern::Pattern;
|
||||||
|
use std_unicode::lossy;
|
||||||
use std_unicode::char::{decode_utf16, REPLACEMENT_CHARACTER};
|
use std_unicode::char::{decode_utf16, REPLACEMENT_CHARACTER};
|
||||||
|
|
||||||
use borrow::{Cow, ToOwned};
|
use borrow::{Cow, ToOwned};
|
||||||
@@ -533,111 +533,34 @@ impl String {
|
|||||||
/// ```
|
/// ```
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
pub fn from_utf8_lossy<'a>(v: &'a [u8]) -> Cow<'a, str> {
|
pub fn from_utf8_lossy<'a>(v: &'a [u8]) -> Cow<'a, str> {
|
||||||
let mut i;
|
let mut iter = lossy::Utf8Lossy::from_bytes(v).chunks();
|
||||||
match str::from_utf8(v) {
|
|
||||||
Ok(s) => return Cow::Borrowed(s),
|
let (first_valid, first_broken) = if let Some(chunk) = iter.next() {
|
||||||
Err(e) => i = e.valid_up_to(),
|
let lossy::Utf8LossyChunk { valid, broken } = chunk;
|
||||||
|
if valid.len() == v.len() {
|
||||||
|
debug_assert!(broken.is_empty());
|
||||||
|
return Cow::Borrowed(valid);
|
||||||
}
|
}
|
||||||
|
(valid, broken)
|
||||||
const TAG_CONT_U8: u8 = 128;
|
|
||||||
const REPLACEMENT: &'static [u8] = b"\xEF\xBF\xBD"; // U+FFFD in UTF-8
|
|
||||||
let total = v.len();
|
|
||||||
fn unsafe_get(xs: &[u8], i: usize) -> u8 {
|
|
||||||
unsafe { *xs.get_unchecked(i) }
|
|
||||||
}
|
|
||||||
fn safe_get(xs: &[u8], i: usize, total: usize) -> u8 {
|
|
||||||
if i >= total { 0 } else { unsafe_get(xs, i) }
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut res = String::with_capacity(total);
|
|
||||||
|
|
||||||
if i > 0 {
|
|
||||||
unsafe { res.as_mut_vec().extend_from_slice(&v[..i]) };
|
|
||||||
}
|
|
||||||
|
|
||||||
// subseqidx is the index of the first byte of the subsequence we're
|
|
||||||
// looking at. It's used to copy a bunch of contiguous good codepoints
|
|
||||||
// at once instead of copying them one by one.
|
|
||||||
let mut subseqidx = i;
|
|
||||||
|
|
||||||
while i < total {
|
|
||||||
let i_ = i;
|
|
||||||
let byte = unsafe_get(v, i);
|
|
||||||
i += 1;
|
|
||||||
|
|
||||||
macro_rules! error { () => ({
|
|
||||||
unsafe {
|
|
||||||
if subseqidx != i_ {
|
|
||||||
res.as_mut_vec().extend_from_slice(&v[subseqidx..i_]);
|
|
||||||
}
|
|
||||||
subseqidx = i;
|
|
||||||
res.as_mut_vec().extend_from_slice(REPLACEMENT);
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
|
|
||||||
if byte < 128 {
|
|
||||||
// subseqidx handles this
|
|
||||||
} else {
|
} else {
|
||||||
let w = core_str::utf8_char_width(byte);
|
return Cow::Borrowed("");
|
||||||
|
};
|
||||||
|
|
||||||
match w {
|
const REPLACEMENT: &'static str = "\u{FFFD}";
|
||||||
2 => {
|
|
||||||
if safe_get(v, i, total) & 192 != TAG_CONT_U8 {
|
let mut res = String::with_capacity(v.len());
|
||||||
error!();
|
res.push_str(first_valid);
|
||||||
continue;
|
if !first_broken.is_empty() {
|
||||||
|
res.push_str(REPLACEMENT);
|
||||||
}
|
}
|
||||||
i += 1;
|
|
||||||
}
|
for lossy::Utf8LossyChunk { valid, broken } in iter {
|
||||||
3 => {
|
res.push_str(valid);
|
||||||
match (byte, safe_get(v, i, total)) {
|
if !broken.is_empty() {
|
||||||
(0xE0, 0xA0...0xBF) => (),
|
res.push_str(REPLACEMENT);
|
||||||
(0xE1...0xEC, 0x80...0xBF) => (),
|
|
||||||
(0xED, 0x80...0x9F) => (),
|
|
||||||
(0xEE...0xEF, 0x80...0xBF) => (),
|
|
||||||
_ => {
|
|
||||||
error!();
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i += 1;
|
|
||||||
if safe_get(v, i, total) & 192 != TAG_CONT_U8 {
|
|
||||||
error!();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
4 => {
|
|
||||||
match (byte, safe_get(v, i, total)) {
|
|
||||||
(0xF0, 0x90...0xBF) => (),
|
|
||||||
(0xF1...0xF3, 0x80...0xBF) => (),
|
|
||||||
(0xF4, 0x80...0x8F) => (),
|
|
||||||
_ => {
|
|
||||||
error!();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i += 1;
|
|
||||||
if safe_get(v, i, total) & 192 != TAG_CONT_U8 {
|
|
||||||
error!();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
i += 1;
|
|
||||||
if safe_get(v, i, total) & 192 != TAG_CONT_U8 {
|
|
||||||
error!();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
error!();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if subseqidx < total {
|
|
||||||
unsafe { res.as_mut_vec().extend_from_slice(&v[subseqidx..total]) };
|
|
||||||
}
|
|
||||||
Cow::Owned(res)
|
Cow::Owned(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,5 +9,9 @@ path = "lib.rs"
|
|||||||
test = false
|
test = false
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
name = "std_unicode_tests"
|
||||||
|
path = "tests/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
core = { path = "../libcore" }
|
core = { path = "../libcore" }
|
||||||
|
|||||||
@@ -34,6 +34,8 @@
|
|||||||
|
|
||||||
#![feature(char_escape_debug)]
|
#![feature(char_escape_debug)]
|
||||||
#![feature(core_char_ext)]
|
#![feature(core_char_ext)]
|
||||||
|
#![feature(str_internals)]
|
||||||
|
#![feature(core_intrinsics)]
|
||||||
#![feature(decode_utf8)]
|
#![feature(decode_utf8)]
|
||||||
#![feature(fused)]
|
#![feature(fused)]
|
||||||
#![feature(fn_traits)]
|
#![feature(fn_traits)]
|
||||||
@@ -45,6 +47,7 @@
|
|||||||
mod tables;
|
mod tables;
|
||||||
mod u_str;
|
mod u_str;
|
||||||
pub mod char;
|
pub mod char;
|
||||||
|
pub mod lossy;
|
||||||
|
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
pub mod str {
|
pub mod str {
|
||||||
|
|||||||
198
src/libstd_unicode/lossy.rs
Normal file
198
src/libstd_unicode/lossy.rs
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
// Copyright 2012-2017 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// 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. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
use core::str as core_str;
|
||||||
|
use core::fmt;
|
||||||
|
use core::fmt::Write;
|
||||||
|
use char;
|
||||||
|
use core::intrinsics;
|
||||||
|
|
||||||
|
|
||||||
|
/// Lossy UTF-8 string.
|
||||||
|
#[unstable(feature = "str_internals", issue = "0")]
|
||||||
|
pub struct Utf8Lossy {
|
||||||
|
bytes: [u8]
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Utf8Lossy {
|
||||||
|
pub fn from_str(s: &str) -> &Utf8Lossy {
|
||||||
|
Utf8Lossy::from_bytes(s.as_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_bytes(bytes: &[u8]) -> &Utf8Lossy {
|
||||||
|
unsafe { intrinsics::transmute(bytes) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn chunks(&self) -> Utf8LossyChunksIter {
|
||||||
|
Utf8LossyChunksIter { source: &self.bytes }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Iterator over lossy UTF-8 string
|
||||||
|
#[unstable(feature = "str_internals", issue = "0")]
|
||||||
|
pub struct Utf8LossyChunksIter<'a> {
|
||||||
|
source: &'a [u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unstable(feature = "str_internals", issue = "0")]
|
||||||
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
|
pub struct Utf8LossyChunk<'a> {
|
||||||
|
/// Sequence of valid chars.
|
||||||
|
/// Can be empty between broken UTF-8 chars.
|
||||||
|
pub valid: &'a str,
|
||||||
|
/// Single broken char, empty if none.
|
||||||
|
/// Empty iff iterator item is last.
|
||||||
|
pub broken: &'a [u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for Utf8LossyChunksIter<'a> {
|
||||||
|
type Item = Utf8LossyChunk<'a>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Utf8LossyChunk<'a>> {
|
||||||
|
if self.source.len() == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TAG_CONT_U8: u8 = 128;
|
||||||
|
fn unsafe_get(xs: &[u8], i: usize) -> u8 {
|
||||||
|
unsafe { *xs.get_unchecked(i) }
|
||||||
|
}
|
||||||
|
fn safe_get(xs: &[u8], i: usize) -> u8 {
|
||||||
|
if i >= xs.len() { 0 } else { unsafe_get(xs, i) }
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
while i < self.source.len() {
|
||||||
|
let i_ = i;
|
||||||
|
|
||||||
|
let byte = unsafe_get(self.source, i);
|
||||||
|
i += 1;
|
||||||
|
|
||||||
|
if byte < 128 {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
let w = core_str::utf8_char_width(byte);
|
||||||
|
|
||||||
|
macro_rules! error { () => ({
|
||||||
|
unsafe {
|
||||||
|
let r = Utf8LossyChunk {
|
||||||
|
valid: core_str::from_utf8_unchecked(&self.source[0..i_]),
|
||||||
|
broken: &self.source[i_..i],
|
||||||
|
};
|
||||||
|
self.source = &self.source[i..];
|
||||||
|
return Some(r);
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
|
||||||
|
match w {
|
||||||
|
2 => {
|
||||||
|
if safe_get(self.source, i) & 192 != TAG_CONT_U8 {
|
||||||
|
error!();
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
3 => {
|
||||||
|
match (byte, safe_get(self.source, i)) {
|
||||||
|
(0xE0, 0xA0 ... 0xBF) => (),
|
||||||
|
(0xE1 ... 0xEC, 0x80 ... 0xBF) => (),
|
||||||
|
(0xED, 0x80 ... 0x9F) => (),
|
||||||
|
(0xEE ... 0xEF, 0x80 ... 0xBF) => (),
|
||||||
|
_ => {
|
||||||
|
error!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
if safe_get(self.source, i) & 192 != TAG_CONT_U8 {
|
||||||
|
error!();
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
4 => {
|
||||||
|
match (byte, safe_get(self.source, i)) {
|
||||||
|
(0xF0, 0x90 ... 0xBF) => (),
|
||||||
|
(0xF1 ... 0xF3, 0x80 ... 0xBF) => (),
|
||||||
|
(0xF4, 0x80 ... 0x8F) => (),
|
||||||
|
_ => {
|
||||||
|
error!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
if safe_get(self.source, i) & 192 != TAG_CONT_U8 {
|
||||||
|
error!();
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
if safe_get(self.source, i) & 192 != TAG_CONT_U8 {
|
||||||
|
error!();
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
error!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let r = Utf8LossyChunk {
|
||||||
|
valid: unsafe { core_str::from_utf8_unchecked(self.source) },
|
||||||
|
broken: &[],
|
||||||
|
};
|
||||||
|
self.source = &[];
|
||||||
|
return Some(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl fmt::Display for Utf8Lossy {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
for Utf8LossyChunk { valid, broken } in self.chunks() {
|
||||||
|
f.write_str(valid)?;
|
||||||
|
if !broken.is_empty() {
|
||||||
|
f.write_char(char::REPLACEMENT_CHARACTER)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Utf8Lossy {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
f.write_char('"')?;
|
||||||
|
|
||||||
|
for Utf8LossyChunk { valid, broken } in self.chunks() {
|
||||||
|
|
||||||
|
// Valid part.
|
||||||
|
// Here we partially parse UTF-8 again which is suboptimal.
|
||||||
|
{
|
||||||
|
let mut from = 0;
|
||||||
|
for (i, c) in valid.char_indices() {
|
||||||
|
let esc = c.escape_debug();
|
||||||
|
// If char needs escaping, flush backlog so far and write, else skip
|
||||||
|
if esc.len() != 1 {
|
||||||
|
f.write_str(&valid[from..i])?;
|
||||||
|
for c in esc {
|
||||||
|
f.write_char(c)?;
|
||||||
|
}
|
||||||
|
from = i + c.len_utf8();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.write_str(&valid[from..])?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Broken parts of string as hex escape.
|
||||||
|
for &b in broken {
|
||||||
|
write!(f, "\\x{:02x}", b)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f.write_char('"')
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/libstd_unicode/tests/lib.rs
Normal file
15
src/libstd_unicode/tests/lib.rs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// Copyright 2012-2017 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// 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. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
#![feature(str_internals, unicode)]
|
||||||
|
|
||||||
|
extern crate std_unicode;
|
||||||
|
|
||||||
|
mod lossy;
|
||||||
91
src/libstd_unicode/tests/lossy.rs
Normal file
91
src/libstd_unicode/tests/lossy.rs
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
// Copyright 2012-2017 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// 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. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
use std_unicode::lossy::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn chunks() {
|
||||||
|
let mut iter = Utf8Lossy::from_bytes(b"hello").chunks();
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "hello", broken: b"", }), iter.next());
|
||||||
|
assert_eq!(None, iter.next());
|
||||||
|
|
||||||
|
let mut iter = Utf8Lossy::from_bytes("ศไทย中华Việt Nam".as_bytes()).chunks();
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "ศไทย中华Việt Nam", broken: b"", }), iter.next());
|
||||||
|
assert_eq!(None, iter.next());
|
||||||
|
|
||||||
|
let mut iter = Utf8Lossy::from_bytes(b"Hello\xC2 There\xFF Goodbye").chunks();
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "Hello", broken: b"\xC2", }), iter.next());
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: " There", broken: b"\xFF", }), iter.next());
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: " Goodbye", broken: b"", }), iter.next());
|
||||||
|
assert_eq!(None, iter.next());
|
||||||
|
|
||||||
|
let mut iter = Utf8Lossy::from_bytes(b"Hello\xC0\x80 There\xE6\x83 Goodbye").chunks();
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "Hello", broken: b"\xC0", }), iter.next());
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\x80", }), iter.next());
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: " There", broken: b"\xE6\x83", }), iter.next());
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: " Goodbye", broken: b"", }), iter.next());
|
||||||
|
assert_eq!(None, iter.next());
|
||||||
|
|
||||||
|
let mut iter = Utf8Lossy::from_bytes(b"\xF5foo\xF5\x80bar").chunks();
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\xF5", }), iter.next());
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "foo", broken: b"\xF5", }), iter.next());
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\x80", }), iter.next());
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "bar", broken: b"", }), iter.next());
|
||||||
|
assert_eq!(None, iter.next());
|
||||||
|
|
||||||
|
let mut iter = Utf8Lossy::from_bytes(b"\xF1foo\xF1\x80bar\xF1\x80\x80baz").chunks();
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\xF1", }), iter.next());
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "foo", broken: b"\xF1\x80", }), iter.next());
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "bar", broken: b"\xF1\x80\x80", }), iter.next());
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "baz", broken: b"", }), iter.next());
|
||||||
|
assert_eq!(None, iter.next());
|
||||||
|
|
||||||
|
let mut iter = Utf8Lossy::from_bytes(b"\xF4foo\xF4\x80bar\xF4\xBFbaz").chunks();
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\xF4", }), iter.next());
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "foo", broken: b"\xF4\x80", }), iter.next());
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "bar", broken: b"\xF4", }), iter.next());
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\xBF", }), iter.next());
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "baz", broken: b"", }), iter.next());
|
||||||
|
assert_eq!(None, iter.next());
|
||||||
|
|
||||||
|
let mut iter = Utf8Lossy::from_bytes(b"\xF0\x80\x80\x80foo\xF0\x90\x80\x80bar").chunks();
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\xF0", }), iter.next());
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\x80", }), iter.next());
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\x80", }), iter.next());
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\x80", }), iter.next());
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "foo\u{10000}bar", broken: b"", }), iter.next());
|
||||||
|
assert_eq!(None, iter.next());
|
||||||
|
|
||||||
|
// surrogates
|
||||||
|
let mut iter = Utf8Lossy::from_bytes(b"\xED\xA0\x80foo\xED\xBF\xBFbar").chunks();
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\xED", }), iter.next());
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\xA0", }), iter.next());
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\x80", }), iter.next());
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "foo", broken: b"\xED", }), iter.next());
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\xBF", }), iter.next());
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\xBF", }), iter.next());
|
||||||
|
assert_eq!(Some(Utf8LossyChunk { valid: "bar", broken: b"", }), iter.next());
|
||||||
|
assert_eq!(None, iter.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn display() {
|
||||||
|
assert_eq!(
|
||||||
|
"Hello\u{FFFD}\u{FFFD} There\u{FFFD} Goodbye",
|
||||||
|
&format!("{}", Utf8Lossy::from_bytes(b"Hello\xC0\x80 There\xE6\x83 Goodbye")));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn debug() {
|
||||||
|
assert_eq!(
|
||||||
|
"\"Hello\\xc0\\x80 There\\xe6\\x83 Goodbye\\u{10d4ea}\"",
|
||||||
|
&format!("{:?}", Utf8Lossy::from_bytes(
|
||||||
|
b"Hello\xC0\x80 There\xE6\x83 Goodbye\xf4\x8d\x93\xaa")));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user