Add decrement and increment for numberic types

Signed-off-by: Peng Gao <peng.gao.dut@gmail.com>
This commit is contained in:
Peng Gao 2016-08-29 18:58:22 +08:00
parent 3cceb4fab0
commit ebc1ab826e
5 changed files with 157 additions and 54 deletions

View File

@ -16,9 +16,9 @@ type Attr_tpl struct {
// not when it is overwritten.) Set to nil to disable. // not when it is overwritten.) Set to nil to disable.
OnEvicted func(k string, v ValueType_tpl) OnEvicted func(k string, v ValueType_tpl)
DefaultCleanupInterval time.Duration // default clean interval DefaultCleanupInterval time.Duration // Default clean interval, this is a time interval to cleanup expired items
DefaultExpiration time.Duration // default expiration duration DefaultExpiration time.Duration // Default expiration duration
Size int64 // initial size of map Size int64 // Initial size of map
} }
// Item struct // Item struct
@ -27,7 +27,7 @@ type Item struct {
Expiration int64 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 { func (item Item) Expired() bool {
return item.Expiration != 0 && time.Now().UnixNano() > item.Expiration return item.Expiration != 0 && time.Now().UnixNano() > item.Expiration
} }
@ -37,7 +37,7 @@ const (
NoExpiration time.Duration = -1 NoExpiration time.Duration = -1
// DefaultExpiration is for use with functions that take an // DefaultExpiration is for use with functions that take an
// expiration time. Equivalent to passing in the same expiration // 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.) // created (e.g. 5 minutes.)
DefaultExpiration time.Duration = 0 DefaultExpiration time.Duration = 0
) )
@ -143,13 +143,23 @@ func (c *cache) get(k string) (*ValueType_tpl, bool) {
return &item.Object, true return &item.Object, true
} }
// MARK_Numberic_tpl_begin
// Increment an item of type int, int8, int16, int32, int64, uintptr, uint, // 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 // 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 // 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 // possible to increment it by n. To retrieve the incremented value, use one
// of the specialized methods, e.g. IncrementInt64. // of the specialized methods, e.g. IncrementInt64.
// TODO: Increment for numberic type. func (c *cache) Increment(k string, n ValueType_tpl) error {
func (c *cache) Increment(k string, n int64) 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 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 // 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 // possible to decrement it by n. To retrieve the decremented value, use one
// of the specialized methods, e.g. DecrementInt64. // of the specialized methods, e.g. DecrementInt64.
// TODO: Decrement func (c *cache) Decrement(k string, n ValueType_tpl) error {
func (c *cache) Decrement(k string, n int64) error {
// TODO: Implement Increment and Decrement more cleanly. // TODO: Implement Increment and Decrement more cleanly.
// (Cannot do Increment(k, n*-1) for uints.) // (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 return nil
} }
// MARK_Numberic_tpl_end
// Delete an item from the cache. Does nothing if the key is not in the cache. // Delete an item from the cache. Does nothing if the key is not in the cache.
func (c *cache) Delete(k string) { func (c *cache) Delete(k string) {
// fast path // fast path

View File

@ -110,8 +110,42 @@ func TestCacheTimes(t *testing.T) {
} }
} }
// TODO: test increment. func TestIncrement(t *testing.T) {
func TestIncrementWithInt(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) { func TestAdd(t *testing.T) {
@ -481,7 +515,7 @@ func BenchmarkIncrementInt(b *testing.B) {
tc.Set("foo", 0, DefaultExpiration) tc.Set("foo", 0, DefaultExpiration)
b.StartTimer() b.StartTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
// tc.IncrementInt("foo", 1) tc.Increment("foo", 1)
} }
} }

View File

@ -1,5 +1,7 @@
package {{.PackageName}} package {{.PackageName}}
// The package is used as a template, don't use it directly!
import ( import (
"fmt" "fmt"
"runtime" "runtime"
@ -9,10 +11,14 @@ import (
// Attr is cachmap attribute // Attr is cachmap attribute
type {{.ValueType}}CacheAttr struct { type {{.ValueType}}CacheAttr struct {
OnEvicted func(k string, v {{.ValueType}}) // called when k evicted if set // An (optional) function that is called with the key and value when an
DefaultCleanupInterval time.Duration // default clean interval // item is evicted from the cache. (Including when it is deleted manually, but
DefaultExpiration time.Duration // default expiration duration // not when it is overwritten.) Set to nil to disable.
Size int64 // inital size of map 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 // Item struct
@ -21,7 +27,7 @@ type Item struct {
Expiration int64 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 { func (item Item) Expired() bool {
return item.Expiration != 0 && time.Now().UnixNano() > item.Expiration return item.Expiration != 0 && time.Now().UnixNano() > item.Expiration
} }
@ -31,7 +37,7 @@ const (
NoExpiration time.Duration = -1 NoExpiration time.Duration = -1
// DefaultExpiration is for use with functions that take an // DefaultExpiration is for use with functions that take an
// expiration time. Equivalent to passing in the same expiration // 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.) // created (e.g. 5 minutes.)
DefaultExpiration time.Duration = 0 DefaultExpiration time.Duration = 0
) )
@ -137,13 +143,23 @@ func (c *cache) get(k string) (*{{.ValueType}}, bool) {
return &item.Object, true return &item.Object, true
} }
{{ if call .IsNumberic .RealType }}
// Increment an item of type int, int8, int16, int32, int64, uintptr, uint, // 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 // 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 // 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 // possible to increment it by n. To retrieve the incremented value, use one
// of the specialized methods, e.g. IncrementInt64. // of the specialized methods, e.g. IncrementInt64.
// TODO: Increment for numberic type. func (c *cache) Increment(k string, n {{.ValueType}}) error {
func (c *cache) Increment(k string, n int64) 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 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 // 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 // possible to decrement it by n. To retrieve the decremented value, use one
// of the specialized methods, e.g. DecrementInt64. // of the specialized methods, e.g. DecrementInt64.
// TODO: Decrement func (c *cache) Decrement(k string, n {{.ValueType}}) error {
func (c *cache) Decrement(k string, n int64) error {
// TODO: Implement Increment and Decrement more cleanly. // TODO: Implement Increment and Decrement more cleanly.
// (Cannot do Increment(k, n*-1) for uints.) // (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 return nil
} }
{{end}}
// Delete an item from the cache. Does nothing if the key is not in the cache. // Delete an item from the cache. Does nothing if the key is not in the cache.
func (c *cache) Delete(k string) { func (c *cache) Delete(k string) {
// fast path // fast path
@ -182,7 +207,8 @@ func (c *cache) delete(k string) ({{.ValueType}}, bool) {
delete(c.items, k) delete(c.items, k)
return v.Object, true return v.Object, true
} }
return {{ .ZeroValue }}, false //TODO: zeroValue
return 0, false
} }
func (c *cache) deleteFast(k string) { 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 // 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()). // expired, but have not yet been cleaned up. Equivalent to len(c.Items()).
func (c *cache) ItemCount() int { func (c *cache) ItemCount() int {

View File

@ -10,6 +10,7 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strings"
"text/template" "text/template"
) )
@ -26,25 +27,27 @@ func packageDir() string {
return path.Dir(filename) return path.Dir(filename)
} }
// find value of ident 'grammar' in GenDecl. // TODO: parse type if type is also not builtin type.
func findInGenDecl(genDecl *ast.GenDecl, grammarName string) string { // find literal value of type.
func findInGenDecl(genDecl *ast.GenDecl, valueName string) string {
for _, spec := range genDecl.Specs { for _, spec := range genDecl.Specs {
valueSpec, ok := spec.(*ast.TypeSpec) valueSpec, ok := spec.(*ast.TypeSpec)
if ok { if ok {
// type ident if ok && valueSpec.Name.Name == valueName {
ident, ok := valueSpec.Type.(*ast.Ident) indent, ok := valueSpec.Type.(*ast.Ident)
if ok { if ok {
return ident.Name return indent.Name
}
} }
} }
} }
return "" return ""
} }
func findInDecl(decl ast.Decl, grammarName string) string { func findInDecl(decl ast.Decl, valueName string) string {
genDecl, ok := decl.(*ast.GenDecl) genDecl, ok := decl.(*ast.GenDecl)
if ok { if ok {
g := findInGenDecl(genDecl, grammarName) g := findInGenDecl(genDecl, valueName)
if g != "" { if g != "" {
return g return g
} }
@ -56,28 +59,52 @@ func findInDecl(decl ast.Decl, grammarName string) string {
func zeroValue(s string) string { func zeroValue(s string) string {
// TODO: support func type. // TODO: support func type.
switch s { switch s {
case "bool":
return "false"
case "string": case "string":
return "\"\"" return "\"\""
case "int", "uint", "int64", "uint64", "uint32", "int32", "int16", case "int", "uint", "int64", "uint64", "uint32", "int32", "int16",
"uint16", "int8", "uint8", "byte", "rune", "float64", "float32", "uint16", "int8", "uint8", "byte", "rune", "float64", "float32",
"complex64", "complex32", "uintptr": "complex64", "complex32", "uintptr":
return "0" return "0"
case "slice":
return "nil"
default: 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 "nil"
} }
return s + "{}" return s + "{}"
} }
} }
// TODO: support more builtin types var builtinTypes = []string{
func builtin(s string) bool { "bool",
switch s { "string",
case "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 true
} }
}
return false
}
func isBuiltin(s string) bool {
for _, v := range builtinTypes {
if v == s {
return true
}
}
return false 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)) fatal(fmt.Errorf("found no definition of %s in files\n", *valueType))
} }
if typeName == "" { if typeName == "" {
typeName = *valueType typeName = *valueType
} }
fmt.Println("real", typeName, "value", *valueType)
zeroTypeValue := zeroValue(typeName) 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) 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 { if err != nil {
@ -122,13 +150,18 @@ func main() {
if err != nil { if err != nil {
fatal(err) fatal(err)
} }
if isBuiltin(*valueType) {
*valueType = strings.Title(*valueType)
}
err = tpl.Execute( err = tpl.Execute(
f, f,
map[string]string{ map[string]interface{}{
"ValueType": *valueType, "ValueType": *valueType,
"RealType": typeName,
"PackageName": packageName, "PackageName": packageName,
"Cache": fmt.Sprintf("String2%sCache", *valueType), "Cache": fmt.Sprintf("String2%sCache", *valueType),
"ZeroValue": zeroTypeValue, "ZeroValue": zeroTypeValue,
"IsNumberic": isNumberic,
}, },
) )
if err != nil { if err != nil {

View File

@ -82,11 +82,11 @@ func (sc *shardedCache) Get(k string) (ValueType_tpl, bool) {
return sc.bucket(k).Get(k) 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) 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) return sc.bucket(k).Decrement(k, n)
} }