From 71b242c269e60d14cc47ad54f7dba5aed8dc9fd0 Mon Sep 17 00:00:00 2001
From: Dustin Oprea <myselfasunder@gmail.com>
Date: Tue, 31 Dec 2019 06:09:35 -0500
Subject: [PATCH] value_context.go: We now store the IFD-path and tag-ID on the
 value-context

This allows us to add an API for accessing an undefined-value/structure
directly on ValueContext.

- ifd_tag_entry.go: Fixed several methods on `IfdTagEntry` to be by-
  reference. It was a latent bug that they weren't.

- value_context.go: Added `newValueContextFromTag` to streamline
  the changes above.
---
 ifd_enumerate.go | 33 +++++++++++++++------------------
 ifd_tag_entry.go | 41 ++++++++++++++++-------------------------
 value_context.go | 20 +++++++++++++++++++-
 3 files changed, 50 insertions(+), 44 deletions(-)

diff --git a/ifd_enumerate.go b/ifd_enumerate.go
index c56c906..b088971 100644
--- a/ifd_enumerate.go
+++ b/ifd_enumerate.go
@@ -210,6 +210,18 @@ func (ie *IfdEnumerate) parseTag(fqIfdPath string, tagPosition int, ite *IfdTagE
 	return tag, nil
 }
 
+func (ie *IfdEnumerate) GetValueContext(ite *IfdTagEntry) *ValueContext {
+
+	// TODO(dustin): Add test
+
+	addressableData := ie.exifData[ExifAddressableAreaStart:]
+
+	return newValueContextFromTag(
+		ite,
+		addressableData,
+		ie.byteOrder)
+}
+
 func (ie *IfdEnumerate) resolveTagValue(ite *IfdTagEntry) (valueBytes []byte, isUnhandledUnknown bool, err error) {
 	defer func() {
 		if state := recover(); state != nil {
@@ -225,15 +237,7 @@ func (ie *IfdEnumerate) resolveTagValue(ite *IfdTagEntry) (valueBytes []byte, is
 	// (obviously). However, here, in order to produce the list of bytes, we
 	// need to coerce whatever `UndefinedValue()` returns.
 	if ite.TagType == TypeUndefined {
-		valueContext :=
-			newValueContext(
-				ite.UnitCount,
-				ite.ValueOffset,
-				ite.RawValueOffset,
-				addressableData,
-				ite.TagType,
-				ie.byteOrder,
-			)
+		valueContext := ie.GetValueContext(ite)
 
 		value, err := UndefinedValue(ite.IfdPath, ite.TagId, valueContext, ie.byteOrder)
 		if err != nil {
@@ -372,16 +376,9 @@ func (ie *IfdEnumerate) ParseIfd(fqIfdPath string, ifdIndex int, ite *IfdTagEnum
 		if visitorWrapper != nil {
 			tt := NewTagType(tag.TagType, ie.byteOrder)
 
-			vc :=
-				newValueContext(
-					tag.UnitCount,
-					tag.ValueOffset,
-					tag.RawValueOffset,
-					ie.exifData[ExifAddressableAreaStart:],
-					tag.TagType,
-					ie.byteOrder)
+			valueContext := ie.GetValueContext(tag)
 
-			err := visitorWrapper.Visit(fqIfdPath, ifdIndex, tag.TagId, tt, vc)
+			err := visitorWrapper.Visit(fqIfdPath, ifdIndex, tag.TagId, tt, valueContext)
 			log.PanicIf(err)
 		}
 
diff --git a/ifd_tag_entry.go b/ifd_tag_entry.go
index 7519b4a..89902c1 100644
--- a/ifd_tag_entry.go
+++ b/ifd_tag_entry.go
@@ -43,34 +43,31 @@ type IfdTagEntry struct {
 	isUnhandledUnknown bool
 }
 
-func (ite IfdTagEntry) String() string {
+func (ite *IfdTagEntry) String() string {
 	return fmt.Sprintf("IfdTagEntry<TAG-IFD-PATH=[%s] TAG-ID=(0x%04x) TAG-TYPE=[%s] UNIT-COUNT=(%d)>", ite.IfdPath, ite.TagId, TypeNames[ite.TagType], ite.UnitCount)
 }
 
 // ValueString renders a string from whatever the value in this tag is.
-func (ite IfdTagEntry) ValueString(addressableData []byte, byteOrder binary.ByteOrder) (value string, err error) {
+func (ite *IfdTagEntry) ValueString(addressableData []byte, byteOrder binary.ByteOrder) (value string, err error) {
 	defer func() {
 		if state := recover(); state != nil {
 			err = log.Wrap(state.(error))
 		}
 	}()
 
-	vc :=
-		newValueContext(
-			ite.UnitCount,
-			ite.ValueOffset,
-			ite.RawValueOffset,
+	valueContext :=
+		newValueContextFromTag(
+			ite,
 			addressableData,
-			ite.TagType,
 			byteOrder)
 
 	if ite.TagType == TypeUndefined {
-		valueRaw, err := UndefinedValue(ite.IfdPath, ite.TagId, vc, byteOrder)
+		valueRaw, err := UndefinedValue(ite.IfdPath, ite.TagId, valueContext, byteOrder)
 		log.PanicIf(err)
 
 		value = fmt.Sprintf("%v", valueRaw)
 	} else {
-		value, err = vc.Format()
+		value, err = valueContext.Format()
 		log.PanicIf(err)
 	}
 
@@ -78,7 +75,7 @@ func (ite IfdTagEntry) ValueString(addressableData []byte, byteOrder binary.Byte
 }
 
 // ValueBytes renders a specific list of bytes from the value in this tag.
-func (ite IfdTagEntry) ValueBytes(addressableData []byte, byteOrder binary.ByteOrder) (value []byte, err error) {
+func (ite *IfdTagEntry) ValueBytes(addressableData []byte, byteOrder binary.ByteOrder) (value []byte, err error) {
 	defer func() {
 		if state := recover(); state != nil {
 			err = log.Wrap(state.(error))
@@ -92,12 +89,9 @@ func (ite IfdTagEntry) ValueBytes(addressableData []byte, byteOrder binary.ByteO
 	// need to coerce whatever `UndefinedValue()` returns.
 	if ite.TagType == TypeUndefined {
 		valueContext :=
-			newValueContext(
-				ite.UnitCount,
-				ite.ValueOffset,
-				ite.RawValueOffset,
+			newValueContextFromTag(
+				ite,
 				addressableData,
-				ite.TagType,
 				byteOrder)
 
 		value, err := UndefinedValue(ite.IfdPath, ite.TagId, valueContext, byteOrder)
@@ -145,29 +139,26 @@ func (ite IfdTagEntry) ValueBytes(addressableData []byte, byteOrder binary.ByteO
 }
 
 // Value returns the specific, parsed, typed value from the tag.
-func (ite IfdTagEntry) Value(addressableData []byte, byteOrder binary.ByteOrder) (value interface{}, err error) {
+func (ite *IfdTagEntry) Value(addressableData []byte, byteOrder binary.ByteOrder) (value interface{}, err error) {
 	defer func() {
 		if state := recover(); state != nil {
 			err = log.Wrap(state.(error))
 		}
 	}()
 
-	vc :=
-		newValueContext(
-			ite.UnitCount,
-			ite.ValueOffset,
-			ite.RawValueOffset,
+	valueContext :=
+		newValueContextFromTag(
+			ite,
 			addressableData,
-			ite.TagType,
 			byteOrder)
 
 	if ite.TagType == TypeUndefined {
-		value, err = UndefinedValue(ite.IfdPath, ite.TagId, vc, byteOrder)
+		value, err = UndefinedValue(ite.IfdPath, ite.TagId, valueContext, byteOrder)
 		log.PanicIf(err)
 	} else {
 		tt := NewTagType(ite.TagType, byteOrder)
 
-		value, err = tt.Resolve(vc)
+		value, err = tt.Resolve(valueContext)
 		log.PanicIf(err)
 	}
 
diff --git a/value_context.go b/value_context.go
index 4c187dc..3d3f044 100644
--- a/value_context.go
+++ b/value_context.go
@@ -28,9 +28,12 @@ type ValueContext struct {
 	// undefinedValueUnitCount is the effective unit-count to use if this is an
 	// "undefined" value.
 	undefinedValueUnitCount uint32
+
+	ifdPath string
+	tagId   uint16
 }
 
-func newValueContext(unitCount, valueOffset uint32, rawValueOffset, addressableData []byte, tagType TagTypePrimitive, byteOrder binary.ByteOrder) *ValueContext {
+func newValueContext(ifdPath string, tagId uint16, unitCount, valueOffset uint32, rawValueOffset, addressableData []byte, tagType TagTypePrimitive, byteOrder binary.ByteOrder) *ValueContext {
 	return &ValueContext{
 		unitCount:       unitCount,
 		valueOffset:     valueOffset,
@@ -39,9 +42,24 @@ func newValueContext(unitCount, valueOffset uint32, rawValueOffset, addressableD
 
 		tagType:   tagType,
 		byteOrder: byteOrder,
+
+		ifdPath: ifdPath,
+		tagId:   tagId,
 	}
 }
 
+func newValueContextFromTag(ite *IfdTagEntry, addressableData []byte, byteOrder binary.ByteOrder) *ValueContext {
+	return newValueContext(
+		ite.IfdPath,
+		ite.TagId,
+		ite.UnitCount,
+		ite.ValueOffset,
+		ite.RawValueOffset,
+		addressableData,
+		ite.TagType,
+		byteOrder)
+}
+
 func (vc *ValueContext) SetUnknownValueParameters(tagType TagTypePrimitive, unitCount uint32) {
 	vc.undefinedValueTagType = tagType
 	vc.undefinedValueUnitCount = unitCount