Xamarin Public Jenkins ea5caba957 Imported Upstream version 4.2.1.36
Former-commit-id: f3008ca867fe7e4b7ae9b9a8844c0ad5798925a9
2015-11-10 14:54:41 +00:00

396 lines
8.2 KiB
C#

/*
Copyright (C) 2008 Jeroen Frijters
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Jeroen Frijters
jeroen@frijters.net
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using IKVM.Reflection.Metadata;
namespace IKVM.Reflection.Writer
{
abstract class Heap
{
protected bool frozen;
protected int unalignedlength;
internal void Write(MetadataWriter mw)
{
uint pos = mw.Position;
WriteImpl(mw);
Debug.Assert(mw.Position == pos + unalignedlength);
int align = Length - unalignedlength;
for (int i = 0; i < align; i++)
{
mw.Write((byte)0);
}
}
internal bool IsBig
{
get { return Length > 65535; }
}
internal int Length
{
get
{
if (!frozen)
throw new InvalidOperationException();
return (unalignedlength + 3) & ~3;
}
}
protected abstract void WriteImpl(MetadataWriter mw);
}
abstract class SimpleHeap : Heap
{
internal void Freeze()
{
if (frozen)
throw new InvalidOperationException();
frozen = true;
unalignedlength = GetLength();
}
protected abstract int GetLength();
}
sealed class TableHeap : Heap
{
internal void Freeze(MetadataWriter mw)
{
if (frozen)
throw new InvalidOperationException();
frozen = true;
unalignedlength = GetLength(mw);
}
protected override void WriteImpl(MetadataWriter mw)
{
Table[] tables = mw.ModuleBuilder.GetTables();
// Header
mw.Write(0); // Reserved
int ver = mw.ModuleBuilder.MDStreamVersion;
mw.Write((byte)(ver >> 16)); // MajorVersion
mw.Write((byte)ver); // MinorVersion
byte heapSizes = 0;
if (mw.ModuleBuilder.Strings.IsBig)
{
heapSizes |= 0x01;
}
if (mw.ModuleBuilder.Guids.IsBig)
{
heapSizes |= 0x02;
}
if (mw.ModuleBuilder.Blobs.IsBig)
{
heapSizes |= 0x04;
}
mw.Write(heapSizes);// HeapSizes
// LAMESPEC spec says reserved, but .NET 2.0 Ref.Emit sets it to 0x10
mw.Write((byte)0x10); // Reserved
long bit = 1;
long valid = 0;
foreach (Table table in tables)
{
if (table != null && table.RowCount > 0)
{
valid |= bit;
}
bit <<= 1;
}
mw.Write(valid); // Valid
mw.Write(0x0016003301FA00L); // Sorted
// Rows
foreach (Table table in tables)
{
if (table != null && table.RowCount > 0)
{
mw.Write(table.RowCount);
}
}
// Tables
foreach (Table table in tables)
{
if (table != null && table.RowCount > 0)
{
uint pos = mw.Position;
table.Write(mw);
Debug.Assert(mw.Position - pos == table.GetLength(mw));
}
}
// unexplained extra padding
mw.Write((byte)0);
}
private static int GetLength(MetadataWriter mw)
{
int len = 4 + 4 + 8 + 8;
foreach (Table table in mw.ModuleBuilder.GetTables())
{
if (table != null && table.RowCount > 0)
{
len += 4; // row count
len += table.GetLength(mw);
}
}
// note that we pad one extra (unexplained) byte
return len + 1;
}
}
sealed class StringHeap : SimpleHeap
{
private List<string> list = new List<string>();
private Dictionary<string, int> strings = new Dictionary<string, int>();
private int nextOffset;
internal StringHeap()
{
Add("");
}
internal int Add(string str)
{
Debug.Assert(!frozen);
int offset;
if (!strings.TryGetValue(str, out offset))
{
offset = nextOffset;
nextOffset += System.Text.Encoding.UTF8.GetByteCount(str) + 1;
list.Add(str);
strings.Add(str, offset);
}
return offset;
}
internal string Find(int index)
{
foreach (KeyValuePair<string, int> kv in strings)
{
if (kv.Value == index)
{
return kv.Key;
}
}
return null;
}
protected override int GetLength()
{
return nextOffset;
}
protected override void WriteImpl(MetadataWriter mw)
{
foreach (string str in list)
{
mw.Write(System.Text.Encoding.UTF8.GetBytes(str));
mw.Write((byte)0);
}
}
}
sealed class UserStringHeap : SimpleHeap
{
private List<string> list = new List<string>();
private Dictionary<string, int> strings = new Dictionary<string, int>();
private int nextOffset;
internal UserStringHeap()
{
nextOffset = 1;
}
internal bool IsEmpty
{
get { return nextOffset == 1; }
}
internal int Add(string str)
{
Debug.Assert(!frozen);
int offset;
if (!strings.TryGetValue(str, out offset))
{
int length = str.Length * 2 + 1 + MetadataWriter.GetCompressedUIntLength(str.Length * 2 + 1);
if (nextOffset + length > 0xFFFFFF)
{
throw new FileFormatLimitationExceededException("No logical space left to create more user strings.", FileFormatLimitationExceededException.META_E_STRINGSPACE_FULL);
}
offset = nextOffset;
nextOffset += length;
list.Add(str);
strings.Add(str, offset);
}
return offset;
}
protected override int GetLength()
{
return nextOffset;
}
protected override void WriteImpl(MetadataWriter mw)
{
mw.Write((byte)0);
foreach (string str in list)
{
mw.WriteCompressedUInt(str.Length * 2 + 1);
byte hasSpecialChars = 0;
foreach (char ch in str)
{
mw.Write((ushort)ch);
if (hasSpecialChars == 0 && (ch < 0x20 || ch > 0x7E))
{
if (ch > 0x7E
|| (ch >= 0x01 && ch <= 0x08)
|| (ch >= 0x0E && ch <= 0x1F)
|| ch == 0x27
|| ch == 0x2D)
{
hasSpecialChars = 1;
}
}
}
mw.Write(hasSpecialChars);
}
}
}
sealed class GuidHeap : SimpleHeap
{
private List<Guid> list = new List<Guid>();
internal GuidHeap()
{
}
internal int Add(Guid guid)
{
Debug.Assert(!frozen);
list.Add(guid);
return list.Count;
}
protected override int GetLength()
{
return list.Count * 16;
}
protected override void WriteImpl(MetadataWriter mw)
{
foreach (Guid guid in list)
{
mw.Write(guid.ToByteArray());
}
}
}
sealed class BlobHeap : SimpleHeap
{
private Key[] map = new Key[8179];
private readonly ByteBuffer buf = new ByteBuffer(32);
private struct Key
{
internal Key[] next;
internal int len;
internal int hash;
internal int offset;
}
internal BlobHeap()
{
buf.Write((byte)0);
}
internal int Add(ByteBuffer bb)
{
Debug.Assert(!frozen);
int bblen = bb.Length;
if (bblen == 0)
{
return 0;
}
int lenlen = MetadataWriter.GetCompressedUIntLength(bblen);
int hash = bb.Hash();
int index = (hash & 0x7FFFFFFF) % map.Length;
Key[] keys = map;
int last = index;
while (keys[index].offset != 0)
{
if (keys[index].hash == hash
&& keys[index].len == bblen
&& buf.Match(keys[index].offset + lenlen, bb, 0, bblen))
{
return keys[index].offset;
}
if (index == last)
{
if (keys[index].next == null)
{
keys[index].next = new Key[4];
keys = keys[index].next;
index = 0;
break;
}
keys = keys[index].next;
index = -1;
last = keys.Length - 1;
}
index++;
}
int offset = buf.Position;
buf.WriteCompressedUInt(bblen);
buf.Write(bb);
keys[index].len = bblen;
keys[index].hash = hash;
keys[index].offset = offset;
return offset;
}
protected override int GetLength()
{
return buf.Position;
}
protected override void WriteImpl(MetadataWriter mw)
{
mw.Write(buf);
}
internal bool IsEmpty
{
get { return buf.Position == 1; }
}
internal IKVM.Reflection.Reader.ByteReader GetBlob(int blobIndex)
{
return buf.GetBlob(blobIndex);
}
}
}