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
dustin/master
Dustin Oprea 2020-07-08 04:01:09 -04:00
parent 56058635d0
commit 312218d7b1
17 changed files with 633 additions and 121 deletions

View File

@ -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

View File

@ -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)

50
v3/data_layer.go Normal file
View File

@ -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
}

View File

@ -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)

View File

@ -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<BYTE-ORDER=[BigEndian] FIRST-IFD-OFFSET=(0x11223344)>
}

View File

@ -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

View File

@ -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=

View File

@ -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

View File

@ -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)
}

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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
}