@@ -17,7 +17,7 @@ use crate::error::{AsyncTiffError, AsyncTiffResult};
17
17
use async_mutex:: Mutex ;
18
18
#[ cfg( feature = "tokio" ) ]
19
19
use tokio:: sync:: Mutex ;
20
- #[ cfg( not( any( feature= "tokio" , feature= "async_mutex" ) ) ) ]
20
+ #[ cfg( not( any( feature = "tokio" , feature = "async_mutex" ) ) ) ]
21
21
compile_error ! ( "at least one of 'tokio' or 'async_mutex' features should be enabled" ) ;
22
22
23
23
/// The asynchronous interface used to read COG files
@@ -233,14 +233,77 @@ impl AsyncFileReader for ReqwestReader {
233
233
}
234
234
}
235
235
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" ) ) ]
237
299
#[ derive( Debug ) ]
238
300
pub struct PrefetchReader {
239
301
reader : Arc < dyn AsyncFileReader > ,
240
302
buffer : Bytes ,
241
303
tile_info_cache : Mutex < ( Range < u64 > , Bytes ) > ,
242
304
}
243
305
306
+ #[ cfg( any( feature = "tokio" , feature = "async_mutex" ) ) ]
244
307
impl PrefetchReader {
245
308
/// Construct a new PrefetchReader, catching the first `prefetch` bytes of the file.
246
309
pub async fn new ( reader : Arc < dyn AsyncFileReader > , prefetch : u64 ) -> AsyncTiffResult < Self > {
@@ -254,31 +317,38 @@ impl PrefetchReader {
254
317
}
255
318
}
256
319
320
+ #[ cfg( any( feature = "tokio" , feature = "async_mutex" ) ) ]
257
321
impl AsyncFileReader for PrefetchReader {
258
322
fn get_metadata_bytes ( & self , range : Range < u64 > ) -> BoxFuture < ' _ , AsyncTiffResult < Bytes > > {
323
+ // check prefetch
259
324
if range. end < self . buffer . len ( ) as _ {
260
325
let usize_range = range. start as usize ..range. end as usize ;
261
326
let result = self . buffer . slice ( usize_range) ;
262
327
async { Ok ( result) } . boxed ( )
263
328
} else {
264
329
async move {
265
330
{
331
+ // check cache
266
332
let lock = self . tile_info_cache . lock ( ) . await ;
267
- // let (c_range, cache) = (lock.0, lock.1);
268
333
if range. start >= lock. 0 . start && range. end <= lock. 0 . end {
269
334
let usize_range = ( range. start - lock. 0 . start ) as usize
270
335
..( range. end - lock. 0 . start ) as usize ;
271
336
return Ok ( lock. 1 . slice ( usize_range) ) ;
272
337
}
273
338
}
339
+ // determine new cache size
274
340
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 ) ;
276
342
let new_c_range = range. start ..range. start + estimate;
343
+
344
+ // put in new cache
277
345
let res = self . reader . get_metadata_bytes ( new_c_range. clone ( ) ) . await ?;
278
346
{
279
347
let mut lock = self . tile_info_cache . lock ( ) . await ;
280
348
* lock = ( new_c_range, res. clone ( ) ) ;
281
349
}
350
+
351
+ // yay
282
352
Ok ( res. slice ( 0 ..range_len as _ ) )
283
353
}
284
354
. boxed ( )
0 commit comments