test
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 888687b767334731951f3f18ffef0b75
|
||||
timeCreated: 1506924199
|
@@ -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
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c6a2891083312645a7be416176261c1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2349376ef4c6487db94a6e15873ee6a8
|
||||
timeCreated: 1621355960
|
@@ -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<Vector2>
|
||||
/// {
|
||||
/// [InputControl(layout = "Vector2")]
|
||||
/// public int part;
|
||||
///
|
||||
/// public override Vector2 ReadValue(ref InputBindingCompositeContext context)
|
||||
/// {
|
||||
/// // Return the Vector3 with the greatest magnitude.
|
||||
/// return context.ReadValue<Vector2, Vector2MagnitudeComparer>(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<Vector3>
|
||||
/// {
|
||||
/// [InputControl(layout = "Vector3")]
|
||||
/// public int part;
|
||||
///
|
||||
/// public override Vector3 ReadValue(ref InputBindingCompositeContext context)
|
||||
/// {
|
||||
/// // Return the Vector3 with the greatest magnitude.
|
||||
/// return context.ReadValue<Vector3, Vector2MagnitudeComparer>(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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e687839a3c5f7a4ea700f2967051b3d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bb83bbf668a324899963d1ea424c5ad0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d2c401960c5dc624c8970b2bee0371dc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d45ac8148fd4e264eb17df8586b09581
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d717b0e790ee146dd93d1be641984b6a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff1bc3762d8741ff8f40d54da690efb1
|
||||
timeCreated: 1506737298
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 98a0df93c82e4369b500b62affa160db
|
||||
timeCreated: 1506841462
|
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 640e1e705a694e56835bacff3d7f7b4b
|
||||
timeCreated: 1508302068
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9e082355c53c54d19a09c1d4f7629360
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 760fac1b814a4d99a4fc534934e4dc99
|
||||
timeCreated: 1506974628
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9d9d9634637c440896a123c44c9e262b
|
||||
timeCreated: 1602782357
|
@@ -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)};
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0ffbcc8a975d04f12967a085a10aeb7b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0e0c005ff24ec4cfb951eefa16239b9b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 15eaf34440a14edf94d5a0a00cbded20
|
||||
timeCreated: 1515636424
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f8944bd1ba848ab8f95afe7acfb4b79
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8242c97e8771412fb5f5637b7278e18f
|
||||
timeCreated: 1627655656
|
@@ -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<Gamepad>()
|
||||
/// .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));
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a9586270a56c4658a83ff649811e0cb3
|
||||
timeCreated: 1627654668
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 92b3f5ed3a3e41daaf0bed64ebc854d9
|
||||
timeCreated: 1627651936
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e12c30fe9774947a81ce7eb7054cfb7
|
||||
timeCreated: 1627658976
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 29a78370a46c4b59875764266185b105
|
||||
timeCreated: 1627651658
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 25881642ba784356b23ef34898fdc690
|
||||
timeCreated: 1627651729
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ea70775d141141a3bca036e60a89e8fa
|
||||
timeCreated: 1627651688
|
@@ -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
Reference in New Issue
Block a user