您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
234 行
8.6 KiB
234 行
8.6 KiB
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
|
|
namespace Unity.MLAgents.Actuators
|
|
{
|
|
/// <summary>
|
|
/// ActionSegment{T} is a data structure that allows access to a segment of an underlying array
|
|
/// in order to avoid the copying and allocation of sub-arrays. The segment is defined by
|
|
/// the offset into the original array, and an length.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of object stored in the underlying <see cref="Array"/></typeparam>
|
|
public readonly struct ActionSegment<T> : IEnumerable<T>, IEquatable<ActionSegment<T>>
|
|
where T : struct
|
|
{
|
|
/// <summary>
|
|
/// The zero-based offset into the original array at which this segment starts.
|
|
/// </summary>
|
|
public readonly int Offset;
|
|
|
|
/// <summary>
|
|
/// The number of items this segment can access in the underlying array.
|
|
/// </summary>
|
|
public readonly int Length;
|
|
|
|
/// <summary>
|
|
/// An Empty segment which has an offset of 0, a Length of 0, and it's underlying array
|
|
/// is also empty.
|
|
/// </summary>
|
|
public static ActionSegment<T> Empty = new ActionSegment<T>(System.Array.Empty<T>(), 0, 0);
|
|
|
|
static void CheckParameters(IReadOnlyCollection<T> actionArray, int offset, int length)
|
|
{
|
|
#if DEBUG
|
|
if (offset + length > actionArray.Count)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(offset),
|
|
$"Arguments offset: {offset} and length: {length} " +
|
|
$"are out of bounds of actionArray: {actionArray.Count}.");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// Construct an <see cref="ActionSegment{T}"/> with just an actionArray. The <see cref="Offset"/> will
|
|
/// be set to 0 and the <see cref="Length"/> will be set to `actionArray.Length`.
|
|
/// </summary>
|
|
/// <param name="actionArray">The action array to use for the this segment.</param>
|
|
public ActionSegment(T[] actionArray)
|
|
: this(actionArray ?? System.Array.Empty<T>(), 0, actionArray?.Length ?? 0) { }
|
|
|
|
/// <summary>
|
|
/// Construct an <see cref="ActionSegment{T}"/> with an underlying array
|
|
/// and offset, and a length.
|
|
/// </summary>
|
|
/// <param name="actionArray">The underlying array which this segment has a view into</param>
|
|
/// <param name="offset">The zero-based offset into the underlying array.</param>
|
|
/// <param name="length">The length of the segment.</param>
|
|
public ActionSegment(T[] actionArray, int offset, int length)
|
|
{
|
|
#if DEBUG
|
|
CheckParameters(actionArray ?? System.Array.Empty<T>(), offset, length);
|
|
#endif
|
|
Array = actionArray ?? System.Array.Empty<T>();
|
|
Offset = offset;
|
|
Length = length;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the underlying <see cref="Array"/> of this segment.
|
|
/// </summary>
|
|
public T[] Array { get; }
|
|
|
|
/// <summary>
|
|
/// Allows access to the underlying array using array syntax.
|
|
/// </summary>
|
|
/// <param name="index">The zero-based index of the segment.</param>
|
|
/// <exception cref="IndexOutOfRangeException">Thrown when the index is less than 0 or
|
|
/// greater than or equal to <see cref="Length"/></exception>
|
|
public T this[int index]
|
|
{
|
|
get
|
|
{
|
|
if (index < 0 || index > Length)
|
|
{
|
|
throw new IndexOutOfRangeException($"Index out of bounds, expected a number between 0 and {Length}");
|
|
}
|
|
return Array[Offset + index];
|
|
}
|
|
set
|
|
{
|
|
if (index < 0 || index > Length)
|
|
{
|
|
throw new IndexOutOfRangeException($"Index out of bounds, expected a number between 0 and {Length}");
|
|
}
|
|
Array[Offset + index] = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the segment of the backing array to all zeros.
|
|
/// </summary>
|
|
public void Clear()
|
|
{
|
|
System.Array.Clear(Array, Offset, Length);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check if the segment is empty.
|
|
/// </summary>
|
|
/// <returns>Whether or not the segment is empty.</returns>
|
|
public bool IsEmpty()
|
|
{
|
|
return Array == null || Array.Length == 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns an enumerator that iterates through the ActionSegment.
|
|
/// </summary>
|
|
/// <returns>An IEnumerator object that can be used to iterate through the ActionSegment.</returns>
|
|
IEnumerator<T> IEnumerable<T>.GetEnumerator()
|
|
{
|
|
return new Enumerator(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns an enumerator that iterates through the ActionSegment.
|
|
/// </summary>
|
|
/// <returns>An IEnumerator object that can be used to iterate through the ActionSegment.</returns>
|
|
public IEnumerator GetEnumerator()
|
|
{
|
|
return new Enumerator(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Indicates whether the current ActionSegment is equal to another ActionSegment.
|
|
/// </summary>
|
|
/// <param name="obj">An ActionSegment to compare with this ActionSegment.</param>
|
|
/// <returns>true if the current ActionSegment is equal to the other parameter; otherwise, false.</returns>
|
|
public override bool Equals(object obj)
|
|
{
|
|
if (!(obj is ActionSegment<T>))
|
|
{
|
|
return false;
|
|
}
|
|
return Equals((ActionSegment<T>)obj);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Indicates whether the current ActionSegment is equal to another ActionSegment.
|
|
/// </summary>
|
|
/// <param name="other">An ActionSegment to compare with this ActionSegment.</param>
|
|
/// <returns>true if the current ActionSegment is equal to the other parameter; otherwise, false.</returns>
|
|
public bool Equals(ActionSegment<T> other)
|
|
{
|
|
return Offset == other.Offset && Length == other.Length && Array.SequenceEqual(other.Array);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Computes the hash code of the ActionSegment.
|
|
/// </summary>
|
|
/// <returns>A hash code for the current ActionSegment.</returns>
|
|
public override int GetHashCode()
|
|
{
|
|
unchecked
|
|
{
|
|
var hashCode = Offset;
|
|
hashCode = (hashCode * 397) ^ Length;
|
|
hashCode = (hashCode * 397) ^ (Array != null ? Array.GetHashCode() : 0);
|
|
return hashCode;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// A private <see cref="IEnumerator{T}"/> for the <see cref="ActionSegment{T}"/> value type which follows its
|
|
/// rules of being a view into an underlying <see cref="Array"/>.
|
|
/// </summary>
|
|
struct Enumerator : IEnumerator<T>
|
|
{
|
|
readonly T[] m_Array;
|
|
readonly int m_Start;
|
|
readonly int m_End; // cache Offset + Count, since it's a little slow
|
|
int m_Current;
|
|
|
|
internal Enumerator(ActionSegment<T> arraySegment)
|
|
{
|
|
Debug.Assert(arraySegment.Array != null);
|
|
Debug.Assert(arraySegment.Offset >= 0);
|
|
Debug.Assert(arraySegment.Length >= 0);
|
|
Debug.Assert(arraySegment.Offset + arraySegment.Length <= arraySegment.Array.Length);
|
|
|
|
m_Array = arraySegment.Array;
|
|
m_Start = arraySegment.Offset;
|
|
m_End = arraySegment.Offset + arraySegment.Length;
|
|
m_Current = arraySegment.Offset - 1;
|
|
}
|
|
|
|
public bool MoveNext()
|
|
{
|
|
if (m_Current < m_End)
|
|
{
|
|
m_Current++;
|
|
return m_Current < m_End;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public T Current
|
|
{
|
|
get
|
|
{
|
|
if (m_Current < m_Start)
|
|
throw new InvalidOperationException("Enumerator not started.");
|
|
if (m_Current >= m_End)
|
|
throw new InvalidOperationException("Enumerator has reached the end already.");
|
|
return m_Array[m_Current];
|
|
}
|
|
}
|
|
|
|
object IEnumerator.Current => Current;
|
|
|
|
void IEnumerator.Reset()
|
|
{
|
|
m_Current = m_Start - 1;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
}
|
|
}
|
|
}
|
|
}
|