refactor: don't need the 'anchor' type as part of the pointer runs.

Each run just needs a simple list of things that point to it.
refactor: PointerRuns don't need the model at construction time.
refactor: use a base class to remove duplicate code (IFormattedRun)
This commit is contained in:
Benjamin Popp
2018-12-10 21:44:40 -06:00
parent 7617af8391
commit eff3f59aff
4 changed files with 71 additions and 76 deletions

View File

@@ -139,25 +139,25 @@ namespace HavenSoft.Gen3Hex.Core.Models {
while (moreDestinations && moreSources) {
if (destinations.Current < sources.Current) {
runs.Add(new NoInfoRun(destinations.Current, new Anchor(sources: pointersForDestination[destinations.Current])));
runs.Add(new NoInfoRun(destinations.Current, pointersForDestination[destinations.Current]));
moreDestinations = destinations.MoveNext();
} else if (sources.Current < destinations.Current) {
runs.Add(new PointerRun(this, sources.Current));
runs.Add(new PointerRun(sources.Current));
moreSources = sources.MoveNext();
} else {
runs.Add(new PointerRun(this, sources.Current, new Anchor(sources: pointersForDestination[destinations.Current])));
runs.Add(new PointerRun(sources.Current, pointersForDestination[destinations.Current]));
moreDestinations = destinations.MoveNext();
moreSources = sources.MoveNext();
}
}
while (moreDestinations) {
runs.Add(new NoInfoRun(destinations.Current, new Anchor(sources: pointersForDestination[destinations.Current])));
runs.Add(new NoInfoRun(destinations.Current, pointersForDestination[destinations.Current]));
moreDestinations = destinations.MoveNext();
}
while (moreSources) {
runs.Add(new PointerRun(this, sources.Current));
runs.Add(new PointerRun(sources.Current));
moreSources = sources.MoveNext();
}
}
@@ -232,7 +232,7 @@ namespace HavenSoft.Gen3Hex.Core.Models {
}
runs[index] = run;
run.MergeAnchor(existingRun.Anchor);
run.MergeAnchor(existingRun.PointerSources);
}
if (run is PointerRun pointerRun) {
@@ -242,9 +242,9 @@ namespace HavenSoft.Gen3Hex.Core.Models {
if (index < 0) {
// the pointer is brand new
index = ~index;
runs.Insert(index, new NoInfoRun(destination, new Anchor(new[] { run.Start })));
runs.Insert(index, new NoInfoRun(destination, new[] { run.Start }));
} else {
runs[index].MergeAnchor(new Anchor(new[] { run.Start }));
runs[index].MergeAnchor(new[] { run.Start });
}
}
}
@@ -269,23 +269,25 @@ namespace HavenSoft.Gen3Hex.Core.Models {
addressForAnchor.Add(anchorName, location);
}
List<int> sources = null;
List<int> sources;
if (unmappedNameToSources.TryGetValue(anchorName, out sources)) {
foreach (var source in sources) {
index = BinarySearch(source);
Debug.Assert(index >= 0 && runs[index] is PointerRun);
runs[index] = new PointerRun(this, source, runs[index].Anchor);
runs[index] = new PointerRun(source, runs[index].PointerSources);
sourceToUnmappedName.Remove(source);
WritePointer(source, location);
}
unmappedNameToSources.Remove(anchorName);
} else {
sources = new List<int>(); // an anchor was added: there is a list. It's just that in this case, the list is empty for now.
}
index = BinarySearch(location);
if (index < 0) {
runs.Insert(~index, new NoInfoRun(location, new Anchor(sources)));
runs.Insert(~index, new NoInfoRun(location, sources));
} else {
runs[index].MergeAnchor(new Anchor(sources));
runs[index].MergeAnchor(sources); // merging will give us anything that already pointed here for free
}
}
@@ -297,8 +299,8 @@ namespace HavenSoft.Gen3Hex.Core.Models {
var destination = ReadPointer(pointerRun.Start);
if (destination != Pointer.NULL) {
var anchorRun = runs[BinarySearch(destination)];
anchorRun.Anchor.RemoveSource(pointerRun.Start);
if (anchorRun.Anchor.PointerSources.Count == 0) {
anchorRun.RemoveSource(pointerRun.Start);
if (anchorRun.PointerSources.Count == 0) {
ClearFormat(anchorRun.Start, length);
if (anchorForAddress.ContainsKey(anchorRun.Start)) {
addressForAnchor.Remove(anchorForAddress[anchorRun.Start]);
@@ -312,10 +314,10 @@ namespace HavenSoft.Gen3Hex.Core.Models {
if (unmappedNameToSources[name].Count == 0) unmappedNameToSources.Remove(name);
}
}
foreach (var source in run.Anchor?.PointerSources ?? new int[0]) WriteValue(source, 0);
foreach (var source in run.PointerSources ?? new int[0]) WriteValue(source, 0);
if (anchorForAddress.ContainsKey(run.Start)) {
unmappedNameToSources[anchorForAddress[run.Start]] = new List<int>(run.Anchor.PointerSources);
foreach (var source in run.Anchor.PointerSources) sourceToUnmappedName[source] = anchorForAddress[run.Start];
unmappedNameToSources[anchorForAddress[run.Start]] = new List<int>(run.PointerSources);
foreach (var source in run.PointerSources) sourceToUnmappedName[source] = anchorForAddress[run.Start];
addressForAnchor.Remove(anchorForAddress[run.Start]);
anchorForAddress.Remove(run.Start);
}

View File

@@ -7,9 +7,10 @@ namespace HavenSoft.Gen3Hex.Core.Models {
public interface IFormattedRun {
int Start { get; }
int Length { get; }
Anchor Anchor { get; }
IReadOnlyList<int> PointerSources { get; }
IDataFormat CreateDataFormat(IModel data, int index);
void MergeAnchor(Anchor other);
void MergeAnchor(IReadOnlyList<int> sources);
void RemoveSource(int source);
}
public class FormattedRunComparer : IComparer<IFormattedRun> {
@@ -26,61 +27,53 @@ namespace HavenSoft.Gen3Hex.Core.Models {
public CompareFormattedRun(int start) => Start = start;
public Anchor Anchor => throw new NotImplementedException();
public IReadOnlyList<int> PointerSources => throw new NotImplementedException();
public IDataFormat CreateDataFormat(IModel data, int index) => throw new NotImplementedException();
public void MergeAnchor(Anchor other) => throw new NotImplementedException();
public void MergeAnchor(IReadOnlyList<int> other) => throw new NotImplementedException();
public void RemoveSource(int source) => throw new NotImplementedException();
}
public class NoInfoRun : IFormattedRun {
public abstract class BaseRun : IFormattedRun {
public int Start { get; }
public int Length => 1;
public Anchor Anchor { get; private set; }
public NoInfoRun(int start, Anchor anchor = null) => (Start, Anchor) = (start, anchor);
public IDataFormat CreateDataFormat(IModel data, int index) => None.Instance;
public void MergeAnchor(Anchor other) {
if (other == null) return;
if (Anchor == null) { Anchor = other; return; }
var sources = other.PointerSources.Concat(Anchor.PointerSources).Distinct().OrderBy(i => i).ToList();
Anchor = new Anchor(sources);
}
}
public class PointerRun : IFormattedRun {
private readonly IModel parent;
public int Start { get; }
public int Length => 4;
public Anchor Anchor { get; private set; }
public PointerRun(IModel parent, int start, Anchor anchor = null) {
this.parent = parent;
Start = start;
Anchor = anchor;
}
public IDataFormat CreateDataFormat(IModel data, int index) {
var destinationAddress = Math.Max(0, data.ReadPointer(Start));
var anchor = parent.GetAnchorFromAddress(Start, destinationAddress);
var pointer = new Pointer(Start, index - Start, data.ReadPointer(Start), anchor);
return pointer;
}
public void MergeAnchor(Anchor other) {
if (other == null) return;
if (Anchor == null) { Anchor = other; return; }
var sources = other.PointerSources.Concat(Anchor.PointerSources).Distinct().OrderBy(i => i).ToList();
Anchor = new Anchor(sources);
}
}
public class Anchor {
public abstract int Length { get; }
public IReadOnlyList<int> PointerSources { get; private set; }
public Anchor(IReadOnlyList<int> sources = null) => PointerSources = sources ?? new int[0];
public BaseRun(int start, IReadOnlyList<int> sources = null) {
Start = start;
PointerSources = sources;
}
public abstract IDataFormat CreateDataFormat(IModel data, int index);
public void MergeAnchor(IReadOnlyList<int> sources) {
if (sources == null) return;
if (PointerSources == null) { PointerSources = sources; return; }
PointerSources = sources.Concat(PointerSources).Distinct().OrderBy(i => i).ToList();
}
public void RemoveSource(int source) {
PointerSources = PointerSources.Except(new[] { source }).ToList();
}
}
public class NoInfoRun : BaseRun {
public override int Length => 1;
public NoInfoRun(int start, IReadOnlyList<int> sources = null) : base(start, sources) { }
public override IDataFormat CreateDataFormat(IModel data, int index) => None.Instance;
}
public class PointerRun : BaseRun {
public override int Length => 4;
public PointerRun(int start, IReadOnlyList<int> sources = null) : base(start, sources) { }
public override IDataFormat CreateDataFormat(IModel data, int index) {
var destinationAddress = Math.Max(0, data.ReadPointer(Start));
var anchor = data.GetAnchorFromAddress(Start, destinationAddress);
var pointer = new Pointer(Start, index - Start, data.ReadPointer(Start), anchor);
return pointer;
}
}
}

View File

@@ -538,7 +538,7 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels {
scroll.DataLength = Model.Count;
Model.ClearFormat(index, 4);
Model.WritePointer(index, fullValue);
Model.ObserveRunWritten(new PointerRun(Model, index));
Model.ObserveRunWritten(new PointerRun(index));
ClearEdits(point);
SilentScroll(index + 4);
}
@@ -601,9 +601,9 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels {
currentView[x, y] = HexElement.Undefined;
} else if (index >= run.Start) {
var format = run.CreateDataFormat(Model, index);
if (run.Anchor != null && run.Start == index) {
if (run.PointerSources != null && run.Start == index) {
var name = Model.GetAnchorFromAddress(-1, run.Start);
format = new DataFormats.Anchor(format, name, string.Empty, run.Anchor.PointerSources);
format = new Anchor(format, name, string.Empty, run.PointerSources);
}
currentView[x, y] = new HexElement(Model[index], format);
} else {

View File

@@ -35,7 +35,7 @@ namespace HavenSoft.Gen3Hex.Tests {
Assert.IsType<PointerRun>(model.GetNextRun(0x206));
Assert.IsType<NoInfoRun>(model.GetNextRun(0x208));
Assert.Single(model.GetNextRun(0x400).Anchor.PointerSources);
Assert.Single(model.GetNextRun(0x400).PointerSources);
Assert.Equal(0x4050, model.GetNextRun(0x4050).Start);
Assert.Equal(4, model.GetNextRun(0x4071).Length);
@@ -64,7 +64,7 @@ namespace HavenSoft.Gen3Hex.Tests {
model = new PointerModel(buffer);
var run = model.GetNextRun(0x14);
Assert.Equal(2, run.Anchor.PointerSources.Count);
Assert.Equal(2, run.PointerSources.Count);
}
[Fact]
@@ -118,7 +118,7 @@ namespace HavenSoft.Gen3Hex.Tests {
viewPort.SelectionStart = new Point(0, 2);
viewPort.Edit("^bob ");
Assert.IsType<Core.ViewModels.DataFormats.Anchor>(viewPort[0, 2].Format);
Assert.IsType<Anchor>(viewPort[0, 2].Format);
}
[Fact]
@@ -156,7 +156,7 @@ namespace HavenSoft.Gen3Hex.Tests {
var model = new PointerModel(buffer);
model.WritePointer(16, 100);
model.ObserveRunWritten(new PointerRun(model, 16));
model.ObserveRunWritten(new PointerRun(16));
Assert.Equal(16, model.GetNextRun(10).Start);
Assert.Equal(16, model.GetNextRun(17).Start);
Assert.Equal(16, model.GetNextRun(19).Start);
@@ -164,7 +164,7 @@ namespace HavenSoft.Gen3Hex.Tests {
model.ClearFormat(14, 4);
model.WritePointer(14, 200);
model.ObserveRunWritten(new PointerRun(model, 14));
model.ObserveRunWritten(new PointerRun(14));
Assert.Equal(14, model.GetNextRun(10).Start);
Assert.Equal(14, model.GetNextRun(15).Start);
Assert.Equal(14, model.GetNextRun(16).Start);
@@ -178,7 +178,7 @@ namespace HavenSoft.Gen3Hex.Tests {
var model = new PointerModel(buffer);
model.WritePointer(16, 12);
model.ObserveRunWritten(new PointerRun(model, 16));
model.ObserveRunWritten(new PointerRun(16));
model.ObserveAnchorWritten(18, "bob", string.Empty);
Assert.Equal(18, model.GetNextRun(10).Start);
@@ -190,11 +190,11 @@ namespace HavenSoft.Gen3Hex.Tests {
var model = new PointerModel(buffer);
model.WritePointer(16, 32);
model.ObserveRunWritten(new PointerRun(model, 16));
model.ObserveRunWritten(new PointerRun(16));
model.ClearFormat(30, 4);
model.WritePointer(30, 64);
model.ObserveRunWritten(new PointerRun(model, 30));
model.ObserveRunWritten(new PointerRun(30));
Assert.Equal(16, model.GetNextRun(10).Start); // original pointer at 16 is still there, but it no longer knows what it's pointing to
Assert.Equal(30, model.GetNextRun(24).Start); // next data is the pointer at 30