using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;
namespace AsyncStreaming
{
///
/// Specifies identifiers to indicate the state of AsyncStreaming.
///
public enum AsyncStreamState
{
None,
///
/// This state occurs when stream is ready to start.
///
Ready,
///
/// This state occurs when stream is started.
///
Started,
///
/// This state occurs when stream is paused.
///
Paused,
///
/// This state occurs when stream is stoped.
///
Stoped,
///
/// This state occurs when stream is finished.
///
Finished,
///
/// This state occurs when a stream exception is happent.
///
Error
}
///
/// Represents errors that occur during async streaming.
///
public class AsyncStreamException : Exception
{
public AsyncStreamException() : base() { }
public AsyncStreamException(string message) : base(message) { }
public AsyncStreamException(string message, Exception innerException) : base(message, innerException) { }
}
#region AsyncStreamStateChangeArgs
///
/// Provides data for the AsyncStreaming.AsyncStreamReader class event on state change.
///
public class AsyncStreamStateChangeArgs : EventArgs
{
private AsyncStreamState currentState = AsyncStreamState.Ready;
///
/// Gets current object AsyncStream.AsyncStreamReader state.
///
public AsyncStreamState CurrentState
{
get { return currentState; }
}
///
/// Initializes a new instance of AsyncStreaming.AsyncStreamStateChangeArgs class.
///
/// The state that have been changed.
public AsyncStreamStateChangeArgs(AsyncStreamState state)
{
this.currentState = state;
}
}
#endregion
#region AsyncStreamErrorEventArgs
///
/// Provides data for the AsyncStreaming.AsyncStreamReader and AsyncStreaming.AsyncStreamWriter class event on error.
///
public class AsyncStreamErrorEventArgs : EventArgs
{
private AsyncStreamException error;
///
/// Gets AsyncStreamException exception data.
///
public AsyncStreamException Error
{
get { return error; }
}
///
/// Initializes a new instance of AsyncStreaming.AsyncStreamReader and AsyncStreaming.AsyncStreamWriter class.
///
/// AsyncStreamException that have been happen.
public AsyncStreamErrorEventArgs(AsyncStreamException e)
{
error = e;
}
}
#endregion
#region AsyncReadEventArgs
///
/// Provides data for the AsyncStreaming.AsyncReadEventArgs class event on async read.
///
public class AsyncReadEventArgs : EventArgs
{
private long bytesReaded;
///
/// Gets the number of bytes that have been readed.
///
public long BytesReaded
{
get { return bytesReaded; }
}
private int percentReaded;
///
/// Gets percent of current readed bytes.
///
public int PercentReaded
{
get { return percentReaded; }
}
private long length;
///
/// Gets the length in bytes of the stream.
///
public long Length
{
get { return length; }
}
private bool isComplete;
///
/// Gets value that indicating whether the current stream read is complete.
///
public bool IsComplete
{
get { return isComplete; }
set { isComplete = value; }
}
private byte[] result;
///
/// Gets current readed strem byte array.
///
public byte[] Result
{
get { return result; }
set { result = value; }
}
///
/// Initializes a new instance of AsyncStreaming.AsyncReadEventArgs class.
///
/// How many bytes have been readed till now.
/// Total stream length.
public AsyncReadEventArgs(long bytesReaded, long length)
{
this.bytesReaded = bytesReaded;
this.length = length;
this.isComplete = false;
try
{
//int p = (int)length / 100;
this.percentReaded = GetPercent(bytesReaded, length); // (int)bytesReaded / p;
if (this.percentReaded > 100)
this.percentReaded = 100;
}
catch (DivideByZeroException) { this.percentReaded = 100; }
}
public static int GetPercent(long bytesReaded, long length)
{
int result = 0;
try
{
int p = (int)length / 100;
result = (int)bytesReaded / p;
}
catch (DivideByZeroException) { result = 100; }
return result;
}
///
/// Initializes a new instance of AsyncStreaming.AsyncReadEventArgs class with result when readed is finished.
///
///
///
public static AsyncReadEventArgs EndArgs(byte[] result)
{
AsyncReadEventArgs args = new AsyncReadEventArgs(0, 0);
args.IsComplete = true;
args.Result = result;
return args;
}
}
#endregion
#region AsyncWriteEventArgs
///
/// Provides data for the AsyncStreaming.AsyncStreamWriter class event on read error.
///
public class AsyncWriteEventArgs : EventArgs
{
private long bytesWrote;
///
/// Gets the number of bytes that have been wrote.
///
public long BytesWrote
{
get { return bytesWrote; }
}
private int percentWrote;
///
/// Gets percent of current wrote bytes.
///
public int PercentWrote
{
get { return percentWrote; }
}
private long length;
///
/// Gets the length in bytes of the stream.
///
public long Length
{
get { return length; }
}
private bool isComplete;
///
/// Gets value that indicating whether the current stream write is complete.
///
public bool IsComplete
{
get { return isComplete; }
set { isComplete = value; }
}
///
/// Initializes a new instance of AsyncStreaming.AsyncReadEventArgs class.
///
/// How many bytes have been wrote till now.
/// Total stream length.
public AsyncWriteEventArgs(long bytesWrote, long length)
{
this.bytesWrote = bytesWrote;
this.length = length;
this.isComplete = false;
try
{
this.percentWrote = GetPercent(bytesWrote, length);
if (this.percentWrote > 100)
this.percentWrote = 100;
}
catch (DivideByZeroException) { this.percentWrote = 100; }
}
///
/// Initializes a new instance of AsyncStreaming.AsyncWriteEventArgs class with result when write is finished.
///
///
///
public static AsyncWriteEventArgs EndArgs()
{
AsyncWriteEventArgs args = new AsyncWriteEventArgs(0, 0);
args.IsComplete = true;
return args;
}
public static int GetPercent(long bytesWrote, long length)
{
int result = 0;
try
{
int p = (int)bytesWrote * 100;
result = p / (int)length + 1;
if (result > 100) result = 100;
}
catch (DivideByZeroException) { result = 0; }
return result;
}
}
#endregion
[Serializable]
public class BaseAsyncStreamReader : Interfaces.IAsyncStreamReader
{
#region Event declarations
///
/// Occurs when the byte is readed from the System.IO.StreamReader.
///
public event EventHandler OnReadBytes;
///
/// Occurs when the all bytes are readed from the System.IO.StreamReader.
///
public event EventHandler OnEndRead;
///
/// Occurs when an AsyncStreaming.AsyncExcpetion is happen.
///
public event EventHandler OnError;
///
/// Occurs when state of AsyncStreaming.AsyncStreamReader is changed.
///
public event EventHandler OnStateChanged;
#endregion
#region Fields
protected AsyncStreamState _state = AsyncStreamState.Ready;
protected ManualResetEvent _event = new ManualResetEvent(true);
protected string _path = String.Empty;
protected Thread _worker;
protected StreamReader _reader;
protected long _streamLenght;
protected int _byteBuffer;
protected byte[] _buffer;
protected int _currentPercent;
#endregion
#region Properties
///
/// Gets the current state of AsyncStreaming.AsyncStreamReader.
///
public AsyncStreamState State
{
get { return _state; }
}
///
/// Gets the complete file path to be read.
///
public string Path
{
get { return _path; }
}
#endregion
#region Methods
///
/// Starts an asynchronous read operation.
///
public void StartRead()
{
if (String.IsNullOrEmpty(_path))
throw new AsyncStreamException("Cannot start read because file path is null or empty!");
_currentPercent = 0;
_byteBuffer = 0;
_reader = new StreamReader(_path);
_streamLenght = _reader.BaseStream.Length;
_buffer = new byte[_streamLenght];
_worker = new Thread(new ThreadStart(readWork));
_worker.Start();
}
private void readWork()
{
try
{
int position = 0;
changeState(AsyncStreamState.Started);
while (_byteBuffer > -1)
{
_event.WaitOne();
_byteBuffer = _reader.BaseStream.ReadByte();
if (_byteBuffer != -1)
{
_buffer[position] = (byte)_byteBuffer;
}
if (OnReadBytes != null && _byteBuffer != -1)
{
if (AsyncReadEventArgs.GetPercent(position, _streamLenght) > _currentPercent)
{
_currentPercent = AsyncReadEventArgs.GetPercent(position, _streamLenght);
OnReadBytes(null, new AsyncReadEventArgs(position, _streamLenght));
}
}
else if (OnEndRead != null && _byteBuffer == -1)
{
OnEndRead(null, AsyncReadEventArgs.EndArgs(_buffer));
changeState(AsyncStreamState.Finished);
_reader.Close();
_reader.Dispose();
_worker.Abort();
}
position++;
}
}
catch (ThreadAbortException) { }
catch (Exception e)
{
AsyncStreamException exc = new AsyncStreamException("Error async reading from: " + _path + ".", e);
if (OnError != null)
{
OnError(null, new AsyncStreamErrorEventArgs(exc));
}
else
throw exc;
this.StopRead();
changeState(AsyncStreamState.Error);
}
}
///
/// Pause an asynchronous read operation.
///
public void PauseRead()
{
changeState(AsyncStreamState.Paused);
//_worker.Suspend();
_event.Reset();
}
///
/// Resume an paused asynchronous read operation.
///
public void ResumeRead()
{
changeState(AsyncStreamState.Started);
//_worker.Resume();
_event.Set();
}
///
/// Stops an asynchronous read operation.
///
public void StopRead()
{
changeState(AsyncStreamState.Stoped);
_currentPercent = 0;
if (_worker != null)
{
_worker.Abort();
_worker = null;
}
if (_reader != null)
{
if (_reader.BaseStream != null)
{
_reader.BaseStream.Flush();
_reader.BaseStream.Close();
}
_reader.Close();
_reader.Dispose();
}
}
private void changeState(AsyncStreamState state)
{
_state = state;
if (OnStateChanged != null)
OnStateChanged(null, new AsyncStreamStateChangeArgs(state));
}
#endregion
#region IDisposable Members
public void Dispose()
{
if (_worker != null)
{
_worker.Abort();
_worker = null;
}
if (_reader != null)
{
if (_reader.BaseStream != null)
{
_reader.BaseStream.Flush();
_reader.BaseStream.Close();
}
_reader.Close();
_reader.Dispose();
}
}
#endregion
}
public class AsyncStreamReader : BaseAsyncStreamReader
{
///
/// Implements a System.IO.TextReader that reads characters from a byte stream
/// in a particular encoding.
///
/// The complete file path to be read.
public AsyncStreamReader(string path)
{
if (String.IsNullOrEmpty(path))
throw new AsyncStreamException("Cannot create AsyncStreamReader class because file path in null or empty!");
this._path = path;
this._currentPercent = 0;
}
}
public class BaseAsyncStreamWriter : Interfaces.IAsyncStreamWriter
{
#region Events
///
/// Occurs when state of AsyncStream.AsyncStreamWriter is changed.
///
public event EventHandler OnStateChanged;
///
/// Occurs when the byte is writed to the System.IO.StreamWriter.
///
public event EventHandler OnWroteBytes;
///
/// Occurs when the all bytes are has been written.
///
public event EventHandler OnEndWrite;
///
/// Occurs when an AsyncStream.AsyncExcpetion is happen.
///
public event EventHandler OnError;
#endregion
#region Fields
protected string _outputPath = String.Empty;
protected Encoding _streamEncoding = Encoding.Default;
protected AsyncStreamState _state = AsyncStreamState.Ready;
protected ManualResetEvent _event = new ManualResetEvent(true);
protected Thread _worker;
protected StreamWriter _writer;
protected long _streamLength;
protected int _currentPecent;
protected byte[] _buffer;
protected int _byteBuffer = 0;
#endregion
#region Properties
///
/// Gets the complete file path to write to. Path can be a file name.
///
public string OutputPath
{
get { return _outputPath; }
}
///
/// Gets or sets the character encoding to use. Default encoding is Encoding.ASCII.
///
public Encoding StreamEncoding
{
get { return _streamEncoding; }
set { _streamEncoding = value; }
}
///
/// Gets the current state of AsyncStreaming.AsyncStreamWriter.
///
public AsyncStreamState State
{
get { return _state; }
}
#endregion
///
/// Begins an asynchronous write operation.
///
public void StartWrite()
{
try
{
_currentPecent = 0;
if (_buffer == null || _buffer.Length == 0)
throw new AsyncStreamException("Cannot start write because buffer is null or empty!");
_byteBuffer = 0;
_streamLength = _buffer.Length;
if (String.IsNullOrEmpty(this._outputPath))
throw new AsyncStreamException("Cannot start write because output file path is null or empty!");
_writer = new StreamWriter(this._outputPath);
_worker = new Thread(new ThreadStart(writeWork));
_worker.Start();
}
catch (AsyncStreamException aexc)
{
changeState(AsyncStreamState.Error);
if (OnError != null)
OnError(null, new AsyncStreamErrorEventArgs(aexc));
else
throw aexc;
}
}
private void writeWork()
{
try
{
int position = 0;
changeState(AsyncStreamState.Started);
while (position < _streamLength)
{
_event.WaitOne();
_writer.BaseStream.WriteByte(_buffer[position]);
if (OnWroteBytes != null)
{
if (AsyncWriteEventArgs.GetPercent(position, _streamLength) > _currentPecent)
{
_currentPecent = AsyncWriteEventArgs.GetPercent(position, _streamLength);
OnWroteBytes(null, new AsyncWriteEventArgs(position, _streamLength));
}
}
position++;
}
changeState(AsyncStreamState.Finished);
if (OnEndWrite != null)
{
OnEndWrite(null, AsyncWriteEventArgs.EndArgs());
}
this.Dispose();
}
catch (ThreadAbortException) { }
catch (Exception e)
{
AsyncStreamException exc = new AsyncStreamException("Error async writing to: " + _outputPath + ".", e);
if (OnError != null)
{
OnError(null, new AsyncStreamErrorEventArgs(exc));
}
else
throw exc;
this.StopWrite();
changeState(AsyncStreamState.Error);
}
}
///
/// Pause an asynchronous write operation.
///
public void PauseWrite()
{
changeState(AsyncStreamState.Paused);
//_worker.Suspend();
_event.Reset();
}
///
/// Resume an paused asynchronous write operation.
///
public void ResumeWrite()
{
changeState(AsyncStreamState.Started);
//_worker.Resume();
_event.Set();
}
///
/// Stops an asynchronous write operation.
///
public void StopWrite()
{
changeState(AsyncStreamState.Stoped);
this.Dispose();
}
///
/// Set AsyncStreaming.AsyncStreamWriter class.
///
/// An array of bytes. This method copies count bytes from buffer to the current stream.
public void SetBuffer(byte[] buffer)
{
if (buffer == null)
throw new AsyncStreamException("Cannot set writer because buffer is null or empty!");
this._buffer = buffer;
}
///
/// Set AsyncStreaming.AsyncStreamWriter class.
///
/// The complete file path to write to. Path can be a file name.
/// An array of bytes. This method copies count bytes from buffer to the current stream.
public void SetWriter(string outputPath, byte[] buffer)
{
if (buffer == null)
throw new AsyncStreamException("Cannot set writer because buffer is null or empty!");
if (String.IsNullOrEmpty(outputPath))
throw new AsyncStreamException("Cannot set writer beaause output file path is null or empty!");
this._outputPath = outputPath;
this._buffer = buffer;
}
///
/// Set AsyncStreaming.AsyncStreamWriter class.
///
/// The complete file path to write to. Path can be a file name.
/// The string to write to the stream. If value is null, nothing is written.
public void SetWriter(string outputPath, string buffer)
{
if (String.IsNullOrEmpty(buffer))
throw new AsyncStreamException("Cannot set writer becuse buffer is null or empty!");
if (String.IsNullOrEmpty(outputPath))
throw new AsyncStreamException("Cannot set writer beause output file path is null or empty!");
this._outputPath = outputPath;
this._buffer = _streamEncoding.GetBytes(buffer);
}
///
/// Set AsyncStreaming.AsyncStreamWriter class.
///
/// The complete file path to write to. Path can be a file name.
/// The string to write to the stream. If value is null, nothing is written.
/// The string encoding to use.
public void SetWriter(string outputPath, string buffer, Encoding encoding)
{
if (String.IsNullOrEmpty(buffer))
throw new AsyncStreamException("Cannot set writer because buffer is null or empty!");
if (String.IsNullOrEmpty(outputPath))
throw new AsyncStreamException("Cannot set writer beaause output file path is null or empty!");
if (encoding == null)
throw new AsyncStreamException("Cannot set writer because encoding is null!");
this._streamEncoding = encoding;
this._outputPath = outputPath;
this._buffer = _streamEncoding.GetBytes(buffer);
}
private void changeState(AsyncStreamState state)
{
this._state = state;
if (OnStateChanged != null)
OnStateChanged(null, new AsyncStreamStateChangeArgs(_state));
}
#region IDisposable Members
public void Dispose()
{
if (_writer != null)
{
if (_writer.BaseStream != null)
{
_writer.BaseStream.Flush();
_writer.BaseStream.Close();
}
else
{
_writer.Close();
_writer.Dispose();
}
}
if (_worker != null)
_worker.Abort();
}
#endregion
}
public class AsyncStreamWriter : BaseAsyncStreamWriter
{
#region Constructors
///
/// Initializes a new empty instance of the AsyncStreaming.AsyncStreamWriter class.
///
public AsyncStreamWriter() { }
///
/// Initializes a new instance of the AsyncStreaming.AsyncStreamWriter class for the specified
/// file on the specified path, using the default encoding.
///
/// The complete file path to write to. Path can be a file name.
public AsyncStreamWriter(string outputPath)
{
this._outputPath = outputPath;
}
///
/// Initializes a new instance of the AsyncStreaming.AsyncStreamWriter class for the specified
/// file on the specified path, using the default encoding.
///
/// The complete file path to write to. Path can be a file name.
/// The string to write to the stream. If value is null, nothing is written.
/// The string encoding to use.
public AsyncStreamWriter(string outputPath, string buffer, Encoding encoding)
{
if (String.IsNullOrEmpty(buffer))
return;
this._outputPath = outputPath;
this._buffer = _streamEncoding.GetBytes(buffer);
if (encoding != null)
this._streamEncoding = encoding;
}
///
/// Initializes a new instance of the AsyncStreaming.AsyncStreamWriter class for the specified
/// file on the specified path, using the default encoding.
///
/// The complete file path to write to. Path can be a file name.
/// The string to write to the stream. If value is null, nothing is written.
public AsyncStreamWriter(string outputPath, string buffer)
{
if (String.IsNullOrEmpty(buffer))
return;
this._outputPath = outputPath;
this._buffer = _streamEncoding.GetBytes(buffer);
}
///
/// Initializes a new instance of the AsyncStreaming.AsyncStreamWriter class for the specified
/// file on the specified path, using the default encoding.
///
/// The complete file path to write to. Path can be a file name.
/// An array of bytes. This method copies count bytes from buffer to the current stream.
public AsyncStreamWriter(string outputPath, byte[] buffer)
{
if (buffer == null)
return;
this._outputPath = outputPath;
this._buffer = buffer;
}
#endregion
}
}
namespace AsyncStreaming.Interfaces
{
public interface IAsyncStreamReader : IDisposable
{
///
/// Gets the current state of AsyncStreaming.AsyncStreamReader.
///
AsyncStreamState State { get; }
///
/// Gets the complete file path to be read.
///
string Path { get; }
///
/// Starts an asynchronous read operation.
///
void StartRead();
///
/// Pause an asynchronous read operation.
///
void PauseRead();
///
/// Resume an paused asynchronous read operation.
///
void ResumeRead();
///
/// Stops an asynchronous read operation.
///
void StopRead();
}
public interface IAsyncStreamWriter : IDisposable
{
///
/// Gets the complete file path to write to. Path can be a file name.
///
string OutputPath { get; }
///
/// Gets or sets the character encoding to use. Default encoding is Encoding.ASCII.
///
Encoding StreamEncoding { get; set; }
///
/// Gets the current state of AsyncStreaming.AsyncStreamWriter.
///
AsyncStreamState State { get; }
///
/// Starts an asynchronous write operation.
///
void StartWrite();
///
/// Pause an asynchronous write operation.
///
void PauseWrite();
///
/// Resume an paused asynchronous write operation.
///
void ResumeWrite();
///
/// Stops an asynchronous write operation.
///
void StopWrite();
///
/// Set AsyncStreaming.AsyncStreamWriter class.
///
/// An array of bytes. This method copies count bytes from buffer to the current stream.
void SetBuffer(byte[] buffer);
///
/// Set AsyncStreaming.AsyncStreamWriter class.
///
/// The complete file path to write to. Path can be a file name.
/// An array of bytes. This method copies count bytes from buffer to the current stream.
void SetWriter(string outputPath, byte[] buffer);
///
/// Set AsyncStreaming.AsyncStreamWriter class.
///
/// The complete file path to write to. Path can be a file name.
/// The string to write to the stream. If value is null, nothing is written.
void SetWriter(string outputPath, string buffer);
///
/// Set AsyncStreaming.AsyncStreamWriter class.
///
/// The complete file path to write to. Path can be a file name.
/// The string to write to the stream. If value is null, nothing is written.
/// The string encoding to use.
void SetWriter(string outputPath, string buffer, Encoding encoding);
}
}