diff --git a/Flazzy/ABC/ABCFile.cs b/Flazzy/ABC/ABCFile.cs index eaec65e..4fe2204 100644 --- a/Flazzy/ABC/ABCFile.cs +++ b/Flazzy/ABC/ABCFile.cs @@ -2,9 +2,8 @@ namespace Flazzy.ABC; -public class ABCFile : FlashItem, IDisposable +public class ABCFile : IFlashItem, IDisposable { - private readonly FlashReader _input; private readonly Dictionary> _classByQNameCache; private readonly Dictionary _instanceByConstructorCache; @@ -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>(); @@ -32,28 +29,60 @@ public ABCFile() Scripts = new List(); MethodBodies = new List(); } - public ABCFile(byte[] data) - : this(new FlashReader(data)) - { } - public ABCFile(FlashReader input) + public ABCFile(ReadOnlySpan 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(); @@ -147,21 +176,6 @@ public IEnumerable 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)) @@ -170,42 +184,59 @@ private ASMultiname GetMultiname(string qualifiedName) } return null; } - private void PopulateList(List list, Func 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(FlashWriter output, List 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() @@ -216,7 +247,6 @@ protected virtual void Dispose(bool disposing) { if (disposing) { - _input.Dispose(); _classByQNameCache.Clear(); _instanceByConstructorCache.Clear(); @@ -236,4 +266,19 @@ protected virtual void Dispose(bool disposing) Pool.Multinames.Clear(); } } + + public override string ToString() => "Version: " + Version; + + private static void WriteTo(ref SpanFlashWriter output, List 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); + } + } } \ No newline at end of file diff --git a/Flazzy/ABC/AS3Item.cs b/Flazzy/ABC/AS3Item.cs index eab6d1f..825a8c0 100644 --- a/Flazzy/ABC/AS3Item.cs +++ b/Flazzy/ABC/AS3Item.cs @@ -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(); } \ No newline at end of file diff --git a/Flazzy/ABC/ASClass.cs b/Flazzy/ABC/ASClass.cs index 9b4409a..b0f4390 100644 --- a/Flazzy/ABC/ASClass.cs +++ b/Flazzy/ABC/ASClass.cs @@ -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]; @@ -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(); } \ No newline at end of file diff --git a/Flazzy/ABC/ASConstantPool.cs b/Flazzy/ABC/ASConstantPool.cs index 77e901a..2f97bf0 100644 --- a/Flazzy/ABC/ASConstantPool.cs +++ b/Flazzy/ABC/ASConstantPool.cs @@ -1,17 +1,18 @@ -using Flazzy.IO; +using System.Runtime.InteropServices; +using System.Text; + +using Flazzy.IO; namespace Flazzy.ABC; /// /// Represents a block of array-based entries that reflect the constants used by all the methods. /// -public class ASConstantPool : FlashItem +public class ASConstantPool : IFlashItem { private readonly Dictionary _multinamesIndicesCache; private readonly Dictionary> _multinamesByNameCache; - private readonly FlashReader _input; - public ABCFile ABC { get; } public List Integers { get; } @@ -40,44 +41,75 @@ public ASConstantPool(ABCFile abc) { ABC = abc; } - public ASConstantPool(ABCFile abc, FlashReader input) + public ASConstantPool(ABCFile abc, ref SpanFlashReader input) : this(abc) { - _input = input; - - PopulateList(Integers, input.ReadInt30, 0); - PopulateList(UIntegers, input.ReadUInt30, 0); - PopulateList(Doubles, input.ReadDouble, double.NaN); - PopulateList(Strings, input.ReadString, null); - PopulateList(Namespaces, ReadNamespace, null); - PopulateList(NamespaceSets, ReadNamespaceSet, null); - PopulateList(Multinames, ReadMultiname, null); + Integers.Capacity = input.ReadEncodedInt(); + if (Integers.Capacity > 0) Integers.Add(0); + for (int i = 1; i < Integers.Capacity; i++) + { + Integers.Add(input.ReadEncodedInt()); + } - _multinamesByNameCache.TrimExcess(); - _multinamesIndicesCache.TrimExcess(); - } + UIntegers.Capacity = input.ReadEncodedInt(); + if (UIntegers.Capacity > 0) UIntegers.Add(0); + for (int i = 1; i < UIntegers.Capacity; i++) + { + UIntegers.Add(input.ReadEncodedUInt()); + } - public object GetConstant(ConstantKind type, int index) - { - switch (type) + Doubles.Capacity = input.ReadEncodedInt(); + if (Doubles.Capacity > 0) Doubles.Add(double.NaN); + for (int i = 1; i < Doubles.Capacity; i++) { - case ConstantKind.True: return true; - case ConstantKind.False: return false; + Doubles.Add(input.ReadDouble()); + } - case ConstantKind.Null: - case ConstantKind.Undefined: return null; + Strings.Capacity = input.ReadEncodedInt(); + if (Strings.Capacity > 0) Strings.Add(default); + for (int i = 1; i < Strings.Capacity; i++) + { + Strings.Add(input.ReadString()); + } - case ConstantKind.String: return Strings[index]; - case ConstantKind.Double: return Doubles[index]; - case ConstantKind.Integer: return Integers[index]; - case ConstantKind.UInteger: return UIntegers[index]; + Namespaces.Capacity = input.ReadEncodedInt(); + if (Namespaces.Capacity > 0) Namespaces.Add(default); + for (int i = 1; i < Namespaces.Capacity; i++) + { + Namespaces.Add(new ASNamespace(this, ref input)); + } - case ConstantKind.Namespace: return Namespaces[index]; + NamespaceSets.Capacity = input.ReadEncodedInt(); + if (NamespaceSets.Capacity > 0) NamespaceSets.Add(default); + for (int i = 1; i < NamespaceSets.Capacity; i++) + { + NamespaceSets.Add(new ASNamespaceSet(this, ref input)); + } - default: return null; + Multinames.Capacity = input.ReadEncodedInt(); + if (Multinames.Capacity > 0) Multinames.Add(default); + for (int i = 1; i < Multinames.Capacity; i++) + { + Multinames.Add(ReadMultiname(ref input)); } + + _multinamesByNameCache.TrimExcess(); + _multinamesIndicesCache.TrimExcess(); } + public object GetConstant(ConstantKind type, int index) => type switch + { + ConstantKind.True => true, + ConstantKind.False => false, + ConstantKind.String => Strings[index], + ConstantKind.Double => Doubles[index], + ConstantKind.Integer => Integers[index], + ConstantKind.UInteger => UIntegers[index], + ConstantKind.Namespace => Namespaces[index], + + ConstantKind.Null or ConstantKind.Undefined or _ => null, + }; + public int AddConstant(object value, bool recycle = true) { switch (Type.GetTypeCode(value.GetType())) @@ -130,9 +162,9 @@ public IEnumerable GetMultinames(string name) return _multinamesByNameCache.GetValueOrDefault(name) ?? Enumerable.Empty(); } - private ASMultiname ReadMultiname() + private ASMultiname ReadMultiname(ref SpanFlashReader input) { - ASMultiname multiname = new(this, _input); + ASMultiname multiname = new(this, ref input); if (!string.IsNullOrWhiteSpace(multiname.Name)) { if (!_multinamesByNameCache.TryGetValue(multiname.Name, out List multinames)) @@ -145,51 +177,93 @@ private ASMultiname ReadMultiname() _multinamesIndicesCache.Add(multiname, Multinames.Count); return multiname; } - private ASNamespace ReadNamespace() + + public int GetSize() { - return new ASNamespace(this, _input); + int size = 0; + size += SpanFlashWriter.GetEncodedIntSize(Integers.Count); + for (int i = 1; i < Integers.Count; i++) + { + size += SpanFlashWriter.GetEncodedIntSize(Integers[i]); + } + + size += SpanFlashWriter.GetEncodedIntSize(UIntegers.Count); + for (int i = 1; i < UIntegers.Count; i++) + { + size += SpanFlashWriter.GetEncodedUIntSize(UIntegers[i]); + } + + size += SpanFlashWriter.GetEncodedIntSize(Doubles.Count); + if (Doubles.Count > 1) + { + size += (Doubles.Count - 1) * sizeof(double); + } + + size += SpanFlashWriter.GetEncodedIntSize(Strings.Count); + for (int i = 1; i < Strings.Count; i++) + { + int length = Encoding.UTF8.GetByteCount(Strings[i]); + size += SpanFlashWriter.GetEncodedIntSize(length); + size += length; + } + + size += SpanFlashWriter.GetEncodedIntSize(Namespaces.Count); + for (int i = 1; i < Namespaces.Count; i++) + { + size += Namespaces[i].GetSize(); + } + + size += SpanFlashWriter.GetEncodedIntSize(NamespaceSets.Count); + for (int i = 1; i < NamespaceSets.Count; i++) + { + size += NamespaceSets[i].GetSize(); + } + + size += SpanFlashWriter.GetEncodedIntSize(Multinames.Count); + for (int i = 1; i < Multinames.Count; i++) + { + size += Multinames[i].GetSize(); + } + return size; } - private ASNamespaceSet ReadNamespaceSet() + public void WriteTo(ref SpanFlashWriter output) { - return new ASNamespaceSet(this, _input); - } + output.WriteEncodedInt(Integers.Count); + for (int i = 1; i < Integers.Count; i++) + { + output.WriteEncodedInt(Integers[i]); + } - private void PopulateList(List list, Func reader, T defaultValue) - { - list.Capacity = _input.ReadInt30(); - if (list.Equals(Multinames)) + output.WriteEncodedInt(UIntegers.Count); + for (int i = 1; i < UIntegers.Count; i++) { - _multinamesByNameCache.EnsureCapacity(list.Capacity); - _multinamesIndicesCache.EnsureCapacity(list.Capacity); + output.WriteEncodedUInt(UIntegers[i]); } - if (list.Capacity > 0) + + output.WriteEncodedInt(Doubles.Count); + if (Doubles.Count > 1) { - list.Add(defaultValue); - for (int i = 1; i < list.Capacity; i++) - { - T value = reader(); - list.Add(value); - } + output.WriteDoubleArray(CollectionsMarshal.AsSpan(Doubles).Slice(1)); } - } - public override void WriteTo(FlashWriter output) - { - WriteTo(output, output.WriteInt30, Integers); - WriteTo(output, output.WriteUInt30, UIntegers); - WriteTo(output, output.Write, Doubles); - WriteTo(output, output.Write, Strings); - WriteTo(output, output.WriteItem, Namespaces); - WriteTo(output, output.WriteItem, NamespaceSets); - WriteTo(output, output.WriteItem, Multinames); + output.WriteEncodedInt(Strings.Count); + for (int i = 1; i < Strings.Count; i++) + { + output.WriteString(Strings[i]); + } + + WriteItems(ref output, Namespaces); + WriteItems(ref output, NamespaceSets); + WriteItems(ref output, Multinames); } - private void WriteTo(FlashWriter output, Action writer, List constants) + + private static void WriteItems(ref SpanFlashWriter output, List constants) + where T : IFlashItem { - output.WriteInt30(constants.Count); + output.WriteEncodedInt(constants.Count); for (int i = 1; i < constants.Count; i++) { - T value = constants[i]; - writer(value); + constants[i].WriteTo(ref output); } } } \ No newline at end of file diff --git a/Flazzy/ABC/ASContainer.cs b/Flazzy/ABC/ASContainer.cs index 7a79366..56cd04e 100644 --- a/Flazzy/ABC/ASContainer.cs +++ b/Flazzy/ABC/ASContainer.cs @@ -2,31 +2,18 @@ namespace Flazzy.ABC; -public abstract class ASContainer : AS3Item +public abstract class ASContainer : IFlashItem { + public ABCFile ABC { get; } + public List Traits { get; } public virtual bool IsStatic { get; } public abstract ASMultiname QName { get; } - protected override string DebuggerDisplay - { - get - { - int methodCount = Traits.Count( - t => t.Kind == TraitKind.Method || - t.Kind == TraitKind.Getter || - t.Kind == TraitKind.Setter); - - int slotCount = Traits.Count(t => t.Kind == TraitKind.Slot); - int constantCount = Traits.Count(t => t.Kind == TraitKind.Constant); - - return $"{QName}, Traits: {Traits.Count}"; - } - } public ASContainer(ABCFile abc) - : base(abc) { + ABC = abc; Traits = new List(); } @@ -150,17 +137,17 @@ private int AddPublicQualifiedName(string qualifiedName) return ABC.Pool.AddConstant(qName); } - protected void PopulateTraits(FlashReader input) + protected void PopulateTraits(ref SpanFlashReader input) { - Traits.Capacity = input.ReadInt30(); + Traits.Capacity = input.ReadEncodedInt(); for (int i = 0; i < Traits.Capacity; i++) { - var trait = new ASTrait(ABC, input); - trait.IsStatic = IsStatic; + var trait = new ASTrait(ABC, ref input) + { + IsStatic = IsStatic + }; - if (trait.Kind == TraitKind.Method || - trait.Kind == TraitKind.Getter || - trait.Kind == TraitKind.Setter) + if (trait.Kind is TraitKind.Method or TraitKind.Getter or TraitKind.Setter) { trait.Method.Container = this; } @@ -168,13 +155,33 @@ protected void PopulateTraits(FlashReader input) Traits.Add(trait); } } - public override void WriteTo(FlashWriter output) + public virtual int GetSize() + { + int size = 0; + size += SpanFlashWriter.GetEncodedIntSize(Traits.Count); + for (int i = 0; i < Traits.Count; i++) + { + size += Traits[i].GetSize(); + } + return size; + } + public virtual void WriteTo(ref SpanFlashWriter output) { - output.WriteInt30(Traits.Count); + output.WriteEncodedInt(Traits.Count); for (int i = 0; i < Traits.Count; i++) { - ASTrait trait = Traits[i]; - trait.WriteTo(output); + Traits[i].WriteTo(ref output); } } + + public override string ToString() + { + int methodCount = Traits.Count( + t => t.Kind is TraitKind.Method or TraitKind.Getter or TraitKind.Setter); + + int slotCount = Traits.Count(t => t.Kind is TraitKind.Slot); + int constantCount = Traits.Count(t => t.Kind is TraitKind.Constant); + + return $"{QName}, Traits: {Traits.Count}"; + } } \ No newline at end of file diff --git a/Flazzy/ABC/ASException.cs b/Flazzy/ABC/ASException.cs index 1c15bfd..839c90c 100644 --- a/Flazzy/ABC/ASException.cs +++ b/Flazzy/ABC/ASException.cs @@ -2,8 +2,10 @@ namespace Flazzy.ABC; -public class ASException : AS3Item +public class ASException : IFlashItem { + public ABCFile ABC { get; } + public int To { get; set; } public int From { get; set; } public int Target { get; set; } @@ -15,24 +17,35 @@ public class ASException : AS3Item public ASMultiname ExceptionType => ABC.Pool.Multinames[ExceptionTypeIndex]; public ASException(ABCFile abc) - : base(abc) - { } - public ASException(ABCFile abc, FlashReader input) - : base(abc) { - From = input.ReadInt30(); - To = input.ReadInt30(); - Target = input.ReadInt30(); - ExceptionTypeIndex = input.ReadInt30(); - VariableNameIndex = input.ReadInt30(); + ABC = abc; + } + public ASException(ABCFile abc, ref SpanFlashReader input) + : this(abc) + { + From = input.ReadEncodedInt(); + To = input.ReadEncodedInt(); + Target = input.ReadEncodedInt(); + ExceptionTypeIndex = input.ReadEncodedInt(); + VariableNameIndex = input.ReadEncodedInt(); } - public override void WriteTo(FlashWriter output) + public int GetSize() + { + int size = 0; + size += SpanFlashWriter.GetEncodedIntSize(From); + size += SpanFlashWriter.GetEncodedIntSize(To); + size += SpanFlashWriter.GetEncodedIntSize(Target); + size += SpanFlashWriter.GetEncodedIntSize(ExceptionTypeIndex); + size += SpanFlashWriter.GetEncodedIntSize(VariableNameIndex); + return size; + } + public void WriteTo(ref SpanFlashWriter output) { - output.WriteInt30(From); - output.WriteInt30(To); - output.WriteInt30(Target); - output.WriteInt30(ExceptionTypeIndex); - output.WriteInt30(VariableNameIndex); + output.WriteEncodedInt(From); + output.WriteEncodedInt(To); + output.WriteEncodedInt(Target); + output.WriteEncodedInt(ExceptionTypeIndex); + output.WriteEncodedInt(VariableNameIndex); } } \ No newline at end of file diff --git a/Flazzy/ABC/ASInstance.cs b/Flazzy/ABC/ASInstance.cs index bc5285b..5a1a76d 100644 --- a/Flazzy/ABC/ASInstance.cs +++ b/Flazzy/ABC/ASInstance.cs @@ -2,7 +2,7 @@ namespace Flazzy.ABC; -public class ASInstance : ASContainer +public class ASInstance : ASContainer, IAS3Item { public ClassFlags Flags { get; set; } public bool IsInterface => Flags.HasFlag(ClassFlags.Interface); @@ -19,8 +19,6 @@ public class ASInstance : ASContainer public int ProtectedNamespaceIndex { get; set; } public ASNamespace ProtectedNamespace => ABC.Pool.Namespaces[ProtectedNamespaceIndex]; - protected override string DebuggerDisplay => ToAS3(); - public List InterfaceIndices { get; } public ASInstance(ABCFile abc) @@ -28,39 +26,36 @@ public ASInstance(ABCFile abc) { InterfaceIndices = new List(); } - public ASInstance(ABCFile abc, FlashReader input) + public ASInstance(ABCFile abc, ref SpanFlashReader input) : this(abc) { - QNameIndex = input.ReadInt30(); - SuperIndex = input.ReadInt30(); + QNameIndex = input.ReadEncodedInt(); + SuperIndex = input.ReadEncodedInt(); Flags = (ClassFlags)input.ReadByte(); if (Flags.HasFlag(ClassFlags.ProtectedNamespace)) { - ProtectedNamespaceIndex = input.ReadInt30(); + ProtectedNamespaceIndex = input.ReadEncodedInt(); } - InterfaceIndices.Capacity = input.ReadInt30(); + InterfaceIndices.Capacity = input.ReadEncodedInt(); for (int i = 0; i < InterfaceIndices.Capacity; i++) { - int interfaceIndex = input.ReadInt30(); - InterfaceIndices.Add(interfaceIndex); + InterfaceIndices.Add(input.ReadEncodedInt()); } - ConstructorIndex = input.ReadInt30(); + ConstructorIndex = input.ReadEncodedInt(); Constructor.IsConstructor = true; Constructor.Container = this; - PopulateTraits(input); + PopulateTraits(ref input); } public IEnumerable GetInterfaces() { for (int i = 0; i < InterfaceIndices.Count; i++) { - int interfaceIndex = InterfaceIndices[i]; - ASMultiname @interface = ABC.Pool.Multinames[interfaceIndex]; - yield return @interface; + yield return ABC.Pool.Multinames[InterfaceIndices[i]]; } } public bool ContainsInterface(string qualifiedName) @@ -81,7 +76,7 @@ public bool ContainsInterface(string qualifiedName) return false; } - public override string ToAS3() + public string ToAS3() { string as3 = QName.Namespace.GetAS3Modifiers(); bool isInterface = Flags.HasFlag(ClassFlags.Interface); @@ -114,25 +109,48 @@ public override string ToAS3() return as3; } - public override void WriteTo(FlashWriter output) + + public override int GetSize() + { + int size = 0; + size += SpanFlashWriter.GetEncodedIntSize(QNameIndex); + size += SpanFlashWriter.GetEncodedIntSize(SuperIndex); + size += sizeof(byte); + + if (Flags.HasFlag(ClassFlags.ProtectedNamespace)) + { + size += SpanFlashWriter.GetEncodedIntSize(ProtectedNamespaceIndex); + } + + size += SpanFlashWriter.GetEncodedIntSize(InterfaceIndices.Count); + for (int i = 0; i < InterfaceIndices.Count; i++) + { + size += SpanFlashWriter.GetEncodedIntSize(InterfaceIndices[i]); + } + + size += SpanFlashWriter.GetEncodedIntSize(ConstructorIndex); + return size + base.GetSize(); + } + public override void WriteTo(ref SpanFlashWriter output) { - output.WriteInt30(QNameIndex); - output.WriteInt30(SuperIndex); + output.WriteEncodedInt(QNameIndex); + output.WriteEncodedInt(SuperIndex); output.Write((byte)Flags); if (Flags.HasFlag(ClassFlags.ProtectedNamespace)) { - output.WriteInt30(ProtectedNamespaceIndex); + output.WriteEncodedInt(ProtectedNamespaceIndex); } - output.WriteInt30(InterfaceIndices.Count); + output.WriteEncodedInt(InterfaceIndices.Count); for (int i = 0; i < InterfaceIndices.Count; i++) { - int interfaceIndex = InterfaceIndices[i]; - output.WriteInt30(interfaceIndex); + output.WriteEncodedInt(InterfaceIndices[i]); } - output.WriteInt30(ConstructorIndex); - base.WriteTo(output); + output.WriteEncodedInt(ConstructorIndex); + base.WriteTo(ref output); } + + public override string ToString() => ToAS3(); } \ No newline at end of file diff --git a/Flazzy/ABC/ASMetadata.cs b/Flazzy/ABC/ASMetadata.cs index 8386857..a736add 100644 --- a/Flazzy/ABC/ASMetadata.cs +++ b/Flazzy/ABC/ASMetadata.cs @@ -2,7 +2,7 @@ namespace Flazzy.ABC; -public sealed class ASMetadata : FlashItem +public sealed class ASMetadata : IFlashItem { private readonly ABCFile _abc; @@ -17,36 +17,48 @@ public ASMetadata(ABCFile abc) Items = new List(); } - public ASMetadata(ABCFile abc, FlashReader input) + public ASMetadata(ABCFile abc, ref SpanFlashReader input) : this(abc) { - NameIndex = input.ReadInt30(); - Items.Capacity = input.ReadInt30(); + NameIndex = input.ReadEncodedInt(); + Items.Capacity = input.ReadEncodedInt(); if (Items.Capacity > 0) { Span keys = stackalloc int[Items.Capacity]; for (int i = 0; i < Items.Capacity; i++) { - keys[i] = input.ReadInt30(); + keys[i] = input.ReadEncodedInt(); } for (int i = 0; i < keys.Length; i++) { - Items.Add(new ASItemInfo(abc, keys[i], input.ReadInt30())); + Items.Add(new ASItemInfo(abc, keys[i], input.ReadEncodedInt())); } } } - public override void WriteTo(FlashWriter output) + public int GetSize() { - output.WriteInt30(NameIndex); - output.WriteInt30(Items.Count); + int size = 0; + size += SpanFlashWriter.GetEncodedIntSize(NameIndex); + size += SpanFlashWriter.GetEncodedIntSize(Items.Count); for (int i = 0; i < Items.Count; i++) { - output.WriteInt30(Items[i].KeyIndex); + size += SpanFlashWriter.GetEncodedIntSize(Items[i].KeyIndex); + size += SpanFlashWriter.GetEncodedIntSize(Items[i].ValueIndex); + } + return size; + } + public void WriteTo(ref SpanFlashWriter output) + { + output.WriteEncodedInt(NameIndex); + output.WriteEncodedInt(Items.Count); + for (int i = 0; i < Items.Count; i++) + { + output.WriteEncodedInt(Items[i].KeyIndex); } for (int i = 0; i < Items.Count; i++) { - output.WriteInt30(Items[i].ValueIndex); + output.WriteEncodedInt(Items[i].ValueIndex); } } } \ No newline at end of file diff --git a/Flazzy/ABC/ASMethod.cs b/Flazzy/ABC/ASMethod.cs index c7785d9..1bab756 100644 --- a/Flazzy/ABC/ASMethod.cs +++ b/Flazzy/ABC/ASMethod.cs @@ -1,11 +1,14 @@ -using System.Text; +using System.Runtime.InteropServices; +using System.Text; using Flazzy.IO; namespace Flazzy.ABC; -public class ASMethod : AS3Item +public class ASMethod : IFlashItem, IAS3Item { + public ABCFile ABC { get; set; } + public int NameIndex { get; set; } public string Name => ABC.Pool.Strings[NameIndex]; @@ -21,54 +24,151 @@ public class ASMethod : AS3Item public ASContainer Container { get; internal set; } public bool IsAnonymous => Trait == null && !IsConstructor; - protected override string DebuggerDisplay => ToAS3(); - public ASMethod(ABCFile abc) - : base(abc) { + ABC = abc; Parameters = new List(); } - public ASMethod(ABCFile abc, FlashReader input) + public ASMethod(ABCFile abc, ref SpanFlashReader input) : this(abc) { - Parameters.Capacity = input.ReadInt30(); - ReturnTypeIndex = input.ReadInt30(); + Parameters.Capacity = input.ReadEncodedInt(); + ReturnTypeIndex = input.ReadEncodedInt(); for (int i = 0; i < Parameters.Capacity; i++) { - var parameter = new ASParameter(this); - parameter.TypeIndex = input.ReadInt30(); - Parameters.Add(parameter); + Parameters.Add(new ASParameter(this) + { + TypeIndex = input.ReadEncodedInt() + }); } - NameIndex = input.ReadInt30(); + NameIndex = input.ReadEncodedInt(); Flags = (MethodFlags)input.ReadByte(); if (Flags.HasFlag(MethodFlags.HasOptional)) { - int optionalParamCount = input.ReadInt30(); - for (int i = Parameters.Count - optionalParamCount; - optionalParamCount > 0; - i++, optionalParamCount--) + int optionalParamCount = input.ReadEncodedInt(); + foreach (var parameter in CollectionsMarshal.AsSpan(Parameters) + .Slice(Parameters.Count - optionalParamCount)) { - ASParameter parameter = Parameters[i]; parameter.IsOptional = true; - parameter.ValueIndex = input.ReadInt30(); + parameter.ValueIndex = input.ReadEncodedInt(); parameter.ValueKind = (ConstantKind)input.ReadByte(); } } if (Flags.HasFlag(MethodFlags.HasParamNames)) { - for (int i = 0; i < Parameters.Count; i++) + foreach (var parameter in Parameters) + { + parameter.NameIndex = input.ReadEncodedInt(); + } + } + } + + public int GetSize() + { + int size = 0; + size += SpanFlashWriter.GetEncodedIntSize(Parameters.Count); + size += SpanFlashWriter.GetEncodedIntSize(ReturnTypeIndex); + + int optionalParamCount = 0; + if (Parameters.Count > 0) + { + foreach (var parameter in Parameters) + { + size += SpanFlashWriter.GetEncodedIntSize(parameter.TypeIndex); + + // One named parameter is enough to attain this flag. + if (!string.IsNullOrWhiteSpace(parameter.Name)) + { + Flags |= MethodFlags.HasParamNames; + } + + // Just one optional parameter is enough to attain this flag. + if (parameter.IsOptional) + { + optionalParamCount++; + Flags |= MethodFlags.HasOptional; + } + } + } + + size += SpanFlashWriter.GetEncodedIntSize(NameIndex); + size += sizeof(byte); + if (Flags.HasFlag(MethodFlags.HasOptional)) + { + size += SpanFlashWriter.GetEncodedIntSize(optionalParamCount); + foreach (var parameter in CollectionsMarshal.AsSpan(Parameters) + .Slice(Parameters.Count - optionalParamCount)) + { + size += SpanFlashWriter.GetEncodedIntSize(parameter.ValueIndex); + } + size += optionalParamCount * sizeof(byte); + } + + if (Flags.HasFlag(MethodFlags.HasParamNames)) + { + foreach (var parameter in Parameters) + { + size += SpanFlashWriter.GetEncodedIntSize(parameter.NameIndex); + } + } + return size; + } + public void WriteTo(ref SpanFlashWriter output) + { + output.WriteEncodedInt(Parameters.Count); + output.WriteEncodedInt(ReturnTypeIndex); + + // TODO: This logic is pretty fragile. + // I think we should make Flags a getter-only and manage/validate the state some other way. + int optionalParamCount = 0; + if (Parameters.Count > 0) + { + foreach (var parameter in Parameters) + { + output.WriteEncodedInt(parameter.TypeIndex); + + // One named parameter is enough to attain this flag. + if (!string.IsNullOrWhiteSpace(parameter.Name)) + { + Flags |= MethodFlags.HasParamNames; + } + + // Just one optional parameter is enough to attain this flag. + if (parameter.IsOptional) + { + optionalParamCount++; + Flags |= MethodFlags.HasOptional; + } + } + } + + output.WriteEncodedInt(NameIndex); + output.Write((byte)Flags); + if (Flags.HasFlag(MethodFlags.HasOptional)) + { + output.WriteEncodedInt(optionalParamCount); + foreach (var parameter in CollectionsMarshal.AsSpan(Parameters) + .Slice(Parameters.Count - optionalParamCount)) + { + output.WriteEncodedInt(parameter.ValueIndex); + output.Write((byte)parameter.ValueKind); + } + } + + if (Flags.HasFlag(MethodFlags.HasParamNames)) + { + foreach (var parameter in Parameters) { - ASParameter parameter = Parameters[i]; - parameter.NameIndex = input.ReadInt30(); + output.WriteEncodedInt(parameter.NameIndex); } } } - public override string ToAS3() + public string ToAS3() { var builder = new StringBuilder(); @@ -133,61 +233,5 @@ public override string ToAS3() return builder.ToString(); } - public override void WriteTo(FlashWriter output) - { - output.WriteInt30(Parameters.Count); - output.WriteInt30(ReturnTypeIndex); - - int optionalParamCount = 0; - int optionalParamStartIndex = (Parameters.Count - 1); - if (Parameters.Count > 0) - { - // This flag will be removed if at least a single parameter has no name assigned. - Flags |= MethodFlags.HasParamNames; - for (int i = 0; i < Parameters.Count; i++) - { - ASParameter parameter = Parameters[i]; - output.WriteInt30(parameter.TypeIndex); - - // This flag should only be present when all parameters are assigned a Name. - if (string.IsNullOrWhiteSpace(parameter.Name)) - { - Flags &= ~MethodFlags.HasParamNames; - } - - // Just one optional parameter is enough to attain this flag. - if (parameter.IsOptional) - { - if (i < optionalParamStartIndex) - { - optionalParamStartIndex = i; - } - optionalParamCount++; - Flags |= MethodFlags.HasOptional; - } - } - } - - output.WriteInt30(NameIndex); - output.Write((byte)Flags); - if (Flags.HasFlag(MethodFlags.HasOptional)) - { - output.WriteInt30(optionalParamCount); - for (int i = optionalParamStartIndex; i < Parameters.Count; i++) - { - ASParameter parameter = Parameters[i]; - output.WriteInt30(parameter.ValueIndex); - output.Write((byte)parameter.ValueKind); - } - } - - if (Flags.HasFlag(MethodFlags.HasParamNames)) - { - for (int i = 0; i < Parameters.Count; i++) - { - ASParameter parameter = Parameters[i]; - output.WriteInt30(parameter.NameIndex); - } - } - } + public override string ToString() => ToAS3(); } \ No newline at end of file diff --git a/Flazzy/ABC/ASMethodBody.cs b/Flazzy/ABC/ASMethodBody.cs index 4a94fae..bf4e823 100644 --- a/Flazzy/ABC/ASMethodBody.cs +++ b/Flazzy/ABC/ASMethodBody.cs @@ -17,58 +17,75 @@ public class ASMethodBody : ASContainer public List Exceptions { get; } public override ASMultiname QName => Method.Trait?.QName; - protected override string DebuggerDisplay => $"LocalCount: {LocalCount:n0}, MaxStack: {MaxStack:n0}"; public ASMethodBody(ABCFile abc) : base(abc) { Exceptions = new List(); } - public ASMethodBody(ABCFile abc, FlashReader input) + public ASMethodBody(ABCFile abc, ref SpanFlashReader input) : this(abc) { - MethodIndex = input.ReadInt30(); + MethodIndex = input.ReadEncodedInt(); Method.Body = this; - MaxStack = input.ReadInt30(); - LocalCount = input.ReadInt30(); - InitialScopeDepth = input.ReadInt30(); - MaxScopeDepth = input.ReadInt30(); + MaxStack = input.ReadEncodedInt(); + LocalCount = input.ReadEncodedInt(); + InitialScopeDepth = input.ReadEncodedInt(); + MaxScopeDepth = input.ReadEncodedInt(); - int codeLength = input.ReadInt30(); - Code = input.ReadBytes(codeLength); + int codeLength = input.ReadEncodedInt(); + Code = input.ReadBytes(codeLength).ToArray(); - Exceptions.Capacity = input.ReadInt30(); + Exceptions.Capacity = input.ReadEncodedInt(); for (int i = 0; i < Exceptions.Capacity; i++) { - var exception = new ASException(abc, input); - Exceptions.Add(exception); + Exceptions.Add(new ASException(abc, ref input)); } - PopulateTraits(input); + PopulateTraits(ref input); } - public ASCode ParseCode() + public ASCode ParseCode() => new(ABC, this); + + public override int GetSize() { - return new ASCode(ABC, this); - } + int size = 0; + size += SpanFlashWriter.GetEncodedIntSize(MethodIndex); + size += SpanFlashWriter.GetEncodedIntSize(MaxStack); + size += SpanFlashWriter.GetEncodedIntSize(LocalCount); + size += SpanFlashWriter.GetEncodedIntSize(InitialScopeDepth); + size += SpanFlashWriter.GetEncodedIntSize(MaxScopeDepth); + + size += SpanFlashWriter.GetEncodedIntSize(Code.Length); + size += Code.Length; - public override void WriteTo(FlashWriter output) + size += SpanFlashWriter.GetEncodedIntSize(Exceptions.Count); + for (int i = 0; i < Exceptions.Count; i++) + { + size += Exceptions[i].GetSize(); + } + + size += base.GetSize(); + return size; + } + public override void WriteTo(ref SpanFlashWriter output) { - output.WriteInt30(MethodIndex); - output.WriteInt30(MaxStack); - output.WriteInt30(LocalCount); - output.WriteInt30(InitialScopeDepth); - output.WriteInt30(MaxScopeDepth); + output.WriteEncodedInt(MethodIndex); + output.WriteEncodedInt(MaxStack); + output.WriteEncodedInt(LocalCount); + output.WriteEncodedInt(InitialScopeDepth); + output.WriteEncodedInt(MaxScopeDepth); - output.WriteInt30(Code.Length); + output.WriteEncodedInt(Code.Length); output.Write(Code); - output.WriteInt30(Exceptions.Count); + output.WriteEncodedInt(Exceptions.Count); for (int i = 0; i < Exceptions.Count; i++) { - ASException exception = Exceptions[i]; - exception.WriteTo(output); + Exceptions[i].WriteTo(ref output); } - base.WriteTo(output); + base.WriteTo(ref output); } + + public override string ToString() => $"LocalCount: {LocalCount:n0}, MaxStack: {MaxStack:n0}"; } \ No newline at end of file diff --git a/Flazzy/ABC/ASMultiname.cs b/Flazzy/ABC/ASMultiname.cs index fac40f0..a3c0812 100644 --- a/Flazzy/ABC/ASMultiname.cs +++ b/Flazzy/ABC/ASMultiname.cs @@ -2,7 +2,7 @@ namespace Flazzy.ABC; -public sealed class ASMultiname : FlashItem, IEquatable, IPoolConstant, IQName, IRTQName, IMultiname, IMultinameL +public sealed class ASMultiname : IFlashItem, IEquatable, IPoolConstant, IQName, IRTQName, IMultiname, IMultinameL { public MultinameKind Kind { get; set; } public ASConstantPool Pool { get; init; } @@ -54,23 +54,13 @@ MultinameKind.RTQNameL or public ASNamespaceSet NamespaceSet => Pool.NamespaceSets[NamespaceSetIndex]; public List TypeIndices { get; } - protected override string DebuggerDisplay => $"{Kind}: \"{Namespace.Name}.{Name}\""; - - public static bool operator ==(ASMultiname left, ASMultiname right) - { - return EqualityComparer.Default.Equals(left, right); - } - public static bool operator !=(ASMultiname left, ASMultiname right) - { - return !(left == right); - } public ASMultiname(ASConstantPool pool) { Pool = pool; TypeIndices = new List(); } - public ASMultiname(ASConstantPool pool, FlashReader input) + public ASMultiname(ASConstantPool pool, ref SpanFlashReader input) : this(pool) { Kind = (MultinameKind)input.ReadByte(); @@ -79,15 +69,15 @@ public ASMultiname(ASConstantPool pool, FlashReader input) case MultinameKind.QName: case MultinameKind.QNameA: { - NamespaceIndex = input.ReadInt30(); - NameIndex = input.ReadInt30(); + NamespaceIndex = input.ReadEncodedInt(); + NameIndex = input.ReadEncodedInt(); break; } case MultinameKind.RTQName: case MultinameKind.RTQNameA: { - NameIndex = input.ReadInt30(); + NameIndex = input.ReadEncodedInt(); break; } @@ -101,25 +91,25 @@ public ASMultiname(ASConstantPool pool, FlashReader input) case MultinameKind.Multiname: case MultinameKind.MultinameA: { - NameIndex = input.ReadInt30(); - NamespaceSetIndex = input.ReadInt30(); + NameIndex = input.ReadEncodedInt(); + NamespaceSetIndex = input.ReadEncodedInt(); break; } case MultinameKind.MultinameL: case MultinameKind.MultinameLA: { - NamespaceSetIndex = input.ReadInt30(); + NamespaceSetIndex = input.ReadEncodedInt(); break; } case MultinameKind.TypeName: { - QNameIndex = input.ReadInt30(); - TypeIndices.Capacity = input.ReadInt30(); + QNameIndex = input.ReadEncodedInt(); + TypeIndices.Capacity = input.ReadEncodedInt(); for (int i = 0; i < TypeIndices.Capacity; i++) { - int typeIndex = input.ReadInt30(); + int typeIndex = input.ReadEncodedInt(); TypeIndices.Add(typeIndex); } break; @@ -134,7 +124,57 @@ public IEnumerable GetTypes() yield return Pool.Multinames[TypeIndices[i]]; } } - public override void WriteTo(FlashWriter output) + + public int GetSize() + { + int size = 0; + size += sizeof(byte); + switch (Kind) + { + case MultinameKind.QName: + case MultinameKind.QNameA: + { + size += SpanFlashWriter.GetEncodedIntSize(NamespaceIndex); + size += SpanFlashWriter.GetEncodedIntSize(NameIndex); + break; + } + + case MultinameKind.RTQName: + case MultinameKind.RTQNameA: + { + size += SpanFlashWriter.GetEncodedIntSize(NameIndex); + break; + } + + case MultinameKind.Multiname: + case MultinameKind.MultinameA: + { + size += SpanFlashWriter.GetEncodedIntSize(NameIndex); + size += SpanFlashWriter.GetEncodedIntSize(NamespaceSetIndex); + break; + } + + case MultinameKind.MultinameL: + case MultinameKind.MultinameLA: + { + size += SpanFlashWriter.GetEncodedIntSize(NamespaceSetIndex); + break; + } + + case MultinameKind.TypeName: + { + size += SpanFlashWriter.GetEncodedIntSize(QNameIndex); + size += SpanFlashWriter.GetEncodedIntSize(TypeIndices.Count); + for (int i = 0; i < TypeIndices.Count; i++) + { + size += SpanFlashWriter.GetEncodedIntSize(TypeIndices[i]); + } + break; + } + } + return size; + } + public void WriteTo(ref SpanFlashWriter output) { output.Write((byte)Kind); switch (Kind) @@ -142,15 +182,15 @@ public override void WriteTo(FlashWriter output) case MultinameKind.QName: case MultinameKind.QNameA: { - output.WriteInt30(NamespaceIndex); - output.WriteInt30(NameIndex); + output.WriteEncodedInt(NamespaceIndex); + output.WriteEncodedInt(NameIndex); break; } case MultinameKind.RTQName: case MultinameKind.RTQNameA: { - output.WriteInt30(NameIndex); + output.WriteEncodedInt(NameIndex); break; } @@ -164,26 +204,26 @@ public override void WriteTo(FlashWriter output) case MultinameKind.Multiname: case MultinameKind.MultinameA: { - output.WriteInt30(NameIndex); - output.WriteInt30(NamespaceSetIndex); + output.WriteEncodedInt(NameIndex); + output.WriteEncodedInt(NamespaceSetIndex); break; } case MultinameKind.MultinameL: case MultinameKind.MultinameLA: { - output.WriteInt30(NamespaceSetIndex); + output.WriteEncodedInt(NamespaceSetIndex); break; } case MultinameKind.TypeName: { - output.WriteInt30(QNameIndex); - output.WriteInt30(TypeIndices.Count); + output.WriteEncodedInt(QNameIndex); + output.WriteEncodedInt(TypeIndices.Count); for (int i = 0; i < TypeIndices.Count; i++) { int typeIndex = TypeIndices[i]; - output.WriteInt30(typeIndex); + output.WriteEncodedInt(typeIndex); } break; } @@ -227,7 +267,16 @@ public bool Equals(ASMultiname other) return true; } public override bool Equals(object obj) + => obj is ASMultiname multiname && Equals(multiname); + + public static bool operator ==(ASMultiname left, ASMultiname right) { - return Equals(obj as ASMultiname); + return EqualityComparer.Default.Equals(left, right); + } + public static bool operator !=(ASMultiname left, ASMultiname right) + { + return !(left == right); } + + public override string ToString() => $"{Kind}: \"{Namespace.Name}.{Name}\""; } \ No newline at end of file diff --git a/Flazzy/ABC/ASNamespace.cs b/Flazzy/ABC/ASNamespace.cs index ef78d2b..df361a1 100644 --- a/Flazzy/ABC/ASNamespace.cs +++ b/Flazzy/ABC/ASNamespace.cs @@ -1,19 +1,22 @@ -using Flazzy.IO; +using System.Diagnostics; + +using Flazzy.IO; namespace Flazzy.ABC; /// /// Represents a namespace in the bytecode. /// -public class ASNamespace : FlashItem, IEquatable, IPoolConstant +[DebuggerDisplay("{Kind}: \"{Name}\"")] +public class ASNamespace : IFlashItem, IEquatable, IPoolConstant { + public ASConstantPool Pool { get; init; } + /// /// Gets or sets the index of the string in representing the namespace name. /// public int NameIndex { get; set; } - public ASConstantPool Pool { get; init; } - /// /// Gets the name of the namespace. /// @@ -24,22 +27,11 @@ public class ASNamespace : FlashItem, IEquatable, IPoolConstant /// public NamespaceKind Kind { get; set; } - protected override string DebuggerDisplay => $"{Kind}: \"{Name}\""; - - public static bool operator ==(ASNamespace left, ASNamespace right) - { - return EqualityComparer.Default.Equals(left, right); - } - public static bool operator !=(ASNamespace left, ASNamespace right) - { - return !(left == right); - } - public ASNamespace(ASConstantPool pool) { Pool = pool; } - public ASNamespace(ASConstantPool pool, FlashReader input) + public ASNamespace(ASConstantPool pool, ref SpanFlashReader input) : this(pool) { Kind = (NamespaceKind)input.ReadByte(); @@ -47,7 +39,7 @@ public ASNamespace(ASConstantPool pool, FlashReader input) { throw new InvalidCastException($"Invalid namespace kind for value {Kind:0x00}."); } - NameIndex = input.ReadInt30(); + NameIndex = input.ReadEncodedInt(); } public string GetAS3Modifiers() => Kind switch @@ -58,16 +50,15 @@ public ASNamespace(ASConstantPool pool, FlashReader input) NamespaceKind.StaticProtected or NamespaceKind.Protected => "protected", _ => string.Empty, }; - public override void WriteTo(FlashWriter output) + + public int GetSize() => sizeof(byte) + SpanFlashWriter.GetEncodedIntSize(NameIndex); + public void WriteTo(ref SpanFlashWriter output) { output.Write((byte)Kind); - output.WriteInt30(NameIndex); + output.WriteEncodedInt(NameIndex); } - public override int GetHashCode() - { - return HashCode.Combine(Name, Kind); - } + public override int GetHashCode() => HashCode.Combine(Name, Kind); public bool Equals(ASNamespace other) { if (other == null) return false; @@ -82,4 +73,15 @@ public override bool Equals(object obj) { return Equals(obj as ASNamespace); } + + public static bool operator ==(ASNamespace left, ASNamespace right) + { + return EqualityComparer.Default.Equals(left, right); + } + public static bool operator !=(ASNamespace left, ASNamespace right) + { + return !(left == right); + } + + public override string ToString() => $"{Kind}: \"{Name}\""; } \ No newline at end of file diff --git a/Flazzy/ABC/ASNamespaceSet.cs b/Flazzy/ABC/ASNamespaceSet.cs index 72e62b7..b58999b 100644 --- a/Flazzy/ABC/ASNamespaceSet.cs +++ b/Flazzy/ABC/ASNamespaceSet.cs @@ -2,35 +2,23 @@ namespace Flazzy.ABC; -public class ASNamespaceSet : FlashItem, IEquatable, IPoolConstant +public class ASNamespaceSet : IFlashItem, IEquatable, IPoolConstant { public ASConstantPool Pool { get; init; } public List NamespaceIndices { get; } - protected override string DebuggerDisplay => $"Namespaces: {NamespaceIndices.Count:n0}"; - - public static bool operator ==(ASNamespaceSet left, ASNamespaceSet right) - { - return EqualityComparer.Default.Equals(left, right); - } - public static bool operator !=(ASNamespaceSet left, ASNamespaceSet right) - { - return !(left == right); - } - public ASNamespaceSet(ASConstantPool pool) { Pool = pool; NamespaceIndices = new List(); } - public ASNamespaceSet(ASConstantPool pool, FlashReader input) + public ASNamespaceSet(ASConstantPool pool, ref SpanFlashReader input) : this(pool) { - NamespaceIndices.Capacity = input.ReadInt30(); + NamespaceIndices.Capacity = input.ReadEncodedInt(); for (int i = 0; i < NamespaceIndices.Capacity; i++) { - int namespaceIndex = input.ReadInt30(); - NamespaceIndices.Add(namespaceIndex); + NamespaceIndices.Add(input.ReadEncodedInt()); } } @@ -68,14 +56,34 @@ public IEnumerable GetNamespaces() yield return Pool.Namespaces[NamespaceIndices[i]]; } } - public override void WriteTo(FlashWriter output) + + public int GetSize() { - output.WriteInt30(NamespaceIndices.Count); + int size = 0; + size += SpanFlashWriter.GetEncodedIntSize(NamespaceIndices.Count); for (int i = 0; i < NamespaceIndices.Count; i++) { - int namespaceIndex = NamespaceIndices[i]; - output.WriteInt30(namespaceIndex); + size += SpanFlashWriter.GetEncodedIntSize(NamespaceIndices[i]); } + return size; + } + public void WriteTo(ref SpanFlashWriter output) + { + output.WriteEncodedInt(NamespaceIndices.Count); + for (int i = 0; i < NamespaceIndices.Count; i++) + { + output.WriteEncodedInt(NamespaceIndices[i]); + } + } + + public static bool operator ==(ASNamespaceSet left, ASNamespaceSet right) + { + return EqualityComparer.Default.Equals(left, right); + } + public static bool operator !=(ASNamespaceSet left, ASNamespaceSet right) + { + return !(left == right); } + public override string ToString() => $"Namespaces: {NamespaceIndices.Count:n0}"; } \ No newline at end of file diff --git a/Flazzy/ABC/ASParameter.cs b/Flazzy/ABC/ASParameter.cs index fefc730..8c8f11f 100644 --- a/Flazzy/ABC/ASParameter.cs +++ b/Flazzy/ABC/ASParameter.cs @@ -4,7 +4,7 @@ namespace Flazzy.ABC; [DebuggerDisplay("{ToString(),nq}")] -public class ASParameter +public class ASParameter : IAS3Item { private readonly ASMethod _method; @@ -25,7 +25,7 @@ public ASParameter(ASMethod method) _method = method; } - public override string ToString() + public string ToAS3() { var builder = new StringBuilder(); Append(builder); @@ -83,4 +83,5 @@ internal void Append(StringBuilder builder, int? index = null) } } } + public override string ToString() => ToAS3(); } \ No newline at end of file diff --git a/Flazzy/ABC/ASScript.cs b/Flazzy/ABC/ASScript.cs index b1ac30a..749ed3d 100644 --- a/Flazzy/ABC/ASScript.cs +++ b/Flazzy/ABC/ASScript.cs @@ -12,16 +12,20 @@ public class ASScript : ASContainer // TODO: Check QName usages public ASScript(ABCFile abc) : base(abc) { } - public ASScript(ABCFile abc, FlashReader input) + public ASScript(ABCFile abc, ref SpanFlashReader input) : base(abc) { - InitializerIndex = input.ReadInt30(); - PopulateTraits(input); + InitializerIndex = input.ReadEncodedInt(); + PopulateTraits(ref input); } - public override void WriteTo(FlashWriter output) + public override int GetSize() { - output.WriteInt30(InitializerIndex); - base.WriteTo(output); + return SpanFlashWriter.GetEncodedIntSize(InitializerIndex) + base.GetSize(); + } + public override void WriteTo(ref SpanFlashWriter output) + { + output.WriteEncodedInt(InitializerIndex); + base.WriteTo(ref output); } } \ No newline at end of file diff --git a/Flazzy/ABC/ASTrait.cs b/Flazzy/ABC/ASTrait.cs index 0c8079f..626e6d3 100644 --- a/Flazzy/ABC/ASTrait.cs +++ b/Flazzy/ABC/ASTrait.cs @@ -4,59 +4,32 @@ namespace Flazzy.ABC; -public class ASTrait : AS3Item, IMethodGSTrait, ISlotConstantTrait, IClassTrait, IFunctionTrait +public class ASTrait : IFlashItem, IAS3Item, IMethodGSTrait, ISlotConstantTrait, IClassTrait, IFunctionTrait { + public ABCFile ABC { get; } + public int QNameIndex { get; set; } public ASMultiname QName => ABC.Pool.Multinames[QNameIndex]; - public ASMultiname Type - { - get - { - if (Kind == TraitKind.Slot || - Kind == TraitKind.Constant) - { - return ABC.Pool.Multinames[TypeIndex]; - } - return null; - } - } public int TypeIndex { get; set; } - - public ASMethod Method + public ASMultiname Type => Kind switch { - get - { - if (Kind == TraitKind.Method || - Kind == TraitKind.Getter || - Kind == TraitKind.Setter) - { - return ABC.Methods[MethodIndex]; - } - return null; - } - } - public int MethodIndex { get; set; } + TraitKind.Slot or TraitKind.Constant => ABC.Pool.Multinames[TypeIndex], + _ => null + }; - public ASMethod Function + public int MethodIndex { get; set; } + public ASMethod Method => Kind switch { - get - { - if (Kind != TraitKind.Function) return null; - return ABC.Methods[FunctionIndex]; - } - } + TraitKind.Method or TraitKind.Getter or TraitKind.Setter => ABC.Methods[MethodIndex], + _ => null + }; + public int FunctionIndex { get; set; } + public ASMethod Function => Kind is TraitKind.Function ? ABC.Methods[FunctionIndex] : null; - public ASClass Class - { - get - { - if (Kind != TraitKind.Class) return null; - return ABC.Classes[ClassIndex]; - } - } public int ClassIndex { get; set; } + public ASClass Class => Kind is TraitKind.Class ? ABC.Classes[ClassIndex] : null; public int ValueIndex { get; set; } public object Value => ABC.Pool.GetConstant(ValueKind, ValueIndex); @@ -71,30 +44,28 @@ public ASClass Class public TraitKind Kind { get; set; } public TraitAttributes Attributes { get; set; } - protected override string DebuggerDisplay => (Kind + ": " + QName.Name); - public ASTrait(ABCFile abc) - : base(abc) { + ABC = abc; MetadataIndices = new List(); } - public ASTrait(ABCFile abc, FlashReader input) + public ASTrait(ABCFile abc, ref SpanFlashReader input) : this(abc) { - QNameIndex = input.ReadInt30(); + QNameIndex = input.ReadEncodedInt(); byte bitContainer = input.ReadByte(); Kind = (TraitKind)(bitContainer & 0x0F); Attributes = (TraitAttributes)(bitContainer >> 4); - Id = input.ReadInt30(); + Id = input.ReadEncodedInt(); switch (Kind) { case TraitKind.Slot: case TraitKind.Constant: { - TypeIndex = input.ReadInt30(); - ValueIndex = input.ReadInt30(); + TypeIndex = input.ReadEncodedInt(); + ValueIndex = input.ReadEncodedInt(); if (ValueIndex != 0) { ValueKind = (ConstantKind)input.ReadByte(); @@ -106,31 +77,30 @@ public ASTrait(ABCFile abc, FlashReader input) case TraitKind.Getter: case TraitKind.Setter: { - MethodIndex = input.ReadInt30(); + MethodIndex = input.ReadEncodedInt(); Method.Trait = this; break; } case TraitKind.Class: { - ClassIndex = input.ReadInt30(); + ClassIndex = input.ReadEncodedInt(); break; } case TraitKind.Function: { - FunctionIndex = input.ReadInt30(); + FunctionIndex = input.ReadEncodedInt(); break; } } if (Attributes.HasFlag(TraitAttributes.Metadata)) { - MetadataIndices.Capacity = input.ReadInt30(); + MetadataIndices.Capacity = input.ReadEncodedInt(); for (int i = 0; i < MetadataIndices.Capacity; i++) { - int metadatumIndex = input.ReadInt30(); - MetadataIndices.Add(metadatumIndex); + MetadataIndices.Add(input.ReadEncodedInt()); } } } @@ -145,74 +115,117 @@ public IEnumerable GetMetadata() } } - public override string ToAS3() + public string ToAS3() { + if (Kind is TraitKind.Constant or TraitKind.Slot) + { + StringBuilder builder = new(); + if (Attributes.HasFlag(TraitAttributes.Override)) + { + builder.Append("override "); + } + var modifiers = QName.Namespace.GetAS3Modifiers(); + if (!string.IsNullOrEmpty(modifiers)) + { + builder.Append(modifiers); + builder.Append(' '); + } + if (IsStatic) + { + builder.Append("static "); + } + builder.Append(Kind == TraitKind.Constant ? "const " : "var "); + builder.Append(QName.Name); + if (Type != null) + { + builder.Append(':'); + builder.Append(Type.Name ?? Type.QName.Name); + if (Type.Kind == MultinameKind.TypeName) + { + builder.Append(".<"); + builder.Append(string.Join(',', Type.TypeIndices.Select(i => ABC.Pool.Multinames[i].Name))); + builder.Append('>'); + } + } + if (!string.IsNullOrEmpty(Value?.ToString())) + { + builder.Append(" = "); + if (ValueKind == ConstantKind.String) + { + builder.Append('"'); + builder.Append(Value.ToString()); + builder.Append('"'); + } + else builder.Append(Value.ToString().ToLower()); + } + builder.Append(';'); + return builder.ToString(); + } + else return string.Empty; + } + public int GetSize() + { + int size = 0; + size += SpanFlashWriter.GetEncodedIntSize(QNameIndex); + size += sizeof(byte); + size += SpanFlashWriter.GetEncodedIntSize(Id); switch (Kind) { - case TraitKind.Constant: case TraitKind.Slot: + case TraitKind.Constant: { - StringBuilder builder = new(); - if (Attributes.HasFlag(TraitAttributes.Override)) - { - builder.Append("override "); - } - var modifiers = QName.Namespace.GetAS3Modifiers(); - if (!string.IsNullOrEmpty(modifiers)) - { - builder.Append(modifiers); - builder.Append(' '); - } - if (IsStatic) - { - builder.Append("static "); - } - builder.Append(Kind == TraitKind.Constant ? "const " : "var "); - builder.Append(QName.Name); - if (Type != null) - { - builder.Append(':'); - builder.Append(Type.Name ?? Type.QName.Name); - if (Type.Kind == MultinameKind.TypeName) - { - builder.Append(".<"); - builder.Append(string.Join(',', Type.TypeIndices.Select(i => ABC.Pool.Multinames[i].Name))); - builder.Append('>'); - } - } - if (!string.IsNullOrEmpty(Value?.ToString())) + size += SpanFlashWriter.GetEncodedIntSize(TypeIndex); + size += SpanFlashWriter.GetEncodedIntSize(ValueIndex); + if (ValueIndex != 0) { - builder.Append(" = "); - if (ValueKind == ConstantKind.String) - { - builder.Append('"'); - builder.Append(Value.ToString()); - builder.Append('"'); - } - else builder.Append(Value.ToString().ToLower()); + size += sizeof(byte); } - builder.Append(';'); - return builder.ToString(); + break; + } + + case TraitKind.Method: + case TraitKind.Getter: + case TraitKind.Setter: + { + size += SpanFlashWriter.GetEncodedIntSize(MethodIndex); + break; + } + + case TraitKind.Class: + { + size += SpanFlashWriter.GetEncodedIntSize(ClassIndex); + break; + } + + case TraitKind.Function: + { + size += SpanFlashWriter.GetEncodedIntSize(FunctionIndex); + break; } } - return string.Empty; - } - public override void WriteTo(FlashWriter output) + if (Attributes.HasFlag(TraitAttributes.Metadata)) + { + size += SpanFlashWriter.GetEncodedIntSize(MetadataIndices.Count); + for (int i = 0; i < MetadataIndices.Count; i++) + { + size += SpanFlashWriter.GetEncodedIntSize(MetadataIndices[i]); + } + } + return size; + } + public void WriteTo(ref SpanFlashWriter output) { - var bitContainer = (byte)( - ((byte)Attributes << 4) + (byte)Kind); - - output.WriteInt30(QNameIndex); - output.Write(bitContainer); - output.WriteInt30(Id); + output.WriteEncodedInt(QNameIndex); + output.Write((byte)(((byte)Attributes << 4) + (byte)Kind)); + output.WriteEncodedInt(Id); switch (Kind) { case TraitKind.Slot: case TraitKind.Constant: { - output.WriteInt30(TypeIndex); - output.WriteInt30(ValueIndex); + output.WriteEncodedInt(TypeIndex); + output.WriteEncodedInt(ValueIndex); if (ValueIndex != 0) { output.Write((byte)ValueKind); @@ -224,31 +237,33 @@ public override void WriteTo(FlashWriter output) case TraitKind.Getter: case TraitKind.Setter: { - output.WriteInt30(MethodIndex); + output.WriteEncodedInt(MethodIndex); break; } case TraitKind.Class: { - output.WriteInt30(ClassIndex); + output.WriteEncodedInt(ClassIndex); break; } case TraitKind.Function: { - output.WriteInt30(FunctionIndex); + output.WriteEncodedInt(FunctionIndex); break; } } if (Attributes.HasFlag(TraitAttributes.Metadata)) { - output.WriteInt30(MetadataIndices.Count); + output.WriteEncodedInt(MetadataIndices.Count); for (int i = 0; i < MetadataIndices.Count; i++) { int metadatumIndex = MetadataIndices[i]; - output.WriteInt30(metadatumIndex); + output.WriteEncodedInt(metadatumIndex); } } } + + public override string ToString() => (Kind + ": " + QName.Name); } \ No newline at end of file diff --git a/Flazzy/ABC/AVM2/ASCode.cs b/Flazzy/ABC/AVM2/ASCode.cs index 8f46329..f1f9275 100644 --- a/Flazzy/ABC/AVM2/ASCode.cs +++ b/Flazzy/ABC/AVM2/ASCode.cs @@ -1,8 +1,9 @@ -using System.Collections; +using System.Buffers; +using System.Collections; using System.Diagnostics; -using Flazzy.ABC.AVM2.Instructions; using Flazzy.IO; +using Flazzy.ABC.AVM2.Instructions; namespace Flazzy.ABC.AVM2; @@ -674,7 +675,7 @@ private void Rewrite(FlashWriter output, ASInstruction instruction, long positio long currentPosition = output.Position; output.Position = position; - instruction.WriteTo(output); + instruction.WriteTo(output.BaseStream); output.Position = currentPosition; } private bool IsRelyingOnLocals(ASInstruction instruction, Dictionary> conversions) => conversions.GetValueOrDefault(instruction) != null; @@ -823,11 +824,13 @@ public override void WriteTo(FlashWriter output) var marks = new Dictionary(); var sharedExits = new Dictionary>(); var switchCases = new Dictionary(); + foreach (ASInstruction instruction in _instructions) { long previousPosition = output.Position; marks.Add(instruction, previousPosition); - instruction.WriteTo(output); + + instruction.WriteTo(output.BaseStream); if (sharedExits.TryGetValue(instruction, out List jumpers)) { diff --git a/Flazzy/ABC/AVM2/Instructions/ASInstruction.cs b/Flazzy/ABC/AVM2/Instructions/ASInstruction.cs index 5afe2f1..3e24c92 100644 --- a/Flazzy/ABC/AVM2/Instructions/ASInstruction.cs +++ b/Flazzy/ABC/AVM2/Instructions/ASInstruction.cs @@ -1,10 +1,8 @@ -using System.Runtime.CompilerServices; - -using Flazzy.IO; +using Flazzy.IO; namespace Flazzy.ABC.AVM2.Instructions; -public abstract class ASInstruction : FlashItem, ICloneable +public abstract class ASInstruction : IFlashItem, ICloneable { public OPCode OP { get; } protected ABCFile ABC { get; } @@ -35,18 +33,6 @@ public void WriteTo(ref SpanFlashWriter output) WriteValuesTo(ref output); } - [SkipLocalsInit] - // Temporary hack stackalloc until rest of the IO is "spanified". - public override void WriteTo(FlashWriter output) - { - Span buffer = stackalloc byte[64].Slice(0, GetSize()); - - var outputWriter = new SpanFlashWriter(buffer); - WriteTo(ref outputWriter); - - output.Write(buffer); - } - protected static int ResolveMultinamePops(ASMultiname multiname) { int popCount = 0; diff --git a/Flazzy/ABC/Enums/ConstantKind.cs b/Flazzy/ABC/Enums/ConstantKind.cs index 92b8967..36d2229 100644 --- a/Flazzy/ABC/Enums/ConstantKind.cs +++ b/Flazzy/ABC/Enums/ConstantKind.cs @@ -1,6 +1,6 @@ namespace Flazzy.ABC; -public enum ConstantKind +public enum ConstantKind : byte { Null = 0x0C, Undefined = 0x00, diff --git a/Flazzy/ABC/Enums/MultinameKind.cs b/Flazzy/ABC/Enums/MultinameKind.cs index e473f48..9c83606 100644 --- a/Flazzy/ABC/Enums/MultinameKind.cs +++ b/Flazzy/ABC/Enums/MultinameKind.cs @@ -1,6 +1,6 @@ namespace Flazzy.ABC; -public enum MultinameKind +public enum MultinameKind : byte { QName = 0x07, QNameA = 0x0D, diff --git a/Flazzy/ABC/Enums/TraitAttributes.cs b/Flazzy/ABC/Enums/TraitAttributes.cs index 3a8d735..baf05bf 100644 --- a/Flazzy/ABC/Enums/TraitAttributes.cs +++ b/Flazzy/ABC/Enums/TraitAttributes.cs @@ -1,7 +1,7 @@ namespace Flazzy.ABC; [Flags] -public enum TraitAttributes +public enum TraitAttributes : byte { None = 0x00, Final = 0x01, diff --git a/Flazzy/ABC/Enums/TraitKind.cs b/Flazzy/ABC/Enums/TraitKind.cs index 42d3d70..dbe39b9 100644 --- a/Flazzy/ABC/Enums/TraitKind.cs +++ b/Flazzy/ABC/Enums/TraitKind.cs @@ -1,6 +1,6 @@ namespace Flazzy.ABC; -public enum TraitKind +public enum TraitKind : byte { Slot = 0, Method = 1, diff --git a/Flazzy/ABC/IPoolConstant.cs b/Flazzy/ABC/IPoolConstant.cs index 2f02fe1..b1e3863 100644 --- a/Flazzy/ABC/IPoolConstant.cs +++ b/Flazzy/ABC/IPoolConstant.cs @@ -2,5 +2,5 @@ public interface IPoolConstant { - public ASConstantPool Pool { get; init; } + ASConstantPool Pool { get; init; } } \ No newline at end of file diff --git a/Flazzy/Extensions/IFlashItemExtensions.cs b/Flazzy/Extensions/IFlashItemExtensions.cs new file mode 100644 index 0000000..67bab3f --- /dev/null +++ b/Flazzy/Extensions/IFlashItemExtensions.cs @@ -0,0 +1,55 @@ +using System.Buffers; +using System.Runtime.CompilerServices; + +using Flazzy.IO; + +namespace Flazzy; + +public static class IFlashItemExtensions +{ + [SkipLocalsInit] + public static void WriteTo(this IFlashItem item, Stream output) + { + const int StackallocThreshold = 512; + + int size = item.GetSize(); + + byte[] rentedBuffer = null; + Span buffer = size <= StackallocThreshold + ? stackalloc byte[StackallocThreshold] : + (rentedBuffer = ArrayPool.Shared.Rent(size)); + + buffer = buffer.Slice(0, size); + buffer.Clear(); + + var writer = new SpanFlashWriter(buffer); + item.WriteTo(ref writer); + + try + { + output.Write(buffer); + } + finally + { + if (rentedBuffer is not null) + ArrayPool.Shared.Return(rentedBuffer); + } + } + public static void WriteTo(this IFlashItem item, IBufferWriter output) + { + int size = item.GetSize(); + var writer = new SpanFlashWriter(output.GetSpan(size).Slice(0, size)); + item.WriteTo(ref writer); + + output.Advance(size); + } + + public static byte[] ToArray(this IFlashItem item) + { + byte[] buffer = new byte[item.GetSize()]; + var writer = new SpanFlashWriter(buffer); + item.WriteTo(ref writer); + + return buffer; + } +} diff --git a/Flazzy/FlashItem.cs b/Flazzy/FlashItem.cs index 3984511..c4abae8 100644 --- a/Flazzy/FlashItem.cs +++ b/Flazzy/FlashItem.cs @@ -4,6 +4,7 @@ namespace Flazzy; +// TODO: Remove abstract FlashItem and move to composition with IFlashItem [DebuggerDisplay("{DebuggerDisplay,nq}")] public abstract class FlashItem { @@ -20,4 +21,13 @@ public byte[] ToArray() } } public abstract void WriteTo(FlashWriter output); +} + +/// +/// Represents a serializable structure in the Shockwave Flash file. +/// +public interface IFlashItem +{ + int GetSize(); + void WriteTo(ref SpanFlashWriter output); } \ No newline at end of file diff --git a/Flazzy/IO/FlashReader.cs b/Flazzy/IO/FlashReader.cs index 78fcaa2..7ef2b26 100644 --- a/Flazzy/IO/FlashReader.cs +++ b/Flazzy/IO/FlashReader.cs @@ -47,35 +47,6 @@ public uint ReadUInt24() return value; } - public int ReadInt30() - { - int result = ReadByte(); - if ((result & 0x00000080) == 0) - { - return result; - } - result = (result & 0x0000007f) | (ReadByte()) << 7; - if ((result & 0x00004000) == 0) - { - return result; - } - result = (result & 0x00003fff) | (ReadByte()) << 14; - if ((result & 0x00200000) == 0) - { - return result; - } - result = (result & 0x001fffff) | (ReadByte()) << 21; - if ((result & 0x10000000) == 0) - { - return result; - } - return (result & 0x0fffffff) | (ReadByte()) << 28; - } - public uint ReadUInt30() - { - return (uint)ReadInt30(); - } - public int ReadUB(int bitCount) { int result = 0; diff --git a/Flazzy/IO/FlashWriter.cs b/Flazzy/IO/FlashWriter.cs index cf570d9..7483b83 100644 --- a/Flazzy/IO/FlashWriter.cs +++ b/Flazzy/IO/FlashWriter.cs @@ -47,34 +47,11 @@ public void WriteItem(FlashItem item) { item.WriteTo(this); } - - public void WriteInt30(int value) + public void WriteItem(IFlashItem item) { - Align(); - Write7BitEncodedInt(value); + item.WriteTo(BaseStream); } - public void WriteUInt24(uint value) - { - Align(); - - var byteValue = (byte)(value & 0xff); - Write(byteValue); - - value >>= 8; - byteValue = (byte)(value & 0xff); - Write(byteValue); - - value >>= 8; - - byteValue = (byte)(value & 0xff); - Write(byteValue); - } - public void WriteUInt30(uint value) - { - Align(); - Write7BitEncodedInt((int)value); - } public void WriteBits(int maxBits, long value) { for (int i = 0; i < maxBits; i++) diff --git a/Flazzy/IO/SpanFlashWriter.cs b/Flazzy/IO/SpanFlashWriter.cs index dba647f..8763c3b 100644 --- a/Flazzy/IO/SpanFlashWriter.cs +++ b/Flazzy/IO/SpanFlashWriter.cs @@ -92,23 +92,9 @@ internal void WriteDoubleArray(ReadOnlySpan values) } else { - // Execute bounds-check. - if (_data.Length - Position < values.Length * sizeof(double)) - ThrowOutOfRange(); - - ref byte destinationPtr = ref Unsafe.Add( - ref MemoryMarshal.GetReference(_data), (nint)(uint)Position); - - for (int i = 0; i < values.Length; i++) - { - // Reverse the binary representation of the double and blit it to the output - Unsafe.WriteUnaligned(ref destinationPtr, - BinaryPrimitives.ReverseEndianness( - BitConverter.DoubleToUInt64Bits(values[i]))); - - // Bump the destination pointer by element size. - destinationPtr = Unsafe.Add(ref destinationPtr, sizeof(double)); - } + BinaryPrimitives.ReverseEndianness( + source: MemoryMarshal.Cast(values), + destination: MemoryMarshal.Cast(_data)); } Position += values.Length * sizeof(double);