summaryrefslogtreecommitdiff
path: root/vendor/github.com/dsoprea/go-exif/v3/common/value_encoder.go
diff options
context:
space:
mode:
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.go273
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
+}