func (b *BAMProvider) GenerateShards(opts GenerateShardsOpts) ([]gbam.Shard, error) {
	if (opts.SplitMappedCoords || opts.SplitUnmappedCoords) && (opts.Padding != 0) {
		return nil, fmt.Errorf("GenerateShards: nonzero Padding cannot be specified with Split*Coords")
	}
	header, err := b.GetHeader()
	if err != nil {
		return nil, err
	}
	if opts.BytesPerShard <= 0 {
		if opts.NumShards > 0 {
			info, err := file.Stat(vcontext.Background(), b.Path)
			if err != nil {
				return nil, err
			}
			opts.BytesPerShard = info.Size() / int64(opts.NumShards)
		}
		if opts.BytesPerShard < DefaultBytesPerShard {
			opts.BytesPerShard = DefaultBytesPerShard
		}
	}
	if opts.MinBasesPerShard <= 0 {
		opts.MinBasesPerShard = DefaultMinBasesPerShard
	}
	if opts.Strategy == ByteBased {
		return gbam.GetByteBasedShards(
			b.Path, b.indexPath(), opts.BytesPerShard, opts.MinBasesPerShard, opts.Padding, opts.IncludeUnmapped)
	}
	return gbam.GetPositionBasedShards(
		header, 100000, opts.Padding, opts.IncludeUnmapped)
}
GenerateShards implements the Provider interface.
package bamprovider
import (
	"fmt"
	"io"
	"strings"
	"sync"
	"github.com/grailbio/base/errors"
	"github.com/grailbio/base/file"
	"github.com/grailbio/base/vcontext"
	"github.com/grailbio/bio/biopb"
	gbam "github.com/grailbio/bio/encoding/bam"
	"github.com/grailbio/hts/bam"
	"github.com/grailbio/hts/bgzf"
	"github.com/grailbio/hts/bgzf/index"
	"github.com/grailbio/hts/sam"
	"v.io/x/lib/vlog"
)
const (
	DefaultBytesPerShard = int64(128 << 20)
	DefaultMinBasesPerShard = 10000
)
type BAMProvider struct {
	Path string
	Index string
	err   errors.Once
	mu        sync.Mutex
	nActive   int
	freeIters []*bamIterator
	indexOnce sync.Once
	bindex    *bam.Index
	gindex    *gbam.GIndex
	infoOnce sync.Once
	header   *sam.Header
	info     FileInfo
}
type bamIterator struct {
	provider *BAMProvider
	in       file.File
	reader   *bam.Reader
	firstRecord bgzf.Offset
	startAddr, limitAddr biopb.Coord
	active bool
	err    error
	next   *sam.Record
	done   bool
}
func (b *BAMProvider) indexPath() string {
	index := b.Index
	if index == "" {
		index = b.Path + ".bai"
	}
	return index
}
func (b *BAMProvider) readIndex() error {
	b.indexOnce.Do(func() {
		ctx := vcontext.Background()
		in, err := file.Open(ctx, b.indexPath())
		if err != nil {
			b.err.Set(err)
			return
		}
		var bindex *bam.Index
		var gindex *gbam.GIndex
		if strings.HasSuffix(b.indexPath(), ".gbai") {
			gindex, err = gbam.ReadGIndex(in.Reader(ctx))
		} else {
			bindex, err = bam.ReadIndex(in.Reader(ctx))
		}
		if err != nil {
			b.err.Set(err)
			return
		}
		if err = in.Close(ctx); err != nil {
			b.err.Set(err)
			return
		}
		b.bindex = bindex
		b.gindex = gindex
	})
	return b.err.Err()
}
func (b *BAMProvider) FileInfo() (FileInfo, error) {
	b.initInfo()
	if err := b.err.Err(); err != nil {
		return FileInfo{}, err
	}
	return b.info, nil
}
func (b *BAMProvider) GetHeader() (*sam.Header, error) {
	b.initInfo()
	if err := b.err.Err(); err != nil {
		return nil, err
	}
	return b.header, nil
}
func (b *BAMProvider) initInfo() {
	b.infoOnce.Do(func() {
		ctx := vcontext.Background()
		reader, err := file.Open(ctx, b.Path)
		if err != nil {
			b.err.Set(err)
			return
		}
		info, err := reader.Stat(ctx)
		if err != nil {
			b.err.Set(err)
			reader.Close(ctx)  
			return
		}
		b.info = FileInfo{ModTime: info.ModTime(), Size: info.Size()}
		bamReader, err := bam.NewReader(reader.Reader(ctx), 1)
		if err != nil {
			b.err.Set(err)
			reader.Close(ctx)  
			return
		}
		b.header = bamReader.Header()
		if err := bamReader.Close(); err != nil {
			b.err.Set(err)
			reader.Close(ctx)  
			return
		}
		if err := reader.Close(ctx); err != nil {
			b.err.Set(err)
			return
		}
	})
}
func (b *BAMProvider) Close() error {
	if b.nActive > 0 {
		vlog.Panicf("%d iterators still active for %+v", b.nActive, b)
	}
	for _, iter := range b.freeIters {
		iter.internalClose()
	}
	b.freeIters = nil
	return b.err.Err()
}
func (b *BAMProvider) freeIterator(i *bamIterator) {
	if !i.active {
		vlog.Panic(i)
	}
	i.active = false
	if i.Err() != nil {
		vlog.Errorf("freeiterator: %v", i.Err())
		i.internalClose()  
		i = nil
	}
	b.mu.Lock()
	if i != nil {
		b.freeIters = append(b.freeIters, i)
	}
	b.nActive--
	if b.nActive < 0 {
		vlog.Panicf("Negative active count for %+v", b)
	}
	b.mu.Unlock()
}
func (b *BAMProvider) allocateIterator() *bamIterator {
	b.mu.Lock()
	b.nActive++
	if len(b.freeIters) > 0 {
		iter := b.freeIters[len(b.freeIters)-1]
		iter.active = true
		iter.err = nil
		iter.done = false
		iter.next = nil
		b.freeIters = b.freeIters[:len(b.freeIters)-1]
		b.mu.Unlock()
		return iter
	}
	b.mu.Unlock()
	iter := bamIterator{
		provider: b,
		active:   true,
	}
	if iter.err = b.readIndex(); iter.err != nil {
		return &iter
	}
	ctx := vcontext.Background()
	if iter.in, iter.err = file.Open(ctx, b.Path); iter.err != nil {
		return &iter
	}
	if iter.reader, iter.err = bam.NewReader(iter.in.Reader(ctx), 1); iter.err != nil {
		return &iter
	}
	iter.firstRecord = iter.reader.LastChunk().End
	return &iter
}