Skip to content

Commit

Permalink
refactor: spanify ABC parsing io
Browse files Browse the repository at this point in the history
  • Loading branch information
PaulusParssinen committed Mar 23, 2024
1 parent c8c32b2 commit 3e71e54
Show file tree
Hide file tree
Showing 28 changed files with 891 additions and 562 deletions.
167 changes: 106 additions & 61 deletions Flazzy/ABC/ABCFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

namespace Flazzy.ABC;

public class ABCFile : FlashItem, IDisposable
public class ABCFile : IFlashItem, IDisposable
{
private readonly FlashReader _input;
private readonly Dictionary<ASMultiname, List<ASClass>> _classByQNameCache;
private readonly Dictionary<string, ASInstance> _instanceByConstructorCache;

Expand All @@ -18,8 +17,6 @@ public class ABCFile : FlashItem, IDisposable
public ASConstantPool Pool { get; }
public Version Version { get; set; }

protected override string DebuggerDisplay => "Version: " + Version;

public ABCFile()
{
_classByQNameCache = new Dictionary<ASMultiname, List<ASClass>>();
Expand All @@ -32,28 +29,60 @@ public ABCFile()
Scripts = new List<ASScript>();
MethodBodies = new List<ASMethodBody>();
}
public ABCFile(byte[] data)
: this(new FlashReader(data))
{ }
public ABCFile(FlashReader input)
public ABCFile(ReadOnlySpan<byte> data)
: this()
{
_input = input;
var input = new SpanFlashReader(data);

Version = new Version(
minor: input.ReadUInt16(),
major: input.ReadUInt16());

Pool = new ASConstantPool(this, ref input);

Methods.Capacity = input.ReadEncodedInt();
for (int i = 0; i < Methods.Capacity; i++)
{
Methods.Add(new ASMethod(this, ref input));
}

Metadata.Capacity = input.ReadEncodedInt();
for (int i = 0; i < Metadata.Capacity; i++)
{
Metadata.Add(new ASMetadata(this, ref input));
}

ushort minor = input.ReadUInt16();
ushort major = input.ReadUInt16();
Version = new Version(major, minor);
Pool = new ASConstantPool(this, input);
Instances.Capacity = input.ReadEncodedInt();
for (int i = 0; i < Instances.Capacity; i++)
{
Instances.Add(new ASInstance(this, ref input));
}

PopulateList(Methods, ReadMethod);
PopulateList(Metadata, ReadMetadata);
PopulateList(Instances, ReadInstance);
_classByQNameCache.EnsureCapacity(Instances.Count);
_instanceByConstructorCache.EnsureCapacity(Instances.Count);

PopulateList(Classes, ReadClass, Instances.Count);
PopulateList(Scripts, ReadScript);
PopulateList(MethodBodies, ReadMethodBody);
Classes.Capacity = Instances.Count;
for (int i = 0; i < Classes.Capacity; i++)
{
var @class = new ASClass(this, ref input)
{
InstanceIndex = i
};
CacheByNaming(@class);
Classes.Add(@class);
}

Scripts.Capacity = input.ReadEncodedInt();
for (int i = 0; i < Scripts.Capacity; i++)
{
Scripts.Add(new ASScript(this, ref input));
}

MethodBodies.Capacity = input.ReadEncodedInt();
for (int i = 0; i < MethodBodies.Capacity; i++)
{
MethodBodies.Add(new ASMethodBody(this, ref input));
}

_classByQNameCache.TrimExcess();
_instanceByConstructorCache.TrimExcess();
Expand Down Expand Up @@ -147,21 +176,6 @@ public IEnumerable<ASClass> GetClasses(ASMultiname multiname)

public ASInstance GetInstanceByConstructor(string constructorName) => _instanceByConstructorCache.GetValueOrDefault(constructorName);

private ASMethod ReadMethod(int index) => new(this, _input);
private ASMetadata ReadMetadata(int index) => new(this, _input);
private ASInstance ReadInstance(int index) => new(this, _input);
private ASClass ReadClass(int index)
{
var @class = new ASClass(this, _input)
{
InstanceIndex = index
};
CacheByNaming(@class);
return @class;
}
private ASScript ReadScript(int index) => new(this, _input);
private ASMethodBody ReadMethodBody(int index) => new(this, _input);

private ASMultiname GetMultiname(string qualifiedName)
{
foreach (ASMultiname multiname in Pool.GetMultinames(qualifiedName))
Expand All @@ -170,42 +184,59 @@ private ASMultiname GetMultiname(string qualifiedName)
}
return null;
}
private void PopulateList<T>(List<T> list, Func<int, T> reader, int count = -1)

public int GetSize()
{
list.Capacity = count < 0 ? _input.ReadInt30() : count;
for (int i = 0; i < list.Capacity; i++)
int size = 0;
size += sizeof(ushort);
size += sizeof(ushort);
size += Pool.GetSize();

size += SpanFlashWriter.GetEncodedIntSize(Methods.Count);
for (int i = 0; i < Methods.Count; i++)
{
T value = reader(i);
list.Add(value);
size += Methods[i].GetSize();
}
}

public override void WriteTo(FlashWriter output)
{
output.Write((ushort)Version.Minor);
output.Write((ushort)Version.Major);
size += SpanFlashWriter.GetEncodedIntSize(Metadata.Count);
for (int i = 0; i < Metadata.Count; i++)
{
size += Metadata[i].GetSize();
}

Pool.WriteTo(output);
size += SpanFlashWriter.GetEncodedIntSize(Instances.Count);
for (int i = 0; i < Instances.Count; i++)
{
size += Instances[i].GetSize();
size += Classes[i].GetSize();
}

WriteTo(output, Methods);
WriteTo(output, Metadata);
WriteTo(output, Instances);
WriteTo(output, Classes, false);
WriteTo(output, Scripts);
WriteTo(output, MethodBodies);
}
private void WriteTo<T>(FlashWriter output, List<T> list, bool writeCount = true)
where T : FlashItem
{
if (writeCount)
size += SpanFlashWriter.GetEncodedIntSize(Scripts.Count);
for (int i = 0; i < Scripts.Count; i++)
{
output.WriteInt30(list.Count);
size += Scripts[i].GetSize();
}
for (int i = 0; i < list.Count; i++)

size += SpanFlashWriter.GetEncodedIntSize(MethodBodies.Count);
for (int i = 0; i < MethodBodies.Count; i++)
{
FlashItem item = list[i];
item.WriteTo(output);
size += MethodBodies[i].GetSize();
}
return size;
}
public void WriteTo(ref SpanFlashWriter output)
{
output.Write((ushort)Version.Minor);
output.Write((ushort)Version.Major);

Pool.WriteTo(ref output);

WriteTo(ref output, Methods);
WriteTo(ref output, Metadata);
WriteTo(ref output, Instances);
WriteTo(ref output, Classes, false);
WriteTo(ref output, Scripts);
WriteTo(ref output, MethodBodies);
}

public void Dispose()
Expand All @@ -216,7 +247,6 @@ protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_input.Dispose();
_classByQNameCache.Clear();
_instanceByConstructorCache.Clear();

Expand All @@ -236,4 +266,19 @@ protected virtual void Dispose(bool disposing)
Pool.Multinames.Clear();
}
}

public override string ToString() => "Version: " + Version;

private static void WriteTo<T>(ref SpanFlashWriter output, List<T> list, bool writeCount = true)
where T : IFlashItem
{
if (writeCount)
{
output.WriteEncodedInt(list.Count);
}
for (int i = 0; i < list.Count; i++)
{
list[i].WriteTo(ref output);
}
}
}
14 changes: 2 additions & 12 deletions Flazzy/ABC/AS3Item.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,6 @@
namespace Flazzy.ABC;

public abstract class AS3Item : FlashItem
public interface IAS3Item
{
public ABCFile ABC { get; }

public AS3Item(ABCFile abc)
{
ABC = abc;
}

public virtual string ToAS3()
{
throw new NotSupportedException();
}
string ToAS3();
}
22 changes: 12 additions & 10 deletions Flazzy/ABC/ASClass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Flazzy.ABC;

public class ASClass : ASContainer
public class ASClass : ASContainer, IAS3Item
{
internal int InstanceIndex { get; set; }
public ASInstance Instance => ABC.Instances[InstanceIndex];
Expand All @@ -12,28 +12,30 @@ public class ASClass : ASContainer

public override bool IsStatic => true;
public override ASMultiname QName => Instance.QName;
protected override string DebuggerDisplay => ToAS3();

public ASClass(ABCFile abc)
: base(abc)
{ }
public ASClass(ABCFile abc, FlashReader input)
public ASClass(ABCFile abc, ref SpanFlashReader input)
: base(abc)
{
ConstructorIndex = input.ReadInt30();
ConstructorIndex = input.ReadEncodedInt();
Constructor.IsConstructor = true;
Constructor.Container = this;

PopulateTraits(input);
PopulateTraits(ref input);
}

public override string ToAS3()
public override int GetSize()
{
return Instance.ToAS3();
return SpanFlashWriter.GetEncodedIntSize(ConstructorIndex) + base.GetSize();
}
public override void WriteTo(FlashWriter output)
public override void WriteTo(ref SpanFlashWriter output)
{
output.WriteInt30(ConstructorIndex);
base.WriteTo(output);
output.WriteEncodedInt(ConstructorIndex);
base.WriteTo(ref output);
}

public string ToAS3() => Instance.ToAS3();
public override string ToString() => ToAS3();
}
Loading

0 comments on commit 3e71e54

Please sign in to comment.