netramesh
1161 строка · 30.3 Кб
1package cache
2
3import (
4"encoding/gob"
5"fmt"
6"io"
7"os"
8"runtime"
9"sync"
10"time"
11)
12
13type Item struct {
14Object interface{}
15Expiration int64
16}
17
18// Returns true if the item has expired.
19func (item Item) Expired() bool {
20if item.Expiration == 0 {
21return false
22}
23return time.Now().UnixNano() > item.Expiration
24}
25
26const (
27// For use with functions that take an expiration time.
28NoExpiration time.Duration = -1
29// For use with functions that take an expiration time. Equivalent to
30// passing in the same expiration duration as was given to New() or
31// NewFrom() when the cache was created (e.g. 5 minutes.)
32DefaultExpiration time.Duration = 0
33)
34
35type Cache struct {
36*cache
37// If this is confusing, see the comment at the bottom of New()
38}
39
40type cache struct {
41defaultExpiration time.Duration
42items map[string]Item
43mu sync.RWMutex
44onEvicted func(string, interface{})
45janitor *janitor
46}
47
48// Add an item to the cache, replacing any existing item. If the duration is 0
49// (DefaultExpiration), the cache's default expiration time is used. If it is -1
50// (NoExpiration), the item never expires.
51func (c *cache) Set(k string, x interface{}, d time.Duration) {
52// "Inlining" of set
53var e int64
54if d == DefaultExpiration {
55d = c.defaultExpiration
56}
57if d > 0 {
58e = time.Now().Add(d).UnixNano()
59}
60c.mu.Lock()
61c.items[k] = Item{
62Object: x,
63Expiration: e,
64}
65// TODO: Calls to mu.Unlock are currently not deferred because defer
66// adds ~200 ns (as of go1.)
67c.mu.Unlock()
68}
69
70func (c *cache) set(k string, x interface{}, d time.Duration) {
71var e int64
72if d == DefaultExpiration {
73d = c.defaultExpiration
74}
75if d > 0 {
76e = time.Now().Add(d).UnixNano()
77}
78c.items[k] = Item{
79Object: x,
80Expiration: e,
81}
82}
83
84// Add an item to the cache, replacing any existing item, using the default
85// expiration.
86func (c *cache) SetDefault(k string, x interface{}) {
87c.Set(k, x, DefaultExpiration)
88}
89
90// Add an item to the cache only if an item doesn't already exist for the given
91// key, or if the existing item has expired. Returns an error otherwise.
92func (c *cache) Add(k string, x interface{}, d time.Duration) error {
93c.mu.Lock()
94_, found := c.get(k)
95if found {
96c.mu.Unlock()
97return fmt.Errorf("Item %s already exists", k)
98}
99c.set(k, x, d)
100c.mu.Unlock()
101return nil
102}
103
104// Set a new value for the cache key only if it already exists, and the existing
105// item hasn't expired. Returns an error otherwise.
106func (c *cache) Replace(k string, x interface{}, d time.Duration) error {
107c.mu.Lock()
108_, found := c.get(k)
109if !found {
110c.mu.Unlock()
111return fmt.Errorf("Item %s doesn't exist", k)
112}
113c.set(k, x, d)
114c.mu.Unlock()
115return nil
116}
117
118// Get an item from the cache. Returns the item or nil, and a bool indicating
119// whether the key was found.
120func (c *cache) Get(k string) (interface{}, bool) {
121c.mu.RLock()
122// "Inlining" of get and Expired
123item, found := c.items[k]
124if !found {
125c.mu.RUnlock()
126return nil, false
127}
128if item.Expiration > 0 {
129if time.Now().UnixNano() > item.Expiration {
130c.mu.RUnlock()
131return nil, false
132}
133}
134c.mu.RUnlock()
135return item.Object, true
136}
137
138// GetWithExpiration returns an item and its expiration time from the cache.
139// It returns the item or nil, the expiration time if one is set (if the item
140// never expires a zero value for time.Time is returned), and a bool indicating
141// whether the key was found.
142func (c *cache) GetWithExpiration(k string) (interface{}, time.Time, bool) {
143c.mu.RLock()
144// "Inlining" of get and Expired
145item, found := c.items[k]
146if !found {
147c.mu.RUnlock()
148return nil, time.Time{}, false
149}
150
151if item.Expiration > 0 {
152if time.Now().UnixNano() > item.Expiration {
153c.mu.RUnlock()
154return nil, time.Time{}, false
155}
156
157// Return the item and the expiration time
158c.mu.RUnlock()
159return item.Object, time.Unix(0, item.Expiration), true
160}
161
162// If expiration <= 0 (i.e. no expiration time set) then return the item
163// and a zeroed time.Time
164c.mu.RUnlock()
165return item.Object, time.Time{}, true
166}
167
168func (c *cache) get(k string) (interface{}, bool) {
169item, found := c.items[k]
170if !found {
171return nil, false
172}
173// "Inlining" of Expired
174if item.Expiration > 0 {
175if time.Now().UnixNano() > item.Expiration {
176return nil, false
177}
178}
179return item.Object, true
180}
181
182// Increment an item of type int, int8, int16, int32, int64, uintptr, uint,
183// uint8, uint32, or uint64, float32 or float64 by n. Returns an error if the
184// item's value is not an integer, if it was not found, or if it is not
185// possible to increment it by n. To retrieve the incremented value, use one
186// of the specialized methods, e.g. IncrementInt64.
187func (c *cache) Increment(k string, n int64) error {
188c.mu.Lock()
189v, found := c.items[k]
190if !found || v.Expired() {
191c.mu.Unlock()
192return fmt.Errorf("Item %s not found", k)
193}
194switch v.Object.(type) {
195case int:
196v.Object = v.Object.(int) + int(n)
197case int8:
198v.Object = v.Object.(int8) + int8(n)
199case int16:
200v.Object = v.Object.(int16) + int16(n)
201case int32:
202v.Object = v.Object.(int32) + int32(n)
203case int64:
204v.Object = v.Object.(int64) + n
205case uint:
206v.Object = v.Object.(uint) + uint(n)
207case uintptr:
208v.Object = v.Object.(uintptr) + uintptr(n)
209case uint8:
210v.Object = v.Object.(uint8) + uint8(n)
211case uint16:
212v.Object = v.Object.(uint16) + uint16(n)
213case uint32:
214v.Object = v.Object.(uint32) + uint32(n)
215case uint64:
216v.Object = v.Object.(uint64) + uint64(n)
217case float32:
218v.Object = v.Object.(float32) + float32(n)
219case float64:
220v.Object = v.Object.(float64) + float64(n)
221default:
222c.mu.Unlock()
223return fmt.Errorf("The value for %s is not an integer", k)
224}
225c.items[k] = v
226c.mu.Unlock()
227return nil
228}
229
230// Increment an item of type float32 or float64 by n. Returns an error if the
231// item's value is not floating point, if it was not found, or if it is not
232// possible to increment it by n. Pass a negative number to decrement the
233// value. To retrieve the incremented value, use one of the specialized methods,
234// e.g. IncrementFloat64.
235func (c *cache) IncrementFloat(k string, n float64) error {
236c.mu.Lock()
237v, found := c.items[k]
238if !found || v.Expired() {
239c.mu.Unlock()
240return fmt.Errorf("Item %s not found", k)
241}
242switch v.Object.(type) {
243case float32:
244v.Object = v.Object.(float32) + float32(n)
245case float64:
246v.Object = v.Object.(float64) + n
247default:
248c.mu.Unlock()
249return fmt.Errorf("The value for %s does not have type float32 or float64", k)
250}
251c.items[k] = v
252c.mu.Unlock()
253return nil
254}
255
256// Increment an item of type int by n. Returns an error if the item's value is
257// not an int, or if it was not found. If there is no error, the incremented
258// value is returned.
259func (c *cache) IncrementInt(k string, n int) (int, error) {
260c.mu.Lock()
261v, found := c.items[k]
262if !found || v.Expired() {
263c.mu.Unlock()
264return 0, fmt.Errorf("Item %s not found", k)
265}
266rv, ok := v.Object.(int)
267if !ok {
268c.mu.Unlock()
269return 0, fmt.Errorf("The value for %s is not an int", k)
270}
271nv := rv + n
272v.Object = nv
273c.items[k] = v
274c.mu.Unlock()
275return nv, nil
276}
277
278// Increment an item of type int8 by n. Returns an error if the item's value is
279// not an int8, or if it was not found. If there is no error, the incremented
280// value is returned.
281func (c *cache) IncrementInt8(k string, n int8) (int8, error) {
282c.mu.Lock()
283v, found := c.items[k]
284if !found || v.Expired() {
285c.mu.Unlock()
286return 0, fmt.Errorf("Item %s not found", k)
287}
288rv, ok := v.Object.(int8)
289if !ok {
290c.mu.Unlock()
291return 0, fmt.Errorf("The value for %s is not an int8", k)
292}
293nv := rv + n
294v.Object = nv
295c.items[k] = v
296c.mu.Unlock()
297return nv, nil
298}
299
300// Increment an item of type int16 by n. Returns an error if the item's value is
301// not an int16, or if it was not found. If there is no error, the incremented
302// value is returned.
303func (c *cache) IncrementInt16(k string, n int16) (int16, error) {
304c.mu.Lock()
305v, found := c.items[k]
306if !found || v.Expired() {
307c.mu.Unlock()
308return 0, fmt.Errorf("Item %s not found", k)
309}
310rv, ok := v.Object.(int16)
311if !ok {
312c.mu.Unlock()
313return 0, fmt.Errorf("The value for %s is not an int16", k)
314}
315nv := rv + n
316v.Object = nv
317c.items[k] = v
318c.mu.Unlock()
319return nv, nil
320}
321
322// Increment an item of type int32 by n. Returns an error if the item's value is
323// not an int32, or if it was not found. If there is no error, the incremented
324// value is returned.
325func (c *cache) IncrementInt32(k string, n int32) (int32, error) {
326c.mu.Lock()
327v, found := c.items[k]
328if !found || v.Expired() {
329c.mu.Unlock()
330return 0, fmt.Errorf("Item %s not found", k)
331}
332rv, ok := v.Object.(int32)
333if !ok {
334c.mu.Unlock()
335return 0, fmt.Errorf("The value for %s is not an int32", k)
336}
337nv := rv + n
338v.Object = nv
339c.items[k] = v
340c.mu.Unlock()
341return nv, nil
342}
343
344// Increment an item of type int64 by n. Returns an error if the item's value is
345// not an int64, or if it was not found. If there is no error, the incremented
346// value is returned.
347func (c *cache) IncrementInt64(k string, n int64) (int64, error) {
348c.mu.Lock()
349v, found := c.items[k]
350if !found || v.Expired() {
351c.mu.Unlock()
352return 0, fmt.Errorf("Item %s not found", k)
353}
354rv, ok := v.Object.(int64)
355if !ok {
356c.mu.Unlock()
357return 0, fmt.Errorf("The value for %s is not an int64", k)
358}
359nv := rv + n
360v.Object = nv
361c.items[k] = v
362c.mu.Unlock()
363return nv, nil
364}
365
366// Increment an item of type uint by n. Returns an error if the item's value is
367// not an uint, or if it was not found. If there is no error, the incremented
368// value is returned.
369func (c *cache) IncrementUint(k string, n uint) (uint, error) {
370c.mu.Lock()
371v, found := c.items[k]
372if !found || v.Expired() {
373c.mu.Unlock()
374return 0, fmt.Errorf("Item %s not found", k)
375}
376rv, ok := v.Object.(uint)
377if !ok {
378c.mu.Unlock()
379return 0, fmt.Errorf("The value for %s is not an uint", k)
380}
381nv := rv + n
382v.Object = nv
383c.items[k] = v
384c.mu.Unlock()
385return nv, nil
386}
387
388// Increment an item of type uintptr by n. Returns an error if the item's value
389// is not an uintptr, or if it was not found. If there is no error, the
390// incremented value is returned.
391func (c *cache) IncrementUintptr(k string, n uintptr) (uintptr, error) {
392c.mu.Lock()
393v, found := c.items[k]
394if !found || v.Expired() {
395c.mu.Unlock()
396return 0, fmt.Errorf("Item %s not found", k)
397}
398rv, ok := v.Object.(uintptr)
399if !ok {
400c.mu.Unlock()
401return 0, fmt.Errorf("The value for %s is not an uintptr", k)
402}
403nv := rv + n
404v.Object = nv
405c.items[k] = v
406c.mu.Unlock()
407return nv, nil
408}
409
410// Increment an item of type uint8 by n. Returns an error if the item's value
411// is not an uint8, or if it was not found. If there is no error, the
412// incremented value is returned.
413func (c *cache) IncrementUint8(k string, n uint8) (uint8, error) {
414c.mu.Lock()
415v, found := c.items[k]
416if !found || v.Expired() {
417c.mu.Unlock()
418return 0, fmt.Errorf("Item %s not found", k)
419}
420rv, ok := v.Object.(uint8)
421if !ok {
422c.mu.Unlock()
423return 0, fmt.Errorf("The value for %s is not an uint8", k)
424}
425nv := rv + n
426v.Object = nv
427c.items[k] = v
428c.mu.Unlock()
429return nv, nil
430}
431
432// Increment an item of type uint16 by n. Returns an error if the item's value
433// is not an uint16, or if it was not found. If there is no error, the
434// incremented value is returned.
435func (c *cache) IncrementUint16(k string, n uint16) (uint16, error) {
436c.mu.Lock()
437v, found := c.items[k]
438if !found || v.Expired() {
439c.mu.Unlock()
440return 0, fmt.Errorf("Item %s not found", k)
441}
442rv, ok := v.Object.(uint16)
443if !ok {
444c.mu.Unlock()
445return 0, fmt.Errorf("The value for %s is not an uint16", k)
446}
447nv := rv + n
448v.Object = nv
449c.items[k] = v
450c.mu.Unlock()
451return nv, nil
452}
453
454// Increment an item of type uint32 by n. Returns an error if the item's value
455// is not an uint32, or if it was not found. If there is no error, the
456// incremented value is returned.
457func (c *cache) IncrementUint32(k string, n uint32) (uint32, error) {
458c.mu.Lock()
459v, found := c.items[k]
460if !found || v.Expired() {
461c.mu.Unlock()
462return 0, fmt.Errorf("Item %s not found", k)
463}
464rv, ok := v.Object.(uint32)
465if !ok {
466c.mu.Unlock()
467return 0, fmt.Errorf("The value for %s is not an uint32", k)
468}
469nv := rv + n
470v.Object = nv
471c.items[k] = v
472c.mu.Unlock()
473return nv, nil
474}
475
476// Increment an item of type uint64 by n. Returns an error if the item's value
477// is not an uint64, or if it was not found. If there is no error, the
478// incremented value is returned.
479func (c *cache) IncrementUint64(k string, n uint64) (uint64, error) {
480c.mu.Lock()
481v, found := c.items[k]
482if !found || v.Expired() {
483c.mu.Unlock()
484return 0, fmt.Errorf("Item %s not found", k)
485}
486rv, ok := v.Object.(uint64)
487if !ok {
488c.mu.Unlock()
489return 0, fmt.Errorf("The value for %s is not an uint64", k)
490}
491nv := rv + n
492v.Object = nv
493c.items[k] = v
494c.mu.Unlock()
495return nv, nil
496}
497
498// Increment an item of type float32 by n. Returns an error if the item's value
499// is not an float32, or if it was not found. If there is no error, the
500// incremented value is returned.
501func (c *cache) IncrementFloat32(k string, n float32) (float32, error) {
502c.mu.Lock()
503v, found := c.items[k]
504if !found || v.Expired() {
505c.mu.Unlock()
506return 0, fmt.Errorf("Item %s not found", k)
507}
508rv, ok := v.Object.(float32)
509if !ok {
510c.mu.Unlock()
511return 0, fmt.Errorf("The value for %s is not an float32", k)
512}
513nv := rv + n
514v.Object = nv
515c.items[k] = v
516c.mu.Unlock()
517return nv, nil
518}
519
520// Increment an item of type float64 by n. Returns an error if the item's value
521// is not an float64, or if it was not found. If there is no error, the
522// incremented value is returned.
523func (c *cache) IncrementFloat64(k string, n float64) (float64, error) {
524c.mu.Lock()
525v, found := c.items[k]
526if !found || v.Expired() {
527c.mu.Unlock()
528return 0, fmt.Errorf("Item %s not found", k)
529}
530rv, ok := v.Object.(float64)
531if !ok {
532c.mu.Unlock()
533return 0, fmt.Errorf("The value for %s is not an float64", k)
534}
535nv := rv + n
536v.Object = nv
537c.items[k] = v
538c.mu.Unlock()
539return nv, nil
540}
541
542// Decrement an item of type int, int8, int16, int32, int64, uintptr, uint,
543// uint8, uint32, or uint64, float32 or float64 by n. Returns an error if the
544// item's value is not an integer, if it was not found, or if it is not
545// possible to decrement it by n. To retrieve the decremented value, use one
546// of the specialized methods, e.g. DecrementInt64.
547func (c *cache) Decrement(k string, n int64) error {
548// TODO: Implement Increment and Decrement more cleanly.
549// (Cannot do Increment(k, n*-1) for uints.)
550c.mu.Lock()
551v, found := c.items[k]
552if !found || v.Expired() {
553c.mu.Unlock()
554return fmt.Errorf("Item not found")
555}
556switch v.Object.(type) {
557case int:
558v.Object = v.Object.(int) - int(n)
559case int8:
560v.Object = v.Object.(int8) - int8(n)
561case int16:
562v.Object = v.Object.(int16) - int16(n)
563case int32:
564v.Object = v.Object.(int32) - int32(n)
565case int64:
566v.Object = v.Object.(int64) - n
567case uint:
568v.Object = v.Object.(uint) - uint(n)
569case uintptr:
570v.Object = v.Object.(uintptr) - uintptr(n)
571case uint8:
572v.Object = v.Object.(uint8) - uint8(n)
573case uint16:
574v.Object = v.Object.(uint16) - uint16(n)
575case uint32:
576v.Object = v.Object.(uint32) - uint32(n)
577case uint64:
578v.Object = v.Object.(uint64) - uint64(n)
579case float32:
580v.Object = v.Object.(float32) - float32(n)
581case float64:
582v.Object = v.Object.(float64) - float64(n)
583default:
584c.mu.Unlock()
585return fmt.Errorf("The value for %s is not an integer", k)
586}
587c.items[k] = v
588c.mu.Unlock()
589return nil
590}
591
592// Decrement an item of type float32 or float64 by n. Returns an error if the
593// item's value is not floating point, if it was not found, or if it is not
594// possible to decrement it by n. Pass a negative number to decrement the
595// value. To retrieve the decremented value, use one of the specialized methods,
596// e.g. DecrementFloat64.
597func (c *cache) DecrementFloat(k string, n float64) error {
598c.mu.Lock()
599v, found := c.items[k]
600if !found || v.Expired() {
601c.mu.Unlock()
602return fmt.Errorf("Item %s not found", k)
603}
604switch v.Object.(type) {
605case float32:
606v.Object = v.Object.(float32) - float32(n)
607case float64:
608v.Object = v.Object.(float64) - n
609default:
610c.mu.Unlock()
611return fmt.Errorf("The value for %s does not have type float32 or float64", k)
612}
613c.items[k] = v
614c.mu.Unlock()
615return nil
616}
617
618// Decrement an item of type int by n. Returns an error if the item's value is
619// not an int, or if it was not found. If there is no error, the decremented
620// value is returned.
621func (c *cache) DecrementInt(k string, n int) (int, error) {
622c.mu.Lock()
623v, found := c.items[k]
624if !found || v.Expired() {
625c.mu.Unlock()
626return 0, fmt.Errorf("Item %s not found", k)
627}
628rv, ok := v.Object.(int)
629if !ok {
630c.mu.Unlock()
631return 0, fmt.Errorf("The value for %s is not an int", k)
632}
633nv := rv - n
634v.Object = nv
635c.items[k] = v
636c.mu.Unlock()
637return nv, nil
638}
639
640// Decrement an item of type int8 by n. Returns an error if the item's value is
641// not an int8, or if it was not found. If there is no error, the decremented
642// value is returned.
643func (c *cache) DecrementInt8(k string, n int8) (int8, error) {
644c.mu.Lock()
645v, found := c.items[k]
646if !found || v.Expired() {
647c.mu.Unlock()
648return 0, fmt.Errorf("Item %s not found", k)
649}
650rv, ok := v.Object.(int8)
651if !ok {
652c.mu.Unlock()
653return 0, fmt.Errorf("The value for %s is not an int8", k)
654}
655nv := rv - n
656v.Object = nv
657c.items[k] = v
658c.mu.Unlock()
659return nv, nil
660}
661
662// Decrement an item of type int16 by n. Returns an error if the item's value is
663// not an int16, or if it was not found. If there is no error, the decremented
664// value is returned.
665func (c *cache) DecrementInt16(k string, n int16) (int16, error) {
666c.mu.Lock()
667v, found := c.items[k]
668if !found || v.Expired() {
669c.mu.Unlock()
670return 0, fmt.Errorf("Item %s not found", k)
671}
672rv, ok := v.Object.(int16)
673if !ok {
674c.mu.Unlock()
675return 0, fmt.Errorf("The value for %s is not an int16", k)
676}
677nv := rv - n
678v.Object = nv
679c.items[k] = v
680c.mu.Unlock()
681return nv, nil
682}
683
684// Decrement an item of type int32 by n. Returns an error if the item's value is
685// not an int32, or if it was not found. If there is no error, the decremented
686// value is returned.
687func (c *cache) DecrementInt32(k string, n int32) (int32, error) {
688c.mu.Lock()
689v, found := c.items[k]
690if !found || v.Expired() {
691c.mu.Unlock()
692return 0, fmt.Errorf("Item %s not found", k)
693}
694rv, ok := v.Object.(int32)
695if !ok {
696c.mu.Unlock()
697return 0, fmt.Errorf("The value for %s is not an int32", k)
698}
699nv := rv - n
700v.Object = nv
701c.items[k] = v
702c.mu.Unlock()
703return nv, nil
704}
705
706// Decrement an item of type int64 by n. Returns an error if the item's value is
707// not an int64, or if it was not found. If there is no error, the decremented
708// value is returned.
709func (c *cache) DecrementInt64(k string, n int64) (int64, error) {
710c.mu.Lock()
711v, found := c.items[k]
712if !found || v.Expired() {
713c.mu.Unlock()
714return 0, fmt.Errorf("Item %s not found", k)
715}
716rv, ok := v.Object.(int64)
717if !ok {
718c.mu.Unlock()
719return 0, fmt.Errorf("The value for %s is not an int64", k)
720}
721nv := rv - n
722v.Object = nv
723c.items[k] = v
724c.mu.Unlock()
725return nv, nil
726}
727
728// Decrement an item of type uint by n. Returns an error if the item's value is
729// not an uint, or if it was not found. If there is no error, the decremented
730// value is returned.
731func (c *cache) DecrementUint(k string, n uint) (uint, error) {
732c.mu.Lock()
733v, found := c.items[k]
734if !found || v.Expired() {
735c.mu.Unlock()
736return 0, fmt.Errorf("Item %s not found", k)
737}
738rv, ok := v.Object.(uint)
739if !ok {
740c.mu.Unlock()
741return 0, fmt.Errorf("The value for %s is not an uint", k)
742}
743nv := rv - n
744v.Object = nv
745c.items[k] = v
746c.mu.Unlock()
747return nv, nil
748}
749
750// Decrement an item of type uintptr by n. Returns an error if the item's value
751// is not an uintptr, or if it was not found. If there is no error, the
752// decremented value is returned.
753func (c *cache) DecrementUintptr(k string, n uintptr) (uintptr, error) {
754c.mu.Lock()
755v, found := c.items[k]
756if !found || v.Expired() {
757c.mu.Unlock()
758return 0, fmt.Errorf("Item %s not found", k)
759}
760rv, ok := v.Object.(uintptr)
761if !ok {
762c.mu.Unlock()
763return 0, fmt.Errorf("The value for %s is not an uintptr", k)
764}
765nv := rv - n
766v.Object = nv
767c.items[k] = v
768c.mu.Unlock()
769return nv, nil
770}
771
772// Decrement an item of type uint8 by n. Returns an error if the item's value is
773// not an uint8, or if it was not found. If there is no error, the decremented
774// value is returned.
775func (c *cache) DecrementUint8(k string, n uint8) (uint8, error) {
776c.mu.Lock()
777v, found := c.items[k]
778if !found || v.Expired() {
779c.mu.Unlock()
780return 0, fmt.Errorf("Item %s not found", k)
781}
782rv, ok := v.Object.(uint8)
783if !ok {
784c.mu.Unlock()
785return 0, fmt.Errorf("The value for %s is not an uint8", k)
786}
787nv := rv - n
788v.Object = nv
789c.items[k] = v
790c.mu.Unlock()
791return nv, nil
792}
793
794// Decrement an item of type uint16 by n. Returns an error if the item's value
795// is not an uint16, or if it was not found. If there is no error, the
796// decremented value is returned.
797func (c *cache) DecrementUint16(k string, n uint16) (uint16, error) {
798c.mu.Lock()
799v, found := c.items[k]
800if !found || v.Expired() {
801c.mu.Unlock()
802return 0, fmt.Errorf("Item %s not found", k)
803}
804rv, ok := v.Object.(uint16)
805if !ok {
806c.mu.Unlock()
807return 0, fmt.Errorf("The value for %s is not an uint16", k)
808}
809nv := rv - n
810v.Object = nv
811c.items[k] = v
812c.mu.Unlock()
813return nv, nil
814}
815
816// Decrement an item of type uint32 by n. Returns an error if the item's value
817// is not an uint32, or if it was not found. If there is no error, the
818// decremented value is returned.
819func (c *cache) DecrementUint32(k string, n uint32) (uint32, error) {
820c.mu.Lock()
821v, found := c.items[k]
822if !found || v.Expired() {
823c.mu.Unlock()
824return 0, fmt.Errorf("Item %s not found", k)
825}
826rv, ok := v.Object.(uint32)
827if !ok {
828c.mu.Unlock()
829return 0, fmt.Errorf("The value for %s is not an uint32", k)
830}
831nv := rv - n
832v.Object = nv
833c.items[k] = v
834c.mu.Unlock()
835return nv, nil
836}
837
838// Decrement an item of type uint64 by n. Returns an error if the item's value
839// is not an uint64, or if it was not found. If there is no error, the
840// decremented value is returned.
841func (c *cache) DecrementUint64(k string, n uint64) (uint64, error) {
842c.mu.Lock()
843v, found := c.items[k]
844if !found || v.Expired() {
845c.mu.Unlock()
846return 0, fmt.Errorf("Item %s not found", k)
847}
848rv, ok := v.Object.(uint64)
849if !ok {
850c.mu.Unlock()
851return 0, fmt.Errorf("The value for %s is not an uint64", k)
852}
853nv := rv - n
854v.Object = nv
855c.items[k] = v
856c.mu.Unlock()
857return nv, nil
858}
859
860// Decrement an item of type float32 by n. Returns an error if the item's value
861// is not an float32, or if it was not found. If there is no error, the
862// decremented value is returned.
863func (c *cache) DecrementFloat32(k string, n float32) (float32, error) {
864c.mu.Lock()
865v, found := c.items[k]
866if !found || v.Expired() {
867c.mu.Unlock()
868return 0, fmt.Errorf("Item %s not found", k)
869}
870rv, ok := v.Object.(float32)
871if !ok {
872c.mu.Unlock()
873return 0, fmt.Errorf("The value for %s is not an float32", k)
874}
875nv := rv - n
876v.Object = nv
877c.items[k] = v
878c.mu.Unlock()
879return nv, nil
880}
881
882// Decrement an item of type float64 by n. Returns an error if the item's value
883// is not an float64, or if it was not found. If there is no error, the
884// decremented value is returned.
885func (c *cache) DecrementFloat64(k string, n float64) (float64, error) {
886c.mu.Lock()
887v, found := c.items[k]
888if !found || v.Expired() {
889c.mu.Unlock()
890return 0, fmt.Errorf("Item %s not found", k)
891}
892rv, ok := v.Object.(float64)
893if !ok {
894c.mu.Unlock()
895return 0, fmt.Errorf("The value for %s is not an float64", k)
896}
897nv := rv - n
898v.Object = nv
899c.items[k] = v
900c.mu.Unlock()
901return nv, nil
902}
903
904// Delete an item from the cache. Does nothing if the key is not in the cache.
905func (c *cache) Delete(k string) {
906c.mu.Lock()
907v, evicted := c.delete(k)
908c.mu.Unlock()
909if evicted {
910c.onEvicted(k, v)
911}
912}
913
914func (c *cache) delete(k string) (interface{}, bool) {
915if c.onEvicted != nil {
916if v, found := c.items[k]; found {
917delete(c.items, k)
918return v.Object, true
919}
920}
921delete(c.items, k)
922return nil, false
923}
924
925type keyAndValue struct {
926key string
927value interface{}
928}
929
930// Delete all expired items from the cache.
931func (c *cache) DeleteExpired() {
932var evictedItems []keyAndValue
933now := time.Now().UnixNano()
934c.mu.Lock()
935for k, v := range c.items {
936// "Inlining" of expired
937if v.Expiration > 0 && now > v.Expiration {
938ov, evicted := c.delete(k)
939if evicted {
940evictedItems = append(evictedItems, keyAndValue{k, ov})
941}
942}
943}
944c.mu.Unlock()
945for _, v := range evictedItems {
946c.onEvicted(v.key, v.value)
947}
948}
949
950// Sets an (optional) function that is called with the key and value when an
951// item is evicted from the cache. (Including when it is deleted manually, but
952// not when it is overwritten.) Set to nil to disable.
953func (c *cache) OnEvicted(f func(string, interface{})) {
954c.mu.Lock()
955c.onEvicted = f
956c.mu.Unlock()
957}
958
959// Write the cache's items (using Gob) to an io.Writer.
960//
961// NOTE: This method is deprecated in favor of c.Items() and NewFrom() (see the
962// documentation for NewFrom().)
963func (c *cache) Save(w io.Writer) (err error) {
964enc := gob.NewEncoder(w)
965defer func() {
966if x := recover(); x != nil {
967err = fmt.Errorf("Error registering item types with Gob library")
968}
969}()
970c.mu.RLock()
971defer c.mu.RUnlock()
972for _, v := range c.items {
973gob.Register(v.Object)
974}
975err = enc.Encode(&c.items)
976return
977}
978
979// Save the cache's items to the given filename, creating the file if it
980// doesn't exist, and overwriting it if it does.
981//
982// NOTE: This method is deprecated in favor of c.Items() and NewFrom() (see the
983// documentation for NewFrom().)
984func (c *cache) SaveFile(fname string) error {
985fp, err := os.Create(fname)
986if err != nil {
987return err
988}
989err = c.Save(fp)
990if err != nil {
991fp.Close()
992return err
993}
994return fp.Close()
995}
996
997// Add (Gob-serialized) cache items from an io.Reader, excluding any items with
998// keys that already exist (and haven't expired) in the current cache.
999//
1000// NOTE: This method is deprecated in favor of c.Items() and NewFrom() (see the
1001// documentation for NewFrom().)
1002func (c *cache) Load(r io.Reader) error {
1003dec := gob.NewDecoder(r)
1004items := map[string]Item{}
1005err := dec.Decode(&items)
1006if err == nil {
1007c.mu.Lock()
1008defer c.mu.Unlock()
1009for k, v := range items {
1010ov, found := c.items[k]
1011if !found || ov.Expired() {
1012c.items[k] = v
1013}
1014}
1015}
1016return err
1017}
1018
1019// Load and add cache items from the given filename, excluding any items with
1020// keys that already exist in the current cache.
1021//
1022// NOTE: This method is deprecated in favor of c.Items() and NewFrom() (see the
1023// documentation for NewFrom().)
1024func (c *cache) LoadFile(fname string) error {
1025fp, err := os.Open(fname)
1026if err != nil {
1027return err
1028}
1029err = c.Load(fp)
1030if err != nil {
1031fp.Close()
1032return err
1033}
1034return fp.Close()
1035}
1036
1037// Copies all unexpired items in the cache into a new map and returns it.
1038func (c *cache) Items() map[string]Item {
1039c.mu.RLock()
1040defer c.mu.RUnlock()
1041m := make(map[string]Item, len(c.items))
1042now := time.Now().UnixNano()
1043for k, v := range c.items {
1044// "Inlining" of Expired
1045if v.Expiration > 0 {
1046if now > v.Expiration {
1047continue
1048}
1049}
1050m[k] = v
1051}
1052return m
1053}
1054
1055// Returns the number of items in the cache. This may include items that have
1056// expired, but have not yet been cleaned up.
1057func (c *cache) ItemCount() int {
1058c.mu.RLock()
1059n := len(c.items)
1060c.mu.RUnlock()
1061return n
1062}
1063
1064// Delete all items from the cache.
1065func (c *cache) Flush() {
1066c.mu.Lock()
1067c.items = map[string]Item{}
1068c.mu.Unlock()
1069}
1070
1071type janitor struct {
1072Interval time.Duration
1073stop chan bool
1074}
1075
1076func (j *janitor) Run(c *cache) {
1077ticker := time.NewTicker(j.Interval)
1078for {
1079select {
1080case <-ticker.C:
1081c.DeleteExpired()
1082case <-j.stop:
1083ticker.Stop()
1084return
1085}
1086}
1087}
1088
1089func stopJanitor(c *Cache) {
1090c.janitor.stop <- true
1091}
1092
1093func runJanitor(c *cache, ci time.Duration) {
1094j := &janitor{
1095Interval: ci,
1096stop: make(chan bool),
1097}
1098c.janitor = j
1099go j.Run(c)
1100}
1101
1102func newCache(de time.Duration, m map[string]Item) *cache {
1103if de == 0 {
1104de = -1
1105}
1106c := &cache{
1107defaultExpiration: de,
1108items: m,
1109}
1110return c
1111}
1112
1113func newCacheWithJanitor(de time.Duration, ci time.Duration, m map[string]Item) *Cache {
1114c := newCache(de, m)
1115// This trick ensures that the janitor goroutine (which--granted it
1116// was enabled--is running DeleteExpired on c forever) does not keep
1117// the returned C object from being garbage collected. When it is
1118// garbage collected, the finalizer stops the janitor goroutine, after
1119// which c can be collected.
1120C := &Cache{c}
1121if ci > 0 {
1122runJanitor(c, ci)
1123runtime.SetFinalizer(C, stopJanitor)
1124}
1125return C
1126}
1127
1128// Return a new cache with a given default expiration duration and cleanup
1129// interval. If the expiration duration is less than one (or NoExpiration),
1130// the items in the cache never expire (by default), and must be deleted
1131// manually. If the cleanup interval is less than one, expired items are not
1132// deleted from the cache before calling c.DeleteExpired().
1133func New(defaultExpiration, cleanupInterval time.Duration) *Cache {
1134items := make(map[string]Item)
1135return newCacheWithJanitor(defaultExpiration, cleanupInterval, items)
1136}
1137
1138// Return a new cache with a given default expiration duration and cleanup
1139// interval. If the expiration duration is less than one (or NoExpiration),
1140// the items in the cache never expire (by default), and must be deleted
1141// manually. If the cleanup interval is less than one, expired items are not
1142// deleted from the cache before calling c.DeleteExpired().
1143//
1144// NewFrom() also accepts an items map which will serve as the underlying map
1145// for the cache. This is useful for starting from a deserialized cache
1146// (serialized using e.g. gob.Encode() on c.Items()), or passing in e.g.
1147// make(map[string]Item, 500) to improve startup performance when the cache
1148// is expected to reach a certain minimum size.
1149//
1150// Only the cache's methods synchronize access to this map, so it is not
1151// recommended to keep any references to the map around after creating a cache.
1152// If need be, the map can be accessed at a later point using c.Items() (subject
1153// to the same caveat.)
1154//
1155// Note regarding serialization: When using e.g. gob, make sure to
1156// gob.Register() the individual types stored in the cache before encoding a
1157// map retrieved with c.Items(), and to register those same types before
1158// decoding a blob containing an items map.
1159func NewFrom(defaultExpiration, cleanupInterval time.Duration, items map[string]Item) *Cache {
1160return newCacheWithJanitor(defaultExpiration, cleanupInterval, items)
1161}
1162