123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508 |
- namespace SevenZip
- {
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.IO;
- using System.Runtime.InteropServices;
- #if UNMANAGED
- /// <summary>
- /// A class that has DisposeStream property.
- /// </summary>
- internal class DisposeVariableWrapper
- {
- public bool DisposeStream { protected get; set; }
- protected DisposeVariableWrapper(bool disposeStream) { DisposeStream = disposeStream; }
- }
- /// <summary>
- /// Stream wrapper used in InStreamWrapper
- /// </summary>
- internal class StreamWrapper : DisposeVariableWrapper, IDisposable
- {
- /// <summary>
- /// File name associated with the stream (for date fix)
- /// </summary>
- private readonly string _fileName;
- private readonly DateTime _fileTime;
- /// <summary>
- /// Worker stream for reading, writing and seeking.
- /// </summary>
- private Stream _baseStream;
- /// <summary>
- /// Initializes a new instance of the StreamWrapper class
- /// </summary>
- /// <param name="baseStream">Worker stream for reading, writing and seeking</param>
- /// <param name="fileName">File name associated with the stream (for attributes fix)</param>
- /// <param name="time">File last write time (for attributes fix)</param>
- /// <param name="disposeStream">Indicates whether to dispose the baseStream</param>
- protected StreamWrapper(Stream baseStream, string fileName, DateTime time, bool disposeStream)
- : base(disposeStream)
- {
- _baseStream = baseStream;
- _fileName = fileName;
- _fileTime = time;
- }
- /// <summary>
- /// Initializes a new instance of the StreamWrapper class
- /// </summary>
- /// <param name="baseStream">Worker stream for reading, writing and seeking</param>
- /// <param name="disposeStream">Indicates whether to dispose the baseStream</param>
- protected StreamWrapper(Stream baseStream, bool disposeStream)
- : base(disposeStream)
- {
- _baseStream = baseStream;
- }
- /// <summary>
- /// Gets the worker stream for reading, writing and seeking.
- /// </summary>
- protected Stream BaseStream => _baseStream;
- #region IDisposable Members
- /// <summary>
- /// Cleans up any resources used and fixes file attributes.
- /// </summary>
- public void Dispose()
- {
- if (_baseStream != null && DisposeStream)
- {
- try
- {
- _baseStream.Dispose();
- }
- catch (ObjectDisposedException) { }
- _baseStream = null;
- }
-
- if (!string.IsNullOrEmpty(_fileName) && File.Exists(_fileName))
- {
- try
- {
- File.SetLastWriteTime(_fileName, _fileTime);
- File.SetLastAccessTime(_fileName, _fileTime);
- File.SetCreationTime(_fileName, _fileTime);
- }
- catch (ArgumentOutOfRangeException) {}
- }
- GC.SuppressFinalize(this);
- }
- #endregion
- public virtual void Seek(long offset, SeekOrigin seekOrigin, IntPtr newPosition)
- {
- if (BaseStream != null)
- {
- long position = BaseStream.Seek(offset, seekOrigin);
- if (newPosition != IntPtr.Zero)
- {
- Marshal.WriteInt64(newPosition, position);
- }
- }
- }
- }
- /// <summary>
- /// IInStream wrapper used in stream read operations.
- /// </summary>
- internal sealed class InStreamWrapper : StreamWrapper, ISequentialInStream, IInStream
- {
- /// <summary>
- /// Initializes a new instance of the InStreamWrapper class.
- /// </summary>
- /// <param name="baseStream">Stream for writing data</param>
- /// <param name="disposeStream">Indicates whether to dispose the baseStream</param>
- public InStreamWrapper(Stream baseStream, bool disposeStream) : base(baseStream, disposeStream) { }
- #region ISequentialInStream Members
- /// <summary>
- /// Reads data from the stream.
- /// </summary>
- /// <param name="data">A data array.</param>
- /// <param name="size">The array size.</param>
- /// <returns>The read bytes count.</returns>
- public int Read(byte[] data, uint size)
- {
- int readCount = 0;
- if (BaseStream != null)
- {
- readCount = BaseStream.Read(data, 0, (int) size);
- if (readCount > 0)
- {
- OnBytesRead(new IntEventArgs(readCount));
- }
- }
- return readCount;
- }
- #endregion
- /// <summary>
- /// Occurs when IntEventArgs.Value bytes were read from the source.
- /// </summary>
- public event EventHandler<IntEventArgs> BytesRead;
- private void OnBytesRead(IntEventArgs e)
- {
- BytesRead?.Invoke(this, e);
- }
- }
- /// <summary>
- /// IOutStream wrapper used in stream write operations.
- /// </summary>
- internal sealed class OutStreamWrapper : StreamWrapper, ISequentialOutStream, IOutStream
- {
- /// <summary>
- /// Initializes a new instance of the OutStreamWrapper class
- /// </summary>
- /// <param name="baseStream">Stream for writing data</param>
- /// <param name="fileName">File name (for attributes fix)</param>
- /// <param name="time">Time of the file creation (for attributes fix)</param>
- /// <param name="disposeStream">Indicates whether to dispose the baseStream</param>
- public OutStreamWrapper(Stream baseStream, string fileName, DateTime time, bool disposeStream) :
- base(baseStream, fileName, time, disposeStream) {}
- /// <summary>
- /// Initializes a new instance of the OutStreamWrapper class
- /// </summary>
- /// <param name="baseStream">Stream for writing data</param>
- /// <param name="disposeStream">Indicates whether to dispose the baseStream</param>
- public OutStreamWrapper(Stream baseStream, bool disposeStream) :
- base(baseStream, disposeStream) {}
- #region IOutStream Members
- public int SetSize(long newSize)
- {
- BaseStream.SetLength(newSize);
- return 0;
- }
- #endregion
- #region ISequentialOutStream Members
- /// <summary>
- /// Writes data to the stream
- /// </summary>
- /// <param name="data">Data array</param>
- /// <param name="size">Array size</param>
- /// <param name="processedSize">Count of written bytes</param>
- /// <returns>Zero if Ok</returns>
- public int Write(byte[] data, uint size, IntPtr processedSize)
- {
- BaseStream.Write(data, 0, (int) size);
- if (processedSize != IntPtr.Zero)
- {
- Marshal.WriteInt32(processedSize, (int) size);
- }
- OnBytesWritten(new IntEventArgs((int) size));
- return 0;
- }
- #endregion
- /// <summary>
- /// Occurs when IntEventArgs.Value bytes were written.
- /// </summary>
- public event EventHandler<IntEventArgs> BytesWritten;
- private void OnBytesWritten(IntEventArgs e)
- {
- BytesWritten?.Invoke(this, e);
- }
- }
- /// <summary>
- /// Base multi volume stream wrapper class.
- /// </summary>
- internal class MultiStreamWrapper : DisposeVariableWrapper, IDisposable
- {
- protected readonly Dictionary<int, KeyValuePair<long, long>> StreamOffsets = new Dictionary<int, KeyValuePair<long, long>>();
- protected readonly List<Stream> Streams = new List<Stream>();
- protected int CurrentStream;
- protected long Position;
- protected long StreamLength;
- /// <summary>
- /// Initializes a new instance of the MultiStreamWrapper class.
- /// </summary>
- /// <param name="dispose">Perform Dispose() if requested to.</param>
- protected MultiStreamWrapper(bool dispose) : base(dispose) {}
- /// <summary>
- /// Gets the total length of input data.
- /// </summary>
- public long Length => StreamLength;
- #region IDisposable Members
- /// <summary>
- /// Cleans up any resources used and fixes file attributes.
- /// </summary>
- public virtual void Dispose()
- {
- if (DisposeStream)
- {
- foreach (Stream stream in Streams)
- {
- try
- {
- stream.Dispose();
- }
- catch (ObjectDisposedException) {}
- }
- Streams.Clear();
- }
- GC.SuppressFinalize(this);
- }
- #endregion
- protected static string VolumeNumber(int num)
- {
- string prefix;
- if (num < 10)
- {
- prefix = ".00";
- }
- else if (num < 100)
- {
- prefix = ".0";
- }
- else
- {
- prefix = ".";
- }
- return prefix + num.ToString(CultureInfo.InvariantCulture);
- }
- private int StreamNumberByOffset(long offset)
- {
- foreach (int number in StreamOffsets.Keys)
- {
- if (StreamOffsets[number].Key <= offset &&
- StreamOffsets[number].Value >= offset)
- {
- return number;
- }
- }
- return -1;
- }
- public void Seek(long offset, SeekOrigin seekOrigin, IntPtr newPosition)
- {
- long absolutePosition;
- switch (seekOrigin) {
- case SeekOrigin.Begin:
- absolutePosition = offset;
- break;
- case SeekOrigin.Current:
- absolutePosition = Position + offset;
- break;
- case SeekOrigin.End:
- absolutePosition = Length + offset;
- break;
- default:
- throw new ArgumentOutOfRangeException(nameof(seekOrigin));
- }
- CurrentStream = StreamNumberByOffset(absolutePosition);
- long delta = Streams[CurrentStream].Seek(
- absolutePosition - StreamOffsets[CurrentStream].Key, SeekOrigin.Begin);
- Position = StreamOffsets[CurrentStream].Key + delta;
- if (newPosition != IntPtr.Zero)
- {
- Marshal.WriteInt64(newPosition, Position);
- }
- }
- }
- /// <summary>
- /// IInStream wrapper used in stream multi volume read operations.
- /// </summary>
- internal sealed class InMultiStreamWrapper : MultiStreamWrapper, ISequentialInStream, IInStream
- {
- /// <summary>
- /// Initializes a new instance of the InMultiStreamWrapper class.
- /// </summary>
- /// <param name="fileName">The archive file name.</param>
- /// <param name="dispose">Perform Dispose() if requested to.</param>
- public InMultiStreamWrapper(string fileName, bool dispose) :
- base(dispose)
- {
- string baseName = fileName.Substring(0, fileName.Length - 4);
- int i = 0;
- while (File.Exists(fileName))
- {
- Streams.Add(new FileStream(fileName, FileMode.Open, FileAccess.Read));
- long length = Streams[i].Length;
- StreamOffsets.Add(i++, new KeyValuePair<long, long>(StreamLength, StreamLength + length));
- StreamLength += length;
- fileName = baseName + VolumeNumber(i + 1);
- }
- }
- #region ISequentialInStream Members
- /// <summary>
- /// Reads data from the stream.
- /// </summary>
- /// <param name="data">A data array.</param>
- /// <param name="size">The array size.</param>
- /// <returns>The read bytes count.</returns>
- public int Read(byte[] data, uint size)
- {
- var readSize = (int) size;
- int readCount = Streams[CurrentStream].Read(data, 0, readSize);
- readSize -= readCount;
- Position += readCount;
- while (readCount < (int) size)
- {
- if (CurrentStream == Streams.Count - 1)
- {
- return readCount;
- }
- CurrentStream++;
- Streams[CurrentStream].Seek(0, SeekOrigin.Begin);
- int count = Streams[CurrentStream].Read(data, readCount, readSize);
- readCount += count;
- readSize -= count;
- Position += count;
- }
- return readCount;
- }
- #endregion
- }
- /// <summary>
- /// IOutStream wrapper used in multi volume stream write operations.
- /// </summary>
- internal sealed class OutMultiStreamWrapper : MultiStreamWrapper, ISequentialOutStream, IOutStream
- {
- private readonly string _archiveName;
- private readonly long _volumeSize;
- private long _overallLength;
- /// <summary>
- /// Initializes a new instance of the OutMultiStreamWrapper class.
- /// </summary>
- /// <param name="archiveName">The archive name.</param>
- /// <param name="volumeSize">The volume size.</param>
- public OutMultiStreamWrapper(string archiveName, long volumeSize) :
- base(true)
- {
- _archiveName = archiveName;
- _volumeSize = volumeSize;
- CurrentStream = -1;
- NewVolumeStream();
- }
- #region IOutStream Members
- public int SetSize(long newSize)
- {
- return 0;
- }
- #endregion
- #region ISequentialOutStream Members
- public int Write(byte[] data, uint size, IntPtr processedSize)
- {
- int offset = 0;
- var originalSize = (int) size;
- Position += size;
- _overallLength = Math.Max(Position + 1, _overallLength);
- while (size > _volumeSize - Streams[CurrentStream].Position)
- {
- var count = (int) (_volumeSize - Streams[CurrentStream].Position);
- Streams[CurrentStream].Write(data, offset, count);
- size -= (uint) count;
- offset += count;
- NewVolumeStream();
- }
- Streams[CurrentStream].Write(data, offset, (int) size);
- if (processedSize != IntPtr.Zero)
- {
- Marshal.WriteInt32(processedSize, originalSize);
- }
- return 0;
- }
- #endregion
- public override void Dispose()
- {
- int lastIndex = Streams.Count - 1;
- Streams[lastIndex].SetLength(lastIndex > 0? Streams[lastIndex].Position : _overallLength);
- base.Dispose();
- }
- private void NewVolumeStream()
- {
- CurrentStream++;
- Streams.Add(File.Create(_archiveName + VolumeNumber(CurrentStream + 1)));
- Streams[CurrentStream].SetLength(_volumeSize);
- StreamOffsets.Add(CurrentStream, new KeyValuePair<long, long>(0, _volumeSize - 1));
- }
- }
- internal sealed class FakeOutStreamWrapper : ISequentialOutStream, IDisposable
- {
- #region IDisposable Members
- public void Dispose()
- {
- GC.SuppressFinalize(this);
- }
- #endregion
- #region ISequentialOutStream Members
- /// <summary>
- /// Does nothing except calling the BytesWritten event
- /// </summary>
- /// <param name="data">Data array</param>
- /// <param name="size">Array size</param>
- /// <param name="processedSize">Count of written bytes</param>
- /// <returns>Zero if Ok</returns>
- public int Write(byte[] data, uint size, IntPtr processedSize)
- {
- OnBytesWritten(new IntEventArgs((int) size));
- if (processedSize != IntPtr.Zero)
- {
- Marshal.WriteInt32(processedSize, (int) size);
- }
- return 0;
- }
- #endregion
- /// <summary>
- /// Occurs when IntEventArgs.Value bytes were written
- /// </summary>
- public event EventHandler<IntEventArgs> BytesWritten;
- private void OnBytesWritten(IntEventArgs e)
- {
- BytesWritten?.Invoke(this, e);
- }
- }
- #endif
- }
|