diff options
Diffstat (limited to 'vendor/github.com/superseriousbusiness/go-jpeg-image-structure/v2/splitter.go')
-rw-r--r-- | vendor/github.com/superseriousbusiness/go-jpeg-image-structure/v2/splitter.go | 437 |
1 files changed, 0 insertions, 437 deletions
diff --git a/vendor/github.com/superseriousbusiness/go-jpeg-image-structure/v2/splitter.go b/vendor/github.com/superseriousbusiness/go-jpeg-image-structure/v2/splitter.go deleted file mode 100644 index 1856beddf..000000000 --- a/vendor/github.com/superseriousbusiness/go-jpeg-image-structure/v2/splitter.go +++ /dev/null @@ -1,437 +0,0 @@ -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 -} |