Мартин обнови решението на 18.11.2014 12:05 (преди над 3 години)
+package main
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+)
+
+func dump() {
+ fmt.Println("hello")
+ strings.HasPrefix("erqa", "f")
+ // f := sync.Mutex
+}
+
+type ExpireMapError struct {
+ s string
+}
+
+func (e ExpireMapError) Error() string {
+ return e.s
+}
+
+type element struct {
+ value interface{}
+ timeStamp time.Time
+ expire time.Duration
+ stopReq chan interface{}
+}
+
+type ExpireMap struct {
+ elements map[string]*element
+ expireElements []chan string
+ mutex sync.Mutex
+}
+
+func NewExpireMap() *ExpireMap {
+ result := new(ExpireMap)
+ result.elements = make(map[string]*element)
+ result.expireElements = make([]chan string, 0)
+ return result
+}
+
+func (em *ExpireMap) sendExpireKeyToChan(key string) {
+ for _, ch := range em.expireElements {
+ go func(ch chan string) {
+ ch <- key
+ }(ch)
+ }
+}
+
+func waitExpire(key string, em *ExpireMap) {
+ select {
+ case <-time.After(em.elements[key].expire):
+ go em.sendExpireKeyToChan(key)
+ em.expiredDelete(key)
+ case _, _ = <-em.elements[key].stopReq:
+ }
+
+}
+
+func newElement(value interface{}, expire time.Duration) *element {
+ result := new(element)
+ result.value = value
+ result.timeStamp = time.Now()
+ result.expire = expire
+ result.stopReq = make(chan interface{})
+ return result
+}
+
+func (em *ExpireMap) Set(key string, value interface{}, expire time.Duration) {
+ em.mutex.Lock()
+ defer em.mutex.Unlock()
+ inMap := em.Contains(key)
+ if inMap {
+ em.elements[key].stopReq <- struct{}{}
+ em.unsafeDelete(key)
+ }
+ em.elements[key] = newElement(value, expire)
+ go waitExpire(key, em)
+}
+
+func (em *ExpireMap) Get(key string) (interface{}, bool) {
+ result, ok := em.elements[key]
+ if !ok {
+ return nil, false
+ }
+
+ return result.value, ok
+}
+
+func (em *ExpireMap) GetString(key string) (string, bool) {
+ result, ok := em.Get(key)
+ if !ok {
+ return "", false
+ }
+ switch result.(type) {
+ default:
+ return "", false
+ case string:
+ return result.(string), true
+ }
+}
+
+func (em *ExpireMap) GetInt(key string) (int, bool) {
+ result, ok := em.Get(key)
+ if !ok {
+ return 0, false
+ }
+ switch result.(type) {
+ default:
+ return 0, false
+ case int:
+ return result.(int), true
+ }
+}
+
+func (em *ExpireMap) GetFloat64(key string) (float64, bool) {
+ result, ok := em.Get(key)
+ if !ok {
+ return 0, false
+ }
+ switch result.(type) {
+ default:
+ return 0, false
+ case float64:
+ return result.(float64), true
+ }
+}
+
+func (em *ExpireMap) GetBool(key string) (bool, bool) {
+ result, ok := em.Get(key)
+ if !ok {
+ return false, false
+ }
+ switch result.(type) {
+ default:
+ return false, false
+ case bool:
+ return result.(bool), true
+ }
+}
+
+func (em *ExpireMap) Expires(key string) (time.Time, bool) {
+ result, ok := em.elements[key]
+ if ok {
+ return result.timeStamp.Add(time.Since(result.timeStamp)), ok
+ }
+ return time.Now(), ok
+}
+
+func (em *ExpireMap) expiredDelete(key string) {
+ em.mutex.Lock()
+ defer em.mutex.Unlock()
+ _, ok := em.elements[key]
+ if ok {
+ em.unsafeDelete(key)
+ }
+
+}
+
+func (em *ExpireMap) unsafeDelete(key string) {
+ value := em.elements[key]
+ delete(em.elements, key)
+ close(value.stopReq)
+}
+
+func (em *ExpireMap) Delete(key string) {
+ em.mutex.Lock()
+ defer em.mutex.Unlock()
+ _, ok := em.elements[key]
+ if ok {
+ em.elements[key].stopReq <- struct{}{}
+ em.unsafeDelete(key)
+ }
+}
+
+func (em *ExpireMap) Contains(key string) bool {
+ _, ok := em.elements[key]
+ return ok
+}
+
+func (em *ExpireMap) Size() int {
+ return len(em.elements)
+}
+
+func (em *ExpireMap) helpIncrementDecrement(key string, sign bool) error {
+ var corrector int
+ if sign {
+ corrector = -1
+ } else {
+ corrector = 1
+ }
+
+ em.mutex.Lock()
+ defer em.mutex.Unlock()
+ result, ok := em.Get(key)
+ if ok {
+ switch result.(type) {
+ case int:
+ em.elements[key].value = em.elements[key].value.(int) + corrector
+ return nil
+
+ case string:
+ i, err := strconv.Atoi(result.(string))
+ if err != nil {
+ return ExpireMapError{"wrong type"}
+ }
+ em.elements[key].value = strconv.Itoa(i + corrector)
+ return nil
+ }
+
+ }
+ return ExpireMapError{"wrong type"}
+
+}
+
+func (em *ExpireMap) Increment(key string) error {
+ return em.helpIncrementDecrement(key, false)
+}
+
+func (em *ExpireMap) Decrement(key string) error {
+ return em.helpIncrementDecrement(key, true)
+}
+
+type mutate func(s string) string
+
+func (em *ExpireMap) helpLowUp(key string, fn mutate) error {
+ result, ok := em.Get(key)
+ if !ok {
+ return ExpireMapError{"Key not found"}
+ }
+ switch result.(type) {
+ default:
+ return ExpireMapError{"wrong type"}
+ case string:
+ em.elements[key].value = fn(result.(string))
+ }
+ return nil
+}
+
+func (em *ExpireMap) ToUpper(key string) error {
+ return em.helpLowUp(key, strings.ToUpper)
+
+}
+
+func (em *ExpireMap) ToLower(key string) error {
+ return em.helpLowUp(key, strings.ToLower)
+}
+
+func (em *ExpireMap) ExpiredChan() <-chan string {
+ ch := make(chan string)
+ em.expireElements = append(em.expireElements, ch)
+ return ch
+}
+
+func (em *ExpireMap) Cleanup() {
+ for key, _ := range em.elements {
+ em.Delete(key)
+ }
+}
+
+func (em *ExpireMap) Destroy() {
+ em.Cleanup()
+}
+