diff options
Diffstat (limited to 'vendor/github.com/dsoprea/go-exif/v3/common/value_encoder.go')
-rw-r--r-- | vendor/github.com/dsoprea/go-exif/v3/common/value_encoder.go | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/vendor/github.com/dsoprea/go-exif/v3/common/value_encoder.go b/vendor/github.com/dsoprea/go-exif/v3/common/value_encoder.go new file mode 100644 index 000000000..2cd26cc7b --- /dev/null +++ b/vendor/github.com/dsoprea/go-exif/v3/common/value_encoder.go @@ -0,0 +1,273 @@ +package exifcommon + +import ( + "bytes" + "math" + "reflect" + "time" + + "encoding/binary" + + "github.com/dsoprea/go-logging" +) + +var ( + typeEncodeLogger = log.NewLogger("exif.type_encode") +) + +// EncodedData encapsulates the compound output of an encoding operation. +type EncodedData struct { + Type TagTypePrimitive + Encoded []byte + + // TODO(dustin): Is this really necessary? We might have this just to correlate to the incoming stream format (raw bytes and a unit-count both for incoming and outgoing). + UnitCount uint32 +} + +// ValueEncoder knows how to encode values of every type to bytes. +type ValueEncoder struct { + byteOrder binary.ByteOrder +} + +// NewValueEncoder returns a new ValueEncoder. +func NewValueEncoder(byteOrder binary.ByteOrder) *ValueEncoder { + return &ValueEncoder{ + byteOrder: byteOrder, + } +} + +func (ve *ValueEncoder) encodeBytes(value []uint8) (ed EncodedData, err error) { + ed.Type = TypeByte + ed.Encoded = []byte(value) + ed.UnitCount = uint32(len(value)) + + return ed, nil +} + +func (ve *ValueEncoder) encodeAscii(value string) (ed EncodedData, err error) { + ed.Type = TypeAscii + + ed.Encoded = []byte(value) + ed.Encoded = append(ed.Encoded, 0) + + ed.UnitCount = uint32(len(ed.Encoded)) + + return ed, nil +} + +// encodeAsciiNoNul returns a string encoded as a byte-string without a trailing +// NUL byte. +// +// Note that: +// +// 1. This type can not be automatically encoded using `Encode()`. The default +// mode is to encode *with* a trailing NUL byte using `encodeAscii`. Only +// certain undefined-type tags using an unterminated ASCII string and these +// are exceptional in nature. +// +// 2. The presence of this method allows us to completely test the complimentary +// no-nul parser. +// +func (ve *ValueEncoder) encodeAsciiNoNul(value string) (ed EncodedData, err error) { + ed.Type = TypeAsciiNoNul + ed.Encoded = []byte(value) + ed.UnitCount = uint32(len(ed.Encoded)) + + return ed, nil +} + +func (ve *ValueEncoder) encodeShorts(value []uint16) (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*2) + + for i := uint32(0); i < ed.UnitCount; i++ { + ve.byteOrder.PutUint16(ed.Encoded[i*2:(i+1)*2], value[i]) + } + + ed.Type = TypeShort + + return ed, nil +} + +func (ve *ValueEncoder) encodeLongs(value []uint32) (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], value[i]) + } + + ed.Type = TypeLong + + 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 { + 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.PutUint32(ed.Encoded[i*8+0:i*8+4], value[i].Numerator) + ve.byteOrder.PutUint32(ed.Encoded[i*8+4:i*8+8], value[i].Denominator) + } + + ed.Type = TypeRational + + return ed, nil +} + +func (ve *ValueEncoder) encodeSignedLongs(value []int32) (ed EncodedData, err error) { + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() + + ed.UnitCount = uint32(len(value)) + + b := bytes.NewBuffer(make([]byte, 0, 8*ed.UnitCount)) + + for i := uint32(0); i < ed.UnitCount; i++ { + err := binary.Write(b, ve.byteOrder, value[i]) + log.PanicIf(err) + } + + ed.Type = TypeSignedLong + ed.Encoded = b.Bytes() + + return ed, nil +} + +func (ve *ValueEncoder) encodeSignedRationals(value []SignedRational) (ed EncodedData, err error) { + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() + + ed.UnitCount = uint32(len(value)) + + b := bytes.NewBuffer(make([]byte, 0, 8*ed.UnitCount)) + + for i := uint32(0); i < ed.UnitCount; i++ { + err := binary.Write(b, ve.byteOrder, value[i].Numerator) + log.PanicIf(err) + + err = binary.Write(b, ve.byteOrder, value[i].Denominator) + log.PanicIf(err) + } + + ed.Type = TypeSignedRational + ed.Encoded = b.Bytes() + + return ed, nil +} + +// Encode returns bytes for the given value, infering type from the actual +// value. This does not support `TypeAsciiNoNull` (all strings are encoded as +// `TypeAscii`). +func (ve *ValueEncoder) Encode(value interface{}) (ed EncodedData, err error) { + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() + + switch t := value.(type) { + case []byte: + ed, err = ve.encodeBytes(t) + log.PanicIf(err) + case string: + ed, err = ve.encodeAscii(t) + log.PanicIf(err) + case []uint16: + ed, err = ve.encodeShorts(t) + log.PanicIf(err) + case []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(t) + log.PanicIf(err) + case []int32: + ed, err = ve.encodeSignedLongs(t) + log.PanicIf(err) + case []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. + + s := ExifFullTimestampString(t) + + ed, err = ve.encodeAscii(s) + log.PanicIf(err) + default: + log.Panicf("value not encodable: [%s] [%v]", reflect.TypeOf(value), value) + } + + return ed, nil +} |