ports.scm 35 KB

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