Code/Wasm/TableSection.cs
using System.Collections.Generic;
using System.IO;
using System.Linq;
using WasmBox.Wasm.Binary;

namespace WasmBox.Wasm {
    /// <summary>
    /// Represents a table section in a WebAssembly file.
    /// </summary>
    public sealed class TableSection : Section {
        /// <summary>
        /// Creates an empty table section.
        /// </summary>
        public TableSection()
            : this(Enumerable.Empty<TableType>()) {
        }

        /// <summary>
        /// Creates a table section from the given list of table descriptions.
        /// </summary>
        /// <param name="tables">The list of table descriptions in this type section.</param>
        public TableSection(IEnumerable<TableType> tables)
            : this(tables, new byte[0]) {
        }

        /// <summary>
        /// Creates a type section from the given list of table descriptions and an additional payload.
        /// </summary>
        /// <param name="tables">The list of table descriptions in this type section.</param>
        /// <param name="extraPayload">The additional payload for this section, as an array of bytes.</param>
        public TableSection(IEnumerable<TableType> tables, byte[] extraPayload) {
            this.Tables = new List<TableType>(tables);
            this.ExtraPayload = extraPayload;
        }

        /// <summary>
        /// Gets this table section's list of tables.
        /// </summary>
        /// <returns>The list of tables in this table section.</returns>
        public List<TableType> Tables { get; private set; }

        /// <summary>
        /// This type section's additional payload.
        /// </summary>
        /// <returns>The additional payload, as an array of bytes.</returns>
        public byte[] ExtraPayload { get; set; }

        /// <inheritdoc/>
        public override SectionName Name => new SectionName(SectionCode.Table);

        /// <inheritdoc/>
        public override void WritePayloadTo(BinaryWasmWriter writer) {
            writer.WriteVarUInt32((uint)Tables.Count);
            foreach (var type in Tables)
                type.WriteTo(writer);

            writer.Writer.Write(ExtraPayload);
        }

        /// <inheritdoc/>
        public override void Dump(TextWriter writer) {
            writer.Write(Name.ToString());
            writer.Write("; number of entries: ");
            writer.Write(Tables.Count);
            writer.WriteLine();
            for (int i = 0; i < Tables.Count; i++) {
                writer.Write("#");
                writer.Write(i);
                writer.Write(" -> ");
                Tables[i].Dump(writer);
                writer.WriteLine();
            }
            if (ExtraPayload.Length > 0) {
                writer.Write("Extra payload size: ");
                writer.Write(ExtraPayload.Length);
                writer.WriteLine();
                DumpHelpers.DumpBytes(ExtraPayload, writer);
                writer.WriteLine();
            }
        }

        /// <summary>
        /// Reads a table section's payload from the given binary WebAssembly reader.
        /// </summary>
        /// <param name="header">The type section's header.</param>
        /// <param name="reader">A reader for a binary WebAssembly file.</param>
        /// <returns>A parsed type section.</returns>
        public static TableSection ReadSectionPayload(SectionHeader header, BinaryWasmReader reader) {
            long initPos = reader.Position;
            uint typeCount = reader.ReadVarUInt32();
            var tables = new List<TableType>((int)typeCount);
            for (uint i = 0; i < typeCount; i++) {
                tables.Add(TableType.ReadFrom(reader));
            }
            var extraBytes = reader.ReadRemainingPayload(initPos, header);
            return new TableSection(tables, extraBytes);
        }
    }

    /// <summary>
    /// Describes a table in a table section.
    /// </summary>
    public struct TableType {
        /// <summary>
        /// Creates a table description from the given element type and limits.
        /// </summary>
        /// <param name="elementType">The table's element type.</param>
        /// <param name="limits">The table's limits.</param>
        public TableType(WasmType elementType, ResizableLimits limits) {
            this.ElementType = elementType;
            this.Limits = limits;
        }

        /// <summary>
        /// Gets the type of element in the table.
        /// </summary>
        /// <returns>The type of element in the table.</returns>
        public WasmType ElementType { get; private set; }

        /// <summary>
        /// Gets the table's limits.
        /// </summary>
        /// <returns>The table's limits.</returns>
        public ResizableLimits Limits { get; private set; }

        /// <summary>
        /// Writes this table description to the given binary WebAssembly file.
        /// </summary>
        /// <param name="writer">The writer for a binary WebAssembly file.</param>
        public void WriteTo(BinaryWasmWriter writer) {
            writer.WriteWasmType(ElementType);
            Limits.WriteTo(writer);
        }

        /// <summary>
        /// Writes a textual representation of this table description to the given writer.
        /// </summary>
        /// <param name="writer">The writer to which text is written.</param>
        public void Dump(TextWriter writer) {
            writer.Write("(elem_type: ");
            DumpHelpers.DumpWasmType(ElementType, writer);
            writer.Write(", limits: ");
            Limits.Dump(writer);
            writer.Write(")");
        }

        /// <summary>
        /// Reads a single table description from the given reader.
        /// </summary>
        /// <returns>The table description.</returns>
        public static TableType ReadFrom(BinaryWasmReader reader) {
            var elemType = (WasmType)reader.ReadWasmType();
            var limits = reader.ReadResizableLimits();
            return new TableType(elemType, limits);
        }
    }
}