/// <summary>
/// Opens a <see cref="LogReader"/> to read the content of a compressed or uncompressed stream.
/// The stream will be closed when <see cref="LogReader.Dispose"/> will be called.
/// </summary>
/// <param name="seekableStream">Stream that must support Seek operations (<see cref="Stream.CanSeek"/> must be true).</param>
/// <param name="dataOffset">
/// An optional offset where the stream position must be initially set: this is the position of an entry in the actual (potentially uncompressed stream),
/// not the offset in the original stream.
/// </param>
/// <param name="filter">An optional <see cref="MulticastFilter"/>.</param>
/// <returns>A <see cref="LogReader"/> that will close the file when disposed.</returns>
/// <remarks>
/// .ckmon files exist in different file versions, depending on headers.
/// The file can be compressed using GZipStream, in which case the header will be the magic GZIP header: 1F 8B.
/// New header (applies to version 5), the file will start with 43 4B 4D 4F 4E (CKMON in ASCII), followed by the version number, instead of only the version number.
/// </remarks>
public static LogReader Open(Stream seekableStream, long dataOffset = 0, MulticastFilter?filter = null)
{
Throw.CheckNotNullArgument(seekableStream);
Throw.CheckArgument(seekableStream.CanSeek);
LogReaderStreamInfo i = LogReaderStreamInfo.OpenStream(seekableStream);
var s = i.LogStream;
if (dataOffset > 0)
{
if (s.CanSeek)
{
s.Seek(dataOffset, SeekOrigin.Current);
}
else
{
var buffer = new byte[8192];
int toRead;
while ((toRead = (int)Math.Min(8192, dataOffset)) > 0 && s.Read(buffer, 0, toRead) == toRead)
{
dataOffset -= toRead;
}
}
}
var r = new LogReader(s, i.Version, i.HeaderLength)
{
CurrentFilter = filter
};
return(r);
}