Решение на ExpireMap от Мартин Добрев

Обратно към всички решения

Към профила на Мартин Добрев

Резултати

  • 9 точки от тестове
  • 0 бонус точки
  • 9 точки общо
  • 22 успешни тест(а)
  • 3 неуспешни тест(а)

Код

package main
import (
"strconv"
"strings"
"sync"
"time"
)
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) {
select {
case ch <- key:
case _, _ = <-ch:
}
}(ch)
}
}
func waitExpire(key string, em *ExpireMap) {
value, ok := em.elements[key]
if ok {
select {
case <-time.After(value.expire):
go em.sendExpireKeyToChan(key)
em.expiredDelete(key)
case _, _ = <-value.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()
exist, ok := em.elements[key]
if ok {
exist.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, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
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()
for _, ch := range em.expireElements {
close(ch)
}
}

Лог от изпълнението

PASS
ok  	_/tmp/d20141204-6466-508rjm	0.012s
PASS
ok  	_/tmp/d20141204-6466-508rjm	0.012s
panic: test timed out

goroutine 15 [running]:
testing.alarm()
	/usr/local/lib/go/src/pkg/testing/testing.go:533 +0x44
created by time.goFunc
	/usr/local/lib/go/src/pkg/time/sleep.go:122 +0x45

goroutine 1 [chan receive]:
testing.RunTests(0x8149778, 0x81c4ba0, 0x19, 0x19, 0x1, ...)
	/usr/local/lib/go/src/pkg/testing/testing.go:434 +0x69f
testing.Main(0x8149778, 0x81c4ba0, 0x19, 0x19, 0x81c7600, ...)
	/usr/local/lib/go/src/pkg/testing/testing.go:365 +0x69
main.main()
	_/tmp/d20141204-6466-508rjm/_test/_testmain.go:91 +0x81

goroutine 4 [sleep]:
time.Sleep(0x3c336080, 0x0)
	/usr/local/lib/go/src/pkg/runtime/ztime_linux_386.c:19 +0x3a
_/tmp/d20141204-6466-508rjm.TestSizes(0x1837a1e0)
	/tmp/d20141204-6466-508rjm/solution_test.go:111 +0x3b6
testing.tRunner(0x1837a1e0, 0x81c4bb8)
	/usr/local/lib/go/src/pkg/testing/testing.go:353 +0x87
created by testing.RunTests
	/usr/local/lib/go/src/pkg/testing/testing.go:433 +0x684

goroutine 5 [select]:
_/tmp/d20141204-6466-508rjm.waitExpire(0x18300308, 0x5, 0x18331800)
	/tmp/d20141204-6466-508rjm/solution.go:52 +0x12d
created by _/tmp/d20141204-6466-508rjm.(*ExpireMap).Set
	/tmp/d20141204-6466-508rjm/solution.go:79 +0x1c1

goroutine 6 [select]:
_/tmp/d20141204-6466-508rjm.waitExpire(0x18300320, 0x5, 0x18331800)
	/tmp/d20141204-6466-508rjm/solution.go:52 +0x12d
created by _/tmp/d20141204-6466-508rjm.(*ExpireMap).Set
	/tmp/d20141204-6466-508rjm/solution.go:79 +0x1c1

goroutine 7 [select]:
_/tmp/d20141204-6466-508rjm.waitExpire(0x18300338, 0x5, 0x18331800)
	/tmp/d20141204-6466-508rjm/solution.go:52 +0x12d
created by _/tmp/d20141204-6466-508rjm.(*ExpireMap).Set
	/tmp/d20141204-6466-508rjm/solution.go:79 +0x1c1

goroutine 8 [select]:
_/tmp/d20141204-6466-508rjm.waitExpire(0x18300350, 0x5, 0x18331800)
	/tmp/d20141204-6466-508rjm/solution.go:52 +0x12d
created by _/tmp/d20141204-6466-508rjm.(*ExpireMap).Set
	/tmp/d20141204-6466-508rjm/solution.go:79 +0x1c1

goroutine 9 [select]:
_/tmp/d20141204-6466-508rjm.waitExpire(0x18300368, 0x5, 0x18331800)
	/tmp/d20141204-6466-508rjm/solution.go:52 +0x12d
created by _/tmp/d20141204-6466-508rjm.(*ExpireMap).Set
	/tmp/d20141204-6466-508rjm/solution.go:79 +0x1c1

goroutine 10 [select]:
_/tmp/d20141204-6466-508rjm.waitExpire(0x18300380, 0x5, 0x18331800)
	/tmp/d20141204-6466-508rjm/solution.go:52 +0x12d
created by _/tmp/d20141204-6466-508rjm.(*ExpireMap).Set
	/tmp/d20141204-6466-508rjm/solution.go:79 +0x1c1

goroutine 11 [select]:
_/tmp/d20141204-6466-508rjm.waitExpire(0x18300398, 0x5, 0x18331800)
	/tmp/d20141204-6466-508rjm/solution.go:52 +0x12d
created by _/tmp/d20141204-6466-508rjm.(*ExpireMap).Set
	/tmp/d20141204-6466-508rjm/solution.go:79 +0x1c1

goroutine 12 [select]:
_/tmp/d20141204-6466-508rjm.waitExpire(0x183003b0, 0x5, 0x18331800)
	/tmp/d20141204-6466-508rjm/solution.go:52 +0x12d
created by _/tmp/d20141204-6466-508rjm.(*ExpireMap).Set
	/tmp/d20141204-6466-508rjm/solution.go:79 +0x1c1

goroutine 13 [select]:
_/tmp/d20141204-6466-508rjm.waitExpire(0x183003c8, 0x5, 0x18331800)
	/tmp/d20141204-6466-508rjm/solution.go:52 +0x12d
created by _/tmp/d20141204-6466-508rjm.(*ExpireMap).Set
	/tmp/d20141204-6466-508rjm/solution.go:79 +0x1c1

goroutine 14 [select]:
_/tmp/d20141204-6466-508rjm.waitExpire(0x183003e0, 0x5, 0x18331800)
	/tmp/d20141204-6466-508rjm/solution.go:52 +0x12d
created by _/tmp/d20141204-6466-508rjm.(*ExpireMap).Set
	/tmp/d20141204-6466-508rjm/solution.go:79 +0x1c1
exit status 2
FAIL	_/tmp/d20141204-6466-508rjm	1.013s
PASS
ok  	_/tmp/d20141204-6466-508rjm	0.122s
PASS
ok  	_/tmp/d20141204-6466-508rjm	0.172s
PASS
ok  	_/tmp/d20141204-6466-508rjm	0.012s
PASS
ok  	_/tmp/d20141204-6466-508rjm	0.012s
PASS
ok  	_/tmp/d20141204-6466-508rjm	0.012s
PASS
ok  	_/tmp/d20141204-6466-508rjm	0.012s
PASS
ok  	_/tmp/d20141204-6466-508rjm	0.041s
PASS
ok  	_/tmp/d20141204-6466-508rjm	0.012s
PASS
ok  	_/tmp/d20141204-6466-508rjm	0.012s
PASS
ok  	_/tmp/d20141204-6466-508rjm	0.012s
PASS
ok  	_/tmp/d20141204-6466-508rjm	0.012s
PASS
ok  	_/tmp/d20141204-6466-508rjm	0.112s
PASS
ok  	_/tmp/d20141204-6466-508rjm	0.062s
--- FAIL: TestExpiredChanWhenNoOneIsReading-2 (0.06 seconds)
	solution_test.go:554: Wrong key expired
FAIL
exit status 1
FAIL	_/tmp/d20141204-6466-508rjm	0.073s
PASS
ok  	_/tmp/d20141204-6466-508rjm	0.113s
PASS
ok  	_/tmp/d20141204-6466-508rjm	0.132s
PASS
ok  	_/tmp/d20141204-6466-508rjm	0.117s
PASS
ok  	_/tmp/d20141204-6466-508rjm	0.013s
PASS
ok  	_/tmp/d20141204-6466-508rjm	0.012s
--- FAIL: TestExpiresMethod-2 (0.00 seconds)
	solution_test.go:862: Expected expire time 2014-12-04 12:04:49.169821103 +0200 EET but got 2014-12-04 12:04:39.169862733 +0200 EET
	solution_test.go:862: Expected expire time 2014-12-04 12:04:54.16984441 +0200 EET but got 2014-12-04 12:04:39.170025128 +0200 EET
	solution_test.go:862: Expected expire time 2014-12-04 12:04:59.169852538 +0200 EET but got 2014-12-04 12:04:39.170056345 +0200 EET
FAIL
exit status 1
FAIL	_/tmp/d20141204-6466-508rjm	0.012s
PASS
ok  	_/tmp/d20141204-6466-508rjm	0.272s
PASS
ok  	_/tmp/d20141204-6466-508rjm	0.018s

История (3 версии и 0 коментара)

Мартин обнови решението на 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()
+}
+

Мартин обнови решението на 18.11.2014 12:50 (преди над 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
+ select {
+ case ch <- key:
+ case _, _ = <-ch:
+ }
}(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:
+ value, ok := em.elements[key]
+ if ok {
+ select {
+ case <-time.After(value.expire):
+ go em.sendExpireKeyToChan(key)
+ em.expiredDelete(key)
+ case _, _ = <-value.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{}{}
+ exist, ok := em.elements[key]
+ if ok {
+ exist.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()
-}
+ for _, ch := range em.expireElements {
+ close(ch)
+ }
+}

Мартин обнови решението на 18.11.2014 13:00 (преди над 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) {
select {
case ch <- key:
case _, _ = <-ch:
}
}(ch)
}
}
func waitExpire(key string, em *ExpireMap) {
value, ok := em.elements[key]
if ok {
select {
case <-time.After(value.expire):
go em.sendExpireKeyToChan(key)
em.expiredDelete(key)
case _, _ = <-value.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()
exist, ok := em.elements[key]
if ok {
exist.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:
+ case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
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()
for _, ch := range em.expireElements {
close(ch)
}
}