Files
2026-05-19 14:14:22 +02:00

89 lines
1.8 KiB
Go

package main
import "sync"
// sigRing is a fixed-capacity circular buffer storing (time, value) pairs.
// Writes come from the Hub.Run() goroutine; reads come from HTTP handler goroutines.
// The embedded RWMutex protects concurrent access.
type sigRing struct {
mu sync.RWMutex
t, v []float64
cap int
head, size int // next write position; current fill
}
func newSigRing(capacity int) *sigRing {
return &sigRing{
t: make([]float64, capacity),
v: make([]float64, capacity),
cap: capacity,
}
}
// write appends (tArr[i], vArr[i]) pairs, overwriting oldest entries when full.
func (rb *sigRing) write(tArr, vArr []float64) {
rb.mu.Lock()
defer rb.mu.Unlock()
for i := 0; i < len(tArr); i++ {
rb.t[rb.head] = tArr[i]
rb.v[rb.head] = vArr[i]
rb.head = (rb.head + 1) % rb.cap
if rb.size < rb.cap {
rb.size++
}
}
}
// slice returns copies of all (t, v) pairs whose timestamp falls in [t0, t1].
// The returned slices are safe to use after the call without holding any lock.
func (rb *sigRing) slice(t0, t1 float64) ([]float64, []float64) {
rb.mu.RLock()
defer rb.mu.RUnlock()
if rb.size == 0 {
return nil, nil
}
start := 0
if rb.size == rb.cap {
start = rb.head
}
physAt := func(k int) int { return (start + k) % rb.cap }
// Binary search for t0
lo, hi := 0, rb.size
for lo < hi {
m := (lo + hi) >> 1
if rb.t[physAt(m)] < t0 {
lo = m + 1
} else {
hi = m
}
}
kStart := lo
// Binary search for t1
lo, hi = kStart, rb.size
for lo < hi {
m := (lo + hi) >> 1
if rb.t[physAt(m)] <= t1 {
lo = m + 1
} else {
hi = m
}
}
kEnd := lo
n := kEnd - kStart
if n <= 0 {
return nil, nil
}
outT := make([]float64, n)
outV := make([]float64, n)
for i := 0; i < n; i++ {
p := physAt(kStart + i)
outT[i] = rb.t[p]
outV[i] = rb.v[p]
}
return outT, outV
}