none
How to access part of a FileStream or MemoryStream RRS feed

  • Question

  •  

     

    I have a sub that accepts an input stream, and it doesn’t care whether it is a FileStream or a  MemoryStream.

     

    The problem is that the sub currently needs “most” of the stream, not “all” of it

    (regardless of whether it is a FileStream or a MemoryStream). I’m not sure how to do this – and I would certainly prefer to avoid making a copy of the file in order to get the “most” of  it that I need.

     

    Here are more details:

     

    A 20 byte hash of the original stream was appended to that original stream. Hence I want to access all but these 20 bytes (thus I want “most” of the current stream) so I can compute the current hash and compare the two hashes to verify that the hash value hasn’t changed.  The .Net ComputeHash function accepts a stream, so I want to pass in “most” of the stream (all but those last 20 bytes) and don’t know how to do that.  I can’t simply copy the stream into a byte array because the filestreams might be too large to store in memory. Nor do I want to copy “most” of the file to another file because it’s slow. Is there any way to pass in “most” of the stream to the   ComputeHash function?

    (System.Security.Cryptography.SHA1Managed().ComputeHash)

    Saturday, March 15, 2008 9:12 AM

Answers

  • You might need to wrap the stream...

     

    Code Snippet

    using System.IO;
    using System;
    static class Program
    {

     // shows that we can read a subset of an existing stream...
        static void Main()
        {
            byte[] buffer = new byte[255];
            for (byte i = 0; i < 255; i++)
            {
                buffer[i] = i;
            }
            using(MemoryStream ms = new MemoryStream(buffer))
            using (SubStream ss = new SubStream(ms, 10, 200))
            {
                const int BUFFER_SIZE = 17; // why not...
                byte[] working = new byte[BUFFER_SIZE];
                int read;
                while ((read = ss.Read(working, 0, BUFFER_SIZE)) > 0)
                {
                    for (int i = 0; i < read; i++)
                    {
                        Console.WriteLine(working[i]);
                    }
                }
            }
        }
    }

    class SubStream : Stream
    {
        private Stream baseStream;
        private readonly long length;
        private long position;
        public SubStream(Stream baseStream, long offset, long length)
        {
            if (baseStream == null) throw new ArgumentNullException("baseStream");
            if (!baseStream.CanRead) throw new ArgumentException("can't read base stream");
            if (offset < 0) throw new ArgumentOutOfRangeException("offset");

            this.baseStream = baseStream;
            this.length = length;

            if (baseStream.CanSeek)
            {
                baseStream.Seek(offset, SeekOrigin.Current);
            }
            else
            { // read it manually...
                const int BUFFER_SIZE = 512;
                byte[] buffer = new byte[BUFFER_SIZE];
                while (offset > 0)
                {
                    int read = baseStream.Read(buffer, 0, offset < BUFFER_SIZE ? (int) offset : BUFFER_SIZE);
                    offset -= read;
                }
            }
        }
        public override int Read(byte[] buffer, int offset, int count)
        {
            CheckDisposed();
            long remaining = length - position;
            if (remaining <= 0) return 0;
            if (remaining < count) count = (int) remaining;
            int read = baseStream.Read(buffer, offset, count);
            position += read;
            return read;
        }
        private void CheckDisposed()
        {
            if (baseStream == null) throw new ObjectDisposedException(GetType().Name);
        }
        public override long Length
        {
            get { CheckDisposed(); return length; }
        }
        public override bool CanRead
        {
            get { CheckDisposed(); return true; }
        }
        public override bool CanWrite
        {
            get { CheckDisposed(); return false; }
        }
        public override bool CanSeek
        {
            get { CheckDisposed(); return false; }
        }
        public override long Position
        {
            get {
                CheckDisposed();
                return position;
            }
            set { throw new NotSupportedException(); }
        }
        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotSupportedException();
        }
        public override void SetLength(long value)
        {
            throw new NotSupportedException();
        }
        public override void Flush()
        {
            CheckDisposed(); baseStream.Flush();
        }
        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            if (disposing)
            {
                if (baseStream != null)
                {
                    try { baseStream.Dispose(); }
                    catch { }
                    baseStream = null;
                }
            }
        }
        public override void Write(byte[] buffer, int offset, int count)
        {
            throw new NotImplementedException();
        }
    }

     

     

     

    Saturday, March 15, 2008 10:58 AM

All replies

  • You might need to wrap the stream...

     

    Code Snippet

    using System.IO;
    using System;
    static class Program
    {

     // shows that we can read a subset of an existing stream...
        static void Main()
        {
            byte[] buffer = new byte[255];
            for (byte i = 0; i < 255; i++)
            {
                buffer[i] = i;
            }
            using(MemoryStream ms = new MemoryStream(buffer))
            using (SubStream ss = new SubStream(ms, 10, 200))
            {
                const int BUFFER_SIZE = 17; // why not...
                byte[] working = new byte[BUFFER_SIZE];
                int read;
                while ((read = ss.Read(working, 0, BUFFER_SIZE)) > 0)
                {
                    for (int i = 0; i < read; i++)
                    {
                        Console.WriteLine(working[i]);
                    }
                }
            }
        }
    }

    class SubStream : Stream
    {
        private Stream baseStream;
        private readonly long length;
        private long position;
        public SubStream(Stream baseStream, long offset, long length)
        {
            if (baseStream == null) throw new ArgumentNullException("baseStream");
            if (!baseStream.CanRead) throw new ArgumentException("can't read base stream");
            if (offset < 0) throw new ArgumentOutOfRangeException("offset");

            this.baseStream = baseStream;
            this.length = length;

            if (baseStream.CanSeek)
            {
                baseStream.Seek(offset, SeekOrigin.Current);
            }
            else
            { // read it manually...
                const int BUFFER_SIZE = 512;
                byte[] buffer = new byte[BUFFER_SIZE];
                while (offset > 0)
                {
                    int read = baseStream.Read(buffer, 0, offset < BUFFER_SIZE ? (int) offset : BUFFER_SIZE);
                    offset -= read;
                }
            }
        }
        public override int Read(byte[] buffer, int offset, int count)
        {
            CheckDisposed();
            long remaining = length - position;
            if (remaining <= 0) return 0;
            if (remaining < count) count = (int) remaining;
            int read = baseStream.Read(buffer, offset, count);
            position += read;
            return read;
        }
        private void CheckDisposed()
        {
            if (baseStream == null) throw new ObjectDisposedException(GetType().Name);
        }
        public override long Length
        {
            get { CheckDisposed(); return length; }
        }
        public override bool CanRead
        {
            get { CheckDisposed(); return true; }
        }
        public override bool CanWrite
        {
            get { CheckDisposed(); return false; }
        }
        public override bool CanSeek
        {
            get { CheckDisposed(); return false; }
        }
        public override long Position
        {
            get {
                CheckDisposed();
                return position;
            }
            set { throw new NotSupportedException(); }
        }
        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotSupportedException();
        }
        public override void SetLength(long value)
        {
            throw new NotSupportedException();
        }
        public override void Flush()
        {
            CheckDisposed(); baseStream.Flush();
        }
        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            if (disposing)
            {
                if (baseStream != null)
                {
                    try { baseStream.Dispose(); }
                    catch { }
                    baseStream = null;
                }
            }
        }
        public override void Write(byte[] buffer, int offset, int count)
        {
            throw new NotImplementedException();
        }
    }

     

     

     

    Saturday, March 15, 2008 10:58 AM
  • Thank you so much!  I am going to keep your method in my notes for future reference.   As for the present scenario, it turns out that the ComputeHash can accept a filestream chunk by chunk. The code is provided here:

     

    http://blogs.msdn.com/shawnfa/archive/2004/02/20/77431.aspx

     

    Saturday, March 15, 2008 11:03 AM