Added Unity project files

This commit is contained in:
2026-06-07 16:58:24 +01:00
parent 3cc05d260b
commit 23bbcab156
3942 changed files with 453676 additions and 0 deletions

View File

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

View File

@ -0,0 +1,70 @@
//-----------------------------------------------------------------------
// <copyright file="FieldInfoExtensions.cs" company="Sirenix IVS">
// Copyright (c) 2018 Sirenix IVS
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
//-----------------------------------------------------------------------
namespace VRC.Udon.Serialization.OdinSerializer.Utilities
{
using System;
using System.Reflection;
/// <summary>
/// FieldInfo method extensions.
/// </summary>
public static class FieldInfoExtensions
{
/// <summary>
/// Determines whether the specified field is an alias.
/// </summary>
/// <param name="fieldInfo">The field to check.</param>
/// <returns>
/// <c>true</c> if the specified field is an alias; otherwise, <c>false</c>.
/// </returns>
public static bool IsAliasField(this FieldInfo fieldInfo)
{
return fieldInfo is MemberAliasFieldInfo;
}
/// <summary>
/// Returns the original, backing field of an alias field if the field is an alias.
/// </summary>
/// <param name="fieldInfo">The field to check.</param>
/// /// <param name="throwOnNotAliased">if set to <c>true</c> an exception will be thrown if the field is not aliased.</param>
/// <returns></returns>
/// <exception cref="System.ArgumentException">The field was not aliased; this only occurs if throwOnNotAliased is true.</exception>
public static FieldInfo DeAliasField(this FieldInfo fieldInfo, bool throwOnNotAliased = false)
{
MemberAliasFieldInfo aliasFieldInfo = fieldInfo as MemberAliasFieldInfo;
if (aliasFieldInfo != null)
{
while (aliasFieldInfo.AliasedField is MemberAliasFieldInfo)
{
aliasFieldInfo = aliasFieldInfo.AliasedField as MemberAliasFieldInfo;
}
return aliasFieldInfo.AliasedField;
}
if (throwOnNotAliased)
{
throw new ArgumentException("The field " + fieldInfo.GetNiceName() + " was not aliased.");
}
return fieldInfo;
}
}
}

View File

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

View File

@ -0,0 +1,330 @@
//-----------------------------------------------------------------------
// <copyright file="GarbageFreeIterators.cs" company="Sirenix IVS">
// Copyright (c) 2018 Sirenix IVS
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
//-----------------------------------------------------------------------
namespace VRC.Udon.Serialization.OdinSerializer.Utilities
{
using System;
using System.Collections.Generic;
/// <summary>
/// Garbage free enumerator methods.
/// </summary>
public static class GarbageFreeIterators
{
/// <summary>
/// Garbage free enumerator for lists.
/// </summary>
public static ListIterator<T> GFIterator<T>(this List<T> list)
{
return new ListIterator<T>(list);
}
/// <summary>
/// Garbage free enumerator for dictionaries.
/// </summary>
public static DictionaryIterator<T1, T2> GFIterator<T1, T2>(this Dictionary<T1, T2> dictionary)
{
return new DictionaryIterator<T1, T2>(dictionary);
}
/// <summary>
/// Garbage free enumator for dictionary values.
/// </summary>
public static DictionaryValueIterator<T1, T2> GFValueIterator<T1, T2>(this Dictionary<T1, T2> dictionary)
{
return new DictionaryValueIterator<T1, T2>(dictionary);
}
/// <summary>
/// Garbage free enumerator for hashsets.
/// </summary>
public static HashsetIterator<T> GFIterator<T>(this HashSet<T> hashset)
{
return new HashsetIterator<T>(hashset);
}
/// <summary>
/// List iterator.
/// </summary>
public struct ListIterator<T> : IDisposable
{
private bool isNull;
private List<T> list;
private List<T>.Enumerator enumerator;
/// <summary>
/// Creates a list iterator.
/// </summary>
public ListIterator(List<T> list)
{
this.isNull = list == null;
if (this.isNull)
{
this.list = null;
this.enumerator = new List<T>.Enumerator();
}
else
{
this.list = list;
this.enumerator = this.list.GetEnumerator();
}
}
/// <summary>
/// Gets the enumerator.
/// </summary>
public ListIterator<T> GetEnumerator()
{
return this;
}
/// <summary>
/// Gets the current value.
/// </summary>
public T Current
{
get
{
return this.enumerator.Current;
}
}
/// <summary>
/// Moves to the next value.
/// </summary>
public bool MoveNext()
{
if (this.isNull)
{
return false;
}
return this.enumerator.MoveNext();
}
/// <summary>
/// Disposes the iterator.
/// </summary>
public void Dispose()
{
this.enumerator.Dispose();
}
}
/// <summary>
/// Hashset iterator.
/// </summary>
public struct HashsetIterator<T> : IDisposable
{
private bool isNull;
private HashSet<T> hashset;
private HashSet<T>.Enumerator enumerator;
/// <summary>
/// Creates a hashset iterator.
/// </summary>
public HashsetIterator(HashSet<T> hashset)
{
this.isNull = hashset == null;
if (this.isNull)
{
this.hashset = null;
this.enumerator = new HashSet<T>.Enumerator();
}
else
{
this.hashset = hashset;
this.enumerator = this.hashset.GetEnumerator();
}
}
/// <summary>
/// Gets the enumerator.
/// </summary>
public HashsetIterator<T> GetEnumerator()
{
return this;
}
/// <summary>
/// Gets the current value.
/// </summary>
public T Current
{
get
{
return this.enumerator.Current;
}
}
/// <summary>
/// Moves to the next value.
/// </summary>
public bool MoveNext()
{
if (this.isNull)
{
return false;
}
return this.enumerator.MoveNext();
}
/// <summary>
/// Disposes the iterator.
/// </summary>
public void Dispose()
{
this.enumerator.Dispose();
}
}
/// <summary>
/// Dictionary iterator.
/// </summary>
public struct DictionaryIterator<T1, T2> : IDisposable
{
private Dictionary<T1, T2> dictionary;
private Dictionary<T1, T2>.Enumerator enumerator;
private bool isNull;
/// <summary>
/// Creates a dictionary iterator.
/// </summary>
public DictionaryIterator(Dictionary<T1, T2> dictionary)
{
this.isNull = dictionary == null;
if (this.isNull)
{
this.dictionary = null;
this.enumerator = new Dictionary<T1, T2>.Enumerator();
}
else
{
this.dictionary = dictionary;
this.enumerator = this.dictionary.GetEnumerator();
}
}
/// <summary>
/// Gets the enumerator.
/// </summary>
public DictionaryIterator<T1, T2> GetEnumerator()
{
return this;
}
/// <summary>
/// Gets the current value.
/// </summary>
public KeyValuePair<T1, T2> Current
{
get
{
return this.enumerator.Current;
}
}
/// <summary>
/// Moves to the next value.
/// </summary>
public bool MoveNext()
{
if (this.isNull)
{
return false;
}
return this.enumerator.MoveNext();
}
/// <summary>
/// Disposes the iterator.
/// </summary>
public void Dispose()
{
this.enumerator.Dispose();
}
}
/// <summary>
/// Dictionary value iterator.
/// </summary>
public struct DictionaryValueIterator<T1, T2> : IDisposable
{
private Dictionary<T1, T2> dictionary;
private Dictionary<T1, T2>.Enumerator enumerator;
private bool isNull;
/// <summary>
/// Creates a dictionary value iterator.
/// </summary>
public DictionaryValueIterator(Dictionary<T1, T2> dictionary)
{
this.isNull = dictionary == null;
if (this.isNull)
{
this.dictionary = null;
this.enumerator = new Dictionary<T1, T2>.Enumerator();
}
else
{
this.dictionary = dictionary;
this.enumerator = this.dictionary.GetEnumerator();
}
}
/// <summary>
/// Gets the enumerator.
/// </summary>
public DictionaryValueIterator<T1, T2> GetEnumerator()
{
return this;
}
/// <summary>
/// Gets the current value.
/// </summary>
public T2 Current
{
get
{
return this.enumerator.Current.Value;
}
}
/// <summary>
/// Moves to the next value.
/// </summary>
public bool MoveNext()
{
if (this.isNull)
{
return false;
}
return this.enumerator.MoveNext();
}
/// <summary>
/// Disposes the iterator.
/// </summary>
public void Dispose()
{
this.enumerator.Dispose();
}
}
}
}

View File

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

View File

@ -0,0 +1,79 @@
//-----------------------------------------------------------------------
// <copyright file="LinqExtensions.cs" company="Sirenix IVS">
// Copyright (c) 2018 Sirenix IVS
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
//-----------------------------------------------------------------------
namespace VRC.Udon.Serialization.OdinSerializer.Utilities
{
using System;
using System.Collections.Generic;
/// <summary>
/// Various LinQ extensions.
/// </summary>
public static class LinqExtensions
{
/// <summary>
/// Perform an action on each item.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="action">The action to perform.</param>
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
foreach (var item in source)
{
action(item);
}
return source;
}
/// <summary>
/// Perform an action on each item.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="action">The action to perform.</param>
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> source, Action<T, int> action)
{
int counter = 0;
foreach (var item in source)
{
action(item, counter++);
}
return source;
}
/// <summary>
/// Add a collection to the end of another collection.
/// </summary>
/// <param name="source">The collection.</param>
/// <param name="append">The collection to append.</param>
public static IEnumerable<T> Append<T>(this IEnumerable<T> source, IEnumerable<T> append)
{
foreach (var item in source)
{
yield return item;
}
foreach (var item in append)
{
yield return item;
}
}
}
}

View File

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

View File

@ -0,0 +1,254 @@
//-----------------------------------------------------------------------
// <copyright file="MemberInfoExtensions.cs" company="Sirenix IVS">
// Copyright (c) 2018 Sirenix IVS
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
//-----------------------------------------------------------------------
namespace VRC.Udon.Serialization.OdinSerializer.Utilities
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Globalization;
/// <summary>
/// MemberInfo method extensions.
/// </summary>
public static class MemberInfoExtensions
{
/// <summary>
/// Returns true if the attribute whose type is specified by the generic argument is defined on this member
/// </summary>
public static bool IsDefined<T>(this ICustomAttributeProvider member, bool inherit) where T : Attribute
{
try
{
return member.IsDefined(typeof(T), inherit);
}
catch
{
return false;
}
}
/// <summary>
/// Returns true if the attribute whose type is specified by the generic argument is defined on this member
/// </summary>
public static bool IsDefined<T>(this ICustomAttributeProvider member) where T : Attribute
{
return IsDefined<T>(member, false);
}
/// <summary>
/// Returns the first found custom attribute of type T on this member
/// Returns null if none was found
/// </summary>
public static T GetAttribute<T>(this ICustomAttributeProvider member, bool inherit) where T : Attribute
{
var all = GetAttributes<T>(member, inherit).ToArray();
return (all == null || all.Length == 0) ? null : all[0];
}
/// <summary>
/// Returns the first found non-inherited custom attribute of type T on this member
/// Returns null if none was found
/// </summary>
public static T GetAttribute<T>(this ICustomAttributeProvider member) where T : Attribute
{
return GetAttribute<T>(member, false);
}
/// <summary>
/// Gets all attributes of the specified generic type.
/// </summary>
/// <param name="member">The member.</param>
public static IEnumerable<T> GetAttributes<T>(this ICustomAttributeProvider member) where T : Attribute
{
return GetAttributes<T>(member, false);
}
/// <summary>
/// Gets all attributes of the specified generic type.
/// </summary>
/// <param name="member">The member.</param>
/// <param name="inherit">If true, specifies to also search the ancestors of element for custom attributes.</param>
public static IEnumerable<T> GetAttributes<T>(this ICustomAttributeProvider member, bool inherit) where T : Attribute
{
try
{
return member.GetCustomAttributes(typeof(T), inherit).Cast<T>();
}
catch
{
return new T[0];
}
}
/// <summary>
/// Gets all attribute instances defined on a MemeberInfo.
/// </summary>
/// <param name="member">The member.</param>
public static Attribute[] GetAttributes(this ICustomAttributeProvider member)
{
try
{
return member.GetAttributes<Attribute>().ToArray();
}
catch
{
return new Attribute[0];
}
}
/// <summary>
/// Gets all attribute instances on a MemberInfo.
/// </summary>
/// <param name="member">The member.</param>
/// <param name="inherit">If true, specifies to also search the ancestors of element for custom attributes.</param>
public static Attribute[] GetAttributes(this ICustomAttributeProvider member, bool inherit)
{
try
{
return member.GetAttributes<Attribute>(inherit).ToArray();
}
catch
{
return new Attribute[0];
}
}
/// <summary>
/// If this member is a method, returns the full method name (name + params) otherwise the member name paskal splitted
/// </summary>
public static string GetNiceName(this MemberInfo member)
{
var method = member as MethodBase;
string result;
if (method != null)
{
result = method.GetFullName();
}
else
{
result = member.Name;
}
return result.ToTitleCase();
}
/// <summary>
/// Determines whether a FieldInfo, PropertyInfo or MethodInfo is static.
/// </summary>
/// <param name="member">The member.</param>
/// <returns>
/// <c>true</c> if the specified member is static; otherwise, <c>false</c>.
/// </returns>
/// <exception cref="System.NotSupportedException"></exception>
public static bool IsStatic(this MemberInfo member)
{
var field = member as FieldInfo;
if (field != null)
{
return field.IsStatic;
}
var property = member as PropertyInfo;
if (property != null)
{
return property.CanRead ? property.GetGetMethod(true).IsStatic : property.GetSetMethod(true).IsStatic;
}
var method = member as MethodBase;
if (method != null)
{
return method.IsStatic;
}
var @event = member as EventInfo;
if (@event != null)
{
return @event.GetRaiseMethod(true).IsStatic;
}
var type = member as Type;
if (type != null)
{
return type.IsSealed && type.IsAbstract;
}
string message = string.Format(
CultureInfo.InvariantCulture,
"Unable to determine IsStatic for member {0}.{1}" +
"MemberType was {2} but only fields, properties, methods, events and types are supported.",
member.DeclaringType.FullName,
member.Name,
member.GetType().FullName);
throw new NotSupportedException(message);
}
/// <summary>
/// Determines whether the specified member is an alias.
/// </summary>
/// <param name="memberInfo">The member to check.</param>
/// <returns>
/// <c>true</c> if the specified member is an alias; otherwise, <c>false</c>.
/// </returns>
public static bool IsAlias(this MemberInfo memberInfo)
{
return memberInfo is MemberAliasFieldInfo
|| memberInfo is MemberAliasPropertyInfo
|| memberInfo is MemberAliasMethodInfo;
}
/// <summary>
/// Returns the original, backing member of an alias member if the member is an alias.
/// </summary>
/// <param name="memberInfo">The member to check.</param>
/// /// <param name="throwOnNotAliased">if set to <c>true</c> an exception will be thrown if the member is not aliased.</param>
/// <returns></returns>
/// <exception cref="System.ArgumentException">The member was not aliased; this only occurs if throwOnNotAliased is true.</exception>
public static MemberInfo DeAlias(this MemberInfo memberInfo, bool throwOnNotAliased = false)
{
MemberAliasFieldInfo aliasFieldInfo = memberInfo as MemberAliasFieldInfo;
if (aliasFieldInfo != null)
{
return aliasFieldInfo.AliasedField;
}
MemberAliasPropertyInfo aliasPropertyInfo = memberInfo as MemberAliasPropertyInfo;
if (aliasPropertyInfo != null)
{
return aliasPropertyInfo.AliasedProperty;
}
MemberAliasMethodInfo aliasMethodInfo = memberInfo as MemberAliasMethodInfo;
if (aliasMethodInfo != null)
{
return aliasMethodInfo.AliasedMethod;
}
if (throwOnNotAliased)
{
throw new ArgumentException("The member " + memberInfo.GetNiceName() + " was not aliased.");
}
return memberInfo;
}
}
}

View File

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

View File

@ -0,0 +1,139 @@
//-----------------------------------------------------------------------
// <copyright file="MethodInfoExtensions.cs" company="Sirenix IVS">
// Copyright (c) 2018 Sirenix IVS
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
//-----------------------------------------------------------------------
namespace VRC.Udon.Serialization.OdinSerializer.Utilities
{
using System;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
/// <summary>
/// Various extensions for MethodInfo.
/// </summary>
public static class MethodInfoExtensions
{
/// <summary>
/// Returns the specified method's full name "methodName(argType1 arg1, argType2 arg2, etc)"
/// Uses the specified gauntlet to replaces type names, ex: "int" instead of "Int32"
/// </summary>
public static string GetFullName(this MethodBase method, string extensionMethodPrefix)
{
var builder = new StringBuilder();
bool isExtensionMethod = method.IsExtensionMethod();
if (isExtensionMethod)
{
builder.Append(extensionMethodPrefix);
}
builder.Append(method.Name);
builder.Append("(");
builder.Append(method.GetParamsNames());
builder.Append(")");
return builder.ToString();
}
/// <summary>
/// Returns a string representing the passed method parameters names. Ex "int num, float damage, Transform target"
/// </summary>
public static string GetParamsNames(this MethodBase method)
{
ParameterInfo[] pinfos = method.IsExtensionMethod() ? method.GetParameters().Skip(1).ToArray() : method.GetParameters();
var builder = new StringBuilder();
for (int i = 0, len = pinfos.Length; i < len; i++)
{
var param = pinfos[i];
var paramTypeName = param.ParameterType.GetNiceName();
builder.Append(paramTypeName);
builder.Append(" ");
builder.Append(param.Name);
if (i < len - 1)
{
builder.Append(", ");
}
}
return builder.ToString();
}
/// <summary>
/// Returns the specified method's full name.
/// </summary>
public static string GetFullName(this MethodBase method)
{
return GetFullName(method, "[ext] ");
}
/// <summary>
/// Tests if a method is an extension method.
/// </summary>
public static bool IsExtensionMethod(this MethodBase method)
{
var type = method.DeclaringType;
return type.IsSealed &&
!type.IsGenericType &&
!type.IsNested &&
method.IsDefined(typeof(ExtensionAttribute), false);
}
/// <summary>
/// Determines whether the specified method is an alias.
/// </summary>
/// <param name="methodInfo">The method to check.</param>
/// <returns>
/// <c>true</c> if the specified method is an alias; otherwise, <c>false</c>.
/// </returns>
public static bool IsAliasMethod(this MethodInfo methodInfo)
{
return methodInfo is MemberAliasMethodInfo;
}
/// <summary>
/// Returns the original, backing method of an alias method if the method is an alias.
/// </summary>
/// <param name="methodInfo">The method to check.</param>
/// /// <param name="throwOnNotAliased">if set to <c>true</c> an exception will be thrown if the method is not aliased.</param>
/// <returns></returns>
/// <exception cref="System.ArgumentException">The method was not aliased; this only occurs if throwOnNotAliased is true.</exception>
public static MethodInfo DeAliasMethod(this MethodInfo methodInfo, bool throwOnNotAliased = false)
{
MemberAliasMethodInfo aliasMethodInfo = methodInfo as MemberAliasMethodInfo;
if (aliasMethodInfo != null)
{
while (aliasMethodInfo.AliasedMethod is MemberAliasMethodInfo)
{
aliasMethodInfo = aliasMethodInfo.AliasedMethod as MemberAliasMethodInfo;
}
return aliasMethodInfo.AliasedMethod;
}
if (throwOnNotAliased)
{
throw new ArgumentException("The method " + methodInfo.GetNiceName() + " was not aliased.");
}
return methodInfo;
}
}
}

View File

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

View File

@ -0,0 +1,126 @@
//-----------------------------------------------------------------------
// <copyright file="Operator.cs" company="Sirenix IVS">
// Copyright (c) 2018 Sirenix IVS
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
//-----------------------------------------------------------------------
namespace VRC.Udon.Serialization.OdinSerializer.Utilities
{
/// <summary>
/// Determines the type of operator.
/// </summary>
/// <seealso cref="TypeExtensions" />
public enum Operator
{
/// <summary>
/// The == operator.
/// </summary>
Equality,
/// <summary>
/// The != operator.
/// </summary>
Inequality,
/// <summary>
/// The + operator.
/// </summary>
Addition,
/// <summary>
/// The - operator.
/// </summary>
Subtraction,
/// <summary>
/// The * operator.
/// </summary>
Multiply,
/// <summary>
/// The / operator.
/// </summary>
Division,
/// <summary>
/// The &lt; operator.
/// </summary>
LessThan,
/// <summary>
/// The &gt; operator.
/// </summary>
GreaterThan,
/// <summary>
/// The &lt;= operator.
/// </summary>
LessThanOrEqual,
/// <summary>
/// The &gt;= operator.
/// </summary>
GreaterThanOrEqual,
/// <summary>
/// The % operator.
/// </summary>
Modulus,
/// <summary>
/// The &gt;&gt; operator.
/// </summary>
RightShift,
/// <summary>
/// The &lt;&lt; operator.
/// </summary>
LeftShift,
/// <summary>
/// The &amp; operator.
/// </summary>
BitwiseAnd,
/// <summary>
/// The | operator.
/// </summary>
BitwiseOr,
/// <summary>
/// The ^ operator.
/// </summary>
ExclusiveOr,
/// <summary>
/// The ~ operator.
/// </summary>
BitwiseComplement,
/// <summary>
/// The &amp;&amp; operator.
/// </summary>
LogicalAnd,
/// <summary>
/// The || operator.
/// </summary>
LogicalOr,
/// <summary>
/// The ! operator.
/// </summary>
LogicalNot,
}
}

View File

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

View File

@ -0,0 +1,53 @@
//-----------------------------------------------------------------------
// <copyright file="PathUtilities.cs" company="Sirenix IVS">
// Copyright (c) 2018 Sirenix IVS
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
//-----------------------------------------------------------------------
namespace VRC.Udon.Serialization.OdinSerializer.Utilities
{
using System;
using System.IO;
using System.Text;
/// <summary>
/// DirectoryInfo method extensions.
/// </summary>
public static class PathUtilities
{
/// <summary>
/// Determines whether the directory has a given directory in its hierarchy of children.
/// </summary>
/// <param name="parentDir">The parent directory.</param>
/// <param name="subDir">The sub directory.</param>
public static bool HasSubDirectory(this DirectoryInfo parentDir, DirectoryInfo subDir)
{
var parentDirName = parentDir.FullName.TrimEnd('\\', '/');
while (subDir != null)
{
if (subDir.FullName.TrimEnd('\\', '/') == parentDirName)
{
return true;
}
else
{
subDir = subDir.Parent;
}
}
return false;
}
}
}

View File

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

View File

@ -0,0 +1,106 @@
//-----------------------------------------------------------------------
// <copyright file="PropertyInfoExtensions.cs" company="Sirenix IVS">
// Copyright (c) 2018 Sirenix IVS
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
//-----------------------------------------------------------------------
namespace VRC.Udon.Serialization.OdinSerializer.Utilities
{
using System;
using System.Reflection;
/// <summary>
/// PropertyInfo method extensions.
/// </summary>
public static class PropertyInfoExtensions
{
/// <summary>
/// Determines whether a property is an auto property with a usable getter and setter.
/// </summary>
public static bool IsAutoProperty(this PropertyInfo propInfo, bool allowVirtual = false)
{
if (!(propInfo.CanWrite && propInfo.CanRead))
{
return false;
}
if (!allowVirtual)
{
var getter = propInfo.GetGetMethod(true);
var setter = propInfo.GetSetMethod(true);
if ((getter != null && (getter.IsAbstract || getter.IsVirtual)) || (setter != null && (setter.IsAbstract || setter.IsVirtual)))
{
return false;
}
}
var flag = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
string compilerGeneratedName = "<" + propInfo.Name + ">";
var fields = propInfo.DeclaringType.GetFields(flag);
for (int i = 0; i < fields.Length; i++)
{
if (fields[i].Name.Contains(compilerGeneratedName))
{
return true;
}
}
return false;
}
/// <summary>
/// Determines whether the specified property is an alias.
/// </summary>
/// <param name="propertyInfo">The property to check.</param>
/// <returns>
/// <c>true</c> if the specified property is an alias; otherwise, <c>false</c>.
/// </returns>
public static bool IsAliasProperty(this PropertyInfo propertyInfo)
{
return propertyInfo is MemberAliasPropertyInfo;
}
/// <summary>
/// Returns the original, backing property of an alias property if the property is an alias.
/// </summary>
/// <param name="propertyInfo">The property to check.</param>
/// /// <param name="throwOnNotAliased">if set to <c>true</c> an exception will be thrown if the property is not aliased.</param>
/// <returns></returns>
/// <exception cref="System.ArgumentException">The property was not aliased; this only occurs if throwOnNotAliased is true.</exception>
public static PropertyInfo DeAliasProperty(this PropertyInfo propertyInfo, bool throwOnNotAliased = false)
{
MemberAliasPropertyInfo aliasPropertyInfo = propertyInfo as MemberAliasPropertyInfo;
if (aliasPropertyInfo != null)
{
while (aliasPropertyInfo.AliasedProperty is MemberAliasPropertyInfo)
{
aliasPropertyInfo = aliasPropertyInfo.AliasedProperty as MemberAliasPropertyInfo;
}
return aliasPropertyInfo.AliasedProperty;
}
if (throwOnNotAliased)
{
throw new ArgumentException("The property " + propertyInfo.GetNiceName() + " was not aliased.");
}
return propertyInfo;
}
}
}

View File

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

View File

@ -0,0 +1,80 @@
//-----------------------------------------------------------------------
// <copyright file="StringExtensions.cs" company="Sirenix IVS">
// Copyright (c) 2018 Sirenix IVS
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
//-----------------------------------------------------------------------
namespace VRC.Udon.Serialization.OdinSerializer.Utilities
{
using System;
using System.Globalization;
using System.Text;
/// <summary>
/// String method extensions.
/// </summary>
public static class StringExtensions
{
/// <summary>
/// Eg MY_INT_VALUE => MyIntValue
/// </summary>
public static string ToTitleCase(this string input)
{
var builder = new StringBuilder();
for (int i = 0; i < input.Length; i++)
{
var current = input[i];
if (current == '_' && i + 1 < input.Length)
{
var next = input[i + 1];
if (char.IsLower(next))
{
next = char.ToUpper(next, CultureInfo.InvariantCulture);
}
builder.Append(next);
i++;
}
else
{
builder.Append(current);
}
}
return builder.ToString();
}
/// <summary>
/// Returns true if this string is null, empty, or contains only whitespace.
/// </summary>
/// <param name="str">The string to check.</param>
/// <returns><c>true</c> if this string is null, empty, or contains only whitespace; otherwise, <c>false</c>.</returns>
public static bool IsNullOrWhitespace(this string str)
{
if (!string.IsNullOrEmpty(str))
{
for (int i = 0; i < str.Length; i++)
{
if (char.IsWhiteSpace(str[i]) == false)
{
return false;
}
}
}
return true;
}
}
}

View File

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

View File

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

View File

@ -0,0 +1,67 @@
//-----------------------------------------------------------------------
// <copyright file="UnityExtensions.cs" company="Sirenix IVS">
// Copyright (c) 2018 Sirenix IVS
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
//-----------------------------------------------------------------------
namespace VRC.Udon.Serialization.OdinSerializer.Utilities
{
using System;
using System.Reflection;
/// <summary>
/// Extends various Unity classes.
/// </summary>
public static class UnityExtensions
{
private static readonly ValueGetter<UnityEngine.Object, IntPtr> UnityObjectCachedPtrFieldGetter;
static UnityExtensions()
{
var field = typeof(UnityEngine.Object).GetField("m_CachedPtr", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (field != null)
{
UnityObjectCachedPtrFieldGetter = EmitUtilities.CreateInstanceFieldGetter<UnityEngine.Object, IntPtr>(field);
}
}
/// <summary>
/// Determines whether a Unity object is null or "fake null",
/// without ever calling Unity's own equality operators.
/// This method is useful for checking if a Unity object is
/// null, destroyed or missing at times when it is not allowed
/// to call Unity's own equality operators, for example when
/// not running on the main thread.
/// </summary>
/// <param name="obj">The Unity object to check.</param>
/// <returns>True if the object is null, missing or destroyed; otherwise false.</returns>
public static bool SafeIsUnityNull(this UnityEngine.Object obj)
{
if (object.ReferenceEquals(obj, null))
{
return true;
}
if (UnityObjectCachedPtrFieldGetter == null)
{
throw new NotSupportedException("Could not find the field 'm_CachedPtr' in the class UnityEngine.Object; cannot perform a special null check.");
}
IntPtr ptr = UnityObjectCachedPtrFieldGetter(ref obj);
return ptr == IntPtr.Zero;
}
}
}

View File

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

View File

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

View File

@ -0,0 +1,291 @@
//-----------------------------------------------------------------------
// <copyright file="AssemblyImportSettingsUtilities.cs" company="Sirenix IVS">
// Copyright (c) 2018 Sirenix IVS
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
//-----------------------------------------------------------------------
#pragma warning disable 618 // VRC
#if UNITY_EDITOR
namespace VRC.Udon.Serialization.OdinSerializer.Utilities.Editor
{
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEditor;
/// <summary>
/// Defines how an assembly's import settings should be configured.
/// </summary>
public enum OdinAssemblyImportSettings
{
/// <summary>
/// Include the assembly in the build, but not in the editor.
/// </summary>
IncludeInBuildOnly,
/// <summary>
/// Include the assembly in the editor, but not in the build.
/// </summary>
IncludeInEditorOnly,
/// <summary>
/// Include the assembly in both the build and in the editor.
/// </summary>
IncludeInAll,
/// <summary>
/// Exclude the assembly from both the build and from the editor.
/// </summary>
ExcludeFromAll,
}
/// <summary>
/// Utility for correctly setting import on OdinSerializer assemblies based on platform and scripting backend.
/// </summary>
public static class AssemblyImportSettingsUtilities
{
private static MethodInfo getPropertyIntMethod;
private static MethodInfo getScriptingBackendMethod;
private static MethodInfo getApiCompatibilityLevelMethod;
private static MethodInfo apiCompatibilityLevelProperty;
/// <summary>
/// All valid Unity BuildTarget platforms.
/// </summary>
public static readonly ImmutableList<BuildTarget> Platforms;
/// <summary>
/// All valid Unity BuildTarget platforms that support Just In Time compilation.
/// </summary>
public static readonly ImmutableList<BuildTarget> JITPlatforms;
/// <summary>
/// All scripting backends that support JIT.
/// </summary>
public static readonly ImmutableList<ScriptingImplementation> JITScriptingBackends;
/// <summary>
/// All API compatibility levels that support JIT.
/// </summary>
public static readonly ImmutableList<ApiCompatibilityLevel> JITApiCompatibilityLevels;
static AssemblyImportSettingsUtilities()
{
// Different methods required for getting the current scripting backend from different versions of the Unity Editor.
getPropertyIntMethod = typeof(PlayerSettings).GetMethod("GetPropertyInt", Flags.StaticPublic, null, new Type[] { typeof(string), typeof(BuildTargetGroup) }, null);
getScriptingBackendMethod = typeof(PlayerSettings).GetMethod("GetScriptingBackend", Flags.StaticPublic, null, new Type[] { typeof(BuildTargetGroup) }, null);
// Diffferent methods required for getting the current api level from different versions of the Unity Editor.
getApiCompatibilityLevelMethod = typeof(PlayerSettings).GetMethod("GetApiCompatibilityLevel", Flags.StaticPublic, null, new Type[] { typeof(BuildTargetGroup) }, null);
var apiLevelProperty = typeof(PlayerSettings).GetProperty("apiCompatibilityLevel", Flags.StaticPublic);
apiCompatibilityLevelProperty = apiLevelProperty != null ? apiLevelProperty.GetGetMethod() : null;
// All valid BuildTarget values.
Platforms = new ImmutableList<BuildTarget>(Enum.GetValues(typeof(BuildTarget))
.Cast<BuildTarget>()
.Where(t => t >= 0 && typeof(BuildTarget).GetMember(t.ToString())[0].IsDefined(typeof(ObsoleteAttribute), false) == false)
.ToArray());
// All BuildTarget values that support JIT.
JITPlatforms = new ImmutableList<BuildTarget>(Platforms
.Where(i => i.ToString().StartsWith("StandaloneOSX")) // Unity 2017.3 replaced StandaloneOSXIntel, StandaloneOSXIntel64 and StandaloneOSXUniversal with StandaloneOSX.
.Append(new BuildTarget[]
{
BuildTarget.StandaloneWindows,
BuildTarget.StandaloneWindows64,
BuildTarget.StandaloneLinux64,
BuildTarget.Android
})
.ToArray());
// All scripting backends that support JIT.
JITScriptingBackends = new ImmutableList<ScriptingImplementation>(new ScriptingImplementation[]
{
ScriptingImplementation.Mono2x,
});
// Names of all api levels that support JIT.
string[] jitApiNames = new string[]
{
"NET_2_0",
"NET_2_0_Subset",
"NET_4_6",
"NET_Web", // TODO: Does NET_Web support JIT stuff?
"NET_Micro" // TODO: Does NET_Micro support JIT stuff?
};
var apiLevelNames = Enum.GetNames(typeof(ApiCompatibilityLevel));
JITApiCompatibilityLevels = new ImmutableList<ApiCompatibilityLevel>(jitApiNames
.Where(x => apiLevelNames.Contains(x))
.Select(x => (ApiCompatibilityLevel)Enum.Parse(typeof(ApiCompatibilityLevel), x))
.ToArray());
}
/// <summary>
/// Set the import settings on the assembly.
/// </summary>
/// <param name="assemblyFilePath">The path to the assembly to configure import settings from.</param>
/// <param name="importSettings">The import settings to configure for the assembly at the path.</param>
public static void SetAssemblyImportSettings(BuildTarget platform, string assemblyFilePath, OdinAssemblyImportSettings importSettings)
{
bool includeInBuild = false;
bool includeInEditor = false;
switch (importSettings)
{
case OdinAssemblyImportSettings.IncludeInAll:
includeInBuild = true;
includeInEditor = true;
break;
case OdinAssemblyImportSettings.IncludeInBuildOnly:
includeInBuild = true;
break;
case OdinAssemblyImportSettings.IncludeInEditorOnly:
includeInEditor = true;
break;
case OdinAssemblyImportSettings.ExcludeFromAll:
break;
}
SetAssemblyImportSettings(platform, assemblyFilePath, includeInBuild, includeInEditor);
}
/// <summary>
/// Set the import settings on the assembly.
/// </summary>
/// <param name="assemblyFilePath">The path to the assembly to configure import settings from.</param>
/// <param name="includeInBuild">Indicates if the assembly should be included in the build.</param>
/// <param name="includeInEditor">Indicates if the assembly should be included in the Unity editor.</param>
public static void SetAssemblyImportSettings(BuildTarget platform, string assemblyFilePath, bool includeInBuild, bool includeInEditor)
{
if (File.Exists(assemblyFilePath) == false)
{
throw new FileNotFoundException(assemblyFilePath);
}
var importer = (PluginImporter)AssetImporter.GetAtPath(assemblyFilePath);
if (importer == null)
{
throw new InvalidOperationException("Failed to get PluginImporter for " + assemblyFilePath);
}
bool updateImportSettings =
importer.GetCompatibleWithAnyPlatform() // If the 'any platform' flag is true, then reapply settings no matter what to ensure that everything is correct.
//|| Platforms.Any(p => importer.GetCompatibleWithPlatform(p) != includeInBuild)
|| importer.GetCompatibleWithPlatform(platform) != includeInBuild
|| importer.GetCompatibleWithEditor() != includeInEditor;
// Apply new import settings if necessary.
if (updateImportSettings)
{
importer.SetCompatibleWithAnyPlatform(false);
//Platforms.ForEach(p => importer.SetCompatibleWithPlatform(p, includeInBuild));
importer.SetCompatibleWithPlatform(platform, includeInBuild);
importer.SetCompatibleWithEditor(includeInEditor);
importer.SaveAndReimport();
}
}
/// <summary>
/// Gets the current scripting backend for the build from the Unity editor. This method is Unity version independent.
/// </summary>
/// <returns></returns>
public static ScriptingImplementation GetCurrentScriptingBackend()
{
var buildGroup = EditorUserBuildSettings.selectedBuildTargetGroup;
if (getScriptingBackendMethod != null)
{
return (ScriptingImplementation)getScriptingBackendMethod.Invoke(null, new object[] { buildGroup });
}
else if (getPropertyIntMethod != null)
{
return (ScriptingImplementation)getPropertyIntMethod.Invoke(null, new object[] { "ScriptingBackend", buildGroup });
}
throw new InvalidOperationException("Was unable to get the current scripting backend!");
}
/// <summary>
/// Gets the current API compatibility level from the Unity Editor. This method is Unity version independent.
/// </summary>
/// <returns></returns>
public static ApiCompatibilityLevel GetCurrentApiCompatibilityLevel()
{
if (getApiCompatibilityLevelMethod != null)
{
var buildGroup = EditorUserBuildSettings.selectedBuildTargetGroup;
return (ApiCompatibilityLevel)getApiCompatibilityLevelMethod.Invoke(null, new object[] { buildGroup });
}
else if (apiCompatibilityLevelProperty != null)
{
return (ApiCompatibilityLevel)apiCompatibilityLevelProperty.Invoke(null, null);
}
throw new InvalidOperationException("Was unable to get the current api compatibility level!");
}
/// <summary>
/// Gets a value that indicates if the specified platform supports JIT.
/// </summary>
/// <param name="platform">The platform to test.</param>
/// <returns><c>true</c> if the platform supports JIT; otherwise <c>false</c>.</returns>
public static bool PlatformSupportsJIT(BuildTarget platform)
{
return JITPlatforms.Contains(platform);
}
/// <summary>
/// Gets a value that indicates if the specified scripting backend supports JIT.
/// </summary>
/// <param name="backend">The backend to test.</param>
/// <returns><c>true</c> if the backend supports JIT; otherwise <c>false</c>.</returns>
public static bool ScriptingBackendSupportsJIT(ScriptingImplementation backend)
{
return JITScriptingBackends.Contains(backend);
}
/// <summary>
/// Gets a value that indicates if the specified api level supports JIT.
/// </summary>
/// <param name="apiLevel">The api level to test.</param>
/// <returns><c>true</c> if the api level supports JIT; otherwise <c>false</c>.</returns>
public static bool ApiCompatibilityLevelSupportsJIT(ApiCompatibilityLevel apiLevel)
{
return JITApiCompatibilityLevels.Contains(apiLevel);
}
/// <summary>
/// Gets a value that indicates if the specified build settings supports JIT.
/// </summary>
/// <param name="platform">The platform build setting.</param>
/// <param name="backend">The scripting backend build settting.</param>
/// <param name="apiLevel">The api level build setting.</param>
/// <returns><c>true</c> if the build settings supports JIT; otherwise <c>false</c>.</returns>
public static bool IsJITSupported(BuildTarget platform, ScriptingImplementation backend, ApiCompatibilityLevel apiLevel)
{
return PlatformSupportsJIT(platform) && ScriptingBackendSupportsJIT(backend) && ApiCompatibilityLevelSupportsJIT(apiLevel);
}
}
}
#endif

View File

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

View File

@ -0,0 +1,282 @@
//-----------------------------------------------------------------------
// <copyright file="Cache.cs" company="Sirenix IVS">
// Copyright (c) 2018 Sirenix IVS
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
//-----------------------------------------------------------------------
namespace VRC.Udon.Serialization.OdinSerializer.Utilities
{
using System;
using System.Threading;
public interface ICache : IDisposable
{
object Value { get; }
}
/// <summary>
/// Provides an easy way of claiming and freeing cached values of any non-abstract reference type with a public parameterless constructor.
/// <para />
/// Cached types which implement the <see cref="ICacheNotificationReceiver"/> interface will receive notifications when they are claimed and freed.
/// <para />
/// Only one thread should be holding a given cache instance at a time if <see cref="ICacheNotificationReceiver"/> is implemented, since the invocation of
/// <see cref="ICacheNotificationReceiver.OnFreed()"/> is not thread safe, IE, weird stuff might happen if multiple different threads are trying to free
/// the same cache instance at the same time. This will practically never happen unless you're doing really strange stuff, but the case is documented here.
/// </summary>
/// <typeparam name="T">The type which is cached.</typeparam>
/// <seealso cref="System.IDisposable" />
public sealed class Cache<T> : ICache where T : class, new()
{
// VRC Unity John: PER-818 - Use a BCL SpinLock (rather than a hand-rolled Interlocked.CompareExchange loop) - this improved load times in testing.
private static SpinLock FreeValuesLock = new SpinLock(false);
// VRC Unity John: PER-818 end
private static readonly bool IsNotificationReceiver = typeof(ICacheNotificationReceiver).IsAssignableFrom(typeof(T));
private static object[] FreeValues = new object[4];
private bool isFree;
private static int maxCacheSize = 5;
/// <summary>
/// Gets or sets the maximum size of the cache. This value can never go beneath 1.
/// </summary>
/// <value>
/// The maximum size of the cache.
/// </value>
public static int MaxCacheSize
{
get
{
return Cache<T>.maxCacheSize;
}
set
{
Cache<T>.maxCacheSize = Math.Max(1, value);
}
}
private Cache()
{
this.Value = new T();
this.isFree = false;
}
/// <summary>
/// The cached value.
/// </summary>
public T Value;
/// <summary>
/// Gets a value indicating whether this cached value is free.
/// </summary>
/// <value>
/// <c>true</c> if this cached value is free; otherwise, <c>false</c>.
/// </value>
public bool IsFree { get { return this.isFree; } }
object ICache.Value { get { return this.Value; } }
/// <summary>
/// Claims a cached value of type <see cref="T"/>.
/// </summary>
/// <returns>A cached value of type <see cref="T"/>.</returns>
public static Cache<T> Claim()
{
Cache<T> result = null;
// VRC Unity John: PER-818 use BCL SpinLock
bool lockTaken = false;
try
{
FreeValuesLock.Enter(ref lockTaken);
// VRC Unity John: PER-818 end
// We now hold the lock
var freeValues = FreeValues;
var length = freeValues.Length;
for (int i = 0; i < length; i++)
{
result = (Cache<T>)freeValues[i];
if (!object.ReferenceEquals(result, null))
{
freeValues[i] = null;
result.isFree = false;
break;
}
}
// VRC Unity John: PER-818 use BCL SpinLock
}
finally
{
if (lockTaken)
{
FreeValuesLock.Exit(false);
}
}
// VRC Unity John: PER-818 end
if (result == null)
{
result = new Cache<T>();
}
if (IsNotificationReceiver)
{
(result.Value as ICacheNotificationReceiver).OnClaimed();
}
return result;
}
/// <summary>
/// Releases a cached value.
/// </summary>
/// <param name="cache">The cached value to release.</param>
/// <exception cref="System.ArgumentNullException">The cached value to release is null.</exception>
public static void Release(Cache<T> cache)
{
if (cache == null)
{
throw new ArgumentNullException("cache");
}
if (cache.isFree) return;
// No need to call this method inside the lock, which might do heavy work
// there is a thread safety hole here, actually - if several different threads
// are trying to free the same cache instance, OnFreed might be called several
// times concurrently for the same cached value.
if (IsNotificationReceiver)
{
(cache.Value as ICacheNotificationReceiver).OnFreed();
}
// VRC Unity John: PER-818 use BCL SpinLock
bool lockTaken = false;
try
{
FreeValuesLock.Enter(ref lockTaken);
// VRC Unity John: PER-818 end
// We now hold the lock
if (!cache.isFree)
{
cache.isFree = true;
var freeValues = FreeValues;
var length = freeValues.Length;
bool added = false;
for (int i = 0; i < length; i++)
{
if (object.ReferenceEquals(freeValues[i], null))
{
freeValues[i] = cache;
added = true;
break;
}
}
if (!added && length < MaxCacheSize)
{
var newArr = new object[length * 2];
for (int i = 0; i < length; i++)
{
newArr[i] = freeValues[i];
}
newArr[length] = cache;
FreeValues = newArr;
}
}
// VRC Unity John: PER-818 use BCL SpinLock
}
finally
{
if (lockTaken)
{
FreeValuesLock.Exit(false);
}
}
// VRC Unity John: PER-818 end
}
/// <summary>
/// Discards all cached T.
/// </summary>
public static void Purge()
{
// VRC Unity John: PER-818 use BCL SpinLock
bool lockTaken = false;
try
{
FreeValuesLock.Enter(ref lockTaken);
// VRC Unity John: PER-818 end
// We now hold the lock
// Clear the free values array
Array.Clear(FreeValues, 0, FreeValues.Length);
// VRC Unity John: PER-818 use spinlock
}
finally
{
if (lockTaken)
{
FreeValuesLock.Exit(false);
}
}
// VRC Unity John: PER-818 end
}
/// <summary>
/// Performs an implicit conversion from <see cref="Cache{T}"/> to <see cref="T"/>.
/// </summary>
/// <param name="cache">The cache to convert.</param>
/// <returns>
/// The result of the conversion.
/// </returns>
public static implicit operator T(Cache<T> cache)
{
if (cache == null)
{
return default(T);
}
return cache.Value;
}
/// <summary>
/// Releases this cached value.
/// </summary>
public void Release()
{
Release(this);
}
/// <summary>
/// Releases this cached value.
/// </summary>
void IDisposable.Dispose()
{
Cache<T>.Release(this);
}
}
}

View File

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

View File

@ -0,0 +1,181 @@
//-----------------------------------------------------------------------
// <copyright file="DoubleLookupDictionary.cs" company="Sirenix IVS">
// Copyright (c) 2018 Sirenix IVS
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
//-----------------------------------------------------------------------
namespace VRC.Udon.Serialization.OdinSerializer.Utilities
{
using System;
using System.Collections.Generic;
/// <summary>
/// Not yet documented.
/// </summary>
[Serializable]
public class DoubleLookupDictionary<TFirstKey, TSecondKey, TValue> : Dictionary<TFirstKey, Dictionary<TSecondKey, TValue>>
{
private readonly IEqualityComparer<TSecondKey> secondKeyComparer;
public DoubleLookupDictionary()
{
this.secondKeyComparer = EqualityComparer<TSecondKey>.Default;
}
public DoubleLookupDictionary(IEqualityComparer<TFirstKey> firstKeyComparer, IEqualityComparer<TSecondKey> secondKeyComparer)
: base(firstKeyComparer)
{
this.secondKeyComparer = secondKeyComparer;
}
/// <summary>
/// Not yet documented.
/// </summary>
public new Dictionary<TSecondKey, TValue> this[TFirstKey firstKey]
{
get
{
Dictionary<TSecondKey, TValue> innerDict;
if (!this.TryGetValue(firstKey, out innerDict))
{
innerDict = new Dictionary<TSecondKey, TValue>(this.secondKeyComparer);
this.Add(firstKey, innerDict);
}
return innerDict;
}
}
/// <summary>
/// Not yet documented.
/// </summary>
public int InnerCount(TFirstKey firstKey)
{
Dictionary<TSecondKey, TValue> innerDict;
if (this.TryGetValue(firstKey, out innerDict))
{
return innerDict.Count;
}
return 0;
}
/// <summary>
/// Not yet documented.
/// </summary>
public int TotalInnerCount()
{
int count = 0;
if (this.Count > 0)
{
foreach (var innerDict in this.Values)
{
count += innerDict.Count;
}
}
return count;
}
/// <summary>
/// Not yet documented.
/// </summary>
public bool ContainsKeys(TFirstKey firstKey, TSecondKey secondKey)
{
Dictionary<TSecondKey, TValue> innerDict;
return this.TryGetValue(firstKey, out innerDict) && innerDict.ContainsKey(secondKey);
}
/// <summary>
/// Not yet documented.
/// </summary>
public bool TryGetInnerValue(TFirstKey firstKey, TSecondKey secondKey, out TValue value)
{
Dictionary<TSecondKey, TValue> innerDict;
if (this.TryGetValue(firstKey, out innerDict) && innerDict.TryGetValue(secondKey, out value))
{
return true;
}
value = default(TValue);
return false;
}
/// <summary>
/// Not yet documented.
/// </summary>
public TValue AddInner(TFirstKey firstKey, TSecondKey secondKey, TValue value)
{
if (this.ContainsKeys(firstKey, secondKey))
{
throw new ArgumentException("An element with the same keys already exists in the " + this.GetType().GetNiceName() + ".");
}
return this[firstKey][secondKey] = value;
}
/// <summary>
/// Not yet documented.
/// </summary>
public bool RemoveInner(TFirstKey firstKey, TSecondKey secondKey)
{
Dictionary<TSecondKey, TValue> innerDict;
if (this.TryGetValue(firstKey, out innerDict))
{
bool removed = innerDict.Remove(secondKey);
if (innerDict.Count == 0)
{
this.Remove(firstKey);
}
return removed;
}
return false;
}
/// <summary>
/// Not yet documented.
/// </summary>
public void RemoveWhere(Func<TValue, bool> predicate)
{
List<TFirstKey> toRemoveBufferFirstKey = new List<TFirstKey>();
List<TSecondKey> toRemoveBufferSecondKey = new List<TSecondKey>();
foreach (var outerDictionary in this.GFIterator())
{
foreach (var innerKeyPair in outerDictionary.Value.GFIterator())
{
if (predicate(innerKeyPair.Value))
{
toRemoveBufferFirstKey.Add(outerDictionary.Key);
toRemoveBufferSecondKey.Add(innerKeyPair.Key);
}
}
}
for (int i = 0; i < toRemoveBufferFirstKey.Count; i++)
{
this.RemoveInner(toRemoveBufferFirstKey[i], toRemoveBufferSecondKey[i]);
}
}
}
}

View File

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

View File

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

View File

@ -0,0 +1,44 @@
//-----------------------------------------------------------------------
// <copyright file="FastTypeComparer.cs" company="Sirenix IVS">
// Copyright (c) 2018 Sirenix IVS
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
//-----------------------------------------------------------------------
namespace VRC.Udon.Serialization.OdinSerializer.Utilities
{
using System;
using System.Collections.Generic;
/// <summary>
/// Compares types by reference before comparing them using the default type equality operator.
/// This can constitute a *significant* speedup when used as the comparer for dictionaries.
/// </summary>
/// <seealso cref="System.Collections.Generic.IEqualityComparer{System.Type}" />
public class FastTypeComparer : IEqualityComparer<Type>
{
public static readonly FastTypeComparer Instance = new FastTypeComparer();
public bool Equals(Type x, Type y)
{
if (object.ReferenceEquals(x, y)) return true; // Oft-used fast path over regular Type.Equals makes this much faster
return x == y;
}
public int GetHashCode(Type obj)
{
return obj.GetHashCode();
}
}
}

View File

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

View File

@ -0,0 +1,108 @@
//-----------------------------------------------------------------------
// <copyright file="Flags.cs" company="Sirenix IVS">
// Copyright (c) 2018 Sirenix IVS
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
//-----------------------------------------------------------------------
namespace VRC.Udon.Serialization.OdinSerializer.Utilities
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
/// <summary>
/// This class encapsulates common <see cref="BindingFlags"/> combinations.
/// </summary>
public static class Flags
{
/// <summary>
/// Search criteria encompassing all public and non-public members, including base members.
/// Note that you also need to specify either the Instance or Static flag.
/// </summary>
public const BindingFlags AnyVisibility = BindingFlags.Public | BindingFlags.NonPublic;
/// <summary>
/// Search criteria encompassing all public instance members, including base members.
/// </summary>
public const BindingFlags InstancePublic = BindingFlags.Public | BindingFlags.Instance;
/// <summary>
/// Search criteria encompassing all non-public instance members, including base members.
/// </summary>
public const BindingFlags InstancePrivate = BindingFlags.NonPublic | BindingFlags.Instance;
/// <summary>
/// Search criteria encompassing all public and non-public instance members, including base members.
/// </summary>
public const BindingFlags InstanceAnyVisibility = AnyVisibility | BindingFlags.Instance;
/// <summary>
/// Search criteria encompassing all public static members, including base members.
/// </summary>
public const BindingFlags StaticPublic = BindingFlags.Public | BindingFlags.Static;
/// <summary>
/// Search criteria encompassing all non-public static members, including base members.
/// </summary>
public const BindingFlags StaticPrivate = BindingFlags.NonPublic | BindingFlags.Static;
/// <summary>
/// Search criteria encompassing all public and non-public static members, including base members.
/// </summary>
public const BindingFlags StaticAnyVisibility = AnyVisibility | BindingFlags.Static;
/// <summary>
/// Search criteria encompassing all public instance members, excluding base members.
/// </summary>
public const BindingFlags InstancePublicDeclaredOnly = InstancePublic | BindingFlags.DeclaredOnly;
/// <summary>
/// Search criteria encompassing all non-public instance members, excluding base members.
/// </summary>
public const BindingFlags InstancePrivateDeclaredOnly = InstancePrivate | BindingFlags.DeclaredOnly;
/// <summary>
/// Search criteria encompassing all public and non-public instance members, excluding base members.
/// </summary>
public const BindingFlags InstanceAnyDeclaredOnly = InstanceAnyVisibility | BindingFlags.DeclaredOnly;
/// <summary>
/// Search criteria encompassing all public static members, excluding base members.
/// </summary>
public const BindingFlags StaticPublicDeclaredOnly = StaticPublic | BindingFlags.DeclaredOnly;
/// <summary>
/// Search criteria encompassing all non-public static members, excluding base members.
/// </summary>
public const BindingFlags StaticPrivateDeclaredOnly = StaticPrivate | BindingFlags.DeclaredOnly;
/// <summary>
/// Search criteria encompassing all public and non-public static members, excluding base members.
/// </summary>
public const BindingFlags StaticAnyDeclaredOnly = StaticAnyVisibility | BindingFlags.DeclaredOnly;
/// <summary>
/// Search criteria encompassing all members, including base and static members.
/// </summary>
public const BindingFlags StaticInstanceAnyVisibility = InstanceAnyVisibility | BindingFlags.Static;
/// <summary>
/// Search criteria encompassing all members (public and non-public, instance and static), including base members.
/// </summary>
public const BindingFlags AllMembers = StaticInstanceAnyVisibility | BindingFlags.FlattenHierarchy;
}
}

View File

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

View File

@ -0,0 +1,36 @@
//-----------------------------------------------------------------------
// <copyright file="ICacheNotificationReceiver.cs" company="Sirenix IVS">
// Copyright (c) 2018 Sirenix IVS
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
//-----------------------------------------------------------------------
namespace VRC.Udon.Serialization.OdinSerializer.Utilities
{
/// <summary>
/// Provides notification callbacks for values that are cached using the <see cref="Cache{T}"/> class.
/// </summary>
public interface ICacheNotificationReceiver
{
/// <summary>
/// Called when the cached value is freed.
/// </summary>
void OnFreed();
/// <summary>
/// Called when the cached value is claimed.
/// </summary>
void OnClaimed();
}
}

View File

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

View File

@ -0,0 +1,605 @@
//-----------------------------------------------------------------------
// <copyright file="ImmutableList.cs" company="Sirenix IVS">
// Copyright (c) 2018 Sirenix IVS
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
//-----------------------------------------------------------------------
namespace VRC.Udon.Serialization.OdinSerializer.Utilities
{
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// Interface for immutable list.
/// </summary>
public interface IImmutableList : IList
{
}
/// <summary>
/// Interface for generic immutable list.
/// </summary>
public interface IImmutableList<T> : IImmutableList, IList<T>
{
/// <summary>
/// Index accessor.
/// </summary>
new T this[int index] { get; }
}
/// <summary>
/// Immutable list wraps another list, and allows for reading the inner list, without the ability to change it.
/// </summary>
[Serializable]
public sealed class ImmutableList : IImmutableList<object>
{
[SerializeField]
private IList innerList;
/// <summary>
/// Creates an immutable list around another list.
/// </summary>
public ImmutableList(IList innerList)
{
if (innerList == null)
{
throw new ArgumentNullException("innerList");
}
this.innerList = innerList;
}
/// <summary>
/// Number of items in the list.
/// </summary>
public int Count { get { return this.innerList.Count; } }
/// <summary>
/// Immutable list cannot be changed directly, so it's size is always fixed.
/// </summary>
public bool IsFixedSize { get { return true; } }
/// <summary>
/// Immutable list are always readonly.
/// </summary>
public bool IsReadOnly { get { return true; } }
/// <summary>
/// Returns <c>true</c> if the inner list is synchronized.
/// </summary>
public bool IsSynchronized { get { return this.innerList.IsSynchronized; } }
/// <summary>
/// Gets the sync root object.
/// </summary>
public object SyncRoot { get { return this.innerList.SyncRoot; } }
object IList.this[int index]
{
get
{
return this.innerList[index];
}
set
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
}
object IList<object>.this[int index]
{
get
{
return this.innerList[index];
}
set
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
}
/// <summary>
/// Index accessor.
/// </summary>
/// <param name="index">Index.</param>
public object this[int index] { get { return this.innerList[index]; } }
/// <summary>
/// Returns <c>true</c> if the item is contained in the list.
/// </summary>
/// <param name="value">The item's value.</param>
public bool Contains(object value)
{
return this.innerList.Contains(value);
}
/// <summary>
/// Copy the list to an array,
/// </summary>
/// <param name="array">Target array.</param>
/// <param name="arrayIndex">Index.</param>
public void CopyTo(object[] array, int arrayIndex)
{
this.innerList.CopyTo(array, arrayIndex);
}
/// <summary>
/// Copy the list to an array,
/// </summary>
/// <param name="array">Target array.</param>
/// <param name="index">Index.</param>
public void CopyTo(Array array, int index)
{
this.innerList.CopyTo(array, index);
}
/// <summary>
/// Gets an enumerator.
/// </summary>
public IEnumerator GetEnumerator()
{
return this.innerList.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
IEnumerator<object> IEnumerable<object>.GetEnumerator()
{
foreach (var obj in this.innerList)
{
yield return obj;
}
}
int IList.Add(object value)
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
void IList.Clear()
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
void IList.Insert(int index, object value)
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
void IList.Remove(object value)
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
void IList.RemoveAt(int index)
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
/// <summary>
/// Get the index of a value.
/// </summary>
/// <param name="value">The item's value.</param>
public int IndexOf(object value)
{
return this.innerList.IndexOf(value);
}
/// <summary>
/// Immutable list cannot be edited.
/// </summary>
/// <param name="index">Index.</param>
void IList<object>.RemoveAt(int index)
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
/// <summary>
/// Immutable list cannot be edited.
/// </summary>
/// <param name="index">Index.</param>
/// <param name="item">Item.</param>
void IList<object>.Insert(int index, object item)
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
/// <summary>
/// Immutable list cannot be edited.
/// </summary>
/// <param name="item">Item.</param>
void ICollection<object>.Add(object item)
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
/// <summary>
/// Immutable list cannot be edited.
/// </summary>
void ICollection<object>.Clear()
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
/// <summary>
/// Immutable list cannot be edited.
/// </summary>
/// <param name="item">Item.</param>
bool ICollection<object>.Remove(object item)
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
}
/// <summary>
/// Not yet documented.
/// </summary>
[Serializable]
public sealed class ImmutableList<T> : IImmutableList<T>
{
[SerializeField]
private IList<T> innerList;
/// <summary>
/// Not yet documented.
/// </summary>
public ImmutableList(IList<T> innerList)
{
if (innerList == null)
{
throw new ArgumentNullException("innerList");
}
this.innerList = innerList;
}
/// <summary>
/// Not yet documented.
/// </summary>
public int Count { get { return this.innerList.Count; } }
bool ICollection.IsSynchronized { get { return false; } }
object ICollection.SyncRoot { get { return null; } }
bool IList.IsFixedSize { get { return true; } }
bool IList.IsReadOnly { get { return true; } }
/// <summary>
/// Not yet documented.
/// </summary>
public bool IsReadOnly { get { return true; } }
object IList.this[int index]
{
get
{
return this[index];
}
set
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
}
T IList<T>.this[int index]
{
get
{
return this.innerList[index];
}
set
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
}
/// <summary>
/// Not yet documented.
/// </summary>
public T this[int index]
{
get
{
return this.innerList[index];
}
}
/// <summary>
/// Not yet documented.
/// </summary>
public bool Contains(T item)
{
return this.innerList.Contains(item);
}
/// <summary>
/// Not yet documented.
/// </summary>
public void CopyTo(T[] array, int arrayIndex)
{
this.innerList.CopyTo(array, arrayIndex);
}
/// <summary>
/// Not yet documented.
/// </summary>
public IEnumerator<T> GetEnumerator()
{
return this.innerList.GetEnumerator();
}
void ICollection.CopyTo(Array array, int index)
{
this.innerList.CopyTo((T[])array, index);
}
void ICollection<T>.Add(T item)
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
void ICollection<T>.Clear()
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
bool ICollection<T>.Remove(T item)
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
int IList.Add(object value)
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
void IList.Clear()
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
bool IList.Contains(object value)
{
return this.innerList.Contains((T)value);
}
int IList.IndexOf(object value)
{
return this.innerList.IndexOf((T)value);
}
void IList.Insert(int index, object value)
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
void IList.Remove(object value)
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
void IList<T>.Insert(int index, T item)
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
void IList.RemoveAt(int index)
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
/// <summary>
/// Not yet documented.
/// </summary>
public int IndexOf(T item)
{
return this.innerList.IndexOf(item);
}
void IList<T>.RemoveAt(int index)
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
}
/// <summary>
/// Immutable list wraps another list, and allows for reading the inner list, without the ability to change it.
/// </summary>
[Serializable]
public sealed class ImmutableList<TList, TElement> : IImmutableList<TElement> where TList : IList<TElement>
{
private TList innerList;
/// <summary>
/// Creates an immutable list around another list.
/// </summary>
public ImmutableList(TList innerList)
{
if (innerList == null)
{
throw new ArgumentNullException("innerList");
}
this.innerList = innerList;
}
/// <summary>
/// Number of items in the list.
/// </summary>
public int Count { get { return this.innerList.Count; } }
bool ICollection.IsSynchronized { get { return false; } }
object ICollection.SyncRoot { get { return null; } }
bool IList.IsFixedSize { get { return true; } }
bool IList.IsReadOnly { get { return true; } }
/// <summary>
/// Immutable list are always readonly.
/// </summary>
public bool IsReadOnly { get { return true; } }
object IList.this[int index]
{
get
{
return this[index];
}
set
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
}
TElement IList<TElement>.this[int index]
{
get
{
return this.innerList[index];
}
set
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
}
/// <summary>
/// Index accessor.
/// </summary>
/// <param name="index">Index.</param>
public TElement this[int index]
{
get
{
return this.innerList[index];
}
}
/// <summary>
/// Returns <c>true</c> if the item is contained in the list.
/// </summary>
public bool Contains(TElement item)
{
return this.innerList.Contains(item);
}
/// <summary>
/// Copies the list to an array.
/// </summary>
public void CopyTo(TElement[] array, int arrayIndex)
{
this.innerList.CopyTo(array, arrayIndex);
}
/// <summary>
/// Gets an enumerator.
/// </summary>
public IEnumerator<TElement> GetEnumerator()
{
return this.innerList.GetEnumerator();
}
void ICollection.CopyTo(Array array, int index)
{
this.innerList.CopyTo((TElement[])array, index);
}
void ICollection<TElement>.Add(TElement item)
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
void ICollection<TElement>.Clear()
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
bool ICollection<TElement>.Remove(TElement item)
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
int IList.Add(object value)
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
void IList.Clear()
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
bool IList.Contains(object value)
{
return this.innerList.Contains((TElement)value);
}
int IList.IndexOf(object value)
{
return this.innerList.IndexOf((TElement)value);
}
void IList.Insert(int index, object value)
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
void IList.Remove(object value)
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
void IList<TElement>.Insert(int index, TElement item)
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
void IList.RemoveAt(int index)
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
/// <summary>
/// Gets the index of an item.
/// </summary>
public int IndexOf(TElement item)
{
return this.innerList.IndexOf(item);
}
void IList<TElement>.RemoveAt(int index)
{
throw new NotSupportedException("Immutable Lists cannot be edited.");
}
}
}

View File

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

View File

@ -0,0 +1,179 @@
//-----------------------------------------------------------------------
// <copyright file="MemberAliasFieldInfo.cs" company="Sirenix IVS">
// Copyright (c) 2018 Sirenix IVS
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
//-----------------------------------------------------------------------
namespace VRC.Udon.Serialization.OdinSerializer.Utilities
{
using System;
using System.Globalization;
using System.Reflection;
/// <summary>
/// Provides a methods of representing imaginary fields which are unique to serialization.
/// <para />
/// We aggregate the FieldInfo associated with this member and return a mangled form of the name.
/// </summary>
/// <seealso cref="System.Reflection.FieldInfo" />
public sealed class MemberAliasFieldInfo : FieldInfo
{
/// <summary>
/// The default fake name separator string.
/// </summary>
private const string FAKE_NAME_SEPARATOR_STRING = "+";
private FieldInfo aliasedField;
private string mangledName;
/// <summary>
/// Initializes a new instance of the <see cref="MemberAliasFieldInfo"/> class.
/// </summary>
/// <param name="field">The field to alias.</param>
/// <param name="namePrefix">The name prefix to use.</param>
public MemberAliasFieldInfo(FieldInfo field, string namePrefix)
{
this.aliasedField = field;
this.mangledName = string.Concat(namePrefix, FAKE_NAME_SEPARATOR_STRING, this.aliasedField.Name);
}
/// <summary>
/// Initializes a new instance of the <see cref="MemberAliasFieldInfo"/> class.
/// </summary>
/// <param name="field">The field to alias.</param>
/// <param name="namePrefix">The name prefix to use.</param>
/// <param name="separatorString">The separator string to use.</param>
public MemberAliasFieldInfo(FieldInfo field, string namePrefix, string separatorString)
{
this.aliasedField = field;
this.mangledName = string.Concat(namePrefix, separatorString, this.aliasedField.Name);
}
/// <summary>
/// Gets the aliased field.
/// </summary>
/// <value>
/// The aliased field.
/// </value>
public FieldInfo AliasedField { get { return this.aliasedField; } }
/// <summary>
/// Gets the module in which the type that declares the member represented by the current <see cref="T:System.Reflection.MemberInfo" /> is defined.
/// </summary>
public override Module Module { get { return this.aliasedField.Module; } }
/// <summary>
/// Gets a value that identifies a metadata element.
/// </summary>
public override int MetadataToken { get { return this.aliasedField.MetadataToken; } }
/// <summary>
/// Gets the name of the current member.
/// </summary>
public override string Name { get { return this.mangledName; } }
/// <summary>
/// Gets the class that declares this member.
/// </summary>
public override Type DeclaringType { get { return this.aliasedField.DeclaringType; } }
/// <summary>
/// Gets the class object that was used to obtain this instance of MemberInfo.
/// </summary>
public override Type ReflectedType { get { return this.aliasedField.ReflectedType; } }
/// <summary>
/// Gets the type of the field.
/// </summary>
/// <value>
/// The type of the field.
/// </value>
public override Type FieldType { get { return this.aliasedField.FieldType; } }
/// <summary>
/// Gets a RuntimeFieldHandle, which is a handle to the internal metadata representation of a field.
/// </summary>
public override RuntimeFieldHandle FieldHandle { get { return this.aliasedField.FieldHandle; } }
/// <summary>
/// Gets the attributes.
/// </summary>
/// <value>
/// The attributes.
/// </value>
public override FieldAttributes Attributes { get { return this.aliasedField.Attributes; } }
/// <summary>
/// When overridden in a derived class, returns an array of all custom attributes applied to this member.
/// </summary>
/// <param name="inherit">True to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks.</param>
/// <returns>
/// An array that contains all the custom attributes applied to this member, or an array with zero elements if no attributes are defined.
/// </returns>
public override object[] GetCustomAttributes(bool inherit)
{
return this.aliasedField.GetCustomAttributes(inherit);
}
/// <summary>
/// When overridden in a derived class, returns an array of custom attributes applied to this member and identified by <see cref="T:System.Type" />.
/// </summary>
/// <param name="attributeType">The type of attribute to search for. Only attributes that are assignable to this type are returned.</param>
/// <param name="inherit">True to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks.</param>
/// <returns>
/// An array of custom attributes applied to this member, or an array with zero elements if no attributes assignable to <paramref name="attributeType" /> have been applied.
/// </returns>
public override object[] GetCustomAttributes(Type attributeType, bool inherit)
{
return this.aliasedField.GetCustomAttributes(attributeType, inherit);
}
/// <summary>
/// When overridden in a derived class, indicates whether one or more attributes of the specified type or of its derived types is applied to this member.
/// </summary>
/// <param name="attributeType">The type of custom attribute to search for. The search includes derived types.</param>
/// <param name="inherit">True to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks.</param>
/// <returns>
/// True if one or more instances of <paramref name="attributeType" /> or any of its derived types is applied to this member; otherwise, false.
/// </returns>
public override bool IsDefined(Type attributeType, bool inherit)
{
return this.aliasedField.IsDefined(attributeType, inherit);
}
/// <summary>
/// Gets the value of the field.
/// </summary>
/// <param name="obj">The object instance to get the value from.</param>
/// <returns>The value of the field.</returns>
public override object GetValue(object obj)
{
return this.aliasedField.GetValue(obj);
}
/// <summary>
/// When overridden in a derived class, sets the value of the field supported by the given object.
/// </summary>
/// <param name="obj">The object whose field value will be set.</param>
/// <param name="value">The value to assign to the field.</param>
/// <param name="invokeAttr">A field of Binder that specifies the type of binding that is desired (for example, Binder.CreateInstance or Binder.ExactBinding).</param>
/// <param name="binder">A set of properties that enables the binding, coercion of argument types, and invocation of members through reflection. If <paramref name="binder" /> is null, then Binder.DefaultBinding is used.</param>
/// <param name="culture">The software preferences of a particular culture.</param>
public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, CultureInfo culture)
{
this.aliasedField.SetValue(obj, value, invokeAttr, binder, culture);
}
}
}

View File

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

View File

@ -0,0 +1,194 @@
//-----------------------------------------------------------------------
// <copyright file="MemberAliasMethodInfo.cs" company="Sirenix IVS">
// Copyright (c) 2018 Sirenix IVS
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
//-----------------------------------------------------------------------
namespace VRC.Udon.Serialization.OdinSerializer.Utilities
{
using System;
using System.Globalization;
using System.Reflection;
/// <summary>
/// Provides a methods of representing aliased methods.
/// <para />
/// In this case, what we're representing is a method on a parent class with the same name.
/// <para />
/// We aggregate the MethodInfo associated with this member and return a mangled form of the name.
/// The name that we return is "parentname+methodName".
/// </summary>
/// <seealso cref="System.Reflection.FieldInfo" />
public sealed class MemberAliasMethodInfo : MethodInfo
{
/// <summary>
/// The default fake name separator string.
/// </summary>
private const string FAKE_NAME_SEPARATOR_STRING = "+";
private MethodInfo aliasedMethod;
private string mangledName;
/// <summary>
/// Initializes a new instance of the <see cref="MemberAliasMethodInfo"/> class.
/// </summary>
/// <param name="method">The method to alias.</param>
/// <param name="namePrefix">The name prefix to use.</param>
public MemberAliasMethodInfo(MethodInfo method, string namePrefix)
{
this.aliasedMethod = method;
this.mangledName = string.Concat(namePrefix, FAKE_NAME_SEPARATOR_STRING, this.aliasedMethod.Name);
}
/// <summary>
/// Initializes a new instance of the <see cref="MemberAliasMethodInfo"/> class.
/// </summary>
/// <param name="method">The method to alias.</param>
/// <param name="namePrefix">The name prefix to use.</param>
/// <param name="separatorString">The separator string to use.</param>
public MemberAliasMethodInfo(MethodInfo method, string namePrefix, string separatorString)
{
this.aliasedMethod = method;
this.mangledName = string.Concat(namePrefix, separatorString, this.aliasedMethod.Name);
}
/// <summary>
/// Gets the aliased method.
/// </summary>
/// <value>
/// The aliased method.
/// </value>
public MethodInfo AliasedMethod { get { return this.aliasedMethod; } }
/// <summary>
/// Gets the custom attributes for the return type.
/// </summary>
public override ICustomAttributeProvider ReturnTypeCustomAttributes { get { return this.aliasedMethod.ReturnTypeCustomAttributes; } }
/// <summary>
/// Gets a handle to the internal metadata representation of a method.
/// </summary>
public override RuntimeMethodHandle MethodHandle { get { return this.aliasedMethod.MethodHandle; } }
/// <summary>
/// Gets the attributes associated with this method.
/// </summary>
public override MethodAttributes Attributes { get { return this.aliasedMethod.Attributes; } }
public override Type ReturnType { get { return this.aliasedMethod.ReturnType; } }
/// <summary>
/// Gets the class that declares this member.
/// </summary>
public override Type DeclaringType { get { return this.aliasedMethod.DeclaringType; } }
/// <summary>
/// Gets the name of the current member.
/// </summary>
public override string Name { get { return this.mangledName; } }
/// <summary>
/// Gets the class object that was used to obtain this instance of MemberInfo.
/// </summary>
public override Type ReflectedType { get { return this.aliasedMethod.ReflectedType; } }
/// <summary>
/// When overridden in a derived class, returns the MethodInfo object for the method on the direct or indirect base class in which the method represented by this instance was first declared.
/// </summary>
/// <returns>
/// A MethodInfo object for the first implementation of this method.
/// </returns>
public override MethodInfo GetBaseDefinition()
{
return this.aliasedMethod.GetBaseDefinition();
}
/// <summary>
/// When overridden in a derived class, returns an array of all custom attributes applied to this member.
/// </summary>
/// <param name="inherit">true to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks.</param>
/// <returns>
/// An array that contains all the custom attributes applied to this member, or an array with zero elements if no attributes are defined.
/// </returns>
public override object[] GetCustomAttributes(bool inherit)
{
return this.aliasedMethod.GetCustomAttributes(inherit);
}
/// <summary>
/// When overridden in a derived class, returns an array of custom attributes applied to this member and identified by <see cref="T:System.Type" />.
/// </summary>
/// <param name="attributeType">The type of attribute to search for. Only attributes that are assignable to this type are returned.</param>
/// <param name="inherit">true to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks.</param>
/// <returns>
/// An array of custom attributes applied to this member, or an array with zero elements if no attributes assignable to <paramref name="attributeType" /> have been applied.
/// </returns>
public override object[] GetCustomAttributes(Type attributeType, bool inherit)
{
return this.aliasedMethod.GetCustomAttributes(attributeType, inherit);
}
/// <summary>
/// When overridden in a derived class, returns the <see cref="T:System.Reflection.MethodImplAttributes" /> flags.
/// </summary>
/// <returns>
/// The MethodImplAttributes flags.
/// </returns>
public override MethodImplAttributes GetMethodImplementationFlags()
{
return this.aliasedMethod.GetMethodImplementationFlags();
}
/// <summary>
/// When overridden in a derived class, gets the parameters of the specified method or constructor.
/// </summary>
/// <returns>
/// An array of type ParameterInfo containing information that matches the signature of the method (or constructor) reflected by this MethodBase instance.
/// </returns>
public override ParameterInfo[] GetParameters()
{
return this.aliasedMethod.GetParameters();
}
/// <summary>
/// When overridden in a derived class, invokes the reflected method or constructor with the given parameters.
/// </summary>
/// <param name="obj">The object on which to invoke the method or constructor. If a method is static, this argument is ignored. If a constructor is static, this argument must be null or an instance of the class that defines the constructor.</param>
/// <param name="invokeAttr">A bitmask that is a combination of 0 or more bit flags from <see cref="T:System.Reflection.BindingFlags" />. If <paramref name="binder" /> is null, this parameter is assigned the value <see cref="F:System.Reflection.BindingFlags.Default" />; thus, whatever you pass in is ignored.</param>
/// <param name="binder">An object that enables the binding, coercion of argument types, invocation of members, and retrieval of MemberInfo objects via reflection. If <paramref name="binder" /> is null, the default binder is used.</param>
/// <param name="parameters">An argument list for the invoked method or constructor. This is an array of objects with the same number, order, and type as the parameters of the method or constructor to be invoked. If there are no parameters, this should be null.If the method or constructor represented by this instance takes a ByRef parameter, there is no special attribute required for that parameter in order to invoke the method or constructor using this function. Any object in this array that is not explicitly initialized with a value will contain the default value for that object type. For reference-type elements, this value is null. For value-type elements, this value is 0, 0.0, or false, depending on the specific element type.</param>
/// <param name="culture">An instance of CultureInfo used to govern the coercion of types. If this is null, the CultureInfo for the current thread is used. (This is necessary to convert a String that represents 1000 to a Double value, for example, since 1000 is represented differently by different cultures.)</param>
/// <returns>
/// An Object containing the return value of the invoked method, or null in the case of a constructor, or null if the method's return type is void. Before calling the method or constructor, Invoke checks to see if the user has access permission and verifies that the parameters are valid.CautionElements of the <paramref name="parameters" /> array that represent parameters declared with the ref or out keyword may also be modified.
/// </returns>
public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture)
{
return this.aliasedMethod.Invoke(obj, invokeAttr, binder, parameters, culture);
}
/// <summary>
/// When overridden in a derived class, indicates whether one or more attributes of the specified type or of its derived types is applied to this member.
/// </summary>
/// <param name="attributeType">The type of custom attribute to search for. The search includes derived types.</param>
/// <param name="inherit">true to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks.</param>
/// <returns>
/// true if one or more instances of <paramref name="attributeType" /> or any of its derived types is applied to this member; otherwise, false.
/// </returns>
public override bool IsDefined(Type attributeType, bool inherit)
{
return this.aliasedMethod.IsDefined(attributeType, inherit);
}
}
}

View File

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

View File

@ -0,0 +1,237 @@
//-----------------------------------------------------------------------
// <copyright file="MemberAliasPropertyInfo.cs" company="Sirenix IVS">
// Copyright (c) 2018 Sirenix IVS
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
//-----------------------------------------------------------------------
namespace VRC.Udon.Serialization.OdinSerializer.Utilities
{
using System;
using System.Globalization;
using System.Reflection;
/// <summary>
/// Provides a methods of representing imaginary properties which are unique to serialization.
/// <para />
/// We aggregate the PropertyInfo associated with this member and return a mangled form of the name.
/// </summary>
/// <seealso cref="System.Reflection.FieldInfo" />
public sealed class MemberAliasPropertyInfo : PropertyInfo
{
/// <summary>
/// The default fake name separator string.
/// </summary>
private const string FakeNameSeparatorString = "+";
private PropertyInfo aliasedProperty;
private string mangledName;
/// <summary>
/// Initializes a new instance of the <see cref="MemberAliasPropertyInfo"/> class.
/// </summary>
/// <param name="prop">The property to alias.</param>
/// <param name="namePrefix">The name prefix to use.</param>
public MemberAliasPropertyInfo(PropertyInfo prop, string namePrefix)
{
this.aliasedProperty = prop;
this.mangledName = string.Concat(namePrefix, FakeNameSeparatorString, this.aliasedProperty.Name);
}
/// <summary>
/// Initializes a new instance of the <see cref="MemberAliasPropertyInfo"/> class.
/// </summary>
/// <param name="prop">The property to alias.</param>
/// <param name="namePrefix">The name prefix to use.</param>
/// <param name="separatorString">The separator string to use.</param>
public MemberAliasPropertyInfo(PropertyInfo prop, string namePrefix, string separatorString)
{
this.aliasedProperty = prop;
this.mangledName = string.Concat(namePrefix, separatorString, this.aliasedProperty.Name);
}
/// <summary>
/// The backing PropertyInfo that is being aliased.
/// </summary>
public PropertyInfo AliasedProperty { get { return this.aliasedProperty; } }
/// <summary>
/// Gets the module in which the type that declares the member represented by the current <see cref="T:System.Reflection.MemberInfo" /> is defined.
/// </summary>
public override Module Module { get { return this.aliasedProperty.Module; } }
/// <summary>
/// Gets a value that identifies a metadata element.
/// </summary>
public override int MetadataToken { get { return this.aliasedProperty.MetadataToken; } }
/// <summary>
/// Gets the name of the current member.
/// </summary>
public override string Name { get { return this.mangledName; } }
/// <summary>
/// Gets the class that declares this member.
/// </summary>
public override Type DeclaringType { get { return this.aliasedProperty.DeclaringType; } }
/// <summary>
/// Gets the class object that was used to obtain this instance of MemberInfo.
/// </summary>
public override Type ReflectedType { get { return this.aliasedProperty.ReflectedType; } }
/// <summary>
/// Gets the type of the property.
/// </summary>
/// <value>
/// The type of the property.
/// </value>
public override Type PropertyType { get { return this.aliasedProperty.PropertyType; } }
/// <summary>
/// Gets the attributes.
/// </summary>
/// <value>
/// The attributes.
/// </value>
public override PropertyAttributes Attributes { get { return this.aliasedProperty.Attributes; } }
/// <summary>
/// Gets a value indicating whether this instance can read.
/// </summary>
/// <value>
/// <c>true</c> if this instance can read; otherwise, <c>false</c>.
/// </value>
public override bool CanRead { get { return this.aliasedProperty.CanRead; } }
/// <summary>
/// Gets a value indicating whether this instance can write.
/// </summary>
/// <value>
/// <c>true</c> if this instance can write; otherwise, <c>false</c>.
/// </value>
public override bool CanWrite { get { return this.aliasedProperty.CanWrite; } }
/// <summary>
/// When overridden in a derived class, returns an array of all custom attributes applied to this member.
/// </summary>
/// <param name="inherit">True to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks.</param>
/// <returns>
/// An array that contains all the custom attributes applied to this member, or an array with zero elements if no attributes are defined.
/// </returns>
public override object[] GetCustomAttributes(bool inherit)
{
return this.aliasedProperty.GetCustomAttributes(inherit);
}
/// <summary>
/// When overridden in a derived class, returns an array of custom attributes applied to this member and identified by <see cref="T:System.Type" />.
/// </summary>
/// <param name="attributeType">The type of attribute to search for. Only attributes that are assignable to this type are returned.</param>
/// <param name="inherit">True to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks.</param>
/// <returns>
/// An array of custom attributes applied to this member, or an array with zero elements if no attributes assignable to <paramref name="attributeType" /> have been applied.
/// </returns>
public override object[] GetCustomAttributes(Type attributeType, bool inherit)
{
return this.aliasedProperty.GetCustomAttributes(attributeType, inherit);
}
/// <summary>
/// When overridden in a derived class, indicates whether one or more attributes of the specified type or of its derived types is applied to this member.
/// </summary>
/// <param name="attributeType">The type of custom attribute to search for. The search includes derived types.</param>
/// <param name="inherit">True to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks.</param>
/// <returns>
/// True if one or more instances of <paramref name="attributeType" /> or any of its derived types is applied to this member; otherwise, false.
/// </returns>
public override bool IsDefined(Type attributeType, bool inherit)
{
return this.aliasedProperty.IsDefined(attributeType, inherit);
}
/// <summary>
/// Returns an array whose elements reflect the public and, if specified, non-public get, set, and other accessors of the property reflected by the current instance.
/// </summary>
/// <param name="nonPublic">Indicates whether non-public methods should be returned in the MethodInfo array. true if non-public methods are to be included; otherwise, false.</param>
/// <returns>
/// An array of <see cref="T:System.Reflection.MethodInfo" /> objects whose elements reflect the get, set, and other accessors of the property reflected by the current instance. If <paramref name="nonPublic" /> is true, this array contains public and non-public get, set, and other accessors. If <paramref name="nonPublic" /> is false, this array contains only public get, set, and other accessors. If no accessors with the specified visibility are found, this method returns an array with zero (0) elements.
/// </returns>
public override MethodInfo[] GetAccessors(bool nonPublic)
{
return this.aliasedProperty.GetAccessors(nonPublic);
}
/// <summary>
/// When overridden in a derived class, returns the public or non-public get accessor for this property.
/// </summary>
/// <param name="nonPublic">Indicates whether a non-public get accessor should be returned. true if a non-public accessor is to be returned; otherwise, false.</param>
/// <returns>
/// A MethodInfo object representing the get accessor for this property, if <paramref name="nonPublic" /> is true. Returns null if <paramref name="nonPublic" /> is false and the get accessor is non-public, or if <paramref name="nonPublic" /> is true but no get accessors exist.
/// </returns>
public override MethodInfo GetGetMethod(bool nonPublic)
{
return this.aliasedProperty.GetGetMethod(nonPublic);
}
/// <summary>
/// Gets the index parameters of the property.
/// </summary>
/// <returns>The index parameters of the property.</returns>
public override ParameterInfo[] GetIndexParameters()
{
return this.aliasedProperty.GetIndexParameters();
}
/// <summary>
/// When overridden in a derived class, returns the set accessor for this property.
/// </summary>
/// <param name="nonPublic">Indicates whether the accessor should be returned if it is non-public. true if a non-public accessor is to be returned; otherwise, false.</param>
/// <returns>
/// Value Condition A <see cref="T:System.Reflection.MethodInfo" /> object representing the Set method for this property. The set accessor is public.-or- <paramref name="nonPublic" /> is true and the set accessor is non-public. null<paramref name="nonPublic" /> is true, but the property is read-only.-or- <paramref name="nonPublic" /> is false and the set accessor is non-public.-or- There is no set accessor.
/// </returns>
public override MethodInfo GetSetMethod(bool nonPublic)
{
return this.aliasedProperty.GetSetMethod(nonPublic);
}
/// <summary>
/// Gets the value of the property on the given instance.
/// </summary>
/// <param name="obj">The object to invoke the getter on.</param>
/// <param name="invokeAttr">The <see cref="BindingFlags"/> to invoke with.</param>
/// <param name="binder">The binder to use.</param>
/// <param name="index">The indices to use.</param>
/// <param name="culture">The culture to use.</param>
/// <returns>The value of the property on the given instance.</returns>
public override object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture)
{
return this.aliasedProperty.GetValue(obj, invokeAttr, binder, index, culture);
}
/// <summary>
/// Sets the value of the property on the given instance.
/// </summary>
/// <param name="obj">The object to set the value on.</param>
/// <param name="value">The value to set.</param>
/// <param name="invokeAttr">The <see cref="BindingFlags"/> to invoke with.</param>
/// <param name="binder">The binder to use.</param>
/// <param name="index">The indices to use.</param>
/// <param name="culture">The culture to use.</param>
public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture)
{
this.aliasedProperty.SetValue(obj, value, invokeAttr, binder, index, culture);
}
}
}

View File

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

View File

@ -0,0 +1,57 @@
//-----------------------------------------------------------------------
// <copyright file="ReferenceEqualityComparer.cs" company="Sirenix IVS">
// Copyright (c) 2018 Sirenix IVS
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
//-----------------------------------------------------------------------
namespace VRC.Udon.Serialization.OdinSerializer.Utilities
{
using System;
using System.Collections.Generic;
/// <summary>
/// Compares objects by reference only, ignoring equality operators completely. This is used by the property tree reference dictionaries to keep track of references.
/// </summary>
public class ReferenceEqualityComparer<T> : IEqualityComparer<T> where T : class
{
/// <summary>
/// A default, cached instance of this generic variant of the reference equality comparer.
/// </summary>
public static readonly ReferenceEqualityComparer<T> Default = new ReferenceEqualityComparer<T>();
/// <summary>
/// Returns true if the object references are equal.
/// </summary>
public bool Equals(T x, T y)
{
return object.ReferenceEquals(x, y);
}
/// <summary>
/// Returns the result of the object's own GetHashCode method.
/// </summary>
public int GetHashCode(T obj)
{
try
{
return obj.GetHashCode();
}
catch (NullReferenceException)
{
return -1;
}
}
}
}

View File

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

View File

@ -0,0 +1,81 @@
//-----------------------------------------------------------------------
// <copyright file="UnityVersion.cs" company="Sirenix IVS">
// Copyright (c) 2018 Sirenix IVS
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
//-----------------------------------------------------------------------
namespace VRC.Udon.Serialization.OdinSerializer.Utilities
{
using UnityEngine;
/// <summary>
/// Utility class indicating current Unity version.
/// </summary>
#if UNITY_EDITOR
[UnityEditor.InitializeOnLoad]
#endif
public static class UnityVersion
{
static UnityVersion()
{
string[] version = Application.unityVersion.Split('.');
if (version.Length < 2)
{
Debug.LogError("Could not parse current Unity version '" + Application.unityVersion + "'; not enough version elements.");
return;
}
if (int.TryParse(version[0], out Major) == false)
{
Debug.LogError("Could not parse major part '" + version[0] + "' of Unity version '" + Application.unityVersion + "'.");
}
if (int.TryParse(version[1], out Minor) == false)
{
Debug.LogError("Could not parse minor part '" + version[1] + "' of Unity version '" + Application.unityVersion + "'.");
}
}
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void EnsureLoaded()
{
// This method ensures that this type has been initialized before any loading of objects occurs.
// If this isn't done, the static constructor may be invoked at an illegal time that is not
// allowed by Unity. During scene deserialization, off the main thread, is an example.
}
/// <summary>
/// Tests current Unity version is equal or greater.
/// </summary>
/// <param name="major">Minimum major version.</param>
/// <param name="minor">Minimum minor version.</param>
/// <returns><c>true</c> if the current Unity version is greater. Otherwise <c>false</c>.</returns>
public static bool IsVersionOrGreater(int major, int minor)
{
return UnityVersion.Major > major || (UnityVersion.Major == major && UnityVersion.Minor >= minor);
}
/// <summary>
/// The current Unity version major.
/// </summary>
public static readonly int Major;
/// <summary>
/// The current Unity version minor.
/// </summary>
public static readonly int Minor;
}
}

View File

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

View File

@ -0,0 +1,408 @@
//-----------------------------------------------------------------------
// <copyright file="UnsafeUtilities.cs" company="Sirenix IVS">
// Copyright (c) 2018 Sirenix IVS
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
//-----------------------------------------------------------------------
namespace VRC.Udon.Serialization.OdinSerializer.Utilities.Unsafe
{
using System;
using System.Runtime.InteropServices;
#pragma warning disable 0649 // Field is never assigned to and will have its default value // VRC
/// <summary>
/// Contains utilities for performing common unsafe operations.
/// </summary>
public static class UnsafeUtilities
{
/// <summary>
/// Blindly creates an array of structs from an array of bytes via direct memory copy/blit.
/// </summary>
public static unsafe T[] StructArrayFromBytes<T>(byte[] bytes, int byteLength) where T : struct
{
return StructArrayFromBytes<T>(bytes, 0, 0);
}
/// <summary>
/// Blindly creates an array of structs from an array of bytes via direct memory copy/blit.
/// </summary>
public static unsafe T[] StructArrayFromBytes<T>(byte[] bytes, int byteLength, int byteOffset) where T : struct
{
if (bytes == null)
{
throw new ArgumentNullException("bytes");
}
if (byteLength <= 0)
{
throw new ArgumentException("Byte length must be larger than zero.");
}
if (byteOffset < 0)
{
throw new ArgumentException("Byte offset must be larger than or equal to zero.");
}
int typeSize = Marshal.SizeOf(typeof(T));
if (byteOffset % sizeof(ulong) != 0)
{
throw new ArgumentException("Byte offset must be divisible by " + sizeof(ulong) + " (IE, sizeof(ulong))");
}
if (byteLength + byteOffset >= bytes.Length)
{
throw new ArgumentException("Given byte array of size " + bytes.Length + " is not large enough to copy requested number of bytes " + byteLength + ".");
}
if ((byteLength - byteOffset) % typeSize != 0)
{
throw new ArgumentException("The length in the given byte array (" + bytes.Length + ", and " + (bytes.Length - byteOffset) + " minus byteOffset " + byteOffset + ") to convert to type " + typeof(T).Name + " is not divisible by the size of " + typeof(T).Name + " (" + typeSize + ").");
}
int elementCount = (bytes.Length - byteOffset) / typeSize;
T[] array = new T[elementCount];
MemoryCopy(bytes, array, byteLength, byteOffset, 0);
return array;
}
/// <summary>
/// Blindly copies an array of structs into an array of bytes via direct memory copy/blit.
/// </summary>
public static unsafe byte[] StructArrayToBytes<T>(T[] array) where T : struct
{
byte[] bytes = null;
return StructArrayToBytes(array, ref bytes, 0);
}
/// <summary>
/// Blindly copies an array of structs into an array of bytes via direct memory copy/blit.
/// </summary>
public static unsafe byte[] StructArrayToBytes<T>(T[] array, ref byte[] bytes, int byteOffset) where T : struct
{
if (array == null)
{
throw new ArgumentNullException("array");
}
if (byteOffset < 0)
{
throw new ArgumentException("Byte offset must be larger than or equal to zero.");
}
int typeSize = Marshal.SizeOf(typeof(T));
int byteCount = typeSize * array.Length;
if (bytes == null)
{
bytes = new byte[byteCount + byteOffset];
}
else if (bytes.Length + byteOffset > byteCount)
{
throw new ArgumentException("Byte array must be at least " + (bytes.Length + byteOffset) + " long with the given byteOffset.");
}
MemoryCopy(array, bytes, byteCount, 0, byteOffset);
return bytes;
}
/// <summary>
/// Creates a new string from the contents of a given byte buffer.
/// </summary>
public static unsafe string StringFromBytes(byte[] buffer, int charLength, bool needs16BitSupport)
{
int byteCount = needs16BitSupport ? charLength * 2 : charLength;
if (buffer.Length < byteCount)
{
throw new ArgumentException("Buffer is not large enough to contain the given string; a size of at least " + byteCount + " is required.");
}
GCHandle toHandle = default(GCHandle);
string result = new string(' ', charLength); // Creaty empty string of required length
try
{
toHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
if (needs16BitSupport)
{
if (BitConverter.IsLittleEndian)
{
fixed (char* charPtr1 = result)
{
ushort* fromPtr1 = (ushort*)toHandle.AddrOfPinnedObject().ToPointer();
ushort* toPtr1 = (ushort*)charPtr1;
for (int i = 0; i < byteCount; i += sizeof(ushort))
{
*toPtr1++ = *fromPtr1++;
}
}
}
else
{
fixed (char* charPtr2 = result)
{
byte* fromPtr2 = (byte*)toHandle.AddrOfPinnedObject().ToPointer();
byte* toPtr2 = (byte*)charPtr2;
for (int i = 0; i < byteCount; i += sizeof(ushort))
{
*toPtr2 = *(fromPtr2 + 1);
*(toPtr2 + 1) = *fromPtr2;
fromPtr2 += 2;
toPtr2 += 2;
}
}
}
}
else
{
if (BitConverter.IsLittleEndian)
{
fixed (char* charPtr3 = result)
{
byte* fromPtr3 = (byte*)toHandle.AddrOfPinnedObject().ToPointer();
byte* toPtr3 = (byte*)charPtr3;
for (int i = 0; i < byteCount; i += sizeof(byte))
{
*toPtr3++ = *fromPtr3++;
toPtr3++; // Skip every other string byte
}
}
}
else
{
fixed (char* charPtr4 = result)
{
byte* fromPtr4 = (byte*)toHandle.AddrOfPinnedObject().ToPointer();
byte* toPtr4 = (byte*)charPtr4;
for (int i = 0; i < byteCount; i += sizeof(byte))
{
toPtr4++; // Skip every other string byte
*toPtr4++ = *fromPtr4++;
}
}
}
}
}
finally
{
if (toHandle.IsAllocated)
{
toHandle.Free();
}
}
// Retrieve proper string reference from the intern pool.
// This code removed for now, as the slight decrease in memory use is not considered worth the performance cost of the intern lookup and the potential extra garbage to be collected.
// Might eventually become a global config option, if this is considered necessary.
//result = string.Intern(result);
return result;
}
/// <summary>
/// Writes the contents of a string into a given byte buffer.
/// </summary>
public static unsafe int StringToBytes(byte[] buffer, string value, bool needs16BitSupport)
{
int byteCount = needs16BitSupport ? value.Length * 2 : value.Length;
if (buffer.Length < byteCount)
{
throw new ArgumentException("Buffer is not large enough to contain the given string; a size of at least " + byteCount + " is required.");
}
GCHandle toHandle = default(GCHandle);
try
{
toHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
if (needs16BitSupport)
{
if (BitConverter.IsLittleEndian)
{
fixed (char* charPtr1 = value)
{
ushort* fromPtr1 = (ushort*)charPtr1;
ushort* toPtr1 = (ushort*)toHandle.AddrOfPinnedObject().ToPointer();
for (int i = 0; i < byteCount; i += sizeof(ushort))
{
*toPtr1++ = *fromPtr1++;
}
}
}
else
{
fixed (char* charPtr2 = value)
{
byte* fromPtr2 = (byte*)charPtr2;
byte* toPtr2 = (byte*)toHandle.AddrOfPinnedObject().ToPointer();
for (int i = 0; i < byteCount; i += sizeof(ushort))
{
*toPtr2 = *(fromPtr2 + 1);
*(toPtr2 + 1) = *fromPtr2;
fromPtr2 += 2;
toPtr2 += 2;
}
}
}
}
else
{
if (BitConverter.IsLittleEndian)
{
fixed (char* charPtr3 = value)
{
byte* fromPtr3 = (byte*)charPtr3;
byte* toPtr3 = (byte*)toHandle.AddrOfPinnedObject().ToPointer();
for (int i = 0; i < byteCount; i += sizeof(byte))
{
fromPtr3++; // Skip every other string byte
*toPtr3++ = *fromPtr3++;
}
}
}
else
{
fixed (char* charPtr4 = value)
{
byte* fromPtr4 = (byte*)charPtr4;
byte* toPtr4 = (byte*)toHandle.AddrOfPinnedObject().ToPointer();
for (int i = 0; i < byteCount; i += sizeof(byte))
{
*toPtr4++ = *fromPtr4++;
fromPtr4++; // Skip every other string byte
}
}
}
}
}
finally
{
if (toHandle.IsAllocated)
{
toHandle.Free();
}
}
return byteCount;
}
private struct Struct256Bit
{
public decimal d1;
public decimal d2;
}
public static unsafe void MemoryCopy(void* from, void* to, int bytes)
{
byte* end = (byte*)to + bytes;
Struct256Bit* fromBigPtr = (Struct256Bit*)from;
Struct256Bit* toBigPtr = (Struct256Bit*)to;
while ((toBigPtr + 1) <= end)
{
*toBigPtr++ = *fromBigPtr++;
}
byte* fromSmallPtr = (byte*)fromBigPtr;
byte* toSmallPtr = (byte*)toBigPtr;
while (toSmallPtr < end)
{
*toSmallPtr++ = *fromSmallPtr++;
}
}
/// <summary>
/// Blindly mem-copies a given number of bytes from the memory location of one object to another. WARNING: This method is ridiculously dangerous. Only use if you know what you're doing.
/// </summary>
public static unsafe void MemoryCopy(object from, object to, int byteCount, int fromByteOffset, int toByteOffset)
{
GCHandle fromHandle = default(GCHandle);
GCHandle toHandle = default(GCHandle);
if (fromByteOffset % sizeof(ulong) != 0 || toByteOffset % sizeof(ulong) != 0)
{
throw new ArgumentException("Byte offset must be divisible by " + sizeof(ulong) + " (IE, sizeof(ulong))");
}
try
{
int restBytes = byteCount % sizeof(ulong);
int ulongCount = (byteCount - restBytes) / sizeof(ulong);
int fromOffsetCount = fromByteOffset / sizeof(ulong);
int toOffsetCount = toByteOffset / sizeof(ulong);
fromHandle = GCHandle.Alloc(from, GCHandleType.Pinned);
toHandle = GCHandle.Alloc(to, GCHandleType.Pinned);
ulong* fromUlongPtr = (ulong*)fromHandle.AddrOfPinnedObject().ToPointer();
ulong* toUlongPtr = (ulong*)toHandle.AddrOfPinnedObject().ToPointer();
if (fromOffsetCount > 0)
{
fromUlongPtr += fromOffsetCount;
}
if (toOffsetCount > 0)
{
toUlongPtr += toOffsetCount;
}
for (int i = 0; i < ulongCount; i++)
{
*toUlongPtr++ = *fromUlongPtr++;
}
if (restBytes > 0)
{
byte* fromBytePtr = (byte*)fromUlongPtr;
byte* toBytePtr = (byte*)toUlongPtr;
for (int i = 0; i < restBytes; i++)
{
*toBytePtr++ = *fromBytePtr++;
}
}
}
finally
{
if (fromHandle.IsAllocated)
{
fromHandle.Free();
}
if (toHandle.IsAllocated)
{
toHandle.Free();
}
}
}
}
}

View File

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