-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add MultiReader and TeeReader #62
base: master
Are you sure you want to change the base?
Changes from all commits
a639fbb
452abe1
973d0e3
5e638cf
83b811b
ebfcc5d
fba2582
2a4cde1
4bb9178
1f2ba89
ad34ff6
4cf3fa8
0285b14
039e6a9
36f6fbe
2d77348
9584055
acdba61
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
use std::io::{copy, stdout}; | ||
|
||
use lib::io::MultiReader; | ||
|
||
fn main() -> std::io::Result<()> { | ||
let header = "---- HEADER ----\n".as_bytes(); | ||
let content = "Example of MultiReader\n".as_bytes(); | ||
let footer = "---- FOOTER ----\n".as_bytes(); | ||
let mut multi_reader = | ||
MultiReader::new(vec![Box::new(header), Box::new(content), Box::new(footer)]); | ||
copy(&mut multi_reader, &mut stdout())?; | ||
|
||
Ok(()) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
use lib::io::TeeReader; | ||
use std::io::Read; | ||
|
||
fn main() -> std::io::Result<()> { | ||
// TeeReader を使用した場合の例。 | ||
let mut buf = Vec::new(); | ||
let reader = "Example of TeeReader".as_bytes(); | ||
let mut tee_reader = TeeReader::new(reader, &mut buf); | ||
// データを読み捨てる。 | ||
let _ = tee_reader.read_to_end(&mut Vec::new())?; | ||
|
||
println!( | ||
"{}", | ||
// けどバッファには残っている。 | ||
String::from_utf8(buf) | ||
.expect("UTF-8 形式でない文字列の可能性があります。UTF-8 にしてください。") | ||
); | ||
|
||
Ok(()) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,11 @@ | ||
mod limited_reader; | ||
mod multi_reader; | ||
mod multi_writer; | ||
mod section_reader; | ||
mod tee_reader; | ||
|
||
pub use limited_reader::LimitedReader; | ||
pub use multi_reader::MultiReader; | ||
pub use multi_writer::MultiWriter; | ||
pub use section_reader::SectionReader; | ||
pub use tee_reader::TeeReader; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
use std::{collections::VecDeque, io::Read}; | ||
|
||
/// Takes multiple `std::io::Read` at once. | ||
/// This is inspired by `io.MultiReader` in Go. | ||
/// | ||
/// # Example | ||
/// | ||
/// ``` | ||
/// use std::io::{copy, stdout}; | ||
/// use lib::io::MultiReader; | ||
/// | ||
/// fn main() -> std::io::Result<()> { | ||
/// let header = "---- HEADER ----\n".as_bytes(); | ||
/// let content = "Example of MultiReader\n".as_bytes(); | ||
/// let footer = "---- FOOTER ----\n".as_bytes(); | ||
/// let mut multi_reader = | ||
/// MultiReader::new(vec![Box::new(header), Box::new(content), Box::new(footer)]); | ||
/// copy(&mut multi_reader, &mut stdout())?; | ||
/// Ok(()) | ||
/// } | ||
/// ``` | ||
pub struct MultiReader { | ||
readers: VecDeque<Box<dyn Read>>, | ||
/// Points to current element while reading. | ||
current: Option<Box<dyn Read>>, | ||
} | ||
|
||
impl MultiReader { | ||
/// Constructs `MultiReader`. `current` is set to the first element that is popped out from `VecDeque`. | ||
pub fn new(readers: Vec<Box<dyn Read>>) -> Self { | ||
let mut deque: VecDeque<Box<dyn Read>> = readers.into(); | ||
let current = deque.pop_front(); | ||
Self { | ||
readers: deque, | ||
current, | ||
} | ||
} | ||
} | ||
|
||
impl Read for MultiReader { | ||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { | ||
// Doesn't move to the next one until the first one is completely read. | ||
// If the processing ends in the middle of reading the first one, | ||
// it returns the control at that point. | ||
loop { | ||
laysakura marked this conversation as resolved.
Show resolved
Hide resolved
|
||
match self.current { | ||
Some(ref mut r) => { | ||
let n = r.read(buf)?; | ||
if n > 0 { | ||
return Ok(n); | ||
} | ||
} | ||
None => return Ok(0), | ||
} | ||
self.current = self.readers.pop_front(); | ||
Comment on lines
+46
to
+55
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Goの実装に依るのですが ここの実装はreadersの切れ目で使う側の実装によっては強制的に読み込みが終了してしまう気がしています。 一般的に let mut buf: [u8; 128];
let mut some_reader;
while some_reader.read(&mut buf) >= buf.len() {
...
} その場合に There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Go の実装はこちらですね(ただ、もともと参考にはしていなくて、だいぶ実装の形が違っています) Go の実装に寄せるのはありかもです。
申し訳ないですが、ちょっと具体的な例がイメージできておらず…!なにかコーナーケースをパッと思いついたりしますでしょうか?修正するにしても、理解を深めてから修正したいなと思っており。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. コーナーケースの具体例とその上での僕の想定は以下になります。(github上で書いたので雰囲気Rustですが) #[test]
fn multi_reader_end_of_first_reader() {
let header = "---- HEADER ----\n".as_bytes();
let content = "Example of MultiReader\n".as_bytes();
let mut multi_reader = MultiReader::new(vec![Box::new(header), Box::new(content)]);
let mut buf: Vec<u8> = vec![0; 256];
let size = multi_reader.read(&mut buf).unwrap();
assert_eq!(size, header.len() + content.len()); // ここが現状の実装だとsize == header.len()になってそう?
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ちなみに、Goの実装を読んでみたら僕の読解が正しければ errとしてEOFは返さないけどbuf.len未満のsizeを返す( Rustは通常のReadではEOFを There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 上記コードを実行してみた結果、たしかに落ちています。考えてみますね。
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @yuk1ty Rustの標準ライブラリのコード( ですので、僕の書いた let mut buf: [u8; 128];
let mut some_reader;
while some_reader.read(&mut buf) >= buf.len() {
...
} 上のようなコードは良くないみたいです。(コンパイル時に検知できないのはRustらしくないなとは思いましたが…) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. あと、 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
実装してみたところ、出来たけどあんまり簡単じゃなかったです。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ありがとうございます。
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
おっしゃるとおりですね。 pub struct AnotherMultiReader {
reader: Option<Chain<Box<dyn Read>, Box<dyn Read>>>,
}
impl Read for AnotherMultiReader {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
self.reader.as_mut().map(|x| x.read(buf)).unwrap_or(Ok(0))
}
}
impl AnotherMultiReader {
pub fn new(readers: Vec<Box<dyn Read>>) -> Self {
Self::new_impl(readers.into())
}
fn new_impl(mut readers: VecDeque<Box<dyn Read>>) -> Self {
let first = readers.pop_front();
first
.map(|r| {
let remain: Box<dyn Read> = Box::new(AnotherMultiReader::new_impl(readers));
Self {
reader: Some(r.chain(remain)),
}
})
.unwrap_or(Self { reader: None })
}
} メリット(AnotherMultiReaderの)
デメリット
という感じなのでそのままで大丈夫だと思います。 |
||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
use std::io::{Read, Write}; | ||
|
||
pub struct TeeReader<R, W> | ||
where | ||
R: Read, | ||
W: Write, | ||
{ | ||
reader: R, | ||
writer: W, | ||
} | ||
|
||
impl<R, W> TeeReader<R, W> | ||
where | ||
R: Read, | ||
W: Write, | ||
{ | ||
pub fn new(reader: R, writer: W) -> Self { | ||
Self { reader, writer } | ||
} | ||
} | ||
|
||
impl<R, W> Read for TeeReader<R, W> | ||
where | ||
R: Read, | ||
W: Write, | ||
{ | ||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { | ||
let n = self.reader.read(buf)?; | ||
self.writer.write_all(&buf[..n])?; | ||
Ok(n) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
直感的にはこれは常に
readers.front()
の結果になるので、このように別途フィールド(ステート)として持つ必要はないと思いますが、いかがでしょう?