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 }