package tsm1 type prefixTreeKey [8]byte const prefixTreeKeySize = len(prefixTreeKey{}) // prefixTree is a type that keeps track of a slice of time ranges for prefixes and allows // querying for all of the time ranges for prefixes that match a provided key. It chunks // added prefixes by 8 bytes and then by 1 byte because typical prefixes will be 8 or 16 // bytes. This allows for effectively O(1) searches, but degrades to O(len(key)) in the // worst case when there is a matching prefix for every byte of the key. Appending a prefix // is similar. type prefixTree struct { values []TimeRange short map[byte]*prefixTree long map[prefixTreeKey]*prefixTree } func newPrefixTree() *prefixTree { return &prefixTree{ short: make(map[byte]*prefixTree), long: make(map[prefixTreeKey]*prefixTree), } } func (p *prefixTree) Append(prefix []byte, values ...TimeRange) { if len(prefix) >= prefixTreeKeySize { var lookup prefixTreeKey copy(lookup[:], prefix) ch, ok := p.long[lookup] if !ok { ch = newPrefixTree() p.long[lookup] = ch } ch.Append(prefix[prefixTreeKeySize:], values...) } else if len(prefix) > 0 { ch, ok := p.short[prefix[0]] if !ok { ch = newPrefixTree() p.short[prefix[0]] = ch } ch.Append(prefix[1:], values...) } else { p.values = append(p.values, values...) } } func (p *prefixTree) Search(key []byte, buf []TimeRange) []TimeRange { buf = append(buf, p.values...) if len(key) > 0 { if ch, ok := p.short[key[0]]; ok { buf = ch.Search(key[1:], buf) } } if len(key) >= prefixTreeKeySize { var lookup prefixTreeKey copy(lookup[:], key) if ch, ok := p.long[lookup]; ok { buf = ch.Search(key[prefixTreeKeySize:], buf) } } return buf } func (p *prefixTree) Count(key []byte) int { count := len(p.values) if len(key) > 0 { if ch, ok := p.short[key[0]]; ok { count += ch.Count(key[1:]) } } if len(key) >= prefixTreeKeySize { var lookup prefixTreeKey copy(lookup[:], key) if ch, ok := p.long[lookup]; ok { count += ch.Count(key[prefixTreeKeySize:]) } } return count } func (p *prefixTree) checkOverlap(key []byte, ts int64) bool { for _, t := range p.values { if t.Min <= ts && ts <= t.Max { return true } } if len(key) > 0 { if ch, ok := p.short[key[0]]; ok && ch.checkOverlap(key[1:], ts) { return true } } if len(key) >= prefixTreeKeySize { var lookup prefixTreeKey copy(lookup[:], key) if ch, ok := p.long[lookup]; ok && ch.checkOverlap(key[prefixTreeKeySize:], ts) { return true } } return false }