From 478cc77ab9d00fea95e09127d98a705e08516fbe Mon Sep 17 00:00:00 2001 From: rahulguptajss Date: Mon, 24 Jun 2024 11:38:08 +0530 Subject: [PATCH] chore: use timedmap v2 --- go.mod | 2 - go.sum | 21 +- .../github.com/zekroTJA/timedmap/section.go | 136 --------- .../zekroTJA/timedmap/{ => v2}/CHANGELOG.md | 16 ++ .../zekroTJA/timedmap/{ => v2}/LICENCE | 0 .../zekroTJA/timedmap/{ => v2}/README.md | 50 +++- .../zekroTJA/timedmap/{ => v2}/errors.go | 0 .../zekroTJA/timedmap/{ => v2}/timedmap.go | 263 +++++++----------- vendor/modules.txt | 6 +- 9 files changed, 157 insertions(+), 337 deletions(-) delete mode 100644 vendor/github.com/zekroTJA/timedmap/section.go rename vendor/github.com/zekroTJA/timedmap/{ => v2}/CHANGELOG.md (52%) rename vendor/github.com/zekroTJA/timedmap/{ => v2}/LICENCE (100%) rename vendor/github.com/zekroTJA/timedmap/{ => v2}/README.md (50%) rename vendor/github.com/zekroTJA/timedmap/{ => v2}/errors.go (100%) rename vendor/github.com/zekroTJA/timedmap/{ => v2}/timedmap.go (50%) diff --git a/go.mod b/go.mod index 05cccbab8..0d5b25ebc 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,6 @@ require ( github.com/spf13/cobra v1.8.1 github.com/tidwall/gjson v1.17.1 github.com/zekroTJA/timedmap/v2 v2.0.0 - github.com/zekroTJA/timedmap/v2 v2.0.0 golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 golang.org/x/sys v0.21.0 golang.org/x/term v0.21.0 @@ -34,7 +33,6 @@ require ( github.com/rivo/uniseg v0.4.7 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/objx v0.5.2 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tklauser/go-sysconf v0.3.14 // indirect diff --git a/go.sum b/go.sum index b7e99b281..8d86f7fc3 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,5 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= @@ -57,16 +56,12 @@ github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFt github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= -github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= -github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= @@ -82,13 +77,8 @@ github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYg github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -github.com/zekroTJA/timedmap v1.5.2 h1:5bhWBdyvekLHLrZu8cNJB6iCpIQl4bGaG4HTmPbTNKY= -github.com/zekroTJA/timedmap v1.5.2/go.mod h1:Go4uPxMN1Wjl5IgO6HYD1tM9IQhkYEVqcrrdsI4ljXo= +github.com/zekroTJA/timedmap/v2 v2.0.0 h1:Bo9oq8AExd0GuDFbcPXm3xoidUAtrnNsZN1d1Hc3PvY= github.com/zekroTJA/timedmap/v2 v2.0.0/go.mod h1:xHDLg687zASqLBJqoysF+WORHxL/kYNphVD36CRJxhM= -golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc h1:O9NuF4s+E/PvMIy+9IUZB9znFwUIXEWSstNjek6VpVg= -golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= -golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 h1:LoYXNGAShUG3m/ehNk4iFctuhGX/+R1ZpfJ4/ia80JM= -golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -97,16 +87,10 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -114,6 +98,5 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/vendor/github.com/zekroTJA/timedmap/section.go b/vendor/github.com/zekroTJA/timedmap/section.go deleted file mode 100644 index f09e7e0da..000000000 --- a/vendor/github.com/zekroTJA/timedmap/section.go +++ /dev/null @@ -1,136 +0,0 @@ -package timedmap - -import ( - "time" -) - -// Section defines a sectioned access -// wrapper of TimedMap. -type Section interface { - - // Ident returns the current sections identifier - Ident() int - - // Set appends a key-value pair to the map or sets the value of - // a key. expiresAfter sets the expire time after the key-value pair - // will automatically be removed from the map. - Set(key, value interface{}, expiresAfter time.Duration, cb ...callback) - - // GetValue returns an interface of the value of a key in the - // map. The returned value is nil if there is no value to the - // passed key or if the value was expired. - GetValue(key interface{}) interface{} - - // GetExpires returns the expire time of a key-value pair. - // If the key-value pair does not exist in the map or - // was expired, this will return an error object. - GetExpires(key interface{}) (time.Time, error) - - // SetExpires sets the expire time for a key-value - // pair to the passed duration. If there is no value - // to the key passed , this will return an error. - SetExpires(key interface{}, d time.Duration) error - - // Contains returns true, if the key exists in the map. - // false will be returned, if there is no value to the - // key or if the key-value pair was expired. - Contains(key interface{}) bool - - // Remove deletes a key-value pair in the map. - Remove(key interface{}) - - // Refresh extends the expire time for a key-value pair - // about the passed duration. If there is no value to - // the key passed, this will return an error. - Refresh(key interface{}, d time.Duration) error - - // Flush deletes all key-value pairs of the section - // in the map. - Flush() - - // Size returns the current number of key-value pairs - // existent in the section of the map. - Size() (i int) - - // Snapshot returns a new map which represents the - // current key-value state of the internal container. - Snapshot() map[interface{}]interface{} -} - -// section wraps access to a specific -// section of the map. -type section struct { - tm *TimedMap - sec int -} - -// newSection creates a new Section instance -// wrapping the given TimedMap instance and -// section identifier. -func newSection(tm *TimedMap, sec int) *section { - return §ion{ - tm: tm, - sec: sec, - } -} - -func (s *section) Ident() int { - return s.sec -} - -func (s *section) Set(key, value interface{}, expiresAfter time.Duration, cb ...callback) { - s.tm.set(key, s.sec, value, expiresAfter, cb...) -} - -func (s *section) GetValue(key interface{}) interface{} { - v := s.tm.get(key, s.sec) - if v == nil { - return nil - } - return v.value -} - -func (s *section) GetExpires(key interface{}) (time.Time, error) { - v := s.tm.get(key, s.sec) - if v == nil { - return time.Time{}, ErrKeyNotFound - } - return v.expires, nil -} - -func (s *section) SetExpires(key interface{}, d time.Duration) error { - return s.tm.setExpires(key, s.sec, d) -} - -func (s *section) Contains(key interface{}) bool { - return s.tm.get(key, s.sec) != nil -} - -func (s *section) Remove(key interface{}) { - s.tm.remove(key, s.sec) -} - -func (s *section) Refresh(key interface{}, d time.Duration) error { - return s.tm.refresh(key, s.sec, d) -} - -func (s *section) Flush() { - for k := range s.tm.container { - if k.sec == s.sec { - s.tm.remove(k.key, k.sec) - } - } -} - -func (s *section) Size() (i int) { - for k := range s.tm.container { - if k.sec == s.sec { - i++ - } - } - return -} - -func (s *section) Snapshot() map[interface{}]interface{} { - return s.tm.getSnapshot(s.sec) -} diff --git a/vendor/github.com/zekroTJA/timedmap/CHANGELOG.md b/vendor/github.com/zekroTJA/timedmap/v2/CHANGELOG.md similarity index 52% rename from vendor/github.com/zekroTJA/timedmap/CHANGELOG.md rename to vendor/github.com/zekroTJA/timedmap/v2/CHANGELOG.md index cc3fcf471..093862b99 100644 --- a/vendor/github.com/zekroTJA/timedmap/CHANGELOG.md +++ b/vendor/github.com/zekroTJA/timedmap/v2/CHANGELOG.md @@ -1,3 +1,19 @@ +## v2.0.0 + +- Add type parameters for the key and value type of a TimedMap and corresponding constructor functions. +- Remove the section system for better simplicity and usability. +- Remove deprecated `SetExpire` method. +- Update documentation. +- Update minimum required Go version to v1.19.0. + +## v1.5.2 + +- Multiple race conditions have been fixed (by @ShivamKumar2002 in https://github.com/zekroTJA/timedmap/pull/8) + +## v1.5.1 + +- Add [`FromMap`](https://pkg.go.dev/github.com/zekroTJA/timedmap#FromMap) constructor which can be used to create a `TimedMap` from an existing map with the given expiration values for each key-value pair. + ## v1.4.0 - Add `SetExpires` method to match `Section` interface and match naming scheme of the other expire-related endpoints. diff --git a/vendor/github.com/zekroTJA/timedmap/LICENCE b/vendor/github.com/zekroTJA/timedmap/v2/LICENCE similarity index 100% rename from vendor/github.com/zekroTJA/timedmap/LICENCE rename to vendor/github.com/zekroTJA/timedmap/v2/LICENCE diff --git a/vendor/github.com/zekroTJA/timedmap/README.md b/vendor/github.com/zekroTJA/timedmap/v2/README.md similarity index 50% rename from vendor/github.com/zekroTJA/timedmap/README.md rename to vendor/github.com/zekroTJA/timedmap/v2/README.md index 374182958..e57f6a5ca 100644 --- a/vendor/github.com/zekroTJA/timedmap/README.md +++ b/vendor/github.com/zekroTJA/timedmap/v2/README.md @@ -12,7 +12,7 @@ ---
- go get -u github.com/zekroTJA/timedmap + go get -u github.com/zekroTJA/timedmap/v2
--- @@ -21,7 +21,16 @@ This package allows to set values to a map which will expire and disappear after a specified time. -[Here](https://pkg.go.dev/github.com/zekroTJA/timedmap) you can read the docs of this package, generated by pkg.go.dev. +[Here](https://pkg.go.dev/github.com/zekroTJA/timedmap/v2) you can read the docs of this package, generated by pkg.go.dev. + +> [!IMPORTANT] +> The package has been updated to `v2` which will introduce breaking changes to `v1`. +> - The package now requires a minimum Go version of `v1.19`. +> - `TimedMap` and corresponding constructor function now take type parameters for key and value types for improved type safety. +> - Sections have been removed in favor of performance and simplicity of the package. +> - Previously deprecated functions have been removed. +> +> If you experience issues with `v1`, please create an issue with the specific version mentioned. `v1` will still receive updates for bugs and incosistencies alongside `v2`. --- @@ -34,26 +43,37 @@ import ( "log" "time" - "github.com/zekroTJA/timedmap" + "github.com/zekroTJA/timedmap/v2" ) func main() { - // Create a timed map with a cleanup timer interval of 1 second - tm := timedmap.New(1 * time.Second) - // Set value of key "hey" to 213, which will expire after 3 seconds - tm.Set("hey", 213, 3*time.Second) - // Print the value of "hey" + + // Creates a new timed map which scans for + // expired keys every 1 second + tm := timedmap.New[string, int](1 * time.Second) + + // Add a key "hey" with the value 213, which should + // expire after 3 seconds and execute the callback, which + // prints that the key was expired + tm.Set("hey", 213, 3*time.Second, func(v int) { + log.Println("key-value pair of 'hey' has expired") + }) + + // Print key "hey" from timed map printKeyVal(tm, "hey") - // Block the main thread for 5 seconds - // After this time, the key-value pair "hey": 213 has expired + + // Wait for 5 seconds + // During this time the main thread is blocked, the + // key-value pair of "hey" will be expired time.Sleep(5 * time.Second) - // Now, this function should show that there is no key "hey" - // in the map, because it has been expired + + // Printing value of key "hey" wil lfail because the + // key-value pair does not exist anymore printKeyVal(tm, "hey") } -func printKeyVal(tm *timedmap.TimedMap, key interface{}) { - d, ok := tm.GetValue(key).(int) +func printKeyVal(tm *timedmap.TimedMap[string, int], key string) { + d, ok := tm.GetValue(key) if !ok { log.Println("data expired") return @@ -63,7 +83,7 @@ func printKeyVal(tm *timedmap.TimedMap, key interface{}) { } ``` -Further examples, you can find in the [example](examples) directory. +Further examples, you can find in the [examples](examples) directory. If you want to see this package in a practcal use case scenario, please take a look at the rate limiter implementation of the REST API of [myrunes.com](https://myrunes.com), where I have used `timedmap` for storing client-based limiter instances: https://github.com/myrunes/backend/blob/master/internal/ratelimit/ratelimit.go diff --git a/vendor/github.com/zekroTJA/timedmap/errors.go b/vendor/github.com/zekroTJA/timedmap/v2/errors.go similarity index 100% rename from vendor/github.com/zekroTJA/timedmap/errors.go rename to vendor/github.com/zekroTJA/timedmap/v2/errors.go diff --git a/vendor/github.com/zekroTJA/timedmap/timedmap.go b/vendor/github.com/zekroTJA/timedmap/v2/timedmap.go similarity index 50% rename from vendor/github.com/zekroTJA/timedmap/timedmap.go rename to vendor/github.com/zekroTJA/timedmap/v2/timedmap.go index b74a8600d..e0e0b6972 100644 --- a/vendor/github.com/zekroTJA/timedmap/timedmap.go +++ b/vendor/github.com/zekroTJA/timedmap/v2/timedmap.go @@ -1,52 +1,47 @@ package timedmap import ( - "reflect" "sync" "sync/atomic" "time" ) -type callback func(value interface{}) +// Callback is a function which can be called when a key-value-pair has expired. +type Callback[TVal any] func(value TVal) -// TimedMap contains a map with all key-value pairs, -// and a timer, which cleans the map in the set -// tick durations from expired keys. -type TimedMap struct { +// TimedMap is a key-value map with lifetimes attached to values. +// Expired values are removed on access or via a cleanup coroutine, +// which can be enabled via the StartCleanerInternal method. +type TimedMap[TKey comparable, TVal any] struct { mtx sync.RWMutex - container map[keyWrap]*element + container map[TKey]*Element[TVal] elementPool *sync.Pool cleanupTickTime time.Duration cleanerTicker *time.Ticker cleanerStopChan chan bool - cleanerRunning *uint32 + cleanerRunning atomic.Bool } -type keyWrap struct { - sec int - key interface{} -} - -// element contains the actual value as interface type, -// the thime when the value expires and an array of -// callbacks, which will be executed when the element +// Element contains the actual value as interface type, +// the time when the value expires and an array of +// callbacks, which will be executed when the Element // expires. -type element struct { - value interface{} +type Element[TVal any] struct { + value TVal expires time.Time - cbs []callback + cbs []Callback[TVal] } // New creates and returns a new instance of TimedMap. // The passed cleanupTickTime will be passed to the // cleanup ticker, which iterates through the map and -// deletes expired key-value pairs. +// deletes expired key-value pairs on each iteration. // -// Optionally, you can also pass a custom <-chan time.Time +// Optionally, you can also pass a custom <-chan time.Time, // which controls the cleanup cycle if you want to use -// a single syncronyzed timer or if you want to have more -// control over the cleanup loop. +// a single synchronized timer or if you want to have more +// granular control over the cleanup loop. // // When passing 0 as cleanupTickTime and no tickerChan, // the cleanup loop will not be started. You can call @@ -54,124 +49,96 @@ type element struct { // manually start the cleanup loop. These both methods // can also be used to re-define the specification of // the cleanup loop when already running if you want to. -func New(cleanupTickTime time.Duration, tickerChan ...<-chan time.Time) *TimedMap { - return newTimedMap(make(map[keyWrap]*element), cleanupTickTime, tickerChan) +func New[TKey comparable, TVal any](cleanupTickTime time.Duration, tickerChan ...<-chan time.Time) *TimedMap[TKey, TVal] { + return newTimedMap[TKey, TVal](make(map[TKey]*Element[TVal]), cleanupTickTime, tickerChan) } -func FromMap( - m interface{}, +// FromMap creates a new TimedMap containing all entries from +// the passed map m. Each entry will get assigned the passed +// expiration duration. +func FromMap[TKey comparable, TVal any]( + m map[TKey]TVal, expiration time.Duration, cleanupTickTime time.Duration, tickerChan ...<-chan time.Time, -) (*TimedMap, error) { - mv := reflect.ValueOf(m) - if mv.Kind() != reflect.Map { +) (*TimedMap[TKey, TVal], error) { + if m == nil { return nil, ErrValueNoMap } exp := time.Now().Add(expiration) - container := make(map[keyWrap]*element) - - iter := mv.MapRange() - for iter.Next() { - key := iter.Key() - val := iter.Value() - kw := keyWrap{ - sec: 0, - key: key.Interface(), - } - el := &element{ - value: val.Interface(), + container := make(map[TKey]*Element[TVal]) + + for k, v := range m { + el := &Element[TVal]{ + value: v, expires: exp, } - container[kw] = el + container[k] = el } return newTimedMap(container, cleanupTickTime, tickerChan), nil } -// Section returns a sectioned subset of -// the timed map with the given section -// identifier i. -func (tm *TimedMap) Section(i int) Section { - if i == 0 { - return tm - } - return newSection(tm, i) -} - -// Ident returns the current sections ident. -// In the case of the root object TimedMap, -// this is always 0. -func (tm *TimedMap) Ident() int { - return 0 -} - // Set appends a key-value pair to the map or sets the value of -// a key. expiresAfter sets the expire time after the key-value pair +// a key. expiresAfter sets the expiry time after the key-value pair // will automatically be removed from the map. -func (tm *TimedMap) Set(key, value interface{}, expiresAfter time.Duration, cb ...callback) { - tm.set(key, 0, value, expiresAfter, cb...) +func (tm *TimedMap[TKey, TVal]) Set(key TKey, value TVal, expiresAfter time.Duration, cb ...Callback[TVal]) { + tm.set(key, value, expiresAfter, cb...) } // GetValue returns an interface of the value of a key in the // map. The returned value is nil if there is no value to the // passed key or if the value was expired. -func (tm *TimedMap) GetValue(key interface{}) interface{} { - v := tm.get(key, 0) +func (tm *TimedMap[TKey, TVal]) GetValue(key TKey) (val TVal, ok bool) { + v := tm.get(key) if v == nil { - return nil + return val, false } tm.mtx.RLock() defer tm.mtx.RUnlock() - return v.value + return v.value, true } -// GetExpires returns the expire time of a key-value pair. +// GetExpires returns the expiry time of a key-value pair. // If the key-value pair does not exist in the map or // was expired, this will return an error object. -func (tm *TimedMap) GetExpires(key interface{}) (time.Time, error) { - v := tm.get(key, 0) +func (tm *TimedMap[TKey, TVal]) GetExpires(key TKey) (time.Time, error) { + v := tm.get(key) if v == nil { return time.Time{}, ErrKeyNotFound } return v.expires, nil } -// SetExpire is deprecated. -// Please use SetExpires instead. -func (tm *TimedMap) SetExpire(key interface{}, d time.Duration) error { - return tm.SetExpires(key, d) -} - -// SetExpires sets the expire time for a key-value +// SetExpires sets the expiry time for a key-value // pair to the passed duration. If there is no value // to the key passed , this will return an error. -func (tm *TimedMap) SetExpires(key interface{}, d time.Duration) error { - return tm.setExpires(key, 0, d) +func (tm *TimedMap[TKey, TVal]) SetExpires(key TKey, d time.Duration) error { + return tm.setExpires(key, d) } // Contains returns true, if the key exists in the map. // false will be returned, if there is no value to the // key or if the key-value pair was expired. -func (tm *TimedMap) Contains(key interface{}) bool { - return tm.get(key, 0) != nil +func (tm *TimedMap[TKey, TVal]) Contains(key TKey) bool { + return tm.get(key) != nil } // Remove deletes a key-value pair in the map. -func (tm *TimedMap) Remove(key interface{}) { - tm.remove(key, 0) +func (tm *TimedMap[TKey, TVal]) Remove(key TKey) { + tm.remove(key) } -// Refresh extends the expire time for a key-value pair +// Refresh extends the expiry time for a key-value pair // about the passed duration. If there is no value to // the key passed, this will return an error object. -func (tm *TimedMap) Refresh(key interface{}, d time.Duration) error { - return tm.refresh(key, 0, d) +func (tm *TimedMap[TKey, TVal]) Refresh(key TKey, d time.Duration) error { + return tm.refresh(key, d) } // Flush deletes all key-value pairs of the map. -func (tm *TimedMap) Flush() { +func (tm *TimedMap[TKey, TVal]) Flush() { tm.mtx.Lock() defer tm.mtx.Unlock() @@ -183,7 +150,7 @@ func (tm *TimedMap) Flush() { // Size returns the current number of key-value pairs // existent in the map. -func (tm *TimedMap) Size() int { +func (tm *TimedMap[TKey, TVal]) Size() int { return len(tm.container) } @@ -192,8 +159,8 @@ func (tm *TimedMap) Size() int { // // If the cleanup loop is already running, it will be // stopped and restarted using the new specification. -func (tm *TimedMap) StartCleanerInternal(interval time.Duration) { - if atomic.LoadUint32(tm.cleanerRunning) != 0 { +func (tm *TimedMap[TKey, TVal]) StartCleanerInternal(interval time.Duration) { + if tm.cleanerRunning.Load() { tm.StopCleaner() } tm.cleanerTicker = time.NewTicker(interval) @@ -203,12 +170,12 @@ func (tm *TimedMap) StartCleanerInternal(interval time.Duration) { // StartCleanerExternal starts the cleanup loop controlled // by the given initiator channel. This is useful if you // want to have more control over the cleanup loop or if -// you want to sync up multiple timedmaps. +// you want to sync up multiple TimedMaps. // // If the cleanup loop is already running, it will be // stopped and restarted using the new specification. -func (tm *TimedMap) StartCleanerExternal(initiator <-chan time.Time) { - if atomic.LoadUint32(tm.cleanerRunning) != 0 { +func (tm *TimedMap[TKey, TVal]) StartCleanerExternal(initiator <-chan time.Time) { + if tm.cleanerRunning.Load() { tm.StopCleaner() } go tm.cleanupLoop(initiator) @@ -218,8 +185,8 @@ func (tm *TimedMap) StartCleanerExternal(initiator <-chan time.Time) { // This should always be called after exiting a scope // where TimedMap is used that the data can be cleaned // up correctly. -func (tm *TimedMap) StopCleaner() { - if atomic.LoadUint32(tm.cleanerRunning) == 0 { +func (tm *TimedMap[TKey, TVal]) StopCleaner() { + if !tm.cleanerRunning.Load() { return } tm.cleanerStopChan <- true @@ -230,16 +197,16 @@ func (tm *TimedMap) StopCleaner() { // Snapshot returns a new map which represents the // current key-value state of the internal container. -func (tm *TimedMap) Snapshot() map[interface{}]interface{} { - return tm.getSnapshot(0) +func (tm *TimedMap[TKey, TVal]) Snapshot() map[TKey]TVal { + return tm.getSnapshot() } // cleanupLoop holds the loop executing the cleanup // when initiated by tc. -func (tm *TimedMap) cleanupLoop(tc <-chan time.Time) { - atomic.StoreUint32(tm.cleanerRunning, 1) +func (tm *TimedMap[TKey, TVal]) cleanupLoop(tc <-chan time.Time) { + tm.cleanerRunning.Store(true) defer func() { - atomic.StoreUint32(tm.cleanerRunning, 0) + tm.cleanerRunning.Store(false) }() for { @@ -252,25 +219,20 @@ func (tm *TimedMap) cleanupLoop(tc <-chan time.Time) { } } -// expireElement removes the specified key-value element -// from the map and executes all defined callback functions -func (tm *TimedMap) expireElement(key interface{}, sec int, v *element) { +// expireElement removes the specified key-value Element +// from the map and executes all defined Callback functions +func (tm *TimedMap[TKey, TVal]) expireElement(key TKey, v *Element[TVal]) { for _, cb := range v.cbs { cb(v.value) } - k := keyWrap{ - sec: sec, - key: key, - } - tm.elementPool.Put(v) - delete(tm.container, k) + delete(tm.container, key) } -// cleanUp iterates trhough the map and expires all key-value +// cleanUp iterates through the map and expires all key-value // pairs which expire time after the current time -func (tm *TimedMap) cleanUp() { +func (tm *TimedMap[TKey, TVal]) cleanUp() { now := time.Now() tm.mtx.Lock() @@ -278,16 +240,16 @@ func (tm *TimedMap) cleanUp() { for k, v := range tm.container { if now.After(v.expires) { - tm.expireElement(k.key, k.sec, v) + tm.expireElement(k, v) } } } // set sets the value for a key and section with the // given expiration parameters -func (tm *TimedMap) set(key interface{}, sec int, val interface{}, expiresAfter time.Duration, cb ...callback) { - // re-use element when existent on this key - if v := tm.getRaw(key, sec); v != nil { +func (tm *TimedMap[TKey, TVal]) set(key TKey, val TVal, expiresAfter time.Duration, cb ...Callback[TVal]) { + // re-use Element when existent on this key + if v := tm.getRaw(key); v != nil { tm.mtx.Lock() defer tm.mtx.Unlock() v.value = val @@ -296,25 +258,20 @@ func (tm *TimedMap) set(key interface{}, sec int, val interface{}, expiresAfter return } - k := keyWrap{ - sec: sec, - key: key, - } - tm.mtx.Lock() defer tm.mtx.Unlock() - v := tm.elementPool.Get().(*element) + v := tm.elementPool.Get().(*Element[TVal]) v.value = val v.expires = time.Now().Add(expiresAfter) v.cbs = cb - tm.container[k] = v + tm.container[key] = v } -// get returns an element object by key and section +// get returns an Element object by key and section // if the value has not already expired -func (tm *TimedMap) get(key interface{}, sec int) *element { - v := tm.getRaw(key, sec) +func (tm *TimedMap[TKey, TVal]) get(key TKey) *Element[TVal] { + v := tm.getRaw(key) if v == nil { return nil @@ -324,23 +281,18 @@ func (tm *TimedMap) get(key interface{}, sec int) *element { defer tm.mtx.Unlock() if time.Now().After(v.expires) { - tm.expireElement(key, sec, v) + tm.expireElement(key, v) return nil } return v } -// getRaw returns the raw element object by key, +// getRaw returns the raw Element object by key, // not depending on expiration time -func (tm *TimedMap) getRaw(key interface{}, sec int) *element { - k := keyWrap{ - sec: sec, - key: key, - } - +func (tm *TimedMap[TKey, TVal]) getRaw(key TKey) *Element[TVal] { tm.mtx.RLock() - v, ok := tm.container[k] + v, ok := tm.container[key] tm.mtx.RUnlock() if !ok { @@ -350,30 +302,24 @@ func (tm *TimedMap) getRaw(key interface{}, sec int) *element { return v } -// remove removes an element from the map by giveb -// key and section -func (tm *TimedMap) remove(key interface{}, sec int) { - k := keyWrap{ - sec: sec, - key: key, - } - +// remove removes an Element from the map by give back the key +func (tm *TimedMap[TKey, TVal]) remove(key TKey) { tm.mtx.Lock() defer tm.mtx.Unlock() - v, ok := tm.container[k] + v, ok := tm.container[key] if !ok { return } tm.elementPool.Put(v) - delete(tm.container, k) + delete(tm.container, key) } // refresh extends the lifetime of the given key in the // given section by the duration d. -func (tm *TimedMap) refresh(key interface{}, sec int, d time.Duration) error { - v := tm.get(key, sec) +func (tm *TimedMap[TKey, TVal]) refresh(key TKey, d time.Duration) error { + v := tm.get(key) if v == nil { return ErrKeyNotFound } @@ -385,8 +331,8 @@ func (tm *TimedMap) refresh(key interface{}, sec int, d time.Duration) error { // setExpires sets the lifetime of the given key in the // given section to the duration d. -func (tm *TimedMap) setExpires(key interface{}, sec int, d time.Duration) error { - v := tm.get(key, sec) +func (tm *TimedMap[TKey, TVal]) setExpires(key TKey, d time.Duration) error { + v := tm.get(key) if v == nil { return ErrKeyNotFound } @@ -396,33 +342,30 @@ func (tm *TimedMap) setExpires(key interface{}, sec int, d time.Duration) error return nil } -func (tm *TimedMap) getSnapshot(sec int) (m map[interface{}]interface{}) { - m = make(map[interface{}]interface{}) +func (tm *TimedMap[TKey, TVal]) getSnapshot() (m map[TKey]TVal) { + m = make(map[TKey]TVal) tm.mtx.RLock() defer tm.mtx.RUnlock() for k, v := range tm.container { - if k.sec == sec { - m[k.key] = v.value - } + m[k] = v.value } return } -func newTimedMap( - container map[keyWrap]*element, +func newTimedMap[TKey comparable, TVal any]( + container map[TKey]*Element[TVal], cleanupTickTime time.Duration, tickerChan []<-chan time.Time, -) *TimedMap { - tm := &TimedMap{ +) *TimedMap[TKey, TVal] { + tm := &TimedMap[TKey, TVal]{ container: container, - cleanerRunning: new(uint32), cleanerStopChan: make(chan bool), elementPool: &sync.Pool{ - New: func() interface{} { - return new(element) + New: func() any { + return new(Element[TVal]) }, }, } diff --git a/vendor/modules.txt b/vendor/modules.txt index e40ddaa4f..1b4d63834 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -68,8 +68,6 @@ github.com/spf13/cobra # github.com/spf13/pflag v1.0.5 ## explicit; go 1.12 github.com/spf13/pflag -# github.com/stretchr/objx v0.5.2 -## explicit; go 1.20 # github.com/tidwall/gjson v1.17.1 ## explicit; go 1.12 github.com/tidwall/gjson @@ -88,11 +86,9 @@ github.com/tklauser/numcpus # github.com/yusufpapurcu/wmi v1.2.4 ## explicit; go 1.16 github.com/yusufpapurcu/wmi -# github.com/zekroTJA/timedmap v1.5.2 -## explicit; go 1.13 -github.com/zekroTJA/timedmap # github.com/zekroTJA/timedmap/v2 v2.0.0 ## explicit; go 1.19 +github.com/zekroTJA/timedmap/v2 # golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 ## explicit; go 1.20 golang.org/x/exp/maps