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 CopySourceAsHtml Visual Studio 2005 Add-In.


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



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