-
Notifications
You must be signed in to change notification settings - Fork 0
/
sw.go
147 lines (130 loc) · 3.57 KB
/
sw.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Package slidingwindow implements an sliding window. But without move back.
// It's like a ringbuffer with the possibility to read an arbitary cell and remove items from the front.
//
// import sw "github.com/djboris9/slidingwindow"
//
// w := sw.Window{}
// w.Create(19, 3)
// // Creates the following:
// // ┌--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┐
// // |00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|
// // |__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|
// // |----------------------CAPACITY OF 19-----------------------|
//
// w.Add(1)
// w.Add(2)
// // ┌--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┐
// // |01|02|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|
// // |__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|
// // |-----|
// // Sliding Window (Size 3, but not enough items in it)
//
// w.Add(3)
// w.Add(4)
// // ┌--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┬--┐
// // |01|02|03|04|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|
// // |__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|
// // |--------|
// // Sliding Window (Size 3)
//
// w.Slice()
// // Returns [2, 3, 4]
package slidingwindow
import (
"errors"
"sync"
)
// Window implements an sliding window
type Window struct {
base []float64
start int
Len int // Len <= Windowsize
Windowsize int
mx *sync.RWMutex
}
// Create initializes the window with a capacity and an window size
func (w *Window) Create(capacity, windowsize int) error {
w.mx = new(sync.RWMutex)
w.base = make([]float64, 0, capacity)
w.Windowsize = windowsize
if windowsize > capacity {
return errors.New("capacity can't be smaller than windowsize")
}
return nil
}
// Add appends an item to the window
func (w *Window) Add(i float64) {
w.mx.Lock()
w.add(i)
w.mx.Unlock()
}
// add is like Add, but without locking
func (w *Window) add(i float64) {
// Move all values to front, if would reach end of base
if w.start+w.Len+1 > cap(w.base) {
for j := 0; j < w.Len-1; j++ {
w.base[j] = w.base[w.start+j+1]
}
w.start = 0
w.Len--
}
// Check capacity and append if needed
if len(w.base) < w.start+w.Len+1 {
w.base = append(w.base, i)
} else {
w.base[w.start+w.Len] = i
}
// If window is "full" => Move one
if w.Len == w.Windowsize {
w.start++
} else {
w.Len++
}
}
// Clear removes all items from the sliding window. Very efficient
func (w *Window) Clear() {
w.mx.Lock()
w.start = 0
w.Len = 0
w.mx.Unlock()
}
// Remove will remove the last item from the window. If the window is empty, nothing happens
func (w *Window) Remove() {
w.mx.Lock()
if w.Len > 0 {
w.Len--
}
if w.Len == 0 {
w.start = 0
}
w.mx.Unlock()
}
// Load loads all data from an slice to the window. The slice can be larger then the window
func (w *Window) Load(x []float64) {
// Nothing to load
if len(x) == 0 {
return
}
w.mx.Lock()
// Move to front if possible (reduces unneccessairy rollovers
if len(x) >= w.Windowsize {
w.start = 0
w.Len = 0
}
loadlen := len(x)
if loadlen >= w.Windowsize {
loadlen = w.Windowsize
}
for _, i := range x[len(x)-loadlen : len(x)] {
w.add(i)
}
w.mx.Unlock()
}
// Slice returns an slice to the window
func (w *Window) Slice() []float64 {
w.mx.RLock()
// 4 Times faster than "defer Unlock"
ret := w.base[w.start : w.start+w.Len]
w.mx.RUnlock()
return ret
}