Skip to content

Commit 8fca88e

Browse files
committed
Remove get_metadata from AsyncFileReader
Use file APIs directly
1 parent 60dbc82 commit 8fca88e

File tree

5 files changed

+90
-64
lines changed

5 files changed

+90
-64
lines changed

src/cog.rs

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
use std::sync::Arc;
2-
3-
use crate::error::AsyncTiffResult;
41
use crate::ifd::ImageFileDirectory;
5-
use crate::reader::AsyncFileReader;
62

73
/// A TIFF file.
84
#[derive(Debug, Clone)]
@@ -16,14 +12,6 @@ impl TIFF {
1612
Self { ifds }
1713
}
1814

19-
/// Open a new TIFF file.
20-
///
21-
/// This will read all the Image File Directories (IFDs) in the file.
22-
pub async fn try_open(reader: Arc<dyn AsyncFileReader>) -> AsyncTiffResult<Self> {
23-
let ifds = reader.get_metadata().await?;
24-
Ok(Self { ifds })
25-
}
26-
2715
/// Access the underlying Image File Directories.
2816
pub fn ifds(&self) -> &[ImageFileDirectory] {
2917
&self.ifds
@@ -36,7 +24,8 @@ mod test {
3624
use std::sync::Arc;
3725

3826
use crate::decoder::DecoderRegistry;
39-
use crate::reader::ObjectReader;
27+
use crate::metadata::{PrefetchMetadataFetch, TiffMetadataReader};
28+
use crate::reader::{AsyncFileReader, ObjectReader};
4029

4130
use super::*;
4231
use object_store::local::LocalFileSystem;
@@ -48,11 +37,20 @@ mod test {
4837
let folder = "/Users/kyle/github/developmentseed/async-tiff/";
4938
let path = object_store::path::Path::parse("m_4007307_sw_18_060_20220803.tif").unwrap();
5039
let store = Arc::new(LocalFileSystem::new_with_prefix(folder).unwrap());
51-
let reader = Arc::new(ObjectReader::new(store, path));
52-
53-
let cog_reader = TIFF::try_open(reader.clone()).await.unwrap();
40+
let reader = Arc::new(ObjectReader::new(store, path)) as Arc<dyn AsyncFileReader>;
41+
let prefetch_reader = PrefetchMetadataFetch::new(reader.clone(), 32 * 1024)
42+
.await
43+
.unwrap();
44+
let mut metadata_reader = TiffMetadataReader::try_open(&prefetch_reader)
45+
.await
46+
.unwrap();
47+
let ifds = metadata_reader
48+
.read_all_ifds(&prefetch_reader)
49+
.await
50+
.unwrap();
51+
let tiff = TIFF::new(ifds);
5452

55-
let ifd = &cog_reader.ifds[1];
53+
let ifd = &tiff.ifds[1];
5654
let decoder_registry = DecoderRegistry::default();
5755
let tile = ifd.fetch_tile(0, 0, reader.as_ref()).await.unwrap();
5856
let tile = tile.decode(&decoder_registry).unwrap();

src/metadata/mod.rs

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,60 @@
1-
//! TIFF metadata API
1+
//! API for reading metadata out of a TIFF file.
2+
//!
3+
//! ### Reading all TIFF metadata
4+
//!
5+
//! We can use [`TiffMetadataReader::read_all_ifds`] to read all IFDs up front:
6+
//!
7+
//! ```
8+
//! use std::env::current_dir;
9+
//! use std::sync::Arc;
10+
//!
11+
//! use object_store::local::LocalFileSystem;
12+
//!
13+
//! use async_tiff::metadata::{PrefetchMetadataFetch, TiffMetadataReader};
14+
//! use async_tiff::reader::ObjectReader;
15+
//!
16+
//! // Create new Arc<dyn ObjectStore>
17+
//! let store = Arc::new(LocalFileSystem::new_with_prefix(current_dir().unwrap()).unwrap());
18+
//!
19+
//! // Create new ObjectReader to map the ObjectStore to the AsyncFileReader trait
20+
//! let reader = ObjectReader::new(
21+
//! store,
22+
//! "tests/image_tiff/images/tiled-jpeg-rgb-u8.tif".into(),
23+
//! );
24+
//!
25+
//! // Use PrefetchMetadataFetch to ensure that a given number of bytes at the start of the
26+
//! // file are prefetched.
27+
//! //
28+
//! // This or a similar caching layer should **always** be used and ensures that the
29+
//! // underlying read calls that the TiffMetadataReader makes don't translate to actual
30+
//! // network fetches.
31+
//! let prefetch_reader = PrefetchMetadataFetch::new(reader.clone(), 32 * 1024)
32+
//! .await
33+
//! .unwrap();
34+
//!
35+
//! // Create a TiffMetadataReader wrapping some MetadataFetch
36+
//! let mut metadata_reader = TiffMetadataReader::try_open(&prefetch_reader)
37+
//! .await
38+
//! .unwrap();
39+
//!
40+
//! // Read all IFDs out of the source.
41+
//! let ifds = metadata_reader
42+
//! .read_all_ifds(&prefetch_reader)
43+
//! .await
44+
//! .unwrap();
45+
//! ```
46+
//!
47+
//!
48+
//! ### Caching/prefetching/buffering
49+
//!
50+
//! The underlying [`ImageFileDirectoryReader`] used to read tags out of the TIFF file reads each
51+
//! tag individually. This means that it will make many small byte range requests to the
52+
//! [`MetadataFetch`] implementation.
53+
//!
54+
//! Thus, it is **imperative to always supply some sort of caching, prefetching, or buffering**
55+
//! middleware when reading metadata. [`PrefetchMetadataFetch`] is an example of this, which
56+
//! fetches the first `N` bytes out of a file.
57+
//!
258
359
mod fetch;
460
mod reader;

src/metadata/reader.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,15 @@ use crate::tiff::tags::{Tag, Type};
1111
use crate::tiff::{TiffError, TiffFormatError, Value};
1212
use crate::ImageFileDirectory;
1313

14-
/// Read TIFF metadata from an async source.
14+
/// Entry point to reading TIFF metadata.
15+
///
16+
/// This is a stateful reader because we don't know how many IFDs will be encountered.
17+
///
18+
/// ```notest
19+
/// // fetch implements MetadataFetch
20+
/// let mut metadata_reader = TiffMetadataReader::try_open(&fetch).await?;
21+
/// let ifds = metadata_reader.read_all_ifds(&fetch).await?;
22+
/// ```
1523
pub struct TiffMetadataReader {
1624
endianness: Endianness,
1725
bigtiff: bool,
@@ -189,9 +197,9 @@ impl ImageFileDirectoryReader {
189197
})
190198
}
191199

192-
/// Manually read the tag with the specified idx.
200+
/// Manually read the tag with the specified index.
193201
///
194-
/// If there are no more tags, returns `None`.
202+
/// Panics if the tag index is out of range of the tag count.
195203
///
196204
/// This can be useful if you need to access tags at a low level. You'll need to call
197205
/// [`ImageFileDirectory::from_tags`] on the resulting collection of tags.

src/reader.rs

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ use futures::future::{BoxFuture, FutureExt};
1212
use futures::TryFutureExt;
1313

1414
use crate::error::AsyncTiffResult;
15-
use crate::metadata::TiffMetadataReader;
16-
use crate::ImageFileDirectory;
1715

1816
/// The asynchronous interface used to read COG files
1917
///
@@ -32,9 +30,6 @@ use crate::ImageFileDirectory;
3230
///
3331
/// [`tokio::fs::File`]: https://docs.rs/tokio/latest/tokio/fs/struct.File.html
3432
pub trait AsyncFileReader: Debug + Send + Sync {
35-
/// Retrieve the bytes in `range` as part of a request for header metadata.
36-
fn get_metadata(&self) -> BoxFuture<'_, AsyncTiffResult<Vec<ImageFileDirectory>>>;
37-
3833
/// Retrieve the bytes in `range` as part of a request for image data, not header metadata.
3934
///
4035
/// This is also used as the default implementation of [`MetadataFetch`] if not overridden.
@@ -62,10 +57,6 @@ pub trait AsyncFileReader: Debug + Send + Sync {
6257

6358
/// This allows Box<dyn AsyncFileReader + '_> to be used as an AsyncFileReader,
6459
impl AsyncFileReader for Box<dyn AsyncFileReader + '_> {
65-
fn get_metadata(&self) -> BoxFuture<'_, AsyncTiffResult<Vec<ImageFileDirectory>>> {
66-
self.as_ref().get_metadata()
67-
}
68-
6960
fn get_bytes(&self, range: Range<u64>) -> BoxFuture<'_, AsyncTiffResult<Bytes>> {
7061
self.as_ref().get_bytes(range)
7162
}
@@ -80,10 +71,6 @@ impl AsyncFileReader for Box<dyn AsyncFileReader + '_> {
8071

8172
/// This allows Arc<dyn AsyncFileReader + '_> to be used as an AsyncFileReader,
8273
impl AsyncFileReader for Arc<dyn AsyncFileReader + '_> {
83-
fn get_metadata(&self) -> BoxFuture<'_, AsyncTiffResult<Vec<ImageFileDirectory>>> {
84-
self.as_ref().get_metadata()
85-
}
86-
8774
fn get_bytes(&self, range: Range<u64>) -> BoxFuture<'_, AsyncTiffResult<Bytes>> {
8875
self.as_ref().get_bytes(range)
8976
}
@@ -144,15 +131,6 @@ impl<T: tokio::io::AsyncRead + tokio::io::AsyncSeek + Unpin + Send + Debug> Toki
144131
impl<T: tokio::io::AsyncRead + tokio::io::AsyncSeek + Unpin + Send + Debug> AsyncFileReader
145132
for TokioReader<T>
146133
{
147-
fn get_metadata(&self) -> BoxFuture<'_, AsyncTiffResult<Vec<ImageFileDirectory>>> {
148-
async move {
149-
let mut tiff_metadata_reader = TiffMetadataReader::try_open(self).await?;
150-
let ifds = tiff_metadata_reader.read_all_ifds(self).await?;
151-
Ok(ifds)
152-
}
153-
.boxed()
154-
}
155-
156134
fn get_bytes(&self, range: Range<u64>) -> BoxFuture<'_, AsyncTiffResult<Bytes>> {
157135
self.make_range_request(range).boxed()
158136
}
@@ -186,15 +164,6 @@ impl ObjectReader {
186164

187165
#[cfg(feature = "object_store")]
188166
impl AsyncFileReader for ObjectReader {
189-
fn get_metadata(&self) -> BoxFuture<'_, AsyncTiffResult<Vec<ImageFileDirectory>>> {
190-
async move {
191-
let mut tiff_metadata_reader = TiffMetadataReader::try_open(self).await?;
192-
let ifds = tiff_metadata_reader.read_all_ifds(self).await?;
193-
Ok(ifds)
194-
}
195-
.boxed()
196-
}
197-
198167
fn get_bytes(&self, range: Range<u64>) -> BoxFuture<'_, AsyncTiffResult<Bytes>> {
199168
self.make_range_request(range).boxed()
200169
}
@@ -253,15 +222,6 @@ impl ReqwestReader {
253222

254223
#[cfg(feature = "reqwest")]
255224
impl AsyncFileReader for ReqwestReader {
256-
fn get_metadata(&self) -> BoxFuture<'_, AsyncTiffResult<Vec<ImageFileDirectory>>> {
257-
async move {
258-
let mut tiff_metadata_reader = TiffMetadataReader::try_open(self).await?;
259-
let ifds = tiff_metadata_reader.read_all_ifds(self).await?;
260-
Ok(ifds)
261-
}
262-
.boxed()
263-
}
264-
265225
fn get_bytes(&self, range: Range<u64>) -> BoxFuture<'_, AsyncTiffResult<Bytes>> {
266226
self.make_range_request(range)
267227
}

tests/image_tiff/util.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use std::env::current_dir;
22
use std::sync::Arc;
33

4-
use async_tiff::reader::ObjectReader;
4+
use async_tiff::metadata::TiffMetadataReader;
5+
use async_tiff::reader::{AsyncFileReader, ObjectReader};
56
use async_tiff::TIFF;
67
use object_store::local::LocalFileSystem;
78

@@ -10,6 +11,9 @@ const TEST_IMAGE_DIR: &str = "tests/image_tiff/images/";
1011
pub(crate) async fn open_tiff(filename: &str) -> TIFF {
1112
let store = Arc::new(LocalFileSystem::new_with_prefix(current_dir().unwrap()).unwrap());
1213
let path = format!("{TEST_IMAGE_DIR}/{filename}");
13-
let reader = Arc::new(ObjectReader::new(store.clone(), path.as_str().into()));
14-
TIFF::try_open(reader).await.unwrap()
14+
let reader = Arc::new(ObjectReader::new(store.clone(), path.as_str().into()))
15+
as Arc<dyn AsyncFileReader>;
16+
let mut metadata_reader = TiffMetadataReader::try_open(&reader).await.unwrap();
17+
let ifds = metadata_reader.read_all_ifds(&reader).await.unwrap();
18+
TIFF::new(ifds)
1519
}

0 commit comments

Comments
 (0)