220 lines
4.8 KiB
Go
220 lines
4.8 KiB
Go
|
package exifcommon
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
|
||
|
"encoding/binary"
|
||
|
|
||
|
log "github.com/dsoprea/go-logging"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
parserLogger = log.NewLogger("exifcommon.parser")
|
||
|
)
|
||
|
|
||
|
// Parser knows how to parse all well-defined, encoded EXIF types.
|
||
|
type Parser struct {
|
||
|
}
|
||
|
|
||
|
// ParseBytesknows how to parse a byte-type value.
|
||
|
func (p *Parser) ParseBytes(data []byte, unitCount uint32) (value []uint8, err error) {
|
||
|
defer func() {
|
||
|
if state := recover(); state != nil {
|
||
|
err = log.Wrap(state.(error))
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
// TODO(dustin): Add test
|
||
|
|
||
|
count := int(unitCount)
|
||
|
|
||
|
if len(data) < (TypeByte.Size() * count) {
|
||
|
log.Panic(ErrNotEnoughData)
|
||
|
}
|
||
|
|
||
|
value = []uint8(data[:count])
|
||
|
|
||
|
return value, nil
|
||
|
}
|
||
|
|
||
|
// ParseAscii returns a string and auto-strips the trailing NUL character that
|
||
|
// should be at the end of the encoding.
|
||
|
func (p *Parser) ParseAscii(data []byte, unitCount uint32) (value string, err error) {
|
||
|
defer func() {
|
||
|
if state := recover(); state != nil {
|
||
|
err = log.Wrap(state.(error))
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
// TODO(dustin): Add test
|
||
|
|
||
|
count := int(unitCount)
|
||
|
|
||
|
if len(data) < (TypeAscii.Size() * count) {
|
||
|
log.Panic(ErrNotEnoughData)
|
||
|
}
|
||
|
|
||
|
if len(data) == 0 || data[count-1] != 0 {
|
||
|
s := string(data[:count])
|
||
|
parserLogger.Warningf(nil, "ascii not terminated with nul as expected: [%v]", s)
|
||
|
|
||
|
return s, nil
|
||
|
}
|
||
|
|
||
|
// Auto-strip the NUL from the end. It serves no purpose outside of
|
||
|
// encoding semantics.
|
||
|
|
||
|
return string(data[:count-1]), nil
|
||
|
}
|
||
|
|
||
|
// ParseAsciiNoNul returns a string without any consideration for a trailing NUL
|
||
|
// character.
|
||
|
func (p *Parser) ParseAsciiNoNul(data []byte, unitCount uint32) (value string, err error) {
|
||
|
defer func() {
|
||
|
if state := recover(); state != nil {
|
||
|
err = log.Wrap(state.(error))
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
// TODO(dustin): Add test
|
||
|
|
||
|
count := int(unitCount)
|
||
|
|
||
|
if len(data) < (TypeAscii.Size() * count) {
|
||
|
log.Panic(ErrNotEnoughData)
|
||
|
}
|
||
|
|
||
|
return string(data[:count]), nil
|
||
|
}
|
||
|
|
||
|
// ParseShorts knows how to parse an encoded list of shorts.
|
||
|
func (p *Parser) ParseShorts(data []byte, unitCount uint32, byteOrder binary.ByteOrder) (value []uint16, err error) {
|
||
|
defer func() {
|
||
|
if state := recover(); state != nil {
|
||
|
err = log.Wrap(state.(error))
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
// TODO(dustin): Add test
|
||
|
|
||
|
count := int(unitCount)
|
||
|
|
||
|
if len(data) < (TypeShort.Size() * count) {
|
||
|
log.Panic(ErrNotEnoughData)
|
||
|
}
|
||
|
|
||
|
value = make([]uint16, count)
|
||
|
for i := 0; i < count; i++ {
|
||
|
value[i] = byteOrder.Uint16(data[i*2:])
|
||
|
}
|
||
|
|
||
|
return value, nil
|
||
|
}
|
||
|
|
||
|
// ParseLongs knows how to encode an encoded list of unsigned longs.
|
||
|
func (p *Parser) ParseLongs(data []byte, unitCount uint32, byteOrder binary.ByteOrder) (value []uint32, err error) {
|
||
|
defer func() {
|
||
|
if state := recover(); state != nil {
|
||
|
err = log.Wrap(state.(error))
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
// TODO(dustin): Add test
|
||
|
|
||
|
count := int(unitCount)
|
||
|
|
||
|
if len(data) < (TypeLong.Size() * count) {
|
||
|
log.Panic(ErrNotEnoughData)
|
||
|
}
|
||
|
|
||
|
value = make([]uint32, count)
|
||
|
for i := 0; i < count; i++ {
|
||
|
value[i] = byteOrder.Uint32(data[i*4:])
|
||
|
}
|
||
|
|
||
|
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() {
|
||
|
if state := recover(); state != nil {
|
||
|
err = log.Wrap(state.(error))
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
// TODO(dustin): Add test
|
||
|
|
||
|
count := int(unitCount)
|
||
|
|
||
|
if len(data) < (TypeRational.Size() * count) {
|
||
|
log.Panic(ErrNotEnoughData)
|
||
|
}
|
||
|
|
||
|
value = make([]Rational, count)
|
||
|
for i := 0; i < count; i++ {
|
||
|
value[i].Numerator = byteOrder.Uint32(data[i*8:])
|
||
|
value[i].Denominator = byteOrder.Uint32(data[i*8+4:])
|
||
|
}
|
||
|
|
||
|
return value, nil
|
||
|
}
|
||
|
|
||
|
// ParseSignedLongs knows how to parse an encoded list of signed longs.
|
||
|
func (p *Parser) ParseSignedLongs(data []byte, unitCount uint32, byteOrder binary.ByteOrder) (value []int32, err error) {
|
||
|
defer func() {
|
||
|
if state := recover(); state != nil {
|
||
|
err = log.Wrap(state.(error))
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
// TODO(dustin): Add test
|
||
|
|
||
|
count := int(unitCount)
|
||
|
|
||
|
if len(data) < (TypeSignedLong.Size() * count) {
|
||
|
log.Panic(ErrNotEnoughData)
|
||
|
}
|
||
|
|
||
|
b := bytes.NewBuffer(data)
|
||
|
|
||
|
value = make([]int32, count)
|
||
|
for i := 0; i < count; i++ {
|
||
|
err := binary.Read(b, byteOrder, &value[i])
|
||
|
log.PanicIf(err)
|
||
|
}
|
||
|
|
||
|
return value, nil
|
||
|
}
|
||
|
|
||
|
// ParseSignedRationals knows how to parse an encoded list of signed
|
||
|
// rationals.
|
||
|
func (p *Parser) ParseSignedRationals(data []byte, unitCount uint32, byteOrder binary.ByteOrder) (value []SignedRational, err error) {
|
||
|
defer func() {
|
||
|
if state := recover(); state != nil {
|
||
|
err = log.Wrap(state.(error))
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
// TODO(dustin): Add test
|
||
|
|
||
|
count := int(unitCount)
|
||
|
|
||
|
if len(data) < (TypeSignedRational.Size() * count) {
|
||
|
log.Panic(ErrNotEnoughData)
|
||
|
}
|
||
|
|
||
|
b := bytes.NewBuffer(data)
|
||
|
|
||
|
value = make([]SignedRational, count)
|
||
|
for i := 0; i < count; i++ {
|
||
|
err = binary.Read(b, byteOrder, &value[i].Numerator)
|
||
|
log.PanicIf(err)
|
||
|
|
||
|
err = binary.Read(b, byteOrder, &value[i].Denominator)
|
||
|
log.PanicIf(err)
|
||
|
}
|
||
|
|
||
|
return value, nil
|
||
|
}
|