func (bg *Reader) cachePut(b Block) (evicted Block, retained bool) {
	if b == nil || !b.hasData() {
		return b, false
	}
	return bg.cache.Put(b)
}
cachePut puts the given Block into the cache if it exists, it returns
the Block that was evicted or b if it was not retained, and whether
the Block was retained by the cache.
package bgzf
import (
	"bufio"
	"bytes"
	"compress/flate"
	"compress/gzip"
	"io"
	"runtime"
	"sync"
)
type countReader struct {
	fr flate.Reader
	off int64
}
func newCountReader(r io.Reader) *countReader {
	switch r := r.(type) {
	case *countReader:
		panic("bgzf: illegal use of internal type")
	case flate.Reader:
		return &countReader{fr: r}
	default:
		return &countReader{fr: bufio.NewReader(r)}
	}
}
func (r *countReader) Read(p []byte) (int, error) {
	n, err := r.fr.Read(p)
	r.off += int64(n)
	return n, err
}
func (r *countReader) ReadByte() (byte, error) {
	b, err := r.fr.ReadByte()
	if err == nil {
		r.off++
	}
	return b, err
}
func (r *countReader) offset() int64 { return r.off }
func (r *countReader) seek(rs io.ReadSeeker, off int64) error {
	_, err := rs.Seek(off, 0)
	if err != nil {
		return err
	}
	type reseter interface {
		Reset(io.Reader)
	}
	switch cr := r.fr.(type) {
	case reseter:
		cr.Reset(rs)
	default:
		r.fr = newCountReader(rs)
	}
	r.off = off
	return nil
}
type buffer struct {
	off  int  
	size int  
	data [MaxBlockSize]byte
}
func (r *buffer) Read(b []byte) (int, error) {
	if r.off >= r.size {
		return 0, io.EOF
	}
	if n := r.size - r.off; len(b) > n {
		b = b[:n]
	}
	n := copy(b, r.data[r.off:])
	r.off += n
	return n, nil
}
func (r *buffer) ReadByte() (byte, error) {
	if r.off == r.size {
		return 0, io.EOF
	}
	b := r.data[r.off]
	r.off++
	return b, nil
}
func (r *buffer) reset() { r.size = 0 }
func (r *buffer) hasData() bool { return r.size != 0 }
func (r *buffer) readLimited(n int, src *countReader) error {
	if r.hasData() {
		panic("bgzf: read into non-empty buffer")
	}
	r.off = 0
	var err error
	r.size, err = io.ReadFull(src, r.data[:n])
	return err
}
func (r *buffer) equals(b []byte) bool { return bytes.Equal(r.data[:r.size], b) }
type decompressor struct {
	owner *Reader
	gz gzip.Reader
	cr *countReader
	blockSize int
	buf buffer
	wg  sync.WaitGroup
	blk Block
	err error
}
func (d *decompressor) Read(b []byte) (int, error) {
	if d.buf.hasData() {
		return d.buf.Read(b)
	}
	return d.cr.Read(b)
}
func (d *decompressor) ReadByte() (byte, error) {
	if d.buf.hasData() {
		return d.buf.ReadByte()
	}
	return d.cr.ReadByte()
}
func (d *decompressor) lazyBlock() {
	if d.blk == nil {
		if w, ok := d.owner.cache.(Wrapper); ok {
			d.blk = w.Wrap(&block{owner: d.owner})
		} else {
			d.blk = &block{owner: d.owner}
		}
		return
	}
	if !d.blk.ownedBy(d.owner) {
		d.blk.setOwner(d.owner)
	}
}
func (d *decompressor) acquireHead() {
	d.wg.Add(1)
	d.cr = <-d.owner.head
}
func (d *decompressor) releaseHead() {
	d.owner.head <- d.cr
	d.cr = nil  
}
func (d *decompressor) wait() (Block, error) {
	d.wg.Wait()
	blk := d.blk
	d.blk = nil
	return blk, d.err
}
func (d *decompressor) using(b Block) *decompressor { d.blk = b; return d }
func (d *decompressor) nextBlockAt(off int64, rs io.ReadSeeker) *decompressor {
	d.err = nil
	for {
		exists, next := d.owner.cacheHasBlockFor(off)
		if !exists {
			break
		}
		off = next
	}
	d.lazyBlock()
	d.acquireHead()
	defer d.releaseHead()
	if d.cr.offset() != off {
		if rs == nil {
			var ok bool
			rs, ok = d.owner.r.(io.ReadSeeker)
			if !ok {
				panic("bgzf: unexpected offset without seek")
			}
		}
		d.err = d.cr.seek(rs, off)
		if d.err != nil {
			d.wg.Done()
			return d
		}
	}
	d.blk.setBase(d.cr.offset())
	d.err = d.readMember()
	if d.err != nil {
		d.wg.Done()
		return d
	}
	d.blk.setHeader(d.gz.Header)
	d.gz.Header = gzip.Header{}  
	go func() {
		d.err = d.blk.readFrom(&d.gz)
		d.wg.Done()
	}()
	return d
}
func expectedMemberSize(h gzip.Header) int {
	i := bytes.Index(h.Extra, bgzfExtraPrefix)
	if i < 0 || i+5 >= len(h.Extra) {
		return -1
	}
	return (int(h.Extra[i+4]) | int(h.Extra[i+5])<<8) + 1
}
func (d *decompressor) readMember() error {
	d.buf.reset()
	mark := d.cr.offset()
	err := d.gz.Reset(d)
	if err != nil {
		d.blockSize = -1
		return err
	}
	d.blockSize = expectedMemberSize(d.gz.Header)
	if d.blockSize < 0 {
		return ErrNoBlockSize
	}
	skipped := int(d.cr.offset() - mark)
	need := d.blockSize - skipped
	if need == 0 {
		return io.EOF
	} else if need < 0 {
		return ErrCorrupt
	}
	return d.buf.readLimited(need, d.cr)
}
type Offset struct {
	File  int64
	Block uint16
}
type Chunk struct {
	Begin Offset
	End   Offset
}
type Reader struct {
	gzip.Header
	r io.Reader
	head chan *countReader
	lastChunk Chunk
	Blocked bool
	dec *decompressor
	waiting chan *decompressor
	working chan *decompressor
	control chan int64
	done    chan struct{}
	current Block
	mu    sync.RWMutex
	cache Cache
	err error
}
func NewReader(r io.Reader, rd int) (*Reader, error) {
	if rd == 0 {
		rd = runtime.GOMAXPROCS(0)
	}
	bg := &Reader{
		r: r,
		head: make(chan *countReader, 1),
	}
	bg.head <- newCountReader(r)
	if rd > 1 {
		bg.waiting = make(chan *decompressor, rd)
		bg.working = make(chan *decompressor, rd)
		bg.control = make(chan int64, 1)
		bg.done = make(chan struct{})
		for ; rd > 1; rd-- {
			bg.waiting <- &decompressor{owner: bg}
		}
	}
	bg.dec = &decompressor{owner: bg}
	blk, err := bg.dec.nextBlockAt(0, nil).wait()
	if err != nil {
		return nil, err
	}
	bg.current = blk
	bg.Header = bg.current.header()
	if bg.control != nil {
		bg.waiting <- bg.dec
		bg.dec = nil
		next := blk.NextBase()
		go func() {
			defer func() {
				bg.mu.Lock()
				bg.cache = nil
				bg.mu.Unlock()
				close(bg.done)
			}()
			for dec := range bg.waiting {
				var open bool
				if next < 0 {
					next, open = <-bg.control
					if !open {
						return
					}
				} else {
					select {
					case next, open = <-bg.control:
						if !open {
							return
						}
					default:
					}
				}
				dec.nextBlockAt(next, nil)
				next = dec.blk.NextBase()
				bg.working <- dec
			}
		}()
	}
	return bg, nil
}
func (bg *Reader) SetCache(c Cache) {
	bg.mu.Lock()
	bg.cache = c
	bg.mu.Unlock()
}
func (bg *Reader) Seek(off Offset) error {
	rs, ok := bg.r.(io.ReadSeeker)
	if !ok {
		return ErrNotASeeker
	}
	if off.File != bg.current.Base() || !bg.current.hasData() {
		ok := bg.cacheSwap(off.File)
		if !ok {
			var dec *decompressor
			if bg.dec != nil {
				dec = bg.dec
			} else {
				select {
				case dec = <-bg.waiting:
				case dec = <-bg.working:
					blk, err := dec.wait()
					if err == nil {
						bg.keep(blk)
						if blk.Base() == off.File {
							bg.current = blk
							bg.control <- bg.current.NextBase()
							bg.waiting <- dec
							dec = nil
						}
					}
				}
			}
			if dec != nil {
				bg.current, bg.err = dec.
					using(bg.current).
					nextBlockAt(off.File, rs).
					wait()
				if bg.dec == nil {
					select {
					case <-bg.control:
					default:
					}
					bg.control <- bg.current.NextBase()
					bg.waiting <- dec
				}
				bg.Header = bg.current.header()
				if bg.err != nil {
					return bg.err
				}
			}
		}
	}
	bg.err = bg.current.seek(int64(off.Block))
	if bg.err == nil {
		bg.lastChunk = Chunk{Begin: off, End: off}
	}
	return bg.err
}
func (bg *Reader) LastChunk() Chunk { return bg.lastChunk }
func (bg *Reader) BlockLen() int { return bg.current.len() }
func (bg *Reader) Close() error {
	if bg.control != nil {
		close(bg.control)
		close(bg.waiting)
		<-bg.done
	}
	if bg.err == io.EOF {
		return nil
	}
	return bg.err
}
func (bg *Reader) Read(p []byte) (int, error) {
	if bg.err != nil {
		return 0, bg.err
	}
	for bg.current.len() == 0 {
		bg.err = bg.nextBlock()
		if bg.err != nil {
			return 0, bg.err
		}
	}
	bg.lastChunk.Begin = bg.current.txOffset()
	var n int
	for n < len(p) && bg.err == nil {
		var _n int
		_n, bg.err = bg.current.Read(p[n:])
		n += _n
		if bg.err == io.EOF {
			if n == len(p) {
				bg.err = nil
				break
			}
			if bg.Blocked {
				bg.err = nil
				bg.lastChunk.End = bg.current.txOffset()
				return n, io.EOF
			}
			bg.err = bg.nextBlock()
			if bg.err != nil {
				break
			}
		}
	}
	bg.lastChunk.End = bg.current.txOffset()
	return n, bg.err
}
func (bg *Reader) ReadByte() (byte, error) {
	if bg.err != nil {
		return 0, bg.err
	}
	for bg.current.len() == 0 {
		bg.err = bg.nextBlock()
		if bg.err != nil {
			return 0, bg.err
		}
	}
	bg.lastChunk.Begin = bg.current.txOffset()
	var b byte
	b, bg.err = bg.current.ReadByte()
	if bg.err == io.EOF {
		if bg.Blocked {
			bg.err = nil
			bg.lastChunk.End = bg.current.txOffset()
			return b, io.EOF
		}
		bg.err = bg.nextBlock()
	}
	bg.lastChunk.End = bg.current.txOffset()
	return b, bg.err
}
func (bg *Reader) nextBlock() error {
	base := bg.current.NextBase()
	ok := bg.cacheSwap(base)
	if ok {
		bg.Header = bg.current.header()
		return nil
	}
	var err error
	if bg.dec != nil {
		bg.dec.using(bg.current).nextBlockAt(base, nil)
		bg.current, err = bg.dec.wait()
	} else {
		var ok bool
		for i := 0; i < cap(bg.working); i++ {
			dec := <-bg.working
			bg.current, err = dec.wait()
			bg.waiting <- dec
			if bg.current.Base() == base {
				ok = true
				break
			}
			if err == nil {
				bg.keep(bg.current)
				bg.current = nil
			}
		}
		if !ok {
			panic("bgzf: unexpected block")
		}
	}
	if err != nil {
		return err
	}
	h := bg.current.header()
	if bg.current.isMagicBlock() {
		bg.Header.Extra = h.Extra
	} else {
		bg.Header = h
	}
	return nil
}
func (bg *Reader) cacheSwap(base int64) bool {
	bg.mu.RLock()
	defer bg.mu.RUnlock()
	if bg.cache == nil {
		return false
	}
	blk, err := bg.cachedBlockFor(base)
	if err != nil {
		return false
	}
	if blk != nil {
		bg.cachePut(bg.current)
		bg.current = blk
		return true
	}
	var retained bool
	bg.current, retained = bg.cachePut(bg.current)
	if retained {
		bg.current = nil
	}
	return false
}
func (bg *Reader) cacheHasBlockFor(base int64) (exists bool, next int64) {
	bg.mu.RLock()
	defer bg.mu.RUnlock()
	if bg.cache == nil {
		return false, -1
	}
	return bg.cache.Peek(base)
}
func (bg *Reader) cachedBlockFor(base int64) (Block, error) {
	blk := bg.cache.Get(base)
	if blk != nil {
		if !blk.ownedBy(bg) {
			return nil, ErrContaminatedCache
		}
		err := blk.seek(0)
		if err != nil {
			return nil, err
		}
	}
	return blk, nil
}