This instance will be upgraded to Heptapod 0.27.1 on 2021-12-02 at 15:00 UTC+1 (a few minutes of down time)

Commit 59c20d32 authored by Jesse McGrew's avatar Jesse McGrew
Browse files

Refactor filesystem access to go through IFileSystem interface.

parent 13f99c3fa6f8
...@@ -25,11 +25,10 @@ using System.Text; ...@@ -25,11 +25,10 @@ using System.Text;
using Zilf.Common.StringEncoding; using Zilf.Common.StringEncoding;
using Zapf.Parsing.Diagnostics; using Zapf.Parsing.Diagnostics;
using Zapf.Parsing.Instructions; using Zapf.Parsing.Instructions;
using Zilf.Common;
namespace Zapf namespace Zapf
{ {
delegate Stream OpenFileDelegate(string filename, bool writing);
delegate bool FileExistsDelegate(string filename);
delegate IDebugFileWriter GetDebugWriterDelegate(Stream stream); delegate IDebugFileWriter GetDebugWriterDelegate(Stream stream);
class Context : IErrorSink, IDisposable class Context : IErrorSink, IDisposable
...@@ -76,12 +75,9 @@ namespace Zapf ...@@ -76,12 +75,9 @@ namespace Zapf
public int? TableStart, TableSize; public int? TableStart, TableSize;
public bool InVocab; public bool InVocab;
public OpenFileDelegate? InterceptOpenFile; public IFileSystem FileSystem { get; set; } = PhysicalFileSystem.Instance;
public FileExistsDelegate? InterceptFileExists;
#pragma warning disable CS0649
public GetDebugWriterDelegate? InterceptGetDebugWriter; public GetDebugWriterDelegate? InterceptGetDebugWriter;
#pragma warning restore CS0649
char? LanguageEscapeChar { get; set; } char? LanguageEscapeChar { get; set; }
...@@ -327,8 +323,10 @@ namespace Zapf ...@@ -327,8 +323,10 @@ namespace Zapf
if (InVocab) if (InVocab)
{ {
// restore stream // restore stream
var buffer = ((MemoryStream?)stream)!.GetBuffer(); if (!((MemoryStream)stream!).TryGetBuffer(out var bufferSegment))
var bufLen = (int)stream.Length; throw new InvalidOperationException("Can't access vocab buffer");
var buffer = bufferSegment.AsSpan();
stream = prevStream; stream = prevStream;
prevStream = null; prevStream = null;
...@@ -336,8 +334,8 @@ namespace Zapf ...@@ -336,8 +334,8 @@ namespace Zapf
// sort vocab words // sort vocab words
// we use an insertion sort because ZILF's vocab table is mostly sorted already // we use an insertion sort because ZILF's vocab table is mostly sorted already
int records = bufLen / vocabRecSize; int records = buffer.Length / vocabRecSize;
var temp = new byte[vocabRecSize]; Span<byte> temp = stackalloc byte[vocabRecSize];
var newIndexes = new int[records]; var newIndexes = new int[records];
newIndexes[0] = 0; newIndexes[0] = 0;
...@@ -346,15 +344,17 @@ namespace Zapf ...@@ -346,15 +344,17 @@ namespace Zapf
{ {
if (VocabCompare(buffer, i - 1, i) > 0) if (VocabCompare(buffer, i - 1, i) > 0)
{ {
VocabRecord(buffer, i).CopyTo(temp);
var home = VocabSearch(buffer, i - 1, i); var home = VocabSearch(buffer, i - 1, i);
Array.Copy(buffer, i * vocabRecSize, temp, 0, vocabRecSize); VocabMove(buffer, home, home + 1, i - home);
Array.Copy(buffer, home * vocabRecSize, buffer, (home + 1) * vocabRecSize,
(i - home) * vocabRecSize); temp.CopyTo(VocabRecord(buffer, home));
Array.Copy(temp, 0, buffer, home * vocabRecSize, vocabRecSize);
for (int j = 0; j < i; j++) for (int j = 0; j < i; j++)
if (newIndexes[j] >= home && newIndexes[j] < i) if (newIndexes[j] >= home && newIndexes[j] < i)
newIndexes[j]++; newIndexes[j]++;
newIndexes[i] = home; newIndexes[i] = home;
} }
else else
...@@ -388,7 +388,7 @@ namespace Zapf ...@@ -388,7 +388,7 @@ namespace Zapf
} }
} }
stream?.Write(buffer, 0, bufLen); stream?.Write(buffer);
// apply fixups // apply fixups
var vocabEnd = position; var vocabEnd = position;
...@@ -419,6 +419,20 @@ namespace Zapf ...@@ -419,6 +419,20 @@ namespace Zapf
vocabKeySize = 0; vocabKeySize = 0;
} }
void VocabMove(Span<byte> buffer, int srcIndex, int destIndex, int recordCount)
{
var byteCount = recordCount * vocabRecSize;
var src = buffer.Slice(srcIndex * vocabRecSize, byteCount);
var dest = buffer.Slice(destIndex * vocabRecSize, byteCount);
src.CopyTo(dest);
}
Span<byte> VocabRecord(Span<byte> buffer, int index) =>
buffer.Slice(index * vocabRecSize, vocabRecSize);
ReadOnlySpan<byte> VocabRecord(ReadOnlySpan<byte> buffer, int index) =>
buffer.Slice(index * vocabRecSize, vocabRecSize);
int MapVocabAddress(int oldAddress, int[] newIndexes) int MapVocabAddress(int oldAddress, int[] newIndexes)
{ {
var oldOffsetFromVocab = oldAddress - vocabStart; var oldOffsetFromVocab = oldAddress - vocabStart;
...@@ -428,7 +442,7 @@ namespace Zapf ...@@ -428,7 +442,7 @@ namespace Zapf
return vocabStart + (newIndex * vocabRecSize) + offsetWithinEntry; return vocabStart + (newIndex * vocabRecSize) + offsetWithinEntry;
} }
int VocabSearch(byte[] buffer, int numRecs, int keyRec) int VocabSearch(ReadOnlySpan<byte> buffer, int numRecs, int keyRec)
{ {
int start = 0, end = numRecs - 1; int start = 0, end = numRecs - 1;
while (start <= end) while (start <= end)
...@@ -446,20 +460,8 @@ namespace Zapf ...@@ -446,20 +460,8 @@ namespace Zapf
return start; return start;
} }
int VocabCompare(byte[] buffer, int idx1, int idx2) int VocabCompare(ReadOnlySpan<byte> buffer, int idx1, int idx2) =>
{ VocabRecord(buffer, idx1).SequenceCompareTo(VocabRecord(buffer, idx2));
idx1 *= vocabRecSize;
idx2 *= vocabRecSize;
for (int i = 0; i < vocabKeySize; i++)
{
int diff = buffer[idx1 + i] - buffer[idx2 + i];
if (diff != 0)
return diff;
}
return 0;
}
string? VocabLabel(int index) string? VocabLabel(int index)
{ {
...@@ -497,7 +499,7 @@ namespace Zapf ...@@ -497,7 +499,7 @@ namespace Zapf
OutFile = Path.ChangeExtension(OutFile, ".z" + ZVersion); OutFile = Path.ChangeExtension(OutFile, ".z" + ZVersion);
position = 0; position = 0;
stream = OpenFile(OutFile, true); stream = FileSystem.OpenForWriting(OutFile);
} }
public void CloseOutput() public void CloseOutput()
...@@ -510,7 +512,7 @@ namespace Zapf ...@@ -510,7 +512,7 @@ namespace Zapf
{ {
Debug.Assert(DebugFile != null); Debug.Assert(DebugFile != null);
var debugStream = OpenFile(DebugFile, true); var debugStream = FileSystem.OpenForWriting(DebugFile);
if (InterceptGetDebugWriter != null) if (InterceptGetDebugWriter != null)
{ {
...@@ -850,36 +852,17 @@ namespace Zapf ...@@ -850,36 +852,17 @@ namespace Zapf
Console.Error.WriteLine("fatal error: {0}", fer.Message); Console.Error.WriteLine("fatal error: {0}", fer.Message);
} }
public Stream OpenFile(string filename, bool writing)
{
var intercept = InterceptOpenFile;
if (intercept != null)
return intercept(filename, writing);
return new FileStream(
filename,
writing ? FileMode.Create : FileMode.Open,
writing ? FileAccess.ReadWrite : FileAccess.Read);
}
public bool FileExists(string filename)
{
var intercept = InterceptFileExists;
return intercept?.Invoke(filename) ?? File.Exists(filename);
}
[SuppressMessage("ReSharper", "ConvertIfStatementToReturnStatement")]
public string? FindInsertedFile(string name) public string? FindInsertedFile(string name)
{ {
if (FileExists(name)) if (FileSystem.Exists(name))
return name; return name;
string search = name + ".zap"; string search = name + ".zap";
if (FileExists(search)) if (FileSystem.Exists(search))
return search; return search;
search = name + ".xzap"; search = name + ".xzap";
if (FileExists(search)) if (FileSystem.Exists(search))
return search; return search;
return null; return null;
...@@ -973,7 +956,7 @@ namespace Zapf ...@@ -973,7 +956,7 @@ namespace Zapf
Object, Object,
} }
class Symbol sealed class Symbol
{ {
/// <summary> /// <summary>
/// The symbol's name in the source code. /// The symbol's name in the source code.
...@@ -1011,7 +994,7 @@ namespace Zapf ...@@ -1011,7 +994,7 @@ namespace Zapf
} }
} }
class Fixup sealed class Fixup
{ {
public Fixup(string symbol) => Symbol = symbol; public Fixup(string symbol) => Symbol = symbol;
......
...@@ -29,6 +29,7 @@ using Zapf.Parsing.Diagnostics; ...@@ -29,6 +29,7 @@ using Zapf.Parsing.Diagnostics;
using Zapf.Parsing.Directives; using Zapf.Parsing.Directives;
using Zapf.Parsing.Expressions; using Zapf.Parsing.Expressions;
using Zapf.Parsing.Instructions; using Zapf.Parsing.Instructions;
using System.Diagnostics.CodeAnalysis;
namespace Zapf namespace Zapf
{ {
...@@ -40,8 +41,7 @@ namespace Zapf ...@@ -40,8 +41,7 @@ namespace Zapf
public static int Main(string[] args) public static int Main(string[] args)
{ {
var ctx = ParseArgs(args); if (!TryParseArgs(args, out var ctx))
if (ctx == null)
{ {
Usage(); Usage();
return 1; return 1;
...@@ -231,82 +231,95 @@ namespace Zapf ...@@ -231,82 +231,95 @@ namespace Zapf
return result; return result;
} }
internal static Context? ParseArgs(IReadOnlyList<string> args) internal static bool TryParseArgs(IReadOnlyList<string> args, [NotNullWhen(true)] out Context? ctx)
{ {
using var result = new Context(); var result = new Context();
if (TryParseArgs(args, result))
{
ctx = result;
return true;
}
ctx = null;
return false;
}
internal static bool TryParseArgs(IReadOnlyList<string> args, Context ctx)
{
string? inFile = null, outFile = null;
for (int i = 0; i < args.Count; i++) for (int i = 0; i < args.Count; i++)
{ {
switch (args[i]) switch (args[i])
{ {
case "-ab": case "-ab":
result.AbbreviateMode = true; ctx.AbbreviateMode = true;
break; break;
case "-q": case "-q":
result.Quiet = true; ctx.Quiet = true;
break; break;
case "-i": case "-i":
result.InformMode = true; ctx.InformMode = true;
break; break;
case "-la": case "-la":
result.ListAddresses = true; ctx.ListAddresses = true;
break; break;
case "-r": case "-r":
if (++i == args.Count) if (++i == args.Count)
return null; return false;
result.Release = short.Parse(args[i]); ctx.Release = short.Parse(args[i]);
break; break;
case "-s": case "-s":
if (++i == args.Count) if (++i == args.Count)
return null; return false;
result.Serial = args[i]; ctx.Serial = args[i];
break; break;
case "-c": case "-c":
if (++i == args.Count) if (++i == args.Count)
return null; return false;
result.Creator = args[i]; ctx.Creator = args[i];
break; break;
case "-c0": case "-c0":
result.Creator = null; ctx.Creator = null;
break; break;
case "-dx": case "-dx":
result.XmlDebugMode = true; ctx.XmlDebugMode = true;
break; break;
case "-?": case "-?":
case "--help": case "--help":
case "/?": case "/?":
return null; return false;
default: default:
if (result.InFile == null) if (inFile == null)
result.InFile = args[i]; inFile = args[i];
else if (result.OutFile == null) else if (outFile == null)
result.OutFile = args[i]; outFile = args[i];
else else
return null; return false;
break; break;
} }
} }
// validate // validate
if (result.InFile == null) if (inFile == null)
return null; return false;
if (result.OutFile == null)
result.OutFile = Path.ChangeExtension(result.InFile, ".z#");
result.DebugFile = Path.ChangeExtension(result.OutFile, result.XmlDebugMode ? ".dbg.xml" : ".dbg"); ctx.InFile = inFile;
ctx.OutFile = outFile ?? Path.ChangeExtension(ctx.InFile, ".z#");
ctx.DebugFile = Path.ChangeExtension(ctx.OutFile, ctx.XmlDebugMode ? ".dbg.xml" : ".dbg");
return result; return true;
} }
static void Usage() static void Usage()
...@@ -806,7 +819,8 @@ General switches: ...@@ -806,7 +819,8 @@ General switches:
static IEnumerable<AsmLine> ReadRootsFromFile(Context ctx, string path) static IEnumerable<AsmLine> ReadRootsFromFile(Context ctx, string path)
{ {
using var stream = ctx.OpenFile(path, false); using var stream = ctx.FileSystem.OpenForReading(path);
Debug.Assert(ctx.OpcodeDict != null); Debug.Assert(ctx.OpcodeDict != null);
var parser = new ZapParser(ctx, ctx.OpcodeDict); var parser = new ZapParser(ctx, ctx.OpcodeDict);
var result = parser.Parse(stream, path); var result = parser.Parse(stream, path);
......
...@@ -21,9 +21,11 @@ using System.Diagnostics.CodeAnalysis; ...@@ -21,9 +21,11 @@ using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using Zapf.Parsing; using Zapf.Parsing;
using Zapf.Parsing.Diagnostics; using Zapf.Parsing.Diagnostics;
using Zilf.Common;
namespace Zapf namespace Zapf
{ {
[Obsolete("Use " + nameof(IFileSystem) + " instead.")]
class OpeningFileEventArgs : EventArgs class OpeningFileEventArgs : EventArgs
{ {
public OpeningFileEventArgs(string filename, bool writing) public OpeningFileEventArgs(string filename, bool writing)
...@@ -39,6 +41,7 @@ namespace Zapf ...@@ -39,6 +41,7 @@ namespace Zapf
public Stream? Stream { get; set; } public Stream? Stream { get; set; }
} }
[Obsolete("Use " + nameof(IFileSystem) + " instead.")]
class CheckingFilePresenceEventArgs : EventArgs class CheckingFilePresenceEventArgs : EventArgs
{ {
public CheckingFilePresenceEventArgs(string filename) => FileName = filename; public CheckingFilePresenceEventArgs(string filename) => FileName = filename;
...@@ -52,7 +55,7 @@ namespace Zapf ...@@ -52,7 +55,7 @@ namespace Zapf
{ {
public InitializingContextEventArgs(Context ctx) => Context = ctx; public InitializingContextEventArgs(Context ctx) => Context = ctx;
public Context Context { get; set; } public Context Context { get; }
} }
readonly struct AssemblyResult readonly struct AssemblyResult
...@@ -72,44 +75,9 @@ namespace Zapf ...@@ -72,44 +75,9 @@ namespace Zapf
sealed class ZapfAssembler sealed class ZapfAssembler
{ {
public event EventHandler<OpeningFileEventArgs>? OpeningFile; public IFileSystem FileSystem { get; set; } = PhysicalFileSystem.Instance;
public event EventHandler<CheckingFilePresenceEventArgs>? CheckingFilePresence;
public event EventHandler<InitializingContextEventArgs>? InitializingContext;
Stream OpenFile(string path, bool writing)
{
var handler = OpeningFile;
if (handler != null)
{
var args = new OpeningFileEventArgs(path, writing);
handler(this, args);
if (args.Stream != null)
return args.Stream;
}
return new FileStream(
path,
writing ? FileMode.Create : FileMode.Open,
writing ? FileAccess.ReadWrite : FileAccess.Read);
}
bool CheckFileExists(string path) public event EventHandler<InitializingContextEventArgs>? InitializingContext;
{
var handler = CheckingFilePresence;
if (handler != null)
{
var args = new CheckingFilePresenceEventArgs(path);
handler(this, args);
if (args.Exists.HasValue)
return args.Exists.Value;
}
return File.Exists(path);
}
Context InitializeContext(string inputFileName, string? outputFileName) Context InitializeContext(string inputFileName, string? outputFileName)
{ {
...@@ -118,7 +86,8 @@ namespace Zapf ...@@ -118,7 +86,8 @@ namespace Zapf
Quiet = true, Quiet = true,
InFile = inputFileName, InFile = inputFileName,
OutFile = outputFileName, OutFile = outputFileName,
DebugFile = Path.ChangeExtension(outputFileName, ".dbg") DebugFile = Path.ChangeExtension(outputFileName, ".dbg"),
FileSystem = FileSystem
}; };
var handler = InitializingContext; var handler = InitializingContext;
...@@ -126,12 +95,8 @@ namespace Zapf ...@@ -126,12 +95,8 @@ namespace Zapf
{ {
var args = new InitializingContextEventArgs(ctx); var args = new InitializingContextEventArgs(ctx);
handler(this, args); handler(this, args);
ctx = args.Context;
} }
ctx.InterceptOpenFile = OpenFile;
ctx.InterceptFileExists = CheckFileExists;
return ctx; return ctx;
} }
......
/* Copyright 2010-2018 Jesse McGrew
*
* This file is part of ZILF.
*
* ZILF is free software: you can redistribute it and/or modify it
* under t