forked from AdguardTeam/urlfilter
-
Notifications
You must be signed in to change notification settings - Fork 0
/
rule_storage.go
159 lines (128 loc) · 4.02 KB
/
rule_storage.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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package filterlist
import (
"fmt"
"sync"
"github.com/bikecrazyy/urlfilter/rules"
"github.com/AdguardTeam/golibs/log"
"github.com/joomcode/errorx"
)
// RuleStorage is an abstraction that combines several rule lists
// It can be scanned using RuleStorageScanner, and also it allows
// retrieving rules by its index
//
// The idea is to keep rules in a serialized format (even original format in the case of FileRuleList)
// and create them in a lazy manner only when we really need them. When the filtering engine is
// being initialized, we need to scan the rule lists once in order to fill up the lookup tables.
// We use rule indexes as a unique rule identifier instead of the rule itself.
// The rule is created (see RetrieveRule) only when there's a chance that it's needed.
//
// Rule index is an int64 value that actually consists of two int32 values:
// One is the rule list identifier, and the second is the index of the rule inside of that list.
type RuleStorage struct {
// Lists is an array of rules lists which can be accessed
// using this RuleStorage
Lists []RuleList
listsMap map[int]RuleList // map with rule lists. map key is the list ID.
cache map[int64]rules.Rule // cache with the rules which were retrieved.
sync.Mutex
}
// NewRuleStorage creates a new instance of the RuleStorage
// and validates the list of rules specified
func NewRuleStorage(lists []RuleList) (*RuleStorage, error) {
if lists == nil {
lists = make([]RuleList, 0)
}
listsMap := make(map[int]RuleList)
for _, list := range lists {
if _, ok := listsMap[list.GetID()]; ok {
return nil, fmt.Errorf("duplicate list ID: %d", list.GetID())
}
listsMap[list.GetID()] = list
}
return &RuleStorage{
Lists: lists,
listsMap: listsMap,
cache: map[int64]rules.Rule{},
}, nil
}
// NewRuleStorageScanner creates a new instance of RuleStorageScanner.
// It can be used to read and parse all the storage contents.
func (s *RuleStorage) NewRuleStorageScanner() *RuleStorageScanner {
var scanners []*RuleScanner
for _, list := range s.Lists {
scanner := list.NewScanner()
scanners = append(scanners, scanner)
}
return &RuleStorageScanner{
Scanners: scanners,
}
}
// RetrieveRule looks for the filtering rule in this storage
// storageIdx is the lookup index that you can get from the rule storage scanner
func (s *RuleStorage) RetrieveRule(storageIdx int64) (rules.Rule, error) {
s.Lock()
defer s.Unlock()
rule, ok := s.cache[storageIdx]
if ok {
return rule, nil
}
listID, ruleIdx := storageIdxToRuleListIdx(storageIdx)
list, ok := s.listsMap[int(listID)]
if !ok {
return nil, fmt.Errorf("list %d does not exist", listID)
}
f, err := list.RetrieveRule(int(ruleIdx))
if f != nil {
s.cache[storageIdx] = f
}
return f, err
}
// RetrieveNetworkRule is a helper method that retrieves a network rule from the storage
// It returns a pointer to the rule or nil in any other case (not found or error)
func (s *RuleStorage) RetrieveNetworkRule(idx int64) *rules.NetworkRule {
r, err := s.RetrieveRule(idx)
if err != nil {
log.Error("Cannot retrieve rule %d: %s", idx, err)
return nil
}
v, ok := r.(*rules.NetworkRule)
if ok {
return v
}
return nil
}
// RetrieveHostRule is a helper method that retrieves a host rule from the storage
// It returns a pointer to the rule or nil in any other case (not found or error)
func (s *RuleStorage) RetrieveHostRule(idx int64) *rules.HostRule {
r, err := s.RetrieveRule(idx)
if err != nil {
log.Error("Cannot retrieve rule %d: %s", idx, err)
return nil
}
v, ok := r.(*rules.HostRule)
if ok {
return v
}
return nil
}
// Close closes the storage instance
func (s *RuleStorage) Close() error {
if len(s.Lists) == 0 {
return nil
}
var errs []error
for _, l := range s.Lists {
err := l.Close()
if err != nil {
errs = append(errs, err)
}
}
if len(errs) > 0 {
return errorx.DecorateMany("couldn't close all rule lists", errs...)
}
return nil
}
// GetCacheSize returns the size of the in-memory rules cache
func (s *RuleStorage) GetCacheSize() int {
return len(s.cache)
}