From 4776bfc291c310e75ba04d39bf476013c5a29523 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Tue, 31 May 2016 16:49:20 +0200 Subject: [PATCH] Replace Read::chars() by a private copy. --- src/char_iter.rs | 106 +++++++++++++++++++++++++++++++++++++++++++++++ src/error.rs | 8 ++-- src/lib.rs | 16 ++++--- 3 files changed, 118 insertions(+), 12 deletions(-) create mode 100644 src/char_iter.rs diff --git a/src/char_iter.rs b/src/char_iter.rs new file mode 100644 index 00000000..7d0c46de --- /dev/null +++ b/src/char_iter.rs @@ -0,0 +1,106 @@ +//! An iterator over the `char`s of a reader. +//! +//! A copy of the unstable code from the stdlib's std::io::Read::chars. + +use std::error; +use std::fmt; +use std::io; +use std::io::Read; +use std::str; + +pub fn chars<R: Read>(read: R) -> Chars<R> where R: Sized { + Chars { inner: read } +} + +// https://tools.ietf.org/html/rfc3629 +static UTF8_CHAR_WIDTH: [u8; 256] = [ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x1F + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x3F + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x5F + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x7F + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x9F + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xBF + 0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xDF + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, // 0xEF + 4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0, // 0xFF +]; + +/// Given a first byte, determine how many bytes are in this UTF-8 character +#[inline] +fn utf8_char_width(b: u8) -> usize { + return UTF8_CHAR_WIDTH[b as usize] as usize; +} + +pub struct Chars<R> { + inner: R, +} + +#[derive(Debug)] +pub enum CharsError { + NotUtf8, + Other(io::Error), +} + +impl<R: Read> Iterator for Chars<R> { + type Item = Result<char, CharsError>; + + fn next(&mut self) -> Option<Result<char, CharsError>> { + let mut buf = [0]; + let first_byte = match self.inner.read(&mut buf) { + Ok(0) => return None, + Ok(..) => buf[0], + Err(e) => return Some(Err(CharsError::Other(e))), + }; + let width = utf8_char_width(first_byte); + if width == 1 { return Some(Ok(first_byte as char)) } + if width == 0 { return Some(Err(CharsError::NotUtf8)) } + let mut buf = [first_byte, 0, 0, 0]; + { + let mut start = 1; + while start < width { + match self.inner.read(&mut buf[start..width]) { + Ok(0) => return Some(Err(CharsError::NotUtf8)), + Ok(n) => start += n, + Err(e) => return Some(Err(CharsError::Other(e))), + } + } + } + Some(match str::from_utf8(&buf[..width]).ok() { + Some(s) => Ok(s.chars().next().unwrap()), + None => Err(CharsError::NotUtf8), + }) + } +} + +impl error::Error for CharsError { + fn description(&self) -> &str { + match *self { + CharsError::NotUtf8 => "invalid utf8 encoding", + CharsError::Other(ref e) => error::Error::description(e), + } + } + fn cause(&self) -> Option<&error::Error> { + match *self { + CharsError::NotUtf8 => None, + CharsError::Other(ref e) => e.cause(), + } + } +} + +impl fmt::Display for CharsError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + CharsError::NotUtf8 => { + "byte stream did not contain valid utf8".fmt(f) + } + CharsError::Other(ref e) => e.fmt(f), + } + } +} diff --git a/src/error.rs b/src/error.rs index d4654a09..844805b5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,6 +4,8 @@ use std::error; use std::fmt; use nix; +use char_iter; + /// The error type for Rustyline errors that can arise from /// I/O related errors or Errno when using the nix-rust library #[derive(Debug)] @@ -13,7 +15,7 @@ pub enum ReadlineError { /// Error from syscall Errno(nix::Error), /// Chars Error - Char(io::CharsError), + Char(char_iter::CharsError), /// EOF (Ctrl-d) Eof, /// Ctrl-C @@ -56,8 +58,8 @@ impl From<nix::Error> for ReadlineError { } } -impl From<io::CharsError> for ReadlineError { - fn from(err: io::CharsError) -> ReadlineError { +impl From<char_iter::CharsError> for ReadlineError { + fn from(err: char_iter::CharsError) -> ReadlineError { ReadlineError::Char(err) } } diff --git a/src/lib.rs b/src/lib.rs index 14b8de74..2c717294 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,6 @@ //! Err(_) => println!("No input"), //! } //! ``` -#![feature(io)] #![cfg_attr(feature="clippy", feature(plugin))] #![cfg_attr(feature="clippy", plugin(clippy))] @@ -30,9 +29,10 @@ pub mod error; pub mod history; mod kill_ring; pub mod line_buffer; +mod char_iter; use std::fmt; -use std::io::{self, Read, Write}; +use std::io::{self, Write}; use std::mem; use std::path::Path; use std::result; @@ -525,7 +525,7 @@ fn edit_history_next(s: &mut State, history: &History, prev: bool) -> Result<()> } /// Completes the line/word -fn complete_line<R: io::Read>(chars: &mut io::Chars<R>, +fn complete_line<R: io::Read>(chars: &mut char_iter::Chars<R>, s: &mut State, completer: &Completer) -> Result<Option<char>> { @@ -578,7 +578,7 @@ fn complete_line<R: io::Read>(chars: &mut io::Chars<R>, /// Incremental search #[cfg_attr(feature="clippy", allow(if_not_else))] -fn reverse_incremental_search<R: io::Read>(chars: &mut io::Chars<R>, +fn reverse_incremental_search<R: io::Read>(chars: &mut char_iter::Chars<R>, s: &mut State, history: &History) -> Result<Option<KeyPress>> { @@ -655,7 +655,7 @@ fn reverse_incremental_search<R: io::Read>(chars: &mut io::Chars<R>, Ok(Some(key)) } -fn escape_sequence<R: io::Read>(chars: &mut io::Chars<R>) -> Result<KeyPress> { +fn escape_sequence<R: io::Read>(chars: &mut char_iter::Chars<R>) -> Result<KeyPress> { // Read the next two bytes representing the escape sequence. let seq1 = try!(chars.next().unwrap()); if seq1 == '[' { @@ -733,7 +733,7 @@ fn readline_edit(prompt: &str, kill_ring.reset(); let mut s = State::new(&mut stdout, prompt, MAX_LINE, get_columns(), history.len()); let stdin = io::stdin(); - let mut chars = stdin.lock().chars(); + let mut chars = char_iter::chars(stdin.lock()); loop { let c = chars.next().unwrap(); if c.is_err() && SIGWINCH.compare_and_swap(true, false, atomic::Ordering::SeqCst) { @@ -1162,12 +1162,10 @@ mod test { #[test] fn complete_line() { - use std::io::Read; - let mut out = ::std::io::sink(); let mut s = init_state(&mut out, "rus", 3, 80); let input = b"\n"; - let mut chars = input.chars(); + let mut chars = ::char_iter::chars(&input[..]); let completer = SimpleCompleter; let ch = super::complete_line(&mut chars, &mut s, &completer).unwrap(); assert_eq!(Some('\n'), ch); -- GitLab