Add VRChat and GitHub packages to Unity project

This commit is contained in:
gunnbr committed 2021-01-18 09:14:18 -08:00
1 parent 0334a52b21
commit 85538aaccc
3297 files changed
+847877

No files matched your search

@@ -0,0 +1,38 @@
//-----------------------------------------------------------------------
// <copyright file="AllowDeserializeInvalidDataAttribute.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
{
using System;
/// <summary>
/// <para>
/// Applying this attribute to a type indicates that in the case where, when expecting to deserialize an instance of the type
/// or any of its derived types, but encountering an incompatible, uncastable type in the data being read, the serializer
/// should attempt to deserialize an instance of the expected type using the stored, possibly invalid data.
/// </para>
/// <para>
/// This is equivalent to the <see cref="SerializationConfig.AllowDeserializeInvalidData"/> option, expect type-specific instead
/// of global.
/// </para>
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = true)]
public class AllowDeserializeInvalidDataAttribute : Attribute
{
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 23fa5d3fed3b4b9de502257a594b00de
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,34 @@
//-----------------------------------------------------------------------
// <copyright file="AlwaysFormatsSelfAttribute.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
{
using System;
/// <summary>
/// Use this attribute to specify that a type that implements the <see cref="ISelfFormatter"/>
/// interface should *always* format itself regardless of other formatters being specified.
/// <para />
/// This means that the interface will be used to format all types derived from the type that
/// is decorated with this attribute, regardless of custom formatters for the derived types.
/// </summary>
/// <seealso cref="System.Attribute" />
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = true)]
public sealed class AlwaysFormatsSelfAttribute : Attribute
{
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 92726834b08002d525b86fbb012e184f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,109 @@
//-----------------------------------------------------------------------
// <copyright file="ArchitectureInfo.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
{
using System;
/// <summary>
/// This class gathers info about the current architecture for the purpose of determinining
/// the unaligned read/write capabilities that we have to work with.
/// </summary>
public unsafe static class ArchitectureInfo
{
public static readonly bool Architecture_Supports_Unaligned_Float32_Reads;
/// <summary>
/// This will be false on some ARM architectures, such as ARMv7.
/// In these cases, we will have to perform slower but safer int-by-int read/writes of data.
/// <para />
/// Since this value will never change at runtime, performance hits from checking this
/// everywhere should hopefully be negligible, since branch prediction from speculative
/// execution will always predict it correctly.
/// </summary>
public static bool Architecture_Supports_All_Unaligned_ReadWrites;
static ArchitectureInfo()
{
#if UNITY_EDITOR
Architecture_Supports_Unaligned_Float32_Reads = true;
Architecture_Supports_All_Unaligned_ReadWrites = true;
return;
#endif
#pragma warning disable 0162 // Unreachable Code Detected
try
{
// Try to perform some unaligned float reads.
// If this throws an exception, the current
// architecture does not support doing this.
// Note that there are cases where this is supported
// but other unaligned read/writes are not, usually
// 64-bit read/writes. However, testing indicates
// that these read/writes cause hard crashes and not
// NullReferenceExceptions, and so we cannot test for
// them but must instead look at the architecture.
byte[] testArray = new byte[8];
fixed (byte* test = testArray)
{
// Even if test is weirdly aligned in the stack, trying four differently aligned
// reads will definitely have an unligned read or two in there.
// If all of these reads work, we are safe. We do it this way instead of just having one read,
// because as far as I have been able to determine, there are no guarantees about the alignment
// of local stack memory.
for (int i = 0; i < 4; i++)
{
float value = *(float*)(test + i);
}
Architecture_Supports_Unaligned_Float32_Reads = true;
}
}
catch (NullReferenceException)
{
Architecture_Supports_Unaligned_Float32_Reads = false;
}
}
internal static void SetIsOnAndroid(string architecture)
{
if (!Architecture_Supports_Unaligned_Float32_Reads || architecture == "armv7l" || architecture == "armv7" || IntPtr.Size == 4)
{
Architecture_Supports_All_Unaligned_ReadWrites = false;
}
else
{
Architecture_Supports_All_Unaligned_ReadWrites = true;
}
UnityEngine.Debug.Log("OdinSerializer detected Android architecture '" + architecture + "' for determining unaligned read/write capabilities. Unaligned read/write support: all=" + Architecture_Supports_All_Unaligned_ReadWrites + ", float=" + Architecture_Supports_Unaligned_Float32_Reads + "");
}
internal static void SetIsNotOnAndroid()
{
if (Architecture_Supports_Unaligned_Float32_Reads)
{
Architecture_Supports_All_Unaligned_ReadWrites = true;
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 72783638708ea644ba5c3e1b91f827f6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,210 @@
//-----------------------------------------------------------------------
// <copyright file="Buffer.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
{
using System;
using System.Collections.Generic;
/// <summary>
/// Provides a way of claiming and releasing cached array buffers.
/// </summary>
/// <typeparam name="T">The element type of the array to buffer.</typeparam>
/// <seealso cref="System.IDisposable" />
public sealed class Buffer<T> : IDisposable
{
private static readonly object LOCK = new object();
private static readonly List<Buffer<T>> FreeBuffers = new List<Buffer<T>>();
private int count;
private T[] array;
private volatile bool isFree;
private Buffer(int count)
{
this.array = new T[count];
this.count = count;
this.isFree = false; // Always start as non-free
}
/// <summary>
/// Gets the total element count of the buffered array. This will always be a power of two.
/// </summary>
/// <value>
/// The total element count of the buffered array.
/// </value>
/// <exception cref="System.InvalidOperationException">Cannot access a buffer while it is freed.</exception>
public int Count
{
get
{
if (this.isFree)
{
throw new InvalidOperationException("Cannot access a buffer while it is freed.");
}
return this.count;
}
}
/// <summary>
/// Gets the buffered array.
/// </summary>
/// <value>
/// The buffered array.
/// </value>
/// <exception cref="System.InvalidOperationException">Cannot access a buffer while it is freed.</exception>
public T[] Array
{
get
{
if (this.isFree)
{
throw new InvalidOperationException("Cannot access a buffer while it is freed.");
}
return this.array;
}
}
/// <summary>
/// Gets a value indicating whether this buffer is free.
/// </summary>
/// <value>
/// <c>true</c> if this buffer is free; otherwise, <c>false</c>.
/// </value>
public bool IsFree { get { return this.isFree; } }
/// <summary>
/// Claims a buffer with the specified minimum capacity. Note: buffers always have a capacity equal to or larger than 256.
/// </summary>
/// <param name="minimumCapacity">The minimum capacity.</param>
/// <returns>A buffer which has a capacity equal to or larger than the specified minimum capacity.</returns>
/// <exception cref="System.ArgumentException">Requested size of buffer must be larger than 0.</exception>
public static Buffer<T> Claim(int minimumCapacity)
{
if (minimumCapacity < 0)
{
throw new ArgumentException("Requested size of buffer must be larger than or equal to 0.");
}
if (minimumCapacity < 256)
{
minimumCapacity = 256; // Minimum buffer size
}
Buffer<T> result = null;
lock (LOCK)
{
// Search for a free buffer of sufficient size
for (int i = 0; i < Buffer<T>.FreeBuffers.Count; i++)
{
var buffer = Buffer<T>.FreeBuffers[i];
if (buffer != null && buffer.count >= minimumCapacity)
{
result = buffer;
result.isFree = false;
Buffer<T>.FreeBuffers[i] = null;
break;
}
}
}
if (result == null)
{
// Allocate new buffer
result = new Buffer<T>(Buffer<T>.NextPowerOfTwo(minimumCapacity));
}
return result;
}
/// <summary>
/// Frees the specified buffer.
/// </summary>
/// <param name="buffer">The buffer to free.</param>
/// <exception cref="System.ArgumentNullException">The buffer argument is null.</exception>
public static void Free(Buffer<T> buffer)
{
if (buffer == null)
{
throw new ArgumentNullException("buffer");
}
if (buffer.isFree == false)
{
lock (LOCK)
{
if (buffer.isFree == false)
{
buffer.isFree = true;
bool added = false;
for (int i = 0; i < Buffer<T>.FreeBuffers.Count; i++)
{
if (Buffer<T>.FreeBuffers[i] == null)
{
Buffer<T>.FreeBuffers[i] = buffer;
added = true;
break;
}
}
if (!added)
{
Buffer<T>.FreeBuffers.Add(buffer);
}
}
}
}
}
/// <summary>
/// Frees this buffer.
/// </summary>
public void Free()
{
Buffer<T>.Free(this);
}
/// <summary>
/// Frees this buffer.
/// </summary>
public void Dispose()
{
Buffer<T>.Free(this);
}
private static int NextPowerOfTwo(int v)
{
// Engage bit hax
// http://stackoverflow.com/questions/466204/rounding-up-to-nearest-power-of-2
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ad4e17831e9503c1f11149997c609477
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,91 @@
//-----------------------------------------------------------------------
// <copyright file="CachedMemoryStream.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
{
using VRC.Udon.Serialization.OdinSerializer.Utilities;
using System.IO;
internal sealed class CachedMemoryStream : ICacheNotificationReceiver
{
public static int InitialCapacity = 1024 * 1; // Initial capacity of 1 kb
public static int MaxCapacity = 1024 * 32; // Max of 32 kb cached stream size
private MemoryStream memoryStream;
public MemoryStream MemoryStream
{
get
{
if (!this.memoryStream.CanRead)
{
this.memoryStream = new MemoryStream(InitialCapacity);
}
return this.memoryStream;
}
}
public CachedMemoryStream()
{
this.memoryStream = new MemoryStream(InitialCapacity);
}
public void OnFreed()
{
this.memoryStream.SetLength(0);
this.memoryStream.Position = 0;
if (this.memoryStream.Capacity > MaxCapacity)
{
this.memoryStream.Capacity = MaxCapacity;
}
}
public void OnClaimed()
{
this.memoryStream.SetLength(0);
this.memoryStream.Position = 0;
}
public static Cache<CachedMemoryStream> Claim(int minCapacity)
{
var cache = Cache<CachedMemoryStream>.Claim();
if (cache.Value.MemoryStream.Capacity < minCapacity)
{
cache.Value.MemoryStream.Capacity = minCapacity;
}
return cache;
}
public static Cache<CachedMemoryStream> Claim(byte[] bytes = null)
{
var cache = Cache<CachedMemoryStream>.Claim();
if (bytes != null)
{
cache.Value.MemoryStream.Write(bytes, 0, bytes.Length);
cache.Value.MemoryStream.Position = 0;
}
return cache;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e7e73146f1e861c27c5608bff4142402
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,53 @@
//-----------------------------------------------------------------------
// <copyright file="CustomFormatterAttribute.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
{
using System;
/// <summary>
/// Attribute indicating that a class which implements the <see cref="IFormatter{T}" /> interface somewhere in its hierarchy is a custom formatter for the type T.
/// </summary>
/// <seealso cref="System.Attribute" />
[AttributeUsage(AttributeTargets.Class)]
[Obsolete("Use a RegisterFormatterAttribute applied to the containing assembly instead.", true)]
public class CustomFormatterAttribute : Attribute
{
/// <summary>
/// The priority of the formatter. Of all the available custom formatters, the formatter with the highest priority is always chosen.
/// </summary>
public readonly int Priority;
/// <summary>
/// Initializes a new instance of the <see cref="CustomFormatterAttribute"/> class with priority 0.
/// </summary>
public CustomFormatterAttribute()
{
this.Priority = 0;
}
/// <summary>
/// Initializes a new instance of the <see cref="CustomFormatterAttribute"/> class.
/// </summary>
/// <param name="priority">The priority of the formatter. Of all the available custom formatters, the formatter with the highest priority is always chosen.</param>
public CustomFormatterAttribute(int priority = 0)
{
this.Priority = priority;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4fd6ff4077bbbef9b366d8ffd9236173
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,63 @@
//-----------------------------------------------------------------------
// <copyright file="CustomGenericFormatterAttribute.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
{
using System;
/// <summary>
/// Attribute indicating that a generic type definition class which implements the <see cref="IFormatter{T}" /> interface somewhere in its hierarchy is a custom formatter for *any variation* of the generic type definition T.
/// <para />
/// The formatter's generic type parameters are mapped onto the serialized type's generic type parameters.
/// <para />
/// For example, <see cref="DictionaryFormatter{TKey, TValue}"/> implements <see cref="IFormatter{T}"/>, where T is <see cref="System.Collections.Generic.Dictionary{TKey, TValue}"/>.
/// </summary>
/// <seealso cref="CustomFormatterAttribute" />
[AttributeUsage(AttributeTargets.Class)]
[Obsolete("Use a RegisterFormatterAttribute applied to the containing assembly instead.", true)]
public class CustomGenericFormatterAttribute : CustomFormatterAttribute
{
/// <summary>
/// The generic type definition of the serialized type.
/// </summary>
public readonly Type SerializedGenericTypeDefinition;
/// <summary>
/// Initializes a new instance of the <see cref="CustomGenericFormatterAttribute"/> class.
/// </summary>
/// <param name="serializedGenericTypeDefinition">The generic type definition of the serialized type.</param>
/// <param name="priority">The priority of the formatter. Of all the available custom formatters, the formatter with the highest priority is always chosen.</param>
/// <exception cref="System.ArgumentNullException"><paramref name="serializedGenericTypeDefinition"/> was null.</exception>
/// <exception cref="System.ArgumentException">The type given in <paramref name="serializedGenericTypeDefinition"/> is not a generic type definition.</exception>
public CustomGenericFormatterAttribute(Type serializedGenericTypeDefinition, int priority = 0)
: base(priority)
{
if (serializedGenericTypeDefinition == null)
{
throw new ArgumentNullException();
}
if (serializedGenericTypeDefinition.IsGenericTypeDefinition == false)
{
throw new ArgumentException("The type " + serializedGenericTypeDefinition.Name + " is not a generic type definition.");
}
this.SerializedGenericTypeDefinition = serializedGenericTypeDefinition;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e02123fad495d06f2a89e5335f00126c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,85 @@
//-----------------------------------------------------------------------
// <copyright file="ColorFormatter.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
{
using System;
/// <summary>
/// A helper class for quickly and easily defining custom loggers.
/// </summary>
/// <seealso cref="ILogger" />
public class CustomLogger : ILogger
{
private Action<string> logWarningDelegate;
private Action<string> logErrorDelegate;
private Action<Exception> logExceptionDelegate;
/// <summary>
/// Creates a new custom logger using a set of given delegates.
/// </summary>
public CustomLogger(Action<string> logWarningDelegate, Action<string> logErrorDelegate, Action<Exception> logExceptionDelegate)
{
if (logWarningDelegate == null)
{
throw new ArgumentNullException("logWarningDelegate");
}
if (logErrorDelegate == null)
{
throw new ArgumentNullException("logErrorDelegate");
}
if (logExceptionDelegate == null)
{
throw new ArgumentNullException("logExceptionDelegate");
}
this.logWarningDelegate = logWarningDelegate;
this.logErrorDelegate = logErrorDelegate;
this.logExceptionDelegate = logExceptionDelegate;
}
/// <summary>
/// Logs a warning.
/// </summary>
/// <param name="warning">The warning to log.</param>
public void LogWarning(string warning)
{
this.logWarningDelegate(warning);
}
/// <summary>
/// Logs an error.
/// </summary>
/// <param name="error">The error to log.</param>
public void LogError(string error)
{
this.logErrorDelegate(error);
}
/// <summary>
/// Logs an exception.
/// </summary>
/// <param name="exception">The exception to log.</param>
public void LogException(Exception exception)
{
this.logExceptionDelegate(exception);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 97e9e01eb36fd43879b166b6b3c2469b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,86 @@
//-----------------------------------------------------------------------
// <copyright file="CustomSerializationPolicy.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
{
using System;
using System.Reflection;
/// <summary>
/// Helper class for quickly and easily implementing the <see cref="ISerializationPolicy"/> interface.
/// </summary>
public class CustomSerializationPolicy : ISerializationPolicy
{
private string id;
private bool allowNonSerializableTypes;
private Func<MemberInfo, bool> shouldSerializeFunc;
/// <summary>
/// Initializes a new instance of the <see cref="CustomSerializationPolicy"/> class.
/// </summary>
/// <param name="id">The policy ID.</param>
/// <param name="allowNonSerializableTypes">if set to <c>true</c> non serializable types will be allowed.</param>
/// <param name="shouldSerializeFunc">The delegate to use for determining whether members should be serialized.</param>
/// <exception cref="System.ArgumentNullException">
/// The id argument or the shouldSerializeFunc argument was null.
/// </exception>
public CustomSerializationPolicy(string id, bool allowNonSerializableTypes, Func<MemberInfo, bool> shouldSerializeFunc)
{
if (id == null)
{
throw new ArgumentNullException("id");
}
if (shouldSerializeFunc == null)
{
throw new ArgumentNullException("shouldSerializeFunc");
}
this.id = id;
this.allowNonSerializableTypes = allowNonSerializableTypes;
this.shouldSerializeFunc = shouldSerializeFunc;
}
/// <summary>
/// Gets the identifier of the policy. This can be stored in the serialization metadata, so the policy used to serialize it can be recovered without knowing the policy at runtime. This ID should preferably be unique.
/// </summary>
/// <value>
/// The identifier of the policy.
/// </value>
public string ID { get { return this.id; } }
/// <summary>
/// Gets a value indicating whether to allow non serializable types. (Types which are not decorated with <see cref="System.SerializableAttribute" />.)
/// </summary>
/// <value>
/// <c>true</c> if serializable types are allowed; otherwise, <c>false</c>.
/// </value>
public bool AllowNonSerializableTypes { get { return this.allowNonSerializableTypes; } }
/// <summary>
/// Gets a value indicating whether a given <see cref="MemberInfo" /> should be serialized or not.
/// </summary>
/// <param name="member">The member to check.</param>
/// <returns>
/// <c>true</c> if the given member should be serialized, otherwise, <c>false</c>.
/// </returns>
public bool ShouldSerializeMember(MemberInfo member)
{
return this.shouldSerializeFunc(member);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 95bb5531b6c1d1a5eab8400ea1bd6167
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,51 @@
//-----------------------------------------------------------------------
// <copyright file="DataFormat.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
{
/// <summary>
/// Specifies a data format to read and write in.
/// </summary>
public enum DataFormat
{
/// <summary>
/// A custom packed binary format. This format is most efficient and almost allocation-free,
/// but its serialized data is not human-readable.
/// </summary>
Binary = 0,
/// <summary>
/// A JSON format compliant with the json specification found at "http://www.json.org/".
/// <para />
/// This format has rather sluggish performance and allocates frightening amounts of string garbage.
/// </summary>
JSON = 1,
/// <summary>
/// A format that does not serialize to a byte stream, but to a list of data nodes in memory
/// which can then be serialized by Unity.
/// <para />
/// This format is highly inefficient, and is primarily used for ensuring that Unity assets
/// are mergeable by individual values when saved in Unity's text format. This makes
/// serialized values more robust and data recovery easier in case of issues.
/// <para />
/// This format is *not* recommended for use in builds.
/// </summary>
Nodes = 2
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c2a40a3e6a114e5a50c0af209b8ae35e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,44 @@
namespace VRC.Udon.Serialization.OdinSerializer
{
/// <summary>
/// Defines default loggers for serialization and deserialization. This class and all of its loggers are thread safe.
/// </summary>
public static class DefaultLoggers
{
private static readonly object LOCK = new object();
private static volatile ILogger unityLogger;
/// <summary>
/// The default logger - usually this is <see cref="UnityLogger"/>.
/// </summary>
public static ILogger DefaultLogger
{
get
{
return UnityLogger;
}
}
/// <summary>
/// Logs messages using Unity's <see cref="UnityEngine.Debug"/> class.
/// </summary>
public static ILogger UnityLogger
{
get
{
if (unityLogger == null)
{
lock (LOCK)
{
if (unityLogger == null)
{
unityLogger = new CustomLogger(UnityEngine.Debug.LogWarning, UnityEngine.Debug.LogError, UnityEngine.Debug.LogException);
}
}
}
return unityLogger;
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0bd9ab6cf3bd913588b6652279b7a6ba
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,548 @@
//-----------------------------------------------------------------------
// <copyright file="DefaultSerializationBinder.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
{
using VRC.Udon.Serialization.OdinSerializer.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
/// <summary>
/// An attribute that lets you help the DefaultSerializationBinder bind type names to types. This is useful if you're renaming a type,
/// that would result in data loss, and what to specify the new type name to avoid loss of data.
/// </summary>
/// <seealso cref="DefaultSerializationBinder" />
/// <example>
/// <code>
/// [assembly: OdinSerializer.BindTypeNameToType("Namespace.OldTypeName", typeof(Namespace.NewTypeName))]
/// //[assembly: OdinSerializer.BindTypeNameToType("Namespace.OldTypeName, OldFullAssemblyName", typeof(Namespace.NewTypeName))]
///
/// namespace Namespace
/// {
/// public class SomeComponent : SerializedMonoBehaviour
/// {
/// public IInterface test; // Contains an instance of OldTypeName;
/// }
///
/// public interface IInterface { }
///
/// public class NewTypeName : IInterface { }
///
/// //public class OldTypeName : IInterface { }
/// }
/// </code>
/// </example>
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public sealed class BindTypeNameToTypeAttribute : Attribute
{
internal readonly Type NewType;
internal readonly string OldTypeName;
/// <summary>
/// Initializes a new instance of the <see cref="BindTypeNameToTypeAttribute"/> class.
/// </summary>
/// <param name="oldFullTypeName">Old old full type name. If it's moved to new a new assembly you must specify the old assembly name as well. See example code in the documentation.</param>
/// <param name="newType">The new type.</param>
public BindTypeNameToTypeAttribute(string oldFullTypeName, Type newType)
{
this.OldTypeName = oldFullTypeName;
this.NewType = newType;
}
}
/// <summary>
/// Provides a default, catch-all <see cref="TwoWaySerializationBinder"/> implementation. This binder only includes assembly names, without versions and tokens, in order to increase compatibility.
/// </summary>
/// <seealso cref="TwoWaySerializationBinder" />
/// <seealso cref="BindTypeNameToTypeAttribute" />
public class DefaultSerializationBinder : TwoWaySerializationBinder
{
private static readonly object ASSEMBLY_LOOKUP_LOCK = new object();
private static readonly Dictionary<string, Assembly> assemblyNameLookUp = new Dictionary<string, Assembly>();
private static readonly Dictionary<string, Type> customTypeNameToTypeBindings = new Dictionary<string, Type>();
private static readonly object TYPETONAME_LOCK = new object();
private static readonly Dictionary<Type, string> nameMap = new Dictionary<Type, string>(FastTypeComparer.Instance);
private static readonly object NAMETOTYPE_LOCK = new object();
private static readonly Dictionary<string, Type> typeMap = new Dictionary<string, Type>();
private static readonly List<string> genericArgNamesList = new List<string>();
private static readonly List<Type> genericArgTypesList = new List<Type>();
static DefaultSerializationBinder()
{
AppDomain.CurrentDomain.AssemblyLoad += (sender, args) =>
{
RegisterAssembly(args.LoadedAssembly);
};
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
RegisterAssembly(assembly);
}
}
private static void RegisterAssembly(Assembly assembly)
{
var name = assembly.GetName().Name;
bool wasAdded = false;
lock (ASSEMBLY_LOOKUP_LOCK)
{
if (!assemblyNameLookUp.ContainsKey(name))
{
assemblyNameLookUp.Add(name, assembly);
wasAdded = true;
}
}
if (wasAdded)
{
try
{
var customAttributes = assembly.SafeGetCustomAttributes(typeof(BindTypeNameToTypeAttribute), false);
if (customAttributes != null)
{
for (int i = 0; i < customAttributes.Length; i++)
{
var attr = customAttributes[i] as BindTypeNameToTypeAttribute;
if (attr != null && attr.NewType != null)
{
lock (ASSEMBLY_LOOKUP_LOCK)
{
//if (attr.OldTypeName.Contains(","))
//{
customTypeNameToTypeBindings[attr.OldTypeName] = attr.NewType;
//}
//else
//{
// customTypeNameToTypeBindings[attr.OldTypeName + ", " + assembly.GetName().Name] = attr.NewType;
//}
}
}
}
}
}
catch { } // Assembly is invalid somehow
}
}
/// <summary>
/// Bind a type to a name.
/// </summary>
/// <param name="type">The type to bind.</param>
/// <param name="debugContext">The debug context to log to.</param>
/// <returns>
/// The name that the type has been bound to.
/// </returns>
/// <exception cref="System.ArgumentNullException">The type argument is null.</exception>
public override string BindToName(Type type, DebugContext debugContext = null)
{
if (type == null)
{
throw new ArgumentNullException("type");
}
string result;
lock (TYPETONAME_LOCK)
{
if (nameMap.TryGetValue(type, out result) == false)
{
if (type.IsGenericType)
{
// We track down all assemblies in the generic type definition
List<Type> toResolve = type.GetGenericArguments().ToList();
HashSet<Assembly> assemblies = new HashSet<Assembly>();
while (toResolve.Count > 0)
{
var t = toResolve[0];
if (t.IsGenericType)
{
toResolve.AddRange(t.GetGenericArguments());
}
assemblies.Add(t.Assembly);
toResolve.RemoveAt(0);
}
result = type.FullName + ", " + type.Assembly.GetName().Name;
foreach (var ass in assemblies)
{
result = result.Replace(ass.FullName, ass.GetName().Name);
}
}
else if (type.IsDefined(typeof(CompilerGeneratedAttribute), false))
{
result = type.FullName + ", " + type.Assembly.GetName().Name;
}
else
{
result = type.FullName + ", " + type.Assembly.GetName().Name;
}
nameMap.Add(type, result);
}
}
return result;
}
/// <summary>
/// Determines whether the specified type name is mapped.
/// </summary>
public override bool ContainsType(string typeName)
{
lock (NAMETOTYPE_LOCK)
{
return typeMap.ContainsKey(typeName);
}
}
/// <summary>
/// Binds a name to type.
/// </summary>
/// <param name="typeName">The name of the type to bind.</param>
/// <param name="debugContext">The debug context to log to.</param>
/// <returns>
/// The type that the name has been bound to, or null if the type could not be resolved.
/// </returns>
/// <exception cref="System.ArgumentNullException">The typeName argument is null.</exception>
public override Type BindToType(string typeName, DebugContext debugContext = null)
{
if (typeName == null)
{
throw new ArgumentNullException("typeName");
}
Type result;
lock (NAMETOTYPE_LOCK)
{
if (typeMap.TryGetValue(typeName, out result) == false)
{
result = this.ParseTypeName(typeName, debugContext);
if (result == null && debugContext != null)
{
debugContext.LogWarning("Failed deserialization type lookup for type name '" + typeName + "'.");
}
// We allow null values on purpose so we don't have to keep re-performing invalid name lookups
typeMap.Add(typeName, result);
}
}
return result;
}
private Type ParseTypeName(string typeName, DebugContext debugContext)
{
Type type;
lock (ASSEMBLY_LOOKUP_LOCK)
{
// Look for custom defined type name lookups defined with the BindTypeNameToTypeAttribute.
if (customTypeNameToTypeBindings.TryGetValue(typeName, out type))
{
return type;
}
}
// Let's try it the traditional .NET way
type = Type.GetType(typeName);
if (type != null) return type;
// Generic/array type name handling
type = ParseGenericAndOrArrayType(typeName, debugContext);
if (type != null) return type;
string typeStr, assemblyStr;
ParseName(typeName, out typeStr, out assemblyStr);
if (!string.IsNullOrEmpty(typeStr))
{
lock (ASSEMBLY_LOOKUP_LOCK)
{
// Look for custom defined type name lookups defined with the BindTypeNameToTypeAttribute.
if (customTypeNameToTypeBindings.TryGetValue(typeStr, out type))
{
return type;
}
}
Assembly assembly;
// Try to load from the named assembly
if (assemblyStr != null)
{
lock (ASSEMBLY_LOOKUP_LOCK)
{
assemblyNameLookUp.TryGetValue(assemblyStr, out assembly);
}
if (assembly == null)
{
try
{
assembly = Assembly.Load(assemblyStr);
}
catch { }
}
if (assembly != null)
{
try
{
type = assembly.GetType(typeStr);
}
catch { } // Assembly is invalid
if (type != null) return type;
}
}
// Try to check all assemblies for the type string
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
for (int i = 0; i < assemblies.Length; i++)
{
assembly = assemblies[i];
try
{
type = assembly.GetType(typeStr, false);
}
catch { } // Assembly is invalid
if (type != null) return type;
}
}
//type = AssemblyUtilities.GetTypeByCachedFullName(typeStr);
//if (type != null) return type;
return null;
}
private static void ParseName(string fullName, out string typeName, out string assemblyName)
{
typeName = null;
assemblyName = null;
int firstComma = fullName.IndexOf(',');
if (firstComma < 0 || (firstComma + 1) == fullName.Length)
{
typeName = fullName.Trim(',', ' ');
return;
}
else
{
typeName = fullName.Substring(0, firstComma);
}
int secondComma = fullName.IndexOf(',', firstComma + 1);
if (secondComma < 0)
{
assemblyName = fullName.Substring(firstComma).Trim(',', ' ');
}
else
{
assemblyName = fullName.Substring(firstComma, secondComma - firstComma).Trim(',', ' ');
}
}
private Type ParseGenericAndOrArrayType(string typeName, DebugContext debugContext)
{
string actualTypeName;
List<string> genericArgNames;
bool isGeneric;
bool isArray;
int arrayRank;
if (!TryParseGenericAndOrArrayTypeName(typeName, out actualTypeName, out isGeneric, out genericArgNames, out isArray, out arrayRank)) return null;
Type type = this.BindToType(actualTypeName, debugContext);
if (type == null) return null;
if (isGeneric)
{
if (!type.IsGenericType) return null;
List<Type> args = genericArgTypesList;
args.Clear();
for (int i = 0; i < genericArgNames.Count; i++)
{
Type arg = this.BindToType(genericArgNames[i], debugContext);
if (arg == null) return null;
args.Add(arg);
}
var argsArray = args.ToArray();
if (!type.AreGenericConstraintsSatisfiedBy(argsArray))
{
if (debugContext != null)
{
string argsStr = "";
foreach (var arg in args)
{
if (argsStr != "") argsStr += ", ";
argsStr += arg.GetNiceFullName();
}
debugContext.LogWarning("Deserialization type lookup failure: The generic type arguments '" + argsStr + "' do not satisfy the generic constraints of generic type definition '" + type.GetNiceFullName() + "'. All this parsed from the full type name string: '" + typeName + "'");
}
return null;
}
type = type.MakeGenericType(argsArray);
}
if (isArray)
{
type = type.MakeArrayType(arrayRank);
}
return type;
}
private static bool TryParseGenericAndOrArrayTypeName(string typeName, out string actualTypeName, out bool isGeneric, out List<string> genericArgNames, out bool isArray, out int arrayRank)
{
isGeneric = false;
isArray = false;
arrayRank = 0;
bool parsingGenericArguments = false;
string argName;
genericArgNames = null;
actualTypeName = null;
for (int i = 0; i < typeName.Length; i++)
{
if (typeName[i] == '[')
{
var next = Peek(typeName, i, 1);
if (next == ',' || next == ']')
{
if (actualTypeName == null)
{
actualTypeName = typeName.Substring(0, i);
}
isArray = true;
arrayRank = 1;
i++;
if (next == ',')
{
while (next == ',')
{
arrayRank++;
next = Peek(typeName, i, 1);
i++;
}
if (next != ']')
return false; // Malformed type name
}
}
else
{
if (!isGeneric)
{
actualTypeName = typeName.Substring(0, i);
isGeneric = true;
parsingGenericArguments = true;
genericArgNames = genericArgNamesList;
genericArgNames.Clear();
}
else if (isGeneric && ReadGenericArg(typeName, ref i, out argName))
{
genericArgNames.Add(argName);
}
else return false; // Malformed type name
}
}
else if (typeName[i] == ']')
{
if (!parsingGenericArguments) return false; // This is not a valid type name, since we're hitting "]" without currently being in the process of parsing the generic arguments or an array thingy
parsingGenericArguments = false;
}
else if (typeName[i] == ',' && !parsingGenericArguments)
{
actualTypeName += typeName.Substring(i);
break;
}
}
return isArray || isGeneric;
}
private static char Peek(string str, int i, int ahead)
{
if (i + ahead < str.Length) return str[i + ahead];
return '\0';
}
private static bool ReadGenericArg(string typeName, ref int i, out string argName)
{
argName = null;
if (typeName[i] != '[') return false;
int start = i + 1;
int genericDepth = 0;
for (; i < typeName.Length; i++)
{
if (typeName[i] == '[') genericDepth++;
else if (typeName[i] == ']')
{
genericDepth--;
if (genericDepth == 0)
{
int length = i - start;
argName = typeName.Substring(start, length);
return true;
}
}
}
return false;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 996e793dcc0920d2590cb61f0761d498
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,304 @@
//-----------------------------------------------------------------------
// <copyright file="DeserializationContext.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
{
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using Utilities;
/// <summary>
/// The context of a given deserialization session. This class maintains all internal and external references during deserialization.
/// </summary>
/// <seealso cref="ICacheNotificationReceiver" />
public sealed class DeserializationContext : ICacheNotificationReceiver
{
private SerializationConfig config;
private Dictionary<int, object> internalIdReferenceMap = new Dictionary<int, object>(128);
private StreamingContext streamingContext;
private IFormatterConverter formatterConverter;
private TwoWaySerializationBinder binder;
/// <summary>
/// Initializes a new instance of the <see cref="DeserializationContext"/> class.
/// </summary>
public DeserializationContext()
: this(new StreamingContext(), new FormatterConverter())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DeserializationContext"/> class.
/// </summary>
/// <param name="context">The streaming context to use.</param>
public DeserializationContext(StreamingContext context)
: this(context, new FormatterConverter())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DeserializationContext"/> class.
/// </summary>
/// <param name="formatterConverter">The formatter converter to use.</param>
public DeserializationContext(FormatterConverter formatterConverter)
: this(new StreamingContext(), formatterConverter)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DeserializationContext"/> class.
/// </summary>
/// <param name="context">The streaming context to use.</param>
/// <param name="formatterConverter">The formatter converter to use.</param>
/// <exception cref="System.ArgumentNullException">The formatterConverter parameter is null.</exception>
public DeserializationContext(StreamingContext context, FormatterConverter formatterConverter)
{
if (formatterConverter == null)
{
throw new ArgumentNullException("formatterConverter");
}
this.streamingContext = context;
this.formatterConverter = formatterConverter;
this.Reset();
}
/// <summary>
/// Gets or sets the context's type binder.
/// </summary>
/// <value>
/// The context's serialization binder.
/// </value>
public TwoWaySerializationBinder Binder
{
get
{
if (this.binder == null)
{
this.binder = DefaultSerializationBinder.Default;
}
return this.binder;
}
set
{
this.binder = value;
}
}
/// <summary>
/// Gets or sets the string reference resolver.
/// </summary>
/// <value>
/// The string reference resolver.
/// </value>
public IExternalStringReferenceResolver StringReferenceResolver { get; set; }
/// <summary>
/// Gets or sets the Guid reference resolver.
/// </summary>
/// <value>
/// The Guid reference resolver.
/// </value>
public IExternalGuidReferenceResolver GuidReferenceResolver { get; set; }
/// <summary>
/// Gets or sets the index reference resolver.
/// </summary>
/// <value>
/// The index reference resolver.
/// </value>
public IExternalIndexReferenceResolver IndexReferenceResolver { get; set; }
/// <summary>
/// Gets the streaming context.
/// </summary>
/// <value>
/// The streaming context.
/// </value>
public StreamingContext StreamingContext { get { return this.streamingContext; } }
/// <summary>
/// Gets the formatter converter.
/// </summary>
/// <value>
/// The formatter converter.
/// </value>
public IFormatterConverter FormatterConverter { get { return this.formatterConverter; } }
/// <summary>
/// Gets or sets the serialization configuration.
/// </summary>
/// <value>
/// The serialization configuration.
/// </value>
public SerializationConfig Config
{
get
{
if (this.config == null)
{
this.config = new SerializationConfig();
}
return this.config;
}
set
{
this.config = value;
}
}
/// <summary>
/// Registers an internal reference to a given id.
/// </summary>
/// <param name="id">The id to register the reference with.</param>
/// <param name="reference">The reference to register.</param>
public void RegisterInternalReference(int id, object reference)
{
this.internalIdReferenceMap[id] = reference;
}
/// <summary>
/// Gets an internal reference from a given id, or null if the id has not been registered.
/// </summary>
/// <param name="id">The id of the reference to get.</param>
/// <returns>An internal reference from a given id, or null if the id has not been registered.</returns>
public object GetInternalReference(int id)
{
object result;
this.internalIdReferenceMap.TryGetValue(id, out result);
return result;
}
/// <summary>
/// Gets an external object reference by index, or null if the index could not be resolved.
/// </summary>
/// <param name="index">The index to resolve.</param>
/// <returns>An external object reference by the given index, or null if the index could not be resolved.</returns>
public object GetExternalObject(int index)
{
if (this.IndexReferenceResolver == null)
{
this.Config.DebugContext.LogWarning("Tried to resolve external reference by index (" + index + "), but no index reference resolver is assigned to the deserialization context. External reference has been lost.");
return null;
}
object result;
if (this.IndexReferenceResolver.TryResolveReference(index, out result))
{
return result;
}
this.Config.DebugContext.LogWarning("Failed to resolve external reference by index (" + index + "); the index resolver could not resolve the index. Reference lost.");
return null;
}
/// <summary>
/// Gets an external object reference by guid, or null if the guid could not be resolved.
/// </summary>
/// <param name="guid">The guid to resolve.</param>
/// <returns>An external object reference by the given guid, or null if the guid could not be resolved.</returns>
public object GetExternalObject(Guid guid)
{
if (this.GuidReferenceResolver == null)
{
this.Config.DebugContext.LogWarning("Tried to resolve external reference by guid (" + guid + "), but no guid reference resolver is assigned to the deserialization context. External reference has been lost.");
return null;
}
var resolver = this.GuidReferenceResolver;
object result;
while (resolver != null)
{
if (resolver.TryResolveReference(guid, out result))
{
return result;
}
resolver = resolver.NextResolver;
}
this.Config.DebugContext.LogWarning("Failed to resolve external reference by guid (" + guid + "); no guid resolver could resolve the guid. Reference lost.");
return null;
}
/// <summary>
/// Gets an external object reference by an id string, or null if the id string could not be resolved.
/// </summary>
/// <param name="id">The id string to resolve.</param>
/// <returns>An external object reference by an id string, or null if the id string could not be resolved.</returns>
public object GetExternalObject(string id)
{
if (this.StringReferenceResolver == null)
{
this.Config.DebugContext.LogWarning("Tried to resolve external reference by string (" + id + "), but no string reference resolver is assigned to the deserialization context. External reference has been lost.");
return null;
}
var resolver = this.StringReferenceResolver;
object result;
while (resolver != null)
{
if (resolver.TryResolveReference(id, out result))
{
return result;
}
resolver = resolver.NextResolver;
}
this.Config.DebugContext.LogWarning("Failed to resolve external reference by string (" + id + "); no string resolver could resolve the string. Reference lost.");
return null;
}
/// <summary>
/// Resets the deserialization context completely to baseline status, as if its constructor has just been called.
/// This allows complete reuse of a deserialization context, with all of its internal reference buffers.
/// </summary>
public void Reset()
{
if (!object.ReferenceEquals(this.config, null))
{
this.config.ResetToDefault();
}
this.internalIdReferenceMap.Clear();
this.IndexReferenceResolver = null;
this.GuidReferenceResolver = null;
this.StringReferenceResolver = null;
this.binder = null;
}
void ICacheNotificationReceiver.OnFreed()
{
this.Reset();
}
void ICacheNotificationReceiver.OnClaimed()
{
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c79df97337d89089be40beb2e272df0b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,29 @@
//-----------------------------------------------------------------------
// <copyright file="EmittedAssemblyAttribute.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
{
using System;
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
public sealed class EmittedAssemblyAttribute : Attribute
{
[Obsolete("This attribute cannot be used in code, and is only meant to be applied to dynamically emitted assemblies.", true)]
public EmittedAssemblyAttribute() { }
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ae849a3e6d277006f3b4dd58a5765955
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,111 @@
//-----------------------------------------------------------------------
// <copyright file="EntryType.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
{
/// <summary>
/// An entry type which is part of a stream being read by a <see cref="IDataReader"/>.
/// </summary>
public enum EntryType : byte
{
/// <summary>
/// Could not parse entry.
/// </summary>
Invalid = 0,
/// <summary>
/// Entry is a primitive value of type string or char.
/// </summary>
String = 1,
/// <summary>
/// Entry is a primitive value of type guid.
/// </summary>
Guid = 2,
/// <summary>
/// Entry is a primitive value of type sbyte, byte, short, ushort, int, uint, long or ulong.
/// </summary>
Integer = 3,
/// <summary>
/// Entry is a primitive value of type float, double or decimal.
/// </summary>
FloatingPoint = 4,
/// <summary>
/// Entry is a primitive boolean value.
/// </summary>
Boolean = 5,
/// <summary>
/// Entry is a null value.
/// </summary>
Null = 6,
/// <summary>
/// Entry marks the start of a node, IE, a complex type that contains values of its own.
/// </summary>
StartOfNode = 7,
/// <summary>
/// Entry marks the end of a node, IE, a complex type that contains values of its own.
/// </summary>
EndOfNode = 8,
/// <summary>
/// Entry contains an ID that is a reference to a node defined previously in the stream.
/// </summary>
InternalReference = 9,
/// <summary>
/// Entry contains the index of an external object in the DeserializationContext.
/// </summary>
ExternalReferenceByIndex = 10,
/// <summary>
/// Entry contains the guid of an external object in the DeserializationContext.
/// </summary>
ExternalReferenceByGuid = 11,
/// <summary>
/// Entry marks the start of an array.
/// </summary>
StartOfArray = 12,
/// <summary>
/// Entry marks the end of an array.
/// </summary>
EndOfArray = 13,
/// <summary>
/// Entry marks a primitive array.
/// </summary>
PrimitiveArray = 14,
/// <summary>
/// Entry indicating that the reader has reached the end of the data stream.
/// </summary>
EndOfStream = 15,
/// <summary>
/// Entry contains the string id of an external object in the DeserializationContext.
/// </summary>
ExternalReferenceByString = 16
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3b06b106636f38afbb25ddd11e0c597c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,23 @@
namespace VRC.Udon.Serialization.OdinSerializer
{
/// <summary>
/// The policy for handling errors during serialization and deserialization.
/// </summary>
public enum ErrorHandlingPolicy
{
/// <summary>
/// Attempts will be made to recover from errors and continue serialization. Data may become invalid.
/// </summary>
Resilient,
/// <summary>
/// Exceptions will be thrown when errors are logged.
/// </summary>
ThrowOnErrors,
/// <summary>
/// Exceptions will be thrown when warnings or errors are logged.
/// </summary>
ThrowOnWarningsAndErrors
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c73435dff291e72c0d9ce55b59c39145
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,34 @@
//-----------------------------------------------------------------------
// <copyright file="ExcludeDataFromInspectorAttribute.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
{
using System;
/// <summary>
/// <para>
/// Causes Odin's inspector to completely ignore a given member, preventing it from even being included in an Odin PropertyTree,
/// and such will not cause any performance hits in the inspector.
/// </para>
/// <para>Note that Odin can still serialize an excluded member - it is merely ignored in the inspector itself.</para>
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
[Obsolete("Use [HideInInspector] instead - it now also excludes the member completely from becoming a property in the property tree.", false)]
public sealed class ExcludeDataFromInspectorAttribute : Attribute
{
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: df06475ac5299f402ca1bdee3cf7e702
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,26 @@
//-----------------------------------------------------------------------
// <copyright file="FormatterLocationStep.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
{
public enum FormatterLocationStep
{
BeforeRegisteredFormatters,
AfterRegisteredFormatters
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 08528593c8dd764b6d928dcee6daca9f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,402 @@
//-----------------------------------------------------------------------
// <copyright file="FormatterUtilities.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
{
using System.Globalization;
using VRC.Udon.Serialization.OdinSerializer.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using UnityEngine;
/// <summary>
/// Provides an array of utility methods which are commonly used by serialization formatters.
/// </summary>
#if UNITY_EDITOR
[UnityEditor.InitializeOnLoad]
#endif
public static class FormatterUtilities
{
private static readonly DoubleLookupDictionary<ISerializationPolicy, Type, MemberInfo[]> MemberArrayCache = new DoubleLookupDictionary<ISerializationPolicy, Type, MemberInfo[]>();
private static readonly DoubleLookupDictionary<ISerializationPolicy, Type, Dictionary<string, MemberInfo>> MemberMapCache = new DoubleLookupDictionary<ISerializationPolicy, Type, Dictionary<string, MemberInfo>>();
private static readonly object LOCK = new object();
private static readonly HashSet<Type> PrimitiveArrayTypes = new HashSet<Type>(FastTypeComparer.Instance)
{
typeof(char),
typeof(sbyte),
typeof(short),
typeof(int),
typeof(long),
typeof(byte),
typeof(ushort),
typeof(uint),
typeof(ulong),
typeof(decimal),
typeof(bool),
typeof(float),
typeof(double),
typeof(Guid)
};
private static readonly FieldInfo UnityObjectRuntimeErrorStringField;
private const string UnityObjectRuntimeErrorString =
@"The variable nullValue of {0} has not been assigned.
You probably need to assign the nullValue variable of the {0} script in the inspector.";
static FormatterUtilities()
{
// The required field is missing in Unity builds
#if UNITY_EDITOR
UnityObjectRuntimeErrorStringField = typeof(UnityEngine.Object).GetField("m_UnityRuntimeErrorString", Flags.InstanceAnyVisibility);
if (UnityObjectRuntimeErrorStringField == null)
{
Debug.LogWarning("A change in Unity has hindered the Serialization system's ability to create proper fake Unity null values; the UnityEngine.Object.m_UnityRuntimeErrorString field has been renamed or removed.");
}
#endif
}
/// <summary>
/// Gets a map of all serializable members on the given type. This will also properly map names extracted from <see cref="UnityEngine.Serialization.FormerlySerializedAsAttribute"/> and <see cref="PreviouslySerializedAsAttribute"/> to their corresponding members.
/// </summary>
/// <param name="type">The type to get a map for.</param>
/// <param name="policy">The serialization policy to use. If null, <see cref="SerializationPolicies.Strict"/> is used.</param>
/// <returns>A map of all serializable members on the given type.</returns>
public static Dictionary<string, MemberInfo> GetSerializableMembersMap(Type type, ISerializationPolicy policy)
{
Dictionary<string, MemberInfo> result;
if (policy == null)
{
policy = SerializationPolicies.Strict;
}
lock (LOCK)
{
if (MemberMapCache.TryGetInnerValue(policy, type, out result) == false)
{
result = FindSerializableMembersMap(type, policy);
MemberMapCache.AddInner(policy, type, result);
}
}
return result;
}
/// <summary>
/// Gets an array of all serializable members on the given type.
/// </summary>
/// <param name="type">The type to get serializable members for.</param>
/// <param name="policy">The serialization policy to use. If null, <see cref="SerializationPolicies.Strict"/> is used.</param>
/// <returns>An array of all serializable members on the given type.</returns>
public static MemberInfo[] GetSerializableMembers(Type type, ISerializationPolicy policy)
{
MemberInfo[] result;
if (policy == null)
{
policy = SerializationPolicies.Strict;
}
lock (LOCK)
{
if (MemberArrayCache.TryGetInnerValue(policy, type, out result) == false)
{
List<MemberInfo> list = new List<MemberInfo>();
FindSerializableMembers(type, list, policy);
result = list.ToArray();
MemberArrayCache.AddInner(policy, type, result);
}
}
return result;
}
/// <summary>
/// Creates a fake Unity null value of a given type, for the given <see cref="UnityEngine.Object"/>-derived owning type.
/// <para />
/// Unity uses these kinds of values to indicate missing object references.
/// </summary>
/// <param name="nullType">Type of the null value.</param>
/// <param name="owningType">Type of the owning value. This is the value which changes the <see cref="MissingReferenceException"/> which you get.</param>
/// <returns>A fake Unity null value of a given type.</returns>
/// <exception cref="System.ArgumentNullException">The nullType or owningType parameter is null.</exception>
/// <exception cref="System.ArgumentException">
/// The type given in the nullType parameter is not a Unity object.
/// or
/// The type given in the owningType parameter is not a Unity object.
/// </exception>
public static UnityEngine.Object CreateUnityNull(Type nullType, Type owningType)
{
if (nullType == null || owningType == null)
{
throw new ArgumentNullException();
}
if (nullType.ImplementsOrInherits(typeof(UnityEngine.Object)) == false)
{
throw new ArgumentException("Type " + nullType.Name + " is not a Unity object.");
}
if (owningType.ImplementsOrInherits(typeof(UnityEngine.Object)) == false)
{
throw new ArgumentException("Type " + owningType.Name + " is not a Unity object.");
}
UnityEngine.Object nullValue = (UnityEngine.Object)FormatterServices.GetUninitializedObject(nullType);
if (UnityObjectRuntimeErrorStringField != null)
{
UnityObjectRuntimeErrorStringField.SetValue(nullValue, string.Format(CultureInfo.InvariantCulture, UnityObjectRuntimeErrorString, owningType.Name));
}
return nullValue;
}
/// <summary>
/// Determines whether a given type is a primitive type to the serialization system.
/// <para />
/// The following criteria are checked: type.IsPrimitive or type.IsEnum, or type is a <see cref="decimal"/>, <see cref="string"/> or <see cref="Guid"/>.
/// </summary>
/// <param name="type">The type to check.</param>
/// <returns><c>true</c> if the given type is a primitive type; otherwise, <c>false</c>.</returns>
public static bool IsPrimitiveType(Type type)
{
return type.IsPrimitive
|| type.IsEnum
|| type == typeof(decimal)
|| type == typeof(string)
|| type == typeof(Guid);
}
/// <summary>
/// Determines whether a given type is a primitive array type. Namely, arrays with primitive array types as elements are primitive arrays.
/// <para />
/// The following types are primitive array types: <see cref="char"/>, <see cref="sbyte"/>, <see cref="short"/>, <see cref="int"/>, <see cref="long"/>, <see cref="byte"/>, <see cref="ushort"/>, <see cref="uint"/>, <see cref="ulong"/>, <see cref="decimal"/>, <see cref="bool"/>, <see cref="float"/>, <see cref="double"/> and <see cref="Guid"/>.
/// </summary>
/// <param name="type">The type to check.</param>
/// <returns><c>true</c> if the given type is a primitive array type; otherwise, <c>false</c>.</returns>
public static bool IsPrimitiveArrayType(Type type)
{
return PrimitiveArrayTypes.Contains(type);
}
/// <summary>
/// Gets the type contained in the given <see cref="MemberInfo"/>. Currently only <see cref="FieldInfo"/> and <see cref="PropertyInfo"/> is supported.
/// </summary>
/// <param name="member">The <see cref="MemberInfo"/> to get the contained type of.</param>
/// <returns>The type contained in the given <see cref="MemberInfo"/>.</returns>
/// <exception cref="System.ArgumentException">Can't get the contained type of the given <see cref="MemberInfo"/> type.</exception>
public static Type GetContainedType(MemberInfo member)
{
if (member is FieldInfo)
{
return (member as FieldInfo).FieldType;
}
else if (member is PropertyInfo)
{
return (member as PropertyInfo).PropertyType;
}
else
{
throw new ArgumentException("Can't get the contained type of a " + member.GetType().Name);
}
}
/// <summary>
/// Gets the value contained in a given <see cref="MemberInfo"/>. Currently only <see cref="FieldInfo"/> and <see cref="PropertyInfo"/> is supported.
/// </summary>
/// <param name="member">The <see cref="MemberInfo"/> to get the value of.</param>
/// <param name="obj">The instance to get the value from.</param>
/// <returns>The value contained in the given <see cref="MemberInfo"/>.</returns>
/// <exception cref="System.ArgumentException">Can't get the value of the given <see cref="MemberInfo"/> type.</exception>
public static object GetMemberValue(MemberInfo member, object obj)
{
if (member is FieldInfo)
{
return (member as FieldInfo).GetValue(obj);
}
else if (member is PropertyInfo)
{
return (member as PropertyInfo).GetGetMethod(true).Invoke(obj, null);
}
else
{
throw new ArgumentException("Can't get the value of a " + member.GetType().Name);
}
}
/// <summary>
/// Sets the value of a given MemberInfo. Currently only <see cref="FieldInfo"/> and <see cref="PropertyInfo"/> is supported.
/// </summary>
/// <param name="member">The <see cref="MemberInfo"/> to set the value of.</param>
/// <param name="obj">The object to set the value on.</param>
/// <param name="value">The value to set.</param>
/// <exception cref="System.ArgumentException">
/// Property has no setter
/// or
/// Can't set the value of the given <see cref="MemberInfo"/> type.
/// </exception>
public static void SetMemberValue(MemberInfo member, object obj, object value)
{
if (member is FieldInfo)
{
(member as FieldInfo).SetValue(obj, value);
}
else if (member is PropertyInfo)
{
var method = (member as PropertyInfo).GetSetMethod(true);
if (method != null)
{
method.Invoke(obj, new object[] { value });
}
else
{
throw new ArgumentException("Property " + member.Name + " has no setter");
}
}
else
{
throw new ArgumentException("Can't set the value of a " + member.GetType().Name);
}
}
private static Dictionary<string, MemberInfo> FindSerializableMembersMap(Type type, ISerializationPolicy policy)
{
var map = GetSerializableMembers(type, policy).ToDictionary(n => n.Name, n => n);
foreach (var member in map.Values.ToList())
{
var serializedAsAttributes = member.GetAttributes<UnityEngine.Serialization.FormerlySerializedAsAttribute>();
foreach (var attr in serializedAsAttributes)
{
if (map.ContainsKey(attr.oldName) == false)
{
map.Add(attr.oldName, member);
}
}
}
return map;
}
private static void FindSerializableMembers(Type type, List<MemberInfo> members, ISerializationPolicy policy)
{
const BindingFlags Flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly;
if (type.BaseType != typeof(object) && type.BaseType != null)
{
FindSerializableMembers(type.BaseType, members, policy);
}
foreach (var member in type.GetMembers(Flags).Where(n => n is FieldInfo || n is PropertyInfo))
{
if (policy.ShouldSerializeMember(member))
{
bool nameAlreadyExists = members.Any(n => n.Name == member.Name);
if (MemberIsPrivate(member) && nameAlreadyExists)
{
members.Add(GetPrivateMemberAlias(member));
}
else if (nameAlreadyExists)
{
members.Add(GetPrivateMemberAlias(member));
}
else
{
members.Add(member);
}
}
}
}
/// <summary>
/// Gets an aliased version of a member, with the declaring type name included in the member name, so that there are no conflicts with private fields and properties with the same name in different classes in the same inheritance hierarchy.
/// </summary>
public static MemberInfo GetPrivateMemberAlias(MemberInfo member, string prefixString = null, string separatorString = null)
{
if (member is FieldInfo)
{
if (separatorString != null)
{
return new MemberAliasFieldInfo(member as FieldInfo, prefixString ?? member.DeclaringType.Name, separatorString);
}
else
{
return new MemberAliasFieldInfo(member as FieldInfo, prefixString ?? member.DeclaringType.Name);
}
}
else if (member is PropertyInfo)
{
if (separatorString != null)
{
return new MemberAliasPropertyInfo(member as PropertyInfo, prefixString ?? member.DeclaringType.Name, separatorString);
}
else
{
return new MemberAliasPropertyInfo(member as PropertyInfo, prefixString ?? member.DeclaringType.Name);
}
}
else if (member is MethodInfo)
{
if (separatorString != null)
{
return new MemberAliasMethodInfo(member as MethodInfo, prefixString ?? member.DeclaringType.Name, separatorString);
}
else
{
return new MemberAliasMethodInfo(member as MethodInfo, prefixString ?? member.DeclaringType.Name);
}
}
throw new NotImplementedException();
}
private static bool MemberIsPrivate(MemberInfo member)
{
if (member is FieldInfo)
{
return (member as FieldInfo).IsPrivate;
}
else if (member is PropertyInfo)
{
var prop = member as PropertyInfo;
var getter = prop.GetGetMethod();
var setter = prop.GetSetMethod();
return getter != null && setter != null && getter.IsPrivate && setter.IsPrivate;
}
else if (member is MethodInfo)
{
return (member as MethodInfo).IsPrivate;
}
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 30194d27b77855bf09b9af809a761ca5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,27 @@
//-----------------------------------------------------------------------
// <copyright file="IAskIfCanFormatTypes.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
{
using System;
public interface IAskIfCanFormatTypes
{
bool CanFormatType(Type type);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 32f94aca65b8d09ddd7b3db72e08db3f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,52 @@
//-----------------------------------------------------------------------
// <copyright file="IExternalGuidReferenceResolver.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
{
using System;
/// <summary>
/// Resolves external guid references to reference objects during serialization and deserialization.
/// </summary>
public interface IExternalGuidReferenceResolver
{
/// <summary>
/// Gets or sets the next resolver in the chain.
/// </summary>
/// <value>
/// The next resolver in the chain.
/// </value>
IExternalGuidReferenceResolver NextResolver { get; set; }
/// <summary>
/// Tries to resolve a reference from a given Guid.
/// </summary>
/// <param name="guid">The Guid to resolve.</param>
/// <param name="value">The resolved value.</param>
/// <returns><c>true</c> if the value was resolved; otherwise, <c>false</c>.</returns>
bool TryResolveReference(Guid guid, out object value);
/// <summary>
/// Determines whether this resolver can reference the specified value with a Guid.
/// </summary>
/// <param name="value">The value to check.</param>
/// <param name="guid">The Guid which references the value.</param>
/// <returns><c>true</c> if the value can be referenced; otherwise, <c>false</c>.</returns>
bool CanReference(object value, out Guid guid);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7ef6b6dd5e3be66c3a66753cc7e799de
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,42 @@
//-----------------------------------------------------------------------
// <copyright file="IExternalIndexReferenceResolver.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
{
/// <summary>
/// Resolves external index references to reference objects during serialization and deserialization.
/// </summary>
public interface IExternalIndexReferenceResolver
{
/// <summary>
/// Tries to resolve the given reference index to a reference value.
/// </summary>
/// <param name="index">The index to resolve.</param>
/// <param name="value">The resolved value.</param>
/// <returns><c>true</c> if the index could be resolved to a value, otherwise <c>false</c>.</returns>
bool TryResolveReference(int index, out object value);
/// <summary>
/// Determines whether the specified value can be referenced externally via this resolver.
/// </summary>
/// <param name="value">The value to reference.</param>
/// <param name="index">The index of the resolved value, if it can be referenced.</param>
/// <returns><c>true</c> if the reference can be resolved, otherwise <c>false</c>.</returns>
bool CanReference(object value, out int index);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d1eaa1a505a876bebb9cad40d01989e9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,49 @@
//-----------------------------------------------------------------------
// <copyright file="IExternalStringReferenceResolver.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
{
/// <summary>
/// Resolves external strings references to reference objects during serialization and deserialization.
/// </summary>
public interface IExternalStringReferenceResolver
{
/// <summary>
/// Gets or sets the next resolver in the chain.
/// </summary>
/// <value>
/// The next resolver in the chain.
/// </value>
IExternalStringReferenceResolver NextResolver { get; set; }
/// <summary>
/// Tries to resolve a reference from a given Guid.
/// </summary>
/// <param name="id">The <see cref="string"/> to resolve.</param>
/// <param name="value">The resolved value.</param>
/// <returns><c>true</c> if the value was resolved; otherwise, <c>false</c>.</returns>
bool TryResolveReference(string id, out object value);
/// <summary>
/// Determines whether this resolver can reference the specified value with a string.
/// </summary>
/// <param name="value">The value to check.</param>
/// <param name="id">The string which references the value.</param>
/// <returns><c>true</c> if the value can be referenced; otherwise, <c>false</c>.</returns>
bool CanReference(object value, out string id);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9414cf6a3ea9a51afcf648fe9ea02bed
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,28 @@
namespace VRC.Udon.Serialization.OdinSerializer
{
using System;
/// <summary>
/// Implements methods for logging warnings, errors and exceptions during serialization and deserialization.
/// </summary>
public interface ILogger
{
/// <summary>
/// Logs a warning.
/// </summary>
/// <param name="warning">The warning to log.</param>
void LogWarning(string warning);
/// <summary>
/// Logs an error.
/// </summary>
/// <param name="error">The error to log.</param>
void LogError(string error);
/// <summary>
/// Logs an exception.
/// </summary>
/// <param name="exception">The exception to log.</param>
void LogException(Exception exception);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8bab352682356b8a2b02842520a68a11
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,43 @@
//-----------------------------------------------------------------------
// <copyright file="ISelfFormatter.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
{
/// <summary>
/// Specifies that a type is capable of serializing itself using an <see cref="IDataWriter"/> and an
/// <see cref="IDataReader"/>.
/// <para />
/// The deserialized type instance will be created without a constructor call using the
/// <see cref="System.Runtime.Serialization.FormatterServices.GetUninitializedObject(System.Type)"/>
/// method if it is a reference type, otherwise it will be created using default(type).
/// <para />
/// Use <see cref="AlwaysFormatsSelfAttribute"/> to specify that a class which implements this
/// interface should *always* format itself regardless of other formatters being specified.
/// </summary>
public interface ISelfFormatter
{
/// <summary>
/// Serializes the instance's data using the given writer.
/// </summary>
void Serialize(IDataWriter writer);
/// <summary>
/// Deserializes data into the instance using the given reader.
/// </summary>
void Deserialize(IDataReader reader);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 106ca47adfa52732b129015337a1c8cd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,51 @@
//-----------------------------------------------------------------------
// <copyright file="ISerializationPolicy.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
{
using System.Reflection;
/// <summary>
/// Defines which members to serialize and deserialize when there aren't any custom formatters for a type.
/// Usually, it governs the behaviour of the <see cref="FormatterEmitter"/> and <see cref="ReflectionFormatter{T}"/> classes.
/// </summary>
public interface ISerializationPolicy
{
/// <summary>
/// Gets the identifier of the policy. This can be stored in the serialization metadata, so the policy used to serialize can be recovered upon deserialization without knowing the policy ahead of time. This ID should preferably be unique.
/// </summary>
/// <value>
/// The identifier of the policy.
/// </value>
string ID { get; }
/// <summary>
/// Gets a value indicating whether to allow non serializable types. (Types which are not decorated with <see cref="System.SerializableAttribute"/>.)
/// </summary>
/// <value>
/// <c>true</c> if serializable types are allowed; otherwise, <c>false</c>.
/// </value>
bool AllowNonSerializableTypes { get; }
/// <summary>
/// Gets a value indicating whether a given <see cref="MemberInfo"/> should be serialized or not.
/// </summary>
/// <param name="member">The member to check.</param>
/// <returns><c>true</c> if the given member should be serialized, otherwise, <c>false</c>.</returns>
bool ShouldSerializeMember(MemberInfo member);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 90bcbfdc0286ca48d51fc578a1e15b8f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,23 @@
namespace VRC.Udon.Serialization.OdinSerializer
{
/// <summary>
/// The policy for which level of logging to do during serialization and deserialization.
/// </summary>
public enum LoggingPolicy
{
/// <summary>
/// Log errors.
/// </summary>
LogErrors,
/// <summary>
/// Log both warnings and errors.
/// </summary>
LogWarningsAndErrors,
/// <summary>
/// Log nothing at all. Note: Some extremely severe categories of errors are logged regardless of this setting.
/// </summary>
Silent
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7de3f23805ad9d4b3d033eef45e3b59b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,161 @@
//-----------------------------------------------------------------------
// <copyright file="NodeInfo.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
{
using System;
/// <summary>
/// Contains information about a node during deserialization and serialization.
/// </summary>
public struct NodeInfo
{
/// <summary>
/// An empty node.
/// </summary>
public static readonly NodeInfo Empty = new NodeInfo(true);
/// <summary>
/// The name of the node.
/// </summary>
public readonly string Name;
/// <summary>
/// The id of the node, or -1 if the node has no id.
/// </summary>
public readonly int Id;
/// <summary>
/// The type of the node, or null if the node has no type metadata.
/// </summary>
public readonly Type Type;
/// <summary>
/// Whether the node is an array or not.
/// </summary>
public readonly bool IsArray;
/// <summary>
/// Whether the node is an empty node.
/// </summary>
public readonly bool IsEmpty;
/// <summary>
/// Initializes a new instance of the <see cref="NodeInfo"/> struct.
/// </summary>
/// <param name="name">The name of the node.</param>
/// <param name="id">The id of the node.</param>
/// <param name="type">The type of the node.</param>
/// <param name="isArray">If set to <c>true</c> the node is an array node.</param>
public NodeInfo(string name, int id, Type type, bool isArray)
{
this.Name = name;
this.Id = id;
this.Type = type;
this.IsArray = isArray;
this.IsEmpty = false;
}
private NodeInfo(bool parameter)
{
this.Name = null;
this.Id = -1;
this.Type = null;
this.IsArray = false;
this.IsEmpty = true;
}
/// <summary>
/// Implements the operator == between <see cref="NodeInfo"/> and <see cref="NodeInfo"/>.
/// </summary>
/// <param name="a">The first <see cref="NodeInfo"/>.</param>
/// <param name="b">The second <see cref="NodeInfo"/>.</param>
/// <returns>
/// <c>true</c> if the nodes were equal; otherwise, <c>false</c>.
/// </returns>
public static bool operator ==(NodeInfo a, NodeInfo b)
{
return a.Name == b.Name
&& a.Id == b.Id
&& a.Type == b.Type
&& a.IsArray == b.IsArray
&& a.IsEmpty == b.IsEmpty;
}
/// <summary>
/// Implements the operator != between <see cref="NodeInfo"/> and <see cref="NodeInfo"/>.
/// </summary>
/// <param name="a">The first <see cref="NodeInfo"/>.</param>
/// <param name="b">The second <see cref="NodeInfo"/>.</param>
/// <returns>
/// <c>true</c> if the nodes were not equal; otherwise, <c>false</c>.
/// </returns>
public static bool operator !=(NodeInfo a, NodeInfo b)
{
return !(a == b);
}
/// <summary>
/// Determines whether the specified <see cref="System.Object" />, is equal to this instance.
/// </summary>
/// <param name="obj">The <see cref="System.Object" /> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
public override bool Equals(object obj)
{
if (object.ReferenceEquals(obj, null))
{
return false;
}
if (obj is NodeInfo)
{
return (NodeInfo)obj == this;
}
return false;
}
/// <summary>
/// Returns a hash code for this instance.
/// </summary>
/// <returns>
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
/// </returns>
public override int GetHashCode()
{
if (this.IsEmpty)
{
return 0;
}
const int P = 16777619;
unchecked
{
return (int)2166136261
^ ((this.Name == null ? 12321 : this.Name.GetHashCode()) * P)
^ (this.Id * P)
^ ((this.Type == null ? 1423 : this.Type.GetHashCode()) * P)
^ ((this.IsArray ? 124124 : 43234) * P)
^ ((this.IsEmpty ? 872934 : 27323) * P);
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 10eb7be2b7c363367c46bc5699a361a8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,32 @@
//-----------------------------------------------------------------------
// <copyright file="OdinSerializeAttribute.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
{
using System;
/// <summary>
/// Indicates that an instance field or auto-property should be serialized by Odin.
/// </summary>
/// <seealso cref="System.Attribute" />
[JetBrains.Annotations.MeansImplicitUse]
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class OdinSerializeAttribute : Attribute
{
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 766bbafe64ad16f63af4b81eb430e380
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,656 @@
//-----------------------------------------------------------------------
// <copyright file="PrefabModification.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
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Utilities;
/// <summary>
/// An Odin-serialized prefab modification, containing all the information necessary to apply the modification.
/// </summary>
public sealed class PrefabModification
{
/// <summary>
/// The type of modification to be made.
/// </summary>
public PrefabModificationType ModificationType;
/// <summary>
/// The deep reflection path at which to make the modification.
/// </summary>
public string Path;
/// <summary>
/// A list of all deep reflection paths in the target object where the value referenced by this modification was also located.
/// </summary>
public List<string> ReferencePaths;
/// <summary>
/// The modified value to set.
/// </summary>
public object ModifiedValue;
/// <summary>
/// The new list length to set.
/// </summary>
public int NewLength;
/// <summary>
/// The dictionary keys to add.
/// </summary>
public object[] DictionaryKeysAdded;
/// <summary>
/// The dictionary keys to remove.
/// </summary>
public object[] DictionaryKeysRemoved;
/// <summary>
/// Applies the modification to the given Object.
/// </summary>
public void Apply(UnityEngine.Object unityObject)
{
if (this.ModificationType == PrefabModificationType.Value)
{
this.ApplyValue(unityObject);
}
else if (this.ModificationType == PrefabModificationType.ListLength)
{
this.ApplyListLength(unityObject);
}
else if (this.ModificationType == PrefabModificationType.Dictionary)
{
this.ApplyDictionaryModifications(unityObject);
}
else
{
throw new NotImplementedException(this.ModificationType.ToString());
}
}
private void ApplyValue(UnityEngine.Object unityObject)
{
Type valueType = null;
if (!object.ReferenceEquals(this.ModifiedValue, null))
{
valueType = this.ModifiedValue.GetType();
}
if (valueType != null && this.ReferencePaths != null && this.ReferencePaths.Count > 0)
{
for (int i = 0; i < this.ReferencePaths.Count; i++)
{
var path = this.ReferencePaths[i];
try
{
var refValue = GetInstanceFromPath(path, unityObject);
if (!object.ReferenceEquals(refValue, null) && refValue.GetType() == valueType)
{
this.ModifiedValue = refValue;
break;
}
}
catch (Exception) { }
}
}
SetInstanceToPath(this.Path, unityObject, this.ModifiedValue);
}
private void ApplyListLength(UnityEngine.Object unityObject)
{
object listObj = GetInstanceFromPath(this.Path, unityObject);
if (listObj == null)
{
// The list has been deleted on the prefab;
// that supersedes our length change.
return;
}
Type listType = listObj.GetType();
if (listType.IsArray)
{
Array array = (Array)listObj;
if (this.NewLength == array.Length)
{
// If this happens, for some weird reason, then we can actually just not do anything
return;
}
// We actually need to replace all references to this array in the entire object graph!
// Ridiculous, we know - but there's no choice...
// Let's create a new, modified array
Array newArray = Array.CreateInstance(listType.GetElementType(), this.NewLength);
if (this.NewLength > array.Length)
{
Array.Copy(array, 0, newArray, 0, array.Length);
ReplaceAllReferencesInGraph(unityObject, array, newArray);
}
else
{
Array.Copy(array, 0, newArray, 0, newArray.Length);
ReplaceAllReferencesInGraph(unityObject, array, newArray);
}
}
else if (typeof(IList).IsAssignableFrom(listType))
{
IList list = (IList)listObj;
Type listElementType = listType.ImplementsOpenGenericInterface(typeof(IList<>)) ? listType.GetArgumentsOfInheritedOpenGenericInterface(typeof(IList<>))[0] : null;
bool elementIsValueType = listElementType != null ? listElementType.IsValueType : false;
int count = 0;
while (list.Count < this.NewLength)
{
if (elementIsValueType)
{
list.Add(Activator.CreateInstance(listElementType));
}
else
{
list.Add(null);
}
count++;
}
while (list.Count > this.NewLength)
{
list.RemoveAt(list.Count - 1);
}
}
else if (listType.ImplementsOpenGenericInterface(typeof(IList<>)))
{
Type elementType = listType.GetArgumentsOfInheritedOpenGenericInterface(typeof(IList<>))[0];
Type collectionType = typeof(ICollection<>).MakeGenericType(elementType);
bool elementIsValueType = elementType.IsValueType;
PropertyInfo countProp = collectionType.GetProperty("Count");
int count = (int)countProp.GetValue(listObj, null);
if (count < this.NewLength)
{
int add = this.NewLength - count;
MethodInfo addMethod = collectionType.GetMethod("Add");
for (int i = 0; i < add; i++)
{
if (elementIsValueType)
{
addMethod.Invoke(listObj, new object[] { Activator.CreateInstance(elementType) });
}
else
{
addMethod.Invoke(listObj, new object[] { null });
}
count++;
}
}
else if (count > this.NewLength)
{
int remove = count - this.NewLength;
Type listInterfaceType = typeof(IList<>).MakeGenericType(elementType);
MethodInfo removeAtMethod = listInterfaceType.GetMethod("RemoveAt");
for (int i = 0; i < remove; i++)
{
removeAtMethod.Invoke(listObj, new object[] { count - (remove + 1) });
}
}
}
}
private void ApplyDictionaryModifications(UnityEngine.Object unityObject)
{
object dictionaryObj = GetInstanceFromPath(this.Path, unityObject);
if (dictionaryObj == null)
{
// The dictionary has been deleted on the prefab;
// that supersedes our dictionary modifications.
return;
}
var type = dictionaryObj.GetType();
if (!type.ImplementsOpenGenericInterface(typeof(IDictionary<,>)))
{
// A value change has changed the target modified value to
// not be a dictionary - that also supersedes this modification.
return;
}
var typeArgs = type.GetArgumentsOfInheritedOpenGenericInterface(typeof(IDictionary<,>));
var iType = typeof(IDictionary<,>).MakeGenericType(typeArgs);
//
// First, remove keys
//
if (this.DictionaryKeysRemoved != null && this.DictionaryKeysRemoved.Length > 0)
{
MethodInfo method = iType.GetMethod("Remove", new Type[] { typeArgs[0] });
object[] parameters = new object[1];
for (int i = 0; i < this.DictionaryKeysRemoved.Length; i++)
{
parameters[0] = this.DictionaryKeysRemoved[i];
// Ensure the key value is safe to add
if (object.ReferenceEquals(parameters[0], null) || !typeArgs[0].IsAssignableFrom(parameters[0].GetType()))
continue;
method.Invoke(dictionaryObj, parameters);
}
}
//
// Then, add keys
//
if (this.DictionaryKeysAdded != null && this.DictionaryKeysAdded.Length > 0)
{
MethodInfo method = iType.GetMethod("set_Item", typeArgs);
object[] parameters = new object[2];
// Get default value to set key to
parameters[1] = typeArgs[1].IsValueType ? Activator.CreateInstance(typeArgs[1]) : null;
for (int i = 0; i < this.DictionaryKeysAdded.Length; i++)
{
parameters[0] = this.DictionaryKeysAdded[i];
// Ensure the key value is safe to add
if (object.ReferenceEquals(parameters[0], null) || !typeArgs[0].IsAssignableFrom(parameters[0].GetType()))
continue;
method.Invoke(dictionaryObj, parameters);
}
}
}
private static void ReplaceAllReferencesInGraph(object graph, object oldReference, object newReference, HashSet<object> processedReferences = null)
{
if (processedReferences == null)
{
processedReferences = new HashSet<object>(ReferenceEqualityComparer<object>.Default);
}
processedReferences.Add(graph);
if (graph.GetType().IsArray)
{
Array array = (Array)graph;
for (int i = 0; i < array.Length; i++)
{
var value = array.GetValue(i);
if (object.ReferenceEquals(value, null))
{
continue;
}
if (object.ReferenceEquals(value, oldReference))
{
array.SetValue(newReference, i);
value = newReference;
}
if (!processedReferences.Contains(value))
{
ReplaceAllReferencesInGraph(value, oldReference, newReference, processedReferences);
}
}
}
else
{
var members = FormatterUtilities.GetSerializableMembers(graph.GetType(), SerializationPolicies.Everything);
for (int i = 0; i < members.Length; i++)
{
FieldInfo field = (FieldInfo)members[i];
if (field.FieldType.IsPrimitive || field.FieldType == typeof(SerializationData) || field.FieldType == typeof(string))
continue;
object value = field.GetValue(graph);
if (object.ReferenceEquals(value, null))
{
continue;
}
Type valueType = value.GetType();
if (valueType.IsPrimitive || valueType == typeof(SerializationData) || valueType == typeof(string))
continue;
if (object.ReferenceEquals(value, oldReference))
{
field.SetValue(graph, newReference);
value = newReference;
}
if (!processedReferences.Contains(value))
{
ReplaceAllReferencesInGraph(value, oldReference, newReference, processedReferences);
}
}
}
}
private static object GetInstanceFromPath(string path, object instance)
{
string[] steps = path.Split('.');
object currentInstance = instance;
for (int i = 0; i < steps.Length; i++)
{
currentInstance = GetInstanceOfStep(steps[i], currentInstance);
if (object.ReferenceEquals(currentInstance, null))
{
//Debug.LogWarning("Failed to resolve modification path '" + path + "' at step '" + steps[i] + "'.");
return null;
}
}
return currentInstance;
}
private static object GetInstanceOfStep(string step, object instance)
{
Type type = instance.GetType();
if (step.StartsWith("[", StringComparison.InvariantCulture) && step.EndsWith("]", StringComparison.InvariantCulture))
{
int index;
string indexStr = step.Substring(1, step.Length - 2);
if (!int.TryParse(indexStr, out index))
{
throw new ArgumentException("Couldn't parse an index from the path step '" + step + "'.");
}
// We need to check the current type to see if we can treat it as a list
if (type.IsArray)
{
Array array = (Array)instance;
if (index < 0 || index >= array.Length)
{
return null;
}
return array.GetValue(index);
}
else if (typeof(IList).IsAssignableFrom(type))
{
IList list = (IList)instance;
if (index < 0 || index >= list.Count)
{
return null;
}
return list[index];
}
else if (type.ImplementsOpenGenericInterface(typeof(IList<>)))
{
Type elementType = type.GetArgumentsOfInheritedOpenGenericInterface(typeof(IList<>))[0];
Type listType = typeof(IList<>).MakeGenericType(elementType);
MethodInfo getItemMethod = listType.GetMethod("get_Item", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
try
{
return getItemMethod.Invoke(instance, new object[] { index });
}
catch (Exception)
{
return null;
}
}
}
else if (step.StartsWith("{", StringComparison.InvariantCultureIgnoreCase) && step.EndsWith("}", StringComparison.InvariantCultureIgnoreCase))
{
if (type.ImplementsOpenGenericInterface(typeof(IDictionary<,>)))
{
var dictArgs = type.GetArgumentsOfInheritedOpenGenericInterface(typeof(IDictionary<,>));
object key = DictionaryKeyUtility.GetDictionaryKeyValue(step, dictArgs[0]);
Type dictType = typeof(IDictionary<,>).MakeGenericType(dictArgs);
MethodInfo getItemMethod = dictType.GetMethod("get_Item", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
try
{
return getItemMethod.Invoke(instance, new object[] { key });
}
catch (Exception)
{
return null;
}
}
}
else
{
string privateTypeName = null;
int plusIndex = step.IndexOf('+');
if (plusIndex >= 0)
{
privateTypeName = step.Substring(0, plusIndex);
step = step.Substring(plusIndex + 1);
}
var possibleMembers = type.GetAllMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(n => n is FieldInfo || n is PropertyInfo);
foreach (var member in possibleMembers)
{
if (member.Name == step)
{
if (privateTypeName != null && member.DeclaringType.Name != privateTypeName)
{
continue;
}
return member.GetMemberValue(instance);
}
}
}
return null;
}
private static void SetInstanceToPath(string path, object instance, object value)
{
bool setParentInstance;
string[] steps = path.Split('.');
SetInstanceToPath(path, steps, 0, instance, value, out setParentInstance);
}
private static void SetInstanceToPath(string path, string[] steps, int index, object instance, object value, out bool setParentInstance)
{
setParentInstance = false;
if (index < steps.Length - 1)
{
object currentInstance = GetInstanceOfStep(steps[index], instance);
if (object.ReferenceEquals(currentInstance, null))
{
//Debug.LogWarning("Failed to resolve prefab modification path '" + path + "' at step '" + steps[index] + "'.");
return;
}
SetInstanceToPath(path, steps, index + 1, currentInstance, value, out setParentInstance);
if (setParentInstance)
{
// We need to set the current instance to the parent instance member,
// because the current instance is a value type, and thus it may have
// been boxed. If we don't do this, the value set might be lost.
TrySetInstanceOfStep(steps[index], instance, currentInstance, out setParentInstance);
}
}
else
{
TrySetInstanceOfStep(steps[index], instance, value, out setParentInstance);
}
}
private static bool TrySetInstanceOfStep(string step, object instance, object value, out bool setParentInstance)
{
setParentInstance = false;
try
{
Type type = instance.GetType();
if (step.StartsWith("[", StringComparison.InvariantCulture) && step.EndsWith("]", StringComparison.InvariantCulture))
{
int index;
string indexStr = step.Substring(1, step.Length - 2);
if (!int.TryParse(indexStr, out index))
{
throw new ArgumentException("Couldn't parse an index from the path step '" + step + "'.");
}
// We need to check the current type to see if we can treat it as a list
if (type.IsArray)
{
Array array = (Array)instance;
if (index < 0 || index >= array.Length)
{
return false;
}
array.SetValue(value, index);
return true;
}
else if (typeof(IList).IsAssignableFrom(type))
{
IList list = (IList)instance;
if (index < 0 || index >= list.Count)
{
return false;
}
list[index] = value;
return true;
}
else if (type.ImplementsOpenGenericInterface(typeof(IList<>)))
{
Type elementType = type.GetArgumentsOfInheritedOpenGenericInterface(typeof(IList<>))[0];
Type listType = typeof(IList<>).MakeGenericType(elementType);
MethodInfo setItemMethod = listType.GetMethod("set_Item", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
setItemMethod.Invoke(instance, new object[] { index, value });
return true;
}
}
else if (step.StartsWith("{", StringComparison.InvariantCulture) && step.EndsWith("}", StringComparison.InvariantCulture))
{
if (type.ImplementsOpenGenericInterface(typeof(IDictionary<,>)))
{
var dictArgs = type.GetArgumentsOfInheritedOpenGenericInterface(typeof(IDictionary<,>));
object key = DictionaryKeyUtility.GetDictionaryKeyValue(step, dictArgs[0]);
Type dictType = typeof(IDictionary<,>).MakeGenericType(dictArgs);
MethodInfo containsKeyMethod = dictType.GetMethod("ContainsKey", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
MethodInfo setItemMethod = dictType.GetMethod("set_Item", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
bool containsKey = (bool)containsKeyMethod.Invoke(instance, new object[] { key });
if (!containsKey)
{
// We are *not* allowed to add new keys during this step
return false;
}
setItemMethod.Invoke(instance, new object[] { key, value });
}
}
else
{
string privateTypeName = null;
int plusIndex = step.IndexOf('+');
if (plusIndex >= 0)
{
privateTypeName = step.Substring(0, plusIndex);
step = step.Substring(plusIndex + 1);
}
var possibleMembers = type.GetAllMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(n => n is FieldInfo || n is PropertyInfo);
foreach (var member in possibleMembers)
{
if (member.Name == step)
{
if (privateTypeName != null && member.DeclaringType.Name != privateTypeName)
{
continue;
}
member.SetMemberValue(instance, value);
if (instance.GetType().IsValueType)
{
setParentInstance = true;
}
return true;
}
}
}
return false;
}
catch (Exception)
{
return false;
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3db8c00661ec222984427ab12295940f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,40 @@
//-----------------------------------------------------------------------
// <copyright file="PrefabModificationType.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
{
/// <summary>
/// Types of prefab modification that can be applied.
/// </summary>
public enum PrefabModificationType
{
/// <summary>
/// A value has been changed at a given path.
/// </summary>
Value,
/// <summary>
/// A list length has been changed at a given path.
/// </summary>
ListLength,
/// <summary>
/// A dictionary has been changed at a given path.
/// </summary>
Dictionary
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 23ceed712f987034deb8e92c561a1d3b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,45 @@
//-----------------------------------------------------------------------
// <copyright file="PreviouslySerializedAsAttribute.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
{
using System;
/// <summary>
/// Indicates that an instance field or auto-property was previously serialized with a different name, so that values serialized with the old name will be properly deserialized into this member.
///
/// This does the same as Unity's FormerlySerializedAs attribute, except it can also be applied to properties.
/// </summary>
/// <seealso cref="System.Attribute" />
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class PreviouslySerializedAsAttribute : Attribute
{
/// <summary>
/// The former name.
/// </summary>
public string Name { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="PreviouslySerializedAsAttribute"/> class.
/// </summary>
/// <param name="name">The former name.</param>
public PreviouslySerializedAsAttribute(string name)
{
this.Name = name;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 96fec6c04f13e378def42ea5ad5dc940
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,927 @@
//-----------------------------------------------------------------------
// <copyright file="ProperBitConverter.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>
//-----------------------------------------------------------------------
// Tested and verified to work
#pragma warning disable 0675
using System.Globalization;
namespace VRC.Udon.Serialization.OdinSerializer
{
using System;
using System.Runtime.InteropServices;
/// <summary>
/// Corresponds to the .NET <see cref="BitConverter"/> class, but works only with buffers and so never allocates garbage.
/// <para />
/// This class always writes and reads bytes in a little endian format, regardless of system architecture.
/// </summary>
public static class ProperBitConverter
{
private static readonly uint[] ByteToHexCharLookupLowerCase = CreateByteToHexLookup(false);
private static readonly uint[] ByteToHexCharLookupUpperCase = CreateByteToHexLookup(true);
// 16x16 table, set up for direct visual correlation to Unicode table with hex coords
private static readonly byte[] HexToByteLookup = new byte[] {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
private static uint[] CreateByteToHexLookup(bool upperCase)
{
var result = new uint[256];
if (upperCase)
{
for (int i = 0; i < 256; i++)
{
string s = i.ToString("X2", CultureInfo.InvariantCulture);
result[i] = ((uint)s[0]) + ((uint)s[1] << 16);
}
}
else
{
for (int i = 0; i < 256; i++)
{
string s = i.ToString("x2", CultureInfo.InvariantCulture);
result[i] = ((uint)s[0]) + ((uint)s[1] << 16);
}
}
return result;
}
/// <summary>
/// Converts a byte array into a hexadecimal string.
/// </summary>
public static string BytesToHexString(byte[] bytes, bool lowerCaseHexChars = true)
{
var lookup = lowerCaseHexChars ? ByteToHexCharLookupLowerCase : ByteToHexCharLookupUpperCase;
var result = new char[bytes.Length * 2];
for (int i = 0; i < bytes.Length; i++)
{
int offset = i * 2;
var val = lookup[bytes[i]];
result[offset] = (char)val;
result[offset + 1] = (char)(val >> 16);
}
return new string(result);
}
/// <summary>
/// Converts a hexadecimal string into a byte array.
/// </summary>
public static byte[] HexStringToBytes(string hex)
{
int length = hex.Length;
int rLength = length / 2;
if (length % 2 != 0)
{
throw new ArgumentException("Hex string must have an even length.");
}
byte[] result = new byte[rLength];
for (int i = 0; i < rLength; i++)
{
int offset = i * 2;
byte b1;
byte b2;
try
{
b1 = HexToByteLookup[hex[offset]];
if (b1 == 0xff)
{
throw new ArgumentException("Expected a hex character, got '" + hex[offset] + "' at string index '" + offset + "'.");
}
}
catch (IndexOutOfRangeException)
{
throw new ArgumentException("Expected a hex character, got '" + hex[offset] + "' at string index '" + offset + "'.");
}
try
{
b2 = HexToByteLookup[hex[offset + 1]];
if (b2 == 0xff)
{
throw new ArgumentException("Expected a hex character, got '" + hex[offset + 1] + "' at string index '" + (offset + 1) + "'.");
}
}
catch (IndexOutOfRangeException)
{
throw new ArgumentException("Expected a hex character, got '" + hex[offset + 1] + "' at string index '" + (offset + 1) + "'.");
}
result[i] = (byte)(b1 << 4 | b2);
}
return result;
}
/// <summary>
/// Reads two bytes from a buffer and converts them into a <see cref="short"/> value.
/// </summary>
/// <param name="buffer">The buffer to read from.</param>
/// <param name="index">The index to start reading at.</param>
/// <returns>The converted value.</returns>
public static short ToInt16(byte[] buffer, int index)
{
short value = default(short);
value |= buffer[index + 1];
value <<= 8;
value |= buffer[index];
return value;
}
/// <summary>
/// Reads two bytes from a buffer and converts them into a <see cref="ushort"/> value.
/// </summary>
/// <param name="buffer">The buffer to read from.</param>
/// <param name="index">The index to start reading at.</param>
/// <returns>The converted value.</returns>
public static ushort ToUInt16(byte[] buffer, int index)
{
ushort value = default(ushort);
value |= buffer[index + 1];
value <<= 8;
value |= buffer[index];
return value;
}
/// <summary>
/// Reads four bytes from a buffer and converts them into an <see cref="int"/> value.
/// </summary>
/// <param name="buffer">The buffer to read from.</param>
/// <param name="index">The index to start reading at.</param>
/// <returns>The converted value.</returns>
public static int ToInt32(byte[] buffer, int index)
{
int value = default(int);
value |= buffer[index + 3];
value <<= 8;
value |= buffer[index + 2];
value <<= 8;
value |= buffer[index + 1];
value <<= 8;
value |= buffer[index];
return value;
}
/// <summary>
/// Reads four bytes from a buffer and converts them into an <see cref="uint"/> value.
/// </summary>
/// <param name="buffer">The buffer to read from.</param>
/// <param name="index">The index to start reading at.</param>
/// <returns>The converted value.</returns>
public static uint ToUInt32(byte[] buffer, int index)
{
uint value = default(uint);
value |= buffer[index + 3];
value <<= 8;
value |= buffer[index + 2];
value <<= 8;
value |= buffer[index + 1];
value <<= 8;
value |= buffer[index];
return value;
}
/// <summary>
/// Reads eight bytes from a buffer and converts them into a <see cref="long"/> value.
/// </summary>
/// <param name="buffer">The buffer to read from.</param>
/// <param name="index">The index to start reading at.</param>
/// <returns>The converted value.</returns>
public static long ToInt64(byte[] buffer, int index)
{
long value = default(long);
value |= buffer[index + 7];
value <<= 8;
value |= buffer[index + 6];
value <<= 8;
value |= buffer[index + 5];
value <<= 8;
value |= buffer[index + 4];
value <<= 8;
value |= buffer[index + 3];
value <<= 8;
value |= buffer[index + 2];
value <<= 8;
value |= buffer[index + 1];
value <<= 8;
value |= buffer[index];
return value;
}
/// <summary>
/// Reads eight bytes from a buffer and converts them into an <see cref="ulong"/> value.
/// </summary>
/// <param name="buffer">The buffer to read from.</param>
/// <param name="index">The index to start reading at.</param>
/// <returns>The converted value.</returns>
public static ulong ToUInt64(byte[] buffer, int index)
{
ulong value = default(ulong);
value |= buffer[index + 7];
value <<= 8;
value |= buffer[index + 6];
value <<= 8;
value |= buffer[index + 5];
value <<= 8;
value |= buffer[index + 4];
value <<= 8;
value |= buffer[index + 3];
value <<= 8;
value |= buffer[index + 2];
value <<= 8;
value |= buffer[index + 1];
value <<= 8;
value |= buffer[index];
return value;
}
/// <summary>
/// Reads four bytes from a buffer and converts them into an <see cref="float"/> value.
/// </summary>
/// <param name="buffer">The buffer to read from.</param>
/// <param name="index">The index to start reading at.</param>
/// <returns>The converted value.</returns>
public static float ToSingle(byte[] buffer, int index)
{
var union = default(SingleByteUnion);
if (BitConverter.IsLittleEndian)
{
union.Byte0 = buffer[index];
union.Byte1 = buffer[index + 1];
union.Byte2 = buffer[index + 2];
union.Byte3 = buffer[index + 3];
}
else
{
union.Byte3 = buffer[index];
union.Byte2 = buffer[index + 1];
union.Byte1 = buffer[index + 2];
union.Byte0 = buffer[index + 3];
}
return union.Value;
}
/// <summary>
/// Reads eight bytes from a buffer and converts them into an <see cref="double"/> value.
/// </summary>
/// <param name="buffer">The buffer to read from.</param>
/// <param name="index">The index to start reading at.</param>
/// <returns>The converted value.</returns>
public static double ToDouble(byte[] buffer, int index)
{
var union = default(DoubleByteUnion);
if (BitConverter.IsLittleEndian)
{
union.Byte0 = buffer[index];
union.Byte1 = buffer[index + 1];
union.Byte2 = buffer[index + 2];
union.Byte3 = buffer[index + 3];
union.Byte4 = buffer[index + 4];
union.Byte5 = buffer[index + 5];
union.Byte6 = buffer[index + 6];
union.Byte7 = buffer[index + 7];
}
else
{
union.Byte7 = buffer[index];
union.Byte6 = buffer[index + 1];
union.Byte5 = buffer[index + 2];
union.Byte4 = buffer[index + 3];
union.Byte3 = buffer[index + 4];
union.Byte2 = buffer[index + 5];
union.Byte1 = buffer[index + 6];
union.Byte0 = buffer[index + 7];
}
return union.Value;
}
/// <summary>
/// Reads sixteen bytes from a buffer and converts them into a <see cref="decimal"/> value.
/// </summary>
/// <param name="buffer">The buffer to read from.</param>
/// <param name="index">The index to start reading at.</param>
/// <returns>The converted value.</returns>
public static decimal ToDecimal(byte[] buffer, int index)
{
var union = default(DecimalByteUnion);
if (BitConverter.IsLittleEndian)
{
union.Byte0 = buffer[index];
union.Byte1 = buffer[index + 1];
union.Byte2 = buffer[index + 2];
union.Byte3 = buffer[index + 3];
union.Byte4 = buffer[index + 4];
union.Byte5 = buffer[index + 5];
union.Byte6 = buffer[index + 6];
union.Byte7 = buffer[index + 7];
union.Byte8 = buffer[index + 8];
union.Byte9 = buffer[index + 9];
union.Byte10 = buffer[index + 10];
union.Byte11 = buffer[index + 11];
union.Byte12 = buffer[index + 12];
union.Byte13 = buffer[index + 13];
union.Byte14 = buffer[index + 14];
union.Byte15 = buffer[index + 15];
}
else
{
union.Byte15 = buffer[index];
union.Byte14 = buffer[index + 1];
union.Byte13 = buffer[index + 2];
union.Byte12 = buffer[index + 3];
union.Byte11 = buffer[index + 4];
union.Byte10 = buffer[index + 5];
union.Byte9 = buffer[index + 6];
union.Byte8 = buffer[index + 7];
union.Byte7 = buffer[index + 8];
union.Byte6 = buffer[index + 9];
union.Byte5 = buffer[index + 10];
union.Byte4 = buffer[index + 11];
union.Byte3 = buffer[index + 12];
union.Byte2 = buffer[index + 13];
union.Byte1 = buffer[index + 14];
union.Byte0 = buffer[index + 15];
}
return union.Value;
}
/// <summary>
/// Reads sixteen bytes from a buffer and converts them into a <see cref="Guid"/> value.
/// </summary>
/// <param name="buffer">The buffer to read from.</param>
/// <param name="index">The index to start reading at.</param>
/// <returns>The converted value.</returns>
public static Guid ToGuid(byte[] buffer, int index)
{
var union = default(GuidByteUnion);
// First 10 bytes of a guid are always little endian
// Last 6 bytes depend on architecture endianness
// See http://stackoverflow.com/questions/10190817/guid-byte-order-in-
union.Byte0 = buffer[index];
union.Byte1 = buffer[index + 1];
union.Byte2 = buffer[index + 2];
union.Byte3 = buffer[index + 3];
union.Byte4 = buffer[index + 4];
union.Byte5 = buffer[index + 5];
union.Byte6 = buffer[index + 6];
union.Byte7 = buffer[index + 7];
union.Byte8 = buffer[index + 8];
union.Byte9 = buffer[index + 9];
if (BitConverter.IsLittleEndian)
{
union.Byte10 = buffer[index + 10];
union.Byte11 = buffer[index + 11];
union.Byte12 = buffer[index + 12];
union.Byte13 = buffer[index + 13];
union.Byte14 = buffer[index + 14];
union.Byte15 = buffer[index + 15];
}
else
{
union.Byte15 = buffer[index + 10];
union.Byte14 = buffer[index + 11];
union.Byte13 = buffer[index + 12];
union.Byte12 = buffer[index + 13];
union.Byte11 = buffer[index + 14];
union.Byte10 = buffer[index + 15];
}
return union.Value;
}
/// <summary>
/// Turns a <see cref="short"/> value into two bytes and writes those bytes to a given buffer.
/// </summary>
/// <param name="buffer">The buffer to write to.</param>
/// <param name="index">The index to start writing at.</param>
/// <param name="value">The value to write.</param>
public static void GetBytes(byte[] buffer, int index, short value)
{
if (BitConverter.IsLittleEndian)
{
buffer[index] = (byte)value;
buffer[index + 1] = (byte)(value >> 8);
}
else
{
buffer[index] = (byte)(value >> 8);
buffer[index + 1] = (byte)value;
}
}
/// <summary>
/// Turns an <see cref="ushort"/> value into two bytes and writes those bytes to a given buffer.
/// </summary>
/// <param name="buffer">The buffer to write to.</param>
/// <param name="index">The index to start writing at.</param>
/// <param name="value">The value to write.</param>
public static void GetBytes(byte[] buffer, int index, ushort value)
{
if (BitConverter.IsLittleEndian)
{
buffer[index] = (byte)value;
buffer[index + 1] = (byte)(value >> 8);
}
else
{
buffer[index] = (byte)(value >> 8);
buffer[index + 1] = (byte)value;
}
}
/// <summary>
/// Turns an <see cref="int"/> value into four bytes and writes those bytes to a given buffer.
/// </summary>
/// <param name="buffer">The buffer to write to.</param>
/// <param name="index">The index to start writing at.</param>
/// <param name="value">The value to write.</param>
public static void GetBytes(byte[] buffer, int index, int value)
{
if (BitConverter.IsLittleEndian)
{
buffer[index] = (byte)value;
buffer[index + 1] = (byte)(value >> 8);
buffer[index + 2] = (byte)(value >> 16);
buffer[index + 3] = (byte)(value >> 24);
}
else
{
buffer[index] = (byte)(value >> 24);
buffer[index + 1] = (byte)(value >> 16);
buffer[index + 2] = (byte)(value >> 8);
buffer[index + 3] = (byte)value;
}
}
/// <summary>
/// Turns an <see cref="uint"/> value into four bytes and writes those bytes to a given buffer.
/// </summary>
/// <param name="buffer">The buffer to write to.</param>
/// <param name="index">The index to start writing at.</param>
/// <param name="value">The value to write.</param>
public static void GetBytes(byte[] buffer, int index, uint value)
{
if (BitConverter.IsLittleEndian)
{
buffer[index] = (byte)value;
buffer[index + 1] = (byte)(value >> 8);
buffer[index + 2] = (byte)(value >> 16);
buffer[index + 3] = (byte)(value >> 24);
}
else
{
buffer[index] = (byte)(value >> 24);
buffer[index + 1] = (byte)(value >> 16);
buffer[index + 2] = (byte)(value >> 8);
buffer[index + 3] = (byte)value;
}
}
/// <summary>
/// Turns a <see cref="long"/> value into eight bytes and writes those bytes to a given buffer.
/// </summary>
/// <param name="buffer">The buffer to write to.</param>
/// <param name="index">The index to start writing at.</param>
/// <param name="value">The value to write.</param>
public static void GetBytes(byte[] buffer, int index, long value)
{
if (BitConverter.IsLittleEndian)
{
buffer[index] = (byte)value;
buffer[index + 1] = (byte)(value >> 8);
buffer[index + 2] = (byte)(value >> 16);
buffer[index + 3] = (byte)(value >> 24);
buffer[index + 4] = (byte)(value >> 32);
buffer[index + 5] = (byte)(value >> 40);
buffer[index + 6] = (byte)(value >> 48);
buffer[index + 7] = (byte)(value >> 56);
}
else
{
buffer[index] = (byte)(value >> 56);
buffer[index + 1] = (byte)(value >> 48);
buffer[index + 2] = (byte)(value >> 40);
buffer[index + 3] = (byte)(value >> 32);
buffer[index + 4] = (byte)(value >> 24);
buffer[index + 5] = (byte)(value >> 16);
buffer[index + 6] = (byte)(value >> 8);
buffer[index + 7] = (byte)value;
}
}
/// <summary>
/// Turns an <see cref="ulong"/> value into eight bytes and writes those bytes to a given buffer.
/// </summary>
/// <param name="buffer">The buffer to write to.</param>
/// <param name="index">The index to start writing at.</param>
/// <param name="value">The value to write.</param>
public static void GetBytes(byte[] buffer, int index, ulong value)
{
if (BitConverter.IsLittleEndian)
{
buffer[index] = (byte)value;
buffer[index + 1] = (byte)(value >> 8);
buffer[index + 2] = (byte)(value >> 16);
buffer[index + 3] = (byte)(value >> 24);
buffer[index + 4] = (byte)(value >> 32);
buffer[index + 5] = (byte)(value >> 40);
buffer[index + 6] = (byte)(value >> 48);
buffer[index + 7] = (byte)(value >> 56);
}
else
{
buffer[index] = (byte)(value >> 56);
buffer[index + 1] = (byte)(value >> 48);
buffer[index + 2] = (byte)(value >> 40);
buffer[index + 3] = (byte)(value >> 32);
buffer[index + 4] = (byte)(value >> 24);
buffer[index + 5] = (byte)(value >> 16);
buffer[index + 6] = (byte)(value >> 8);
buffer[index + 7] = (byte)value;
}
}
/// <summary>
/// Turns a <see cref="float"/> value into four bytes and writes those bytes to a given buffer.
/// </summary>
/// <param name="buffer">The buffer to write to.</param>
/// <param name="index">The index to start writing at.</param>
/// <param name="value">The value to write.</param>
public static void GetBytes(byte[] buffer, int index, float value)
{
var union = default(SingleByteUnion);
union.Value = value;
if (BitConverter.IsLittleEndian)
{
buffer[index] = union.Byte0;
buffer[index + 1] = union.Byte1;
buffer[index + 2] = union.Byte2;
buffer[index + 3] = union.Byte3;
}
else
{
buffer[index] = union.Byte3;
buffer[index + 1] = union.Byte2;
buffer[index + 2] = union.Byte1;
buffer[index + 3] = union.Byte0;
}
}
/// <summary>
/// Turns a <see cref="double"/> value into eight bytes and writes those bytes to a given buffer.
/// </summary>
/// <param name="buffer">The buffer to write to.</param>
/// <param name="index">The index to start writing at.</param>
/// <param name="value">The value to write.</param>
public static void GetBytes(byte[] buffer, int index, double value)
{
var union = default(DoubleByteUnion);
union.Value = value;
if (BitConverter.IsLittleEndian)
{
buffer[index] = union.Byte0;
buffer[index + 1] = union.Byte1;
buffer[index + 2] = union.Byte2;
buffer[index + 3] = union.Byte3;
buffer[index + 4] = union.Byte4;
buffer[index + 5] = union.Byte5;
buffer[index + 6] = union.Byte6;
buffer[index + 7] = union.Byte7;
}
else
{
buffer[index] = union.Byte7;
buffer[index + 1] = union.Byte6;
buffer[index + 2] = union.Byte5;
buffer[index + 3] = union.Byte4;
buffer[index + 4] = union.Byte3;
buffer[index + 5] = union.Byte2;
buffer[index + 6] = union.Byte1;
buffer[index + 7] = union.Byte0;
}
}
/// <summary>
/// Turns a <see cref="decimal"/> value into sixteen bytes and writes those bytes to a given buffer.
/// </summary>
/// <param name="buffer">The buffer to write to.</param>
/// <param name="index">The index to start writing at.</param>
/// <param name="value">The value to write.</param>
public static void GetBytes(byte[] buffer, int index, decimal value)
{
var union = default(DecimalByteUnion);
union.Value = value;
if (BitConverter.IsLittleEndian)
{
buffer[index] = union.Byte0;
buffer[index + 1] = union.Byte1;
buffer[index + 2] = union.Byte2;
buffer[index + 3] = union.Byte3;
buffer[index + 4] = union.Byte4;
buffer[index + 5] = union.Byte5;
buffer[index + 6] = union.Byte6;
buffer[index + 7] = union.Byte7;
buffer[index + 8] = union.Byte8;
buffer[index + 9] = union.Byte9;
buffer[index + 10] = union.Byte10;
buffer[index + 11] = union.Byte11;
buffer[index + 12] = union.Byte12;
buffer[index + 13] = union.Byte13;
buffer[index + 14] = union.Byte14;
buffer[index + 15] = union.Byte15;
}
else
{
buffer[index] = union.Byte15;
buffer[index + 1] = union.Byte14;
buffer[index + 2] = union.Byte13;
buffer[index + 3] = union.Byte12;
buffer[index + 4] = union.Byte11;
buffer[index + 5] = union.Byte10;
buffer[index + 6] = union.Byte9;
buffer[index + 7] = union.Byte8;
buffer[index + 8] = union.Byte7;
buffer[index + 9] = union.Byte6;
buffer[index + 10] = union.Byte5;
buffer[index + 11] = union.Byte4;
buffer[index + 12] = union.Byte3;
buffer[index + 13] = union.Byte2;
buffer[index + 14] = union.Byte1;
buffer[index + 15] = union.Byte0;
}
}
/// <summary>
/// Turns a <see cref="Guid"/> value into sixteen bytes and writes those bytes to a given buffer.
/// </summary>
/// <param name="buffer">The buffer to write to.</param>
/// <param name="index">The index to start writing at.</param>
/// <param name="value">The value to write.</param>
public static void GetBytes(byte[] buffer, int index, Guid value)
{
var union = default(GuidByteUnion);
union.Value = value;
// First 10 bytes of a guid are always little endian
// Last 6 bytes depend on architecture endianness
// See http://stackoverflow.com/questions/10190817/guid-byte-order-in-net
// TODO: Test if this actually works on big-endian architecture. Where the hell do we find that?
buffer[index] = union.Byte0;
buffer[index + 1] = union.Byte1;
buffer[index + 2] = union.Byte2;
buffer[index + 3] = union.Byte3;
buffer[index + 4] = union.Byte4;
buffer[index + 5] = union.Byte5;
buffer[index + 6] = union.Byte6;
buffer[index + 7] = union.Byte7;
buffer[index + 8] = union.Byte8;
buffer[index + 9] = union.Byte9;
if (BitConverter.IsLittleEndian)
{
buffer[index + 10] = union.Byte10;
buffer[index + 11] = union.Byte11;
buffer[index + 12] = union.Byte12;
buffer[index + 13] = union.Byte13;
buffer[index + 14] = union.Byte14;
buffer[index + 15] = union.Byte15;
}
else
{
buffer[index + 10] = union.Byte15;
buffer[index + 11] = union.Byte14;
buffer[index + 12] = union.Byte13;
buffer[index + 13] = union.Byte12;
buffer[index + 14] = union.Byte11;
buffer[index + 15] = union.Byte10;
}
}
[StructLayout(LayoutKind.Explicit)]
private struct SingleByteUnion
{
[FieldOffset(0)]
public byte Byte0;
[FieldOffset(1)]
public byte Byte1;
[FieldOffset(2)]
public byte Byte2;
[FieldOffset(3)]
public byte Byte3;
[FieldOffset(0)]
public float Value;
}
[StructLayout(LayoutKind.Explicit)]
private struct DoubleByteUnion
{
[FieldOffset(0)]
public byte Byte0;
[FieldOffset(1)]
public byte Byte1;
[FieldOffset(2)]
public byte Byte2;
[FieldOffset(3)]
public byte Byte3;
[FieldOffset(4)]
public byte Byte4;
[FieldOffset(5)]
public byte Byte5;
[FieldOffset(6)]
public byte Byte6;
[FieldOffset(7)]
public byte Byte7;
[FieldOffset(0)]
public double Value;
}
[StructLayout(LayoutKind.Explicit)]
private struct DecimalByteUnion
{
[FieldOffset(0)]
public byte Byte0;
[FieldOffset(1)]
public byte Byte1;
[FieldOffset(2)]
public byte Byte2;
[FieldOffset(3)]
public byte Byte3;
[FieldOffset(4)]
public byte Byte4;
[FieldOffset(5)]
public byte Byte5;
[FieldOffset(6)]
public byte Byte6;
[FieldOffset(7)]
public byte Byte7;
[FieldOffset(8)]
public byte Byte8;
[FieldOffset(9)]
public byte Byte9;
[FieldOffset(10)]
public byte Byte10;
[FieldOffset(11)]
public byte Byte11;
[FieldOffset(12)]
public byte Byte12;
[FieldOffset(13)]
public byte Byte13;
[FieldOffset(14)]
public byte Byte14;
[FieldOffset(15)]
public byte Byte15;
[FieldOffset(0)]
public decimal Value;
}
[StructLayout(LayoutKind.Explicit)]
private struct GuidByteUnion
{
[FieldOffset(0)]
public byte Byte0;
[FieldOffset(1)]
public byte Byte1;
[FieldOffset(2)]
public byte Byte2;
[FieldOffset(3)]
public byte Byte3;
[FieldOffset(4)]
public byte Byte4;
[FieldOffset(5)]
public byte Byte5;
[FieldOffset(6)]
public byte Byte6;
[FieldOffset(7)]
public byte Byte7;
[FieldOffset(8)]
public byte Byte8;
[FieldOffset(9)]
public byte Byte9;
[FieldOffset(10)]
public byte Byte10;
[FieldOffset(11)]
public byte Byte11;
[FieldOffset(12)]
public byte Byte12;
[FieldOffset(13)]
public byte Byte13;
[FieldOffset(14)]
public byte Byte14;
[FieldOffset(15)]
public byte Byte15;
[FieldOffset(0)]
public Guid Value;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 989e99cd5b8f922edc1b13bbc22f4289
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,35 @@
//-----------------------------------------------------------------------
// <copyright file="RegisterFormatterAttribute.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
{
using System;
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public class RegisterFormatterAttribute : Attribute
{
public Type FormatterType { get; private set; }
public int Priority { get; private set; }
public RegisterFormatterAttribute(Type formatterType, int priority = 0)
{
this.FormatterType = formatterType;
this.Priority = priority;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 82702718797409c332f9174bdad57bbc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,35 @@
//-----------------------------------------------------------------------
// <copyright file="RegisterFormatterLocatorAttribute.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
{
using System;
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public class RegisterFormatterLocatorAttribute : Attribute
{
public Type FormatterLocatorType { get; private set; }
public int Priority { get; private set; }
public RegisterFormatterLocatorAttribute(Type formatterLocatorType, int priority = 0)
{
this.FormatterLocatorType = formatterLocatorType;
this.Priority = priority;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a000ffc63858a974eb63d9ef6f91adac
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,47 @@
//-----------------------------------------------------------------------
// <copyright file="SerializationOptions.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>
//-----------------------------------------------------------------------
using System;
namespace VRC.Udon.Serialization.OdinSerializer
{
/// <summary>
/// An exception thrown when the serialization system has encountered an issue so severe that serialization is being aborted. If this exception is caught in the serialization system somewhere, it should be rethrown.
/// </summary>
public class SerializationAbortException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="SerializationException"/> class.
/// </summary>
/// <param name="message">The message.</param>
public SerializationAbortException(string message)
: base(message)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SerializationException"/> class.
/// </summary>
/// <param name="message">The message.</param>
/// <param name="innerException">The inner exception.</param>
public SerializationAbortException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dca124a461001ad1494664ed95539612
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,266 @@
//-----------------------------------------------------------------------
// <copyright file="SerializationOptions.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>
//-----------------------------------------------------------------------
using System;
namespace VRC.Udon.Serialization.OdinSerializer
{
/// <summary>
/// Defines the configuration during serialization and deserialization. This class is thread-safe.
/// </summary>
public class SerializationConfig
{
private readonly object LOCK = new object();
private volatile ISerializationPolicy serializationPolicy;
private volatile DebugContext debugContext;
/// <summary>
/// Initializes a new instance of the <see cref="SerializationConfig"/> class.
/// </summary>
public SerializationConfig()
{
this.ResetToDefault();
}
/// <summary>
/// <para>
/// Setting this member to true indicates that in the case where, when expecting to deserialize an instance of a certain type,
/// but encountering an incompatible, uncastable type in the data being read, the serializer should attempt to deserialize an
/// instance of the expected type using the stored, possibly invalid data.
/// </para>
/// <para>
/// This is equivalent to applying the <see cref="SerializationConfig.AllowDeserializeInvalidData"/> attribute, except global
/// instead of specific to a single type. Note that if this member is set to false, individual types may still be deserialized
/// with invalid data if they are decorated with the <see cref="SerializationConfig.AllowDeserializeInvalidData"/> attribute.
/// </para>
/// </summary>
public bool AllowDeserializeInvalidData = false;
/// <summary>
/// Gets or sets the serialization policy. This value is never null; if set to null, it will default to <see cref="SerializationPolicies.Unity"/>.
/// </summary>
/// <value>
/// The serialization policy.
/// </value>
public ISerializationPolicy SerializationPolicy
{
get
{
if (this.serializationPolicy == null)
{
lock (this.LOCK)
{
if (this.serializationPolicy == null)
{
this.serializationPolicy = SerializationPolicies.Unity;
}
}
}
return this.serializationPolicy;
}
set
{
lock (this.LOCK)
{
this.serializationPolicy = value;
}
}
}
/// <summary>
/// Gets or sets the debug context. This value is never null; if set to null, a new default instance of <see cref="DebugContext"/> will be created upon the next get.
/// </summary>
/// <value>
/// The debug context.
/// </value>
public DebugContext DebugContext
{
get
{
if (this.debugContext == null)
{
lock (this.LOCK)
{
if (this.debugContext == null)
{
this.debugContext = new DebugContext();
}
}
}
return this.debugContext;
}
set
{
lock (this.LOCK)
{
this.debugContext = value;
}
}
}
/// <summary>
/// Resets the configuration to a default configuration, as if the constructor had just been called.
/// </summary>
public void ResetToDefault()
{
lock (this.LOCK)
{
this.AllowDeserializeInvalidData = false;
this.serializationPolicy = null;
if (!object.ReferenceEquals(this.debugContext, null))
{
this.debugContext.ResetToDefault();
}
}
}
}
/// <summary>
/// Defines a context for debugging and logging during serialization and deserialization. This class is thread-safe.
/// </summary>
public sealed class DebugContext
{
private readonly object LOCK = new object();
private volatile ILogger logger;
private volatile LoggingPolicy loggingPolicy;
private volatile ErrorHandlingPolicy errorHandlingPolicy;
/// <summary>
/// The logger to use for logging messages.
/// </summary>
public ILogger Logger
{
get
{
if (this.logger == null)
{
lock (this.LOCK)
{
if (this.logger == null)
{
this.logger = DefaultLoggers.UnityLogger;
}
}
}
return this.logger;
}
set
{
lock (this.LOCK)
{
this.logger = value;
}
}
}
/// <summary>
/// The logging policy to use.
/// </summary>
public LoggingPolicy LoggingPolicy
{
get { return this.loggingPolicy; }
set { this.loggingPolicy = value; }
}
/// <summary>
/// The error handling policy to use.
/// </summary>
public ErrorHandlingPolicy ErrorHandlingPolicy
{
get { return this.errorHandlingPolicy; }
set { this.errorHandlingPolicy = value; }
}
/// <summary>
/// Log a warning. Depending on the logging policy and error handling policy, this message may be suppressed or result in an exception being thrown.
/// </summary>
public void LogWarning(string message)
{
if (this.errorHandlingPolicy == ErrorHandlingPolicy.ThrowOnWarningsAndErrors)
{
throw new SerializationAbortException("The following warning was logged during serialization or deserialization: " + (message ?? "EMPTY EXCEPTION MESSAGE"));
}
if (this.loggingPolicy == LoggingPolicy.LogWarningsAndErrors)
{
this.Logger.LogWarning(message);
}
}
/// <summary>
/// Log an error. Depending on the logging policy and error handling policy, this message may be suppressed or result in an exception being thrown.
/// </summary>
public void LogError(string message)
{
if (this.errorHandlingPolicy != ErrorHandlingPolicy.Resilient)
{
throw new SerializationAbortException("The following error was logged during serialization or deserialization: " + (message ?? "EMPTY EXCEPTION MESSAGE"));
}
if (this.loggingPolicy != LoggingPolicy.Silent)
{
this.Logger.LogError(message);
}
}
/// <summary>
/// Log an exception. Depending on the logging policy and error handling policy, this message may be suppressed or result in an exception being thrown.
/// </summary>
public void LogException(Exception exception)
{
if (exception == null)
{
throw new ArgumentNullException("exception");
}
// We must always rethrow abort exceptions
if (exception is SerializationAbortException)
{
throw exception;
}
var policy = this.errorHandlingPolicy;
if (policy != ErrorHandlingPolicy.Resilient)
{
throw new SerializationAbortException("An exception of type " + exception.GetType().Name + " occurred during serialization or deserialization.", exception);
}
if (this.loggingPolicy != LoggingPolicy.Silent)
{
this.Logger.LogException(exception);
}
}
public void ResetToDefault()
{
lock (LOCK)
{
this.logger = null;
this.loggingPolicy = default(LoggingPolicy);
this.errorHandlingPolicy = default(ErrorHandlingPolicy);
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: eba33c8e77e2084c660af46be1b547dd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,318 @@
//-----------------------------------------------------------------------
// <copyright file="SerializationContext.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
{
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using Utilities;
/// <summary>
/// The context of a given serialization session. This class maintains all internal and external references during serialization.
/// </summary>
/// <seealso cref="ICacheNotificationReceiver" />
public sealed class SerializationContext : ICacheNotificationReceiver
{
private SerializationConfig config;
private Dictionary<object, int> internalReferenceIdMap = new Dictionary<object, int>(128, ReferenceEqualityComparer<object>.Default);
private StreamingContext streamingContext;
private IFormatterConverter formatterConverter;
private TwoWaySerializationBinder binder;
/// <summary>
/// Initializes a new instance of the <see cref="SerializationContext"/> class.
/// </summary>
public SerializationContext()
: this(new StreamingContext(), new FormatterConverter())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SerializationContext"/> class.
/// </summary>
/// <param name="context">The streaming context to use.</param>
public SerializationContext(StreamingContext context)
: this(context, new FormatterConverter())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SerializationContext"/> class.
/// </summary>
/// <param name="formatterConverter">The formatter converter to use.</param>
public SerializationContext(FormatterConverter formatterConverter)
: this(new StreamingContext(), formatterConverter)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SerializationContext"/> class.
/// </summary>
/// <param name="context">The streaming context to use.</param>
/// <param name="formatterConverter">The formatter converter to use.</param>
/// <exception cref="System.ArgumentNullException">The formatterConverter parameter is null.</exception>
public SerializationContext(StreamingContext context, FormatterConverter formatterConverter)
{
if (formatterConverter == null)
{
throw new ArgumentNullException("formatterConverter");
}
this.streamingContext = context;
this.formatterConverter = formatterConverter;
this.ResetToDefault();
}
/// <summary>
/// Gets or sets the context's type binder.
/// </summary>
/// <value>
/// The context's serialization binder.
/// </value>
public TwoWaySerializationBinder Binder
{
get
{
if (this.binder == null)
{
this.binder = DefaultSerializationBinder.Default;
}
return this.binder;
}
set
{
this.binder = value;
}
}
/// <summary>
/// Gets the streaming context.
/// </summary>
/// <value>
/// The streaming context.
/// </value>
public StreamingContext StreamingContext { get { return this.streamingContext; } }
/// <summary>
/// Gets the formatter converter.
/// </summary>
/// <value>
/// The formatter converter.
/// </value>
public IFormatterConverter FormatterConverter { get { return this.formatterConverter; } }
/// <summary>
/// Gets or sets the index reference resolver.
/// </summary>
/// <value>
/// The index reference resolver.
/// </value>
public IExternalIndexReferenceResolver IndexReferenceResolver { get; set; }
/// <summary>
/// Gets or sets the string reference resolver.
/// </summary>
/// <value>
/// The string reference resolver.
/// </value>
public IExternalStringReferenceResolver StringReferenceResolver { get; set; }
/// <summary>
/// Gets or sets the Guid reference resolver.
/// </summary>
/// <value>
/// The Guid reference resolver.
/// </value>
public IExternalGuidReferenceResolver GuidReferenceResolver { get; set; }
/// <summary>
/// Gets or sets the serialization configuration.
/// </summary>
/// <value>
/// The serialization configuration.
/// </value>
public SerializationConfig Config
{
get
{
if (this.config == null)
{
this.config = new SerializationConfig();
}
return this.config;
}
set
{
this.config = value;
}
}
/// <summary>
/// Tries to get the id of an internally referenced object.
/// </summary>
/// <param name="reference">The reference to get the id of.</param>
/// <param name="id">The id that was found, or -1 if no id was found.</param>
/// <returns><c>true</c> if a reference was found, otherwise <c>false</c>.</returns>
public bool TryGetInternalReferenceId(object reference, out int id)
{
return this.internalReferenceIdMap.TryGetValue(reference, out id);
}
/// <summary>
/// Tries to register an internal reference. Returns <c>true</c> if the reference was registered, otherwise, <c>false</c> when the reference has already been registered.
/// </summary>
/// <param name="reference">The reference to register.</param>
/// <param name="id">The id of the registered reference.</param>
/// <returns><c>true</c> if the reference was registered, otherwise, <c>false</c> when the reference has already been registered.</returns>
public bool TryRegisterInternalReference(object reference, out int id)
{
if (this.internalReferenceIdMap.TryGetValue(reference, out id) == false)
{
id = this.internalReferenceIdMap.Count;
this.internalReferenceIdMap.Add(reference, id);
return true;
}
return false;
}
/// <summary>
/// Tries to register an external index reference.
/// </summary>
/// <param name="obj">The object to reference.</param>
/// <param name="index">The index of the referenced object.</param>
/// <returns><c>true</c> if the object could be referenced by index; otherwise, <c>false</c>.</returns>
public bool TryRegisterExternalReference(object obj, out int index)
{
if (this.IndexReferenceResolver == null)
{
index = -1;
return false;
}
if (this.IndexReferenceResolver.CanReference(obj, out index))
{
return true;
}
index = -1;
return false;
}
/// <summary>
/// Tries to register an external guid reference.
/// </summary>
/// <param name="obj">The object to reference.</param>
/// <param name="guid">The guid of the referenced object.</param>
/// <returns><c>true</c> if the object could be referenced by guid; otherwise, <c>false</c>.</returns>
public bool TryRegisterExternalReference(object obj, out Guid guid)
{
if (this.GuidReferenceResolver == null)
{
guid = Guid.Empty;
return false;
}
var resolver = this.GuidReferenceResolver;
while (resolver != null)
{
if (resolver.CanReference(obj, out guid))
{
return true;
}
resolver = resolver.NextResolver;
}
guid = Guid.Empty;
return false;
}
/// <summary>
/// Tries to register an external string reference.
/// </summary>
/// <param name="obj">The object to reference.</param>
/// <param name="id">The id string of the referenced object.</param>
/// <returns><c>true</c> if the object could be referenced by string; otherwise, <c>false</c>.</returns>
public bool TryRegisterExternalReference(object obj, out string id)
{
if (this.StringReferenceResolver == null)
{
id = null;
return false;
}
var resolver = this.StringReferenceResolver;
while (resolver != null)
{
if (resolver.CanReference(obj, out id))
{
return true;
}
resolver = resolver.NextResolver;
}
id = null;
return false;
}
/// <summary>
/// Resets the context's internal reference map.
/// </summary>
public void ResetInternalReferences()
{
this.internalReferenceIdMap.Clear();
}
/// <summary>
/// Resets the serialization context completely to baseline status, as if its constructor has just been called.
/// This allows complete reuse of a serialization context, with all of its internal reference buffers.
/// </summary>
public void ResetToDefault()
{
if (!object.ReferenceEquals(this.config, null))
{
this.config.ResetToDefault();
}
this.internalReferenceIdMap.Clear();
this.IndexReferenceResolver = null;
this.GuidReferenceResolver = null;
this.StringReferenceResolver = null;
this.binder = null;
}
void ICacheNotificationReceiver.OnFreed()
{
this.ResetToDefault();
}
void ICacheNotificationReceiver.OnClaimed()
{
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1e93880e733f9a6a084cf4061634e7fb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,202 @@
//-----------------------------------------------------------------------
// <copyright file="SerializationPolicies.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
{
using Utilities;
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using UnityEngine;
/// <summary>
/// Contains a set of default implementations of the <see cref="ISerializationPolicy"/> interface.
/// <para />
/// NOTE: Policies are not necessarily compatible with each other in intuitive ways.
/// Data serialized with the <see cref="SerializationPolicies.Everything"/> policy
/// will for example fail to deserialize auto-properties with <see cref="SerializationPolicies.Strict"/>,
/// even if only strict data is needed.
/// It is best to ensure that you always use the same policy for serialization and deserialization.
/// <para />
/// This class and all of its policies are thread-safe.
/// </summary>
public static class SerializationPolicies
{
private static readonly object LOCK = new object();
private static volatile ISerializationPolicy everythingPolicy;
private static volatile ISerializationPolicy unityPolicy;
private static volatile ISerializationPolicy strictPolicy;
/// <summary>
/// Tries to get a serialization policy by its id, in case a serialization graph has the policy used for serialization stored by name.
/// </summary>
public static bool TryGetByID(string name, out ISerializationPolicy policy)
{
switch (name)
{
case "OdinSerializerPolicies.Everything":
policy = SerializationPolicies.Everything;
break;
case "OdinSerializerPolicies.Unity":
policy = SerializationPolicies.Unity;
break;
case "OdinSerializerPolicies.Strict":
policy = SerializationPolicies.Strict;
break;
default:
policy = null;
break;
}
return policy != null;
}
/// <summary>
/// All fields not marked with <see cref="NonSerializedAttribute"/> are serialized. If a field is marked with both <see cref="NonSerializedAttribute"/> and <see cref="OdinSerializeAttribute"/>, then the field will be serialized.
/// </summary>
public static ISerializationPolicy Everything
{
get
{
if (everythingPolicy == null)
{
lock (LOCK)
{
if (everythingPolicy == null)
{
everythingPolicy = new CustomSerializationPolicy("OdinSerializerPolicies.Everything", true, (member) =>
{
if (!(member is FieldInfo))
{
return false;
}
if (member.IsDefined<OdinSerializeAttribute>(true))
{
return true;
}
return !member.IsDefined<NonSerializedAttribute>(true);
});
}
}
}
return everythingPolicy;
}
}
/// <summary>
/// Public fields, as well as fields or auto-properties marked with <see cref="SerializeField"/> or <see cref="OdinSerializeAttribute"/> and not marked with <see cref="NonSerializedAttribute"/>, are serialized.
/// <para />
/// There are two exceptions:
/// <para/>1) All fields in tuples, as well as in private nested types marked as compiler generated (e.g. lambda capture classes) are also serialized.
/// <para/>2) Virtual auto-properties are never serialized. Note that properties specified by an implemented interface are automatically marked virtual by the compiler.
/// </summary>
public static ISerializationPolicy Unity
{
get
{
if (unityPolicy == null)
{
lock (LOCK)
{
if (unityPolicy == null)
{
// In Unity 2017.1's .NET 4.6 profile, Tuples implement System.ITuple. In Unity 2017.2 and up, tuples implement System.ITupleInternal instead for some reason.
Type tupleInterface = typeof(string).Assembly.GetType("System.ITuple") ?? typeof(string).Assembly.GetType("System.ITupleInternal");
unityPolicy = new CustomSerializationPolicy("OdinSerializerPolicies.Unity", true, (member) =>
{
// Non-auto properties are never supported.
if (member is PropertyInfo && ((PropertyInfo)member).IsAutoProperty() == false)
{
return false;
}
// If OdinSerializeAttribute is defined, NonSerializedAttribute is ignored.
// This enables users to ignore Unity's infinite serialization depth warnings.
if (member.IsDefined<NonSerializedAttribute>(true) && !member.IsDefined<OdinSerializeAttribute>())
{
return false;
}
if (member is FieldInfo && ((member as FieldInfo).IsPublic || (member.DeclaringType.IsNestedPrivate && member.DeclaringType.IsDefined<CompilerGeneratedAttribute>()) || (tupleInterface != null && tupleInterface.IsAssignableFrom(member.DeclaringType))))
{
return true;
}
return member.IsDefined<SerializeField>(true) || member.IsDefined<OdinSerializeAttribute>(true);
});
}
}
}
return unityPolicy;
}
}
/// <summary>
/// Only fields and auto-properties marked with <see cref="SerializeField"/> or <see cref="OdinSerializeAttribute"/> and not marked with <see cref="NonSerializedAttribute"/> are serialized.
/// <para />
/// There are two exceptions:
/// <para/>1) All fields in private nested types marked as compiler generated (e.g. lambda capture classes) are also serialized.
/// <para/>2) Virtual auto-properties are never serialized. Note that properties specified by an implemented interface are automatically marked virtual by the compiler.
/// </summary>
public static ISerializationPolicy Strict
{
get
{
if (strictPolicy == null)
{
lock (LOCK)
{
if (strictPolicy == null)
{
strictPolicy = new CustomSerializationPolicy("OdinSerializerPolicies.Strict", true, (member) =>
{
// Non-auto properties are never supported.
if (member is PropertyInfo && ((PropertyInfo)member).IsAutoProperty() == false)
{
return false;
}
if (member.IsDefined<NonSerializedAttribute>())
{
return false;
}
if (member is FieldInfo && member.DeclaringType.IsNestedPrivate && member.DeclaringType.IsDefined<CompilerGeneratedAttribute>())
{
return true;
}
return member.IsDefined<SerializeField>(true) || member.IsDefined<OdinSerializeAttribute>(true);
});
}
}
}
return strictPolicy;
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 67a19021ff9e6b27d8e9257ad075055a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,747 @@
//-----------------------------------------------------------------------
// <copyright file="SerializationUtility.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
{
using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using Utilities;
/// <summary>
/// Provides an array of utility wrapper methods for easy serialization and deserialization of objects of any type.
/// </summary>
public static class SerializationUtility
{
/// <summary>
/// Creates an <see cref="IDataWriter" /> for a given format.
/// </summary>
/// <param name="stream">The stream to write to.</param>
/// <param name="context">The serialization context to use.</param>
/// <param name="format">The format to write.</param>
/// <returns>
/// An <see cref="IDataWriter" /> for a given format.
/// </returns>
/// <exception cref="System.NotImplementedException"></exception>
public static IDataWriter CreateWriter(Stream stream, SerializationContext context, DataFormat format)
{
switch (format)
{
case DataFormat.Binary:
return new BinaryDataWriter(stream, context);
case DataFormat.JSON:
return new JsonDataWriter(stream, context);
case DataFormat.Nodes:
Debug.LogError("Cannot automatically create a writer for the format '" + DataFormat.Nodes + "', because it does not use a stream.");
return null;
default:
throw new NotImplementedException(format.ToString());
}
}
/// <summary>
/// Creates an <see cref="IDataReader" /> for a given format.
/// </summary>
/// <param name="stream">The stream to read from.</param>
/// <param name="context">The deserialization context to use.</param>
/// <param name="format">The format to read.</param>
/// <returns>
/// An <see cref="IDataReader" /> for a given format.
/// </returns>
/// <exception cref="System.NotImplementedException"></exception>
public static IDataReader CreateReader(Stream stream, DeserializationContext context, DataFormat format)
{
switch (format)
{
case DataFormat.Binary:
return new BinaryDataReader(stream, context);
case DataFormat.JSON:
return new JsonDataReader(stream, context);
case DataFormat.Nodes:
Debug.LogError("Cannot automatically create a reader for the format '" + DataFormat.Nodes + "', because it does not use a stream.");
return null;
default:
throw new NotImplementedException(format.ToString());
}
}
private static IDataWriter GetCachedWriter(out IDisposable cache, DataFormat format, Stream stream, SerializationContext context)
{
IDataWriter writer;
if (format == DataFormat.Binary)
{
var binaryCache = Cache<BinaryDataWriter>.Claim();
var binaryWriter = binaryCache.Value;
binaryWriter.Stream = stream;
binaryWriter.Context = context;
binaryWriter.PrepareNewSerializationSession();
writer = binaryWriter;
cache = binaryCache;
}
else if (format == DataFormat.JSON)
{
var jsonCache = Cache<JsonDataWriter>.Claim();
var jsonWriter = jsonCache.Value;
jsonWriter.Stream = stream;
jsonWriter.Context = context;
jsonWriter.PrepareNewSerializationSession();
writer = jsonWriter;
cache = jsonCache;
}
else if (format == DataFormat.Nodes)
{
throw new InvalidOperationException("Cannot automatically create a writer for the format '" + DataFormat.Nodes + "', because it does not use a stream.");
}
else
{
throw new NotImplementedException(format.ToString());
}
return writer;
}
private static IDataReader GetCachedReader(out IDisposable cache, DataFormat format, Stream stream, DeserializationContext context)
{
IDataReader reader;
if (format == DataFormat.Binary)
{
var binaryCache = Cache<BinaryDataReader>.Claim();
var binaryReader = binaryCache.Value;
binaryReader.Stream = stream;
binaryReader.Context = context;
binaryReader.PrepareNewSerializationSession();
reader = binaryReader;
cache = binaryCache;
}
else if (format == DataFormat.JSON)
{
var jsonCache = Cache<JsonDataReader>.Claim();
var jsonReader = jsonCache.Value;
jsonReader.Stream = stream;
jsonReader.Context = context;
jsonReader.PrepareNewSerializationSession();
reader = jsonReader;
cache = jsonCache;
}
else if (format == DataFormat.Nodes)
{
throw new InvalidOperationException("Cannot automatically create a reader for the format '" + DataFormat.Nodes + "', because it does not use a stream.");
}
else
{
throw new NotImplementedException(format.ToString());
}
return reader;
}
/// <summary>
/// Serializes the given value using the given writer.
/// </summary>
/// <param name="value">The value to serialize.</param>
/// <param name="writer">The writer to use.</param>
public static void SerializeValueWeak(object value, IDataWriter writer)
{
Serializer.GetForValue(value).WriteValueWeak(value, writer);
writer.FlushToStream();
}
/// <summary>
/// Serializes the given value, using the given writer.
/// </summary>
/// <param name="value">The value to serialize.</param>
/// <param name="writer">The writer to use.</param>
/// <param name="unityObjects">A list of the Unity objects which were referenced during serialization.</param>
public static void SerializeValueWeak(object value, IDataWriter writer, out List<UnityEngine.Object> unityObjects)
{
using (var unityResolver = Cache<UnityReferenceResolver>.Claim())
{
writer.Context.IndexReferenceResolver = unityResolver.Value;
Serializer.GetForValue(value).WriteValueWeak(value, writer);
writer.FlushToStream();
unityObjects = unityResolver.Value.GetReferencedUnityObjects();
}
}
/// <summary>
/// Serializes the given value using the given writer.
/// </summary>
/// <typeparam name="T">The type of the value to serialize.</typeparam>
/// <param name="value">The value to serialize.</param>
/// <param name="writer">The writer to use.</param>
public static void SerializeValue<T>(T value, IDataWriter writer)
{
Serializer.Get<T>().WriteValue(value, writer);
writer.FlushToStream();
}
/// <summary>
/// Serializes the given value, using the given writer.
/// </summary>
/// <typeparam name="T">The type of the value to serialize.</typeparam>
/// <param name="value">The value to serialize.</param>
/// <param name="writer">The writer to use.</param>
/// <param name="unityObjects">A list of the Unity objects which were referenced during serialization.</param>
public static void SerializeValue<T>(T value, IDataWriter writer, out List<UnityEngine.Object> unityObjects)
{
using (var unityResolver = Cache<UnityReferenceResolver>.Claim())
{
writer.Context.IndexReferenceResolver = unityResolver.Value;
Serializer.Get<T>().WriteValue(value, writer);
writer.FlushToStream();
unityObjects = unityResolver.Value.GetReferencedUnityObjects();
}
}
/// <summary>
/// Serializes the given value to a given stream in the specified format.
/// </summary>
/// <param name="value">The value to serialize.</param>
/// <param name="stream">The stream to serialize to.</param>
/// <param name="format">The format to serialize in.</param>
/// <param name="context">The context.</param>
public static void SerializeValueWeak(object value, Stream stream, DataFormat format, SerializationContext context = null)
{
IDisposable cache;
var writer = GetCachedWriter(out cache, format, stream, context);
try
{
if (context != null)
{
SerializeValueWeak(value, writer);
}
else
{
using (var con = Cache<SerializationContext>.Claim())
{
writer.Context = con;
SerializeValueWeak(value, writer);
}
}
}
finally
{
cache.Dispose();
}
}
/// <summary>
/// Serializes the given value to a given stream in the specified format.
/// </summary>
/// <param name="value">The value to serialize.</param>
/// <param name="stream">The stream to serialize to.</param>
/// <param name="format">The format to serialize in.</param>
/// <param name="unityObjects">A list of the Unity objects which were referenced during serialization.</param>
/// <param name="context">The context.</param>
public static void SerializeValueWeak(object value, Stream stream, DataFormat format, out List<UnityEngine.Object> unityObjects, SerializationContext context = null)
{
IDisposable cache;
var writer = GetCachedWriter(out cache, format, stream, context);
try
{
if (context != null)
{
SerializeValueWeak(value, writer, out unityObjects);
}
else
{
using (var con = Cache<SerializationContext>.Claim())
{
writer.Context = con;
SerializeValueWeak(value, writer, out unityObjects);
}
}
}
finally
{
cache.Dispose();
}
}
/// <summary>
/// Serializes the given value to a given stream in the specified format.
/// </summary>
/// <typeparam name="T">The type of the value to serialize.</typeparam>
/// <param name="value">The value to serialize.</param>
/// <param name="stream">The stream to serialize to.</param>
/// <param name="format">The format to serialize in.</param>
/// <param name="context">The context.</param>
public static void SerializeValue<T>(T value, Stream stream, DataFormat format, SerializationContext context = null)
{
IDisposable cache;
var writer = GetCachedWriter(out cache, format, stream, context);
try
{
if (context != null)
{
SerializeValue(value, writer);
}
else
{
using (var con = Cache<SerializationContext>.Claim())
{
writer.Context = con;
SerializeValue(value, writer);
}
}
}
finally
{
cache.Dispose();
}
}
/// <summary>
/// Serializes the given value to a given stream in the specified format.
/// </summary>
/// <typeparam name="T">The type of the value to serialize.</typeparam>
/// <param name="value">The value to serialize.</param>
/// <param name="stream">The stream to serialize to.</param>
/// <param name="format">The format to serialize in.</param>
/// <param name="unityObjects">A list of the Unity objects which were referenced during serialization.</param>
/// <param name="context">The context.</param>
public static void SerializeValue<T>(T value, Stream stream, DataFormat format, out List<UnityEngine.Object> unityObjects, SerializationContext context = null)
{
IDisposable cache;
var writer = GetCachedWriter(out cache, format, stream, context);
try
{
if (context != null)
{
SerializeValue(value, writer, out unityObjects);
}
else
{
using (var con = Cache<SerializationContext>.Claim())
{
writer.Context = con;
SerializeValue(value, writer, out unityObjects);
}
}
}
finally
{
cache.Dispose();
}
}
/// <summary>
/// Serializes the given value using the specified format, and returns the result as a byte array.
/// </summary>
/// <param name="value">The value to serialize.</param>
/// <param name="format">The format to use.</param>
/// <param name="context">The context.</param>
/// <returns>A byte array containing the serialized value.</returns>
public static byte[] SerializeValueWeak(object value, DataFormat format, SerializationContext context = null)
{
using (var stream = CachedMemoryStream.Claim())
{
SerializeValueWeak(value, stream.Value.MemoryStream, format, context);
return stream.Value.MemoryStream.ToArray();
}
}
/// <summary>
/// Serializes the given value using the specified format, and returns the result as a byte array.
/// </summary>
/// <param name="value">The value to serialize.</param>
/// <param name="format">The format to use.</param>
/// <param name="unityObjects">A list of the Unity objects which were referenced during serialization.</param>
/// <returns>A byte array containing the serialized value.</returns>
public static byte[] SerializeValueWeak(object value, DataFormat format, out List<UnityEngine.Object> unityObjects)
{
using (var stream = CachedMemoryStream.Claim())
{
SerializeValueWeak(value, stream.Value.MemoryStream, format, out unityObjects);
return stream.Value.MemoryStream.ToArray();
}
}
/// <summary>
/// Serializes the given value using the specified format, and returns the result as a byte array.
/// </summary>
/// <typeparam name="T">The type of the value to serialize.</typeparam>
/// <param name="value">The value to serialize.</param>
/// <param name="format">The format to use.</param>
/// <param name="context">The context to use.</param>
/// <returns>A byte array containing the serialized value.</returns>
public static byte[] SerializeValue<T>(T value, DataFormat format, SerializationContext context = null)
{
using (var stream = CachedMemoryStream.Claim())
{
SerializeValue(value, stream.Value.MemoryStream, format, context);
return stream.Value.MemoryStream.ToArray();
}
}
/// <summary>
/// Serializes the given value using the specified format and returns the result as a byte array.
/// </summary>
/// <typeparam name="T">The type of the value to serialize.</typeparam>
/// <param name="value">The value to serialize.</param>
/// <param name="format">The format to use.</param>
/// <param name="unityObjects">A list of the Unity objects which were referenced during serialization.</param>
/// <param name="context">The context to use.</param>
/// <returns>A byte array containing the serialized value.</returns>
public static byte[] SerializeValue<T>(T value, DataFormat format, out List<UnityEngine.Object> unityObjects, SerializationContext context = null)
{
using (var stream = CachedMemoryStream.Claim())
{
SerializeValue(value, stream.Value.MemoryStream, format, out unityObjects, context);
return stream.Value.MemoryStream.ToArray();
}
}
/// <summary>
/// Deserializes a value from the given reader. This might fail with primitive values, as they don't come with metadata.
/// </summary>
/// <param name="reader">The reader to use.</param>
/// <returns>The deserialized value.</returns>
public static object DeserializeValueWeak(IDataReader reader)
{
return Serializer.Get<object>().ReadValueWeak(reader);
}
/// <summary>
/// Deserializes a value from the given reader, using the given list of Unity objects for external index reference resolution. This might fail with primitive values, as they don't come with type metadata.
/// </summary>
/// <param name="reader">The reader to use.</param>
/// <param name="referencedUnityObjects">The list of Unity objects to use for external index reference resolution.</param>
/// <returns>
/// The deserialized value.
/// </returns>
public static object DeserializeValueWeak(IDataReader reader, List<UnityEngine.Object> referencedUnityObjects)
{
using (var unityResolver = Cache<UnityReferenceResolver>.Claim())
{
unityResolver.Value.SetReferencedUnityObjects(referencedUnityObjects);
reader.Context.IndexReferenceResolver = unityResolver.Value;
return Serializer.Get<object>().ReadValueWeak(reader);
}
}
/// <summary>
/// Deserializes a value from the given reader.
/// </summary>
/// <typeparam name="T">The type to deserialize.</typeparam>
/// <param name="reader">The reader to use.</param>
/// <returns>The deserialized value.</returns>
public static T DeserializeValue<T>(IDataReader reader)
{
return Serializer.Get<T>().ReadValue(reader);
}
/// <summary>
/// Deserializes a value of a given type from the given reader, using the given list of Unity objects for external index reference resolution.
/// </summary>
/// <typeparam name="T">The type to deserialize.</typeparam>
/// <param name="reader">The reader to use.</param>
/// <param name="referencedUnityObjects">The list of Unity objects to use for external index reference resolution.</param>
/// <returns>
/// The deserialized value.
/// </returns>
public static T DeserializeValue<T>(IDataReader reader, List<UnityEngine.Object> referencedUnityObjects)
{
using (var unityResolver = Cache<UnityReferenceResolver>.Claim())
{
unityResolver.Value.SetReferencedUnityObjects(referencedUnityObjects);
reader.Context.IndexReferenceResolver = unityResolver.Value;
return Serializer.Get<T>().ReadValue(reader);
}
}
/// <summary>
/// Deserializes a value from the given stream in the given format. This might fail with primitive values, as they don't come with type metadata.
/// </summary>
/// <param name="stream">The reader to use.</param>
/// <param name="format">The format to read.</param>
/// <param name="context">The context.</param>
/// <returns>
/// The deserialized value.
/// </returns>
public static object DeserializeValueWeak(Stream stream, DataFormat format, DeserializationContext context = null)
{
IDisposable cache;
var reader = GetCachedReader(out cache, format, stream, context);
try
{
if (context != null)
{
return DeserializeValueWeak(reader);
}
else
{
using (var con = Cache<DeserializationContext>.Claim())
{
reader.Context = con;
return DeserializeValueWeak(reader);
}
}
}
finally
{
cache.Dispose();
}
}
/// <summary>
/// Deserializes a value from the given stream in the given format, using the given list of Unity objects for external index reference resolution. This might fail with primitive values, as they don't come with type metadata.
/// </summary>
/// <param name="stream">The stream to read from.</param>
/// <param name="format">The format to read.</param>
/// <param name="referencedUnityObjects">The list of Unity objects to use for external index reference resolution.</param>
/// <param name="context">The context.</param>
/// <returns>
/// The deserialized value.
/// </returns>
public static object DeserializeValueWeak(Stream stream, DataFormat format, List<UnityEngine.Object> referencedUnityObjects, DeserializationContext context = null)
{
IDisposable cache;
var reader = GetCachedReader(out cache, format, stream, context);
try
{
if (context != null)
{
return DeserializeValueWeak(reader, referencedUnityObjects);
}
else
{
using (var con = Cache<DeserializationContext>.Claim())
{
reader.Context = con;
return DeserializeValueWeak(reader, referencedUnityObjects);
}
}
}
finally
{
cache.Dispose();
}
}
/// <summary>
/// Deserializes a value of a given type from the given stream in the given format.
/// </summary>
/// <typeparam name="T">The type to deserialize.</typeparam>
/// <param name="stream">The stream to read from.</param>
/// <param name="format">The format to read.</param>
/// <param name="context">The context.</param>
/// <returns>
/// The deserialized value.
/// </returns>
public static T DeserializeValue<T>(Stream stream, DataFormat format, DeserializationContext context = null)
{
IDisposable cache;
var reader = GetCachedReader(out cache, format, stream, context);
try
{
if (context != null)
{
return DeserializeValue<T>(reader);
}
else
{
using (var con = Cache<DeserializationContext>.Claim())
{
reader.Context = con;
return DeserializeValue<T>(reader);
}
}
}
finally
{
cache.Dispose();
}
}
/// <summary>
/// Deserializes a value of a given type from the given stream in the given format, using the given list of Unity objects for external index reference resolution.
/// </summary>
/// <typeparam name="T">The type to deserialize.</typeparam>
/// <param name="stream">The stream to read from.</param>
/// <param name="format">The format to read.</param>
/// <param name="referencedUnityObjects">The list of Unity objects to use for external index reference resolution.</param>
/// <param name="context">The context.</param>
/// <returns>
/// The deserialized value.
/// </returns>
public static T DeserializeValue<T>(Stream stream, DataFormat format, List<UnityEngine.Object> referencedUnityObjects, DeserializationContext context = null)
{
IDisposable cache;
var reader = GetCachedReader(out cache, format, stream, context);
try
{
if (context != null)
{
return DeserializeValue<T>(reader, referencedUnityObjects);
}
else
{
using (var con = Cache<DeserializationContext>.Claim())
{
reader.Context = con;
return DeserializeValue<T>(reader, referencedUnityObjects);
}
}
}
finally
{
cache.Dispose();
}
}
/// <summary>
/// Deserializes a value from the given byte array in the given format. This might fail with primitive values, as they don't come with type metadata.
/// </summary>
/// <param name="bytes">The bytes to deserialize from.</param>
/// <param name="format">The format to read.</param>
/// <param name="context">The context.</param>
/// <returns>
/// The deserialized value.
/// </returns>
public static object DeserializeValueWeak(byte[] bytes, DataFormat format, DeserializationContext context = null)
{
using (var stream = CachedMemoryStream.Claim(bytes))
{
return DeserializeValueWeak(stream.Value.MemoryStream, format, context);
}
}
/// <summary>
/// Deserializes a value from the given byte array in the given format, using the given list of Unity objects for external index reference resolution. This might fail with primitive values, as they don't come with type metadata.
/// </summary>
/// <param name="bytes">The bytes to deserialize from.</param>
/// <param name="format">The format to read.</param>
/// <param name="referencedUnityObjects">The list of Unity objects to use for external index reference resolution.</param>
/// <returns>
/// The deserialized value.
/// </returns>
public static object DeserializeValueWeak(byte[] bytes, DataFormat format, List<UnityEngine.Object> referencedUnityObjects)
{
using (var stream = CachedMemoryStream.Claim(bytes))
{
return DeserializeValueWeak(stream.Value.MemoryStream, format, referencedUnityObjects);
}
}
/// <summary>
/// Deserializes a value of a given type from the given byte array in the given format.
/// </summary>
/// <typeparam name="T">The type to deserialize.</typeparam>
/// <param name="bytes">The bytes to deserialize from.</param>
/// <param name="format">The format to read.</param>
/// <param name="context">The context to use.</param>
/// <returns>
/// The deserialized value.
/// </returns>
public static T DeserializeValue<T>(byte[] bytes, DataFormat format, DeserializationContext context = null)
{
using (var stream = CachedMemoryStream.Claim(bytes))
{
return DeserializeValue<T>(stream.Value.MemoryStream, format, context);
}
}
/// <summary>
/// Deserializes a value of a given type from the given byte array in the given format, using the given list of Unity objects for external index reference resolution.
/// </summary>
/// <typeparam name="T">The type to deserialize.</typeparam>
/// <param name="bytes">The bytes to deserialize from.</param>
/// <param name="format">The format to read.</param>
/// <param name="referencedUnityObjects">The list of Unity objects to use for external index reference resolution.</param>
/// <param name="context">The context to use.</param>
/// <returns>
/// The deserialized value.
/// </returns>
public static T DeserializeValue<T>(byte[] bytes, DataFormat format, List<UnityEngine.Object> referencedUnityObjects, DeserializationContext context = null)
{
using (var stream = CachedMemoryStream.Claim(bytes))
{
return DeserializeValue<T>(stream.Value.MemoryStream, format, referencedUnityObjects, context);
}
}
/// <summary>
/// Creates a deep copy of an object. Returns null if null. All Unity objects references will remain the same - they will not get copied.
/// </summary>
public static object CreateCopy(object obj)
{
if (obj == null)
{
return null;
}
if (obj as string != null)
{
return obj;
}
var type = obj.GetType();
if (type.IsValueType)
{
return obj;
}
if (type.InheritsFrom(typeof(UnityEngine.Object)))
{
return obj;
}
using (var stream = new System.IO.MemoryStream())
using (var serContext = Cache<SerializationContext>.Claim())
using (var deserContext = Cache<DeserializationContext>.Claim())
{
serContext.Value.Config.SerializationPolicy = SerializationPolicies.Everything;
deserContext.Value.Config.SerializationPolicy = SerializationPolicies.Everything;
List<UnityEngine.Object> unityReferences;
SerializeValue(obj, stream, DataFormat.Binary, out unityReferences, serContext);
stream.Position = 0;
return DeserializeValue<object>(stream, DataFormat.Binary, unityReferences, deserContext);
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 08607b6e9c39ec19c1b61341583c2f3e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,54 @@
//-----------------------------------------------------------------------
// <copyright file="TwoWaySerializationBinder.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
{
using System;
/// <summary>
/// Binds types to strings during serialization, and strings to types during deserialization.
/// </summary>
public abstract class TwoWaySerializationBinder
{
/// <summary>
/// Provides a default, catch-all <see cref="TwoWaySerializationBinder"/> implementation. This binder only includes assembly names, without versions and tokens, in order to increase compatibility.
/// </summary>
public static readonly TwoWaySerializationBinder Default = new DefaultSerializationBinder();
/// <summary>
/// Bind a type to a name.
/// </summary>
/// <param name="type">The type to bind.</param>
/// <param name="debugContext">The debug context to log to.</param>
/// <returns>The name that the type has been bound to.</returns>
public abstract string BindToName(Type type, DebugContext debugContext = null);
/// <summary>
/// Binds a name to a type.
/// </summary>
/// <param name="typeName">The name of the type to bind.</param>
/// <param name="debugContext">The debug context to log to.</param>
/// <returns>The type that the name has been bound to, or null if the type could not be resolved.</returns>
public abstract Type BindToType(string typeName, DebugContext debugContext = null);
/// <summary>
/// Determines whether the specified type name is mapped.
/// </summary>
public abstract bool ContainsType(string typeName);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bc69d8fd9d15913a491a45d1e040faf6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: