[m-rev.] For review: Java implementation of IO library

James Goddard goddardjames at yahoo.com
Thu Jan 15 14:07:10 AEDT 2004


Is this what you had in mind re binary stdin/stdout?  They now read/write data
using the same method as randomaccess, so in that respect they're
treated the same as binary files.  The only way to allow proper seeking on
stdin and stdout would be to buffer them entirely in memory.
Would that be preferable?

Also, I've added some more comments and made some of the casting more
explicit.  Java now writes binary files in the same fashion as the C#
version, grabbing the lower 8 bits of each byte.

diff -u io.m io.m
--- io.m	14 Jan 2004 05:29:03 -0000
+++ io.m	15 Jan 2004 01:49:26 -0000
@@ -4832,7 +4832,7 @@
 		public	static int			ML_next_stream_id = 0;
 		public	int				id;
 
-		public	int				line_number;
+		public	int				line_number	= 1;
 
 		public	static final int		INPUT		= 0;
 		public	static final int		OUTPUT		= 1;
@@ -4847,35 +4847,38 @@
 		private java.io.OutputStreamWriter	output		= null;
 		private java.io.RandomAccessFile	randomaccess	= null;
 
-		private java.lang.String		filename	= null;
+		/* binary_stdin only */
+		private java.io.BufferedInputStream	binary_input	= null;
+		/* binary_stdout only */
+		private java.io.BufferedOutputStream	binary_output	= null;
+		int					position	= 0;
 
+		/*
+		** This constructor is for text input streams.
+		*/
 		public MR_MercuryFileStruct(java.io.InputStream stream) {
-			line_number	= 1;
-			id		= ML_next_stream_id++;
-			mode		= INPUT;
-			pushback	= new java.util.Stack();
-			input		= new java.io.InputStreamReader(
-							stream);
+			this(stream, false);
 		}
 
+		/*
+		** This constructor is for text output streams.
+		*/
 		public MR_MercuryFileStruct(java.io.OutputStream stream) {
-			line_number	= 1;
-			id		= ML_next_stream_id++;
-			mode		= OUTPUT;
-			output		= new java.io.OutputStreamWriter(
-							stream);
+			this(stream, false);
 		}
 
+		/*
+		** This constructor handles binary files (in or out) but does
+		** not cover mercury_stdin_binary/mecury_stdout_binary.
+		*/
 		public MR_MercuryFileStruct(java.lang.String file, char mode) {
-			line_number	= 1;
 			id		= ML_next_stream_id++;
-			filename	= file;
 			String openstring;
 			if (mode == 'r') {
 				openstring = ""r"";
 				this.mode = INPUT;
 				pushback = new java.util.Stack();
-			} else if (mode == 'r' || mode == 'a') {
+			} else if (mode == 'w' || mode == 'a') {
 				openstring = ""rw"";
 				this.mode = OUTPUT;
 				// There is no such mode as ""w"", which could
@@ -4897,20 +4900,101 @@
 			}
 		}
 
+		/*
+		** This constructor is only used for mercury_stdin_binary. It
+		** opens stdin as an approximation of a binary file and allows
+		** a limited amount of seeking.
+		*/
+		public MR_MercuryFileStruct(java.io.InputStream stream,
+				boolean openAsBinary)
+		{
+			if (!openAsBinary) {
+				id		= ML_next_stream_id++;
+				mode		= INPUT;
+				pushback	= new java.util.Stack();
+				input		= new java.io.
+						InputStreamReader(stream);
+			} else {
+				id		= ML_next_stream_id++;
+				mode		= INPUT;
+				pushback	= new java.util.Stack();
+				
+				binary_input = new java.io.BufferedInputStream(
+						stream);
+			}
+		}
+
+		/*
+		** This constructor is only used for mercury_stdout_binary. It
+		** opens stdout as an approximation of a binary file and allows
+		** a limited amount of seeking.
+		*/
+		public MR_MercuryFileStruct(java.io.OutputStream stream,
+				boolean openAsBinary)
+		{
+			if (!openAsBinary) {
+				id		= ML_next_stream_id++;
+				mode		= OUTPUT;
+				output		= new java.io.
+						OutputStreamWriter(stream);
+			} else {
+				id		= ML_next_stream_id++;
+				mode		= OUTPUT;
+				
+				binary_output = new java.io.
+						BufferedOutputStream(stream);
+			}
+		}
+		
+		/*
+		** size():
+		**	Returns the length of a binary file. XXX Note that this
+		**	method will return -1 for mercury_stdin_binary and
+		**	the current position for mercury_stdout_binary.
+		*/
 		public int size() {
-			try {
-				return (int) randomaccess.length();
-			} catch (java.lang.Exception e) {
+			if (randomaccess != null) {
+				try {
+					return (int) randomaccess.length();
+				} catch (java.io.IOException e) {
+					return -1;
+				}
+			} else if (binary_output != null) {
+				return position;
+			} else {
 				return -1;
 			}
 		}
 
+		/*
+		** seek():
+		**	seek relative to start, current position or end
+		**	depending on the flag.
+		**	This function only works for binary files.
+		**	XXX Note that for mercury_stdin_binary, seek may be
+		**	only be done forwards from the current position and for
+		**	mercury_stdout_binary seek is not supported at all.
+		*/
 		public void seek(int flag, int offset) {
-			if (randomaccess == null) {
+			if (input != null || output != null) {
+				throw new java.lang.RuntimeException(
+						""text streams are not "" +
+						""seekable"");
+			}
+			if (binary_output != null) {
 				throw new java.lang.RuntimeException(
-						""Stream not seekable"");
-				// this applies to stdin, stdout and stderr
+						""mercury_stdout_binary is "" +
+						""not seekable"");
 			}
+			if (binary_input != null &&
+					(flag != SEEK_CUR || offset < 0))
+			{
+				throw new java.lang.RuntimeException(
+						""mercury_stdin_binary may "" +
+						""only seek forwards from "" +
+						""the current position"");
+			}
+
 			pushback = new java.util.Stack();
 			try {
 				switch (flag) {
@@ -4918,10 +5002,14 @@
 						randomaccess.seek(offset);
 						break;
 					case SEEK_CUR:
-						randomaccess.seek(
+						if (randomaccess != null) {
+						    randomaccess.seek(
 							randomaccess.
 							getFilePointer() +
 							offset);
+						} else {
+						    binary_input.skip(offset);
+						}
 						break;
 					case SEEK_END:
 						randomaccess.seek(
@@ -4939,17 +5027,31 @@
 			}
 		}
 
+		/*
+		** getOffset():
+		**	Returns the current position in a binary file.
+		*/
 		public int getOffset() {
-			try {
-				return (int) randomaccess.getFilePointer();
-			} catch (java.lang.Exception e) {
+			if (randomaccess != null) {
+				try {
+					return (int) randomaccess.
+							getFilePointer();
+				} catch (java.io.IOException e) {
+					return -1;
+				}
+			} else if (binary_input != null ||
+					binary_output != null)
+			{
+				return position;
+			} else {
 				return -1;
 			}
 		}
 
-		/* read_char():
-		**	Reads one character in using the default charset
-		**	decoding.  Returns -1 at end of file.
+		/*
+		** read_char():
+		**	Reads one character in from a text input file using the
+		**	default charset decoding.  Returns -1 at end of file.
 		*/
 		public int read_char() {
 			int c;
@@ -4960,13 +5062,13 @@
 			}
 			if (pushback.empty()) {
 				try {
-					c = input.read();
+					c = (char) input.read();
 				} catch (java.io.IOException e) {
 					throw new java.lang.RuntimeException(
 							e.getMessage());
 				}
 			} else {
-				c = ((java.lang.Integer)pushback.pop()).
+				c = (char) ((java.lang.Integer)pushback.pop()).
 						intValue();
 			}
 			if (c == '\\n') {
@@ -4976,7 +5078,8 @@
 			return c;
 		}
 
-		/* read_char():
+		/*
+		** read_byte():
 		**	Reads one byte in from a binary file.
 		**	Returns -1 at end of file.
 		*/
@@ -4986,29 +5089,43 @@
 				throw new java.lang.RuntimeException(
 					""Attempted to read output stream"");
 			}
-			if (randomaccess == null) {
+			if (randomaccess == null && binary_input == null) {
 				throw new java.lang.RuntimeException(
 					""read_byte_val may only be called"" +
 					"" on binary input streams"");
 			}
 			if (pushback.empty()) {
 				try {
-					c = randomaccess.read();
+					if (binary_input != null) {
+						c = (byte) binary_input.read();
+					} else {
+						c = (byte) randomaccess.read();
+					}
 				} catch (java.io.IOException e) {
 					throw new java.lang.RuntimeException(
 							e.getMessage());
 				}
 			} else {
-				c = ((java.lang.Integer)pushback.pop()).
+				c = (byte) ((java.lang.Integer)pushback.pop()).
 						intValue();
 			}
 			if (c == '\\n') {
 				line_number++;
 			}
+			if (binary_input != null) {
+				position++;
+			}
 
 			return c;
 		}
 
+		/*
+		** ungetc():
+		**	Pushes an integer, which may represent either a byte or
+		**	a character, onto the pushback stack.  This stack
+		**	is the same, regardless of whether the file is text or
+		**	binary.
+		*/
 		public void ungetc(int c) {
 			if (mode == OUTPUT) {
 				throw new java.lang.RuntimeException(
@@ -5021,7 +5138,15 @@
 			pushback.push(new Integer(c));
 		}
 
-		public void putc(int c) {
+		/*
+		** put():
+		**	Write one unit to an output stream.  If the file is
+		**	text, the int will hold a character.  If the file is
+		**	binary, this will be a single byte.  In the former case
+		**	we assume that the lower order 16 bits hold a char, in
+		**	the latter we take only the lowest 8 bits for a byte.
+		*/
+		public void put(int c) {
 			if (mode == INPUT) {
 				throw new java.lang.RuntimeException(
 					""Attempted to write to input stream"");
@@ -5029,12 +5154,17 @@
 			if (c == '\\n') {
 				line_number++;
 			}
+			if (binary_output != null) {
+				position++;
+			}
 	
 			try {
 				if (output != null) {
-					output.write(c);
+					output.write((char) c);
+				} else if (randomaccess != null) {
+					randomaccess.write((byte) c);
 				} else {
-					randomaccess.write(c);
+					binary_output.write((byte) c);
 				}
 			} catch (java.io.IOException e) {
 				throw new java.lang.RuntimeException(
@@ -5042,26 +5172,31 @@
 			}
 		}
 
+		/*
+		** write():
+		**	Writes a string to a file stream.  For text files, this
+		**	string is encoded as a byte stream using default
+		**	encoding.  For binary files, the lower order 8 bits of
+		**	each character are written.
+		*/
 		public void write(java.lang.String s) {
 			if (mode == INPUT) {
 				throw new java.lang.RuntimeException(
 					""Attempted to write to input stream"");
 			}
-			for (int i = 0; i < s.length(); i++) {
-				if (s.charAt(i) == '\\n') {
-					line_number++;
-				}
-			}
 			
 			try {
 				if (output != null) {
+					for (int i = 0; i < s.length(); i++) {
+						if (s.charAt(i) == '\\n') {
+							line_number++;
+						}
+					}
 					output.write(s);
 				} else {
-					randomaccess.write(s.getBytes());
-					// XXX for binary files, the call to
-					// getBytes() results in unspecified
-					// behaviour if the string cannot be
-					// decoded.
+					for (int i = 0; i < s.length(); i++) {
+						put(s.charAt(i));
+					}
 				}
 			} catch (java.io.IOException e) {
 				throw new java.lang.RuntimeException(
@@ -5079,6 +5214,9 @@
 				if (output != null) {
 					output.flush();
 				}
+				if (binary_output != null) {
+					binary_output.flush();
+				}
 				// else randomaccess is already unbuffered.
 			} catch (java.io.IOException e) {
 				throw new java.lang.RuntimeException(
@@ -5090,9 +5228,17 @@
 			try {
 				if (input != null) {
 					input.close();
-				} else if (output != null) {
+				}
+				if (output != null) {
 					output.close();
-				} else {
+				}
+				if (randomaccess != null) {
+					randomaccess.close();
+				}
+				if (binary_input != null) {
+					randomaccess.close();
+				}
+				if (binary_output != null) {
 					randomaccess.close();
 				}
 			} catch (java.io.IOException e) {
@@ -5129,7 +5275,7 @@
 					if (b == -1 || interrupted()) {
 						break;
 					}
-					out.putc(b);
+					out.put(b);
 				}
 				out.flush();
 				if (closeOutput) {
@@ -5253,16 +5399,18 @@
 static MR_MercuryFileStruct mercury_stderr =
 		new MR_MercuryFileStruct(java.lang.System.err);
 static MR_MercuryFileStruct mercury_stdin_binary =
-		new MR_MercuryFileStruct(java.lang.System.in);
+		new MR_MercuryFileStruct(java.lang.System.in, true);
 static MR_MercuryFileStruct mercury_stdout_binary =
-		new MR_MercuryFileStruct(java.lang.System.out);
+		new MR_MercuryFileStruct(java.lang.System.out, true);
 
 static MR_MercuryFileStruct mercury_current_text_input = mercury_stdin;
 static MR_MercuryFileStruct mercury_current_text_output = mercury_stdout;
-static MR_MercuryFileStruct mercury_current_binary_input = mercury_stdin_binary;
+static MR_MercuryFileStruct mercury_current_binary_input =
+		mercury_stdin_binary;
 static MR_MercuryFileStruct mercury_current_binary_output =
 		mercury_stdout_binary;
 		
+// XXX not thread-safe!
 static java.lang.Exception MR_io_exception;
 ").
 
@@ -6164,7 +6312,10 @@
 	io__write_byte(Byte::in, _IO0::di, _IO::uo),
 	[may_call_mercury, promise_pure, thread_safe, tabled_for_io],
 "
-	mercury_current_binary_output.putc(Byte);
+	// XXX possibly the Byte should be converted here somehow...
+	// we only want 8 bits..
+
+	mercury_current_binary_output.put(Byte);
 ").
 
 :- pragma foreign_proc("Java",
@@ -6416,7 +6567,7 @@
 	io__write_char(Stream::in, Character::in, _IO0::di, _IO::uo),
 	[may_call_mercury, promise_pure, thread_safe, tabled_for_io],
 "
-	Stream.putc(Character);
+	Stream.put(Character);
 ").
 
 :- pragma foreign_proc("Java",
@@ -6437,7 +6588,7 @@
 	io__write_byte(Stream::in, Byte::in, _IO0::di, _IO::uo),
 	[may_call_mercury, promise_pure, thread_safe, tabled_for_io],
 "
-	Stream.putc(Byte);
+	Stream.put(Byte);
 ").
 
 :- pragma foreign_proc("Java",
--------------------------------------------------------------------------
mercury-reviews mailing list
post:  mercury-reviews at cs.mu.oz.au
administrative address: owner-mercury-reviews at cs.mu.oz.au
unsubscribe: Address: mercury-reviews-request at cs.mu.oz.au Message: unsubscribe
subscribe:   Address: mercury-reviews-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------



More information about the reviews mailing list