| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714 |
- // ==++==
- //
- // Copyright (c) Microsoft Corporation. All rights reserved.
- //
- // ==--==
- /*============================================================
- **
- ** Class: BufferedStream2
- **
- **
- ===========================================================*/
- using System;
- using System.Diagnostics;
- using System.Globalization;
- using System.Runtime.Versioning;
- using System.Threading;
- using System.Runtime.InteropServices;
- using System.Runtime.Remoting.Messaging;
- using System.Runtime.CompilerServices;
- using Microsoft.Win32;
- using Microsoft.Win32.SafeHandles;
- using System.Security.Permissions;
- namespace System.IO {
- // This abstract implementation adds thread safe buffering on top
- // of the underlying stream. For most streams, having this intermediate
- // buffer translates to better performance due to the costly nature of
- // underlying IO, P/Invoke (such as disk IO). This also improves the locking
- // efficiency when operating under heavy concurrency. The synchronization
- // technique used in this implementation is specifically optimized for IO
- // The main differences between this implementation and the existing System.IO.BufferedStream
- // - the design allows for inheritance as opposed to wrapping streams
- // - it is thread safe, though currently only synchronous Write is optimized
- [HostProtection(Synchronization=true)]
- internal abstract class BufferedStream2 : Stream
- {
- protected internal const int DefaultBufferSize = 32*1024; //32KB or 64KB seems to give the best throughput
- protected int bufferSize; // Length of internal buffer, if it's allocated.
- private byte[] _buffer; // Internal buffer. Alloc on first use.
-
- // At present only concurrent buffer writing is optimized implicitly
- // while reading relies on explicit locking.
-
- // Ideally we want these fields to be volatile
- private /*volatile*/ int _pendingBufferCopy; // How many buffer writes are pending.
- private /*volatile*/ int _writePos; // Write pointer within shared buffer.
-
- // Should we use a separate buffer for reading Vs writing?
- private /*volatile*/ int _readPos; // Read pointer within shared buffer.
- private /*volatile*/ int _readLen; // Number of bytes read in buffer from file.
- // Ideally we want this field to be volatile but Interlocked operations
- // on 64bit int is not guaranteed to be atomic especially on 32bit platforms
- protected long pos; // Cache current location in the underlying stream.
- // Derived streams should override CanRead/CanWrite/CanSeek to enable/disable functionality as desired
- [ResourceExposure(ResourceScope.None)]
- [ResourceConsumption(ResourceScope.AppDomain, ResourceScope.AppDomain)]
- public override void Write(byte[] array, int offset, int count)
- {
- if (array==null)
- throw new ArgumentNullException("array", SR.GetString(SR.ArgumentNull_Buffer));
- if (offset < 0)
- throw new ArgumentOutOfRangeException("offset", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNum));
- if (count < 0)
- throw new ArgumentOutOfRangeException("count", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNum));
- if (array.Length - offset < count)
- throw new ArgumentException(SR.GetString(SR.Argument_InvalidOffLen));
- Debug.Assert((_readPos==0 && _readLen==0 && _writePos >= 0) || (_writePos==0 && _readPos <= _readLen), "We're either reading or writing, but not both.");
- if (_writePos==0) {
- // Ensure we can write to the stream, and ready buffer for writing.
- if (!CanWrite) __Error.WriteNotSupported();
- if (_readPos < _readLen) FlushRead();
- _readPos = 0;
- _readLen = 0;
- }
- // Optimization:
- // Don't allocate a buffer then call memcpy for 0 bytes.
- if (count == 0)
- return;
- do {
- // Avoid contention around spilling over the buffer, the locking mechanism here is bit unconventional.
- // Let's call this a YieldLock. It is closer to a spin lock than a semaphore but not quite a spin lock.
- // Forced thread context switching is better than a tight spin lock here for several reasons.
- // We utilize less CPU, yield to other threads (potentially the one doing the write, this is
- // especially important under heavy thread/processor contention environment) and also yield to
- // runtime thread aborts (important when run from a high pri thread like finalizer).
- if (_writePos > bufferSize) {
- Thread.Sleep(1);
- continue;
- }
- // Optimization:
- // For input chunk larger than internal buffer size, write directly
- // It is okay to have a ---- here with the _writePos check, which means
- // we have a loose order between flushing the intenal cache Vs writing
- // this larger chunk but that is fine. This step will nicely optimize
- // repeated writing of larger chunks by skipping the interlocked operation
- if ((_writePos == 0) && (count >= bufferSize)) {
- WriteCore(array, offset, count, true);
- return;
- }
- // We should review whether we need critical region markers for hosts.
- Thread.BeginCriticalRegion();
- Interlocked.Increment(ref _pendingBufferCopy);
- int newPos = Interlocked.Add(ref _writePos, count);
- int oldPos = (newPos - count);
- // Clear the buffer
- if (newPos > bufferSize) {
- Interlocked.Decrement(ref _pendingBufferCopy);
- Thread.EndCriticalRegion();
-
- // Though the lock below is not necessary for correctness, when operating in a heavy
- // thread contention environment, augmenting the YieldLock techinique with a critical
- // section around write seems to be giving slightly better performance while
- // not having noticable impact in the less contended situations.
- // Perhaps we can build a technique that keeps track of the contention?
- //lock (this)
- {
- // Make sure we didn't get pre-empted by another thread
- if (_writePos > bufferSize) {
- if ((oldPos <= bufferSize) && (oldPos > 0)) {
- while (_pendingBufferCopy != 0) {
- Thread.SpinWait(1);
- }
- WriteCore(_buffer, 0, oldPos, true);
- _writePos = 0;
- }
- }
- }
- }
- else {
- if (_buffer == null)
- Interlocked.CompareExchange(ref _buffer, new byte[bufferSize], null);
- // Copy user data into buffer, to write at a later date.
- Buffer.BlockCopy(array, offset, _buffer, oldPos, count);
-
- Interlocked.Decrement(ref _pendingBufferCopy);
- Thread.EndCriticalRegion();
- return;
- }
- } while (true);
- }
- #if _ENABLE_STREAM_FACTORING
- public override long Position
- {
- // Making the getter thread safe is not very useful anyways
- get {
- if (!CanSeek) __Error.SeekNotSupported();
- Debug.Assert((_readPos==0 && _readLen==0 && _writePos >= 0) || (_writePos==0 && _readPos <= _readLen), "We're either reading or writing, but not both.");
- // Compensate for buffer that we read from the handle (_readLen) Vs what the user
- // read so far from the internel buffer (_readPos). Of course add any unwrittern
- // buffered data
- return pos + (_readPos - _readLen + _writePos);
- }
- [MethodImplAttribute(MethodImplOptions.Synchronized)]
- set {
- if (value < 0) throw new ArgumentOutOfRangeException("value", Helper.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
-
- if (_writePos > 0) FlushWrite(false);
- _readPos = 0;
- _readLen = 0;
- Seek(value, SeekOrigin.Begin);
- }
- }
- [MethodImplAttribute(MethodImplOptions.Synchronized)]
- public override int Read(/*[In, Out]*/ byte[] array, int offset, int count)
- {
- if (array == null)
- throw new ArgumentNullException("array", Helper.GetResourceString("ArgumentNull_Buffer"));
- if (offset < 0)
- throw new ArgumentOutOfRangeException("offset", Helper.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
- if (count < 0)
- throw new ArgumentOutOfRangeException("count", Helper.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
- if (array.Length - offset < count)
- throw new ArgumentException(Helper.GetResourceString("Argument_InvalidOffLen"));
-
- Debug.Assert((_readPos==0 && _readLen==0 && _writePos >= 0) || (_writePos==0 && _readPos <= _readLen), "We're either reading or writing, but not both.");
- bool isBlocked = false;
- int n = _readLen - _readPos;
- // If the read buffer is empty, read into either user's array or our
- // buffer, depending on number of bytes user asked for and buffer size.
- if (n == 0) {
- if (!CanRead) __Error.ReadNotSupported();
- if (_writePos > 0) FlushWrite(false);
- if (!CanSeek || (count >= bufferSize)) {
- n = ReadCore(array, offset, count);
- // Throw away read buffer.
- _readPos = 0;
- _readLen = 0;
- return n;
- }
- if (_buffer == null) _buffer = new byte[bufferSize];
- n = ReadCore(_buffer, 0, bufferSize);
- if (n == 0) return 0;
- isBlocked = n < bufferSize;
- _readPos = 0;
- _readLen = n;
- }
- // Now copy min of count or numBytesAvailable (ie, near EOF) to array.
- if (n > count) n = count;
- Buffer.BlockCopy(_buffer, _readPos, array, offset, n);
- _readPos += n;
- // We may have read less than the number of bytes the user asked
- // for, but that is part of the Stream contract. Reading again for
- // more data may cause us to block if we're using a device with
- // no clear end of file, such as a serial port or pipe. If we
- // blocked here & this code was used with redirected pipes for a
- // process's standard output, this can lead to deadlocks involving
- // two processes. But leave this here for files to avoid what would
- // probably be a breaking change. --
- return n;
- }
- [HostProtection(ExternalThreading=true)]
- [MethodImplAttribute(MethodImplOptions.Synchronized)]
- public override IAsyncResult BeginRead(byte[] array, int offset, int numBytes, AsyncCallback userCallback, Object stateObject)
- {
- if (array==null)
- throw new ArgumentNullException("array");
- if (offset < 0)
- throw new ArgumentOutOfRangeException("offset", Helper.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
- if (numBytes < 0)
- throw new ArgumentOutOfRangeException("numBytes", Helper.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
- if (array.Length - offset < numBytes)
- throw new ArgumentException(Helper.GetResourceString("Argument_InvalidOffLen"));
- if (!CanRead) __Error.ReadNotSupported();
- Debug.Assert((_readPos==0 && _readLen==0 && _writePos >= 0) || (_writePos==0 && _readPos <= _readLen), "We're either reading or writing, but not both.");
-
- if (_writePos > 0) FlushWrite(false);
- if (_readPos == _readLen) {
- // I can't see how to handle buffering of async requests when
- // filling the buffer asynchronously, without a lot of complexity.
- // The problems I see are issuing an async read, we do an async
- // read to fill the buffer, then someone issues another read
- // (either synchronously or asynchronously) before the first one
- // returns. This would involve some sort of complex buffer locking
- // that we probably don't want to get into, at least not in V1.
- // If we did a [....] read to fill the buffer, we could avoid the
- // problem, and any async read less than 64K gets turned into a
- // synchronous read by NT anyways... --
- if (numBytes < bufferSize) {
- if (_buffer == null) _buffer = new byte[bufferSize];
- IAsyncResult bufferRead = BeginReadCore(_buffer, 0, bufferSize, null, null, 0);
- _readLen = EndRead(bufferRead);
- int n = _readLen;
- if (n > numBytes) n = numBytes;
- Buffer.BlockCopy(_buffer, 0, array, offset, n);
- _readPos = n;
- // Fake async
- return BufferedStreamAsyncResult.Complete(n, userCallback, stateObject, false);
- }
- // Here we're making our position pointer inconsistent
- // with our read buffer. Throw away the read buffer's contents.
- _readPos = 0;
- _readLen = 0;
- return BeginReadCore(array, offset, numBytes, userCallback, stateObject, 0);
- }
- else {
- int n = _readLen - _readPos;
- if (n > numBytes) n = numBytes;
- Buffer.BlockCopy(_buffer, _readPos, array, offset, n);
- _readPos += n;
- if (n >= numBytes)
- return BufferedStreamAsyncResult.Complete(n, userCallback, stateObject, false);
- // For streams with no clear EOF like serial ports or pipes
- // we cannot read more data without causing an app to block
- // incorrectly. Pipes don't go down this path
- // though. This code needs to be fixed.
- // Throw away read buffer.
- _readPos = 0;
- _readLen = 0;
-
- // WARNING: all state on asyncResult objects must be set before
- // we call ReadFile in BeginReadCore, since the OS can run our
- // callback & the user's callback before ReadFile returns.
- return BeginReadCore(array, offset + n, numBytes - n, userCallback, stateObject, n);
- }
- }
- public unsafe override int EndRead(IAsyncResult asyncResult)
- {
- if (asyncResult == null)
- throw new ArgumentNullException("asyncResult");
- BufferedStreamAsyncResult bsar = asyncResult as BufferedStreamAsyncResult;
- if (bsar != null) {
- if (bsar._isWrite)
- __Error.WrongAsyncResult();
- return bsar._numBytes;
- }
- else {
- return EndReadCore(asyncResult);
- }
- }
- [HostProtection(ExternalThreading=true)]
- [MethodImplAttribute(MethodImplOptions.Synchronized)]
- public override IAsyncResult BeginWrite(byte[] array, int offset, int numBytes, AsyncCallback userCallback, Object stateObject)
- {
- if (array==null)
- throw new ArgumentNullException("array");
- if (offset < 0)
- throw new ArgumentOutOfRangeException("offset", Helper.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
- if (numBytes < 0)
- throw new ArgumentOutOfRangeException("numBytes", Helper.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
- if (array.Length - offset < numBytes)
- throw new ArgumentException(Helper.GetResourceString("Argument_InvalidOffLen"));
- if (!CanWrite) __Error.WriteNotSupported();
- Debug.Assert((_readPos==0 && _readLen==0 && _writePos >= 0) || (_writePos==0 && _readPos <= _readLen), "We're either reading or writing, but not both.");
- if (_writePos==0) {
- if (_readPos < _readLen) FlushRead();
- _readPos = 0;
- _readLen = 0;
- }
- int n = bufferSize - _writePos;
- if (numBytes <= n) {
- if (_buffer == null) _buffer = new byte[bufferSize];
- Buffer.BlockCopy(array, offset, _buffer, _writePos, numBytes);
- _writePos += numBytes;
- return BufferedStreamAsyncResult.Complete(numBytes, userCallback, stateObject, true);
- }
- if (_writePos > 0) FlushWrite(false);
- return BeginWriteCore(array, offset, numBytes, userCallback, stateObject);
- }
- public unsafe override void EndWrite(IAsyncResult asyncResult)
- {
- if (asyncResult == null)
- throw new ArgumentNullException("asyncResult");
- BufferedStreamAsyncResult bsar = asyncResult as BufferedStreamAsyncResult;
- if (bsar == null)
- EndWriteCore(asyncResult);
- }
- // Reads a byte from the file stream. Returns the byte cast to an int
- // or -1 if reading from the end of the stream.
- [MethodImplAttribute(MethodImplOptions.Synchronized)]
- public override int ReadByte()
- {
- if (_readLen == 0 && !CanRead)
- __Error.ReadNotSupported();
-
- Debug.Assert((_readPos==0 && _readLen==0 && _writePos >= 0) || (_writePos==0 && _readPos <= _readLen), "We're either reading or writing, but not both.");
-
- if (_readPos == _readLen) {
- if (_writePos > 0) FlushWrite(false);
- Debug.Assert(bufferSize > 0, "bufferSize > 0");
- if (_buffer == null) _buffer = new byte[bufferSize];
- _readLen = ReadCore(_buffer, 0, bufferSize);
- _readPos = 0;
- }
- if (_readPos == _readLen)
- return -1;
- int result = _buffer[_readPos];
- _readPos++;
- return result;
- }
- [MethodImplAttribute(MethodImplOptions.Synchronized)]
- public override void WriteByte(byte value)
- {
- if (_writePos == 0) {
- if (!CanWrite)
- __Error.WriteNotSupported();
- if (_readPos < _readLen)
- FlushRead();
- _readPos = 0;
- _readLen = 0;
- Debug.Assert(bufferSize > 0, "bufferSize > 0");
- if (_buffer==null)
- _buffer = new byte[bufferSize];
- }
- if (_writePos == bufferSize)
- FlushWrite(false);
- _buffer[_writePos] = value;
- _writePos++;
- }
- [MethodImplAttribute(MethodImplOptions.Synchronized)]
- public override long Seek(long offset, SeekOrigin origin)
- {
- if (origin<SeekOrigin.Begin || origin>SeekOrigin.End)
- throw new ArgumentException(Helper.GetResourceString("Argument_InvalidSeekOrigin"));
-
- if (!CanSeek) __Error.SeekNotSupported();
- Debug.Assert((_readPos==0 && _readLen==0 && _writePos >= 0) || (_writePos==0 && _readPos <= _readLen), "We're either reading or writing, but not both.");
- // If we've got bytes in our buffer to write, write them out.
- // If we've read in and consumed some bytes, we'll have to adjust
- // our seek positions ONLY IF we're seeking relative to the current
- // position in the stream. This simulates doing a seek to the new
- // position, then a read for the number of bytes we have in our buffer.
- if (_writePos > 0) {
- FlushWrite(false);
- }
- else if (origin == SeekOrigin.Current) {
- // Don't call FlushRead here, which would have caused an infinite
- // loop. Simply adjust the seek origin. This isn't necessary
- // if we're seeking relative to the beginning or end of the stream.
- offset -= (_readLen - _readPos);
- }
- long oldPos = pos + (_readPos - _readLen);
- long curPos = SeekCore(offset, origin);
- // We now must update the read buffer. We can in some cases simply
- // update _readPos within the buffer, copy around the buffer so our
- // Position property is still correct, and avoid having to do more
- // reads from the disk. Otherwise, discard the buffer's contents.
- if (_readLen > 0) {
- // We can optimize the following condition:
- // oldPos - _readPos <= curPos < oldPos + _readLen - _readPos
- if (oldPos == curPos) {
- if (_readPos > 0) {
- Buffer.BlockCopy(_buffer, _readPos, _buffer, 0, _readLen - _readPos);
- _readLen -= _readPos;
- _readPos = 0;
- }
- // If we still have buffered data, we must update the stream's
- // position so our Position property is correct.
- if (_readLen > 0)
- SeekCore(_readLen, SeekOrigin.Current);
- }
- else if (oldPos - _readPos < curPos && curPos < oldPos + _readLen - _readPos) {
- int diff = (int)(curPos - oldPos);
- Buffer.BlockCopy(_buffer, _readPos+diff, _buffer, 0, _readLen - (_readPos + diff));
- _readLen -= (_readPos + diff);
- _readPos = 0;
- if (_readLen > 0)
- SeekCore(_readLen, SeekOrigin.Current);
- }
- else {
- // Lose the read buffer.
- _readPos = 0;
- _readLen = 0;
- }
- Debug.Assert(_readLen >= 0 && _readPos <= _readLen, "_readLen should be nonnegative, and _readPos should be less than or equal _readLen");
- Debug.Assert(curPos == Position, "Seek optimization: curPos != Position! Buffer math was mangled.");
- }
- return curPos;
- }
- #endif //_ENABLE_STREAM_FACTORING
- [MethodImplAttribute(MethodImplOptions.Synchronized)]
- public override void Flush()
- {
- try {
- if (_writePos > 0)
- FlushWrite(false);
- else if (_readPos < _readLen)
- FlushRead();
- }
- finally {
- _writePos = 0;
- _readPos = 0;
- _readLen = 0;
- }
- }
- // Reading is done by blocks from the file, but someone could read
- // 1 byte from the buffer then write. At that point, the OS's file
- // pointer is out of [....] with the stream's position. All write
- // functions should call this function to preserve the position in the file.
- [MethodImplAttribute(MethodImplOptions.Synchronized)]
- protected void FlushRead() {
- #if _ENABLE_STREAM_FACTORING
- Debug.Assert(_writePos == 0, "BufferedStream: Write buffer must be empty in FlushRead!");
-
- if (_readPos - _readLen != 0) {
- Debug.Assert(CanSeek, "BufferedStream will lose buffered read data now.");
- if (CanSeek)
- SeekCore(_readPos - _readLen, SeekOrigin.Current);
- }
- _readPos = 0;
- _readLen = 0;
- #endif //_ENABLE_STREAM_FACTORING
- }
- // Writes are buffered. Anytime the buffer fills up
- // (_writePos + delta > bufferSize) or the buffer switches to reading
- // and there is left over data (_writePos > 0), this function must be called.
- [MethodImplAttribute(MethodImplOptions.Synchronized)]
- protected void FlushWrite(bool blockForWrite) {
- Debug.Assert(_readPos == 0 && _readLen == 0, "BufferedStream: Read buffer must be empty in FlushWrite!");
-
- if (_writePos > 0)
- WriteCore(_buffer, 0, _writePos, blockForWrite);
- _writePos = 0;
- }
- protected override void Dispose(bool disposing)
- {
- try {
- // Flush data to disk iff we were writing.
- if (_writePos > 0) {
- // With our Whidbey async IO & overlapped support for AD unloads,
- // we don't strictly need to block here to release resources
- // if the underlying IO is overlapped since that support
- // takes care of the pinning & freeing the
- // overlapped struct. We need to do this when called from
- // Close so that the handle is closed when Close returns, but
- // we do't need to call EndWrite from the finalizer.
- // Additionally, if we do call EndWrite, we block forever
- // because AD unloads prevent us from running the managed
- // callback from the IO completion port. Blocking here when
- // called from the finalizer during AD unload is clearly wrong,
- // but we can't use any sort of test for whether the AD is
- // unloading because if we weren't unloading, an AD unload
- // could happen on a separate thread before we call EndWrite.
- FlushWrite(disposing);
- }
- }
- finally {
- // Don't set the buffer to null, to avoid a NullReferenceException
- // when users have a race condition in their code (ie, they call
- // Close when calling another method on Stream like Read).
- //_buffer = null;
- _readPos = 0;
- _readLen = 0;
- _writePos = 0;
- base.Dispose(disposing);
- }
- }
- //
- // Helper methods
- //
- #if _ENABLE_STREAM_FACTORING
- protected int BufferedWritePosition
- {
- // Making the getter thread safe is not very useful anyways
- get {
- return _writePos;
- }
- //set {
- // Interlocked.Exchange(ref _writePos, value);
- //}
- }
- protected int BufferedReadPosition
- {
- // Making the getter thread safe is not very useful anyways
- get {
- return _readPos;
- }
- //set {
- // Interlocked.Exchange(ref _readPos, value);
- //}
- }
- #endif //_ENABLE_STREAM_FACTORING
- protected long UnderlyingStreamPosition
- {
- // Making the getter thread safe is not very useful anyways
- get {
- return pos;
- }
- set {
- Interlocked.Exchange(ref pos, value);
- }
- }
- protected long AddUnderlyingStreamPosition(long posDelta)
- {
- return Interlocked.Add(ref pos, posDelta);
- }
-
- [MethodImplAttribute(MethodImplOptions.Synchronized)]
- protected internal void DiscardBuffer()
- {
- _readPos = 0;
- _readLen = 0;
- _writePos = 0;
- }
- private void WriteCore(byte[] buffer, int offset, int count, bool blockForWrite)
- {
- long streamPos;
- WriteCore(buffer, offset, count, blockForWrite, out streamPos);
- }
- protected abstract void WriteCore(byte[] buffer, int offset, int count, bool blockForWrite, out long streamPos);
- #if _ENABLE_STREAM_FACTORING
- private int ReadCore(byte[] buffer, int offset, int count)
- {
- long streamPos;
- return ReadCore(buffer, offset, count, out streamPos);
- }
-
- private int EndReadCore(IAsyncResult asyncResult)
- {
- long streamPos;
- return EndReadCore(asyncResult, out streamPos);
- }
- private void EndWriteCore(IAsyncResult asyncResult)
- {
- long streamPos;
- EndWriteCore(asyncResult, out streamPos);
- }
- // Derived streams should implement the following core methods
- protected abstract int ReadCore(byte[] buffer, int offset, int count, out long streamPos);
- [ResourceExposure(ResourceScope.None)]
- [ResourceConsumption(ResourceScope.AppDomain, ResourceScope.AppDomain)]
- protected abstract IAsyncResult BeginReadCore(byte[] bytes, int offset, int numBytes, AsyncCallback userCallback, Object stateObject, int numBufferedBytesRead);
- protected abstract int EndReadCore(IAsyncResult asyncResult, out long streamPos);
- [ResourceExposure(ResourceScope.None)]
- [ResourceConsumption(ResourceScope.AppDomain, ResourceScope.AppDomain)]
- protected abstract IAsyncResult BeginWriteCore(byte[] bytes, int offset, int numBytes, AsyncCallback userCallback, Object stateObject);
- protected abstract void EndWriteCore(IAsyncResult asyncResult, out long streamPos);
- protected abstract long SeekCore(long offset, SeekOrigin origin);
- #endif //_ENABLE_STREAM_FACTORING
- }
- #if _ENABLE_STREAM_FACTORING
- // Fake async result
- unsafe internal sealed class BufferedStreamAsyncResult : IAsyncResult
- {
- // User code callback
- internal AsyncCallback _userCallback;
- internal Object _userStateObject;
- internal int _numBytes; // number of bytes read OR written
- //internal int _errorCode;
- internal bool _isWrite; // Whether this is a read or a write
- public Object AsyncState
- {
- get { return _userStateObject; }
- }
- public bool IsCompleted
- {
- get { return true; }
- }
- public WaitHandle AsyncWaitHandle
- {
- get { return null; }
- }
- public bool CompletedSynchronously
- {
- get { return true; }
- }
- internal static IAsyncResult Complete(int numBufferedBytes, AsyncCallback userCallback, Object userStateObject, bool isWrite)
- {
- // Fake async
- BufferedStreamAsyncResult asyncResult = new BufferedStreamAsyncResult();
- asyncResult._numBytes = numBufferedBytes;
- asyncResult._userCallback = userCallback;
- asyncResult._userStateObject = userStateObject;
- asyncResult._isWrite = isWrite;
- if (userCallback != null) {
- userCallback(asyncResult);
- }
- return asyncResult;
- }
- }
- #endif //_ENABLE_STREAM_FACTORING
- }
|