From 662dc22c16aa206916a9971a8b2b268934da2ce0 Mon Sep 17 00:00:00 2001 From: Simon Hauser Date: Sun, 19 Sep 2021 13:36:11 +0200 Subject: [PATCH 1/5] chore: c project setup --- Makefile | 31 +++++++++++++++++++++++++++++++ src/telescope.c | 0 src/telescope.h | 0 3 files changed, 31 insertions(+) create mode 100644 src/telescope.c create mode 100644 src/telescope.h diff --git a/Makefile b/Makefile index c3da51def6..9861b97900 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,34 @@ +CFLAGS = -Wall -Werror -fpic -std=gnu99 +MODE ?= -Og -ggdb3 + +ifeq ($(OS),Windows_NT) + MKD = -mkdir + RM = cmd /C rmdir /Q /S + CC = gcc + TARGET := libtelescope.dll +else + MKD = mkdir -p + RM = rm -rf + TARGET := libtelescope.so +endif + +all: build/$(TARGET) + +build/$(TARGET): src/telescope.c src/telescope.h + $(MKD) build + $(CC) $(MODE) $(CFLAGS) -shared src/telescope.c -o build/$(TARGET) + +.PHONY: lint clangdhappy clean test docgen + +ntest: + nvim --headless --noplugin -u test/minrc.vim -c "PlenaryBustedDirectory test/ { minimal_init = './test/minrc.vim' }" + +clangdhappy: + compiledb make + +clean: + $(RM) build + test: nvim --headless --noplugin -u scripts/minimal_init.vim -c "PlenaryBustedDirectory lua/tests/automated/ { minimal_init = './scripts/minimal_init.vim' }" diff --git a/src/telescope.c b/src/telescope.c new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/telescope.h b/src/telescope.h new file mode 100644 index 0000000000..e69de29bb2 From 44b8905435f6acd4308eff80a0ef5eef142ae813 Mon Sep 17 00:00:00 2001 From: Simon Hauser Date: Sun, 19 Sep 2021 14:55:23 +0200 Subject: [PATCH 2/5] chore: setup ffi module --- lua/telescope/ffi.lua | 17 +++++++++++++++++ src/telescope.c | 5 +++++ src/telescope.h | 6 ++++++ 3 files changed, 28 insertions(+) create mode 100644 lua/telescope/ffi.lua diff --git a/lua/telescope/ffi.lua b/lua/telescope/ffi.lua new file mode 100644 index 0000000000..decb37476f --- /dev/null +++ b/lua/telescope/ffi.lua @@ -0,0 +1,17 @@ +local ffi = require "ffi" + +local library_path = (function() + local dirname = string.sub(debug.getinfo(1).source, 2, #"/fzf_telescope.lua" * -1) + if package.config:sub(1, 1) == "\\" then + return dirname .. "../build/libtelescope.dll" + else + return dirname .. "../build/libtelescope.so" + end +end)() +local native = ffi.load(library_path) + +ffi.cdef [[ +int new_linked_list(); +]] + +return native diff --git a/src/telescope.c b/src/telescope.c index e69de29bb2..6a1f6008aa 100644 --- a/src/telescope.c +++ b/src/telescope.c @@ -0,0 +1,5 @@ +#include "telescope.h" + +int new_linked_list() { + return 5; +} diff --git a/src/telescope.h b/src/telescope.h index e69de29bb2..1bd87023c5 100644 --- a/src/telescope.h +++ b/src/telescope.h @@ -0,0 +1,6 @@ +#ifndef _TELESCOPE_H_ +#define _TELESCOPE_H_ + +int new_linked_list(); + +#endif From 77c14ce35428fa5e6b42931de25aaf2edb69b1aa Mon Sep 17 00:00:00 2001 From: Simon Hauser Date: Sun, 19 Sep 2021 15:24:39 +0200 Subject: [PATCH 3/5] feat: replacing linked list it kinda works underwhelming. Idk if it worth it. Same speed with a debug build. ~1.5 times faster with a release build --- .github/workflows/ci.yml | 1 + .gitignore | 1 + .luacheckrc | 1 + Makefile | 2 +- lua/telescope/algos/c_linked_list.lua | 84 ++++++++++ lua/telescope/c_entry_manager.lua | 184 +++++++++++++++++++++ lua/telescope/ffi.lua | 30 +++- lua/telescope/pickers.lua | 2 +- lua/tests/automated/entry_manager_spec.lua | 4 +- scratch/benchmark.lua | 48 ++++++ src/telescope.c | 147 +++++++++++++++- src/telescope.h | 30 +++- 12 files changed, 525 insertions(+), 9 deletions(-) create mode 100644 lua/telescope/algos/c_linked_list.lua create mode 100644 lua/telescope/c_entry_manager.lua create mode 100644 scratch/benchmark.lua diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 54a05d9619..9ab598dc15 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,6 +47,7 @@ jobs: git clone --depth 1 https://github.com/nvim-lua/plenary.nvim ~/.local/share/nvim/site/pack/vendor/start/plenary.nvim git clone --depth 1 https://github.com/kyazdani42/nvim-web-devicons ~/.local/share/nvim/site/pack/vendor/start/nvim-web-devicons ln -s $(pwd) ~/.local/share/nvim/site/pack/vendor/start + make - name: Run tests run: | diff --git a/.gitignore b/.gitignore index ddeae0dfcc..a0f963ac2c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ build/ doc/tags .luacheckcache +.gdb_history diff --git a/.luacheckrc b/.luacheckrc index b035079570..20604c06bb 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -18,6 +18,7 @@ globals = { "_TelescopeConfigurationValues", "_TelescopeConfigurationPickers", "__TelescopeKeymapStore", + "__Telescope_FFI_DEFINED", } -- Global objects defined by the C code diff --git a/Makefile b/Makefile index 9861b97900..c88a997a6a 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ CFLAGS = -Wall -Werror -fpic -std=gnu99 -MODE ?= -Og -ggdb3 +MODE ?= -O3 ifeq ($(OS),Windows_NT) MKD = -mkdir diff --git a/lua/telescope/algos/c_linked_list.lua b/lua/telescope/algos/c_linked_list.lua new file mode 100644 index 0000000000..d6fc6fc01b --- /dev/null +++ b/lua/telescope/algos/c_linked_list.lua @@ -0,0 +1,84 @@ +local ffi = require "ffi" +local native = require "telescope.ffi" + +local LinkedList = {} +LinkedList.__index = LinkedList + +function LinkedList:new(opts) + opts = opts or {} + + return setmetatable({ + list = ffi.gc(native.fzf_list_create(opts.track_at), native.fzf_list_free), + tbl = {}, + idx = 0, + }, self) +end + +function LinkedList:has_tracked() + return self.list._tracked_node ~= nil +end + +function LinkedList:tracked() + return self.tbl[self.list._tracked_node.item] +end + +function LinkedList:append(item) + self.idx = self.idx + 1 + self.tbl[self.idx] = item + native.fzf_list_append(self.list, self.idx) +end + +function LinkedList:prepend(item) + self.idx = self.idx + 1 + self.tbl[self.idx] = item + native.fzf_list_prepend(self.list, self.idx) +end + +function LinkedList:place_after(index, node, item) + self.idx = self.idx + 1 + self.tbl[self.idx] = item + native.fzf_list_place_after(self.list, index, node, self.idx) +end + +function LinkedList:place_before(index, node, item) + self.idx = self.idx + 1 + self.tbl[self.idx] = item + native.fzf_list_place_before(self.list, index, node, self.idx) +end + +function LinkedList:size() + return tonumber(self.list.len) +end + +function LinkedList:iter() + local current_node = self.list.head + return function() + local node = current_node + if node == nil then + return nil + end + + current_node = current_node.next + return self.tbl[node.item] + end +end + +function LinkedList:ipairs() + local index = 0 + local current_node = self.list.head + + return function() + local node = current_node + if node == nil then + return nil + end + + current_node = current_node.next + index = index + 1 + return index, self.tbl[node.item], node + end +end + +function LinkedList:truncate() end + +return LinkedList diff --git a/lua/telescope/c_entry_manager.lua b/lua/telescope/c_entry_manager.lua new file mode 100644 index 0000000000..a8e9bba2d7 --- /dev/null +++ b/lua/telescope/c_entry_manager.lua @@ -0,0 +1,184 @@ +local log = require "telescope.log" + +local LinkedList = require "telescope.algos.c_linked_list" + +--[[ + +OK, new idea. +We can do linked list here. +To convert at the end to quickfix, just run the list. +... + +start node +end node + +if past loop of must have scores, + then we can just add to end node and shift end node to current node. + etc. + + + always inserts a row, because we clear everything before? + + can also optimize by keeping worst acceptable score around. + +--]] + +local EntryManager = {} +EntryManager.__index = EntryManager + +function EntryManager:new(max_results, set_entry, info) + log.trace "Creating entry_manager..." + + info = info or {} + info.looped = 0 + info.inserted = 0 + info.find_loop = 0 + + -- state contains list of + -- { entry, score } + -- Stored directly in a table, accessed as [1], [2] + set_entry = set_entry or function() end + + return setmetatable({ + linked_states = LinkedList:new { track_at = max_results }, + info = info, + max_results = max_results, + set_entry = set_entry, + worst_acceptable_score = math.huge, + }, self) +end + +function EntryManager:num_results() + return self.linked_states:size() +end + +function EntryManager:get_container(index) + for k, v in self.linked_states:ipairs() do + if k == index then + return v + end + end + + return {} +end + +function EntryManager:get_entry(index) + return self:get_container(index)[1] +end + +function EntryManager:get_score(index) + return self:get_container(index)[2] +end + +function EntryManager:get_ordinal(index) + return self:get_entry(index).ordinal +end + +function EntryManager:find_entry(entry) + local info = self.info + + local count = 0 + for container in self.linked_states:iter() do + count = count + 1 + + if container[1] == entry then + info.find_loop = info.find_loop + count + + return count + end + end + + info.find_loop = info.find_loop + count + return nil +end + +function EntryManager:_update_score_from_tracked() + if self.linked_states:has_tracked() then + self.worst_acceptable_score = math.min(self.worst_acceptable_score, self.linked_states:tracked()[2]) + end +end + +function EntryManager:_insert_container_before(picker, index, linked_node, new_container) + self.linked_states:place_before(index, linked_node, new_container) + self.set_entry(picker, index, new_container[1], new_container[2], true) + + self:_update_score_from_tracked() +end + +function EntryManager:_insert_container_after(picker, index, linked_node, new_container) + self.linked_states:place_after(index, linked_node, new_container) + self.set_entry(picker, index, new_container[1], new_container[2], true) + + self:_update_score_from_tracked() +end + +function EntryManager:_append_container(picker, new_container, should_update) + self.linked_states:append(new_container) + self.worst_acceptable_score = math.min(self.worst_acceptable_score, new_container[2]) + + if should_update then + self.set_entry(picker, self.linked_states:size(), new_container[1], new_container[2]) + end +end + +function EntryManager:add_entry(picker, score, entry) + score = score or 0 + + local max_res = self.max_results + local worst_score = self.worst_acceptable_score + local size = self.linked_states:size() + + local info = self.info + info.maxed = info.maxed or 0 + + local new_container = { entry, score } + + -- Short circuit for bad scores -- they never need to be displayed. + -- Just save them and we'll deal with them later. + if score >= worst_score then + return self.linked_states:append(new_container) + end + + -- Short circuit for first entry. + if size == 0 then + self.linked_states:prepend(new_container) + self.set_entry(picker, 1, entry, score) + return + end + + for index, container, node in self.linked_states:ipairs() do + info.looped = info.looped + 1 + + if container[2] > score then + return self:_insert_container_before(picker, index, node, new_container) + end + + if score < 1 and container[2] == score and #entry.ordinal < #container[1].ordinal then + return self:_insert_container_before(picker, index, node, new_container) + end + + -- Don't add results that are too bad. + if index >= max_res then + info.maxed = info.maxed + 1 + return self:_append_container(picker, new_container, false) + end + end + + if self.linked_states:size() >= max_res then + self.worst_acceptable_score = math.min(self.worst_acceptable_score, score) + end + + return self:_insert_container_after(picker, size + 1, self.linked_states.list.tail, new_container) +end + +function EntryManager:iter() + local iterator = self.linked_states:iter() + return function() + local val = iterator() + if val then + return val[1] + end + end +end + +return EntryManager diff --git a/lua/telescope/ffi.lua b/lua/telescope/ffi.lua index decb37476f..433f715e4f 100644 --- a/lua/telescope/ffi.lua +++ b/lua/telescope/ffi.lua @@ -10,8 +10,34 @@ local library_path = (function() end)() local native = ffi.load(library_path) -ffi.cdef [[ -int new_linked_list(); +if not __Telescope_FFI_DEFINED then + __Telescope_FFI_DEFINED = true + ffi.cdef [[ +typedef struct fzf_node_s fzf_node_t; +struct fzf_node_s { + fzf_node_t *next; + fzf_node_t *prev; + int item; +}; +typedef struct { + fzf_node_t *head; + fzf_node_t *tail; + fzf_node_t *_tracked_node; + size_t len; + size_t track_at; +} fzf_linked_list_t; + +fzf_linked_list_t *fzf_list_create(size_t track_at); +void fzf_list_free(fzf_linked_list_t *list); + +fzf_node_t *fzf_list_append(fzf_linked_list_t *list, int item); +void fzf_list_prepend(fzf_linked_list_t *list, int item); + +void fzf_list_place_after(fzf_linked_list_t *list, size_t index, + fzf_node_t *node, int item); +void fzf_list_place_before(fzf_linked_list_t *list, size_t index, + fzf_node_t *node, int item); ]] +end return native diff --git a/lua/telescope/pickers.lua b/lua/telescope/pickers.lua index 09a062f2ba..074fbceb95 100644 --- a/lua/telescope/pickers.lua +++ b/lua/telescope/pickers.lua @@ -22,7 +22,7 @@ local p_highlighter = require "telescope.pickers.highlights" local p_scroller = require "telescope.pickers.scroller" local p_window = require "telescope.pickers.window" -local EntryManager = require "telescope.entry_manager" +local EntryManager = require "telescope.c_entry_manager" local MultiSelect = require "telescope.pickers.multi" local get_default = utils.get_default diff --git a/lua/tests/automated/entry_manager_spec.lua b/lua/tests/automated/entry_manager_spec.lua index bae23bc2ad..849b335187 100644 --- a/lua/tests/automated/entry_manager_spec.lua +++ b/lua/tests/automated/entry_manager_spec.lua @@ -1,4 +1,4 @@ -local EntryManager = require "telescope.entry_manager" +local EntryManager = require "telescope.c_entry_manager" local eq = assert.are.same @@ -17,7 +17,7 @@ describe("process_result", function() manager:add_entry(nil, 1, "hello") manager:add_entry(nil, 2, "later") - eq(2, manager.linked_states.size) + eq(2, manager:num_results()) eq("hello", manager:get_entry(1)) eq("later", manager:get_entry(2)) diff --git a/scratch/benchmark.lua b/scratch/benchmark.lua new file mode 100644 index 0000000000..7d1bc5cdd1 --- /dev/null +++ b/scratch/benchmark.lua @@ -0,0 +1,48 @@ +local bench = require "plenary.benchmark" +local fzf = require "fzf_lib" +local CEntryManager = require "telescope.c_entry_manager" +local EntryManager = require "telescope.entry_manager" + +local function lines_from(file) + local lines = {} + for line in io.lines(file) do + lines[#lines + 1] = line + end + return lines +end + +local function filter(manager, prompt, lines) + local slab = fzf.allocate_slab() + local p = fzf.parse_pattern(prompt, 0) + for _, line in ipairs(lines) do + manager:add_entry({}, fzf.get_score(line, p, slab), { value = line, display = line, ordinal = line }) + end + fzf.free_pattern(p) + fzf.free_slab(slab) + + return manager:num_results() +end + +local lines = lines_from "../telescope-fzf-native.nvim/files" + +local c_manager = CEntryManager:new(10000) +local manager = EntryManager:new(10000) + +bench("lua vs c ffi", { + warmup = 3, + runs = 10, + fun = { + { + "c ffi", + function() + filter(c_manager, "fzf.c", lines) + end, + }, + { + "lua", + function() + filter(manager, "fzf.c", lines) + end, + }, + }, +}) diff --git a/src/telescope.c b/src/telescope.c index 6a1f6008aa..1a39848fde 100644 --- a/src/telescope.c +++ b/src/telescope.c @@ -1,5 +1,148 @@ #include "telescope.h" -int new_linked_list() { - return 5; +#include +#include +#include + +// ENTRYMANAGER THINGS +static fzf_node_t *create_node(int item) { + fzf_node_t *node = (fzf_node_t *)malloc(sizeof(fzf_node_t)); + node->item = item; + node->next = NULL; + node->prev = NULL; + return node; +} + +fzf_linked_list_t *fzf_list_create(size_t track_at) { + fzf_linked_list_t *list = + (fzf_linked_list_t *)malloc(sizeof(fzf_linked_list_t)); + list->len = 0; + list->track_at = track_at; + list->head = NULL; + list->tail = NULL; + list->_tracked_node = NULL; + return list; +} + +void fzf_list_free(fzf_linked_list_t *list) { + if (list->head) { + fzf_node_t *curr = list->head; + while (curr != NULL) { + fzf_node_t *tmp = curr->next; + free(curr); + curr = tmp; + } + } + + free(list); +} + +fzf_node_t *fzf_list_append(fzf_linked_list_t *list, int item) { + ++list->len; + fzf_node_t *node = create_node(item); + + if (list->head == NULL) { + list->head = node; + } + + if (list->tail) { + list->tail->next = node; + node->prev = list->tail; + } + + list->tail = node; + if (list->len == list->track_at) { + list->_tracked_node = node; + } + + return node; +} + +void fzf_list_prepend(fzf_linked_list_t *list, int item) { + ++list->len; + fzf_node_t *node = create_node(item); + + if (list->tail == NULL) { + list->tail = node; + } + + if (list->head) { + list->head->prev = node; + node->next = list->head; + } + list->head = node; + if (list->len == list->track_at) { + list->_tracked_node = list->tail; + } else if (list->len > list->track_at) { + list->_tracked_node = list->_tracked_node->prev; + } +} + +void fzf_list_place_after(fzf_linked_list_t *list, size_t index, + fzf_node_t *node, int item) { + ++list->len; + fzf_node_t *new_node = create_node(item); + + assert(node->prev != node); + assert(node->next != node); + + if (list->tail == node) { + list->tail = new_node; + } + + new_node->prev = node; + new_node->next = node->next; + node->next = new_node; + + if (new_node->prev) { + new_node->prev->next = new_node; + } + + if (new_node->next) { + new_node->next->prev = new_node; + } + + if (index == list->track_at) { + list->_tracked_node = new_node; + } else if (index < list->track_at) { + if (list->len == list->track_at) { + list->_tracked_node = list->tail; + } else if (list->len > list->track_at) { + list->_tracked_node = list->_tracked_node->prev; + } + } +} + +void fzf_list_place_before(fzf_linked_list_t *list, size_t index, + fzf_node_t *node, int item) { + ++list->len; + fzf_node_t *new_node = create_node(item); + + assert(node->prev != node); + assert(node->next != node); + + if (list->head == node) { + list->head = new_node; + } + + new_node->prev = node->prev; + new_node->next = node; + node->prev = new_node; + + if (new_node->prev) { + new_node->prev->next = new_node; + } + if (new_node->next) { + new_node->next->prev = new_node; + } + + if (index == list->track_at - 1) { + list->_tracked_node = node; + } else if (index < list->track_at) { + if (list->len == list->track_at) { + list->_tracked_node = list->tail; + } else if (list->len > list->track_at) { + list->_tracked_node = list->_tracked_node->prev; + } + } } diff --git a/src/telescope.h b/src/telescope.h index 1bd87023c5..7ff777e6e3 100644 --- a/src/telescope.h +++ b/src/telescope.h @@ -1,6 +1,34 @@ #ifndef _TELESCOPE_H_ #define _TELESCOPE_H_ -int new_linked_list(); +#include +#include +#include + +typedef struct fzf_node_s fzf_node_t; +struct fzf_node_s { + fzf_node_t *next; + fzf_node_t *prev; + int item; +}; + +typedef struct { + fzf_node_t *head; + fzf_node_t *tail; + fzf_node_t *_tracked_node; + size_t len; + size_t track_at; +} fzf_linked_list_t; + +fzf_linked_list_t *fzf_list_create(size_t track_at); +void fzf_list_free(fzf_linked_list_t *list); + +fzf_node_t *fzf_list_append(fzf_linked_list_t *list, int item); +void fzf_list_prepend(fzf_linked_list_t *list, int item); + +void fzf_list_place_after(fzf_linked_list_t *list, size_t index, + fzf_node_t *node, int item); +void fzf_list_place_before(fzf_linked_list_t *list, size_t index, + fzf_node_t *node, int item); #endif From 63f474874750b30ef88ca4ad6d7a0b1293799cb1 Mon Sep 17 00:00:00 2001 From: Simon Hauser Date: Mon, 20 Sep 2021 10:51:27 +0200 Subject: [PATCH 4/5] refactor: score is now stored in c 2.2 times faster --- .clang-format | 7 +++ lua/telescope/algos/c_linked_list.lua | 24 +++++----- lua/telescope/c_entry_manager.lua | 64 +++++++++++++-------------- lua/telescope/ffi.lua | 45 ++++++++++--------- src/telescope.c | 39 ++++++++-------- src/telescope.h | 40 ++++++++++------- 6 files changed, 117 insertions(+), 102 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000..32799e7d57 --- /dev/null +++ b/.clang-format @@ -0,0 +1,7 @@ +AllowShortFunctionsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +IndentCaseLabels: false +SortIncludes: false +ColumnLimit: 80 +IndentWidth: 2 diff --git a/lua/telescope/algos/c_linked_list.lua b/lua/telescope/algos/c_linked_list.lua index d6fc6fc01b..aa6b78df87 100644 --- a/lua/telescope/algos/c_linked_list.lua +++ b/lua/telescope/algos/c_linked_list.lua @@ -8,7 +8,7 @@ function LinkedList:new(opts) opts = opts or {} return setmetatable({ - list = ffi.gc(native.fzf_list_create(opts.track_at), native.fzf_list_free), + list = ffi.gc(native.tele_list_create(opts.track_at), native.tele_list_free), tbl = {}, idx = 0, }, self) @@ -19,31 +19,31 @@ function LinkedList:has_tracked() end function LinkedList:tracked() - return self.tbl[self.list._tracked_node.item] + return self.tbl[self.list._tracked_node.item.idx], self.list._tracked_node.item.score end -function LinkedList:append(item) +function LinkedList:append(item, score) self.idx = self.idx + 1 self.tbl[self.idx] = item - native.fzf_list_append(self.list, self.idx) + native.tele_list_append(self.list, self.idx, score) end -function LinkedList:prepend(item) +function LinkedList:prepend(item, score) self.idx = self.idx + 1 self.tbl[self.idx] = item - native.fzf_list_prepend(self.list, self.idx) + native.tele_list_prepend(self.list, self.idx, score) end -function LinkedList:place_after(index, node, item) +function LinkedList:place_after(index, node, item, score) self.idx = self.idx + 1 self.tbl[self.idx] = item - native.fzf_list_place_after(self.list, index, node, self.idx) + native.tele_list_place_after(self.list, index, node, self.idx, score) end -function LinkedList:place_before(index, node, item) +function LinkedList:place_before(index, node, item, score) self.idx = self.idx + 1 self.tbl[self.idx] = item - native.fzf_list_place_before(self.list, index, node, self.idx) + native.tele_list_place_before(self.list, index, node, self.idx, score) end function LinkedList:size() @@ -59,7 +59,7 @@ function LinkedList:iter() end current_node = current_node.next - return self.tbl[node.item] + return self.tbl[node.item.idx], node.item.score end end @@ -75,7 +75,7 @@ function LinkedList:ipairs() current_node = current_node.next index = index + 1 - return index, self.tbl[node.item], node + return index, self.tbl[node.item.idx], node.item.score, node end end diff --git a/lua/telescope/c_entry_manager.lua b/lua/telescope/c_entry_manager.lua index a8e9bba2d7..df1c520689 100644 --- a/lua/telescope/c_entry_manager.lua +++ b/lua/telescope/c_entry_manager.lua @@ -34,9 +34,6 @@ function EntryManager:new(max_results, set_entry, info) info.inserted = 0 info.find_loop = 0 - -- state contains list of - -- { entry, score } - -- Stored directly in a table, accessed as [1], [2] set_entry = set_entry or function() end return setmetatable({ @@ -53,9 +50,9 @@ function EntryManager:num_results() end function EntryManager:get_container(index) - for k, v in self.linked_states:ipairs() do + for k, entry, score in self.linked_states:ipairs() do if k == index then - return v + return entry, score end end @@ -63,11 +60,13 @@ function EntryManager:get_container(index) end function EntryManager:get_entry(index) - return self:get_container(index)[1] + local entry = self:get_container(index) + return entry end function EntryManager:get_score(index) - return self:get_container(index)[2] + local _, score = self:get_container(index) + return score end function EntryManager:get_ordinal(index) @@ -78,10 +77,10 @@ function EntryManager:find_entry(entry) local info = self.info local count = 0 - for container in self.linked_states:iter() do + for o_entry in self.linked_states:iter() do count = count + 1 - if container[1] == entry then + if o_entry == entry then info.find_loop = info.find_loop + count return count @@ -94,30 +93,31 @@ end function EntryManager:_update_score_from_tracked() if self.linked_states:has_tracked() then - self.worst_acceptable_score = math.min(self.worst_acceptable_score, self.linked_states:tracked()[2]) + local _, tracked_score = self.linked_states:tracked() + self.worst_acceptable_score = math.min(self.worst_acceptable_score, tracked_score) end end -function EntryManager:_insert_container_before(picker, index, linked_node, new_container) - self.linked_states:place_before(index, linked_node, new_container) - self.set_entry(picker, index, new_container[1], new_container[2], true) +function EntryManager:_insert_container_before(picker, index, linked_node, entry, score) + self.linked_states:place_before(index, linked_node, entry, score) + self.set_entry(picker, index, entry, score, true) self:_update_score_from_tracked() end -function EntryManager:_insert_container_after(picker, index, linked_node, new_container) - self.linked_states:place_after(index, linked_node, new_container) - self.set_entry(picker, index, new_container[1], new_container[2], true) +function EntryManager:_insert_container_after(picker, index, linked_node, entry, score) + self.linked_states:place_after(index, linked_node, entry, score) + self.set_entry(picker, index, entry, score, true) self:_update_score_from_tracked() end -function EntryManager:_append_container(picker, new_container, should_update) - self.linked_states:append(new_container) - self.worst_acceptable_score = math.min(self.worst_acceptable_score, new_container[2]) +function EntryManager:_append_container(picker, entry, score, should_update) + self.linked_states:append(entry, score) + self.worst_acceptable_score = math.min(self.worst_acceptable_score, score) if should_update then - self.set_entry(picker, self.linked_states:size(), new_container[1], new_container[2]) + self.set_entry(picker, self.linked_states:size(), entry, score) end end @@ -131,36 +131,34 @@ function EntryManager:add_entry(picker, score, entry) local info = self.info info.maxed = info.maxed or 0 - local new_container = { entry, score } - -- Short circuit for bad scores -- they never need to be displayed. -- Just save them and we'll deal with them later. if score >= worst_score then - return self.linked_states:append(new_container) + return self.linked_states:append(entry, score) end -- Short circuit for first entry. if size == 0 then - self.linked_states:prepend(new_container) + self.linked_states:prepend(entry, score) self.set_entry(picker, 1, entry, score) return end - for index, container, node in self.linked_states:ipairs() do + for index, o_entry, o_score, node in self.linked_states:ipairs() do info.looped = info.looped + 1 - if container[2] > score then - return self:_insert_container_before(picker, index, node, new_container) + if o_score > score then + return self:_insert_container_before(picker, index, node, entry, score) end - if score < 1 and container[2] == score and #entry.ordinal < #container[1].ordinal then - return self:_insert_container_before(picker, index, node, new_container) + if score < 1 and o_score == score and #entry.ordinal < #o_entry.ordinal then + return self:_insert_container_before(picker, index, node, entry, score) end -- Don't add results that are too bad. if index >= max_res then info.maxed = info.maxed + 1 - return self:_append_container(picker, new_container, false) + return self:_append_container(picker, entry, score, false) end end @@ -168,16 +166,14 @@ function EntryManager:add_entry(picker, score, entry) self.worst_acceptable_score = math.min(self.worst_acceptable_score, score) end - return self:_insert_container_after(picker, size + 1, self.linked_states.list.tail, new_container) + return self:_insert_container_after(picker, size + 1, self.linked_states.list.tail, entry, score) end function EntryManager:iter() local iterator = self.linked_states:iter() return function() local val = iterator() - if val then - return val[1] - end + return val end end diff --git a/lua/telescope/ffi.lua b/lua/telescope/ffi.lua index 433f715e4f..3387244c5e 100644 --- a/lua/telescope/ffi.lua +++ b/lua/telescope/ffi.lua @@ -1,11 +1,11 @@ local ffi = require "ffi" local library_path = (function() - local dirname = string.sub(debug.getinfo(1).source, 2, #"/fzf_telescope.lua" * -1) + local dirname = string.sub(debug.getinfo(1).source, 2, #"/ffi.lua" * -1) if package.config:sub(1, 1) == "\\" then - return dirname .. "../build/libtelescope.dll" + return dirname .. "../../build/libtelescope.dll" else - return dirname .. "../build/libtelescope.so" + return dirname .. "../../build/libtelescope.so" end end)() local native = ffi.load(library_path) @@ -13,30 +13,35 @@ local native = ffi.load(library_path) if not __Telescope_FFI_DEFINED then __Telescope_FFI_DEFINED = true ffi.cdef [[ -typedef struct fzf_node_s fzf_node_t; -struct fzf_node_s { - fzf_node_t *next; - fzf_node_t *prev; - int item; +typedef struct { + int32_t idx; + double score; +} tele_container; + +typedef struct tele_node_s tele_node_t; +struct tele_node_s { + tele_node_t *next; + tele_node_t *prev; + tele_container item; }; typedef struct { - fzf_node_t *head; - fzf_node_t *tail; - fzf_node_t *_tracked_node; + tele_node_t *head; + tele_node_t *tail; + tele_node_t *_tracked_node; size_t len; size_t track_at; -} fzf_linked_list_t; +} tele_linked_list_t; -fzf_linked_list_t *fzf_list_create(size_t track_at); -void fzf_list_free(fzf_linked_list_t *list); +tele_linked_list_t *tele_list_create(size_t); +void tele_list_free(tele_linked_list_t *); -fzf_node_t *fzf_list_append(fzf_linked_list_t *list, int item); -void fzf_list_prepend(fzf_linked_list_t *list, int item); +tele_node_t *tele_list_append(tele_linked_list_t *, int32_t, double); +void tele_list_prepend(tele_linked_list_t *, int32_t, double); -void fzf_list_place_after(fzf_linked_list_t *list, size_t index, - fzf_node_t *node, int item); -void fzf_list_place_before(fzf_linked_list_t *list, size_t index, - fzf_node_t *node, int item); +void tele_list_place_after(tele_linked_list_t *, size_t, tele_node_t *, int32_t, + double); +void tele_list_place_before(tele_linked_list_t *, size_t, tele_node_t *, + int32_t, double); ]] end diff --git a/src/telescope.c b/src/telescope.c index 1a39848fde..32c9289368 100644 --- a/src/telescope.c +++ b/src/telescope.c @@ -5,17 +5,17 @@ #include // ENTRYMANAGER THINGS -static fzf_node_t *create_node(int item) { - fzf_node_t *node = (fzf_node_t *)malloc(sizeof(fzf_node_t)); - node->item = item; +static tele_node_t *create_node(int32_t item, double score) { + tele_node_t *node = (tele_node_t *)malloc(sizeof(tele_node_t)); + node->item = (tele_container){.idx = item, .score = score}; node->next = NULL; node->prev = NULL; return node; } -fzf_linked_list_t *fzf_list_create(size_t track_at) { - fzf_linked_list_t *list = - (fzf_linked_list_t *)malloc(sizeof(fzf_linked_list_t)); +tele_linked_list_t *tele_list_create(size_t track_at) { + tele_linked_list_t *list = + (tele_linked_list_t *)malloc(sizeof(tele_linked_list_t)); list->len = 0; list->track_at = track_at; list->head = NULL; @@ -24,11 +24,11 @@ fzf_linked_list_t *fzf_list_create(size_t track_at) { return list; } -void fzf_list_free(fzf_linked_list_t *list) { +void tele_list_free(tele_linked_list_t *list) { if (list->head) { - fzf_node_t *curr = list->head; + tele_node_t *curr = list->head; while (curr != NULL) { - fzf_node_t *tmp = curr->next; + tele_node_t *tmp = curr->next; free(curr); curr = tmp; } @@ -37,9 +37,10 @@ void fzf_list_free(fzf_linked_list_t *list) { free(list); } -fzf_node_t *fzf_list_append(fzf_linked_list_t *list, int item) { +tele_node_t *tele_list_append(tele_linked_list_t *list, int32_t item, + double score) { ++list->len; - fzf_node_t *node = create_node(item); + tele_node_t *node = create_node(item, score); if (list->head == NULL) { list->head = node; @@ -58,9 +59,9 @@ fzf_node_t *fzf_list_append(fzf_linked_list_t *list, int item) { return node; } -void fzf_list_prepend(fzf_linked_list_t *list, int item) { +void tele_list_prepend(tele_linked_list_t *list, int32_t item, double score) { ++list->len; - fzf_node_t *node = create_node(item); + tele_node_t *node = create_node(item, score); if (list->tail == NULL) { list->tail = node; @@ -78,10 +79,10 @@ void fzf_list_prepend(fzf_linked_list_t *list, int item) { } } -void fzf_list_place_after(fzf_linked_list_t *list, size_t index, - fzf_node_t *node, int item) { +void tele_list_place_after(tele_linked_list_t *list, size_t index, + tele_node_t *node, int32_t item, double score) { ++list->len; - fzf_node_t *new_node = create_node(item); + tele_node_t *new_node = create_node(item, score); assert(node->prev != node); assert(node->next != node); @@ -113,10 +114,10 @@ void fzf_list_place_after(fzf_linked_list_t *list, size_t index, } } -void fzf_list_place_before(fzf_linked_list_t *list, size_t index, - fzf_node_t *node, int item) { +void tele_list_place_before(tele_linked_list_t *list, size_t index, + tele_node_t *node, int32_t item, double score) { ++list->len; - fzf_node_t *new_node = create_node(item); + tele_node_t *new_node = create_node(item, score); assert(node->prev != node); assert(node->next != node); diff --git a/src/telescope.h b/src/telescope.h index 7ff777e6e3..82a621d479 100644 --- a/src/telescope.h +++ b/src/telescope.h @@ -5,30 +5,36 @@ #include #include -typedef struct fzf_node_s fzf_node_t; -struct fzf_node_s { - fzf_node_t *next; - fzf_node_t *prev; - int item; +typedef struct { + int32_t idx; + double score; +} tele_container; + +typedef struct tele_node_s tele_node_t; +struct tele_node_s { + tele_node_t *next; + tele_node_t *prev; + tele_container item; }; typedef struct { - fzf_node_t *head; - fzf_node_t *tail; - fzf_node_t *_tracked_node; + tele_node_t *head; + tele_node_t *tail; + tele_node_t *_tracked_node; size_t len; size_t track_at; -} fzf_linked_list_t; +} tele_linked_list_t; -fzf_linked_list_t *fzf_list_create(size_t track_at); -void fzf_list_free(fzf_linked_list_t *list); +tele_linked_list_t *tele_list_create(size_t track_at); +void tele_list_free(tele_linked_list_t *list); -fzf_node_t *fzf_list_append(fzf_linked_list_t *list, int item); -void fzf_list_prepend(fzf_linked_list_t *list, int item); +tele_node_t *tele_list_append(tele_linked_list_t *list, int32_t item, + double score); +void tele_list_prepend(tele_linked_list_t *list, int32_t item, double score); -void fzf_list_place_after(fzf_linked_list_t *list, size_t index, - fzf_node_t *node, int item); -void fzf_list_place_before(fzf_linked_list_t *list, size_t index, - fzf_node_t *node, int item); +void tele_list_place_after(tele_linked_list_t *list, size_t index, + tele_node_t *node, int32_t item, double score); +void tele_list_place_before(tele_linked_list_t *list, size_t index, + tele_node_t *node, int32_t item, double score); #endif From 27d65bf58a956612b18a9d56c34afcc7582f62aa Mon Sep 17 00:00:00 2001 From: Simon Hauser Date: Mon, 20 Sep 2021 11:25:43 +0200 Subject: [PATCH 5/5] refactor: rewrite entry_manager in c --- lua/telescope/algos/c_linked_list.lua | 84 ---------- lua/telescope/c_entry_manager.lua | 179 ++++++--------------- lua/telescope/entry_manager.lua | 4 + lua/telescope/ffi.lua | 17 +- lua/telescope/pickers.lua | 2 +- lua/tests/automated/entry_manager_spec.lua | 19 +-- scratch/benchmark.lua | 5 +- src/telescope.c | 90 +++++++++-- src/telescope.h | 21 ++- 9 files changed, 161 insertions(+), 260 deletions(-) delete mode 100644 lua/telescope/algos/c_linked_list.lua diff --git a/lua/telescope/algos/c_linked_list.lua b/lua/telescope/algos/c_linked_list.lua deleted file mode 100644 index aa6b78df87..0000000000 --- a/lua/telescope/algos/c_linked_list.lua +++ /dev/null @@ -1,84 +0,0 @@ -local ffi = require "ffi" -local native = require "telescope.ffi" - -local LinkedList = {} -LinkedList.__index = LinkedList - -function LinkedList:new(opts) - opts = opts or {} - - return setmetatable({ - list = ffi.gc(native.tele_list_create(opts.track_at), native.tele_list_free), - tbl = {}, - idx = 0, - }, self) -end - -function LinkedList:has_tracked() - return self.list._tracked_node ~= nil -end - -function LinkedList:tracked() - return self.tbl[self.list._tracked_node.item.idx], self.list._tracked_node.item.score -end - -function LinkedList:append(item, score) - self.idx = self.idx + 1 - self.tbl[self.idx] = item - native.tele_list_append(self.list, self.idx, score) -end - -function LinkedList:prepend(item, score) - self.idx = self.idx + 1 - self.tbl[self.idx] = item - native.tele_list_prepend(self.list, self.idx, score) -end - -function LinkedList:place_after(index, node, item, score) - self.idx = self.idx + 1 - self.tbl[self.idx] = item - native.tele_list_place_after(self.list, index, node, self.idx, score) -end - -function LinkedList:place_before(index, node, item, score) - self.idx = self.idx + 1 - self.tbl[self.idx] = item - native.tele_list_place_before(self.list, index, node, self.idx, score) -end - -function LinkedList:size() - return tonumber(self.list.len) -end - -function LinkedList:iter() - local current_node = self.list.head - return function() - local node = current_node - if node == nil then - return nil - end - - current_node = current_node.next - return self.tbl[node.item.idx], node.item.score - end -end - -function LinkedList:ipairs() - local index = 0 - local current_node = self.list.head - - return function() - local node = current_node - if node == nil then - return nil - end - - current_node = current_node.next - index = index + 1 - return index, self.tbl[node.item.idx], node.item.score, node - end -end - -function LinkedList:truncate() end - -return LinkedList diff --git a/lua/telescope/c_entry_manager.lua b/lua/telescope/c_entry_manager.lua index df1c520689..f131e94115 100644 --- a/lua/telescope/c_entry_manager.lua +++ b/lua/telescope/c_entry_manager.lua @@ -1,72 +1,67 @@ -local log = require "telescope.log" - -local LinkedList = require "telescope.algos.c_linked_list" - ---[[ - -OK, new idea. -We can do linked list here. -To convert at the end to quickfix, just run the list. -... - -start node -end node - -if past loop of must have scores, - then we can just add to end node and shift end node to current node. - etc. - - - always inserts a row, because we clear everything before? - - can also optimize by keeping worst acceptable score around. - ---]] +local ffi = require "ffi" +local native = require "telescope.ffi" local EntryManager = {} EntryManager.__index = EntryManager -function EntryManager:new(max_results, set_entry, info) - log.trace "Creating entry_manager..." - - info = info or {} - info.looped = 0 - info.inserted = 0 - info.find_loop = 0 - - set_entry = set_entry or function() end - +function EntryManager:new(max_results, set_entry) return setmetatable({ - linked_states = LinkedList:new { track_at = max_results }, - info = info, + manager = ffi.gc(native.tele_manager_create(max_results), native.tele_manager_free), + tbl = {}, + idx = 0, max_results = max_results, - set_entry = set_entry, - worst_acceptable_score = math.huge, + set_entry = vim.F.if_nil(set_entry, function() end), }, self) end function EntryManager:num_results() - return self.linked_states:size() + return tonumber(self.manager.list.len) +end + +function EntryManager:worst_acceptable_score() + return tonumber(self.manager.worst_acceptable_score) +end + +local function __iter(self) + local current_node = self.manager.list.head + return function() + local node = current_node + if node == nil then + return nil + end + + current_node = current_node.next + return self.tbl[node.item.idx], node.item.score + end end function EntryManager:get_container(index) - for k, entry, score in self.linked_states:ipairs() do + local k = 0 + local current_node = self.manager.list.head + + while true do + local node = current_node + if node == nil then + return nil + end + current_node = current_node.next + k = k + 1 if k == index then - return entry, score + return node.item end end - - return {} end function EntryManager:get_entry(index) - local entry = self:get_container(index) - return entry + local node = self:get_container(index) + if node then + return self.tbl[node.idx] + end + return {} end function EntryManager:get_score(index) - local _, score = self:get_container(index) - return score + return self:get_container(index).score end function EntryManager:get_ordinal(index) @@ -74,107 +69,35 @@ function EntryManager:get_ordinal(index) end function EntryManager:find_entry(entry) - local info = self.info - local count = 0 - for o_entry in self.linked_states:iter() do + for o_entry in __iter(self) do count = count + 1 if o_entry == entry then - info.find_loop = info.find_loop + count - return count end end - - info.find_loop = info.find_loop + count - return nil -end - -function EntryManager:_update_score_from_tracked() - if self.linked_states:has_tracked() then - local _, tracked_score = self.linked_states:tracked() - self.worst_acceptable_score = math.min(self.worst_acceptable_score, tracked_score) - end -end - -function EntryManager:_insert_container_before(picker, index, linked_node, entry, score) - self.linked_states:place_before(index, linked_node, entry, score) - self.set_entry(picker, index, entry, score, true) - - self:_update_score_from_tracked() -end - -function EntryManager:_insert_container_after(picker, index, linked_node, entry, score) - self.linked_states:place_after(index, linked_node, entry, score) - self.set_entry(picker, index, entry, score, true) - - self:_update_score_from_tracked() -end - -function EntryManager:_append_container(picker, entry, score, should_update) - self.linked_states:append(entry, score) - self.worst_acceptable_score = math.min(self.worst_acceptable_score, score) - - if should_update then - self.set_entry(picker, self.linked_states:size(), entry, score) - end end function EntryManager:add_entry(picker, score, entry) score = score or 0 - local max_res = self.max_results - local worst_score = self.worst_acceptable_score - local size = self.linked_states:size() - - local info = self.info - info.maxed = info.maxed or 0 - - -- Short circuit for bad scores -- they never need to be displayed. - -- Just save them and we'll deal with them later. - if score >= worst_score then - return self.linked_states:append(entry, score) + self.idx = self.idx + 1 + self.tbl[self.idx] = entry + local index = native.tele_manager_add(self.manager, self.idx, score) + if index > 0 then + self.set_entry(picker, index, entry, score, true) end - - -- Short circuit for first entry. - if size == 0 then - self.linked_states:prepend(entry, score) - self.set_entry(picker, 1, entry, score) - return - end - - for index, o_entry, o_score, node in self.linked_states:ipairs() do - info.looped = info.looped + 1 - - if o_score > score then - return self:_insert_container_before(picker, index, node, entry, score) - end - - if score < 1 and o_score == score and #entry.ordinal < #o_entry.ordinal then - return self:_insert_container_before(picker, index, node, entry, score) - end - - -- Don't add results that are too bad. - if index >= max_res then - info.maxed = info.maxed + 1 - return self:_append_container(picker, entry, score, false) - end - end - - if self.linked_states:size() >= max_res then - self.worst_acceptable_score = math.min(self.worst_acceptable_score, score) - end - - return self:_insert_container_after(picker, size + 1, self.linked_states.list.tail, entry, score) end function EntryManager:iter() - local iterator = self.linked_states:iter() + local iterator = __iter(self) return function() local val = iterator() return val end end +function EntryManager:truncate() end + return EntryManager diff --git a/lua/telescope/entry_manager.lua b/lua/telescope/entry_manager.lua index 5055171eaa..ab0ebdf9ea 100644 --- a/lua/telescope/entry_manager.lua +++ b/lua/telescope/entry_manager.lua @@ -186,4 +186,8 @@ function EntryManager:iter() end end +function EntryManager:truncate(...) + return self.linked_states:truncate(...) +end + return EntryManager diff --git a/lua/telescope/ffi.lua b/lua/telescope/ffi.lua index 3387244c5e..6447e7c777 100644 --- a/lua/telescope/ffi.lua +++ b/lua/telescope/ffi.lua @@ -32,16 +32,15 @@ typedef struct { size_t track_at; } tele_linked_list_t; -tele_linked_list_t *tele_list_create(size_t); -void tele_list_free(tele_linked_list_t *); - -tele_node_t *tele_list_append(tele_linked_list_t *, int32_t, double); -void tele_list_prepend(tele_linked_list_t *, int32_t, double); +typedef struct { + size_t max_results; + double worst_acceptable_score; + tele_linked_list_t *list; +} tele_manager_t; -void tele_list_place_after(tele_linked_list_t *, size_t, tele_node_t *, int32_t, - double); -void tele_list_place_before(tele_linked_list_t *, size_t, tele_node_t *, - int32_t, double); +tele_manager_t *tele_manager_create(size_t max_results); +void tele_manager_free(tele_manager_t *manager); +int32_t tele_manager_add(tele_manager_t *manager, int32_t item, double score); ]] end diff --git a/lua/telescope/pickers.lua b/lua/telescope/pickers.lua index 074fbceb95..32520506b1 100644 --- a/lua/telescope/pickers.lua +++ b/lua/telescope/pickers.lua @@ -1169,7 +1169,7 @@ function pickers.on_close_prompt(prompt_bufnr) if picker.cache_picker.limit_entries > 0 then -- edge case: starting in normal mode and not having run a search means having no manager instantiated if picker.manager then - picker.manager.linked_states:truncate(picker.cache_picker.limit_entries) + picker.manager:truncate(picker.cache_picker.limit_entries) else picker.manager = EntryManager:new(picker.max_results, picker.entry_adder, picker.stats) end diff --git a/lua/tests/automated/entry_manager_spec.lua b/lua/tests/automated/entry_manager_spec.lua index 849b335187..34fddf5894 100644 --- a/lua/tests/automated/entry_manager_spec.lua +++ b/lua/tests/automated/entry_manager_spec.lua @@ -103,28 +103,17 @@ describe("process_result", function() end) it("should not loop a bunch", function() - local info = {} - local manager = EntryManager:new(5, nil, info) + local manager = EntryManager:new(5, nil) manager:add_entry(nil, 4, "better result") manager:add_entry(nil, 3, "better result") manager:add_entry(nil, 2, "better result") - - -- Loops once to find 3 < 4 - -- Loops again to find 2 < 3 - eq(2, info.looped) end) it("should not loop a bunch, part 2", function() - local info = {} - local manager = EntryManager:new(5, nil, info) + local manager = EntryManager:new(5, nil) manager:add_entry(nil, 4, "better result") manager:add_entry(nil, 2, "better result") manager:add_entry(nil, 3, "better result") - - -- Loops again to find 2 < 4 - -- Loops once to find 3 > 2 - -- but less than 4 - eq(3, info.looped) end) it("should update worst score in all append case", function() @@ -133,7 +122,7 @@ describe("process_result", function() manager:add_entry(nil, 3, "result 3") manager:add_entry(nil, 4, "result 4") - eq(3, manager.worst_acceptable_score) + eq(3, manager:worst_acceptable_score()) end) it("should update worst score in all prepend case", function() @@ -151,6 +140,6 @@ describe("process_result", function() eq(3, called_count) eq("better result", manager:get_entry(1)) - eq(4, manager.worst_acceptable_score) + eq(4, manager:worst_acceptable_score()) end) end) diff --git a/scratch/benchmark.lua b/scratch/benchmark.lua index 7d1bc5cdd1..85f8c7d011 100644 --- a/scratch/benchmark.lua +++ b/scratch/benchmark.lua @@ -25,8 +25,7 @@ end local lines = lines_from "../telescope-fzf-native.nvim/files" -local c_manager = CEntryManager:new(10000) -local manager = EntryManager:new(10000) +local max = 10000 bench("lua vs c ffi", { warmup = 3, @@ -35,12 +34,14 @@ bench("lua vs c ffi", { { "c ffi", function() + local c_manager = CEntryManager:new(max) filter(c_manager, "fzf.c", lines) end, }, { "lua", function() + local manager = EntryManager:new(max) filter(manager, "fzf.c", lines) end, }, diff --git a/src/telescope.c b/src/telescope.c index 32c9289368..1ffaecca61 100644 --- a/src/telescope.c +++ b/src/telescope.c @@ -3,6 +3,8 @@ #include #include #include +#include +#include // ENTRYMANAGER THINGS static tele_node_t *create_node(int32_t item, double score) { @@ -13,7 +15,7 @@ static tele_node_t *create_node(int32_t item, double score) { return node; } -tele_linked_list_t *tele_list_create(size_t track_at) { +static tele_linked_list_t *tele_list_create(size_t track_at) { tele_linked_list_t *list = (tele_linked_list_t *)malloc(sizeof(tele_linked_list_t)); list->len = 0; @@ -24,7 +26,7 @@ tele_linked_list_t *tele_list_create(size_t track_at) { return list; } -void tele_list_free(tele_linked_list_t *list) { +static void tele_list_free(tele_linked_list_t *list) { if (list->head) { tele_node_t *curr = list->head; while (curr != NULL) { @@ -37,8 +39,8 @@ void tele_list_free(tele_linked_list_t *list) { free(list); } -tele_node_t *tele_list_append(tele_linked_list_t *list, int32_t item, - double score) { +static tele_node_t *tele_list_append(tele_linked_list_t *list, int32_t item, + double score) { ++list->len; tele_node_t *node = create_node(item, score); @@ -59,7 +61,8 @@ tele_node_t *tele_list_append(tele_linked_list_t *list, int32_t item, return node; } -void tele_list_prepend(tele_linked_list_t *list, int32_t item, double score) { +static void tele_list_prepend(tele_linked_list_t *list, int32_t item, + double score) { ++list->len; tele_node_t *node = create_node(item, score); @@ -79,8 +82,9 @@ void tele_list_prepend(tele_linked_list_t *list, int32_t item, double score) { } } -void tele_list_place_after(tele_linked_list_t *list, size_t index, - tele_node_t *node, int32_t item, double score) { +static void tele_list_place_after(tele_linked_list_t *list, size_t index, + tele_node_t *node, int32_t item, + double score) { ++list->len; tele_node_t *new_node = create_node(item, score); @@ -114,8 +118,9 @@ void tele_list_place_after(tele_linked_list_t *list, size_t index, } } -void tele_list_place_before(tele_linked_list_t *list, size_t index, - tele_node_t *node, int32_t item, double score) { +static void tele_list_place_before(tele_linked_list_t *list, size_t index, + tele_node_t *node, int32_t item, + double score) { ++list->len; tele_node_t *new_node = create_node(item, score); @@ -147,3 +152,70 @@ void tele_list_place_before(tele_linked_list_t *list, size_t index, } } } + +tele_manager_t *tele_manager_create(size_t max_results) { + tele_manager_t *manager = (tele_manager_t *)malloc(sizeof(tele_manager_t)); + manager->worst_acceptable_score = DBL_MAX; + manager->max_results = max_results; + manager->list = tele_list_create(max_results); + return manager; +} + +void tele_manager_free(tele_manager_t *manager) { + tele_list_free(manager->list); + free(manager); +} + +int32_t tele_manager_add(tele_manager_t *manager, int32_t item, double score) { + if (score >= manager->worst_acceptable_score) { + tele_list_append(manager->list, item, score); + return -1; + } + + if (manager->list->len == 0) { + tele_list_prepend(manager->list, item, score); + // set_entry(picker, 1, entry, score) + return 1; + } + + size_t index = 1; + for (tele_node_t *curr = manager->list->head; curr != NULL; + curr = curr->next) { + // TODO(conni2461): TIEBREAKER (we need ordinal lens) + // TODO(conni2461): double == double is not correct + if (curr->item.score > score) { + tele_list_place_before(manager->list, index, curr, item, score); + if (manager->list->_tracked_node) { + manager->worst_acceptable_score = + fmin(manager->worst_acceptable_score, + manager->list->_tracked_node->item.score); + } + // set_entry(picker, index, entry, score, true); + return index; + } + + if (index >= manager->max_results) { + tele_list_append(manager->list, item, score); + manager->worst_acceptable_score = + fmin(manager->worst_acceptable_score, score); + return -1; + } + + ++index; + } + + if (manager->list->len >= manager->max_results) { + manager->worst_acceptable_score = + fmin(manager->worst_acceptable_score, score); + } + + tele_list_place_after(manager->list, manager->list->len + 1, + manager->list->tail, item, score); + if (manager->list->_tracked_node) { + manager->worst_acceptable_score = + fmin(manager->worst_acceptable_score, + manager->list->_tracked_node->item.score); + } + // set_entry(picker, index, entry, score, true); + return manager->list->len; +} diff --git a/src/telescope.h b/src/telescope.h index 82a621d479..963119f6d1 100644 --- a/src/telescope.h +++ b/src/telescope.h @@ -1,7 +1,6 @@ #ifndef _TELESCOPE_H_ #define _TELESCOPE_H_ -#include #include #include @@ -25,16 +24,14 @@ typedef struct { size_t track_at; } tele_linked_list_t; -tele_linked_list_t *tele_list_create(size_t track_at); -void tele_list_free(tele_linked_list_t *list); - -tele_node_t *tele_list_append(tele_linked_list_t *list, int32_t item, - double score); -void tele_list_prepend(tele_linked_list_t *list, int32_t item, double score); - -void tele_list_place_after(tele_linked_list_t *list, size_t index, - tele_node_t *node, int32_t item, double score); -void tele_list_place_before(tele_linked_list_t *list, size_t index, - tele_node_t *node, int32_t item, double score); +typedef struct { + size_t max_results; + double worst_acceptable_score; + tele_linked_list_t *list; +} tele_manager_t; + +tele_manager_t *tele_manager_create(size_t max_results); +void tele_manager_free(tele_manager_t *manager); +int32_t tele_manager_add(tele_manager_t *manager, int32_t item, double score); #endif