ports.scm 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046
  1. ;;; Ports
  2. ;;; Copyright (C) 2024 Igalia, S.L.
  3. ;;;
  4. ;;; Licensed under the Apache License, Version 2.0 (the "License");
  5. ;;; you may not use this file except in compliance with the License.
  6. ;;; You may obtain a copy of the License at
  7. ;;;
  8. ;;; http://www.apache.org/licenses/LICENSE-2.0
  9. ;;;
  10. ;;; Unless required by applicable law or agreed to in writing, software
  11. ;;; distributed under the License is distributed on an "AS IS" BASIS,
  12. ;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. ;;; See the License for the specific language governing permissions and
  14. ;;; limitations under the License.
  15. ;;; Commentary:
  16. ;;;
  17. ;;; Ports.
  18. ;;;
  19. ;;; Code:
  20. (library (hoot ports)
  21. (export %port-fold-case?
  22. %set-port-fold-case?!
  23. make-port
  24. port-filename
  25. port-line
  26. port-column
  27. port-encoding
  28. port-conversion-strategy
  29. set-port-encoding!
  30. set-port-conversion-strategy!
  31. (rename %port-read-buffering port-read-buffering)
  32. get-output-bytevector
  33. open-output-bytevector
  34. open-input-bytevector
  35. open-input-string
  36. open-output-string
  37. get-output-string
  38. ;; R7RS ports
  39. eof-object?
  40. eof-object
  41. port?
  42. input-port?
  43. output-port?
  44. binary-port?
  45. textual-port?
  46. port-open?
  47. input-port-open?
  48. output-port-open?
  49. close-input-port
  50. close-output-port
  51. close-port
  52. call-with-port
  53. seek
  54. flush-input-port
  55. flush-output-port
  56. u8-ready?
  57. peek-u8
  58. read-u8
  59. read-bytevector
  60. read-bytevector!
  61. char-ready?
  62. peek-char
  63. read-char
  64. read-string
  65. read-line
  66. write-u8
  67. write-bytevector
  68. write-char
  69. newline
  70. write-string
  71. standard-input-port
  72. standard-output-port
  73. standard-error-port
  74. current-input-port
  75. current-output-port
  76. current-error-port)
  77. (import (only (hoot primitives)
  78. %eof-object? guile:make-void-port
  79. %the-eof-object)
  80. (hoot apply)
  81. (hoot bitwise)
  82. (hoot boxes)
  83. (hoot bytevectors)
  84. (hoot char)
  85. (hoot cond-expand)
  86. (hoot eq)
  87. (hoot errors)
  88. (hoot inline-wasm)
  89. (hoot lists)
  90. (hoot match)
  91. (hoot not)
  92. (hoot numbers)
  93. (hoot pairs)
  94. (hoot parameters)
  95. (hoot procedures)
  96. (hoot strings)
  97. (hoot syntax)
  98. (hoot values)
  99. (hoot vectors))
  100. (define-syntax define-low-level-port-constructor-and-accessors
  101. (lambda (stx)
  102. (define (iota n)
  103. (let lp ((i 0))
  104. (if (= i n)
  105. '()
  106. (cons i (lp (1+ i))))))
  107. (syntax-case stx ()
  108. ((_ constructor pred (field wasm-type . accessors) ...)
  109. (with-syntax (((idx ...) (iota (length #'(field ...)))))
  110. #'(begin
  111. (define-low-level-port-constructor constructor
  112. (field wasm-type) ...)
  113. (define-low-level-port-predicate pred field ...)
  114. (define-low-level-port-accessors field idx . accessors)
  115. ...))))))
  116. (define-syntax-rule (define-low-level-port-constructor constructor
  117. (field wasm-type) ...)
  118. (define (constructor field ...)
  119. (cond-expand
  120. (guile-vm
  121. ;; Use a tagged vector to represent ports at expansion time,
  122. ;; since (hoot records) depends upon this module.
  123. (vector '<port> field ...))
  124. (hoot
  125. (%inline-wasm
  126. '(func (param field wasm-type) ... (result (ref eq))
  127. (struct.new $port (i32.const 0) (local.get field) ...))
  128. field ...)))))
  129. (define-syntax-rule (define-low-level-port-predicate pred field ...)
  130. (define (pred obj)
  131. (cond-expand
  132. (guile-vm
  133. (and (vector? obj)
  134. (eq? (vector-length obj) (1+ (length '(field ...))))
  135. (eq? (vector-ref obj 0) '<port>)))
  136. (hoot
  137. (%inline-wasm
  138. '(func (param $obj (ref eq))
  139. (result (ref eq))
  140. (if (ref eq)
  141. (ref.test $port (local.get $obj))
  142. (then (ref.i31 (i32.const 17)))
  143. (else (ref.i31 (i32.const 1)))))
  144. obj)))))
  145. (define-syntax define-low-level-port-accessors
  146. (syntax-rules ()
  147. ((_ field idx getter)
  148. (define-low-level-port-getter getter field idx))
  149. ((_ field idx getter setter)
  150. (begin
  151. (define-low-level-port-getter getter field idx)
  152. (define-low-level-port-setter setter field idx)))))
  153. (define-syntax-rule (define-low-level-port-getter getter field idx)
  154. (define (getter port)
  155. (cond-expand
  156. (guile-vm
  157. (vector-ref port (1+ idx)))
  158. (hoot
  159. (%inline-wasm
  160. '(func (param $port (ref $port)) (result (ref eq))
  161. (struct.get $port field (local.get $port)))
  162. port)))))
  163. (define-syntax-rule (define-low-level-port-setter setter field idx)
  164. (define (setter port val)
  165. (cond-expand
  166. (guile-vm
  167. (vector-set! port (1+ idx) val))
  168. (hoot
  169. (%inline-wasm
  170. '(func (param $port (ref $port)) (param $val (ref eq))
  171. (struct.set $port field (local.get $port) (local.get $val)))
  172. port val)))))
  173. (define-low-level-port-constructor-and-accessors
  174. %make-port
  175. port?
  176. ($open? (ref eq) %port-open? %set-port-open?!)
  177. ($read (ref eq) %port-read)
  178. ($write (ref eq) %port-write)
  179. ($input-waiting? (ref eq) %port-input-waiting?)
  180. ($seek (ref eq) %port-seek)
  181. ($close (ref eq) %port-close)
  182. ($truncate (ref eq) %port-truncate)
  183. ($repr (ref $string) %port-repr)
  184. ($filename (ref eq) port-filename %set-port-filename!)
  185. ($position (ref $mutable-pair) %port-position)
  186. ($read-buf (ref eq) %port-read-buffer %set-port-read-buffer!)
  187. ($write-buf (ref eq) %port-write-buffer %set-port-write-buffer!)
  188. ($read-buffering (ref eq) %port-read-buffering %set-port-read-buffering!)
  189. ($r/w-random-access? (ref eq) %port-r/w-random-access?)
  190. ($fold-case? (ref eq) %port-fold-case? %set-port-fold-case?!)
  191. ($private-data (ref eq) %port-private-data))
  192. ;; FIXME: kwargs
  193. ;; FIXME: suspendability
  194. (define (make-port read
  195. write
  196. input-waiting?
  197. seek
  198. close
  199. truncate
  200. repr
  201. file-name
  202. read-buf-size
  203. write-buf-size
  204. r/w-random-access?
  205. fold-case?
  206. private-data)
  207. (when file-name (check-type file-name string? 'make-port))
  208. (let ((read-buf (and read (vector (make-bytevector read-buf-size 0) 0 0 #f)))
  209. (write-buf (and write (vector (make-bytevector write-buf-size 0) 0 0))))
  210. (%make-port #t
  211. read
  212. write
  213. input-waiting?
  214. seek
  215. close
  216. truncate
  217. repr
  218. file-name
  219. (cons 0 0)
  220. read-buf
  221. write-buf
  222. read-buf-size
  223. r/w-random-access?
  224. fold-case?
  225. private-data)))
  226. (define (%set-port-buffer-cur! buf cur) (vector-set! buf 1 cur))
  227. (define (%set-port-buffer-end! buf end) (vector-set! buf 2 end))
  228. (define (%set-port-buffer-has-eof?! buf has-eof?) (vector-set! buf 3 has-eof?))
  229. (define (port-line port)
  230. (check-type port port? 'port-line)
  231. (car (%port-position port)))
  232. (define (port-column port)
  233. (check-type port port? 'port-column)
  234. (cdr (%port-position port)))
  235. ;; FIXME: These are stubs.
  236. (define (port-encoding port) "UTF-8")
  237. (define (port-conversion-strategy port) 'substitute)
  238. (define (set-port-encoding! port encoding) (values))
  239. (define (set-port-conversion-strategy! port strategy) (values))
  240. (define* (get-output-bytevector port #:optional (clear-buffer? #f))
  241. ;; FIXME: How to know it's a bytevector output port?
  242. (check-type port output-port? 'get-output-bytevector)
  243. (define accum (%port-private-data port))
  244. (flush-output-port port)
  245. (let ((flattened (bytevector-concatenate-reverse (box-ref accum))))
  246. (box-set! accum (if clear-buffer?
  247. '()
  248. (list flattened)))
  249. flattened))
  250. (define (open-output-bytevector)
  251. (define accum (make-box '()))
  252. (define pos #f)
  253. (define (appending?) (not pos))
  254. (define default-buffer-size 1024)
  255. (define (bv-write bv start count) ; write
  256. (unless (zero? count)
  257. (cond
  258. ((appending?)
  259. (box-set! accum
  260. (cons (bytevector-copy bv start (+ start count))
  261. (box-ref accum))))
  262. (else
  263. (let* ((dst (get-output-bytevector port))
  264. (to-copy (min count (- (bytevector-length dst) pos))))
  265. (bytevector-copy! dst pos bv start to-copy)
  266. (cond
  267. ((< to-copy count)
  268. (box-set!
  269. accum
  270. (list (bytevector-copy bv (+ start to-copy) (- count to-copy))
  271. dst))
  272. (set! pos #f))
  273. (else
  274. (set! pos (+ pos count))))))))
  275. count)
  276. (define (bv-seek offset whence) ; seek
  277. (define len (bytevector-length (get-output-bytevector port)))
  278. (define base (match whence ('start 0) ('cur (or pos len)) ('end len)))
  279. (define dst (+ base offset))
  280. (check-range dst 0 len 'seek)
  281. (set! pos (if (= pos dst) #f dst))
  282. dst)
  283. (define port
  284. (make-port #f ; read
  285. bv-write
  286. #f ; input-waiting?
  287. bv-seek
  288. #f ; close
  289. #f ; truncate
  290. "bytevector" ; repr
  291. #f ; filename
  292. #f ; read-buf-size
  293. default-buffer-size ; write-buf-size
  294. #t ; r/w-random-access
  295. #f ; fold-case?
  296. accum ; private data
  297. ))
  298. port)
  299. (define (open-input-bytevector src)
  300. (check-type src bytevector? 'open-input-bytevector)
  301. (define pos 0)
  302. (define default-buffer-size 1024)
  303. (define (bv-read dst start count)
  304. (let* ((to-copy (min count (- (bytevector-length src) pos)))
  305. (end (+ pos to-copy)))
  306. (bytevector-copy! dst start src pos end)
  307. (set! pos end)
  308. to-copy))
  309. (define (bv-seek offset whence) ; seek
  310. (define len (bytevector-length src))
  311. (define base (match whence ('start 0) ('cur pos) ('end len)))
  312. (define dst (+ base offset))
  313. (check-range dst 0 len 'seek)
  314. (set! pos dst)
  315. dst)
  316. ;; FIXME: Can we just provide `src` directly as the read buffer?
  317. (make-port bv-read
  318. #f ; write
  319. #f ; input-waiting?
  320. bv-seek
  321. #f ; close
  322. #f ; truncate
  323. "bytevector" ; repr
  324. #f ; filename
  325. default-buffer-size ; read-buf-size
  326. #f ; write-buf-size
  327. #t ; r/w-random-access
  328. #f ; fold-case?
  329. #f ; private data
  330. ))
  331. ;; FIXME: kwargs
  332. (define (make-soft-port repr %read-string %write-string input-waiting? close)
  333. (check-type repr string? 'make-port)
  334. (define (make-reader read-string)
  335. (define buffer #f)
  336. (define buffer-pos 0)
  337. (lambda (bv start count)
  338. (unless (and buffer (< buffer-pos (bytevector-length buffer)))
  339. (let* ((str (%read-string)))
  340. (set! buffer (string->utf8 str))
  341. (set! buffer-pos 0)))
  342. (let* ((to-copy (min count (- (bytevector-length buffer) buffer-pos)))
  343. (next-pos (+ buffer-pos to-copy)))
  344. (bytevector-copy! bv start buffer buffer-pos next-pos)
  345. (if (= (bytevector-length buffer) next-pos)
  346. (set! buffer #f)
  347. (set! buffer-pos next-pos))
  348. to-copy)))
  349. (define (make-writer write-string)
  350. (lambda (bv start count)
  351. ;; FIXME: If the writer is binary, that could split a codepoint in
  352. ;; two, resulting in badness. Shouldn't happen with textual
  353. ;; writers but it's worth noting.
  354. (%write-string (utf8->string bv start (+ start count)))
  355. count))
  356. (define default-buffer-size 1024)
  357. (make-port (and read-string (make-reader read-string))
  358. (and write-string (make-writer write-string))
  359. input-waiting?
  360. #f ; seek
  361. #f ; close
  362. #f ; truncate
  363. repr ; repr
  364. #f ; filename
  365. default-buffer-size ; read-buf-size
  366. default-buffer-size ; write-buf-size
  367. #f ; r/w-random-access
  368. #f ; fold-case?
  369. #f ; private data
  370. ))
  371. (define (open-input-string str)
  372. (open-input-bytevector (string->utf8 str)))
  373. (define (open-output-string) (open-output-bytevector))
  374. (define* (get-output-string p #:optional (clear-buffer? #f))
  375. (utf8->string (get-output-bytevector p clear-buffer?)))
  376. ;; R7RS ports
  377. (define (eof-object? x) (%eof-object? x))
  378. (define (eof-object)
  379. (define-syntax eof-object
  380. (lambda (stx) #`'#,%the-eof-object))
  381. (eof-object))
  382. (define (input-port? x) (and (port? x) (%port-read x) #t))
  383. (define (output-port? x) (and (port? x) (%port-write x) #t))
  384. (define (binary-port? x) (port? x))
  385. (define (textual-port? x) (port? x))
  386. (define (port-open? x)
  387. (check-type x port? 'port-open?)
  388. (%port-open? x))
  389. (define (input-port-open? x)
  390. (check-type x input-port? 'input-port-open?)
  391. (%port-open? x))
  392. (define (output-port-open? x)
  393. (check-type x output-port? 'output-port-open?)
  394. (%port-open? x))
  395. (define (close-input-port port)
  396. (check-type port input-port? 'close-input-port)
  397. ;; FIXME: Allow half-closing of socket-like ports.
  398. (close-port port))
  399. (define (close-output-port port)
  400. (check-type port output-port? 'close-output-port)
  401. ;; FIXME: Allow half-closing of socket-like ports.
  402. (close-port port))
  403. (define (close-port port)
  404. (check-type port port? 'close-port)
  405. (when (%port-open? port)
  406. (when (output-port? port) (flush-output-port port))
  407. (%set-port-open?! port #f)
  408. (match (%port-close port)
  409. (#f #f)
  410. (close (close))))
  411. (values))
  412. (define (call-with-port port proc)
  413. (check-type port port? 'call-with-port)
  414. (check-type proc procedure? 'call-with-port)
  415. (call-with-values (lambda () (proc port))
  416. (lambda vals
  417. (close-port port)
  418. (apply values vals))))
  419. (define (seek port offset whence)
  420. (check-type port port? 'seek)
  421. (check-type offset exact-integer? 'seek)
  422. (assert (case whence ((cur start end) #t) (else #f)) 'seek)
  423. (define (buffered-bytes buf)
  424. (define (port-buffer-cur buf) (vector-ref buf 1))
  425. (define (port-buffer-end buf) (vector-ref buf 2))
  426. (if (vector? buf)
  427. (- (port-buffer-end buf) (port-buffer-cur buf))
  428. 0))
  429. (cond
  430. ((%port-seek port)
  431. => (lambda (%seek)
  432. (cond
  433. ((and (eq? whence 'cur) (zero? offset))
  434. ;; Query current position, adjust for buffering without
  435. ;; flush.
  436. (let ((pos (%seek offset whence))
  437. (buf-in (buffered-bytes (%port-read-buffer port)))
  438. (buf-out (buffered-bytes (%port-write-buffer port))))
  439. (+ pos (- buf-in) buf-out)))
  440. ((not (%port-r/w-random-access? port))
  441. (raise (make-not-seekable-error port 'seek)))
  442. (else
  443. (%flush-input port)
  444. (%flush-output port)
  445. (let ((pos (%seek offset whence)))
  446. (when (input-port? port)
  447. (%set-port-buffer-has-eof?! (%port-read-buffer port) #f))
  448. pos)))))
  449. (else (raise (make-not-seekable-error port 'seek)))))
  450. (define (%write-bytes port bv start count)
  451. (let ((written ((%port-write port) bv start count)))
  452. (check-range written 0 count '%write-bytes)
  453. (when (< written count)
  454. (%write-bytes port bv (+ start written) (- count written)))))
  455. (define (%read-bytes port bv start count)
  456. (let ((read ((%port-read port) bv start count)))
  457. (check-range read 0 count '%read-bytes)
  458. read))
  459. (define (%flush-input port)
  460. ;; For buffered input+output ports that are random-access?, it's
  461. ;; likely that when switching from reading to writing that we will
  462. ;; have some bytes waiting to be read, and that the underlying
  463. ;; port-position is ahead. This function discards buffered input and
  464. ;; seeks back from before the buffered input.
  465. (match (%port-read-buffer port)
  466. (#f (values))
  467. ((and buf #(bv cur end has-eof?))
  468. (when (< cur end)
  469. (%set-port-buffer-cur! buf 0)
  470. (%set-port-buffer-end! buf 0)
  471. (seek port (- cur end) 'cur))
  472. (values))))
  473. (define* (flush-input-port #:optional (port (current-output-port)))
  474. ;; For buffered input+output ports that are random-access?, it's
  475. ;; likely that when switching from reading to writing that we will
  476. ;; have some bytes waiting to be read, and that the underlying
  477. ;; port-position is ahead. This function discards buffered input and
  478. ;; seeks back from before the buffered input.
  479. (check-type port input-port? 'flush-input-port)
  480. (%flush-input port))
  481. (define (%flush-output port)
  482. (match (%port-write-buffer port)
  483. (#f (values))
  484. ((and buf #(bv cur end))
  485. (when (< cur end)
  486. (%set-port-buffer-cur! buf 0)
  487. (%set-port-buffer-end! buf 0)
  488. (%write-bytes port bv cur (- end cur)))
  489. (values))))
  490. (define* (flush-output-port #:optional (port (current-output-port)))
  491. (check-type port output-port? 'flush-output-port)
  492. (%flush-output port))
  493. (define* (u8-ready? #:optional (port (current-input-port)))
  494. (check-type port port? 'u8-ready?)
  495. (match (%port-read-buffer port)
  496. (#f (raise (make-type-error port 'u8-ready? 'input-port?)))
  497. (#(bv cur end has-eof?)
  498. (or (< cur end)
  499. has-eof?
  500. (match (%port-input-waiting? port)
  501. (#f #t)
  502. (proc (proc)))))))
  503. (define (%fill-input port buf minimum-buffering)
  504. (match buf
  505. (#(bv cur end has-eof?)
  506. (let ((avail (- end cur)))
  507. (cond
  508. ((or has-eof?
  509. (<= minimum-buffering avail))
  510. (values buf avail))
  511. ((< (bytevector-length bv) minimum-buffering)
  512. (let* ((expanded (make-bytevector minimum-buffering 0))
  513. (buf (vector expanded 0 (- end cur) #f)))
  514. (when (< cur end)
  515. (bytevector-copy! expanded 0 bv cur end))
  516. (%set-port-read-buffer! port buf)
  517. (%fill-input port buf minimum-buffering)))
  518. (else
  519. (when (< 0 cur)
  520. (%set-port-buffer-cur! buf 0))
  521. (cond
  522. ((not (zero? avail))
  523. ;; If there is buffered input, we know a random access port
  524. ;; has no buffered output.
  525. (bytevector-copy! bv 0 bv cur end))
  526. ((%port-r/w-random-access? port)
  527. (%flush-output port)))
  528. (let lp ((end avail))
  529. (let* ((must-read (- minimum-buffering end))
  530. ;; precondition: read-buffering <= len(read-buffer)
  531. ;; precondition: minimum-buffering <= len(read-buffer)
  532. ;; precondition: end < minimum-buffering
  533. (count (- (max (%port-read-buffering port)
  534. minimum-buffering)
  535. end))
  536. (read (%read-bytes port bv end count))
  537. (end (+ end read)))
  538. (cond
  539. ((zero? read)
  540. (%set-port-buffer-end! buf end)
  541. (%set-port-buffer-has-eof?! buf #t)
  542. (values buf end))
  543. ((< end minimum-buffering)
  544. (lp end))
  545. (else
  546. (%set-port-buffer-end! buf end)
  547. (values buf end)))))))))))
  548. (define* (peek-u8 #:optional (port (current-input-port)))
  549. (check-type port port? 'peek-u8)
  550. (let lp ((buf (%port-read-buffer port)))
  551. (match buf
  552. (#f (raise (make-type-error port 'peek-u8 'input-port?)))
  553. (#(bv cur end has-eof?)
  554. (cond
  555. ((eq? cur end)
  556. (if has-eof?
  557. (eof-object)
  558. (call-with-values (lambda ()
  559. (%fill-input port buf 1))
  560. (lambda (buf avail)
  561. (if (zero? avail)
  562. (eof-object)
  563. (lp buf))))))
  564. (else
  565. (bytevector-u8-ref bv cur)))))))
  566. (define* (read-u8 #:optional (port (current-input-port)))
  567. (check-type port port? 'read-u8)
  568. (define (read-eof! buf)
  569. (%set-port-buffer-has-eof?! buf #f)
  570. (eof-object))
  571. (let lp ((buf (%port-read-buffer port)))
  572. (match buf
  573. (#f (raise (make-type-error port 'read-u8 'input-port?)))
  574. (#(bv cur end has-eof?)
  575. (cond
  576. ((eq? cur end)
  577. (if has-eof?
  578. (read-eof! buf)
  579. (call-with-values (lambda ()
  580. (%fill-input port buf 1))
  581. (lambda (buf avail)
  582. (if (zero? avail)
  583. (read-eof! buf)
  584. (lp buf))))))
  585. (else
  586. (%set-port-buffer-cur! buf (1+ cur))
  587. (bytevector-u8-ref bv cur)))))))
  588. (define* (read-bytevector k #:optional (port (current-input-port)))
  589. (check-range k 0 (1- (ash 1 29)) 'read-bytevector)
  590. (check-type port input-port? 'read-bytevector)
  591. (call-with-values (lambda ()
  592. (%fill-input port (%port-read-buffer port) (max k 1)))
  593. (lambda (buf avail)
  594. (cond
  595. ((zero? avail)
  596. (%set-port-buffer-has-eof?! buf #f)
  597. (eof-object))
  598. (else
  599. (match buf
  600. (#(src cur end has-eof?)
  601. (let* ((cur* (min (+ cur k) end))
  602. (bv (bytevector-copy src cur cur*)))
  603. (%set-port-buffer-cur! buf cur*)
  604. bv))))))))
  605. (define* (read-bytevector! dst #:optional (port (current-input-port))
  606. (start 0) (end (bytevector-length dst)))
  607. (check-type dst bytevector? 'read-bytevector!)
  608. (check-range start 0 (bytevector-length dst) 'read-bytevector!)
  609. (check-range end start (bytevector-length dst) 'read-bytevector!)
  610. (check-type port input-port? 'read-bytevector!)
  611. (let ((count (- start end)))
  612. (call-with-values (lambda ()
  613. (%fill-input port (%port-read-buffer port)
  614. (max count 1)))
  615. (lambda (buf avail)
  616. (cond
  617. ((zero? avail)
  618. (%set-port-buffer-has-eof?! buf #f)
  619. (eof-object))
  620. (else
  621. (match buf
  622. (#(src cur end has-eof?)
  623. (let* ((cur* (min (+ cur count) end))
  624. (count (- cur* cur)))
  625. (bytevector-copy! dst start src cur cur*)
  626. (%set-port-buffer-cur! buf cur*)
  627. count)))))))))
  628. (define* (char-ready? #:optional (port (current-input-port)))
  629. (u8-ready? port))
  630. (define* (peek-char #:optional (port (current-input-port)))
  631. (let ((a (peek-u8 port)))
  632. (cond
  633. ((eof-object? a) a)
  634. ((< a #b10000000) (integer->char a))
  635. (else
  636. ;; FIXME: This is a sloppy UTF-8 decoder. Need to think more
  637. ;; about this.
  638. (let ((len (cond ((< a #b11100000) 2)
  639. ((< a #b11110000) 3)
  640. (else 4))))
  641. (call-with-values (lambda ()
  642. (%fill-input port (%port-read-buffer port) len))
  643. (lambda (buf avail)
  644. (when (< avail len)
  645. (error "decoding error: partial utf-8 sequence"))
  646. (match buf
  647. (#(bv cur end has-eof?)
  648. (integer->char
  649. (cond-expand
  650. (guile-vm
  651. (raise (make-unimplemented-error 'peek-char)))
  652. (hoot
  653. (%inline-wasm
  654. '(func (param $bv (ref $bytevector))
  655. (param $cur i32)
  656. (param $end i32)
  657. (result i64)
  658. (i64.extend_i32_s
  659. (stringview_iter.next
  660. (string.as_iter
  661. (string.new_lossy_utf8_array
  662. (struct.get $bytevector $vals (local.get $bv))
  663. (local.get $cur)
  664. (local.get $end))))))
  665. bv cur (+ cur len))))))))))))))
  666. (define (scan-codepoint u8 line col)
  667. (cond
  668. ((< u8 #b10000000)
  669. (call-with-values
  670. (lambda ()
  671. (case (integer->char u8)
  672. ((#\alarm) (values line col))
  673. ((#\backspace) (values line (if (> col 0) (1- col) col)))
  674. ((#\newline) (values (1+ line) 0))
  675. ((#\return) (values line 0))
  676. ((#\tab) (values line (logand (+ col 8) (lognot 7))))
  677. (else (values line (1+ col)))))
  678. (lambda (line col)
  679. (values 1 line col))))
  680. ((< u8 #b11100000) (values 2 line (1+ col)))
  681. ((< u8 #b11110000) (values 3 line (1+ col)))
  682. (else (values 4 line (1+ col)))))
  683. (define* (read-char #:optional (port (current-input-port)))
  684. (define (decode-wtf8/1 a)
  685. (integer->char a))
  686. (define (decode-wtf8/2 a b)
  687. (unless (and (eq? #b110 (ash a -5))
  688. (eq? #b10 (ash b -6)))
  689. (error "decoding error: bad utf-8 sequence"))
  690. (let ((a (logand a #b11111))
  691. (b (logand b #b111111)))
  692. (integer->char (logior (ash a 6) b))))
  693. (define (decode-wtf8/3 a b c)
  694. (unless (and (eq? #b1110 (ash a -4))
  695. (eq? #b10 (ash b -6))
  696. (eq? #b10 (ash c -6)))
  697. (error "decoding error: bad utf-8 sequence"))
  698. (let ((a (logand a #b1111))
  699. (b (logand b #b111111))
  700. (c (logand c #b111111)))
  701. (integer->char (logior (ash a 12) (ash b 6) c))))
  702. (define (decode-wtf8/4 a b c d)
  703. (unless (and (eq? #b11110 (ash a -3))
  704. (eq? #b10 (ash b -6))
  705. (eq? #b10 (ash c -6))
  706. (eq? #b10 (ash d -6)))
  707. (error "decoding error: bad utf-8 sequence"))
  708. (let ((a (logand a #b111))
  709. (b (logand b #b111111))
  710. (c (logand c #b111111))
  711. (d (logand d #b111111)))
  712. (integer->char (logior (ash a 18) (ash b 12) (ash c 6) d))))
  713. (match (peek-u8 port)
  714. ((? eof-object? a) a)
  715. (u8
  716. (match (%port-position port)
  717. ((and pos (line . col))
  718. (call-with-values (lambda ()
  719. (scan-codepoint u8 line col))
  720. (lambda (len line col)
  721. (call-with-values
  722. (lambda ()
  723. (if (eq? len 1)
  724. (values (%port-read-buffer port) 1)
  725. (%fill-input port (%port-read-buffer port) len)))
  726. (lambda (buf avail)
  727. (when (< avail len)
  728. (error "decoding error: partial utf-8 sequence"))
  729. (match buf
  730. (#(bv cur end has-eof?)
  731. (define char
  732. (match len
  733. ('1 (decode-wtf8/1 u8))
  734. ('2 (decode-wtf8/2 u8 (bytevector-u8-ref bv (+ cur 1))))
  735. ('3 (decode-wtf8/3 u8 (bytevector-u8-ref bv (+ cur 1))
  736. (bytevector-u8-ref bv (+ cur 2))))
  737. (_ (decode-wtf8/4 u8 (bytevector-u8-ref bv (+ cur 1))
  738. (bytevector-u8-ref bv (+ cur 2))
  739. (bytevector-u8-ref bv (+ cur 3))))))
  740. (%set-port-buffer-cur! buf (+ cur len))
  741. (set-car! pos line)
  742. (set-cdr! pos col)
  743. char)))))))))))
  744. (define* (read-string k #:optional (port (current-input-port)))
  745. (check-type port input-port? 'read-string)
  746. (cond
  747. ;; Call peek-char to ensure we're at the start of some UTF-8.
  748. ((eof-object? (peek-char port)) (eof-object))
  749. (else
  750. (match (%port-read-buffer port)
  751. ((and buf #(bv cur end has-eof?))
  752. (match (%port-position port)
  753. ((and pos (line . col))
  754. (define (take-string count cur* line col)
  755. (define str (utf8->string bv cur cur*))
  756. (%set-port-buffer-cur! buf cur*)
  757. (set-car! pos line)
  758. (set-cdr! pos col)
  759. (let ((remaining (- k count)))
  760. (if (zero? remaining)
  761. str
  762. (match (read-string remaining port)
  763. ((? eof-object?) str)
  764. (tail (string-append str tail))))))
  765. ;; Count codepoints in buffer.
  766. (let count-codepoints ((count 0) (cur cur) (line line) (col col))
  767. (if (and (< cur end) (< count k))
  768. (call-with-values
  769. (lambda ()
  770. (scan-codepoint (bytevector-u8-ref bv cur) line col))
  771. (lambda (len line* col*)
  772. (if (<= (+ cur len) end)
  773. (count-codepoints (1+ count) (+ cur len) line* col*)
  774. (take-string count cur line col))))
  775. (take-string count cur line col))))))))))
  776. (define* (read-line #:optional (port (current-input-port)))
  777. (check-type port input-port? 'read-line)
  778. (define bytes '())
  779. (define (advance-column bv col)
  780. (define len (bytevector-length bv))
  781. (let lp ((idx 0) (col col))
  782. (cond
  783. ((< idx len)
  784. (call-with-values
  785. (lambda ()
  786. (scan-codepoint (bytevector-u8-ref bv idx) 0 col))
  787. (lambda (len line col)
  788. (lp (+ idx len) col))))
  789. (else col))))
  790. (define (finish newline?)
  791. (let* ((bv (bytevector-concatenate-reverse bytes))
  792. (str (utf8->string bv)))
  793. (match (%port-position port)
  794. ((and pos (line . col))
  795. (cond
  796. (newline?
  797. (set-car! pos (1+ line))
  798. (set-cdr! pos 0))
  799. (else
  800. (set-cdr! pos (advance-column bv col))))))
  801. str))
  802. (let read-some ((buf (%port-read-buffer port)))
  803. (match buf
  804. (#(bv cur end has-eof?)
  805. (define (accumulate-bytes! end)
  806. (set! bytes (cons (bytevector-copy bv cur end) bytes)))
  807. (let scan-for-newline ((pos cur))
  808. (cond
  809. ((< pos end)
  810. (let ((u8 (bytevector-u8-ref bv pos)))
  811. (cond
  812. ((or (eq? u8 (char->integer #\newline))
  813. (eq? u8 (char->integer #\return)))
  814. (accumulate-bytes! pos)
  815. (%set-port-buffer-cur! buf (1+ pos))
  816. (when (and (eq? u8 (char->integer #\return))
  817. (eq? (peek-u8 port) (char->integer #\newline)))
  818. (read-u8 port))
  819. (finish #t))
  820. (else
  821. (scan-for-newline (1+ pos))))))
  822. ((< cur pos)
  823. (accumulate-bytes! pos)
  824. (%set-port-buffer-cur! buf pos)
  825. (read-some (%fill-input port buf 1)))
  826. ((not has-eof?)
  827. (read-some (%fill-input port buf 1)))
  828. ((null? bytes)
  829. (%set-port-buffer-has-eof?! buf #f)
  830. (eof-object))
  831. (else
  832. (finish #f))))))))
  833. (define* (write-u8 u8 #:optional (port (current-output-port)))
  834. (check-type port port? 'write-u8)
  835. (match (%port-write-buffer port)
  836. (#f (raise (make-type-error port 'write-u8 'output-port?)))
  837. ((and buf #(dst cur end))
  838. (when (and (eq? cur end) (%port-r/w-random-access? port))
  839. (%flush-input port))
  840. (cond
  841. ((= end (bytevector-length dst))
  842. ;; Multiple threads racing; race to flush, then retry.
  843. (flush-output-port port)
  844. (write-u8 u8 port))
  845. (else
  846. (bytevector-u8-set! dst end u8)
  847. (let ((end (1+ end)))
  848. (%set-port-buffer-end! buf end)
  849. (when (= end (bytevector-length dst))
  850. (flush-output-port port))))))))
  851. (define* (write-bytevector bv #:optional (port (current-output-port))
  852. (start 0) (end (bytevector-length bv)))
  853. (check-type port port? 'write-u8)
  854. (let ((count (- end start)))
  855. (match (%port-write-buffer port)
  856. (#f (raise (make-type-error port 'write-bytevector 'output-port?)))
  857. ((and buf #(dst cur end))
  858. (when (and (eq? cur end) (%port-r/w-random-access? port))
  859. (%flush-input port))
  860. (let ((size (bytevector-length dst))
  861. (buffered (- end cur)))
  862. (cond
  863. ((<= (+ end count) size)
  864. ;; Bytes fit in buffer: copy directly.
  865. (bytevector-copy! dst end bv start (+ start count))
  866. (let ((end (+ end count)))
  867. (%set-port-buffer-end! buf end)
  868. (when (= end size)
  869. (flush-output-port port))))
  870. ((< count size)
  871. ;; Bytes fit in buffer, but we have to flush output first.
  872. (flush-output-port port)
  873. (bytevector-copy! dst 0 bv start (+ start count))
  874. (%set-port-buffer-cur! buf 0)
  875. (%set-port-buffer-end! buf count)
  876. (when (= count size)
  877. (flush-output-port port)))
  878. (else
  879. ;; Otherwise flush any buffered output, then make an
  880. ;; unbuffered write.
  881. (unless (zero? buffered) (flush-output-port port))
  882. (%write-bytes port bv start count))))))))
  883. (define* (write-char x #:optional (port (current-output-port)))
  884. ;; FIXME: update port position.
  885. (define (low-six i) (logand i #b111111))
  886. (let ((i (char->integer x)))
  887. (cond
  888. ((<= i #x7f)
  889. (write-u8 i port))
  890. ((<= i #x7ff)
  891. (write-bytevector
  892. (bytevector (logior #b11000000 (ash i -6))
  893. (logior #b10000000 (low-six i)))
  894. port))
  895. ((<= i #xffff)
  896. (write-bytevector
  897. (bytevector (logior #b11100000 (ash i -12))
  898. (logior #b10000000 (low-six (ash i -6)))
  899. (logior #b10000000 (low-six i)))
  900. port))
  901. (else
  902. (write-bytevector
  903. (bytevector (logior #b11110000 (ash i -18))
  904. (logior #b10000000 (low-six (ash i -12)))
  905. (logior #b10000000 (low-six (ash i -6)))
  906. (logior #b10000000 (low-six i)))
  907. port)))))
  908. (define* (newline #:optional (port (current-output-port)))
  909. (write-char #\newline port))
  910. (define* (write-string str #:optional (port (current-output-port)))
  911. ;; FIXME: Could avoid the double-copy and encode directly to buffer.
  912. (write-bytevector (string->utf8 str) port))
  913. (define (standard-input-port)
  914. (make-soft-port "stdin"
  915. (lambda ()
  916. (cond-expand
  917. (guile-vm
  918. (make-unimplemented-error 'standard-input-port))
  919. (hoot
  920. (%inline-wasm
  921. '(func (result (ref eq))
  922. (struct.new $string
  923. (i32.const 0)
  924. (call $read-stdin)))))))
  925. #f #f #f))
  926. (define (standard-output-port)
  927. (make-soft-port "stdout"
  928. #f
  929. (lambda (str)
  930. (cond-expand
  931. (guile-vm
  932. (make-unimplemented-error 'standard-output-port))
  933. (hoot
  934. (%inline-wasm
  935. '(func (param $str (ref string))
  936. (call $write-stdout (local.get $str)))
  937. str))))
  938. #f #f))
  939. (define (standard-error-port)
  940. (make-soft-port "stderr"
  941. #f
  942. (lambda (str)
  943. (cond-expand
  944. (guile-vm
  945. (make-unimplemented-error 'standard-error-port))
  946. (hoot
  947. (%inline-wasm
  948. '(func (param $str (ref string))
  949. (call $write-stderr (local.get $str)))
  950. str))))
  951. #f #f))
  952. (cond-expand
  953. (guile-vm
  954. (define current-input-port
  955. (make-parameter (guile:make-void-port "r")))
  956. (define current-output-port
  957. (make-parameter (guile:make-void-port "w")))
  958. (define current-error-port
  959. (make-parameter (guile:make-void-port "w"))))
  960. (hoot-main
  961. (define current-input-port
  962. (make-parameter (standard-input-port)
  963. (lambda (val)
  964. (check-type val input-port? 'current-input-port)
  965. val)))
  966. (define current-output-port
  967. (make-parameter (standard-output-port)
  968. (lambda (val)
  969. (check-type val output-port? 'current-output-port)
  970. val)))
  971. (define current-error-port
  972. (make-parameter (standard-error-port)
  973. (lambda (val)
  974. (check-type val output-port? 'current-error-port)
  975. val)))
  976. (%inline-wasm
  977. '(func (param $current-input-port (ref eq))
  978. (param $current-output-port (ref eq))
  979. (param $current-error-port (ref eq))
  980. (global.set $current-input-port (local.get $current-input-port))
  981. (global.set $current-output-port (local.get $current-output-port))
  982. (global.set $current-error-port (local.get $current-error-port)))
  983. current-input-port
  984. current-output-port
  985. current-error-port))
  986. (hoot-aux
  987. (define current-input-port
  988. (%inline-wasm
  989. '(func (result (ref eq)) (global.get $current-input-port))))
  990. (define current-output-port
  991. (%inline-wasm
  992. '(func (result (ref eq)) (global.get $current-output-port))))
  993. (define current-error-port
  994. (%inline-wasm
  995. '(func (result (ref eq)) (global.get $current-error-port)))))))