2019-01-01 11:53:32 +00:00
/ *
2019-01-01 12:40:59 +00:00
Copyright ( C ) 2018 - 2019 de4dot @gmail . com
2018-10-04 19:03:30 +00:00
2019-01-01 12:40:59 +00:00
Permission is hereby granted , free of charge , to any person obtaining
a copy of this software and associated documentation files ( the
"Software" ) , to deal in the Software without restriction , including
without limitation the rights to use , copy , modify , merge , publish ,
distribute , sublicense , and / or sell copies of the Software , and to
permit persons to whom the Software is furnished to do so , subject to
the following conditions :
2018-10-04 19:03:30 +00:00
2019-01-01 12:40:59 +00:00
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software .
2018-10-04 19:03:30 +00:00
2019-01-01 12:40:59 +00:00
THE SOFTWARE IS PROVIDED "AS IS" , WITHOUT WARRANTY OF ANY KIND ,
EXPRESS OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT .
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER IN AN ACTION OF CONTRACT ,
TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE .
2018-10-04 19:03:30 +00:00
* /
2019-02-15 18:58:18 +00:00
#if ! NET35
#define HAS_ROLIST
#endif
2018-10-04 19:03:30 +00:00
using System ;
using System.Collections ;
using System.Collections.Generic ;
using System.Collections.ObjectModel ;
using System.Diagnostics ;
using System.Runtime.CompilerServices ;
namespace Iced.Intel {
/// <summary>
/// A list of <see cref="Instruction"/>s. It's faster than <see cref="List{T}"/> and has
/// methods to get references to the elements so no <see cref="Instruction"/> gets copied.
/// Use 'foreach (ref var instr in list)' to use the foreach ref iterator.
/// </summary>
[DebuggerDisplay("Count = {Count}")]
[DebuggerTypeProxy(typeof(InstructionListDebugView))]
2019-02-15 18:58:18 +00:00
#if HAS_ROLIST
2018-10-04 19:03:30 +00:00
public sealed class InstructionList : IList < Instruction > , IReadOnlyList < Instruction > , IList {
2019-02-15 18:58:18 +00:00
#else
public sealed class InstructionList : IList < Instruction > , IList {
#endif
2018-10-04 19:03:30 +00:00
Instruction [ ] elements ;
int count ;
/// <summary>
/// Gets the number of valid elements
/// </summary>
public int Count = > count ;
int ICollection < Instruction > . Count = > count ;
int ICollection . Count = > count ;
2019-02-15 18:58:18 +00:00
#if HAS_ROLIST
2018-10-04 19:03:30 +00:00
int IReadOnlyCollection < Instruction > . Count = > count ;
2019-02-15 18:58:18 +00:00
#endif
2018-10-04 19:03:30 +00:00
/// <summary>
/// Gets the size of the internal array
/// </summary>
public int Capacity = > elements . Length ;
bool ICollection < Instruction > . IsReadOnly = > false ;
bool IList . IsReadOnly = > false ;
bool IList . IsFixedSize = > false ;
bool ICollection . IsSynchronized = > false ;
object ICollection . SyncRoot = > this ;
static void ThrowArgumentNullException ( string paramName ) = > throw new ArgumentNullException ( paramName ) ;
static void ThrowArgumentException ( ) = > throw new ArgumentException ( ) ;
static void ThrowArgumentOutOfRangeException ( string paramName ) = > throw new ArgumentOutOfRangeException ( paramName ) ;
/// <summary>
/// Gets a reference to an element. The returned reference is valid until the internal array is resized.
/// </summary>
/// <param name="index">Index of element</param>
/// <returns></returns>
public ref Instruction this [ int index ] = >
// Let the jitter validate the index. Note that this also allows code to accidentally
// get a reference to an unused element if Count < Capacity; this code won't throw.
// It's acceptable, this list is used for PERF, use List<Instruction> to throw.
ref elements [ index ] ;
Instruction IList < Instruction > . this [ int index ] {
get = > elements [ index ] ;
set = > elements [ index ] = value ;
}
2019-02-15 18:58:18 +00:00
#if HAS_ROLIST
2018-10-04 19:03:30 +00:00
Instruction IReadOnlyList < Instruction > . this [ int index ] = > elements [ index ] ;
2019-02-15 18:58:18 +00:00
#endif
2018-10-04 19:03:30 +00:00
object IList . this [ int index ] {
get = > elements [ index ] ;
set {
if ( value = = null )
ThrowArgumentNullException ( nameof ( value ) ) ;
if ( ! ( value is Instruction ) )
ThrowArgumentException ( ) ;
elements [ index ] = ( Instruction ) value ;
}
}
/// <summary>
/// Constructor
/// </summary>
2019-02-15 18:58:18 +00:00
public InstructionList ( ) = > elements = Array2 . Empty < Instruction > ( ) ;
2018-10-04 19:03:30 +00:00
/// <summary>
/// Constructor
/// </summary>
/// <param name="capacity">Initial size of the internal array</param>
public InstructionList ( int capacity ) {
if ( capacity < 0 )
ThrowArgumentOutOfRangeException ( nameof ( capacity ) ) ;
2019-02-15 18:58:18 +00:00
elements = capacity = = 0 ? Array2 . Empty < Instruction > ( ) : new Instruction [ capacity ] ;
2018-10-04 19:03:30 +00:00
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="list">List that will be copied to this instance</param>
public InstructionList ( InstructionList list ) {
if ( list = = null )
ThrowArgumentNullException ( nameof ( list ) ) ;
int length = list . count ;
if ( length = = 0 )
2019-02-15 18:58:18 +00:00
elements = Array2 . Empty < Instruction > ( ) ;
2018-10-04 19:03:30 +00:00
else {
var elements = new Instruction [ length ] ;
this . elements = elements ;
count = length ;
Array . Copy ( list . elements , 0 , elements , 0 , length ) ;
}
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="collection">Collection that will be copied to this instance</param>
public InstructionList ( IEnumerable < Instruction > collection ) {
2018-11-10 20:10:57 +00:00
if ( collection = = null )
ThrowArgumentNullException ( nameof ( collection ) ) ;
2018-10-04 19:03:30 +00:00
if ( collection is ICollection < Instruction > coll ) {
int count = coll . Count ;
if ( count = = 0 )
2019-02-15 18:58:18 +00:00
elements = Array2 . Empty < Instruction > ( ) ;
2018-10-04 19:03:30 +00:00
else {
var elements = new Instruction [ count ] ;
this . elements = elements ;
coll . CopyTo ( elements , 0 ) ;
this . count = count ;
}
}
else {
2019-02-15 18:58:18 +00:00
elements = Array2 . Empty < Instruction > ( ) ;
2018-10-04 19:03:30 +00:00
foreach ( var elem in collection )
Add ( elem ) ;
}
}
void SetMinCapacity ( int minCapacity ) {
var elements = this . elements ;
uint capacity = ( uint ) elements . Length ;
if ( minCapacity < = ( int ) capacity )
return ;
const uint DEFAULT_SIZE = 4 ; // Same as List<Instruction>
uint newCapacity = capacity * 2 ;
if ( newCapacity < DEFAULT_SIZE )
newCapacity = DEFAULT_SIZE ;
if ( newCapacity < ( uint ) minCapacity )
newCapacity = ( uint ) minCapacity ;
// See coreclr/vm/gchelpers.cpp:MaxArrayLength()
const uint MaxArrayLength = 0x7FEFFFFF ;
Debug . Assert ( MaxArrayLength < = int . MaxValue ) ;
if ( newCapacity > MaxArrayLength )
newCapacity = MaxArrayLength ;
var newElements = new Instruction [ ( int ) newCapacity ] ;
Array . Copy ( elements , 0 , newElements , 0 , count ) ;
this . elements = newElements ;
}
/// <summary>
/// Allocates an uninitialized element at the end of the list and returns a reference to it.
2018-10-25 20:25:36 +00:00
/// The return value can be passed to eg. Decoder.Decode(out Instruction).
2018-10-04 19:03:30 +00:00
/// The returned reference is valid until the internal array is resized.
/// </summary>
/// <returns></returns>
2019-02-15 18:58:18 +00:00
[MethodImpl(MethodImplOptions2.AggressiveInlining)] // Add() is inlined, and this method does almost the same thing
2018-10-04 19:03:30 +00:00
public ref Instruction AllocUninitializedElement ( ) {
var count = this . count ;
var elements = this . elements ;
if ( count = = elements . Length ) {
SetMinCapacity ( count + 1 ) ;
elements = this . elements ;
}
this . count = count + 1 ;
return ref elements [ count ] ;
}
void MakeRoom ( int index , int extraLength ) {
//TODO: This can be optimized to copy less data. The current code can copy the same
// data twice if the internal array is resized by SetMinCapacity()
SetMinCapacity ( count + extraLength ) ;
int copyCount = count - index ;
if ( copyCount ! = 0 ) {
var elements = this . elements ;
Array . Copy ( elements , index , elements , index + extraLength , copyCount ) ;
}
}
/// <summary>
/// Inserts an element
/// </summary>
/// <param name="index">Index of element</param>
/// <param name="instruction">Instruction to add</param>
public void Insert ( int index , in Instruction instruction ) {
var count = this . count ;
if ( ( uint ) index > ( uint ) count )
ThrowArgumentOutOfRangeException ( nameof ( index ) ) ;
MakeRoom ( index , 1 ) ;
elements [ index ] = instruction ;
this . count = count + 1 ;
}
void IList < Instruction > . Insert ( int index , Instruction instruction ) = > Insert ( index , instruction ) ;
void IList . Insert ( int index , object value ) {
if ( value = = null )
ThrowArgumentNullException ( nameof ( value ) ) ;
if ( ! ( value is Instruction ) )
ThrowArgumentException ( ) ;
Insert ( index , ( Instruction ) value ) ;
}
/// <summary>
/// Removes an element from the list
/// </summary>
/// <param name="index">Index of element to remove</param>
public void RemoveAt ( int index ) {
var newCount = count ;
if ( ( uint ) index > = ( uint ) newCount )
ThrowArgumentOutOfRangeException ( nameof ( index ) ) ;
newCount - - ;
count = newCount ;
int copyCount = newCount - index ;
if ( copyCount ! = 0 ) {
var elements = this . elements ;
Array . Copy ( elements , index + 1 , elements , index , copyCount ) ;
}
}
void IList < Instruction > . RemoveAt ( int index ) = > RemoveAt ( index ) ;
void IList . RemoveAt ( int index ) = > RemoveAt ( index ) ;
/// <summary>
/// Adds a collection to the end of this list
/// </summary>
/// <param name="collection">Collection to add</param>
public void AddRange ( IEnumerable < Instruction > collection ) = > InsertRange ( count , collection ) ;
/// <summary>
/// Inserts elements
/// </summary>
/// <param name="index">Index of element</param>
/// <param name="collection">Items to insert</param>
public void InsertRange ( int index , IEnumerable < Instruction > collection ) {
if ( ( uint ) index > ( uint ) count )
ThrowArgumentOutOfRangeException ( nameof ( index ) ) ;
2018-11-10 20:10:57 +00:00
if ( collection = = null )
ThrowArgumentNullException ( nameof ( collection ) ) ;
2018-10-04 19:03:30 +00:00
if ( collection is InstructionList list ) {
int list_count = list . count ;
if ( list_count ! = 0 ) {
MakeRoom ( index , list_count ) ;
count + = list_count ;
Array . Copy ( list . elements , 0 , elements , index , list_count ) ;
}
}
2019-02-15 18:58:18 +00:00
else if ( collection is IList < Instruction > ilist ) {
int ilist_Count = ilist . Count ;
if ( ilist_Count ! = 0 ) {
MakeRoom ( index , ilist_Count ) ;
count + = ilist_Count ;
var elements = this . elements ;
for ( int i = 0 ; i < ilist_Count ; i + + )
elements [ index + i ] = ilist [ i ] ;
}
}
#if HAS_ROLIST
2018-10-04 19:03:30 +00:00
else if ( collection is IReadOnlyList < Instruction > roList ) {
int roList_Count = roList . Count ;
if ( roList_Count ! = 0 ) {
MakeRoom ( index , roList_Count ) ;
count + = roList_Count ;
var elements = this . elements ;
for ( int i = 0 ; i < roList_Count ; i + + )
elements [ index + i ] = roList [ i ] ;
}
}
2019-02-15 18:58:18 +00:00
#endif
2018-10-04 19:03:30 +00:00
else {
foreach ( var instruction in collection )
Insert ( index + + , instruction ) ;
}
}
/// <summary>
/// Removes elements
/// </summary>
/// <param name="index">Index of element</param>
/// <param name="count">Number of elements to remove</param>
public void RemoveRange ( int index , int count ) {
if ( index < 0 )
ThrowArgumentOutOfRangeException ( nameof ( index ) ) ;
if ( count < 0 )
ThrowArgumentOutOfRangeException ( nameof ( count ) ) ;
if ( ( uint ) index + ( uint ) count > ( uint ) this . count )
ThrowArgumentOutOfRangeException ( nameof ( count ) ) ;
var newCount = this . count ;
newCount - = count ;
2018-10-05 16:58:52 +00:00
this . count = newCount ;
2018-10-04 19:03:30 +00:00
int copyCount = newCount - index ;
if ( copyCount ! = 0 ) {
var elements = this . elements ;
Array . Copy ( elements , index + count , elements , index , copyCount ) ;
}
}
/// <summary>
/// Adds a new instruction to the end of the list
/// </summary>
/// <param name="instruction">Instruction to add</param>
2019-02-15 18:58:18 +00:00
[MethodImpl(MethodImplOptions2.AggressiveInlining)] // Needed since it's not inlined otherwise (because of 'in') (List<Instruction>.Add() gets auto-inlined)
2018-10-04 19:03:30 +00:00
public void Add ( in Instruction instruction ) {
var count = this . count ;
var elements = this . elements ;
if ( count = = elements . Length ) {
SetMinCapacity ( count + 1 ) ;
elements = this . elements ;
}
elements [ count ] = instruction ;
this . count = count + 1 ;
}
void ICollection < Instruction > . Add ( Instruction instruction ) = > Add ( instruction ) ;
int IList . Add ( object value ) {
if ( value = = null )
ThrowArgumentNullException ( nameof ( value ) ) ;
if ( ! ( value is Instruction ) )
ThrowArgumentException ( ) ;
Add ( ( Instruction ) value ) ;
return count - 1 ;
}
/// <summary>
/// Clears the list
/// </summary>
public void Clear ( ) = > count = 0 ; // There are no GC refs in Instruction, so we don't have to clear the elements
void ICollection < Instruction > . Clear ( ) = > Clear ( ) ;
void IList . Clear ( ) = > Clear ( ) ;
2018-10-05 16:58:52 +00:00
/// <summary>
/// Checks if <paramref name="instruction"/> exists in the list
/// </summary>
/// <param name="instruction">Instruction</param>
/// <returns></returns>
public bool Contains ( in Instruction instruction ) = > IndexOf ( instruction ) > = 0 ;
bool ICollection < Instruction > . Contains ( Instruction instruction ) = > Contains ( instruction ) ;
bool IList . Contains ( object value ) {
if ( value is Instruction )
return Contains ( ( Instruction ) value ) ;
return false ;
}
/// <summary>
/// Gets the index of <paramref name="instruction"/> or -1 if it doesn't exist in the list
/// </summary>
/// <param name="instruction">Instruction</param>
/// <returns></returns>
public int IndexOf ( in Instruction instruction ) {
var elements = this . elements ;
int count = this . count ;
for ( int i = 0 ; i < count ; i + + ) {
if ( elements [ i ] = = instruction )
return i ;
}
return - 1 ;
}
int IList < Instruction > . IndexOf ( Instruction instruction ) = > IndexOf ( instruction ) ;
int IList . IndexOf ( object value ) {
if ( value is Instruction )
return IndexOf ( ( Instruction ) value ) ;
return - 1 ;
}
/// <summary>
/// Gets the index of <paramref name="instruction"/> or -1 if it doesn't exist in the list
/// </summary>
/// <param name="instruction">Instruction</param>
/// <param name="index">Start index</param>
/// <returns></returns>
public int IndexOf ( in Instruction instruction , int index ) {
int count = this . count ;
if ( ( uint ) index > ( uint ) count )
ThrowArgumentOutOfRangeException ( nameof ( index ) ) ;
var elements = this . elements ;
for ( int i = index ; i < count ; i + + ) {
if ( elements [ i ] = = instruction )
return i ;
}
return - 1 ;
}
/// <summary>
/// Gets the index of <paramref name="instruction"/> or -1 if it doesn't exist in the list
/// </summary>
/// <param name="instruction">Instruction</param>
/// <param name="index">Start index</param>
/// <param name="count">Number of instructions to check</param>
/// <returns></returns>
public int IndexOf ( in Instruction instruction , int index , int count ) {
if ( index < 0 )
ThrowArgumentOutOfRangeException ( nameof ( index ) ) ;
if ( count < 0 )
ThrowArgumentOutOfRangeException ( nameof ( count ) ) ;
int end = index + count ;
if ( ( uint ) end > ( uint ) this . count )
ThrowArgumentOutOfRangeException ( nameof ( count ) ) ;
var elements = this . elements ;
for ( int i = index ; i < end ; i + + ) {
if ( elements [ i ] = = instruction )
return i ;
}
return - 1 ;
}
/// <summary>
/// Gets the last index of <paramref name="instruction"/> or -1 if it doesn't exist in the list
/// </summary>
/// <param name="instruction">Instruction</param>
/// <returns></returns>
public int LastIndexOf ( in Instruction instruction ) {
for ( int i = count - 1 ; i > = 0 ; i - - ) {
if ( elements [ i ] = = instruction )
return i ;
}
return - 1 ;
}
/// <summary>
/// Gets the last index of <paramref name="instruction"/> or -1 if it doesn't exist in the list
/// </summary>
/// <param name="instruction">Instruction</param>
/// <param name="index">Start index</param>
/// <returns></returns>
public int LastIndexOf ( in Instruction instruction , int index ) {
int count = this . count ;
if ( ( uint ) index > ( uint ) count )
ThrowArgumentOutOfRangeException ( nameof ( index ) ) ;
var elements = this . elements ;
for ( int i = count - 1 ; i > = index ; i - - ) {
if ( elements [ i ] = = instruction )
return i ;
}
return - 1 ;
}
/// <summary>
/// Gets the last index of <paramref name="instruction"/> or -1 if it doesn't exist in the list
/// </summary>
/// <param name="instruction">Instruction</param>
/// <param name="index">Start index</param>
/// <param name="count">Number of instructions to check</param>
/// <returns></returns>
public int LastIndexOf ( in Instruction instruction , int index , int count ) {
if ( index < 0 )
ThrowArgumentOutOfRangeException ( nameof ( index ) ) ;
if ( count < 0 )
ThrowArgumentOutOfRangeException ( nameof ( count ) ) ;
int end = index + count ;
if ( ( uint ) end > ( uint ) this . count )
ThrowArgumentOutOfRangeException ( nameof ( count ) ) ;
var elements = this . elements ;
for ( int i = end - 1 ; i > = index ; i - - ) {
if ( elements [ i ] = = instruction )
return i ;
}
return - 1 ;
}
/// <summary>
/// Removes the first copy of <paramref name="instruction"/> and returns true if it was removed
/// </summary>
/// <param name="instruction">Instruction</param>
/// <returns></returns>
public bool Remove ( in Instruction instruction ) {
int index = IndexOf ( instruction ) ;
if ( index > = 0 )
RemoveAt ( index ) ;
return index > = 0 ;
}
bool ICollection < Instruction > . Remove ( Instruction instruction ) = > Remove ( instruction ) ;
void IList . Remove ( object value ) {
if ( value is Instruction )
Remove ( ( Instruction ) value ) ;
}
2018-10-04 19:03:30 +00:00
/// <summary>
/// Copies this collection to <paramref name="array"/>
/// </summary>
/// <param name="array">Destination array</param>
public void CopyTo ( Instruction [ ] array ) = > CopyTo ( array , 0 ) ;
/// <summary>
/// Copies this collection to <paramref name="array"/>
/// </summary>
/// <param name="array">Destination array</param>
/// <param name="arrayIndex">Start index in <paramref name="array"/></param>
public void CopyTo ( Instruction [ ] array , int arrayIndex ) = >
Array . Copy ( elements , 0 , array , arrayIndex , count ) ;
void ICollection < Instruction > . CopyTo ( Instruction [ ] array , int arrayIndex ) = > CopyTo ( array , arrayIndex ) ;
void ICollection . CopyTo ( Array array , int index ) {
2018-11-10 20:10:57 +00:00
if ( array = = null )
2018-10-04 19:03:30 +00:00
ThrowArgumentNullException ( nameof ( array ) ) ;
2018-11-10 20:10:57 +00:00
else if ( array is Instruction [ ] elemArray )
CopyTo ( elemArray , index ) ;
2018-10-04 19:03:30 +00:00
else
ThrowArgumentException ( ) ;
}
/// <summary>
/// Copies this collection to <paramref name="array"/>
/// </summary>
/// <param name="index">Index in this collection</param>
/// <param name="array">Destination array</param>
/// <param name="arrayIndex">Destination index</param>
/// <param name="count">Number of elements to copy</param>
public void CopyTo ( int index , Instruction [ ] array , int arrayIndex , int count ) = >
Array . Copy ( elements , index , array , arrayIndex , count ) ;
/// <summary>
/// Creates a new list that contains some of the instructions in this list
/// </summary>
/// <param name="index">Index of first instruction</param>
/// <param name="count">Number of instructions</param>
/// <returns></returns>
public InstructionList GetRange ( int index , int count ) {
if ( index < 0 )
ThrowArgumentOutOfRangeException ( nameof ( index ) ) ;
if ( count < 0 )
ThrowArgumentOutOfRangeException ( nameof ( count ) ) ;
if ( ( uint ) index + ( uint ) count > ( uint ) this . count )
ThrowArgumentOutOfRangeException ( nameof ( count ) ) ;
var list = new InstructionList ( count ) ;
Array . Copy ( elements , index , list . elements , 0 , count ) ;
list . count = count ;
return list ;
}
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public struct Enumerator : IEnumerator < Instruction > {
readonly InstructionList list ;
int index ;
public ref Instruction Current = > ref list . elements [ index ] ;
Instruction IEnumerator < Instruction > . Current = > list . elements [ index ] ;
object IEnumerator . Current = > list . elements [ index ] ;
internal Enumerator ( InstructionList list ) {
// Only two fields, the jitter can put both fields in two registers and
// won't allocate anything on the stack
this . list = list ;
index = - 1 ;
}
public bool MoveNext ( ) {
// Keep both statements, the jitter generates better code if it looks like this.
// Both fields should already be in registers.
index + + ;
return index < list . count ;
}
void IEnumerator . Reset ( ) = > throw new NotSupportedException ( ) ;
public void Dispose ( ) { }
}
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
/// <summary>
/// Gets a ref iterator (use 'foreach ref')
/// </summary>
/// <returns></returns>
public Enumerator GetEnumerator ( ) = > new Enumerator ( this ) ;
IEnumerator < Instruction > IEnumerable < Instruction > . GetEnumerator ( ) = > new Enumerator ( this ) ;
IEnumerator IEnumerable . GetEnumerator ( ) = > new Enumerator ( this ) ;
/// <summary>
/// Returns a read-only wrapper for this list
/// </summary>
/// <returns></returns>
public ReadOnlyCollection < Instruction > AsReadOnly ( ) = > new ReadOnlyCollection < Instruction > ( this ) ;
/// <summary>
/// Creates a new array with all instructions and returns it
/// </summary>
/// <returns></returns>
public Instruction [ ] ToArray ( ) {
int count = this . count ;
if ( count = = 0 )
2019-02-15 18:58:18 +00:00
return Array2 . Empty < Instruction > ( ) ;
2018-10-04 19:03:30 +00:00
var res = new Instruction [ count ] ;
Array . Copy ( elements , 0 , res , 0 , res . Length ) ;
return res ;
}
}
}