This commit is contained in:
2025-01-17 13:10:42 +01:00
commit 4536213c91
15115 changed files with 1442174 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 888687b767334731951f3f18ffef0b75
timeCreated: 1506924199

View File

@@ -0,0 +1,161 @@
using System;
using System.Linq;
using System.Reflection;
using System.Text;
////REVIEW: this seems like it should be #if UNITY_EDITOR
namespace UnityEngine.InputSystem.Utilities
{
internal static class CSharpCodeHelpers
{
public static bool IsProperIdentifier(string name)
{
if (string.IsNullOrEmpty(name))
return false;
if (char.IsDigit(name[0]))
return false;
for (var i = 0; i < name.Length; ++i)
{
var ch = name[i];
if (!char.IsLetterOrDigit(ch) && ch != '_')
return false;
}
return true;
}
public static bool IsEmptyOrProperIdentifier(string name)
{
if (string.IsNullOrEmpty(name))
return true;
return IsProperIdentifier(name);
}
public static bool IsEmptyOrProperNamespaceName(string name)
{
if (string.IsNullOrEmpty(name))
return true;
return name.Split('.').All(IsProperIdentifier);
}
////TODO: this one should add the @escape automatically so no other code has to worry
public static string MakeIdentifier(string name, string suffix = "")
{
if (string.IsNullOrEmpty(name))
throw new ArgumentNullException(nameof(name));
if (char.IsDigit(name[0]))
name = "_" + name;
// See if we have invalid characters in the name.
var nameHasInvalidCharacters = false;
for (var i = 0; i < name.Length; ++i)
{
var ch = name[i];
if (!char.IsLetterOrDigit(ch) && ch != '_')
{
nameHasInvalidCharacters = true;
break;
}
}
// If so, create a new string where we remove them.
if (nameHasInvalidCharacters)
{
var buffer = new StringBuilder();
for (var i = 0; i < name.Length; ++i)
{
var ch = name[i];
if (char.IsLetterOrDigit(ch) || ch == '_')
buffer.Append(ch);
}
name = buffer.ToString();
}
return name + suffix;
}
public static string MakeTypeName(string name, string suffix = "")
{
var symbolName = MakeIdentifier(name, suffix);
if (char.IsLower(symbolName[0]))
symbolName = char.ToUpper(symbolName[0]) + symbolName.Substring(1);
return symbolName;
}
#if UNITY_EDITOR
public static string MakeAutoGeneratedCodeHeader(string toolName, string toolVersion, string sourceFileName = null)
{
return
"//------------------------------------------------------------------------------\n"
+ "// <auto-generated>\n"
+ $"// This code was auto-generated by {toolName}\n"
+ $"// version {toolVersion}\n"
+ (string.IsNullOrEmpty(sourceFileName) ? "" : $"// from {sourceFileName}\n")
+ "//\n"
+ "// Changes to this file may cause incorrect behavior and will be lost if\n"
+ "// the code is regenerated.\n"
+ "// </auto-generated>\n"
+ "//------------------------------------------------------------------------------\n";
}
public static string ToLiteral(this object value)
{
if (value == null)
return "null";
var type = value.GetType();
if (type == typeof(bool))
{
if ((bool)value)
return "true";
return "false";
}
if (type == typeof(char))
return $"'\\u{(int)(char)value:X2}'";
if (type == typeof(float))
return value + "f";
if (type == typeof(uint) || type == typeof(ulong))
return value + "u";
if (type == typeof(long))
return value + "l";
if (type.IsEnum)
{
var enumValue = type.GetEnumName(value);
if (!string.IsNullOrEmpty(enumValue))
return $"{type.FullName.Replace("+", ".")}.{enumValue}";
}
return value.ToString();
}
public static string GetInitializersForPublicPrimitiveTypeFields(this object instance)
{
var type = instance.GetType();
var defaults = Activator.CreateInstance(type);
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public);
var fieldInits = string.Join(", ",
fields.Where(f => (f.FieldType.IsPrimitive || f.FieldType.IsEnum) && !f.GetValue(instance).Equals(f.GetValue(defaults)))
.Select(f => $"{f.Name} = {f.GetValue(instance).ToLiteral()}"));
if (string.IsNullOrEmpty(fieldInits))
return "()";
return " { " + fieldInits + " }";
}
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7c6a2891083312645a7be416176261c1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,78 @@
namespace UnityEngine.InputSystem.Utilities
{
// Keeps a copy of the callback list while executing so that the callback list can safely
// be mutated from within callbacks.
internal struct CallbackArray<TDelegate>
where TDelegate : System.Delegate
{
private bool m_CannotMutateCallbacksArray;
private InlinedArray<TDelegate> m_Callbacks;
private InlinedArray<TDelegate> m_CallbacksToAdd;
private InlinedArray<TDelegate> m_CallbacksToRemove;
public int length => m_Callbacks.length;
public TDelegate this[int index] => m_Callbacks[index];
public void Clear()
{
m_Callbacks.Clear();
m_CallbacksToAdd.Clear();
m_CallbacksToRemove.Clear();
}
public void AddCallback(TDelegate dlg)
{
if (m_CannotMutateCallbacksArray)
{
if (m_CallbacksToAdd.Contains(dlg))
return;
var removeIndex = m_CallbacksToRemove.IndexOf(dlg);
if (removeIndex != -1)
m_CallbacksToRemove.RemoveAtByMovingTailWithCapacity(removeIndex);
m_CallbacksToAdd.AppendWithCapacity(dlg);
return;
}
if (!m_Callbacks.Contains(dlg))
m_Callbacks.AppendWithCapacity(dlg, capacityIncrement: 4);
}
public void RemoveCallback(TDelegate dlg)
{
if (m_CannotMutateCallbacksArray)
{
if (m_CallbacksToRemove.Contains(dlg))
return;
var addIndex = m_CallbacksToAdd.IndexOf(dlg);
if (addIndex != -1)
m_CallbacksToAdd.RemoveAtByMovingTailWithCapacity(addIndex);
m_CallbacksToRemove.AppendWithCapacity(dlg);
return;
}
var index = m_Callbacks.IndexOf(dlg);
if (index >= 0)
m_Callbacks.RemoveAtWithCapacity(index);
}
public void LockForChanges()
{
m_CannotMutateCallbacksArray = true;
}
public void UnlockForChanges()
{
m_CannotMutateCallbacksArray = false;
// Process mutations that have happened while we were executing callbacks.
for (var i = 0; i < m_CallbacksToRemove.length; ++i)
RemoveCallback(m_CallbacksToRemove[i]);
for (var i = 0; i < m_CallbacksToAdd.length; ++i)
AddCallback(m_CallbacksToAdd[i]);
m_CallbacksToAdd.Clear();
m_CallbacksToRemove.Clear();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2349376ef4c6487db94a6e15873ee6a8
timeCreated: 1621355960

View File

@@ -0,0 +1,70 @@
using System.Collections.Generic;
namespace UnityEngine.InputSystem.Utilities
{
/// <summary>
/// Compare two <see cref="Vector2"/> by magnitude.
/// </summary>
/// <example>
/// <code>
/// </code>
/// public class CompositeWithVector2Part : InputBindingComposite&lt;Vector2&gt;
/// {
/// [InputControl(layout = "Vector2")]
/// public int part;
///
/// public override Vector2 ReadValue(ref InputBindingCompositeContext context)
/// {
/// // Return the Vector3 with the greatest magnitude.
/// return context.ReadValue&lt;Vector2, Vector2MagnitudeComparer&gt;(part);
/// }
/// }
/// </example>
public struct Vector2MagnitudeComparer : IComparer<Vector2>
{
public int Compare(Vector2 x, Vector2 y)
{
var lenx = x.sqrMagnitude;
var leny = y.sqrMagnitude;
if (lenx < leny)
return -1;
if (lenx > leny)
return 1;
return 0;
}
}
/// <summary>
/// Compare two <see cref="Vector3"/> by magnitude.
/// </summary>
/// <example>
/// <code>
/// </code>
/// public class CompositeWithVector3Part : InputBindingComposite&lt;Vector3&gt;
/// {
/// [InputControl(layout = "Vector3")]
/// public int part;
///
/// public override Vector3 ReadValue(ref InputBindingCompositeContext context)
/// {
/// // Return the Vector3 with the greatest magnitude.
/// return context.ReadValue&lt;Vector3, Vector2MagnitudeComparer&gt;(part);
/// }
/// }
/// </example>
public struct Vector3MagnitudeComparer : IComparer<Vector3>
{
public int Compare(Vector3 x, Vector3 y)
{
var lenx = x.sqrMagnitude;
var leny = y.sqrMagnitude;
if (lenx < leny)
return -1;
if (lenx > leny)
return 1;
return 0;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6e687839a3c5f7a4ea700f2967051b3d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,195 @@
using System;
using Unity.Profiling;
namespace UnityEngine.InputSystem.Utilities
{
internal static class DelegateHelpers
{
// InvokeCallbacksSafe protects both against the callback getting removed while being called
// and against exceptions being thrown by the callback.
public static void InvokeCallbacksSafe(ref CallbackArray<Action> callbacks, ProfilerMarker marker, string callbackName, object context = null)
{
if (callbacks.length == 0)
return;
marker.Begin();
callbacks.LockForChanges();
for (var i = 0; i < callbacks.length; ++i)
{
try
{
callbacks[i]();
}
catch (Exception exception)
{
Debug.LogException(exception);
if (context != null)
Debug.LogError($"{exception.GetType().Name} while executing '{callbackName}' callbacks of '{context}'");
else
Debug.LogError($"{exception.GetType().Name} while executing '{callbackName}' callbacks");
}
}
callbacks.UnlockForChanges();
marker.End();
}
public static void InvokeCallbacksSafe<TValue>(ref CallbackArray<Action<TValue>> callbacks, TValue argument, string callbackName, object context = null)
{
if (callbacks.length == 0)
return;
Profiling.Profiler.BeginSample(callbackName);
callbacks.LockForChanges();
for (var i = 0; i < callbacks.length; ++i)
{
try
{
callbacks[i](argument);
}
catch (Exception exception)
{
Debug.LogException(exception);
if (context != null)
Debug.LogError($"{exception.GetType().Name} while executing '{callbackName}' callbacks of '{context}'");
else
Debug.LogError($"{exception.GetType().Name} while executing '{callbackName}' callbacks");
}
}
callbacks.UnlockForChanges();
Profiling.Profiler.EndSample();
}
public static void InvokeCallbacksSafe<TValue1, TValue2>(ref CallbackArray<Action<TValue1, TValue2>> callbacks, TValue1 argument1, TValue2 argument2, ProfilerMarker marker, string callbackName, object context = null)
{
if (callbacks.length == 0)
return;
marker.Begin();
callbacks.LockForChanges();
for (var i = 0; i < callbacks.length; ++i)
{
try
{
callbacks[i](argument1, argument2);
}
catch (Exception exception)
{
Debug.LogException(exception);
if (context != null)
Debug.LogError($"{exception.GetType().Name} while executing '{callbackName}' callbacks of '{context}'");
else
Debug.LogError($"{exception.GetType().Name} while executing '{callbackName}' callbacks");
}
}
callbacks.UnlockForChanges();
marker.End();
}
public static bool InvokeCallbacksSafe_AnyCallbackReturnsTrue<TValue1, TValue2>(ref CallbackArray<Func<TValue1, TValue2, bool>> callbacks,
TValue1 argument1, TValue2 argument2, string callbackName, object context = null)
{
if (callbacks.length == 0)
return true;
Profiling.Profiler.BeginSample(callbackName);
callbacks.LockForChanges();
for (var i = 0; i < callbacks.length; ++i)
{
try
{
if (callbacks[i](argument1, argument2))
{
callbacks.UnlockForChanges();
Profiling.Profiler.EndSample();
return true;
}
}
catch (Exception exception)
{
Debug.LogException(exception);
if (context != null)
Debug.LogError($"{exception.GetType().Name} while executing '{callbackName}' callbacks of '{context}'");
else
Debug.LogError($"{exception.GetType().Name} while executing '{callbackName}' callbacks");
}
}
callbacks.UnlockForChanges();
Profiling.Profiler.EndSample();
return false;
}
/// <summary>
/// Invokes the given callbacks and also invokes any callback returned from the result of the first.
/// </summary>
/// <seealso cref="System.Action"/>
/// <remarks>
/// Allows an chaining up an additional, optional block of code to the original callback
/// and allow the external code make the decision about whether this code should be executed.
/// </remarks>
public static void InvokeCallbacksSafe_AndInvokeReturnedActions<TValue>(
ref CallbackArray<Func<TValue, Action>> callbacks, TValue argument,
string callbackName, object context = null)
{
if (callbacks.length == 0)
return;
Profiling.Profiler.BeginSample(callbackName);
callbacks.LockForChanges();
for (var i = 0; i < callbacks.length; ++i)
{
try
{
callbacks[i](argument)?.Invoke();
}
catch (Exception exception)
{
Debug.LogException(exception);
if (context != null)
Debug.LogError($"{exception.GetType().Name} while executing '{callbackName}' callbacks of '{context}'");
else
Debug.LogError($"{exception.GetType().Name} while executing '{callbackName}' callbacks");
}
}
callbacks.UnlockForChanges();
Profiling.Profiler.EndSample();
}
/// <summary>
/// Invokes the given callbacks and returns true if any of them returned a non-null result.
/// </summary>
/// <remarks>
/// Returns false if every callback invocation returned null.
/// </remarks>
public static bool InvokeCallbacksSafe_AnyCallbackReturnsObject<TValue, TReturn>(
ref CallbackArray<Func<TValue, TReturn>> callbacks, TValue argument,
string callbackName, object context = null)
{
if (callbacks.length == 0)
return false;
Profiling.Profiler.BeginSample(callbackName);
callbacks.LockForChanges();
for (var i = 0; i < callbacks.length; ++i)
{
try
{
var ret = callbacks[i](argument);
if (ret != null)
{
callbacks.UnlockForChanges();
Profiling.Profiler.EndSample();
return true;
}
}
catch (Exception exception)
{
Debug.LogException(exception);
if (context != null)
Debug.LogError($"{exception.GetType().Name} while executing '{callbackName}' callbacks of '{context}'");
else
Debug.LogError($"{exception.GetType().Name} while executing '{callbackName}' callbacks");
}
}
callbacks.UnlockForChanges();
Profiling.Profiler.EndSample();
return false;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bb83bbf668a324899963d1ea424c5ad0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,22 @@
using System;
namespace UnityEngine.InputSystem.Utilities
{
/// <summary>
/// Provide a format string to use when creating display strings for instances of the class.
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = true)]
public class DisplayStringFormatAttribute : Attribute
{
/// <summary>
/// Format template string in the form of "{namedPart} verbatimText". All named parts enclosed in
/// curly braces are replaced from context whereas other text is included as is.
/// </summary>
public string formatString { get; set; }
public DisplayStringFormatAttribute(string formatString)
{
this.formatString = formatString;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d2c401960c5dc624c8970b2bee0371dc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,69 @@
using UnityEngine.InputSystem.Utilities;
namespace UnityEngine.InputSystem
{
/// <summary>
/// Struct replacement for System.Collections.Bitfield.
/// </summary>
/// <remarks>
/// We don't want the extra heap object just for keeping the header
/// state of the bitfield. This struct directly embeds the header
/// into the owner. Also doesn't allocate any array while length is
/// less than or equal to 64 bits.
/// </remarks>
internal struct DynamicBitfield
{
public InlinedArray<ulong> array;
public int length;
public void SetLength(int newLength)
{
// Don't touch array size if we don't have to. We're fine having a
// larger array to work with if it's already in place.
var ulongCount = BitCountToULongCount(newLength);
if (array.length < ulongCount)
array.SetLength(ulongCount);
length = newLength;
}
public void SetBit(int bitIndex)
{
Debug.Assert(bitIndex >= 0);
Debug.Assert(bitIndex < length);
array[bitIndex / 64] |= 1UL << (bitIndex % 64);
}
public bool TestBit(int bitIndex)
{
Debug.Assert(bitIndex >= 0);
Debug.Assert(bitIndex < length);
return (array[bitIndex / 64] & (1UL << (bitIndex % 64))) != 0;
}
public void ClearBit(int bitIndex)
{
Debug.Assert(bitIndex >= 0);
Debug.Assert(bitIndex < length);
array[bitIndex / 64] &= ~(1UL << (bitIndex % 64));
}
public bool AnyBitIsSet()
{
for (var i = 0; i < array.length; ++i)
{
if (array[i] != 0)
return true;
}
return false;
}
private static int BitCountToULongCount(int bitCount)
{
return (bitCount + 63) / 64;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d45ac8148fd4e264eb17df8586b09581
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,16 @@
using System;
namespace UnityEngine.InputSystem.Utilities
{
internal static class ExceptionHelpers
{
public static bool IsExceptionIndicatingBugInCode(this Exception exception)
{
Debug.Assert(exception != null, "Exception is null");
return exception is NullReferenceException ||
exception is IndexOutOfRangeException ||
exception is ArgumentException;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d717b0e790ee146dd93d1be641984b6a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,178 @@
using System;
using System.Runtime.CompilerServices;
namespace UnityEngine.InputSystem.Utilities
{
/// <summary>
/// A four-character code.
/// </summary>
/// <remarks>
/// A four-character code is a struct containing four byte characters totalling a single <c>int</c>.
/// FourCCs are frequently used in the input system to identify the format of data sent to or from
/// the native backend representing events, input device state or commands sent to input devices.
/// </remarks>
public struct FourCC : IEquatable<FourCC>
{
private int m_Code;
/// <summary>
/// Create a FourCC from the given integer.
/// </summary>
/// <param name="code">FourCC code represented as an <c>int</c>. Character order is
/// little endian. "ABCD" is stored with A in the highest order 8 bits and D in the
/// lowest order 8 bits.</param>
/// <remarks>
/// This method does not actually verify whether the four characters in the code
/// are printable.
/// </remarks>
public FourCC(int code)
{
m_Code = code;
}
/// <summary>
/// Create a FourCC from the given four characters.
/// </summary>
/// <param name="a">First character.</param>
/// <param name="b">Second character.</param>
/// <param name="c">Third character.</param>
/// <param name="d">Fourth character.</param>
public FourCC(char a, char b = ' ', char c = ' ', char d = ' ')
{
m_Code = (a << 24) | (b << 16) | (c << 8) | d;
}
/// <summary>
/// Create a FourCC from the given string.
/// </summary>
/// <param name="str">A string with four characters or less but with at least one character.</param>
/// <exception cref="ArgumentException"><paramref name="str"/> is empty or has more than four characters.</exception>
/// <exception cref="ArgumentNullException"><paramref name="str"/> is <c>null</c>.</exception>
public FourCC(string str)
: this()
{
if (str == null)
throw new ArgumentNullException(nameof(str));
var length = str.Length;
if (length < 1 || length > 4)
throw new ArgumentException("FourCC string must be one to four characters long!", nameof(str));
var a = str[0];
var b = length > 1 ? str[1] : ' ';
var c = length > 2 ? str[2] : ' ';
var d = length > 3 ? str[3] : ' ';
m_Code = (a << 24) | (b << 16) | (c << 8) | d;
}
/// <summary>
/// Convert the given FourCC into an <c>int</c>.
/// </summary>
/// <param name="fourCC">A FourCC.</param>
/// <returns>The four characters of the code packed into one <c>int</c>. Character order is
/// little endian. "ABCD" is stored with A in the highest order 8 bits and D in the
/// lowest order 8 bits.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator int(FourCC fourCC)
{
return fourCC.m_Code;
}
/// <summary>
/// Convert the given <c>int</c> into a FourCC.
/// </summary>
/// <param name="i">FourCC code represented as an <c>int</c>. Character order is
/// little endian. "ABCD" is stored with A in the highest order 8 bits and D in the
/// lowest order 8 bits.</param>
/// <returns>The FourCC converted from <paramref name="i"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator FourCC(int i)
{
return new FourCC(i);
}
/// <summary>
/// Convert the FourCC into a string in the form of "ABCD".
/// </summary>
/// <returns>String representation of the FourCC.</returns>
public override string ToString()
{
return
$"{(char) (m_Code >> 24)}{(char) ((m_Code & 0xff0000) >> 16)}{(char) ((m_Code & 0xff00) >> 8)}{(char) (m_Code & 0xff)}";
}
/// <summary>
/// Compare two FourCCs for equality.
/// </summary>
/// <param name="other">Another FourCC.</param>
/// <returns>True if the two FourCCs are equal, i.e. have the same exact
/// character codes.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(FourCC other)
{
return m_Code == other.m_Code;
}
/// <summary>
/// Compare the FourCC to the given object.
/// </summary>
/// <param name="obj">An object. Can be null.</param>
/// <returns>True if <paramref name="obj"/> is a FourCC that has the same
/// character code sequence.</returns>
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
return false;
return obj is FourCC cc && Equals(cc);
}
/// <summary>
/// Compute a hash code for the FourCC.
/// </summary>
/// <returns>Simply returns the FourCC converted to an <c>int</c>.</returns>
public override int GetHashCode()
{
return m_Code;
}
/// <summary>
/// Compare two FourCCs for equality.
/// </summary>
/// <param name="left">First FourCC.</param>
/// <param name="right">Second FourCC.</param>
/// <returns>True if the two FourCCs are equal, i.e. have the same exact
/// character codes.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator==(FourCC left, FourCC right)
{
return left.m_Code == right.m_Code;
}
/// <summary>
/// Compare two FourCCs for inequality.
/// </summary>
/// <param name="left">First FourCC.</param>
/// <param name="right">Second FourCC.</param>
/// <returns>True if the two FourCCs are not equal, i.e. do not have the same exact
/// character codes.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator!=(FourCC left, FourCC right)
{
return left.m_Code != right.m_Code;
}
// Make annoying Microsoft code analyzer happy.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static FourCC FromInt32(int i)
{
return i;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int ToInt32(FourCC fourCC)
{
return fourCC.m_Code;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ff1bc3762d8741ff8f40d54da690efb1
timeCreated: 1506737298

View File

@@ -0,0 +1,459 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
////REVIEW: what about ignoring 'firstValue' entirely in case length > 1 and putting everything into an array in that case
namespace UnityEngine.InputSystem.Utilities
{
/// <summary>
/// Helper to avoid array allocations if there's only a single value in the array.
/// </summary>
/// <remarks>
/// Also, once more than one entry is necessary, allows treating the extra array as having capacity.
/// This means that, for example, 5 or 10 entries can be allocated in batch rather than growing an
/// array one by one.
/// </remarks>
/// <typeparam name="TValue">Element type for the array.</typeparam>
internal struct InlinedArray<TValue> : IEnumerable<TValue>
{
// We inline the first value so if there's only one, there's
// no additional allocation. If more are added, we allocate an array.
public int length;
public TValue firstValue;
public TValue[] additionalValues;
public int Capacity => additionalValues?.Length + 1 ?? 1;
public InlinedArray(TValue value)
{
length = 1;
firstValue = value;
additionalValues = null;
}
public InlinedArray(TValue firstValue, params TValue[] additionalValues)
{
length = 1 + additionalValues.Length;
this.firstValue = firstValue;
this.additionalValues = additionalValues;
}
public InlinedArray(IEnumerable<TValue> values)
: this()
{
length = values.Count();
if (length > 1)
additionalValues = new TValue[length - 1];
else
additionalValues = null;
var index = 0;
foreach (var value in values)
{
if (index == 0)
firstValue = value;
else
additionalValues[index - 1] = value;
++index;
}
}
public TValue this[int index]
{
get
{
if (index < 0 || index >= length)
throw new ArgumentOutOfRangeException(nameof(index));
if (index == 0)
return firstValue;
return additionalValues[index - 1];
}
set
{
if (index < 0 || index >= length)
throw new ArgumentOutOfRangeException(nameof(index));
if (index == 0)
firstValue = value;
else
additionalValues[index - 1] = value;
}
}
public void Clear()
{
length = 0;
firstValue = default;
additionalValues = null;
}
public void ClearWithCapacity()
{
firstValue = default;
for (var i = 0; i < length - 1; ++i)
additionalValues[i] = default;
length = 0;
}
////REVIEW: This is inconsistent with ArrayHelpers.Clone() which also clones elements
public InlinedArray<TValue> Clone()
{
return new InlinedArray<TValue>
{
length = length,
firstValue = firstValue,
additionalValues = additionalValues != null ? ArrayHelpers.Copy(additionalValues) : null
};
}
public void SetLength(int size)
{
// Null out everything we're cutting off.
if (size < length)
{
for (var i = size; i < length; ++i)
this[i] = default;
}
length = size;
if (size > 1 && (additionalValues == null || additionalValues.Length < size - 1))
Array.Resize(ref additionalValues, size - 1);
}
public TValue[] ToArray()
{
return ArrayHelpers.Join(firstValue, additionalValues);
}
public TOther[] ToArray<TOther>(Func<TValue, TOther> mapFunction)
{
if (length == 0)
return null;
var result = new TOther[length];
for (var i = 0; i < length; ++i)
result[i] = mapFunction(this[i]);
return result;
}
public int IndexOf(TValue value)
{
var comparer = EqualityComparer<TValue>.Default;
if (length > 0)
{
if (comparer.Equals(firstValue, value))
return 0;
if (additionalValues != null)
{
for (var i = 0; i < length - 1; ++i)
if (comparer.Equals(additionalValues[i], value))
return i + 1;
}
}
return -1;
}
public int Append(TValue value)
{
if (length == 0)
{
firstValue = value;
}
else if (additionalValues == null)
{
additionalValues = new TValue[1];
additionalValues[0] = value;
}
else
{
Array.Resize(ref additionalValues, length);
additionalValues[length - 1] = value;
}
var index = length;
++length;
return index;
}
public int AppendWithCapacity(TValue value, int capacityIncrement = 10)
{
if (length == 0)
{
firstValue = value;
}
else
{
var numAdditionalValues = length - 1;
ArrayHelpers.AppendWithCapacity(ref additionalValues, ref numAdditionalValues, value, capacityIncrement: capacityIncrement);
}
var index = length;
++length;
return index;
}
public void AssignWithCapacity(InlinedArray<TValue> values)
{
if (Capacity < values.length && values.length > 1)
additionalValues = new TValue[values.length - 1];
length = values.length;
if (length > 0)
firstValue = values.firstValue;
if (length > 1)
Array.Copy(values.additionalValues, additionalValues, length - 1);
}
public void Append(IEnumerable<TValue> values)
{
foreach (var value in values)
Append(value);
}
public void Remove(TValue value)
{
if (length < 1)
return;
if (EqualityComparer<TValue>.Default.Equals(firstValue, value))
{
RemoveAt(0);
}
else if (additionalValues != null)
{
for (var i = 0; i < length - 1; ++i)
{
if (EqualityComparer<TValue>.Default.Equals(additionalValues[i], value))
{
RemoveAt(i + 1);
break;
}
}
}
}
public void RemoveAtWithCapacity(int index)
{
if (index < 0 || index >= length)
throw new ArgumentOutOfRangeException(nameof(index));
if (index == 0)
{
if (length == 1)
{
firstValue = default;
}
else if (length == 2)
{
firstValue = additionalValues[0];
additionalValues[0] = default;
}
else
{
Debug.Assert(length > 2);
firstValue = additionalValues[0];
var numAdditional = length - 1;
ArrayHelpers.EraseAtWithCapacity(additionalValues, ref numAdditional, 0);
}
}
else
{
var numAdditional = length - 1;
ArrayHelpers.EraseAtWithCapacity(additionalValues, ref numAdditional, index - 1);
}
--length;
}
public void RemoveAt(int index)
{
if (index < 0 || index >= length)
throw new ArgumentOutOfRangeException(nameof(index));
if (index == 0)
{
if (additionalValues != null)
{
firstValue = additionalValues[0];
if (additionalValues.Length == 1)
additionalValues = null;
else
{
Array.Copy(additionalValues, 1, additionalValues, 0, additionalValues.Length - 1);
Array.Resize(ref additionalValues, additionalValues.Length - 1);
}
}
else
{
firstValue = default;
}
}
else
{
Debug.Assert(additionalValues != null);
var numAdditionalValues = length - 1;
if (numAdditionalValues == 1)
{
// Remove only entry in array.
additionalValues = null;
}
else if (index == length - 1)
{
// Remove entry at end.
Array.Resize(ref additionalValues, numAdditionalValues - 1);
}
else
{
// Remove entry at beginning or in middle by pasting together
// into a new array.
var newAdditionalValues = new TValue[numAdditionalValues - 1];
if (index >= 2)
{
// Copy elements before entry.
Array.Copy(additionalValues, 0, newAdditionalValues, 0, index - 1);
}
// Copy elements after entry. We already know that we're not removing
// the last entry so there have to be entries.
Array.Copy(additionalValues, index + 1 - 1, newAdditionalValues, index - 1,
length - index - 1);
additionalValues = newAdditionalValues;
}
}
--length;
}
public void RemoveAtByMovingTailWithCapacity(int index)
{
if (index < 0 || index >= length)
throw new ArgumentOutOfRangeException(nameof(index));
var numAdditionalValues = length - 1;
if (index == 0)
{
if (length > 1)
{
firstValue = additionalValues[numAdditionalValues - 1];
additionalValues[numAdditionalValues - 1] = default;
}
else
{
firstValue = default;
}
}
else
{
Debug.Assert(additionalValues != null);
ArrayHelpers.EraseAtByMovingTail(additionalValues, ref numAdditionalValues, index - 1);
}
--length;
}
public bool RemoveByMovingTailWithCapacity(TValue value)
{
var index = IndexOf(value);
if (index == -1)
return false;
RemoveAtByMovingTailWithCapacity(index);
return true;
}
public bool Contains(TValue value, IEqualityComparer<TValue> comparer)
{
for (var n = 0; n < length; ++n)
if (comparer.Equals(this[n], value))
return true;
return false;
}
public void Merge(InlinedArray<TValue> other)
{
var comparer = EqualityComparer<TValue>.Default;
for (var i = 0; i < other.length; ++i)
{
var value = other[i];
if (Contains(value, comparer))
continue;
////FIXME: this is ugly as it repeatedly copies
Append(value);
}
}
public IEnumerator<TValue> GetEnumerator()
{
return new Enumerator { array = this, index = -1 };
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private struct Enumerator : IEnumerator<TValue>
{
public InlinedArray<TValue> array;
public int index;
public bool MoveNext()
{
if (index >= array.length)
return false;
++index;
return index < array.length;
}
public void Reset()
{
index = -1;
}
public TValue Current => array[index];
object IEnumerator.Current => Current;
public void Dispose()
{
}
}
}
internal static class InputArrayExtensions
{
public static int IndexOfReference<TValue>(this InlinedArray<TValue> array, TValue value)
where TValue : class
{
for (var i = 0; i < array.length; ++i)
if (ReferenceEquals(array[i], value))
return i;
return -1;
}
public static bool Contains<TValue>(this InlinedArray<TValue> array, TValue value)
{
for (var i = 0; i < array.length; ++i)
if (array[i].Equals(value))
return true;
return false;
}
public static bool ContainsReference<TValue>(this InlinedArray<TValue> array, TValue value)
where TValue : class
{
return IndexOfReference(array, value) != -1;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 98a0df93c82e4369b500b62affa160db
timeCreated: 1506841462

View File

@@ -0,0 +1,218 @@
using System;
using System.Globalization;
////TODO: goal should be to end up with this being internal
////TODO: instead of using string.Intern, put them in a custom table and allow passing them around as indices
//// (this will probably also be useful for jobs)
//// when this is implemented, also allow interning directly from Substrings
namespace UnityEngine.InputSystem.Utilities
{
/// <summary>
/// Wraps around a string to allow for faster case-insensitive string comparisons while
/// preserving original casing.
/// </summary>
/// <remarks>
/// Unlike <c>string</c>, InternedStrings can be compared with a quick <c>Object.ReferenceEquals</c>
/// comparison and without actually comparing string contents.
///
/// Also, unlike <c>string</c>, the representation of an empty and a <c>null</c> string is identical.
///
/// Note that all string comparisons using InternedStrings are both case-insensitive and culture-insensitive.
///
/// There is a non-zero cost to creating an InternedString. The first time a new unique InternedString
/// is encountered, there may also be a GC heap allocation.
/// </remarks>
public struct InternedString : IEquatable<InternedString>, IComparable<InternedString>
{
private readonly string m_StringOriginalCase;
private readonly string m_StringLowerCase;
/// <summary>
/// Length of the string in characters. Equivalent to <c>string.Length</c>.
/// </summary>
/// <value>Length of the string.</value>
public int length => m_StringLowerCase?.Length ?? 0;
/// <summary>
/// Initialize the InternedString with the given string. Except if the string is <c>null</c>
/// or empty, this requires an internal lookup (this is the reason the conversion from <c>string</c>
/// to InternedString is not implicit).
/// </summary>
/// <param name="text">A string. Can be null.</param>
/// <remarks>
/// The InternedString preserves the original casing. Meaning that <see cref="ToString()"/> will
/// return the string as it was supplied through <paramref name="text"/>. However, comparison
/// between two InternedStrings is still always just a reference comparisons regardless of case
/// and culture.
///
/// <example>
/// <code>
/// var lowerCase = new InternedString("text");
/// var upperCase = new InternedString("TEXT");
///
/// // This is still just a quick reference comparison:
/// if (lowerCase == upperCase)
/// Debug.Log("True");
///
/// // But this prints the strings in their original casing.
/// Debug.Log(lowerCase);
/// Debug.Log(upperCase);
/// </code>
/// </example>
/// </remarks>
public InternedString(string text)
{
if (string.IsNullOrEmpty(text))
{
m_StringOriginalCase = null;
m_StringLowerCase = null;
}
else
{
////TODO: I think instead of string.Intern() this should use a custom weak-referenced intern table
//// (this way we can also avoid the garbage from ToLower())
m_StringOriginalCase = string.Intern(text);
m_StringLowerCase = string.Intern(text.ToLower(CultureInfo.InvariantCulture));
}
}
/// <summary>
/// Whether the string is empty, i.e. has a <see cref="length"/> of zero. If so, the
/// InternedString corresponds to <c>default(InternedString)</c>.
/// </summary>
/// <returns>True if the string is empty.</returns>
public bool IsEmpty()
{
return m_StringLowerCase == null;
}
/// <summary>
/// Return a lower-case version of the string.
/// </summary>
/// <returns>A lower-case version of the string.</returns>
/// <remarks>
/// InternedStrings internally always store a lower-case version which means that this
/// method does not incur a GC heap allocation cost.
/// </remarks>
public string ToLower()
{
return m_StringLowerCase;
}
/// <summary>
/// Compare the InternedString to given object.
/// </summary>
/// <param name="obj">An object. If it is a <c>string</c>, performs a string comparison. If
/// it is an InternedString, performs an InternedString-comparison. Otherwise returns false.</param>
/// <returns>True if the InternedString is equal to <paramref name="obj"/>.</returns>
public override bool Equals(object obj)
{
if (obj is InternedString other)
return Equals(other);
if (obj is string str)
{
if (m_StringLowerCase == null)
return string.IsNullOrEmpty(str);
return string.Equals(m_StringLowerCase, str.ToLower(CultureInfo.InvariantCulture));
}
return false;
}
/// <summary>
/// Compare two InternedStrings for equality. They are equal if, ignoring case and culture,
/// their text is equal.
/// </summary>
/// <param name="other">Another InternedString.</param>
/// <returns>True if the two InternedStrings are equal.</returns>
/// <remarks>
/// This operation is cheap and does not involve an actual string comparison. Instead,
/// a simple <c>Object.ReferenceEquals</c> comparison is performed.
/// </remarks>
public bool Equals(InternedString other)
{
return ReferenceEquals(m_StringLowerCase, other.m_StringLowerCase);
}
public int CompareTo(InternedString other)
{
return string.Compare(m_StringLowerCase, other.m_StringLowerCase,
StringComparison.InvariantCultureIgnoreCase);
}
/// <summary>
/// Compute a hash code for the string. Equivalent to <c>string.GetHashCode</c>.
/// </summary>
/// <returns>A hash code.</returns>
public override int GetHashCode()
{
if (m_StringLowerCase == null)
return 0;
return m_StringLowerCase.GetHashCode();
}
public override string ToString()
{
return m_StringOriginalCase ?? string.Empty;
}
public static bool operator==(InternedString a, InternedString b)
{
return a.Equals(b);
}
public static bool operator!=(InternedString a, InternedString b)
{
return !a.Equals(b);
}
public static bool operator==(InternedString a, string b)
{
return string.Compare(a.m_StringLowerCase, b.ToLower(CultureInfo.InvariantCulture),
StringComparison.InvariantCultureIgnoreCase) == 0;
}
public static bool operator!=(InternedString a, string b)
{
return string.Compare(a.m_StringLowerCase, b.ToLower(CultureInfo.InvariantCulture),
StringComparison.InvariantCultureIgnoreCase) != 0;
}
public static bool operator==(string a, InternedString b)
{
return string.Compare(a.ToLower(CultureInfo.InvariantCulture), b.m_StringLowerCase,
StringComparison.InvariantCultureIgnoreCase) == 0;
}
public static bool operator!=(string a, InternedString b)
{
return string.Compare(a.ToLower(CultureInfo.InvariantCulture), b.m_StringLowerCase,
StringComparison.InvariantCultureIgnoreCase) != 0;
}
public static bool operator<(InternedString left, InternedString right)
{
return string.Compare(left.m_StringLowerCase, right.m_StringLowerCase,
StringComparison.InvariantCultureIgnoreCase) < 0;
}
public static bool operator>(InternedString left, InternedString right)
{
return string.Compare(left.m_StringLowerCase, right.m_StringLowerCase,
StringComparison.InvariantCultureIgnoreCase) > 0;
}
/// <summary>
/// Convert the given InternedString back to a <c>string</c>. Equivalent to <see cref="ToString()"/>.
/// </summary>
/// <param name="str">An InternedString.</param>
/// <returns>A string.</returns>
public static implicit operator string(InternedString str)
{
return str.ToString();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 640e1e705a694e56835bacff3d7f7b4b
timeCreated: 1508302068

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9e082355c53c54d19a09c1d4f7629360
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 760fac1b814a4d99a4fc534934e4dc99
timeCreated: 1506974628

View File

@@ -0,0 +1,40 @@
using System.Collections.Generic;
namespace UnityEngine.InputSystem.Utilities
{
internal static class MiscHelpers
{
public static TValue GetValueOrDefault<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key)
{
return dictionary.TryGetValue(key, out var value) ? value : default;
}
public static IEnumerable<TValue> EveryNth<TValue>(this IEnumerable<TValue> enumerable, int n, int start = 0)
{
var index = 0;
foreach (var element in enumerable)
{
if (index < start)
{
++index;
continue;
}
if ((index - start) % n == 0)
yield return element;
++index;
}
}
public static int IndexOf<TValue>(this IEnumerable<TValue> enumerable, TValue value)
{
var index = 0;
foreach (var element in enumerable)
if (EqualityComparer<TValue>.Default.Equals(element, value))
return index;
else
++index;
return -1;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9d9d9634637c440896a123c44c9e262b
timeCreated: 1602782357

View File

@@ -0,0 +1,121 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
////REVIEW: why is this public?
////TODO: add array support
////TODO: switch parsing to use to Substring
namespace UnityEngine.InputSystem.Utilities
{
/// <summary>
/// A combination of a name and an optional list of named parameter values. For example, "Clamp(min=1,max=2)".
/// </summary>
public struct NameAndParameters
{
public string name { get; set; }
public ReadOnlyArray<NamedValue> parameters { get; set; }
public override string ToString()
{
if (parameters.Count == 0)
return name;
var parameterString = string.Join(NamedValue.Separator, parameters.Select(x => x.ToString()).ToArray());
return $"{name}({parameterString})";
}
public static IEnumerable<NameAndParameters> ParseMultiple(string text)
{
List<NameAndParameters> list = null;
if (!ParseMultiple(text, ref list))
return Enumerable.Empty<NameAndParameters>();
return list;
}
internal static bool ParseMultiple(string text, ref List<NameAndParameters> list)
{
text = text.Trim();
if (string.IsNullOrEmpty(text))
return false;
if (list == null)
list = new List<NameAndParameters>();
else
list.Clear();
var index = 0;
var textLength = text.Length;
while (index < textLength)
list.Add(ParseNameAndParameters(text, ref index));
return true;
}
internal static string ParseName(string text)
{
if (text == null)
throw new ArgumentNullException(nameof(text));
var index = 0;
return ParseNameAndParameters(text, ref index, true).name;
}
public static NameAndParameters Parse(string text)
{
if (text == null)
throw new ArgumentNullException(nameof(text));
var index = 0;
return ParseNameAndParameters(text, ref index);
}
private static NameAndParameters ParseNameAndParameters(string text, ref int index, bool nameOnly = false)
{
var textLength = text.Length;
// Skip whitespace.
while (index < textLength && char.IsWhiteSpace(text[index]))
++index;
// Parse name.
var nameStart = index;
while (index < textLength)
{
var nextChar = text[index];
if (nextChar == '(' || nextChar == NamedValue.Separator[0] || char.IsWhiteSpace(nextChar))
break;
++index;
}
if (index - nameStart == 0)
throw new ArgumentException($"Expecting name at position {nameStart} in '{text}'", nameof(text));
var name = text.Substring(nameStart, index - nameStart);
if (nameOnly)
return new NameAndParameters {name = name};
// Skip whitespace.
while (index < textLength && char.IsWhiteSpace(text[index]))
++index;
// Parse parameters.
NamedValue[] parameters = null;
if (index < textLength && text[index] == '(')
{
++index;
var closeParenIndex = text.IndexOf(')', index);
if (closeParenIndex == -1)
throw new ArgumentException($"Expecting ')' after '(' at position {index} in '{text}'", nameof(text));
var parameterString = text.Substring(index, closeParenIndex - index);
parameters = NamedValue.ParseMultiple(parameterString);
index = closeParenIndex + 1;
}
if (index < textLength && (text[index] == ',' || text[index] == InputBinding.Separator))
++index;
return new NameAndParameters {name = name, parameters = new ReadOnlyArray<NamedValue>(parameters)};
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0ffbcc8a975d04f12967a085a10aeb7b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,192 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace UnityEngine.InputSystem.Utilities
{
/// <summary>
/// A combination of a name and a value assignment for it.
/// </summary>
public struct NamedValue : IEquatable<NamedValue>
{
public const string Separator = ",";
/// <summary>
/// Name of the parameter.
/// </summary>
public string name { get; set; }
/// <summary>
/// Value of the parameter.
/// </summary>
public PrimitiveValue value { get; set; }
public TypeCode type => value.type;
public NamedValue ConvertTo(TypeCode type)
{
return new NamedValue
{
name = name,
value = value.ConvertTo(type)
};
}
public static NamedValue From<TValue>(string name, TValue value)
where TValue : struct
{
return new NamedValue
{
name = name,
value = PrimitiveValue.From(value)
};
}
public override string ToString()
{
return $"{name}={value}";
}
public bool Equals(NamedValue other)
{
return string.Equals(name, other.name, StringComparison.InvariantCultureIgnoreCase)
&& value == other.value;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
return false;
return obj is NamedValue parameterValue && Equals(parameterValue);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = (name != null ? name.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ value.GetHashCode();
return hashCode;
}
}
public static bool operator==(NamedValue left, NamedValue right)
{
return left.Equals(right);
}
public static bool operator!=(NamedValue left, NamedValue right)
{
return !left.Equals(right);
}
public static NamedValue[] ParseMultiple(string parameterString)
{
if (parameterString == null)
throw new ArgumentNullException(nameof(parameterString));
parameterString = parameterString.Trim();
if (string.IsNullOrEmpty(parameterString))
return null;
var parameterCount = parameterString.CountOccurrences(Separator[0]) + 1;
var parameters = new NamedValue[parameterCount];
var index = 0;
for (var i = 0; i < parameterCount; ++i)
{
var parameter = ParseParameter(parameterString, ref index);
parameters[i] = parameter;
}
return parameters;
}
public static NamedValue Parse(string str)
{
var index = 0;
return ParseParameter(str, ref index);
}
private static NamedValue ParseParameter(string parameterString, ref int index)
{
var parameter = new NamedValue();
var parameterStringLength = parameterString.Length;
// Skip whitespace.
while (index < parameterStringLength && char.IsWhiteSpace(parameterString[index]))
++index;
// Parse name.
var nameStart = index;
while (index < parameterStringLength)
{
var nextChar = parameterString[index];
if (nextChar == '=' || nextChar == Separator[0] || char.IsWhiteSpace(nextChar))
break;
++index;
}
parameter.name = parameterString.Substring(nameStart, index - nameStart);
// Skip whitespace.
while (index < parameterStringLength && char.IsWhiteSpace(parameterString[index]))
++index;
if (index == parameterStringLength || parameterString[index] != '=')
{
// No value given so take "=true" as implied.
parameter.value = true;
}
else
{
++index; // Skip over '='.
// Skip whitespace.
while (index < parameterStringLength && char.IsWhiteSpace(parameterString[index]))
++index;
// Parse value.
var valueStart = index;
while (index < parameterStringLength &&
!(parameterString[index] == Separator[0] || char.IsWhiteSpace(parameterString[index])))
++index;
////TODO: use Substring struct here so that we don't allocate lots of useless strings
var value = parameterString.Substring(valueStart, index - valueStart);
parameter.value = PrimitiveValue.FromString(value);
}
if (index < parameterStringLength && parameterString[index] == Separator[0])
++index;
return parameter;
}
public void ApplyToObject(object instance)
{
if (instance == null)
throw new System.ArgumentNullException(nameof(instance));
var instanceType = instance.GetType();
////REVIEW: what about properties?
var field = instanceType.GetField(name,
BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (field == null)
throw new ArgumentException(
$"Cannot find public field '{name}' in '{instanceType.Name}' (while trying to apply parameter)", nameof(instance));
////REVIEW: would be awesome to be able to do this without boxing
var fieldTypeCode = Type.GetTypeCode(field.FieldType);
field.SetValue(instance, value.ConvertTo(fieldTypeCode).ToObject());
}
public static void ApplyAllToObject<TParameterList>(object instance, TParameterList parameters)
where TParameterList : IEnumerable<NamedValue>
{
foreach (var parameter in parameters)
parameter.ApplyToObject(instance);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0e0c005ff24ec4cfb951eefa16239b9b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,97 @@
using System;
using System.Runtime.CompilerServices;
namespace UnityEngine.InputSystem.Utilities
{
internal static class NumberHelpers
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int AlignToMultipleOf(this int number, int alignment)
{
var remainder = number % alignment;
if (remainder == 0)
return number;
return number + alignment - remainder;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long AlignToMultipleOf(this long number, long alignment)
{
var remainder = number % alignment;
if (remainder == 0)
return number;
return number + alignment - remainder;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint AlignToMultipleOf(this uint number, uint alignment)
{
var remainder = number % alignment;
if (remainder == 0)
return number;
return number + alignment - remainder;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Approximately(double a, double b)
{
return Math.Abs(b - a) < Math.Max(1E-06 * Math.Max(Math.Abs(a), Math.Abs(b)), double.Epsilon * 8);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float IntToNormalizedFloat(int value, int minValue, int maxValue)
{
if (value <= minValue)
return 0.0f;
if (value >= maxValue)
return 1.0f;
// using double here because int.MaxValue is not representable in floats
// as int.MaxValue = 2147483647 will become 2147483648.0 when casted to a float
return (float)(((double)value - minValue) / ((double)maxValue - minValue));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int NormalizedFloatToInt(float value, int intMinValue, int intMaxValue)
{
if (value <= 0.0f)
return intMinValue;
if (value >= 1.0f)
return intMaxValue;
return (int)(value * ((double)intMaxValue - intMinValue) + intMinValue);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float UIntToNormalizedFloat(uint value, uint minValue, uint maxValue)
{
if (value <= minValue)
return 0.0f;
if (value >= maxValue)
return 1.0f;
// using double here because uint.MaxValue is not representable in floats
return (float)(((double)value - minValue) / ((double)maxValue - minValue));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint NormalizedFloatToUInt(float value, uint uintMinValue, uint uintMaxValue)
{
if (value <= 0.0f)
return uintMinValue;
if (value >= 1.0f)
return uintMaxValue;
return (uint)(value * ((double)uintMaxValue - uintMinValue) + uintMinValue);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint RemapUIntBitsToNormalizeFloatToUIntBits(uint value, uint inBitSize, uint outBitSize)
{
var inMaxValue = (uint)((1UL << (int)inBitSize) - 1);
var outMaxValue = (uint)((1UL << (int)outBitSize) - 1);
var normFloat = UIntToNormalizedFloat(value, 0, inMaxValue);
return NormalizedFloatToUInt(normFloat, 0, outMaxValue);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 15eaf34440a14edf94d5a0a00cbded20
timeCreated: 1515636424

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9f8944bd1ba848ab8f95afe7acfb4b79
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,65 @@
using System;
using UnityEngine.InputSystem.LowLevel;
namespace UnityEngine.InputSystem.Utilities
{
internal class ForDeviceEventObservable : IObservable<InputEventPtr>
{
private IObservable<InputEventPtr> m_Source;
private InputDevice m_Device;
private Type m_DeviceType;
public ForDeviceEventObservable(IObservable<InputEventPtr> source, Type deviceType, InputDevice device)
{
m_Source = source;
m_DeviceType = deviceType;
m_Device = device;
}
public IDisposable Subscribe(IObserver<InputEventPtr> observer)
{
return m_Source.Subscribe(new ForDevice(m_DeviceType, m_Device, observer));
}
private class ForDevice : IObserver<InputEventPtr>
{
private IObserver<InputEventPtr> m_Observer;
private InputDevice m_Device;
private Type m_DeviceType;
public ForDevice(Type deviceType, InputDevice device, IObserver<InputEventPtr> observer)
{
m_Device = device;
m_DeviceType = deviceType;
m_Observer = observer;
}
public void OnCompleted()
{
}
public void OnError(Exception error)
{
Debug.LogException(error);
}
public void OnNext(InputEventPtr value)
{
if (m_DeviceType != null)
{
var device = InputSystem.GetDeviceById(value.deviceId);
if (device == null)
return;
if (!m_DeviceType.IsInstanceOfType(device))
return;
}
if (m_Device != null && value.deviceId != m_Device.deviceId)
return;
m_Observer.OnNext(value);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8242c97e8771412fb5f5637b7278e18f
timeCreated: 1627655656

View File

@@ -0,0 +1,243 @@
using System;
using System.Collections.Generic;
using UnityEngine.InputSystem.LowLevel;
namespace UnityEngine.InputSystem.Utilities
{
/// <summary>
/// Extension methods for working with <a ref="https://docs.microsoft.com/en-us/dotnet/api/system.iobservable-1">IObservable</a>
/// in the context of the Input System.
/// </summary>
public static class Observable
{
/// <summary>
/// Filter a stream of observable values by a predicate.
/// </summary>
/// <param name="source">The stream of observable values.</param>
/// <param name="predicate">Filter to apply to the stream. Only values for which the predicate returns true
/// are passed on to <c>OnNext</c> of the observer.</param>
/// <typeparam name="TValue">Value type for the observable stream.</typeparam>
/// <exception cref="ArgumentNullException"><paramref name="source"/> is <c>null</c> -or- <paramref name="predicate"/> is <c>null</c>.</exception>
/// <returns>A new observable that is filtered by the given predicate.</returns>
/// <remarks>
/// <example>
/// <code>
/// InputSystem.onEvent
/// .Where(e => e.HasButtonPress())
/// .Call(e => Debug.Log("Press"));
/// </code>
/// </example>
/// </remarks>
/// <seealso cref="InputEventListener"/>
/// <seealso cref="InputSystem.onEvent"/>
public static IObservable<TValue> Where<TValue>(this IObservable<TValue> source, Func<TValue, bool> predicate)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
if (predicate == null)
throw new ArgumentNullException(nameof(predicate));
return new WhereObservable<TValue>(source, predicate);
}
/// <summary>
/// Transform each value in an observable stream of values into a value of a different type.
/// </summary>
/// <param name="source">The stream of observable values.</param>
/// <param name="filter">Function to transform values in the stream.</param>
/// <typeparam name="TSource">Type of source values to transform from.</typeparam>
/// <typeparam name="TResult">Type of target values to transform to.</typeparam>
/// <exception cref="ArgumentNullException"><paramref name="source"/> is <c>null</c> -or- <paramref name="filter"/> is <c>null</c>.</exception>
/// <returns>A new observable of values of the new result type.</returns>
/// <remarks>
/// <example>
/// <code>
/// InputSystem.onEvent
/// .Select(eventPtr => eventPtr.GetFirstButtonPressOrNull())
/// .Call(ctrl =>
/// {
/// if (ctrl != null)
/// Debug.Log(ctrl);
/// });
/// </code>
/// </example>
/// </remarks>
/// <seealso cref="InputEventListener"/>
/// <seealso cref="InputSystem.onEvent"/>
public static IObservable<TResult> Select<TSource, TResult>(this IObservable<TSource> source, Func<TSource, TResult> filter)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
if (filter == null)
throw new ArgumentNullException(nameof(filter));
return new SelectObservable<TSource, TResult>(source, filter);
}
/// <summary>
/// Transform each value in an observable stream of values such that one value is translated to zero or more values
/// of a new type.
/// </summary>
/// <param name="source">The stream of observable values.</param>
/// <param name="filter">Function to transform each value in the stream into zero or more new values.</param>
/// <typeparam name="TSource">Type of source values to transform from.</typeparam>
/// <typeparam name="TResult">Type of target values to transform to.</typeparam>
/// <exception cref="ArgumentNullException"><paramref name="source"/> is <c>null</c> -or- <paramref name="filter"/> is <c>null</c>.</exception>
/// <returns>A new observable of values of the new result type.</returns>
/// <remarks>
/// <example>
/// <code>
/// InputSystem.onEvent
/// .SelectMany(eventPtr => eventPtr.GetAllButtonPresses())
/// .Call(ctrl =>
/// Debug.Log($"Button {ctrl} pressed"));
/// </code>
/// </example>
/// </remarks>
/// <seealso cref="InputEventListener"/>
/// <seealso cref="InputSystem.onEvent"/>
public static IObservable<TResult> SelectMany<TSource, TResult>(this IObservable<TSource> source, Func<TSource, IEnumerable<TResult>> filter)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
if (filter == null)
throw new ArgumentNullException(nameof(filter));
return new SelectManyObservable<TSource, TResult>(source, filter);
}
/// <summary>
/// Take up to the first N values from the given observable stream of values.
/// </summary>
/// <param name="source">An observable source of values.</param>
/// <param name="count">The maximum number of values to take from the source.</param>
/// <typeparam name="TValue">Types of values to read from the stream.</typeparam>
/// <exception cref="ArgumentNullException"><paramref name="source"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="count"/> is negative.</exception>
/// <returns>A stream of up to <paramref name="count"/> values.</returns>
public static IObservable<TValue> Take<TValue>(this IObservable<TValue> source, int count)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
if (count < 0)
throw new ArgumentOutOfRangeException(nameof(count));
return new TakeNObservable<TValue>(source, count);
}
/// <summary>
/// From an observable stream of events, take only those that are for the given <paramref name="device"/>.
/// </summary>
/// <param name="source">An observable stream of events.</param>
/// <param name="device">Device to filter events for.</param>
/// <exception cref="ArgumentNullException"><paramref name="source"/> is <c>null</c>.</exception>
/// <returns>An observable stream of events for the given device.</returns>
/// <remarks>
/// Each event has an <see cref="InputEvent.deviceId"/> associated with it. This is used to match
/// against the <see cref="InputDevice.deviceId"/> of <paramref name="device"/>.
///
/// <example>
/// <code>
/// InputSystem.onEvent
/// .ForDevice(Mouse.current)
/// .Call(e => Debug.Log($"Mouse event: {e}");
/// </code>
/// </example>
/// </remarks>
/// <seealso cref="InputEvent.deviceId"/>
/// <seealso cref="InputEventListener"/>
/// <seealso cref="InputSystem.onEvent"/>
public static IObservable<InputEventPtr> ForDevice(this IObservable<InputEventPtr> source, InputDevice device)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
return new ForDeviceEventObservable(source, null, device);
}
/// <summary>
/// From an observable stream of events, take only those that are for a device of the given type.
/// </summary>
/// <param name="source">An observable stream of events.</param>
/// <typeparam name="TDevice">Type of device (such as <see cref="Gamepad"/>) to filter for.</typeparam>
/// <exception cref="ArgumentNullException"><paramref name="source"/> is <c>null</c>.</exception>
/// <returns>An observable stream of events for devices of type <typeparamref name="TDevice"/>.</returns>
/// <remarks>
/// <example>
/// <code>
/// InputSystem.onEvent
/// .ForDevice&lt;Gamepad&gt;()
/// .Where(e => e.HasButtonPress())
/// .CallOnce(e => PlayerInput.Instantiate(myPrefab,
/// pairWithDevice: InputSystem.GetDeviceById(e.deviceId)));
/// </code>
/// </example>
/// </remarks>
/// <seealso cref="InputEventListener"/>
/// <seealso cref="InputSystem.onEvent"/>
public static IObservable<InputEventPtr> ForDevice<TDevice>(this IObservable<InputEventPtr> source)
where TDevice : InputDevice
{
if (source == null)
throw new ArgumentNullException(nameof(source));
return new ForDeviceEventObservable(source, typeof(TDevice), null);
}
/// <summary>
/// Call an action for the first value in the given stream of values and then automatically dispose
/// the observer.
/// </summary>
/// <param name="source">An observable source of values.</param>
/// <param name="action">Action to call for the first value that arrives from the source.</param>
/// <typeparam name="TValue">Type of values delivered by the source.</typeparam>
/// <exception cref="ArgumentNullException"><paramref name="source"/> is <c>null</c> -or- <paramref name="action"/> is <c>null</c>.</exception>
/// <returns>A handle to the subscription. Call <c>Dispose</c> to unsubscribe at any time.</returns>
/// <remarks>
/// <example>
/// <code>
/// InputSystem.onEvent
/// .Where(e => e.type == DeviceConfigurationEvent.typeStatic)
/// .CallOnce(_ => Debug.Log("Device configuration changed"));
/// </code>
/// </example>
/// </remarks>
/// <seealso cref="InputEventListener"/>
/// <seealso cref="InputSystem.onEvent"/>
/// <seealso cref="Call{TValue}"/>
public static IDisposable CallOnce<TValue>(this IObservable<TValue> source, Action<TValue> action)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
if (action == null)
throw new ArgumentNullException(nameof(action));
IDisposable subscription = null;
subscription = source.Take(1).Subscribe(new Observer<TValue>(action, () => subscription?.Dispose()));
return subscription;
}
/// <summary>
/// Call the given callback for every value generated by the given observable stream of values.
/// </summary>
/// <param name="source">An observable stream of values.</param>
/// <param name="action">A callback to invoke for each value.</param>
/// <typeparam name="TValue"></typeparam>
/// <exception cref="ArgumentNullException"><paramref name="source"/> is <c>null</c> -or- <paramref name="action"/> is <c>null</c>.</exception>
/// <returns>A handle to the subscription. Call <c>Dispose</c> to unsubscribe at any time.</returns>
/// <remarks>
/// <example>
/// <code>
/// InputSystem.onEvent
/// .Where(e => e.type == DeviceConfigurationEvent.typeStatic)
/// .Call(_ => Debug.Log("Device configuration changed"));
/// </code>
/// </example>
/// </remarks>
/// <seealso cref="InputEventListener"/>
/// <seealso cref="InputSystem.onEvent"/>
/// <seealso cref="CallOnce{TValue}"/>
public static IDisposable Call<TValue>(this IObservable<TValue> source, Action<TValue> action)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
if (action == null)
throw new ArgumentNullException(nameof(action));
return source.Subscribe(new Observer<TValue>(action));
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a9586270a56c4658a83ff649811e0cb3
timeCreated: 1627654668

View File

@@ -0,0 +1,31 @@
using System;
namespace UnityEngine.InputSystem.Utilities
{
internal class Observer<TValue> : IObserver<TValue>
{
private Action<TValue> m_OnNext;
private Action m_OnCompleted;
public Observer(Action<TValue> onNext, Action onCompleted = null)
{
m_OnNext = onNext;
m_OnCompleted = onCompleted;
}
public void OnCompleted()
{
m_OnCompleted?.Invoke();
}
public void OnError(Exception error)
{
Debug.LogException(error);
}
public void OnNext(TValue evt)
{
m_OnNext?.Invoke(evt);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 92b3f5ed3a3e41daaf0bed64ebc854d9
timeCreated: 1627651936

View File

@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
namespace UnityEngine.InputSystem.Utilities
{
internal class SelectManyObservable<TSource, TResult> : IObservable<TResult>
{
private readonly IObservable<TSource> m_Source;
private readonly Func<TSource, IEnumerable<TResult>> m_Filter;
public SelectManyObservable(IObservable<TSource> source, Func<TSource, IEnumerable<TResult>> filter)
{
m_Source = source;
m_Filter = filter;
}
public IDisposable Subscribe(IObserver<TResult> observer)
{
return m_Source.Subscribe(new Select(this, observer));
}
private class Select : IObserver<TSource>
{
private SelectManyObservable<TSource, TResult> m_Observable;
private readonly IObserver<TResult> m_Observer;
public Select(SelectManyObservable<TSource, TResult> observable, IObserver<TResult> observer)
{
m_Observable = observable;
m_Observer = observer;
}
public void OnCompleted()
{
}
public void OnError(Exception error)
{
Debug.LogException(error);
}
public void OnNext(TSource evt)
{
foreach (var result in m_Observable.m_Filter(evt))
m_Observer.OnNext(result);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3e12c30fe9774947a81ce7eb7054cfb7
timeCreated: 1627658976

View File

@@ -0,0 +1,48 @@
using System;
namespace UnityEngine.InputSystem.LowLevel
{
internal class SelectObservable<TSource, TResult> : IObservable<TResult>
{
private readonly IObservable<TSource> m_Source;
private readonly Func<TSource, TResult> m_Filter;
public SelectObservable(IObservable<TSource> source, Func<TSource, TResult> filter)
{
m_Source = source;
m_Filter = filter;
}
public IDisposable Subscribe(IObserver<TResult> observer)
{
return m_Source.Subscribe(new Select(this, observer));
}
private class Select : IObserver<TSource>
{
private SelectObservable<TSource, TResult> m_Observable;
private readonly IObserver<TResult> m_Observer;
public Select(SelectObservable<TSource, TResult> observable, IObserver<TResult> observer)
{
m_Observable = observable;
m_Observer = observer;
}
public void OnCompleted()
{
}
public void OnError(Exception error)
{
Debug.LogException(error);
}
public void OnNext(TSource evt)
{
var result = m_Observable.m_Filter(evt);
m_Observer.OnNext(result);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 29a78370a46c4b59875764266185b105
timeCreated: 1627651658

View File

@@ -0,0 +1,57 @@
using System;
namespace UnityEngine.InputSystem.Utilities
{
internal class TakeNObservable<TValue> : IObservable<TValue>
{
private IObservable<TValue> m_Source;
private int m_Count;
public TakeNObservable(IObservable<TValue> source, int count)
{
m_Source = source;
m_Count = count;
}
public IDisposable Subscribe(IObserver<TValue> observer)
{
return m_Source.Subscribe(new Take(this, observer));
}
private class Take : IObserver<TValue>
{
private IObserver<TValue> m_Observer;
private int m_Remaining;
public Take(TakeNObservable<TValue> observable, IObserver<TValue> observer)
{
m_Observer = observer;
m_Remaining = observable.m_Count;
}
public void OnCompleted()
{
}
public void OnError(Exception error)
{
Debug.LogException(error);
}
public void OnNext(TValue evt)
{
if (m_Remaining <= 0)
return;
m_Remaining--;
m_Observer.OnNext(evt);
if (m_Remaining == 0)
{
m_Observer.OnCompleted();
m_Observer = default;
}
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 25881642ba784356b23ef34898fdc690
timeCreated: 1627651729

View File

@@ -0,0 +1,48 @@
using System;
namespace UnityEngine.InputSystem.Utilities
{
internal class WhereObservable<TValue> : IObservable<TValue>
{
private readonly IObservable<TValue> m_Source;
private readonly Func<TValue, bool> m_Predicate;
public WhereObservable(IObservable<TValue> source, Func<TValue, bool> predicate)
{
m_Source = source;
m_Predicate = predicate;
}
public IDisposable Subscribe(IObserver<TValue> observer)
{
return m_Source.Subscribe(new Where(this, observer));
}
private class Where : IObserver<TValue>
{
private WhereObservable<TValue> m_Observable;
private readonly IObserver<TValue> m_Observer;
public Where(WhereObservable<TValue> observable, IObserver<TValue> observer)
{
m_Observable = observable;
m_Observer = observer;
}
public void OnCompleted()
{
}
public void OnError(Exception error)
{
Debug.LogException(error);
}
public void OnNext(TValue evt)
{
if (m_Observable.m_Predicate(evt))
m_Observer.OnNext(evt);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ea70775d141141a3bca036e60a89e8fa
timeCreated: 1627651688

View File

@@ -0,0 +1,94 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace UnityEngine.InputSystem.Utilities
{
/// <summary>
/// Helper when having either a single element or a list of elements. Avoids
/// having to allocate GC heap garbage or having to alternatively split code paths.
/// </summary>
/// <typeparam name="TValue"></typeparam>
internal struct OneOrMore<TValue, TList> : IReadOnlyList<TValue>
where TList : IReadOnlyList<TValue>
{
private readonly bool m_IsSingle;
private readonly TValue m_Single;
private readonly TList m_Multiple;
public int Count => m_IsSingle ? 1 : m_Multiple.Count;
public TValue this[int index]
{
get
{
if (!m_IsSingle)
return m_Multiple[index];
if (index < 0 || index > 1)
throw new ArgumentOutOfRangeException(nameof(index));
return m_Single;
}
}
public OneOrMore(TValue single)
{
m_IsSingle = true;
m_Single = single;
m_Multiple = default;
}
public OneOrMore(TList multiple)
{
m_IsSingle = false;
m_Single = default;
m_Multiple = multiple;
}
public static implicit operator OneOrMore<TValue, TList>(TValue single)
{
return new OneOrMore<TValue, TList>(single);
}
public static implicit operator OneOrMore<TValue, TList>(TList multiple)
{
return new OneOrMore<TValue, TList>(multiple);
}
public IEnumerator<TValue> GetEnumerator()
{
return new Enumerator { m_List = this };
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private class Enumerator : IEnumerator<TValue>
{
internal int m_Index = -1;
internal OneOrMore<TValue, TList> m_List;
public bool MoveNext()
{
++m_Index;
if (m_Index >= m_List.Count)
return false;
return true;
}
public void Reset()
{
m_Index = -1;
}
public TValue Current => m_List[m_Index];
object IEnumerator.Current => Current;
public void Dispose()
{
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More