diff --git a/v3/common/parser.go b/v3/common/parser.go index bbdd8f5..b6f57fd 100644 --- a/v3/common/parser.go +++ b/v3/common/parser.go @@ -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() { diff --git a/v3/common/parser_test.go b/v3/common/parser_test.go index b4ddc07..293c5db 100644 --- a/v3/common/parser_test.go +++ b/v3/common/parser_test.go @@ -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) diff --git a/v3/common/type.go b/v3/common/type.go index 4a64bab..5d6cfff 100644 --- a/v3/common/type.go +++ b/v3/common/type.go @@ -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) diff --git a/v3/common/type_test.go b/v3/common/type_test.go index 512d7b2..b3953d1 100644 --- a/v3/common/type_test.go +++ b/v3/common/type_test.go @@ -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) diff --git a/v3/common/value_context.go b/v3/common/value_context.go index 712438a..9b3974b 100644 --- a/v3/common/value_context.go +++ b/v3/common/value_context.go @@ -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) { diff --git a/v3/common/value_context_test.go b/v3/common/value_context_test.go index 313df1c..8b4e93f 100644 --- a/v3/common/value_context_test.go +++ b/v3/common/value_context_test.go @@ -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) diff --git a/v3/common/value_encoder.go b/v3/common/value_encoder.go index 52e0eac..2cd26cc 100644 --- a/v3/common/value_encoder.go +++ b/v3/common/value_encoder.go @@ -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) diff --git a/v3/common/value_encoder_test.go b/v3/common/value_encoder_test.go index aa26138..8f3d663 100644 --- a/v3/common/value_encoder_test.go +++ b/v3/common/value_encoder_test.go @@ -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)