Skip to content

Commit 48233dc

Browse files
authored
Merge pull request #108 from gpittarelli/gjp/even-faster
Make parsing even faster, and use less memory
2 parents def8920 + 40090a7 commit 48233dc

File tree

1 file changed

+52
-44
lines changed

1 file changed

+52
-44
lines changed

lsp-receive.el

+52-44
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727
(headers '()) ;; alist of headers
2828
(body nil) ;; message body
2929
(reading-body nil) ;; If non-nil, reading body
30-
(leftovers "") ;; Leftover data from previous chunk; to be processed
30+
(body-length nil) ;; length of current message body
31+
(body-received 0) ;; amount of current message body currently stored in 'body'
32+
(leftovers nil) ;; Leftover data from previous chunk; to be processed
3133

3234
(queued-notifications nil)
3335
(queued-requests nil)
@@ -116,8 +118,8 @@ Else it is queued (unless DONT-QUEUE is non-nil)"
116118
message
117119
(or (car (alist-get code lsp--errors)) "Unknown error"))))
118120

119-
(defun lsp--parser-length-header (p)
120-
(string-to-number (cdr (assoc "Content-Length" (lsp--parser-headers p)))))
121+
(defun lsp--get-body-length (headers)
122+
(string-to-number (cdr (assoc "Content-Length" headers))))
121123

122124
(defun lsp--parse-header (s)
123125
"Parse string S as a LSP (KEY . VAL) header."
@@ -135,10 +137,13 @@ Else it is queued (unless DONT-QUEUE is non-nil)"
135137
(cons key val)))
136138

137139
(defun lsp--parser-reset (p)
138-
(setf (lsp--parser-leftovers p) ""
139-
(lsp--parser-headers p) '()
140-
(lsp--parser-body p) nil
141-
(lsp--parser-reading-body p) nil))
140+
(setf
141+
(lsp--parser-leftovers p) ""
142+
(lsp--parser-body-length p) nil
143+
(lsp--parser-body-received p) nil
144+
(lsp--parser-headers p) '()
145+
(lsp--parser-body p) nil
146+
(lsp--parser-reading-body p) nil))
142147

143148
(defun lsp--parser-on-message (p)
144149
"Called when the parser reads a complete message from the server."
@@ -159,50 +164,53 @@ Else it is queued (unless DONT-QUEUE is non-nil)"
159164
('request (lsp--on-request p json-data))))
160165
(lsp--parser-reset p))
161166

162-
(defun lsp--parser-read (p chunk-)
167+
(defun lsp--parser-read (p chunk)
163168
(cl-assert (lsp--parser-workspace p) nil "Parser workspace cannot be nil.")
164-
(let* ((leftovers (lsp--parser-leftovers p))
165-
(chunk (if (> (length leftovers) 0)
166-
(concat leftovers chunk-)
167-
chunk-)))
168-
169-
;; Read headers
170-
(when (not (lsp--parser-reading-body p))
171-
(let ((body-sep-pos (string-match-p "\r\n\r\n" chunk)))
169+
170+
(while (not (string-empty-p chunk))
171+
(if (not (lsp--parser-reading-body p))
172+
(let* ((full-chunk (concat (lsp--parser-leftovers p) chunk))
173+
(body-sep-pos (string-match-p "\r\n\r\n" chunk)))
172174
(if body-sep-pos
173175
;; We've got all the headers, handle them all at once:
174176
(let* ((header-raw (substring chunk 0 body-sep-pos))
175-
(content (substring chunk (+ body-sep-pos 4)))
176-
(headers
177-
(mapcar 'lsp--parse-header
178-
(split-string header-raw "\r\n"))))
179-
(setf (lsp--parser-headers p) headers)
180-
(setf (lsp--parser-reading-body p) t)
177+
(content (substring chunk (+ body-sep-pos 4)))
178+
(headers
179+
(mapcar 'lsp--parse-header
180+
(split-string header-raw "\r\n")))
181+
(body-length (lsp--get-body-length headers)))
182+
(setf
183+
(lsp--parser-headers p) headers
184+
(lsp--parser-reading-body p) t
185+
(lsp--parser-body-length p) body-length
186+
(lsp--parser-body-received p) 0
187+
(lsp--parser-body p) (make-string body-length ?\0)
188+
(lsp--parser-leftovers p) nil)
181189
(setq chunk content))
182190

183191
;; Haven't found the end of the headers yet, save everything
184-
;; for later:
185-
(setf (lsp--parser-leftovers p) chunk))))
186-
187-
;; Read body
188-
(when (lsp--parser-reading-body p)
189-
(let ((body-length (lsp--parser-length-header p)))
190-
(if (>= (length chunk) body-length)
191-
;; Have a full body (and maybe a bit of the next one)
192-
(let ((full-body (substring chunk 0 body-length))
193-
(bit-of-next-message (substring chunk body-length)))
194-
(setf (lsp--parser-body p) full-body)
195-
(when lsp-print-io
196-
(message "Output from language server: %s" full-body))
197-
(lsp--parser-on-message p)
198-
199-
(unless (string-empty-p bit-of-next-message)
200-
;; We've got a bit of the next message. Hopefully we
201-
;; don't do this _too_ much
202-
(lsp--parser-read p bit-of-next-message)))
203-
204-
;; else, still waiting for the rest of the current body
205-
(setf (lsp--parser-leftovers p) chunk))))))
192+
;; for when the next chunk arrives:
193+
(setf (lsp--parser-leftovers p) full-chunk)
194+
(setq chunk "")))
195+
196+
;; Read body
197+
(let* ((total-body-length (lsp--parser-body-length p))
198+
(received-body-length (lsp--parser-body-received p))
199+
(chunk-length (length chunk))
200+
(left-to-receive (- total-body-length received-body-length))
201+
(this-body (substring chunk 0 (min left-to-receive chunk-length)))
202+
(leftovers (substring chunk (length this-body))))
203+
204+
(store-substring (lsp--parser-body p) received-body-length this-body)
205+
(setf (lsp--parser-body-received p) (+ (lsp--parser-body-received p)
206+
(length this-body)))
207+
208+
(when (>= chunk-length left-to-receive)
209+
(when lsp-print-io
210+
(message "Output from language server: %s" (lsp--parser-body p)))
211+
(lsp--parser-on-message p))
212+
213+
(setq chunk leftovers)))))
206214

207215
(defun lsp--parser-make-filter (p ignore-regexps)
208216
#'(lambda (proc output)

0 commit comments

Comments
 (0)