mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-04 09:02:25 -06:00 
			
		
		
		
	
		
			
				
	
	
		
			173 lines
		
	
	
	
		
			3.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			173 lines
		
	
	
	
		
			3.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
 * Copyright 2021 ByteDance Inc.
 | 
						|
 *
 | 
						|
 * 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 caching
 | 
						|
 | 
						|
import (
 | 
						|
    `sync`
 | 
						|
    `sync/atomic`
 | 
						|
    `unsafe`
 | 
						|
 | 
						|
    `github.com/bytedance/sonic/internal/rt`
 | 
						|
)
 | 
						|
 | 
						|
/** Program Map **/
 | 
						|
 | 
						|
const (
 | 
						|
    _LoadFactor   = 0.5
 | 
						|
    _InitCapacity = 4096    // must be a power of 2
 | 
						|
)
 | 
						|
 | 
						|
type _ProgramMap struct {
 | 
						|
    n uint64
 | 
						|
    m uint32
 | 
						|
    b []_ProgramEntry
 | 
						|
}
 | 
						|
 | 
						|
type _ProgramEntry struct {
 | 
						|
    vt *rt.GoType
 | 
						|
    fn interface{}
 | 
						|
}
 | 
						|
 | 
						|
func newProgramMap() *_ProgramMap {
 | 
						|
    return &_ProgramMap {
 | 
						|
        n: 0,
 | 
						|
        m: _InitCapacity - 1,
 | 
						|
        b: make([]_ProgramEntry, _InitCapacity),
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
func (self *_ProgramMap) copy() *_ProgramMap {
 | 
						|
    fork := &_ProgramMap{
 | 
						|
        n: self.n,
 | 
						|
        m: self.m,
 | 
						|
        b: make([]_ProgramEntry, len(self.b)),
 | 
						|
    }
 | 
						|
    for i, f := range self.b {
 | 
						|
        fork.b[i] = f
 | 
						|
    }
 | 
						|
    return fork
 | 
						|
}
 | 
						|
 | 
						|
func (self *_ProgramMap) get(vt *rt.GoType) interface{} {
 | 
						|
    i := self.m + 1
 | 
						|
    p := vt.Hash & self.m
 | 
						|
 | 
						|
    /* linear probing */
 | 
						|
    for ; i > 0; i-- {
 | 
						|
        if b := self.b[p]; b.vt == vt {
 | 
						|
            return b.fn
 | 
						|
        } else if b.vt == nil {
 | 
						|
            break
 | 
						|
        } else {
 | 
						|
            p = (p + 1) & self.m
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /* not found */
 | 
						|
    return nil
 | 
						|
}
 | 
						|
 | 
						|
func (self *_ProgramMap) add(vt *rt.GoType, fn interface{}) *_ProgramMap {
 | 
						|
    p := self.copy()
 | 
						|
    f := float64(atomic.LoadUint64(&p.n) + 1) / float64(p.m + 1)
 | 
						|
 | 
						|
    /* check for load factor */
 | 
						|
    if f > _LoadFactor {
 | 
						|
        p = p.rehash()
 | 
						|
    }
 | 
						|
 | 
						|
    /* insert the value */
 | 
						|
    p.insert(vt, fn)
 | 
						|
    return p
 | 
						|
}
 | 
						|
 | 
						|
func (self *_ProgramMap) rehash() *_ProgramMap {
 | 
						|
    c := (self.m + 1) << 1
 | 
						|
    r := &_ProgramMap{m: c - 1, b: make([]_ProgramEntry, int(c))}
 | 
						|
 | 
						|
    /* rehash every entry */
 | 
						|
    for i := uint32(0); i <= self.m; i++ {
 | 
						|
        if b := self.b[i]; b.vt != nil {
 | 
						|
            r.insert(b.vt, b.fn)
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /* rebuild successful */
 | 
						|
    return r
 | 
						|
}
 | 
						|
 | 
						|
func (self *_ProgramMap) insert(vt *rt.GoType, fn interface{}) {
 | 
						|
    h := vt.Hash
 | 
						|
    p := h & self.m
 | 
						|
 | 
						|
    /* linear probing */
 | 
						|
    for i := uint32(0); i <= self.m; i++ {
 | 
						|
        if b := &self.b[p]; b.vt != nil {
 | 
						|
            p += 1
 | 
						|
            p &= self.m
 | 
						|
        } else {
 | 
						|
            b.vt = vt
 | 
						|
            b.fn = fn
 | 
						|
            atomic.AddUint64(&self.n, 1)
 | 
						|
            return
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /* should never happens */
 | 
						|
    panic("no available slots")
 | 
						|
}
 | 
						|
 | 
						|
/** RCU Program Cache **/
 | 
						|
 | 
						|
type ProgramCache struct {
 | 
						|
    m sync.Mutex
 | 
						|
    p unsafe.Pointer
 | 
						|
}
 | 
						|
 | 
						|
func CreateProgramCache() *ProgramCache {
 | 
						|
    return &ProgramCache {
 | 
						|
        m: sync.Mutex{},
 | 
						|
        p: unsafe.Pointer(newProgramMap()),
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
func (self *ProgramCache) Get(vt *rt.GoType) interface{} {
 | 
						|
    return (*_ProgramMap)(atomic.LoadPointer(&self.p)).get(vt)
 | 
						|
}
 | 
						|
 | 
						|
func (self *ProgramCache) Compute(vt *rt.GoType, compute func(*rt.GoType, ... interface{}) (interface{}, error), ex ...interface{}) (interface{}, error) {
 | 
						|
    var err error
 | 
						|
    var val interface{}
 | 
						|
 | 
						|
    /* use defer to prevent inlining of this function */
 | 
						|
    self.m.Lock()
 | 
						|
    defer self.m.Unlock()
 | 
						|
 | 
						|
    /* double check with write lock held */
 | 
						|
    if val = self.Get(vt); val != nil {
 | 
						|
        return val, nil
 | 
						|
    }
 | 
						|
 | 
						|
    /* compute the value */
 | 
						|
    if val, err = compute(vt, ex...); err != nil {
 | 
						|
        return nil, err
 | 
						|
    }
 | 
						|
 | 
						|
    /* update the RCU cache */
 | 
						|
    atomic.StorePointer(&self.p, unsafe.Pointer((*_ProgramMap)(atomic.LoadPointer(&self.p)).add(vt, val)))
 | 
						|
    return val, nil
 | 
						|
}
 |