func noeof(n int, err error) (int, error) {
	if err == io.EOF {
		return n, io.ErrUnexpectedEOF
	}
	return n, err
}
noeof is used after reads in situations where EOF signifies invalid
formatting or corruption.
package snappystream
import (
	"bytes"
	"fmt"
	"hash/crc32"
	"io"
	"io/ioutil"
	"github.com/mreiferson/go-snappystream/snappy-go"
)
var errMissingStreamID = fmt.Errorf("missing stream identifier")
type reader struct {
	reader io.Reader
	err error
	seenStreamID   bool
	verifyChecksum bool
	buf bytes.Buffer
	hdr []byte
	src []byte
	dst []byte
}
func NewReader(r io.Reader, verifyChecksum bool) io.Reader {
	return &reader{
		reader: r,
		verifyChecksum: verifyChecksum,
		hdr: make([]byte, 4),
		src: make([]byte, 4096),
		dst: make([]byte, 4096),
	}
}
func (r *reader) WriteTo(w io.Writer) (int64, error) {
	if r.err != nil {
		return 0, r.err
	}
	n, err := r.buf.WriteTo(w)
	if err != nil {
		return n, err
	}
	wfallback := &bufferFallbackWriter{
		w:   w,
		buf: &r.buf,
	}
	for {
		var m int
		m, err = r.nextFrame(wfallback)
		if wfallback.writerErr != nil && err == nil {
			n += wfallback.n
			return n, wfallback.writerErr
		}
		n += int64(m)
		if err == io.EOF {
			return n, nil
		}
		if err != nil {
			r.err = err
			return n, err
		}
	}
	panic("unreachable")
}
type bufferFallbackWriter struct {
	w         io.Writer
	buf       *bytes.Buffer
	n         int64  
	writerErr error  
}
func (w *bufferFallbackWriter) Write(b []byte) (int, error) {
	if w.writerErr != nil {
		return w.buf.Write(b)
	}
	n, err := w.w.Write(b)
	w.n += int64(n)
	if err != nil {
		w.writerErr = err
		w.Write(b[n:])
		return len(b), nil
	}
	return n, nil
}
func (r *reader) read(b []byte) (int, error) {
	n, err := r.buf.Read(b)
	r.err = err
	return n, err
}
func (r *reader) Read(b []byte) (int, error) {
	if r.err != nil {
		return 0, r.err
	}
	if r.buf.Len() < len(b) {
		_, r.err = r.nextFrame(&r.buf)
		if r.err == io.EOF {
			return r.read(b)
		}
		if r.err != nil {
			return 0, r.err
		}
	}
	return r.read(b)
}
func (r *reader) nextFrame(w io.Writer) (int, error) {
	for {
		_, err := io.ReadFull(r.reader, r.hdr)
		if err != nil {
			return 0, err
		}
		if r.hdr[0] == blockStreamIdentifier {
			err := r.readStreamID()
			if err != nil {
				return 0, err
			}
			r.seenStreamID = true
			continue
		}
		if !r.seenStreamID {
			return 0, errMissingStreamID
		}
		switch typ := r.hdr[0]; {
		case typ == blockCompressed || typ == blockUncompressed:
			return r.decodeBlock(w)
		case typ == blockPadding || (0x80 <= typ && typ <= 0xfd):
			err := r.discardBlock()
			if err != nil {
				return 0, err
			}
			continue
		default:
			err = r.discardBlock()
			if err != nil {
				return 0, err
			}
			return 0, fmt.Errorf("unrecognized unskippable frame %#x", r.hdr[0])
		}
	}
	panic("unreachable")
}
func (r *reader) decodeBlock(w io.Writer) (int, error) {
	buf, err := r.readBlock()
	if err != nil {
		return 0, err
	}
	declen := len(buf[4:])
	if r.hdr[0] == blockCompressed {
		declen, err = snappy.DecodedLen(buf[4:])
		if err != nil {
			return 0, err
		}
	}
	if declen > MaxBlockSize {
		return 0, fmt.Errorf("decoded block data too large %d > %d", declen, MaxBlockSize)
	}
	crc32le, blockdata := buf[:4], buf[4:]
	if r.hdr[0] == blockCompressed {
		r.dst, err = snappy.Decode(r.dst, blockdata)
		if err != nil {
			return 0, err
		}
		blockdata = r.dst
	}
	if r.verifyChecksum {
		checksum := unmaskChecksum(uint32(crc32le[0]) | uint32(crc32le[1])<<8 | uint32(crc32le[2])<<16 | uint32(crc32le[3])<<24)
		actualChecksum := crc32.Checksum(blockdata, crcTable)
		if checksum != actualChecksum {
			return 0, fmt.Errorf("checksum does not match %x != %x", checksum, actualChecksum)
		}
	}
	return w.Write(blockdata)
}
func (r *reader) readStreamID() error {
	if !bytes.Equal(r.hdr, streamID[:4]) {
		return fmt.Errorf("invalid stream identifier length")
	}
	block := r.src[:6]
	_, err := noeof(io.ReadFull(r.reader, block))
	if err != nil {
		return err
	}
	if !bytes.Equal(block, streamID[4:]) {
		return fmt.Errorf("invalid stream identifier block")
	}
	return nil
}
func (r *reader) discardBlock() error {
	length := uint64(decodeLength(r.hdr[1:]))
	_, err := noeof64(io.CopyN(ioutil.Discard, r.reader, int64(length)))
	return err
}
func (r *reader) readBlock() ([]byte, error) {
	length := decodeLength(r.hdr[1:])
	if length > (maxEncodedBlockSize + 4) {
		return nil, fmt.Errorf("encoded block data too large %d > %d", length, (maxEncodedBlockSize + 4))
	}
	if int(length) > len(r.src) {
		r.src = make([]byte, length)
	}
	buf := r.src[:length]
	_, err := noeof(io.ReadFull(r.reader, buf))
	if err != nil {
		return nil, err
	}
	return buf, nil
}
func decodeLength(b []byte) uint32 {
	return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16
}
func unmaskChecksum(c uint32) uint32 {
	x := c - 0xa282ead8
	return ((x >> 17) | (x << 15))
}