mirror of
https://github.com/Llewellynvdm/fzf.git
synced 2024-06-21 08:32:22 +00:00
bbe10f4f77
By not storing item index twice, we can cut down the size of Result struct and now it makes more sense to store and pass Results by values. Benchmarks show no degradation of performance by additional pointer indirection for looking up index.
122 lines
2.5 KiB
Go
122 lines
2.5 KiB
Go
package fzf
|
|
|
|
import "fmt"
|
|
|
|
// EmptyMerger is a Merger with no data
|
|
var EmptyMerger = NewMerger(nil, [][]Result{}, false, false)
|
|
|
|
// Merger holds a set of locally sorted lists of items and provides the view of
|
|
// a single, globally-sorted list
|
|
type Merger struct {
|
|
pattern *Pattern
|
|
lists [][]Result
|
|
merged []Result
|
|
chunks *[]*Chunk
|
|
cursors []int
|
|
sorted bool
|
|
tac bool
|
|
final bool
|
|
count int
|
|
}
|
|
|
|
// PassMerger returns a new Merger that simply returns the items in the
|
|
// original order
|
|
func PassMerger(chunks *[]*Chunk, tac bool) *Merger {
|
|
mg := Merger{
|
|
pattern: nil,
|
|
chunks: chunks,
|
|
tac: tac,
|
|
count: 0}
|
|
|
|
for _, chunk := range *mg.chunks {
|
|
mg.count += len(*chunk)
|
|
}
|
|
return &mg
|
|
}
|
|
|
|
// NewMerger returns a new Merger
|
|
func NewMerger(pattern *Pattern, lists [][]Result, sorted bool, tac bool) *Merger {
|
|
mg := Merger{
|
|
pattern: pattern,
|
|
lists: lists,
|
|
merged: []Result{},
|
|
chunks: nil,
|
|
cursors: make([]int, len(lists)),
|
|
sorted: sorted,
|
|
tac: tac,
|
|
final: false,
|
|
count: 0}
|
|
|
|
for _, list := range mg.lists {
|
|
mg.count += len(list)
|
|
}
|
|
return &mg
|
|
}
|
|
|
|
// Length returns the number of items
|
|
func (mg *Merger) Length() int {
|
|
return mg.count
|
|
}
|
|
|
|
// Get returns the pointer to the Result object indexed by the given integer
|
|
func (mg *Merger) Get(idx int) Result {
|
|
if mg.chunks != nil {
|
|
if mg.tac {
|
|
idx = mg.count - idx - 1
|
|
}
|
|
chunk := (*mg.chunks)[idx/chunkSize]
|
|
return Result{item: &(*chunk)[idx%chunkSize]}
|
|
}
|
|
|
|
if mg.sorted {
|
|
return mg.mergedGet(idx)
|
|
}
|
|
|
|
if mg.tac {
|
|
idx = mg.count - idx - 1
|
|
}
|
|
for _, list := range mg.lists {
|
|
numItems := len(list)
|
|
if idx < numItems {
|
|
return list[idx]
|
|
}
|
|
idx -= numItems
|
|
}
|
|
panic(fmt.Sprintf("Index out of bounds (unsorted, %d/%d)", idx, mg.count))
|
|
}
|
|
|
|
func (mg *Merger) cacheable() bool {
|
|
return mg.count < mergerCacheMax
|
|
}
|
|
|
|
func (mg *Merger) mergedGet(idx int) Result {
|
|
for i := len(mg.merged); i <= idx; i++ {
|
|
minRank := minRank()
|
|
minIdx := -1
|
|
for listIdx, list := range mg.lists {
|
|
cursor := mg.cursors[listIdx]
|
|
if cursor < 0 || cursor == len(list) {
|
|
mg.cursors[listIdx] = -1
|
|
continue
|
|
}
|
|
if cursor >= 0 {
|
|
rank := list[cursor]
|
|
if minIdx < 0 || compareRanks(rank, minRank, mg.tac) {
|
|
minRank = rank
|
|
minIdx = listIdx
|
|
}
|
|
}
|
|
mg.cursors[listIdx] = cursor
|
|
}
|
|
|
|
if minIdx >= 0 {
|
|
chosen := mg.lists[minIdx]
|
|
mg.merged = append(mg.merged, chosen[mg.cursors[minIdx]])
|
|
mg.cursors[minIdx]++
|
|
} else {
|
|
panic(fmt.Sprintf("Index out of bounds (sorted, %d/%d)", i, mg.count))
|
|
}
|
|
}
|
|
return mg.merged[idx]
|
|
}
|