| File: Mono.Cecil.PE\ByteBuffer.cs | Web Access |
| Project: src\cecil\Mono.Cecil.csproj (Mono.Cecil) |
// // Author: // Jb Evain (jbevain@gmail.com) // // Copyright (c) 2008 - 2015 Jb Evain // Copyright (c) 2008 - 2011 Novell, Inc. // // Licensed under the MIT/X11 license. // using System; namespace Mono.Cecil.PE { class ByteBuffer { internal byte [] buffer; internal int length; internal int position; public ByteBuffer () { this.buffer = Empty<byte>.Array; } public ByteBuffer (int length) { this.buffer = new byte [length]; } public ByteBuffer (byte [] buffer) { this.buffer = buffer ?? Empty<byte>.Array; this.length = this.buffer.Length; } public void Advance (int length) { position += length; } public byte ReadByte () { return buffer [position++]; } public sbyte ReadSByte () { return (sbyte) ReadByte (); } public byte [] ReadBytes (int length) { var bytes = new byte [length]; Buffer.BlockCopy (buffer, position, bytes, 0, length); position += length; return bytes; } public ushort ReadUInt16 () { ushort value = (ushort) (buffer [position] | (buffer [position + 1] << 8)); position += 2; return value; } public short ReadInt16 () { return (short) ReadUInt16 (); } public uint ReadUInt32 () { uint value = (uint) (buffer [position] | (buffer [position + 1] << 8) | (buffer [position + 2] << 16) | (buffer [position + 3] << 24)); position += 4; return value; } public int ReadInt32 () { return (int) ReadUInt32 (); } public ulong ReadUInt64 () { uint low = ReadUInt32 (); uint high = ReadUInt32 (); return (((ulong) high) << 32) | low; } public long ReadInt64 () { return (long) ReadUInt64 (); } public uint ReadCompressedUInt32 () { byte first = ReadByte (); if ((first & 0x80) == 0) return first; if ((first & 0x40) == 0) return ((uint) (first & ~0x80) << 8) | ReadByte (); return ((uint) (first & ~0xc0) << 24) | (uint) ReadByte () << 16 | (uint) ReadByte () << 8 | ReadByte (); } public int ReadCompressedInt32 () { var b = buffer [position]; var u = (int) ReadCompressedUInt32 (); var v = u >> 1; if ((u & 1) == 0) return v; switch (b & 0xc0) { case 0: case 0x40: return v - 0x40; case 0x80: return v - 0x2000; default: return v - 0x10000000; } } public float ReadSingle () { if (!BitConverter.IsLittleEndian) { var bytes = ReadBytes (4); Array.Reverse (bytes); return BitConverter.ToSingle (bytes, 0); } float value = BitConverter.ToSingle (buffer, position); position += 4; return value; } public double ReadDouble () { if (!BitConverter.IsLittleEndian) { var bytes = ReadBytes (8); Array.Reverse (bytes); return BitConverter.ToDouble (bytes, 0); } double value = BitConverter.ToDouble (buffer, position); position += 8; return value; } public void WriteByte (byte value) { if (position == buffer.Length) Grow (1); buffer [position++] = value; if (position > length) length = position; } public void WriteSByte (sbyte value) { WriteByte ((byte) value); } public void WriteUInt16 (ushort value) { if (position + 2 > buffer.Length) Grow (2); buffer [position++] = (byte) value; buffer [position++] = (byte) (value >> 8); if (position > length) length = position; } public void WriteInt16 (short value) { WriteUInt16 ((ushort) value); } public void WriteUInt32 (uint value) { if (position + 4 > buffer.Length) Grow (4); buffer [position++] = (byte) value; buffer [position++] = (byte) (value >> 8); buffer [position++] = (byte) (value >> 16); buffer [position++] = (byte) (value >> 24); if (position > length) length = position; } public void WriteInt32 (int value) { WriteUInt32 ((uint) value); } public void WriteUInt64 (ulong value) { if (position + 8 > buffer.Length) Grow (8); buffer [position++] = (byte) value; buffer [position++] = (byte) (value >> 8); buffer [position++] = (byte) (value >> 16); buffer [position++] = (byte) (value >> 24); buffer [position++] = (byte) (value >> 32); buffer [position++] = (byte) (value >> 40); buffer [position++] = (byte) (value >> 48); buffer [position++] = (byte) (value >> 56); if (position > length) length = position; } public void WriteInt64 (long value) { WriteUInt64 ((ulong) value); } public void WriteCompressedUInt32 (uint value) { if (value < 0x80) WriteByte ((byte) value); else if (value < 0x4000) { WriteByte ((byte) (0x80 | (value >> 8))); WriteByte ((byte) (value & 0xff)); } else { WriteByte ((byte) ((value >> 24) | 0xc0)); WriteByte ((byte) ((value >> 16) & 0xff)); WriteByte ((byte) ((value >> 8) & 0xff)); WriteByte ((byte) (value & 0xff)); } } public void WriteCompressedInt32 (int value) { // extracted from System.Reflection.Metadata unchecked { const int b6 = (1 << 6) - 1; const int b13 = (1 << 13) - 1; const int b28 = (1 << 28) - 1; // 0xffffffff for negative value // 0x00000000 for non-negative int signMask = value >> 31; if ((value & ~b6) == (signMask & ~b6)) { int n = ((value & b6) << 1) | (signMask & 1); WriteByte ((byte)n); } else if ((value & ~b13) == (signMask & ~b13)) { int n = ((value & b13) << 1) | (signMask & 1); ushort val = (ushort)(0x8000 | n); WriteUInt16 (BitConverter.IsLittleEndian ? ReverseEndianness (val) : val); } else if ((value & ~b28) == (signMask & ~b28)) { int n = ((value & b28) << 1) | (signMask & 1); uint val = 0xc0000000 | (uint)n; WriteUInt32 (BitConverter.IsLittleEndian ? ReverseEndianness (val) : val); } else { throw new ArgumentOutOfRangeException ("value", "valid range is -2^28 to 2^28 -1"); } } } static uint ReverseEndianness (uint value) { // extracted from .net BCL // This takes advantage of the fact that the JIT can detect // ROL32 / ROR32 patterns and output the correct intrinsic. // // Input: value = [ ww xx yy zz ] // // First line generates : [ ww xx yy zz ] // & [ 00 FF 00 FF ] // = [ 00 xx 00 zz ] // ROR32(8) = [ zz 00 xx 00 ] // // Second line generates: [ ww xx yy zz ] // & [ FF 00 FF 00 ] // = [ ww 00 yy 00 ] // ROL32(8) = [ 00 yy 00 ww ] // // (sum) = [ zz yy xx ww ] // // Testing shows that throughput increases if the AND // is performed before the ROL / ROR. return RotateRight (value & 0x00FF00FFu, 8) // xx zz + RotateLeft (value & 0xFF00FF00u, 8); // ww yy } // extracted from .net BCL static uint RotateRight (uint value, int offset) => (value >> offset) | (value << (32 - offset)); static uint RotateLeft (uint value, int offset) => (value << offset) | (value >> (32 - offset)); static ushort ReverseEndianness (ushort value) { // extracted from .net BCL // Don't need to AND with 0xFF00 or 0x00FF since the final // cast back to ushort will clear out all bits above [ 15 .. 00 ]. // This is normally implemented via "movzx eax, ax" on the return. // Alternatively, the compiler could elide the movzx instruction // entirely if it knows the caller is only going to access "ax" // instead of "eax" / "rax" when the function returns. return (ushort)((value >> 8) + (value << 8)); } public void WriteBytes (byte [] bytes) { var length = bytes.Length; if (position + length > buffer.Length) Grow (length); Buffer.BlockCopy (bytes, 0, buffer, position, length); position += length; if (position > this.length) this.length = position; } public void WriteBytes (int length) { if (position + length > buffer.Length) Grow (length); position += length; if (position > this.length) this.length = position; } public void WriteBytes (ByteBuffer buffer) { if (position + buffer.length > this.buffer.Length) Grow (buffer.length); Buffer.BlockCopy (buffer.buffer, 0, this.buffer, position, buffer.length); position += buffer.length; if (position > this.length) this.length = position; } public void WriteSingle (float value) { var bytes = BitConverter.GetBytes (value); if (!BitConverter.IsLittleEndian) Array.Reverse (bytes); WriteBytes (bytes); } public void WriteDouble (double value) { var bytes = BitConverter.GetBytes (value); if (!BitConverter.IsLittleEndian) Array.Reverse (bytes); WriteBytes (bytes); } void Grow (int desired) { var current = this.buffer; var current_length = current.Length; var buffer = new byte [System.Math.Max (current_length + desired, current_length * 2)]; Buffer.BlockCopy (current, 0, buffer, 0, current_length); this.buffer = buffer; } } }