func (b *bucket) Put(key, value []byte) error {
	if err := b.tx.checkClosed(); err != nil {
		return err
	}
	if !b.tx.writable {
		str := "setting a key requires a writable database transaction"
		return makeDbErr(database.ErrTxNotWritable, str, nil)
	}
	if len(key) == 0 {
		str := "put requires a key"
		return makeDbErr(database.ErrKeyRequired, str, nil)
	}
	return b.tx.putKey(bucketizedKey(b.id, key), value)
}
Put saves the specified key/value pair to the bucket.  Keys that do not
already exist are added and keys that already exist are overwritten.

Returns the following errors as required by the interface contract:
- ErrKeyRequired if the key is empty
- ErrIncompatibleValue if the key is the same as an existing bucket
- ErrTxNotWritable if attempted against a read-only transaction
- ErrTxClosed if the transaction has already been closed

This function is part of the database.Bucket interface implementation.
package ffldb
import (
	"bytes"
	"encoding/binary"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"sort"
	"sync"
	"github.com/soteria-dag/soterd/chaincfg/chainhash"
	"github.com/soteria-dag/soterd/database"
	"github.com/soteria-dag/soterd/database/internal/treap"
	"github.com/soteria-dag/soterd/wire"
	"github.com/soteria-dag/soterd/soterutil"
	"github.com/btcsuite/goleveldb/leveldb"
	"github.com/btcsuite/goleveldb/leveldb/comparer"
	ldberrors "github.com/btcsuite/goleveldb/leveldb/errors"
	"github.com/btcsuite/goleveldb/leveldb/filter"
	"github.com/btcsuite/goleveldb/leveldb/iterator"
	"github.com/btcsuite/goleveldb/leveldb/opt"
	"github.com/btcsuite/goleveldb/leveldb/util"
)
const (
	metadataDbName = "metadata"
	blockHdrSize = wire.MaxBlockHeaderPayload
	blockHdrOffset = blockLocSize
)
var (
	byteOrder = binary.LittleEndian
	bucketIndexPrefix = []byte("bidx")
	curBucketIDKeyName = []byte("bidx-cbid")
	metadataBucketID = [4]byte{}
	blockIdxBucketID = [4]byte{0x00, 0x00, 0x00, 0x01}
	blockIdxBucketName = []byte("ffldb-blockidx")
	writeLocKeyName = []byte("ffldb-writeloc")
)
const (
	errDbNotOpenStr = "database is not open"
	errTxClosedStr = "database tx is closed"
)
type bulkFetchData struct {
	*blockLocation
	replyIndex int
}
type bulkFetchDataSorter []bulkFetchData
func (s bulkFetchDataSorter) Len() int {
	return len(s)
}
func (s bulkFetchDataSorter) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}
func (s bulkFetchDataSorter) Less(i, j int) bool {
	if s[i].blockFileNum < s[j].blockFileNum {
		return true
	}
	if s[i].blockFileNum > s[j].blockFileNum {
		return false
	}
	return s[i].fileOffset < s[j].fileOffset
}
func makeDbErr(c database.ErrorCode, desc string, err error) database.Error {
	return database.Error{ErrorCode: c, Description: desc, Err: err}
}
func convertErr(desc string, ldbErr error) database.Error {
	var code = database.ErrDriverSpecific
	switch {
	case ldberrors.IsCorrupted(ldbErr):
		code = database.ErrCorruption
	case ldbErr == leveldb.ErrClosed:
		code = database.ErrDbNotOpen
	case ldbErr == leveldb.ErrSnapshotReleased:
		code = database.ErrTxClosed
	case ldbErr == leveldb.ErrIterReleased:
		code = database.ErrTxClosed
	}
	return database.Error{ErrorCode: code, Description: desc, Err: ldbErr}
}
func copySlice(slice []byte) []byte {
	ret := make([]byte, len(slice))
	copy(ret, slice)
	return ret
}
type cursor struct {
	bucket      *bucket
	dbIter      iterator.Iterator
	pendingIter iterator.Iterator
	currentIter iterator.Iterator
}
var _ database.Cursor = (*cursor)(nil)
func (c *cursor) Bucket() database.Bucket {
	if err := c.bucket.tx.checkClosed(); err != nil {
		return nil
	}
	return c.bucket
}
func (c *cursor) Delete() error {
	if err := c.bucket.tx.checkClosed(); err != nil {
		return err
	}
	if c.currentIter == nil {
		str := "cursor is exhausted"
		return makeDbErr(database.ErrIncompatibleValue, str, nil)
	}
	key := c.currentIter.Key()
	if bytes.HasPrefix(key, bucketIndexPrefix) {
		str := "buckets may not be deleted from a cursor"
		return makeDbErr(database.ErrIncompatibleValue, str, nil)
	}
	c.bucket.tx.deleteKey(copySlice(key), true)
	return nil
}
func (c *cursor) skipPendingUpdates(forwards bool) {
	for c.dbIter.Valid() {
		var skip bool
		key := c.dbIter.Key()
		if c.bucket.tx.pendingRemove.Has(key) {
			skip = true
		} else if c.bucket.tx.pendingKeys.Has(key) {
			skip = true
		}
		if !skip {
			break
		}
		if forwards {
			c.dbIter.Next()
		} else {
			c.dbIter.Prev()
		}
	}
}
func (c *cursor) chooseIterator(forwards bool) bool {
	c.skipPendingUpdates(forwards)
	if !c.dbIter.Valid() && !c.pendingIter.Valid() {
		c.currentIter = nil
		return false
	}
	if !c.pendingIter.Valid() {
		c.currentIter = c.dbIter
		return true
	}
	if !c.dbIter.Valid() {
		c.currentIter = c.pendingIter
		return true
	}
	compare := bytes.Compare(c.dbIter.Key(), c.pendingIter.Key())
	if (forwards && compare > 0) || (!forwards && compare < 0) {
		c.currentIter = c.pendingIter
	} else {
		c.currentIter = c.dbIter
	}
	return true
}
func (c *cursor) First() bool {
	if err := c.bucket.tx.checkClosed(); err != nil {
		return false
	}
	c.dbIter.First()
	c.pendingIter.First()
	return c.chooseIterator(true)
}
func (c *cursor) Last() bool {
	if err := c.bucket.tx.checkClosed(); err != nil {
		return false
	}
	c.dbIter.Last()
	c.pendingIter.Last()
	return c.chooseIterator(false)
}
func (c *cursor) Next() bool {
	if err := c.bucket.tx.checkClosed(); err != nil {
		return false
	}
	if c.currentIter == nil {
		return false
	}
	c.currentIter.Next()
	return c.chooseIterator(true)
}
func (c *cursor) Prev() bool {
	if err := c.bucket.tx.checkClosed(); err != nil {
		return false
	}
	if c.currentIter == nil {
		return false
	}
	c.currentIter.Prev()
	return c.chooseIterator(false)
}
func (c *cursor) Seek(seek []byte) bool {
	if err := c.bucket.tx.checkClosed(); err != nil {
		return false
	}
	seekKey := bucketizedKey(c.bucket.id, seek)
	c.dbIter.Seek(seekKey)
	c.pendingIter.Seek(seekKey)
	return c.chooseIterator(true)
}
func (c *cursor) rawKey() []byte {
	if c.currentIter == nil {
		return nil
	}
	return copySlice(c.currentIter.Key())
}
func (c *cursor) Key() []byte {
	if err := c.bucket.tx.checkClosed(); err != nil {
		return nil
	}
	if c.currentIter == nil {
		return nil
	}
	key := c.currentIter.Key()
	if bytes.HasPrefix(key, bucketIndexPrefix) {
		key = key[len(bucketIndexPrefix)+4:]
		return copySlice(key)
	}
	key = key[len(c.bucket.id):]
	return copySlice(key)
}
func (c *cursor) rawValue() []byte {
	if c.currentIter == nil {
		return nil
	}
	return copySlice(c.currentIter.Value())
}
func (c *cursor) Value() []byte {
	if err := c.bucket.tx.checkClosed(); err != nil {
		return nil
	}
	if c.currentIter == nil {
		return nil
	}
	if bytes.HasPrefix(c.currentIter.Key(), bucketIndexPrefix) {
		return nil
	}
	return copySlice(c.currentIter.Value())
}
type cursorType int
const (
	ctKeys cursorType = iota
	ctBuckets
	ctFull
)
func cursorFinalizer(c *cursor) {
	c.dbIter.Release()
	c.pendingIter.Release()
}
func newCursor(b *bucket, bucketID []byte, cursorTyp cursorType) *cursor {
	var dbIter, pendingIter iterator.Iterator
	switch cursorTyp {
	case ctKeys:
		keyRange := util.BytesPrefix(bucketID)
		dbIter = b.tx.snapshot.NewIterator(keyRange)
		pendingKeyIter := newLdbTreapIter(b.tx, keyRange)
		pendingIter = pendingKeyIter
	case ctBuckets:
		prefix := make([]byte, len(bucketIndexPrefix)+4)
		copy(prefix, bucketIndexPrefix)
		copy(prefix[len(bucketIndexPrefix):], bucketID)
		bucketRange := util.BytesPrefix(prefix)
		dbIter = b.tx.snapshot.NewIterator(bucketRange)
		pendingBucketIter := newLdbTreapIter(b.tx, bucketRange)
		pendingIter = pendingBucketIter
	case ctFull:
		fallthrough
	default:
		prefix := make([]byte, len(bucketIndexPrefix)+4)
		copy(prefix, bucketIndexPrefix)
		copy(prefix[len(bucketIndexPrefix):], bucketID)
		bucketRange := util.BytesPrefix(prefix)
		keyRange := util.BytesPrefix(bucketID)
		dbKeyIter := b.tx.snapshot.NewIterator(keyRange)
		dbBucketIter := b.tx.snapshot.NewIterator(bucketRange)
		iters := []iterator.Iterator{dbKeyIter, dbBucketIter}
		dbIter = iterator.NewMergedIterator(iters,
			comparer.DefaultComparer, true)
		pendingKeyIter := newLdbTreapIter(b.tx, keyRange)
		pendingBucketIter := newLdbTreapIter(b.tx, bucketRange)
		iters = []iterator.Iterator{pendingKeyIter, pendingBucketIter}
		pendingIter = iterator.NewMergedIterator(iters,
			comparer.DefaultComparer, true)
	}
	return &cursor{bucket: b, dbIter: dbIter, pendingIter: pendingIter}
}
type bucket struct {
	tx *transaction
	id [4]byte
}
var _ database.Bucket = (*bucket)(nil)
func bucketIndexKey(parentID [4]byte, key []byte) []byte {
	indexKey := make([]byte, len(bucketIndexPrefix)+4+len(key))
	copy(indexKey, bucketIndexPrefix)
	copy(indexKey[len(bucketIndexPrefix):], parentID[:])
	copy(indexKey[len(bucketIndexPrefix)+4:], key)
	return indexKey
}
func bucketizedKey(bucketID [4]byte, key []byte) []byte {
	bKey := make([]byte, 4+len(key))
	copy(bKey, bucketID[:])
	copy(bKey[4:], key)
	return bKey
}
func (b *bucket) Bucket(key []byte) database.Bucket {
	if err := b.tx.checkClosed(); err != nil {
		return nil
	}
	childID := b.tx.fetchKey(bucketIndexKey(b.id, key))
	if childID == nil {
		return nil
	}
	childBucket := &bucket{tx: b.tx}
	copy(childBucket.id[:], childID)
	return childBucket
}
func (b *bucket) CreateBucket(key []byte) (database.Bucket, error) {
	if err := b.tx.checkClosed(); err != nil {
		return nil, err
	}
	if !b.tx.writable {
		str := "create bucket requires a writable database transaction"
		return nil, makeDbErr(database.ErrTxNotWritable, str, nil)
	}
	if len(key) == 0 {
		str := "create bucket requires a key"
		return nil, makeDbErr(database.ErrBucketNameRequired, str, nil)
	}
	bidxKey := bucketIndexKey(b.id, key)
	if b.tx.hasKey(bidxKey) {
		str := "bucket already exists"
		return nil, makeDbErr(database.ErrBucketExists, str, nil)
	}
	var childID [4]byte
	if b.id == metadataBucketID && bytes.Equal(key, blockIdxBucketName) {
		childID = blockIdxBucketID
	} else {
		var err error
		childID, err = b.tx.nextBucketID()
		if err != nil {
			return nil, err
		}
	}
	if err := b.tx.putKey(bidxKey, childID[:]); err != nil {
		str := fmt.Sprintf("failed to create bucket with key %q", key)
		return nil, convertErr(str, err)
	}
	return &bucket{tx: b.tx, id: childID}, nil
}
func (b *bucket) CreateBucketIfNotExists(key []byte) (database.Bucket, error) {
	if err := b.tx.checkClosed(); err != nil {
		return nil, err
	}
	if !b.tx.writable {
		str := "create bucket requires a writable database transaction"
		return nil, makeDbErr(database.ErrTxNotWritable, str, nil)
	}
	if bucket := b.Bucket(key); bucket != nil {
		return bucket, nil
	}
	return b.CreateBucket(key)
}
func (b *bucket) DeleteBucket(key []byte) error {
	if err := b.tx.checkClosed(); err != nil {
		return err
	}
	if !b.tx.writable {
		str := "delete bucket requires a writable database transaction"
		return makeDbErr(database.ErrTxNotWritable, str, nil)
	}
	bidxKey := bucketIndexKey(b.id, key)
	childID := b.tx.fetchKey(bidxKey)
	if childID == nil {
		str := fmt.Sprintf("bucket %q does not exist", key)
		return makeDbErr(database.ErrBucketNotFound, str, nil)
	}
	childIDs := [][]byte{childID}
	for len(childIDs) > 0 {
		childID = childIDs[len(childIDs)-1]
		childIDs = childIDs[:len(childIDs)-1]
		keyCursor := newCursor(b, childID, ctKeys)
		for ok := keyCursor.First(); ok; ok = keyCursor.Next() {
			b.tx.deleteKey(keyCursor.rawKey(), false)
		}
		cursorFinalizer(keyCursor)
		bucketCursor := newCursor(b, childID, ctBuckets)
		for ok := bucketCursor.First(); ok; ok = bucketCursor.Next() {
			childID := bucketCursor.rawValue()
			childIDs = append(childIDs, childID)
			b.tx.deleteKey(bucketCursor.rawKey(), false)
		}
		cursorFinalizer(bucketCursor)
	}
	b.tx.deleteKey(bidxKey, true)
	return nil
}
func (b *bucket) Cursor() database.Cursor {
	if err := b.tx.checkClosed(); err != nil {
		return &cursor{bucket: b}
	}
	c := newCursor(b, b.id[:], ctFull)
	runtime.SetFinalizer(c, cursorFinalizer)
	return c
}
func (b *bucket) ForEach(fn func(k, v []byte) error) error {
	if err := b.tx.checkClosed(); err != nil {
		return err
	}
	c := newCursor(b, b.id[:], ctKeys)
	defer cursorFinalizer(c)
	for ok := c.First(); ok; ok = c.Next() {
		err := fn(c.Key(), c.Value())
		if err != nil {
			return err
		}
	}
	return nil
}
func (b *bucket) ForEachBucket(fn func(k []byte) error) error {
	if err := b.tx.checkClosed(); err != nil {
		return err
	}
	c := newCursor(b, b.id[:], ctBuckets)
	defer cursorFinalizer(c)
	for ok := c.First(); ok; ok = c.Next() {
		err := fn(c.Key())
		if err != nil {
			return err
		}
	}
	return nil
}
func (b *bucket) Writable() bool {
	return b.tx.writable
}