Skip to content

Commit 567da83

Browse files
committed
Optimize rpc msg parse loop
1 parent b08e05d commit 567da83

File tree

1 file changed

+66
-52
lines changed

1 file changed

+66
-52
lines changed

lua/dap/rpc.lua

Lines changed: 66 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,79 @@
11
local utils = require('dap.utils')
22
local M = {}
33

4-
-- Copied from neovim rpc.lua
54
---@param header string
6-
local function parse_headers(header)
7-
local headers = {}
8-
for line in vim.gsplit(header, '\r\n', {plain = true}) do
9-
if line == '' then
10-
break
11-
end
12-
local key, value = line:match('^%s*(%S+)%s*:%s*(.+)%s*$')
13-
if key then
14-
key = key:lower():gsub('%-', '_')
15-
headers[key] = value
16-
else
17-
error(string.format("Invalid header line %q", line))
5+
---@return integer?
6+
local function get_content_length(header)
7+
for line in header:gmatch("(.-)\r\n") do
8+
local key, value = line:match('^%s*(%S+)%s*:%s*(%d+)%s*$')
9+
if key and key:lower() == "content-length" then
10+
return tonumber(value)
1811
end
1912
end
20-
headers.content_length = tonumber(headers.content_length)
21-
or error(string.format("Content-Length not found in headers. %q", header))
22-
return headers
2313
end
2414

2515

26-
-- Mostly copied from neovim rpc.lua
27-
local header_start_pattern = ("content"):gsub("%w", function(c) return "["..c..c:upper().."]" end)
28-
local function parse_chunk_loop()
29-
local buffer = ''
30-
while true do
31-
local start, finish = buffer:find('\r\n\r\n', 1, true)
32-
if start then
33-
local buffer_start = buffer:find(header_start_pattern)
34-
if not buffer_start then
35-
error(string.format(
36-
"Headers were expected but debug adapter sent: %s",
37-
buffer
38-
))
39-
end
40-
local headers = parse_headers(buffer:sub(buffer_start, start - 1))
41-
local content_length = headers.content_length
42-
local body_chunks = {buffer:sub(finish + 1)}
43-
local body_length = #body_chunks[1]
44-
while body_length < content_length do
16+
local parse_chunk_loop
17+
local has_strbuffer, strbuffer = pcall(require, "string.buffer")
18+
19+
if has_strbuffer then
20+
parse_chunk_loop = function()
21+
local buf = strbuffer.new()
22+
while true do
23+
local msg = buf:tostring()
24+
local header_end = msg:find('\r\n\r\n', 1, true)
25+
if header_end then
26+
local header = buf:get(header_end + 1)
27+
buf:skip(2) -- skip past header boundary
28+
local content_length = get_content_length(header)
29+
if not content_length then
30+
error("Content-Length not found in headers: " .. header)
31+
end
32+
while #buf < content_length do
33+
local chunk = coroutine.yield()
34+
buf:put(chunk)
35+
end
36+
local body = buf:get(content_length)
37+
coroutine.yield(body)
38+
else
4539
local chunk = coroutine.yield()
46-
or error("Expected more data for the body. The server may have died.")
47-
table.insert(body_chunks, chunk)
48-
body_length = body_length + #chunk
40+
buf:put(chunk)
4941
end
50-
local last_chunk = body_chunks[#body_chunks]
42+
end
43+
end
44+
else
45+
parse_chunk_loop = function()
46+
local buffer = ''
47+
while true do
48+
local header_end, body_start = buffer:find('\r\n\r\n', 1, true)
49+
if header_end then
50+
local header = buffer:sub(1, header_end + 1)
51+
local content_length = get_content_length(header)
52+
if not content_length then
53+
error("Content-Length not found in headers: " .. header)
54+
end
55+
local body_chunks = {buffer:sub(body_start + 1)}
56+
local body_length = #body_chunks[1]
57+
while body_length < content_length do
58+
local chunk = coroutine.yield()
59+
or error("Expected more data for the body. The server may have died.")
60+
table.insert(body_chunks, chunk)
61+
body_length = body_length + #chunk
62+
end
63+
local last_chunk = body_chunks[#body_chunks]
5164

52-
body_chunks[#body_chunks] = last_chunk:sub(1, content_length - body_length - 1)
53-
local rest = ''
54-
if body_length > content_length then
55-
rest = last_chunk:sub(content_length - body_length)
65+
body_chunks[#body_chunks] = last_chunk:sub(1, content_length - body_length - 1)
66+
local rest = ''
67+
if body_length > content_length then
68+
rest = last_chunk:sub(content_length - body_length)
69+
end
70+
local body = table.concat(body_chunks)
71+
buffer = rest .. (coroutine.yield(body)
72+
or error("Expected more data for the body. The server may have died."))
73+
else
74+
buffer = buffer .. (coroutine.yield()
75+
or error("Expected more data for the header. The server may have died."))
5676
end
57-
local body = table.concat(body_chunks)
58-
buffer = rest .. (coroutine.yield(headers, body)
59-
or error("Expected more data for the body. The server may have died."))
60-
else
61-
buffer = buffer .. (coroutine.yield()
62-
or error("Expected more data for the header. The server may have died."))
6377
end
6478
end
6579
end
@@ -80,10 +94,10 @@ function M.create_read_loop(handle_body, on_no_chunk)
8094
return
8195
end
8296
while true do
83-
local headers, body = parse_chunk(chunk)
84-
if headers then
97+
local body = parse_chunk(chunk)
98+
if body then
8599
handle_body(body)
86-
chunk = ''
100+
chunk = ""
87101
else
88102
break
89103
end

0 commit comments

Comments
 (0)