Changeset 1106


Ignore:
Timestamp:
Jan 26, 2010, 12:32:13 PM (12 years ago)
Author:
Matthäus Wander
Message:

CStream finished, ready for deployment:

  • added CStream as exchange class
  • removed finalizer, relying on FileOptions.DeleteOnClose now
Location:
trunk
Files:
1 added
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/CrypPluginBase/CrypPluginBase.csproj

    r1085 r1106  
    6565    <Compile Include="Control\IControlEncryption.cs" />
    6666    <Compile Include="Control\IControlWsdl.cs" />
     67    <Compile Include="IO\CStream.cs" />
    6768    <Compile Include="IO\CStreamReader.cs" />
    6869    <Compile Include="IO\CStreamWriter.cs" />
  • trunk/CrypPluginBase/IO/CStreamReader.cs

    r1101 r1106  
    2626{
    2727    /// <summary>
    28     /// Read from a corresponding CStreamWriter.
     28    /// Read from a CStream. Use POSIX Read() or more convenient ReadFully() to retrieve data
     29    /// from CStream.
     30    ///
     31    /// You MAY seek in the CStream to re-use the reader or skip data (beware of seeking too far:
     32    /// will lead to EOF).
     33    /// You SHOULD dispose the reader when you're done using it. If you don't, the GC will release
     34    /// your resources.
     35    /// You SHOULD NOT pass the same reader instance to other components. Concurrent access on
     36    /// the same reader will lead to a probably unwanted behaviour. You MAY however use two different
     37    /// readers on the same CStream. Each reader maintains its own state.
    2938    /// </summary>
    30     public class CStream : Stream, IDisposable
     39    public class CStreamReader : Stream, IDisposable
    3140    {
    3241        #region Private fields and constructors
     
    3948        private bool _disposed;
    4049
    41         [Obsolete("for more or less clean disposal of obsolete self-created CryptoolStream instances")]
    42         private List<CryptoolStream> disposeStreams = new List<CryptoolStream>();
    43 
    44         public CStream(CStreamWriter writer)
     50        /// <summary>
     51        /// Create a reader to read from the passed CStream.
     52        /// </summary>
     53        public CStreamReader(CStreamWriter writer)
    4554        {
    4655            _writer = writer;
    47             _writer.ShutdownEvent += shutdownHandler;
     56
    4857            _writer.SwapEvent += swapHandler;
    4958
     
    5261                swapHandler();
    5362            }
    54         }
    55 
    56         ~CStream()
    57         {
    58             Dispose(false);
     63
     64            CStream = _writer.CStream;
    5965        }
    6066
     
    6369        #region Public properties
    6470
     71        public CStream CStream
     72        {
     73            get;
     74            private set;
     75        }
     76
    6577        public override bool CanRead
    6678        {
     
    7082        public override bool CanSeek
    7183        {
     84            get { return true; }
     85        }
     86
     87        public override bool CanWrite
     88        {
    7289            get { return false; }
    7390        }
    7491
    75         public override bool CanWrite
    76         {
    77             get { return false; }
    78         }
    79 
    8092        public bool IsSwapped
    8193        {
     
    90102            get
    91103            {
     104                // TODO: cache FileStream property
    92105                return _writer.Length;
    93106            }
     
    99112            {
    100113                if (IsSwapped)
     114                    // TODO: cache FileStream property
    101115                    return _readStream.Position;
    102116                else
     
    104118            }
    105119
    106             // Seeking currently not supported.
    107120            set
    108121            {
    109                 throw new NotSupportedException();
     122                Seek(value, SeekOrigin.Begin);
    110123            }
    111124        }
     
    122135        public new void Dispose()
    123136        {
    124             if (!_disposed)
    125             {
    126                 Dispose(true);
    127                 GC.SuppressFinalize(this);
    128             }
     137            if (_disposed)
     138                return;
     139
     140            _disposed = true;
     141
     142            base.Dispose();
     143
     144            if (IsSwapped)
     145            {
     146                _readStream.Close();
     147                _readStream = null;
     148            }
     149
     150            _writer.SwapEvent -= swapHandler;
    129151        }
    130152
     
    155177        public override int Read(byte[] buffer, int offset, int count)
    156178        {
     179            checkDisposal();
     180
    157181            lock (_writer.InternalMonitor)
    158182            {
     
    161185                while ((available = availableRead()) < 1)
    162186                {
    163                     if (_writer.IsClosed)
     187                    // writer has been closed or reader has seeked beyond available length
     188                    if (_writer.IsClosed || available < 0)
    164189                        return 0; // EOF
    165190
     
    188213        /// Convenience method for Read: read and block until EOF occurs.
    189214        ///
    190         /// This method is inefficient for large data amounts. You should avoid it in stable code.
     215        /// This method is inefficient for large data amounts. You should avoid it in production code.
    191216        /// </summary>
    192217        public byte[] ReadFully()
     
    195220            int overall = 0;
    196221
    197             { // read list of buffers
     222            { // read bunch of byte arrays
    198223                byte[] buf;
    199224                int read;
     
    222247            }
    223248
    224             { // concat buffers to bigbuffer
     249            { // concat small buffers to bigbuffer
    225250                byte[] bigbuffer = new byte[overall];
    226251                int offset = 0;
     
    266291
    267292        /// <summary>
    268         /// Seeking is currently not supported
     293        /// Seek to another position. Seeking beyond the length of the stream is permitted
     294        /// (successive read will block until writer stream is closed).
     295        ///
     296        /// Note: there is no boundary check and no comprehensive check for int wraparound
     297        /// (internal pointer on memory buffer is int32). Don't drink and seek.
    269298        /// </summary>
    270299        public override long Seek(long offset, SeekOrigin origin)
    271300        {
     301            checkDisposal();
     302
     303            if (IsSwapped)
     304            {
     305                return _readStream.Seek(offset, origin);
     306            }
     307            else
     308            {
     309                switch (origin)
     310                {
     311                    case SeekOrigin.Begin:
     312                        _readPtr = (int)offset;
     313                        break;
     314                    case SeekOrigin.Current:
     315                        _readPtr += (int)offset;
     316                        break;
     317                    case SeekOrigin.End:
     318                        _readPtr = _writer.MemBuff.Length + (int)offset;
     319                        break;
     320                }
     321
     322                if (_readPtr < 0)
     323                    _readPtr = 0;
     324
     325                return _readPtr;
     326            }
     327        }
     328
     329        public override void SetLength(long value)
     330        {
    272331            throw new NotSupportedException();
    273332        }
    274333
    275         public override void SetLength(long value)
     334        /// <summary>
     335        /// Reader can't write.
     336        /// </summary>
     337        public override void Write(byte[] buffer, int offset, int count)
    276338        {
    277339            throw new NotSupportedException();
    278340        }
    279341
    280         /// <summary>
    281         /// Reader can't write.
    282         /// </summary>
    283         public override void Write(byte[] buffer, int offset, int count)
    284         {
    285             throw new NotSupportedException();
    286         }
    287 
    288         /// <summary>
    289         /// Compatibility with obsolete CryptoolStream. This method has not been tested thoroughly.
    290         /// If it breaks, upgrade your plugin to use the new CStream natively.
    291         /// </summary>
    292         /// <returns></returns>
    293         [Obsolete("Replace usage of CryptoolStream with CStream")]
    294         public CryptoolStream ReadCryptoolStream()
    295         {
    296             // CryptoolStream requires a file to read
    297             _writer.EnsureSwap();
    298 
    299             CryptoolStream cs = new CryptoolStream();
    300             cs.OpenRead(_writer.FilePath);
    301             disposeStreams.Add(cs);
    302             return cs;
    303         }
    304 
    305342        #endregion
    306343
     
    309346        private int availableRead()
    310347        {
     348            // Caveat! Breaks if writer is disposed and reader is still being used
     349            Debug.Assert(!_writer.IsDisposed);
     350
    311351            long avail = _writer.Position - (IsSwapped ? _readStream.Position : _readPtr);
    312352            return (int)Math.Min(int.MaxValue, avail);
    313353        }
    314354
     355        private void checkDisposal()
     356        {
     357            if (_disposed)
     358                throw new ObjectDisposedException("Reader is already disposed");
     359
     360            if (_writer.IsDisposed)
     361                throw new ObjectDisposedException("Corresponding writer is disposed");
     362        }
     363
    315364        /// <summary>
    316365        /// Switch from membuff to swapfile
     
    318367        private void swapHandler()
    319368        {
    320             _readStream = new FileStream(_writer.FilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
     369            _readStream = new FileStream(_writer.FilePath, FileMode.Open, FileAccess.Read, (FileShare.ReadWrite | FileShare.Delete));
    321370            if (_readPtr > 0)
    322371            {
     372                Debug.Assert(_readPtr <= _writer.Length);
    323373                _readStream.Seek(_readPtr, SeekOrigin.Begin);
    324374            }
    325         }
    326 
    327         /// <summary>
    328         /// Writer shutting down, release file.
    329         /// </summary>
    330         private void shutdownHandler()
    331         {
    332             if (IsSwapped)
    333             {
    334                 _readStream.Close();
    335             }
    336         }
    337 
    338         protected override void Dispose(bool disposing)
    339         {
    340             if (disposing)
    341             {
    342                 base.Dispose(disposing);
    343             }
    344 
    345             if (IsSwapped)
    346             {
    347                 _readStream.Close();
    348                 _readStream = null;
    349             }
    350 
    351             _writer.ShutdownEvent -= shutdownHandler;
    352             _writer.SwapEvent -= swapHandler;
    353 
    354             // disposal of obsolete CryptoolStreams
    355             foreach (CryptoolStream cs in disposeStreams)
    356             {
    357                 cs.Dispose();
    358             }
    359 
    360             _disposed = true;
    361375        }
    362376
  • trunk/CrypPluginBase/IO/CStreamWriter.cs

    r1101 r1106  
    3232    /// forget old data, therefore you can derive an arbitary number of stream readers at any time.
    3333    ///
    34     /// You MAY CreateReader() and pass it even if the writing has not been finished yet.
    3534    /// You SHOULD Flush() the stream when you're writing large data amounts and expect the readers
    3635    /// to perform intermediate processing before writing has been finished.
     
    3938    ///
    4039    /// You SHOULD Dispose() the stream when you're done using it (or use the C# "using" keyword) in
    41     /// order to remove the temporary swapfile, however if you forget to, the GC finalizer will clean
    42     /// up for you.
     40    /// order to remove the temporary swapfile, however if you forget to, the GC will clean up for you.
    4341    /// </public>
    4442    public class CStreamWriter : Stream, IDisposable
     
    4644        #region Private fields and constructors
    4745
     46        internal const int FileBufferSize = 8192;
     47
    4848        private readonly object _monitor;
    4949        private bool _closed;
     
    5757        private FileStream _writeStream;
    5858        private string _filePath;
    59         private long _closedFileLength;
    6059
    6160        /// <summary>
    6261        /// Init CStreamWriter with 64 KB memory buffer
    6362        /// </summary>
    64         public CStreamWriter() : this(64*1024)
     63        public CStreamWriter() : this(65536)
    6564        {
    6665        }
     
    6968        /// Init CStreamWriter with custom size memory buffer (in bytes)
    7069        /// </summary>
    71         /// <param name="bufSize"></param>
    7270        public CStreamWriter(int bufSize)
    7371        {
    7472            _buffer = new byte[bufSize];
    7573            _monitor = new object();
    76         }
    77 
    78         ~CStreamWriter()
    79         {
    80             Dispose(false);
     74
     75            CStream = new CStream(this);
     76        }
     77
     78        /// <summary>
     79        /// Init CStreamWriter and copy some data to memory buffer.
     80        ///
     81        /// Please note: Data is *copied* from passed buffer to internal memory buffer.
     82        /// </summary>
     83        /// <param name="buf">Pre-initialized byte array which is copied into internal membuff</param>
     84        /// <param name="autoClose">close after initialization</param>
     85        public CStreamWriter(byte[] buf, bool autoClose) : this(buf.Length)
     86        {
     87            Array.Copy(buf, _buffer, buf.Length);
     88            _bufPtr = buf.Length;
     89
     90            if (autoClose)
     91            {
     92                Close();
     93            }
    8194        }
    8295
     
    8598        #region Public properties
    8699
     100        public CStream CStream
     101        {
     102            get;
     103            private set;
     104        }
     105
    87106        public override bool CanRead
    88107        {
     
    109128
    110129        /// <summary>
     130        /// Has the writer stream been marked as closed?
     131        ///
     132        /// Please note: closing the write stream is not equivalent with disposing it.
     133        /// Readers can still read from a closed stream, but not from a disposed one.
     134        /// </summary>
     135        public bool IsClosed
     136        {
     137            get
     138            {
     139                return _closed;
     140            }
     141            set
     142            {
     143                if (value)
     144                    Close();
     145            }
     146        }
     147
     148        /// <summary>
    111149        /// Returns whether the underlying buffer is swapped out to filesystem or not.
    112150        /// </summary>
     
    125163                if (IsSwapped)
    126164                {
    127                     return _closed ? _closedFileLength : _writeStream.Length;
     165                    // TODO: cache FileStream property
     166                    return _writeStream.Length;
    128167                }
    129168                else
     
    140179                if (IsSwapped)
    141180                {
    142                     return _closed ? _closedFileLength : _writeStream.Position;
     181                    // TODO: cache FileStream property
     182                    return _writeStream.Position;
    143183                }
    144184                else
     
    166206        {
    167207            /*
    168              * Note 1: We're NOT following the advice of implementing cleanup code in Dispose() without
    169              * touching Close(), because we explicitly want to mark a stream as closed without disposing
    170              * the underlying mem/file buffer.
     208             * Note 1: We're NOT following the pattern of the Stream class to implement cleanup code in
     209             * Dispose() without touching Close(), because we explicitly want to mark a stream as closed
     210             * without disposing the underlying mem/file buffer. That's why we also don't call base.Close().
    171211             *
    172              * Note 2: There is no call to base.Close() as it would suppress the finalization.
     212             * Note 2: Closing the CStream does not automatically close the underlying file handle, as
     213             * the file is marked as DeleteOnClose and may be removed too early. File is closed when the
     214             * CStreamWriter is disposed or garbage collected.
    173215             */
    174216
    175             lock (_monitor)
    176             {
    177                 // do nothing if already closed
    178                 if (_closed)
    179                     return;
    180 
    181                 _closed = true;
    182 
    183                 if (IsSwapped)
    184                 {
    185                     _closedFileLength = _writeStream.Length;
    186                     _writeStream.Close();
    187                 }
    188 
    189                 Monitor.PulseAll(_monitor);
    190             }
    191         }
    192 
    193         /// <summary>
    194         /// Create a new instance to read from this CStream.
    195         /// </summary>
    196         /// <returns></returns>
    197         public CStream CreateReader()
    198         {
    199             return new CStream(this);
    200         }
    201 
     217            // do nothing if already closed
     218            if (_closed)
     219                return;
     220
     221            _closed = true;
     222
     223            Flush();
     224        }
     225
     226        /// <summary>
     227        /// Explicitly destroy object.
     228        /// </summary>
    202229        public new void Dispose()
    203230        {
    204             if (!_disposed)
    205             {
    206                 Dispose(true);
    207                 GC.SuppressFinalize(this);
    208             }
    209         }
    210 
     231            if (_disposed)
     232                return;
     233
     234            _disposed = true;
     235
     236            base.Dispose();
     237
     238            if (IsSwapped)
     239            {
     240                _writeStream.Close(); // release file handle
     241                _writeStream = null;
     242            }
     243
     244            _buffer = null;
     245            SwapEvent = null;
     246        }
     247
     248        /// <summary>
     249        /// Flush any caches, announce buffer freshness to readers.
     250        /// </summary>
    211251        public override void Flush()
    212252        {
     
    277317                else
    278318                {
    279                     EnsureSwap();
     319                    if (!IsSwapped)
     320                    {
     321                        createSwapFile();
     322                        _writeStream.Write(_buffer, 0, _bufPtr);
     323                        _writeStream.Flush(); // ensure reader can seek before announcing swap event
     324                        _buffer = null;
     325
     326                        if (SwapEvent != null)
     327                            SwapEvent();
     328                    }
    280329
    281330                    _writeStream.Write(buf, offset, count);
     
    290339        #region Private/protected methods
    291340
    292         protected override void Dispose(bool disposing)
    293         {
    294             if (disposing)
    295             {
    296                 base.Dispose(disposing);
    297             }
    298 
    299             if (IsSwapped)
    300             {
    301                 // Force readers to close file.
    302                 if (ShutdownEvent != null)
    303                     ShutdownEvent();
    304 
    305                 // Remove swapfile
    306                 _writeStream.Close();
    307                 _writeStream = null;
    308                 File.Delete(_filePath);
    309             }
    310 
    311             ShutdownEvent = null;
    312             SwapEvent = null;
    313 
    314             _disposed = true;
    315         }
    316 
    317341        private bool hasMemorySpace(int count)
    318342        {
     
    323347        {
    324348            _filePath = DirectoryHelper.GetNewTempFilePath();
    325             _writeStream = new FileStream(_filePath, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.Read);
     349            _writeStream = new FileStream(_filePath, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.Read, FileBufferSize, FileOptions.DeleteOnClose);
    326350        }
    327351
     
    330354        #region Internal members for stream readers
    331355
     356        internal bool IsDisposed
     357        {
     358            get { return _disposed; }
     359        }
     360
    332361        internal object InternalMonitor
    333362        {
     
    339368        internal event ReaderCallback SwapEvent;
    340369
    341         internal event ReaderCallback ShutdownEvent;
    342 
    343370        internal byte[] MemBuff
    344371        {
    345372            get { return _buffer; }
    346         }
    347 
    348         internal bool IsClosed
    349         {
    350             get { return _closed; }
    351         }
    352 
    353         internal void EnsureSwap()
    354         {
    355             if (!IsSwapped)
    356             {
    357                 createSwapFile();
    358                 _writeStream.Write(_buffer, 0, _bufPtr);
    359                 _writeStream.Flush(); // ensure reader can seek before announcing swap event
    360                 _buffer = null;
    361 
    362                 if (SwapEvent != null)
    363                     SwapEvent();
    364             }
    365373        }
    366374
  • trunk/CrypPluginBase/IO/CryptoolStream.cs

    r1101 r1106  
    257257        public override long Seek(long offset, SeekOrigin origin)
    258258        {
    259           this.position = offset;
     259          this.position = offset; // BROKEN: does not support other origins than Begin
    260260          return this.fileStream.Seek(offset, origin);
    261261        }
  • trunk/DevTestMethods/CrypPluginBase/CStream.cs

    r1101 r1106  
    109109            // put 6 bytes
    110110            writer.Write(ShortData);
    111             CStream reader = writer.CreateReader();
     111            CStreamReader reader = writer.CStream.CreateReader();
    112112
    113113            // length==6, position==0
     
    156156        {
    157157            CStreamWriter writer = new CStreamWriter();
    158             CStream reader = writer.CreateReader();
     158            CStreamReader reader = writer.CStream.CreateReader();
    159159
    160160            // write, not swapped
     
    164164            // read a few bytes, but there are still a few bytes left
    165165            byte[] buf = new byte[ShortData.Length];
    166             reader.Read(buf, 0, buf.Length);
     166            reader.Read(buf);
    167167            Assert.IsTrue(reader.Position > 0);
    168168            Assert.IsTrue(reader.Length > reader.Position);
     
    210210
    211211        [TestMethod]
     212        public void TestDisposedExceptionWithMem()
     213        {
     214            CStreamWriter writer = new CStreamWriter();
     215
     216            // write not, swapped
     217            writer.Write(LongData);
     218            Assert.IsFalse(writer.IsSwapped);
     219
     220            // try to read
     221            CStreamReader reader = writer.CStream.CreateReader();
     222            byte[] buf = new byte[ShortData.Length];
     223            {
     224                int read = reader.Read(buf);
     225                Assert.AreEqual(ShortData.Length, read);
     226            }
     227
     228            // dispose CStream and try to read again, assert error
     229            writer.Dispose();
     230            {
     231                try
     232                {
     233                    int read = reader.Read(buf);
     234                    Assert.Fail("unreachable code, missed exception");
     235                }
     236                catch (ObjectDisposedException e)
     237                {
     238                    // everything ok
     239                }
     240            }
     241        }
     242
     243        [TestMethod]
     244        public void TestDisposedExceptionWithSwap()
     245        {
     246            CStreamWriter writer = new CStreamWriter();
     247
     248            writer.Write(LongData);
     249            writer.Write(LongData);
     250            writer.Write(LongData);
     251            Assert.IsTrue(writer.IsSwapped);
     252
     253            CStreamReader reader = writer.CStream.CreateReader();
     254
     255            // dispose CStream and try to read, assert error
     256            writer.Dispose();
     257            try
     258            {
     259                byte[] buf = new byte[1024];
     260                int read = reader.Read(buf);
     261                Assert.Fail("unreachable code, missed exception");
     262            }
     263            catch (ObjectDisposedException e)
     264            {
     265                // everything ok
     266            }
     267        }
     268
     269        [TestMethod]
    212270        public void TestDestructorWithReader()
    213271        {
     
    218276
    219277            // read something and assert there's more
    220             CStream reader = writer.CreateReader();
     278            CStreamReader reader = writer.CStream.CreateReader();
    221279            byte[] buf = new byte[ShortData.Length];
    222             reader.Read(buf, 0, buf.Length);
     280            reader.Read(buf);
    223281            Assert.IsTrue(reader.Position > 0);
    224282            Assert.IsTrue(reader.Length > reader.Position);
     
    270328            writer.Close();
    271329
    272             CStream reader = writer.CreateReader();
     330            CStreamReader reader = writer.CStream.CreateReader();
    273331            byte[] bigbuf = reader.ReadFully();
    274332
    275333            Assert.AreEqual(LongData.Length * 3, bigbuf.Length);
     334        }
     335
     336        [TestMethod]
     337        public void TestSeek()
     338        {
     339            CStreamWriter writer = new CStreamWriter();
     340            writer.Write(LongData);
     341            writer.Close();
     342
     343            CStreamReader reader = writer.CStream.CreateReader();
     344            byte[] buf = new byte[1024];
     345
     346            { // seek 5 bytes before EOF, attempt to read much, get 5 bytes
     347                reader.Seek(LongData.Length - 5, SeekOrigin.Begin);
     348                int read = reader.Read(buf, 0, int.MaxValue);
     349                Assert.AreEqual(5, read);
     350            }
     351
     352            { // read EOF
     353                int read = reader.Read(buf, 0, int.MaxValue);
     354                Assert.AreEqual(0, read);
     355            }
     356
     357            { // seek beyond stream length, read EOF
     358                reader.Seek(LongData.Length * 3, SeekOrigin.Begin);
     359                int read = reader.Read(buf, 0, int.MaxValue);
     360                Assert.AreEqual(0, read);
     361            }
     362
     363            { // seek back, read again
     364                reader.Seek(LongData.Length - 5, SeekOrigin.Begin);
     365                int read = reader.Read(buf, 0, int.MaxValue);
     366                Assert.AreEqual(5, read);
     367            }
     368        }
     369
     370        [TestMethod]
     371        public void TestSeekSwap()
     372        {
     373            CStreamWriter writer = new CStreamWriter();
     374            writer.Write(LongData);
     375
     376            CStreamReader reader = writer.CStream.CreateReader();
     377            byte[] buf = new byte[1024];
     378
     379            // seek 5 bytes before EOF
     380            reader.Seek(LongData.Length - 5, SeekOrigin.Begin);
     381
     382            // write more, ensure swap
     383            writer.Write(LongData);
     384            writer.Write(LongData);
     385            Assert.IsTrue(writer.IsSwapped);
     386
     387            { // seek somewhere to the middle, read
     388                reader.Seek(LongData.Length * 2, SeekOrigin.Begin);
     389                int read = reader.Read(buf);
     390                Assert.AreEqual(buf.Length, read);
     391            }
     392
     393            { // seek beyond length, assert still open, get EOF
     394                reader.Seek(LongData.Length * 3, SeekOrigin.Current);
     395                Assert.IsFalse(writer.IsClosed);
     396                int read = reader.Read(buf);
     397                Assert.AreEqual(0, read);
     398            }
    276399        }
    277400    }
Note: See TracChangeset for help on using the changeset viewer.