binary-ports.scm 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. ;;; binary-ports.scm --- Binary IO on ports
  2. ;;; Copyright (C) 2009-2011,2013,2016,2019,2021,2023 Free Software Foundation, Inc.
  3. ;;;
  4. ;;; This library is free software: you can redistribute it and/or modify
  5. ;;; it under the terms of the GNU Lesser General Public License as
  6. ;;; published by the Free Software Foundation, either version 3 of the
  7. ;;; License, or (at your option) any later version.
  8. ;;;
  9. ;;; This library is distributed in the hope that it will be useful, but
  10. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. ;;; Lesser General Public License for more details.
  13. ;;;
  14. ;;; You should have received a copy of the GNU Lesser General Public
  15. ;;; License along with this program. If not, see
  16. ;;; <http://www.gnu.org/licenses/>.
  17. ;;; Author: Ludovic Courtès <ludo@gnu.org>
  18. ;;; Commentary:
  19. ;;;
  20. ;;; The I/O port API of the R6RS is provided by this module. In many areas
  21. ;;; it complements or refines Guile's own historical port API. For instance,
  22. ;;; it allows for binary I/O with bytevectors.
  23. ;;;
  24. ;;; Code:
  25. (define-module (ice-9 binary-ports)
  26. #:use-module (rnrs bytevectors)
  27. #:use-module (ice-9 match)
  28. #:use-module (ice-9 custom-ports)
  29. #:export (eof-object
  30. open-bytevector-input-port
  31. open-bytevector-output-port
  32. get-u8
  33. lookahead-u8
  34. get-bytevector-n
  35. get-bytevector-n!
  36. get-bytevector-some
  37. get-bytevector-some! ; Guile extension, not in R6RS
  38. get-bytevector-all
  39. get-string-n!
  40. put-u8
  41. put-bytevector
  42. unget-bytevector
  43. make-custom-binary-input-port
  44. make-custom-binary-output-port
  45. make-custom-binary-input/output-port
  46. call-with-input-bytevector
  47. call-with-output-bytevector))
  48. ;; Note that this extension also defines %make-transcoded-port, which is
  49. ;; not exported but is used by (rnrs io ports).
  50. (load-extension (string-append "libguile-" (effective-version))
  51. "scm_init_r6rs_ports")
  52. (define (call-with-input-bytevector bv proc)
  53. "Call the one-argument procedure @var{proc} with a newly created
  54. binary input port from which the bytevector @var{bv}'s contents may be
  55. read. All values yielded by @var{proc} are returned."
  56. (proc (open-bytevector-input-port bv)))
  57. (define (call-with-output-bytevector proc)
  58. "Call the one-argument procedure @var{proc} with a newly created
  59. binary output port. When the function returns, port is closed and the
  60. bytevector composed of the bytes written into the port is returned."
  61. (call-with-values
  62. (lambda ()
  63. (open-bytevector-output-port))
  64. (lambda (port get-bytevector)
  65. (proc port)
  66. (let ((bv (get-bytevector)))
  67. (close-port port)
  68. bv))))
  69. (define (type-error proc expecting val)
  70. (scm-error 'wrong-type-arg proc "Wrong type (expecting `~S'): ~S"
  71. (list expecting val) (list val)))
  72. (define (custom-binary-port-read read)
  73. (unless (procedure? read)
  74. (type-error "custom-binary-port-read" "procedure" read))
  75. (lambda (port bv start count)
  76. (let ((ret (read bv start count)))
  77. (unless (and (exact-integer? ret) (<= 0 ret count))
  78. (scm-error 'out-of-range "custom-binary-port-read"
  79. "Value out of range: ~S" (list ret) (list ret)))
  80. ret)))
  81. (define (custom-binary-port-write write)
  82. (unless (procedure? write)
  83. (type-error "custom-binary-port-write" "procedure" write))
  84. (lambda (port bv start count)
  85. (let ((ret (write bv start count)))
  86. (unless (and (exact-integer? ret) (<= 0 ret count))
  87. (scm-error 'out-of-range "custom-binary-port-write"
  88. "Value out of range: ~S" (list ret) (list ret)))
  89. ret)))
  90. (define (custom-binary-port-seek get-position set-position!)
  91. (when get-position
  92. (unless (procedure? get-position)
  93. (type-error "custom-binary-port-seek" "procedure" get-position)))
  94. (when set-position!
  95. (unless (procedure? set-position!)
  96. (type-error "custom-binary-port-seek" "procedure" set-position!)))
  97. (define (seek port offset whence)
  98. (cond
  99. ((eqv? whence SEEK_CUR)
  100. (unless get-position
  101. (type-error "custom-binary-port-seek"
  102. "R6RS custom binary port with `port-position` support"
  103. port))
  104. (if (zero? offset)
  105. (get-position)
  106. (seek port (+ (get-position) offset) SEEK_SET)))
  107. ((eqv? whence SEEK_SET)
  108. (unless set-position!
  109. (type-error "custom-binary-port-seek"
  110. "Seekable R6RS custom binary port"
  111. port))
  112. (set-position! offset)
  113. ;; Assume setting the position succeeds.
  114. offset)
  115. ((eqv? whence SEEK_END)
  116. (error "R6RS custom binary ports do not support `SEEK_END'"))))
  117. seek)
  118. (define (custom-binary-port-close close)
  119. (match close
  120. (#f (lambda (port) #t))
  121. ((? procedure?) (lambda (port) (close)))
  122. (_ (type-error "custom-binary-port-close" "procedure" close))))
  123. (define (custom-binary-port-random-access? set-position!)
  124. (if set-position!
  125. (lambda (port) #t)
  126. (lambda (port) #f)))
  127. (define (make-custom-binary-input-port id read get-position set-position! close)
  128. (unless (string? id)
  129. (type-error "make-custom-binary-input-port" "string" id))
  130. (make-custom-port #:id id
  131. #:read (custom-binary-port-read read)
  132. #:seek (custom-binary-port-seek get-position set-position!)
  133. #:close (custom-binary-port-close close)
  134. #:random-access?
  135. (custom-binary-port-random-access? set-position!)
  136. ;; FIXME: Instead default to current encoding, if
  137. ;; someone reads text from this port.
  138. #:encoding 'ISO-8859-1 #:conversion-strategy 'error))
  139. (define (make-custom-binary-output-port id write get-position set-position!
  140. close)
  141. (unless (string? id)
  142. (type-error "make-custom-binary-output-port" "string" id))
  143. (make-custom-port #:id id
  144. #:write (custom-binary-port-write write)
  145. #:seek (custom-binary-port-seek get-position set-position!)
  146. #:close (custom-binary-port-close close)
  147. #:random-access?
  148. (custom-binary-port-random-access? set-position!)
  149. ;; FIXME: Instead default to current encoding, if
  150. ;; someone reads text from this port.
  151. #:encoding 'ISO-8859-1 #:conversion-strategy 'error))
  152. (define (make-custom-binary-input/output-port id read write get-position
  153. set-position! close)
  154. (unless (string? id)
  155. (type-error "make-custom-binary-input/output-port" "string" id))
  156. (make-custom-port #:id id
  157. #:read (custom-binary-port-read read)
  158. #:write (custom-binary-port-write write)
  159. #:seek (custom-binary-port-seek get-position set-position!)
  160. #:close (custom-binary-port-close close)
  161. #:random-access?
  162. (custom-binary-port-random-access? set-position!)
  163. ;; FIXME: Instead default to current encoding, if
  164. ;; someone reads text from this port.
  165. #:encoding 'ISO-8859-1 #:conversion-strategy 'error))