func (b *Bundler) canFit(bu *bundle, size int) bool {
	return (b.BundleByteLimit <= 0 || bu.size+size <= b.BundleByteLimit) &&
		(b.BundleCountThreshold <= 0 || bu.items.Len() < b.BundleCountThreshold)
}
canFit returns true if bu can fit an additional item of size bytes based
on the limits of Bundler b.
package bundler
import (
	"context"
	"errors"
	"reflect"
	"sync"
	"time"
	"golang.org/x/sync/semaphore"
)
type mode int
const (
	DefaultDelayThreshold       = time.Second
	DefaultBundleCountThreshold = 10
	DefaultBundleByteThreshold  = 1e6  
	DefaultBufferedByteLimit    = 1e9  
)
const (
	none mode = iota
	add
	addWait
)
var (
	ErrOverflow = errors.New("bundler reached buffered byte limit")
	ErrOversizedItem = errors.New("item size exceeds bundle byte limit")
	errMixedMethods = errors.New("calls to Add and AddWait cannot be mixed")
)
type Bundler struct {
	DelayThreshold time.Duration
	BundleCountThreshold int
	BundleByteThreshold int
	BundleByteLimit int
	BufferedByteLimit int
	HandlerLimit int
	handler       func(interface{})  
	itemSliceZero reflect.Value      
	mu           sync.Mutex           
	flushTimer   *time.Timer          
	handlerCount int                  
	sem          *semaphore.Weighted  
	semOnce      sync.Once            
	curBundle *bundle
	head *bundle
	tail      *bundle
	curFlush  *sync.WaitGroup  
	prevFlush chan bool        
	mode mode
}
type bundle struct {
	items reflect.Value    
	size  int              
	next  *bundle          
	flush *sync.WaitGroup  
}
func (bu *bundle) add(item interface{}, size int) {
	bu.items = reflect.Append(bu.items, reflect.ValueOf(item))
	bu.size += size
}
func NewBundler(itemExample interface{}, handler func(interface{})) *Bundler {
	b := &Bundler{
		DelayThreshold:       DefaultDelayThreshold,
		BundleCountThreshold: DefaultBundleCountThreshold,
		BundleByteThreshold:  DefaultBundleByteThreshold,
		BufferedByteLimit:    DefaultBufferedByteLimit,
		HandlerLimit:         1,
		handler:       handler,
		itemSliceZero: reflect.Zero(reflect.SliceOf(reflect.TypeOf(itemExample))),
		curFlush:      &sync.WaitGroup{},
	}
	return b
}
func (b *Bundler) initSemaphores() {
	b.semOnce.Do(func() {
		b.sem = semaphore.NewWeighted(int64(b.BufferedByteLimit))
	})
}
func (b *Bundler) enqueueCurBundle() {
	if b.curBundle == nil {
		return
	}
	if b.handlerCount < b.HandlerLimit {
		b.handlerCount++
		go b.handle(b.curBundle)
	} else if b.tail != nil {
		b.tail.next = b.curBundle
		b.tail = b.curBundle
	} else {
		b.head = b.curBundle
		b.tail = b.curBundle
	}
	b.curBundle = nil
	if b.flushTimer != nil {
		b.flushTimer.Stop()
		b.flushTimer = nil
	}
}
func (b *Bundler) setMode(m mode) error {
	b.mu.Lock()
	defer b.mu.Unlock()
	if b.mode == m || b.mode == none {
		b.mode = m
		return nil
	}
	return errMixedMethods
}