A go template renderer based on Perl's Template Toolkit
1package gott
2
3import (
4 "container/list"
5 "sync"
6)
7
8// cacheEntry holds a cached template AST
9type cacheEntry struct {
10 key string
11 tmpl *Template
12 element *list.Element // pointer to LRU list element
13}
14
15// astCache is a thread-safe LRU cache for parsed template ASTs
16type astCache struct {
17 mu sync.RWMutex
18 items map[string]*cacheEntry
19 lru *list.List // front = most recently used, back = least recently used
20 maxSize int // 0 = unlimited
21}
22
23// newCache creates a new LRU cache with the specified max size.
24// If maxSize is 0, the cache has no size limit.
25func newCache(maxSize int) *astCache {
26 return &astCache{
27 items: make(map[string]*cacheEntry),
28 lru: list.New(),
29 maxSize: maxSize,
30 }
31}
32
33// Get retrieves a cached template by key.
34// Returns the template and true if found, nil and false otherwise.
35// Accessing an entry moves it to the front of the LRU list.
36func (c *astCache) Get(key string) (*Template, bool) {
37 c.mu.Lock()
38 defer c.mu.Unlock()
39
40 entry, ok := c.items[key]
41 if !ok {
42 return nil, false
43 }
44
45 // Move to front of LRU list (most recently used)
46 c.lru.MoveToFront(entry.element)
47 return entry.tmpl, true
48}
49
50// Put stores a template in the cache.
51// If the cache is at capacity, the least recently used entry is evicted.
52func (c *astCache) Put(key string, tmpl *Template) {
53 c.mu.Lock()
54 defer c.mu.Unlock()
55
56 // Check if key already exists
57 if entry, ok := c.items[key]; ok {
58 // Update existing entry
59 entry.tmpl = tmpl
60 c.lru.MoveToFront(entry.element)
61 return
62 }
63
64 // Evict LRU entry if at capacity
65 if c.maxSize > 0 && len(c.items) >= c.maxSize {
66 c.evictLRU()
67 }
68
69 // Create new entry
70 entry := &cacheEntry{
71 key: key,
72 tmpl: tmpl,
73 }
74 entry.element = c.lru.PushFront(entry)
75 c.items[key] = entry
76}
77
78// evictLRU removes the least recently used entry.
79// Caller must hold the write lock.
80func (c *astCache) evictLRU() {
81 oldest := c.lru.Back()
82 if oldest == nil {
83 return
84 }
85
86 entry := oldest.Value.(*cacheEntry)
87 c.lru.Remove(oldest)
88 delete(c.items, entry.key)
89}
90
91// Clear removes all entries from the cache.
92func (c *astCache) Clear() {
93 c.mu.Lock()
94 defer c.mu.Unlock()
95
96 c.items = make(map[string]*cacheEntry)
97 c.lru.Init()
98}
99
100// Len returns the number of entries in the cache.
101func (c *astCache) Len() int {
102 c.mu.RLock()
103 defer c.mu.RUnlock()
104 return len(c.items)
105}