Removing the FileStreamWrapper as it is unecessary (#156)

This commit is contained in:
Benjamin Russell
2016-11-22 17:21:41 -08:00
committed by GitHub
parent ba0f564126
commit 0841ad7cf4
9 changed files with 149 additions and 651 deletions

View File

@@ -1,273 +0,0 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Diagnostics;
using System.IO;
using Microsoft.SqlTools.ServiceLayer.Utility;
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
{
/// <summary>
/// Wrapper for a file stream, providing simplified creation, deletion, read, and write
/// functionality.
/// </summary>
public class FileStreamWrapper : IFileStreamWrapper
{
#region Member Variables
private byte[] buffer;
private int bufferDataSize;
private FileStream fileStream;
private long startOffset;
private long currentOffset;
#endregion
/// <summary>
/// Constructs a new FileStreamWrapper and initializes its state.
/// </summary>
public FileStreamWrapper()
{
// Initialize the internal state
bufferDataSize = 0;
startOffset = 0;
currentOffset = 0;
}
#region IFileStreamWrapper Implementation
/// <summary>
/// Initializes the wrapper by creating the internal buffer and opening the requested file.
/// If the file does not already exist, it will be created.
/// </summary>
/// <param name="fileName">Name of the file to open/create</param>
/// <param name="bufferLength">The length of the internal buffer</param>
/// <param name="accessMethod">
/// Whether or not the wrapper will be used for reading. If <c>true</c>, any calls to a
/// method that writes will cause an InvalidOperationException
/// </param>
public void Init(string fileName, int bufferLength, FileAccess accessMethod)
{
// Sanity check for valid buffer length, fileName, and accessMethod
Validate.IsGreaterThan(nameof(bufferLength), bufferLength, 0);
Validate.IsNotNullOrWhitespaceString(nameof(fileName), fileName);
if (accessMethod == FileAccess.Write)
{
throw new ArgumentException(SR.QueryServiceFileWrapperWriteOnly, nameof(fileName));
}
// Setup the buffer
buffer = new byte[bufferLength];
// Open the requested file for reading/writing, creating one if it doesn't exist
fileStream = new FileStream(fileName, FileMode.OpenOrCreate, accessMethod, FileShare.ReadWrite,
bufferLength, false /*don't use asyncio*/);
}
/// <summary>
/// Reads data into a buffer from the current offset into the file
/// </summary>
/// <param name="buf">The buffer to output the read data to</param>
/// <param name="bytes">The number of bytes to read into the buffer</param>
/// <returns>The number of bytes read</returns>
public int ReadData(byte[] buf, int bytes)
{
return ReadData(buf, bytes, currentOffset);
}
/// <summary>
/// Reads data into a buffer from the specified offset into the file
/// </summary>
/// <param name="buf">The buffer to output the read data to</param>
/// <param name="bytes">The number of bytes to read into the buffer</param>
/// <param name="offset">The offset into the file to start reading bytes from</param>
/// <returns>The number of bytes read</returns>
public int ReadData(byte[] buf, int bytes, long offset)
{
// Make sure that we're initialized before performing operations
if (buffer == null)
{
throw new InvalidOperationException(SR.QueryServiceFileWrapperNotInitialized);
}
MoveTo(offset);
int bytesCopied = 0;
while (bytesCopied < bytes)
{
int bufferOffset, bytesToCopy;
GetByteCounts(bytes, bytesCopied, out bufferOffset, out bytesToCopy);
Buffer.BlockCopy(buffer, bufferOffset, buf, bytesCopied, bytesToCopy);
bytesCopied += bytesToCopy;
if (bytesCopied < bytes && // did not get all the bytes yet
bufferDataSize == buffer.Length) // since current data buffer is full we should continue reading the file
{
// move forward one full length of the buffer
MoveTo(startOffset + buffer.Length);
}
else
{
// copied all the bytes requested or possible, adjust the current buffer pointer
currentOffset += bytesToCopy;
break;
}
}
return bytesCopied;
}
/// <summary>
/// Writes data to the underlying filestream, with buffering.
/// </summary>
/// <param name="buf">The buffer of bytes to write to the filestream</param>
/// <param name="bytes">The number of bytes to write</param>
/// <returns>The number of bytes written</returns>
public int WriteData(byte[] buf, int bytes)
{
// Make sure that we're initialized before performing operations
if (buffer == null)
{
throw new InvalidOperationException(SR.QueryServiceFileWrapperNotInitialized);
}
if (!fileStream.CanWrite)
{
throw new InvalidOperationException(SR.QueryServiceFileWrapperReadOnly);
}
int bytesCopied = 0;
while (bytesCopied < bytes)
{
int bufferOffset, bytesToCopy;
GetByteCounts(bytes, bytesCopied, out bufferOffset, out bytesToCopy);
Buffer.BlockCopy(buf, bytesCopied, buffer, bufferOffset, bytesToCopy);
bytesCopied += bytesToCopy;
// adjust the current buffer pointer
currentOffset += bytesToCopy;
if (bytesCopied < bytes) // did not get all the bytes yet
{
Debug.Assert((int)(currentOffset - startOffset) == buffer.Length);
// flush buffer
Flush();
}
}
Debug.Assert(bytesCopied == bytes);
return bytesCopied;
}
/// <summary>
/// Flushes the internal buffer to the filestream
/// </summary>
public void Flush()
{
// Make sure that we're initialized before performing operations
if (buffer == null)
{
throw new InvalidOperationException(SR.QueryServiceFileWrapperNotInitialized);
}
if (!fileStream.CanWrite)
{
throw new InvalidOperationException(SR.QueryServiceFileWrapperReadOnly);
}
// Make sure we are at the right place in the file
Debug.Assert(fileStream.Position == startOffset);
int bytesToWrite = (int)(currentOffset - startOffset);
fileStream.Write(buffer, 0, bytesToWrite);
startOffset += bytesToWrite;
fileStream.Flush();
Debug.Assert(startOffset == currentOffset);
}
/// <summary>
/// Deletes the given file (ideally, created with this wrapper) from the filesystem
/// </summary>
/// <param name="fileName">The path to the file to delete</param>
public static void DeleteFile(string fileName)
{
File.Delete(fileName);
}
#endregion
/// <summary>
/// Perform calculations to determine how many bytes to copy and what the new buffer offset
/// will be for copying.
/// </summary>
/// <param name="bytes">Number of bytes requested to copy</param>
/// <param name="bytesCopied">Number of bytes copied so far</param>
/// <param name="bufferOffset">New offset to start copying from/to</param>
/// <param name="bytesToCopy">Number of bytes to copy in this iteration</param>
private void GetByteCounts(int bytes, int bytesCopied, out int bufferOffset, out int bytesToCopy)
{
bufferOffset = (int) (currentOffset - startOffset);
bytesToCopy = bytes - bytesCopied;
if (bytesToCopy > buffer.Length - bufferOffset)
{
bytesToCopy = buffer.Length - bufferOffset;
}
}
/// <summary>
/// Moves the internal buffer to the specified offset into the file
/// </summary>
/// <param name="offset">Offset into the file to move to</param>
private void MoveTo(long offset)
{
if (buffer.Length > bufferDataSize || // buffer is not completely filled
offset < startOffset || // before current buffer start
offset >= (startOffset + buffer.Length)) // beyond current buffer end
{
// init the offset
startOffset = offset;
// position file pointer
fileStream.Seek(startOffset, SeekOrigin.Begin);
// fill in the buffer
bufferDataSize = fileStream.Read(buffer, 0, buffer.Length);
}
// make sure to record where we are
currentOffset = offset;
}
#region IDisposable Implementation
private bool disposed;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposed)
{
return;
}
if (disposing && fileStream != null)
{
if(fileStream.CanWrite) { Flush(); }
fileStream.Dispose();
}
disposed = true;
}
~FileStreamWrapper()
{
Dispose(false);
}
#endregion
}
}

View File

@@ -1,22 +0,0 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.IO;
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
{
/// <summary>
/// Interface for a wrapper around a filesystem reader/writer, mainly for unit testing purposes
/// </summary>
public interface IFileStreamWrapper : IDisposable
{
void Init(string fileName, int bufferSize, FileAccess fileAccessMode);
int ReadData(byte[] buffer, int bytes);
int ReadData(byte[] buffer, int bytes, long fileOffset);
int WriteData(byte[] buffer, int bytes);
void Flush();
}
}

View File

@@ -29,7 +29,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// <returns>A <see cref="ServiceBufferFileStreamReader"/></returns>
public IFileStreamReader GetReader(string fileName)
{
return new ServiceBufferFileStreamReader(new FileStreamWrapper(), fileName);
return new ServiceBufferFileStreamReader(new FileStream(fileName, FileMode.Open, FileAccess.Read));
}
/// <summary>
@@ -42,7 +42,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// <returns>A <see cref="ServiceBufferFileStreamWriter"/></returns>
public IFileStreamWriter GetWriter(string fileName, int maxCharsToStore, int maxXmlCharsToStore)
{
return new ServiceBufferFileStreamWriter(new FileStreamWrapper(), fileName, maxCharsToStore, maxXmlCharsToStore);
return new ServiceBufferFileStreamWriter(new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite), maxCharsToStore, maxXmlCharsToStore);
}
/// <summary>
@@ -51,14 +51,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// <param name="fileName">The file to dispose of</param>
public void DisposeFile(string fileName)
{
try
{
FileStreamWrapper.DeleteFile(fileName);
}
catch
{
// If we have problems deleting the file from a temp location, we don't really care
}
FileUtils.SafeFileDelete(fileName);
}
}
}

View File

@@ -23,22 +23,24 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
private byte[] buffer;
private readonly IFileStreamWrapper fileStream;
private readonly Stream fileStream;
private Dictionary<Type, Func<long, FileStreamReadResult>> readMethods;
private readonly Dictionary<Type, Func<long, FileStreamReadResult>> readMethods;
#endregion
/// <summary>
/// Constructs a new ServiceBufferFileStreamReader and initializes its state
/// </summary>
/// <param name="fileWrapper">The filestream wrapper to read from</param>
/// <param name="fileName">The name of the file to read from</param>
public ServiceBufferFileStreamReader(IFileStreamWrapper fileWrapper, string fileName)
/// <param name="stream">The filestream to read from</param>
public ServiceBufferFileStreamReader(Stream stream)
{
// Open file for reading/writing
fileStream = fileWrapper;
fileStream.Init(fileName, DefaultBufferSize, FileAccess.Read);
if (!stream.CanRead || !stream.CanSeek)
{
throw new InvalidOperationException("Stream must be readable and seekable");
}
fileStream = stream;
// Create internal buffer
buffer = new byte[DefaultBufferSize];
@@ -372,7 +374,8 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
{
// read in length information
int lengthValue;
int lengthLength = fileStream.ReadData(buffer, 1, offset);
fileStream.Seek(offset, SeekOrigin.Begin);
int lengthLength = fileStream.Read(buffer, 0, 1);
if (buffer[0] != 0xFF)
{
// one byte is enough
@@ -381,7 +384,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
else
{
// read in next 4 bytes
lengthLength += fileStream.ReadData(buffer, 4);
lengthLength += fileStream.Read(buffer, 0, 4);
// reconstruct the length
lengthValue = BitConverter.ToInt32(buffer, 0);
@@ -433,7 +436,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
else
{
AssureBufferLength(length.ValueLength);
fileStream.ReadData(buffer, length.ValueLength);
fileStream.Read(buffer, 0, length.ValueLength);
T resultObject = convertFunc(length.ValueLength);
result.RawObject = resultObject;
result.DisplayValue = toStringFunc == null ? result.RawObject.ToString() : toStringFunc(resultObject);

View File

@@ -23,7 +23,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
#region Member Variables
private readonly IFileStreamWrapper fileStream;
private readonly Stream fileStream;
private readonly int maxCharsToStore;
private readonly int maxXmlCharsToStore;
@@ -45,15 +45,17 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// <summary>
/// Constructs a new writer
/// </summary>
/// <param name="fileWrapper">The file wrapper to use as the underlying file stream</param>
/// <param name="fileName">Name of the file to write to</param>
/// <param name="stream">The file wrapper to use as the underlying file stream</param>
/// <param name="maxCharsToStore">Maximum number of characters to store for long text fields</param>
/// <param name="maxXmlCharsToStore">Maximum number of characters to store for XML fields</param>
public ServiceBufferFileStreamWriter(IFileStreamWrapper fileWrapper, string fileName, int maxCharsToStore, int maxXmlCharsToStore)
public ServiceBufferFileStreamWriter(Stream stream, int maxCharsToStore, int maxXmlCharsToStore)
{
// open file for reading/writing
fileStream = fileWrapper;
fileStream.Init(fileName, DefaultBufferLength, FileAccess.ReadWrite);
if (!stream.CanWrite || !stream.CanSeek)
{
throw new InvalidOperationException("Stream must be writable and seekable.");
}
fileStream = stream;
// create internal buffer
byteBuffer = new byte[DefaultBufferLength];
@@ -212,7 +214,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
public int WriteNull()
{
byteBuffer[0] = 0x00;
return fileStream.WriteData(byteBuffer, 1);
return WriteHelper(byteBuffer, 1);
}
/// <summary>
@@ -224,7 +226,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
byteBuffer[0] = 0x02; // length
shortBuffer[0] = val;
Buffer.BlockCopy(shortBuffer, 0, byteBuffer, 1, 2);
return fileStream.WriteData(byteBuffer, 3);
return WriteHelper(byteBuffer, 3);
}
/// <summary>
@@ -236,7 +238,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
byteBuffer[0] = 0x04; // length
intBuffer[0] = val;
Buffer.BlockCopy(intBuffer, 0, byteBuffer, 1, 4);
return fileStream.WriteData(byteBuffer, 5);
return WriteHelper(byteBuffer, 5);
}
/// <summary>
@@ -248,7 +250,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
byteBuffer[0] = 0x08; // length
longBuffer[0] = val;
Buffer.BlockCopy(longBuffer, 0, byteBuffer, 1, 8);
return fileStream.WriteData(byteBuffer, 9);
return WriteHelper(byteBuffer, 9);
}
/// <summary>
@@ -260,7 +262,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
byteBuffer[0] = 0x02; // length
charBuffer[0] = val;
Buffer.BlockCopy(charBuffer, 0, byteBuffer, 1, 2);
return fileStream.WriteData(byteBuffer, 3);
return WriteHelper(byteBuffer, 3);
}
/// <summary>
@@ -271,7 +273,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
{
byteBuffer[0] = 0x01; // length
byteBuffer[1] = (byte) (val ? 0x01 : 0x00);
return fileStream.WriteData(byteBuffer, 2);
return WriteHelper(byteBuffer, 2);
}
/// <summary>
@@ -282,7 +284,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
{
byteBuffer[0] = 0x01; // length
byteBuffer[1] = val;
return fileStream.WriteData(byteBuffer, 2);
return WriteHelper(byteBuffer, 2);
}
/// <summary>
@@ -294,7 +296,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
byteBuffer[0] = 0x04; // length
floatBuffer[0] = val;
Buffer.BlockCopy(floatBuffer, 0, byteBuffer, 1, 4);
return fileStream.WriteData(byteBuffer, 5);
return WriteHelper(byteBuffer, 5);
}
/// <summary>
@@ -306,7 +308,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
byteBuffer[0] = 0x08; // length
doubleBuffer[0] = val;
Buffer.BlockCopy(doubleBuffer, 0, byteBuffer, 1, 8);
return fileStream.WriteData(byteBuffer, 9);
return WriteHelper(byteBuffer, 9);
}
/// <summary>
@@ -330,7 +332,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
// data value
Buffer.BlockCopy(arrInt32, 0, byteBuffer, 3, iLen - 3);
iTotalLen += fileStream.WriteData(byteBuffer, iLen);
iTotalLen += WriteHelper(byteBuffer, iLen);
return iTotalLen; // len+data
}
@@ -346,7 +348,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
int iTotalLen = WriteLength(iLen); // length
Buffer.BlockCopy(arrInt32, 0, byteBuffer, 0, iLen);
iTotalLen += fileStream.WriteData(byteBuffer, iLen);
iTotalLen += WriteHelper(byteBuffer, iLen);
return iTotalLen; // len+data
}
@@ -374,7 +376,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
longBufferOffset[0] = dtoVal.Ticks;
longBufferOffset[1] = dtoVal.Offset.Ticks;
Buffer.BlockCopy(longBufferOffset, 0, byteBuffer, 1, 16);
return fileStream.WriteData(byteBuffer, 17);
return WriteHelper(byteBuffer, 17);
}
/// <summary>
@@ -406,7 +408,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
byteBuffer[3] = 0x00;
byteBuffer[4] = 0x00;
iTotalLen = fileStream.WriteData(byteBuffer, 5);
iTotalLen = WriteHelper(byteBuffer, 5);
}
else
{
@@ -415,7 +417,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
// convert char array into byte array and write it out
iTotalLen = WriteLength(bytes.Length);
iTotalLen += fileStream.WriteData(bytes, bytes.Length);
iTotalLen += WriteHelper(bytes, bytes.Length);
}
return iTotalLen; // len+data
}
@@ -438,12 +440,12 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
byteBuffer[3] = 0x00;
byteBuffer[4] = 0x00;
iTotalLen = fileStream.WriteData(byteBuffer, 5);
iTotalLen = WriteHelper(byteBuffer, 5);
}
else
{
iTotalLen = WriteLength(bytesVal.Length);
iTotalLen += fileStream.WriteData(bytesVal, bytesVal.Length);
iTotalLen += WriteHelper(bytesVal, bytesVal.Length);
}
return iTotalLen; // len+data
}
@@ -507,7 +509,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
int iTmp = iLen & 0x000000FF;
byteBuffer[0] = Convert.ToByte(iTmp);
return fileStream.WriteData(byteBuffer, 1);
return WriteHelper(byteBuffer, 1);
}
// The length won't fit in 1 byte, so we need to use 1 byte to signify that the length
// is a full 4 bytes.
@@ -516,7 +518,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
// convert int32 into array of bytes
intBuffer[0] = iLen;
Buffer.BlockCopy(intBuffer, 0, byteBuffer, 1, 4);
return fileStream.WriteData(byteBuffer, 5);
return WriteHelper(byteBuffer, 5);
}
/// <summary>
@@ -532,6 +534,12 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
return val.IsNull ? WriteNull() : valueWriteFunc(val);
}
private int WriteHelper(byte[] buffer, int length)
{
fileStream.Write(buffer, 0, length);
return length;
}
#endregion
#region IDisposable Implementation