-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcache.go
More file actions
268 lines (220 loc) · 7.15 KB
/
cache.go
File metadata and controls
268 lines (220 loc) · 7.15 KB
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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
package goverhaul
import (
"fmt"
"github.com/gophersatwork/granular"
"github.com/mus-format/mus-go/ord"
"github.com/mus-format/mus-go/varint"
"github.com/spf13/afero"
)
// Cache provides high-performance caching using MUS serialization.
// MUS is a binary serialization format optimized for speed and size.
// This implementation uses varint encoding and ord package for optimal performance.
type Cache struct {
gCache *granular.Cache
fs afero.Fs
}
// NewCache creates a new cache with MUS encoding for maximum performance.
// MUS uses varint encoding and manual serialization for optimal efficiency.
func NewCache(path string, fs afero.Fs) (*Cache, error) {
opts := []granular.Option{}
if fs != nil {
opts = append(opts, granular.WithFs(fs))
}
cache, err := granular.New(path, opts...)
if err != nil {
return nil, fmt.Errorf("failed to create granular cache: %w", err)
}
return &Cache{
gCache: cache,
fs: fs,
}, nil
}
// AddFile adds a file entry to the cache without violations
func (c *Cache) AddFile(path string) error {
normalizedPath := NormalizePath(path)
key := granular.Key{
Inputs: []granular.Input{granular.FileInput{
Path: normalizedPath,
Fs: c.fs,
}},
}
return c.gCache.Store(key, granular.Result{})
}
// AddFileWithViolations stores violations for a file using MUS encoding
func (c *Cache) AddFileWithViolations(path string, lv []LintViolation) error {
normalizedPath := NormalizePath(path)
key := granular.Key{
Inputs: []granular.Input{granular.FileInput{
Path: normalizedPath,
Fs: c.fs,
}},
}
violations := LintViolations{
Violations: lv,
}
// Encode violations using MUS with varint encoding
data, err := marshalLintViolations(violations)
if err != nil {
return fmt.Errorf("failed to encode violations: %w", err)
}
metadata := map[string]string{
"violations": string(data),
}
res := granular.Result{
Metadata: metadata,
}
if err := c.gCache.Store(key, res); err != nil {
return fmt.Errorf("failed to store in cache: %w", err)
}
return nil
}
// HasEntry checks if a file has cached violations and returns them
func (c *Cache) HasEntry(filePath string) (LintViolations, error) {
normalizedPath := NormalizePath(filePath)
key := granular.Key{
Inputs: []granular.Input{granular.FileInput{
Path: normalizedPath,
Fs: c.fs,
}},
}
result, found, err := c.gCache.Get(key)
if !found {
return LintViolations{}, ErrEntryNotFound
}
if err != nil {
return LintViolations{}, NewCacheError("failed to read cache entry", err)
}
violations, ok := result.Metadata["violations"]
if !ok {
return LintViolations{}, nil
}
// Decode violations using MUS
lv, err := unmarshalLintViolations([]byte(violations))
if err != nil {
return LintViolations{}, fmt.Errorf("%w: %v", ErrReadingCachedViolations, err)
}
// Mark violations as cached
for i := range lv.Violations {
lv.Violations[i].Cached = true
}
return lv, nil
}
// Clear removes all entries from the cache
func (c *Cache) Clear() error {
return c.gCache.Clear()
}
// marshalLintViolations serializes LintViolations using MUS format with varint encoding
func marshalLintViolations(lv LintViolations) ([]byte, error) {
// Pre-calculate size for efficient allocation
size := lintViolationsSize(lv)
buf := make([]byte, size)
n := marshalLintViolationsTo(lv, buf)
return buf[:n], nil
}
// unmarshalLintViolations deserializes LintViolations from MUS format
func unmarshalLintViolations(data []byte) (LintViolations, error) {
lv, _, err := unmarshalLintViolationsFrom(data)
return lv, err
}
// lintViolationsSize calculates the exact size needed for MUS encoding
func lintViolationsSize(lv LintViolations) int {
// Size of the violations slice length (varint encoded)
size := varint.Uint64.Size(uint64(len(lv.Violations)))
// Size of each violation
for _, v := range lv.Violations {
size += lintViolationSize(v)
}
return size
}
// lintViolationSize calculates the size needed for a single LintViolation
// Uses ord.SizeString with varint encoding for optimal space efficiency
func lintViolationSize(v LintViolation) int {
size := 0
size += ord.SizeString(v.File, varint.PositiveInt)
size += ord.SizeString(v.Import, varint.PositiveInt)
size += ord.SizeString(v.Rule, varint.PositiveInt)
size += ord.SizeString(v.Cause, varint.PositiveInt)
size += ord.SizeString(v.Details, varint.PositiveInt)
size += ord.Bool.Size(v.Cached)
return size
}
// marshalLintViolationsTo serializes LintViolations into the provided buffer
func marshalLintViolationsTo(lv LintViolations, buf []byte) int {
// Marshal the number of violations using varint
n := varint.Uint64.Marshal(uint64(len(lv.Violations)), buf)
// Marshal each violation
for _, v := range lv.Violations {
n += marshalLintViolationTo(v, buf[n:])
}
return n
}
// marshalLintViolationTo serializes a single LintViolation into the buffer
// Uses ord.MarshalString with varint for length encoding
func marshalLintViolationTo(v LintViolation, buf []byte) int {
n := ord.MarshalString(v.File, varint.PositiveInt, buf)
n += ord.MarshalString(v.Import, varint.PositiveInt, buf[n:])
n += ord.MarshalString(v.Rule, varint.PositiveInt, buf[n:])
n += ord.MarshalString(v.Cause, varint.PositiveInt, buf[n:])
n += ord.MarshalString(v.Details, varint.PositiveInt, buf[n:])
n += ord.Bool.Marshal(v.Cached, buf[n:])
return n
}
// unmarshalLintViolationsFrom deserializes LintViolations from the buffer
func unmarshalLintViolationsFrom(buf []byte) (LintViolations, int, error) {
var lv LintViolations
// Unmarshal the number of violations using varint
length, n, err := varint.Uint64.Unmarshal(buf)
if err != nil {
return lv, n, fmt.Errorf("failed to unmarshal violations length: %w", err)
}
// Pre-allocate slice with exact capacity for efficiency
lv.Violations = make([]LintViolation, length)
// Unmarshal each violation
for i := uint64(0); i < length; i++ {
v, bytesRead, err := unmarshalLintViolationFrom(buf[n:])
if err != nil {
return lv, n, fmt.Errorf("failed to unmarshal violation at index %d: %w", i, err)
}
lv.Violations[i] = v
n += bytesRead
}
return lv, n, nil
}
// unmarshalLintViolationFrom deserializes a single LintViolation from the buffer
// Uses ord.String.Unmarshal for symmetric marshaling/unmarshaling
func unmarshalLintViolationFrom(buf []byte) (LintViolation, int, error) {
var v LintViolation
var n, m int
var err error
v.File, m, err = ord.String.Unmarshal(buf[n:])
if err != nil {
return v, n, fmt.Errorf("failed to unmarshal File: %w", err)
}
n += m
v.Import, m, err = ord.String.Unmarshal(buf[n:])
if err != nil {
return v, n, fmt.Errorf("failed to unmarshal Import: %w", err)
}
n += m
v.Rule, m, err = ord.String.Unmarshal(buf[n:])
if err != nil {
return v, n, fmt.Errorf("failed to unmarshal Rule: %w", err)
}
n += m
v.Cause, m, err = ord.String.Unmarshal(buf[n:])
if err != nil {
return v, n, fmt.Errorf("failed to unmarshal Cause: %w", err)
}
n += m
v.Details, m, err = ord.String.Unmarshal(buf[n:])
if err != nil {
return v, n, fmt.Errorf("failed to unmarshal Details: %w", err)
}
n += m
v.Cached, m, err = ord.Bool.Unmarshal(buf[n:])
if err != nil {
return v, n, fmt.Errorf("failed to unmarshal Cached: %w", err)
}
n += m
return v, n, nil
}