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