Skip to content

Commit e2394c7

Browse files
Read to string (compio-rs#413)
* add read_to_string * add read_to_string_at * try to return origin String when read_to_string failed * fix: use unchecked because the buffer is empty --------- Co-authored-by: Yuyi Wang <Strawberry_Str@hotmail.com>
1 parent d2df07f commit e2394c7

2 files changed

Lines changed: 63 additions & 0 deletions

File tree

compio-io/src/read/ext.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#[cfg(feature = "allocator_api")]
22
use std::alloc::Allocator;
3+
use std::{io, io::ErrorKind};
34

45
use compio_buf::{BufResult, IntoInner, IoBuf, IoBufMut, IoVectoredBufMut, t_alloc};
56

@@ -141,6 +142,31 @@ macro_rules! loop_read_to_end {
141142
}};
142143
}
143144

145+
#[inline]
146+
fn after_read_to_string(res: io::Result<usize>, buf: Vec<u8>) -> BufResult<usize, String> {
147+
match res {
148+
Err(err) => {
149+
// we have to clear the read bytes if it is not valid utf8 bytes
150+
let buf = String::from_utf8(buf).unwrap_or_else(|err| {
151+
let mut buf = err.into_bytes();
152+
buf.clear();
153+
154+
// Safety: the buffer is empty
155+
unsafe { String::from_utf8_unchecked(buf) }
156+
});
157+
158+
BufResult(Err(err), buf)
159+
}
160+
Ok(n) => match String::from_utf8(buf) {
161+
Err(err) => BufResult(
162+
Err(std::io::Error::new(ErrorKind::InvalidData, err)),
163+
String::new(),
164+
),
165+
Ok(data) => BufResult(Ok(n), data),
166+
},
167+
}
168+
}
169+
144170
/// Implemented as an extension trait, adding utility methods to all
145171
/// [`AsyncRead`] types. Callers will tend to import this trait instead of
146172
/// [`AsyncRead`].
@@ -161,6 +187,12 @@ pub trait AsyncReadExt: AsyncRead {
161187
loop_read_exact!(buf, buf.buf_capacity(), read, loop self.read(buf.slice(read..)));
162188
}
163189

190+
/// Read all bytes as [`String`] until underlying reader reaches `EOF`.
191+
async fn read_to_string(&mut self, buf: String) -> BufResult<usize, String> {
192+
let BufResult(res, buf) = self.read_to_end(buf.into_bytes()).await;
193+
after_read_to_string(res, buf)
194+
}
195+
164196
/// Read all bytes until underlying reader reaches `EOF`.
165197
async fn read_to_end<#[cfg(feature = "allocator_api")] A: Allocator + 'static>(
166198
&mut self,
@@ -238,6 +270,13 @@ pub trait AsyncReadAtExt: AsyncReadAt {
238270
);
239271
}
240272

273+
/// Read all bytes as [`String`] until EOF in this source, placing them into
274+
/// `buffer`.
275+
async fn read_to_string_at(&mut self, buf: String, pos: u64) -> BufResult<usize, String> {
276+
let BufResult(res, buf) = self.read_to_end_at(buf.into_bytes(), pos).await;
277+
after_read_to_string(res, buf)
278+
}
279+
241280
/// Read all bytes until EOF in this source, placing them into `buffer`.
242281
///
243282
/// All bytes read from this source will be appended to the specified buffer

compio-io/tests/io.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,17 @@ fn read_to_end() {
364364
})
365365
}
366366

367+
#[test]
368+
fn read_to_string() {
369+
block_on(async {
370+
let mut src = ReadOne(Cursor::new("test".to_string().into_bytes()));
371+
372+
let (len, buf) = src.read_to_string(String::new()).await.unwrap();
373+
assert_eq!(len, 4);
374+
assert_eq!(buf, "test");
375+
})
376+
}
377+
367378
struct ReadOneAt(Vec<u8>);
368379

369380
impl AsyncReadAt for ReadOneAt {
@@ -401,6 +412,19 @@ fn read_to_end_at() {
401412
})
402413
}
403414

415+
#[test]
416+
fn read_to_string_at() {
417+
block_on(async {
418+
let mut src = vec![1, 1];
419+
src.extend_from_slice("test".as_bytes());
420+
let mut src = ReadOneAt(src);
421+
422+
let (len, buf) = src.read_to_string_at(String::new(), 2).await.unwrap();
423+
assert_eq!(len, 4);
424+
assert_eq!(buf, "test");
425+
})
426+
}
427+
404428
#[test]
405429
fn split_unsplit() {
406430
block_on(async {

0 commit comments

Comments
 (0)