mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-30 11:42:24 -05:00 
			
		
		
		
	
		
			
	
	
		
			63 lines
		
	
	
	
		
			1.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			63 lines
		
	
	
	
		
			1.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
|  | package mangler | ||
|  | 
 | ||
|  | import ( | ||
|  | 	"sync/atomic" | ||
|  | 	"unsafe" | ||
|  | 
 | ||
|  | 	"codeberg.org/gruf/go-xunsafe" | ||
|  | ) | ||
|  | 
 | ||
|  | var manglers cache | ||
|  | 
 | ||
|  | // cache is a concurrency-safe map[xunsafe.TypeInfo]Mangler | ||
|  | // cache, designed for heavy reads but with unfortunately expensive | ||
|  | // writes. it is designed such that after some initial load period | ||
|  | // in which functions are cached by types, all future ops are reads. | ||
|  | type cache struct{ p unsafe.Pointer } | ||
|  | 
 | ||
|  | // Get will check cache for mangler func under key. | ||
|  | func (c *cache) Get(t xunsafe.TypeInfo) Mangler { | ||
|  | 	if p := c.load(); p != nil { | ||
|  | 		return (*p)[t] | ||
|  | 	} | ||
|  | 	return nil | ||
|  | } | ||
|  | 
 | ||
|  | // Put will place given mangler func in cache under key, if not already exists. | ||
|  | func (c *cache) Put(t xunsafe.TypeInfo, fn Mangler) { | ||
|  | 	for { | ||
|  | 		p := c.load() | ||
|  | 
 | ||
|  | 		var cache map[xunsafe.TypeInfo]Mangler | ||
|  | 
 | ||
|  | 		if p != nil { | ||
|  | 			if _, ok := (*p)[t]; ok { | ||
|  | 				return | ||
|  | 			} | ||
|  | 
 | ||
|  | 			cache = make(map[xunsafe.TypeInfo]Mangler, len(*p)+1) | ||
|  | 			for key, value := range *p { | ||
|  | 				cache[key] = value | ||
|  | 			} | ||
|  | 		} else { | ||
|  | 			cache = make(map[xunsafe.TypeInfo]Mangler, 1) | ||
|  | 		} | ||
|  | 
 | ||
|  | 		cache[t] = fn | ||
|  | 
 | ||
|  | 		if c.cas(p, &cache) { | ||
|  | 			return | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | // load is a typed wrapper around atomic.LoadPointer(). | ||
|  | func (c *cache) load() *map[xunsafe.TypeInfo]Mangler { | ||
|  | 	return (*map[xunsafe.TypeInfo]Mangler)(atomic.LoadPointer(&c.p)) | ||
|  | } | ||
|  | 
 | ||
|  | // cas is a typed wrapper around atomic.CompareAndSwapPointer(). | ||
|  | func (c *cache) cas(old, new *map[xunsafe.TypeInfo]Mangler) bool { | ||
|  | 	return atomic.CompareAndSwapPointer(&c.p, unsafe.Pointer(old), unsafe.Pointer(new)) | ||
|  | } |