| package util |
| |
| import ( |
| "encoding/json" |
| "reflect" |
| "sync" |
| |
| "github.com/golang/groupcache/lru" |
| ) |
| |
| // Generic LRUCache interface. |
| type LRUCache interface { |
| // Add adds a key-value pair to the cache. |
| Add(key, value interface{}) bool |
| |
| // Get returns a key value for the given cache. If ok is true |
| // the fetch was succesfull. |
| Get(key interface{}) (value interface{}, ok bool) |
| |
| // Len returns the current size of the cache. |
| Len() int |
| |
| // Remove removes a key value pair from the cache. |
| Remove(key interface{}) |
| |
| // Keys returns all the keys in the LRUCache. |
| Keys() []interface{} |
| } |
| |
| // LRUCodec converts serializes/deserializes an instance of a type to/from |
| // byte arrays. Encode and Decode have to be the inverse of each other. |
| type LRUCodec interface { |
| // Encode serializes the given value to a byte array (inverse of Decode). |
| Encode(interface{}) ([]byte, error) |
| |
| // Decode deserializes the byte array to an instance of the type that |
| // was passed to Encode in a prior call. |
| Decode([]byte) (interface{}, error) |
| } |
| |
| type MemLRUCache struct { |
| cache *lru.Cache |
| keys map[string]bool |
| mutex sync.RWMutex |
| } |
| |
| func NewMemLRUCache(maxEntries int) *MemLRUCache { |
| ret := &MemLRUCache{ |
| cache: lru.New(maxEntries), |
| keys: map[string]bool{}, |
| } |
| |
| ret.cache.OnEvicted = func(key lru.Key, value interface{}) { |
| delete(ret.keys, getStringKey(key)) |
| } |
| |
| return ret |
| } |
| |
| func getStringKey(key interface{}) string { |
| k := "" |
| switch key := key.(type) { |
| case string: |
| k = key |
| case []byte: |
| if key != nil { |
| k = string(key) |
| } |
| } |
| return k |
| } |
| |
| func (m *MemLRUCache) Add(key, value interface{}) bool { |
| m.mutex.Lock() |
| defer m.mutex.Unlock() |
| m.cache.Add(key, value) |
| m.keys[getStringKey(key)] = true |
| return true |
| } |
| |
| func (m *MemLRUCache) Get(key interface{}) (value interface{}, ok bool) { |
| m.mutex.RLock() |
| m.mutex.RUnlock() |
| return m.cache.Get(key) |
| } |
| |
| func (m *MemLRUCache) Len() int { |
| m.mutex.RLock() |
| m.mutex.RUnlock() |
| return m.cache.Len() |
| } |
| |
| func (m *MemLRUCache) Remove(key interface{}) { |
| m.mutex.Lock() |
| defer m.mutex.Unlock() |
| m.cache.Remove(key) |
| delete(m.keys, getStringKey(key)) |
| } |
| |
| func (m *MemLRUCache) Keys() []interface{} { |
| m.mutex.RLock() |
| defer m.mutex.RUnlock() |
| ret := make([]interface{}, 0, len(m.keys)) |
| for k := range m.keys { |
| ret = append(ret, k) |
| } |
| return ret |
| } |
| |
| type jsonCodec struct { |
| targetType reflect.Type // the type we want to encode/decode. |
| stripPtr bool // indicates whether to derefence the pointer of the result. |
| } |
| |
| // JSONCodec implements the LRUCodec interface by serializing and |
| // deserializing instances of the underlying type of 'instance'. |
| // Generally it's assumed that 'instance' is a struct, a pointer to |
| // a struct, a slice or a map. |
| func JSONCodec(instance interface{}) LRUCodec { |
| targetType := reflect.TypeOf(instance) |
| if targetType.Kind() == reflect.Ptr { |
| targetType = targetType.Elem() |
| } |
| kind := targetType.Kind() |
| return &jsonCodec{ |
| targetType: targetType, |
| stripPtr: (kind == reflect.Slice) || (kind == reflect.Map), |
| } |
| } |
| |
| // See LRUCodec interface. |
| func (j *jsonCodec) Encode(data interface{}) ([]byte, error) { |
| return json.Marshal(data) |
| } |
| |
| // See LRUCodec interface. |
| func (j *jsonCodec) Decode(byteData []byte) (interface{}, error) { |
| // Get a pointer to a new instance, because that's what Unmarshal needs. |
| ret := reflect.New(j.targetType).Interface() |
| err := json.Unmarshal(byteData, ret) |
| if err != nil { |
| return nil, err |
| } else if j.stripPtr { |
| // Strip the pointer for slices and maps. |
| return reflect.ValueOf(ret).Elem().Interface(), nil |
| } |
| return ret, nil |
| } |