The Labs \ Source Viewer \ SSCLI \ System.IO \ UnmanagedMemoryStream

  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. **
  17. ** Class:  UnmanagedMemoryStream
  18. **
  19. ** Purpose: Create a stream over unmanaged memory, mostly
  20. **          useful for memory-mapped files.
  21. **
  22. ** Date:  October 20, 2000 (made public August 4, 2003)
  23. **
  24. ===========================================================*/
  25. using System;
  26. using System.Runtime.InteropServices;
  27. using System.Security.Permissions;
  28. namespace System.IO
  29. {
  30.    
  31. /*
  32.     * This class is used to access a contiguous block of memory, likely outside
  33.     * the GC heap (or pinned in place in the GC heap, but a MemoryStream may
  34.     * make more sense in those cases).  It's great if you have a pointer and
  35.     * a length for a section of memory mapped in by someone else and you don't
  36.     * want to copy this into the GC heap.  UnmanagedMemoryStream assumes these
  37.     * two things:
  38.     *
  39.     * 1) All the memory in the specified block is readable or writable,
  40.     *    depending on the values you pass to the constructor.
  41.     * 2) The lifetime of the block of memory is at least as long as the lifetime
  42.     *    of the UnmanagedMemoryStream.
  43.     * 3) You clean up the memory when appropriate.  The UnmanagedMemoryStream
  44.     *    currently will do NOTHING to free this memory.
  45.     * 4) All calls to Write and WriteByte may not be threadsafe currently.
  46.     *
  47.     * It may become necessary to add in some sort of
  48.     * DeallocationMode enum, specifying whether we unmap a section of memory,
  49.     * call free, run a user-provided delegate to free the memory, etc etc. 
  50.     * We'll suggest user write a subclass of UnmanagedMemoryStream that uses
  51.     * a SafeHandle subclass to hold onto the memory.
  52.     * Check for problems when using this in the negative parts of a
  53.     * process's address space.  We may need to use unsigned longs internally
  54.     * and change the overflow detection logic.
  55.     */   
  56.     [CLSCompliant(false)]
  57.     public class UnmanagedMemoryStream : Stream
  58.     {
  59.         private const long UnmanagedMemStreamMaxLength = Int64.MaxValue;
  60.        
  61.         unsafe private byte* _mem;
  62.         private long _length;
  63.         private long _capacity;
  64.         private long _position;
  65.         private FileAccess _access;
  66.         internal bool _isOpen;
  67.        
  68.         // Needed for subclasses that need to map a file, etc.
  69.         unsafe protected UnmanagedMemoryStream()
  70.         {
  71.             _mem = null;
  72.             _isOpen = false;
  73.         }
  74.        
  75.         unsafe public UnmanagedMemoryStream(byte* pointer, long length)
  76.         {
  77.             Initialize(pointer, length, length, FileAccess.Read, false);
  78.         }
  79.        
  80.         unsafe public UnmanagedMemoryStream(byte* pointer, long length, long capacity, FileAccess access)
  81.         {
  82.             Initialize(pointer, length, capacity, access, false);
  83.         }
  84.        
  85.         // We must create one of these without doing a security check. This
  86.         // class is created while security is trying to start up. Plus, doing
  87.         // a Demand from Assembly.GetManifestResourceStream isn't useful.
  88.         unsafe internal UnmanagedMemoryStream(byte* pointer, long length, long capacity, FileAccess access, bool skipSecurityCheck)
  89.         {
  90.             Initialize(pointer, length, capacity, access, skipSecurityCheck);
  91.         }
  92.        
  93.         unsafe protected void Initialize(byte* pointer, long length, long capacity, FileAccess access)
  94.         {
  95.             Initialize(pointer, length, capacity, access, false);
  96.         }
  97.        
  98.         unsafe internal void Initialize(byte* pointer, long length, long capacity, FileAccess access, bool skipSecurityCheck)
  99.         {
  100.             if (pointer == null)
  101.                 throw new ArgumentNullException("pointer");
  102.             if (length < 0 || capacity < 0)
  103.                 throw new ArgumentOutOfRangeException((length < 0) ? "length" : "capacity", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  104.             if (length > capacity)
  105.                 throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_LengthGreaterThanCapacity"));
  106.             // Check for wraparound.
  107.             if (((byte*)((long)pointer + capacity)) < pointer)
  108.                 throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_UnmanagedMemStreamWrapAround"));
  109.             if (access < FileAccess.Read || access > FileAccess.ReadWrite)
  110.                 throw new ArgumentOutOfRangeException("access", Environment.GetResourceString("ArgumentOutOfRange_Enum"));
  111.             if (_isOpen)
  112.                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CalledTwice"));
  113.            
  114.             if (!skipSecurityCheck)
  115.                 new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
  116.            
  117.             _mem = pointer;
  118.             _length = length;
  119.             _capacity = capacity;
  120.             _access = access;
  121.             _isOpen = true;
  122.         }
  123.        
  124.         public override bool CanRead {
  125.             get { return _isOpen && (_access & FileAccess.Read) != 0; }
  126.         }
  127.        
  128.         public override bool CanSeek {
  129.             get { return _isOpen; }
  130.         }
  131.        
  132.         public override bool CanWrite {
  133.             get { return _isOpen && (_access & FileAccess.Write) != 0; }
  134.         }
  135.        
  136.         protected override void Dispose(bool disposing)
  137.         {
  138.             _isOpen = false;
  139.             //_mem = null;
  140.            
  141.             // Stream allocates WaitHandles for async calls. So for correctness
  142.             // call base.Dispose(disposing) for better perf, avoiding waiting
  143.             // for the finalizers to run on those types.
  144.             base.Dispose(disposing);
  145.         }
  146.        
  147.         public override void Flush()
  148.         {
  149.             if (!_isOpen)
  150.                 __Error.StreamIsClosed();
  151.         }
  152.        
  153.         public override long Length {
  154.             get {
  155.                 if (!_isOpen)
  156.                     __Error.StreamIsClosed();
  157.                 return _length;
  158.             }
  159.         }
  160.        
  161.         public long Capacity {
  162.             get {
  163.                 if (!_isOpen)
  164.                     __Error.StreamIsClosed();
  165.                 return _capacity;
  166.             }
  167.         }
  168.        
  169.         unsafe public override long Position {
  170.             get {
  171.                 if (!_isOpen)
  172.                     __Error.StreamIsClosed();
  173.                 return _position;
  174.             }
  175.             set {
  176.                 if (!_isOpen)
  177.                     __Error.StreamIsClosed();
  178.                 if (value < 0)
  179.                     throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  180.                
  181.                 #if WIN32
  182.                 // On 32 bit machines, ensure we don't wrap around.
  183.                 if (value > (long)Int32.MaxValue || _mem + value < _mem)
  184.                     throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_MemStreamLength"));
  185.                 #endif
  186.                 _position = value;
  187.             }
  188.         }
  189.        
  190.         unsafe public byte* PositionPointer {
  191.             [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
  192.             get {
  193.                 long pos = _position;
  194.                 // Use a temp to avoid a race
  195.                 if (pos > _capacity)
  196.                     throw new IndexOutOfRangeException(Environment.GetResourceString("IndexOutOfRange_UMSPosition"));
  197.                 byte* ptr = _mem + pos;
  198.                 if (!_isOpen)
  199.                     __Error.StreamIsClosed();
  200.                 return ptr;
  201.             }
  202.             [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
  203.             set {
  204.                 if (!_isOpen)
  205.                     __Error.StreamIsClosed();
  206.                 // Note: subtracting pointers returns an Int64. Working around
  207.                 // to avoid hitting compiler warning CS0652 on this line.
  208.                 if (new IntPtr(value - _mem).ToInt64() > UnmanagedMemStreamMaxLength)
  209.                     throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_UnmanagedMemStreamLength"));
  210.                 if (value < _mem)
  211.                     throw new IOException(Environment.GetResourceString("IO.IO_SeekBeforeBegin"));
  212.                 _position = value - _mem;
  213.             }
  214.         }
  215.        
  216.         unsafe internal byte* Pointer {
  217.             get { return _mem; }
  218.         }
  219.        
  220.         unsafe public override int Read(        [In(), Out()]
  221. byte[] buffer, int offset, int count)
  222.         {
  223.             if (!_isOpen)
  224.                 __Error.StreamIsClosed();
  225.             if ((_access & FileAccess.Read) == 0)
  226.                 __Error.ReadNotSupported();
  227.             if (buffer == null)
  228.                 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
  229.             if (offset < 0)
  230.                 throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  231.             if (count < 0)
  232.                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  233.             if (buffer.Length - offset < count)
  234.                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
  235.            
  236.             // Use a local variable to avoid a race where another thread
  237.             // changes our position after we decide we can read some bytes.
  238.             long pos = _position;
  239.             long n = _length - pos;
  240.             if (n > count)
  241.                 n = count;
  242.             if (n <= 0)
  243.                 return 0;
  244.            
  245.             int nInt = (int)n;
  246.             // Safe because n <= count, which is an Int32
  247.             if (nInt < 0)
  248.                 nInt = 0;
  249.             // _position could be beyond EOF
  250.             BCLDebug.Assert(pos + nInt >= 0, "_position + n >= 0");
  251.             // len is less than 2^63 -1.
  252.             Buffer.memcpy(_mem + pos, 0, buffer, offset, nInt);
  253.             _position = pos + n;
  254.             return nInt;
  255.         }
  256.        
  257.         unsafe public override int ReadByte()
  258.         {
  259.             if (!_isOpen)
  260.                 __Error.StreamIsClosed();
  261.             if ((_access & FileAccess.Read) == 0)
  262.                 __Error.ReadNotSupported();
  263.            
  264.             long pos = _position;
  265.             // Use a local to avoid a race condition
  266.             if (pos >= _length)
  267.                 return -1;
  268.             _position = pos + 1;
  269.             return _mem[pos];
  270.         }
  271.        
  272.         unsafe public override long Seek(long offset, SeekOrigin loc)
  273.         {
  274.             if (!_isOpen)
  275.                 __Error.StreamIsClosed();
  276.             if (offset > UnmanagedMemStreamMaxLength)
  277.                 throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_UnmanagedMemStreamLength"));
  278.             switch (loc) {
  279.                 case SeekOrigin.Begin:
  280.                     if (offset < 0)
  281.                         throw new IOException(Environment.GetResourceString("IO.IO_SeekBeforeBegin"));
  282.                     _position = offset;
  283.                     break;
  284.                 case SeekOrigin.Current:
  285.                    
  286.                     if (offset + _position < 0)
  287.                         throw new IOException(Environment.GetResourceString("IO.IO_SeekBeforeBegin"));
  288.                     _position += offset;
  289.                     break;
  290.                 case SeekOrigin.End:
  291.                    
  292.                     if (_length + offset < 0)
  293.                         throw new IOException(Environment.GetResourceString("IO.IO_SeekBeforeBegin"));
  294.                     _position = _length + offset;
  295.                     break;
  296.                 default:
  297.                    
  298.                     throw new ArgumentException(Environment.GetResourceString("Argument_InvalidSeekOrigin"));
  299.                     break;
  300.             }
  301.            
  302.             BCLDebug.Assert(_position >= 0, "_position >= 0");
  303.             return _position;
  304.         }
  305.        
  306.         unsafe public override void SetLength(long value)
  307.         {
  308.             if (!_isOpen)
  309.                 __Error.StreamIsClosed();
  310.             if ((_access & FileAccess.Write) == 0)
  311.                 __Error.WriteNotSupported();
  312.             if (value < 0)
  313.                 throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  314.             if (value > _capacity)
  315.                 throw new IOException(Environment.GetResourceString("IO.IO_FixedCapacity"));
  316.            
  317.             long len = _length;
  318.             if (value > len)
  319.                 Buffer.ZeroMemory(_mem + len, value - len);
  320.             _length = value;
  321.             if (_position > value)
  322.                 _position = value;
  323.         }
  324.        
  325.         unsafe public override void Write(byte[] buffer, int offset, int count)
  326.         {
  327.             if (!_isOpen)
  328.                 __Error.StreamIsClosed();
  329.             if ((_access & FileAccess.Write) == 0)
  330.                 __Error.WriteNotSupported();
  331.             if (buffer == null)
  332.                 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
  333.             if (offset < 0)
  334.                 throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  335.             if (count < 0)
  336.                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  337.             if (buffer.Length - offset < count)
  338.                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
  339.            
  340.             long pos = _position;
  341.             // Use a local to avoid a race condition
  342.             long len = _length;
  343.             long n = pos + count;
  344.             // Check for overflow
  345.             if (n < 0)
  346.                 throw new IOException(Environment.GetResourceString("IO.IO_StreamTooLong"));
  347.            
  348.             if (n > len) {
  349.                 if (n > _capacity)
  350.                     throw new NotSupportedException(Environment.GetResourceString("IO.IO_FixedCapacity"));
  351.                 _length = n;
  352.             }
  353.             // Check to see whether we are now expanding the stream and must
  354.             // zero any memory in the middle.
  355.             if (pos > len)
  356.                 Buffer.ZeroMemory(_mem + len, pos - len);
  357.            
  358.             Buffer.memcpy(buffer, offset, _mem + pos, 0, count);
  359.             _position = n;
  360.             return;
  361.         }
  362.        
  363.         unsafe public override void WriteByte(byte value)
  364.         {
  365.             if (!_isOpen)
  366.                 __Error.StreamIsClosed();
  367.             if ((_access & FileAccess.Write) == 0)
  368.                 __Error.WriteNotSupported();
  369.            
  370.             long pos = _position;
  371.             // Use a local to avoid a race condition
  372.             long len = _length;
  373.             long n = pos + 1;
  374.             if (pos >= len) {
  375.                 // Check for overflow
  376.                 if (n < 0)
  377.                     throw new IOException(Environment.GetResourceString("IO.IO_StreamTooLong"));
  378.                
  379.                 if (n > _capacity)
  380.                     throw new NotSupportedException(Environment.GetResourceString("IO.IO_FixedCapacity"));
  381.                 _length = n;
  382.                
  383.                 // Check to see whether we are now expanding the stream and must
  384.                 // zero any memory in the middle.
  385.                 if (pos > len)
  386.                     Buffer.ZeroMemory(_mem + len, pos - len);
  387.             }
  388.            
  389.             _mem[pos] = value;
  390.             _position = n;
  391.         }
  392.     }
  393. }

Developer Fusion