Skip to content

Commit

Permalink
Merge pull request #491 from essentialkaos/develop
Browse files Browse the repository at this point in the history
Version 13.3.0
  • Loading branch information
andyone authored Jul 30, 2024
2 parents ca3e455 + c26c58e commit 2b5b082
Show file tree
Hide file tree
Showing 45 changed files with 1,210 additions and 292 deletions.
5 changes: 3 additions & 2 deletions .scripts/packages.list
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
* + ansi
* + cache
* + cache/fs
* + cache/memory
* + color
* + cron
* + csv
* + directio
* + easing
* + emoji
* + env
* + errutil
* + events
* + directio
* + fmtc
* + fmtutil
* + fmtutil/barcode
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
## Changelog

### [13.3.0](https://kaos.sh/ek/13.3.0)

- `[cache/fs]` Added cache with file system storage
- `[cache]` In-memory cache moved to `cache/memory`
- `[sliceutil]` Added method `Join`

### [13.2.1](https://kaos.sh/ek/13.2.1)

- `[terminal/input]` Added `NewLine` flag
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ Currently we support Linux and macOS (_except some packages_). All packages have
### Sub-packages

- [`ansi`](https://kaos.sh/g/ek.v13/ansi) — Package provides methods for working with ANSI/VT100 control sequences
- [`cache`](https://kaos.sh/g/ek.v13/cache) — Package provides a simple in-memory key/value cache
- [`cache`](https://kaos.sh/g/ek.v13/cache/fs) — Package provides a cache with file system storage
- [`cache`](https://kaos.sh/g/ek.v13/cache/memory) — Package provides a cache with memory storage
- [`color`](https://kaos.sh/g/ek.v13/color) — Package provides methods for working with colors
- [`cron`](https://kaos.sh/g/ek.v13/cron) — Package provides methods for working with cron expressions
- [`csv`](https://kaos.sh/g/ek.v13/csv) — Package with simple CSV parser compatible with default Go parser
Expand Down
240 changes: 22 additions & 218 deletions cache/cache.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Package cache provides a simple in-memory key:value cache
// Package cache provides methods and structs for caching data
package cache

// ////////////////////////////////////////////////////////////////////////////////// //
Expand All @@ -8,232 +8,36 @@ package cache
// //
// ////////////////////////////////////////////////////////////////////////////////// //

import (
"sync"
"time"
)
import "time"

// ////////////////////////////////////////////////////////////////////////////////// //

// Cache is cache instance
type Cache struct {
expiration time.Duration
data map[string]any
expiry map[string]int64
mu *sync.RWMutex
isJanitorWorks bool
}

// ////////////////////////////////////////////////////////////////////////////////// //

// New creates new cache instance
func New(defaultExpiration, cleanupInterval time.Duration) *Cache {
s := &Cache{
expiration: defaultExpiration,
data: make(map[string]any),
expiry: make(map[string]int64),
mu: &sync.RWMutex{},
}

if cleanupInterval != 0 {
s.isJanitorWorks = true
go s.janitor(cleanupInterval)
}

return s
}

// ////////////////////////////////////////////////////////////////////////////////// //

// Has returns true if cache contains data for given key
func (s *Cache) Has(key string) bool {
if s == nil {
return false
}

s.mu.RLock()

expiration, ok := s.expiry[key]

if !ok {
s.mu.RUnlock()
return false
}

if time.Now().UnixNano() > expiration {
s.mu.RUnlock()

if !s.isJanitorWorks {
s.Delete(key)
}

return false
}

s.mu.RUnlock()

return ok
}

// Size returns number of items in cache
func (s *Cache) Size() int {
if s == nil {
return 0
}

s.mu.RLock()
defer s.mu.RUnlock()

return len(s.data)
}

// Expired returns number of expired items in cache
func (s *Cache) Expired() int {
if s == nil {
return 0
}

items := 0
now := time.Now().UnixNano()

s.mu.Lock()

for _, expiration := range s.expiry {
if now > expiration {
items++
}
}

s.mu.Unlock()

return items
}

// Set adds or updates item in cache
func (s *Cache) Set(key string, data any) {
if s == nil {
return
}

s.mu.Lock()

s.expiry[key] = time.Now().Add(s.expiration).UnixNano()
s.data[key] = data
// Cache is cache backend interface
type Cache interface {
// Has returns true if cache contains data for given key
Has(key string) bool

s.mu.Unlock()
}

// Get returns item from cache or nil
func (s *Cache) Get(key string) any {
if s == nil {
return nil
}

s.mu.RLock()

expiration, ok := s.expiry[key]

if !ok {
s.mu.RUnlock()
return nil
}

if time.Now().UnixNano() > expiration {
s.mu.RUnlock()

if !s.isJanitorWorks {
s.Delete(key)
}

return nil
}

item := s.data[key]

s.mu.RUnlock()

return item
}

// GetWithExpiration returns item from cache and expiration date or nil
func (s *Cache) GetWithExpiration(key string) (any, time.Time) {
if s == nil {
return nil, time.Time{}
}

s.mu.RLock()

expiration, ok := s.expiry[key]

if !ok {
s.mu.RUnlock()
return nil, time.Time{}
}

if time.Now().UnixNano() > expiration {
s.mu.RUnlock()
// Size returns number of items in cache
Size() int

if !s.isJanitorWorks {
s.Delete(key)
}
// Expired returns number of expired items in cache
Expired() int

return nil, time.Time{}
}

item := s.data[key]

s.mu.RUnlock()

return item, time.Unix(0, expiration)
}

// Delete removes item from cache
func (s *Cache) Delete(key string) {
if s == nil {
return
}

s.mu.Lock()

delete(s.data, key)
delete(s.expiry, key)

s.mu.Unlock()
}

// Flush removes all data from cache
func (s *Cache) Flush() {
if s == nil {
return
}

s.mu.Lock()

s.data = make(map[string]any)
s.expiry = make(map[string]int64)

s.mu.Unlock()
}

// ////////////////////////////////////////////////////////////////////////////////// //
// Set adds or updates item in cache
Set(key string, data any, expiration ...time.Duration) bool

func (s *Cache) janitor(interval time.Duration) {
for range time.NewTicker(interval).C {
if len(s.data) == 0 {
continue
}
// GetWithExpiration returns item from cache
Get(key string) any

now := time.Now().UnixNano()
// GetWithExpiration returns item expiration date
GetExpiration(key string) time.Time

s.mu.Lock()
// GetWithExpiration returns item from cache and expiration date or nil
GetWithExpiration(key string) (any, time.Time)

for key, expiration := range s.expiry {
if now > expiration {
delete(s.data, key)
delete(s.expiry, key)
}
}
// Delete removes item from cache
Delete(key string) bool

s.mu.Unlock()
}
// Flush removes all data from cache
Flush() bool
}
Loading

0 comments on commit 2b5b082

Please sign in to comment.