Skip to content

Commit c92867a

Browse files
andriyDevcart
andauthored
Allow Reader to implement AsyncSeek and provide a way for loaders to "ask" for a compatible reader. (#22104)
# Objective - In #14194, we replaced `AsyncSeek` with `AsyncSeekForward`. This allowed us to support more kinds of sources, but the cost was that loaders were more limited in what they could do with the reader. - Archive-like things generally use offsets relative to the start of the file. `AsyncSeekForward` made it difficult to deal with this (or impossible in some cases). - This is a step towards #21641. - Fixes #12880. ## Solution - Allow `AssetLoader` to say which "features" of a reader it needs. - Pass that list of features to `AssetReader` so it can decide how to handle it. - Add a new `AssetReaderError::UnsupportedFeature`. This design is kind of "weak" - there's no guarantee that a loader that requests `AnySeek` will get a reader that actually implements `AsyncSeek`. Or on the other side, there's no guarantee that a loader actually requests the features that it uses. However, in practice it's likely enough: errors are likely to guide users to the correct situation. In the future, we could perhaps have a "sieve reader", which blocks any features the loader didn't explicitly request. Perhaps this is a debug only feature, or something that can be toggled. ## Testing - Ran the modified examples. They still seem to work! --------- Co-authored-by: Carter Anderson <[email protected]>
1 parent 61a4645 commit c92867a

File tree

22 files changed

+594
-342
lines changed

22 files changed

+594
-342
lines changed

crates/bevy_asset/src/io/android.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use crate::io::{get_meta_path, AssetReader, AssetReaderError, PathStream, Reader, VecReader};
1+
use crate::io::{
2+
get_meta_path, AssetReader, AssetReaderError, PathStream, Reader, ReaderRequiredFeatures,
3+
VecReader,
4+
};
25
use alloc::{borrow::ToOwned, boxed::Box, ffi::CString, vec::Vec};
36
use futures_lite::stream;
47
use std::path::Path;
@@ -16,7 +19,11 @@ use std::path::Path;
1619
pub struct AndroidAssetReader;
1720

1821
impl AssetReader for AndroidAssetReader {
19-
async fn read<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
22+
async fn read<'a>(
23+
&'a self,
24+
path: &'a Path,
25+
_required_features: ReaderRequiredFeatures,
26+
) -> Result<impl Reader + 'a, AssetReaderError> {
2027
let asset_manager = bevy_android::ANDROID_APP
2128
.get()
2229
.expect("Bevy must be setup with the #[bevy_main] macro on Android")

crates/bevy_asset/src/io/file/file_asset.rs

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,23 @@
11
use crate::io::{
2-
get_meta_path, AssetReader, AssetReaderError, AssetWriter, AssetWriterError, AsyncSeekForward,
3-
PathStream, Reader, Writer,
2+
get_meta_path, AssetReader, AssetReaderError, AssetWriter, AssetWriterError, PathStream,
3+
Reader, ReaderRequiredFeatures, Writer,
44
};
55
use async_fs::{read_dir, File};
6-
use futures_io::AsyncSeek;
76
use futures_lite::StreamExt;
87

98
use alloc::{borrow::ToOwned, boxed::Box};
10-
use core::{pin::Pin, task, task::Poll};
119
use std::path::Path;
1210

1311
use super::{FileAssetReader, FileAssetWriter};
1412

15-
impl AsyncSeekForward for File {
16-
fn poll_seek_forward(
17-
mut self: Pin<&mut Self>,
18-
cx: &mut task::Context<'_>,
19-
offset: u64,
20-
) -> Poll<futures_io::Result<u64>> {
21-
let offset: Result<i64, _> = offset.try_into();
22-
23-
if let Ok(offset) = offset {
24-
Pin::new(&mut self).poll_seek(cx, futures_io::SeekFrom::Current(offset))
25-
} else {
26-
Poll::Ready(Err(std::io::Error::new(
27-
std::io::ErrorKind::InvalidInput,
28-
"seek position is out of range",
29-
)))
30-
}
31-
}
32-
}
33-
3413
impl Reader for File {}
3514

3615
impl AssetReader for FileAssetReader {
37-
async fn read<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
16+
async fn read<'a>(
17+
&'a self,
18+
path: &'a Path,
19+
_required_features: ReaderRequiredFeatures,
20+
) -> Result<impl Reader + 'a, AssetReaderError> {
3821
let full_path = self.root_path.join(path);
3922
File::open(&full_path).await.map_err(|e| {
4023
if e.kind() == std::io::ErrorKind::NotFound {

crates/bevy_asset/src/io/file/sync_file_asset.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ use futures_io::{AsyncRead, AsyncWrite};
22
use futures_lite::Stream;
33

44
use crate::io::{
5-
get_meta_path, AssetReader, AssetReaderError, AssetWriter, AssetWriterError, AsyncSeekForward,
6-
PathStream, Reader, Writer,
5+
get_meta_path, AssetReader, AssetReaderError, AssetWriter, AssetWriterError, AsyncSeek,
6+
PathStream, Reader, ReaderRequiredFeatures, Writer,
77
};
88

99
use alloc::{borrow::ToOwned, boxed::Box, vec::Vec};
1010
use core::{pin::Pin, task::Poll};
1111
use std::{
1212
fs::{read_dir, File},
13-
io::{Read, Seek, Write},
13+
io::{Read, Seek, SeekFrom, Write},
1414
path::{Path, PathBuf},
1515
};
1616

@@ -30,17 +30,13 @@ impl AsyncRead for FileReader {
3030
}
3131
}
3232

33-
impl AsyncSeekForward for FileReader {
34-
fn poll_seek_forward(
35-
self: Pin<&mut Self>,
33+
impl AsyncSeek for FileReader {
34+
fn poll_seek(
35+
mut self: Pin<&mut Self>,
3636
_cx: &mut core::task::Context<'_>,
37-
offset: u64,
37+
pos: SeekFrom,
3838
) -> Poll<std::io::Result<u64>> {
39-
let this = self.get_mut();
40-
let current = this.0.stream_position()?;
41-
let seek = this.0.seek(std::io::SeekFrom::Start(current + offset));
42-
43-
Poll::Ready(seek)
39+
Poll::Ready(self.0.seek(pos))
4440
}
4541
}
4642

@@ -99,7 +95,11 @@ impl Stream for DirReader {
9995
}
10096

10197
impl AssetReader for FileAssetReader {
102-
async fn read<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
98+
async fn read<'a>(
99+
&'a self,
100+
path: &'a Path,
101+
_required_features: ReaderRequiredFeatures,
102+
) -> Result<impl Reader + 'a, AssetReaderError> {
103103
let full_path = self.root_path.join(path);
104104
match File::open(&full_path) {
105105
Ok(file) => Ok(FileReader(file)),

crates/bevy_asset/src/io/gated.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::io::{AssetReader, AssetReaderError, PathStream, Reader};
1+
use crate::io::{AssetReader, AssetReaderError, PathStream, Reader, ReaderRequiredFeatures};
22
use alloc::{boxed::Box, sync::Arc};
33
use async_channel::{Receiver, Sender};
44
use bevy_platform::{collections::HashMap, sync::RwLock};
@@ -55,7 +55,11 @@ impl<R: AssetReader> GatedReader<R> {
5555
}
5656

5757
impl<R: AssetReader> AssetReader for GatedReader<R> {
58-
async fn read<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
58+
async fn read<'a>(
59+
&'a self,
60+
path: &'a Path,
61+
required_features: ReaderRequiredFeatures,
62+
) -> Result<impl Reader + 'a, AssetReaderError> {
5963
let receiver = {
6064
let mut gates = self.gates.write().unwrap_or_else(PoisonError::into_inner);
6165
let gates = gates
@@ -64,7 +68,7 @@ impl<R: AssetReader> AssetReader for GatedReader<R> {
6468
gates.1.clone()
6569
};
6670
receiver.recv().await.unwrap();
67-
let result = self.reader.read(path).await?;
71+
let result = self.reader.read(path, required_features).await?;
6872
Ok(result)
6973
}
7074

crates/bevy_asset/src/io/memory.rs

Lines changed: 33 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
1-
use crate::io::{AssetReader, AssetReaderError, AssetWriter, AssetWriterError, PathStream, Reader};
1+
use crate::io::{
2+
AssetReader, AssetReaderError, AssetWriter, AssetWriterError, PathStream, Reader,
3+
ReaderRequiredFeatures,
4+
};
25
use alloc::{borrow::ToOwned, boxed::Box, sync::Arc, vec, vec::Vec};
36
use bevy_platform::{
47
collections::HashMap,
58
sync::{PoisonError, RwLock},
69
};
710
use core::{pin::Pin, task::Poll};
811
use futures_io::{AsyncRead, AsyncWrite};
9-
use futures_lite::{ready, Stream};
12+
use futures_lite::Stream;
1013
use std::{
11-
io::{Error, ErrorKind},
14+
io::{Error, ErrorKind, SeekFrom},
1215
path::{Path, PathBuf},
1316
};
1417

15-
use super::AsyncSeekForward;
18+
use super::AsyncSeek;
1619

1720
#[derive(Default, Debug)]
1821
struct DirInternal {
@@ -314,41 +317,33 @@ struct DataReader {
314317

315318
impl AsyncRead for DataReader {
316319
fn poll_read(
317-
mut self: Pin<&mut Self>,
318-
cx: &mut core::task::Context<'_>,
320+
self: Pin<&mut Self>,
321+
_cx: &mut core::task::Context<'_>,
319322
buf: &mut [u8],
320323
) -> Poll<futures_io::Result<usize>> {
321-
if self.bytes_read >= self.data.value().len() {
322-
Poll::Ready(Ok(0))
323-
} else {
324-
let n =
325-
ready!(Pin::new(&mut &self.data.value()[self.bytes_read..]).poll_read(cx, buf))?;
326-
self.bytes_read += n;
327-
Poll::Ready(Ok(n))
328-
}
324+
// Get the mut borrow to avoid trying to borrow the pin itself multiple times.
325+
let this = self.get_mut();
326+
Poll::Ready(Ok(crate::io::slice_read(
327+
this.data.value(),
328+
&mut this.bytes_read,
329+
buf,
330+
)))
329331
}
330332
}
331333

332-
impl AsyncSeekForward for DataReader {
333-
fn poll_seek_forward(
334-
mut self: Pin<&mut Self>,
334+
impl AsyncSeek for DataReader {
335+
fn poll_seek(
336+
self: Pin<&mut Self>,
335337
_cx: &mut core::task::Context<'_>,
336-
offset: u64,
338+
pos: SeekFrom,
337339
) -> Poll<std::io::Result<u64>> {
338-
let result = self
339-
.bytes_read
340-
.try_into()
341-
.map(|bytes_read: u64| bytes_read + offset);
342-
343-
if let Ok(new_pos) = result {
344-
self.bytes_read = new_pos as _;
345-
Poll::Ready(Ok(new_pos as _))
346-
} else {
347-
Poll::Ready(Err(Error::new(
348-
ErrorKind::InvalidInput,
349-
"seek position is out of range",
350-
)))
351-
}
340+
// Get the mut borrow to avoid trying to borrow the pin itself multiple times.
341+
let this = self.get_mut();
342+
Poll::Ready(crate::io::slice_seek(
343+
this.data.value(),
344+
&mut this.bytes_read,
345+
pos,
346+
))
352347
}
353348
}
354349

@@ -357,21 +352,16 @@ impl Reader for DataReader {
357352
&'a mut self,
358353
buf: &'a mut Vec<u8>,
359354
) -> stackfuture::StackFuture<'a, std::io::Result<usize>, { super::STACK_FUTURE_SIZE }> {
360-
stackfuture::StackFuture::from(async {
361-
if self.bytes_read >= self.data.value().len() {
362-
Ok(0)
363-
} else {
364-
buf.extend_from_slice(&self.data.value()[self.bytes_read..]);
365-
let n = self.data.value().len() - self.bytes_read;
366-
self.bytes_read = self.data.value().len();
367-
Ok(n)
368-
}
369-
})
355+
crate::io::read_to_end(self.data.value(), &mut self.bytes_read, buf)
370356
}
371357
}
372358

373359
impl AssetReader for MemoryAssetReader {
374-
async fn read<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
360+
async fn read<'a>(
361+
&'a self,
362+
path: &'a Path,
363+
_required_features: ReaderRequiredFeatures,
364+
) -> Result<impl Reader + 'a, AssetReaderError> {
375365
self.root
376366
.get_asset(path)
377367
.map(|data| DataReader {

0 commit comments

Comments
 (0)