source: trunk/CrypPluginBase/IO/CStreamReader.cs

Last change on this file was 8983, checked in by kopal, 3 months ago

Complete CrypTool 2 project

  • renamed "Cryptool" namespace to "CrypTool" namespace
File size: 11.8 KB
Line 
1/*
2   Copyright 2009-2010 Matthäus Wander, University of Duisburg-Essen
3
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7
8       http://www.apache.org/licenses/LICENSE-2.0
9
10   Unless required by applicable law or agreed to in writing, software
11   distributed under the License is distributed on an "AS IS" BASIS,
12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   See the License for the specific language governing permissions and
14   limitations under the License.
15*/
16
17using System;
18using System.Collections.Generic;
19using System.IO;
20using System.Threading;
21using System.Diagnostics;
22
23namespace CrypTool.PluginBase.IO
24{
25    /// <summary>
26    /// Read from a CStream. Use POSIX Read() or more convenient ReadFully() to retrieve data
27    /// from CStream.
28    ///
29    /// <para>You MAY seek in the CStream to re-use the reader or skip data (beware of seeking too far:
30    /// will lead to EOF).</para>
31    ///
32    /// <para>You SHOULD dispose the reader when you're done using it. If you don't, the GC will release
33    /// your resources.</para>
34    ///
35    /// <para>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.</para>
38    /// </summary>
39    public class CStreamReader : Stream, IDisposable
40    {
41        #region Private fields and constructors
42
43        private readonly CStreamWriter _writer;
44
45        private FileStream _readStream;
46        private int _readPtr;
47
48        private bool _disposed;
49
50        /// <summary>
51        /// Create a reader to read from the passed CStream.
52        /// </summary>
53        public CStreamReader(CStreamWriter writer)
54        {
55            _writer = writer;
56
57            _writer.SwapEvent += swapHandler;
58
59            if (_writer.IsSwapped)
60            {
61                swapHandler();
62            }
63        }
64
65        #endregion
66
67        #region Public properties
68
69        public override bool CanRead
70        {
71            get { return !_disposed; }
72        }
73
74        public override bool CanSeek
75        {
76            get { return !_disposed; }
77        }
78
79        public override bool CanWrite
80        {
81            get { return false; }
82        }
83
84        public bool IsSwapped
85        {
86            get
87            {
88                return _readStream != null;
89            }
90        }
91
92        /// <summary>
93        /// Caveat: The length may grow while the writer has not closed the stream. If you rely on Length, you may want to call WaitEof() before.
94        /// </summary>
95        public override long Length
96        {
97            get
98            {
99                // TODO: cache FileStream property
100                return _writer.Length;
101            }
102        }
103
104        public override long Position
105        {
106            get
107            {
108                if (IsSwapped)
109                    // TODO: cache FileStream property
110                    return _readStream.Position;
111                else
112                    return _readPtr;
113            }
114
115            set
116            {
117                Seek(value, SeekOrigin.Begin);
118            }
119        }
120
121        #endregion
122
123        #region Public methods
124
125        public override void Close()
126        {
127            Dispose();
128        }
129
130        public new void Dispose()
131        {
132            if (_disposed)
133                return;
134
135            _disposed = true;
136
137            base.Dispose();
138
139            if (IsSwapped)
140            {
141                _readStream.Close();
142                _readStream = null;
143            }
144
145            _writer.SwapEvent -= swapHandler;
146        }
147
148        public override void Flush()
149        {
150            throw new NotSupportedException();
151        }
152
153        /// <summary>
154        /// Convenience method for Read(byte[] buf, 0, buf.Length)
155        /// </summary>
156        /// <param name="buffer"></param>
157        /// <returns></returns>
158        public int Read(byte[] buffer)
159        {
160            return Read(buffer, 0, buffer.Length);
161        }
162
163        /// <summary>
164        /// Read POSIX-like 1 to count amount of bytes into given byte array.
165        /// Blocks until at least 1 byte has been read or underlying stream has been closed.
166        /// Does not guarantee to read the requested/available amount of data, can read less.
167        /// </summary>
168        /// <param name="buffer"></param>
169        /// <param name="offset"></param>
170        /// <param name="count"></param>
171        /// <returns>amount of bytes that has been read into buffer or 0 if EOF</returns>
172        public override int Read(byte[] buffer, int offset, int count)
173        {
174            checkDisposal();
175
176            lock (_writer.InternalMonitor)
177            {
178                int available;
179
180                while ((available = availableRead()) < 1)
181                {
182                    // writer has been closed or reader has seeked beyond available length
183                    if (_writer.IsClosed || available < 0)
184                        return 0; // EOF
185
186                    Monitor.Wait(_writer.InternalMonitor);
187                }
188
189                int readAttempt = Math.Min(available, count);
190
191                if (IsSwapped)
192                {
193                    // MUST NOT block, otherwise we're potentially deadlocked
194                    Debug.Assert(_writer.Length - _readStream.Position > 0);
195                    return _readStream.Read(buffer, offset, readAttempt);
196                }
197                else
198                {
199                    Array.Copy(_writer.MemBuff, _readPtr, buffer, offset, readAttempt);
200                    _readPtr += readAttempt;
201
202                    return readAttempt;
203                }
204            }
205        }
206
207        /// <summary>
208        /// Convenience method for Read: read and block until EOF occurs.
209        ///
210        /// This method is inefficient for large data amounts. You should avoid it in production code.
211        /// </summary>
212        public byte[] ReadFully()
213        {
214            List<byte[]> list = new List<byte[]>();
215            int overall = 0;
216
217            { // read bunch of byte arrays
218                byte[] buf;
219                int read;
220                do
221                {
222                    buf = new byte[4096];
223                    read = ReadFully(buf);
224
225                    if (read > 0)
226                    {
227                        if (read < buf.Length)
228                        { // special case: read less bytes than buffer can hold
229                            byte[] resizedBuf = new byte[read];
230                            Array.Copy(buf, resizedBuf, read);
231                            list.Add(resizedBuf);
232                        }
233                        else
234                        { // default case
235                            Debug.Assert(buf.Length == read);
236                            list.Add(buf);
237                        }
238
239                        overall += read;
240                    }
241                } while (read == buf.Length); // not EOF
242            }
243
244            { // concat small buffers to bigbuffer
245                byte[] bigbuffer = new byte[overall];
246                int offset = 0;
247                foreach (byte[] buf in list)
248                {
249                    Array.Copy(buf, 0, bigbuffer, offset, buf.Length);
250                    offset += buf.Length;
251                }
252
253                Debug.Assert(offset == overall);
254
255                return bigbuffer;
256            }
257        }
258
259        /// <summary>
260        /// Convenience method for Read: read and block until array is full or EOF occurs.
261        /// </summary>
262        public int ReadFully(byte[] buffer)
263        {
264            return ReadFully(buffer, 0, buffer.Length);
265        }
266
267        /// <summary>
268        /// Convenience method for Read: read and block until required amount of data has
269        /// been retrieved or EOF occurs.
270        /// </summary>
271        public int ReadFully(byte[] buffer, int offset, int count)
272        {
273            int readSum = 0;
274            while (readSum < count)
275            {
276                int read = Read(buffer, offset, (count - readSum));
277               
278                if (read == 0) // EOF
279                    return readSum;
280
281                readSum += read;
282            }
283
284            return readSum;
285        }
286
287        /// <summary>
288        /// Seek to another position. Seeking beyond the length of the stream is permitted
289        /// (successive read will block until writer stream is closed).
290        ///
291        /// Note: there is no boundary check and no comprehensive check for int wraparound
292        /// (internal pointer on memory buffer is int32). Don't drink and seek.
293        /// </summary>
294        public override long Seek(long offset, SeekOrigin origin)
295        {
296            checkDisposal();
297
298            if (IsSwapped)
299            {
300                return _readStream.Seek(offset, origin);
301            }
302            else
303            {
304                switch (origin)
305                {
306                    case SeekOrigin.Begin:
307                        _readPtr = (int)offset;
308                        break;
309                    case SeekOrigin.Current:
310                        _readPtr += (int)offset;
311                        break;
312                    case SeekOrigin.End:
313                        _readPtr = _writer.MemBuff.Length + (int)offset;
314                        break;
315                }
316
317                if (_readPtr < 0)
318                    _readPtr = 0;
319
320                return _readPtr;
321            }
322        }
323
324        /// <summary>
325        /// Not supported.
326        /// </summary>
327        /// <param name="value"></param>
328        public override void SetLength(long value)
329        {
330            throw new NotSupportedException();
331        }
332
333        /// <summary>
334        /// Waits until the writer has stopped generating data and has closed the stream.
335        /// This method is for lazy readers waiting to have full length available before starting any processing.
336        /// It's usually less effective than trying to read continously.
337        /// </summary>
338        public void WaitEof()
339        {
340            checkDisposal();
341
342            lock (_writer.InternalMonitor)
343            {
344                while (!_writer.IsClosed)
345                {
346                    Monitor.Wait(_writer.InternalMonitor);
347                }
348            }
349        }
350
351        /// <summary>
352        /// Not supported.
353        /// </summary>
354        public override void Write(byte[] buffer, int offset, int count)
355        {
356            throw new NotSupportedException();
357        }
358
359        #endregion
360
361        #region Private/protected methods
362
363        private int availableRead()
364        {
365            long avail = _writer.Position - (IsSwapped ? _readStream.Position : _readPtr);
366            return (int)Math.Min(int.MaxValue, avail);
367        }
368
369        private void checkDisposal()
370        {
371            if (_disposed)
372                throw new ObjectDisposedException("Reader is already disposed");
373        }
374
375        /// <summary>
376        /// Switch from membuff to swapfile
377        /// </summary>
378        private void swapHandler()
379        {
380            _readStream = new FileStream(_writer.FilePath, FileMode.Open, FileAccess.Read, (FileShare.ReadWrite | FileShare.Delete));
381            if (_readPtr > 0)
382            {
383                Debug.Assert(_readPtr <= _writer.Length);
384                _readStream.Seek(_readPtr, SeekOrigin.Begin);
385            }
386        }
387
388        #endregion
389    }
390}
Note: See TracBrowser for help on using the repository browser.