mirror of https://github.com/dsoprea/go-exif.git
adding float and double as types with name and size tests (#51)
add translate to type cases replace float compare by Nextafter and Nextafter32 add parser for floats and doubles add float and double parsers add support for doubles and floats in FormatFrom functions add ReadFloats and ReadDoubles for context add float32 and double encoder merging merge add float and double parsers add ReadFloats and ReadDoubles for context add float32 and double encoder removing log alias from parser removing log alias from parser_test removing log alias from type removing log alias from type_test removing log alias from value_context removing aliases from value_contex_test removing log alias from value_encoder removing log alias from value_encoder_test update parser slices from floats and doubles merge update values for tests raise exception when parsing with different expected sizepull/52/head
parent
b3f4f3b4b7
commit
325de3c5bb
|
@ -2,6 +2,7 @@ package exifcommon
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
|
||||
"encoding/binary"
|
||||
|
||||
|
@ -135,6 +136,50 @@ func (p *Parser) ParseLongs(data []byte, unitCount uint32, byteOrder binary.Byte
|
|||
return value, nil
|
||||
}
|
||||
|
||||
// ParseFloats knows how to encode an encoded list of floats.
|
||||
func (p *Parser) ParseFloats(data []byte, unitCount uint32, byteOrder binary.ByteOrder) (value []float32, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
count := int(unitCount)
|
||||
|
||||
if len(data) != (TypeFloat.Size() * count) {
|
||||
log.Panic(ErrNotEnoughData)
|
||||
}
|
||||
|
||||
value = make([]float32, count)
|
||||
for i := 0; i < count; i++ {
|
||||
value[i] = math.Float32frombits(byteOrder.Uint32(data[i*4 : (i+1)*4]))
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// ParseDoubles knows how to encode an encoded list of doubles.
|
||||
func (p *Parser) ParseDoubles(data []byte, unitCount uint32, byteOrder binary.ByteOrder) (value []float64, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
count := int(unitCount)
|
||||
|
||||
if len(data) != (TypeDouble.Size() * count) {
|
||||
log.Panic(ErrNotEnoughData)
|
||||
}
|
||||
|
||||
value = make([]float64, count)
|
||||
for i := 0; i < count; i++ {
|
||||
value[i] = math.Float64frombits(byteOrder.Uint64(data[i*8 : (i+1)*8]))
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// ParseRationals knows how to parse an encoded list of unsigned rationals.
|
||||
func (p *Parser) ParseRationals(data []byte, unitCount uint32, byteOrder binary.ByteOrder) (value []Rational, err error) {
|
||||
defer func() {
|
||||
|
|
|
@ -2,6 +2,7 @@ package exifcommon
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
|
@ -170,6 +171,78 @@ func TestParser_ParseLongs__Multiple(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestParser_ParseFloats__Single(t *testing.T) {
|
||||
p := new(Parser)
|
||||
|
||||
encoded := []byte{0x40, 0x49, 0x0f, 0xdb}
|
||||
|
||||
value, err := p.ParseFloats(encoded, 1, TestDefaultByteOrder)
|
||||
log.PanicIf(err)
|
||||
|
||||
expectedResult := []float32{3.14159265}
|
||||
|
||||
for i, v := range value {
|
||||
if v < expectedResult[i] ||
|
||||
v >= math.Nextafter32(expectedResult[i], expectedResult[i]+1) {
|
||||
t.Fatalf("Encoding not correct (1): %v", value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParser_ParseFloats__Multiple(t *testing.T) {
|
||||
p := new(Parser)
|
||||
|
||||
encoded := []byte{0x40, 0x49, 0x0f, 0xdb, 0x40, 0x2d, 0xf8, 0x54}
|
||||
|
||||
value, err := p.ParseFloats(encoded, 2, TestDefaultByteOrder)
|
||||
log.PanicIf(err)
|
||||
|
||||
expectedResult := []float32{3.14159265, 2.71828182}
|
||||
|
||||
for i, v := range value {
|
||||
if v < expectedResult[i] ||
|
||||
v >= math.Nextafter32(expectedResult[i], expectedResult[i]+1) {
|
||||
t.Fatalf("Encoding not correct (1): %v", value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParser_ParseDoubles__Single(t *testing.T) {
|
||||
p := new(Parser)
|
||||
|
||||
encoded := []byte{0x40, 0x09, 0x21, 0xfb, 0x53, 0xc8, 0xd4, 0xf1}
|
||||
|
||||
value, err := p.ParseDoubles(encoded, 1, TestDefaultByteOrder)
|
||||
log.PanicIf(err)
|
||||
|
||||
expectedResult := []float64{3.14159265}
|
||||
for i, v := range value {
|
||||
if v < expectedResult[i] ||
|
||||
v >= math.Nextafter(expectedResult[i], expectedResult[i]+1) {
|
||||
t.Fatalf("Encoding not correct (1): %v", value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParser_ParseDoubles__Multiple(t *testing.T) {
|
||||
p := new(Parser)
|
||||
|
||||
encoded := []byte{0x40, 0x09, 0x21, 0xfb, 0x53, 0xc8, 0xd4, 0xf1,
|
||||
0x40, 0x05, 0xbf, 0x0a, 0x89, 0xf1, 0xb0, 0xdd}
|
||||
|
||||
value, err := p.ParseDoubles(encoded, 2, TestDefaultByteOrder)
|
||||
log.PanicIf(err)
|
||||
|
||||
expectedResult := []float64{3.14159265, 2.71828182}
|
||||
|
||||
for i, v := range value {
|
||||
if v < expectedResult[i] ||
|
||||
v >= math.Nextafter(expectedResult[i], expectedResult[i]+1) {
|
||||
t.Fatalf("Encoding not correct: %v", value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParser_ParseRationals__Single(t *testing.T) {
|
||||
p := new(Parser)
|
||||
|
||||
|
|
|
@ -64,6 +64,12 @@ const (
|
|||
// TypeSignedRational describes an encoded list of signed rationals.
|
||||
TypeSignedRational TagTypePrimitive = 10
|
||||
|
||||
// TypeFloat describes an encoded list of floats
|
||||
TypeFloat TagTypePrimitive = 11
|
||||
|
||||
// TypeDouble describes an encoded list of doubles.
|
||||
TypeDouble TagTypePrimitive = 12
|
||||
|
||||
// TypeAsciiNoNul is just a pseudo-type, for our own purposes.
|
||||
TypeAsciiNoNul TagTypePrimitive = 0xf0
|
||||
)
|
||||
|
@ -75,23 +81,19 @@ func (typeType TagTypePrimitive) String() string {
|
|||
|
||||
// Size returns the size of one atomic unit of the type.
|
||||
func (tagType TagTypePrimitive) Size() int {
|
||||
if tagType == TypeByte {
|
||||
switch tagType {
|
||||
case TypeByte, TypeAscii, TypeAsciiNoNul:
|
||||
return 1
|
||||
} else if tagType == TypeAscii || tagType == TypeAsciiNoNul {
|
||||
return 1
|
||||
} else if tagType == TypeShort {
|
||||
case TypeShort:
|
||||
return 2
|
||||
} else if tagType == TypeLong {
|
||||
case TypeLong, TypeSignedLong, TypeFloat:
|
||||
return 4
|
||||
} else if tagType == TypeRational {
|
||||
case TypeRational, TypeSignedRational, TypeDouble:
|
||||
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])
|
||||
|
||||
default:
|
||||
log.Panicf("can not determine tag-value size for type (%d): [%s]",
|
||||
tagType,
|
||||
TypeNames[tagType])
|
||||
// Never called.
|
||||
return 0
|
||||
}
|
||||
|
@ -110,6 +112,8 @@ func (tagType TagTypePrimitive) IsValid() bool {
|
|||
tagType == TypeRational ||
|
||||
tagType == TypeSignedLong ||
|
||||
tagType == TypeSignedRational ||
|
||||
tagType == TypeFloat ||
|
||||
tagType == TypeDouble ||
|
||||
tagType == TypeUndefined
|
||||
}
|
||||
|
||||
|
@ -124,6 +128,8 @@ var (
|
|||
TypeUndefined: "UNDEFINED",
|
||||
TypeSignedLong: "SLONG",
|
||||
TypeSignedRational: "SRATIONAL",
|
||||
TypeFloat: "FLOAT",
|
||||
TypeDouble: "DOUBLE",
|
||||
|
||||
TypeAsciiNoNul: "_ASCII_NO_NUL",
|
||||
}
|
||||
|
@ -186,36 +192,23 @@ func FormatFromType(value interface{}, justFirst bool) (phrase string, err error
|
|||
}
|
||||
|
||||
return t, nil
|
||||
case []uint16:
|
||||
if len(t) == 0 {
|
||||
case []uint16, []uint32, []int32, []float64, []float32:
|
||||
val := reflect.ValueOf(t)
|
||||
|
||||
if val.Len() == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if justFirst == true {
|
||||
var valueSuffix string
|
||||
if len(t) > 1 {
|
||||
if val.Len() > 1 {
|
||||
valueSuffix = "..."
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v%s", t[0], valueSuffix), nil
|
||||
return fmt.Sprintf("%v%s", val.Index(0), valueSuffix), nil
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v", t), nil
|
||||
case []uint32:
|
||||
if len(t) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if justFirst == true {
|
||||
var valueSuffix string
|
||||
if len(t) > 1 {
|
||||
valueSuffix = "..."
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v%s", t[0], valueSuffix), nil
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v", t), nil
|
||||
return fmt.Sprintf("%v", val), nil
|
||||
case []Rational:
|
||||
if len(t) == 0 {
|
||||
return "", nil
|
||||
|
@ -240,21 +233,6 @@ func FormatFromType(value interface{}, justFirst bool) (phrase string, err error
|
|||
}
|
||||
|
||||
return fmt.Sprintf("%v", parts), nil
|
||||
case []int32:
|
||||
if len(t) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if justFirst == true {
|
||||
var valueSuffix string
|
||||
if len(t) > 1 {
|
||||
valueSuffix = "..."
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v%s", t[0], valueSuffix), nil
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v", t), nil
|
||||
case []SignedRational:
|
||||
if len(t) == 0 {
|
||||
return "", nil
|
||||
|
@ -348,6 +326,16 @@ func FormatFromBytes(rawBytes []byte, tagType TagTypePrimitive, justFirst bool,
|
|||
|
||||
value, err = parser.ParseLongs(rawBytes, unitCount, byteOrder)
|
||||
log.PanicIf(err)
|
||||
case TypeFloat:
|
||||
var err error
|
||||
|
||||
value, err = parser.ParseFloats(rawBytes, unitCount, byteOrder)
|
||||
log.PanicIf(err)
|
||||
case TypeDouble:
|
||||
var err error
|
||||
|
||||
value, err = parser.ParseDoubles(rawBytes, unitCount, byteOrder)
|
||||
log.PanicIf(err)
|
||||
case TypeRational:
|
||||
var err error
|
||||
|
||||
|
@ -432,6 +420,16 @@ func TranslateStringToType(tagType TagTypePrimitive, valueString string) (value
|
|||
log.PanicIf(err)
|
||||
|
||||
return int32(n), nil
|
||||
} else if tagType == TypeFloat {
|
||||
n, err := strconv.ParseFloat(valueString, 32)
|
||||
log.PanicIf(err)
|
||||
|
||||
return float32(n), nil
|
||||
} else if tagType == TypeDouble {
|
||||
n, err := strconv.ParseFloat(valueString, 64)
|
||||
log.PanicIf(err)
|
||||
|
||||
return float64(n), nil
|
||||
} else if tagType == TypeSignedRational {
|
||||
parts := strings.SplitN(valueString, "/", 2)
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package exifcommon
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/dsoprea/go-logging"
|
||||
|
@ -54,6 +55,18 @@ func TestTypeSignedRational_String(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestTypeFloat_String(t *testing.T) {
|
||||
if TypeFloat.String() != "FLOAT" {
|
||||
t.Fatalf("Type name not correct (float): [%s]", TypeFloat.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestTypeDouble_String(t *testing.T) {
|
||||
if TypeDouble.String() != "DOUBLE" {
|
||||
t.Fatalf("Type name not correct (double): [%s]", TypeDouble.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestTypeByte_Size(t *testing.T) {
|
||||
if TypeByte.Size() != 1 {
|
||||
t.Fatalf("Type size not correct (byte): (%d)", TypeByte.Size())
|
||||
|
@ -102,6 +115,18 @@ func TestTypeSignedRational_Size(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestTypeFloat_Size(t *testing.T) {
|
||||
if TypeFloat.Size() != 4 {
|
||||
t.Fatalf("Type size not correct (float): (%d)", TypeFloat.Size())
|
||||
}
|
||||
}
|
||||
|
||||
func TestTypeDouble_Size(t *testing.T) {
|
||||
if TypeDouble.Size() != 8 {
|
||||
t.Fatalf("Type size not correct (double): (%d)", TypeDouble.Size())
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormat__Byte(t *testing.T) {
|
||||
r := []byte{1, 2, 3, 4, 5, 6, 7, 8}
|
||||
|
||||
|
@ -157,6 +182,30 @@ func TestFormat__Long(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestFormat__Float(t *testing.T) {
|
||||
r := []byte{0x3f, 0x80, 0x00, 0x00,
|
||||
0x40, 0x00, 0x00, 0x00}
|
||||
|
||||
s, err := FormatFromBytes(r, TypeFloat, false, TestDefaultByteOrder)
|
||||
log.PanicIf(err)
|
||||
|
||||
if s != "[1 2]" {
|
||||
t.Fatalf("Format output not correct (floats): [%s]", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormat__Double(t *testing.T) {
|
||||
r := []byte{0x40, 0x09, 0x21, 0xfb, 0x53, 0xc8, 0xd4, 0xf1,
|
||||
0x40, 0x05, 0xbf, 0x0a, 0x89, 0xf1, 0xb0, 0xdd}
|
||||
|
||||
s, err := FormatFromBytes(r, TypeDouble, false, TestDefaultByteOrder)
|
||||
log.PanicIf(err)
|
||||
|
||||
if s != "[3.14159265 2.71828182]" {
|
||||
t.Fatalf("Format output not correct (doubles): [%s]", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormat__Rational(t *testing.T) {
|
||||
r := []byte{
|
||||
0, 0, 0, 1, 0, 0, 0, 2,
|
||||
|
@ -261,6 +310,26 @@ func TestTranslateStringToType__TypeLong(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestTranslateStringToType__TypeFloat(t *testing.T) {
|
||||
v, err := TranslateStringToType(TypeFloat, "3.14159265")
|
||||
log.PanicIf(err)
|
||||
|
||||
expected := float32(3.14159265)
|
||||
if v.(float32) < expected || v.(float32) >= math.Nextafter32(expected, expected+1) {
|
||||
t.Fatalf("Translation of string to type not correct (float32): %v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTranslateStringToType__TypeDouble(t *testing.T) {
|
||||
v, err := TranslateStringToType(TypeDouble, "3.14159265")
|
||||
log.PanicIf(err)
|
||||
|
||||
expected := float64(3.14159265)
|
||||
if v.(float64) < expected || v.(float64) >= math.Nextafter(expected, expected+1) {
|
||||
t.Fatalf("Translation of string to type not correct (double): %v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTranslateStringToType__TypeRational(t *testing.T) {
|
||||
v, err := TranslateStringToType(TypeRational, "11/22")
|
||||
log.PanicIf(err)
|
||||
|
|
|
@ -315,6 +315,40 @@ func (vc *ValueContext) ReadLongs() (value []uint32, err error) {
|
|||
return value, nil
|
||||
}
|
||||
|
||||
// ReadFloats parses the list of encoded, floats from the value-context.
|
||||
func (vc *ValueContext) ReadFloats() (value []float32, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
rawValue, err := vc.readRawEncoded()
|
||||
log.PanicIf(err)
|
||||
|
||||
value, err = parser.ParseFloats(rawValue, vc.unitCount, vc.byteOrder)
|
||||
log.PanicIf(err)
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// ReadDoubles parses the list of encoded, doubles from the value-context.
|
||||
func (vc *ValueContext) ReadDoubles() (value []float64, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
rawValue, err := vc.readRawEncoded()
|
||||
log.PanicIf(err)
|
||||
|
||||
value, err = parser.ParseDoubles(rawValue, vc.unitCount, vc.byteOrder)
|
||||
log.PanicIf(err)
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// ReadRationals parses the list of encoded, unsigned rationals from the value-
|
||||
// context.
|
||||
func (vc *ValueContext) ReadRationals() (value []Rational, err error) {
|
||||
|
|
|
@ -2,6 +2,7 @@ package exifcommon
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
|
@ -950,6 +951,72 @@ func TestValueContext_ReadLongs(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestValueContext_ReadFloats(t *testing.T) {
|
||||
unitCount := uint32(2)
|
||||
|
||||
rawValueOffset := []byte{0, 0, 0, 4}
|
||||
valueOffset := uint32(4)
|
||||
|
||||
data := []byte{0x40, 0x49, 0x0f, 0xdb, 0x40, 0x2d, 0xf8, 0x54}
|
||||
|
||||
addressableData := []byte{0, 0, 0, 0}
|
||||
addressableData = append(addressableData, data...)
|
||||
sb := rifs.NewSeekableBufferWithBytes(addressableData)
|
||||
|
||||
vc := NewValueContext(
|
||||
"aa/bb",
|
||||
0x1234,
|
||||
unitCount,
|
||||
valueOffset,
|
||||
rawValueOffset,
|
||||
sb,
|
||||
TypeFloat,
|
||||
TestDefaultByteOrder)
|
||||
|
||||
value, err := vc.ReadFloats()
|
||||
log.PanicIf(err)
|
||||
|
||||
expectedResult := []float32{3.14159265, 2.71828182}
|
||||
for i, v := range value {
|
||||
if v < expectedResult[i] || v >= math.Nextafter32(expectedResult[i], expectedResult[i]+1) {
|
||||
t.Fatalf("ReadFloats expecting %v, received %v", expectedResult[i], v)
|
||||
}
|
||||
}
|
||||
}
|
||||
func TestValueContext_ReadDoubles(t *testing.T) {
|
||||
unitCount := uint32(2)
|
||||
|
||||
rawValueOffset := []byte{0, 0, 0, 4}
|
||||
valueOffset := uint32(4)
|
||||
|
||||
data := []byte{0x40, 0x09, 0x21, 0xfb, 0x53, 0xc8, 0xd4, 0xf1,
|
||||
0x40, 0x05, 0xbf, 0x0a, 0x89, 0xf1, 0xb0, 0xdd}
|
||||
|
||||
addressableData := []byte{0, 0, 0, 0}
|
||||
addressableData = append(addressableData, data...)
|
||||
sb := rifs.NewSeekableBufferWithBytes(addressableData)
|
||||
|
||||
vc := NewValueContext(
|
||||
"aa/bb",
|
||||
0x1234,
|
||||
unitCount,
|
||||
valueOffset,
|
||||
rawValueOffset,
|
||||
sb,
|
||||
TypeDouble,
|
||||
TestDefaultByteOrder)
|
||||
|
||||
value, err := vc.ReadDoubles()
|
||||
log.PanicIf(err)
|
||||
|
||||
expectedResult := []float64{3.14159265, 2.71828182}
|
||||
for i, v := range value {
|
||||
if v < expectedResult[i] || v >= math.Nextafter(expectedResult[i], expectedResult[i]+1) {
|
||||
t.Fatalf("ReadDoubles expecting %v, received %v", expectedResult[i], v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValueContext_ReadRationals(t *testing.T) {
|
||||
unitCount := uint32(2)
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package exifcommon
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
|
@ -113,6 +114,44 @@ func (ve *ValueEncoder) encodeLongs(value []uint32) (ed EncodedData, err error)
|
|||
return ed, nil
|
||||
}
|
||||
|
||||
func (ve *ValueEncoder) encodeFloats(value []float32) (ed EncodedData, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
ed.UnitCount = uint32(len(value))
|
||||
ed.Encoded = make([]byte, ed.UnitCount*4)
|
||||
|
||||
for i := uint32(0); i < ed.UnitCount; i++ {
|
||||
ve.byteOrder.PutUint32(ed.Encoded[i*4:(i+1)*4], math.Float32bits(value[i]))
|
||||
}
|
||||
|
||||
ed.Type = TypeFloat
|
||||
|
||||
return ed, nil
|
||||
}
|
||||
|
||||
func (ve *ValueEncoder) encodeDoubles(value []float64) (ed EncodedData, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
err = log.Wrap(state.(error))
|
||||
}
|
||||
}()
|
||||
|
||||
ed.UnitCount = uint32(len(value))
|
||||
ed.Encoded = make([]byte, ed.UnitCount*8)
|
||||
|
||||
for i := uint32(0); i < ed.UnitCount; i++ {
|
||||
ve.byteOrder.PutUint64(ed.Encoded[i*8:(i+1)*8], math.Float64bits(value[i]))
|
||||
}
|
||||
|
||||
ed.Type = TypeDouble
|
||||
|
||||
return ed, nil
|
||||
}
|
||||
|
||||
func (ve *ValueEncoder) encodeRationals(value []Rational) (ed EncodedData, err error) {
|
||||
defer func() {
|
||||
if state := recover(); state != nil {
|
||||
|
@ -190,33 +229,38 @@ func (ve *ValueEncoder) Encode(value interface{}) (ed EncodedData, err error) {
|
|||
}
|
||||
}()
|
||||
|
||||
switch value.(type) {
|
||||
switch t := value.(type) {
|
||||
case []byte:
|
||||
ed, err = ve.encodeBytes(value.([]byte))
|
||||
ed, err = ve.encodeBytes(t)
|
||||
log.PanicIf(err)
|
||||
case string:
|
||||
ed, err = ve.encodeAscii(value.(string))
|
||||
ed, err = ve.encodeAscii(t)
|
||||
log.PanicIf(err)
|
||||
case []uint16:
|
||||
ed, err = ve.encodeShorts(value.([]uint16))
|
||||
ed, err = ve.encodeShorts(t)
|
||||
log.PanicIf(err)
|
||||
case []uint32:
|
||||
ed, err = ve.encodeLongs(value.([]uint32))
|
||||
ed, err = ve.encodeLongs(t)
|
||||
log.PanicIf(err)
|
||||
case []float32:
|
||||
ed, err = ve.encodeFloats(t)
|
||||
log.PanicIf(err)
|
||||
case []float64:
|
||||
ed, err = ve.encodeDoubles(t)
|
||||
log.PanicIf(err)
|
||||
case []Rational:
|
||||
ed, err = ve.encodeRationals(value.([]Rational))
|
||||
ed, err = ve.encodeRationals(t)
|
||||
log.PanicIf(err)
|
||||
case []int32:
|
||||
ed, err = ve.encodeSignedLongs(value.([]int32))
|
||||
ed, err = ve.encodeSignedLongs(t)
|
||||
log.PanicIf(err)
|
||||
case []SignedRational:
|
||||
ed, err = ve.encodeSignedRationals(value.([]SignedRational))
|
||||
ed, err = ve.encodeSignedRationals(t)
|
||||
log.PanicIf(err)
|
||||
case time.Time:
|
||||
// For convenience, if the user doesn't want to deal with translation
|
||||
// semantics with timestamps.
|
||||
|
||||
t := value.(time.Time)
|
||||
s := ExifFullTimestampString(t)
|
||||
|
||||
ed, err = ve.encodeAscii(s)
|
||||
|
|
|
@ -2,6 +2,7 @@ package exifcommon
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -173,6 +174,80 @@ func TestValueEncoder_encodeLongs__Cycle(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestValueEncoder_encodeFloats__Cycle(t *testing.T) {
|
||||
byteOrder := TestDefaultByteOrder
|
||||
ve := NewValueEncoder(byteOrder)
|
||||
|
||||
original := []float32{3.14159265, 2.71828182, 51.0, 68.0, 85.0}
|
||||
|
||||
ed, err := ve.encodeFloats(original)
|
||||
log.PanicIf(err)
|
||||
|
||||
if ed.Type != TypeFloat {
|
||||
t.Fatalf("IFD type not expected.")
|
||||
}
|
||||
|
||||
expected := []byte{
|
||||
0x40, 0x49, 0x0f, 0xdb,
|
||||
0x40, 0x2d, 0xf8, 0x54,
|
||||
0x42, 0x4c, 0x00, 0x00,
|
||||
0x42, 0x88, 0x00, 0x00,
|
||||
0x42, 0xaa, 0x00, 0x00,
|
||||
}
|
||||
|
||||
if bytes.Equal(ed.Encoded, expected) != true {
|
||||
t.Fatalf("Data not encoded correctly.")
|
||||
} else if ed.UnitCount != 5 {
|
||||
t.Fatalf("Unit-count not correct.")
|
||||
}
|
||||
|
||||
recovered, err := parser.ParseFloats(ed.Encoded, ed.UnitCount, byteOrder)
|
||||
log.PanicIf(err)
|
||||
|
||||
for i, v := range recovered {
|
||||
if v < original[i] || v >= math.Nextafter32(original[i], original[i]+1) {
|
||||
t.Fatalf("ReadFloats expecting %v, received %v", original[i], v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValueEncoder_encodeDoubles__Cycle(t *testing.T) {
|
||||
byteOrder := TestDefaultByteOrder
|
||||
ve := NewValueEncoder(byteOrder)
|
||||
|
||||
original := []float64{3.14159265, 2.71828182, 954877.1230695, 68.0, 85.0}
|
||||
|
||||
ed, err := ve.encodeDoubles(original)
|
||||
log.PanicIf(err)
|
||||
|
||||
if ed.Type != TypeDouble {
|
||||
t.Fatalf("IFD type not expected.")
|
||||
}
|
||||
|
||||
expected := []byte{
|
||||
0x40, 0x09, 0x21, 0xfb, 0x53, 0xc8, 0xd4, 0xf1,
|
||||
0x40, 0x05, 0xbf, 0x0a, 0x89, 0xf1, 0xb0, 0xdd,
|
||||
0x41, 0x2d, 0x23, 0xfa, 0x3f, 0x02, 0xf7, 0x2b,
|
||||
0x40, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x40, 0x55, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(ed.Encoded, expected) != true {
|
||||
t.Fatalf("Data not encoded correctly.")
|
||||
} else if ed.UnitCount != 5 {
|
||||
t.Fatalf("Unit-count not correct.")
|
||||
}
|
||||
|
||||
recovered, err := parser.ParseDoubles(ed.Encoded, ed.UnitCount, byteOrder)
|
||||
log.PanicIf(err)
|
||||
|
||||
for i, v := range recovered {
|
||||
if v < original[i] || v >= math.Nextafter(original[i], original[i]+1) {
|
||||
t.Fatalf("ReadDoubles expecting %v, received %v", original[i], v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValueEncoder_encodeRationals__Cycle(t *testing.T) {
|
||||
byteOrder := TestDefaultByteOrder
|
||||
ve := NewValueEncoder(byteOrder)
|
||||
|
@ -431,6 +506,64 @@ func TestValueEncoder_Encode__Long(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestValueEncoder_Encode__Float(t *testing.T) {
|
||||
byteOrder := TestDefaultByteOrder
|
||||
ve := NewValueEncoder(byteOrder)
|
||||
|
||||
original := []float32{3.14159265, 2.71828182, 51.0, 68.0, 85.0}
|
||||
|
||||
ed, err := ve.Encode(original)
|
||||
log.PanicIf(err)
|
||||
|
||||
if ed.Type != TypeFloat {
|
||||
t.Fatalf("IFD type not expected.")
|
||||
}
|
||||
|
||||
expected := []byte{
|
||||
0x40, 0x49, 0x0f, 0xdb,
|
||||
0x40, 0x2d, 0xf8, 0x54,
|
||||
0x42, 0x4c, 0x00, 0x00,
|
||||
0x42, 0x88, 0x00, 0x00,
|
||||
0x42, 0xaa, 0x00, 0x00,
|
||||
}
|
||||
|
||||
if bytes.Equal(ed.Encoded, expected) != true {
|
||||
t.Fatalf("Data not encoded correctly.")
|
||||
} else if ed.UnitCount != 5 {
|
||||
t.Fatalf("Unit-count not correct.")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestValueEncoder_Encode__Doubles(t *testing.T) {
|
||||
byteOrder := TestDefaultByteOrder
|
||||
ve := NewValueEncoder(byteOrder)
|
||||
|
||||
original := []float64{3.14159265, 2.71828182, 954877.1230695, 68.0, 85.0}
|
||||
|
||||
ed, err := ve.Encode(original)
|
||||
log.PanicIf(err)
|
||||
|
||||
if ed.Type != TypeDouble {
|
||||
t.Fatalf("IFD type not expected.")
|
||||
}
|
||||
|
||||
expected := []byte{
|
||||
0x40, 0x09, 0x21, 0xfb, 0x53, 0xc8, 0xd4, 0xf1,
|
||||
0x40, 0x05, 0xbf, 0x0a, 0x89, 0xf1, 0xb0, 0xdd,
|
||||
0x41, 0x2d, 0x23, 0xfa, 0x3f, 0x02, 0xf7, 0x2b,
|
||||
0x40, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x40, 0x55, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
if bytes.Equal(ed.Encoded, expected) != true {
|
||||
t.Fatalf("Data not encoded correctly.")
|
||||
} else if ed.UnitCount != 5 {
|
||||
t.Fatalf("Unit-count not correct.")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestValueEncoder_Encode__Rational(t *testing.T) {
|
||||
byteOrder := TestDefaultByteOrder
|
||||
ve := NewValueEncoder(byteOrder)
|
||||
|
|
Loading…
Reference in New Issue