using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
namespace Vanara.InteropServices
{
/// An safe unmanaged array of bytes allocated on the global heap.
///
///
///
///
public class SafeByteArray : SafeMemoryHandle, IList, ICloneable, IList, IStructuralComparable, IStructuralEquatable
{
/// Initializes a new instance of the class from a copy of a managed byte array.
/// The array of bytes to copy.
public SafeByteArray(byte[] array) : base(array?.Length ?? 0)
{
if (array != null)
Marshal.Copy(array, 0, handle, array.Length);
}
/// Initializes a new instance of the class and allocates bytes.
/// The byte count to allocate.
public SafeByteArray(int byteCount) : base(byteCount) => Zero();
///
/// Initializes a new instance of the class by copying the bytes from another unmanaged array.
///
/// Another unmanaged array.
public SafeByteArray(SafeByteArray src) : base(src?.Count ?? 0)
{
if (src == null) throw new ArgumentNullException(nameof(src));
if (src.IsInvalid) throw new ArgumentException(@"Invalid source object.", nameof(src));
CopyMemory(src.handle, handle, src.Count);
}
// Prevents construction of an invalid instance.
[ExcludeFromCodeCoverage]
private SafeByteArray() : base(0) { }
/// Gets the number of elements contained in the .
public int Count => Size;
/// Gets a value indicating whether the is read-only.
public bool IsReadOnly => false;
/// Gets a value indicating whether the has a fixed size.
bool IList.IsFixedSize => true;
///
/// Gets a value indicating whether access to the is synchronized (thread safe).
///
bool ICollection.IsSynchronized => true;
/// Gets an object that can be used to synchronize access to the .
object ICollection.SyncRoot => this;
[ExcludeFromCodeCoverage]
private new int Size { get => base.Size; set => base.Size = value; }
/// Gets or sets the at the specified index.
/// The .
/// The index.
///
/// index
/// Object is not valid.
public byte this[int index]
{
get
{
if (index < 0 || index >= Count) throw new IndexOutOfRangeException();
return Marshal.ReadByte(handle, index);
}
set
{
if (index < 0 || index >= Count) throw new IndexOutOfRangeException();
Marshal.WriteByte(handle, index, value);
}
}
/// Gets or sets the at the specified index.
/// The .
/// The index.
///
object IList.this[int index]
{
get => this[index];
set => this[index] = (byte)value;
}
/// Removes all items from the .
///
public void Clear() => Size = 0;
/// Creates a new object that is a copy of the current instance.
/// A new object that is a copy of this instance.
public object Clone() => new SafeByteArray(this);
/// Determines whether the contains a specific value.
/// The object to locate in the .
///
/// true if is found in the ; otherwise, false.
///
public bool Contains(byte item) => IndexOf(item) != -1;
///
/// Copies the elements of the to an , starting
/// at a particular index.
///
///
/// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing.
///
/// The zero-based index in at which copying begins.
/// array
/// array
public void CopyTo(byte[] array, int arrayIndex)
{
if (array == null) throw new ArgumentNullException(nameof(array));
if (array.Length - arrayIndex < Count) throw new ArgumentOutOfRangeException(nameof(array));
Marshal.Copy(handle, array, arrayIndex, Count);
}
/// Returns an enumerator that iterates through the collection.
/// A that can be used to iterate through the collection.
public IEnumerator GetEnumerator()
{
if (IsInvalid) yield break;
for (var i = 0; i < Count; i++)
yield return Marshal.ReadByte(handle, i);
}
/// Determines the index of a specific item in the .
/// The object to locate in the .
/// The index of if found in the list; otherwise, -1.
public int IndexOf(byte item)
{
var i = 0;
foreach (var b in this)
{
if (b == item) return i;
i++;
}
return -1;
}
/// Copies unmanaged bytes to a managed byte array.
/// Copied byte array.
public byte[] ToArray()
{
var array = new byte[Count];
CopyTo(array, 0);
return array;
}
/// Adds an item to the .
/// The object to add to the .
///
/// The position into which the new element was inserted, or -1 to indicate that the item was not inserted into the collection.
///
int IList.Add(object value) => throw new NotSupportedException();
/// Adds an item to the .
/// The object to add to the .
///
[ExcludeFromCodeCoverage]
void ICollection.Add(byte item) => ((IList)this).Add(item);
///
/// Determines whether the current collection object precedes, occurs in the same position as, or follows another object in the sort order.
///
/// The object to compare with the current instance.
///
/// An object that compares members of the current collection object with the corresponding members of .
///
///
/// An integer that indicates the relationship of the current collection object to , as shown in the
/// following table.Return valueDescription-1The current instance precedes .0The current instance and
/// are equal.1The current instance follows .
///
int IStructuralComparable.CompareTo(object other, IComparer comparer)
{
if (comparer == null)
throw new ArgumentNullException(nameof(comparer));
if (other == null)
return 1;
if (!(other is IEnumerable o))
throw new ArgumentOutOfRangeException(nameof(other), @"Other value is not enumerable.");
var l = other as ICollection ?? new List(o);
if (Count != l.Count)
throw new ArgumentOutOfRangeException(nameof(other), @"Other value doesn't have the same number of elements.");
using (var tenum = GetEnumerator())
using (var oenum = l.GetEnumerator())
{
while (tenum.MoveNext() && oenum.MoveNext())
{
var i = comparer.Compare(tenum.Current, oenum.Current);
if (i != 0) return i;
}
}
return 0;
}
/// Determines whether the contains a specific value.
/// The object to locate in the .
/// true if the is found in the ; otherwise, false.
bool IList.Contains(object value) => value != null && Contains((byte)value);
///
/// Copies the elements of the to an , starting at a
/// particular index.
///
///
/// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing.
///
/// The zero-based index in at which copying begins.
/// array
/// array
void ICollection.CopyTo(Array array, int index)
{
if (array == null) throw new ArgumentNullException(nameof(array));
if (array.Rank != 1 || array.Length - index < Count) throw new ArgumentOutOfRangeException(nameof(array));
for (var i = 0; i < Count; i++)
array.SetValue(this[i], i + index);
}
/// Determines whether the specified , is equal to this instance.
/// The to compare with this instance.
/// The comparer.
/// true if the specified is equal to this instance; otherwise, false.
bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer)
{
if (comparer == null)
throw new ArgumentNullException(nameof(comparer));
if (other == null)
return false;
if (ReferenceEquals(this, other))
return true;
if (!(other is IEnumerable o))
throw new ArgumentOutOfRangeException(nameof(other), @"Other value is not enumerable.");
var l = other as ICollection ?? new List(o);
if (Count != l.Count)
throw new ArgumentOutOfRangeException(nameof(other), @"Other value doesn't have the same number of elements.");
using (var tenum = GetEnumerator())
using (var oenum = l.GetEnumerator())
{
while (tenum.MoveNext() && oenum.MoveNext())
{
if (!comparer.Equals(tenum.Current, oenum.Current))
return false;
}
}
return true;
}
/// Returns an enumerator that iterates through a collection.
/// An object that can be used to iterate through the collection.
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// Returns a hash code for this instance.
/// The comparer.
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
/// comparer
int IStructuralEquatable.GetHashCode(IEqualityComparer comparer)
{
if (comparer == null)
throw new ArgumentNullException(nameof(comparer));
var ret = 0;
for (var i = Size >= 8 ? Size - 8 : 0; i < Size; i++)
ret = CombineHashCodes(ret, comparer.GetHashCode(this[i]));
return ret;
}
/// Determines the index of a specific item in the .
/// The object to locate in the .
/// The index of if found in the list; otherwise, -1.
int IList.IndexOf(object value) => IndexOf((byte)value);
/// Inserts an item to the at the specified index.
/// The zero-based index at which should be inserted.
/// The object to insert into the .
[ExcludeFromCodeCoverage]
void IList.Insert(int index, object value) => ((IList)this).Insert(index, (byte)value);
/// Inserts an item to the at the specified index.
/// The zero-based index at which should be inserted.
/// The object to insert into the .
///
void IList.Insert(int index, byte item) => throw new NotSupportedException();
/// Removes the first occurrence of a specific object from the .
/// The object to remove from the .
[ExcludeFromCodeCoverage]
void IList.Remove(object value) => ((ICollection)this).Remove((byte)value);
/// Removes the first occurrence of a specific object from the .
/// The object to remove from the .
///
/// true if was successfully removed from the ;
/// otherwise, false. This method also returns false if is not found in the original .
///
///
bool ICollection.Remove(byte item) => throw new NotSupportedException();
/// Removes the item at the specified index.
/// The zero-based index of the item to remove.
///
void IList.RemoveAt(int index) => throw new NotSupportedException();
/// Removes the item at the specified index.
/// The zero-based index of the item to remove.
[ExcludeFromCodeCoverage]
void IList.RemoveAt(int index) => ((IList)this).RemoveAt(index);
private static int CombineHashCodes(int h1, int h2) => ((h1 << 5) + h1) ^ h2;
private static void CopyMemory(IntPtr src, IntPtr dest, int length)
{
var size1 = length % 8;
var size8 = length - size1;
int ofs;
// copy multiples of 8 bytes first
for (ofs = 0; ofs < size8; ofs += 8)
Marshal.WriteInt64(dest, ofs, Marshal.ReadInt64(src, ofs));
// copy remaining bytes
for (var n = 0; n < size1; n++, ofs++)
Marshal.WriteByte(dest, ofs, Marshal.ReadByte(src, ofs));
}
}
}