// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved using System; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; using System.IO; namespace Microsoft.Samples.Fsrm.ManagedContentClassifier { /// /// This class wraps the IStream interface into a Stream interface /// This is useful for IFsrmPropertyBag's Istream /// public class StreamWrapperForIStream : Stream { private System.Runtime.InteropServices.ComTypes.IStream m_cachedIstream; private long m_position; /// /// Constructor /// /// an Istream to encapsulate public StreamWrapperForIStream(IStream istream) { m_cachedIstream = istream; } /// /// This stream can always be read so always returning true /// public override bool CanRead { get { return true; } } /// /// This stream can always be seeked, so always returning true /// public override bool CanSeek { get { return true; } } /// /// This stream can never be written to so always returning false /// public override bool CanWrite { get { return false; } } /// /// Not Implemented /// public override void Flush() { throw new NotImplementedException(); } /// /// Get the length associated with the stream /// public override long Length { get { long length = 0; // not locking the m_LengthCached because there is nothing // wrong with 2 variables getting here simultaneosly // Note setting long in 64bit is atomic so this lock isn't needed lock (this) { System.Runtime.InteropServices.ComTypes.STATSTG stat; // todo: I should figure out the stat flags and only get the length m_cachedIstream.Stat(out stat, 0); length = stat.cbSize; } return length; } } /// /// Get the current position in the Stream /// Set the current position in the stream /// public override long Position { get { // this is getting locked even though this code will always run on 64bit // on 64bit updates/reads from longs are atomic therefore this lock is uncessary lock (this) { return m_position; } } set { //operating outside lock due to seek being locked long newPosition = Seek(value, SeekOrigin.Begin); if (newPosition != value) { throw new Exception("Seek failed"); } } } /// /// Read from the current position in the stream, and advance the current position the number of bytes read /// /// The buffer to read the stream into /// The offset of buffer to start reading into, For performance try and always use 0 /// The number of bytes to read from the stream /// The number of bytes read into buffer/the number of bytes the position in the stream is advanced public override int Read(byte[] buffer, int offset, int count) { if (offset < 0 || offset + count > buffer.Length) { throw new AccessViolationException("Tried to read into an invalid index"); } // ensure that length gets accessed outside the lock long length = this.Length; int internal_offset = offset; int totalBytesRead = 0; lock (this) { // ensure to follow Read's behavior of always filling the buffer if we are not reading till the end of the file int MaxBytesToRead = count; if ((length - m_position) < (long)MaxBytesToRead) { MaxBytesToRead = (int)(length - m_position); } if (MaxBytesToRead == 0) return 0; // if reading from offset 0 we can directly read into the buffer if (internal_offset == 0) { unsafe { // This memory will be fixed due to it being declared in unsafe and allow taking the address UInt32 numBytesRead = 0; m_cachedIstream.Read(buffer, count, new IntPtr(&numBytesRead)); //Note that numBytesRead is a UInt32 //But since I read only values of size int, I can can safely assume this value //to be less than MaxInt internal_offset += (Int32)numBytesRead; totalBytesRead += (Int32)numBytesRead; } } // we might need to read more data, and can't read directly into the buffer if (totalBytesRead < MaxBytesToRead) { byte[] byteBuffer = new byte[MaxBytesToRead - totalBytesRead]; while (totalBytesRead < MaxBytesToRead) { unsafe { // This memory will be fixed due to it being declared in unsafe and allow taking the address UInt32 numBytesRead = 0; m_cachedIstream.Read(byteBuffer, MaxBytesToRead - totalBytesRead, new IntPtr(&numBytesRead)); //Note that numBytesRead is a UInt32 //But since I read only values of size int, I can can safely assume this value //to be less than MaxInt Array.Copy(byteBuffer, 0, buffer, internal_offset, (Int32)numBytesRead); internal_offset += (Int32)numBytesRead; totalBytesRead += (Int32)numBytesRead; } } } m_position += totalBytesRead; } return totalBytesRead; } /// /// Move the current pointer in the strea relative to the seek origin /// /// The number of bytes to move from origin /// Move from begging, current cursor, or end of file /// public override long Seek(long offset, SeekOrigin origin) { int dwOrigin = 0; switch (origin) { case SeekOrigin.Begin: dwOrigin = 0; break; case SeekOrigin.Current: dwOrigin = 1; break; case SeekOrigin.End: dwOrigin = 2; break; default: throw new ArgumentException( "Invalid seek origin" ); } //ensure not seeking while reading lock (this) { unsafe { // getting casting errors is unlikely as that would be millions of terrabytes for a single file // This memory will be fixed due to it being declared in unsafe and allow taking the address ulong ulongPosition = (ulong)m_position; m_cachedIstream.Seek(offset, dwOrigin, new IntPtr(&ulongPosition)); m_position = (long)ulongPosition; } } return m_position; } /// /// Not Implemented /// /// public override void SetLength(long value) { throw new NotImplementedException(); } /// /// NotImplemented /// /// /// /// public override void Write(byte[] buffer, int offset, int count) { throw new NotImplementedException(); } } }