ikarus/scheme/ikarus.io.ss

1786 lines
62 KiB
Scheme

;;; Ikarus Scheme -- A compiler for R6RS Scheme.
;;; Copyright (C) 2006,2007 Abdulaziz Ghuloum
;;;
;;; This program is free software: you can redistribute it and/or modify
;;; it under the terms of the GNU General Public License version 3 as
;;; published by the Free Software Foundation.
;;;
;;; This program is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;;; General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with this program. If not, see <http://www.gnu.org/licenses/>.
#;
(library (ikarus system $io tmp)
(export $make-port $port-tag $port-id $port-cookie
port? $port-transcoder
$port-index $port-size $port-buffer
$port-get-position $port-set-position! $port-close
$port-read! $port-write! $set-port-index! $set-port-size!
$port-attrs $set-port-attrs!)
(import (except (ikarus) port?))
(define-struct $port
(attrs index size buffer transcoder
id read! write! get-position set-position! close cookie))
(define port? $port?)
(define $set-port-index! set-$port-index!)
(define $set-port-size! set-$port-size!)
(define $set-port-attrs! set-$port-attrs!)
(define $make-port make-$port)
(define ($port-tag x) (if ($port? x) ($port-attrs x) 0)))
(library (io-spec)
(export
port? input-port? output-port? textual-port? binary-port?
open-file-input-port open-input-file
call-with-input-file with-input-from-file
standard-input-port current-input-port
open-bytevector-input-port
open-string-input-port
make-custom-binary-input-port
make-custom-binary-output-port
make-custom-textual-input-port
make-custom-textual-output-port
transcoded-port port-transcoder
close-port close-input-port close-output-port
port-eof?
get-char lookahead-char read-char peek-char
get-string-n get-string-n! get-string-all get-line
get-u8 lookahead-u8
get-bytevector-n get-bytevector-n!
get-bytevector-some get-bytevector-all
;port-has-port-position? port-position
;port-has-set-port-position!? set-port-position!
call-with-port
flush-output-port
put-u8
put-char write-char
put-string
open-bytevector-output-port
call-with-bytevector-output-port
open-string-output-port
call-with-string-output-port
standard-output-port standard-error-port
current-output-port current-error-port
open-file-output-port open-output-file
console-output-port
console-error-port
console-input-port
newline
input-port-name
output-port-name
port-mode set-port-mode!
reset-input-port!
port-id
)
(import
(ikarus system $io)
(except (ikarus)
port? input-port? output-port? textual-port? binary-port?
open-file-input-port open-input-file
call-with-input-file with-input-from-file
standard-input-port current-input-port
open-bytevector-input-port
open-string-input-port
make-custom-binary-input-port
make-custom-binary-output-port
make-custom-textual-input-port
make-custom-textual-output-port
transcoded-port port-transcoder
close-port close-input-port close-output-port
port-eof?
get-char lookahead-char read-char peek-char
get-string-n get-string-n! get-string-all get-line
get-u8 lookahead-u8
get-bytevector-n get-bytevector-n!
get-bytevector-some get-bytevector-all
;port-has-port-position? port-position
;port-has-set-port-position!? set-port-position!
call-with-port
flush-output-port
put-u8
put-char write-char
put-string
open-bytevector-output-port
call-with-bytevector-output-port
open-string-output-port
call-with-string-output-port
standard-output-port standard-error-port
current-output-port current-error-port
open-file-output-port open-output-file
console-output-port
console-input-port
console-error-port
newline
input-port-name
output-port-name
port-mode set-port-mode!
reset-input-port!
port-id
))
(module UNSAFE (fx< fx<= fx> fx>= fx= fx+ fx-
fxior fxand fxsra fxsll
integer->char char->integer
string-ref string-set! string-length
bytevector-u8-ref bytevector-u8-set!)
(import
(rename (ikarus system $strings)
($string-length string-length)
($string-ref string-ref)
($string-set! string-set!))
(rename (ikarus system $chars)
($char->fixnum char->integer)
($fixnum->char integer->char))
(rename (ikarus system $bytevectors)
($bytevector-set! bytevector-u8-set!)
($bytevector-u8-ref bytevector-u8-ref))
(rename (ikarus system $fx)
($fxsra fxsra)
($fxsll fxsll)
($fxlogor fxior)
($fxlogand fxand)
($fx+ fx+)
($fx- fx-)
($fx< fx<)
($fx> fx>)
($fx>= fx>=)
($fx<= fx<=)
($fx= fx=))))
(define (port? x)
(import (only (ikarus) port?))
(port? x))
(define-syntax define-rrr
(syntax-rules ()
[(_ name)
(define (name . args)
(apply error 'name "not implemented" args))]))
(define-syntax u8?
(let ()
(import (ikarus system $fx))
(syntax-rules ()
[(_ x)
($fxzero? ($fxlogand x -256))])))
;(define (u8? x) (and (fixnum? x) (fx>= x 0) (fx< x 256)))
(define (textual-port? x)
(fx= (fxand ($port-tag x) textual-port-tag) textual-port-tag))
(define (binary-port? x)
(fx= (fxand ($port-tag x) binary-port-tag) binary-port-tag))
(define (output-port? x)
(fx= (fxand ($port-tag x) output-port-tag) output-port-tag))
(define (input-port? x)
(fx= (fxand ($port-tag x) input-port-tag) input-port-tag))
;;; everything above this line will turn into primitive
;;; ----------------------------------------------------------
(define input-port-tag #b00000000000001)
(define output-port-tag #b00000000000010)
(define textual-port-tag #b00000000000100)
(define binary-port-tag #b00000000001000)
(define fast-char-text-tag #b00000000010000)
(define fast-u7-text-tag #b00000000100000)
(define fast-u8-text-tag #b00000001100000)
(define r6rs-mode-tag #b01000000000000)
(define closed-port-tag #b10000000000000)
(define port-type-mask #b00000000001111)
(define binary-input-port-bits #b00000000001001)
(define binary-output-port-bits #b00000000001010)
(define textual-input-port-bits #b00000000000101)
(define textual-output-port-bits #b00000000000110)
(define fast-get-byte-tag #b00000000001001)
(define fast-get-char-tag #b00000000010101)
(define fast-get-utf8-tag #b00000000100101)
(define fast-get-latin-tag #b00000001100101)
(define fast-put-byte-tag #b00000000001010)
(define fast-put-char-tag #b00000000010110)
(define fast-put-utf8-tag #b00000000100110)
(define fast-put-latin-tag #b00000001100110)
(define fast-attrs-mask #xFFF)
(define ($port-fast-attrs x) (fxand ($port-tag x) fast-attrs-mask))
(define (input-port-name p)
(if (input-port? p)
($port-id p)
(error 'input-port-name "not an input port" p)))
(define (output-port-name p)
(if (output-port? p)
($port-id p)
(error 'output-port-name "not an output port" p)))
(define (port-id p)
(if (port? p)
($port-id p)
(error 'port-id "not a port" p)))
(define guarded-port
(let ([G (make-guardian)])
(define (clean-up)
(cond
[(G) =>
(lambda (p)
(close-port p)
(clean-up))]))
(lambda (p)
(clean-up)
(when (fixnum? ($port-cookie p))
(G p))
p)))
(define ($make-custom-binary-port attrs init-size id
read! write! get-position set-position! close buffer-size)
(let ([bv (make-bytevector buffer-size)])
($make-port attrs 0 init-size bv #f id read! write! get-position
set-position! close #f)))
(define ($make-custom-textual-port attrs init-size id
read! write! get-position set-position! close buffer-size)
(let ([bv (make-string buffer-size)])
($make-port attrs 0 init-size bv #t id read! write! get-position
set-position! close #f)))
(define (make-custom-binary-input-port id
read! get-position set-position! close)
;;; FIXME: get-position and set-position! are ignored for now
(define who 'make-custom-binary-input-port)
(unless (string? id)
(error who "id is not a string" id))
(unless (procedure? read!)
(error who "read! is not a procedure" read!))
(unless (or (procedure? close) (not close))
(error who "close should be either a procedure or #f" close))
($make-custom-binary-port
binary-input-port-bits
0
id read! #f get-position
set-position! close 256))
(define (make-custom-binary-output-port id
write! get-position set-position! close)
;;; FIXME: get-position and set-position! are ignored for now
(define who 'make-custom-binary-output-port)
(unless (string? id)
(error who "id is not a string" id))
(unless (procedure? write!)
(error who "write! is not a procedure" write!))
(unless (or (procedure? close) (not close))
(error who "close should be either a procedure or #f" close))
($make-custom-binary-port
binary-output-port-bits
256
id #f write! get-position
set-position! close 256))
(define (make-custom-textual-input-port id
read! get-position set-position! close)
;;; FIXME: get-position and set-position! are ignored for now
(define who 'make-custom-textual-input-port)
(unless (string? id)
(error who "id is not a string" id))
(unless (procedure? read!)
(error who "read! is not a procedure" read!))
(unless (or (procedure? close) (not close))
(error who "close should be either a procedure or #f" close))
($make-custom-textual-port
textual-input-port-bits
0
id read! #f get-position
set-position! close 256))
(define (make-custom-textual-output-port id
write! get-position set-position! close)
;;; FIXME: get-position and set-position! are ignored for now
(define who 'make-custom-textual-output-port)
(unless (string? id)
(error who "id is not a string" id))
(unless (procedure? write!)
(error who "write! is not a procedure" write!))
(unless (or (procedure? close) (not close))
(error who "close should be either a procedure or #f" close))
($make-custom-textual-port
textual-output-port-bits
256
id #f write! get-position
set-position! close 256))
(define (input-transcoder-attrs x)
(cond
[(not x) ;;; binary input port
binary-input-port-bits]
[(and (eq? 'latin-1-codec (transcoder-codec x))
(eq? 'none (transcoder-eol-style x)))
(fxior textual-input-port-bits fast-u8-text-tag)]
[else textual-input-port-bits]))
(define (output-transcoder-attrs x)
(cond
[(not x) ;;; binary input port
binary-output-port-bits]
[(and (eq? 'latin-1-codec (transcoder-codec x))
(eq? 'none (transcoder-eol-style x)))
(fxior textual-output-port-bits fast-u8-text-tag)]
[(and (eq? 'utf-8-codec (transcoder-codec x))
(eq? 'none (transcoder-eol-style x)))
(fxior textual-output-port-bits fast-u7-text-tag)]
[else textual-output-port-bits]))
(define open-bytevector-input-port
(case-lambda
[(bv) (open-bytevector-input-port bv #f)]
[(bv maybe-transcoder)
(unless (bytevector? bv)
(error 'open-bytevector-input-port
"not a bytevector" bv))
(when (and maybe-transcoder
(not (transcoder? maybe-transcoder)))
(error 'open-bytevector-input-port
"not a transcoder" maybe-transcoder))
($make-port
(input-transcoder-attrs maybe-transcoder)
0 (bytevector-length bv) bv
maybe-transcoder
"*bytevector-input-port*"
(lambda (bv i c) 0) ;;; read!
#f ;;; write!
#f ;;; FIXME: get-position
#f ;;; FIXME: set-position!
#f ;;; close
#f)]))
(define open-bytevector-output-port
(case-lambda
[() (open-bytevector-output-port #f)]
[(transcoder)
(define who 'open-bytevector-output-port)
(unless (or (not transcoder) (transcoder? transcoder))
(error who "invalid transcoder value" transcoder))
(let ([buf* '()] [buffer-size 256])
(let ([p
($make-port
(output-transcoder-attrs transcoder)
0 buffer-size (make-bytevector buffer-size)
transcoder
"*bytevector-output-port*"
#f
(lambda (bv i c)
(unless (= c 0)
(let ([x (make-bytevector c)])
(bytevector-copy! bv i x 0 c)
(set! buf* (cons x buf*))))
c)
#f ;;; FIXME: get-position
#f ;;; FIXME: set-position!
#f
#f)])
(values
p
(lambda ()
(define (append-bv-buf* ls)
(let f ([ls ls] [i 0])
(cond
[(null? ls)
(values (make-bytevector i) 0)]
[else
(let* ([a (car ls)]
[n (bytevector-length a)])
(let-values ([(bv i) (f (cdr ls) (fx+ i n))])
(bytevector-copy! a 0 bv i n)
(values bv (fx+ i n))))])))
(unless ($port-closed? p)
(flush-output-port p))
(let-values ([(bv len) (append-bv-buf* buf*)])
(set! buf* '())
bv)))))]))
(define call-with-bytevector-output-port
(case-lambda
[(proc) (call-with-bytevector-output-port proc #f)]
[(proc transcoder)
(define who 'call-with-bytevector-output-port)
(unless (procedure? proc)
(error who "not a procedure" proc))
(unless (or (not transcoder) (transcoder? transcoder))
(error who "invalid transcoder argument" transcoder))
(let-values ([(p extract)
(open-bytevector-output-port transcoder)])
(proc p)
(extract))]))
(define (call-with-string-output-port proc)
(define who 'call-with-string-output-port)
(unless (procedure? proc)
(error who "not a procedure" proc))
(let-values ([(p extract) (open-string-output-port)])
(proc p)
(extract)))
(define (open-string-output-port)
(define who 'open-string-output-port)
(let ([buf* '()] [buffer-size 256])
(let ([p
($make-port
(fxior textual-output-port-bits fast-char-text-tag)
0 buffer-size (make-string buffer-size)
#t ;;; transcoder
"*string-output-port*"
#f
(lambda (str i c)
(unless (= c 0)
(let ([x (make-string c)])
(string-copy! str i x 0 c)
(set! buf* (cons x buf*))))
c)
#f ;;; FIXME: get-position
#f ;;; FIXME: set-position!
#f
#f)])
(values
p
(lambda ()
(define (append-str-buf* ls)
(let f ([ls ls] [i 0])
(cond
[(null? ls)
(values (make-string i) 0)]
[else
(let* ([a (car ls)]
[n (string-length a)])
(let-values ([(bv i) (f (cdr ls) (fx+ i n))])
(string-copy! a 0 bv i n)
(values bv (fx+ i n))))])))
(unless ($port-closed? p)
(flush-output-port p))
(let-values ([(bv len) (append-str-buf* buf*)])
(set! buf* '())
bv))))))
(define (open-string-input-port str)
(unless (string? str)
(error 'open-string-input-port str))
($make-port
(fxior textual-input-port-bits fast-char-text-tag)
0 (string-length str) str
#t ;;; transcoder
"*string-input-port*"
(lambda (str i c) 0) ;;; read!
#f ;;; write!
#f ;;; FIXME: get-position
#f ;;; FIXME: set-position!
#f ;;; close
#f))
(define (transcoded-port p transcoder)
(define who 'transcoded-port)
(unless (transcoder? transcoder)
(error who "not a transcoder" transcoder))
(unless (port? p) (error who "not a port" p))
(when ($port-transcoder p) (error who "not a binary port" p))
(when ($port-closed? p) (error who "cannot transcode closed port" p))
(let ([read! ($port-read! p)]
[write! ($port-write! p)])
($mark-port-closed! p)
(guarded-port
($make-port
(cond
[read! (input-transcoder-attrs transcoder)]
[write! (output-transcoder-attrs transcoder)]
[else
(error 'transcoded-port
"port is neither input nor output!")])
($port-index p)
($port-size p)
($port-buffer p)
transcoder
($port-id p)
read!
write!
($port-get-position p)
($port-set-position! p)
($port-close p)
($port-cookie p)))))
(define (reset-input-port! p)
(if (input-port? p)
($set-port-index! p ($port-size p))
(error 'reset-input-port! "not an input port" p)))
(define (port-transcoder p)
(if (port? p)
(let ([tr ($port-transcoder p)])
(and (transcoder? tr) tr))
(error 'port-transcoder "not a port" p)))
(define ($port-closed? p)
(not (fxzero? (fxand ($port-attrs p) closed-port-tag))))
(define ($mark-port-closed! p)
($set-port-attrs! p
(fxior closed-port-tag
(fxand ($port-attrs p) port-type-mask))))
(define (port-mode p)
(if (port? p)
(if (fxzero? (fxand ($port-attrs p) r6rs-mode-tag))
'ikarus-mode
'r6rs-mode)
(error 'port-mode "not a port" p)))
(define (set-port-mode! p mode)
(if (port? p)
(case mode
[(r6rs-mode)
($set-port-attrs! p
(fxior ($port-attrs p) r6rs-mode-tag))]
[(ikarus-mode)
($set-port-attrs! p
(fxand ($port-attrs p) (fxnot r6rs-mode-tag)))]
[else (error 'set-port-mode! "invalid mode" mode)])
(error 'set-port-mode! "not a port" p)))
(define flush-output-port
(case-lambda
[() (flush-output-port (*the-output-port*))]
[(p)
(unless (output-port? p)
(error 'flush-output-port "not an output port" p))
(when ($port-closed? p)
(error 'flush-output-port "port is closed" p))
(let ([idx ($port-index p)]
[buf ($port-buffer p)])
(unless (fx= idx 0)
(let ([bytes (($port-write! p) buf 0 idx)])
(unless (and (fixnum? bytes) (fx>= bytes 0) (fx<= bytes idx))
(error 'flush-output-port
"write! returned an invalid value"
bytes))
(cond
[(fx= bytes idx)
($set-port-index! p 0)]
[(fx= bytes 0)
(error 'flush-output-port "could not write bytes to sink")]
[else
(bytevector-copy! buf bytes buf 0 (fx- idx bytes))
($set-port-index! p (fx- idx bytes))
(flush-output-port p)]))))]))
(define ($close-port p)
(cond
[($port-closed? p) (void)]
[else
(when ($port-write! p)
(flush-output-port p))
($mark-port-closed! p)
(let ([close ($port-close p)])
(when (procedure? close)
(close)))]))
(define (close-port p)
(unless (port? p)
(error 'close-port "not a port" p))
($close-port p))
(define (close-input-port p)
(unless (input-port? p)
(error 'close-input-port "not an input port" p))
($close-port p))
(define (close-output-port p)
(unless (output-port? p)
(error 'close-output-port "not an output port" p))
($close-port p))
;(define-rrr port-has-port-position?)
;(define-rrr port-position)
;(define-rrr port-has-set-port-position!?)
;(define-rrr set-port-position!)
;;; ----------------------------------------------------------
(module (get-char lookahead-char)
(import UNSAFE)
(define (refill-bv-start p who)
(when ($port-closed? p) (error who "port is closed" p))
(let* ([bv ($port-buffer p)]
[n (bytevector-length bv)])
(let ([j (($port-read! p) bv 0 n)])
(unless (fixnum? j)
(error who "invalid return value from read! procedure" j))
(cond
[(fx>= j 0)
(unless (fx<= j n)
(error who "read! returned a value out of range" j))
($set-port-index! p 0)
($set-port-size! p j)
j]
[else
(error who "read! returned a value out of range" j)]))))
(define (refill-bv-buffer p who)
(when ($port-closed? p) (error who "port is closed" p))
(let ([bv ($port-buffer p)] [i ($port-index p)] [j ($port-size p)])
(let ([c0 (fx- j i)])
(bytevector-copy! bv i bv 0 c0)
(let* ([max (fx- (bytevector-length bv) c0)]
[c1 (($port-read! p) bv c0 max)])
(unless (fixnum? c1)
(error who "invalid return value from read! procedure" c1))
(cond
[(fx>= j 0)
(unless (fx<= j max)
(error who "read! returned a value out of range" j))
($set-port-index! p c0)
($set-port-size! p (fx+ c1 c0))
c1]
[else
(error who "read! returned a value out of range" c1)])))))
(define (get-char-latin-mode p who idx)
(let ([n (refill-bv-start p who)])
(cond
[(fx= n 0) (eof-object)]
[else
($set-port-index! p idx)
(integer->char (bytevector-u8-ref ($port-buffer p) 0))])))
(define (get-char-utf8-mode p who)
(define (do-error p who)
(case (transcoder-error-handling-mode ($port-transcoder p))
[(ignore) (get-char p)]
[(replace) #\xFFFD]
[(raise)
(raise (make-i/o-decoding-error p))]
[else (error who "cannot happen")]))
(let ([i ($port-index p)]
[j ($port-size p)]
[buf ($port-buffer p)])
(cond
[(fx= i j) ;;; exhausted
(let ([bytes (refill-bv-buffer p who)])
(cond
[(fx= bytes 0) (eof-object)]
[else (get-char p)]))]
[else
(let ([b0 (bytevector-u8-ref buf i)])
(cond
[(fx= (fxsra b0 5) #b110) ;;; two-byte-encoding
(let ([i (fx+ i 1)])
(cond
[(fx< i j)
(let ([b1 (bytevector-u8-ref buf i)])
(cond
[(fx= (fxsra b1 6) #b10)
($set-port-index! p (fx+ i 1))
(integer->char
(fxior (fxand b1 #b111111)
(fxsll (fxand b0 #b11111) 6)))]
[else
($set-port-index! p i)
(do-error p who)]))]
[else
(let ([bytes (refill-bv-buffer p who)])
(cond
[(fx= bytes 0)
($set-port-index! p (fx+ ($port-index p) 1))
(do-error p who)]
[else (get-char-utf8-mode p who)]))]))]
[(fx= (fxsra b0 4) #b1110) ;;; three-byte-encoding
(cond
[(fx< (fx+ i 2) j)
(let ([b1 (bytevector-u8-ref buf (fx+ i 1))]
[b2 (bytevector-u8-ref buf (fx+ i 2))])
(cond
[(fx= (fxsra (fxlogor b1 b2) 6) #b10)
(let ([n (fxlogor
(fxsll (fxand b0 #b1111) 12)
(fxsll (fxand b1 #b111111) 6)
(fxand b2 #b111111))])
(cond
[(and (fx<= #xD800 n) (fx<= n #xDFFF))
($set-port-index! p (fx+ i 1))
(do-error p who)]
[else
($set-port-index! p (fx+ i 3))
(integer->char n)]))]
[else
($set-port-index! p (fx+ i 1))
(do-error p who)]))]
[else
(let ([bytes (refill-bv-buffer p who)])
(cond
[(fx= bytes 0)
($set-port-index! p (fx+ ($port-index p) 1))
(do-error p who)]
[else (get-char-utf8-mode p who)]))])]
[(fx= (fxsra b0 3) #b11110) ;;; four-byte-encoding
(cond
[(fx< (fx+ i 3) j)
(let ([b1 (bytevector-u8-ref buf (fx+ i 1))]
[b2 (bytevector-u8-ref buf (fx+ i 2))]
[b3 (bytevector-u8-ref buf (fx+ i 3))])
(cond
[(fx= (fxsra (fxlogor b1 b2 b3) 6) #b10)
(let ([n (fxlogor
(fxsll (fxand b0 #b111) 18)
(fxsll (fxand b1 #b111111) 12)
(fxsll (fxand b2 #b111111) 6)
(fxand b3 #b111111))])
(cond
[(and (fx<= #x10000 n) (fx<= n #x10FFFF))
($set-port-index! p (fx+ i 4))
(integer->char n)]
[else
($set-port-index! p (fx+ i 1))
(do-error p who)]))]
[else
($set-port-index! p (fx+ i 1))
(do-error p who)]))]
[else
(let ([bytes (refill-bv-buffer p who)])
(cond
[(fx= bytes 0)
($set-port-index! p (fx+ ($port-index p) 1))
(do-error p who)]
[else (get-char-utf8-mode p who)]))])]
[else
($set-port-index! p (fx+ i 1))
(do-error p who)]))])))
(define (lookahead-char-utf8-mode p who)
(define (do-error p who)
(case (transcoder-error-handling-mode ($port-transcoder p))
[(ignore) (get-char p)]
[(replace) #\xFFFD]
[(raise)
(raise (make-i/o-decoding-error p))]
[else (error who "cannot happen")]))
(let ([i ($port-index p)]
[j ($port-size p)]
[buf ($port-buffer p)])
(cond
[(fx= i j) ;;; exhausted
(let ([bytes (refill-bv-buffer p who)])
(cond
[(fx= bytes 0) (eof-object)]
[else (lookahead-char p)]))]
[else
(let ([b0 (bytevector-u8-ref buf i)])
(cond
[(fx= (fxsra b0 5) #b110) ;;; two-byte-encoding
(let ([i (fx+ i 1)])
(cond
[(fx< i j)
(let ([b1 (bytevector-u8-ref buf i)])
(cond
[(fx= (fxsra b1 6) #b10)
(integer->char
(fxior (fxand b1 #b111111)
(fxsll (fxand b0 #b11111) 6)))]
[else
(do-error p who)]))]
[else
(let ([bytes (refill-bv-buffer p who)])
(cond
[(fx= bytes 0) (do-error p who)]
[else (lookahead-char-utf8-mode p who)]))]))]
[(fx= (fxsra b0 4) #b1110) ;;; three-byte-encoding
(cond
[(fx< (fx+ i 2) j)
(let ([b1 (bytevector-u8-ref buf (fx+ i 1))]
[b2 (bytevector-u8-ref buf (fx+ i 2))])
(cond
[(fx= (fxsra (fxlogor b1 b2) 6) #b10)
(let ([n (fxlogor
(fxsll (fxand b0 #b1111) 12)
(fxsll (fxand b1 #b111111) 6)
(fxand b2 #b111111))])
(cond
[(and (fx<= #xD800 n) (fx<= n #xDFFF))
(do-error p who)]
[else (integer->char n)]))]
[else (do-error p who)]))]
[else
(let ([bytes (refill-bv-buffer p who)])
(cond
[(fx= bytes 0) (do-error p who)]
[else (lookahead-char-utf8-mode p who)]))])]
[(fx= (fxsra b0 3) #b11110) ;;; four-byte-encoding
(cond
[(fx< (fx+ i 3) j)
(let ([b1 (bytevector-u8-ref buf (fx+ i 1))]
[b2 (bytevector-u8-ref buf (fx+ i 2))]
[b3 (bytevector-u8-ref buf (fx+ i 3))])
(cond
[(fx= (fxsra (fxlogor b1 b2 b3) 6) #b10)
(let ([n (fxlogor
(fxsll (fxand b0 #b111) 18)
(fxsll (fxand b1 #b111111) 12)
(fxsll (fxand b2 #b111111) 6)
(fxand b3 #b111111))])
(cond
[(and (fx<= #x10000 n) (fx<= n #x10FFFF))
(integer->char n)]
[else
(do-error p who)]))]
[else
(do-error p who)]))]
[else
(let ([bytes (refill-bv-buffer p who)])
(cond
[(fx= bytes 0)
(do-error p who)]
[else (lookahead-char-utf8-mode p who)]))])]
[else (do-error p who)]))])))
(define (advance-bom p who bom-seq)
;;; return eof if port is eof,
;;; #t if a bom is present, updating the port index to
;;; point just past the bom.
;;; #f otherwise.
(cond
[(fx< ($port-index p) ($port-size p))
(let f ([i 0] [ls bom-seq])
(cond
[(null? ls)
($set-port-index! p (fx+ ($port-index p) i))
#t]
[else
(let ([idx (fx+ i ($port-index p))])
(cond
[(fx< idx ($port-size p))
(if (fx=? (car ls)
(bytevector-u8-ref ($port-buffer p) idx))
(f (fx+ i 1) (cdr ls))
#f)]
[else
(let ([bytes (refill-bv-buffer p who)])
(if (fx= bytes 0)
#f
(f i ls)))]))]))]
[else
(let ([bytes (refill-bv-buffer p who)])
(if (fx= bytes 0)
(eof-object)
(advance-bom p who bom-seq)))]))
(define (speedup-input-port p who)
;;; returns #t if port is eof, #f otherwise
(unless (input-port? p)
(error who "not an input port" p))
(let ([tr ($port-transcoder p)])
(unless tr
(error who "not a textual port" p))
(case (transcoder-codec tr)
[(utf-8-codec)
;;;
($set-port-attrs! p
(fxior textual-input-port-bits fast-u7-text-tag))
(eof-object? (advance-bom p who '(#xEF #xBB #xBF)))]
[else (error 'slow-get-char "codec not handled")])))
(define (lookahead-char-char-mode p who)
(let ([str ($port-buffer p)]
[read! ($port-read! p)])
(let ([n (read! str 0 (string-length str))])
(unless (fixnum? n)
(error who "invalid return value from read!" n))
(unless (<= 0 n (string-length str))
(error who "return value from read! is out of range" n))
($set-port-index! p 0)
($set-port-size! p n)
(cond
[(fx= n 0)
(eof-object)]
[else
(string-ref str 0)]))))
;;;
(define (lookahead-char p)
(define who 'lookahead-char)
(let ([m ($port-fast-attrs p)])
(cond
[(eq? m fast-get-utf8-tag)
(let ([i ($port-index p)])
(cond
[(fx< i ($port-size p))
(let ([b (bytevector-u8-ref ($port-buffer p) i)])
(cond
[(fx< b 128) (integer->char b)]
[else (lookahead-char-utf8-mode p who)]))]
[else
(lookahead-char-utf8-mode p who)]))]
[(eq? m fast-get-char-tag)
(let ([i ($port-index p)])
(cond
[(fx< i ($port-size p))
(string-ref ($port-buffer p) i)]
[else
(lookahead-char-char-mode p who)]))]
[(eq? m fast-get-latin-tag)
(let ([i ($port-index p)])
(cond
[(fx< i ($port-size p))
(integer->char
(bytevector-u8-ref ($port-buffer p) i))]
[else
(get-char-latin-mode p who 0)]))]
[else
(if (speedup-input-port p who)
(eof-object)
(lookahead-char p))])))
;;;
(define (get-char-char-mode p who)
(let ([str ($port-buffer p)]
[read! ($port-read! p)])
(let ([n (read! str 0 (string-length str))])
(unless (fixnum? n)
(error who "invalid return value from read!" n))
(unless (<= 0 n (string-length str))
(error who "return value from read! is out of range" n))
($set-port-size! p n)
(cond
[(fx= n 0)
($set-port-index! p 0)
(eof-object)]
[else
($set-port-index! p 1)
(string-ref str 0)]))))
(define (get-char p)
(define who 'get-char)
(let ([m ($port-fast-attrs p)])
(cond
[(eq? m fast-get-utf8-tag)
(let ([i ($port-index p)])
(cond
[(fx< i ($port-size p))
(let ([b (bytevector-u8-ref ($port-buffer p) i)])
(cond
[(fx< b 128)
($set-port-index! p (fx+ i 1))
(integer->char b)]
[else (get-char-utf8-mode p who)]))]
[else
(get-char-utf8-mode p who)]))]
[(eq? m fast-get-char-tag)
(let ([i ($port-index p)])
(cond
[(fx< i ($port-size p))
($set-port-index! p (fx+ i 1))
(string-ref ($port-buffer p) i)]
[else (get-char-char-mode p who)]))]
[(eq? m fast-get-latin-tag)
(let ([i ($port-index p)])
(cond
[(fx< i ($port-size p))
($set-port-index! p (fx+ i 1))
(integer->char
(bytevector-u8-ref ($port-buffer p) i))]
[else
(get-char-latin-mode p who 1)]))]
[else
(if (speedup-input-port p who)
(eof-object)
(get-char p))]))))
;;; ----------------------------------------------------------
(define (assert-binary-input-port p who)
(unless (port? p) (error who "not a port" p))
(when ($port-closed? p) (error who "port is closed" p))
(when ($port-transcoder p) (error who "port is not binary" p))
(unless ($port-read! p)
(error who "port is not an input port" p)))
(module (get-u8 lookahead-u8)
(import UNSAFE)
(define (get-u8-byte-mode p who start)
(when ($port-closed? p) (error who "port is closed" p))
(let* ([bv ($port-buffer p)]
[n (bytevector-length bv)])
(let ([j (($port-read! p) bv 0 n)])
(unless (fixnum? j)
(error who "invalid return value from read! procedure" j))
(cond
[(fx> j 0)
(unless (fx<= j n)
(error who "read! returned a value out of range" j))
($set-port-index! p start)
($set-port-size! p j)
(bytevector-u8-ref bv 0)]
[(fx= j 0) (eof-object)]
[else
(error who "read! returned a value out of range" j)]))))
(define (slow-get-u8 p who start)
(assert-binary-input-port p who)
($set-port-attrs! p fast-get-byte-tag)
(get-u8-byte-mode p who start))
;;;
(define (get-u8 p)
(define who 'get-u8)
(let ([m ($port-fast-attrs p)])
(cond
[(eq? m fast-get-byte-tag)
(let ([i ($port-index p)])
(cond
[(fx< i ($port-size p))
($set-port-index! p (fx+ i 1))
(bytevector-u8-ref ($port-buffer p) i)]
[else (get-u8-byte-mode p who 1)]))]
[else (slow-get-u8 p who 1)])))
(define (lookahead-u8 p)
(define who 'lookahead-u8)
(let ([m ($port-fast-attrs p)])
(cond
[(eq? m fast-get-byte-tag)
(let ([i ($port-index p)])
(cond
[(fx< i ($port-size p))
(bytevector-u8-ref ($port-buffer p) i)]
[else (get-u8-byte-mode p who 0)]))]
[else (slow-get-u8 p who 0)]))))
(define (port-eof? p)
(import UNSAFE)
(define who 'port-eof?)
(let ([m ($port-fast-attrs p)])
(cond
[(not (eq? m 0))
(if (fx< ($port-index p) ($port-size p))
#f
(if ($port-transcoder p)
(eof-object? (lookahead-char p))
(eof-object? (lookahead-u8 p))))]
[(input-port? p)
(when ($port-closed? p)
(error 'port-eof? "port is closed" p))
(if (textual-port? p)
(eof-object? (lookahead-char p))
(eof-object? (lookahead-u8 p)))]
[else (error 'port-eof? "not an input port" p)])))
(define io-errors-vec
'#("unknown error"
"bad file name"
"operation interrupted"
"not a directory"
"file name too long"
"missing entities"
"insufficient access privileges"
"circular path"
"file is a directory"
"file system is read-only"
"maximum open files reached"
"maximum open files reached"
"ENXIO"
"operation not supported"
"not enough space on device"
"quota exceeded"
"io error"
"device is busy"
"access fault"
"file already exists"
"invalid file name"))
(define (io-error who id err)
(let ([msg
(let ([err (fxnot err)])
(cond
[(fx< err (vector-length io-errors-vec))
(vector-ref io-errors-vec err)]
[else "unknown error"]))])
(raise
(condition
(make-who-condition who)
(make-message-condition msg)
(make-i/o-filename-error id)))))
(define block-size 4096)
(define input-file-buffer-size (+ block-size 128))
(define output-file-buffer-size block-size)
(define (fh->input-port fd id size transcoder close?)
(guarded-port
($make-port
(input-transcoder-attrs transcoder)
0 0 (make-bytevector size)
transcoder
id
(lambda (bv idx cnt)
(let ([bytes
(foreign-call "ikrt_read_fd" fd bv idx
(fxmin block-size cnt))])
(when (fx< bytes 0) (io-error 'read id bytes))
bytes))
#f ;;; write!
#f ;;; get-position
#f ;;; set-position!
(and close?
(lambda ()
(cond
[(foreign-call "ikrt_close_fd" fd) =>
(lambda (err)
(io-error 'close id err))])))
fd)))
(define (fh->output-port fd id size transcoder close?)
(guarded-port
($make-port
(output-transcoder-attrs transcoder)
0 size (make-bytevector size)
transcoder
id
#f
(lambda (bv idx cnt)
(let ([bytes
(foreign-call "ikrt_write_fd" fd bv idx
(fxmin block-size cnt))])
(when (fx< bytes 0) (io-error 'write id bytes))
bytes))
#f ;;; get-position
#f ;;; set-position!
(and close?
(lambda ()
(cond
[(foreign-call "ikrt_close_fd" fd) =>
(lambda (err)
(io-error 'close id err))])))
fd)))
(define (open-input-file-handle filename who)
(let ([fh (foreign-call "ikrt_open_input_fd"
(string->utf8 filename))])
(cond
[(fx< fh 0) (io-error who filename fh)]
[else fh])))
(define (open-output-file-handle filename file-options who)
(let ([opt (case file-options
[(fo:default) 0]
[(fo:no-create) 1]
[(fo:no-fail) 2]
[(fo:no-fail/no-create) 3]
[(fo:no-truncate) 4]
[(fo:no-truncate/no-create) 5]
[(fo:no-truncate/no-fail) 6]
[(fo:no-truncate/no-fail/no-create) 7]
[else (error who "invalid file option" file-options)])])
(let ([fh (foreign-call "ikrt_open_output_fd"
(string->utf8 filename)
opt)])
(cond
[(fx< fh 0) (io-error who filename fh)]
[else fh]))))
(define open-file-input-port
(case-lambda
[(filename)
(open-file-input-port filename (file-options) 'block #f)]
[(filename file-options)
(open-file-input-port filename file-options 'block #f)]
[(filename file-options buffer-mode)
(open-file-input-port filename file-options buffer-mode #f)]
[(filename file-options buffer-mode transcoder)
(unless (string? filename)
(error 'open-file-input-port "invalid filename" filename))
(unless (or (not transcoder) (transcoder? transcoder))
(error 'open-file-input-port "invalid transcoder" transcoder))
; FIXME: file-options ignored
; FIXME: buffer-mode ignored
(fh->input-port
(open-input-file-handle filename 'open-file-input-port)
filename
input-file-buffer-size
transcoder
#t)]))
(define open-file-output-port
(case-lambda
[(filename)
(open-file-output-port filename (file-options) 'block #f)]
[(filename file-options)
(open-file-output-port filename file-options 'block #f)]
[(filename file-options buffer-mode)
(open-file-output-port filename file-options buffer-mode #f)]
[(filename file-options buffer-mode transcoder)
(unless (string? filename)
(error 'open-file-output-port "invalid filename" filename))
; FIXME: file-options ignored
; FIXME: buffer-mode ignored
(unless (or (not transcoder) (transcoder? transcoder))
(error 'open-file-output-port "invalid transcoder" transcoder))
(fh->output-port
(open-output-file-handle filename file-options
'open-file-output-port)
filename
output-file-buffer-size
transcoder
#t)]))
(define (open-output-file filename)
(unless (string? filename)
(error 'open-output-file "invalid filename" filename))
(fh->output-port
(open-output-file-handle filename (file-options)
'open-input-file)
filename
output-file-buffer-size
(native-transcoder)
#t))
(define (open-input-file filename)
(unless (string? filename)
(error 'open-input-file "invalid filename" filename))
(fh->input-port
(open-input-file-handle filename 'open-input-file)
filename
input-file-buffer-size
(native-transcoder)
#t))
(define (call-with-input-file filename proc)
(unless (string? filename)
(error 'call-with-input-file "invalid filename" filename))
(unless (procedure? proc)
(error 'call-with-input-file "not a procedure" proc))
(call-with-port
(fh->input-port
(open-input-file-handle filename 'call-with-input-file)
filename
input-file-buffer-size
(native-transcoder)
#t)
proc))
(define (with-input-from-file filename proc)
(unless (string? filename)
(error 'with-input-from-file "invalid filename" filename))
(unless (procedure? proc)
(error 'with-input-from-file "not a procedure" proc))
(let ([p
(fh->input-port
(open-input-file-handle filename 'with-input-from-file)
filename
input-file-buffer-size
(native-transcoder)
#t)])
(parameterize ([*the-input-port* p])
(proc))))
(define (standard-input-port)
(fh->input-port 0 '*stdin* 256 #f #f))
(define (standard-output-port)
(fh->output-port 1 '*stdout* 256 #f #f))
(define (standard-error-port)
(fh->output-port 2 '*stderr* 256 #f #f))
(define *the-input-port*
(make-parameter
(transcoded-port
(fh->input-port 0 '*stdin* input-file-buffer-size #f #f)
(native-transcoder))))
(define *the-output-port*
(make-parameter
(transcoded-port
(fh->output-port 1 '*stdout* output-file-buffer-size #f #f)
(native-transcoder))))
(define *the-error-port*
(make-parameter
(transcoded-port
(fh->output-port 2 '*stderr* output-file-buffer-size #f #f)
(native-transcoder))))
(define console-output-port
(let ([p (*the-output-port*)])
(lambda () p)))
(define console-error-port
(let ([p (*the-error-port*)])
(lambda () p)))
(define console-input-port
(let ([p (*the-input-port*)])
(lambda () p)))
(define (current-input-port) (*the-input-port*))
(define (current-output-port) (*the-output-port*))
(define (current-error-port) (*the-error-port*))
(define (call-with-port p proc)
(if (port? p)
(if (procedure? proc)
(dynamic-wind
void
(lambda () (proc p))
(lambda () (close-port p)))
(error 'call-with-port "not a procedure" proc))
(error 'call-with-port "not a port" p)))
(define read-char
(case-lambda
[() (get-char (*the-input-port*))]
[(p)
(if (input-port? p)
(if (textual-port? p)
(get-char p)
(error 'read-char "not a textual port" p))
(error 'read-char "not an input-port" p))]))
;;;
(define peek-char
(case-lambda
[() (lookahead-char (*the-input-port*))]
[(p)
(if (input-port? p)
(if (textual-port? p)
(lookahead-char p)
(error 'peek-char "not a textual port" p))
(error 'peek-char "not an input-port" p))]))
(define (get-bytevector-n p n)
(import (ikarus system $fx) (ikarus system $bytevectors))
(define (subbytevector s n)
(let ([p ($make-bytevector n)])
(let f ([s s] [n n] [p p])
(let ([n ($fx- n 1)])
($bytevector-set! p n ($bytevector-u8-ref s n))
(if ($fx= n 0)
p
(f s n p))))))
(unless (input-port? p)
(error 'get-bytevector-n "not an input port" p))
(unless (binary-port? p)
(error 'get-bytevector-n "not a binary port" p))
(unless (fixnum? n)
(error 'get-bytevector-n "count is not a fixnum" n))
(cond
[($fx> n 0)
(let ([s ($make-bytevector n)])
(let f ([p p] [n n] [s s] [i 0])
(let ([x (get-u8 p)])
(cond
[(eof-object? x)
(if ($fx= i 0)
(eof-object)
(subbytevector s i))]
[else
($bytevector-set! s i x)
(let ([i ($fxadd1 i)])
(if ($fx= i n)
s
(f p n s i)))]))))]
[($fx= n 0) '#vu8()]
[else (error 'get-bytevector-n "count is negative" n)]))
(define (get-bytevector-n! p s i c)
(import (ikarus system $fx) (ikarus system $bytevectors))
(unless (input-port? p)
(error 'get-bytevector-n! "not an input port" p))
(unless (binary-port? p)
(error 'get-bytevector-n! "not a binary port" p))
(unless (bytevector? s)
(error 'get-bytevector-n! "not a bytevector" s))
(let ([len ($bytevector-length s)])
(unless (fixnum? i)
(error 'get-bytevector-n! "starting index is not a fixnum" i))
(when (or ($fx< i 0) ($fx> i len))
(error 'get-bytevector-n!
(format "starting index is out of range 0..~a" len)
i))
(unless (fixnum? c)
(error 'get-bytevector-n! "count is not a fixnum" c))
(cond
[($fx> c 0)
(let ([j (+ i c)])
(when (> j len)
(error 'get-bytevector-n!
(format "count is out of range 0..~a" (- len i))
c))
(let ([x (get-u8 p)])
(cond
[(eof-object? x) x]
[else
($bytevector-set! s i x)
(let f ([p p] [s s] [start i] [i 1] [c c])
(let ([x (get-u8 p)])
(cond
[(eof-object? x) i]
[else
($bytevector-set! s ($fx+ start i) x)
(let ([i ($fxadd1 i)])
(if ($fx= i c)
i
(f p s start i c)))])))])))]
[($fx= c 0) 0]
[else (error 'get-bytevector-n! "count is negative" c)])))
(define-rrr get-bytevector-some)
(define (get-bytevector-all p)
(define (get-it p)
(let f ([p p] [n 0] [ac '()])
(let ([x (get-u8 p)])
(cond
[(eof-object? x)
(if (null? ac)
(eof-object)
(make-it n ac))]
[else (f p (+ n 1) (cons x ac))]))))
(define (make-it n revls)
(let f ([s (make-bytevector n)] [i (- n 1)] [ls revls])
(cond
[(pair? ls)
(bytevector-u8-set! s i (car ls))
(f s (- i 1) (cdr ls))]
[else s])))
(if (input-port? p)
(if (binary-port? p)
(get-it p)
(error 'get-bytevector-all "not a binary port" p))
(error 'get-bytevector-all "not an input port" p)))
(define (get-string-n p n)
(import (ikarus system $fx) (ikarus system $strings))
(unless (input-port? p)
(error 'get-string-n "not an input port" p))
(unless (textual-port? p)
(error 'get-string-n "not a textual port" p))
(unless (fixnum? n)
(error 'get-string-n "count is not a fixnum" n))
(cond
[($fx> n 0)
(let ([s ($make-string n)])
(let f ([p p] [n n] [s s] [i 0])
(let ([x (get-char p)])
(cond
[(eof-object? x)
(if ($fx= i 0)
(eof-object)
(substring s 0 i))]
[else
($string-set! s i x)
(let ([i ($fxadd1 i)])
(if ($fx= i n)
s
(f p n s i)))]))))]
[($fx= n 0) ""]
[else (error 'get-string-n "count is negative" n)]))
(define (get-string-n! p s i c)
(import (ikarus system $fx) (ikarus system $strings))
(unless (input-port? p)
(error 'get-string-n! "not an input port" p))
(unless (textual-port? p)
(error 'get-string-n! "not a textual port" p))
(unless (string? s)
(error 'get-string-n! "not a string" s))
(let ([len ($string-length s)])
(unless (fixnum? i)
(error 'get-string-n! "starting index is not a fixnum" i))
(when (or ($fx< i 0) ($fx> i len))
(error 'get-string-n!
(format "starting index is out of range 0..~a" len)
i))
(unless (fixnum? c)
(error 'get-string-n! "count is not a fixnum" c))
(cond
[($fx> c 0)
(let ([j (+ i c)])
(when (> j len)
(error 'get-string-n!
(format "count is out of range 0..~a" (- len i))
c))
(let ([x (get-char p)])
(cond
[(eof-object? x) x]
[else
($string-set! s i x)
(let f ([p p] [s s] [start i] [i 1] [c c])
(let ([x (get-char p)])
(cond
[(eof-object? x) i]
[else
($string-set! s ($fx+ start i) x)
(let ([i ($fxadd1 i)])
(if ($fx= i c)
i
(f p s start i c)))])))])))]
[($fx= c 0) 0]
[else (error 'get-string-n! "count is negative" c)])))
(define (get-line p)
(import UNSAFE)
(define (get-it p)
(let f ([p p] [n 0] [ac '()])
(let ([x (get-char p)])
(cond
[(eqv? x #\newline)
(make-it n ac)]
[(eof-object? x)
(if (null? ac) x (make-it n ac))]
[else (f p (fx+ n 1) (cons x ac))]))))
(define (make-it n revls)
(let f ([s (make-string n)] [i (fx- n 1)] [ls revls])
(cond
[(pair? ls)
(string-set! s i (car ls))
(f s (fx- i 1) (cdr ls))]
[else s])))
(if (input-port? p)
(if (textual-port? p)
(get-it p)
(error 'get-line "not a textual port" p))
(error 'get-line "not an input port" p)))
(define (get-string-all p)
(define (get-it p)
(let f ([p p] [n 0] [ac '()])
(let ([x (get-char p)])
(cond
[(eof-object? x)
(if (null? ac)
(eof-object)
(make-it n ac))]
[else (f p (+ n 1) (cons x ac))]))))
(define (make-it n revls)
(let f ([s (make-string n)] [i (- n 1)] [ls revls])
(cond
[(pair? ls)
(string-set! s i (car ls))
(f s (- i 1) (cdr ls))]
[else s])))
(if (input-port? p)
(if (textual-port? p)
(get-it p)
(error 'get-string-all "not a textual port" p))
(error 'get-string-all "not an input port" p)))
;;; ----------------------------------------------------------
(module (put-char write-char put-string)
(import UNSAFE)
(define (put-char-utf8-mode p b who)
(cond
[(fx< b 128)
(flush-output-port p)
(let ([i ($port-index p)] [j ($port-size p)])
(cond
[(fx< i j)
(bytevector-u8-set! ($port-buffer p) i b)
($set-port-index! p (fx+ i 1))]
[else
(error who "insufficient space on port" p)]))]
[(fx<= b #x7FF)
(let ([i ($port-index p)]
[j ($port-size p)]
[buf ($port-buffer p)])
(cond
[(fx< (fx+ i 1) j)
(bytevector-u8-set! buf i
(fxior #b11000000 (fxsra b 6)))
(bytevector-u8-set! buf (fx+ i 1)
(fxior #b10000000 (fxand b #b111111)))
($set-port-index! p (fx+ i 2))]
[else
(flush-output-port p)
(put-char-utf8-mode p b who)]))]
[(fx<= b #xFFFF)
(let ([i ($port-index p)]
[j ($port-size p)]
[buf ($port-buffer p)])
(cond
[(fx< (fx+ i 2) j)
(bytevector-u8-set! buf i
(fxior #b11100000 (fxsra b 12)))
(bytevector-u8-set! buf (fx+ i 1)
(fxior #b10000000 (fxand (fxsra b 6) #b111111)))
(bytevector-u8-set! buf (fx+ i 2)
(fxior #b10000000 (fxand b #b111111)))
($set-port-index! p (fx+ i 3))]
[else
(flush-output-port p)
(put-char-utf8-mode p b who)]))]
[else
(let ([i ($port-index p)]
[j ($port-size p)]
[buf ($port-buffer p)])
(cond
[(fx< (fx+ i 3) j)
(bytevector-u8-set! buf i
(fxior #b11110000 (fxsra b 18)))
(bytevector-u8-set! buf (fx+ i 1)
(fxior #b10000000 (fxand (fxsra b 12) #b111111)))
(bytevector-u8-set! buf (fx+ i 2)
(fxior #b10000000 (fxand (fxsra b 6) #b111111)))
(bytevector-u8-set! buf (fx+ i 3)
(fxior #b10000000 (fxand b #b111111)))
($set-port-index! p (fx+ i 4))]
[else
(flush-output-port p)
(put-char-utf8-mode p b who)]))]))
(define (put-char-latin-mode p b who)
(cond
[(fx< b 256)
(flush-output-port p)
(let ([i ($port-index p)] [j ($port-size p)])
(cond
[(fx< i j)
(bytevector-u8-set! ($port-buffer p) i b)
($set-port-index! p (fx+ i 1))]
[else
(error who "insufficient space in port" p)]))]
[else
(case (transcoder-error-handling-mode (port-transcoder p))
[(ignore) (void)]
[(replace) (put-char p #\?)]
[(raise)
(raise (make-i/o-encoding-error p))]
[else (error who "BUG: invalid error handling mode" p)])]))
(define (put-char-char-mode p c who)
(flush-output-port p)
(let ([i ($port-index p)] [j ($port-size p)])
(cond
[(fx< i j)
(string-set! ($port-buffer p) i c)
($set-port-index! p (fx+ i 1))]
[else
(error who "insufficient space in port" p)])))
;;;
(define write-char
(case-lambda
[(c p) (do-put-char p c 'write-char)]
[(c) (do-put-char (*the-output-port*) c 'write-char)]))
(define (put-char p c)
(do-put-char p c 'put-char))
(define (put-string p str)
(unless (string? str) (error 'put-string "not a string" str))
(unless (output-port? p)
(error 'put-string "not an output port" p))
(unless (textual-port? p)
(error 'put-string "not a textual port" p))
(let f ([i 0] [n (string-length str)])
(unless (fx= i n)
(do-put-char p (string-ref str i) 'put-string)
(f (fx+ i 1) n))))
(define (do-put-char p c who)
(unless (char? c) (error who "not a char" c))
(let ([m ($port-fast-attrs p)])
(cond
[(eq? m fast-put-utf8-tag)
(let ([i ($port-index p)])
(let ([b (char->integer c)])
(cond
[(and (fx< i ($port-size p)) (fx< b 128))
(bytevector-u8-set! ($port-buffer p) i b)
($set-port-index! p (fx+ i 1))]
[else
(put-char-utf8-mode p b who)])))]
[(eq? m fast-put-char-tag)
(let ([i ($port-index p)])
(cond
[(fx< i ($port-size p))
($set-port-index! p (fx+ i 1))
(string-set! ($port-buffer p) i c)]
[else
(put-char-char-mode p c who)]))]
[(eq? m fast-put-latin-tag)
(let ([i ($port-index p)])
(let ([b (char->integer c)])
(cond
[(and (fx< i ($port-size p)) (fx< b 256))
(bytevector-u8-set! ($port-buffer p) i b)
($set-port-index! p (fx+ i 1))]
[else
(put-char-latin-mode p b who)])))]
[else
(if (output-port? p)
(error who "not a textual port" p)
(error who "not an output port" p))]))))
(define newline
(case-lambda
[()
(put-char (*the-output-port*) #\newline)
(flush-output-port (*the-output-port*))]
[(p)
(unless (output-port? p)
(error 'newline "not an output port" p))
(unless (textual-port? p)
(error 'newline "not a textual port" p))
(when ($port-closed? p)
(error 'newline "port is closed" p))
(put-char p #\newline)
(flush-output-port p)]))
(module (put-u8)
(import UNSAFE)
(define (put-u8-byte-mode p b who)
(let ([write! ($port-write! p)])
(let ([i ($port-index p)]
[buf ($port-buffer p)])
(let ([bytes (write! buf 0 i)])
(when (or (not (fixnum? bytes))
(fx< bytes 0)
(fx> bytes i))
(error who "write! returned an invalid value" bytes))
(cond
[(fx= bytes i)
(bytevector-u8-set! buf 0 b)
($set-port-index! p 1)]
[(fx= bytes 0)
(error who "could not write bytes to sink")]
[else
(let ([i (fx- i bytes)])
(bytevector-copy! buf bytes buf 0 i)
(bytevector-u8-set! buf i b)
($set-port-index! p (fx+ i 1)))])))))
;;;
(define (put-u8 p b)
(define who 'put-u8)
(unless (u8? b) (error who "not a u8" b))
(let ([m ($port-fast-attrs p)])
(cond
[(eq? m fast-put-byte-tag)
(let ([i ($port-index p)])
(cond
[(fx< i ($port-size p))
($set-port-index! p (fx+ i 1))
(bytevector-u8-set! ($port-buffer p) i b)]
[else
(put-u8-byte-mode p b who)]))]
[else
(if (output-port? p)
(error who "not a binary port" p)
(error who "not an output port" p))]))))
)