mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-04 00:42:24 -06:00 
			
		
		
		
	
		
			
	
	
		
			438 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			438 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 
								 | 
							
								package jpegstructure
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import (
							 | 
						||
| 
								 | 
							
									"bufio"
							 | 
						||
| 
								 | 
							
									"bytes"
							 | 
						||
| 
								 | 
							
									"io"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									"encoding/binary"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									"github.com/dsoprea/go-logging"
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// JpegSplitter uses the Go stream splitter to divide the JPEG stream into
							 | 
						||
| 
								 | 
							
								// segments.
							 | 
						||
| 
								 | 
							
								type JpegSplitter struct {
							 | 
						||
| 
								 | 
							
									lastMarkerId   byte
							 | 
						||
| 
								 | 
							
									lastMarkerName string
							 | 
						||
| 
								 | 
							
									counter        int
							 | 
						||
| 
								 | 
							
									lastIsScanData bool
							 | 
						||
| 
								 | 
							
									visitor        interface{}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									currentOffset int
							 | 
						||
| 
								 | 
							
									segments      *SegmentList
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									scandataOffset int
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// NewJpegSplitter returns a new JpegSplitter.
							 | 
						||
| 
								 | 
							
								func NewJpegSplitter(visitor interface{}) *JpegSplitter {
							 | 
						||
| 
								 | 
							
									return &JpegSplitter{
							 | 
						||
| 
								 | 
							
										segments: NewSegmentList(nil),
							 | 
						||
| 
								 | 
							
										visitor:  visitor,
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Segments returns all found segments.
							 | 
						||
| 
								 | 
							
								func (js *JpegSplitter) Segments() *SegmentList {
							 | 
						||
| 
								 | 
							
									return js.segments
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// MarkerId returns the ID of the last processed marker.
							 | 
						||
| 
								 | 
							
								func (js *JpegSplitter) MarkerId() byte {
							 | 
						||
| 
								 | 
							
									return js.lastMarkerId
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// MarkerName returns the name of the last-processed marker.
							 | 
						||
| 
								 | 
							
								func (js *JpegSplitter) MarkerName() string {
							 | 
						||
| 
								 | 
							
									return js.lastMarkerName
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Counter returns the number of processed segments.
							 | 
						||
| 
								 | 
							
								func (js *JpegSplitter) Counter() int {
							 | 
						||
| 
								 | 
							
									return js.counter
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// IsScanData returns whether the last processed segment was scan-data.
							 | 
						||
| 
								 | 
							
								func (js *JpegSplitter) IsScanData() bool {
							 | 
						||
| 
								 | 
							
									return js.lastIsScanData
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (js *JpegSplitter) processScanData(data []byte) (advanceBytes int, err error) {
							 | 
						||
| 
								 | 
							
									defer func() {
							 | 
						||
| 
								 | 
							
										if state := recover(); state != nil {
							 | 
						||
| 
								 | 
							
											err = log.Wrap(state.(error))
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Search through the segment, past all 0xff's therein, until we encounter
							 | 
						||
| 
								 | 
							
									// the EOI segment.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									dataLength := -1
							 | 
						||
| 
								 | 
							
									for i := js.scandataOffset; i < len(data); i++ {
							 | 
						||
| 
								 | 
							
										thisByte := data[i]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if i == 0 {
							 | 
						||
| 
								 | 
							
											continue
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										lastByte := data[i-1]
							 | 
						||
| 
								 | 
							
										if lastByte != 0xff {
							 | 
						||
| 
								 | 
							
											continue
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if thisByte == 0x00 || thisByte >= 0xd0 && thisByte <= 0xd8 {
							 | 
						||
| 
								 | 
							
											continue
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// After all of the other checks, this means that we're on the EOF
							 | 
						||
| 
								 | 
							
										// segment.
							 | 
						||
| 
								 | 
							
										if thisByte != MARKER_EOI {
							 | 
						||
| 
								 | 
							
											continue
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										dataLength = i - 1
							 | 
						||
| 
								 | 
							
										break
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if dataLength == -1 {
							 | 
						||
| 
								 | 
							
										// On the next pass, start on the last byte of this pass, just in case
							 | 
						||
| 
								 | 
							
										// the first byte of the two-byte sequence is here.
							 | 
						||
| 
								 | 
							
										js.scandataOffset = len(data) - 1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										jpegLogger.Debugf(nil, "Scan-data not fully available (%d).", len(data))
							 | 
						||
| 
								 | 
							
										return 0, nil
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									js.lastIsScanData = true
							 | 
						||
| 
								 | 
							
									js.lastMarkerId = 0
							 | 
						||
| 
								 | 
							
									js.lastMarkerName = ""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Note that we don't increment the counter since this isn't an actual
							 | 
						||
| 
								 | 
							
									// segment.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									jpegLogger.Debugf(nil, "End of scan-data.")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									err = js.handleSegment(0x0, "!SCANDATA", 0x0, data[:dataLength])
							 | 
						||
| 
								 | 
							
									log.PanicIf(err)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return dataLength, nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (js *JpegSplitter) readSegment(data []byte) (count int, err error) {
							 | 
						||
| 
								 | 
							
									defer func() {
							 | 
						||
| 
								 | 
							
										if state := recover(); state != nil {
							 | 
						||
| 
								 | 
							
											err = log.Wrap(state.(error))
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if js.counter == 0 {
							 | 
						||
| 
								 | 
							
										// Verify magic bytes.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if len(data) < 3 {
							 | 
						||
| 
								 | 
							
											jpegLogger.Debugf(nil, "Not enough (1)")
							 | 
						||
| 
								 | 
							
											return 0, nil
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if data[0] == jpegMagic2000[0] && data[1] == jpegMagic2000[1] && data[2] == jpegMagic2000[2] {
							 | 
						||
| 
								 | 
							
											// TODO(dustin): Revisit JPEG2000 support.
							 | 
						||
| 
								 | 
							
											log.Panicf("JPEG2000 not supported")
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if data[0] != jpegMagicStandard[0] || data[1] != jpegMagicStandard[1] || data[2] != jpegMagicStandard[2] {
							 | 
						||
| 
								 | 
							
											log.Panicf("file does not look like a JPEG: (%02x) (%02x) (%02x)", data[0], data[1], data[2])
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									chunkLength := len(data)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									jpegLogger.Debugf(nil, "SPLIT: LEN=(%d) COUNTER=(%d)", chunkLength, js.counter)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if js.scanDataIsNext() == true {
							 | 
						||
| 
								 | 
							
										// If the last segment was the SOS, we're currently sitting on scan data.
							 | 
						||
| 
								 | 
							
										// Search for the EOI marker afterward in order to know how much data
							 | 
						||
| 
								 | 
							
										// there is. Return this as its own token.
							 | 
						||
| 
								 | 
							
										//
							 | 
						||
| 
								 | 
							
										// REF: https://stackoverflow.com/questions/26715684/parsing-jpeg-sos-marker
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										advanceBytes, err := js.processScanData(data)
							 | 
						||
| 
								 | 
							
										log.PanicIf(err)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// This will either return 0 and implicitly request that we need more
							 | 
						||
| 
								 | 
							
										// data and then need to run again or will return an actual byte count
							 | 
						||
| 
								 | 
							
										// to progress by.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return advanceBytes, nil
							 | 
						||
| 
								 | 
							
									} else if js.lastMarkerId == MARKER_EOI {
							 | 
						||
| 
								 | 
							
										// We have more data following the EOI, which is unexpected. There
							 | 
						||
| 
								 | 
							
										// might be non-standard cruft at the end of the file. Terminate the
							 | 
						||
| 
								 | 
							
										// parse because the file-structure is, technically, complete at this
							 | 
						||
| 
								 | 
							
										// point.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return 0, io.EOF
							 | 
						||
| 
								 | 
							
									} else {
							 | 
						||
| 
								 | 
							
										js.lastIsScanData = false
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// If we're here, we're supposed to be sitting on the 0xff bytes at the
							 | 
						||
| 
								 | 
							
									// beginning of a segment (just before the marker).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if data[0] != 0xff {
							 | 
						||
| 
								 | 
							
										log.Panicf("not on new segment marker @ (%d): (%02X)", js.currentOffset, data[0])
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									i := 0
							 | 
						||
| 
								 | 
							
									found := false
							 | 
						||
| 
								 | 
							
									for ; i < chunkLength; i++ {
							 | 
						||
| 
								 | 
							
										jpegLogger.Debugf(nil, "Prefix check: (%d) %02X", i, data[i])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if data[i] != 0xff {
							 | 
						||
| 
								 | 
							
											found = true
							 | 
						||
| 
								 | 
							
											break
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									jpegLogger.Debugf(nil, "Skipped over leading 0xFF bytes: (%d)", i)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if found == false || i >= chunkLength {
							 | 
						||
| 
								 | 
							
										jpegLogger.Debugf(nil, "Not enough (3)")
							 | 
						||
| 
								 | 
							
										return 0, nil
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									markerId := data[i]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									js.lastMarkerName = markerNames[markerId]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									sizeLen, found := markerLen[markerId]
							 | 
						||
| 
								 | 
							
									jpegLogger.Debugf(nil, "MARKER-ID=%x SIZELEN=%v FOUND=%v", markerId, sizeLen, found)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									i++
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									b := bytes.NewBuffer(data[i:])
							 | 
						||
| 
								 | 
							
									payloadLength := 0
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// marker-ID + size => 2 + <dynamic>
							 | 
						||
| 
								 | 
							
									headerSize := 2 + sizeLen
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if found == false {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// It's not one of the static-length markers. Read the length.
							 | 
						||
| 
								 | 
							
										//
							 | 
						||
| 
								 | 
							
										// The length is an unsigned 16-bit network/big-endian.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// marker-ID + size => 2 + 2
							 | 
						||
| 
								 | 
							
										headerSize = 2 + 2
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if i+2 >= chunkLength {
							 | 
						||
| 
								 | 
							
											jpegLogger.Debugf(nil, "Not enough (4)")
							 | 
						||
| 
								 | 
							
											return 0, nil
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										l := uint16(0)
							 | 
						||
| 
								 | 
							
										err = binary.Read(b, binary.BigEndian, &l)
							 | 
						||
| 
								 | 
							
										log.PanicIf(err)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if l < 2 {
							 | 
						||
| 
								 | 
							
											log.Panicf("length of size read for non-special marker (%02x) is unexpectedly less than two.", markerId)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// (l includes the bytes of the length itself.)
							 | 
						||
| 
								 | 
							
										payloadLength = int(l) - 2
							 | 
						||
| 
								 | 
							
										jpegLogger.Debugf(nil, "DataLength (dynamically-sized segment): (%d)", payloadLength)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										i += 2
							 | 
						||
| 
								 | 
							
									} else if sizeLen > 0 {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Accommodates the non-zero markers in our marker index, which only
							 | 
						||
| 
								 | 
							
										// represent J2C extensions.
							 | 
						||
| 
								 | 
							
										//
							 | 
						||
| 
								 | 
							
										// The length is an unsigned 32-bit network/big-endian.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// TODO(dustin): !! This needs to be tested, but we need an image.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if sizeLen != 4 {
							 | 
						||
| 
								 | 
							
											log.Panicf("known non-zero marker is not four bytes, which is not currently handled: M=(%x)", markerId)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if i+4 >= chunkLength {
							 | 
						||
| 
								 | 
							
											jpegLogger.Debugf(nil, "Not enough (5)")
							 | 
						||
| 
								 | 
							
											return 0, nil
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										l := uint32(0)
							 | 
						||
| 
								 | 
							
										err = binary.Read(b, binary.BigEndian, &l)
							 | 
						||
| 
								 | 
							
										log.PanicIf(err)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										payloadLength = int(l) - 4
							 | 
						||
| 
								 | 
							
										jpegLogger.Debugf(nil, "DataLength (four-byte-length segment): (%u)", l)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										i += 4
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									jpegLogger.Debugf(nil, "PAYLOAD-LENGTH: %d", payloadLength)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									payload := data[i:]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if payloadLength < 0 {
							 | 
						||
| 
								 | 
							
										log.Panicf("payload length less than zero: (%d)", payloadLength)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									i += int(payloadLength)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if i > chunkLength {
							 | 
						||
| 
								 | 
							
										jpegLogger.Debugf(nil, "Not enough (6)")
							 | 
						||
| 
								 | 
							
										return 0, nil
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									jpegLogger.Debugf(nil, "Found whole segment.")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									js.lastMarkerId = markerId
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									payloadWindow := payload[:payloadLength]
							 | 
						||
| 
								 | 
							
									err = js.handleSegment(markerId, js.lastMarkerName, headerSize, payloadWindow)
							 | 
						||
| 
								 | 
							
									log.PanicIf(err)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									js.counter++
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									jpegLogger.Debugf(nil, "Returning advance of (%d)", i)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return i, nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (js *JpegSplitter) scanDataIsNext() bool {
							 | 
						||
| 
								 | 
							
									return js.lastMarkerId == MARKER_SOS
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Split is the base splitting function that satisfies `bufio.SplitFunc`.
							 | 
						||
| 
								 | 
							
								func (js *JpegSplitter) Split(data []byte, atEOF bool) (advance int, token []byte, err error) {
							 | 
						||
| 
								 | 
							
									defer func() {
							 | 
						||
| 
								 | 
							
										if state := recover(); state != nil {
							 | 
						||
| 
								 | 
							
											err = log.Wrap(state.(error))
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									for len(data) > 0 {
							 | 
						||
| 
								 | 
							
										currentAdvance, err := js.readSegment(data)
							 | 
						||
| 
								 | 
							
										if err != nil {
							 | 
						||
| 
								 | 
							
											if err == io.EOF {
							 | 
						||
| 
								 | 
							
												// We've encountered an EOI marker.
							 | 
						||
| 
								 | 
							
												return 0, nil, err
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											log.Panic(err)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if currentAdvance == 0 {
							 | 
						||
| 
								 | 
							
											if len(data) > 0 && atEOF == true {
							 | 
						||
| 
								 | 
							
												// Provide a little context in the error message.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if js.scanDataIsNext() == true {
							 | 
						||
| 
								 | 
							
													// Yes, we've ran into this.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													log.Panicf("scan-data is unbounded; EOI not encountered before EOF")
							 | 
						||
| 
								 | 
							
												} else {
							 | 
						||
| 
								 | 
							
													log.Panicf("partial segment data encountered before scan-data")
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// We don't have enough data for another segment.
							 | 
						||
| 
								 | 
							
											break
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										data = data[currentAdvance:]
							 | 
						||
| 
								 | 
							
										advance += currentAdvance
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return advance, nil, nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (js *JpegSplitter) parseSof(data []byte) (sof *SofSegment, err error) {
							 | 
						||
| 
								 | 
							
									defer func() {
							 | 
						||
| 
								 | 
							
										if state := recover(); state != nil {
							 | 
						||
| 
								 | 
							
											err = log.Wrap(state.(error))
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									stream := bytes.NewBuffer(data)
							 | 
						||
| 
								 | 
							
									buffer := bufio.NewReader(stream)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									bitsPerSample, err := buffer.ReadByte()
							 | 
						||
| 
								 | 
							
									log.PanicIf(err)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									height := uint16(0)
							 | 
						||
| 
								 | 
							
									err = binary.Read(buffer, binary.BigEndian, &height)
							 | 
						||
| 
								 | 
							
									log.PanicIf(err)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									width := uint16(0)
							 | 
						||
| 
								 | 
							
									err = binary.Read(buffer, binary.BigEndian, &width)
							 | 
						||
| 
								 | 
							
									log.PanicIf(err)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									componentCount, err := buffer.ReadByte()
							 | 
						||
| 
								 | 
							
									log.PanicIf(err)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									sof = &SofSegment{
							 | 
						||
| 
								 | 
							
										BitsPerSample:  bitsPerSample,
							 | 
						||
| 
								 | 
							
										Width:          width,
							 | 
						||
| 
								 | 
							
										Height:         height,
							 | 
						||
| 
								 | 
							
										ComponentCount: componentCount,
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return sof, nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (js *JpegSplitter) parseAppData(markerId byte, data []byte) (err error) {
							 | 
						||
| 
								 | 
							
									defer func() {
							 | 
						||
| 
								 | 
							
										if state := recover(); state != nil {
							 | 
						||
| 
								 | 
							
											err = log.Wrap(state.(error))
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (js *JpegSplitter) handleSegment(markerId byte, markerName string, headerSize int, payload []byte) (err error) {
							 | 
						||
| 
								 | 
							
									defer func() {
							 | 
						||
| 
								 | 
							
										if state := recover(); state != nil {
							 | 
						||
| 
								 | 
							
											err = log.Wrap(state.(error))
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									cloned := make([]byte, len(payload))
							 | 
						||
| 
								 | 
							
									copy(cloned, payload)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									s := &Segment{
							 | 
						||
| 
								 | 
							
										MarkerId:   markerId,
							 | 
						||
| 
								 | 
							
										MarkerName: markerName,
							 | 
						||
| 
								 | 
							
										Offset:     js.currentOffset,
							 | 
						||
| 
								 | 
							
										Data:       cloned,
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									jpegLogger.Debugf(nil, "Encountered marker (0x%02x) [%s] at offset (%d)", markerId, markerName, js.currentOffset)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									js.currentOffset += headerSize + len(payload)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									js.segments.Add(s)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									sv, ok := js.visitor.(SegmentVisitor)
							 | 
						||
| 
								 | 
							
									if ok == true {
							 | 
						||
| 
								 | 
							
										err = sv.HandleSegment(js.lastMarkerId, js.lastMarkerName, js.counter, js.lastIsScanData)
							 | 
						||
| 
								 | 
							
										log.PanicIf(err)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if markerId >= MARKER_SOF0 && markerId <= MARKER_SOF15 {
							 | 
						||
| 
								 | 
							
										ssv, ok := js.visitor.(SofSegmentVisitor)
							 | 
						||
| 
								 | 
							
										if ok == true {
							 | 
						||
| 
								 | 
							
											sof, err := js.parseSof(payload)
							 | 
						||
| 
								 | 
							
											log.PanicIf(err)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											err = ssv.HandleSof(sof)
							 | 
						||
| 
								 | 
							
											log.PanicIf(err)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									} else if markerId >= MARKER_APP0 && markerId <= MARKER_APP15 {
							 | 
						||
| 
								 | 
							
										err := js.parseAppData(markerId, payload)
							 | 
						||
| 
								 | 
							
										log.PanicIf(err)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return nil
							 | 
						||
| 
								 | 
							
								}
							 |