mirror of
https://github.com/TalAloni/SMBLibrary.git
synced 2025-05-22 04:09:21 +02:00
Improved read-ahead mechanism
This commit is contained in:
parent
ae8a3d77d3
commit
8c0ff8fdad
1 changed files with 102 additions and 51 deletions
|
@ -1,4 +1,4 @@
|
||||||
/* Copyright (C) 2016 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
|
/* Copyright (C) 2016-2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
|
||||||
*
|
*
|
||||||
* You can redistribute this program and/or modify it under the terms of
|
* You can redistribute this program and/or modify it under the terms of
|
||||||
* the GNU Lesser Public License as published by the Free Software Foundation,
|
* the GNU Lesser Public License as published by the Free Software Foundation,
|
||||||
|
@ -14,81 +14,114 @@ namespace Utilities
|
||||||
{
|
{
|
||||||
public class PrefetchedStream : Stream
|
public class PrefetchedStream : Stream
|
||||||
{
|
{
|
||||||
public const int CacheSize = 1048576; // 1 MB
|
public const int CacheSize = 524288; // 512 KB
|
||||||
|
public const int ReadAheadThershold = 65536; // 64 KB
|
||||||
|
|
||||||
private long m_cacheOffset;
|
private long m_cacheOffset;
|
||||||
private byte[] m_cache = new byte[0];
|
private byte[] m_cache = new byte[0];
|
||||||
|
|
||||||
private Stream m_stream;
|
private Stream m_stream;
|
||||||
private object m_syncLock = new object();
|
|
||||||
|
|
||||||
public PrefetchedStream(Stream stream)
|
public PrefetchedStream(Stream stream)
|
||||||
{
|
{
|
||||||
m_stream = stream;
|
m_stream = stream;
|
||||||
if (m_stream.CanRead)
|
if (m_stream.CanRead)
|
||||||
{
|
{
|
||||||
new Thread(delegate()
|
ScheduleReadAhead();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ScheduleReadAhead()
|
||||||
|
{
|
||||||
|
new Thread(delegate()
|
||||||
|
{
|
||||||
|
ReadAhead();
|
||||||
|
}).Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReadAhead()
|
||||||
|
{
|
||||||
|
lock (m_stream)
|
||||||
|
{
|
||||||
|
long position = this.Position;
|
||||||
|
bool isInCache = (position >= m_cacheOffset) && (position < m_cacheOffset + m_cache.Length);
|
||||||
|
int bytesAlreadyRead;
|
||||||
|
if (isInCache)
|
||||||
{
|
{
|
||||||
lock (m_syncLock)
|
int offsetInCache = (int)(position - m_cacheOffset);
|
||||||
{
|
bytesAlreadyRead = m_cache.Length - offsetInCache;
|
||||||
m_cacheOffset = 0;
|
byte[] oldCache = m_cache;
|
||||||
m_cache = new byte[CacheSize];
|
m_cache = new byte[CacheSize];
|
||||||
int bytesRead = m_stream.Read(m_cache, 0, CacheSize);
|
Array.Copy(oldCache, offsetInCache, m_cache, 0, bytesAlreadyRead);
|
||||||
System.Diagnostics.Debug.Print("[{0}] {1} bytes have been prefetched.", DateTime.Now.ToString("HH:mm:ss:ffff"), bytesRead);
|
this.Position = position + bytesAlreadyRead;
|
||||||
this.Position = 0;
|
}
|
||||||
if (bytesRead < CacheSize)
|
else
|
||||||
{
|
{
|
||||||
// EOF, we must trim the response data array
|
bytesAlreadyRead = 0;
|
||||||
m_cache = ByteReader.ReadBytes(m_cache, 0, bytesRead);
|
m_cache = new byte[CacheSize];
|
||||||
}
|
}
|
||||||
}
|
m_cacheOffset = position;
|
||||||
}).Start();
|
int bytesRead = m_stream.Read(m_cache, bytesAlreadyRead, CacheSize - bytesAlreadyRead);
|
||||||
|
System.Diagnostics.Debug.Print("[{0}] {1} bytes have been read ahead from offset {2}.", DateTime.Now.ToString("HH:mm:ss:ffff"), bytesRead, position);
|
||||||
|
if (bytesAlreadyRead + bytesRead < CacheSize)
|
||||||
|
{
|
||||||
|
// EOF, we must trim the response data array
|
||||||
|
m_cache = ByteReader.ReadBytes(m_cache, 0, bytesAlreadyRead + bytesRead);
|
||||||
|
}
|
||||||
|
this.Position = position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int Read(byte[] buffer, int offset, int count)
|
public override int Read(byte[] buffer, int offset, int count)
|
||||||
{
|
{
|
||||||
long position;
|
int bytesCopied;
|
||||||
lock (m_syncLock)
|
lock (m_stream)
|
||||||
{
|
{
|
||||||
position = this.Position;
|
long position = this.Position;
|
||||||
bool isInCache = (position >= m_cacheOffset) && (position + count <= m_cacheOffset + m_cache.Length);
|
bool isInCache = (position >= m_cacheOffset) && (position < m_cacheOffset + m_cache.Length);
|
||||||
if (!isInCache)
|
if (isInCache)
|
||||||
{
|
{
|
||||||
m_cacheOffset = position;
|
int offsetInCache = (int)(position - m_cacheOffset);
|
||||||
int cacheSize = Math.Max(CacheSize, count);
|
int bytesAvailableInCache = m_cache.Length - offsetInCache;
|
||||||
m_cache = new byte[cacheSize];
|
bytesCopied = Math.Min(count, bytesAvailableInCache);
|
||||||
int bytesRead = m_stream.Read(m_cache, 0, cacheSize);
|
Array.Copy(m_cache, offsetInCache, buffer, offset, bytesCopied);
|
||||||
|
this.Position = position + bytesCopied;
|
||||||
|
|
||||||
if (bytesRead < cacheSize)
|
if (bytesCopied < count)
|
||||||
{
|
{
|
||||||
// EOF, we must trim the response data array
|
int bytesMissing = count - bytesCopied;
|
||||||
m_cache = ByteReader.ReadBytes(m_cache, 0, bytesRead);
|
int bytesRead = m_stream.Read(buffer, offset + bytesCopied, bytesMissing);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offsetInCache + ReadAheadThershold >= m_cache.Length)
|
||||||
|
{
|
||||||
|
ScheduleReadAhead();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bytesCopied = m_stream.Read(buffer, 0, count);
|
||||||
|
ScheduleReadAhead();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return bytesCopied;
|
||||||
int offsetInCache = (int)(position - m_cacheOffset);
|
|
||||||
int bytesRemained = m_cache.Length - offsetInCache;
|
|
||||||
int dataLength = Math.Min(count, bytesRemained);
|
|
||||||
|
|
||||||
Array.Copy(m_cache, offsetInCache, buffer, offset, dataLength);
|
|
||||||
lock (m_syncLock)
|
|
||||||
{
|
|
||||||
this.Position = position + dataLength;
|
|
||||||
}
|
|
||||||
return dataLength;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Write(byte[] buffer, int offset, int count)
|
public override void Write(byte[] buffer, int offset, int count)
|
||||||
{
|
{
|
||||||
m_cache = new byte[0];
|
lock (m_stream)
|
||||||
m_stream.Write(buffer, offset, count);
|
{
|
||||||
|
m_cache = new byte[0];
|
||||||
|
m_stream.Write(buffer, offset, count);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Close()
|
public override void Close()
|
||||||
{
|
{
|
||||||
m_stream.Close();
|
lock (m_stream)
|
||||||
|
{
|
||||||
|
m_stream.Close();
|
||||||
|
}
|
||||||
base.Close();
|
base.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,7 +153,10 @@ namespace Utilities
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return m_stream.Length;
|
lock (m_stream)
|
||||||
|
{
|
||||||
|
return m_stream.Length;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,27 +164,42 @@ namespace Utilities
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return m_stream.Position;
|
lock (m_stream)
|
||||||
|
{
|
||||||
|
return m_stream.Position;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
m_stream.Position = value;
|
lock (m_stream)
|
||||||
|
{
|
||||||
|
m_stream.Position = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Flush()
|
public override void Flush()
|
||||||
{
|
{
|
||||||
m_stream.Flush();
|
lock (m_stream)
|
||||||
|
{
|
||||||
|
m_stream.Flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override long Seek(long offset, SeekOrigin origin)
|
public override long Seek(long offset, SeekOrigin origin)
|
||||||
{
|
{
|
||||||
return m_stream.Seek(offset, origin);
|
lock (m_stream)
|
||||||
|
{
|
||||||
|
return m_stream.Seek(offset, origin);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void SetLength(long value)
|
public override void SetLength(long value)
|
||||||
{
|
{
|
||||||
m_stream.SetLength(value);
|
lock (m_stream)
|
||||||
|
{
|
||||||
|
m_stream.SetLength(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue