/ src / ARMeilleure / State / V128.cs
V128.cs
  1  using System;
  2  using System.Runtime.CompilerServices;
  3  using System.Runtime.InteropServices;
  4  
  5  namespace ARMeilleure.State
  6  {
  7      /// <summary>
  8      /// Represents a 128-bit vector.
  9      /// </summary>
 10      [StructLayout(LayoutKind.Sequential, Size = 16)]
 11      public struct V128 : IEquatable<V128>
 12      {
 13          // _e0 & _e1 could be marked as readonly, however they are not readonly because we modify them through the Unsafe
 14          // APIs. This also means that one should be careful when changing the layout of this struct.
 15  
 16          private readonly ulong _e0;
 17          private readonly ulong _e1;
 18  
 19          /// <summary>
 20          /// Gets a new <see cref="V128"/> with all bits set to zero.
 21          /// </summary>
 22          public static V128 Zero => new(0, 0);
 23  
 24          /// <summary>
 25          /// Initializes a new instance of the <see cref="V128"/> struct with the specified <see cref="double"/> value
 26          /// as a scalar.
 27          /// </summary>
 28          /// <param name="value">Scalar value</param>
 29          public V128(double value) : this(value, 0) { }
 30  
 31          /// <summary>
 32          /// Initializes a new instance of the <see cref="V128"/> struct with the specified <see cref="double"/> elements.
 33          /// </summary>
 34          /// <param name="e0">Element 0</param>
 35          /// <param name="e1">Element 1</param>
 36          public V128(double e0, double e1)
 37          {
 38              _e0 = (ulong)BitConverter.DoubleToInt64Bits(e0);
 39              _e1 = (ulong)BitConverter.DoubleToInt64Bits(e1);
 40          }
 41  
 42          /// <summary>
 43          /// Initializes a new instance of the <see cref="V128"/> struct with the specified <see cref="float"/> value as a
 44          /// scalar.
 45          /// </summary>
 46          /// <param name="value">Scalar value</param>
 47          public V128(float value) : this(value, 0, 0, 0) { }
 48  
 49          /// <summary>
 50          /// Initializes a new instance of the <see cref="V128"/> struct with the specified <see cref="float"/> elements.
 51          /// </summary>
 52          /// <param name="e0">Element 0</param>
 53          /// <param name="e1">Element 1</param>
 54          /// <param name="e2">Element 2</param>
 55          /// <param name="e3">Element 3</param>
 56          public V128(float e0, float e1, float e2, float e3)
 57          {
 58              _e0 = (ulong)(uint)BitConverter.SingleToInt32Bits(e0) << 0;
 59              _e0 |= (ulong)(uint)BitConverter.SingleToInt32Bits(e1) << 32;
 60              _e1 = (ulong)(uint)BitConverter.SingleToInt32Bits(e2) << 0;
 61              _e1 |= (ulong)(uint)BitConverter.SingleToInt32Bits(e3) << 32;
 62          }
 63  
 64          /// <summary>
 65          /// Initializes a new instance of the <see cref="V128"/> struct with the specified <see cref="ulong"/>
 66          /// elements.
 67          /// </summary>
 68          /// <param name="e0">Element 0</param>
 69          /// <param name="e1">Element 1</param>
 70          public V128(long e0, long e1) : this((ulong)e0, (ulong)e1) { }
 71  
 72          /// <summary>
 73          /// Initializes a new instance of the <see cref="V128"/> struct with the specified <see cref="long"/> elements.
 74          /// </summary>
 75          /// <param name="e0">Element 0</param>
 76          /// <param name="e1">Element 1</param>
 77          public V128(ulong e0, ulong e1)
 78          {
 79              _e0 = e0;
 80              _e1 = e1;
 81          }
 82  
 83          /// <summary>
 84          /// Initializes a new instance of the <see cref="V128"/> struct with the specified <see cref="int"/> elements.
 85          /// </summary>
 86          /// <param name="e0">Element 0</param>
 87          /// <param name="e1">Element 1</param>
 88          /// <param name="e2">Element 2</param>
 89          /// <param name="e3">Element 3</param>
 90          public V128(int e0, int e1, int e2, int e3) : this((uint)e0, (uint)e1, (uint)e2, (uint)e3) { }
 91  
 92          /// <summary>
 93          /// Initializes a new instance of the <see cref="V128"/> struct with the specified <see cref="uint"/> elements.
 94          /// </summary>
 95          /// <param name="e0">Element 0</param>
 96          /// <param name="e1">Element 1</param>
 97          /// <param name="e2">Element 2</param>
 98          /// <param name="e3">Element 3</param>
 99          public V128(uint e0, uint e1, uint e2, uint e3)
100          {
101              _e0 = (ulong)e0 << 0;
102              _e0 |= (ulong)e1 << 32;
103              _e1 = (ulong)e2 << 0;
104              _e1 |= (ulong)e3 << 32;
105          }
106  
107          /// <summary>
108          /// Initializes a new instance of the <see cref="V128"/> struct from the specified <see cref="byte"/> array.
109          /// </summary>
110          /// <param name="data"><see cref="byte"/> array to use</param>
111          public V128(byte[] data)
112          {
113              _e0 = (ulong)BitConverter.ToInt64(data, 0);
114              _e1 = (ulong)BitConverter.ToInt64(data, 8);
115          }
116  
117          /// <summary>
118          /// Returns the value of the <see cref="V128"/> as a <typeparamref name="T"/> scalar.
119          /// </summary>
120          /// <typeparam name="T">Type of scalar</typeparam>
121          /// <returns>Value of the <see cref="V128"/> as a <typeparamref name="T"/> scalar</returns>
122          /// <exception cref="ArgumentOutOfRangeException">Size of <typeparamref name="T"/> is larger than 16 bytes</exception>
123          public T As<T>() where T : unmanaged
124          {
125              return Extract<T>(0);
126          }
127  
128          /// <summary>
129          /// Extracts the element at the specified index as a <typeparamref name="T"/> from the <see cref="V128"/>.
130          /// </summary>
131          /// <typeparam name="T">Element type</typeparam>
132          /// <param name="index">Index of element</param>
133          /// <returns>Element at the specified index as a <typeparamref name="T"/> from the <see cref="V128"/></returns>
134          /// <exception cref="ArgumentOutOfRangeException">
135          /// <paramref name="index"/> is out of bound or the size of <typeparamref name="T"/> is larger than 16 bytes
136          /// </exception>
137          public T Extract<T>(int index) where T : unmanaged
138          {
139              if ((uint)index >= GetElementCount<T>())
140              {
141                  ThrowIndexOutOfRange();
142              }
143  
144              // Performs:
145              //  return *((*T)this + index);
146              return Unsafe.Add(ref Unsafe.As<V128, T>(ref this), index);
147          }
148  
149          /// <summary>
150          /// Inserts the specified value into the element at the specified index in the <see cref="V128"/>.
151          /// </summary>
152          /// <typeparam name="T">Element type</typeparam>
153          /// <param name="index">Index of element</param>
154          /// <param name="value">Value to insert</param>
155          /// <exception cref="ArgumentOutOfRangeException">
156          /// <paramref name="index"/> is out of bound or the size of <typeparamref name="T"/> is larger than 16 bytes
157          /// </exception>
158          public void Insert<T>(int index, T value) where T : unmanaged
159          {
160              if ((uint)index >= GetElementCount<T>())
161              {
162                  ThrowIndexOutOfRange();
163              }
164  
165              // Performs:
166              //  *((*T)this + index) = value;
167              Unsafe.Add(ref Unsafe.As<V128, T>(ref this), index) = value;
168          }
169  
170          /// <summary>
171          /// Returns a new <see cref="byte"/> array which represents the <see cref="V128"/>.
172          /// </summary>
173          /// <returns>A new <see cref="byte"/> array which represents the <see cref="V128"/></returns>
174          public readonly byte[] ToArray()
175          {
176              byte[] data = new byte[16];
177              Span<byte> span = data;
178  
179              BitConverter.TryWriteBytes(span, _e0);
180              BitConverter.TryWriteBytes(span[8..], _e1);
181  
182              return data;
183          }
184  
185          /// <summary>
186          /// Performs a bitwise logical left shift on the specified <see cref="V128"/> by the specified shift count.
187          /// </summary>
188          /// <param name="x"><see cref="V128"/> instance</param>
189          /// <param name="shift">Number of shifts</param>
190          /// <returns>Result of left shift</returns>
191          /// <remarks>
192          /// This supports shift counts up to 63; anything above may result in unexpected behaviour.
193          /// </remarks>
194          public static V128 operator <<(V128 x, int shift)
195          {
196              if (shift == 0)
197              {
198                  return new V128(x._e0, x._e1);
199              }
200  
201              ulong shiftOut = x._e0 >> (64 - shift);
202  
203              return new V128(x._e0 << shift, (x._e1 << shift) | shiftOut);
204          }
205  
206          /// <summary>
207          /// Performs a bitwise logical right shift on the specified <see cref="V128"/> by the specified shift count.
208          /// </summary>
209          /// <param name="x"><see cref="V128"/> instance</param>
210          /// <param name="shift">Number of shifts</param>
211          /// <returns>Result of right shift</returns>
212          /// <remarks>
213          /// This supports shift counts up to 63; anything above may result in unexpected behaviour.
214          /// </remarks>
215          public static V128 operator >>(V128 x, int shift)
216          {
217              if (shift == 0)
218              {
219                  return new V128(x._e0, x._e1);
220              }
221  
222              ulong shiftOut = x._e1 & ((1UL << shift) - 1);
223  
224              return new V128((x._e0 >> shift) | (shiftOut << (64 - shift)), x._e1 >> shift);
225          }
226  
227          /// <summary>
228          /// Performs a bitwise not on the specified <see cref="V128"/>.
229          /// </summary>
230          /// <param name="x">Target <see cref="V128"/></param>
231          /// <returns>Result of not operation</returns>
232          public static V128 operator ~(V128 x) => new(~x._e0, ~x._e1);
233  
234          /// <summary>
235          /// Performs a bitwise and on the specified <see cref="V128"/> instances.
236          /// </summary>
237          /// <param name="x">First instance</param>
238          /// <param name="y">Second instance</param>
239          /// <returns>Result of and operation</returns>
240          public static V128 operator &(V128 x, V128 y) => new(x._e0 & y._e0, x._e1 & y._e1);
241  
242          /// <summary>
243          /// Performs a bitwise or on the specified <see cref="V128"/> instances.
244          /// </summary>
245          /// <param name="x">First instance</param>
246          /// <param name="y">Second instance</param>
247          /// <returns>Result of or operation</returns>
248          public static V128 operator |(V128 x, V128 y) => new(x._e0 | y._e0, x._e1 | y._e1);
249  
250          /// <summary>
251          /// Performs a bitwise exlusive or on the specified <see cref="V128"/> instances.
252          /// </summary>
253          /// <param name="x">First instance</param>
254          /// <param name="y">Second instance</param>
255          /// <returns>Result of exclusive or operation</returns>
256          public static V128 operator ^(V128 x, V128 y) => new(x._e0 ^ y._e0, x._e1 ^ y._e1);
257  
258          /// <summary>
259          /// Determines if the specified <see cref="V128"/> instances are equal.
260          /// </summary>
261          /// <param name="x">First instance</param>
262          /// <param name="y">Second instance</param>
263          /// <returns>true if equal; otherwise false</returns>
264          public static bool operator ==(V128 x, V128 y) => x.Equals(y);
265  
266          /// <summary>
267          /// Determines if the specified <see cref="V128"/> instances are not equal.
268          /// </summary>
269          /// <param name="x">First instance</param>
270          /// <param name="y">Second instance</param>
271          /// <returns>true if not equal; otherwise false</returns>
272          public static bool operator !=(V128 x, V128 y) => !x.Equals(y);
273  
274          /// <summary>
275          /// Determines if the specified <see cref="V128"/> is equal to this <see cref="V128"/> instance.
276          /// </summary>
277          /// <param name="other">Other <see cref="V128"/> instance</param>
278          /// <returns>true if equal; otherwise false</returns>
279          public readonly bool Equals(V128 other)
280          {
281              return other._e0 == _e0 && other._e1 == _e1;
282          }
283  
284          /// <summary>
285          /// Determines if the specified <see cref="object"/> is equal to this <see cref="V128"/> instance.
286          /// </summary>
287          /// <param name="obj">Other <see cref="object"/> instance</param>
288          /// <returns>true if equal; otherwise false</returns>
289          public readonly override bool Equals(object obj)
290          {
291              return obj is V128 vector && Equals(vector);
292          }
293  
294          /// <inheritdoc/>
295          public readonly override int GetHashCode()
296          {
297              return HashCode.Combine(_e0, _e1);
298          }
299  
300          /// <inheritdoc/>
301          public readonly override string ToString()
302          {
303              return $"0x{_e1:X16}{_e0:X16}";
304          }
305  
306          private static uint GetElementCount<T>() where T : unmanaged
307          {
308              return (uint)(Unsafe.SizeOf<V128>() / Unsafe.SizeOf<T>());
309          }
310  
311          private static void ThrowIndexOutOfRange()
312          {
313              throw new ArgumentOutOfRangeException("index");
314          }
315      }
316  }