func (b *Blob) chunkAt(off int64) (chunk Hash, chunkStart, chunkLength int64) {
	if off < 0 {
		return Hash{}, -1, -1
	}
	idx := sort.Search(len(b.chunkStart), func(i int) bool {
		return b.chunkStart[i] >= off
	})
	if idx == len(b.chunks) || b.chunkStart[idx] > off {
		idx--
	}
	var chunkEnd int64
	if idx+1 == len(b.chunks) {
		chunkEnd = b.length
	} else {
		chunkEnd = b.chunkStart[idx+1]
	}
	if off > chunkEnd {
		return Hash{}, -1, -1
	}
	return b.chunks[idx], off - b.chunkStart[idx], chunkEnd - off
}
chunkAt returns the chunk that contains the given offset within the
blob, the offset within the chunk to read, and the number of bytes to
read from the chunk.
package blob
import (
	"bytes"
	"context"
	"crypto/sha256"
	"encoding/gob"
	"encoding/hex"
	"io"
	"math"
	"sort"
	"sync"
	"github.com/bobvawter/cacheroach/api/tenant"
	"github.com/bobvawter/cacheroach/pkg/cache"
	"github.com/bobvawter/cacheroach/pkg/store/config"
	"github.com/bobvawter/cacheroach/pkg/store/util"
	"github.com/jackc/pgx/v4"
	"github.com/jackc/pgx/v4/pgxpool"
	"github.com/pkg/errors"
)
const HashSize = sha256.Size
type Hash [HashSize]byte
func (h Hash) MarshalBinary() ([]byte, error) {
	return h[:], nil
}
func (h *Hash) String() string {
	return hex.EncodeToString(h[:])
}
func (h *Hash) UnmarshalBinary(data []byte) error {
	if len(data) != HashSize {
		return errors.Errorf("length mismatch %d vs %d", HashSize, len(data))
	}
	copy(h[:], data)
	return nil
}
type ropeMeta struct {
	chunks     []Hash   
	chunkStart []int64  
	hash       Hash     
	length     int64    
}
func (m *ropeMeta) MarshalBinary() ([]byte, error) {
	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	if err := enc.Encode(1); err != nil {
		return nil, err
	}
	if err := enc.Encode(m.chunks); err != nil {
		return nil, err
	}
	if err := enc.Encode(m.chunkStart); err != nil {
		return nil, err
	}
	if err := enc.Encode(m.hash); err != nil {
		return nil, err
	}
	if err := enc.Encode(m.length); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}
func (m *ropeMeta) UnmarshalBinary(data []byte) error {
	dec := gob.NewDecoder(bytes.NewReader(data))
	var ver int
	if err := dec.Decode(&ver); err != nil {
		return err
	}
	if err := dec.Decode(&m.chunks); err != nil {
		return err
	}
	if err := dec.Decode(&m.chunkStart); err != nil {
		return err
	}
	if err := dec.Decode(&m.hash); err != nil {
		return err
	}
	if err := dec.Decode(&m.length); err != nil {
		return err
	}
	return nil
}
type Blob struct {
	*ropeMeta
	cache  *cache.Cache
	config *config.Config
	db     *pgxpool.Pool
	tID    *tenant.ID
	mu     struct {
		sync.Mutex
		ctx           context.Context  
		lastChunk     []byte           
		lastChunkHash Hash             
		pos           int64            
	}
}
var (
	_ io.ReadSeekCloser = (*Blob)(nil)
	_ io.WriterTo       = (*Blob)(nil)
)
func (b *Blob) Close() error {
	b.mu.Lock()
	defer b.mu.Unlock()
	b.mu.ctx = util.Canceled
	b.mu.lastChunk = nil
	return nil
}
func (b *Blob) Hash() Hash { return b.hash }
func (b *Blob) Length() int64 { return b.length }
func (b *Blob) Read(buf []byte) (int, error) {
	w := bytes.NewBuffer(buf[:0])
	count, err := b.WriteN(w, int64(len(buf)))
	return int(count), err
}
func (b *Blob) Seek(offset int64, whence int) (int64, error) {
	b.mu.Lock()
	defer b.mu.Unlock()
	switch whence {
	case io.SeekStart:
		b.mu.pos = offset
	case io.SeekCurrent:
		b.mu.pos += offset
	case io.SeekEnd:
		b.mu.pos = b.length - offset
	default:
		return 0, errors.Errorf("unexpected whence: %d", whence)
	}
	return b.mu.pos, nil
}
func (b *Blob) String() string { return b.hash.String() }
func (b *Blob) WriteN(w io.Writer, count int64) (int64, error) {
	b.mu.Lock()
	defer b.mu.Unlock()
	startPos := b.mu.pos
	if startPos >= b.length {
		return 0, io.EOF
	}
	ctx := b.mu.ctx
	if hasCtx, ok := w.(interface{ Context() context.Context }); ok {
		ctx = hasCtx.Context()
	}
	for b.mu.pos < b.length && count > 0 {
		chunk, start, length := b.chunkAt(b.mu.pos)
		if count < length {
			length = count
		}
		buf, err := b.loadChunkLocked(ctx, chunk, true)
		if errors.Is(err, pgx.ErrNoRows) {
			buf, err = b.loadChunkLocked(ctx, chunk, false)
		}
		if err != nil {
			return b.mu.pos - startPos, err
		}
		written, err := w.Write(buf[start : start+length])
		count -= int64(written)
		b.mu.pos += int64(written)
		if err != nil {
			err = errors.Wrapf(err, "WriteN %s", b.hash)
			return b.mu.pos - startPos, err
		}
	}
	return b.mu.pos - startPos, nil
}
func (b *Blob) WriteTo(w io.Writer) (written int64, err error) {
	return b.WriteN(w, math.MaxInt64)
}