748 lines
34 KiB
Rust
748 lines
34 KiB
Rust
/*
|
||
* CharReader is a not entirely redundant flattening/chimera of std's
|
||
* BufReader and unicode_reader's CodePoints, introduced to allow
|
||
* peekable buffered UTF-8 codepoints and access to the underlying
|
||
* reader.
|
||
*
|
||
* Unlike CodePoints, it doesn't make the reader inaccessible by
|
||
* wrapping it a Bytes struct.
|
||
*
|
||
* Unlike BufReader, its buffer is peekable as a char.
|
||
*/
|
||
|
||
use smallvec::*;
|
||
|
||
use std::error::Error;
|
||
use std::fmt;
|
||
use std::io;
|
||
use std::io::{ErrorKind, IoSliceMut, Read};
|
||
use std::str;
|
||
|
||
pub struct CharReader<R> {
|
||
inner: R,
|
||
buf: SmallVec<[u8;4]>,
|
||
pos: usize,
|
||
}
|
||
|
||
/// An error raised when parsing a UTF-8 byte stream fails.
|
||
#[derive(Debug)]
|
||
pub struct BadUtf8Error {
|
||
/// The bytes that could not be parsed as a code point.
|
||
pub bytes: Vec<u8>,
|
||
}
|
||
|
||
impl Error for BadUtf8Error {
|
||
fn description(&self) -> &str {
|
||
"BadUtf8Error"
|
||
}
|
||
}
|
||
|
||
impl fmt::Display for BadUtf8Error {
|
||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||
write!(f, "Bad UTF-8: {:?}", self.bytes)
|
||
}
|
||
}
|
||
|
||
impl<R> CharReader<R> {
|
||
pub fn new(inner: R) -> CharReader<R> {
|
||
Self {
|
||
inner,
|
||
buf: SmallVec::new(),
|
||
pos: 0,
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
pub fn inner(&self) -> &R {
|
||
&self.inner
|
||
}
|
||
|
||
#[inline]
|
||
pub fn inner_mut(&mut self) -> &mut R {
|
||
&mut self.inner
|
||
}
|
||
}
|
||
|
||
pub trait CharRead {
|
||
fn read_char(&mut self) -> Option<io::Result<char>> {
|
||
match self.peek_char() {
|
||
Some(Ok(c)) => {
|
||
self.consume(c.len_utf8());
|
||
Some(Ok(c))
|
||
}
|
||
result => result
|
||
}
|
||
}
|
||
|
||
fn peek_char(&mut self) -> Option<io::Result<char>>;
|
||
fn put_back_char(&mut self, c: char);
|
||
fn consume(&mut self, nread: usize);
|
||
}
|
||
|
||
impl<R> CharReader<R> {
|
||
pub fn get_ref(&self) -> &R {
|
||
&self.inner
|
||
}
|
||
|
||
pub fn get_mut(&mut self) -> &mut R {
|
||
&mut self.inner
|
||
}
|
||
|
||
pub fn buffer(&self) -> &[u8] {
|
||
&self.buf[self.pos..]
|
||
}
|
||
|
||
pub fn into_inner(self) -> R {
|
||
self.inner
|
||
}
|
||
|
||
fn reset_buffer(&mut self) {
|
||
self.buf.clear();
|
||
self.pos = 0;
|
||
}
|
||
}
|
||
|
||
impl<R: Read> CharReader<R> {
|
||
fn refresh_buffer(&mut self) -> io::Result<&[u8]> {
|
||
// If we've reached the end of our internal buffer then we need to fetch
|
||
// some more data from the underlying reader.
|
||
// Branch using `>=` instead of the more correct `==`
|
||
// to tell the compiler that the pos..cap slice is always valid.
|
||
if self.pos >= self.buf.len() {
|
||
debug_assert!(self.pos == self.buf.len());
|
||
|
||
self.buf.clear();
|
||
|
||
let mut word = [0u8;4];
|
||
let nread = self.inner.read(&mut word)?;
|
||
|
||
self.buf.extend_from_slice(&word[..nread]);
|
||
self.pos = 0;
|
||
}
|
||
|
||
Ok(&self.buf[self.pos..])
|
||
}
|
||
}
|
||
|
||
impl<R: Read> CharRead for CharReader<R> {
|
||
fn peek_char(&mut self) -> Option<io::Result<char>> {
|
||
match self.refresh_buffer() {
|
||
Ok(_buf) => {}
|
||
Err(e) => return Some(Err(e)),
|
||
}
|
||
|
||
loop {
|
||
let buf = &self.buf[self.pos..];
|
||
|
||
if !buf.is_empty() {
|
||
let e = match str::from_utf8(buf) {
|
||
Ok(s) => {
|
||
let mut chars = s.chars();
|
||
let c = chars.next().unwrap();
|
||
|
||
return Some(Ok(c));
|
||
}
|
||
Err(e) => {
|
||
e
|
||
}
|
||
};
|
||
|
||
if buf.len() - e.valid_up_to() >= 4 {
|
||
// If we have 4 bytes that still don't make up
|
||
// a valid code point, then we have garbage.
|
||
|
||
// We have bad data in the buffer. Remove
|
||
// leading bytes until either the buffer is
|
||
// empty, or we have a valid code point.
|
||
|
||
let mut split_point = 1;
|
||
let mut badbytes = vec![];
|
||
|
||
loop {
|
||
let (bad, rest) = buf.split_at(split_point);
|
||
|
||
if rest.is_empty() || str::from_utf8(rest).is_ok() {
|
||
badbytes.extend_from_slice(bad);
|
||
break;
|
||
}
|
||
|
||
split_point += 1;
|
||
}
|
||
|
||
// Raise the error. If we still have data in
|
||
// the buffer, it will be returned on the next
|
||
// loop.
|
||
|
||
return Some(Err(io::Error::new(io::ErrorKind::InvalidData,
|
||
BadUtf8Error { bytes: badbytes })));
|
||
} else {
|
||
if self.pos >= self.buf.len() {
|
||
return None;
|
||
} else if self.buf.len() - self.pos >= 4 {
|
||
return match str::from_utf8(&self.buf[..e.valid_up_to()]) {
|
||
Ok(s) => {
|
||
let mut chars = s.chars();
|
||
let c = chars.next().unwrap();
|
||
|
||
Some(Ok(c))
|
||
}
|
||
Err(e) => {
|
||
let badbytes = self.buf[..e.valid_up_to()].to_vec();
|
||
|
||
Some(Err(io::Error::new(io::ErrorKind::InvalidData,
|
||
BadUtf8Error { bytes: badbytes })))
|
||
}
|
||
};
|
||
} else {
|
||
let buf_len = self.buf.len();
|
||
|
||
for (c, idx) in (self.pos..buf_len).enumerate() {
|
||
self.buf[c] = self.buf[idx];
|
||
}
|
||
|
||
self.buf.truncate(buf_len - self.pos);
|
||
|
||
let buf_len = self.buf.len();
|
||
|
||
let mut word = [0u8;4];
|
||
let word_slice = &mut word[buf_len..4];
|
||
|
||
match self.inner.read(word_slice) {
|
||
Err(e) => return Some(Err(e)),
|
||
Ok(nread) => {
|
||
self.buf.extend_from_slice(&word_slice[0..nread]);
|
||
}
|
||
}
|
||
|
||
self.pos = 0;
|
||
}
|
||
}
|
||
} else {
|
||
return None;
|
||
}
|
||
}
|
||
}
|
||
|
||
#[inline(always)]
|
||
fn put_back_char(&mut self, c: char) {
|
||
let src_len = self.buf.len() - self.pos;
|
||
debug_assert!(src_len <= 4);
|
||
|
||
let c_len = c.len_utf8();
|
||
let mut shifted_slice = [0u8; 4];
|
||
|
||
shifted_slice[0..src_len].copy_from_slice(&self.buf[self.pos .. self.buf.len()]);
|
||
|
||
self.buf.resize(c_len, 0);
|
||
self.buf.extend_from_slice(&shifted_slice[0..src_len]);
|
||
self.pos = 0;
|
||
|
||
c.encode_utf8(&mut self.buf[0..c_len]);
|
||
}
|
||
|
||
#[inline(always)]
|
||
fn consume(&mut self, nread: usize) {
|
||
self.pos += nread;
|
||
}
|
||
}
|
||
|
||
/*
|
||
impl<R: Seek> BufReader<R> {
|
||
/// Seeks relative to the current position. If the new position lies within the buffer,
|
||
/// the buffer will not be flushed, allowing for more efficient seeks.
|
||
/// This method does not return the location of the underlying reader, so the caller
|
||
/// must track this information themselves if it is required.
|
||
#[stable(feature = "bufreader_seek_relative", since = "1.53.0")]
|
||
pub fn seek_relative(&mut self, offset: i64) -> io::Result<()> {
|
||
let pos = self.pos as u64;
|
||
if offset < 0 {
|
||
if let Some(new_pos) = pos.checked_sub((-offset) as u64) {
|
||
self.pos = new_pos as usize;
|
||
return Ok(());
|
||
}
|
||
} else {
|
||
if let Some(new_pos) = pos.checked_add(offset as u64) {
|
||
if new_pos <= self.cap as u64 {
|
||
self.pos = new_pos as usize;
|
||
return Ok(());
|
||
}
|
||
}
|
||
}
|
||
self.seek(SeekFrom::Current(offset)).map(drop)
|
||
}
|
||
}
|
||
*/
|
||
|
||
impl<R: Read> Read for CharReader<R> {
|
||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||
// // If we don't have any buffered data and we're doing a massive read
|
||
// // (larger than our internal buffer), bypass our internal buffer
|
||
// // entirely.
|
||
// if self.pos == self.cap && buf.len() >= self.buf.len() {
|
||
// self.discard_buffer();
|
||
// return self.inner.read(buf);
|
||
// }
|
||
|
||
let mut inner_buf = self.refresh_buffer()?;
|
||
let nread = inner_buf.read(buf)?;
|
||
|
||
// let nread = {
|
||
// let mut rem = self.fill_buf()?;
|
||
// rem.read(buf)?
|
||
// };
|
||
|
||
self.consume(nread);
|
||
Ok(nread)
|
||
}
|
||
|
||
// Small read_exacts from a BufReader are extremely common when used with a deserializer.
|
||
// The default implementation calls read in a loop, which results in surprisingly poor code
|
||
// generation for the common path where the buffer has enough bytes to fill the passed-in
|
||
// buffer.
|
||
fn read_exact(&mut self, mut buf: &mut [u8]) -> io::Result<()> {
|
||
if self.buffer().len() >= buf.len() {
|
||
buf.copy_from_slice(&self.buffer()[..buf.len()]);
|
||
self.consume(buf.len());
|
||
return Ok(());
|
||
}
|
||
|
||
while !buf.is_empty() {
|
||
match self.read(buf) {
|
||
Ok(0) => break,
|
||
Ok(n) => {
|
||
let tmp = buf;
|
||
buf = &mut tmp[n..];
|
||
}
|
||
Err(e) if e.kind() == ErrorKind::Interrupted => {}
|
||
Err(e) => return Err(e),
|
||
}
|
||
}
|
||
|
||
if !buf.is_empty() {
|
||
Err(io::Error::new(ErrorKind::UnexpectedEof, "failed to fill whole buffer"))
|
||
} else {
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||
let total_len = bufs.iter().map(|b| b.len()).sum::<usize>();
|
||
|
||
if self.pos == self.buf.len() && total_len >= self.buf.len() {
|
||
self.reset_buffer(); // self.discard_buffer();
|
||
return self.inner.read_vectored(bufs);
|
||
}
|
||
|
||
let nread = {
|
||
self.refresh_buffer()?;
|
||
(&self.buf[self.pos..]).read_vectored(bufs)?
|
||
};
|
||
|
||
self.consume(nread);
|
||
Ok(nread)
|
||
}
|
||
}
|
||
|
||
/*
|
||
#[stable(feature = "rust1", since = "1.0.0")]
|
||
impl<R: Read> BufRead for BufReader<R> {
|
||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||
// If we've reached the end of our internal buffer then we need to fetch
|
||
// some more data from the underlying reader.
|
||
// Branch using `>=` instead of the more correct `==`
|
||
// to tell the compiler that the pos..cap slice is always valid.
|
||
if self.pos >= self.cap {
|
||
debug_assert!(self.pos == self.cap);
|
||
self.cap = self.inner.read(&mut self.buf)?;
|
||
self.pos = 0;
|
||
}
|
||
Ok(&self.buf[self.pos..self.cap])
|
||
}
|
||
|
||
fn consume(&mut self, amt: usize) {
|
||
self.pos = cmp::min(self.pos + amt, self.cap);
|
||
}
|
||
}
|
||
*/
|
||
|
||
impl<R> fmt::Debug for CharReader<R>
|
||
where
|
||
R: fmt::Debug,
|
||
{
|
||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||
fmt.debug_struct("CharReader")
|
||
.field("reader", &self.inner)
|
||
.field("buf", &format_args!("{}/{}", self.buf.capacity() - self.pos, self.buf.len()))
|
||
.finish()
|
||
}
|
||
}
|
||
|
||
/*
|
||
#[stable(feature = "rust1", since = "1.0.0")]
|
||
impl<R: Seek> Seek for BufReader<R> {
|
||
/// Seek to an offset, in bytes, in the underlying reader.
|
||
///
|
||
/// The position used for seeking with [`SeekFrom::Current`]`(_)` is the
|
||
/// position the underlying reader would be at if the `BufReader<R>` had no
|
||
/// internal buffer.
|
||
///
|
||
/// Seeking always discards the internal buffer, even if the seek position
|
||
/// would otherwise fall within it. This guarantees that calling
|
||
/// [`BufReader::into_inner()`] immediately after a seek yields the underlying reader
|
||
/// at the same position.
|
||
///
|
||
/// To seek without discarding the internal buffer, use [`BufReader::seek_relative`].
|
||
///
|
||
/// See [`std::io::Seek`] for more details.
|
||
///
|
||
/// Note: In the edge case where you're seeking with [`SeekFrom::Current`]`(n)`
|
||
/// where `n` minus the internal buffer length overflows an `i64`, two
|
||
/// seeks will be performed instead of one. If the second seek returns
|
||
/// [`Err`], the underlying reader will be left at the same position it would
|
||
/// have if you called `seek` with [`SeekFrom::Current`]`(0)`.
|
||
///
|
||
/// [`std::io::Seek`]: Seek
|
||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||
let result: u64;
|
||
if let SeekFrom::Current(n) = pos {
|
||
let remainder = (self.cap - self.pos) as i64;
|
||
// it should be safe to assume that remainder fits within an i64 as the alternative
|
||
// means we managed to allocate 8 exbibytes and that's absurd.
|
||
// But it's not out of the realm of possibility for some weird underlying reader to
|
||
// support seeking by i64::MIN so we need to handle underflow when subtracting
|
||
// remainder.
|
||
if let Some(offset) = n.checked_sub(remainder) {
|
||
result = self.inner.seek(SeekFrom::Current(offset))?;
|
||
} else {
|
||
// seek backwards by our remainder, and then by the offset
|
||
self.inner.seek(SeekFrom::Current(-remainder))?;
|
||
self.discard_buffer();
|
||
result = self.inner.seek(SeekFrom::Current(n))?;
|
||
}
|
||
} else {
|
||
// Seeking with Start/End doesn't care about our buffer length.
|
||
result = self.inner.seek(pos)?;
|
||
}
|
||
self.discard_buffer();
|
||
Ok(result)
|
||
}
|
||
|
||
/// Returns the current seek position from the start of the stream.
|
||
///
|
||
/// The value returned is equivalent to `self.seek(SeekFrom::Current(0))`
|
||
/// but does not flush the internal buffer. Due to this optimization the
|
||
/// function does not guarantee that calling `.into_inner()` immediately
|
||
/// afterwards will yield the underlying reader at the same position. Use
|
||
/// [`BufReader::seek`] instead if you require that guarantee.
|
||
///
|
||
/// # Panics
|
||
///
|
||
/// This function will panic if the position of the inner reader is smaller
|
||
/// than the amount of buffered data. That can happen if the inner reader
|
||
/// has an incorrect implementation of [`Seek::stream_position`], or if the
|
||
/// position has gone out of sync due to calling [`Seek::seek`] directly on
|
||
/// the underlying reader.
|
||
///
|
||
/// # Example
|
||
///
|
||
/// ```no_run
|
||
/// use std::{
|
||
/// io::{self, BufRead, BufReader, Seek},
|
||
/// fs::File,
|
||
/// };
|
||
///
|
||
/// fn main() -> io::Result<()> {
|
||
/// let mut f = BufReader::new(File::open("foo.txt")?);
|
||
///
|
||
/// let before = f.stream_position()?;
|
||
/// f.read_line(&mut String::new())?;
|
||
/// let after = f.stream_position()?;
|
||
///
|
||
/// println!("The first line was {} bytes long", after - before);
|
||
/// Ok(())
|
||
/// }
|
||
/// ```
|
||
fn stream_position(&mut self) -> io::Result<u64> {
|
||
let remainder = (self.cap - self.pos) as u64;
|
||
self.inner.stream_position().map(|pos| {
|
||
pos.checked_sub(remainder).expect(
|
||
"overflow when subtracting remaining buffer size from inner stream position",
|
||
)
|
||
})
|
||
}
|
||
}
|
||
*/
|
||
/*
|
||
impl<T> SizeHint for CharReader<T> {
|
||
fn lower_bound(&self) -> usize {
|
||
self.buffer().len()
|
||
}
|
||
}
|
||
*/
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use crate::parser::char_reader::*;
|
||
use std::io::Cursor;
|
||
|
||
#[test]
|
||
fn plain_string() {
|
||
let mut read_string = CharReader::new(Cursor::new("a string"));
|
||
|
||
for c in "a string".chars() {
|
||
assert_eq!(read_string.peek_char().unwrap().ok(), Some(c));
|
||
assert_eq!(read_string.read_char().unwrap().ok(), Some(c));
|
||
}
|
||
|
||
assert!(read_string.read_char().is_none());
|
||
}
|
||
|
||
#[test]
|
||
fn greek_string() {
|
||
let mut read_string = CharReader::new(Cursor::new("λέξη"));
|
||
|
||
for c in "λέξη".chars() {
|
||
assert_eq!(read_string.peek_char().unwrap().ok(), Some(c));
|
||
assert_eq!(read_string.read_char().unwrap().ok(), Some(c));
|
||
}
|
||
|
||
assert!(read_string.read_char().is_none());
|
||
}
|
||
|
||
#[test]
|
||
fn russian_string() {
|
||
let mut read_string = CharReader::new(Cursor::new("слово"));
|
||
|
||
for c in "слово".chars() {
|
||
assert_eq!(read_string.peek_char().unwrap().ok(), Some(c));
|
||
assert_eq!(read_string.read_char().unwrap().ok(), Some(c));
|
||
}
|
||
|
||
assert!(read_string.read_char().is_none());
|
||
}
|
||
|
||
#[test]
|
||
fn greek_lorem_ipsum() {
|
||
let lorem_ipsum = "Λορεμ ιπσθμ δολορ σιτ αμετ, οφφενδιτ
|
||
εφφιcιενδι σιτ ει, ηαρθμ λεγερε qθαερενδθμ ιθσ νε. Ηασ νο εροσ
|
||
σιγνιφερθμqθε, σεδ ετ μθτατ jθστο, ει cθμ ελιγενδι σcριπτορεμ
|
||
ρεπρεηενδθντ. Εοσ ατ αμετ μαλισ ελειφενδ. Ιν cθμ εριπθιτ
|
||
νομινατι. Θσθ ιν cετεροσ μαιορθμ, μθνερε ατομορθμ ινcιδεριντ θτ
|
||
ηασ. Αν ηασ λιβρισ πραεσεντ πατριοqθε, ηινc θτιναμ πριμισ νε
|
||
cθμ. Cθ μοδο ερρεμ σcριβεντθρ cθμ. Ει vισ δεcορε μαλορθμ
|
||
σεντεντιαε, σεδ νο λιβερ εvερτι μεντιτθμ. Προ φαcερ vολθτπατ
|
||
σαπιεντεμ ιν. Cθ εροσ περσεqθερισ πρι, εα ποσσιτ cετεροσ δθο. Πρι
|
||
εα μαλισ μθνερε.
|
||
|
||
Qθισ jθστο μαλορθμ cθ qθο. Νεc ατ οδιο σολετ μαιεστατισ, νε
|
||
φορενσιβθσ σαδιπσcινγ ιθσ, αν qθι ειθσ βρθτε σαπιεντεμ. Cομμθνε
|
||
περcιπιτθρ ιθσ αδ, μθνερε δολορθμ ιμπεδιτ ηισ νε. Νεc ετιαμ
|
||
προπριαε vιτθπερατα ιν. Σονετ νεμορε ιθσ cθ, ιν αφφερτ ινερμισ
|
||
cοτιδιεqθε vισ.
|
||
|
||
Ηασ ιδ νονθμυ δοcτθσ cοτιδιεqθε. Σινγθλισ πηιλοσοπηια εξ δθο. Εστ
|
||
νο ιραcθνδια cονσεqθθντθρ. Τε διcτασ επιcθρει εφφιcιαντθρ δθο, εοσ
|
||
νε νθλλα νομιναvι. Εθμ cθ ελιτρ λιβεραvισσε, σιτ περσεqθερισ
|
||
cομπλεcτιτθρ εξ, πονδερθμ σιμιλιqθε ηασ νο.
|
||
|
||
Σολθμ ποσσιμ λαβιτθρ εξ ηισ, ει δομινγ εξπετενδισ vελ, διαμ μινιμ
|
||
σcριπσεριτ ει περ. Αθδιαμ οcθρρερετ προ εξ, δομινγ vολθπταρια ετ
|
||
qθο. Cονσθλ σανcτθσ αccθμσαν νο ιθσ, αδ εαμ αλβθcιθσ
|
||
ηονεστατισ. Ετ vιξ φαcιλισ qθαλισqθε ερροριβθσ, ηισ εθ πθρτο
|
||
ασσεντιορ. Ιθσ βονορθμ ηονεστατισ σcριπσεριτ ατ, ιν ναμ εσσε μοvετ
|
||
γραεcο. Αθγθε cονσεcτετθερ εστ ατ.
|
||
|
||
Αδ ταλε σθασ μθνερε σεδ, vισ φεθγαιτ αντιοπαμ ιδ. Προ εθ ινερμισ
|
||
σαλθτατθσ, σαεπε qθαεστιο θρβανιτασ cθ περ. Ιν μαλορθμ σαλθτατθσ
|
||
δετερρθισσετ περ, νε παρτεμ vολθτπατ ινστρθcτιορ vιξ. Νο vισ
|
||
δεμοcριτθμ εφφιcιαντθρ, επιcθρει αδολεσcενσ εστ cθ, ιδ vιξ
|
||
λθcιλιθσ αδιπισcινγ. Σεα τε cλιτα ιραcθνδια. Σεα αν σιμθλ
|
||
εσσεντ. Vοcιβθσ ελειφενδ cονσεqθθντθρ περ αδ, αν ναμ πονδερθμ
|
||
vολθπταρια.
|
||
|
||
Λιβερ ερθδιτι αccθσαμθσ θτ ναμ. Σιτ αντιοπαμ γθβεργρεν νε. Αμετ
|
||
ανcιλλαε ετ qθι, μεα σολθμ λαθδεμ εα. Εθ μελ παρτεμ οβλιqθε
|
||
πηαεδρθμ. Εξ μελ jθστο αccομμοδαρε, νε νολθισσε σινγθλισ σενσιβθσ
|
||
cθμ, vισ εθ τιμεαμ αδιπισcινγ.
|
||
|
||
Τε νολθισσε vολθπτατθμ εστ. Ασσθμ νομιναvι πρι νε, ει νοστρθμ
|
||
επιcθρει μεα. Σεδ cθ ελιτ δεσερθντ, γραεcε ερροριβθσ προ θτ, περ
|
||
νε εθισμοδ vολθπταρια. Νο εθμ διcατ ποσσιμ, νεc πρινcιπεσ
|
||
cονcεπταμ νε. Εθ αππαρεατ ιντελλεγατ σεα. Μελ θτ ελιτ λαθδεμ, θσθ
|
||
δολορεμ cομπλεcτιτθρ ετ, νε μεα δολορεσ μολεστιαε.
|
||
|
||
Θσθ λεγενδοσ vολθπτατιβθσ cθ. Qθο νε αδηθc ρεφερρεντθρ, αλια
|
||
μεδιοcρεμ δθο νε, σεδ ερρεμ δολορθμ αccομμοδαρε νε. Ετιαμ εqθιδεμ
|
||
δετερρθισσετ cθ μει, ετ εροσ cετεροσ σεα, εξ vιξ ενιμ cασε
|
||
δετραξιτ. Σεδ σολθτα λιβρισ ειρμοδ τε, νοvθμ ποπθλο νε εθμ. Σθμμο
|
||
αδμοδθμ δεσερθντ εστ εξ, εστ διcαμ εqθιδεμ cθ.
|
||
|
||
Ιλλθμ cορπορα ινvιδθντ εαμ ετ. Σεδ μαλισ ταcιματεσ εvερτιτθρ εα,
|
||
μαζιμ νθλλαμ vοcιβθσ μεα ει. Μεα ορνατθσ λθπτατθμ αδιπισcινγ
|
||
αδ. Μεα αφφερτ νοστερ ατ, ναμ αν σολεατ ερροριβθσ. Εξ σεα αεqθε
|
||
μθνερε cετερο, εοσ ηινc ελειφενδ δεμοcριτθμ.";
|
||
|
||
let mut lorem_ipsum_reader = CharReader::new(Cursor::new(lorem_ipsum));
|
||
|
||
for c in lorem_ipsum.chars() {
|
||
assert_eq!(lorem_ipsum_reader.peek_char().unwrap().ok(), Some(c));
|
||
assert_eq!(lorem_ipsum_reader.read_char().unwrap().ok(), Some(c));
|
||
}
|
||
|
||
assert!(lorem_ipsum_reader.read_char().is_none());
|
||
}
|
||
|
||
#[test]
|
||
fn armenian_lorem_ipsum() {
|
||
let lorem_ipsum = "լոռեմ իպսում դոլոռ սիթ ամեթ, նովում գռաեծո
|
||
սեա եա, աբհոռռեանթ դիսպութանդո եի քուի. իդ քուոդ ինդոծթում
|
||
եսթ, մեա թե ծոմմոդո ծոռպոռա. եթ ծոնսուլ ադիպիսծինգ ռեֆոռմիդանս
|
||
պեռ, ինեռմիս ֆեուգաիթ նո քուո, թալե սալե պռո եա. եթ նիբհ
|
||
աուգուե վոլումուս դուո, նե ծում եխեռծի սալութաթուս գլոռիաթուռ,
|
||
ծու թաթիոն պռաեսենթ մեդիոծռեմ վիս.
|
||
|
||
վիխ եռոս ռեֆեռռենթուռ եու. պեռսիուս վիթուպեռաթոռիբուս ութ սեա,
|
||
վիդե ինվիդունթ պռոբաթուս նո քուո. մեի եռոս մելիուս նոմինավի
|
||
իդ, ութ պռո քուաս քուաեսթիո. եթ նաթում պեթենթիում սուավիթաթե
|
||
հիս. քուի ծոնսթիթութո մեդիոծռիթաթեմ թե. ծեթեռո դեթռածթո
|
||
ծոնծեպթամ սեա եթ. դիսսենթիեթ ելոքուենթիամ թհեոպհռասթուս նեծ
|
||
աթ, աթ ֆածեթե եռիպուիթ վիխ.
|
||
|
||
ասսուեվեռիթ սծռիպսեռիթ եսթ եթ, վիդիթ դեբեթ եվեռթի եխ
|
||
եսթ. աութեմ լաուդեմ պոսիդոնիում մեի եի. ռեբում դիծամ ծեթեռոս
|
||
եում ծու. նիհիլ եխպեթենդա ասսուեվեռիթ ուսու ան. ւիսի թաթիոն
|
||
դելենիթ նո իուս, սեդ եխ իդքուե սիգնիֆեռումքուե, բռութե զռիլ
|
||
ալբուծիուս ան պռի.
|
||
|
||
մովեթ իռիուռե սալութանդի պեռ նո, եի ոմնիս աֆֆեռթ պեռսեքուեռիս
|
||
իուս, եթ պռաեսենթ մալուիսսեթ եսթ. եսթ պռոբո գուբեռգռեն եթ, հաս
|
||
ին դիամ նումքուամ. ֆեուգաիթ ինվենիռե ռեպուդիանդաե աթ սեդ,
|
||
իուվառեթ ծոնսուլաթու եֆֆիծիանթուռ ուսու եի. ութ մեա ածծումսան
|
||
նոմինավի թինծիդունթ, մեի դիծթա ածծումսան ութ. վիմ ոմնիում
|
||
ելիգենդի սծռիպթոռեմ եու.
|
||
|
||
իդ վիս եռռոռ ալիքուիպ ելոքուենթիամ, ադ դելենիթի պեռծիպիթ
|
||
դեֆինիթիոնես իուս. վիմ իուդիծո դեմոծռիթում ծոմպռեհենսամ թե,
|
||
ութ նիհիլ լոբոռթիս վոլուպթաթիբուս վել, դիծունթ մենթիթում
|
||
ֆածիլիսիս եի եում. եսսե սալե մինիմ եոս նե. ագամ ոմնեսքուե ծում
|
||
ին.
|
||
|
||
իուվառեթ իուդիծաբիթ ծում աթ, ուսու նիբհ աթքուի դոմինգ եխ. եի
|
||
քուի սանծթուս սենսիբուս, նամ ուբիքուե ապպեթեռե պռոդեսսեթ
|
||
եու. ուսու եթ աուգուե ծոնվենիռե սծռիբենթուռ. ան ոմնիում վեռեառ
|
||
ութռոքուե դուո, եսթ եի լիբեռ մեդիոծռեմ եխպլիծառի, ոմնիս
|
||
աուդիռե թե պռի. վիմ մունեռե սոլեաթ ծու, եռոս ինվենիռե
|
||
դիսպութաթիոնի եի քուո, ան ալթեռա պութենթ լաբոռես պռո. անթիոպամ
|
||
դեմոծռիթում պեռ ին.
|
||
|
||
նե քուի ծիբո ելիթռ. նեծ նե լիբեռ վոլուպթուա. նիսլ ծոմմունե
|
||
եխպեթենդիս նամ եխ, իուդիծո պլածեռաթ պեռծիպիթուռ մել նո, եթ
|
||
պառթեմ պութանթ քուի. վիմ թինծիդունթ ածծոմմոդառե աթ, նե նամ
|
||
վիդիթ իռիուռե, պռո եա ելիգենդի պոսթուլանթ ծոնսթիթութո.
|
||
|
||
մել ութ ոդիո նուլլամ եխպլիծառի. պռոպռիաե թինծիդունթ
|
||
դելիծաթիսսիմի եամ ան, մոդո քուոդսի ապեռիռի եու եսթ, պեռ աթ
|
||
լաբոռես սենսեռիթ. վիմ ծոնգուե ռեպուդիանդաե եի, նեծ ագամ
|
||
դիծունթ դելիծաթիսսիմի աթ. պոսսիթ լիբեռավիսսե եոս եու.
|
||
|
||
աթ ալիա դեբեթ ելաբոռառեթ քուո, ին ալիի ածծումսան ծոնսթիթուամ
|
||
հաս, մել թոթա ոմիթթանթուռ ինսթռուծթիոռ նո. պեռ նե ծաուսաե
|
||
սապիենթեմ, պաուլո ոմնեսքուե եի քուո, եխ ոռաթիո պհիլոսոպհիա
|
||
սիթ. իգնոթա ծաուսաե աթ ուսու, եխ քուո դիծթաս քուոդսի
|
||
ռեպուդիառե. ծոռպոռա պռոդեսսեթ ռեֆեռռենթուռ եոս եխ.
|
||
|
||
եու եթիամ ելեիֆենդ մել, սալե սծռիպսեռիթ հիս եու. պոռռո
|
||
ադոլեսծենս մեի եա. ին մեա զռիլ պռոբաթուս սալութաթուս. եոս ադ
|
||
մինիմ թեմպոռիբուս. սեա նե եթիամ.";
|
||
|
||
let mut lorem_ipsum_reader = CharReader::new(Cursor::new(lorem_ipsum));
|
||
|
||
for c in lorem_ipsum.chars() {
|
||
assert_eq!(lorem_ipsum_reader.peek_char().unwrap().ok(), Some(c));
|
||
assert_eq!(lorem_ipsum_reader.read_char().unwrap().ok(), Some(c));
|
||
}
|
||
|
||
assert!(lorem_ipsum_reader.read_char().is_none());
|
||
}
|
||
|
||
#[test]
|
||
fn russian_lorem_ipsum() {
|
||
let lorem_ipsum = "Лорем ипсум долор сит амет, атяуи дицам еи
|
||
сит, ид сеа фацилис елаборарет. Меа еу яуас алияуид, те яуи
|
||
саперет аппеллантур. Ех иус диам дицта волуптариа, еу пер
|
||
бруте омиттам аццусата. Хис сапиентем губергрен те, яуидам
|
||
луптатум персеяуерис ад ест.
|
||
|
||
Ан алияуип перицулис нам, нец апериам цотидиеяуе волуптатибус
|
||
но. Солум тритани пер ех, меи не одио тритани рецусабо, цу при
|
||
веро мелиоре импердиет. Ин граеци индоцтум салутатус нец, диам
|
||
сцаевола пертинациа про те. Ут сеа дебитис лаборамус
|
||
диссентиас, еи цум яуот лобортис.
|
||
|
||
Децоре сингулис вим не. Еос не риденс оффициис, еу нонумы
|
||
лабитур еррорибус хас, вел омнис цонституто посидониум но. Вел
|
||
персиус фастидии репрехендунт ид. Натум иллум ипсум сит ад, еа
|
||
еам новум латине. Еос нолуиссе патриояуе елояуентиам те.
|
||
|
||
Стет малис яуаерендум хас ад, прима цотидиеяуе мел ан,
|
||
трацтатос десеруиссе нам ех. Ин малорум сусципиантур вим, ех
|
||
меа граецо тритани адолесценс. Промпта цонцлусионемяуе нам еи,
|
||
дуо ин лаборе алтерум цотидиеяуе. Но елитр промпта сплендиде
|
||
еум, аеяуе ассуеверит цонституам яуи ид. Ад тале еррор
|
||
интеллегебат хас, ерудити граецис хас не, пер ут лабитур
|
||
еуисмод. Те при суммо путант. Про утинам цоммуне урбанитас еа.
|
||
|
||
Идяуе репрехендунт еи нам, алии толлит легере нам не, хис еа
|
||
виси адверсариум цонцлусионемяуе. Хас ассум омиттам луцилиус
|
||
ет, вих цонсул малорум фастидии не, сенсибус ассуеверит дуо
|
||
ут. Дуо алиа видит цетеро ат, еа аппареат пертинах вел. Пер
|
||
цонституто инцидеринт ин, убияуе риденс сенсерит цум цу. Про
|
||
ет цетерос темпорибус, те вел пурто суммо, дуо мунере вертерем
|
||
урбанитас ад. Сит оптион елецтрам форенсибус но. Еи татион
|
||
сапиентем ест, лаборе сцрипта сингулис но вим, усу еу елигенди
|
||
персецути.
|
||
|
||
Иус ан елецтрам цонтентионес. Меи атяуи нонумес ут, вел амет
|
||
репрехендунт ан, вис еу яуаестио патриояуе. Про синт легере
|
||
детрацто ад. Постеа долорем евертитур при ет, вим номинави
|
||
принципес ирацундиа ех. Доцтус интеллегебат но нам. Фацете
|
||
оффициис нецесситатибус цу меа.
|
||
|
||
Промпта симилияуе вис ин. Пер бонорум перицулис аргументум
|
||
ад. Еу дицат фацилис губергрен нам, еффициенди цомпрехенсам
|
||
хас еу. Инани нонумы усу но, ад цонцептам репудиандае
|
||
про. Тота нуллам делицата еа яуо, усу дуис дебет путент еи.
|
||
|
||
Вис апериам доценди елояуентиам еа. Ех яуот детрацто
|
||
елояуентиам цум, ерос малис дицерет вис ин. Еа цум модус
|
||
еяуидем, дебет нуллам ан меи. Алтерум омиттам про ет.
|
||
|
||
Яуи ех латине алияуам, ан меи одио нуллам. Ид хас омнис ребум
|
||
либрис. Ет убияуе путант дебитис про, ех хис медиоцрем
|
||
партиендо, но елит елецтрам дуо. Еу меа сонет номинави
|
||
цотидиеяуе. Нам фалли новум минимум еу, перфецто ратионибус
|
||
цонституто ад меа.
|
||
|
||
Нобис детрацто еам ид, при еу ассум пертинах, те етиам
|
||
проприае салутанди яуо. Легимус сусципиантур ет хас, сед
|
||
поссит дефинитионес еа. Ест не патриояуе омиттантур
|
||
интеллегебат, еу яуо дебет цонцлудатуряуе. Еум ад мнесарчум
|
||
дефинитионем, елитр лаборамус перципитур про не, хас феугаит
|
||
фастидии луцилиус ид. Фастидии интеллегат ех.";
|
||
|
||
let mut lorem_ipsum_reader = CharReader::new(Cursor::new(lorem_ipsum));
|
||
|
||
for c in lorem_ipsum.chars() {
|
||
assert_eq!(lorem_ipsum_reader.peek_char().unwrap().ok(), Some(c));
|
||
assert_eq!(lorem_ipsum_reader.read_char().unwrap().ok(), Some(c));
|
||
|
||
lorem_ipsum_reader.put_back_char(c);
|
||
|
||
assert_eq!(lorem_ipsum_reader.peek_char().unwrap().ok(), Some(c));
|
||
assert_eq!(lorem_ipsum_reader.read_char().unwrap().ok(), Some(c));
|
||
}
|
||
|
||
assert!(lorem_ipsum_reader.read_char().is_none());
|
||
}
|
||
}
|