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 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 }