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)
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, but as I am new both to Scheme (or any Lisp for that matter) and Gambit-C, I thought I would both say "hello" to this list and ask for its help and guidance. If this sort of question is not a faux pas, I certainly have more!
-Sam
Afficher les réponses par date
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.
- 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.
On 11-Mar-06, at 10:40 PM, 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.
- 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)
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, but as I am new both to Scheme (or any Lisp for that matter) and Gambit-C, I thought I would both say "hello" to this list and ask for its help and guidance. If this sort of question is not a faux pas, I certainly have more!
-Sam _______________________________________________ Gambit-list mailing list Gambit-list@iro.umontreal.ca http://mailman.iro.umontreal.ca/mailman/listinfo/gambit-list
Well, Christian just beat me to it, but here's my version anyway (which shows off binary I/O as well as text I/O which is useful if you are converting character and end-of-line encodings).
Marc
(define (copy-octet-port-to-octet-port in out) (let ((buf (make-u8vector 4096))) (let loop () (let ((n (read-subu8vector buf 0 (u8vector-length buf) in))) (if (> n 0) (begin (write-subu8vector buf 0 n out) (loop)))))))
(define (copy-char-port-to-char-port in out) (let ((buf (make-string 4096))) (let loop () (let ((n (read-substring buf 0 (string-length buf) in))) (if (> n 0) (begin (write-substring buf 0 n out) (loop)))))))
(define (copy-binary-file in-filename out-filename) (call-with-input-file in-filename (lambda (in) (call-with-output-file out-filename (lambda (out) (copy-octet-port-to-octet-port in out))))))
(define (copy-text-file in-filename out-filename) (call-with-input-file in-filename (lambda (in) (call-with-output-file out-filename (lambda (out) (copy-char-port-to-char-port in out))))))
(define (test)
; binary copy gsi/gsi -> gsi2
(time (copy-binary-file "gsi/gsi" "gsi2"))
; copy text file a.txt to b.txt, converting from UTF-8 character ; encoding (and any end-of-line encoding) to UCS-2 character ; encoding with BOM and DOS end-of-line encoding (compatible with ; Windows)
(time (copy-text-file (list path: "a.txt" char-encoding: 'utf8 eol-encoding: 'cr-lf) (list path: "b.txt" char-encoding: 'ucs2 eol-encoding: 'cr- lf))))
(test)