Add generator for code

Signed-off-by: Peng Gao <peng.gao.dut@gmail.com>
This commit is contained in:
Peng Gao 2016-08-11 16:42:28 +08:00
parent 4606d7097e
commit 96eafc2dcd
3 changed files with 364 additions and 2 deletions

View File

@ -1,4 +1,4 @@
This is a list of people who have contributed code to go-cache. They, or their
This is a list of people who have contributed code to cachemap. They, or their
employers, are the copyright holders of the contributed code. Contributed code
is subject to the license restrictions listed in LICENSE (as they were when the
code was contributed.)
@ -6,3 +6,4 @@ code was contributed.)
Dustin Sallings <dustin@spy.net>
Jason Mooberry <jasonmoo@me.com>
Sergey Shepelev <temotor@gmail.com>
Peng Gao <peng.gao.dut@gmail.com>

View File

@ -21,7 +21,7 @@ func (item Item) Expired() bool {
}
const (
// For use with functions that take an expiration time.
// For use with functions that take no expiration time.
NoExpiration time.Duration = -1
// For use with functions that take an expiration time. Equivalent to
// passing in the same expiration duration as was given to New() or

361
cachemap/main.go Normal file
View File

@ -0,0 +1,361 @@
package main
import (
"flag"
"fmt"
"go/parser"
"go/token"
"os"
"text/template"
)
var cachemapTemplate = `// Automatically generated file; DO NOT EDIT
package {{ .PackageName }}
import (
"fmt"
"runtime"
"sync"
"time"
)
type Item struct {
Object {{ .ValueType }}
Expiration int64
}
// Returns true if the item has expired.
func (item Item) Expired() bool {
if item.Expiration == 0 {
return false
}
return time.Now().UnixNano() > item.Expiration
}
const (
// For use with functions that take no expiration time.
NoExpiration time.Duration = -1
// For use with functions that take an expiration time. Equivalent to
// passing in the same expiration duration as was given to {{ .Cache }}New().
DefaultExpiration time.Duration = 0
)
type {{ .Cache }} struct {
*cache
// If this is confusing, see the comment at the bottom of {{ .Cache }}New()
}
type cache struct {
defaultExpiration time.Duration
items map[string]Item
mu sync.RWMutex
onEvicted func(string, *{{ .ValueType }})
janitor *janitor
}
// Add an item to the cache, replacing any existing item. If the duration is 0
// (DefaultExpiration), the cache's default expiration time is used. If it is -1
// (NoExpiration), the item never expires.
func (c *cache) Set(k string, x {{ .ValueType }}, d time.Duration) {
// "Inlining" of set
var e int64
if d == DefaultExpiration {
d = c.defaultExpiration
}
if d > 0 {
e = time.Now().Add(d).UnixNano()
}
c.mu.Lock()
c.items[k] = Item{
Object: x,
Expiration: e,
}
// TODO: Calls to mu.Unlock are currently not deferred because defer
// adds ~200 ns (as of go1.)
c.mu.Unlock()
}
func (c *cache) set(k string, x {{ .ValueType }}, d time.Duration) {
var e int64
if d == DefaultExpiration {
d = c.defaultExpiration
}
if d > 0 {
e = time.Now().Add(d).UnixNano()
}
c.items[k] = Item{
Object: x,
Expiration: e,
}
}
// Add an item to the cache only if an item doesn't already exist for the given
// key, or if the existing item has expired. Returns an error otherwise.
func (c *cache) Add(k string, x {{ .ValueType }}, d time.Duration) error {
c.mu.Lock()
_, found := c.get(k)
if found {
c.mu.Unlock()
return fmt.Errorf("Item %s already exists", k)
}
c.set(k, x, d)
c.mu.Unlock()
return nil
}
// Set a new value for the cache key only if it already exists, and the existing
// item hasn't expired. Returns an error otherwise.
func (c *cache) Replace(k string, x {{ .ValueType }}, d time.Duration) error {
c.mu.Lock()
_, found := c.get(k)
if !found {
c.mu.Unlock()
return fmt.Errorf("Item %s doesn't exist", k)
}
c.set(k, x, d)
c.mu.Unlock()
return nil
}
// Get an item from the cache. Returns the item or nil, and a bool indicating
// whether the key was found.
func (c *cache) Get(k string) *{{ .ValueType }} {
c.mu.RLock()
// "Inlining" of get and Expired
item, found := c.items[k]
if !found {
c.mu.RUnlock()
return nil
}
if item.Expiration > 0 {
if time.Now().UnixNano() > item.Expiration {
c.mu.RUnlock()
return nil
}
}
c.mu.RUnlock()
return &item.Object
}
func (c *cache) get(k string) (*{{ .ValueType }}, bool) {
item, found := c.items[k]
if !found {
return nil, false
}
// "Inlining" of Expired
if item.Expiration > 0 {
if time.Now().UnixNano() > item.Expiration {
return nil, false
}
}
return &item.Object, true
}
// 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 {
return nil
}
// Decrement 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 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 {
// TODO: Implement Increment and Decrement more cleanly.
// (Cannot do Increment(k, n*-1) for uints.)
return nil
}
// Delete an item from the cache. Does nothing if the key is not in the cache.
func (c *cache) Delete(k string) {
c.mu.Lock()
v, evicted := c.delete(k)
c.mu.Unlock()
if evicted {
c.onEvicted(k, v)
}
}
func (c *cache) delete(k string) (*{{ .ValueType }}, bool) {
if c.onEvicted != nil {
if v, found := c.items[k]; found {
delete(c.items, k)
return &v.Object, true
}
}
delete(c.items, k)
return nil, false
}
type keyAndValue struct {
key string
value *{{ .ValueType }}
}
// Delete all expired items from the cache.
func (c *cache) DeleteExpired() {
var evictedItems []keyAndValue
now := time.Now().UnixNano()
c.mu.Lock()
for k, v := range c.items {
// "Inlining" of expired
if v.Expiration > 0 && now > v.Expiration {
ov, evicted := c.delete(k)
if evicted {
evictedItems = append(evictedItems, keyAndValue{k, ov})
}
}
}
c.mu.Unlock()
for _, v := range evictedItems {
c.onEvicted(v.key, v.value)
}
}
// 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 {
c.mu.RLock()
n := len(c.items)
c.mu.RUnlock()
return n
}
// Delete all items from the cache.
func (c *cache) Flush() {
c.mu.Lock()
c.items = map[string]Item{}
c.mu.Unlock()
}
type janitor struct {
Interval time.Duration
stop chan bool
}
func (j *janitor) Run(c *cache) {
j.stop = make(chan bool)
ticker := time.NewTicker(j.Interval)
for {
select {
case <-ticker.C:
c.DeleteExpired()
case <-j.stop:
ticker.Stop()
return
}
}
}
func stopJanitor(c *{{ .Cache }}) {
c.janitor.stop <- true
}
func runJanitor(c *cache, ci time.Duration) {
j := &janitor{
Interval: ci,
}
c.janitor = j
go j.Run(c)
}
func newCache(de time.Duration, m map[string]Item) *cache {
if de == 0 {
de = -1
}
c := &cache{
defaultExpiration: de,
items: m,
}
return c
}
func newCacheWithJanitor(de time.Duration, ci time.Duration, m map[string]Item) *{{ .Cache }} {
c := newCache(de, m)
// This trick ensures that the janitor goroutine (which--granted it
// was enabled--is running DeleteExpired on c forever) does not keep
// the returned C object from being garbage collected. When it is
// garbage collected, the finalizer stops the janitor goroutine, after
// which c can be collected.
C := &{{ .Cache }}{c}
if ci > 0 {
runJanitor(c, ci)
// 如果C被回收了,但是c不会,因为stopJanitor是一个一直运行的
// goroutine对c一直有引用不会被回收,所以加一个Finalizer来停掉
// 这个goroutine然后让c被回收.
runtime.SetFinalizer(C, stopJanitor)
}
return C
}
// Return a new cache with a given default expiration duration and cleanup
// interval. If the expiration duration is less than one (or NoExpiration),
// the items in the cache never expire (by default), and must be deleted
// manually. If the cleanup interval is less than one, expired items are not
// deleted from the cache before calling c.DeleteExpired().
func {{ .Cache }}New(defaultExpiration, cleanupInterval time.Duration) *{{ .Cache }}{
items := make(map[string]Item)
return newCacheWithJanitor(defaultExpiration, cleanupInterval, items)
}`
func fatal(v ...interface{}) {
fmt.Fprintln(os.Stderr, v...)
os.Exit(1)
}
func main() {
keyType := flag.String("k", "", "key type")
valueType := flag.String("v", "", "value type")
flag.Parse()
if *keyType == "" {
fatal("key empty")
}
if *valueType == "" {
fatal("value empty")
}
fset := token.NewFileSet()
pkgs, err := parser.ParseDir(fset, ".", nil, parser.ParseComments)
if err != nil {
fatal(err)
}
var packageName string
for name := range pkgs {
packageName = name
}
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 {
fatal(err)
}
defer f.Close()
tpl, err := template.New("cachemap").Parse(cachemapTemplate)
if err != nil {
fatal(err)
}
err = tpl.Execute(
f,
map[string]string{
"ValueType": *valueType,
"PackageName": packageName,
"Cache": fmt.Sprintf("String2%sCache", *valueType),
},
)
if err != nil {
fatal(err)
}
}