The Labs \ Source Viewer \ SSCLI \ System.Runtime.Remoting.Channels \ ChunkedMemoryStream

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. //==========================================================================
  16. // File: ChunkedMemoryStream.cs
  17. //
  18. // Summary: Memory stream that doesn't need to be resized.
  19. //
  20. //==========================================================================
  21. using System;
  22. using System.IO;
  23. using System.Runtime.Remoting.Channels;
  24. namespace System.Runtime.Remoting.Channels
  25. {
  26.    
  27.     internal class ChunkedMemoryStream : Stream
  28.     {
  29.         private class MemoryChunk
  30.         {
  31.             public byte[] Buffer = null;
  32.             public MemoryChunk Next = null;
  33.         }
  34.        
  35.         private static IByteBufferPool s_defaultBufferPool = new ByteBufferAllocator(1024);
  36.        
  37.         // state
  38.         private MemoryChunk _chunks = null;
  39.         // data
  40.         private IByteBufferPool _bufferPool = null;
  41.         // pool of byte buffers to use
  42.         private bool _bClosed = false;
  43.         // has the stream been closed.
  44.         private MemoryChunk _writeChunk = null;
  45.         // current chunk to write to
  46.         private int _writeOffset = 0;
  47.         // offset into chunk to write to
  48.         private MemoryChunk _readChunk = null;
  49.         // current chunk to read from
  50.         private int _readOffset = 0;
  51.         // offset into chunk to read from
  52.        
  53.         public ChunkedMemoryStream(IByteBufferPool bufferPool)
  54.         {
  55.             _bufferPool = bufferPool;
  56.         }
  57.         // ChunkedMemoryStream
  58.        
  59.         public override bool CanRead {
  60.             get { return true; }
  61.         }
  62.         public override bool CanSeek {
  63.             get { return true; }
  64.         }
  65.         public override bool CanWrite {
  66.             get { return true; }
  67.         }
  68.        
  69.         public override long Length {
  70.             get {
  71.                 if (_bClosed) {
  72.                     throw new RemotingException(CoreChannel.GetResourceString("Remoting_Stream_StreamIsClosed"));
  73.                 }
  74.                
  75.                
  76.                 int length = 0;
  77.                 MemoryChunk chunk = _chunks;
  78.                 while (chunk != null) {
  79.                     MemoryChunk next = chunk.Next;
  80.                     if (next != null)
  81.                         length += chunk.Buffer.Length;
  82.                     else
  83.                         length += _writeOffset;
  84.                    
  85.                     chunk = next;
  86.                 }
  87.                
  88.                 return (long)length;
  89.             }
  90.         }
  91.         // Length
  92.         public override long Position {
  93.             get {
  94.                 if (_bClosed) {
  95.                     throw new RemotingException(CoreChannel.GetResourceString("Remoting_Stream_StreamIsClosed"));
  96.                 }
  97.                
  98.                 if (_readChunk == null)
  99.                     return 0;
  100.                
  101.                 int pos = 0;
  102.                 MemoryChunk chunk = _chunks;
  103.                 while (chunk != _readChunk) {
  104.                     pos += chunk.Buffer.Length;
  105.                     chunk = chunk.Next;
  106.                 }
  107.                 pos += _readOffset;
  108.                
  109.                 return (long)pos;
  110.             }
  111.            
  112.             set {
  113.                 if (_bClosed) {
  114.                     throw new RemotingException(CoreChannel.GetResourceString("Remoting_Stream_StreamIsClosed"));
  115.                 }
  116.                
  117.                 if (value < 0)
  118.                     throw new ArgumentOutOfRangeException("value");
  119.                
  120.                 // back up current position in case new position is out of range
  121.                 MemoryChunk backupReadChunk = _readChunk;
  122.                 int backupReadOffset = _readOffset;
  123.                
  124.                 _readChunk = null;
  125.                 _readOffset = 0;
  126.                
  127.                 int leftUntilAtPos = (int)value;
  128.                 MemoryChunk chunk = _chunks;
  129.                 while (chunk != null) {
  130.                     if ((leftUntilAtPos < chunk.Buffer.Length) || ((leftUntilAtPos == chunk.Buffer.Length) && (chunk.Next == null))) {
  131.                         // the desired position is in this chunk
  132.                         _readChunk = chunk;
  133.                         _readOffset = leftUntilAtPos;
  134.                         break;
  135.                     }
  136.                    
  137.                     leftUntilAtPos -= chunk.Buffer.Length;
  138.                     chunk = chunk.Next;
  139.                 }
  140.                
  141.                 if (_readChunk == null) {
  142.                     // position is out of range
  143.                     _readChunk = backupReadChunk;
  144.                     _readOffset = backupReadOffset;
  145.                     throw new ArgumentOutOfRangeException("value");
  146.                 }
  147.             }
  148.         }
  149.         // Position
  150.         public override long Seek(long offset, SeekOrigin origin)
  151.         {
  152.             if (_bClosed) {
  153.                 throw new RemotingException(CoreChannel.GetResourceString("Remoting_Stream_StreamIsClosed"));
  154.             }
  155.            
  156.             switch (origin) {
  157.                 case SeekOrigin.Begin:
  158.                     Position = offset;
  159.                     break;
  160.                 case SeekOrigin.Current:
  161.                    
  162.                     Position = Position + offset;
  163.                     break;
  164.                 case SeekOrigin.End:
  165.                    
  166.                     Position = Length + offset;
  167.                     break;
  168.             }
  169.            
  170.             return Position;
  171.         }
  172.         // Seek
  173.        
  174.         public override void SetLength(long value)
  175.         {
  176.             throw new NotSupportedException();
  177.         }
  178.        
  179.         protected override void Dispose(bool disposing)
  180.         {
  181.             try {
  182.                 _bClosed = true;
  183.                 if (disposing)
  184.                     ReleaseMemoryChunks(_chunks);
  185.                 _chunks = null;
  186.                 _writeChunk = null;
  187.                 _readChunk = null;
  188.             }
  189.             finally {
  190.                 base.Dispose(disposing);
  191.             }
  192.         }
  193.         // Close
  194.         public override void Flush()
  195.         {
  196.         }
  197.         // Flush
  198.        
  199.         public override int Read(byte[] buffer, int offset, int count)
  200.         {
  201.             if (_bClosed) {
  202.                 throw new RemotingException(CoreChannel.GetResourceString("Remoting_Stream_StreamIsClosed"));
  203.             }
  204.            
  205.             if (_readChunk == null) {
  206.                 if (_chunks == null)
  207.                     return 0;
  208.                 _readChunk = _chunks;
  209.                 _readOffset = 0;
  210.             }
  211.            
  212.             byte[] chunkBuffer = _readChunk.Buffer;
  213.             int chunkSize = chunkBuffer.Length;
  214.             if (_readChunk.Next == null)
  215.                 chunkSize = _writeOffset;
  216.            
  217.             int bytesRead = 0;
  218.            
  219.             while (count > 0) {
  220.                 if (_readOffset == chunkSize) {
  221.                     // exit if no more chunks are currently available
  222.                     if (_readChunk.Next == null)
  223.                         break;
  224.                    
  225.                     _readChunk = _readChunk.Next;
  226.                     _readOffset = 0;
  227.                     chunkBuffer = _readChunk.Buffer;
  228.                     chunkSize = chunkBuffer.Length;
  229.                     if (_readChunk.Next == null)
  230.                         chunkSize = _writeOffset;
  231.                 }
  232.                
  233.                 int readCount = Math.Min(count, chunkSize - _readOffset);
  234.                 Buffer.BlockCopy(chunkBuffer, _readOffset, buffer, offset, readCount);
  235.                 offset += readCount;
  236.                 count -= readCount;
  237.                 _readOffset += readCount;
  238.                 bytesRead += readCount;
  239.             }
  240.            
  241.             return bytesRead;
  242.         }
  243.         // Read
  244.         public override int ReadByte()
  245.         {
  246.             if (_bClosed) {
  247.                 throw new RemotingException(CoreChannel.GetResourceString("Remoting_Stream_StreamIsClosed"));
  248.             }
  249.            
  250.             if (_readChunk == null) {
  251.                 if (_chunks == null)
  252.                     return 0;
  253.                 _readChunk = _chunks;
  254.                 _readOffset = 0;
  255.             }
  256.            
  257.             byte[] chunkBuffer = _readChunk.Buffer;
  258.             int chunkSize = chunkBuffer.Length;
  259.             if (_readChunk.Next == null)
  260.                 chunkSize = _writeOffset;
  261.            
  262.             if (_readOffset == chunkSize) {
  263.                 // exit if no more chunks are currently available
  264.                 if (_readChunk.Next == null)
  265.                     return -1;
  266.                
  267.                 _readChunk = _readChunk.Next;
  268.                 _readOffset = 0;
  269.                 chunkBuffer = _readChunk.Buffer;
  270.                 chunkSize = chunkBuffer.Length;
  271.                 if (_readChunk.Next == null)
  272.                     chunkSize = _writeOffset;
  273.             }
  274.            
  275.             return chunkBuffer[_readOffset++];
  276.         }
  277.         // ReadByte
  278.         public override void Write(byte[] buffer, int offset, int count)
  279.         {
  280.             if (_bClosed) {
  281.                 throw new RemotingException(CoreChannel.GetResourceString("Remoting_Stream_StreamIsClosed"));
  282.             }
  283.            
  284.             if (_chunks == null) {
  285.                 _chunks = AllocateMemoryChunk();
  286.                 _writeChunk = _chunks;
  287.                 _writeOffset = 0;
  288.             }
  289.            
  290.             byte[] chunkBuffer = _writeChunk.Buffer;
  291.             int chunkSize = chunkBuffer.Length;
  292.            
  293.             while (count > 0) {
  294.                 if (_writeOffset == chunkSize) {
  295.                     // allocate a new chunk if the current one is full
  296.                     _writeChunk.Next = AllocateMemoryChunk();
  297.                     _writeChunk = _writeChunk.Next;
  298.                     _writeOffset = 0;
  299.                     chunkBuffer = _writeChunk.Buffer;
  300.                     chunkSize = chunkBuffer.Length;
  301.                 }
  302.                
  303.                 int copyCount = Math.Min(count, chunkSize - _writeOffset);
  304.                 Buffer.BlockCopy(buffer, offset, chunkBuffer, _writeOffset, copyCount);
  305.                 offset += copyCount;
  306.                 count -= copyCount;
  307.                 _writeOffset += copyCount;
  308.             }
  309.            
  310.         }
  311.         // Write
  312.         public override void WriteByte(byte value)
  313.         {
  314.             if (_bClosed) {
  315.                 throw new RemotingException(CoreChannel.GetResourceString("Remoting_Stream_StreamIsClosed"));
  316.             }
  317.            
  318.             if (_chunks == null) {
  319.                 _chunks = AllocateMemoryChunk();
  320.                 _writeChunk = _chunks;
  321.                 _writeOffset = 0;
  322.             }
  323.            
  324.             byte[] chunkBuffer = _writeChunk.Buffer;
  325.             int chunkSize = chunkBuffer.Length;
  326.            
  327.             if (_writeOffset == chunkSize) {
  328.                 // allocate a new chunk if the current one is full
  329.                 _writeChunk.Next = AllocateMemoryChunk();
  330.                 _writeChunk = _writeChunk.Next;
  331.                 _writeOffset = 0;
  332.                 chunkBuffer = _writeChunk.Buffer;
  333.                 chunkSize = chunkBuffer.Length;
  334.             }
  335.            
  336.             chunkBuffer[_writeOffset++] = value;
  337.         }
  338.         // WriteByte
  339.        
  340.         // copy entire buffer into an array
  341.         public virtual byte[] ToArray()
  342.         {
  343.             int length = (int)Length;
  344.             // this will throw if stream is closed
  345.             byte[] copy = new byte[Length];
  346.            
  347.             MemoryChunk backupReadChunk = _readChunk;
  348.             int backupReadOffset = _readOffset;
  349.            
  350.             _readChunk = _chunks;
  351.             _readOffset = 0;
  352.             Read(copy, 0, length);
  353.            
  354.             _readChunk = backupReadChunk;
  355.             _readOffset = backupReadOffset;
  356.            
  357.             return copy;
  358.         }
  359.         // ToArray
  360.        
  361.         // write remainder of this stream to another stream
  362.         public virtual void WriteTo(Stream stream)
  363.         {
  364.             if (_bClosed) {
  365.                 throw new RemotingException(CoreChannel.GetResourceString("Remoting_Stream_StreamIsClosed"));
  366.             }
  367.            
  368.             if (stream == null)
  369.                 throw new ArgumentNullException("stream");
  370.            
  371.             if (_readChunk == null) {
  372.                 if (_chunks == null)
  373.                     return;
  374.                
  375.                 _readChunk = _chunks;
  376.                 _readOffset = 0;
  377.             }
  378.            
  379.             byte[] chunkBuffer = _readChunk.Buffer;
  380.             int chunkSize = chunkBuffer.Length;
  381.             if (_readChunk.Next == null)
  382.                 chunkSize = _writeOffset;
  383.            
  384.             // following code mirrors Read() logic (_readChunk/_readOffset should
  385.             // point just past last byte of last chunk when done)
  386.            
  387.             // loop until end of chunks is found
  388.             for (;;) {
  389.                 if (_readOffset == chunkSize) {
  390.                     // exit if no more chunks are currently available
  391.                     if (_readChunk.Next == null)
  392.                         break;
  393.                    
  394.                     _readChunk = _readChunk.Next;
  395.                     _readOffset = 0;
  396.                     chunkBuffer = _readChunk.Buffer;
  397.                     chunkSize = chunkBuffer.Length;
  398.                     if (_readChunk.Next == null)
  399.                         chunkSize = _writeOffset;
  400.                 }
  401.                
  402.                 int writeCount = chunkSize - _readOffset;
  403.                 stream.Write(chunkBuffer, _readOffset, writeCount);
  404.                 _readOffset = chunkSize;
  405.             }
  406.            
  407.         }
  408.         // WriteTo
  409.        
  410.        
  411.         private MemoryChunk AllocateMemoryChunk()
  412.         {
  413.             MemoryChunk chunk = new MemoryChunk();
  414.             chunk.Buffer = _bufferPool.GetBuffer();
  415.             chunk.Next = null;
  416.            
  417.             return chunk;
  418.         }
  419.         // AllocateMemoryChunk
  420.         private void ReleaseMemoryChunks(MemoryChunk chunk)
  421.         {
  422.             // If the buffer pool always allocates a new buffer,
  423.             // there's no point to trying to return all of the buffers.
  424.             if (_bufferPool is ByteBufferAllocator)
  425.                 return;
  426.            
  427.             while (chunk != null) {
  428.                 _bufferPool.ReturnBuffer(chunk.Buffer);
  429.                 chunk = chunk.Next;
  430.             }
  431.            
  432.         }
  433.         // FreeMemoryChunk
  434.        
  435.     }
  436.     // ChunkedMemoryStream
  437.    
  438. }
  439. // namespace System.IO

Developer Fusion