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.
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

View File

@ -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)
}
}

View File

@ -1,5 +1,7 @@
package {{.PackageName}}
// The package is used as a template, don't use it directly!
import (
"fmt"
"runtime"
@ -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 {

View File

@ -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 && valueSpec.Name.Name == valueName {
indent, ok := valueSpec.Type.(*ast.Ident)
if ok {
return ident.Name
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,28 +59,52 @@ 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":
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 {

View File

@ -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)
}