Решение на ExpireMap от Стоян Трифонов

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

Към профила на Стоян Трифонов

Резултати

  • 6 точки от тестове
  • 0 бонус точки
  • 6 точки общо
  • 15 успешни тест(а)
  • 10 неуспешни тест(а)

Код

package main
import "time"
import "strconv"
import "strings"
type ExpireMap struct {
items map[string]*Item
expireChannel chan string
}
//Връща указател към нов обект от тип ExpireMap, готов за използване.
func NewExpireMap() *ExpireMap {
em := new(ExpireMap)
em.items = make(map[string]*Item)
em.expireChannel = make(chan string)
return em
}
//Добавя в хранилището нов ключ key със стойност value за expire време.
//Duration е дефиниран в пакета time.
//expire време след добавянето на ключа той вече трябва да спре да бъде в хранилището.
//Стойността може да е от всякакъв тип, докато ключа е от тип string.
func (em *ExpireMap) Set(key string, value interface{}, expire time.Duration) {
item := new(Item)
//item.key = key
item.value = value
item.expires = expire
item.timeAdded = time.Now()
em.items[key] = item
go func(key string, value interface{}, expire time.Duration) {
time.Sleep(expire)
if item.expires <= time.Since(item.timeAdded) {
delete(em.items, key)
em.expireChannel <- key
}
}(key, value, expire)
}
//Връща стойността на ключ и true, когато ключа е намерен. Когато ключа липсва или е изтекъл връща nil и false.
func (em *ExpireMap) Get(key string) (interface{}, bool) {
if item, ok := em.items[key]; ok {
return item.value, true
}
return nil, false
}
//Връща стойността, отговаряща на този ключ и true, когато ключа е намерен и стойността му е от тип int.
//Когато ключа липсва, изтекъл е или стойността му не е от този тип метода трябва да върне нула и false.
func (em *ExpireMap) GetInt(key string) (int, bool) {
if value, ok := em.Get(key); ok {
if val, ok := value.(int); ok {
return val, true
}
return 0, false
}
return 0, false
}
//Връща стойността на този ключ и true, когато ключа е намерен и стойността му е от тип float64.
//Когато ключа липсва, изтекъл е или стойността му не е от този тип метода трябва да върне нула и false.
func (em *ExpireMap) GetFloat64(key string) (float64, bool) {
if value, ok := em.Get(key); ok {
if val, ok := value.(float64); ok {
return val, true
}
return 0, false
}
return 0, false
}
//Връща стойността на ключ и true, когато ключа е намерен и стойността му е от тип string.
//Когато ключа липсва, изтекъл е или стойността не е от тип string метода трябва да върне празен стринг и false.
func (em *ExpireMap) GetString(key string) (string, bool) {
if value, ok := em.Get(key); ok {
if val, ok := value.(string); ok {
return val, true
}
return "", false
}
return "", false
}
//Връща стойността на ключ и true, когато ключа е намерен и стойността му е от тип bool.
//Когато ключа липсва, изтекъл е или стойността не е от тип bool метода трябва да върне false и false.
func (em *ExpireMap) GetBool(key string) (bool, bool) {
if value, ok := em.Get(key); ok {
if val, ok := value.(bool); ok {
return val, true
}
return false, false
}
return false, false
}
//За определен ключ тази функция трябва да върне времето, когато той ще изтече.
//Втората върната стойност е true когато ключа е намерен в хранилището. Когато не е намерен функцията трябва да върне нулевото време и false.
func (em *ExpireMap) Expires(key string) (time.Time, bool) {
item, ok := em.items[key]
if ok {
return item.timeAdded.Add(item.expires), true
}
return time.Time{}, false
}
//Връща големината на хранилището. Големина е бройката неизтекли ключове, които са в него.
func (em *ExpireMap) Size() int {
//lock ?
size := 0
for _, item := range em.items {
if item.expires >= time.Since(item.timeAdded) {
size++
}
}
return size
}
//Казва дали ключ е в хранилището или не. Отново, ключове са в хранилището само ако техния expire не е вече изтекъл.
func (em *ExpireMap) Contains(key string) bool {
//lock
item, ok := em.items[key]
if ok && item.expires >= time.Since(item.timeAdded) {
return true
}
return false
}
//Премахва ключа от хранилището. Когато няма такъв ключ метода не трябав да гърми.
func (em *ExpireMap) Delete(key string) {
delete(em.items, key)
}
//За ключ увеличава стойността му числово с 1 когато тази стойност е от тип int или стринг, представящ цяло число.
//Когато стойността е от тип, различен от тези неща функцията трябва да върне грешка, различна от nil.
//float32 и float64 не са целочислен типове. Времето на изтичане на ключа не трябва да се променя.
func (em *ExpireMap) Increment(key string) error {
item, ok := em.items[key]
error := new(ExpireMapError)
error.ErrorMessage = "Error"
if ok {
if val, ok := item.value.(int); ok {
item.value = val + 1
return nil
}
if val, ok := item.value.(string); ok {
if n, err := strconv.Atoi(val); err == nil {
item.value = n + 1
return nil
}
return error
}
return error
}
return error
}
//Същото като Increment, но намалява стойността с 1 вместо да я увеличава.
func (em *ExpireMap) Decrement(key string) error {
item, ok := em.items[key]
error := new(ExpireMapError)
error.ErrorMessage = "Error"
if ok {
if val, ok := item.value.(int); ok {
item.value = val - 1
return nil
}
if val, ok := item.value.(string); ok {
if n, err := strconv.Atoi(val); err == nil {
item.value = n - 1
return nil
}
return error
}
return error
}
return error
}
//Ако стойността за ключа key е от тип string, то всички букви в този string трябва да станат главни.
//При успех метода трябва да върне nil, а при невъзможност да изпълни задачата - грешка.
func (em *ExpireMap) ToUpper(key string) error {
item, ok := em.items[key]
error := new(ExpireMapError)
error.ErrorMessage = "Error"
if !ok {
return error
}
if val, ok := item.value.(string); ok {
item.value = strings.ToUpper(val)
}
return error
}
func (em *ExpireMap) ToLower(key string) error {
item, ok := em.items[key]
error := new(ExpireMapError)
error.ErrorMessage = "Error"
if !ok {
return error
}
if val, ok := item.value.(string); ok {
item.value = strings.ToLower(val)
}
return error
}
//Тази функция трябва да върне канал, по който да идват ключовете, които са изтекли.
// Те трябва да се пускат по канала в момента на изтичането си. Ключове, които са изтрити с Delete или Cleanup не трябва да се връщат по този канал.
// Единствено тези, които са достигнали до времето си на изтичане. Не е задължително през цялото време някой да чете от върнатия канал.
// Ключове добавени в инстанцията след извикването на ExpireChan също трябва да се пратят по канала, когато тяхното време дойде.
func (em *ExpireMap) ExpiredChan() <-chan string {
return em.expireChannel
}
func (em *ExpireMap) Cleanup() {
em.items = make(map[string]*Item)
}
func (em *ExpireMap) Destroy() {
}
type Item struct {
//key string
value interface{}
expires time.Duration
timeAdded time.Time
}
type ExpireMapError struct {
ErrorMessage string
}
func (error *ExpireMapError) Error() string {
return error.ErrorMessage
}

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

PASS
ok  	_/tmp/d20141204-6466-bi4osd	0.012s
PASS
ok  	_/tmp/d20141204-6466-bi4osd	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(0x8147d30, 0x81c1ba0, 0x19, 0x19, 0x1, ...)
	/usr/local/lib/go/src/pkg/testing/testing.go:434 +0x69f
testing.Main(0x8147d30, 0x81c1ba0, 0x19, 0x19, 0x81c4600, ...)
	/usr/local/lib/go/src/pkg/testing/testing.go:365 +0x69
main.main()
	_/tmp/d20141204-6466-bi4osd/_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-bi4osd.TestSizes(0x1837a1e0)
	/tmp/d20141204-6466-bi4osd/solution_test.go:111 +0x38e
testing.tRunner(0x1837a1e0, 0x81c1bb8)
	/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 [chan send]:
_/tmp/d20141204-6466-bi4osd.func·001(0x18300310, 0x5, 0x80f85c0, 0x18300320, 0x3b9aca00, ...)
	/tmp/d20141204-6466-bi4osd/solution.go:37 +0xc7
created by _/tmp/d20141204-6466-bi4osd.(*ExpireMap).Set
	/tmp/d20141204-6466-bi4osd/solution.go:40 +0x136

goroutine 6 [chan send]:
_/tmp/d20141204-6466-bi4osd.func·001(0x18300338, 0x5, 0x80f85c0, 0x18300348, 0x3b9aca00, ...)
	/tmp/d20141204-6466-bi4osd/solution.go:37 +0xc7
created by _/tmp/d20141204-6466-bi4osd.(*ExpireMap).Set
	/tmp/d20141204-6466-bi4osd/solution.go:40 +0x136

goroutine 7 [chan send]:
_/tmp/d20141204-6466-bi4osd.func·001(0x18300360, 0x5, 0x80f85c0, 0x18300370, 0x3b9aca00, ...)
	/tmp/d20141204-6466-bi4osd/solution.go:37 +0xc7
created by _/tmp/d20141204-6466-bi4osd.(*ExpireMap).Set
	/tmp/d20141204-6466-bi4osd/solution.go:40 +0x136

goroutine 8 [chan send]:
_/tmp/d20141204-6466-bi4osd.func·001(0x18300388, 0x5, 0x80f85c0, 0x18300398, 0x3b9aca00, ...)
	/tmp/d20141204-6466-bi4osd/solution.go:37 +0xc7
created by _/tmp/d20141204-6466-bi4osd.(*ExpireMap).Set
	/tmp/d20141204-6466-bi4osd/solution.go:40 +0x136

goroutine 9 [chan send]:
_/tmp/d20141204-6466-bi4osd.func·001(0x183003b0, 0x5, 0x80f85c0, 0x183003c0, 0x3b9aca00, ...)
	/tmp/d20141204-6466-bi4osd/solution.go:37 +0xc7
created by _/tmp/d20141204-6466-bi4osd.(*ExpireMap).Set
	/tmp/d20141204-6466-bi4osd/solution.go:40 +0x136

goroutine 10 [chan send]:
_/tmp/d20141204-6466-bi4osd.func·001(0x183003d8, 0x5, 0x80f85c0, 0x183003e8, 0x3b9aca00, ...)
	/tmp/d20141204-6466-bi4osd/solution.go:37 +0xc7
created by _/tmp/d20141204-6466-bi4osd.(*ExpireMap).Set
	/tmp/d20141204-6466-bi4osd/solution.go:40 +0x136

goroutine 11 [chan send]:
_/tmp/d20141204-6466-bi4osd.func·001(0x18300400, 0x5, 0x80f85c0, 0x18300410, 0x3b9aca00, ...)
	/tmp/d20141204-6466-bi4osd/solution.go:37 +0xc7
created by _/tmp/d20141204-6466-bi4osd.(*ExpireMap).Set
	/tmp/d20141204-6466-bi4osd/solution.go:40 +0x136

goroutine 12 [chan send]:
_/tmp/d20141204-6466-bi4osd.func·001(0x18300428, 0x5, 0x80f85c0, 0x18300438, 0x3b9aca00, ...)
	/tmp/d20141204-6466-bi4osd/solution.go:37 +0xc7
created by _/tmp/d20141204-6466-bi4osd.(*ExpireMap).Set
	/tmp/d20141204-6466-bi4osd/solution.go:40 +0x136

goroutine 13 [chan send]:
_/tmp/d20141204-6466-bi4osd.func·001(0x18300450, 0x5, 0x80f85c0, 0x18300460, 0x3b9aca00, ...)
	/tmp/d20141204-6466-bi4osd/solution.go:37 +0xc7
created by _/tmp/d20141204-6466-bi4osd.(*ExpireMap).Set
	/tmp/d20141204-6466-bi4osd/solution.go:40 +0x136

goroutine 14 [chan send]:
_/tmp/d20141204-6466-bi4osd.func·001(0x18300478, 0x5, 0x80f85c0, 0x18300488, 0x3b9aca00, ...)
	/tmp/d20141204-6466-bi4osd/solution.go:37 +0xc7
created by _/tmp/d20141204-6466-bi4osd.(*ExpireMap).Set
	/tmp/d20141204-6466-bi4osd/solution.go:40 +0x136
exit status 2
FAIL	_/tmp/d20141204-6466-bi4osd	1.012s
PASS
ok  	_/tmp/d20141204-6466-bi4osd	0.122s
PASS
ok  	_/tmp/d20141204-6466-bi4osd	0.172s
PASS
ok  	_/tmp/d20141204-6466-bi4osd	0.012s
PASS
ok  	_/tmp/d20141204-6466-bi4osd	0.012s
--- FAIL: TestSimpleIncrementAndDecrementCalls-2 (0.00 seconds)
	solution_test.go:252: Getting the number returned an error
	solution_test.go:256: Expected to get 795 but got 
	solution_test.go:266: Getting the number returned an error
	solution_test.go:270: Expected to get 794 but got 
	solution_test.go:306: Getting the negative number faild
	solution_test.go:310: Incrementing the negative number failed
FAIL
exit status 1
FAIL	_/tmp/d20141204-6466-bi4osd	0.012s
PASS
ok  	_/tmp/d20141204-6466-bi4osd	0.012s
--- FAIL: TestIncAndDecInManyRoutines-2 (0.00 seconds)
	solution_test.go:384: Getting the number faild
FAIL
exit status 1
FAIL	_/tmp/d20141204-6466-bi4osd	0.014s
PASS
ok  	_/tmp/d20141204-6466-bi4osd	0.012s
PASS
ok  	_/tmp/d20141204-6466-bi4osd	0.012s
--- FAIL: TestTheExampleInReadme-2 (0.00 seconds)
	solution_test.go:458: spam was not 5
FAIL
exit status 1
FAIL	_/tmp/d20141204-6466-bi4osd	0.012s
--- FAIL: TestToUpperExample-2 (0.00 seconds)
	solution_test.go:475: ToUpping a string returned error: Error
FAIL
exit status 1
FAIL	_/tmp/d20141204-6466-bi4osd	0.012s
PASS
ok  	_/tmp/d20141204-6466-bi4osd	0.119s
PASS
ok  	_/tmp/d20141204-6466-bi4osd	0.062s
--- FAIL: TestExpiredChanWhenNoOneIsReading-2 (0.06 seconds)
	solution_test.go:554: Wrong key expired
FAIL
exit status 1
FAIL	_/tmp/d20141204-6466-bi4osd	0.072s
--- FAIL: TestExpiredChanDoesNotReturnDeletedKeys-2 (0.05 seconds)
	solution_test.go:590: Expires chan returned deleted key
	solution_test.go:594: Expire chan did not return the expected key: long-key
FAIL
exit status 1
FAIL	_/tmp/d20141204-6466-bi4osd	0.062s
--- FAIL: TestExpiredChanDoesNotReturnCleanedupKeys-2 (0.05 seconds)
	solution_test.go:612: Expire chan returned key key after it was cleaned up
FAIL
exit status 1
FAIL	_/tmp/d20141204-6466-bi4osd	0.065s
PASS
ok  	_/tmp/d20141204-6466-bi4osd	0.084s
PASS
ok  	_/tmp/d20141204-6466-bi4osd	0.012s
--- FAIL: TestDestroyMethodClosesExpireChannel-2 (0.05 seconds)
	solution_test.go:818: Expire channel was not closed in time
FAIL
exit status 1
FAIL	_/tmp/d20141204-6466-bi4osd	0.062s
PASS
ok  	_/tmp/d20141204-6466-bi4osd	0.012s
PASS
ok  	_/tmp/d20141204-6466-bi4osd	0.272s
--- FAIL: TestStringMethods-2 (0.00 seconds)
	solution_test.go:914: ToUpper returned an error
	solution_test.go:929: ToLower returned an error
FAIL
exit status 1
FAIL	_/tmp/d20141204-6466-bi4osd	0.012s

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

Стоян обнови решението на 18.11.2014 16:49 (преди над 3 години)

+package main
+
+import "time"
+import "strconv"
+import "strings"
+
+type ExpireMap struct {
+ items map[string]*Item
+ expireChannel chan string
+}
+
+//Връща указател към нов обект от тип ExpireMap, готов за използване.
+func NewExpireMap() *ExpireMap {
+ em := new(ExpireMap)
+ em.items = make(map[string]*Item)
+ em.expireChannel = make(chan string)
+ return em
+}
+
+//Добавя в хранилището нов ключ key със стойност value за expire време.
+//Duration е дефиниран в пакета time.
+//expire време след добавянето на ключа той вече трябва да спре да бъде в хранилището.
+//Стойността може да е от всякакъв тип, докато ключа е от тип string.
+func (em *ExpireMap) Set(key string, value interface{}, expire time.Duration) {
+ item := new(Item)
+ //item.key = key
+ item.value = value
+ item.expires = expire
+ item.timeAdded = time.Now()
+ em.items[key] = item
+
+ go func(key string, value interface{}, expire time.Duration) {
+ time.Sleep(expire)
+
+ if item.expires <= time.Since(item.timeAdded) {
+ delete(em.items, key)
+ em.expireChannel <- key
+ }
+
+ }(key, value, expire)
+
+}
+
+//Връща стойността на ключ и true, когато ключа е намерен. Когато ключа липсва или е изтекъл връща nil и false.
+func (em *ExpireMap) Get(key string) (interface{}, bool) {
+ if item, ok := em.items[key]; ok {
+ return item.value, true
+ }
+
+ return nil, false
+
+}
+
+//Връща стойността, отговаряща на този ключ и true, когато ключа е намерен и стойността му е от тип int.
+//Когато ключа липсва, изтекъл е или стойността му не е от този тип метода трябва да върне нула и false.
+func (em *ExpireMap) GetInt(key string) (int, bool) {
+ if value, ok := em.Get(key); ok {
+ if val, ok := value.(int); ok {
+ return val, true
+ }
+
+ return 0, false
+ }
+
+ return 0, false
+}
+
+//Връща стойността на този ключ и true, когато ключа е намерен и стойността му е от тип float64.
+//Когато ключа липсва, изтекъл е или стойността му не е от този тип метода трябва да върне нула и false.
+func (em *ExpireMap) GetFloat64(key string) (float64, bool) {
+ if value, ok := em.Get(key); ok {
+ if val, ok := value.(float64); ok {
+ return val, true
+ }
+
+ return 0, false
+ }
+
+ return 0, false
+}
+
+//Връща стойността на ключ и true, когато ключа е намерен и стойността му е от тип string.
+//Когато ключа липсва, изтекъл е или стойността не е от тип string метода трябва да върне празен стринг и false.
+func (em *ExpireMap) GetString(key string) (string, bool) {
+ if value, ok := em.Get(key); ok {
+ if val, ok := value.(string); ok {
+ return val, true
+ }
+
+ return "", false
+ }
+
+ return "", false
+}
+
+//Връща стойността на ключ и true, когато ключа е намерен и стойността му е от тип bool.
+//Когато ключа липсва, изтекъл е или стойността не е от тип bool метода трябва да върне false и false.
+func (em *ExpireMap) GetBool(key string) (bool, bool) {
+ if value, ok := em.Get(key); ok {
+ if val, ok := value.(bool); ok {
+ return val, true
+ }
+
+ return false, false
+ }
+
+ return false, false
+
+}
+
+//За определен ключ тази функция трябва да върне времето, когато той ще изтече.
+//Втората върната стойност е true когато ключа е намерен в хранилището. Когато не е намерен функцията трябва да върне нулевото време и false.
+func (em *ExpireMap) Expires(key string) (time.Time, bool) {
+ item, ok := em.items[key]
+ if ok {
+ return item.timeAdded.Add(item.expires), true
+ }
+
+ return time.Time{}, false
+}
+
+//Връща големината на хранилището. Големина е бройката неизтекли ключове, които са в него.
+func (em *ExpireMap) Size() int {
+ //lock ?
+ size := 0
+ for _, item := range em.items {
+ if item.expires >= time.Since(item.timeAdded) {
+ size++
+ }
+ }
+
+ return size
+}
+
+//Казва дали ключ е в хранилището или не. Отново, ключове са в хранилището само ако техния expire не е вече изтекъл.
+func (em *ExpireMap) Contains(key string) bool {
+ //lock
+ item, ok := em.items[key]
+
+ if ok && item.expires >= time.Since(item.timeAdded) {
+ return true
+ }
+
+ return false
+}
+
+//Премахва ключа от хранилището. Когато няма такъв ключ метода не трябав да гърми.
+func (em *ExpireMap) Delete(key string) {
+ delete(em.items, key)
+}
+
+//За ключ увеличава стойността му числово с 1 когато тази стойност е от тип int или стринг, представящ цяло число.
+//Когато стойността е от тип, различен от тези неща функцията трябва да върне грешка, различна от nil.
+//float32 и float64 не са целочислен типове. Времето на изтичане на ключа не трябва да се променя.
+func (em *ExpireMap) Increment(key string) error {
+ item, ok := em.items[key]
+
+ error := new(ExpireMapError)
+ error.ErrorMessage = "Error"
+
+ if ok {
+ if val, ok := item.value.(int); ok {
+ item.value = val + 1
+ return nil
+ }
+
+ if val, ok := item.value.(string); ok {
+ if n, err := strconv.Atoi(val); err == nil {
+ item.value = n + 1
+ return nil
+ }
+
+ return error
+ }
+
+ return error
+ }
+
+ return error
+}
+
+//Същото като Increment, но намалява стойността с 1 вместо да я увеличава.
+func (em *ExpireMap) Decrement(key string) error {
+ item, ok := em.items[key]
+
+ error := new(ExpireMapError)
+ error.ErrorMessage = "Error"
+
+ if ok {
+ if val, ok := item.value.(int); ok {
+ item.value = val - 1
+ return nil
+ }
+
+ if val, ok := item.value.(string); ok {
+ if n, err := strconv.Atoi(val); err == nil {
+ item.value = n - 1
+ return nil
+ }
+
+ return error
+ }
+
+ return error
+ }
+
+ return error
+}
+
+//Ако стойността за ключа key е от тип string, то всички букви в този string трябва да станат главни.
+//При успех метода трябва да върне nil, а при невъзможност да изпълни задачата - грешка.
+func (em *ExpireMap) ToUpper(key string) error {
+ item, ok := em.items[key]
+
+ error := new(ExpireMapError)
+ error.ErrorMessage = "Error"
+
+ if !ok {
+ return error
+ }
+
+ if val, ok := item.value.(string); ok {
+ item.value = strings.ToUpper(val)
+ }
+
+ return error
+}
+
+func (em *ExpireMap) ToLower(key string) error {
+ item, ok := em.items[key]
+
+ error := new(ExpireMapError)
+ error.ErrorMessage = "Error"
+
+ if !ok {
+ return error
+ }
+
+ if val, ok := item.value.(string); ok {
+ item.value = strings.ToLower(val)
+ }
+
+ return error
+}
+
+//Тази функция трябва да върне канал, по който да идват ключовете, които са изтекли.
+// Те трябва да се пускат по канала в момента на изтичането си. Ключове, които са изтрити с Delete или Cleanup не трябва да се връщат по този канал.
+// Единствено тези, които са достигнали до времето си на изтичане. Не е задължително през цялото време някой да чете от върнатия канал.
+// Ключове добавени в инстанцията след извикването на ExpireChan също трябва да се пратят по канала, когато тяхното време дойде.
+func (em *ExpireMap) ExpiredChan() <-chan string {
+ return nil
+}
+
+func (em *ExpireMap) Cleanup() {
+ em.items = make(map[string]*Item)
+}
+
+func (em *ExpireMap) Destroy() {
+
+}
+
+type Item struct {
+ //key string
+ value interface{}
+ expires time.Duration
+ timeAdded time.Time
+}
+
+type ExpireMapError struct {
+ ErrorMessage string
+}
+
+func (error *ExpireMapError) Error() string {
+ return error.ErrorMessage
+}

Стоян обнови решението на 18.11.2014 16:53 (преди над 3 години)

package main
import "time"
import "strconv"
import "strings"
type ExpireMap struct {
items map[string]*Item
expireChannel chan string
}
//Връща указател към нов обект от тип ExpireMap, готов за използване.
func NewExpireMap() *ExpireMap {
em := new(ExpireMap)
em.items = make(map[string]*Item)
em.expireChannel = make(chan string)
return em
}
//Добавя в хранилището нов ключ key със стойност value за expire време.
//Duration е дефиниран в пакета time.
//expire време след добавянето на ключа той вече трябва да спре да бъде в хранилището.
//Стойността може да е от всякакъв тип, докато ключа е от тип string.
func (em *ExpireMap) Set(key string, value interface{}, expire time.Duration) {
item := new(Item)
//item.key = key
item.value = value
item.expires = expire
item.timeAdded = time.Now()
em.items[key] = item
go func(key string, value interface{}, expire time.Duration) {
time.Sleep(expire)
if item.expires <= time.Since(item.timeAdded) {
delete(em.items, key)
em.expireChannel <- key
}
}(key, value, expire)
}
//Връща стойността на ключ и true, когато ключа е намерен. Когато ключа липсва или е изтекъл връща nil и false.
func (em *ExpireMap) Get(key string) (interface{}, bool) {
if item, ok := em.items[key]; ok {
return item.value, true
}
return nil, false
}
//Връща стойността, отговаряща на този ключ и true, когато ключа е намерен и стойността му е от тип int.
//Когато ключа липсва, изтекъл е или стойността му не е от този тип метода трябва да върне нула и false.
func (em *ExpireMap) GetInt(key string) (int, bool) {
if value, ok := em.Get(key); ok {
if val, ok := value.(int); ok {
return val, true
}
return 0, false
}
return 0, false
}
//Връща стойността на този ключ и true, когато ключа е намерен и стойността му е от тип float64.
//Когато ключа липсва, изтекъл е или стойността му не е от този тип метода трябва да върне нула и false.
func (em *ExpireMap) GetFloat64(key string) (float64, bool) {
if value, ok := em.Get(key); ok {
if val, ok := value.(float64); ok {
return val, true
}
return 0, false
}
return 0, false
}
//Връща стойността на ключ и true, когато ключа е намерен и стойността му е от тип string.
//Когато ключа липсва, изтекъл е или стойността не е от тип string метода трябва да върне празен стринг и false.
func (em *ExpireMap) GetString(key string) (string, bool) {
if value, ok := em.Get(key); ok {
if val, ok := value.(string); ok {
return val, true
}
return "", false
}
return "", false
}
//Връща стойността на ключ и true, когато ключа е намерен и стойността му е от тип bool.
//Когато ключа липсва, изтекъл е или стойността не е от тип bool метода трябва да върне false и false.
func (em *ExpireMap) GetBool(key string) (bool, bool) {
if value, ok := em.Get(key); ok {
if val, ok := value.(bool); ok {
return val, true
}
return false, false
}
return false, false
}
//За определен ключ тази функция трябва да върне времето, когато той ще изтече.
//Втората върната стойност е true когато ключа е намерен в хранилището. Когато не е намерен функцията трябва да върне нулевото време и false.
func (em *ExpireMap) Expires(key string) (time.Time, bool) {
item, ok := em.items[key]
if ok {
return item.timeAdded.Add(item.expires), true
}
return time.Time{}, false
}
//Връща големината на хранилището. Големина е бройката неизтекли ключове, които са в него.
func (em *ExpireMap) Size() int {
//lock ?
size := 0
for _, item := range em.items {
if item.expires >= time.Since(item.timeAdded) {
size++
}
}
return size
}
//Казва дали ключ е в хранилището или не. Отново, ключове са в хранилището само ако техния expire не е вече изтекъл.
func (em *ExpireMap) Contains(key string) bool {
//lock
item, ok := em.items[key]
if ok && item.expires >= time.Since(item.timeAdded) {
return true
}
return false
}
//Премахва ключа от хранилището. Когато няма такъв ключ метода не трябав да гърми.
func (em *ExpireMap) Delete(key string) {
delete(em.items, key)
}
//За ключ увеличава стойността му числово с 1 когато тази стойност е от тип int или стринг, представящ цяло число.
//Когато стойността е от тип, различен от тези неща функцията трябва да върне грешка, различна от nil.
//float32 и float64 не са целочислен типове. Времето на изтичане на ключа не трябва да се променя.
func (em *ExpireMap) Increment(key string) error {
item, ok := em.items[key]
error := new(ExpireMapError)
error.ErrorMessage = "Error"
if ok {
if val, ok := item.value.(int); ok {
item.value = val + 1
return nil
}
if val, ok := item.value.(string); ok {
if n, err := strconv.Atoi(val); err == nil {
item.value = n + 1
return nil
}
return error
}
return error
}
return error
}
//Същото като Increment, но намалява стойността с 1 вместо да я увеличава.
func (em *ExpireMap) Decrement(key string) error {
item, ok := em.items[key]
error := new(ExpireMapError)
error.ErrorMessage = "Error"
if ok {
if val, ok := item.value.(int); ok {
item.value = val - 1
return nil
}
if val, ok := item.value.(string); ok {
if n, err := strconv.Atoi(val); err == nil {
item.value = n - 1
return nil
}
return error
}
return error
}
return error
}
//Ако стойността за ключа key е от тип string, то всички букви в този string трябва да станат главни.
//При успех метода трябва да върне nil, а при невъзможност да изпълни задачата - грешка.
func (em *ExpireMap) ToUpper(key string) error {
item, ok := em.items[key]
error := new(ExpireMapError)
error.ErrorMessage = "Error"
if !ok {
return error
}
if val, ok := item.value.(string); ok {
item.value = strings.ToUpper(val)
}
return error
}
func (em *ExpireMap) ToLower(key string) error {
item, ok := em.items[key]
error := new(ExpireMapError)
error.ErrorMessage = "Error"
if !ok {
return error
}
if val, ok := item.value.(string); ok {
item.value = strings.ToLower(val)
}
return error
}
//Тази функция трябва да върне канал, по който да идват ключовете, които са изтекли.
// Те трябва да се пускат по канала в момента на изтичането си. Ключове, които са изтрити с Delete или Cleanup не трябва да се връщат по този канал.
// Единствено тези, които са достигнали до времето си на изтичане. Не е задължително през цялото време някой да чете от върнатия канал.
// Ключове добавени в инстанцията след извикването на ExpireChan също трябва да се пратят по канала, когато тяхното време дойде.
func (em *ExpireMap) ExpiredChan() <-chan string {
- return nil
+ return em.expireChannel
}
func (em *ExpireMap) Cleanup() {
em.items = make(map[string]*Item)
}
func (em *ExpireMap) Destroy() {
}
type Item struct {
//key string
value interface{}
expires time.Duration
timeAdded time.Time
}
type ExpireMapError struct {
ErrorMessage string
}
func (error *ExpireMapError) Error() string {
return error.ErrorMessage
}