StringStream.NET Source Code

The StringStream class is effectively a wrapper class around two adapter classes. The wrapper inherits from Stream and redirects all operations to its underlying adapter.  The adapters also inherit from Stream and supports read-only operations on an underlying String object, with automatic conversion to a MemoryStream object when write operations are attempted. 

All source code markup was produced by the excellent Copy As HTML Visual Studio extension.


    1 using System;
    2 using System.IO;
    3 using System.Text;
    4 
    5 public class StringStream : Stream
    6 {
    7 
    8     //////////////////////////////////////////////////////////////////////////////////
    9     //// StringStream
   10     //////////////////////////////////////////////////////////////////////////////////
   11     //// Permits any string to function as a read/write stream.
   12     ////
   13     //// This implementation saves space by initially creating a read-only stream 
   14     //// on a reference to the initial string. If write operations are attempted, 
   15     //// the stream converts itself into a MemoryStream for all future operations.
   16     //// 
   17     //// This class readily converts to/from System.String objects.
   18     //// Wrapper classes are employed to permit the StringStream to function 
   19     //// as either a read-only string stream, or as a read/write memory stream.
   20 
   21 
   22     //// Both StringAdapter and MemoryAdapter need these two additional methods.
   23     //// It would be nice if these classes could inherit from a common base class,
   24     //// but without multiple inheritance, the MemoryAdapter would have to wrap
   25     //// every method in the MemoryStream class, which would be too inefficient.
   26     //// This approach only requires the wrapping of the WriteTo method.
   27     public interface IStreamAdapter
   28     {
   29         void WriteTo(Stream stream);
   30         string ToString();
   31     }
   32 
   33     private const int _DefaultBufferSize = 16384;
   34 
   35     //////////////////////////////////////////////////////////////////////////////////
   36     //// StringAdapter
   37     //////////////////////////////////////////////////////////////////////////////////
   38     //// Uses the underlying String for all read operations.
   39     ////
   40     //// If a write operation is attempted, it converts itself into a MemoryStream, 
   41     //// then replaces the underlying Stream in the parent class via
   42     //// a callback sub
   43     ////
   44     protected class StringAdapter : Stream, IStreamAdapter
   45     {
   46 
   47         public delegate void ReplaceStreamDelegate(Stream stream);
   48 
   49         protected string _text;
   50         protected long _position;
   51         protected int _byteLength;
   52 
   53         protected int _bufferSize = _DefaultBufferSize;
   54         protected ReplaceStreamDelegate _replaceStreamCallback;
   55 
   56 
   57         private StringAdapter()
   58         {
   59             //// Default contructor disabled //
   60             //// Class must be created by custom constructor //
   61         }
   62 
   63         public StringAdapter(string text, ReplaceStreamDelegate replaceStreamCallback)
   64         {
   65             _replaceStreamCallback = replaceStreamCallback;
   66             _text = text;
   67             _byteLength = System.Text.Encoding.Unicode.GetByteCount(text);
   68         }
   69 
   70 
   71         ////////////////////////
   72         //// PUBLIC PROPERTIES
   73         ////////////////////////
   74 
   75         //// A buffer is used to copy the String to a stream in chunks
   76         //// (during the WriteTo method).
   77         public virtual int BufferSize
   78         {
   79             get { return _bufferSize; }
   80             set
   81             {
   82                 if (value < 1)
   83                 {
   84                     throw new ArgumentOutOfRangeException("buffer size must be greater than zero");
   85                 }
   86 
   87                 _bufferSize = value;
   88             }
   89         }
   90 
   91 
   92         public override long Length
   93         {
   94             get { return _byteLength; }
   95         }
   96 
   97 
   98         public override long Position
   99         {
  100             get { return _position; }
  101             set
  102             {
  103                 if (value < 0 || value > (long)int.MaxValue)
  104                 {
  105                     throw new ArgumentOutOfRangeException("Position");
  106                 }
  107 
  108                 _position = value;
  109             }
  110         }
  111 
  112 
  113         protected Stream ConvertToMemoryStream()
  114         {
  115             //// Copy the underlying string into a MemoryStream,
  116             //// Then replace the underlying stream in the parent class
  117             //// with the new MemoryStream via a callback sub. 
  118 
  119             MemoryAdapter memoryAdapter;
  120 
  121             memoryAdapter = new MemoryAdapter((int)this.Length);
  122             ((IStreamAdapter)this).WriteTo(memoryAdapter);
  123             memoryAdapter.Position = this.Position;
  124 
  125             _replaceStreamCallback(memoryAdapter);
  126 
  127             return memoryAdapter;
  128         }
  129 
  130 
  131         public override int Read(byte[] buffer, int offset, int count)
  132         {
  133             int result = 0;
  134             int position;
  135             char character;
  136             int temp;
  137             int maxPosition;
  138 
  139             position = (int)this.Position;
  140             maxPosition = (int)this.Length;
  141 
  142             while (result < count)
  143             {
  144 
  145                 if (position >= maxPosition)
  146                 {
  147                     goto ExitWhile;
  148                 }
  149                 else
  150                 {
  151                     //// Map the byte position to the high/low order byte 
  152                     //// of the appropriate Unicode character in the underlying string //
  153 
  154                     character = _text[position / 2];
  155 
  156                     if (position % 2 == 0)
  157                     {
  158                         temp = character & 255;
  159                     }
  160                     else
  161                     {
  162                         temp = character >> 8;
  163                     }
  164                 }
  165 
  166                 buffer[offset + result] = (byte)temp;
  167 
  168                 result = result + 1;
  169                 position = position + 1;
  170             }
  171         ExitWhile:
  172 
  173             this.Position = position;
  174 
  175             return result;
  176         }
  177 
  178 
  179         public override int ReadByte()
  180         {
  181             int result;
  182             char character;
  183             int position;
  184 
  185             position = (int)Position;
  186 
  187             if (position >= Length)
  188             {
  189                 result = -1;
  190             }
  191             else
  192             {
  193                 //// Map the byte position to the high/low order byte 
  194                 //// of the appropriate Unicode character in the underlying string //
  195                 character = _text[position / 2];
  196 
  197                 if (position % 2 == 0)
  198                 {
  199                     result = character & 255;
  200                 }
  201                 else
  202                 {
  203                     result = character >> 8;
  204                 }
  205 
  206                 Position = position + 1;
  207             }
  208 
  209             return result;
  210         }
  211 
  212 
  213         public override long Seek(long offset, System.IO.SeekOrigin origin)
  214         {
  215             switch (origin)
  216             {
  217                 case SeekOrigin.Begin:
  218                     this.Position = offset;
  219                     break;
  220                 case SeekOrigin.End:
  221                     this.Position = this.Length + offset;
  222                     break;
  223                 case SeekOrigin.Current:
  224                     this.Position = this.Position + offset;
  225                     break;
  226             }
  227 
  228             return Position;
  229         }
  230 
  231 
  232         public override void SetLength(long value)
  233         {
  234             if (value != this.Length)
  235             {
  236                 //// Write attempted, convert to memory stream //
  237                 this.ConvertToMemoryStream().SetLength(value);
  238             }
  239         }
  240 
  241 
  242         string IStreamAdapter.ToString()
  243         {
  244             return _text;
  245         }
  246 
  247 
  248         public override void Write(byte[] buffer, int offset, int count)
  249         {
  250             if (count > 0)
  251             {
  252                 //// Write attempted, convert to memory stream //
  253                 this.ConvertToMemoryStream().Write(buffer, offset, count);
  254             }
  255         }
  256 
  257 
  258         public override void WriteByte(byte temp)
  259         {
  260             //// Write attempted, convert to memory stream //
  261             this.ConvertToMemoryStream().WriteByte(temp);
  262         }
  263 
  264 
  265         void IStreamAdapter.WriteTo(Stream stream)
  266         {
  267             byte[] copyBuffer;
  268             int bytesRead;
  269             int bufferSize;
  270             long nOriginalPosition;
  271 
  272             if (this.Length == 0)
  273             {
  274                 return;
  275             }
  276 
  277             bufferSize = Math.Min(this.BufferSize, (int)this.Length);
  278             copyBuffer = new byte[bufferSize];
  279 
  280             nOriginalPosition = this.Position;
  281             this.Position = 0;
  282 
  283             bytesRead = this.Read(copyBuffer, 0, copyBuffer.Length);
  284             while (bytesRead != 0)
  285             {
  286                 stream.Write(copyBuffer, 0, bytesRead);
  287                 bytesRead = this.Read(copyBuffer, 0, copyBuffer.Length);
  288             }
  289 
  290             this.Position = nOriginalPosition;
  291         }
  292 
  293 
  294         //// These functions are required by the Stream class
  295         //// but they are never actually used...
  296         public override bool CanRead
  297         {
  298             get { return true; }
  299         }
  300 
  301         public override bool CanSeek
  302         {
  303             get { return true; }
  304         }
  305 
  306         public override bool CanWrite
  307         {
  308             get { return true; }
  309         }
  310 
  311         public override void Flush()
  312         {
  313             //// do nothing //
  314         }
  315     }
  316 
  317 
  318     //////////////////////////////////////////////////////////////////////////////////
  319     //// MemoryAdapter
  320     //////////////////////////////////////////////////////////////////////////////////
  321     //// Uses the inherited MemoryStream for all operations.
  322     ////
  323     protected class MemoryAdapter : MemoryStream, IStreamAdapter
  324     {
  325 
  326         public MemoryAdapter()
  327             : base()
  328         {
  329         }
  330 
  331         public MemoryAdapter(int nCapacity)
  332             : base(nCapacity)
  333         {
  334         }
  335 
  336         //// MemoryStreams do not readily convert to a string, 
  337         //// so override the behavior here.
  338         string IStreamAdapter.ToString()
  339         {
  340             String text;
  341 
  342             text = System.Text.Encoding.Unicode.GetString(this.GetBuffer());
  343             return text.Substring(0, Convert.ToInt32(this.Length) / 2);
  344         }
  345 
  346         void IStreamAdapter.WriteTo(System.IO.Stream stream)
  347         {
  348             base.WriteTo(stream);
  349         }
  350     }
  351 
  352 
  353     //////////////////////////////////////////////////////////////////////////////////
  354     //// StringStream
  355     //////////////////////////////////////////////////////////////////////////////////
  356     //// This class is wrapper around an underlying Stream object
  357     //// This allows the StringStream to be driven by either
  358     //// an underlying StringAdapter or an underlying MemoryAdapter.
  359     //// 
  360     protected Stream _streamAdapter;
  361     protected int _bufferSize = _DefaultBufferSize;
  362 
  363 
  364     public StringStream()
  365     {
  366         _streamAdapter = new MemoryAdapter();
  367     }
  368 
  369     public StringStream(string text)
  370     {
  371         _streamAdapter = new StringAdapter(text, ReplaceStream);
  372     }
  373 
  374 
  375     public virtual int BufferSize
  376     {
  377         get { return _bufferSize; }
  378         set
  379         {
  380             if (value < 1)
  381             {
  382                 throw new ArgumentOutOfRangeException("buffer size must be greater than zero");
  383             }
  384 
  385             _bufferSize = value;
  386 
  387             //// Only the StringAdapter requires a buffer (for WriteTo method) //
  388             if (_streamAdapter is StringAdapter)
  389             {
  390                 ((StringAdapter)_streamAdapter).BufferSize = _bufferSize;
  391             }
  392         }
  393     }
  394 
  395 
  396     public void Append(string text)
  397     {
  398         long nOriginalPosition;
  399 
  400         nOriginalPosition = _streamAdapter.Position;
  401 
  402         _streamAdapter.Seek(0, SeekOrigin.End);
  403         this.Write(text);
  404 
  405         _streamAdapter.Position = nOriginalPosition;
  406     }
  407 
  408 
  409     //// StringStream is always a read/write class, so just return true for these.
  410     public override bool CanRead
  411     {
  412         get { return true; }
  413     }
  414 
  415 
  416     public override bool CanSeek
  417     {
  418         get { return true; }
  419     }
  420 
  421 
  422     public override bool CanWrite
  423     {
  424         get { return true; }
  425     }
  426 
  427 
  428     public override void Flush()
  429     {
  430         //// StringStream has no buffer to flush... so do nothing //
  431     }
  432 
  433 
  434     public virtual Stream GetStream()
  435     {
  436         return _streamAdapter;
  437     }
  438 
  439 
  440     public override long Length
  441     {
  442         get { return _streamAdapter.Length; }
  443     }
  444 
  445 
  446     public override long Position
  447     {
  448         get { return _streamAdapter.Position; }
  449         set { _streamAdapter.Position = value; }
  450     }
  451 
  452 
  453     public override int Read(byte[] buffer, int offset, int count)
  454     {
  455         return _streamAdapter.Read(buffer, offset, count);
  456     }
  457 
  458 
  459     //// Read the entire (remaining) contents of the specified Stream 
  460     //// into the underlying Stream at the current position
  461     public int ReadFrom(Stream stream)
  462     {
  463         byte[] copyBuffer;
  464         int bytesRead;
  465         int bufferSize;
  466 
  467         bufferSize = this.BufferSize;
  468         copyBuffer = new byte[bufferSize + 1];
  469 
  470         bytesRead = stream.Read(copyBuffer, 0, bufferSize);
  471         while (bytesRead > 0)
  472         {
  473             _streamAdapter.Write(copyBuffer, 0, bytesRead);
  474             bytesRead = stream.Read(copyBuffer, 0, bufferSize);
  475         }
  476 
  477         return bytesRead;
  478     }
  479 
  480 
  481     protected void ReplaceStream(Stream stream)
  482     {
  483         //// Replace the underlying stream object
  484         _streamAdapter = stream;
  485     }
  486 
  487 
  488     public override long Seek(long offset, System.IO.SeekOrigin origin)
  489     {
  490         return _streamAdapter.Seek(offset, origin);
  491     }
  492 
  493 
  494     public override void SetLength(long value)
  495     {
  496         _streamAdapter.SetLength(value);
  497     }
  498 
  499 
  500     public override string ToString()
  501     {
  502         return ((IStreamAdapter)_streamAdapter).ToString();
  503     }
  504 
  505 
  506     public override void Write(byte[] buffer, int offset, int count)
  507     {
  508         _streamAdapter.Write(buffer, offset, count);
  509     }
  510 
  511 
  512     public void Write(string text)
  513     {
  514         byte[] outputBuffer;
  515 
  516         outputBuffer = System.Text.Encoding.Unicode.GetBytes(text);
  517 
  518         _streamAdapter.Write(outputBuffer, 0, outputBuffer.Length);
  519     }
  520 
  521     public void Write(string format, params object[] args)
  522     {
  523         this.Write(string.Format(format, args));
  524     }
  525 
  526 
  527     public void WriteLine(string text)
  528     {
  529         this.Write(text);
  530         this.Write(Environment.NewLine);
  531     }
  532 
  533     public void WriteLine(string format, params object[] args)
  534     {
  535         this.Write(format, args);
  536         this.Write(Environment.NewLine);
  537     }
  538 
  539 
  540     public void WriteTo(Stream stream)
  541     {
  542         ((IStreamAdapter)_streamAdapter).WriteTo(stream);
  543     }
  544 
  545 
  546     ////////////////////////
  547     //// OPERATORS 
  548     ////////////////////////
  549     //// These operators allow the StringStream to be used like any System.String object //
  550 
  551     public static string operator +(StringStream stringStream, string text)
  552     {
  553         return stringStream.ToString() + text;
  554     }
  555 
  556     public static string operator +(string text, StringStream stringStream)
  557     {
  558         return text + stringStream.ToString();
  559     }
  560 
  561     public static implicit operator string(StringStream stringStream)
  562     {
  563         return stringStream.ToString();
  564     }
  565 
  566     public static implicit operator StringStream(string text)
  567     {
  568         return new StringStream(text);
  569     }
  570 
  571     public static bool operator <(StringStream stringStream, string text)
  572     {
  573         return (stringStream.ToString().CompareTo(text) < 0);
  574     }
  575 
  576     public static bool operator <=(StringStream stringStream, string text)
  577     {
  578         return (stringStream.ToString().CompareTo(text) <= 0);
  579     }
  580 
  581     public override bool Equals(object o)
  582     {
  583         return this == o;
  584     }
  585 
  586     public static bool operator ==(StringStream stringStream, string text)
  587     {
  588         return (stringStream.ToString().CompareTo(text) == 0);
  589     }
  590 
  591     public static bool operator >=(StringStream stringStream, string text)
  592     {
  593         return (stringStream.ToString().CompareTo(text) >= 0);
  594     }
  595 
  596     public static bool operator >(StringStream stringStream, string text)
  597     {
  598         return (stringStream.ToString().CompareTo(text) > 0);
  599     }
  600 
  601     public static bool operator !=(StringStream stringStream, string text)
  602     {
  603         return !(stringStream == text);
  604     }
  605 }



Copyright (C) 2007-2009 by Robert Pinchbeck
All Rights Reserved