From 2a2ee8aaf17341a74696600bc3879c6e5bc19b9f Mon Sep 17 00:00:00 2001 From: WendelHime Date: Mon, 30 Nov 2020 18:05:55 -0300 Subject: [PATCH] adding float and double as types with name and size tests 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 size --- v3/common/parser.go | 45 +++++++++++ v3/common/parser_test.go | 73 ++++++++++++++++++ v3/common/type.go | 94 +++++++++++----------- v3/common/type_test.go | 69 +++++++++++++++++ v3/common/value_context.go | 34 ++++++++ v3/common/value_context_test.go | 67 ++++++++++++++++ v3/common/value_encoder.go | 62 ++++++++++++--- v3/common/value_encoder_test.go | 133 ++++++++++++++++++++++++++++++++ 8 files changed, 520 insertions(+), 57 deletions(-) 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)