// Binary file format, all data is little-endian:
        //
        //  [Magic string]                    # XATS
        //  [Format version]                  # 32-bit unsigned integer, 4 bytes
        //  [Entry count]                     # 32-bit unsigned integer, 4 bytes
        //  [Java type name width]            # 32-bit unsigned integer, 4 bytes
        //  [Managed type name width]         # 32-bit unsigned integer, 4 bytes
        //  [Assembly name size]              # 32-bit unsigned integer, 4 bytes
        //  [Assembly name]                   # Non-null terminated assembly name
        //  [Java-to-managed map]             # Format described below, [Entry count] entries
        //  [Managed-to-java map]             # Format described below, [Entry count] entries
        //
        // Java-to-managed map format:
        //
        //    [Java type name]<NUL>[Managed type table index]
        //
        //  Each name is padded with <NUL> to the width specified in the [Java type name width] field above.
        //  Names are written without the size prefix, instead they are always terminated with a nul character
        //  to make it easier and faster to handle by the native runtime.
        //
        //  Each [Managed type table index] is an unsigned 32-bit integer, 4 bytes
        //
        //
        // Managed-to-java map is identical to the [Java-to-managed] table above, with the exception of the index
        // pointing to the Java name table.
        //
        void OutputModule(BinaryWriter bw, ModuleDebugData moduleData)
        {
            if ((uint)moduleData.JavaToManagedMap.Count == InvalidJavaToManagedMappingIndex)
            {
                throw new InvalidOperationException($"Too many types in module {moduleData.ModuleName}");
            }

            bw.Write(moduleMagicString);
            bw.Write(TypeMapFormatVersion);
            bw.Write(moduleData.JavaToManagedMap.Count);
            bw.Write(moduleData.JavaNameWidth);
            bw.Write(moduleData.ManagedNameWidth);
            bw.Write(moduleData.ModuleNameBytes.Length);
            bw.Write(moduleData.ModuleNameBytes);

            foreach (TypeMapDebugEntry entry in moduleData.JavaToManagedMap)
            {
                bw.Write(outputEncoding.GetBytes(entry.JavaName));
                PadField(bw, entry.JavaName.Length, (int)moduleData.JavaNameWidth);

                TypeMapGenerator.TypeMapDebugEntry managedEntry = entry.DuplicateForJavaToManaged != null ? entry.DuplicateForJavaToManaged : entry;
                bw.Write(managedEntry.SkipInJavaToManaged ? InvalidJavaToManagedMappingIndex : (uint)managedEntry.ManagedIndex);
            }

            foreach (TypeMapDebugEntry entry in moduleData.ManagedToJavaMap)
            {
                bw.Write(outputEncoding.GetBytes(entry.ManagedName));
                PadField(bw, entry.ManagedName.Length, (int)moduleData.ManagedNameWidth);
                bw.Write(entry.JavaIndex);
            }
        }
        protected override void WriteSymbols(StreamWriter output)
        {
            bool haveJavaToManaged = data.JavaToManagedMap != null && data.JavaToManagedMap.Count > 0;
            bool haveManagedToJava = data.ManagedToJavaMap != null && data.ManagedToJavaMap.Count > 0;

            using (var sharedOutput = MemoryStreamPool.Shared.CreateStreamWriter(output.Encoding)) {
                WriteSharedBits(sharedOutput, haveJavaToManaged, haveManagedToJava);
                sharedOutput.Flush();
                MonoAndroidHelper.CopyIfStreamChanged(sharedOutput.BaseStream, SharedIncludeFile);
            }

            if (haveJavaToManaged || haveManagedToJava)
            {
                output.Write(Indent);
                output.Write(".include");
                output.Write(Indent);
                output.Write('"');
                output.Write(Path.GetFileName(SharedIncludeFile));
                output.WriteLine('"');

                output.WriteLine();
            }

            uint size = 0;

            WriteCommentLine(output, "Managed to java map: START", indent: false);
            WriteSection(output, $".data.rel.{ManagedToJavaSymbol}", hasStrings: false, writable: true);
            WriteStructureSymbol(output, ManagedToJavaSymbol, alignBits: TargetProvider.DebugTypeMapAlignBits, isGlobal: false);
            if (haveManagedToJava)
            {
                foreach (TypeMapGenerator.TypeMapDebugEntry entry in data.ManagedToJavaMap)
                {
                    size += WritePointer(output, entry.ManagedLabel);
                    size += WritePointer(output, entry.JavaLabel);
                }
            }
            WriteStructureSize(output, ManagedToJavaSymbol, size, alwaysWriteSize: true);
            WriteCommentLine(output, "Managed to java map: END", indent: false);
            output.WriteLine();

            size = 0;
            WriteCommentLine(output, "Java to managed map: START", indent: false);
            WriteSection(output, $".data.rel.{JavaToManagedSymbol}", hasStrings: false, writable: true);
            WriteStructureSymbol(output, JavaToManagedSymbol, alignBits: TargetProvider.DebugTypeMapAlignBits, isGlobal: false);
            if (haveJavaToManaged)
            {
                foreach (TypeMapGenerator.TypeMapDebugEntry entry in data.JavaToManagedMap)
                {
                    size += WritePointer(output, entry.JavaLabel);

                    TypeMapGenerator.TypeMapDebugEntry managedEntry = entry.DuplicateForJavaToManaged != null ? entry.DuplicateForJavaToManaged : entry;
                    size += WritePointer(output, managedEntry.SkipInJavaToManaged ? null : managedEntry.ManagedLabel);
                }
            }
            WriteStructureSize(output, JavaToManagedSymbol, size, alwaysWriteSize: true);
            WriteCommentLine(output, "Java to managed map: END", indent: false);
            output.WriteLine();

            // MUST match src/monodroid/xamarin-app.hh
            WriteCommentLine(output, "TypeMap structure");
            WriteSection(output, $".data.rel.ro.{TypeMapSymbol}", hasStrings: false, writable: true);
            WriteStructureSymbol(output, TypeMapSymbol, alignBits: TargetProvider.DebugTypeMapAlignBits, isGlobal: true);

            size = WriteStructure(output, packed: false, structureWriter: () => WriteTypeMapStruct(output));

            WriteStructureSize(output, TypeMapSymbol, size);
        }