File size: 8,801 Bytes
05c9ac2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
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()
            {
            }
        }
    }
}