mirror of https://github.com/liamg/aminal.git
122 lines
2.8 KiB
Go
122 lines
2.8 KiB
Go
// Copyright 2014 The go-gl Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package mgl32
|
|
|
|
import (
|
|
"sync"
|
|
)
|
|
|
|
var (
|
|
slicePools []*sync.Pool
|
|
listLock sync.RWMutex
|
|
)
|
|
|
|
var shouldPool = true
|
|
|
|
func DisableMemoryPooling() {
|
|
shouldPool = false
|
|
}
|
|
|
|
// Returns the given memory pool. If the pool doesn't exist, it will
|
|
// create all pools up to element i. The number "i" corresponds to "p"
|
|
// in most other comments. That is, it's Ceil(log_2(whatever)). So i=0
|
|
// means you'll get the pool for slices of size 1, i=1 for size 2, i=2 for size 4,
|
|
// and so on.
|
|
//
|
|
// This is concurrency safe and uses an RWMutex to protect the list expansion.
|
|
func getPool(i int) *sync.Pool {
|
|
listLock.RLock()
|
|
if i >= len(slicePools) {
|
|
|
|
// Promote to a write lock because we now
|
|
// need to mutate the pool
|
|
listLock.RUnlock()
|
|
listLock.Lock()
|
|
defer listLock.Unlock()
|
|
|
|
for n := i - len(slicePools); n >= 0; n-- {
|
|
newFunc := genPoolNew(1 << uint(len(slicePools)))
|
|
slicePools = append(slicePools, &sync.Pool{New: newFunc})
|
|
}
|
|
} else {
|
|
defer listLock.RUnlock()
|
|
}
|
|
|
|
return slicePools[i]
|
|
}
|
|
|
|
func genPoolNew(i int) func() interface{} {
|
|
return func() interface{} {
|
|
return make([]float32, 0, i)
|
|
}
|
|
}
|
|
|
|
// Grabs a slice from the memory pool, such that its cap
|
|
// is 2^p where p is Ceil(log_2(size)). It will be downsliced
|
|
// such that the len is size.
|
|
func grabFromPool(size int) []float32 {
|
|
pool, exact := binLog(size)
|
|
|
|
// Tried to grab something of size
|
|
// zero or less
|
|
if pool == -1 {
|
|
return nil
|
|
}
|
|
|
|
// If the log is not exact, we
|
|
// need to "overallocate" so we have
|
|
// log+1
|
|
if !exact {
|
|
pool++
|
|
}
|
|
|
|
slice := getPool(pool).Get().([]float32)
|
|
slice = slice[:size]
|
|
return slice
|
|
}
|
|
|
|
// Returns a slice to the appropriate pool. If the slice does not have a cap that's precisely
|
|
// a power of 2, this will panic.
|
|
func returnToPool(slice []float32) {
|
|
if cap(slice) == 0 {
|
|
return
|
|
}
|
|
|
|
pool, exact := binLog(cap(slice))
|
|
|
|
if !exact {
|
|
panic("attempt to pool slice with non-exact cap. If you're a user, please file an issue with github.com/go-gl/mathgl about this bug. This should never happen.")
|
|
}
|
|
|
|
getPool(pool).Put(slice)
|
|
}
|
|
|
|
// This returns the integer base 2 log of the value
|
|
// and whether the log is exact or rounded down.
|
|
//
|
|
// This is only for positive integers.
|
|
//
|
|
// There are faster ways to do this, I'm open to suggestions. Most rely on knowing system endianness
|
|
// which Go makes hard to do. I'm hesistant to use float conversions and the math package because of off-by-one errors.
|
|
func binLog(val int) (int, bool) {
|
|
if val <= 0 {
|
|
return -1, false
|
|
}
|
|
|
|
exact := true
|
|
l := 0
|
|
for ; val > 1; val = val >> 1 {
|
|
// If the current lsb is 1 and the number
|
|
// is not equal to 1, this is not an exact
|
|
// log, but rather a rounding of it
|
|
if val&1 != 0 {
|
|
exact = false
|
|
}
|
|
l++
|
|
}
|
|
|
|
return l, exact
|
|
}
|