Иван обнови решението на 17.11.2014 19:04 (преди над 3 години)
+package main
+
+import (
+ "errors"
+ "fmt"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+)
+
+type timestampMap struct {
+ mtx sync.Mutex // use RWMutex instead?
+ Values map[string]time.Time
+}
+
+func (tm *timestampMap) Set(key string, t time.Time) {
+ tm.mtx.Lock()
+ tm.Values[key] = t
+ tm.mtx.Unlock()
+}
+
+func (tm *timestampMap) Unset(key string) {
+ tm.mtx.Lock()
+ delete(tm.Values, key)
+ tm.mtx.Unlock()
+}
+
+func (tm *timestampMap) Clear() {
+ tm.mtx.Lock()
+ tm.Values = make(map[string]time.Time)
+ tm.mtx.Unlock()
+}
+
+type ExpireMap struct {
+ m map[string]interface{}
+ expireTimes *timestampMap
+ stop chan struct{}
+ expired chan string
+}
+
+func NewExpireMap() (em *ExpireMap) {
+ em = &ExpireMap{}
+ em.m = make(map[string]interface{})
+
+ expireTimes := ×tampMap{}
+ expireTimes.Values = make(map[string]time.Time)
+ em.expireTimes = expireTimes
+
+ em.stop = make(chan struct{})
+ em.expired = make(chan string)
+
+ go em.cycle()
+
+ return
+}
+
+func (em *ExpireMap) cycle() {
+ for {
+ select {
+ case <-em.stop:
+ return
+ default:
+ for key, expireTime := range em.expireTimes.Values {
+ if !time.Now().Before(expireTime) {
+ em.expireTimes.Unset(key)
+ delete(em.m, key)
+ go func(key string) {
+ em.expired <- key
+ }(key)
+ }
+ }
+ }
+ }
+}
+
+func (em *ExpireMap) Set(key string, value interface{}, expire time.Duration) {
+ em.expireTimes.Set(key, time.Now().Add(expire))
+ em.m[key] = value
+}
+
+func (em *ExpireMap) Get(key string) (value interface{}, found bool) {
+ value, found = em.m[key]
+ return
+}
+
+func (em *ExpireMap) Contains(key string) (found bool) {
+ _, found = em.m[key]
+ return
+}
+
+func (em *ExpireMap) Delete(key string) {
+ if _, found := em.m[key]; !found {
+ panic("Whoa! There is not such key to delete.")
+ }
+ em.expireTimes.Unset(key)
+ delete(em.m, key)
+}
+
+func (em *ExpireMap) Size() int {
+ return len(em.m)
+}
+
+func (em *ExpireMap) Expires(key string) (expires time.Time, found bool) {
+ expires, found = em.expireTimes.Values[key]
+ return
+}
+
+func (em *ExpireMap) Cleanup() {
+ em.expireTimes.Clear()
+ em.m = make(map[string]interface{})
+}
+
+func (em *ExpireMap) Destroy() {
+ em.stop <- struct{}{}
+ em.expireTimes = nil
+ em.m = nil
+}
+
+func (em *ExpireMap) ExpiredChan() <-chan string {
+ return em.expired
+}
+
+func (em *ExpireMap) GetInt(key string) (intValue int, ok bool) {
+ value, found := em.m[key]
+ if !found {
+ return
+ }
+ intValue, ok = value.(int)
+ return
+}
+
+func (em *ExpireMap) GetFloat64(key string) (floatValue float64, ok bool) {
+ value, found := em.m[key]
+ if !found {
+ return
+ }
+ floatValue, ok = value.(float64)
+ return
+}
+
+func (em *ExpireMap) GetBool(key string) (boolValue bool, ok bool) {
+ value, found := em.m[key]
+ if !found {
+ return
+ }
+ boolValue, ok = value.(bool)
+ return
+}
+
+func (em *ExpireMap) GetString(key string) (stringValue string, ok bool) {
+ value, found := em.m[key]
+ if !found {
+ return
+ }
+ stringValue, ok = value.(string)
+ return
+}
+
+func (em *ExpireMap) ToUpper(key string) error {
+ stringValue, found := em.GetString(key)
+ if !found {
+ return errors.New(fmt.Sprintf("There is no value for key %s.", key))
+ }
+ em.m[key] = strings.ToUpper(stringValue)
+ return nil
+}
+
+func (em *ExpireMap) Increment(key string) error {
+ return em.add(key, 1)
+}
+
+func (em *ExpireMap) Decrement(key string) error {
+ return em.add(key, -1)
+}
+
+func (em *ExpireMap) add(key string, toAdd int) error {
+ intValue, found := em.GetInt(key)
+ if found {
+ em.m[key] = intValue + toAdd
+ return nil
+ }
+ stringValue, found := em.GetString(key)
+ if found {
+ intValue, err := strconv.Atoi(stringValue)
+ if err != nil {
+ return err
+ }
+ em.m[key] = strconv.Itoa(intValue + toAdd)
+ return nil
+ }
+ return errors.New(fmt.Sprintf("There is no value for key %s.", key))
+}