| package lib |
| |
| import ( |
| "fmt" |
| "io" |
| "reflect" |
| "sort" |
| "time" |
| |
| "veyron2/ipc" |
| "veyron2/vdl" |
| "veyron2/verror" |
| |
| sample "veyron/examples/wspr_sample" |
| ) |
| |
| var typedNil []int |
| |
| // A simple in-memory implementation of a Cache service. |
| type cacheImpl struct { |
| cache map[string]vdl.Any |
| mostRecent sample.KeyValuePair |
| lastUpdateTime time.Time |
| } |
| |
| // NewCached returns a new implementation of the CacheService. |
| func NewCached() sample.CacheService { |
| return &cacheImpl{cache: make(map[string]vdl.Any)} |
| } |
| |
| // Set sets a value for a key. This should never return an error. |
| func (c *cacheImpl) Set(_ ipc.ServerContext, key string, value vdl.Any) error { |
| c.cache[key] = value |
| c.mostRecent = sample.KeyValuePair{Key: key, Value: value} |
| c.lastUpdateTime = time.Now() |
| return nil |
| } |
| |
| // Get returns the value for a key. If the key is not in the map, it returns |
| // an error. |
| func (c *cacheImpl) Get(_ ipc.ServerContext, key string) (vdl.Any, error) { |
| if value, ok := c.cache[key]; ok { |
| return value, nil |
| } |
| |
| return typedNil, verror.NotFoundf("key not found: %v", key) |
| } |
| |
| // getWithTypeCheck gets the key and tests if its type matches the given time, erroring if it does |
| // not. |
| // This exists mostly to shorted the Get* methods below. |
| func (c *cacheImpl) getWithTypeCheck(key string, rt reflect.Type) (interface{}, error) { |
| v, err := c.Get(nil, key) |
| if err != nil { |
| return reflect.Zero(rt).Interface(), err |
| } |
| if !reflect.TypeOf(v).AssignableTo(rt) { |
| return reflect.Zero(rt).Interface(), |
| fmt.Errorf("Cannot convert %v (type %v) to type %v", v, reflect.TypeOf(v), rt) |
| } |
| return v, nil |
| } |
| |
| // Same as Get, but casts the return argument to an int32. |
| func (c *cacheImpl) GetAsInt32(_ ipc.ServerContext, key string) (int32, error) { |
| v, err := c.getWithTypeCheck(key, reflect.TypeOf(int32(0))) |
| return v.(int32), err |
| } |
| |
| // Same as Get, but casts the return argument to an int64. |
| func (c *cacheImpl) GetAsInt64(_ ipc.ServerContext, key string) (int64, error) { |
| v, err := c.getWithTypeCheck(key, reflect.TypeOf(int64(0))) |
| return v.(int64), err |
| } |
| |
| // Same as Get, but casts the return argument to an uint8. |
| func (c *cacheImpl) GetAsByte(_ ipc.ServerContext, key string) (byte, error) { |
| v, err := c.getWithTypeCheck(key, reflect.TypeOf(byte(0))) |
| return v.(uint8), err |
| } |
| |
| // Same as Get, but casts the return argument to an uint32. |
| func (c *cacheImpl) GetAsUint32(_ ipc.ServerContext, key string) (uint32, error) { |
| v, err := c.getWithTypeCheck(key, reflect.TypeOf(uint32(0))) |
| return v.(uint32), err |
| } |
| |
| // Same as Get, but casts the return argument to an uint64. |
| func (c *cacheImpl) GetAsUint64(_ ipc.ServerContext, key string) (uint64, error) { |
| v, err := c.getWithTypeCheck(key, reflect.TypeOf(uint64(0))) |
| return v.(uint64), err |
| } |
| |
| // Same as Get, but casts the return argument to a float32. |
| func (c *cacheImpl) GetAsFloat32(_ ipc.ServerContext, key string) (float32, error) { |
| v, err := c.getWithTypeCheck(key, reflect.TypeOf(float32(0))) |
| return v.(float32), err |
| } |
| |
| // Same as Get, but casts the return argument to a float64. |
| func (c *cacheImpl) GetAsFloat64(_ ipc.ServerContext, key string) (float64, error) { |
| v, err := c.getWithTypeCheck(key, reflect.TypeOf(float64(0))) |
| return v.(float64), err |
| } |
| |
| // Same as Get, but casts the return argument to a string. |
| func (c *cacheImpl) GetAsString(_ ipc.ServerContext, key string) (string, error) { |
| v, err := c.getWithTypeCheck(key, reflect.TypeOf("")) |
| return v.(string), err |
| } |
| |
| // Same as Get, but casts the return argument to a bool. |
| func (c *cacheImpl) GetAsBool(_ ipc.ServerContext, key string) (bool, error) { |
| v, err := c.getWithTypeCheck(key, reflect.TypeOf(false)) |
| return v.(bool), err |
| } |
| |
| // Same as Get, but converts the string return argument to an error. |
| func (c *cacheImpl) GetAsError(_ ipc.ServerContext, key string) (storedError error, callError error) { |
| v, err := c.getWithTypeCheck(key, reflect.TypeOf([]error{}).Elem()) |
| return v.(error), err |
| } |
| |
| // AsMap returns the full contents of the cache as a map. |
| func (c *cacheImpl) AsMap(ipc.ServerContext) (map[string]vdl.Any, error) { |
| return c.cache, nil |
| } |
| |
| // KeyValuePairs returns the full contents of the cache as a slice of pairs. |
| func (c *cacheImpl) KeyValuePairs(ipc.ServerContext) ([]sample.KeyValuePair, error) { |
| kvp := make([]sample.KeyValuePair, 0, len(c.cache)) |
| for key, val := range c.cache { |
| kvp = append(kvp, sample.KeyValuePair{key, val}) |
| } |
| return kvp, nil |
| } |
| |
| // MostRecentSet returns the key and value and the timestamp for the most |
| // recent set operation |
| // TODO(bprosnitz) support type types and change time to native time type |
| func (c *cacheImpl) MostRecentSet(ipc.ServerContext) (sample.KeyValuePair, int64, error) { |
| var err error |
| if c.lastUpdateTime.IsZero() { |
| err = verror.NotFoundf("no values in the cache so cannot return most recent.") |
| } |
| return c.mostRecent, c.lastUpdateTime.Unix(), err |
| } |
| |
| // KeyPage indexes into the keys (in alphanumerically sorted order) and |
| // returns the indexth page of 10 keys. |
| func (c *cacheImpl) KeyPage(_ ipc.ServerContext, index int64) ([10]string, error) { |
| results := [10]string{} |
| |
| keys := sort.StringSlice{} |
| for key, _ := range c.cache { |
| keys = append(keys, key) |
| } |
| keys.Sort() |
| |
| lowIndex := int(index) * 10 |
| if index < 0 || len(keys) <= lowIndex { |
| return results, verror.BadArgf("Page index out of bounds: %d", index) |
| } |
| highIndex := lowIndex + 9 |
| if highIndex > len(keys)-1 { |
| highIndex = len(keys) - 1 |
| } |
| |
| for i := 0; lowIndex+i <= highIndex; i++ { |
| results[i] = keys[lowIndex+i] |
| } |
| |
| return results, nil |
| } |
| |
| // Size returns the total number of entries in the cache. |
| func (c *cacheImpl) Size(ipc.ServerContext) (int64, error) { |
| return int64(len(c.cache)), nil |
| } |
| |
| // MultiGet handles a stream of get requests. Returns an error if one of the |
| // keys in the stream is not in the map or if there was an issue reading |
| // the stream. |
| func (c *cacheImpl) MultiGet(_ ipc.ServerContext, stream sample.CacheServiceMultiGetStream) error { |
| for { |
| key, err := stream.Recv() |
| if err == io.EOF { |
| return nil |
| } |
| |
| if err != nil { |
| return err |
| } |
| |
| value, ok := c.cache[key] |
| if !ok { |
| return fmt.Errorf("key not found: %v", key) |
| } |
| stream.Send(value) |
| } |
| } |