21
vendor/github.com/benbjohnson/clock/LICENSE
generated
vendored
Normal file
21
vendor/github.com/benbjohnson/clock/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Ben Johnson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
105
vendor/github.com/benbjohnson/clock/README.md
generated
vendored
Normal file
105
vendor/github.com/benbjohnson/clock/README.md
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
clock
|
||||
=====
|
||||
|
||||
[](https://pkg.go.dev/mod/github.com/benbjohnson/clock)
|
||||
|
||||
Clock is a small library for mocking time in Go. It provides an interface
|
||||
around the standard library's [`time`][time] package so that the application
|
||||
can use the realtime clock while tests can use the mock clock.
|
||||
|
||||
The module is currently maintained by @djmitche.
|
||||
|
||||
[time]: https://pkg.go.dev/github.com/benbjohnson/clock
|
||||
|
||||
## Usage
|
||||
|
||||
### Realtime Clock
|
||||
|
||||
Your application can maintain a `Clock` variable that will allow realtime and
|
||||
mock clocks to be interchangeable. For example, if you had an `Application` type:
|
||||
|
||||
```go
|
||||
import "github.com/benbjohnson/clock"
|
||||
|
||||
type Application struct {
|
||||
Clock clock.Clock
|
||||
}
|
||||
```
|
||||
|
||||
You could initialize it to use the realtime clock like this:
|
||||
|
||||
```go
|
||||
var app Application
|
||||
app.Clock = clock.New()
|
||||
...
|
||||
```
|
||||
|
||||
Then all timers and time-related functionality should be performed from the
|
||||
`Clock` variable.
|
||||
|
||||
|
||||
### Mocking time
|
||||
|
||||
In your tests, you will want to use a `Mock` clock:
|
||||
|
||||
```go
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/benbjohnson/clock"
|
||||
)
|
||||
|
||||
func TestApplication_DoSomething(t *testing.T) {
|
||||
mock := clock.NewMock()
|
||||
app := Application{Clock: mock}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Now that you've initialized your application to use the mock clock, you can
|
||||
adjust the time programmatically. The mock clock always starts from the Unix
|
||||
epoch (midnight UTC on Jan 1, 1970).
|
||||
|
||||
|
||||
### Controlling time
|
||||
|
||||
The mock clock provides the same functions that the standard library's `time`
|
||||
package provides. For example, to find the current time, you use the `Now()`
|
||||
function:
|
||||
|
||||
```go
|
||||
mock := clock.NewMock()
|
||||
|
||||
// Find the current time.
|
||||
mock.Now().UTC() // 1970-01-01 00:00:00 +0000 UTC
|
||||
|
||||
// Move the clock forward.
|
||||
mock.Add(2 * time.Hour)
|
||||
|
||||
// Check the time again. It's 2 hours later!
|
||||
mock.Now().UTC() // 1970-01-01 02:00:00 +0000 UTC
|
||||
```
|
||||
|
||||
Timers and Tickers are also controlled by this same mock clock. They will only
|
||||
execute when the clock is moved forward:
|
||||
|
||||
```go
|
||||
mock := clock.NewMock()
|
||||
count := 0
|
||||
|
||||
// Kick off a timer to increment every 1 mock second.
|
||||
go func() {
|
||||
ticker := mock.Ticker(1 * time.Second)
|
||||
for {
|
||||
<-ticker.C
|
||||
count++
|
||||
}
|
||||
}()
|
||||
runtime.Gosched()
|
||||
|
||||
// Move the clock forward 10 seconds.
|
||||
mock.Add(10 * time.Second)
|
||||
|
||||
// This prints 10.
|
||||
fmt.Println(count)
|
||||
```
|
||||
422
vendor/github.com/benbjohnson/clock/clock.go
generated
vendored
Normal file
422
vendor/github.com/benbjohnson/clock/clock.go
generated
vendored
Normal file
@@ -0,0 +1,422 @@
|
||||
package clock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Re-export of time.Duration
|
||||
type Duration = time.Duration
|
||||
|
||||
// Clock represents an interface to the functions in the standard library time
|
||||
// package. Two implementations are available in the clock package. The first
|
||||
// is a real-time clock which simply wraps the time package's functions. The
|
||||
// second is a mock clock which will only change when
|
||||
// programmatically adjusted.
|
||||
type Clock interface {
|
||||
After(d time.Duration) <-chan time.Time
|
||||
AfterFunc(d time.Duration, f func()) *Timer
|
||||
Now() time.Time
|
||||
Since(t time.Time) time.Duration
|
||||
Until(t time.Time) time.Duration
|
||||
Sleep(d time.Duration)
|
||||
Tick(d time.Duration) <-chan time.Time
|
||||
Ticker(d time.Duration) *Ticker
|
||||
Timer(d time.Duration) *Timer
|
||||
WithDeadline(parent context.Context, d time.Time) (context.Context, context.CancelFunc)
|
||||
WithTimeout(parent context.Context, t time.Duration) (context.Context, context.CancelFunc)
|
||||
}
|
||||
|
||||
// New returns an instance of a real-time clock.
|
||||
func New() Clock {
|
||||
return &clock{}
|
||||
}
|
||||
|
||||
// clock implements a real-time clock by simply wrapping the time package functions.
|
||||
type clock struct{}
|
||||
|
||||
func (c *clock) After(d time.Duration) <-chan time.Time { return time.After(d) }
|
||||
|
||||
func (c *clock) AfterFunc(d time.Duration, f func()) *Timer {
|
||||
return &Timer{timer: time.AfterFunc(d, f)}
|
||||
}
|
||||
|
||||
func (c *clock) Now() time.Time { return time.Now() }
|
||||
|
||||
func (c *clock) Since(t time.Time) time.Duration { return time.Since(t) }
|
||||
|
||||
func (c *clock) Until(t time.Time) time.Duration { return time.Until(t) }
|
||||
|
||||
func (c *clock) Sleep(d time.Duration) { time.Sleep(d) }
|
||||
|
||||
func (c *clock) Tick(d time.Duration) <-chan time.Time { return time.Tick(d) }
|
||||
|
||||
func (c *clock) Ticker(d time.Duration) *Ticker {
|
||||
t := time.NewTicker(d)
|
||||
return &Ticker{C: t.C, ticker: t}
|
||||
}
|
||||
|
||||
func (c *clock) Timer(d time.Duration) *Timer {
|
||||
t := time.NewTimer(d)
|
||||
return &Timer{C: t.C, timer: t}
|
||||
}
|
||||
|
||||
func (c *clock) WithDeadline(parent context.Context, d time.Time) (context.Context, context.CancelFunc) {
|
||||
return context.WithDeadline(parent, d)
|
||||
}
|
||||
|
||||
func (c *clock) WithTimeout(parent context.Context, t time.Duration) (context.Context, context.CancelFunc) {
|
||||
return context.WithTimeout(parent, t)
|
||||
}
|
||||
|
||||
// Mock represents a mock clock that only moves forward programmically.
|
||||
// It can be preferable to a real-time clock when testing time-based functionality.
|
||||
type Mock struct {
|
||||
// mu protects all other fields in this struct, and the data that they
|
||||
// point to.
|
||||
mu sync.Mutex
|
||||
|
||||
now time.Time // current time
|
||||
timers clockTimers // tickers & timers
|
||||
}
|
||||
|
||||
// NewMock returns an instance of a mock clock.
|
||||
// The current time of the mock clock on initialization is the Unix epoch.
|
||||
func NewMock() *Mock {
|
||||
return &Mock{now: time.Unix(0, 0)}
|
||||
}
|
||||
|
||||
// Add moves the current time of the mock clock forward by the specified duration.
|
||||
// This should only be called from a single goroutine at a time.
|
||||
func (m *Mock) Add(d time.Duration) {
|
||||
// Calculate the final current time.
|
||||
m.mu.Lock()
|
||||
t := m.now.Add(d)
|
||||
m.mu.Unlock()
|
||||
|
||||
// Continue to execute timers until there are no more before the new time.
|
||||
for {
|
||||
if !m.runNextTimer(t) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that we end with the new time.
|
||||
m.mu.Lock()
|
||||
m.now = t
|
||||
m.mu.Unlock()
|
||||
|
||||
// Give a small buffer to make sure that other goroutines get handled.
|
||||
gosched()
|
||||
}
|
||||
|
||||
// Set sets the current time of the mock clock to a specific one.
|
||||
// This should only be called from a single goroutine at a time.
|
||||
func (m *Mock) Set(t time.Time) {
|
||||
// Continue to execute timers until there are no more before the new time.
|
||||
for {
|
||||
if !m.runNextTimer(t) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that we end with the new time.
|
||||
m.mu.Lock()
|
||||
m.now = t
|
||||
m.mu.Unlock()
|
||||
|
||||
// Give a small buffer to make sure that other goroutines get handled.
|
||||
gosched()
|
||||
}
|
||||
|
||||
// WaitForAllTimers sets the clock until all timers are expired
|
||||
func (m *Mock) WaitForAllTimers() time.Time {
|
||||
// Continue to execute timers until there are no more
|
||||
for {
|
||||
m.mu.Lock()
|
||||
if len(m.timers) == 0 {
|
||||
m.mu.Unlock()
|
||||
return m.Now()
|
||||
}
|
||||
|
||||
sort.Sort(m.timers)
|
||||
next := m.timers[len(m.timers)-1].Next()
|
||||
m.mu.Unlock()
|
||||
m.Set(next)
|
||||
}
|
||||
}
|
||||
|
||||
// runNextTimer executes the next timer in chronological order and moves the
|
||||
// current time to the timer's next tick time. The next time is not executed if
|
||||
// its next time is after the max time. Returns true if a timer was executed.
|
||||
func (m *Mock) runNextTimer(max time.Time) bool {
|
||||
m.mu.Lock()
|
||||
|
||||
// Sort timers by time.
|
||||
sort.Sort(m.timers)
|
||||
|
||||
// If we have no more timers then exit.
|
||||
if len(m.timers) == 0 {
|
||||
m.mu.Unlock()
|
||||
return false
|
||||
}
|
||||
|
||||
// Retrieve next timer. Exit if next tick is after new time.
|
||||
t := m.timers[0]
|
||||
if t.Next().After(max) {
|
||||
m.mu.Unlock()
|
||||
return false
|
||||
}
|
||||
|
||||
// Move "now" forward and unlock clock.
|
||||
m.now = t.Next()
|
||||
now := m.now
|
||||
m.mu.Unlock()
|
||||
|
||||
// Execute timer.
|
||||
t.Tick(now)
|
||||
return true
|
||||
}
|
||||
|
||||
// After waits for the duration to elapse and then sends the current time on the returned channel.
|
||||
func (m *Mock) After(d time.Duration) <-chan time.Time {
|
||||
return m.Timer(d).C
|
||||
}
|
||||
|
||||
// AfterFunc waits for the duration to elapse and then executes a function in its own goroutine.
|
||||
// A Timer is returned that can be stopped.
|
||||
func (m *Mock) AfterFunc(d time.Duration, f func()) *Timer {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
ch := make(chan time.Time, 1)
|
||||
t := &Timer{
|
||||
c: ch,
|
||||
fn: f,
|
||||
mock: m,
|
||||
next: m.now.Add(d),
|
||||
stopped: false,
|
||||
}
|
||||
m.timers = append(m.timers, (*internalTimer)(t))
|
||||
return t
|
||||
}
|
||||
|
||||
// Now returns the current wall time on the mock clock.
|
||||
func (m *Mock) Now() time.Time {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
return m.now
|
||||
}
|
||||
|
||||
// Since returns time since `t` using the mock clock's wall time.
|
||||
func (m *Mock) Since(t time.Time) time.Duration {
|
||||
return m.Now().Sub(t)
|
||||
}
|
||||
|
||||
// Until returns time until `t` using the mock clock's wall time.
|
||||
func (m *Mock) Until(t time.Time) time.Duration {
|
||||
return t.Sub(m.Now())
|
||||
}
|
||||
|
||||
// Sleep pauses the goroutine for the given duration on the mock clock.
|
||||
// The clock must be moved forward in a separate goroutine.
|
||||
func (m *Mock) Sleep(d time.Duration) {
|
||||
<-m.After(d)
|
||||
}
|
||||
|
||||
// Tick is a convenience function for Ticker().
|
||||
// It will return a ticker channel that cannot be stopped.
|
||||
func (m *Mock) Tick(d time.Duration) <-chan time.Time {
|
||||
return m.Ticker(d).C
|
||||
}
|
||||
|
||||
// Ticker creates a new instance of Ticker.
|
||||
func (m *Mock) Ticker(d time.Duration) *Ticker {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
ch := make(chan time.Time, 1)
|
||||
t := &Ticker{
|
||||
C: ch,
|
||||
c: ch,
|
||||
mock: m,
|
||||
d: d,
|
||||
next: m.now.Add(d),
|
||||
}
|
||||
m.timers = append(m.timers, (*internalTicker)(t))
|
||||
return t
|
||||
}
|
||||
|
||||
// Timer creates a new instance of Timer.
|
||||
func (m *Mock) Timer(d time.Duration) *Timer {
|
||||
m.mu.Lock()
|
||||
ch := make(chan time.Time, 1)
|
||||
t := &Timer{
|
||||
C: ch,
|
||||
c: ch,
|
||||
mock: m,
|
||||
next: m.now.Add(d),
|
||||
stopped: false,
|
||||
}
|
||||
m.timers = append(m.timers, (*internalTimer)(t))
|
||||
now := m.now
|
||||
m.mu.Unlock()
|
||||
m.runNextTimer(now)
|
||||
return t
|
||||
}
|
||||
|
||||
// removeClockTimer removes a timer from m.timers. m.mu MUST be held
|
||||
// when this method is called.
|
||||
func (m *Mock) removeClockTimer(t clockTimer) {
|
||||
for i, timer := range m.timers {
|
||||
if timer == t {
|
||||
copy(m.timers[i:], m.timers[i+1:])
|
||||
m.timers[len(m.timers)-1] = nil
|
||||
m.timers = m.timers[:len(m.timers)-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
sort.Sort(m.timers)
|
||||
}
|
||||
|
||||
// clockTimer represents an object with an associated start time.
|
||||
type clockTimer interface {
|
||||
Next() time.Time
|
||||
Tick(time.Time)
|
||||
}
|
||||
|
||||
// clockTimers represents a list of sortable timers.
|
||||
type clockTimers []clockTimer
|
||||
|
||||
func (a clockTimers) Len() int { return len(a) }
|
||||
func (a clockTimers) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a clockTimers) Less(i, j int) bool { return a[i].Next().Before(a[j].Next()) }
|
||||
|
||||
// Timer represents a single event.
|
||||
// The current time will be sent on C, unless the timer was created by AfterFunc.
|
||||
type Timer struct {
|
||||
C <-chan time.Time
|
||||
c chan time.Time
|
||||
timer *time.Timer // realtime impl, if set
|
||||
next time.Time // next tick time
|
||||
mock *Mock // mock clock, if set
|
||||
fn func() // AfterFunc function, if set
|
||||
stopped bool // True if stopped, false if running
|
||||
}
|
||||
|
||||
// Stop turns off the ticker.
|
||||
func (t *Timer) Stop() bool {
|
||||
if t.timer != nil {
|
||||
return t.timer.Stop()
|
||||
}
|
||||
|
||||
t.mock.mu.Lock()
|
||||
registered := !t.stopped
|
||||
t.mock.removeClockTimer((*internalTimer)(t))
|
||||
t.stopped = true
|
||||
t.mock.mu.Unlock()
|
||||
return registered
|
||||
}
|
||||
|
||||
// Reset changes the expiry time of the timer
|
||||
func (t *Timer) Reset(d time.Duration) bool {
|
||||
if t.timer != nil {
|
||||
return t.timer.Reset(d)
|
||||
}
|
||||
|
||||
t.mock.mu.Lock()
|
||||
t.next = t.mock.now.Add(d)
|
||||
defer t.mock.mu.Unlock()
|
||||
|
||||
registered := !t.stopped
|
||||
if t.stopped {
|
||||
t.mock.timers = append(t.mock.timers, (*internalTimer)(t))
|
||||
}
|
||||
|
||||
t.stopped = false
|
||||
return registered
|
||||
}
|
||||
|
||||
type internalTimer Timer
|
||||
|
||||
func (t *internalTimer) Next() time.Time { return t.next }
|
||||
func (t *internalTimer) Tick(now time.Time) {
|
||||
// a gosched() after ticking, to allow any consequences of the
|
||||
// tick to complete
|
||||
defer gosched()
|
||||
|
||||
t.mock.mu.Lock()
|
||||
if t.fn != nil {
|
||||
// defer function execution until the lock is released, and
|
||||
defer func() { go t.fn() }()
|
||||
} else {
|
||||
t.c <- now
|
||||
}
|
||||
t.mock.removeClockTimer((*internalTimer)(t))
|
||||
t.stopped = true
|
||||
t.mock.mu.Unlock()
|
||||
}
|
||||
|
||||
// Ticker holds a channel that receives "ticks" at regular intervals.
|
||||
type Ticker struct {
|
||||
C <-chan time.Time
|
||||
c chan time.Time
|
||||
ticker *time.Ticker // realtime impl, if set
|
||||
next time.Time // next tick time
|
||||
mock *Mock // mock clock, if set
|
||||
d time.Duration // time between ticks
|
||||
stopped bool // True if stopped, false if running
|
||||
}
|
||||
|
||||
// Stop turns off the ticker.
|
||||
func (t *Ticker) Stop() {
|
||||
if t.ticker != nil {
|
||||
t.ticker.Stop()
|
||||
} else {
|
||||
t.mock.mu.Lock()
|
||||
t.mock.removeClockTimer((*internalTicker)(t))
|
||||
t.stopped = true
|
||||
t.mock.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// Reset resets the ticker to a new duration.
|
||||
func (t *Ticker) Reset(dur time.Duration) {
|
||||
if t.ticker != nil {
|
||||
t.ticker.Reset(dur)
|
||||
return
|
||||
}
|
||||
|
||||
t.mock.mu.Lock()
|
||||
defer t.mock.mu.Unlock()
|
||||
|
||||
if t.stopped {
|
||||
t.mock.timers = append(t.mock.timers, (*internalTicker)(t))
|
||||
t.stopped = false
|
||||
}
|
||||
|
||||
t.d = dur
|
||||
t.next = t.mock.now.Add(dur)
|
||||
}
|
||||
|
||||
type internalTicker Ticker
|
||||
|
||||
func (t *internalTicker) Next() time.Time { return t.next }
|
||||
func (t *internalTicker) Tick(now time.Time) {
|
||||
select {
|
||||
case t.c <- now:
|
||||
default:
|
||||
}
|
||||
t.mock.mu.Lock()
|
||||
t.next = now.Add(t.d)
|
||||
t.mock.mu.Unlock()
|
||||
gosched()
|
||||
}
|
||||
|
||||
// Sleep momentarily so that other goroutines can process.
|
||||
func gosched() { time.Sleep(1 * time.Millisecond) }
|
||||
|
||||
var (
|
||||
// type checking
|
||||
_ Clock = &Mock{}
|
||||
)
|
||||
86
vendor/github.com/benbjohnson/clock/context.go
generated
vendored
Normal file
86
vendor/github.com/benbjohnson/clock/context.go
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
package clock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (m *Mock) WithTimeout(parent context.Context, timeout time.Duration) (context.Context, context.CancelFunc) {
|
||||
return m.WithDeadline(parent, m.Now().Add(timeout))
|
||||
}
|
||||
|
||||
func (m *Mock) WithDeadline(parent context.Context, deadline time.Time) (context.Context, context.CancelFunc) {
|
||||
if cur, ok := parent.Deadline(); ok && cur.Before(deadline) {
|
||||
// The current deadline is already sooner than the new one.
|
||||
return context.WithCancel(parent)
|
||||
}
|
||||
ctx := &timerCtx{clock: m, parent: parent, deadline: deadline, done: make(chan struct{})}
|
||||
propagateCancel(parent, ctx)
|
||||
dur := m.Until(deadline)
|
||||
if dur <= 0 {
|
||||
ctx.cancel(context.DeadlineExceeded) // deadline has already passed
|
||||
return ctx, func() {}
|
||||
}
|
||||
ctx.Lock()
|
||||
defer ctx.Unlock()
|
||||
if ctx.err == nil {
|
||||
ctx.timer = m.AfterFunc(dur, func() {
|
||||
ctx.cancel(context.DeadlineExceeded)
|
||||
})
|
||||
}
|
||||
return ctx, func() { ctx.cancel(context.Canceled) }
|
||||
}
|
||||
|
||||
// propagateCancel arranges for child to be canceled when parent is.
|
||||
func propagateCancel(parent context.Context, child *timerCtx) {
|
||||
if parent.Done() == nil {
|
||||
return // parent is never canceled
|
||||
}
|
||||
go func() {
|
||||
select {
|
||||
case <-parent.Done():
|
||||
child.cancel(parent.Err())
|
||||
case <-child.Done():
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
type timerCtx struct {
|
||||
sync.Mutex
|
||||
|
||||
clock Clock
|
||||
parent context.Context
|
||||
deadline time.Time
|
||||
done chan struct{}
|
||||
|
||||
err error
|
||||
timer *Timer
|
||||
}
|
||||
|
||||
func (c *timerCtx) cancel(err error) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if c.err != nil {
|
||||
return // already canceled
|
||||
}
|
||||
c.err = err
|
||||
close(c.done)
|
||||
if c.timer != nil {
|
||||
c.timer.Stop()
|
||||
c.timer = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *timerCtx) Deadline() (deadline time.Time, ok bool) { return c.deadline, true }
|
||||
|
||||
func (c *timerCtx) Done() <-chan struct{} { return c.done }
|
||||
|
||||
func (c *timerCtx) Err() error { return c.err }
|
||||
|
||||
func (c *timerCtx) Value(key interface{}) interface{} { return c.parent.Value(key) }
|
||||
|
||||
func (c *timerCtx) String() string {
|
||||
return fmt.Sprintf("clock.WithDeadline(%s [%s])", c.deadline, c.deadline.Sub(c.clock.Now()))
|
||||
}
|
||||
19
vendor/github.com/benbjohnson/immutable/LICENSE
generated
vendored
Normal file
19
vendor/github.com/benbjohnson/immutable/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright 2019 Ben Johnson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
301
vendor/github.com/benbjohnson/immutable/README.md
generated
vendored
Normal file
301
vendor/github.com/benbjohnson/immutable/README.md
generated
vendored
Normal file
@@ -0,0 +1,301 @@
|
||||
Immutable    
|
||||
=========
|
||||
|
||||
This repository contains immutable collection types for Go. It includes
|
||||
`List`, `Map`, and `SortedMap` implementations. Immutable collections can
|
||||
provide efficient, lock free sharing of data by requiring that edits to the
|
||||
collections return new collections.
|
||||
|
||||
The collection types in this library are meant to mimic Go built-in collections
|
||||
such as`slice` and `map`. The primary usage difference between Go collections
|
||||
and `immutable` collections is that `immutable` collections always return a new
|
||||
collection on mutation so you will need to save the new reference.
|
||||
|
||||
Immutable collections are not for every situation, however, as they can incur
|
||||
additional CPU and memory overhead. Please evaluate the cost/benefit for your
|
||||
particular project.
|
||||
|
||||
Special thanks to the [Immutable.js](https://immutable-js.github.io/immutable-js/)
|
||||
team as the `List` & `Map` implementations are loose ports from that project.
|
||||
|
||||
|
||||
## List
|
||||
|
||||
The `List` type represents a sorted, indexed collection of values and operates
|
||||
similarly to a Go slice. It supports efficient append, prepend, update, and
|
||||
slice operations.
|
||||
|
||||
|
||||
### Adding list elements
|
||||
|
||||
Elements can be added to the end of the list with the `Append()` method or added
|
||||
to the beginning of the list with the `Prepend()` method. Unlike Go slices,
|
||||
prepending is as efficient as appending.
|
||||
|
||||
```go
|
||||
// Create a list with 3 elements.
|
||||
l := immutable.NewList()
|
||||
l = l.Append("foo")
|
||||
l = l.Append("bar")
|
||||
l = l.Prepend("baz")
|
||||
|
||||
fmt.Println(l.Len()) // 3
|
||||
fmt.Println(l.Get(0)) // "baz"
|
||||
fmt.Println(l.Get(1)) // "foo"
|
||||
fmt.Println(l.Get(2)) // "bar"
|
||||
```
|
||||
|
||||
Note that each change to the list results in a new list being created. These
|
||||
lists are all snapshots at that point in time and cannot be changed so they
|
||||
are safe to share between multiple goroutines.
|
||||
|
||||
### Updating list elements
|
||||
|
||||
You can also overwrite existing elements by using the `Set()` method. In the
|
||||
following example, we'll update the third element in our list and return the
|
||||
new list to a new variable. You can see that our old `l` variable retains a
|
||||
snapshot of the original value.
|
||||
|
||||
```go
|
||||
l := immutable.NewList()
|
||||
l = l.Append("foo")
|
||||
l = l.Append("bar")
|
||||
newList := l.Set(2, "baz")
|
||||
|
||||
fmt.Println(l.Get(1)) // "bar"
|
||||
fmt.Println(newList.Get(1)) // "baz"
|
||||
```
|
||||
|
||||
### Deriving sublists
|
||||
|
||||
You can create a sublist by using the `Slice()` method. This method works with
|
||||
the same rules as subslicing a Go slice:
|
||||
|
||||
```go
|
||||
l = l.Slice(0, 2)
|
||||
|
||||
fmt.Println(l.Len()) // 2
|
||||
fmt.Println(l.Get(0)) // "baz"
|
||||
fmt.Println(l.Get(1)) // "foo"
|
||||
```
|
||||
|
||||
Please note that since `List` follows the same rules as slices, it will panic if
|
||||
you try to `Get()`, `Set()`, or `Slice()` with indexes that are outside of
|
||||
the range of the `List`.
|
||||
|
||||
|
||||
|
||||
### Iterating lists
|
||||
|
||||
Iterators provide a clean, simple way to iterate over the elements of the list
|
||||
in order. This is more efficient than simply calling `Get()` for each index.
|
||||
|
||||
Below is an example of iterating over all elements of our list from above:
|
||||
|
||||
```go
|
||||
itr := l.Iterator()
|
||||
for !itr.Done() {
|
||||
index, value := itr.Next()
|
||||
fmt.Printf("Index %d equals %v\n", index, value)
|
||||
}
|
||||
|
||||
// Index 0 equals baz
|
||||
// Index 1 equals foo
|
||||
```
|
||||
|
||||
By default iterators start from index zero, however, the `Seek()` method can be
|
||||
used to jump to a given index.
|
||||
|
||||
|
||||
### Efficiently building lists
|
||||
|
||||
If you are building large lists, it is significantly more efficient to use the
|
||||
`ListBuilder`. It uses nearly the same API as `List` except that it updates
|
||||
a list in-place until you are ready to use it. This can improve bulk list
|
||||
building by 10x or more.
|
||||
|
||||
```go
|
||||
b := immutable.NewListBuilder()
|
||||
b.Append("foo")
|
||||
b.Append("bar")
|
||||
b.Set(2, "baz")
|
||||
|
||||
l := b.List()
|
||||
fmt.Println(l.Get(0)) // "foo"
|
||||
fmt.Println(l.Get(1)) // "baz"
|
||||
```
|
||||
|
||||
Builders are invalid after the call to `List()`.
|
||||
|
||||
|
||||
## Map
|
||||
|
||||
The `Map` represents an associative array that maps unique keys to values. It
|
||||
is implemented to act similarly to the built-in Go `map` type. It is implemented
|
||||
as a [Hash-Array Mapped Trie](https://lampwww.epfl.ch/papers/idealhashtrees.pdf).
|
||||
|
||||
Maps require a `Hasher` to hash keys and check for equality. There are built-in
|
||||
hasher implementations for most primitive types such as `int`, `uint`, `string`,
|
||||
and `[]byte` keys. You may pass in a `nil` hasher to `NewMap()` if you are using
|
||||
one of these key types.
|
||||
|
||||
|
||||
### Setting map key/value pairs
|
||||
|
||||
You can add a key/value pair to the map by using the `Set()` method. It will
|
||||
add the key if it does not exist or it will overwrite the value for the key if
|
||||
it does exist.
|
||||
|
||||
Values may be fetched for a key using the `Get()` method. This method returns
|
||||
the value as well as a flag indicating if the key existed. The flag is useful
|
||||
to check if a `nil` value was set for a key versus a key did not exist.
|
||||
|
||||
```go
|
||||
m := immutable.NewMap(nil)
|
||||
m = m.Set("jane", 100)
|
||||
m = m.Set("susy", 200)
|
||||
m = m.Set("jane", 300) // overwrite
|
||||
|
||||
fmt.Println(m.Len()) // 2
|
||||
|
||||
v, ok := m.Get("jane")
|
||||
fmt.Println(v, ok) // 300 true
|
||||
|
||||
v, ok = m.Get("susy")
|
||||
fmt.Println(v, ok) // 200, true
|
||||
|
||||
v, ok = m.Get("john")
|
||||
fmt.Println(v, ok) // nil, false
|
||||
```
|
||||
|
||||
|
||||
### Removing map keys
|
||||
|
||||
Keys may be removed from the map by using the `Delete()` method. If the key does
|
||||
not exist then the original map is returned instead of a new one.
|
||||
|
||||
```go
|
||||
m := immutable.NewMap(nil)
|
||||
m = m.Set("jane", 100)
|
||||
m = m.Delete("jane")
|
||||
|
||||
fmt.Println(m.Len()) // 0
|
||||
|
||||
v, ok := m.Get("jane")
|
||||
fmt.Println(v, ok) // nil false
|
||||
```
|
||||
|
||||
|
||||
### Iterating maps
|
||||
|
||||
Maps are unsorted, however, iterators can be used to loop over all key/value
|
||||
pairs in the collection. Unlike Go maps, iterators are deterministic when
|
||||
iterating over key/value pairs.
|
||||
|
||||
```go
|
||||
m := immutable.NewMap(nil)
|
||||
m = m.Set("jane", 100)
|
||||
m = m.Set("susy", 200)
|
||||
|
||||
itr := m.Iterator()
|
||||
for !itr.Done() {
|
||||
k, v := itr.Next()
|
||||
fmt.Println(k, v)
|
||||
}
|
||||
|
||||
// susy 200
|
||||
// jane 100
|
||||
```
|
||||
|
||||
Note that you should not rely on two maps with the same key/value pairs to
|
||||
iterate in the same order. Ordering can be insertion order dependent when two
|
||||
keys generate the same hash.
|
||||
|
||||
|
||||
### Efficiently building maps
|
||||
|
||||
If you are executing multiple mutations on a map, it can be much more efficient
|
||||
to use the `MapBuilder`. It uses nearly the same API as `Map` except that it
|
||||
updates a map in-place until you are ready to use it.
|
||||
|
||||
```go
|
||||
b := immutable.NewMapBuilder(immutable.NewMap(nil))
|
||||
b.Set("foo", 100)
|
||||
b.Set("bar", 200)
|
||||
b.Set("foo", 300)
|
||||
|
||||
m := b.Map()
|
||||
fmt.Println(m.Get("foo")) // "300"
|
||||
fmt.Println(m.Get("bar")) // "200"
|
||||
```
|
||||
|
||||
Builders are invalid after the call to `Map()`.
|
||||
|
||||
|
||||
### Implementing a custom Hasher
|
||||
|
||||
If you need to use a key type besides `int`, `uint`, `string`, or `[]byte` then
|
||||
you'll need to create a custom `Hasher` implementation and pass it to `NewMap()`
|
||||
on creation.
|
||||
|
||||
Hashers are fairly simple. They only need to generate hashes for a given key
|
||||
and check equality given two keys.
|
||||
|
||||
```go
|
||||
type Hasher interface {
|
||||
Hash(key interface{}) uint32
|
||||
Equal(a, b interface{}) bool
|
||||
}
|
||||
```
|
||||
|
||||
Please see the internal `intHasher`, `uintHasher`, `stringHasher`, and
|
||||
`byteSliceHasher` for examples.
|
||||
|
||||
|
||||
## Sorted Map
|
||||
|
||||
The `SortedMap` represents an associative array that maps unique keys to values.
|
||||
Unlike the `Map`, however, keys can be iterated over in-order. It is implemented
|
||||
as a B+tree.
|
||||
|
||||
Sorted maps require a `Comparer` to sort keys and check for equality. There are
|
||||
built-in comparer implementations for `int`, `uint`, `string`, and `[]byte` keys.
|
||||
You may pass a `nil` comparer to `NewSortedMap()` if you are using one of these
|
||||
key types.
|
||||
|
||||
The API is identical to the `Map` implementation. The sorted map also has a
|
||||
companion `SortedMapBuilder` for more efficiently building maps.
|
||||
|
||||
|
||||
### Implementing a custom Comparer
|
||||
|
||||
If you need to use a key type besides `int`, `uint`, `string`, or `[]byte`
|
||||
then you'll need to create a custom `Comparer` implementation and pass it to
|
||||
`NewSortedMap()` on creation.
|
||||
|
||||
Comparers on have one method—`Compare()`. It works the same as the
|
||||
`strings.Compare()` function. It returns `-1` if `a` is less than `b`, returns
|
||||
`1` if a is greater than `b`, and returns `0` if `a` is equal to `b`.
|
||||
|
||||
```go
|
||||
type Comparer interface {
|
||||
Compare(a, b interface{}) int
|
||||
}
|
||||
```
|
||||
|
||||
Please see the internal `intComparer`, `uintComparer`, `stringComparer`, and
|
||||
`byteSliceComparer` for examples.
|
||||
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
The goal of `immutable` is to provide stable, reasonably performant, immutable
|
||||
collections library for Go that has a simple, idiomatic API. As such, additional
|
||||
features and minor performance improvements will generally not be accepted. If
|
||||
you have a suggestion for a clearer API or substantial performance improvement,
|
||||
_please_ open an issue first to discuss. All pull requests without a related
|
||||
issue will be closed immediately.
|
||||
|
||||
Please submit issues relating to bugs & documentation improvements.
|
||||
|
||||
2734
vendor/github.com/benbjohnson/immutable/immutable.go
generated
vendored
Normal file
2734
vendor/github.com/benbjohnson/immutable/immutable.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user