diff --git a/src/cache_test.go b/src/cache_test.go index 8a2d2cf..5d9c5cc 100644 --- a/src/cache_test.go +++ b/src/cache_test.go @@ -4,9 +4,8 @@ import "testing" func TestChunkCache(t *testing.T) { cache := NewChunkCache() - chunk2 := make(Chunk, chunkSize) chunk1p := &Chunk{} - chunk2p := &chunk2 + chunk2p := &Chunk{count: chunkSize} items1 := []Result{Result{}} items2 := []Result{Result{}, Result{}} cache.Add(chunk1p, "foo", items1) diff --git a/src/chunklist.go b/src/chunklist.go index 4a4458a..599a1ad 100644 --- a/src/chunklist.go +++ b/src/chunklist.go @@ -3,11 +3,14 @@ package fzf import "sync" // Chunk is a list of Items whose size has the upper limit of chunkSize -type Chunk []Item +type Chunk struct { + items [chunkSize]Item + count int +} // ItemBuilder is a closure type that builds Item object from a pointer to a // string and an integer -type ItemBuilder func([]byte, int) Item +type ItemBuilder func(*Item, []byte, int) bool // ChunkList is a list of Chunks type ChunkList struct { @@ -27,17 +30,16 @@ func NewChunkList(trans ItemBuilder) *ChunkList { } func (c *Chunk) push(trans ItemBuilder, data []byte, index int) bool { - item := trans(data, index) - if item.Nil() { - return false + if trans(&c.items[c.count], data, index) { + c.count++ + return true } - *c = append(*c, item) - return true + return false } // IsFull returns true if the Chunk is full func (c *Chunk) IsFull() bool { - return len(*c) == chunkSize + return c.count == chunkSize } func (cl *ChunkList) lastChunk() *Chunk { @@ -49,7 +51,7 @@ func CountItems(cs []*Chunk) int { if len(cs) == 0 { return 0 } - return chunkSize*(len(cs)-1) + len(*(cs[len(cs)-1])) + return chunkSize*(len(cs)-1) + cs[len(cs)-1].count } // Push adds the item to the list @@ -57,8 +59,7 @@ func (cl *ChunkList) Push(data []byte) bool { cl.mutex.Lock() if len(cl.chunks) == 0 || cl.lastChunk().IsFull() { - newChunk := Chunk(make([]Item, 0, chunkSize)) - cl.chunks = append(cl.chunks, &newChunk) + cl.chunks = append(cl.chunks, &Chunk{}) } if cl.lastChunk().push(cl.trans, data, cl.count) { diff --git a/src/chunklist_test.go b/src/chunklist_test.go index 78468e3..c8d33a6 100644 --- a/src/chunklist_test.go +++ b/src/chunklist_test.go @@ -11,10 +11,10 @@ func TestChunkList(t *testing.T) { // FIXME global sortCriteria = []criterion{byScore, byLength} - cl := NewChunkList(func(s []byte, i int) Item { - chars := util.ToChars(s) - chars.Index = int32(i * 2) - return Item{text: chars} + cl := NewChunkList(func(item *Item, s []byte, i int) bool { + item.text = util.ToChars(s) + item.text.Index = int32(i * 2) + return true }) // Snapshot @@ -40,11 +40,13 @@ func TestChunkList(t *testing.T) { // Check the content of the ChunkList chunk1 := snapshot[0] - if len(*chunk1) != 2 { + if chunk1.count != 2 { t.Error("Snapshot should contain only two items") } - if (*chunk1)[0].text.ToString() != "hello" || (*chunk1)[0].Index() != 0 || - (*chunk1)[1].text.ToString() != "world" || (*chunk1)[1].Index() != 2 { + if chunk1.items[0].text.ToString() != "hello" || + chunk1.items[0].Index() != 0 || + chunk1.items[1].text.ToString() != "world" || + chunk1.items[1].Index() != 2 { t.Error("Invalid data") } if chunk1.IsFull() { @@ -67,14 +69,14 @@ func TestChunkList(t *testing.T) { !snapshot[1].IsFull() || snapshot[2].IsFull() || count != chunkSize*2+2 { t.Error("Expected two full chunks and one more chunk") } - if len(*snapshot[2]) != 2 { + if snapshot[2].count != 2 { t.Error("Unexpected number of items") } cl.Push([]byte("hello")) cl.Push([]byte("world")) - lastChunkCount := len(*snapshot[len(snapshot)-1]) + lastChunkCount := snapshot[len(snapshot)-1].count if lastChunkCount != 2 { t.Error("Unexpected number of items:", lastChunkCount) } diff --git a/src/core.go b/src/core.go index 0b90a51..968d407 100644 --- a/src/core.go +++ b/src/core.go @@ -85,29 +85,30 @@ func Run(opts *Options, revision string) { var chunkList *ChunkList header := make([]string, 0, opts.HeaderLines) if len(opts.WithNth) == 0 { - chunkList = NewChunkList(func(data []byte, index int) Item { + chunkList = NewChunkList(func(item *Item, data []byte, index int) bool { if len(header) < opts.HeaderLines { header = append(header, string(data)) eventBox.Set(EvtHeader, header) - return nilItem + return false } - chars, colors := ansiProcessor(data) - chars.Index = int32(index) - return Item{text: chars, colors: colors} + item.text, item.colors = ansiProcessor(data) + item.text.Index = int32(index) + return true }) } else { - chunkList = NewChunkList(func(data []byte, index int) Item { + chunkList = NewChunkList(func(item *Item, data []byte, index int) bool { tokens := Tokenize(string(data), opts.Delimiter) trans := Transform(tokens, opts.WithNth) transformed := joinTokens(trans) if len(header) < opts.HeaderLines { header = append(header, transformed) eventBox.Set(EvtHeader, header) - return nilItem + return false } - trimmed, colors := ansiProcessor([]byte(transformed)) - trimmed.Index = int32(index) - return Item{text: trimmed, colors: colors, origText: &data} + item.text, item.colors = ansiProcessor([]byte(transformed)) + item.text.Index = int32(index) + item.origText = &data + return true }) } @@ -151,8 +152,8 @@ func Run(opts *Options, revision string) { slab := util.MakeSlab(slab16Size, slab32Size) reader := Reader{ func(runes []byte) bool { - item := chunkList.trans(runes, 0) - if !item.Nil() { + item := Item{} + if chunkList.trans(&item, runes, 0) { if result, _, _ := pattern.MatchItem(&item, false, slab); result != nil { opts.Printer(item.text.ToString()) found = true diff --git a/src/item.go b/src/item.go index b3879cb..cb778cb 100644 --- a/src/item.go +++ b/src/item.go @@ -17,11 +17,7 @@ func (item *Item) Index() int32 { return item.text.Index } -var nilItem = Item{text: util.Chars{Index: -1}} - -func (item *Item) Nil() bool { - return item.Index() < 0 -} +var minItem = Item{text: util.Chars{Index: -1}} func (item *Item) TrimLength() uint16 { return item.text.TrimLength() diff --git a/src/merger.go b/src/merger.go index 7d30a76..d710f2a 100644 --- a/src/merger.go +++ b/src/merger.go @@ -29,7 +29,7 @@ func PassMerger(chunks *[]*Chunk, tac bool) *Merger { count: 0} for _, chunk := range *mg.chunks { - mg.count += len(*chunk) + mg.count += chunk.count } return &mg } @@ -65,7 +65,7 @@ func (mg *Merger) Get(idx int) Result { idx = mg.count - idx - 1 } chunk := (*mg.chunks)[idx/chunkSize] - return Result{item: &(*chunk)[idx%chunkSize]} + return Result{item: &chunk.items[idx%chunkSize]} } if mg.sorted { diff --git a/src/pattern.go b/src/pattern.go index 8de15b9..94615ad 100644 --- a/src/pattern.go +++ b/src/pattern.go @@ -278,8 +278,8 @@ func (p *Pattern) matchChunk(chunk *Chunk, space []Result, slab *util.Slab) []Re matches := []Result{} if space == nil { - for idx := range *chunk { - if match, _, _ := p.MatchItem(&(*chunk)[idx], false, slab); match != nil { + for idx := 0; idx < chunk.count; idx++ { + if match, _, _ := p.MatchItem(&chunk.items[idx], false, slab); match != nil { matches = append(matches, *match) } } diff --git a/src/pattern_test.go b/src/pattern_test.go index 4066eb4..54d6f51 100644 --- a/src/pattern_test.go +++ b/src/pattern_test.go @@ -130,12 +130,11 @@ func TestOrigTextAndTransformed(t *testing.T) { origBytes := []byte("junegunn.choi") for _, extended := range []bool{false, true} { - chunk := Chunk{ - Item{ - text: util.ToChars([]byte("junegunn")), - origText: &origBytes, - transformed: &trans}, - } + chunk := Chunk{count: 1} + chunk.items[0] = Item{ + text: util.ToChars([]byte("junegunn")), + origText: &origBytes, + transformed: &trans} pattern.extended = extended matches := pattern.matchChunk(&chunk, nil, slab) // No cache if !(matches[0].item.text.ToString() == "junegunn" && @@ -144,7 +143,7 @@ func TestOrigTextAndTransformed(t *testing.T) { t.Error("Invalid match result", matches) } - match, offsets, pos := pattern.MatchItem(&chunk[0], true, slab) + match, offsets, pos := pattern.MatchItem(&chunk.items[0], true, slab) if !(match.item.text.ToString() == "junegunn" && string(*match.item.origText) == "junegunn.choi" && offsets[0][0] == 0 && offsets[0][1] == 5 && diff --git a/src/result.go b/src/result.go index 2df101b..58cbafd 100644 --- a/src/result.go +++ b/src/result.go @@ -85,7 +85,7 @@ func (result *Result) Index() int32 { } func minRank() Result { - return Result{item: &nilItem, points: [4]uint16{math.MaxUint16, 0, 0, 0}} + return Result{item: &minItem, points: [4]uint16{math.MaxUint16, 0, 0, 0}} } func (result *Result) colorOffsets(matchOffsets []Offset, theme *tui.ColorTheme, color tui.ColorPair, attr tui.Attr, current bool) []colorOffset {