~ chicken-core (chicken-5) c29ba2b8b26f9bbbafdb9483ae58704dda3eb538


commit c29ba2b8b26f9bbbafdb9483ae58704dda3eb538
Author:     Evan Hanson <evhan@foldling.org>
AuthorDate: Thu Jun 30 20:09:45 2016 +1200
Commit:     Peter Bex <peter@more-magic.net>
CommitDate: Sat Jul 9 16:26:41 2016 +0200

    Generalize port directionality
    
    Convert the port direction and closed flags from booleans to bitmasks,
    to allow for multidirectional ports.
    
    Signed-off-by: Peter Bex <peter@more-magic.net>

diff --git a/NEWS b/NEWS
index 793c99a8..fa8188c8 100644
--- a/NEWS
+++ b/NEWS
@@ -6,6 +6,8 @@
     now return exact numbers where possible, so code relying on flonums
     being returned may need to be changed if rational numbers do not
     provide the desired performance.
+  - Port directionality has been generalized from a simple input/output
+    flag to a bitmap, to allow for multidirectional ports.
 
 - Compiler
   - Fixed an off by one allocation problem in generated C code for (list ...).
diff --git a/chicken.h b/chicken.h
index dbaa92d9..509af5c4 100644
--- a/chicken.h
+++ b/chicken.h
@@ -673,8 +673,8 @@ static inline int isinf_ld (long double x)
 #define C_BAD_ARGUMENT_TYPE_NO_BOOLEAN_ERROR          37
 #define C_BAD_ARGUMENT_TYPE_NO_LOCATIVE_ERROR         38
 #define C_BAD_ARGUMENT_TYPE_NO_PORT_ERROR             39
-#define C_BAD_ARGUMENT_TYPE_NO_INPUT_PORT_ERROR       40
-#define C_BAD_ARGUMENT_TYPE_NO_OUTPUT_PORT_ERROR      41
+#define C_BAD_ARGUMENT_TYPE_PORT_DIRECTION_ERROR      40
+/* unused                                             41 */
 #define C_PORT_CLOSED_ERROR                           42
 #define C_ASCIIZ_REPRESENTATION_ERROR                 43
 #define C_MEMORY_VIOLATION_ERROR                      44
@@ -1180,6 +1180,8 @@ typedef void (C_ccall *C_proc)(C_word, C_word *) C_noret;
 #define C_vectorp(x)              C_mk_bool(C_header_bits(x) == C_VECTOR_TYPE)
 #define C_bytevectorp(x)          C_mk_bool(C_header_bits(x) == C_BYTEVECTOR_TYPE)
 #define C_portp(x)                C_mk_bool(C_header_bits(x) == C_PORT_TYPE)
+#define C_input_portp(x)          C_mk_bool(C_header_bits(x) == C_PORT_TYPE && C_block_item(x, 1) & 0x2)
+#define C_output_portp(x)         C_mk_bool(C_header_bits(x) == C_PORT_TYPE && C_block_item(x, 1) & 0x4)
 #define C_structurep(x)           C_mk_bool(C_header_bits(x) == C_STRUCTURE_TYPE)
 #define C_locativep(x)            C_mk_bool(C_block_header(x) == C_LOCATIVE_TAG)
 #define C_charp(x)                C_mk_bool(((x) & C_IMMEDIATE_TYPE_BITS) == C_CHARACTER_BITS)
diff --git a/library.scm b/library.scm
index 20dd7bfa..1c24ff19 100644
--- a/library.scm
+++ b/library.scm
@@ -2494,36 +2494,34 @@ EOF
 
 ;;; Ports:
 
-(define (port? x) (##core#inline "C_i_portp" x))
-
-(define-inline (%port? x)
+(define (port? x)
   (and (##core#inline "C_blockp" x)
-       (##core#inline "C_portp" x)) )
+       (##core#inline "C_portp" x)))
 
 (define (input-port? x)
-  (and (%port? x)
-       (##sys#slot x 1) ) )
+  (and (##core#inline "C_blockp" x)
+       (##core#inline "C_input_portp" x)))
 
 (define (output-port? x)
-  (and (%port? x)
-       (not (##sys#slot x 1)) ) )
+  (and (##core#inline "C_blockp" x)
+       (##core#inline "C_output_portp" x)))
 
 (define (port-closed? p)
   (##sys#check-port p 'port-closed?)
-  (##sys#slot p 8))
+  (fx= (##sys#slot p 8) 0))
 
 
 ;;; Port layout:
 ;
 ; 0:  FP (special)
-; 1:  input/output (bool)
+; 1:  direction (fixnum)
 ; 2:  class (vector of procedures)
 ; 3:  name (string)
 ; 4:  row (fixnum)
 ; 5:  col (fixnum)
 ; 6:  EOF (bool)
 ; 7:  type ('stream | 'custom | 'string | 'socket)
-; 8:  closed (bool)
+; 8:  closed (fixnum)
 ; 9:  data
 ; 10-15: reserved, port class specific
 ;
@@ -2548,6 +2546,7 @@ EOF
     (##sys#setislot port 4 1)
     (##sys#setislot port 5 0)
     (##sys#setslot port 7 type)
+    (##sys#setslot port 8 i/o)
     port) )
 
 ;;; Stream ports:
@@ -2585,7 +2584,7 @@ EOF
 	    (##core#inline "C_display_char" p c) )
 	  (lambda (p s)			; write-string
 	    (##core#inline "C_display_string" p s) )
-	  (lambda (p)			; close
+	  (lambda (p d)			; close
 	    (##core#inline "C_close_file" p)
 	    (##sys#update-errno) )
 	  (lambda (p)			; flush-output
@@ -2656,9 +2655,9 @@ EOF
 
 (define ##sys#open-file-port (##core#primitive "C_open_file_port"))
 
-(define ##sys#standard-input (##sys#make-port #t ##sys#stream-port-class "(stdin)" 'stream))
-(define ##sys#standard-output (##sys#make-port #f ##sys#stream-port-class "(stdout)" 'stream))
-(define ##sys#standard-error (##sys#make-port #f ##sys#stream-port-class "(stderr)" 'stream))
+(define ##sys#standard-input (##sys#make-port 1 ##sys#stream-port-class "(stdin)" 'stream))
+(define ##sys#standard-output (##sys#make-port 2 ##sys#stream-port-class "(stdout)" 'stream))
+(define ##sys#standard-error (##sys#make-port 2 ##sys#stream-port-class "(stderr)" 'stream))
 
 (##sys#open-file-port ##sys#standard-input 0 #f)
 (##sys#open-file-port ##sys#standard-output 1 #f)
@@ -2666,13 +2665,13 @@ EOF
 
 (define (##sys#check-input-port x open . loc)
   (if (pair? loc)
-      (##core#inline "C_i_check_port_2" x #t open (car loc))
-      (##core#inline "C_i_check_port" x #t open) ) )
+      (##core#inline "C_i_check_port_2" x 1 open (car loc))
+      (##core#inline "C_i_check_port" x 1 open)))
 
 (define (##sys#check-output-port x open . loc)
   (if (pair? loc)
-      (##core#inline "C_i_check_port_2" x #f open (car loc))
-      (##core#inline "C_i_check_port" x #f open) ) )
+      (##core#inline "C_i_check_port_2" x 2 open (car loc))
+      (##core#inline "C_i_check_port" x 2 open)))
 
 (define (##sys#check-port x . loc)
   (if (pair? loc)
@@ -2753,24 +2752,25 @@ EOF
                (##sys#error loc "cannot use append mode with input file")
                (set! fmode "a") ) ]
             [else (##sys#error loc "invalid file option" o)] ) ) )
-      (let ([port (##sys#make-port inp ##sys#stream-port-class name 'stream)])
+      (let ((port (##sys#make-port (if inp 1 2) ##sys#stream-port-class name 'stream)))
         (unless (##sys#open-file-port port name (##sys#string-append fmode bmode))
           (##sys#update-errno)
           (##sys#signal-hook #:file-error loc (##sys#string-append "cannot open file - " strerror) name) )
         port) ) )
 
-  (define (close port loc)
+  (define (close port inp loc)
     (##sys#check-port port loc)
-    ;; repeated closing is ignored
-    (unless (##sys#slot port 8)		; closed?
-      ((##sys#slot (##sys#slot port 2) 4) port) ; close
-      (##sys#setislot port 8 #t) )
-    (##core#undefined) )
+    ; repeated closing is ignored
+    (let* ((old-closed (##sys#slot port 8))
+	   (new-closed (fxand old-closed (fxnot (if inp 1 2)))))
+      (unless (fx= new-closed old-closed) ; already closed?
+	(##sys#setislot port 8 new-closed)
+	((##sys#slot (##sys#slot port 2) 4) port inp))))
 
   (set! open-input-file (lambda (name . mode) (open name #t mode 'open-input-file)))
   (set! open-output-file (lambda (name . mode) (open name #f mode 'open-output-file)))
-  (set! close-input-port (lambda (port) (close port 'close-input-port)))
-  (set! close-output-port (lambda (port) (close port 'close-output-port))) )
+  (set! close-input-port (lambda (port) (close port #t 'close-input-port)))
+  (set! close-output-port (lambda (port) (close port #f 'close-output-port))))
 
 (define call-with-input-file
   (let ([open-input-file open-input-file]
@@ -2857,12 +2857,12 @@ EOF
   (##sys#setslot port 3 name) )
 
 (define (##sys#port-line port)
-  (and (##sys#slot port 1) 
+  (and (fxodd? (##sys#slot port 1)) ; input port?
        (##sys#slot port 4) ) )
 
 (define (port-position #!optional (port ##sys#standard-input))
   (##sys#check-port port 'port-position)
-  (if (##sys#slot port 1) 
+  (if (fxodd? (##sys#slot port 1)) ; input port?
       (##sys#values (##sys#slot port 4) (##sys#slot port 5))
       (##sys#error 'port-position "cannot compute position of port" port) ) )
 
@@ -4071,9 +4071,10 @@ EOF
 		 (outstr port (##sys#lambda-info->string x))
 		 (outchr port #\>) )
 		((##core#inline "C_portp" x)
-		 (if (##sys#slot x 1)
-		     (outstr port "#<input port \"")
-		     (outstr port "#<output port \"") )
+		 (case (##sys#slot x 1)
+		   ((1)  (outstr port "#<input port \""))
+		   ((2)  (outstr port "#<output port \""))
+		   (else (outstr port "#<port \"")))
 		 (outstr port (##sys#slot x 3))
 		 (outstr port "\">") )
 		((##core#inline "C_vectorp" x)
@@ -4295,14 +4296,14 @@ EOF
 
 (define (open-input-string string)
   (##sys#check-string string 'open-input-string)
-  (let ([port (##sys#make-port #t ##sys#string-port-class "(string)" 'string)])
+  (let ((port (##sys#make-port 1 ##sys#string-port-class "(string)" 'string)))
     (##sys#setislot port 11 (##core#inline "C_block_size" string))
     (##sys#setislot port 10 0)
     (##sys#setslot port 12 string)
     port ) )
 
 (define (open-output-string)
-  (let ([port (##sys#make-port #f ##sys#string-port-class "(string)" 'string)])
+  (let ((port (##sys#make-port 2 ##sys#string-port-class "(string)" 'string)))
     (##sys#setislot port 10 0)
     (##sys#setislot port 11 output-string-initial-size)
     (##sys#setslot port 12 (##sys#make-string output-string-initial-size))
@@ -4905,8 +4906,7 @@ EOF
 	((37) (apply ##sys#signal-hook #:type-error loc "bad argument type - not a boolean" args))
 	((38) (apply ##sys#signal-hook #:type-error loc "bad argument type - not a locative" args))
 	((39) (apply ##sys#signal-hook #:type-error loc "bad argument type - not a port" args))
-	((40) (apply ##sys#signal-hook #:type-error loc "bad argument type - not an input-port" args))
-	((41) (apply ##sys#signal-hook #:type-error loc "bad argument type - not an output-port" args))
+	((40) (apply ##sys#signal-hook #:type-error loc "bad argument type - not a port of the correct type" args))
 	((42) (apply ##sys#signal-hook #:file-error loc "port already closed" args))
 	((43) (apply ##sys#signal-hook #:type-error loc "cannot represent string with NUL bytes as C string" args))
 	((44) (apply ##sys#signal-hook #:memory-error loc "segmentation violation" args))
diff --git a/ports.scm b/ports.scm
index 1744c356..33390c47 100644
--- a/ports.scm
+++ b/ports.scm
@@ -254,9 +254,8 @@
 			  last) ] ) ) )
 	     #f				; write-char
 	     #f				; write-string
-	     (lambda (p)		; close
-	       (close)
-	       (##sys#setislot p 8 #t) )
+	     (lambda (p d)		; close
+	       (close))
 	     #f				; flush-output
 	     (lambda (p)		; char-ready?
 	       (ready?) )
@@ -264,7 +263,7 @@
 	     read-line			; read-line
 	     read-buffered))
 	   (data (vector #f))
-	   (port (##sys#make-port #t class "(custom)" 'custom)) )
+	   (port (##sys#make-port 1 class "(custom)" 'custom)))
       (##sys#set-port-data! port data) 
       port) ) )
 
@@ -278,16 +277,15 @@
 	       (write (string c)) )
 	     (lambda (p s)		; write-string
 	       (write s) )
-	     (lambda (p)		; close
-	       (close)
-	       (##sys#setislot p 8 #t) )
+	     (lambda (p d)		; close
+	       (close))
 	     (lambda (p)		; flush-output
 	       (when flush (flush)) )
 	     #f				; char-ready?
 	     #f				; read-string!
 	     #f) )			; read-line
 	   (data (vector #f))
-	   (port (##sys#make-port #f class "(custom)" 'custom)) )
+	   (port (##sys#make-port 2 class "(custom)" 'custom)))
       (##sys#set-port-data! port data) 
       port) ) )
 
diff --git a/posix-common.scm b/posix-common.scm
index 991ac7d2..4bb21fbd 100644
--- a/posix-common.scm
+++ b/posix-common.scm
@@ -380,7 +380,7 @@ EOF
   (define (check loc fd inp r)
     (if (##sys#null-pointer? r)
         (posix-error #:file-error loc "cannot open file" fd)
-        (let ([port (##sys#make-port inp ##sys#stream-port-class "(fdport)" 'stream)])
+        (let ((port (##sys#make-port (if inp 1 2) ##sys#stream-port-class "(fdport)" 'stream)))
           (##core#inline "C_set_file_ptr" port r)
           port) ) )
   (set! open-input-file*
diff --git a/posixunix.scm b/posixunix.scm
index 3e25ff5c..63cef98c 100644
--- a/posixunix.scm
+++ b/posixunix.scm
@@ -662,7 +662,7 @@ EOF
   (define (check loc cmd inp r)
     (if (##sys#null-pointer? r)
 	(posix-error #:file-error loc "cannot open pipe" cmd)
-	(let ([port (##sys#make-port inp ##sys#stream-port-class "(pipe)" 'stream)])
+	(let ((port (##sys#make-port (if inp 1 2) ##sys#stream-port-class "(pipe)" 'stream)))
 	  (##core#inline "C_set_file_ptr" port r)
 	  port) ) )
   (set! open-input-pipe
@@ -1122,12 +1122,10 @@ EOF
 		   (lambda ()		; char-ready?
 		     (or (fx< bufpos buflen)
 			 (ready?)) )
-		   (lambda ()	      ; close
-					; Do nothing when closed already
-		     (unless (##sys#slot this-port 8)
-		       (when (fx< (##core#inline "C_close" fd) 0)
-			 (posix-error #:file-error loc "cannot close" fd nam) )
-		       (on-close) ) )
+		   (lambda ()		; close
+		     (when (fx< (##core#inline "C_close" fd) 0)
+		       (posix-error #:file-error loc "cannot close" fd nam))
+		     (on-close))
 		   (lambda ()		; peek-char
 		     (when (fx>= bufpos buflen)
 		       (fetch))
@@ -1233,11 +1231,10 @@ EOF
 		(make-output-port
 		 (lambda (str)		; write-string
 		   (store str) )
-		 (lambda ()	      ; close - do nothing when closed already
-		   (unless (##sys#slot this-port 8)
-		     (when (fx< (##core#inline "C_close" fd) 0)
-		       (posix-error #:file-error loc "cannot close" fd nam) )
-		     (on-close) ) )
+		 (lambda ()		; close
+		   (when (fx< (##core#inline "C_close" fd) 0)
+		     (posix-error #:file-error loc "cannot close" fd nam))
+		   (on-close))
 		 (lambda ()		; flush
 		   (store #f) ) )] )
 	(set-port-name! this-port nam)
diff --git a/posixwin.scm b/posixwin.scm
index 59776b24..9ad9eff7 100644
--- a/posixwin.scm
+++ b/posixwin.scm
@@ -882,7 +882,7 @@ EOF
     (##sys#update-errno)
     (if (##sys#null-pointer? r)
 	(##sys#signal-hook #:file-error "cannot open pipe" cmd)
-	(let ([port (##sys#make-port inp ##sys#stream-port-class "(pipe)" 'stream)])
+	(let ((port (##sys#make-port (if inp 1 2) ##sys#stream-port-class "(pipe)" 'stream)))
 	  (##core#inline "C_set_file_ptr" port r)
 	  port) ) )
   (set! open-input-pipe
@@ -1072,7 +1072,7 @@ EOF
     (##sys#update-errno)
     (if (##sys#null-pointer? r)
 	(##sys#signal-hook #:file-error "cannot open file" fd)
-	(let ([port (##sys#make-port inp ##sys#stream-port-class "(fdport)" 'stream)])
+	(let ((port (##sys#make-port (if inp 1 2) ##sys#stream-port-class "(fdport)" 'stream)))
 	  (##core#inline "C_set_file_ptr" port r)
 	  port) ) )
   (set! open-input-file*
diff --git a/runtime.c b/runtime.c
index a7282f84..c7a35cb3 100644
--- a/runtime.c
+++ b/runtime.c
@@ -1882,13 +1882,8 @@ void barf(int code, char *loc, ...)
     c = 1;
     break;
 
-  case C_BAD_ARGUMENT_TYPE_NO_INPUT_PORT_ERROR:
-    msg = C_text("bad argument type - not an input-port");
-    c = 1;
-    break;
-
-  case C_BAD_ARGUMENT_TYPE_NO_OUTPUT_PORT_ERROR:
-    msg = C_text("bad argument type - not an output-port");
+  case C_BAD_ARGUMENT_TYPE_PORT_DIRECTION_ERROR:
+    msg = C_text("bad argument type - not a port of the correct type");
     c = 1;
     break;
 
@@ -7062,39 +7057,21 @@ C_regparm C_word C_fcall C_i_check_list_2(C_word x, C_word loc)
 }
 
 
-C_regparm C_word C_fcall C_i_check_port_2(C_word x, C_word input, C_word open, C_word loc)
+C_regparm C_word C_fcall C_i_check_port_2(C_word x, C_word dir, C_word open, C_word loc)
 {
-  int inp;
 
   if(C_immediatep(x) || C_header_bits(x) != C_PORT_TYPE) {
     error_location = loc;
     barf(C_BAD_ARGUMENT_TYPE_NO_PORT_ERROR, NULL, x);
   }
 
-  inp = C_block_item(x, 1) == C_SCHEME_TRUE;	/* slot #1: I/O flag */
-
-  switch(input) {
-  case C_SCHEME_TRUE:
-    if(!inp) {
-      error_location = loc;
-      barf(C_BAD_ARGUMENT_TYPE_NO_INPUT_PORT_ERROR, NULL, x);
-    }
-
-    break;
-
-  case C_SCHEME_FALSE:
-    if(inp) {
-      error_location = loc;
-      barf(C_BAD_ARGUMENT_TYPE_NO_OUTPUT_PORT_ERROR, NULL, x);
-    }
-
-    break;
-
-    /* any other value: omit direction check */
+  if((C_block_item(x, 1) & dir) != dir) {	/* slot #1: I/O direction mask */
+    error_location = loc;
+    barf(C_BAD_ARGUMENT_TYPE_PORT_DIRECTION_ERROR, NULL, x);
   }
 
   if(open == C_SCHEME_TRUE) {
-    if(C_block_item(x, 8) != C_SCHEME_FALSE) {	/* slot #8: closed flag */
+    if(C_block_item(x, 8) == C_FIXNUM_BIT) {	/* slot #8: closed mask */
       error_location = loc;
       barf(C_PORT_CLOSED_ERROR, NULL, x);
     }
diff --git a/tcp.scm b/tcp.scm
index 5f5e5196..bdb90cab 100644
--- a/tcp.scm
+++ b/tcp.scm
@@ -657,10 +657,7 @@ EOF
 
 (define (tcp-abandon-port p)
   (##sys#check-open-port p 'tcp-abandon-port)
-  (##sys#setislot
-   (##sys#port-data p)
-   (if (##sys#slot p 1) 1 2)
-   #t) )
+  (##sys#setislot (##sys#port-data p) (##sys#slot p 1) #t))
 
 (define (tcp-listener-fileno l)
   (##sys#check-structure l 'tcp-listener 'tcp-listener-fileno)
diff --git a/tests/port-tests.scm b/tests/port-tests.scm
index 78565e35..49b8e138 100644
--- a/tests/port-tests.scm
+++ b/tests/port-tests.scm
@@ -95,6 +95,35 @@ EOF
      (lambda (in) (read-char in)))
     (get-output-string out))))
 
+;; direction-specific port closure
+
+(let* ((n 0)
+       (p (make-input-port (constantly #\a)
+			   (constantly #t)
+			   (lambda () (set! n (add1 n))))))
+  (close-output-port p)
+  (assert (not (port-closed? p)))
+  (assert (= n 0))
+  (close-input-port p)
+  (assert (port-closed? p))
+  (assert (= n 1))
+  (close-input-port p)
+  (assert (port-closed? p))
+  (assert (= n 1)))
+
+(let* ((n 0)
+       (p (make-output-port (lambda () (display #\a))
+			    (lambda () (set! n (add1 n))))))
+  (close-input-port p)
+  (assert (not (port-closed? p)))
+  (assert (= n 0))
+  (close-output-port p)
+  (assert (port-closed? p))
+  (assert (= n 1))
+  (close-output-port p)
+  (assert (port-closed? p))
+  (assert (= n 1)))
+
 ;; fill buffers
 (with-input-from-file "compiler.scm" read-string)
 
diff --git a/types.db b/types.db
index d2b2b0ba..dc070f75 100644
--- a/types.db
+++ b/types.db
@@ -1251,7 +1251,7 @@
 (port? (#(procedure #:pure #:predicate (or input-port output-port)) port? (*) boolean))
 
 (port-closed? (#(procedure #:clean #:enforce) port-closed? (port) boolean)
-	      ((port) (##sys#slot #(1) '8)))
+	      ((port) (eq? (##sys#slot #(1) '8) '0)))
 
 (print (procedure print (#!rest *) undefined))
 (print-call-chain (#(procedure #:clean #:enforce) print-call-chain (#!optional output-port fixnum * string) undefined))
@@ -1403,13 +1403,13 @@
 
 (##sys#check-input-port
  (#(procedure #:clean #:enforce) ##sys#check-input-port (input-port * #!optional *) *)
- ((* *) (##core#inline "C_i_check_port" #(1) '#t #(2)))
- ((* * *) (##core#inline "C_i_check_port_2" #(1) '#t #(2) #(3))))
+ ((* *) (##core#inline "C_i_check_port" #(1) '1 #(2)))
+ ((* * *) (##core#inline "C_i_check_port_2" #(1) '1 #(2) #(3))))
 
 (##sys#check-output-port
  (#(procedure #:clean #:enforce) ##sys#check-output-port (output-port * #!optional *) *)
- ((* *) (##core#inline "C_i_check_port" #(1) '#f #(2)))
- ((* * *) (##core#inline "C_i_check_port_2" #(1) '#f #(2) #(3))))
+ ((* *) (##core#inline "C_i_check_port" #(1) '2 #(2)))
+ ((* * *) (##core#inline "C_i_check_port_2" #(1) '2 #(2) #(3))))
 
 (##sys#check-open-port
  (#(procedure #:clean #:enforce) ##sys#check-open-port ((or input-port output-port) #!optional *) *)
Trap