Skip to content

Commit feefadd

Browse files
committed
added docs+doctest to prefetchreader
1 parent ab1f6a1 commit feefadd

File tree

2 files changed

+75
-4
lines changed

2 files changed

+75
-4
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ tokio = { version = "1.9", features = [
3232
"macros",
3333
"fs",
3434
"rt-multi-thread",
35+
# "rt",
3536
"io-util",
3637
] }
3738

src/reader.rs

Lines changed: 74 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use crate::error::{AsyncTiffError, AsyncTiffResult};
1717
use async_mutex::Mutex;
1818
#[cfg(feature = "tokio")]
1919
use tokio::sync::Mutex;
20-
#[cfg(not(any(feature="tokio", feature="async_mutex")))]
20+
#[cfg(not(any(feature = "tokio", feature = "async_mutex")))]
2121
compile_error!("at least one of 'tokio' or 'async_mutex' features should be enabled");
2222

2323
/// The asynchronous interface used to read COG files
@@ -233,14 +233,77 @@ impl AsyncFileReader for ReqwestReader {
233233
}
234234
}
235235

236-
/// An AsyncFileReader that caches the first `prefetch` bytes of a file.
236+
impl AsyncFileReader for Bytes {
237+
fn get_image_bytes(&self, range: Range<u64>) -> BoxFuture<'_, AsyncTiffResult<Bytes>> {
238+
self.get_metadata_bytes(range)
239+
}
240+
241+
fn get_metadata_bytes(&self, range: Range<u64>) -> BoxFuture<'_, AsyncTiffResult<Bytes>> {
242+
if range.end <= self.len() as _ {
243+
let usize_range = range.start as usize..range.end as usize;
244+
async { Ok(self.slice(usize_range)) }.boxed()
245+
} else {
246+
let range_len = range.end - range.start;
247+
let range_read = range_len - (range.end-self.len() as u64);
248+
async move { Err(AsyncTiffError::EndOfFile(range_len, range_read)) }.boxed()
249+
}
250+
}
251+
}
252+
253+
/// An AsyncFileReader that caches the first `prefetch` bytes of a file. Also
254+
/// holds a cache for out-of-prefetch metadata.
255+
///
256+
/// When a request is out-of-bounds, an estimate of the remaining
257+
/// `tile_offsets`/`tile_byte_counts` array is made as `2*(len+2*len.isqrt())`
258+
///
259+
/// # Examples
260+
///
261+
/// ```
262+
/// # futures::executor::block_on( async { // https://stackoverflow.com/a/64597248/14681457
263+
/// # use async_tiff::error::AsyncTiffError;
264+
/// # use async_tiff::reader::{AsyncFileReader, PrefetchReader};
265+
/// # use bytes::Bytes;
266+
/// # use std::sync::Arc;
267+
/// #
268+
/// # let file = Bytes::from_static(&[42u8; 128 * 1024]);
269+
/// #
270+
/// // create a reader that prefetches the first 16 kB
271+
/// let reader = PrefetchReader::new(Arc::new(file), 16 * 1024).await?;
272+
///
273+
/// // get some data from the prefetch
274+
/// assert_eq!(*reader.get_metadata_bytes(42..1312).await?, [42; 1312 - 42]);
275+
///
276+
/// const start: u64 = 16 * 1024;
277+
/// const len: u64 = 16 * 1024;
278+
/// // get some data from outside the prefetch range
279+
/// assert_eq!(
280+
/// *reader.get_metadata_bytes(start..start + len).await?,
281+
/// [42; 16 * 1024]
282+
/// );
283+
///
284+
/// // this is now also (exactly) cached
285+
/// assert_eq!(
286+
/// *reader
287+
/// .get_metadata_bytes(start..start + 2 * (len + 2 * len.isqrt()))
288+
/// .await?,
289+
/// [42; 2 * (len + 2 * len.isqrt()) as usize]
290+
/// );
291+
///
292+
/// // this will not check the cache
293+
/// reader.get_image_bytes(start..start + len).await?;
294+
///
295+
/// # Ok::<(),AsyncTiffError>(())
296+
/// # });
297+
/// ```
298+
#[cfg(any(feature = "tokio", feature = "async_mutex"))]
237299
#[derive(Debug)]
238300
pub struct PrefetchReader {
239301
reader: Arc<dyn AsyncFileReader>,
240302
buffer: Bytes,
241303
tile_info_cache: Mutex<(Range<u64>, Bytes)>,
242304
}
243305

306+
#[cfg(any(feature = "tokio", feature = "async_mutex"))]
244307
impl PrefetchReader {
245308
/// Construct a new PrefetchReader, catching the first `prefetch` bytes of the file.
246309
pub async fn new(reader: Arc<dyn AsyncFileReader>, prefetch: u64) -> AsyncTiffResult<Self> {
@@ -254,31 +317,38 @@ impl PrefetchReader {
254317
}
255318
}
256319

320+
#[cfg(any(feature = "tokio", feature = "async_mutex"))]
257321
impl AsyncFileReader for PrefetchReader {
258322
fn get_metadata_bytes(&self, range: Range<u64>) -> BoxFuture<'_, AsyncTiffResult<Bytes>> {
323+
// check prefetch
259324
if range.end < self.buffer.len() as _ {
260325
let usize_range = range.start as usize..range.end as usize;
261326
let result = self.buffer.slice(usize_range);
262327
async { Ok(result) }.boxed()
263328
} else {
264329
async move {
265330
{
331+
// check cache
266332
let lock = self.tile_info_cache.lock().await;
267-
// let (c_range, cache) = (lock.0, lock.1);
268333
if range.start >= lock.0.start && range.end <= lock.0.end {
269334
let usize_range = (range.start - lock.0.start) as usize
270335
..(range.end - lock.0.start) as usize;
271336
return Ok(lock.1.slice(usize_range));
272337
}
273338
}
339+
// determine new cache size
274340
let range_len = range.end - range.start;
275-
let estimate = 2 * (range_len + range_len.isqrt());
341+
let estimate = 2 * (range_len + 2 * range_len.isqrt()).max(8 * 1024);
276342
let new_c_range = range.start..range.start + estimate;
343+
344+
// put in new cache
277345
let res = self.reader.get_metadata_bytes(new_c_range.clone()).await?;
278346
{
279347
let mut lock = self.tile_info_cache.lock().await;
280348
*lock = (new_c_range, res.clone());
281349
}
350+
351+
// yay
282352
Ok(res.slice(0..range_len as _))
283353
}
284354
.boxed()

0 commit comments

Comments
 (0)