1 /****************************************************************************
3 * Copyright (C) 2014, Andrew Ward <afward@gmail.com> *
5 * See COPYING for license terms (Ms-PL). *
7 ***************************************************************************/
9 using System.Collections.Generic;
15 /// A thread-safe, read-only, buffering stream wrapper.
17 partial class BufferedReadStream : Stream
19 const int DEFAULT_INITIAL_SIZE = 32768; // 32KB (1/2 full page)
20 const int DEFAULT_MAX_SIZE = 262144; // 256KB (4 full pages)
23 StreamReadBuffer _buffer;
25 object _localLock = new object();
26 System.Threading.Thread _owningThread;
29 public BufferedReadStream(Stream baseStream)
30 : this(baseStream, DEFAULT_INITIAL_SIZE, DEFAULT_MAX_SIZE, false)
34 public BufferedReadStream(Stream baseStream, bool minimalRead)
35 : this(baseStream, DEFAULT_INITIAL_SIZE, DEFAULT_MAX_SIZE, minimalRead)
39 public BufferedReadStream(Stream baseStream, int initialSize, int maxSize)
40 : this(baseStream, initialSize, maxSize, false)
44 public BufferedReadStream(Stream baseStream, int initialSize, int maxBufferSize, bool minimalRead)
46 if (baseStream == null) throw new ArgumentNullException("baseStream");
47 if (!baseStream.CanRead) throw new ArgumentException("baseStream");
49 if (maxBufferSize < 1) maxBufferSize = 1;
50 if (initialSize < 1) initialSize = 1;
51 if (initialSize > maxBufferSize) initialSize = maxBufferSize;
53 _baseStream = baseStream;
54 _buffer = new StreamReadBuffer(baseStream, initialSize, maxBufferSize, minimalRead);
55 _buffer.MaxSize = maxBufferSize;
56 _buffer.MinimalRead = minimalRead;
59 protected override void Dispose(bool disposing)
61 base.Dispose(disposing);
77 // route all the container locking through here so we can track whether the caller actually took the lock...
78 public void TakeLock()
80 System.Threading.Monitor.Enter(_localLock);
81 if (++_lockCount == 1)
83 _owningThread = System.Threading.Thread.CurrentThread;
89 if (_owningThread != System.Threading.Thread.CurrentThread)
91 throw new System.Threading.SynchronizationLockException();
95 public void ReleaseLock()
98 if (--_lockCount == 0)
100 _owningThread = null;
102 System.Threading.Monitor.Exit(_localLock);
105 public bool CloseBaseStream
111 public bool MinimalRead
113 get { return _buffer.MinimalRead; }
114 set { _buffer.MinimalRead = value; }
117 public int MaxBufferSize
119 get { return _buffer.MaxSize; }
123 _buffer.MaxSize = value;
127 public long BufferBaseOffset
129 get { return _buffer.BaseOffset; }
132 public int BufferBytesFilled
134 get { return _buffer.BytesFilled; }
137 public void Discard(int bytes)
140 _buffer.DiscardThrough(_buffer.BaseOffset + bytes);
143 public void DiscardThrough(long offset)
146 _buffer.DiscardThrough(offset);
149 public override bool CanRead
154 public override bool CanSeek
159 public override bool CanWrite
161 get { return false; }
164 public override void Flush()
169 public override long Length
171 get { return _baseStream.Length; }
174 public override long Position
176 get { return _readPosition; }
177 set { Seek(value, SeekOrigin.Begin); }
180 public override int ReadByte()
183 var val = _buffer.ReadByte(Position);
186 Seek(1, SeekOrigin.Current);
191 public override int Read(byte[] buffer, int offset, int count)
194 var cnt = _buffer.Read(Position, buffer, offset, count);
195 Seek(cnt, SeekOrigin.Current);
199 public override long Seek(long offset, SeekOrigin origin)
204 case SeekOrigin.Begin:
207 case SeekOrigin.Current:
211 offset += _baseStream.Length;
215 if (!_baseStream.CanSeek)
217 if (offset < _buffer.BaseOffset) throw new InvalidOperationException("Cannot seek to before the start of the buffer!");
218 if (offset >= _buffer.BufferEndOffset) throw new InvalidOperationException("Cannot seek to beyond the end of the buffer! Discard some bytes.");
221 return (_readPosition = offset);
224 public override void SetLength(long value)
226 throw new NotSupportedException();
229 public override void Write(byte[] buffer, int offset, int count)
231 throw new NotSupportedException();