diff --git a/src/Jhu.SharpFitsIO/SpillMemoryStream.cs b/src/Jhu.SharpFitsIO/SpillMemoryStream.cs
index 3702e50..0af0342 100644
--- a/src/Jhu.SharpFitsIO/SpillMemoryStream.cs
+++ b/src/Jhu.SharpFitsIO/SpillMemoryStream.cs
@@ -10,51 +10,70 @@ namespace Jhu.SharpFitsIO
/// Implements a write-only memory stream that spills onto the
/// disk when a given size is reached.
///
- internal class SpillMemoryStream : Stream
+ ///
+ /// This class only implements the write functions of a standard
+ /// stream. When all data is written to the stream, it can be
+ /// written to another using the WriteTo function.
+ ///
+ internal class SpillMemoryStream : Stream, IDisposable
{
#region Private member varibles
private long position;
private long spillLimit;
private string spillPath;
- private MemoryStream memory;
- private FileStream spill;
- #endregion
+ ///
+ /// Internal memory buffer to store data temporarily until the
+ /// spill limit is reached.
+ ///
+ private MemoryStream memoryBuffer;
- #region Constructors and initializers
+ ///
+ /// External file buffer to store data temporarily after the
+ /// spill limit is reached.
+ ///
+ private FileStream spillBuffer;
- public SpillMemoryStream()
- {
- InitializeMembers();
- }
+ #endregion
+ #region Properties
- public SpillMemoryStream(long spillLimit)
+ ///
+ /// Gets the current position of the stream.
+ ///
+ public override long Position
{
- InitializeMembers();
-
- this.spillLimit = spillLimit;
+ get { return position; }
+ set { throw new InvalidOperationException(); }
}
- public SpillMemoryStream(long spillLimit, string spillPath)
+ ///
+ /// Gets or sets the size limit at which data is spilled to the disk.
+ ///
+ public long SpillLimit
{
- InitializeMembers();
-
- this.spillLimit = spillLimit;
- this.spillPath = spillPath;
+ get { return spillLimit; }
+ set
+ {
+ EnsureNotOpen();
+ spillLimit = value;
+ }
}
- private void InitializeMembers()
+ ///
+ /// Gets or sets the path of temporary file used when data is spilled
+ /// to the disk.
+ ///
+ public string SpillPath
{
- this.position = 0;
- this.spillLimit = long.MaxValue; // 1MB
- this.spillPath = null;
- this.memory = new MemoryStream();
- this.spill = null;
+ get { return spillPath; }
+ set
+ {
+ EnsureNotOpen();
+ spillPath = value;
+ }
}
- #endregion
-
public override bool CanRead
{
get { return false; }
@@ -80,51 +99,85 @@ public override long Length
get { return position; }
}
- public override long Position
+ #endregion
+ #region Constructors and initializers
+
+ public SpillMemoryStream()
{
- get { return position; }
- set { throw new InvalidOperationException(); }
+ InitializeMembers();
}
- public override void Close()
+ public SpillMemoryStream(long spillLimit)
{
- if (spill != null)
- {
- spill.Close();
- }
+ InitializeMembers();
- if (memory != null)
- {
- memory.Close();
- }
+ this.spillLimit = spillLimit;
+ }
+
+ public SpillMemoryStream(long spillLimit, string spillPath)
+ {
+ InitializeMembers();
+
+ this.spillLimit = spillLimit;
+ this.spillPath = spillPath;
+ }
+
+ private void InitializeMembers()
+ {
+ this.position = 0;
+ this.spillLimit = 0x100000; // 1MB
+ this.spillPath = null;
+ this.memoryBuffer = null;
+ this.spillBuffer = null;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
- if (spill != null)
+ if (spillBuffer != null)
+ {
+ spillBuffer.Dispose();
+ }
+
+ if (memoryBuffer != null)
{
- spill.Dispose();
+ memoryBuffer.Dispose();
}
- if (memory != null)
+ if (spillPath != null && File.Exists(spillPath))
{
- memory.Dispose();
+ File.Delete(spillPath);
}
}
+ }
- if (spillPath != null && File.Exists(spillPath))
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ #endregion
+ #region Stream implementation
+
+ public override void Close()
+ {
+ if (spillBuffer != null)
+ {
+ spillBuffer.Close();
+ }
+
+ if (memoryBuffer != null)
{
- File.Delete(spillPath);
+ memoryBuffer.Close();
}
}
public override void Flush()
{
- if (spill != null)
+ if (spillBuffer != null)
{
- spill.Flush();
+ spillBuffer.Flush();
}
}
@@ -150,15 +203,15 @@ public override void SetLength(long value)
public override void Write(byte[] buffer, int offset, int count)
{
- if (spill == null && position + count < spillLimit)
+ if (position + count < spillLimit)
{
- memory.Write(buffer, offset, count);
+ OpenMemoryBuffer();
+ memoryBuffer.Write(buffer, offset, count);
}
else
{
- OpenSpillFile();
-
- spill.Write(buffer, offset, count);
+ OpenSpillBuffer();
+ spillBuffer.Write(buffer, offset, count);
}
position += count;
@@ -166,53 +219,69 @@ public override void Write(byte[] buffer, int offset, int count)
public override void WriteByte(byte value)
{
- if (spill == null && position + 1 < spillLimit)
+ if (position + 1 < spillLimit)
{
- memory.WriteByte(value);
+ OpenMemoryBuffer();
+ memoryBuffer.WriteByte(value);
}
else
{
- OpenSpillFile();
-
- spill.WriteByte(value);
+ OpenSpillBuffer();
+ spillBuffer.WriteByte(value);
}
+
+ position++;
}
+ #endregion
+
+ ///
+ /// Writes the content of both buffers to an output stream.
+ ///
+ ///
public void WriteTo(Stream stream)
{
// TODO: this function could use async copy but that just
// overcomplicates things
// Flush memory to stream
- if (memory != null)
+ if (memoryBuffer != null)
{
- memory.WriteTo(stream);
+ memoryBuffer.WriteTo(stream);
}
- if (spill != null)
+ if (spillBuffer != null)
{
// Rewind file but remember position
- var pos = spill.Position;
- spill.Seek(0, SeekOrigin.Begin);
+ var pos = spillBuffer.Position;
+ spillBuffer.Seek(0, SeekOrigin.Begin);
// Copy file to output stream
var i = 0;
var buffer = new byte[0x10000]; // 64k
while (i < pos)
{
- var count = spill.Read(buffer, 0, buffer.Length);
+ var count = spillBuffer.Read(buffer, 0, buffer.Length);
stream.Write(buffer, 0, count);
i += count;
}
- spill.Seek(pos, SeekOrigin.Begin);
+ spillBuffer.Seek(pos, SeekOrigin.Begin);
}
}
- private void OpenSpillFile()
+ private void OpenMemoryBuffer()
{
- if (spill != null)
+ if (memoryBuffer == null && spillLimit > 0)
+ {
+ memoryBuffer = new MemoryStream();
+ }
+ }
+
+ private void OpenSpillBuffer()
+ {
+ if (spillBuffer == null)
{
// If path is not set use temp
if (spillPath == null)
@@ -220,7 +289,15 @@ private void OpenSpillFile()
spillPath = Path.GetTempFileName();
}
- spill = new FileStream(spillPath, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None);
+ spillBuffer = new FileStream(spillPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
+ }
+ }
+
+ private void EnsureNotOpen()
+ {
+ if (memoryBuffer != null || spillBuffer != null)
+ {
+ throw new InvalidOperationException("Stream is already open."); // TODO ***
}
}
}
diff --git a/test/Jhu.SharpFitsIO.Test/Jhu.SharpFitsIO.Test.csproj b/test/Jhu.SharpFitsIO.Test/Jhu.SharpFitsIO.Test.csproj
index fbd17ae..72cdb5c 100644
--- a/test/Jhu.SharpFitsIO.Test/Jhu.SharpFitsIO.Test.csproj
+++ b/test/Jhu.SharpFitsIO.Test/Jhu.SharpFitsIO.Test.csproj
@@ -67,6 +67,7 @@
+
diff --git a/test/Jhu.SharpFitsIO.Test/SpillMemoryStreamTest.cs b/test/Jhu.SharpFitsIO.Test/SpillMemoryStreamTest.cs
new file mode 100644
index 0000000..757adc3
--- /dev/null
+++ b/test/Jhu.SharpFitsIO.Test/SpillMemoryStreamTest.cs
@@ -0,0 +1,46 @@
+using System;
+using System.IO;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Jhu.SharpFitsIO
+{
+ [TestClass]
+ public class SpillMemoryStreamTest
+ {
+ [TestMethod]
+ public void MemoryOnlyTest()
+ {
+ var buffer = new byte[0x10000]; // 64k
+
+ using (var sms = new SpillMemoryStream())
+ {
+ sms.Write(buffer, 0, buffer.Length);
+
+ var ms = new MemoryStream();
+ sms.WriteTo(ms);
+
+ Assert.AreEqual(0x10000, ms.Position);
+ }
+ }
+
+ [TestMethod]
+ public void SpillToTempTest()
+ {
+ var buffer = new byte[0x10000]; // 64k
+
+ using (var sms = new SpillMemoryStream())
+ {
+ // Write 2M
+ for (int i = 0; i < 32; i++)
+ {
+ sms.Write(buffer, 0, buffer.Length);
+ }
+
+ var ms = new MemoryStream();
+ sms.WriteTo(ms);
+
+ Assert.AreEqual(0x200000, ms.Position);
+ }
+ }
+ }
+}