зеркало из https://github.com/microsoft/AMBROSIA.git
437 строки
15 KiB
C#
437 строки
15 KiB
C#
using Ambrosia;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Windows.Storage;
|
|
using Windows.Storage.FileProperties;
|
|
using Windows.Storage.Streams;
|
|
|
|
namespace Ambrosia
|
|
{
|
|
// I wrote this version of LogWriter using the documentation here:
|
|
// https://docs.microsoft.com/en-us/windows/uwp/files/quickstart-reading-and-writing-files
|
|
//
|
|
// TODO: figure out proper way to handle synchronous LogWriter methods when underlying UWP
|
|
// class only provides an async implementation
|
|
//
|
|
// TODO: figure out proper way to handle async LogWriter methods when underlying UWP class only
|
|
// provides a synchronous implementation
|
|
internal class LogWriterUWP : IDisposable, ILogWriter
|
|
{
|
|
StorageFile _file;
|
|
IRandomAccessStream _stream;
|
|
IOutputStream _outputStream;
|
|
DataWriter _dataWriter;
|
|
|
|
private ulong _fileSize = 0;
|
|
|
|
public LogWriterUWP(string fileName,
|
|
uint chunkSize,
|
|
uint maxChunksPerWrite,
|
|
bool appendOpen = false)
|
|
{
|
|
InitializeAsync(fileName, appendOpen).Wait();
|
|
}
|
|
|
|
public async Task InitializeAsync(string fileName, bool appendOpen = false)
|
|
{
|
|
DirectoryInfo pathInfo = new DirectoryInfo(fileName);
|
|
string parentPath = pathInfo.Parent.FullName;
|
|
StorageFolder folder = await StorageFolder.GetFolderFromPathAsync(parentPath);
|
|
_file = await folder.CreateFileAsync(pathInfo.Name, CreationCollisionOption.OpenIfExists);
|
|
|
|
_stream = await _file.OpenAsync(FileAccessMode.ReadWrite);
|
|
ulong position = 0;
|
|
if (appendOpen)
|
|
{
|
|
BasicProperties properties = await _file.GetBasicPropertiesAsync();
|
|
position = properties.Size;
|
|
}
|
|
_outputStream = _stream.GetOutputStreamAt(position);
|
|
_dataWriter = new DataWriter(_outputStream);
|
|
}
|
|
|
|
public ulong FileSize { get { return _fileSize; } }
|
|
|
|
public void Dispose()
|
|
{
|
|
_dataWriter.Dispose();
|
|
_outputStream.Dispose();
|
|
_stream.Dispose();
|
|
}
|
|
public void Flush()
|
|
{
|
|
_dataWriter.StoreAsync().AsTask().Wait();
|
|
_outputStream.FlushAsync().AsTask().Wait();
|
|
}
|
|
|
|
public async Task FlushAsync()
|
|
{
|
|
await _dataWriter.StoreAsync();
|
|
await _outputStream.FlushAsync();
|
|
}
|
|
|
|
public void WriteByte(byte value)
|
|
{
|
|
_fileSize++;
|
|
_dataWriter.WriteByte(value);
|
|
}
|
|
|
|
// These three methods are all copied from the .NET Framework version of LogWriter
|
|
public unsafe void WriteInt(int value)
|
|
{
|
|
var zigZagEncoded = unchecked((uint)((value << 1) ^ (value >> 31)));
|
|
while ((zigZagEncoded & ~0x7F) != 0)
|
|
{
|
|
WriteByte((byte)((zigZagEncoded | 0x80) & 0xFF));
|
|
zigZagEncoded >>= 7;
|
|
}
|
|
WriteByte((byte)zigZagEncoded);
|
|
}
|
|
public void WriteIntFixed(int value)
|
|
{
|
|
WriteByte((byte)(value & 0xFF));
|
|
WriteByte((byte)((value >> 0x8) & 0xFF));
|
|
WriteByte((byte)((value >> 0x10) & 0xFF));
|
|
WriteByte((byte)((value >> 0x18) & 0xFF));
|
|
}
|
|
|
|
public void WriteLongFixed(long value)
|
|
{
|
|
WriteByte((byte)(value & 0xFF));
|
|
WriteByte((byte)((value >> 0x8) & 0xFF));
|
|
WriteByte((byte)((value >> 0x10) & 0xFF));
|
|
WriteByte((byte)((value >> 0x18) & 0xFF));
|
|
WriteByte((byte)((value >> 0x20) & 0xFF));
|
|
WriteByte((byte)((value >> 0x28) & 0xFF));
|
|
WriteByte((byte)((value >> 0x30) & 0xFF));
|
|
WriteByte((byte)((value >> 0x38) & 0xFF));
|
|
}
|
|
|
|
public void Write(byte[] buffer,
|
|
int offset,
|
|
int length)
|
|
{
|
|
_fileSize += (ulong)length;
|
|
|
|
// Hopefully there is a more performant way to do this
|
|
byte[] subBuffer = new byte[length];
|
|
Array.Copy(buffer, offset, subBuffer, 0, length);
|
|
_dataWriter.WriteBytes(subBuffer);
|
|
}
|
|
|
|
// Copied from Write() implementation above
|
|
public async Task WriteAsync(byte[] buffer,
|
|
int offset,
|
|
int length)
|
|
{
|
|
_fileSize += (ulong)length;
|
|
|
|
// Hopefully there is a more performant way to do this
|
|
byte[] subBuffer = new byte[length];
|
|
Array.Copy(buffer, offset, subBuffer, 0, length);
|
|
_dataWriter.WriteBytes(subBuffer);
|
|
}
|
|
}
|
|
|
|
internal class LogWriterStaticsUWP : ILogWriterStatic
|
|
{
|
|
public void CreateDirectoryIfNotExists(string path)
|
|
{
|
|
DirectoryInfo pathInfo = new DirectoryInfo(path);
|
|
string parentPath = pathInfo.Parent.FullName;
|
|
StorageFolder folder = StorageFolder.GetFolderFromPathAsync(parentPath).AsTask().Result;
|
|
folder.CreateFolderAsync(pathInfo.Name, CreationCollisionOption.OpenIfExists).AsTask().Wait();
|
|
}
|
|
|
|
public bool DirectoryExists(string path)
|
|
{
|
|
DirectoryInfo pathInfo = new DirectoryInfo(path);
|
|
string parentPath = pathInfo.Parent.FullName;
|
|
StorageFolder parentFolder = StorageFolder.GetFolderFromPathAsync(parentPath).AsTask().Result;
|
|
bool result;
|
|
try
|
|
{
|
|
StorageFolder queriedFolder = parentFolder.GetFolderAsync(pathInfo.Name).AsTask().Result;
|
|
result = true;
|
|
}
|
|
catch (System.AggregateException)
|
|
{
|
|
result = false;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public bool FileExists(string path)
|
|
{
|
|
FileInfo pathInfo = new FileInfo(path);
|
|
string parentPath = pathInfo.Directory.FullName;
|
|
StorageFolder parentFolder = StorageFolder.GetFolderFromPathAsync(parentPath).AsTask().Result;
|
|
bool result;
|
|
try
|
|
{
|
|
StorageFile queriedFile = parentFolder.GetFileAsync(pathInfo.Name).AsTask().Result;
|
|
result = true;
|
|
}
|
|
catch (System.AggregateException)
|
|
{
|
|
result = false;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public void DeleteFile(string path)
|
|
{
|
|
StorageFile file = StorageFile.GetFileFromPathAsync(path).AsTask().Result;
|
|
file.DeleteAsync().AsTask().Wait();
|
|
}
|
|
|
|
public ILogWriter Generate(string fileName,
|
|
uint chunkSize,
|
|
uint maxChunksPerWrite,
|
|
bool appendOpen = false)
|
|
{
|
|
return new LogWriterUWP(fileName, chunkSize, maxChunksPerWrite, appendOpen);
|
|
}
|
|
}
|
|
|
|
// TODO: Figure out if there is a better way to implement Read().
|
|
//
|
|
// TODO: Figure out if there's a way to avoid having so much duplicated code
|
|
// from the various versions of StreamCommunicator.
|
|
//
|
|
// TODO: Figure out proper way to handle async LogReader methods when the
|
|
// underlying UWP class only provides a synchronous implementation.
|
|
//
|
|
// TODO: Figure out if waiting on a Task in the Position setter will cause
|
|
// problems.
|
|
public class UWPLogReader : ILogReader
|
|
{
|
|
StorageFile _file;
|
|
IRandomAccessStream _stream;
|
|
IInputStream _inputStream;
|
|
DataReader _dataReader;
|
|
|
|
public long Position
|
|
{
|
|
get { return (long)_stream.Position; }
|
|
|
|
set
|
|
{
|
|
_dataReader.Dispose();
|
|
_inputStream.Dispose();
|
|
_inputStream = _stream.GetInputStreamAt((ulong)value);
|
|
_dataReader = new DataReader(_inputStream);
|
|
_dataReader.LoadAsync((uint)_stream.Size).AsTask().Wait();
|
|
}
|
|
}
|
|
|
|
public UWPLogReader(string fileName)
|
|
{
|
|
InitializeAsync(fileName).Wait();
|
|
}
|
|
|
|
public async Task InitializeAsync(string fileName)
|
|
{
|
|
_file = await StorageFile.GetFileFromPathAsync(fileName);
|
|
_stream = await _file.OpenAsync(FileAccessMode.Read);
|
|
_inputStream = _stream.GetInputStreamAt(0);
|
|
_dataReader = new DataReader(_inputStream);
|
|
await _dataReader.LoadAsync((uint)_stream.Size);
|
|
}
|
|
|
|
public async Task<Tuple<int, int>> ReadIntAsync(byte[] buffer,
|
|
CancellationToken ct)
|
|
{
|
|
return ReadInt(buffer);
|
|
}
|
|
|
|
public async Task<Tuple<int, int>> ReadIntAsync(byte[] buffer)
|
|
{
|
|
return ReadInt(buffer);
|
|
}
|
|
|
|
// Copied from StreamCommunicator
|
|
public Tuple<int, int> ReadInt(byte[] buffer)
|
|
{
|
|
buffer[0] = (byte)ReadByte();
|
|
var currentByte = (uint)buffer[0];
|
|
byte read = 1;
|
|
uint result = currentByte & 0x7FU;
|
|
int shift = 7;
|
|
while ((currentByte & 0x80) != 0)
|
|
{
|
|
buffer[read] = (byte)ReadByte();
|
|
currentByte = (uint)buffer[read];
|
|
read++;
|
|
result |= (currentByte & 0x7FU) << shift;
|
|
shift += 7;
|
|
if (read > 5)
|
|
{
|
|
throw new Exception("Invalid integer value in the input stream.");
|
|
}
|
|
}
|
|
return new Tuple<int, int>((int)((-(result & 1)) ^ ((result >> 1) & 0x7FFFFFFFU)), read);
|
|
}
|
|
|
|
// Copied from StreamCommunicator
|
|
public int ReadInt()
|
|
{
|
|
var currentByte = (uint)ReadByte();
|
|
byte read = 1;
|
|
uint result = currentByte & 0x7FU;
|
|
int shift = 7;
|
|
while ((currentByte & 0x80) != 0)
|
|
{
|
|
currentByte = (uint)ReadByte();
|
|
read++;
|
|
result |= (currentByte & 0x7FU) << shift;
|
|
shift += 7;
|
|
if (read > 5)
|
|
{
|
|
throw new Exception("Invalid integer value in the input stream.");
|
|
}
|
|
}
|
|
return (int)((-(result & 1)) ^ ((result >> 1) & 0x7FFFFFFFU));
|
|
}
|
|
|
|
public async Task<int> ReadAllRequiredBytesAsync(byte[] buffer,
|
|
int offset,
|
|
int count,
|
|
CancellationToken ct)
|
|
{
|
|
return ReadAllRequiredBytes(buffer, offset, count);
|
|
}
|
|
|
|
public async Task<int> ReadAllRequiredBytesAsync(byte[] buffer,
|
|
int offset,
|
|
int count)
|
|
{
|
|
return ReadAllRequiredBytes(buffer, offset, count);
|
|
}
|
|
|
|
// Copied from StreamCommunicator
|
|
public int ReadAllRequiredBytes(byte[] buffer,
|
|
int offset,
|
|
int count)
|
|
{
|
|
int toRead = count;
|
|
int currentOffset = offset;
|
|
int currentRead;
|
|
do
|
|
{
|
|
currentRead = Read(buffer, currentOffset, toRead);
|
|
currentOffset += currentRead;
|
|
toRead -= currentRead;
|
|
}
|
|
while (toRead > 0 && currentRead != 0);
|
|
return currentOffset - offset;
|
|
}
|
|
|
|
// Copied from StreamCommunicator
|
|
public long ReadLongFixed()
|
|
{
|
|
var value = new byte[8];
|
|
ReadAllRequiredBytes(value, 0, value.Length);
|
|
long intValue = value[0]
|
|
| (long)value[1] << 0x8
|
|
| (long)value[2] << 0x10
|
|
| (long)value[3] << 0x18
|
|
| (long)value[4] << 0x20
|
|
| (long)value[5] << 0x28
|
|
| (long)value[6] << 0x30
|
|
| (long)value[7] << 0x38;
|
|
return intValue;
|
|
}
|
|
|
|
// Copied from StreamCommunicator
|
|
public int ReadIntFixed()
|
|
{
|
|
var value = new byte[4];
|
|
ReadAllRequiredBytes(value, 0, value.Length);
|
|
int intValue = value[0]
|
|
| (int)value[1] << 0x8
|
|
| (int)value[2] << 0x10
|
|
| (int)value[3] << 0x18;
|
|
return intValue;
|
|
}
|
|
|
|
// Copied from CRA version of StreamCommunicator
|
|
public byte[] ReadByteArray()
|
|
{
|
|
int arraySize = ReadInt32();
|
|
var array = new byte[arraySize];
|
|
if (arraySize > 0)
|
|
{
|
|
ReadAllRequiredBytes(array, 0, array.Length);
|
|
}
|
|
return array;
|
|
}
|
|
|
|
// Copied from CRA version of StreamCommunicator
|
|
private int ReadInt32()
|
|
{
|
|
var currentByte = (uint)ReadByte();
|
|
byte read = 1;
|
|
uint result = currentByte & 0x7FU;
|
|
int shift = 7;
|
|
while ((currentByte & 0x80) != 0)
|
|
{
|
|
currentByte = (uint)ReadByte();
|
|
read++;
|
|
result |= (currentByte & 0x7FU) << shift;
|
|
shift += 7;
|
|
if (read > 5)
|
|
{
|
|
throw new InvalidOperationException("Invalid integer value in the input stream.");
|
|
}
|
|
}
|
|
return (int)((-(result & 1)) ^ ((result >> 1) & 0x7FFFFFFFU));
|
|
}
|
|
|
|
public int ReadByte()
|
|
{
|
|
return _dataReader.ReadByte();
|
|
}
|
|
|
|
public int Read(byte[] buffer, int offset, int count)
|
|
{
|
|
int bytesRead = 0;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
buffer[offset + i] = _dataReader.ReadByte();
|
|
bytesRead++;
|
|
}
|
|
return bytesRead;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_dataReader.Dispose();
|
|
_inputStream.Dispose();
|
|
_stream.Dispose();
|
|
}
|
|
}
|
|
|
|
internal class UWPLogReaderStatics : ILogReaderStatic
|
|
{
|
|
public ILogReader Generate(string fileName)
|
|
{
|
|
return new UWPLogReader(fileName);
|
|
}
|
|
}
|
|
|
|
public static class UWPLogsInterface
|
|
{
|
|
public static void SetToUWPLogs()
|
|
{
|
|
LogReaderStaticPicker.curStatic = new UWPLogReaderStatics();
|
|
LogWriterStaticPicker.curStatic = new LogWriterStaticsUWP();
|
|
}
|
|
}
|
|
}
|