[gambit-list] newbie question: output from one port as input to another port
Christian
christian at pflanze.mine.nu
Sun Mar 12 08:36:08 EST 2006
At 22:40 Uhr -0500 11.03.2006, Samuel Montgomery-Blinn wrote:
>So, I have been trying to learn Gambit-C's port mechanisms. This is
>likely an infantile question, but it seems that it should be the
>most basic thing to be able to take one input port and "connect" it
>to an output port. The specific use for me is sending the contents
>of a file over a network (tcp) port. I have tried a few methods,
>mostly "busy" and likely lossy methods.
>
>1. read the entire contents of the file port, then output the file
>to the network port. (wastes time in which earlier parts of the file
>could be written to the network port)
>2. read a line from the file port, and output the line to the
>network port. repeat until eof. (wastes time for many reasons)
Since there is no builtin, your second suggestion is not far from the
optimum. I would use {read,write}-subu8vector or
{read,write}-substring instead (the latter should be necessary if the
input and output ports use different charset [or end-of-line?]
encodings):
(define bufsiz 4096)
(define (port-copy-bytes from to)
(let ((buf (make-u8vector bufsiz)))
(let lp ((total-bytes 0))
(let ((n-read (read-subu8vector buf 0 bufsiz from)))
(if (= n-read 0) total-bytes ;; eof (or possibly timeout) case
(let ((n-written (write-subu8vector buf 0 n-read to)))
(if (= n-written n-read)
(lp (+ total-bytes n-written))
(error "can this happen?: n-read != n-written:"
n-read n-written))))))))
(define (copy-file from-path to-path)
(call-with-input-file from-path
(lambda (in)
(call-with-output-file to-path
(lambda (out)
(port-copy-bytes in out))))))
Maybe it could be optimized some more by using lowlevel internals.
You could run this in a thread if you want:
; some thread usage simplification:
(define (make-future thunk)
(let ((t (make-thread thunk)))
(thread-start! t)
t))
(define-macro (future arg)
`(make-future (lambda ()
,arg)))
(define future-force thread-join!)
; use it:
> (define f (future (copy-file "foo" "bar")))
> (future-force f)
3342
If you're on a unix with sendfile(2), you should be able to use the C
interface and use that, but in this case be aware of the following:
- Gambit switches file descriptors to non-blocking IO (so that it
doesn't risk blocking the thread scheduler). I don't know how
sendfile(2) handles fd's in non-blocking mode; either you block until
it finishes (good for simplicity, bad for the following point) or not
(in which case coding will be a bit more complicated).
- for the duration of the C calls, other scheme threads (if there are
any) do not have a chance to run. So iff sendfile(2) blocks, you
might want to create a separate pthread and let that do the work (I
still haven't tried this).
(BTW here are my thoughts on the {read,write}-sub* interface:
- I assume that write-subu8vector never returns with fewer than the
requested number of bytes written; correct?
- I hope that those functions never return with 0 unless eof or
timeout has happened (also not if being signalled); probably correct.
- the way for setting a timeout thunk does not seem to be flexible:
there's no documented way getting the current thunk for restoring a
previous one. Why not use dynamic parameter instead? (Or: an
additional lexical argument to the read/write-sub* functions.))
>I was guessing that something to do with the procedures
>"call-with-input-file" or "with-output-to-file" might be something
>towards what might be most succinctly expressed as some sort of UNIX
>"sendfile" functionality
If you're into elegance, you might want a curried version of the
port-copy-bytes function (but it won't buy you that much; one has to
be careful to keep the dynamic nesting of the call-with-* functions
intact):
(define (port-byte-copier from)
(lambda (to)
(port-copy-bytes from to)))
(define (copy-file-from.II from-path to-path)
(call-with-input-file from-path
(lambda (in)
(call-with-output-file to-path
(port-byte-copier in)))))
Christian.
More information about the Gambit-list
mailing list