ebiten/text/v2/cache.go
2024-10-27 22:45:02 +09:00

107 lines
2.2 KiB
Go

// Copyright 2024 The Ebitengine Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package text
import (
"math"
"sync"
"sync/atomic"
"github.com/hajimehoshi/ebiten/v2/internal/hook"
)
var monotonicClock atomic.Int64
const infTime = math.MaxInt64
func init() {
hook.AppendHookOnBeforeUpdate(func() error {
monotonicClock.Add(1)
return nil
})
}
type cacheValue[Value any] struct {
value Value
// atime is the last time when the value was accessed.
atime int64
}
type cache[Key comparable, Value any] struct {
// softLimit indicates the soft limit of the number of values in the cache.
softLimit int
values map[Key]*cacheValue[Value]
// atime is the last time when the cache was accessed.
atime int64
m sync.Mutex
}
func newCache[Key comparable, Value any](softLimit int) *cache[Key, Value] {
return &cache[Key, Value]{
softLimit: softLimit,
}
}
func (c *cache[Key, Value]) getOrCreate(key Key, create func() (Value, bool)) Value {
n := monotonicClock.Load()
c.m.Lock()
defer c.m.Unlock()
e, ok := c.values[key]
if ok {
e.atime = n
return e.value
}
if c.values == nil {
c.values = map[Key]*cacheValue[Value]{}
}
ent, canExpire := create()
e = &cacheValue[Value]{
value: ent,
atime: infTime,
}
if canExpire {
e.atime = n
}
c.values[key] = e
// Clean up old entries.
if c.atime < n {
// If the number of values exceeds the soft limits, old values are removed.
// Even after cleaning up the cache, the number of values might still exceed the soft limit,
// but this is fine.
if len(c.values) > c.softLimit {
for key, e := range c.values {
// 60 is an arbitrary number.
if e.atime >= n-60 {
continue
}
delete(c.values, key)
}
}
}
c.atime = n
return e.value
}