github.com/trim21/go-phpserialize@v0.0.22-0.20240301204449-2fca0319b3f0/internal/encoder/compile_cache.go (about)

     1  package encoder
     2  
     3  import (
     4  	"sync/atomic"
     5  	"unsafe"
     6  
     7  	"github.com/trim21/go-phpserialize/internal/runtime"
     8  )
     9  
    10  var (
    11  	cachedEncoder    []encoder
    12  	cachedEncoderMap unsafe.Pointer // map[uintptr]*OpcodeSet
    13  	typeAddr         *runtime.TypeAddr
    14  )
    15  
    16  func init() {
    17  	typeAddr = runtime.AnalyzeTypeAddr()
    18  	if typeAddr == nil {
    19  		typeAddr = &runtime.TypeAddr{}
    20  	}
    21  
    22  	cachedEncoder = make([]encoder, typeAddr.AddrRange>>typeAddr.AddrShift+1)
    23  }
    24  
    25  func compileToGetEncoderSlowPath(typeID uintptr) (encoder, error) {
    26  	opcodeMap := loadEncoderMap()
    27  	if codeSet, exists := opcodeMap[typeID]; exists {
    28  		return codeSet, nil
    29  	}
    30  	codeSet, err := compileTypeID(typeID)
    31  	if err != nil {
    32  		return nil, err
    33  	}
    34  	storeEncoder(typeID, codeSet, opcodeMap)
    35  	return codeSet, nil
    36  }
    37  
    38  func loadEncoderMap() map[uintptr]encoder {
    39  	p := atomic.LoadPointer(&cachedEncoderMap)
    40  	return *(*map[uintptr]encoder)(unsafe.Pointer(&p))
    41  }
    42  
    43  func storeEncoder(typ uintptr, set encoder, m map[uintptr]encoder) {
    44  	newEncoderMap := make(map[uintptr]encoder, len(m)+1)
    45  	newEncoderMap[typ] = set
    46  
    47  	for k, v := range m {
    48  		newEncoderMap[k] = v
    49  	}
    50  
    51  	atomic.StorePointer(&cachedEncoderMap, *(*unsafe.Pointer)(unsafe.Pointer(&newEncoderMap)))
    52  }
    53  
    54  func compileWithCache(rt *runtime.Type) (encoder, error) {
    55  	typeID := uintptr(unsafe.Pointer(rt))
    56  	return compileToGetCodeSet(typeID)
    57  }
    58  
    59  func compileTypeIDWithCache(typeID uintptr) (encoder, error) {
    60  	return compileToGetCodeSet(typeID)
    61  }