From 1f45de7d357fdeacdb42dc222d6a46088b6b32cf Mon Sep 17 00:00:00 2001 From: morkt Date: Sun, 2 Apr 2017 14:41:25 +0400 Subject: [PATCH] (ArcViewStream): restored bufferization. apparently performance is improved compared to unbuffered mapped file view reads. --- GameRes/ArcStream.cs | 186 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 151 insertions(+), 35 deletions(-) diff --git a/GameRes/ArcStream.cs b/GameRes/ArcStream.cs index 0f0686bb..47c9b35e 100644 --- a/GameRes/ArcStream.cs +++ b/GameRes/ArcStream.cs @@ -28,6 +28,7 @@ using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Text; +using GameRes.Utility; namespace GameRes { @@ -37,6 +38,11 @@ namespace GameRes private readonly long m_start; private readonly long m_size; private long m_position; + private byte[] m_buffer; + private int m_buffer_pos; // read position within buffer + private int m_buffer_len; // length of bytes read in buffer + + private const int DefaultBufferSize = 0x1000; public string Name { get; set; } public uint Signature { get { return ReadSignature(); } } @@ -48,11 +54,20 @@ namespace GameRes public override long Length { get { return m_size; } } public override long Position { - get { return m_position; } + get { return m_position + (m_buffer_pos - m_buffer_len); } set { if (value < 0) throw new ArgumentOutOfRangeException ("value", "Stream position is out of range."); - m_position = value; + var buffer_start = m_position - m_buffer_len; + if (m_buffer_pos != m_buffer_len && value >= buffer_start && value < m_position) + { + m_buffer_pos = (int)(value - buffer_start); + } + else + { + m_position = value; + m_buffer_pos = m_buffer_len = 0; + } } } @@ -117,24 +132,61 @@ namespace GameRes return new CowArray (m_header, 0, size); } + private void RefillBuffer () + { + if (null == m_buffer) + m_buffer = new byte[DefaultBufferSize]; + uint length = (uint)Math.Min (m_size - m_position, m_buffer.Length); + m_buffer_len = m_view.Read (m_start + m_position, m_buffer, 0, length); + m_position += m_buffer_len; + m_buffer_pos = 0; + } + + private void FlushBuffer () + { + if (m_buffer_len != 0) + { + m_position += m_buffer_pos - m_buffer_len; + m_buffer_pos = m_buffer_len = 0; + } + } + private void EnsureAvailable (int length) { - if (m_position + length > m_size) - throw new EndOfStreamException(); + if (m_buffer_pos + length > m_buffer_len) + { + FlushBuffer(); + if (m_position + length > m_size) + throw new EndOfStreamException(); + RefillBuffer(); + } + } + + private int ReadFromBuffer (byte[] array, int offset, int count) + { + int available = Math.Min (m_buffer_len - m_buffer_pos, count); + if (available > 0) + { + Buffer.BlockCopy (m_buffer, m_buffer_pos, array, offset, available); + m_buffer_pos += available; + } + return available; } public int PeekByte () { - if (m_position >= m_size) + if (m_buffer_pos == m_buffer_len) + RefillBuffer(); + if (m_buffer_pos == m_buffer_len) return -1; - return m_view.ReadByte (m_start+m_position); + return m_buffer[m_buffer_pos]; } public override int ReadByte () { int b = PeekByte(); if (-1 != b) - ++m_position; + ++m_buffer_pos; return b; } @@ -154,8 +206,8 @@ namespace GameRes public short ReadInt16 () { EnsureAvailable (2); - var v = m_view.ReadInt16 (m_start+m_position); - m_position += 2; + var v = m_buffer.ToInt16 (m_buffer_pos); + m_buffer_pos += 2; return v; } @@ -167,17 +219,16 @@ namespace GameRes public int ReadInt24 () { EnsureAvailable (3); - int v = m_view.ReadUInt16 (m_start+m_position); - v |= m_view.ReadByte (m_start+m_position+2) << 16; - m_position += 3; + int v = m_buffer.ToInt24 (m_buffer_pos); + m_buffer_pos += 3; return v; } public int ReadInt32 () { EnsureAvailable (4); - var v = m_view.ReadInt32 (m_start+m_position); - m_position += 4; + int v = m_buffer.ToInt32 (m_buffer_pos); + m_buffer_pos += 4; return v; } @@ -189,8 +240,8 @@ namespace GameRes public long ReadInt64 () { EnsureAvailable (8); - var v = m_view.ReadInt64 (m_start+m_position); - m_position += 8; + var v = m_buffer.ToInt64 (m_buffer_pos); + m_buffer_pos += 8; return v; } @@ -206,10 +257,37 @@ namespace GameRes public string ReadCString (int length, Encoding enc) { - uint string_length = (uint)Math.Min (length, m_size-m_position); - var str = m_view.ReadString (m_position, (uint)string_length, enc); - m_position += string_length; - return str; + if (m_buffer_pos == m_buffer_len && length <= DefaultBufferSize) + RefillBuffer(); + if (m_buffer_pos + length <= m_buffer_len) + { + // whole string fit into buffer + var str = Binary.GetCString (m_buffer, m_buffer_pos, length, enc); + m_buffer_pos += length; + return str; + } + else if (length > DefaultBufferSize) + { + // requested string length is larger than internal buffer size + var string_buffer = ReadBytes (length); + return Binary.GetCString (string_buffer, 0, string_buffer.Length, enc); + } + else + { + int available = m_buffer_len - m_buffer_pos; + if (available > 0 && m_buffer_pos != 0) + Buffer.BlockCopy (m_buffer, m_buffer_pos, m_buffer, 0, available); + int count = (int)Math.Min (m_buffer.Length - available, m_size - m_position); + if (count > 0) + { + int read = m_view.Read (m_start + m_position, m_buffer, available, (uint)count); + m_position += read; + available += read; + } + m_buffer_len = available; + m_buffer_pos = Math.Min (length, m_buffer_len); + return Binary.GetCString (m_buffer, 0, m_buffer_pos, enc); + } } public string ReadCString () @@ -219,12 +297,27 @@ namespace GameRes public string ReadCString (Encoding enc) { - // underlying view includes rest of the stream - if (m_view.Offset <= m_position && m_view.Offset + m_view.Reserved >= m_start + m_size) - return ReadCStringUnsafe (enc); + if (m_buffer_pos == m_buffer_len) + RefillBuffer(); + int available = m_buffer_len - m_buffer_pos; + if (0 == available) + return string.Empty; - var string_buf = new byte[0x20]; - int size = 0; + int zero = Array.IndexOf (m_buffer, 0, m_buffer_pos, available); + if (zero != -1) + { + // null byte found within buffer + var str = enc.GetString (m_buffer, m_buffer_pos, zero - m_buffer_pos); + m_buffer_pos = zero+1; + return str; + } + // underlying view includes whole stream + if (m_view.Offset <= m_start && m_view.Offset + m_view.Reserved >= m_start + m_size) + return ReadCStringUnsafe (enc, available); + + var string_buf = new byte[Math.Max (0x20, available * 2)]; + ReadFromBuffer (string_buf, 0, available); + int size = available; for (;;) { int b = ReadByte(); @@ -242,6 +335,7 @@ namespace GameRes private unsafe string ReadCStringUnsafe (Encoding enc, int skip_bytes = 0) { Debug.Assert (m_view.Offset + m_view.Reserved >= m_start + m_size); + FlushBuffer(); using (var ptr = m_view.GetPointer()) { byte* s = ptr.Value + (m_start - m_view.Offset + m_position); @@ -260,27 +354,35 @@ namespace GameRes public byte[] ReadBytes (int count) { - if (0 == count || m_position >= m_size) + if (m_buffer_pos + count <= m_buffer_len && m_buffer_len != 0) + { + var data = new CowArray (m_buffer, m_buffer_pos, count).ToArray(); + m_buffer_pos += count; + return data; + } + var current_pos = Position; + if (0 == count || current_pos >= m_size) return new byte[0]; - var bytes = m_view.ReadBytes (m_start+m_position, (uint)Math.Min (count, m_size - m_position)); - m_position += bytes.Length; + var bytes = m_view.ReadBytes (m_start+current_pos, (uint)Math.Min (count, m_size - current_pos)); + Position = current_pos + bytes.Length; return bytes; } #region System.IO.Stream methods public override void Flush() { + FlushBuffer(); } public override long Seek (long offset, SeekOrigin origin) { switch (origin) { - case SeekOrigin.Current: offset += m_position; break; + case SeekOrigin.Current: offset += Position; break; case SeekOrigin.End: offset += m_size; break; } Position = offset; - return m_position; + return offset; } public override void SetLength (long length) @@ -290,12 +392,26 @@ namespace GameRes public override int Read (byte[] buffer, int offset, int count) { + int read_from_buffer = ReadFromBuffer (buffer, offset, count); + offset += read_from_buffer; + count -= read_from_buffer; if (0 == count || m_position >= m_size) - return 0; - count = (int)Math.Min (count, m_size - m_position); - int read = m_view.Read (m_start + m_position, buffer, offset, (uint)count); - m_position += read; - return read; + return read_from_buffer; + if (count < DefaultBufferSize) + { + RefillBuffer(); + count = Math.Min (count, m_buffer_len); + Buffer.BlockCopy (m_buffer, m_buffer_pos, buffer, offset, count); + m_buffer_pos += count; + return read_from_buffer + count; + } + else + { + uint view_count = (uint)Math.Min (count, m_size - m_position); + int read_from_view = m_view.Read (m_start + m_position, buffer, offset, view_count); + m_position += read_from_view; + return read_from_buffer + read_from_view; + } } public override void Write (byte[] buffer, int offset, int count)