mirror of
https://github.com/neovim/neovim.git
synced 2026-08-27 17:41:48 +00:00
Merge #39389 from ofseed/lsp-parse-perf
This commit is contained in:
@@ -177,6 +177,8 @@ PERFORMANCE
|
||||
Lua.
|
||||
• The table holding LSP data is now cleared using `table.clear`,
|
||||
thus reducing GC and memory reallocation during each data reset.
|
||||
• When parsing the received Content-Length messages,
|
||||
the RPC client will no longer allocate extra strings.
|
||||
|
||||
PLUGINS
|
||||
|
||||
|
||||
@@ -22,21 +22,22 @@ if has_strbuffer then
|
||||
return M
|
||||
end
|
||||
|
||||
--- @class vim._core.stringbuffer.ptr
|
||||
--- @field [integer] integer
|
||||
|
||||
--- @class vim._core.stringbuffer
|
||||
--- @field private ptr vim._core.stringbuffer.ptr
|
||||
--- @field private buf string[]
|
||||
--- @field package len integer absolute length of the `buf`
|
||||
--- @field package skip_ptr integer
|
||||
local StrBuffer = {}
|
||||
StrBuffer.__index = StrBuffer
|
||||
|
||||
--- @return string
|
||||
function StrBuffer:tostring()
|
||||
function StrBuffer:_normalize()
|
||||
if #self.buf > 1 then
|
||||
self.buf = { table.concat(self.buf) }
|
||||
end
|
||||
|
||||
-- assert(self.len == #(self.buf[1] or ''), 'len mismatch')
|
||||
|
||||
if self.skip_ptr > 0 then
|
||||
if self.buf[1] then
|
||||
self.buf[1] = self.buf[1]:sub(self.skip_ptr + 1)
|
||||
@@ -45,18 +46,21 @@ function StrBuffer:tostring()
|
||||
self.skip_ptr = 0
|
||||
end
|
||||
|
||||
-- assert(self.len == #(self.buf[1] or ''), 'len mismatch')
|
||||
return self
|
||||
end
|
||||
|
||||
return self.buf[1] or ''
|
||||
--- @return string
|
||||
function StrBuffer:tostring()
|
||||
return self:_normalize().buf[1] or ''
|
||||
end
|
||||
|
||||
StrBuffer.__tostring = StrBuffer.tostring
|
||||
|
||||
--- @private
|
||||
--- Efficiently peak at the first `n` characters of the buffer.
|
||||
--- Efficiently peek at the first `n` characters of the buffer.
|
||||
--- @param n integer
|
||||
--- @return string
|
||||
function StrBuffer:_peak(n)
|
||||
function StrBuffer:_peek(n)
|
||||
local skip, buf1 = self.skip_ptr, self.buf[1]
|
||||
if buf1 and (n + skip) < #buf1 then
|
||||
return buf1:sub(skip + 1, skip + n)
|
||||
@@ -81,7 +85,7 @@ end
|
||||
--- @return string
|
||||
function StrBuffer:get(n)
|
||||
n = n or self.len
|
||||
local r = self:_peak(n)
|
||||
local r = self:_peek(n)
|
||||
self:skip(n)
|
||||
return r
|
||||
end
|
||||
@@ -99,8 +103,27 @@ function StrBuffer:reset()
|
||||
return self
|
||||
end
|
||||
|
||||
--- Efficiently read the character at the 0-based index `k` in the buffer.
|
||||
--- @param k integer
|
||||
--- @return integer
|
||||
function StrBuffer:_index(k)
|
||||
return self:_normalize().buf[1]:byte(k + 1)
|
||||
end
|
||||
|
||||
--- @return vim._core.stringbuffer.ptr, integer
|
||||
function StrBuffer:ref()
|
||||
return self.ptr, self.len - self.skip_ptr
|
||||
end
|
||||
|
||||
function M.new()
|
||||
return setmetatable({}, StrBuffer):reset()
|
||||
local self = setmetatable({}, StrBuffer):reset()
|
||||
---@diagnostic disable-next-line: invisible
|
||||
self.ptr = setmetatable({}, {
|
||||
__index = function(_, k)
|
||||
return StrBuffer._index(self, k)
|
||||
end,
|
||||
})
|
||||
return self
|
||||
end
|
||||
|
||||
--- @param buf vim._core.stringbuffer
|
||||
|
||||
@@ -191,22 +191,30 @@ local default_dispatchers = {
|
||||
}
|
||||
|
||||
--- @async
|
||||
local function request_parser_loop()
|
||||
local buf = strbuffer.new()
|
||||
local function parse_content_length()
|
||||
local strbuf = strbuffer.new()
|
||||
while true do
|
||||
local msg = buf:tostring()
|
||||
local header_end = msg:find('\r\n\r\n', 1, true)
|
||||
if header_end then
|
||||
local header = buf:get(header_end + 1)
|
||||
buf:skip(2) -- skip past header boundary
|
||||
local content_length = get_content_length(header)
|
||||
while strbuffer.len(buf) < content_length do
|
||||
buf:put(coroutine.yield())
|
||||
local header_len ---@type integer?
|
||||
local ptr, len = strbuf:ref()
|
||||
for i = 0, len - 4 do
|
||||
-- Find the header boundary "\r\n\r\n"
|
||||
-- (compare bytes instead of string.find(), to avoid a string alloc).
|
||||
if ptr[i] == 13 and ptr[i + 1] == 10 and ptr[i + 2] == 13 and ptr[i + 3] == 10 then
|
||||
header_len = i + 2
|
||||
break
|
||||
end
|
||||
local body = buf:get(content_length)
|
||||
buf:put(coroutine.yield(body))
|
||||
end
|
||||
if header_len then
|
||||
local header = strbuf:get(header_len)
|
||||
strbuf:skip(2) -- skip past header boundary
|
||||
local content_length = get_content_length(header)
|
||||
while strbuffer.len(strbuf) < content_length do
|
||||
strbuf:put(coroutine.yield())
|
||||
end
|
||||
local body = strbuf:get(content_length)
|
||||
strbuf:put(coroutine.yield(body))
|
||||
else
|
||||
buf:put(coroutine.yield())
|
||||
strbuf:put(coroutine.yield())
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -218,7 +226,7 @@ end
|
||||
function M.create_read_loop(handle_body, on_exit, on_error)
|
||||
on_exit = on_exit or function() end
|
||||
on_error = on_error or function() end
|
||||
local co = coroutine.create(request_parser_loop)
|
||||
local co = coroutine.create(parse_content_length)
|
||||
coroutine.resume(co)
|
||||
return function(err, chunk)
|
||||
if err then
|
||||
|
||||
Reference in New Issue
Block a user