package frameserver import ( "git.gammaspectra.live/S.O.N.G/Ignite/frame" "os" "path" "runtime" "sync" ) // Pool automatically grows and shrinks as-needed based on how many frame requests come in. type Pool struct { cachedFrameSeekTable []int64 seekTableLock sync.RWMutex sourcePath string pool sync.Pool } func NewPool(sourcePath string, cachedFrameSeekTable []int64) *Pool { p := &Pool{ sourcePath: sourcePath, cachedFrameSeekTable: cachedFrameSeekTable, } p.pool.New = func() any { if f, err := os.Open(p.sourcePath); err != nil { return nil } else { p.seekTableLock.RLock() defer p.seekTableLock.RUnlock() if path.Ext(p.sourcePath) == ".xz" { if s := NewXZCompressed(f, p.cachedFrameSeekTable); s != nil { runtime.SetFinalizer(s, func(s *FrameServer) { f.Close() }) return s } else { return nil } } else { if s := New(f, p.cachedFrameSeekTable); s != nil { runtime.SetFinalizer(s, func(s *FrameServer) { f.Close() }) return s } else { return nil } } } } return p } func (p *Pool) getServer() *FrameServer { if a := p.pool.Get(); a == nil { return nil } else { return a.(*FrameServer) } } func (p *Pool) GetFrameSeekTable() []int64 { p.seekTableLock.RLock() defer p.seekTableLock.RUnlock() return p.cachedFrameSeekTable } func (p *Pool) putServer(s *FrameServer) { if func() bool { p.seekTableLock.RLock() defer p.seekTableLock.RUnlock() return len(p.cachedFrameSeekTable) < len(s.GetFrameSeekTable()) }() { //cache longer table p.seekTableLock.Lock() defer p.seekTableLock.Unlock() //avoid races overwriting things if len(p.cachedFrameSeekTable) < len(s.GetFrameSeekTable()) { p.cachedFrameSeekTable = s.GetFrameSeekTable() } } p.pool.Put(s) } func (p *Pool) GetAllFrames() *frame.Stream { if s := p.getServer(); s == nil { return nil } else { defer p.putServer(s) return s.GetAllFramesLock() } } func (p *Pool) GetFrames(startFrameNumber, endFrameNumber int) *frame.Stream { if s := p.getServer(); s == nil { return nil } else { defer p.putServer(s) return s.GetFramesLock(startFrameNumber, endFrameNumber) } }