blob: 3e67e54335ece742b128b3a5f975e299563177c1 [file] [log] [blame]
package sftp
import (
"sync"
)
type allocator struct {
sync.Mutex
available [][]byte
// map key is the request order
used map[uint32][][]byte
}
func newAllocator() *allocator {
return &allocator{
// micro optimization: initialize available pages with an initial capacity
available: make([][]byte, 0, SftpServerWorkerCount*2),
used: make(map[uint32][][]byte),
}
}
// GetPage returns a previously allocated and unused []byte or create a new one.
// The slice have a fixed size = maxMsgLength, this value is suitable for both
// receiving new packets and reading the files to serve
func (a *allocator) GetPage(requestOrderID uint32) []byte {
a.Lock()
defer a.Unlock()
var result []byte
// get an available page and remove it from the available ones.
if len(a.available) > 0 {
truncLength := len(a.available) - 1
result = a.available[truncLength]
a.available[truncLength] = nil // clear out the internal pointer
a.available = a.available[:truncLength] // truncate the slice
}
// no preallocated slice found, just allocate a new one
if result == nil {
result = make([]byte, maxMsgLength)
}
// put result in used pages
a.used[requestOrderID] = append(a.used[requestOrderID], result)
return result
}
// ReleasePages marks unused all pages in use for the given requestID
func (a *allocator) ReleasePages(requestOrderID uint32) {
a.Lock()
defer a.Unlock()
if used := a.used[requestOrderID]; len(used) > 0 {
a.available = append(a.available, used...)
}
delete(a.used, requestOrderID)
}
// Free removes all the used and available pages.
// Call this method when the allocator is not needed anymore
func (a *allocator) Free() {
a.Lock()
defer a.Unlock()
a.available = nil
a.used = make(map[uint32][][]byte)
}
func (a *allocator) countUsedPages() int {
a.Lock()
defer a.Unlock()
num := 0
for _, p := range a.used {
num += len(p)
}
return num
}
func (a *allocator) countAvailablePages() int {
a.Lock()
defer a.Unlock()
return len(a.available)
}
func (a *allocator) isRequestOrderIDUsed(requestOrderID uint32) bool {
a.Lock()
defer a.Unlock()
_, ok := a.used[requestOrderID]
return ok
}