diff --git a/exif-read-tool/main.go b/exif-read-tool/main.go index 68a3e00..bd2ae64 100644 --- a/exif-read-tool/main.go +++ b/exif-read-tool/main.go @@ -108,12 +108,12 @@ func main() { var value interface{} if tagType.Type() == exif.TypeUndefined { var err error - value, err = exif.UndefinedValue(ifdPath, tagId, valueContext, tagType.ByteOrder()) - if log.Is(err, exif.ErrUnhandledUnknownTypedTag) { + switch value, err = exif.UndefinedValue(ifdPath, tagId, valueContext, tagType.ByteOrder()); { + case log.Is(err, exif.ErrUnhandledUnknownTypedTag): value = nil - } else if err != nil { + case err != nil: log.Panic(err) - } else { + default: valueString = fmt.Sprintf("%v", value) } } else { diff --git a/ifd_builder.go b/ifd_builder.go index e4cc14a..7d71a0f 100644 --- a/ifd_builder.go +++ b/ifd_builder.go @@ -30,7 +30,8 @@ type IfdBuilderTagValue struct { } func (ibtv IfdBuilderTagValue) String() string { - if ibtv.IsBytes() == true { + switch { + case ibtv.IsBytes(): var valuePhrase string if len(ibtv.valueBytes) <= 8 { valuePhrase = fmt.Sprintf("%v", ibtv.valueBytes) @@ -39,9 +40,9 @@ func (ibtv IfdBuilderTagValue) String() string { } return fmt.Sprintf("IfdBuilderTagValue", valuePhrase, len(ibtv.valueBytes)) - } else if ibtv.IsIb() == true { + case ibtv.IsIb(): return fmt.Sprintf("IfdBuilderTagValue", ibtv.ib) - } else { + default: log.Panicf("IBTV state undefined") return "" } @@ -567,12 +568,12 @@ func (ib *IfdBuilder) printTagTree(levels int) { if isChildIb == true { tagName = "" } else { - it, err := ib.tagIndex.Get(tag.ifdPath, tag.tagId) - if log.Is(err, ErrTagNotFound) == true { + switch it, err := ib.tagIndex.Get(tag.ifdPath, tag.tagId); { + case log.Is(err, ErrTagNotFound): tagName = "" - } else if err != nil { + case err != nil: log.Panic(err) - } else { + default: tagName = it.Name } } @@ -808,13 +809,13 @@ func (ib *IfdBuilder) Set(bt *BuilderTag) (err error) { } }() - position, err := ib.Find(bt.tagId) - if err == nil { + switch position, err := ib.Find(bt.tagId); { + case err == nil: ib.tags[position] = bt - } else if log.Is(err, ErrTagEntryNotFound) == true { + case log.Is(err, ErrTagEntryNotFound): err = ib.add(bt) log.PanicIf(err) - } else { + default: log.Panic(err) } @@ -907,11 +908,12 @@ func (ib *IfdBuilder) add(bt *BuilderTag) (err error) { } }() - if bt.ifdPath == "" { + switch { + case bt.ifdPath == "": log.Panicf("BuilderTag ifdPath is not set: %s", bt) - } else if bt.typeId == 0x0 { + case bt.typeId == 0x0: log.Panicf("BuilderTag type-ID is not set: %s", bt) - } else if bt.value == nil { + case bt.value == nil: log.Panicf("BuilderTag value is not set: %s", bt) } diff --git a/tags_unknown.go b/tags_unknown.go index 6f2a66d..a264660 100644 --- a/tags_unknown.go +++ b/tags_unknown.go @@ -209,8 +209,10 @@ func UndefinedValue(ifdPath string, tagId uint16, valueContext ValueContext, byt typeLogger.Debugf(nil, "UndefinedValue: IFD-PATH=[%s] TAG-ID=(0x%02x)", ifdPath, tagId) - if ifdPath == IfdPathStandardExif { - if tagId == 0x9000 { + switch ifdPath { + case IfdPathStandardExif: + switch tagId { + case 0x9000: // ExifVersion tt := NewTagType(TypeAsciiNoNul, byteOrder) @@ -219,7 +221,7 @@ func UndefinedValue(ifdPath string, tagId uint16, valueContext ValueContext, byt log.PanicIf(err) return TagUnknownType_GeneralString(valueString), nil - } else if tagId == 0xa000 { + case 0xa000: // FlashpixVersion tt := NewTagType(TypeAsciiNoNul, byteOrder) @@ -228,7 +230,7 @@ func UndefinedValue(ifdPath string, tagId uint16, valueContext ValueContext, byt log.PanicIf(err) return TagUnknownType_GeneralString(valueString), nil - } else if tagId == 0x9286 { + case 0x9286: // UserComment tt := NewTagType(TypeByte, byteOrder) @@ -255,7 +257,7 @@ func UndefinedValue(ifdPath string, tagId uint16, valueContext ValueContext, byt typeLogger.Warningf(nil, "User-comment encoding not valid. Returning 'unknown' type (the default).") return unknownUc, nil - } else if tagId == 0x927c { + case 0x927c: // MakerNote // TODO(dustin): !! This is the Wild Wild West. This very well might be a child IFD, but any and all OEM's define their own formats. If we're going to be writing changes and this is complete EXIF (which may not have the first eight bytes), it might be fine. However, if these are just IFDs they'll be relative to the main EXIF, this will invalidate the MakerNote data for IFDs and any other implementations that use offsets unless we can interpret them all. It be best to return to this later and just exclude this from being written for now, though means a loss of a wealth of image metadata. // -> We can also just blindly try to interpret as an IFD and just validate that it's looks good (maybe it will even have a 'next ifd' pointer that we can validate is 0x0). @@ -284,7 +286,7 @@ func UndefinedValue(ifdPath string, tagId uint16, valueContext ValueContext, byt } return mn, nil - } else if tagId == 0x9101 { + case 0x9101: // ComponentsConfiguration tt := NewTagType(TypeByte, byteOrder) @@ -310,7 +312,7 @@ func UndefinedValue(ifdPath string, tagId uint16, valueContext ValueContext, byt return cc, nil } - } else if ifdPath == IfdPathStandardGps { + case IfdPathStandardGps: if tagId == 0x001c { // GPSAreaInformation @@ -330,7 +332,7 @@ func UndefinedValue(ifdPath string, tagId uint16, valueContext ValueContext, byt return TagUnknownType_GeneralString(valueString), nil } - } else if ifdPath == IfdPathStandardExifIop { + case IfdPathStandardExifIop: if tagId == 0x0002 { // InteropVersion diff --git a/type.go b/type.go index dbfb11d..f4c60bb 100644 --- a/type.go +++ b/type.go @@ -1,565 +1,562 @@ package exif import ( - "errors" - "bytes" - "fmt" - "strconv" - "strings" + "bytes" + "errors" + "fmt" + "strconv" + "strings" - "encoding/binary" + "encoding/binary" - "github.com/dsoprea/go-logging" + "github.com/dsoprea/go-logging" ) const ( - TypeByte = uint16(1) - TypeAscii = uint16(2) - TypeShort = uint16(3) - TypeLong = uint16(4) - TypeRational = uint16(5) - TypeUndefined = uint16(7) - TypeSignedLong = uint16(9) - TypeSignedRational = uint16(10) + TypeByte = uint16(1) + TypeAscii = uint16(2) + TypeShort = uint16(3) + TypeLong = uint16(4) + TypeRational = uint16(5) + TypeUndefined = uint16(7) + TypeSignedLong = uint16(9) + TypeSignedRational = uint16(10) - // Custom, for our purposes. - TypeAsciiNoNul = uint16(0xf0) + // Custom, for our purposes. + TypeAsciiNoNul = uint16(0xf0) ) var ( - typeLogger = log.NewLogger("exif.type") + typeLogger = log.NewLogger("exif.type") ) var ( - TypeNames = map[uint16]string { - TypeByte: "BYTE", - TypeAscii: "ASCII", - TypeShort: "SHORT", - TypeLong: "LONG", - TypeRational: "RATIONAL", - TypeUndefined: "UNDEFINED", - TypeSignedLong: "SLONG", - TypeSignedRational: "SRATIONAL", + TypeNames = map[uint16]string{ + TypeByte: "BYTE", + TypeAscii: "ASCII", + TypeShort: "SHORT", + TypeLong: "LONG", + TypeRational: "RATIONAL", + TypeUndefined: "UNDEFINED", + TypeSignedLong: "SLONG", + TypeSignedRational: "SRATIONAL", - TypeAsciiNoNul: "_ASCII_NO_NUL", - } + TypeAsciiNoNul: "_ASCII_NO_NUL", + } - TypeNamesR = map[string]uint16 {} + TypeNamesR = map[string]uint16{} ) var ( - // ErrNotEnoughData is used when there isn't enough data to accomodate what - // we're trying to parse (sizeof(type) * unit_count). - ErrNotEnoughData = errors.New("not enough data for type") + // ErrNotEnoughData is used when there isn't enough data to accomodate what + // we're trying to parse (sizeof(type) * unit_count). + ErrNotEnoughData = errors.New("not enough data for type") - // ErrWrongType is used when we try to parse anything other than the - // current type. - ErrWrongType = errors.New("wrong type, can not parse") + // ErrWrongType is used when we try to parse anything other than the + // current type. + ErrWrongType = errors.New("wrong type, can not parse") - // ErrUnhandledUnknownTag is used when we try to parse a tag that's - // recorded as an "unknown" type but not a documented tag (therefore - // leaving us not knowning how to read it). - ErrUnhandledUnknownTypedTag = errors.New("not a standard unknown-typed tag") + // ErrUnhandledUnknownTag is used when we try to parse a tag that's + // recorded as an "unknown" type but not a documented tag (therefore + // leaving us not knowning how to read it). + ErrUnhandledUnknownTypedTag = errors.New("not a standard unknown-typed tag") ) - type Rational struct { - Numerator uint32 - Denominator uint32 + Numerator uint32 + Denominator uint32 } type SignedRational struct { - Numerator int32 - Denominator int32 + Numerator int32 + Denominator int32 } func init() { - for typeId, typeName := range TypeNames { - TypeNamesR[typeName] = typeId - } + for typeId, typeName := range TypeNames { + TypeNamesR[typeName] = typeId + } } - type TagType struct { - tagType uint16 - name string - byteOrder binary.ByteOrder + tagType uint16 + name string + byteOrder binary.ByteOrder } func NewTagType(tagType uint16, byteOrder binary.ByteOrder) TagType { - name, found := TypeNames[tagType] - if found == false { - log.Panicf("tag-type not valid: 0x%04x", tagType) - } + name, found := TypeNames[tagType] + if found == false { + log.Panicf("tag-type not valid: 0x%04x", tagType) + } - return TagType{ - tagType: tagType, - name: name, - byteOrder: byteOrder, - } + return TagType{ + tagType: tagType, + name: name, + byteOrder: byteOrder, + } } func (tt TagType) String() string { - return fmt.Sprintf("TagType", tt.name) + return fmt.Sprintf("TagType", tt.name) } func (tt TagType) Name() string { - return tt.name + return tt.name } func (tt TagType) Type() uint16 { - return tt.tagType + return tt.tagType } func (tt TagType) ByteOrder() binary.ByteOrder { - return tt.byteOrder + return tt.byteOrder } func (tt TagType) Size() int { - return TagTypeSize(tt.Type()) + return TagTypeSize(tt.Type()) } func TagTypeSize(tagType uint16) int { - if tagType == TypeByte { - return 1 - } else if tagType == TypeAscii || tagType == TypeAsciiNoNul { - return 1 - } else if tagType == TypeShort { - return 2 - } else if tagType == TypeLong { - return 4 - } else if tagType == TypeRational { - return 8 - } else if tagType == TypeSignedLong { - return 4 - } else if tagType == TypeSignedRational { - return 8 - } else { - log.Panicf("can not determine tag-value size for type (%d): [%s]", tagType, TypeNames[tagType]) + switch tagType { + case TypeByte, TypeAscii, TypeAsciiNoNul: + return 1 + case TypeShort: + return 2 + case TypeLong: + return 4 + case TypeRational: + return 8 + case TypeSignedLong: + return 4 + case TypeSignedRational: + return 8 + default: + log.Panicf("can not determine tag-value size for type (%d): [%s]", tagType, TypeNames[tagType]) - // Never called. - return 0 - } + // Never called. + return 0 + } } // ValueIsEmbedded will return a boolean indicating whether the value should be // found directly within the IFD entry or an offset to somewhere else. func (tt TagType) ValueIsEmbedded(unitCount uint32) bool { - return (tt.Size() * int(unitCount)) <= 4 + return (tt.Size() * int(unitCount)) <= 4 } func (tt TagType) ParseBytes(data []byte, unitCount uint32) (value []uint8, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - if tt.tagType != TypeByte { - log.Panic(ErrWrongType) - } + if tt.tagType != TypeByte { + log.Panic(ErrWrongType) + } - count := int(unitCount) + count := int(unitCount) - if len(data) < (tt.Size() * count) { - log.Panic(ErrNotEnoughData) - } + if len(data) < (tt.Size() * count) { + log.Panic(ErrNotEnoughData) + } - value = []uint8(data[:count]) + value = []uint8(data[:count]) - return value, nil + return value, nil } // ParseAscii returns a string and auto-strips the trailing NUL character. func (tt TagType) ParseAscii(data []byte, unitCount uint32) (value string, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - if tt.tagType != TypeAscii && tt.tagType != TypeAsciiNoNul { - log.Panic(ErrWrongType) - } + if tt.tagType != TypeAscii && tt.tagType != TypeAsciiNoNul { + log.Panic(ErrWrongType) + } - count := int(unitCount) + count := int(unitCount) - if len(data) < (tt.Size() * count) { - log.Panic(ErrNotEnoughData) - } + if len(data) < (tt.Size() * count) { + log.Panic(ErrNotEnoughData) + } - if len(data) == 0 || data[count - 1] != 0 { - s := string(data[:count]) - typeLogger.Warningf(nil, "ascii not terminated with nul as expected: [%v]", s) + if len(data) == 0 || data[count-1] != 0 { + s := string(data[:count]) + typeLogger.Warningf(nil, "ascii not terminated with nul as expected: [%v]", s) - return s, nil - } else { - // Auto-strip the NUL from the end. It serves no purpose outside of - // encoding semantics. + return s, nil + } else { + // Auto-strip the NUL from the end. It serves no purpose outside of + // encoding semantics. - return string(data[:count - 1]), nil - } + return string(data[:count-1]), nil + } } // ParseAsciiNoNul returns a string without any consideration for a trailing NUL // character. func (tt TagType) ParseAsciiNoNul(data []byte, unitCount uint32) (value string, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - if tt.tagType != TypeAscii && tt.tagType != TypeAsciiNoNul { - log.Panic(ErrWrongType) - } + if tt.tagType != TypeAscii && tt.tagType != TypeAsciiNoNul { + log.Panic(ErrWrongType) + } - count := int(unitCount) + count := int(unitCount) - if len(data) < (tt.Size() * count) { - log.Panic(ErrNotEnoughData) - } + if len(data) < (tt.Size() * count) { + log.Panic(ErrNotEnoughData) + } - return string(data[:count]), nil + return string(data[:count]), nil } func (tt TagType) ParseShorts(data []byte, unitCount uint32) (value []uint16, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - if tt.tagType != TypeShort { - log.Panic(ErrWrongType) - } + if tt.tagType != TypeShort { + log.Panic(ErrWrongType) + } - count := int(unitCount) + count := int(unitCount) - if len(data) < (tt.Size() * count) { - log.Panic(ErrNotEnoughData) - } + if len(data) < (tt.Size() * count) { + log.Panic(ErrNotEnoughData) + } - value = make([]uint16, count) - for i := 0; i < count; i++ { - if tt.byteOrder == binary.BigEndian { - value[i] = binary.BigEndian.Uint16(data[i*2:]) - } else { - value[i] = binary.LittleEndian.Uint16(data[i*2:]) - } - } + value = make([]uint16, count) + for i := 0; i < count; i++ { + if tt.byteOrder == binary.BigEndian { + value[i] = binary.BigEndian.Uint16(data[i*2:]) + } else { + value[i] = binary.LittleEndian.Uint16(data[i*2:]) + } + } - return value, nil + return value, nil } func (tt TagType) ParseLongs(data []byte, unitCount uint32) (value []uint32, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - if tt.tagType != TypeLong { - log.Panic(ErrWrongType) - } + if tt.tagType != TypeLong { + log.Panic(ErrWrongType) + } - count := int(unitCount) + count := int(unitCount) - if len(data) < (tt.Size() * count) { - log.Panic(ErrNotEnoughData) - } + if len(data) < (tt.Size() * count) { + log.Panic(ErrNotEnoughData) + } - value = make([]uint32, count) - for i := 0; i < count; i++ { - if tt.byteOrder == binary.BigEndian { - value[i] = binary.BigEndian.Uint32(data[i*4:]) - } else { - value[i] = binary.LittleEndian.Uint32(data[i*4:]) - } - } + value = make([]uint32, count) + for i := 0; i < count; i++ { + if tt.byteOrder == binary.BigEndian { + value[i] = binary.BigEndian.Uint32(data[i*4:]) + } else { + value[i] = binary.LittleEndian.Uint32(data[i*4:]) + } + } - return value, nil + return value, nil } func (tt TagType) ParseRationals(data []byte, unitCount uint32) (value []Rational, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - if tt.tagType != TypeRational { - log.Panic(ErrWrongType) - } + if tt.tagType != TypeRational { + log.Panic(ErrWrongType) + } - count := int(unitCount) + count := int(unitCount) - if len(data) < (tt.Size() * count) { - log.Panic(ErrNotEnoughData) - } + if len(data) < (tt.Size() * count) { + log.Panic(ErrNotEnoughData) + } - value = make([]Rational, count) - for i := 0; i < count; i++ { - if tt.byteOrder == binary.BigEndian { - value[i].Numerator = binary.BigEndian.Uint32(data[i*8:]) - value[i].Denominator = binary.BigEndian.Uint32(data[i*8 + 4:]) - } else { - value[i].Numerator = binary.LittleEndian.Uint32(data[i*8:]) - value[i].Denominator = binary.LittleEndian.Uint32(data[i*8 + 4:]) - } - } + value = make([]Rational, count) + for i := 0; i < count; i++ { + if tt.byteOrder == binary.BigEndian { + value[i].Numerator = binary.BigEndian.Uint32(data[i*8:]) + value[i].Denominator = binary.BigEndian.Uint32(data[i*8+4:]) + } else { + value[i].Numerator = binary.LittleEndian.Uint32(data[i*8:]) + value[i].Denominator = binary.LittleEndian.Uint32(data[i*8+4:]) + } + } - return value, nil + return value, nil } func (tt TagType) ParseSignedLongs(data []byte, unitCount uint32) (value []int32, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - if tt.tagType != TypeSignedLong { - log.Panic(ErrWrongType) - } + if tt.tagType != TypeSignedLong { + log.Panic(ErrWrongType) + } - count := int(unitCount) + count := int(unitCount) - if len(data) < (tt.Size() * count) { - log.Panic(ErrNotEnoughData) - } + if len(data) < (tt.Size() * count) { + log.Panic(ErrNotEnoughData) + } - b := bytes.NewBuffer(data) + b := bytes.NewBuffer(data) - value = make([]int32, count) - for i := 0; i < count; i++ { - if tt.byteOrder == binary.BigEndian { - err := binary.Read(b, binary.BigEndian, &value[i]) - log.PanicIf(err) - } else { - err := binary.Read(b, binary.LittleEndian, &value[i]) - log.PanicIf(err) - } - } + value = make([]int32, count) + for i := 0; i < count; i++ { + if tt.byteOrder == binary.BigEndian { + err := binary.Read(b, binary.BigEndian, &value[i]) + log.PanicIf(err) + } else { + err := binary.Read(b, binary.LittleEndian, &value[i]) + log.PanicIf(err) + } + } - return value, nil + return value, nil } func (tt TagType) ParseSignedRationals(data []byte, unitCount uint32) (value []SignedRational, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - if tt.tagType != TypeSignedRational { - log.Panic(ErrWrongType) - } + if tt.tagType != TypeSignedRational { + log.Panic(ErrWrongType) + } - count := int(unitCount) + count := int(unitCount) - if len(data) < (tt.Size() * count) { - log.Panic(ErrNotEnoughData) - } + if len(data) < (tt.Size() * count) { + log.Panic(ErrNotEnoughData) + } - b := bytes.NewBuffer(data) + b := bytes.NewBuffer(data) - value = make([]SignedRational, count) - for i := 0; i < count; i++ { - if tt.byteOrder == binary.BigEndian { - err = binary.Read(b, binary.BigEndian, &value[i].Numerator) - log.PanicIf(err) + value = make([]SignedRational, count) + for i := 0; i < count; i++ { + if tt.byteOrder == binary.BigEndian { + err = binary.Read(b, binary.BigEndian, &value[i].Numerator) + log.PanicIf(err) - err = binary.Read(b, binary.BigEndian, &value[i].Denominator) - log.PanicIf(err) - } else { - err = binary.Read(b, binary.LittleEndian, &value[i].Numerator) - log.PanicIf(err) + err = binary.Read(b, binary.BigEndian, &value[i].Denominator) + log.PanicIf(err) + } else { + err = binary.Read(b, binary.LittleEndian, &value[i].Numerator) + log.PanicIf(err) - err = binary.Read(b, binary.LittleEndian, &value[i].Denominator) - log.PanicIf(err) - } - } + err = binary.Read(b, binary.LittleEndian, &value[i].Denominator) + log.PanicIf(err) + } + } - return value, nil + return value, nil } func (tt TagType) ReadByteValues(valueContext ValueContext) (value []byte, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - if tt.ValueIsEmbedded(valueContext.UnitCount) == true { - typeLogger.Debugf(nil, "Reading BYTE value (embedded).") + if tt.ValueIsEmbedded(valueContext.UnitCount) == true { + typeLogger.Debugf(nil, "Reading BYTE value (embedded).") - // In this case, the bytes normally used for the offset are actually - // data. - value, err = tt.ParseBytes(valueContext.RawValueOffset, valueContext.UnitCount) - log.PanicIf(err) - } else { - typeLogger.Debugf(nil, "Reading BYTE value (at offset).") + // In this case, the bytes normally used for the offset are actually + // data. + value, err = tt.ParseBytes(valueContext.RawValueOffset, valueContext.UnitCount) + log.PanicIf(err) + } else { + typeLogger.Debugf(nil, "Reading BYTE value (at offset).") - value, err = tt.ParseBytes(valueContext.AddressableData[valueContext.ValueOffset:], valueContext.UnitCount) - log.PanicIf(err) - } + value, err = tt.ParseBytes(valueContext.AddressableData[valueContext.ValueOffset:], valueContext.UnitCount) + log.PanicIf(err) + } - return value, nil + return value, nil } func (tt TagType) ReadAsciiValue(valueContext ValueContext) (value string, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - if tt.ValueIsEmbedded(valueContext.UnitCount) == true { - typeLogger.Debugf(nil, "Reading ASCII value (embedded).") + if tt.ValueIsEmbedded(valueContext.UnitCount) == true { + typeLogger.Debugf(nil, "Reading ASCII value (embedded).") - value, err = tt.ParseAscii(valueContext.RawValueOffset, valueContext.UnitCount) - log.PanicIf(err) - } else { - typeLogger.Debugf(nil, "Reading ASCII value (at offset).") + value, err = tt.ParseAscii(valueContext.RawValueOffset, valueContext.UnitCount) + log.PanicIf(err) + } else { + typeLogger.Debugf(nil, "Reading ASCII value (at offset).") - value, err = tt.ParseAscii(valueContext.AddressableData[valueContext.ValueOffset:], valueContext.UnitCount) - log.PanicIf(err) - } + value, err = tt.ParseAscii(valueContext.AddressableData[valueContext.ValueOffset:], valueContext.UnitCount) + log.PanicIf(err) + } - return value, nil + return value, nil } func (tt TagType) ReadAsciiNoNulValue(valueContext ValueContext) (value string, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - if tt.ValueIsEmbedded(valueContext.UnitCount) == true { - typeLogger.Debugf(nil, "Reading ASCII value (no-nul; embedded).") + if tt.ValueIsEmbedded(valueContext.UnitCount) == true { + typeLogger.Debugf(nil, "Reading ASCII value (no-nul; embedded).") - value, err = tt.ParseAsciiNoNul(valueContext.RawValueOffset, valueContext.UnitCount) - log.PanicIf(err) - } else { - typeLogger.Debugf(nil, "Reading ASCII value (no-nul; at offset).") + value, err = tt.ParseAsciiNoNul(valueContext.RawValueOffset, valueContext.UnitCount) + log.PanicIf(err) + } else { + typeLogger.Debugf(nil, "Reading ASCII value (no-nul; at offset).") - value, err = tt.ParseAsciiNoNul(valueContext.AddressableData[valueContext.ValueOffset:], valueContext.UnitCount) - log.PanicIf(err) - } + value, err = tt.ParseAsciiNoNul(valueContext.AddressableData[valueContext.ValueOffset:], valueContext.UnitCount) + log.PanicIf(err) + } - return value, nil + return value, nil } func (tt TagType) ReadShortValues(valueContext ValueContext) (value []uint16, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - if tt.ValueIsEmbedded(valueContext.UnitCount) == true { - typeLogger.Debugf(nil, "Reading SHORT value (embedded).") + if tt.ValueIsEmbedded(valueContext.UnitCount) == true { + typeLogger.Debugf(nil, "Reading SHORT value (embedded).") - value, err = tt.ParseShorts(valueContext.RawValueOffset, valueContext.UnitCount) - log.PanicIf(err) - } else { - typeLogger.Debugf(nil, "Reading SHORT value (at offset).") + value, err = tt.ParseShorts(valueContext.RawValueOffset, valueContext.UnitCount) + log.PanicIf(err) + } else { + typeLogger.Debugf(nil, "Reading SHORT value (at offset).") - value, err = tt.ParseShorts(valueContext.AddressableData[valueContext.ValueOffset:], valueContext.UnitCount) - log.PanicIf(err) - } + value, err = tt.ParseShorts(valueContext.AddressableData[valueContext.ValueOffset:], valueContext.UnitCount) + log.PanicIf(err) + } - return value, nil + return value, nil } func (tt TagType) ReadLongValues(valueContext ValueContext) (value []uint32, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - if tt.ValueIsEmbedded(valueContext.UnitCount) == true { - typeLogger.Debugf(nil, "Reading LONG value (embedded).") + if tt.ValueIsEmbedded(valueContext.UnitCount) == true { + typeLogger.Debugf(nil, "Reading LONG value (embedded).") - value, err = tt.ParseLongs(valueContext.RawValueOffset, valueContext.UnitCount) - log.PanicIf(err) - } else { - typeLogger.Debugf(nil, "Reading LONG value (at offset).") + value, err = tt.ParseLongs(valueContext.RawValueOffset, valueContext.UnitCount) + log.PanicIf(err) + } else { + typeLogger.Debugf(nil, "Reading LONG value (at offset).") - value, err = tt.ParseLongs(valueContext.AddressableData[valueContext.ValueOffset:], valueContext.UnitCount) - log.PanicIf(err) - } + value, err = tt.ParseLongs(valueContext.AddressableData[valueContext.ValueOffset:], valueContext.UnitCount) + log.PanicIf(err) + } - return value, nil + return value, nil } func (tt TagType) ReadRationalValues(valueContext ValueContext) (value []Rational, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - if tt.ValueIsEmbedded(valueContext.UnitCount) == true { - typeLogger.Debugf(nil, "Reading RATIONAL value (embedded).") + if tt.ValueIsEmbedded(valueContext.UnitCount) == true { + typeLogger.Debugf(nil, "Reading RATIONAL value (embedded).") - value, err = tt.ParseRationals(valueContext.RawValueOffset, valueContext.UnitCount) - log.PanicIf(err) - } else { - typeLogger.Debugf(nil, "Reading RATIONAL value (at offset).") + value, err = tt.ParseRationals(valueContext.RawValueOffset, valueContext.UnitCount) + log.PanicIf(err) + } else { + typeLogger.Debugf(nil, "Reading RATIONAL value (at offset).") - value, err = tt.ParseRationals(valueContext.AddressableData[valueContext.ValueOffset:], valueContext.UnitCount) - log.PanicIf(err) - } + value, err = tt.ParseRationals(valueContext.AddressableData[valueContext.ValueOffset:], valueContext.UnitCount) + log.PanicIf(err) + } - return value, nil + return value, nil } func (tt TagType) ReadSignedLongValues(valueContext ValueContext) (value []int32, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - if tt.ValueIsEmbedded(valueContext.UnitCount) == true { - typeLogger.Debugf(nil, "Reading SLONG value (embedded).") + if tt.ValueIsEmbedded(valueContext.UnitCount) == true { + typeLogger.Debugf(nil, "Reading SLONG value (embedded).") - value, err = tt.ParseSignedLongs(valueContext.RawValueOffset, valueContext.UnitCount) - log.PanicIf(err) - } else { - typeLogger.Debugf(nil, "Reading SLONG value (at offset).") + value, err = tt.ParseSignedLongs(valueContext.RawValueOffset, valueContext.UnitCount) + log.PanicIf(err) + } else { + typeLogger.Debugf(nil, "Reading SLONG value (at offset).") - value, err = tt.ParseSignedLongs(valueContext.AddressableData[valueContext.ValueOffset:], valueContext.UnitCount) - log.PanicIf(err) - } + value, err = tt.ParseSignedLongs(valueContext.AddressableData[valueContext.ValueOffset:], valueContext.UnitCount) + log.PanicIf(err) + } - return value, nil + return value, nil } func (tt TagType) ReadSignedRationalValues(valueContext ValueContext) (value []SignedRational, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - if tt.ValueIsEmbedded(valueContext.UnitCount) == true { - typeLogger.Debugf(nil, "Reading SRATIONAL value (embedded).") + if tt.ValueIsEmbedded(valueContext.UnitCount) == true { + typeLogger.Debugf(nil, "Reading SRATIONAL value (embedded).") - value, err = tt.ParseSignedRationals(valueContext.RawValueOffset, valueContext.UnitCount) - log.PanicIf(err) - } else { - typeLogger.Debugf(nil, "Reading SRATIONAL value (at offset).") + value, err = tt.ParseSignedRationals(valueContext.RawValueOffset, valueContext.UnitCount) + log.PanicIf(err) + } else { + typeLogger.Debugf(nil, "Reading SRATIONAL value (at offset).") - value, err = tt.ParseSignedRationals(valueContext.AddressableData[valueContext.ValueOffset:], valueContext.UnitCount) - log.PanicIf(err) - } + value, err = tt.ParseSignedRationals(valueContext.AddressableData[valueContext.ValueOffset:], valueContext.UnitCount) + log.PanicIf(err) + } - return value, nil + return value, nil } // ResolveAsString resolves the given value and returns a flat string. @@ -572,110 +569,115 @@ func (tt TagType) ReadSignedRationalValues(valueContext ValueContext) (value []S // byte-order, tag-ID, IFD type), it will return an error if attempted. See // `UndefinedValue()`. func (tt TagType) ResolveAsString(valueContext ValueContext, justFirst bool) (value string, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() -// TODO(dustin): Implement Resolve(), below. - // valueRaw, err := tt.Resolve(valueContext) - // log.PanicIf(err) + // TODO(dustin): Implement Resolve(), below. + // valueRaw, err := tt.Resolve(valueContext) + // log.PanicIf(err) - typeId := tt.Type() + switch typeId := tt.Type(); typeId { + case TypeByte: + raw, err := tt.ReadByteValues(valueContext) + log.PanicIf(err) - if typeId == TypeByte { - raw, err := tt.ReadByteValues(valueContext) - log.PanicIf(err) + switch { + case justFirst == false: + return DumpBytesToString(raw), nil + case valueContext.UnitCount > 0: + return fmt.Sprintf("0x%02x", raw[0]), nil + default: + return "", nil + } + case TypeAscii: + raw, err := tt.ReadAsciiValue(valueContext) + log.PanicIf(err) - if justFirst == false { - return DumpBytesToString(raw), nil - } else if valueContext.UnitCount > 0 { - return fmt.Sprintf("0x%02x", raw[0]), nil - } else { - return "", nil - } - } else if typeId == TypeAscii { - raw, err := tt.ReadAsciiValue(valueContext) - log.PanicIf(err) + return fmt.Sprintf("%s", raw), nil + case TypeAsciiNoNul: + raw, err := tt.ReadAsciiNoNulValue(valueContext) + log.PanicIf(err) - return fmt.Sprintf("%s", raw), nil - } else if typeId == TypeAsciiNoNul { - raw, err := tt.ReadAsciiNoNulValue(valueContext) - log.PanicIf(err) + return fmt.Sprintf("%s", raw), nil + case TypeShort: + raw, err := tt.ReadShortValues(valueContext) + log.PanicIf(err) - return fmt.Sprintf("%s", raw), nil - } else if typeId == TypeShort { - raw, err := tt.ReadShortValues(valueContext) - log.PanicIf(err) + switch { + case justFirst == false: + return fmt.Sprintf("%v", raw), nil + case valueContext.UnitCount > 0: + return fmt.Sprintf("%v", raw[0]), nil + default: + return "", nil + } + case TypeLong: + raw, err := tt.ReadLongValues(valueContext) + log.PanicIf(err) - if justFirst == false { - return fmt.Sprintf("%v", raw), nil - } else if valueContext.UnitCount > 0 { - return fmt.Sprintf("%v", raw[0]), nil - } else { - return "", nil - } - } else if typeId == TypeLong { - raw, err := tt.ReadLongValues(valueContext) - log.PanicIf(err) + switch { + case justFirst == false: + return fmt.Sprintf("%v", raw), nil + case valueContext.UnitCount > 0: + return fmt.Sprintf("%v", raw[0]), nil + default: + return "", nil + } + case TypeRational: + raw, err := tt.ReadRationalValues(valueContext) + log.PanicIf(err) - if justFirst == false { - return fmt.Sprintf("%v", raw), nil - } else if valueContext.UnitCount > 0 { - return fmt.Sprintf("%v", raw[0]), nil - } else { - return "", nil - } - } else if typeId == TypeRational { - raw, err := tt.ReadRationalValues(valueContext) - log.PanicIf(err) + parts := make([]string, len(raw)) + for i, r := range raw { + parts[i] = fmt.Sprintf("%d/%d", r.Numerator, r.Denominator) + } - parts := make([]string, len(raw)) - for i, r := range raw { - parts[i] = fmt.Sprintf("%d/%d", r.Numerator, r.Denominator) - } + switch { + case justFirst == false: + return fmt.Sprintf("%v", parts), nil + case valueContext.UnitCount > 0: + return parts[0], nil + default: + return "", nil + } + case TypeSignedLong: + raw, err := tt.ReadSignedLongValues(valueContext) + log.PanicIf(err) - if justFirst == false { - return fmt.Sprintf("%v", parts), nil - } else if valueContext.UnitCount > 0 { - return parts[0], nil - } else { - return "", nil - } - } else if typeId == TypeSignedLong { - raw, err := tt.ReadSignedLongValues(valueContext) - log.PanicIf(err) + switch { + case justFirst == false: + return fmt.Sprintf("%v", raw), nil + case valueContext.UnitCount > 0: + return fmt.Sprintf("%v", raw[0]), nil + default: + return "", nil + } + case TypeSignedRational: + raw, err := tt.ReadSignedRationalValues(valueContext) + log.PanicIf(err) - if justFirst == false { - return fmt.Sprintf("%v", raw), nil - } else if valueContext.UnitCount > 0 { - return fmt.Sprintf("%v", raw[0]), nil - } else { - return "", nil - } - } else if typeId == TypeSignedRational { - raw, err := tt.ReadSignedRationalValues(valueContext) - log.PanicIf(err) + parts := make([]string, len(raw)) + for i, r := range raw { + parts[i] = fmt.Sprintf("%d/%d", r.Numerator, r.Denominator) + } - parts := make([]string, len(raw)) - for i, r := range raw { - parts[i] = fmt.Sprintf("%d/%d", r.Numerator, r.Denominator) - } + switch { + case justFirst == false: + return fmt.Sprintf("%v", raw), nil + case valueContext.UnitCount > 0: + return parts[0], nil + default: + return "", nil + } + default: + log.Panicf("value of type (%d) [%s] is unparseable", typeId, tt) - if justFirst == false { - return fmt.Sprintf("%v", raw), nil - } else if valueContext.UnitCount > 0 { - return parts[0], nil - } else { - return "", nil - } - } else { - log.Panicf("value of type (%d) [%s] is unparseable", typeId, tt) - - // Never called. - return "", nil - } + // Never called. + return "", nil + } } // Value knows how to resolve the given value. @@ -684,132 +686,132 @@ func (tt TagType) ResolveAsString(valueContext ValueContext, justFirst bool) (va // byte-order, tag-ID, IFD type), it will return an error if attempted. See // `UndefinedValue()`. func (tt TagType) Resolve(valueContext ValueContext) (value interface{}, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - typeId := tt.Type() + switch typeId := tt.Type(); typeId { + case TypeByte: + value, err = tt.ReadByteValues(valueContext) + log.PanicIf(err) + case TypeAscii: + value, err = tt.ReadAsciiValue(valueContext) + log.PanicIf(err) + case TypeAsciiNoNul: + value, err = tt.ReadAsciiNoNulValue(valueContext) + log.PanicIf(err) + case TypeShort: + value, err = tt.ReadShortValues(valueContext) + log.PanicIf(err) + case TypeLong: + value, err = tt.ReadLongValues(valueContext) + log.PanicIf(err) + case TypeRational: + value, err = tt.ReadRationalValues(valueContext) + log.PanicIf(err) + case TypeSignedLong: + value, err = tt.ReadSignedLongValues(valueContext) + log.PanicIf(err) + case TypeSignedRational: + value, err = tt.ReadSignedRationalValues(valueContext) + log.PanicIf(err) + case TypeUndefined: + log.Panicf("will not parse unknown-type value: %v", tt) - if typeId == TypeByte { - value, err = tt.ReadByteValues(valueContext) - log.PanicIf(err) - } else if typeId == TypeAscii { - value, err = tt.ReadAsciiValue(valueContext) - log.PanicIf(err) - } else if typeId == TypeAsciiNoNul { - value, err = tt.ReadAsciiNoNulValue(valueContext) - log.PanicIf(err) - } else if typeId == TypeShort { - value, err = tt.ReadShortValues(valueContext) - log.PanicIf(err) - } else if typeId == TypeLong { - value, err = tt.ReadLongValues(valueContext) - log.PanicIf(err) - } else if typeId == TypeRational { - value, err = tt.ReadRationalValues(valueContext) - log.PanicIf(err) - } else if typeId == TypeSignedLong { - value, err = tt.ReadSignedLongValues(valueContext) - log.PanicIf(err) - } else if typeId == TypeSignedRational { - value, err = tt.ReadSignedRationalValues(valueContext) - log.PanicIf(err) - } else if typeId == TypeUndefined { - log.Panicf("will not parse unknown-type value: %v", tt) + // Never called. + return nil, nil + default: + log.Panicf("value of type (%d) [%s] is unparseable", typeId, tt) - // Never called. - return nil, nil - } else { - log.Panicf("value of type (%d) [%s] is unparseable", typeId, tt) + // Never called. + return nil, nil + } - // Never called. - return nil, nil - } - - return value, nil + return value, nil } // Encode knows how to encode the given value to a byte slice. func (tt TagType) Encode(value interface{}) (encoded []byte, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - ve := NewValueEncoder(tt.byteOrder) + ve := NewValueEncoder(tt.byteOrder) - ed, err := ve.EncodeWithType(tt, value) - log.PanicIf(err) + ed, err := ve.EncodeWithType(tt, value) + log.PanicIf(err) - return ed.Encoded, err + return ed.Encoded, err } func (tt TagType) FromString(valueString string) (value interface{}, err error) { - defer func() { - if state := recover(); state != nil { - err = log.Wrap(state.(error)) - } - }() + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() - if tt.tagType == TypeUndefined { -// TODO(dustin): Circle back to this. - log.Panicf("undefined-type values are not supported") - } + if tt.tagType == TypeUndefined { + // TODO(dustin): Circle back to this. + log.Panicf("undefined-type values are not supported") + } - if tt.tagType == TypeByte { - return []byte(valueString), nil - } else if tt.tagType == TypeAscii || tt.tagType == TypeAsciiNoNul { - // Whether or not we're putting an NUL on the end is only relevant for - // byte-level encoding. This function really just supports a user - // interface. + switch tt.tagType { + case TypeByte: + return []byte(valueString), nil + case TypeAscii, TypeAsciiNoNul: + // Whether or not we're putting an NUL on the end is only relevant for + // byte-level encoding. This function really just supports a user + // interface. - return valueString, nil - } else if tt.tagType == TypeShort { - n, err := strconv.ParseUint(valueString, 10, 16) - log.PanicIf(err) + return valueString, nil + case TypeShort: + n, err := strconv.ParseUint(valueString, 10, 16) + log.PanicIf(err) - return uint16(n), nil - } else if tt.tagType == TypeLong { - n, err := strconv.ParseUint(valueString, 10, 32) - log.PanicIf(err) + return uint16(n), nil + case TypeLong: + n, err := strconv.ParseUint(valueString, 10, 32) + log.PanicIf(err) - return uint32(n), nil - } else if tt.tagType == TypeRational { - parts := strings.SplitN(valueString, "/", 2) + return uint32(n), nil + case TypeRational: + parts := strings.SplitN(valueString, "/", 2) - numerator, err := strconv.ParseUint(parts[0], 10, 32) - log.PanicIf(err) + numerator, err := strconv.ParseUint(parts[0], 10, 32) + log.PanicIf(err) - denominator, err := strconv.ParseUint(parts[1], 10, 32) - log.PanicIf(err) + denominator, err := strconv.ParseUint(parts[1], 10, 32) + log.PanicIf(err) - return Rational{ - Numerator: uint32(numerator), - Denominator: uint32(denominator), - }, nil - } else if tt.tagType == TypeSignedLong { - n, err := strconv.ParseInt(valueString, 10, 32) - log.PanicIf(err) + return Rational{ + Numerator: uint32(numerator), + Denominator: uint32(denominator), + }, nil + case TypeSignedLong: + n, err := strconv.ParseInt(valueString, 10, 32) + log.PanicIf(err) - return int32(n), nil - } else if tt.tagType == TypeSignedRational { - parts := strings.SplitN(valueString, "/", 2) + return int32(n), nil + case TypeSignedRational: + parts := strings.SplitN(valueString, "/", 2) - numerator, err := strconv.ParseInt(parts[0], 10, 32) - log.PanicIf(err) + numerator, err := strconv.ParseInt(parts[0], 10, 32) + log.PanicIf(err) - denominator, err := strconv.ParseInt(parts[1], 10, 32) - log.PanicIf(err) + denominator, err := strconv.ParseInt(parts[1], 10, 32) + log.PanicIf(err) - return SignedRational{ - Numerator: int32(numerator), - Denominator: int32(denominator), - }, nil - } + return SignedRational{ + Numerator: int32(numerator), + Denominator: int32(denominator), + }, nil + } - log.Panicf("from-string encoding for type not supported; this shouldn't happen: (%d)", tt.Type()) - return nil, nil + log.Panicf("from-string encoding for type not supported; this shouldn't happen: (%d)", tt.Type()) + return nil, nil }