From eff3f59affbd91d827d68c8a68add9efa7bf84d2 Mon Sep 17 00:00:00 2001 From: Benjamin Popp Date: Mon, 10 Dec 2018 21:44:40 -0600 Subject: [PATCH] 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) --- src/Gen3Hex.Core/Models/DataFormatModel.cs | 36 ++++----- src/Gen3Hex.Core/Models/FormattedRun.cs | 89 ++++++++++------------ src/Gen3Hex.Core/ViewModels/ViewPort.cs | 6 +- src/Gen3Hex.Tests/ModelTests.cs | 16 ++-- 4 files changed, 71 insertions(+), 76 deletions(-) diff --git a/src/Gen3Hex.Core/Models/DataFormatModel.cs b/src/Gen3Hex.Core/Models/DataFormatModel.cs index ce8be725..2671335b 100644 --- a/src/Gen3Hex.Core/Models/DataFormatModel.cs +++ b/src/Gen3Hex.Core/Models/DataFormatModel.cs @@ -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 sources = null; + List 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(); // 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(run.Anchor.PointerSources); - foreach (var source in run.Anchor.PointerSources) sourceToUnmappedName[source] = anchorForAddress[run.Start]; + unmappedNameToSources[anchorForAddress[run.Start]] = new List(run.PointerSources); + foreach (var source in run.PointerSources) sourceToUnmappedName[source] = anchorForAddress[run.Start]; addressForAnchor.Remove(anchorForAddress[run.Start]); anchorForAddress.Remove(run.Start); } diff --git a/src/Gen3Hex.Core/Models/FormattedRun.cs b/src/Gen3Hex.Core/Models/FormattedRun.cs index 3006a589..33cf7a67 100644 --- a/src/Gen3Hex.Core/Models/FormattedRun.cs +++ b/src/Gen3Hex.Core/Models/FormattedRun.cs @@ -7,9 +7,10 @@ namespace HavenSoft.Gen3Hex.Core.Models { public interface IFormattedRun { int Start { get; } int Length { get; } - Anchor Anchor { get; } + IReadOnlyList PointerSources { get; } IDataFormat CreateDataFormat(IModel data, int index); - void MergeAnchor(Anchor other); + void MergeAnchor(IReadOnlyList sources); + void RemoveSource(int source); } public class FormattedRunComparer : IComparer { @@ -26,61 +27,53 @@ namespace HavenSoft.Gen3Hex.Core.Models { public CompareFormattedRun(int start) => Start = start; - public Anchor Anchor => throw new NotImplementedException(); + public IReadOnlyList 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 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 PointerSources { get; private set; } - public Anchor(IReadOnlyList sources = null) => PointerSources = sources ?? new int[0]; + public BaseRun(int start, IReadOnlyList sources = null) { + Start = start; + PointerSources = sources; + } + + public abstract IDataFormat CreateDataFormat(IModel data, int index); + + public void MergeAnchor(IReadOnlyList 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 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 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; + } + } } diff --git a/src/Gen3Hex.Core/ViewModels/ViewPort.cs b/src/Gen3Hex.Core/ViewModels/ViewPort.cs index 0299c8a8..aa7b9e32 100644 --- a/src/Gen3Hex.Core/ViewModels/ViewPort.cs +++ b/src/Gen3Hex.Core/ViewModels/ViewPort.cs @@ -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 { diff --git a/src/Gen3Hex.Tests/ModelTests.cs b/src/Gen3Hex.Tests/ModelTests.cs index 16e92afe..5d0eb2a5 100644 --- a/src/Gen3Hex.Tests/ModelTests.cs +++ b/src/Gen3Hex.Tests/ModelTests.cs @@ -35,7 +35,7 @@ namespace HavenSoft.Gen3Hex.Tests { Assert.IsType(model.GetNextRun(0x206)); Assert.IsType(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(viewPort[0, 2].Format); + Assert.IsType(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