From ebc1ab826e88e9903bc3ea995ef04fdc1d202951 Mon Sep 17 00:00:00 2001 From: Peng Gao Date: Mon, 29 Aug 2016 18:58:22 +0800 Subject: [PATCH] Add decrement and increment for numberic types Signed-off-by: Peng Gao --- cache.go | 38 +++++++++++++++++++------ cache_test.go | 40 ++++++++++++++++++++++++-- cachemap/cache.tmpl | 60 ++++++++++++++++++++++++--------------- cachemap/main.go | 69 +++++++++++++++++++++++++++++++++------------ sharded.go | 4 +-- 5 files changed, 157 insertions(+), 54 deletions(-) diff --git a/cache.go b/cache.go index 134f9c7..55af84a 100644 --- a/cache.go +++ b/cache.go @@ -16,9 +16,9 @@ type Attr_tpl struct { // not when it is overwritten.) Set to nil to disable. OnEvicted func(k string, v ValueType_tpl) - DefaultCleanupInterval time.Duration // default clean interval - DefaultExpiration time.Duration // default expiration duration - Size int64 // initial size of map + DefaultCleanupInterval time.Duration // Default clean interval, this is a time interval to cleanup expired items + DefaultExpiration time.Duration // Default expiration duration + Size int64 // Initial size of map } // Item struct @@ -27,7 +27,7 @@ type Item struct { Expiration int64 } -// Expired Returns true if the item has expired, if valid Expiration is set. +// Expired returns true if the item has expired. func (item Item) Expired() bool { return item.Expiration != 0 && time.Now().UnixNano() > item.Expiration } @@ -37,7 +37,7 @@ const ( NoExpiration time.Duration = -1 // DefaultExpiration is for use with functions that take an // expiration time. Equivalent to passing in the same expiration - // duration as was given to New() or NewFrom() when the cache was + // duration as was given to New() when the cache was // created (e.g. 5 minutes.) DefaultExpiration time.Duration = 0 ) @@ -143,13 +143,23 @@ func (c *cache) get(k string) (*ValueType_tpl, bool) { return &item.Object, true } +// MARK_Numberic_tpl_begin + // Increment an item of type int, int8, int16, int32, int64, uintptr, uint, // uint8, uint32, or uint64, float32 or float64 by n. Returns an error if the // item's value is not an integer, if it was not found, or if it is not // possible to increment it by n. To retrieve the incremented value, use one // of the specialized methods, e.g. IncrementInt64. -// TODO: Increment for numberic type. -func (c *cache) Increment(k string, n int64) error { +func (c *cache) Increment(k string, n ValueType_tpl) error { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return fmt.Errorf("Item %s not found", k) + } + v.Object += n + c.items[k] = v + c.mu.Unlock() return nil } @@ -158,13 +168,23 @@ func (c *cache) Increment(k string, n int64) error { // item's value is not an integer, if it was not found, or if it is not // possible to decrement it by n. To retrieve the decremented value, use one // of the specialized methods, e.g. DecrementInt64. -// TODO: Decrement -func (c *cache) Decrement(k string, n int64) error { +func (c *cache) Decrement(k string, n ValueType_tpl) error { // TODO: Implement Increment and Decrement more cleanly. // (Cannot do Increment(k, n*-1) for uints.) + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return fmt.Errorf("Item not found") + } + v.Object -= n + c.items[k] = v + c.mu.Unlock() return nil } +// MARK_Numberic_tpl_end + // Delete an item from the cache. Does nothing if the key is not in the cache. func (c *cache) Delete(k string) { // fast path diff --git a/cache_test.go b/cache_test.go index 904a2df..94554a9 100644 --- a/cache_test.go +++ b/cache_test.go @@ -110,8 +110,42 @@ func TestCacheTimes(t *testing.T) { } } -// TODO: test increment. -func TestIncrementWithInt(t *testing.T) { +func TestIncrement(t *testing.T) { + tc := New_tpl(Attr_tpl{ + DefaultExpiration: DefaultExpiration, + DefaultCleanupInterval: 0, + }) + tc.Set("tint", 1, DefaultExpiration) + err := tc.Increment("tint", 2) + if err != nil { + t.Error("Error incrementing:", err) + } + x, found := tc.Get("tint") + if !found { + t.Error("tint was not found") + } + if x != 3 { + t.Error("tint is not 3:", x) + } +} + +func TestDecrement(t *testing.T) { + tc := New_tpl(Attr_tpl{ + DefaultExpiration: DefaultExpiration, + DefaultCleanupInterval: 0, + }) + tc.Set("int", 5, DefaultExpiration) + err := tc.Decrement("int", 2) + if err != nil { + t.Error("Error decrementing:", err) + } + x, found := tc.Get("int") + if !found { + t.Error("int was not found") + } + if x != 3 { + t.Error("int is not 3:", x) + } } func TestAdd(t *testing.T) { @@ -481,7 +515,7 @@ func BenchmarkIncrementInt(b *testing.B) { tc.Set("foo", 0, DefaultExpiration) b.StartTimer() for i := 0; i < b.N; i++ { - // tc.IncrementInt("foo", 1) + tc.Increment("foo", 1) } } diff --git a/cachemap/cache.tmpl b/cachemap/cache.tmpl index 351c3c0..b77273e 100644 --- a/cachemap/cache.tmpl +++ b/cachemap/cache.tmpl @@ -1,4 +1,6 @@ -package {{.PackageName}} +package {{.PackageName}} + +// The package is used as a template, don't use it directly! import ( "fmt" @@ -9,10 +11,14 @@ import ( // Attr is cachmap attribute type {{.ValueType}}CacheAttr struct { - OnEvicted func(k string, v {{.ValueType}}) // called when k evicted if set - DefaultCleanupInterval time.Duration // default clean interval - DefaultExpiration time.Duration // default expiration duration - Size int64 // inital size of map + // An (optional) function that is called with the key and value when an + // item is evicted from the cache. (Including when it is deleted manually, but + // not when it is overwritten.) Set to nil to disable. + OnEvicted func(k string, v {{.ValueType}}) + + DefaultCleanupInterval time.Duration // Default clean interval, this is a time interval to cleanup expired items + DefaultExpiration time.Duration // Default expiration duration + Size int64 // Initial size of map } // Item struct @@ -21,7 +27,7 @@ type Item struct { Expiration int64 } -// Expired Returns true if the item has expired, if valid Expiration is set. +// Expired returns true if the item has expired. func (item Item) Expired() bool { return item.Expiration != 0 && time.Now().UnixNano() > item.Expiration } @@ -31,7 +37,7 @@ const ( NoExpiration time.Duration = -1 // DefaultExpiration is for use with functions that take an // expiration time. Equivalent to passing in the same expiration - // duration as was given to New() or NewFrom() when the cache was + // duration as was given to New() when the cache was // created (e.g. 5 minutes.) DefaultExpiration time.Duration = 0 ) @@ -137,13 +143,23 @@ func (c *cache) get(k string) (*{{.ValueType}}, bool) { return &item.Object, true } +{{ if call .IsNumberic .RealType }} + // Increment an item of type int, int8, int16, int32, int64, uintptr, uint, // uint8, uint32, or uint64, float32 or float64 by n. Returns an error if the // item's value is not an integer, if it was not found, or if it is not // possible to increment it by n. To retrieve the incremented value, use one // of the specialized methods, e.g. IncrementInt64. -// TODO: Increment for numberic type. -func (c *cache) Increment(k string, n int64) error { +func (c *cache) Increment(k string, n {{.ValueType}}) error { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return fmt.Errorf("Item %s not found", k) + } + v.Object += n + c.items[k] = v + c.mu.Unlock() return nil } @@ -152,13 +168,22 @@ func (c *cache) Increment(k string, n int64) error { // item's value is not an integer, if it was not found, or if it is not // possible to decrement it by n. To retrieve the decremented value, use one // of the specialized methods, e.g. DecrementInt64. -// TODO: Decrement -func (c *cache) Decrement(k string, n int64) error { +func (c *cache) Decrement(k string, n {{.ValueType}}) error { // TODO: Implement Increment and Decrement more cleanly. // (Cannot do Increment(k, n*-1) for uints.) + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return fmt.Errorf("Item not found") + } + c.items[k] = v + c.mu.Unlock() return nil } +{{end}} + // Delete an item from the cache. Does nothing if the key is not in the cache. func (c *cache) Delete(k string) { // fast path @@ -182,7 +207,8 @@ func (c *cache) delete(k string) ({{.ValueType}}, bool) { delete(c.items, k) return v.Object, true } - return {{ .ZeroValue }}, false + //TODO: zeroValue + return 0, false } func (c *cache) deleteFast(k string) { @@ -227,16 +253,6 @@ func (c *cache) DeleteExpired() { } } -// Sets an (optional) function that is called with the key and value when an -// item is evicted from the cache. (Including when it is deleted manually, but -// not when it is overwritten.) Set to nil to disable. -// 这里加锁没有意义 -func (c *cache) OnEvicted(f func(string, {{.ValueType}})) { - c.mu.Lock() - c.onEvicted = f - c.mu.Unlock() -} - // Returns the number of items in the cache. This may include items that have // expired, but have not yet been cleaned up. Equivalent to len(c.Items()). func (c *cache) ItemCount() int { diff --git a/cachemap/main.go b/cachemap/main.go index e7f480d..44643b0 100644 --- a/cachemap/main.go +++ b/cachemap/main.go @@ -10,6 +10,7 @@ import ( "path" "path/filepath" "runtime" + "strings" "text/template" ) @@ -26,25 +27,27 @@ func packageDir() string { return path.Dir(filename) } -// find value of ident 'grammar' in GenDecl. -func findInGenDecl(genDecl *ast.GenDecl, grammarName string) string { +// TODO: parse type if type is also not builtin type. +// find literal value of type. +func findInGenDecl(genDecl *ast.GenDecl, valueName string) string { for _, spec := range genDecl.Specs { valueSpec, ok := spec.(*ast.TypeSpec) if ok { - // type ident - ident, ok := valueSpec.Type.(*ast.Ident) - if ok { - return ident.Name + if ok && valueSpec.Name.Name == valueName { + indent, ok := valueSpec.Type.(*ast.Ident) + if ok { + return indent.Name + } } } } return "" } -func findInDecl(decl ast.Decl, grammarName string) string { +func findInDecl(decl ast.Decl, valueName string) string { genDecl, ok := decl.(*ast.GenDecl) if ok { - g := findInGenDecl(genDecl, grammarName) + g := findInGenDecl(genDecl, valueName) if g != "" { return g } @@ -56,27 +59,51 @@ func findInDecl(decl ast.Decl, grammarName string) string { func zeroValue(s string) string { // TODO: support func type. switch s { + case "bool": + return "false" case "string": return "\"\"" case "int", "uint", "int64", "uint64", "uint32", "int32", "int16", "uint16", "int8", "uint8", "byte", "rune", "float64", "float32", "complex64", "complex32", "uintptr": return "0" - case "slice": - return "nil" default: - if s[0] == '*' { // Pointer + if s[0] == '*' || // Pointer + strings.Index(s, "map") == 0 || // map + strings.Index(s, "chan") == 0 || // chan + strings.Index(s, "[]") == 0 { // slice return "nil" } return s + "{}" } } -// TODO: support more builtin types -func builtin(s string) bool { - switch s { - case "string": - return true +var builtinTypes = []string{ + "bool", + "string", + + "int", "int8", "int16", "int32", "int64", // numbericType + "uint", "uint8", "uint16", "uint32", "uint64", "uintptr", + "float32", "float64", + "complex64", "complex128", + "byte", + "rune", +} + +func isNumberic(s string) bool { + for _, v := range builtinTypes[2:] { // 2 is beginning of numberic types in builtinTypes. + if v == s { + return true + } + } + return false +} + +func isBuiltin(s string) bool { + for _, v := range builtinTypes { + if v == s { + return true + } } return false } @@ -106,12 +133,13 @@ func main() { } } } - if typeName == "" && !builtin(*valueType) { + if typeName == "" && !isBuiltin(*valueType) { fatal(fmt.Errorf("found no definition of %s in files\n", *valueType)) } if typeName == "" { typeName = *valueType } + fmt.Println("real", typeName, "value", *valueType) zeroTypeValue := zeroValue(typeName) f, err := os.OpenFile(fmt.Sprintf("%s2%s_cachemap.go", *keyType, *valueType), os.O_TRUNC|os.O_WRONLY|os.O_CREATE, 0644) if err != nil { @@ -122,13 +150,18 @@ func main() { if err != nil { fatal(err) } + if isBuiltin(*valueType) { + *valueType = strings.Title(*valueType) + } err = tpl.Execute( f, - map[string]string{ + map[string]interface{}{ "ValueType": *valueType, + "RealType": typeName, "PackageName": packageName, "Cache": fmt.Sprintf("String2%sCache", *valueType), "ZeroValue": zeroTypeValue, + "IsNumberic": isNumberic, }, ) if err != nil { diff --git a/sharded.go b/sharded.go index 4d360ac..1cd80ef 100644 --- a/sharded.go +++ b/sharded.go @@ -82,11 +82,11 @@ func (sc *shardedCache) Get(k string) (ValueType_tpl, bool) { return sc.bucket(k).Get(k) } -func (sc *shardedCache) Increment(k string, n int64) error { +func (sc *shardedCache) Increment(k string, n ValueType_tpl) error { return sc.bucket(k).Increment(k, n) } -func (sc *shardedCache) Decrement(k string, n int64) error { +func (sc *shardedCache) Decrement(k string, n ValueType_tpl) error { return sc.bucket(k).Decrement(k, n) }