[Mono-devel-list] streamwriter fixes

Paolo Molaro lupus at ximian.com
Mon Aug 18 12:39:00 EDT 2003


I had these fixes in my tree for a few months, but I can't say
I have tested them much. This should get the writer classes closer to
the documentation (which says only Write(char) must be implemented by a
subclass, previous code had Wrute(char[]) as the required method to
implement in a derived class) and it may give a small speedup, since
decoding is done in largers buffers at a time.
Please test and review.

lupus

-- 
-----------------------------------------------------------------
lupus at debian.org                                     debian/rules
lupus at ximian.com                             Monkeys do it better

Index: StreamWriter.cs
===================================================================
RCS file: /cvs/public/mcs/class/corlib/System.IO/StreamWriter.cs,v
retrieving revision 1.19
diff -u -p -r1.19 StreamWriter.cs
--- StreamWriter.cs	19 Nov 2002 12:05:09 -0000	1.19
+++ StreamWriter.cs	18 Aug 2003 16:41:36 -0000
@@ -1,8 +1,9 @@
 //
 // System.IO.StreamWriter.cs
 //
-// Author:
+// Authors:
 //   Dietmar Maurer (dietmar at ximian.com)
+//   Paolo Molaro (lupus at ximian.com)
 //
 // (C) Ximian, Inc.  http://www.ximian.com
 //
@@ -24,11 +25,12 @@ namespace System.IO {
 		
 		private const int DefaultBufferSize = 1024;
 		private const int DefaultFileBufferSize = 4096;
-		private const int MinimumBufferSize = 2;
+		private const int MinimumBufferSize = 256;
 
-		private int pos;
-		private int BufferSize;
-		private byte[] TheBuffer;
+		private byte[] byte_buf;
+		private int byte_pos;
+		private char[] decode_buf;
+		private int decode_pos;
 
 		private bool DisposedAlready = false;
 		private bool preamble_done = false;
@@ -43,9 +45,10 @@ namespace System.IO {
 
 		internal void Initialize(Encoding encoding, int bufferSize) {
 			internalEncoding = encoding;
-			pos = 0;
-			BufferSize = Math.Max(bufferSize, MinimumBufferSize);
-			TheBuffer = new byte[BufferSize];
+			decode_pos = byte_pos = 0;
+			int BufferSize = Math.Max(bufferSize, MinimumBufferSize);
+			decode_buf = new char [BufferSize];
+			byte_buf = new byte [encoding.GetMaxByteCount (BufferSize)];
 		}
 
 		//[MonoTODO("Nothing is done with bufferSize")]
@@ -136,75 +139,119 @@ namespace System.IO {
 			}
 
 			internalStream = null;
-			TheBuffer = null;
+			byte_buf = null;
 			internalEncoding = null;
+			decode_buf = null;
 		}
 
 		public override void Flush () {
 			if (DisposedAlready)
 				throw new ObjectDisposedException("StreamWriter");
 
-			if (pos > 0) {
-				internalStream.Write (TheBuffer, 0, pos);
+			Decode ();
+			if (byte_pos > 0) {
+				FlushBytes ();
 				internalStream.Flush ();
-				pos = 0;
 			}
 		}
-		
-		public override void Write (char[] buffer, int index, int count) {
-			if (DisposedAlready)
-				throw new ObjectDisposedException("StreamWriter");
-
-			byte[] res = new byte [internalEncoding.GetByteCount (buffer)];
-			int len;
-			int BytesToBuffer;
-			int resPos = 0;
 
-			len = internalEncoding.GetBytes (buffer, index, count, res, 0);
+		// how the speedup works:
+		// the Write () methods simply copy the characters in a buffer of chars (decode_buf)
+		// Decode () is called when the buffer is full or we need to flash.
+		// Decode () will use the encoding to get the bytes and but them inside
+		// byte_buf. From byte_buf the data is finally outputted to the stream.
+		void FlushBytes () {
 			// write the encoding preamble only at the start of the stream
-			if (!preamble_done && len > 0) {
+			if (!preamble_done && byte_pos > 0) {
 				byte[] preamble = internalEncoding.GetPreamble ();
 				if (preamble.Length > 0)
 					internalStream.Write (preamble, 0, preamble.Length);
 				preamble_done = true;
 			}
+			internalStream.Write (byte_buf, 0, byte_pos);
+			byte_pos = 0;
+		}
+		
+		void Decode () {
+			if (byte_pos > 0)
+				FlushBytes ();
+			if (decode_pos > 0) {
+				int len = internalEncoding.GetBytes (decode_buf, 0, decode_pos, byte_buf, byte_pos);
+				byte_pos += len;
+				decode_pos = 0;
+			}
+		}
+		
+		public override void Write (char[] buffer, int index, int count) {
+			if (DisposedAlready)
+				throw new ObjectDisposedException("StreamWriter");
+
+			if (buffer == null)
+				throw new ArgumentNullException ("buffer");
+			if (index < 0 || index > buffer.Length)
+				throw new ArgumentOutOfRangeException ("index");
+			if (count < 0 || (index + count) > buffer.Length)
+				throw new ArgumentOutOfRangeException ("count");
 
-			// if they want AutoFlush, don't bother buffering
-			if (iflush) {
+			LowLevelWrite (buffer, index, count);
+			if (iflush)
 				Flush();
-				internalStream.Write (res, 0, len);
-				internalStream.Flush ();
-			} else {
-				// otherwise use the buffer.
-				// NOTE: this logic is not optimized for performance.
-				while (resPos < len) {
-					// fill the buffer if we've got more bytes than will fit
-					BytesToBuffer = Math.Min(BufferSize - pos, len - resPos);
-					Array.Copy(res, resPos, TheBuffer, pos, BytesToBuffer);
-					resPos += BytesToBuffer;
-					pos += BytesToBuffer;
-					// if the buffer is full, flush it out.
-					if (pos == BufferSize) Flush();
+		}
+		
+		void LowLevelWrite (char[] buffer, int index, int count) {
+			while (count > 0) {
+				int todo = decode_buf.Length - decode_pos;
+				if (todo == 0) {
+					Decode ();
+					todo = decode_buf.Length;
 				}
+				if (todo > count)
+					todo = count;
+				Buffer.BlockCopy (buffer, index * 2, decode_buf, decode_pos * 2, todo * 2);
+				count -= todo;
+				index += todo;
+				decode_pos += todo;
 			}
 		}
 
 		public override void Write (char value)
 		{
-			Write (new char [] {value}, 0, 1);
+			// the size of decode_buf is always > 0 and
+			// we check for overflow right away
+			if (decode_pos >= decode_buf.Length)
+				Decode ();
+			decode_buf [decode_pos++] = value;
+			if (iflush)
+				Flush ();
 		}
 
 		public override void Write (char [] value)
 		{
-			Write (value, 0, value.Length);
+			LowLevelWrite (value, 0, value.Length);
+			if (iflush)
+				Flush ();
+		}
+
+		public override void Write (string value) {
+			if (DisposedAlready)
+				throw new ObjectDisposedException("StreamWriter");
+
+			if (value != null)
+				LowLevelWrite (value.ToCharArray (), 0, value.Length);
+			if (iflush)
+				Flush ();
 		}
 
-		public override void Write(string value) {
+		public override void WriteLine (string value) {
 			if (DisposedAlready)
 				throw new ObjectDisposedException("StreamWriter");
 
 			if (value != null)
-				Write (value.ToCharArray (), 0, value.Length);
+				LowLevelWrite (value.ToCharArray (), 0, value.Length);
+			string nl = NewLine;
+				LowLevelWrite (nl.ToCharArray (), 0, nl.Length);
+			if (iflush)
+				Flush ();
 		}
 
 		public override void Close()
Index: TextWriter.cs
===================================================================
RCS file: /cvs/public/mcs/class/corlib/System.IO/TextWriter.cs,v
retrieving revision 1.12
diff -u -p -r1.12 TextWriter.cs
--- TextWriter.cs	5 Apr 2003 19:00:53 -0000	1.12
+++ TextWriter.cs	18 Aug 2003 16:41:36 -0000
@@ -4,6 +4,7 @@
 // Authors:
 //   Marcin Szczepanski (marcins at zipworld.com.au)
 //   Miguel de Icaza (miguel at gnome.org)
+//   Paolo Molaro (lupus at ximian.com)
 //
 
 using System.Text;
@@ -14,14 +15,14 @@ namespace System.IO {
 	public abstract class TextWriter : MarshalByRefObject, IDisposable {
                 
                 protected TextWriter() {
-			CoreNewLine = "\n".ToCharArray ();
+			CoreNewLine = System.Environment.NewLine;
 		}
                 
                 protected TextWriter( IFormatProvider formatProvider ) {
                         internalFormatProvider = formatProvider;
                 }
 
-                protected char[] CoreNewLine;
+                protected string CoreNewLine;
 
                 internal IFormatProvider internalFormatProvider;
 
@@ -37,11 +38,11 @@ namespace System.IO {
 
                 public virtual string NewLine { 
                         get {
-                                return new String(CoreNewLine);
+                                return CoreNewLine;
                         }
                         
                         set {
-                                CoreNewLine = value.ToCharArray();
+                                CoreNewLine = value;
                         }
                 }
 
@@ -84,8 +85,10 @@ namespace System.IO {
 
                 public virtual void Write (char[] value)
 		{
-			if (value != null)
-				Write (new String (value));
+			if (value != null) {
+				for (int i = 0; i < value.Length; ++i)
+					Write (value [i]);
+			}
 		}
 		
                 public virtual void Write (decimal value)
@@ -121,7 +124,8 @@ namespace System.IO {
 		
                 public virtual void Write (string value)
 		{
-			// do nothing
+			if (value != null)
+				Write (value.ToCharArray ());
 		}
 		
 		[CLSCompliant(false)]
@@ -148,7 +152,15 @@ namespace System.IO {
 		
                 public virtual void Write (char[] buffer, int index, int count)
 		{
-			Write (new String (buffer, index, count));
+			if (buffer == null)
+				throw new ArgumentNullException ("buffer");
+			if (index < 0 || index > buffer.Length)
+				throw new ArgumentOutOfRangeException ("index");
+			if (count < 0 || (index + count) > buffer.Length)
+				throw new ArgumentOutOfRangeException ("count");
+			for (; count > 0; --count, ++index) {
+				Write (buffer [index]);
+			}
 		}
 		
                 public virtual void Write (string format, object arg0, object arg1)
@@ -281,6 +293,12 @@ namespace System.IO {
 			}
 			
 			public override void Write (string s)
+			{
+			}
+			public override void Write (char value)
+			{
+			}
+			public override void Write (char[] value, int index, int count)
 			{
 			}
 		}



More information about the Mono-devel-list mailing list