mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-25 17:24:17 -05:00
Removing the FileStreamWrapper as it is unecessary (#156)
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -122,64 +122,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
|
||||
return fileName;
|
||||
});
|
||||
mock.Setup(fsf => fsf.GetReader(It.IsAny<string>()))
|
||||
.Returns<string>(output => new ServiceBufferFileStreamReader(new InMemoryWrapper(storage[output]), output));
|
||||
.Returns<string>(output => new ServiceBufferFileStreamReader(new MemoryStream(storage[output])));
|
||||
mock.Setup(fsf => fsf.GetWriter(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>()))
|
||||
.Returns<string, int, int>((output, chars, xml) => new ServiceBufferFileStreamWriter(
|
||||
new InMemoryWrapper(storage[output]), output, chars, xml));
|
||||
new MemoryStream(storage[output]), chars, xml));
|
||||
|
||||
return mock.Object;
|
||||
}
|
||||
|
||||
public class InMemoryWrapper : IFileStreamWrapper
|
||||
{
|
||||
private readonly MemoryStream memoryStream;
|
||||
private bool readingOnly;
|
||||
|
||||
public InMemoryWrapper(byte[] storage)
|
||||
{
|
||||
memoryStream = new MemoryStream(storage);
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
memoryStream.Dispose();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// We'll dispose this via a special method
|
||||
}
|
||||
|
||||
public void Flush()
|
||||
{
|
||||
if (readingOnly) { throw new InvalidOperationException(); }
|
||||
}
|
||||
|
||||
public void Init(string fileName, int bufferSize, FileAccess fAccess)
|
||||
{
|
||||
readingOnly = fAccess == FileAccess.Read;
|
||||
}
|
||||
|
||||
public int ReadData(byte[] buffer, int bytes)
|
||||
{
|
||||
return ReadData(buffer, bytes, memoryStream.Position);
|
||||
}
|
||||
|
||||
public int ReadData(byte[] buffer, int bytes, long fileOffset)
|
||||
{
|
||||
memoryStream.Seek(fileOffset, SeekOrigin.Begin);
|
||||
return memoryStream.Read(buffer, 0, bytes);
|
||||
}
|
||||
|
||||
public int WriteData(byte[] buffer, int bytes)
|
||||
{
|
||||
if (readingOnly) { throw new InvalidOperationException(); }
|
||||
memoryStream.Write(buffer, 0, bytes);
|
||||
memoryStream.Flush();
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DbConnection Mocking
|
||||
|
||||
@@ -1,218 +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;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.DataStorage
|
||||
{
|
||||
public class FileStreamWrapperTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
[InlineData(" ")]
|
||||
public void InitInvalidFilenameParameter(string fileName)
|
||||
{
|
||||
// If:
|
||||
// ... I have a file stream wrapper that is initialized with invalid fileName
|
||||
// Then:
|
||||
// ... It should throw an argument null exception
|
||||
using (FileStreamWrapper fsw = new FileStreamWrapper())
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => fsw.Init(fileName, 8192, FileAccess.Read));
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0)]
|
||||
[InlineData(-1)]
|
||||
public void InitInvalidBufferLength(int bufferLength)
|
||||
{
|
||||
// If:
|
||||
// ... I have a file stream wrapper that is initialized with an invalid buffer length
|
||||
// Then:
|
||||
// ... I should throw an argument out of range exception
|
||||
using (FileStreamWrapper fsw = new FileStreamWrapper())
|
||||
{
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => fsw.Init("validFileName", bufferLength, FileAccess.Read));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InitInvalidFileAccessMode()
|
||||
{
|
||||
// If:
|
||||
// ... I attempt to open a file stream wrapper that is initialized with an invalid file
|
||||
// access mode
|
||||
// Then:
|
||||
// ... I should get an invalid argument exception
|
||||
using (FileStreamWrapper fsw = new FileStreamWrapper())
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => fsw.Init("validFileName", 8192, FileAccess.Write));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InitSuccessful()
|
||||
{
|
||||
string fileName = Path.GetTempFileName();
|
||||
|
||||
try
|
||||
{
|
||||
using (FileStreamWrapper fsw = new FileStreamWrapper())
|
||||
{
|
||||
// If:
|
||||
// ... I have a file stream wrapper that is initialized with valid parameters
|
||||
fsw.Init(fileName, 8192, FileAccess.ReadWrite);
|
||||
|
||||
// Then:
|
||||
// ... The file should exist
|
||||
FileInfo fileInfo = new FileInfo(fileName);
|
||||
Assert.True(fileInfo.Exists);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Cleanup:
|
||||
// ... Delete the file that was created
|
||||
try { File.Delete(fileName); } catch { /* Don't care */ }
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PerformOpWithoutInit()
|
||||
{
|
||||
byte[] buf = new byte[10];
|
||||
|
||||
using (FileStreamWrapper fsw = new FileStreamWrapper())
|
||||
{
|
||||
// If:
|
||||
// ... I have a file stream wrapper that hasn't been initialized
|
||||
// Then:
|
||||
// ... Attempting to perform any operation will result in an exception
|
||||
Assert.Throws<InvalidOperationException>(() => fsw.ReadData(buf, 1));
|
||||
Assert.Throws<InvalidOperationException>(() => fsw.ReadData(buf, 1, 0));
|
||||
Assert.Throws<InvalidOperationException>(() => fsw.WriteData(buf, 1));
|
||||
Assert.Throws<InvalidOperationException>(() => fsw.Flush());
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PerformWriteOpOnReadOnlyWrapper()
|
||||
{
|
||||
byte[] buf = new byte[10];
|
||||
|
||||
using (FileStreamWrapper fsw = new FileStreamWrapper())
|
||||
{
|
||||
// If:
|
||||
// ... I have a readonly file stream wrapper
|
||||
// Then:
|
||||
// ... Attempting to perform any write operation should result in an exception
|
||||
Assert.Throws<InvalidOperationException>(() => fsw.WriteData(buf, 1));
|
||||
Assert.Throws<InvalidOperationException>(() => fsw.Flush());
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(1024, 20, 10)] // Standard scenario
|
||||
[InlineData(1024, 100, 100)] // Requested more bytes than there are
|
||||
[InlineData(5, 20, 10)] // Internal buffer too small, force a move-to operation
|
||||
public void ReadData(int internalBufferLength, int outBufferLength, int requestedBytes)
|
||||
{
|
||||
// Setup:
|
||||
// ... I have a file that has a handful of bytes in it
|
||||
string fileName = Path.GetTempFileName();
|
||||
const string stringToWrite = "hello";
|
||||
CreateTestFile(fileName, stringToWrite);
|
||||
byte[] targetBytes = Encoding.Unicode.GetBytes(stringToWrite);
|
||||
|
||||
try
|
||||
{
|
||||
// If:
|
||||
// ... I have a file stream wrapper that has been initialized to an existing file
|
||||
// ... And I read some bytes from it
|
||||
int bytesRead;
|
||||
byte[] buf = new byte[outBufferLength];
|
||||
using (FileStreamWrapper fsw = new FileStreamWrapper())
|
||||
{
|
||||
fsw.Init(fileName, internalBufferLength, FileAccess.Read);
|
||||
bytesRead = fsw.ReadData(buf, targetBytes.Length);
|
||||
}
|
||||
|
||||
// Then:
|
||||
// ... I should get those bytes back
|
||||
Assert.Equal(targetBytes.Length, bytesRead);
|
||||
Assert.True(targetBytes.Take(targetBytes.Length).SequenceEqual(buf.Take(targetBytes.Length)));
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Cleanup:
|
||||
// ... Delete the test file
|
||||
CleanupTestFile(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(1024)] // Standard scenario
|
||||
[InlineData(10)] // Internal buffer too small, forces a flush
|
||||
public void WriteData(int internalBufferLength)
|
||||
{
|
||||
string fileName = Path.GetTempFileName();
|
||||
byte[] bytesToWrite = Encoding.Unicode.GetBytes("hello");
|
||||
|
||||
try
|
||||
{
|
||||
// If:
|
||||
// ... I have a file stream that has been initialized
|
||||
// ... And I write some bytes to it
|
||||
using (FileStreamWrapper fsw = new FileStreamWrapper())
|
||||
{
|
||||
fsw.Init(fileName, internalBufferLength, FileAccess.ReadWrite);
|
||||
int bytesWritten = fsw.WriteData(bytesToWrite, bytesToWrite.Length);
|
||||
|
||||
Assert.Equal(bytesToWrite.Length, bytesWritten);
|
||||
}
|
||||
|
||||
// Then:
|
||||
// ... The file I wrote to should contain only the bytes I wrote out
|
||||
using (FileStream fs = File.OpenRead(fileName))
|
||||
{
|
||||
byte[] readBackBytes = new byte[1024];
|
||||
int bytesRead = fs.Read(readBackBytes, 0, readBackBytes.Length);
|
||||
|
||||
Assert.Equal(bytesToWrite.Length, bytesRead); // If bytes read is not equal, then more or less of the original string was written to the file
|
||||
Assert.True(bytesToWrite.SequenceEqual(readBackBytes.Take(bytesRead)));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Cleanup:
|
||||
// ... Delete the test file
|
||||
CleanupTestFile(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateTestFile(string fileName, string value)
|
||||
{
|
||||
using (FileStream fs = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
|
||||
{
|
||||
byte[] bytesToWrite = Encoding.Unicode.GetBytes(value);
|
||||
fs.Write(bytesToWrite, 0, bytesToWrite.Length);
|
||||
fs.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
private static void CleanupTestFile(string fileName)
|
||||
{
|
||||
try { File.Delete(fileName); } catch { /* Don't Care */}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,45 +6,100 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlTypes;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.DataStorage
|
||||
{
|
||||
public class ReaderWriterPairTest
|
||||
{
|
||||
[Fact]
|
||||
public void ReaderInvalidStreamCannotRead()
|
||||
{
|
||||
// If: I create a service buffer file stream reader with a stream that cannot be read
|
||||
// Then: I should get an exception
|
||||
var invalidStream = new Mock<Stream>();
|
||||
invalidStream.SetupGet(s => s.CanRead).Returns(false);
|
||||
invalidStream.SetupGet(s => s.CanSeek).Returns(true);
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
ServiceBufferFileStreamReader obj = new ServiceBufferFileStreamReader(invalidStream.Object);
|
||||
obj.Dispose();
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReaderInvalidStreamCannotSeek()
|
||||
{
|
||||
// If: I create a service buffer file stream reader with a stream that cannot seek
|
||||
// Then: I should get an exception
|
||||
var invalidStream = new Mock<Stream>();
|
||||
invalidStream.SetupGet(s => s.CanRead).Returns(true);
|
||||
invalidStream.SetupGet(s => s.CanSeek).Returns(false);
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
ServiceBufferFileStreamReader obj = new ServiceBufferFileStreamReader(invalidStream.Object);
|
||||
obj.Dispose();
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriterInvalidStreamCannotWrite()
|
||||
{
|
||||
// If: I create a service buffer file stream writer with a stream that cannot be read
|
||||
// Then: I should get an exception
|
||||
var invalidStream = new Mock<Stream>();
|
||||
invalidStream.SetupGet(s => s.CanWrite).Returns(false);
|
||||
invalidStream.SetupGet(s => s.CanSeek).Returns(true);
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
ServiceBufferFileStreamWriter obj = new ServiceBufferFileStreamWriter(invalidStream.Object, 1024, 1024);
|
||||
obj.Dispose();
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriterInvalidStreamCannotSeek()
|
||||
{
|
||||
// If: I create a service buffer file stream writer with a stream that cannot seek
|
||||
// Then: I should get an exception
|
||||
var invalidStream = new Mock<Stream>();
|
||||
invalidStream.SetupGet(s => s.CanWrite).Returns(true);
|
||||
invalidStream.SetupGet(s => s.CanSeek).Returns(false);
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
ServiceBufferFileStreamWriter obj = new ServiceBufferFileStreamWriter(invalidStream.Object, 1024, 1024);
|
||||
obj.Dispose();
|
||||
});
|
||||
}
|
||||
|
||||
private static void VerifyReadWrite<T>(int valueLength, T value, Func<ServiceBufferFileStreamWriter, T, int> writeFunc, Func<ServiceBufferFileStreamReader, FileStreamReadResult> readFunc)
|
||||
{
|
||||
// Setup: Create a mock file stream wrapper
|
||||
Common.InMemoryWrapper mockWrapper = new Common.InMemoryWrapper(new byte[8192]);
|
||||
try
|
||||
// Setup: Create a mock file stream
|
||||
byte[] storage = new byte[8192];
|
||||
|
||||
// If:
|
||||
// ... I write a type T to the writer
|
||||
using (ServiceBufferFileStreamWriter writer = new ServiceBufferFileStreamWriter(new MemoryStream(storage), 10, 10))
|
||||
{
|
||||
// If:
|
||||
// ... I write a type T to the writer
|
||||
using (ServiceBufferFileStreamWriter writer = new ServiceBufferFileStreamWriter(mockWrapper, "abc", 10, 10))
|
||||
{
|
||||
int writtenBytes = writeFunc(writer, value);
|
||||
Assert.Equal(valueLength, writtenBytes);
|
||||
}
|
||||
|
||||
// ... And read the type T back
|
||||
FileStreamReadResult outValue;
|
||||
using (ServiceBufferFileStreamReader reader = new ServiceBufferFileStreamReader(mockWrapper, "abc"))
|
||||
{
|
||||
outValue = readFunc(reader);
|
||||
}
|
||||
|
||||
// Then:
|
||||
Assert.Equal(value, outValue.Value.RawObject);
|
||||
Assert.Equal(valueLength, outValue.TotalLength);
|
||||
Assert.NotNull(outValue.Value);
|
||||
int writtenBytes = writeFunc(writer, value);
|
||||
Assert.Equal(valueLength, writtenBytes);
|
||||
}
|
||||
finally
|
||||
|
||||
// ... And read the type T back
|
||||
FileStreamReadResult outValue;
|
||||
using (ServiceBufferFileStreamReader reader = new ServiceBufferFileStreamReader(new MemoryStream(storage)))
|
||||
{
|
||||
// Cleanup: Close the wrapper
|
||||
mockWrapper.Close();
|
||||
outValue = readFunc(reader);
|
||||
}
|
||||
|
||||
// Then:
|
||||
Assert.Equal(value, outValue.Value.RawObject);
|
||||
Assert.Equal(valueLength, outValue.TotalLength);
|
||||
Assert.NotNull(outValue.Value);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -222,16 +277,17 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.DataStorage
|
||||
[Fact]
|
||||
public void StringNullTest()
|
||||
{
|
||||
// Setup: Create a mock file stream wrapper
|
||||
Common.InMemoryWrapper mockWrapper = new Common.InMemoryWrapper(new byte[8192]);
|
||||
|
||||
// If:
|
||||
// ... I write null as a string to the writer
|
||||
using (ServiceBufferFileStreamWriter writer = new ServiceBufferFileStreamWriter(mockWrapper, "abc", 10, 10))
|
||||
// Setup: Create a mock file stream
|
||||
using (MemoryStream stream = new MemoryStream(new byte[8192]))
|
||||
{
|
||||
// Then:
|
||||
// ... I should get an argument null exception
|
||||
Assert.Throws<ArgumentNullException>(() => writer.WriteString(null));
|
||||
// If:
|
||||
// ... I write null as a string to the writer
|
||||
using (ServiceBufferFileStreamWriter writer = new ServiceBufferFileStreamWriter(stream, 10, 10))
|
||||
{
|
||||
// Then:
|
||||
// ... I should get an argument null exception
|
||||
Assert.Throws<ArgumentNullException>(() => writer.WriteString(null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,15 +315,16 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.DataStorage
|
||||
public void BytesNullTest()
|
||||
{
|
||||
// Setup: Create a mock file stream wrapper
|
||||
Common.InMemoryWrapper mockWrapper = new Common.InMemoryWrapper(new byte[8192]);
|
||||
|
||||
// If:
|
||||
// ... I write null as a string to the writer
|
||||
using (ServiceBufferFileStreamWriter writer = new ServiceBufferFileStreamWriter(mockWrapper, "abc", 10, 10))
|
||||
using (MemoryStream stream = new MemoryStream(new byte[8192]))
|
||||
{
|
||||
// Then:
|
||||
// ... I should get an argument null exception
|
||||
Assert.Throws<ArgumentNullException>(() => writer.WriteBytes(null));
|
||||
// If:
|
||||
// ... I write null as a string to the writer
|
||||
using (ServiceBufferFileStreamWriter writer = new ServiceBufferFileStreamWriter(stream, 10, 10))
|
||||
{
|
||||
// Then:
|
||||
// ... I should get an argument null exception
|
||||
Assert.Throws<ArgumentNullException>(() => writer.WriteBytes(null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user