From 312218d7b1a42ba27907935e1e84dd883eeff55d Mon Sep 17 00:00:00 2001 From: Dustin Oprea Date: Wed, 8 Jul 2020 04:01:09 -0400 Subject: [PATCH] Use encapsulated data/reader rather than bytes Given a stream of data, it is possible to determine the beginning of EXIF data but not the end. Therefore, either an image-aware implementation must know how to parse an image and extract the EXIF data or a brute-force search implementation (one of which is provided by this project) must find the start anchor and then return all bytes from that to the end of the file. We have been made aware of some use-cases where a brute-force search might be unavoidable due to trust or stability issues with the image structure. This leads to large allocations. This can be avoided by accomodating support that will allow for both a byte-slice or an `io.ReadSeeker`. Since the EXIF structure is typically not read- intensive (a couple of kilobytes if no thumbnail is present), this should have a minimal performance impact. Closes #42 --- v3/common/value_context.go | 36 +- v3/common/value_context_test.go | 481 ++++++++++++++++-- v3/data_layer.go | 50 ++ v3/exif.go | 6 +- v3/exif_test.go | 3 + v3/go.mod | 2 + v3/go.sum | 4 + v3/ifd_enumerate.go | 99 ++-- v3/ifd_tag_entry.go | 27 +- v3/ifd_tag_entry_test.go | 4 +- v3/undefined/exif_8828_oecf_test.go | 4 +- v3/undefined/exif_927C_maker_note_test.go | 5 +- v3/undefined/exif_9286_user_comment_test.go | 4 +- .../exif_A000_flashpix_version_test.go | 4 +- ...if_A20C_spatial_frequency_response_test.go | 4 +- v3/undefined/exif_A302_cfa_pattern_test.go | 9 +- v3/utility.go | 12 +- 17 files changed, 633 insertions(+), 121 deletions(-) create mode 100644 v3/data_layer.go diff --git a/v3/common/value_context.go b/v3/common/value_context.go index feb078c..712438a 100644 --- a/v3/common/value_context.go +++ b/v3/common/value_context.go @@ -2,6 +2,7 @@ package exifcommon import ( "errors" + "io" "encoding/binary" @@ -21,10 +22,10 @@ var ( // ValueContext embeds all of the parameters required to find and extract the // actual tag value. type ValueContext struct { - unitCount uint32 - valueOffset uint32 - rawValueOffset []byte - addressableData []byte + unitCount uint32 + valueOffset uint32 + rawValueOffset []byte + rs io.ReadSeeker tagType TagTypePrimitive byteOrder binary.ByteOrder @@ -40,12 +41,12 @@ type ValueContext struct { // TODO(dustin): We can update newValueContext() to derive `valueOffset` itself (from `rawValueOffset`). // NewValueContext returns a new ValueContext struct. -func NewValueContext(ifdPath string, tagId uint16, unitCount, valueOffset uint32, rawValueOffset, addressableData []byte, tagType TagTypePrimitive, byteOrder binary.ByteOrder) *ValueContext { +func NewValueContext(ifdPath string, tagId uint16, unitCount, valueOffset uint32, rawValueOffset []byte, rs io.ReadSeeker, tagType TagTypePrimitive, byteOrder binary.ByteOrder) *ValueContext { return &ValueContext{ - unitCount: unitCount, - valueOffset: valueOffset, - rawValueOffset: rawValueOffset, - addressableData: addressableData, + unitCount: unitCount, + valueOffset: valueOffset, + rawValueOffset: rawValueOffset, + rs: rs, tagType: tagType, byteOrder: byteOrder, @@ -82,8 +83,11 @@ func (vc *ValueContext) RawValueOffset() []byte { } // AddressableData returns the block of data that we can dereference into. -func (vc *ValueContext) AddressableData() []byte { - return vc.addressableData +func (vc *ValueContext) AddressableData() io.ReadSeeker { + + // RELEASE)dustin): Rename from AddressableData() to ReadSeeker() + + return vc.rs } // ByteOrder returns the byte-order of numbers. @@ -152,7 +156,15 @@ func (vc *ValueContext) readRawEncoded() (rawBytes []byte, err error) { return vc.rawValueOffset[:byteLength], nil } - return vc.addressableData[vc.valueOffset : vc.valueOffset+vc.unitCount*unitSizeRaw], nil + _, err = vc.rs.Seek(int64(vc.valueOffset), io.SeekStart) + log.PanicIf(err) + + rawBytes = make([]byte, vc.unitCount*unitSizeRaw) + + _, err = io.ReadFull(vc.rs, rawBytes) + log.PanicIf(err) + + return rawBytes, nil } // GetFarOffset returns the offset if the value is not embedded [within the diff --git a/v3/common/value_context_test.go b/v3/common/value_context_test.go index d98d46e..889a255 100644 --- a/v3/common/value_context_test.go +++ b/v3/common/value_context_test.go @@ -5,13 +5,26 @@ import ( "reflect" "testing" + "io/ioutil" + "github.com/dsoprea/go-logging" + "github.com/dsoprea/go-utility/filesystem" ) func TestNewValueContext(t *testing.T) { rawValueOffset := []byte{0, 0, 0, 22} addressableData := []byte{1, 2, 3, 4} - vc := NewValueContext("aa/bb", 0x1234, 11, 22, rawValueOffset, addressableData, TypeLong, TestDefaultByteOrder) + sb := rifs.NewSeekableBufferWithBytes(addressableData) + + vc := NewValueContext( + "aa/bb", + 0x1234, + 11, + 22, + rawValueOffset, + sb, + TypeLong, + TestDefaultByteOrder) if vc.ifdPath != "aa/bb" { t.Fatalf("ifdPath not correct: [%s]", vc.ifdPath) @@ -23,13 +36,18 @@ func TestNewValueContext(t *testing.T) { t.Fatalf("valueOffset not correct: (%d)", vc.valueOffset) } else if bytes.Equal(vc.rawValueOffset, rawValueOffset) != true { t.Fatalf("rawValueOffset not correct: %v", vc.rawValueOffset) - } else if bytes.Equal(vc.addressableData, addressableData) != true { - t.Fatalf("addressableData not correct: %v", vc.addressableData) } else if vc.tagType != TypeLong { t.Fatalf("tagType not correct: (%d)", vc.tagType) } else if vc.byteOrder != TestDefaultByteOrder { t.Fatalf("byteOrder not correct: %v", vc.byteOrder) } + + recoveredBytes, err := ioutil.ReadAll(vc.AddressableData()) + log.PanicIf(err) + + if bytes.Equal(recoveredBytes, addressableData) != true { + t.Fatalf("AddressableData() not correct: %v", recoveredBytes) + } } func TestValueContext_SetUndefinedValueType__ErrorWhenNotUndefined(t *testing.T) { @@ -47,16 +65,38 @@ func TestValueContext_SetUndefinedValueType__ErrorWhenNotUndefined(t *testing.T) }() rawValueOffset := []byte{0, 0, 0, 22} + addressableData := []byte{1, 2, 3, 4} - vc := NewValueContext("aa/bb", 0x1234, 11, 22, rawValueOffset, addressableData, TypeLong, TestDefaultByteOrder) + sb := rifs.NewSeekableBufferWithBytes(addressableData) + + vc := NewValueContext( + "aa/bb", + 0x1234, + 11, + 22, + rawValueOffset, + sb, + TypeLong, + TestDefaultByteOrder) vc.SetUndefinedValueType(TypeLong) } func TestValueContext_SetUndefinedValueType__Ok(t *testing.T) { rawValueOffset := []byte{0, 0, 0, 22} + addressableData := []byte{1, 2, 3, 4} - vc := NewValueContext("aa/bb", 0x1234, 11, 22, rawValueOffset, addressableData, TypeUndefined, TestDefaultByteOrder) + sb := rifs.NewSeekableBufferWithBytes(addressableData) + + vc := NewValueContext( + "aa/bb", + 0x1234, + 11, + 22, + rawValueOffset, + sb, + TypeUndefined, + TestDefaultByteOrder) vc.SetUndefinedValueType(TypeLong) @@ -71,8 +111,19 @@ func TestValueContext_SetUndefinedValueType__Ok(t *testing.T) { func TestValueContext_effectiveValueType(t *testing.T) { rawValueOffset := []byte{0, 0, 0, 22} + addressableData := []byte{1, 2, 3, 4} - vc := NewValueContext("aa/bb", 0x1234, 11, 22, rawValueOffset, addressableData, TypeUndefined, TestDefaultByteOrder) + sb := rifs.NewSeekableBufferWithBytes(addressableData) + + vc := NewValueContext( + "aa/bb", + 0x1234, + 11, + 22, + rawValueOffset, + sb, + TypeUndefined, + TestDefaultByteOrder) vc.SetUndefinedValueType(TypeLong) @@ -87,8 +138,19 @@ func TestValueContext_effectiveValueType(t *testing.T) { func TestValueContext_UnitCount(t *testing.T) { rawValueOffset := []byte{0, 0, 0, 22} + addressableData := []byte{1, 2, 3, 4} - vc := NewValueContext("aa/bb", 0x1234, 11, 22, rawValueOffset, addressableData, TypeUndefined, TestDefaultByteOrder) + sb := rifs.NewSeekableBufferWithBytes(addressableData) + + vc := NewValueContext( + "aa/bb", + 0x1234, + 11, + 22, + rawValueOffset, + sb, + TypeUndefined, + TestDefaultByteOrder) if vc.UnitCount() != 11 { t.Fatalf("UnitCount() not correct: (%d)", vc.UnitCount()) @@ -97,8 +159,19 @@ func TestValueContext_UnitCount(t *testing.T) { func TestValueContext_ValueOffset(t *testing.T) { rawValueOffset := []byte{0, 0, 0, 22} + addressableData := []byte{1, 2, 3, 4} - vc := NewValueContext("aa/bb", 0x1234, 11, 22, rawValueOffset, addressableData, TypeUndefined, TestDefaultByteOrder) + sb := rifs.NewSeekableBufferWithBytes(addressableData) + + vc := NewValueContext( + "aa/bb", + 0x1234, + 11, + 22, + rawValueOffset, + sb, + TypeUndefined, + TestDefaultByteOrder) if vc.ValueOffset() != 22 { t.Fatalf("ValueOffset() not correct: (%d)", vc.ValueOffset()) @@ -107,8 +180,19 @@ func TestValueContext_ValueOffset(t *testing.T) { func TestValueContext_RawValueOffset(t *testing.T) { rawValueOffset := []byte{0, 0, 0, 22} + addressableData := []byte{1, 2, 3, 4} - vc := NewValueContext("aa/bb", 0x1234, 11, 22, rawValueOffset, addressableData, TypeUndefined, TestDefaultByteOrder) + sb := rifs.NewSeekableBufferWithBytes(addressableData) + + vc := NewValueContext( + "aa/bb", + 0x1234, + 11, + 22, + rawValueOffset, + sb, + TypeUndefined, + TestDefaultByteOrder) if bytes.Equal(vc.RawValueOffset(), rawValueOffset) != true { t.Fatalf("RawValueOffset() not correct: %v", vc.RawValueOffset()) @@ -117,18 +201,43 @@ func TestValueContext_RawValueOffset(t *testing.T) { func TestValueContext_AddressableData(t *testing.T) { rawValueOffset := []byte{0, 0, 0, 22} - addressableData := []byte{1, 2, 3, 4} - vc := NewValueContext("aa/bb", 0x1234, 11, 22, rawValueOffset, addressableData, TypeUndefined, TestDefaultByteOrder) - if bytes.Equal(vc.AddressableData(), addressableData) != true { - t.Fatalf("AddressableData() not correct: %v", vc.AddressableData()) + addressableData := []byte{1, 2, 3, 4} + sb := rifs.NewSeekableBufferWithBytes(addressableData) + + vc := NewValueContext( + "aa/bb", + 0x1234, + 11, + 22, + rawValueOffset, + sb, + TypeUndefined, + TestDefaultByteOrder) + + recoveredBytes, err := ioutil.ReadAll(vc.AddressableData()) + log.PanicIf(err) + + if bytes.Equal(recoveredBytes, addressableData) != true { + t.Fatalf("AddressableData() not correct: %v", recoveredBytes) } } func TestValueContext_ByteOrder(t *testing.T) { rawValueOffset := []byte{0, 0, 0, 22} + addressableData := []byte{1, 2, 3, 4} - vc := NewValueContext("aa/bb", 0x1234, 11, 22, rawValueOffset, addressableData, TypeUndefined, TestDefaultByteOrder) + sb := rifs.NewSeekableBufferWithBytes(addressableData) + + vc := NewValueContext( + "aa/bb", + 0x1234, + 11, + 22, + rawValueOffset, + sb, + TypeUndefined, + TestDefaultByteOrder) if vc.ByteOrder() != TestDefaultByteOrder { t.Fatalf("ByteOrder() not correct: %v", vc.ByteOrder()) @@ -137,8 +246,19 @@ func TestValueContext_ByteOrder(t *testing.T) { func TestValueContext_IfdPath(t *testing.T) { rawValueOffset := []byte{0, 0, 0, 22} + addressableData := []byte{1, 2, 3, 4} - vc := NewValueContext("aa/bb", 0x1234, 11, 22, rawValueOffset, addressableData, TypeUndefined, TestDefaultByteOrder) + sb := rifs.NewSeekableBufferWithBytes(addressableData) + + vc := NewValueContext( + "aa/bb", + 0x1234, + 11, + 22, + rawValueOffset, + sb, + TypeUndefined, + TestDefaultByteOrder) if vc.IfdPath() != "aa/bb" { t.Fatalf("IfdPath() not correct: [%s]", vc.IfdPath()) @@ -147,8 +267,19 @@ func TestValueContext_IfdPath(t *testing.T) { func TestValueContext_TagId(t *testing.T) { rawValueOffset := []byte{0, 0, 0, 22} + addressableData := []byte{1, 2, 3, 4} - vc := NewValueContext("aa/bb", 0x1234, 11, 22, rawValueOffset, addressableData, TypeUndefined, TestDefaultByteOrder) + sb := rifs.NewSeekableBufferWithBytes(addressableData) + + vc := NewValueContext( + "aa/bb", + 0x1234, + 11, + 22, + rawValueOffset, + sb, + TypeUndefined, + TestDefaultByteOrder) if vc.TagId() != 0x1234 { t.Fatalf("TagId() not correct: (%d)", vc.TagId()) @@ -158,8 +289,19 @@ func TestValueContext_TagId(t *testing.T) { func TestValueContext_isEmbedded__True(t *testing.T) { unitCount := uint32(4) rawValueOffset := []byte{0, 0, 0, 22} + addressableData := []byte{1, 2, 3, 4} - vc := NewValueContext("aa/bb", 0x1234, unitCount, 22, rawValueOffset, addressableData, TypeByte, TestDefaultByteOrder) + sb := rifs.NewSeekableBufferWithBytes(addressableData) + + vc := NewValueContext( + "aa/bb", + 0x1234, + unitCount, + 22, + rawValueOffset, + sb, + TypeByte, + TestDefaultByteOrder) if vc.isEmbedded() != true { t.Fatalf("isEmbedded() not correct: %v", vc.isEmbedded()) @@ -169,8 +311,19 @@ func TestValueContext_isEmbedded__True(t *testing.T) { func TestValueContext_isEmbedded__False(t *testing.T) { unitCount := uint32(5) rawValueOffset := []byte{0, 0, 0, 22} + addressableData := []byte{1, 2, 3, 4} - vc := NewValueContext("aa/bb", 0x1234, unitCount, 22, rawValueOffset, addressableData, TypeByte, TestDefaultByteOrder) + sb := rifs.NewSeekableBufferWithBytes(addressableData) + + vc := NewValueContext( + "aa/bb", + 0x1234, + unitCount, + 22, + rawValueOffset, + sb, + TypeByte, + TestDefaultByteOrder) if vc.isEmbedded() != false { t.Fatalf("isEmbedded() not correct: %v", vc.isEmbedded()) @@ -186,7 +339,17 @@ func TestValueContext_readRawEncoded__IsEmbedded(t *testing.T) { valueOffset := uint32(0) addressableData := []byte{} - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeByte, TestDefaultByteOrder) + sb := rifs.NewSeekableBufferWithBytes(addressableData) + + vc := NewValueContext( + "aa/bb", + 0x1234, + unitCount, + valueOffset, + rawValueOffset, + sb, + TypeByte, + TestDefaultByteOrder) recovered, err := vc.readRawEncoded() log.PanicIf(err) @@ -205,10 +368,20 @@ func TestValueContext_readRawEncoded__IsRelative(t *testing.T) { valueOffset := uint32(4) data := []byte{5, 6, 7, 8, 9} + addressableData := []byte{1, 2, 3, 4} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeByte, TestDefaultByteOrder) + vc := NewValueContext( + "aa/bb", + 0x1234, + unitCount, + valueOffset, + rawValueOffset, + sb, + TypeByte, + TestDefaultByteOrder) recovered, err := vc.readRawEncoded() log.PanicIf(err) @@ -225,10 +398,20 @@ func TestValueContext_Format__Byte(t *testing.T) { valueOffset := uint32(4) data := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'} + addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeByte, TestDefaultByteOrder) + vc := NewValueContext( + "aa/bb", + 0x1234, + unitCount, + valueOffset, + rawValueOffset, + sb, + TypeByte, + TestDefaultByteOrder) value, err := vc.Format() log.PanicIf(err) @@ -245,10 +428,20 @@ func TestValueContext_Format__Ascii(t *testing.T) { valueOffset := uint32(4) data := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 0} + addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeAscii, TestDefaultByteOrder) + vc := NewValueContext( + "aa/bb", + 0x1234, + unitCount, + valueOffset, + rawValueOffset, + sb, + TypeAscii, + TestDefaultByteOrder) value, err := vc.Format() log.PanicIf(err) @@ -265,10 +458,20 @@ func TestValueContext_Format__AsciiNoNul(t *testing.T) { valueOffset := uint32(4) data := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'} + addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeAsciiNoNul, TestDefaultByteOrder) + vc := NewValueContext( + "aa/bb", + 0x1234, + unitCount, + valueOffset, + rawValueOffset, + sb, + TypeAsciiNoNul, + TestDefaultByteOrder) value, err := vc.Format() log.PanicIf(err) @@ -285,10 +488,20 @@ func TestValueContext_Format__Short(t *testing.T) { valueOffset := uint32(4) data := []byte{0, 1, 0, 2, 0, 3, 0, 4} + addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeShort, TestDefaultByteOrder) + vc := NewValueContext( + "aa/bb", + 0x1234, + unitCount, + valueOffset, + rawValueOffset, + sb, + TypeShort, + TestDefaultByteOrder) value, err := vc.Format() log.PanicIf(err) @@ -305,10 +518,20 @@ func TestValueContext_Format__Long(t *testing.T) { valueOffset := uint32(4) data := []byte{0, 0, 0, 1, 0, 0, 0, 2} + addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeLong, TestDefaultByteOrder) + vc := NewValueContext( + "aa/bb", + 0x1234, + unitCount, + valueOffset, + rawValueOffset, + sb, + TypeLong, + TestDefaultByteOrder) value, err := vc.Format() log.PanicIf(err) @@ -331,8 +554,17 @@ func TestValueContext_Format__Rational(t *testing.T) { addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeRational, TestDefaultByteOrder) + vc := NewValueContext( + "aa/bb", + 0x1234, + unitCount, + valueOffset, + rawValueOffset, + sb, + TypeRational, + TestDefaultByteOrder) value, err := vc.Format() log.PanicIf(err) @@ -349,10 +581,20 @@ func TestValueContext_Format__SignedLong(t *testing.T) { valueOffset := uint32(4) data := []byte{0, 0, 0, 1, 0, 0, 0, 2} + addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeSignedLong, TestDefaultByteOrder) + vc := NewValueContext( + "aa/bb", + 0x1234, + unitCount, + valueOffset, + rawValueOffset, + sb, + TypeSignedLong, + TestDefaultByteOrder) value, err := vc.Format() log.PanicIf(err) @@ -375,8 +617,17 @@ func TestValueContext_Format__SignedRational(t *testing.T) { addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeSignedRational, TestDefaultByteOrder) + vc := NewValueContext( + "aa/bb", + 0x1234, + unitCount, + valueOffset, + rawValueOffset, + sb, + TypeSignedRational, + TestDefaultByteOrder) value, err := vc.Format() log.PanicIf(err) @@ -406,10 +657,20 @@ func TestValueContext_Format__Undefined__NoEffectiveType(t *testing.T) { valueOffset := uint32(4) data := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'} + addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeUndefined, TestDefaultByteOrder) + vc := NewValueContext( + "aa/bb", + 0x1234, + unitCount, + valueOffset, + rawValueOffset, + sb, + TypeUndefined, + TestDefaultByteOrder) value, err := vc.Format() log.PanicIf(err) @@ -426,10 +687,20 @@ func TestValueContext_Format__Undefined__HasEffectiveType(t *testing.T) { valueOffset := uint32(4) data := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 0} + addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeUndefined, TestDefaultByteOrder) + vc := NewValueContext( + "aa/bb", + 0x1234, + unitCount, + valueOffset, + rawValueOffset, + sb, + TypeUndefined, + TestDefaultByteOrder) vc.SetUndefinedValueType(TypeAscii) @@ -448,10 +719,20 @@ func TestValueContext_FormatFirst__Bytes(t *testing.T) { valueOffset := uint32(4) data := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'} + addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeByte, TestDefaultByteOrder) + vc := NewValueContext( + "aa/bb", + 0x1234, + unitCount, + valueOffset, + rawValueOffset, + sb, + TypeByte, + TestDefaultByteOrder) value, err := vc.FormatFirst() log.PanicIf(err) @@ -468,10 +749,20 @@ func TestValueContext_FormatFirst__String(t *testing.T) { valueOffset := uint32(4) data := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 0} + addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeAscii, TestDefaultByteOrder) + vc := NewValueContext( + "aa/bb", + 0x1234, + unitCount, + valueOffset, + rawValueOffset, + sb, + TypeAscii, + TestDefaultByteOrder) value, err := vc.FormatFirst() log.PanicIf(err) @@ -488,10 +779,20 @@ func TestValueContext_FormatFirst__List(t *testing.T) { valueOffset := uint32(4) data := []byte{0, 1, 0, 2, 0, 3, 0, 4} + addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeShort, TestDefaultByteOrder) + vc := NewValueContext( + "aa/bb", + 0x1234, + unitCount, + valueOffset, + rawValueOffset, + sb, + TypeShort, + TestDefaultByteOrder) value, err := vc.FormatFirst() log.PanicIf(err) @@ -510,8 +811,17 @@ func TestValueContext_ReadBytes(t *testing.T) { data := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'} addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeByte, TestDefaultByteOrder) + vc := NewValueContext( + "aa/bb", + 0x1234, + unitCount, + valueOffset, + rawValueOffset, + sb, + TypeByte, + TestDefaultByteOrder) value, err := vc.ReadBytes() log.PanicIf(err) @@ -528,10 +838,20 @@ func TestValueContext_ReadAscii(t *testing.T) { valueOffset := uint32(4) data := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 0} + addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeAscii, TestDefaultByteOrder) + vc := NewValueContext( + "aa/bb", + 0x1234, + unitCount, + valueOffset, + rawValueOffset, + sb, + TypeAscii, + TestDefaultByteOrder) value, err := vc.ReadAscii() log.PanicIf(err) @@ -550,8 +870,17 @@ func TestValueContext_ReadAsciiNoNul(t *testing.T) { data := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'} addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeAsciiNoNul, TestDefaultByteOrder) + vc := NewValueContext( + "aa/bb", + 0x1234, + unitCount, + valueOffset, + rawValueOffset, + sb, + TypeAsciiNoNul, + TestDefaultByteOrder) value, err := vc.ReadAsciiNoNul() log.PanicIf(err) @@ -568,10 +897,20 @@ func TestValueContext_ReadShorts(t *testing.T) { valueOffset := uint32(4) data := []byte{0, 1, 0, 2, 0, 3, 0, 4} + addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeShort, TestDefaultByteOrder) + vc := NewValueContext( + "aa/bb", + 0x1234, + unitCount, + valueOffset, + rawValueOffset, + sb, + TypeShort, + TestDefaultByteOrder) value, err := vc.ReadShorts() log.PanicIf(err) @@ -588,10 +927,20 @@ func TestValueContext_ReadLongs(t *testing.T) { valueOffset := uint32(4) data := []byte{0, 0, 0, 1, 0, 0, 0, 2} + addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeLong, TestDefaultByteOrder) + vc := NewValueContext( + "aa/bb", + 0x1234, + unitCount, + valueOffset, + rawValueOffset, + sb, + TypeLong, + TestDefaultByteOrder) value, err := vc.ReadLongs() log.PanicIf(err) @@ -614,8 +963,17 @@ func TestValueContext_ReadRationals(t *testing.T) { addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeRational, TestDefaultByteOrder) + vc := NewValueContext( + "aa/bb", + 0x1234, + unitCount, + valueOffset, + rawValueOffset, + sb, + TypeRational, + TestDefaultByteOrder) value, err := vc.ReadRationals() log.PanicIf(err) @@ -637,10 +995,20 @@ func TestValueContext_ReadSignedLongs(t *testing.T) { valueOffset := uint32(4) data := []byte{0, 0, 0, 1, 0, 0, 0, 2} + addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeSignedLong, TestDefaultByteOrder) + vc := NewValueContext( + "aa/bb", + 0x1234, + unitCount, + valueOffset, + rawValueOffset, + sb, + TypeSignedLong, + TestDefaultByteOrder) value, err := vc.ReadSignedLongs() log.PanicIf(err) @@ -663,8 +1031,9 @@ func TestValueContext_ReadSignedRationals(t *testing.T) { addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeSignedRational, TestDefaultByteOrder) + vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, sb, TypeSignedRational, TestDefaultByteOrder) value, err := vc.ReadSignedRationals() log.PanicIf(err) @@ -686,10 +1055,12 @@ func TestValueContext_Values__Byte(t *testing.T) { valueOffset := uint32(4) data := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'} + addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeByte, TestDefaultByteOrder) + vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, sb, TypeByte, TestDefaultByteOrder) value, err := vc.Values() log.PanicIf(err) @@ -706,10 +1077,12 @@ func TestValueContext_Values__Ascii(t *testing.T) { valueOffset := uint32(4) data := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 0} + addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeAscii, TestDefaultByteOrder) + vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, sb, TypeAscii, TestDefaultByteOrder) value, err := vc.Values() log.PanicIf(err) @@ -726,10 +1099,12 @@ func TestValueContext_Values__AsciiNoNul(t *testing.T) { valueOffset := uint32(4) data := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'} + addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeAsciiNoNul, TestDefaultByteOrder) + vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, sb, TypeAsciiNoNul, TestDefaultByteOrder) value, err := vc.Values() log.PanicIf(err) @@ -746,10 +1121,12 @@ func TestValueContext_Values__Short(t *testing.T) { valueOffset := uint32(4) data := []byte{0, 1, 0, 2, 0, 3, 0, 4} + addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeShort, TestDefaultByteOrder) + vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, sb, TypeShort, TestDefaultByteOrder) value, err := vc.Values() log.PanicIf(err) @@ -766,10 +1143,12 @@ func TestValueContext_Values__Long(t *testing.T) { valueOffset := uint32(4) data := []byte{0, 0, 0, 1, 0, 0, 0, 2} + addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeLong, TestDefaultByteOrder) + vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, sb, TypeLong, TestDefaultByteOrder) value, err := vc.Values() log.PanicIf(err) @@ -792,8 +1171,9 @@ func TestValueContext_Values__Rational(t *testing.T) { addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeRational, TestDefaultByteOrder) + vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, sb, TypeRational, TestDefaultByteOrder) value, err := vc.Values() log.PanicIf(err) @@ -815,10 +1195,12 @@ func TestValueContext_Values__SignedLong(t *testing.T) { valueOffset := uint32(4) data := []byte{0, 0, 0, 1, 0, 0, 0, 2} + addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeSignedLong, TestDefaultByteOrder) + vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, sb, TypeSignedLong, TestDefaultByteOrder) value, err := vc.Values() log.PanicIf(err) @@ -841,8 +1223,9 @@ func TestValueContext_Values__SignedRational(t *testing.T) { addressableData := []byte{0, 0, 0, 0} addressableData = append(addressableData, data...) + sb := rifs.NewSeekableBufferWithBytes(addressableData) - vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, addressableData, TypeSignedRational, TestDefaultByteOrder) + vc := NewValueContext("aa/bb", 0x1234, unitCount, valueOffset, rawValueOffset, sb, TypeSignedRational, TestDefaultByteOrder) value, err := vc.Values() log.PanicIf(err) diff --git a/v3/data_layer.go b/v3/data_layer.go new file mode 100644 index 0000000..68a5c89 --- /dev/null +++ b/v3/data_layer.go @@ -0,0 +1,50 @@ +package exif + +import ( + "io" + + "github.com/dsoprea/go-logging" + "github.com/dsoprea/go-utility/filesystem" +) + +type ExifBlobSeeker interface { + GetReadSeeker(initialOffset int64) (rs io.ReadSeeker, err error) +} + +// ExifReadSeeker knows how to retrieve data from the EXIF blob relative to the +// beginning of the blob (so, absolute position (0) is the first byte of the +// EXIF data). +type ExifReadSeeker struct { + rs io.ReadSeeker +} + +func NewExifReadSeeker(rs io.ReadSeeker) *ExifReadSeeker { + return &ExifReadSeeker{ + rs: rs, + } +} + +func NewExifReadSeekerWithBytes(exifData []byte) *ExifReadSeeker { + sb := rifs.NewSeekableBufferWithBytes(exifData) + edbs := NewExifReadSeeker(sb) + + return edbs +} + +// Fork creates a new ReadSeeker instead that wraps a BouncebackReader to +// maintain its own position in the stream. +func (edbs *ExifReadSeeker) GetReadSeeker(initialOffset int64) (rs io.ReadSeeker, err error) { + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() + + br, err := rifs.NewBouncebackReader(edbs.rs) + log.PanicIf(err) + + _, err = br.Seek(initialOffset, io.SeekStart) + log.PanicIf(err) + + return br, nil +} diff --git a/v3/exif.go b/v3/exif.go index 20b7237..607b88b 100644 --- a/v3/exif.go +++ b/v3/exif.go @@ -202,7 +202,8 @@ func Visit(rootIfdIdentity *exifcommon.IfdIdentity, ifdMapping *exifcommon.IfdMa eh, err = ParseExifHeader(exifData) log.PanicIf(err) - ie := NewIfdEnumerate(ifdMapping, tagIndex, exifData, eh.ByteOrder) + ebs := NewExifReadSeekerWithBytes(exifData) + ie := NewIfdEnumerate(ifdMapping, tagIndex, ebs, eh.ByteOrder) _, err = ie.Scan(rootIfdIdentity, eh.FirstIfdOffset, visitor) log.PanicIf(err) @@ -223,7 +224,8 @@ func Collect(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, exifData []b eh, err = ParseExifHeader(exifData) log.PanicIf(err) - ie := NewIfdEnumerate(ifdMapping, tagIndex, exifData, eh.ByteOrder) + ebs := NewExifReadSeekerWithBytes(exifData) + ie := NewIfdEnumerate(ifdMapping, tagIndex, ebs, eh.ByteOrder) index, err = ie.Collect(eh.FirstIfdOffset) log.PanicIf(err) diff --git a/v3/exif_test.go b/v3/exif_test.go index 59f5e16..c0526b5 100644 --- a/v3/exif_test.go +++ b/v3/exif_test.go @@ -43,6 +43,8 @@ func TestVisit(t *testing.T) { // Search for the beginning of the EXIF information. The EXIF is near the // very beginning of our/most JPEGs, so this has a very low cost. + // TODO(dustin): Just use SearchAndExtractExifWithReader() here. + foundAt := -1 for i := 0; i < len(data); i++ { if _, err := ParseExifHeader(data[i:]); err == nil { @@ -416,5 +418,6 @@ func ExampleBuildExifHeader() { log.PanicIf(err) fmt.Printf("%v\n", eh) + // Output: ExifHeader } diff --git a/v3/go.mod b/v3/go.mod index 733ef54..20c47dd 100644 --- a/v3/go.mod +++ b/v3/go.mod @@ -4,9 +4,11 @@ go 1.13 // Development only // replace github.com/dsoprea/go-logging => ../../go-logging +replace github.com/dsoprea/go-utility => ../../go-utility require ( github.com/dsoprea/go-logging v0.0.0-20200517223158-a10564966e9d + github.com/dsoprea/go-utility v0.0.0-20200512094054-1abbbc781176 github.com/golang/geo v0.0.0-20200319012246-673a6f80352d github.com/jessevdk/go-flags v1.4.0 golang.org/x/net v0.0.0-20200513185701-a91f0712d120 // indirect diff --git a/v3/go.sum b/v3/go.sum index 7976385..44dd842 100644 --- a/v3/go.sum +++ b/v3/go.sum @@ -1,3 +1,4 @@ +github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E= github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696 h1:VGFnZAcLwPpt1sHlAxml+pGLZz9A2s+K/s1YNhPC91Y= github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696/go.mod h1:Nm/x2ZUNRW6Fe5C3LxdY1PyZY5wmDv/s5dkPJ/VB3iA= github.com/dsoprea/go-logging v0.0.0-20200502191043-ec333ec7635f h1:XM9MVftaUNA4CcjV97+4bSy7u9Ns04DEYbZkswUrRtc= @@ -6,6 +7,8 @@ github.com/dsoprea/go-logging v0.0.0-20200502201358-170ff607885f h1:FonKAuW3PmNt github.com/dsoprea/go-logging v0.0.0-20200502201358-170ff607885f/go.mod h1:7I+3Pe2o/YSU88W0hWlm9S22W7XI1JFNJ86U0zPKMf8= github.com/dsoprea/go-logging v0.0.0-20200517223158-a10564966e9d h1:F/7L5wr/fP/SKeO5HuMlNEX9Ipyx2MbH2rV9G4zJRpk= github.com/dsoprea/go-logging v0.0.0-20200517223158-a10564966e9d/go.mod h1:7I+3Pe2o/YSU88W0hWlm9S22W7XI1JFNJ86U0zPKMf8= +github.com/dsoprea/go-utility v0.0.0-20200512094054-1abbbc781176 h1:CfXezFYb2STGOd1+n1HshvE191zVx+QX3A1nML5xxME= +github.com/dsoprea/go-utility v0.0.0-20200512094054-1abbbc781176/go.mod h1:95+K3z2L0mqsVYd6yveIv1lmtT3tcQQ3dVakPySffW8= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.0.2 h1:xMxH9j2fNg/L4hLn/4y3M0IUsn0M6Wbu/Uh9QlOfBh4= @@ -19,6 +22,7 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5 h1:WQ8q63x+f/zpC8Ac1s9wLElVoHhm32p6tudrU72n1QA= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120 h1:EZ3cVSzKOlJxAd8e8YAJ7no8nNypTxexh/YE/xW3ZEY= diff --git a/v3/ifd_enumerate.go b/v3/ifd_enumerate.go index 1359333..4d07b8a 100644 --- a/v3/ifd_enumerate.go +++ b/v3/ifd_enumerate.go @@ -4,6 +4,7 @@ import ( "bytes" "errors" "fmt" + "io" "strconv" "strings" "time" @@ -74,23 +75,23 @@ var ( // statically-sized records. So, the tags (though notnecessarily their values) // are fairly simple to enumerate. type byteParser struct { - byteOrder binary.ByteOrder - addressableData []byte - ifdOffset uint32 - currentOffset uint32 + byteOrder binary.ByteOrder + rs io.ReadSeeker + ifdOffset uint32 + currentOffset uint32 } -func newByteParser(addressableData []byte, byteOrder binary.ByteOrder, ifdOffset uint32) (bp *byteParser, err error) { - if ifdOffset >= uint32(len(addressableData)) { - return nil, ErrOffsetInvalid - } - +// newByteParser returns a new byteParser struct. +// +// initialOffset is for arithmetic-based tracking of where we should be at in +// the stream. +func newByteParser(rs io.ReadSeeker, byteOrder binary.ByteOrder, initialOffset uint32) (bp *byteParser, err error) { // TODO(dustin): Add test bp = &byteParser{ - addressableData: addressableData, - byteOrder: byteOrder, - currentOffset: ifdOffset, + rs: rs, + byteOrder: byteOrder, + currentOffset: initialOffset, } return bp, nil @@ -110,11 +111,11 @@ func (bp *byteParser) getUint16() (value uint16, raw []byte, err error) { needBytes := 2 - if bp.currentOffset+needBytes >= len(bp.addressableData) { - return 0, nil, io.EOF - } + raw = make([]byte, needBytes) + + _, err = io.ReadFull(bp.rs, raw) + log.PanicIf(err) - raw = addressableData[bp.currentOffset : bp.currentOffset+needBytes] value = bp.byteOrder.Uint16(raw) bp.currentOffset += uint32(needBytes) @@ -136,12 +137,12 @@ func (bp *byteParser) getUint32() (value uint32, raw []byte, err error) { needBytes := 4 - if bp.currentOffset+needBytes >= len(bp.addressableData) { - return 0, nil, io.EOF - } + raw = make([]byte, needBytes) - raw = addressableData[bp.currentOffset : bp.currentOffset+needBytes] - value = bp.byteOrder.Uint16(raw) + _, err = io.ReadFull(bp.rs, raw) + log.PanicIf(err) + + value = bp.byteOrder.Uint32(raw) bp.currentOffset += uint32(needBytes) @@ -157,7 +158,7 @@ func (bp *byteParser) CurrentOffset() uint32 { // IfdEnumerate is the main enumeration type. It knows how to parse the IFD // containers in the EXIF blob. type IfdEnumerate struct { - exifData []byte + ebs ExifBlobSeeker byteOrder binary.ByteOrder tagIndex *TagIndex ifdMapping *exifcommon.IfdMapping @@ -165,9 +166,9 @@ type IfdEnumerate struct { } // NewIfdEnumerate returns a new instance of IfdEnumerate. -func NewIfdEnumerate(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, exifData []byte, byteOrder binary.ByteOrder) *IfdEnumerate { +func NewIfdEnumerate(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, ebs ExifBlobSeeker, byteOrder binary.ByteOrder) *IfdEnumerate { return &IfdEnumerate{ - exifData: exifData, + ebs: ebs, byteOrder: byteOrder, ifdMapping: ifdMapping, tagIndex: tagIndex, @@ -181,11 +182,16 @@ func (ie *IfdEnumerate) getByteParser(ifdOffset uint32) (bp *byteParser, err err } }() + initialOffset := ExifAddressableAreaStart + ifdOffset + + rs, err := ie.ebs.GetReadSeeker(int64(initialOffset)) + log.PanicIf(err) + bp, err = newByteParser( - ie.exifData[ExifAddressableAreaStart:], + rs, ie.byteOrder, - ifdOffset) + initialOffset) if err != nil { if err == ErrOffsetInvalid { @@ -228,6 +234,9 @@ func (ie *IfdEnumerate) parseTag(ii *exifcommon.IfdIdentity, tagPosition int, bp log.Panic(ErrTagTypeNotValid) } + rs, err := ie.ebs.GetReadSeeker(0) + log.PanicIf(err) + ite = newIfdTagEntry( ii, tagId, @@ -236,7 +245,7 @@ func (ie *IfdEnumerate) parseTag(ii *exifcommon.IfdIdentity, tagPosition int, bp unitCount, valueOffset, rawValueOffset, - ie.exifData[ExifAddressableAreaStart:], + rs, ie.byteOrder) ifdPath := ii.UnindexedString() @@ -266,8 +275,8 @@ func (ie *IfdEnumerate) parseTag(ii *exifcommon.IfdIdentity, tagPosition int, bp // TagVisitorFn is called for each tag when enumerating through the EXIF. type TagVisitorFn func(fqIfdPath string, ifdIndex int, ite *IfdTagEntry) (err error) -// postparseTag do some tag-level processing here following the parse of each. -func (ie *IfdEnumerate) postparseTag(ite *IfdTagEntry, med *MiscellaneousExifData) (err error) { +// tagPostParse do some tag-level processing here following the parse of each. +func (ie *IfdEnumerate) tagPostParse(ite *IfdTagEntry, med *MiscellaneousExifData) (err error) { defer func() { if state := recover(); state != nil { err = log.Wrap(state.(error)) @@ -401,7 +410,7 @@ func (ie *IfdEnumerate) parseIfd(ii *exifcommon.IfdIdentity, bp *byteParser, vis log.Panic(err) } - err = ie.postparseTag(ite, med) + err = ie.tagPostParse(ite, med) if err == nil { if err == ErrTagNotFound { continue @@ -1382,9 +1391,16 @@ func ParseOneIfd(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, ii *exif } }() - ie := NewIfdEnumerate(ifdMapping, tagIndex, make([]byte, 0), byteOrder) + // TODO(dustin): Add test - bp, err := newByteParser(ifdBlock, byteOrder, 0) + // RELEASE(dustin): Stop exporting this. Just supports tests. + + ebs := NewExifReadSeekerWithBytes(ifdBlock) + + rs, err := ebs.GetReadSeeker(0) + log.PanicIf(err) + + bp, err := newByteParser(rs, byteOrder, 0) if err != nil { if err == ErrOffsetInvalid { return 0, nil, err @@ -1393,6 +1409,9 @@ func ParseOneIfd(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, ii *exif log.Panic(err) } + dummyEbs := NewExifReadSeekerWithBytes([]byte{}) + ie := NewIfdEnumerate(ifdMapping, tagIndex, dummyEbs, byteOrder) + nextIfdOffset, entries, _, err = ie.parseIfd(ii, bp, visitor, true, nil) log.PanicIf(err) @@ -1407,9 +1426,16 @@ func ParseOneTag(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, ii *exif } }() - ie := NewIfdEnumerate(ifdMapping, tagIndex, make([]byte, 0), byteOrder) + // TODO(dustin): Add test - bp, err := newByteParser(tagBlock, byteOrder, 0) + // RELEASE(dustin): Stop exporting this. Just supports tests. + + ebs := NewExifReadSeekerWithBytes(tagBlock) + + rs, err := ebs.GetReadSeeker(0) + log.PanicIf(err) + + bp, err := newByteParser(rs, byteOrder, 0) if err != nil { if err == ErrOffsetInvalid { return nil, err @@ -1418,10 +1444,13 @@ func ParseOneTag(ifdMapping *exifcommon.IfdMapping, tagIndex *TagIndex, ii *exif log.Panic(err) } + dummyEbs := NewExifReadSeekerWithBytes([]byte{}) + ie := NewIfdEnumerate(ifdMapping, tagIndex, dummyEbs, byteOrder) + ite, err = ie.parseTag(ii, 0, bp) log.PanicIf(err) - err = ie.postparseTag(ite, nil) + err = ie.tagPostParse(ite, nil) if err != nil { if err == ErrTagNotFound { return nil, err diff --git a/v3/ifd_tag_entry.go b/v3/ifd_tag_entry.go index 789a998..8602b79 100644 --- a/v3/ifd_tag_entry.go +++ b/v3/ifd_tag_entry.go @@ -2,6 +2,7 @@ package exif import ( "fmt" + "io" "encoding/binary" @@ -42,23 +43,23 @@ type IfdTagEntry struct { isUnhandledUnknown bool - addressableData []byte - byteOrder binary.ByteOrder + rs io.ReadSeeker + byteOrder binary.ByteOrder tagName string } -func newIfdTagEntry(ii *exifcommon.IfdIdentity, tagId uint16, tagIndex int, tagType exifcommon.TagTypePrimitive, unitCount uint32, valueOffset uint32, rawValueOffset []byte, addressableData []byte, byteOrder binary.ByteOrder) *IfdTagEntry { +func newIfdTagEntry(ii *exifcommon.IfdIdentity, tagId uint16, tagIndex int, tagType exifcommon.TagTypePrimitive, unitCount uint32, valueOffset uint32, rawValueOffset []byte, rs io.ReadSeeker, byteOrder binary.ByteOrder) *IfdTagEntry { return &IfdTagEntry{ - ifdIdentity: ii, - tagId: tagId, - tagIndex: tagIndex, - tagType: tagType, - unitCount: unitCount, - valueOffset: valueOffset, - rawValueOffset: rawValueOffset, - addressableData: addressableData, - byteOrder: byteOrder, + ifdIdentity: ii, + tagId: tagId, + tagIndex: tagIndex, + tagType: tagType, + unitCount: unitCount, + valueOffset: valueOffset, + rawValueOffset: rawValueOffset, + rs: rs, + byteOrder: byteOrder, } } @@ -291,7 +292,7 @@ func (ite *IfdTagEntry) getValueContext() *exifcommon.ValueContext { ite.unitCount, ite.valueOffset, ite.rawValueOffset, - ite.addressableData, + ite.rs, ite.tagType, ite.byteOrder) } diff --git a/v3/ifd_tag_entry_test.go b/v3/ifd_tag_entry_test.go index d4890e4..210041d 100644 --- a/v3/ifd_tag_entry_test.go +++ b/v3/ifd_tag_entry_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/dsoprea/go-logging" + "github.com/dsoprea/go-utility/filesystem" "github.com/dsoprea/go-exif/v2/common" ) @@ -13,6 +14,7 @@ func TestIfdTagEntry_RawBytes_Allocated(t *testing.T) { data := []byte{0x11, 0x22, 0x33, 0x44, 0x55, 0x66} addressableBytes := data + sb := rifs.NewSeekableBufferWithBytes(addressableBytes) ite := newIfdTagEntry( exifcommon.IfdStandardIfdIdentity, @@ -22,7 +24,7 @@ func TestIfdTagEntry_RawBytes_Allocated(t *testing.T) { 6, 0, nil, - addressableBytes, + sb, exifcommon.TestDefaultByteOrder) value, err := ite.GetRawBytes() diff --git a/v3/undefined/exif_8828_oecf_test.go b/v3/undefined/exif_8828_oecf_test.go index 36fd0de..e0cc465 100644 --- a/v3/undefined/exif_8828_oecf_test.go +++ b/v3/undefined/exif_8828_oecf_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/dsoprea/go-logging" + "github.com/dsoprea/go-utility/filesystem" "github.com/dsoprea/go-exif/v2/common" ) @@ -59,6 +60,7 @@ func TestCodec8828Oecf_Decode(t *testing.T) { 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x16} addressableData := encoded + sb := rifs.NewSeekableBufferWithBytes(addressableData) valueContext := exifcommon.NewValueContext( "", @@ -66,7 +68,7 @@ func TestCodec8828Oecf_Decode(t *testing.T) { uint32(len(encoded)), 0, nil, - addressableData, + sb, exifcommon.TypeUndefined, exifcommon.TestDefaultByteOrder) diff --git a/v3/undefined/exif_927C_maker_note_test.go b/v3/undefined/exif_927C_maker_note_test.go index 539f60d..8d62fd4 100644 --- a/v3/undefined/exif_927C_maker_note_test.go +++ b/v3/undefined/exif_927C_maker_note_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/dsoprea/go-logging" + "github.com/dsoprea/go-utility/filesystem" "github.com/dsoprea/go-exif/v2/common" ) @@ -51,13 +52,15 @@ func TestCodec927CMakerNote_Decode(t *testing.T) { MakerNoteBytes: b, } + sb := rifs.NewSeekableBufferWithBytes(b) + valueContext := exifcommon.NewValueContext( "", 0, uint32(len(b)), 0, nil, - b, + sb, exifcommon.TypeUndefined, exifcommon.TestDefaultByteOrder) diff --git a/v3/undefined/exif_9286_user_comment_test.go b/v3/undefined/exif_9286_user_comment_test.go index f6923a3..870aab0 100644 --- a/v3/undefined/exif_9286_user_comment_test.go +++ b/v3/undefined/exif_9286_user_comment_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/dsoprea/go-logging" + "github.com/dsoprea/go-utility/filesystem" "github.com/dsoprea/go-exif/v2/common" ) @@ -65,6 +66,7 @@ func TestCodec9286UserComment_Decode(t *testing.T) { } addressableBytes := encoded + sb := rifs.NewSeekableBufferWithBytes(addressableBytes) valueContext := exifcommon.NewValueContext( "", @@ -72,7 +74,7 @@ func TestCodec9286UserComment_Decode(t *testing.T) { uint32(len(encoded)), 0, nil, - addressableBytes, + sb, exifcommon.TypeUndefined, exifcommon.TestDefaultByteOrder) diff --git a/v3/undefined/exif_A000_flashpix_version_test.go b/v3/undefined/exif_A000_flashpix_version_test.go index 9b2d641..28992d9 100644 --- a/v3/undefined/exif_A000_flashpix_version_test.go +++ b/v3/undefined/exif_A000_flashpix_version_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/dsoprea/go-logging" + "github.com/dsoprea/go-utility/filesystem" "github.com/dsoprea/go-exif/v2/common" ) @@ -48,6 +49,7 @@ func TestCodecA000FlashpixVersion_Decode(t *testing.T) { encoded := []byte(versionPhrase) addressableBytes := encoded + sb := rifs.NewSeekableBufferWithBytes(addressableBytes) valueContext := exifcommon.NewValueContext( "", @@ -55,7 +57,7 @@ func TestCodecA000FlashpixVersion_Decode(t *testing.T) { uint32(len(encoded)), 0, nil, - addressableBytes, + sb, exifcommon.TypeUndefined, exifcommon.TestDefaultByteOrder) diff --git a/v3/undefined/exif_A20C_spatial_frequency_response_test.go b/v3/undefined/exif_A20C_spatial_frequency_response_test.go index 73ebe5b..d470098 100644 --- a/v3/undefined/exif_A20C_spatial_frequency_response_test.go +++ b/v3/undefined/exif_A20C_spatial_frequency_response_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/dsoprea/go-logging" + "github.com/dsoprea/go-utility/filesystem" "github.com/dsoprea/go-exif/v2/common" ) @@ -82,6 +83,7 @@ func TestCodecA20CSpatialFrequencyResponse_Decode(t *testing.T) { } addressableBytes := encoded + sb := rifs.NewSeekableBufferWithBytes(addressableBytes) valueContext := exifcommon.NewValueContext( "", @@ -89,7 +91,7 @@ func TestCodecA20CSpatialFrequencyResponse_Decode(t *testing.T) { uint32(len(encoded)), 0, nil, - addressableBytes, + sb, exifcommon.TypeUndefined, exifcommon.TestDefaultByteOrder) diff --git a/v3/undefined/exif_A302_cfa_pattern_test.go b/v3/undefined/exif_A302_cfa_pattern_test.go index 9500c90..f9bb0be 100644 --- a/v3/undefined/exif_A302_cfa_pattern_test.go +++ b/v3/undefined/exif_A302_cfa_pattern_test.go @@ -5,8 +5,10 @@ import ( "reflect" "testing" - "github.com/dsoprea/go-exif/v2/common" "github.com/dsoprea/go-logging" + "github.com/dsoprea/go-utility/filesystem" + + "github.com/dsoprea/go-exif/v2/common" ) func TestTagA302CfaPattern_String(t *testing.T) { @@ -63,7 +65,8 @@ func TestCodecA302CfaPattern_Decode(t *testing.T) { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, } - addressableData := encoded + addressableBytes := encoded + sb := rifs.NewSeekableBufferWithBytes(addressableBytes) valueContext := exifcommon.NewValueContext( "", @@ -71,7 +74,7 @@ func TestCodecA302CfaPattern_Decode(t *testing.T) { uint32(len(encoded)), 0, nil, - addressableData, + sb, exifcommon.TypeUndefined, exifcommon.TestDefaultByteOrder) diff --git a/v3/utility.go b/v3/utility.go index e588d35..259772d 100644 --- a/v3/utility.go +++ b/v3/utility.go @@ -2,6 +2,7 @@ package exif import ( "fmt" + "io" "math" "reflect" "strconv" @@ -151,7 +152,8 @@ func GetFlatExifData(exifData []byte) (exifTags []ExifTag, err error) { im := NewIfdMappingWithStandard() ti := NewTagIndex() - ie := NewIfdEnumerate(im, ti, exifData, eh.ByteOrder) + ebs := NewExifReadSeekerWithBytes(exifData) + ie := NewIfdEnumerate(im, ti, ebs, eh.ByteOrder) exifTags = make([]ExifTag, 0) @@ -232,3 +234,11 @@ func GpsDegreesEquals(gi1, gi2 GpsDegrees) bool { func IsTime(v interface{}) bool { return reflect.TypeOf(v) == timeType } + +// TODO(dustin): !! For debugging +func mustGetCurrentOffset(s io.Seeker) int64 { + offset, err := s.Seek(0, io.SeekCurrent) + log.PanicIf(err) + + return offset +}