ports.scm 34 KB

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