|
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,76 +837,80 @@ 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?; |
857 | | - |
858 | | - // TODO: better handle management of cursor state <- should be done now |
859 | | - // let ifd_entry_size = if bigtiff { 20 } else { 12 }; |
860 | | - // cursor.seek(start_cursor_position + ifd_entry_size); |
861 | | - |
862 | | - Ok((tag_name, tag_value)) |
863 | | -} |
864 | | - |
865 | | -/// Read a tag's value from the cursor |
866 | | -/// |
867 | | -/// NOTE: this does not maintain cursor state |
868 | | -// This is derived from the upstream tiff crate: |
869 | | -// 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, |
872 | | - tag_type: Type, |
873 | | - count: u64, |
874 | | - bigtiff: bool, |
875 | | -) -> AsyncTiffResult<Value> { |
876 | | - // Case 1: there are no values so we can return immediately. |
877 | | - if count == 0 { |
878 | | - return Ok(Value::List(vec![])); |
879 | | - } |
880 | | - |
881 | 859 | let tag_size = tag_type.size(); |
882 | 860 |
|
883 | 861 | let value_byte_length = count.checked_mul(tag_size).unwrap(); |
884 | 862 |
|
885 | 863 | // prefetch all tag data |
886 | 864 | let mut data = if (bigtiff && value_byte_length <= 8) || value_byte_length <= 4 { |
887 | 865 | // value fits in offset field |
888 | | - let res = cursor.read(value_byte_length).await?; |
| 866 | + let mut res = vec![0u8; value_byte_length as usize]; |
| 867 | + cursor.read_exact(&mut res)?; |
889 | 868 | if bigtiff { |
890 | | - cursor.advance(8 - value_byte_length); |
| 869 | + cursor.advance(8 - value_byte_length)?; |
891 | 870 | } else { |
892 | | - cursor.advance(4 - value_byte_length); |
| 871 | + cursor.advance(4 - value_byte_length)?; |
893 | 872 | } |
894 | | - res |
| 873 | + EndianAwareReader::new(Bytes::from_owner(res).reader(), cursor.endianness()) |
895 | 874 | } else { |
896 | | - // Seek cursor |
| 875 | + // fetch using file_reader |
897 | 876 | let offset = if bigtiff { |
898 | | - cursor.read_u64().await? |
| 877 | + cursor.read_u64()? |
899 | 878 | } else { |
900 | | - cursor.read_u32().await?.into() |
| 879 | + cursor.read_u32()?.into() |
901 | 880 | }; |
902 | | - let reader = cursor |
903 | | - .reader() |
| 881 | + let reader = file_reader |
904 | 882 | .get_bytes(offset..offset + value_byte_length) |
905 | 883 | .await? |
906 | 884 | .reader(); |
907 | 885 | EndianAwareReader::new(reader, cursor.endianness()) |
908 | 886 | // cursor.seek(offset); |
909 | 887 | // cursor.read(value_byte_length).await? |
910 | 888 | }; |
| 889 | + |
| 890 | + let tag_value = read_tag_value(&mut data, tag_type, count)?; |
| 891 | + |
| 892 | + // TODO: better handle management of cursor state <- should be done now |
| 893 | + // let ifd_entry_size = if bigtiff { 20 } else { 12 }; |
| 894 | + // cursor.seek(start_cursor_position + ifd_entry_size); |
| 895 | + |
| 896 | + Ok((tag_name, tag_value)) |
| 897 | +} |
| 898 | + |
| 899 | +/// Read a tag's value from the cursor |
| 900 | +/// |
| 901 | +/// NOTE: this does not maintain cursor state |
| 902 | +// This is derived from the upstream tiff crate: |
| 903 | +// https://github.com/image-rs/image-tiff/blob/6dc7a266d30291db1e706c8133357931f9e2a053/src/decoder/ifd.rs#L369-L639 |
| 904 | +fn read_tag_value( |
| 905 | + data: &mut EndianAwareReader, |
| 906 | + tag_type: Type, |
| 907 | + count: u64, |
| 908 | +) -> AsyncTiffResult<Value> { |
| 909 | + // Case 1: there are no values so we can return immediately. |
| 910 | + if count == 0 { |
| 911 | + return Ok(Value::List(vec![])); |
| 912 | + } |
| 913 | + |
911 | 914 | // Case 2: there is one value. |
912 | 915 | if count == 1 { |
913 | 916 | return Ok(match tag_type { |
|
0 commit comments