-
Notifications
You must be signed in to change notification settings - Fork 7
/
Cache.go
114 lines (101 loc) · 2.93 KB
/
Cache.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package text
import (
"io/ioutil"
"github.com/inkyblackness/hacked/ss1/resource"
)
type keyResolver func(resource.Key) resource.Key
type textReader func(resource.Selector, resource.Key, Codepage) (string, error)
// Cache retrieves texts from a localizer and keeps them decoded until they are invalidated.
type Cache struct {
cp Codepage
localizer resource.Localizer
reader textReader
keyResolver keyResolver
texts map[resource.Key]string
}
func newCache(cp Codepage, localizer resource.Localizer, keyResolver keyResolver, reader textReader) *Cache {
cache := &Cache{
cp: cp,
localizer: localizer,
reader: reader,
keyResolver: keyResolver,
texts: make(map[resource.Key]string),
}
return cache
}
// NewLineCache returns a cache for single-block texts.
func NewLineCache(cp Codepage, localizer resource.Localizer) *Cache {
return newCache(cp, localizer, func(key resource.Key) resource.Key { return key }, readLine)
}
// NewPageCache returns a cache for resource-based texts.
func NewPageCache(cp Codepage, localizer resource.Localizer) *Cache {
return newCache(cp, localizer, func(key resource.Key) resource.Key {
return resource.KeyOf(key.ID.Plus(key.Index), key.Lang, 0)
}, readPage)
}
func readLine(selector resource.Selector, key resource.Key, cp Codepage) (string, error) {
view, err := selector.Select(key.ID)
if err != nil {
return "", err
}
if view.ContentType() != resource.Text {
return "", resource.ErrWrongType(key, resource.Text)
}
reader, err := view.Block(key.Index)
if err != nil {
return "", err
}
raw, err := ioutil.ReadAll(reader)
if err != nil {
return "", err
}
return cp.Decode(raw), nil
}
func readPage(selector resource.Selector, key resource.Key, cp Codepage) (string, error) {
view, err := selector.Select(key.ID.Plus(key.Index))
if err != nil {
return "", err
}
if view.ContentType() != resource.Text {
return "", resource.ErrWrongType(key, resource.Text)
}
blockCount := view.BlockCount()
value := ""
for block := 0; block < blockCount; block++ {
reader, err := view.Block(block)
if err != nil {
return "", err
}
raw, err := ioutil.ReadAll(reader)
if err != nil {
return "", err
}
value += cp.Decode(raw)
}
return value, nil
}
// InvalidateResources lets the cache remove any texts from resources that are specified in the given slice.
func (cache *Cache) InvalidateResources(ids []resource.ID) {
for _, id := range ids {
for key := range cache.texts {
if key.ID == id {
delete(cache.texts, key)
}
}
}
}
// Text retrieves and caches the text of given key.
func (cache *Cache) Text(key resource.Key) (string, error) {
cacheKey := cache.keyResolver(key)
value, existing := cache.texts[cacheKey]
if existing {
return value, nil
}
selector := cache.localizer.LocalizedResources(key.Lang)
value, err := cache.reader(selector, key, cache.cp)
if err != nil {
return "", err
}
cache.texts[cacheKey] = value
return value, nil
}