1
1
use std:: collections:: HashMap ;
2
2
use std:: io:: Read ;
3
3
use std:: ops:: Range ;
4
+ use std:: sync:: Arc ;
4
5
5
6
use bytes:: { buf:: Buf , Bytes } ;
6
7
use num_enum:: TryFromPrimitive ;
@@ -195,26 +196,24 @@ impl ImageFileDirectory {
195
196
} else {
196
197
cursor. read_u16 ( ) . await ?. into ( )
197
198
} ;
198
- let mut tags = HashMap :: with_capacity ( tag_count as usize ) ;
199
- for _ in 0 ..tag_count {
200
- let ( tag_name, tag_value) = read_tag ( cursor, bigtiff) . await ?;
201
- tags. insert ( tag_name, tag_value) ;
202
- }
203
-
204
199
// Tag 2 bytes
205
200
// Type 2 bytes
206
201
// Count:
207
202
// - bigtiff: 8 bytes
208
203
// - else: 4 bytes
209
204
// Value:
210
- // - bigtiff: 8 bytes either a pointer the value itself
211
- // - else: 4 bytes either a pointer the value itself
205
+ // - bigtiff: 8 bytes either a pointer or the value itself
206
+ // - else: 4 bytes either a pointer or the value itself
212
207
let ifd_entry_byte_size = if bigtiff { 20 } else { 12 } ;
213
- // The size of `tag_count` that we read above
214
- let tag_count_byte_size = if bigtiff { 8 } else { 2 } ;
215
208
216
- // Reset the cursor position before reading the next ifd offset
217
- cursor. seek ( ifd_start + ( ifd_entry_byte_size * tag_count) + tag_count_byte_size) ;
209
+ // read all tag data into an EndianAwareReader
210
+ let mut reader = cursor. read ( ifd_entry_byte_size * tag_count) . await ?;
211
+
212
+ let mut tags = HashMap :: with_capacity ( tag_count as usize ) ;
213
+ for _ in 0 ..tag_count {
214
+ let ( tag_name, tag_value) = read_tag ( & mut reader, cursor. reader ( ) , bigtiff) . await ?;
215
+ tags. insert ( tag_name, tag_value) ;
216
+ }
218
217
219
218
let next_ifd_offset = if bigtiff {
220
219
cursor. read_u64 ( ) . await ?
@@ -838,22 +837,57 @@ impl ImageFileDirectory {
838
837
}
839
838
840
839
/// Read a single tag from the cursor
841
- async fn read_tag ( cursor : & mut AsyncCursor , bigtiff : bool ) -> AsyncTiffResult < ( Tag , Value ) > {
840
+ async fn read_tag (
841
+ cursor : & mut EndianAwareReader ,
842
+ file_reader : & Arc < dyn AsyncFileReader > ,
843
+ bigtiff : bool ,
844
+ ) -> AsyncTiffResult < ( Tag , Value ) > {
842
845
// let start_cursor_position = cursor.position();
843
846
844
- let tag_name = Tag :: from_u16_exhaustive ( cursor. read_u16 ( ) . await ?) ;
847
+ let tag_name = Tag :: from_u16_exhaustive ( cursor. read_u16 ( ) ?) ;
845
848
846
- let tag_type_code = cursor. read_u16 ( ) . await ?;
849
+ let tag_type_code = cursor. read_u16 ( ) ?;
847
850
let tag_type = Type :: from_u16 ( tag_type_code) . expect (
848
851
"Unknown tag type {tag_type_code}. TODO: we should skip entries with unknown tag types." ,
849
852
) ;
850
853
let count = if bigtiff {
851
- cursor. read_u64 ( ) . await ?
854
+ cursor. read_u64 ( ) ?
852
855
} else {
853
- cursor. read_u32 ( ) . await ?. into ( )
856
+ cursor. read_u32 ( ) ?. into ( )
854
857
} ;
855
858
856
- let tag_value = read_tag_value ( cursor, tag_type, count, bigtiff) . await ?;
859
+ let tag_size = tag_type. size ( ) ;
860
+
861
+ let value_byte_length = count. checked_mul ( tag_size) . unwrap ( ) ;
862
+
863
+ // prefetch all tag data
864
+ let mut data = if ( bigtiff && value_byte_length <= 8 ) || value_byte_length <= 4 {
865
+ // value fits in offset field
866
+ let mut res = vec ! [ 0u8 ; value_byte_length as usize ] ;
867
+ cursor. read_exact ( & mut res) ?;
868
+ if bigtiff {
869
+ cursor. advance ( 8 - value_byte_length) ?;
870
+ } else {
871
+ cursor. advance ( 4 - value_byte_length) ?;
872
+ }
873
+ EndianAwareReader :: new ( Bytes :: from_owner ( res) . reader ( ) , cursor. endianness ( ) )
874
+ } else {
875
+ // fetch using file_reader
876
+ let offset = if bigtiff {
877
+ cursor. read_u64 ( ) ?
878
+ } else {
879
+ cursor. read_u32 ( ) ?. into ( )
880
+ } ;
881
+ let reader = file_reader
882
+ . get_bytes ( offset..offset + value_byte_length)
883
+ . await ?
884
+ . reader ( ) ;
885
+ EndianAwareReader :: new ( reader, cursor. endianness ( ) )
886
+ // cursor.seek(offset);
887
+ // cursor.read(value_byte_length).await?
888
+ } ;
889
+
890
+ let tag_value = read_tag_value ( & mut data, tag_type, count) ?;
857
891
858
892
// TODO: better handle management of cursor state <- should be done now
859
893
// let ifd_entry_size = if bigtiff { 20 } else { 12 };
@@ -867,43 +901,16 @@ async fn read_tag(cursor: &mut AsyncCursor, bigtiff: bool) -> AsyncTiffResult<(T
867
901
/// NOTE: this does not maintain cursor state
868
902
// This is derived from the upstream tiff crate:
869
903
// https://github.com/image-rs/image-tiff/blob/6dc7a266d30291db1e706c8133357931f9e2a053/src/decoder/ifd.rs#L369-L639
870
- async fn read_tag_value (
871
- cursor : & mut AsyncCursor ,
904
+ fn read_tag_value (
905
+ data : & mut EndianAwareReader ,
872
906
tag_type : Type ,
873
907
count : u64 ,
874
- bigtiff : bool ,
875
908
) -> AsyncTiffResult < Value > {
876
909
// Case 1: there are no values so we can return immediately.
877
910
if count == 0 {
878
911
return Ok ( Value :: List ( vec ! [ ] ) ) ;
879
912
}
880
913
881
- let tag_size = tag_type. size ( ) ;
882
-
883
- let value_byte_length = count. checked_mul ( tag_size) . unwrap ( ) ;
884
-
885
- // prefetch all tag data
886
- let mut data = if ( bigtiff && value_byte_length <= 8 ) || value_byte_length <= 4 {
887
- // value fits in offset field
888
- let res = cursor. read ( value_byte_length) . await ?;
889
- if bigtiff {
890
- cursor. advance ( 8 -value_byte_length) ;
891
- } else {
892
- cursor. advance ( 4 -value_byte_length) ;
893
- }
894
- res
895
- } else {
896
- // Seek cursor
897
- let offset = if bigtiff {
898
- cursor. read_u64 ( ) . await ?
899
- } else {
900
- cursor. read_u32 ( ) . await ?. into ( )
901
- } ;
902
- let reader = cursor. reader ( ) . get_bytes ( offset..offset+value_byte_length) . await ?. reader ( ) ;
903
- EndianAwareReader :: new ( reader, cursor. endianness ( ) )
904
- // cursor.seek(offset);
905
- // cursor.read(value_byte_length).await?
906
- } ;
907
914
// Case 2: there is one value.
908
915
if count == 1 {
909
916
return Ok ( match tag_type {
0 commit comments