Files
2026-08-09 08:25:50 +12:00

75 lines
2.1 KiB
Go

package subsync
import "math/bits"
// signal is "somebody is speaking" as one bit per time bin.
//
// A bitset rather than a []bool because the search is the whole cost of this feature: a
// two-hour film at 50ms is around 144,000 bins, and every one of ~2,400 shifts has to
// compare all of them against every stretch candidate. Packed into words, one comparison
// is an AND and a population count over 2,250 words instead of 144,000 byte reads, which
// is the difference between a button that answers while somebody is still looking at the
// menu and one they wait on. math/bits.OnesCount64 compiles to a single instruction on
// every architecture this runs on.
type signal struct {
words []uint64
bins int
on int
}
func newSignal(bins int) *signal {
if bins < 1 {
bins = 1
}
return &signal{words: make([]uint64, (bins+63)/64), bins: bins}
}
// set marks the half-open bin range [from, to) as speech.
func (s *signal) set(from, to int) {
if from < 0 {
from = 0
}
if to > s.bins {
to = s.bins
}
for i := from; i < to; i++ {
word, bit := i/64, uint(i%64)
if s.words[word]&(1<<bit) == 0 {
s.words[word] |= 1 << bit
s.on++
}
}
}
// overlap counts the bins where both signals are speaking, with other displaced by shift
// bins. A positive shift means other is read later — the value to add to this signal's
// times to bring them onto other's.
//
// Negative shifts are answered by swapping the two, which is the same count from the other
// side and saves writing the word arithmetic twice in mirror image.
func (s *signal) overlap(other *signal, shift int) int {
if shift < 0 {
return other.overlap(s, -shift)
}
wordShift, bitShift := shift/64, uint(shift%64)
total := 0
for i := range s.words {
mine := s.words[i]
if mine == 0 {
continue
}
j := i + wordShift
if j >= len(other.words) {
break
}
theirs := other.words[j] >> bitShift
// Go defines a shift of 64 or more as zero, so this term vanishes when bitShift
// is zero rather than needing a branch of its own.
if j+1 < len(other.words) {
theirs |= other.words[j+1] << (64 - bitShift)
}
total += bits.OnesCount64(mine & theirs)
}
return total
}