Initial Client/Server Communication
#1 Followed the MagicOnion quickstart guide, borrowing code from the chat sample app to get something working
This commit is contained in:
parent
52b0d4d8ff
commit
a0c8dbee72
493 changed files with 57790 additions and 3 deletions
306
Assets/Scripts/MessagePack/Internal/AsymmetricKeyHashTable.cs
Normal file
306
Assets/Scripts/MessagePack/Internal/AsymmetricKeyHashTable.cs
Normal file
|
|
@ -0,0 +1,306 @@
|
|||
// Copyright (c) All contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#if !UNITY_2018_3_OR_NEWER
|
||||
|
||||
using System;
|
||||
|
||||
#pragma warning disable SA1649 // File name should match first type name
|
||||
|
||||
namespace MessagePack.Internal
|
||||
{
|
||||
/* Safe for multiple-read, single-write.
|
||||
* Add and Get Key is asymmetric. */
|
||||
|
||||
internal interface IAsymmetricEqualityComparer<TKey1, TKey2>
|
||||
{
|
||||
int GetHashCode(TKey1 key1);
|
||||
|
||||
int GetHashCode(TKey2 key2);
|
||||
|
||||
bool Equals(TKey1 x, TKey1 y); // when used rehash
|
||||
|
||||
bool Equals(TKey1 x, TKey2 y); // when used get
|
||||
}
|
||||
|
||||
internal class StringArraySegmentByteAscymmetricEqualityComparer : IAsymmetricEqualityComparer<byte[], ArraySegment<byte>>
|
||||
{
|
||||
private static readonly bool Is32Bit = IntPtr.Size == 4;
|
||||
|
||||
public bool Equals(byte[] x, byte[] y)
|
||||
{
|
||||
if (x.Length != y.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < x.Length; i++)
|
||||
{
|
||||
if (x[i] != y[i])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Equals(byte[] x, ArraySegment<byte> y)
|
||||
{
|
||||
return x.AsSpan().SequenceEqual(y);
|
||||
}
|
||||
|
||||
public int GetHashCode(byte[] key1)
|
||||
{
|
||||
return this.GetHashCode(new ArraySegment<byte>(key1, 0, key1.Length));
|
||||
}
|
||||
|
||||
public int GetHashCode(ArraySegment<byte> key2)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
if (Is32Bit)
|
||||
{
|
||||
return (int)FarmHash.Hash32(key2);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (int)FarmHash.Hash64(key2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class AsymmetricKeyHashTable<TKey1, TKey2, TValue>
|
||||
{
|
||||
private Entry[] buckets;
|
||||
private int size; // only use in writer lock
|
||||
|
||||
private readonly object writerLock = new object();
|
||||
private readonly float loadFactor;
|
||||
private readonly IAsymmetricEqualityComparer<TKey1, TKey2> comparer;
|
||||
|
||||
public AsymmetricKeyHashTable(IAsymmetricEqualityComparer<TKey1, TKey2> comparer)
|
||||
: this(4, 0.72f, comparer)
|
||||
{
|
||||
}
|
||||
|
||||
public AsymmetricKeyHashTable(int capacity, float loadFactor, IAsymmetricEqualityComparer<TKey1, TKey2> comparer)
|
||||
{
|
||||
var tableSize = CalculateCapacity(capacity, loadFactor);
|
||||
this.buckets = new Entry[tableSize];
|
||||
this.loadFactor = loadFactor;
|
||||
this.comparer = comparer;
|
||||
}
|
||||
|
||||
public TValue AddOrGet(TKey1 key1, Func<TKey1, TValue> valueFactory)
|
||||
{
|
||||
TValue v;
|
||||
this.TryAddInternal(key1, valueFactory, out v);
|
||||
return v;
|
||||
}
|
||||
|
||||
public bool TryAdd(TKey1 key, TValue value)
|
||||
{
|
||||
return this.TryAdd(key, _ => value); // closure capture
|
||||
}
|
||||
|
||||
public bool TryAdd(TKey1 key, Func<TKey1, TValue> valueFactory)
|
||||
{
|
||||
return this.TryAddInternal(key, valueFactory, out TValue _);
|
||||
}
|
||||
|
||||
private bool TryAddInternal(TKey1 key, Func<TKey1, TValue> valueFactory, out TValue resultingValue)
|
||||
{
|
||||
lock (this.writerLock)
|
||||
{
|
||||
var nextCapacity = CalculateCapacity(this.size + 1, this.loadFactor);
|
||||
|
||||
if (this.buckets.Length < nextCapacity)
|
||||
{
|
||||
// rehash
|
||||
var nextBucket = new Entry[nextCapacity];
|
||||
for (int i = 0; i < this.buckets.Length; i++)
|
||||
{
|
||||
Entry e = this.buckets[i];
|
||||
while (e != null)
|
||||
{
|
||||
var newEntry = new Entry { Key = e.Key, Value = e.Value, Hash = e.Hash };
|
||||
this.AddToBuckets(nextBucket, key, newEntry, null, out resultingValue);
|
||||
e = e.Next;
|
||||
}
|
||||
}
|
||||
|
||||
// add entry(if failed to add, only do resize)
|
||||
var successAdd = this.AddToBuckets(nextBucket, key, null, valueFactory, out resultingValue);
|
||||
|
||||
// replace field(threadsafe for read)
|
||||
VolatileWrite(ref this.buckets, nextBucket);
|
||||
|
||||
if (successAdd)
|
||||
{
|
||||
this.size++;
|
||||
}
|
||||
|
||||
return successAdd;
|
||||
}
|
||||
else
|
||||
{
|
||||
// add entry(insert last is thread safe for read)
|
||||
var successAdd = this.AddToBuckets(this.buckets, key, null, valueFactory, out resultingValue);
|
||||
if (successAdd)
|
||||
{
|
||||
this.size++;
|
||||
}
|
||||
|
||||
return successAdd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool AddToBuckets(Entry[] buckets, TKey1 newKey, Entry newEntryOrNull, Func<TKey1, TValue> valueFactory, out TValue resultingValue)
|
||||
{
|
||||
var h = (newEntryOrNull != null) ? newEntryOrNull.Hash : this.comparer.GetHashCode(newKey);
|
||||
if (buckets[h & (buckets.Length - 1)] == null)
|
||||
{
|
||||
if (newEntryOrNull != null)
|
||||
{
|
||||
resultingValue = newEntryOrNull.Value;
|
||||
VolatileWrite(ref buckets[h & (buckets.Length - 1)], newEntryOrNull);
|
||||
}
|
||||
else
|
||||
{
|
||||
resultingValue = valueFactory(newKey);
|
||||
VolatileWrite(ref buckets[h & (buckets.Length - 1)], new Entry { Key = newKey, Value = resultingValue, Hash = h });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Entry searchLastEntry = buckets[h & (buckets.Length - 1)];
|
||||
while (true)
|
||||
{
|
||||
if (this.comparer.Equals(searchLastEntry.Key, newKey))
|
||||
{
|
||||
resultingValue = searchLastEntry.Value;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (searchLastEntry.Next == null)
|
||||
{
|
||||
if (newEntryOrNull != null)
|
||||
{
|
||||
resultingValue = newEntryOrNull.Value;
|
||||
VolatileWrite(ref searchLastEntry.Next, newEntryOrNull);
|
||||
}
|
||||
else
|
||||
{
|
||||
resultingValue = valueFactory(newKey);
|
||||
VolatileWrite(ref searchLastEntry.Next, new Entry { Key = newKey, Value = resultingValue, Hash = h });
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
searchLastEntry = searchLastEntry.Next;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey2 key, out TValue value)
|
||||
{
|
||||
Entry[] table = this.buckets;
|
||||
var hash = this.comparer.GetHashCode(key);
|
||||
Entry entry = table[hash & table.Length - 1];
|
||||
|
||||
if (entry == null)
|
||||
{
|
||||
goto NOT_FOUND;
|
||||
}
|
||||
|
||||
if (this.comparer.Equals(entry.Key, key))
|
||||
{
|
||||
value = entry.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
Entry next = entry.Next;
|
||||
while (next != null)
|
||||
{
|
||||
if (this.comparer.Equals(next.Key, key))
|
||||
{
|
||||
value = next.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
next = next.Next;
|
||||
}
|
||||
|
||||
NOT_FOUND:
|
||||
value = default(TValue);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int CalculateCapacity(int collectionSize, float loadFactor)
|
||||
{
|
||||
var initialCapacity = (int)(((float)collectionSize) / loadFactor);
|
||||
var capacity = 1;
|
||||
while (capacity < initialCapacity)
|
||||
{
|
||||
capacity <<= 1;
|
||||
}
|
||||
|
||||
if (capacity < 8)
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
return capacity;
|
||||
}
|
||||
|
||||
private static void VolatileWrite(ref Entry location, Entry value)
|
||||
{
|
||||
System.Threading.Volatile.Write(ref location, value);
|
||||
}
|
||||
|
||||
private static void VolatileWrite(ref Entry[] location, Entry[] value)
|
||||
{
|
||||
System.Threading.Volatile.Write(ref location, value);
|
||||
}
|
||||
|
||||
private class Entry
|
||||
{
|
||||
#pragma warning disable SA1401 // Fields should be private
|
||||
internal TKey1 Key;
|
||||
internal TValue Value;
|
||||
internal int Hash;
|
||||
internal Entry Next;
|
||||
#pragma warning restore SA1401 // Fields should be private
|
||||
|
||||
// from debugger only
|
||||
public override string ToString()
|
||||
{
|
||||
return "Count:" + this.Count;
|
||||
}
|
||||
|
||||
internal int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
var count = 1;
|
||||
Entry n = this;
|
||||
while (n.Next != null)
|
||||
{
|
||||
count++;
|
||||
n = n.Next;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6862254987b9738ab9482616a323c2b5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
504
Assets/Scripts/MessagePack/Internal/AutomataDictionary.cs
Normal file
504
Assets/Scripts/MessagePack/Internal/AutomataDictionary.cs
Normal file
|
|
@ -0,0 +1,504 @@
|
|||
// Copyright (c) All contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
#pragma warning disable SA1509 // Opening braces should not be preceded by blank line
|
||||
|
||||
namespace MessagePack.Internal
|
||||
{
|
||||
// Key = long, Value = int for UTF8String Dictionary
|
||||
|
||||
/// <remarks>
|
||||
/// This code is used by dynamically generated code as well as AOT generated code,
|
||||
/// and thus must be public for the "C# generated and compiled into saved assembly" scenario.
|
||||
/// </remarks>
|
||||
public class AutomataDictionary : IEnumerable<KeyValuePair<string, int>>
|
||||
{
|
||||
private readonly AutomataNode root;
|
||||
|
||||
public AutomataDictionary()
|
||||
{
|
||||
this.root = new AutomataNode(0);
|
||||
}
|
||||
|
||||
public void Add(string str, int value)
|
||||
{
|
||||
ReadOnlySpan<byte> bytes = Encoding.UTF8.GetBytes(str);
|
||||
AutomataNode node = this.root;
|
||||
|
||||
while (bytes.Length > 0)
|
||||
{
|
||||
var key = AutomataKeyGen.GetKey(ref bytes);
|
||||
|
||||
if (bytes.Length == 0)
|
||||
{
|
||||
node = node.Add(key, value, str);
|
||||
}
|
||||
else
|
||||
{
|
||||
node = node.Add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetValue(in ReadOnlySequence<byte> bytes, out int value) => this.TryGetValue(bytes.ToArray(), out value);
|
||||
|
||||
public bool TryGetValue(ReadOnlySpan<byte> bytes, out int value)
|
||||
{
|
||||
AutomataNode node = this.root;
|
||||
|
||||
while (bytes.Length > 0 && node != null)
|
||||
{
|
||||
node = node.SearchNext(ref bytes);
|
||||
}
|
||||
|
||||
if (node == null)
|
||||
{
|
||||
value = -1;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = node.Value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// for debugging
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
ToStringCore(this.root.YieldChildren(), sb, 0);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static void ToStringCore(IEnumerable<AutomataNode> nexts, StringBuilder sb, int depth)
|
||||
{
|
||||
foreach (AutomataNode item in nexts)
|
||||
{
|
||||
if (depth != 0)
|
||||
{
|
||||
sb.Append(' ', depth * 2);
|
||||
}
|
||||
|
||||
sb.Append("[" + item.Key + "]");
|
||||
if (item.Value != -1)
|
||||
{
|
||||
sb.Append("(" + item.OriginalKey + ")");
|
||||
sb.Append(" = ");
|
||||
sb.Append(item.Value);
|
||||
}
|
||||
|
||||
sb.AppendLine();
|
||||
ToStringCore(item.YieldChildren(), sb, depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return this.GetEnumerator();
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<string, int>> GetEnumerator()
|
||||
{
|
||||
return YieldCore(this.root.YieldChildren()).GetEnumerator();
|
||||
}
|
||||
|
||||
private static IEnumerable<KeyValuePair<string, int>> YieldCore(IEnumerable<AutomataNode> nexts)
|
||||
{
|
||||
foreach (AutomataNode item in nexts)
|
||||
{
|
||||
if (item.Value != -1)
|
||||
{
|
||||
yield return new KeyValuePair<string, int>(item.OriginalKey, item.Value);
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<string, int> x in YieldCore(item.YieldChildren()))
|
||||
{
|
||||
yield return x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* IL Emit */
|
||||
|
||||
#if !NET_STANDARD_2_0
|
||||
|
||||
public void EmitMatch(ILGenerator il, LocalBuilder bytesSpan, LocalBuilder key, Action<KeyValuePair<string, int>> onFound, Action onNotFound)
|
||||
{
|
||||
this.root.EmitSearchNext(il, bytesSpan, key, onFound, onNotFound);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
private class AutomataNode : IComparable<AutomataNode>
|
||||
{
|
||||
private static readonly AutomataNode[] EmptyNodes = new AutomataNode[0];
|
||||
private static readonly ulong[] EmptyKeys = new ulong[0];
|
||||
|
||||
#pragma warning disable SA1401 // Fields should be private
|
||||
internal ulong Key;
|
||||
internal int Value;
|
||||
internal string OriginalKey;
|
||||
#pragma warning restore SA1401 // Fields should be private
|
||||
|
||||
private AutomataNode[] nexts;
|
||||
private ulong[] nextKeys;
|
||||
private int count;
|
||||
|
||||
public bool HasChildren
|
||||
{
|
||||
get { return this.count != 0; }
|
||||
}
|
||||
|
||||
public AutomataNode(ulong key)
|
||||
{
|
||||
this.Key = key;
|
||||
this.Value = -1;
|
||||
this.nexts = EmptyNodes;
|
||||
this.nextKeys = EmptyKeys;
|
||||
this.count = 0;
|
||||
this.OriginalKey = null;
|
||||
}
|
||||
|
||||
public AutomataNode Add(ulong key)
|
||||
{
|
||||
var index = Array.BinarySearch(this.nextKeys, 0, this.count, key);
|
||||
if (index < 0)
|
||||
{
|
||||
if (this.nexts.Length == this.count)
|
||||
{
|
||||
Array.Resize<AutomataNode>(ref this.nexts, (this.count == 0) ? 4 : (this.count * 2));
|
||||
Array.Resize<ulong>(ref this.nextKeys, (this.count == 0) ? 4 : (this.count * 2));
|
||||
}
|
||||
|
||||
this.count++;
|
||||
|
||||
var nextNode = new AutomataNode(key);
|
||||
this.nexts[this.count - 1] = nextNode;
|
||||
this.nextKeys[this.count - 1] = key;
|
||||
Array.Sort(this.nexts, 0, this.count);
|
||||
Array.Sort(this.nextKeys, 0, this.count);
|
||||
return nextNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.nexts[index];
|
||||
}
|
||||
}
|
||||
|
||||
public AutomataNode Add(ulong key, int value, string originalKey)
|
||||
{
|
||||
AutomataNode v = this.Add(key);
|
||||
v.Value = value;
|
||||
v.OriginalKey = originalKey;
|
||||
return v;
|
||||
}
|
||||
|
||||
public AutomataNode SearchNext(ref ReadOnlySpan<byte> value)
|
||||
{
|
||||
var key = AutomataKeyGen.GetKey(ref value);
|
||||
if (this.count < 4)
|
||||
{
|
||||
// linear search
|
||||
for (int i = 0; i < this.count; i++)
|
||||
{
|
||||
if (this.nextKeys[i] == key)
|
||||
{
|
||||
return this.nexts[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// binary search
|
||||
var index = BinarySearch(this.nextKeys, 0, this.count, key);
|
||||
if (index >= 0)
|
||||
{
|
||||
return this.nexts[index];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static int BinarySearch(ulong[] array, int index, int length, ulong value)
|
||||
{
|
||||
int lo = index;
|
||||
int hi = index + length - 1;
|
||||
while (lo <= hi)
|
||||
{
|
||||
int i = lo + ((hi - lo) >> 1);
|
||||
|
||||
var arrayValue = array[i];
|
||||
int order;
|
||||
if (arrayValue < value)
|
||||
{
|
||||
order = -1;
|
||||
}
|
||||
else if (arrayValue > value)
|
||||
{
|
||||
order = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
order = 0;
|
||||
}
|
||||
|
||||
if (order == 0)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
|
||||
if (order < 0)
|
||||
{
|
||||
lo = i + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
hi = i - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return ~lo;
|
||||
}
|
||||
|
||||
public int CompareTo(AutomataNode other)
|
||||
{
|
||||
return this.Key.CompareTo(other.Key);
|
||||
}
|
||||
|
||||
public IEnumerable<AutomataNode> YieldChildren()
|
||||
{
|
||||
for (int i = 0; i < this.count; i++)
|
||||
{
|
||||
yield return this.nexts[i];
|
||||
}
|
||||
}
|
||||
|
||||
#if !NET_STANDARD_2_0
|
||||
|
||||
// SearchNext(ref ReadOnlySpan<byte> bytes)
|
||||
public void EmitSearchNext(ILGenerator il, LocalBuilder bytesSpan, LocalBuilder key, Action<KeyValuePair<string, int>> onFound, Action onNotFound)
|
||||
{
|
||||
// key = AutomataKeyGen.GetKey(ref bytesSpan);
|
||||
il.EmitLdloca(bytesSpan);
|
||||
il.EmitCall(AutomataKeyGen.GetKeyMethod);
|
||||
il.EmitStloc(key);
|
||||
|
||||
// match children.
|
||||
EmitSearchNextCore(il, bytesSpan, key, onFound, onNotFound, this.nexts, this.count);
|
||||
}
|
||||
|
||||
private static void EmitSearchNextCore(ILGenerator il, LocalBuilder bytesSpan, LocalBuilder key, Action<KeyValuePair<string, int>> onFound, Action onNotFound, AutomataNode[] nexts, int count)
|
||||
{
|
||||
if (count < 4)
|
||||
{
|
||||
// linear-search
|
||||
AutomataNode[] valueExists = nexts.Take(count).Where(x => x.Value != -1).ToArray();
|
||||
AutomataNode[] childrenExists = nexts.Take(count).Where(x => x.HasChildren).ToArray();
|
||||
Label gotoSearchNext = il.DefineLabel();
|
||||
Label gotoNotFound = il.DefineLabel();
|
||||
|
||||
{
|
||||
// bytesSpan.Length
|
||||
il.EmitLdloca(bytesSpan);
|
||||
il.EmitCall(typeof(ReadOnlySpan<byte>).GetRuntimeProperty(nameof(ReadOnlySpan<byte>.Length)).GetMethod);
|
||||
if (childrenExists.Length != 0 && valueExists.Length == 0)
|
||||
{
|
||||
il.Emit(OpCodes.Brfalse, gotoNotFound); // if(bytesSpan.Length == 0)
|
||||
}
|
||||
else
|
||||
{
|
||||
il.Emit(OpCodes.Brtrue, gotoSearchNext); // if(bytesSpan.Length != 0)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Label[] ifValueNexts = Enumerable.Range(0, Math.Max(valueExists.Length - 1, 0)).Select(_ => il.DefineLabel()).ToArray();
|
||||
for (int i = 0; i < valueExists.Length; i++)
|
||||
{
|
||||
Label notFoundLabel = il.DefineLabel();
|
||||
if (i != 0)
|
||||
{
|
||||
il.MarkLabel(ifValueNexts[i - 1]);
|
||||
}
|
||||
|
||||
il.EmitLdloc(key);
|
||||
il.EmitULong(valueExists[i].Key);
|
||||
il.Emit(OpCodes.Bne_Un, notFoundLabel);
|
||||
|
||||
// found
|
||||
onFound(new KeyValuePair<string, int>(valueExists[i].OriginalKey, valueExists[i].Value));
|
||||
|
||||
// notfound
|
||||
il.MarkLabel(notFoundLabel);
|
||||
if (i != valueExists.Length - 1)
|
||||
{
|
||||
il.Emit(OpCodes.Br, ifValueNexts[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
onNotFound();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
il.MarkLabel(gotoSearchNext);
|
||||
Label[] ifRecNext = Enumerable.Range(0, Math.Max(childrenExists.Length - 1, 0)).Select(_ => il.DefineLabel()).ToArray();
|
||||
for (int i = 0; i < childrenExists.Length; i++)
|
||||
{
|
||||
Label notFoundLabel = il.DefineLabel();
|
||||
if (i != 0)
|
||||
{
|
||||
il.MarkLabel(ifRecNext[i - 1]);
|
||||
}
|
||||
|
||||
il.EmitLdloc(key);
|
||||
il.EmitULong(childrenExists[i].Key);
|
||||
il.Emit(OpCodes.Bne_Un, notFoundLabel);
|
||||
|
||||
// found
|
||||
childrenExists[i].EmitSearchNext(il, bytesSpan, key, onFound, onNotFound);
|
||||
|
||||
// notfound
|
||||
il.MarkLabel(notFoundLabel);
|
||||
if (i != childrenExists.Length - 1)
|
||||
{
|
||||
il.Emit(OpCodes.Br, ifRecNext[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
onNotFound();
|
||||
}
|
||||
}
|
||||
|
||||
il.MarkLabel(gotoNotFound);
|
||||
onNotFound();
|
||||
}
|
||||
else
|
||||
{
|
||||
// binary-search
|
||||
var midline = count / 2;
|
||||
var mid = nexts[midline].Key;
|
||||
AutomataNode[] l = nexts.Take(count).Take(midline).ToArray();
|
||||
AutomataNode[] r = nexts.Take(count).Skip(midline).ToArray();
|
||||
|
||||
Label gotoRight = il.DefineLabel();
|
||||
|
||||
// if(key < mid)
|
||||
il.EmitLdloc(key);
|
||||
il.EmitULong(mid);
|
||||
il.Emit(OpCodes.Bge, gotoRight);
|
||||
EmitSearchNextCore(il, bytesSpan, key, onFound, onNotFound, l, l.Length);
|
||||
|
||||
// else
|
||||
il.MarkLabel(gotoRight);
|
||||
EmitSearchNextCore(il, bytesSpan, key, onFound, onNotFound, r, r.Length);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// This is used by dynamically generated code. It can be made internal after we enable our dynamic assemblies to access internals.
|
||||
/// But that trick may require net46, so maybe we should leave this as public.
|
||||
/// </remarks>
|
||||
public static class AutomataKeyGen
|
||||
{
|
||||
public static readonly MethodInfo GetKeyMethod = typeof(AutomataKeyGen).GetRuntimeMethod(nameof(GetKey), new[] { typeof(ReadOnlySpan<byte>).MakeByRefType() });
|
||||
|
||||
public static ulong GetKey(ref ReadOnlySpan<byte> span)
|
||||
{
|
||||
ulong key;
|
||||
|
||||
unchecked
|
||||
{
|
||||
if (span.Length >= 8)
|
||||
{
|
||||
key = MemoryMarshal.Cast<byte, ulong>(span)[0];
|
||||
span = span.Slice(8);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (span.Length)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
key = span[0];
|
||||
span = span.Slice(1);
|
||||
break;
|
||||
}
|
||||
|
||||
case 2:
|
||||
{
|
||||
key = MemoryMarshal.Cast<byte, ushort>(span)[0];
|
||||
span = span.Slice(2);
|
||||
break;
|
||||
}
|
||||
|
||||
case 3:
|
||||
{
|
||||
var a = span[0];
|
||||
var b = MemoryMarshal.Cast<byte, ushort>(span.Slice(1))[0];
|
||||
key = a | (ulong)b << 8;
|
||||
span = span.Slice(3);
|
||||
break;
|
||||
}
|
||||
|
||||
case 4:
|
||||
{
|
||||
key = MemoryMarshal.Cast<byte, uint>(span)[0];
|
||||
span = span.Slice(4);
|
||||
break;
|
||||
}
|
||||
|
||||
case 5:
|
||||
{
|
||||
var a = span[0];
|
||||
var b = MemoryMarshal.Cast<byte, uint>(span.Slice(1))[0];
|
||||
key = a | (ulong)b << 8;
|
||||
span = span.Slice(5);
|
||||
break;
|
||||
}
|
||||
|
||||
case 6:
|
||||
{
|
||||
ulong a = MemoryMarshal.Cast<byte, ushort>(span)[0];
|
||||
ulong b = MemoryMarshal.Cast<byte, uint>(span.Slice(2))[0];
|
||||
key = a | (b << 16);
|
||||
span = span.Slice(6);
|
||||
break;
|
||||
}
|
||||
|
||||
case 7:
|
||||
{
|
||||
var a = span[0];
|
||||
var b = MemoryMarshal.Cast<byte, ushort>(span.Slice(1))[0];
|
||||
var c = MemoryMarshal.Cast<byte, uint>(span.Slice(3))[0];
|
||||
key = a | (ulong)b << 8 | (ulong)c << 24;
|
||||
span = span.Slice(7);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new MessagePackSerializationException("Not Supported Length");
|
||||
}
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fc94f4e5525f7ba44b4e68f3cdcb1050
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
193
Assets/Scripts/MessagePack/Internal/ByteArrayStringHashTable.cs
Normal file
193
Assets/Scripts/MessagePack/Internal/ByteArrayStringHashTable.cs
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
// Copyright (c) All contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace MessagePack.Internal
|
||||
{
|
||||
// like ArraySegment<byte> hashtable.
|
||||
// Add is safe for construction phase only and requires capacity(does not do rehash)
|
||||
// and specialized for internal use(nongenerics, TValue is int)
|
||||
|
||||
// internal, but code generator requires this class
|
||||
// or at least PerfBenchmarkDotNet
|
||||
public class ByteArrayStringHashTable : IEnumerable<KeyValuePair<string, int>>
|
||||
{
|
||||
private readonly Entry[][] buckets; // immutable array(faster than linkedlist)
|
||||
private readonly ulong indexFor;
|
||||
|
||||
public ByteArrayStringHashTable(int capacity)
|
||||
: this(capacity, 0.42f) // default: 0.75f -> 0.42f
|
||||
{
|
||||
}
|
||||
|
||||
public ByteArrayStringHashTable(int capacity, float loadFactor)
|
||||
{
|
||||
var tableSize = CalculateCapacity(capacity, loadFactor);
|
||||
this.buckets = new Entry[tableSize][];
|
||||
this.indexFor = (ulong)this.buckets.Length - 1;
|
||||
}
|
||||
|
||||
public void Add(string key, int value)
|
||||
{
|
||||
if (!this.TryAddInternal(Encoding.UTF8.GetBytes(key), value))
|
||||
{
|
||||
throw new ArgumentException("Key was already exists. Key:" + key);
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(byte[] key, int value)
|
||||
{
|
||||
if (!this.TryAddInternal(key, value))
|
||||
{
|
||||
throw new ArgumentException("Key was already exists. Key:" + key);
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryAddInternal(byte[] key, int value)
|
||||
{
|
||||
var h = ByteArrayGetHashCode(key);
|
||||
var entry = new Entry { Key = key, Value = value };
|
||||
|
||||
Entry[] array = this.buckets[h & this.indexFor];
|
||||
if (array == null)
|
||||
{
|
||||
this.buckets[h & this.indexFor] = new[] { entry };
|
||||
}
|
||||
else
|
||||
{
|
||||
// check duplicate
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
byte[] e = array[i].Key;
|
||||
if (key.AsSpan().SequenceEqual(e))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var newArray = new Entry[array.Length + 1];
|
||||
Array.Copy(array, newArray, array.Length);
|
||||
array = newArray;
|
||||
array[array.Length - 1] = entry;
|
||||
this.buckets[h & this.indexFor] = array;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryGetValue(in ReadOnlySequence<byte> key, out int value) => this.TryGetValue(CodeGenHelpers.GetSpanFromSequence(key), out value);
|
||||
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryGetValue(ReadOnlySpan<byte> key, out int value)
|
||||
{
|
||||
Entry[][] table = this.buckets;
|
||||
var hash = ByteArrayGetHashCode(key);
|
||||
Entry[] entry = table[hash & this.indexFor];
|
||||
|
||||
if (entry == null)
|
||||
{
|
||||
value = default(int);
|
||||
return false;
|
||||
}
|
||||
|
||||
ref Entry v = ref entry[0];
|
||||
if (key.SequenceEqual(v.Key))
|
||||
{
|
||||
value = v.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
return TryGetValueSlow(key, entry, out value);
|
||||
}
|
||||
|
||||
private bool TryGetValueSlow(ReadOnlySpan<byte> key, Entry[] entry, out int value)
|
||||
{
|
||||
for (int i = 1; i < entry.Length; i++)
|
||||
{
|
||||
ref Entry v = ref entry[i];
|
||||
if (key.SequenceEqual(v.Key))
|
||||
{
|
||||
value = v.Value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
value = default(int);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static readonly bool Is32Bit = IntPtr.Size == 4;
|
||||
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
private static ulong ByteArrayGetHashCode(ReadOnlySpan<byte> x)
|
||||
{
|
||||
// FarmHash https://github.com/google/farmhash
|
||||
if (Is32Bit)
|
||||
{
|
||||
return (ulong)FarmHash.Hash32(x);
|
||||
}
|
||||
else
|
||||
{
|
||||
return FarmHash.Hash64(x);
|
||||
}
|
||||
}
|
||||
|
||||
private static int CalculateCapacity(int collectionSize, float loadFactor)
|
||||
{
|
||||
var initialCapacity = (int)(((float)collectionSize) / loadFactor);
|
||||
var capacity = 1;
|
||||
while (capacity < initialCapacity)
|
||||
{
|
||||
capacity <<= 1;
|
||||
}
|
||||
|
||||
if (capacity < 8)
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
return capacity;
|
||||
}
|
||||
|
||||
// only for Debug use
|
||||
public IEnumerator<KeyValuePair<string, int>> GetEnumerator()
|
||||
{
|
||||
Entry[][] b = this.buckets;
|
||||
|
||||
foreach (Entry[] item in b)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (Entry item2 in item)
|
||||
{
|
||||
yield return new KeyValuePair<string, int>(Encoding.UTF8.GetString(item2.Key), item2.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return this.GetEnumerator();
|
||||
}
|
||||
|
||||
private struct Entry
|
||||
{
|
||||
public byte[] Key;
|
||||
public int Value;
|
||||
|
||||
// for debugging
|
||||
public override string ToString()
|
||||
{
|
||||
return "(" + Encoding.UTF8.GetString(this.Key) + ", " + this.Value + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 28c84212e5b4fc4d7b4b66cdb741984d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
87
Assets/Scripts/MessagePack/Internal/CodeGenHelpers.cs
Normal file
87
Assets/Scripts/MessagePack/Internal/CodeGenHelpers.cs
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
// Copyright (c) All contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace MessagePack.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// Helpers for generated code.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This code is used by dynamically generated code as well as AOT generated code,
|
||||
/// and thus must be public for the "C# generated and compiled into saved assembly" scenario.
|
||||
/// </remarks>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static class CodeGenHelpers
|
||||
{
|
||||
public static byte[] GetEncodedStringBytes(string value)
|
||||
{
|
||||
var byteCount = StringEncoding.UTF8.GetByteCount(value);
|
||||
if (byteCount <= MessagePackRange.MaxFixStringLength)
|
||||
{
|
||||
var bytes = new byte[byteCount + 1];
|
||||
bytes[0] = (byte)(MessagePackCode.MinFixStr | byteCount);
|
||||
StringEncoding.UTF8.GetBytes(value, 0, value.Length, bytes, 1);
|
||||
return bytes;
|
||||
}
|
||||
else if (byteCount <= byte.MaxValue)
|
||||
{
|
||||
var bytes = new byte[byteCount + 2];
|
||||
bytes[0] = MessagePackCode.Str8;
|
||||
bytes[1] = unchecked((byte)byteCount);
|
||||
StringEncoding.UTF8.GetBytes(value, 0, value.Length, bytes, 2);
|
||||
return bytes;
|
||||
}
|
||||
else if (byteCount <= ushort.MaxValue)
|
||||
{
|
||||
var bytes = new byte[byteCount + 3];
|
||||
bytes[0] = MessagePackCode.Str16;
|
||||
bytes[1] = unchecked((byte)(byteCount >> 8));
|
||||
bytes[2] = unchecked((byte)byteCount);
|
||||
StringEncoding.UTF8.GetBytes(value, 0, value.Length, bytes, 3);
|
||||
return bytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
var bytes = new byte[byteCount + 5];
|
||||
bytes[0] = MessagePackCode.Str32;
|
||||
bytes[1] = unchecked((byte)(byteCount >> 24));
|
||||
bytes[2] = unchecked((byte)(byteCount >> 16));
|
||||
bytes[3] = unchecked((byte)(byteCount >> 8));
|
||||
bytes[4] = unchecked((byte)byteCount);
|
||||
StringEncoding.UTF8.GetBytes(value, 0, value.Length, bytes, 5);
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
||||
public static ReadOnlySpan<byte> GetSpanFromSequence(in ReadOnlySequence<byte> sequence)
|
||||
{
|
||||
if (sequence.IsSingleSegment)
|
||||
{
|
||||
return sequence.First.Span;
|
||||
}
|
||||
|
||||
return sequence.ToArray();
|
||||
}
|
||||
|
||||
public static ReadOnlySpan<byte> ReadStringSpan(ref MessagePackReader reader)
|
||||
{
|
||||
if (!reader.TryReadStringSpan(out ReadOnlySpan<byte> result))
|
||||
{
|
||||
return GetSpanFromSequence(reader.ReadStringSequence());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static byte[] GetArrayFromNullableSequence(in ReadOnlySequence<byte>? sequence) => sequence?.ToArray();
|
||||
|
||||
private static ReadOnlySpan<byte> GetSpanFromSequence(in ReadOnlySequence<byte>? sequence)
|
||||
{
|
||||
return sequence.HasValue ? GetSpanFromSequence(sequence.Value) : default;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/MessagePack/Internal/CodeGenHelpers.cs.meta
Normal file
11
Assets/Scripts/MessagePack/Internal/CodeGenHelpers.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4f2dcc1d826e7ff7da02bd691c4dd6a0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
14
Assets/Scripts/MessagePack/Internal/DateTimeConstants.cs
Normal file
14
Assets/Scripts/MessagePack/Internal/DateTimeConstants.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) All contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace MessagePack.Internal
|
||||
{
|
||||
internal static class DateTimeConstants
|
||||
{
|
||||
internal const long BclSecondsAtUnixEpoch = 62135596800;
|
||||
internal const int NanosecondsPerTick = 100;
|
||||
internal static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a0cf18db43b878d71a216123e3309b65
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
75
Assets/Scripts/MessagePack/Internal/DynamicAssembly.cs
Normal file
75
Assets/Scripts/MessagePack/Internal/DynamicAssembly.cs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright (c) All contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#if !(UNITY_2018_3_OR_NEWER && NET_STANDARD_2_0)
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace MessagePack.Internal
|
||||
{
|
||||
internal class DynamicAssembly
|
||||
{
|
||||
#if NETFRAMEWORK // We don't ship a net472 target, but we might add one for debugging purposes
|
||||
private readonly string moduleName;
|
||||
#endif
|
||||
private readonly AssemblyBuilder assemblyBuilder;
|
||||
private readonly ModuleBuilder moduleBuilder;
|
||||
|
||||
// don't expose ModuleBuilder
|
||||
//// public ModuleBuilder ModuleBuilder { get { return moduleBuilder; } }
|
||||
|
||||
private readonly object gate = new object();
|
||||
|
||||
public DynamicAssembly(string moduleName)
|
||||
{
|
||||
#if NETFRAMEWORK // We don't ship a net472 target, but we might add one for debugging purposes
|
||||
AssemblyBuilderAccess builderAccess = AssemblyBuilderAccess.RunAndSave;
|
||||
this.moduleName = moduleName;
|
||||
#else
|
||||
AssemblyBuilderAccess builderAccess = AssemblyBuilderAccess.Run;
|
||||
#endif
|
||||
this.assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(moduleName), builderAccess);
|
||||
this.moduleBuilder = this.assemblyBuilder.DefineDynamicModule(moduleName + ".dll");
|
||||
}
|
||||
|
||||
/* requires lock on mono environment. see: https://github.com/neuecc/MessagePack-CSharp/issues/161 */
|
||||
|
||||
public TypeBuilder DefineType(string name, TypeAttributes attr)
|
||||
{
|
||||
lock (this.gate)
|
||||
{
|
||||
return this.moduleBuilder.DefineType(name, attr);
|
||||
}
|
||||
}
|
||||
|
||||
public TypeBuilder DefineType(string name, TypeAttributes attr, Type parent)
|
||||
{
|
||||
lock (this.gate)
|
||||
{
|
||||
return this.moduleBuilder.DefineType(name, attr, parent);
|
||||
}
|
||||
}
|
||||
|
||||
public TypeBuilder DefineType(string name, TypeAttributes attr, Type parent, Type[] interfaces)
|
||||
{
|
||||
lock (this.gate)
|
||||
{
|
||||
return this.moduleBuilder.DefineType(name, attr, parent, interfaces);
|
||||
}
|
||||
}
|
||||
|
||||
#if NETFRAMEWORK
|
||||
|
||||
public AssemblyBuilder Save()
|
||||
{
|
||||
this.assemblyBuilder.Save(this.moduleName + ".dll");
|
||||
return this.assemblyBuilder;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
11
Assets/Scripts/MessagePack/Internal/DynamicAssembly.cs.meta
Normal file
11
Assets/Scripts/MessagePack/Internal/DynamicAssembly.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f44ca64eddd25f5a896a2f72b21fcc9b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
85
Assets/Scripts/MessagePack/Internal/ExpressionUtility.cs
Normal file
85
Assets/Scripts/MessagePack/Internal/ExpressionUtility.cs
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
// Copyright (c) All contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
namespace MessagePack.Internal
|
||||
{
|
||||
internal static class ExpressionUtility
|
||||
{
|
||||
private static MethodInfo GetMethodInfoCore(LambdaExpression expression)
|
||||
{
|
||||
if (expression == null)
|
||||
{
|
||||
throw new ArgumentNullException("expression");
|
||||
}
|
||||
|
||||
return (expression.Body as MethodCallExpression).Method;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get MethodInfo from Expression for Static(with result) method.
|
||||
/// </summary>
|
||||
public static MethodInfo GetMethodInfo<T>(Expression<Func<T>> expression)
|
||||
{
|
||||
return GetMethodInfoCore(expression);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get MethodInfo from Expression for Static(void) method.
|
||||
/// </summary>
|
||||
public static MethodInfo GetMethodInfo(Expression<Action> expression)
|
||||
{
|
||||
return GetMethodInfoCore(expression);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get MethodInfo from Expression for Instance(with result) method.
|
||||
/// </summary>
|
||||
public static MethodInfo GetMethodInfo<T, TR>(Expression<Func<T, TR>> expression)
|
||||
{
|
||||
return GetMethodInfoCore(expression);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get MethodInfo from Expression for Instance(void) method.
|
||||
/// </summary>
|
||||
public static MethodInfo GetMethodInfo<T>(Expression<Action<T>> expression)
|
||||
{
|
||||
return GetMethodInfoCore(expression);
|
||||
}
|
||||
|
||||
// WithArgument(for ref, out) helper
|
||||
|
||||
/// <summary>
|
||||
/// Get MethodInfo from Expression for Instance(with result) method.
|
||||
/// </summary>
|
||||
public static MethodInfo GetMethodInfo<T, TArg1, TR>(Expression<Func<T, TArg1, TR>> expression)
|
||||
{
|
||||
return GetMethodInfoCore(expression);
|
||||
}
|
||||
|
||||
private static MemberInfo GetMemberInfoCore<T>(Expression<T> source)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
throw new ArgumentNullException("source");
|
||||
}
|
||||
|
||||
var memberExpression = source.Body as MemberExpression;
|
||||
return memberExpression.Member;
|
||||
}
|
||||
|
||||
public static PropertyInfo GetPropertyInfo<T, TR>(Expression<Func<T, TR>> expression)
|
||||
{
|
||||
return GetMemberInfoCore(expression) as PropertyInfo;
|
||||
}
|
||||
|
||||
public static FieldInfo GetFieldInfo<T, TR>(Expression<Func<T, TR>> expression)
|
||||
{
|
||||
return GetMemberInfoCore(expression) as FieldInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1e2163b0f9d41a70599e1d5bd231a4b6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
601
Assets/Scripts/MessagePack/Internal/FarmHash.cs
Normal file
601
Assets/Scripts/MessagePack/Internal/FarmHash.cs
Normal file
|
|
@ -0,0 +1,601 @@
|
|||
// Copyright (c) All contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
#pragma warning disable SA1300 // Element should begin with uppercase letter
|
||||
#pragma warning disable SA1303 // Const field names should begin with uppercase letter
|
||||
#pragma warning disable SA1307 // (public) field names should begin with uppercase letter
|
||||
|
||||
namespace MessagePack.Internal
|
||||
{
|
||||
internal static class FarmHash
|
||||
{
|
||||
#region Hash32
|
||||
|
||||
// entry point of 32bit
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe uint Hash32(ReadOnlySpan<byte> bytes)
|
||||
{
|
||||
if (bytes.Length <= 4)
|
||||
{
|
||||
return Hash32Len0to4(bytes);
|
||||
}
|
||||
|
||||
fixed (byte* p = bytes)
|
||||
{
|
||||
return Hash32(p, (uint)bytes.Length);
|
||||
}
|
||||
}
|
||||
|
||||
// port of farmhash.cc, 32bit only
|
||||
|
||||
// Magic numbers for 32-bit hashing. Copied from Murmur3.
|
||||
private const uint c1 = 0xcc9e2d51;
|
||||
private const uint c2 = 0x1b873593;
|
||||
|
||||
private static unsafe uint Fetch32(byte* p)
|
||||
{
|
||||
return *(uint*)p;
|
||||
}
|
||||
|
||||
private static uint Rotate32(uint val, int shift)
|
||||
{
|
||||
return shift == 0 ? val : ((val >> shift) | (val << (32 - shift)));
|
||||
}
|
||||
|
||||
// A 32-bit to 32-bit integer hash copied from Murmur3.
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static uint fmix(uint h)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
h ^= h >> 16;
|
||||
h *= 0x85ebca6b;
|
||||
h ^= h >> 13;
|
||||
h *= 0xc2b2ae35;
|
||||
h ^= h >> 16;
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static uint Mur(uint a, uint h)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
// Helper from Murmur3 for combining two 32-bit values.
|
||||
a *= c1;
|
||||
a = Rotate32(a, 17);
|
||||
a *= c2;
|
||||
h ^= a;
|
||||
h = Rotate32(h, 19);
|
||||
return (h * 5) + 0xe6546b64;
|
||||
}
|
||||
}
|
||||
|
||||
// 0-4
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe uint Hash32Len0to4(ReadOnlySpan<byte> s)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
uint b = 0;
|
||||
uint c = 9;
|
||||
for (int i = 0; i < s.Length; i++)
|
||||
{
|
||||
b = (b * c1) + s[i];
|
||||
c ^= b;
|
||||
}
|
||||
|
||||
return fmix(Mur(b, Mur((uint)s.Length, c)));
|
||||
}
|
||||
}
|
||||
|
||||
// 5-12
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe uint Hash32Len5to12(byte* s, uint len)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
uint a = len, b = len * 5, c = 9, d = b;
|
||||
a += Fetch32(s);
|
||||
b += Fetch32(s + len - 4);
|
||||
c += Fetch32(s + ((len >> 1) & 4));
|
||||
return fmix(Mur(c, Mur(b, Mur(a, d))));
|
||||
}
|
||||
}
|
||||
|
||||
// 13-24
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe uint Hash32Len13to24(byte* s, uint len)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
uint a = Fetch32(s - 4 + (len >> 1));
|
||||
uint b = Fetch32(s + 4);
|
||||
uint c = Fetch32(s + len - 8);
|
||||
uint d = Fetch32(s + (len >> 1));
|
||||
uint e = Fetch32(s);
|
||||
uint f = Fetch32(s + len - 4);
|
||||
uint h = (d * c1) + len;
|
||||
a = Rotate32(a, 12) + f;
|
||||
h = Mur(c, h) + a;
|
||||
a = Rotate32(a, 3) + c;
|
||||
h = Mur(e, h) + a;
|
||||
a = Rotate32(a + f, 12) + d;
|
||||
h = Mur(b, h) + a;
|
||||
return fmix(h);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe uint Hash32(byte* s, uint len)
|
||||
{
|
||||
if (len <= 24)
|
||||
{
|
||||
return len <= 12 ? Hash32Len5to12(s, len) : Hash32Len13to24(s, len);
|
||||
}
|
||||
|
||||
unchecked
|
||||
{
|
||||
// len > 24
|
||||
uint h = len, g = c1 * len, f = g;
|
||||
uint a0 = Rotate32(Fetch32(s + len - 4) * c1, 17) * c2;
|
||||
uint a1 = Rotate32(Fetch32(s + len - 8) * c1, 17) * c2;
|
||||
uint a2 = Rotate32(Fetch32(s + len - 16) * c1, 17) * c2;
|
||||
uint a3 = Rotate32(Fetch32(s + len - 12) * c1, 17) * c2;
|
||||
uint a4 = Rotate32(Fetch32(s + len - 20) * c1, 17) * c2;
|
||||
h ^= a0;
|
||||
h = Rotate32(h, 19);
|
||||
h = (h * 5) + 0xe6546b64;
|
||||
h ^= a2;
|
||||
h = Rotate32(h, 19);
|
||||
h = (h * 5) + 0xe6546b64;
|
||||
g ^= a1;
|
||||
g = Rotate32(g, 19);
|
||||
g = (g * 5) + 0xe6546b64;
|
||||
g ^= a3;
|
||||
g = Rotate32(g, 19);
|
||||
g = (g * 5) + 0xe6546b64;
|
||||
f += a4;
|
||||
f = Rotate32(f, 19) + 113;
|
||||
uint iters = (len - 1) / 20;
|
||||
do
|
||||
{
|
||||
uint a = Fetch32(s);
|
||||
uint b = Fetch32(s + 4);
|
||||
uint c = Fetch32(s + 8);
|
||||
uint d = Fetch32(s + 12);
|
||||
uint e = Fetch32(s + 16);
|
||||
h += a;
|
||||
g += b;
|
||||
f += c;
|
||||
h = Mur(d, h) + e;
|
||||
g = Mur(c, g) + a;
|
||||
f = Mur(b + (e * c1), f) + d;
|
||||
f += g;
|
||||
g += f;
|
||||
s += 20;
|
||||
}
|
||||
while (--iters != 0);
|
||||
g = Rotate32(g, 11) * c1;
|
||||
g = Rotate32(g, 17) * c1;
|
||||
f = Rotate32(f, 11) * c1;
|
||||
f = Rotate32(f, 17) * c1;
|
||||
h = Rotate32(h + g, 19);
|
||||
h = (h * 5) + 0xe6546b64;
|
||||
h = Rotate32(h, 17) * c1;
|
||||
h = Rotate32(h + f, 19);
|
||||
h = (h * 5) + 0xe6546b64;
|
||||
h = Rotate32(h, 17) * c1;
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region hash64
|
||||
|
||||
// entry point
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ulong Hash64(ReadOnlySpan<byte> bytes)
|
||||
{
|
||||
fixed (byte* p = bytes)
|
||||
{
|
||||
return Hash64(p, (uint)bytes.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/* port from farmhash.cc */
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void swap(ref ulong x, ref ulong z)
|
||||
{
|
||||
var temp = z;
|
||||
z = x;
|
||||
x = temp;
|
||||
}
|
||||
|
||||
// Some primes between 2^63 and 2^64 for various uses.
|
||||
private const ulong k0 = 0xc3a5c85c97cb3127UL;
|
||||
private const ulong k1 = 0xb492b66fbe98f273UL;
|
||||
private const ulong k2 = 0x9ae16a3b2f90404fUL;
|
||||
|
||||
private static unsafe ulong Fetch64(byte* p)
|
||||
{
|
||||
return *(ulong*)p;
|
||||
}
|
||||
|
||||
private static ulong Rotate64(ulong val, int shift)
|
||||
{
|
||||
return shift == 0 ? val : (val >> shift) | (val << (64 - shift));
|
||||
}
|
||||
|
||||
// farmhashna.cc
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static ulong ShiftMix(ulong val)
|
||||
{
|
||||
return val ^ (val >> 47);
|
||||
}
|
||||
|
||||
// farmhashna.cc
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static ulong HashLen16(ulong u, ulong v, ulong mul)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
// Murmur-inspired hashing.
|
||||
ulong a = (u ^ v) * mul;
|
||||
a ^= a >> 47;
|
||||
ulong b = (v ^ a) * mul;
|
||||
b ^= b >> 47;
|
||||
b *= mul;
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
// farmhashxo.cc
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe ulong Hash64(byte* s, uint len)
|
||||
{
|
||||
if (len <= 16)
|
||||
{
|
||||
// farmhashna::
|
||||
return HashLen0to16(s, len);
|
||||
}
|
||||
|
||||
if (len <= 32)
|
||||
{
|
||||
// farmhashna::
|
||||
return HashLen17to32(s, len);
|
||||
}
|
||||
|
||||
if (len <= 64)
|
||||
{
|
||||
return HashLen33to64(s, len);
|
||||
}
|
||||
|
||||
if (len <= 96)
|
||||
{
|
||||
return HashLen65to96(s, len);
|
||||
}
|
||||
|
||||
if (len <= 256)
|
||||
{
|
||||
// farmhashna::
|
||||
return Hash64NA(s, len);
|
||||
}
|
||||
|
||||
// farmhashuo::
|
||||
return Hash64UO(s, len);
|
||||
}
|
||||
|
||||
// 0-16 farmhashna.cc
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe ulong HashLen0to16(byte* s, uint len)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
if (len >= 8)
|
||||
{
|
||||
ulong mul = k2 + (len * 2);
|
||||
ulong a = Fetch64(s) + k2;
|
||||
ulong b = Fetch64(s + len - 8);
|
||||
ulong c = (Rotate64(b, 37) * mul) + a;
|
||||
ulong d = (Rotate64(a, 25) + b) * mul;
|
||||
return HashLen16(c, d, mul);
|
||||
}
|
||||
|
||||
if (len >= 4)
|
||||
{
|
||||
ulong mul = k2 + (len * 2);
|
||||
ulong a = Fetch32(s);
|
||||
return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul);
|
||||
}
|
||||
|
||||
if (len > 0)
|
||||
{
|
||||
ushort a = s[0];
|
||||
ushort b = s[len >> 1];
|
||||
ushort c = s[len - 1];
|
||||
uint y = a + ((uint)b << 8);
|
||||
uint z = len + ((uint)c << 2);
|
||||
return ShiftMix(y * k2 ^ z * k0) * k2;
|
||||
}
|
||||
|
||||
return k2;
|
||||
}
|
||||
}
|
||||
|
||||
// 17-32 farmhashna.cc
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe ulong HashLen17to32(byte* s, uint len)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
ulong mul = k2 + (len * 2);
|
||||
ulong a = Fetch64(s) * k1;
|
||||
ulong b = Fetch64(s + 8);
|
||||
ulong c = Fetch64(s + len - 8) * mul;
|
||||
ulong d = Fetch64(s + len - 16) * k2;
|
||||
return HashLen16(
|
||||
Rotate64(a + b, 43) + Rotate64(c, 30) + d,
|
||||
a + Rotate64(b + k2, 18) + c,
|
||||
mul);
|
||||
}
|
||||
}
|
||||
|
||||
// farmhashxo.cc
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe ulong H32(byte* s, uint len, ulong mul, ulong seed0 = 0, ulong seed1 = 0)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
ulong a = Fetch64(s) * k1;
|
||||
ulong b = Fetch64(s + 8);
|
||||
ulong c = Fetch64(s + len - 8) * mul;
|
||||
ulong d = Fetch64(s + len - 16) * k2;
|
||||
ulong u = Rotate64(a + b, 43) + Rotate64(c, 30) + d + seed0;
|
||||
ulong v = a + Rotate64(b + k2, 18) + c + seed1;
|
||||
a = ShiftMix((u ^ v) * mul);
|
||||
b = ShiftMix((v ^ a) * mul);
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
// 33-64 farmhashxo.cc
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe ulong HashLen33to64(byte* s, uint len)
|
||||
{
|
||||
const ulong mul0 = k2 - 30;
|
||||
|
||||
unchecked
|
||||
{
|
||||
ulong mul1 = k2 - 30 + (2 * len);
|
||||
ulong h0 = H32(s, 32, mul0);
|
||||
ulong h1 = H32(s + len - 32, 32, mul1);
|
||||
return ((h1 * mul1) + h0) * mul1;
|
||||
}
|
||||
}
|
||||
|
||||
// 65-96 farmhashxo.cc
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe ulong HashLen65to96(byte* s, uint len)
|
||||
{
|
||||
const ulong mul0 = k2 - 114;
|
||||
|
||||
unchecked
|
||||
{
|
||||
ulong mul1 = k2 - 114 + (2 * len);
|
||||
ulong h0 = H32(s, 32, mul0);
|
||||
ulong h1 = H32(s + 32, 32, mul1);
|
||||
ulong h2 = H32(s + len - 32, 32, mul1, h0, h1);
|
||||
return ((h2 * 9) + (h0 >> 17) + (h1 >> 21)) * mul1;
|
||||
}
|
||||
}
|
||||
|
||||
// farmhashna.cc
|
||||
// Return a 16-byte hash for 48 bytes. Quick and dirty.
|
||||
// Callers do best to use "random-looking" values for a and b.
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe void WeakHashLen32WithSeeds(ulong w, ulong x, ulong y, ulong z, ulong a, ulong b, out ulong first, out ulong second)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
a += w;
|
||||
b = Rotate64(b + a + z, 21);
|
||||
ulong c = a;
|
||||
a += x;
|
||||
a += y;
|
||||
b += Rotate64(a, 44);
|
||||
first = a + z;
|
||||
second = b + c;
|
||||
}
|
||||
}
|
||||
|
||||
// farmhashna.cc
|
||||
// Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty.
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe void WeakHashLen32WithSeeds(byte* s, ulong a, ulong b, out ulong first, out ulong second)
|
||||
{
|
||||
WeakHashLen32WithSeeds(
|
||||
Fetch64(s),
|
||||
Fetch64(s + 8),
|
||||
Fetch64(s + 16),
|
||||
Fetch64(s + 24),
|
||||
a,
|
||||
b,
|
||||
out first,
|
||||
out second);
|
||||
}
|
||||
|
||||
// na(97-256) farmhashna.cc
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe ulong Hash64NA(byte* s, uint len)
|
||||
{
|
||||
const ulong seed = 81;
|
||||
|
||||
unchecked
|
||||
{
|
||||
// For strings over 64 bytes we loop. Internal state consists of
|
||||
// 56 bytes: v, w, x, y, and z.
|
||||
ulong x = seed;
|
||||
ulong y = (seed * k1) + 113;
|
||||
ulong z = ShiftMix((y * k2) + 113) * k2;
|
||||
ulong v_first = 0;
|
||||
ulong v_second = 0;
|
||||
ulong w_first = 0;
|
||||
ulong w_second = 0;
|
||||
x = (x * k2) + Fetch64(s);
|
||||
|
||||
// Set end so that after the loop we have 1 to 64 bytes left to process.
|
||||
byte* end = s + ((len - 1) / 64 * 64);
|
||||
byte* last64 = end + ((len - 1) & 63) - 63;
|
||||
|
||||
do
|
||||
{
|
||||
x = Rotate64(x + y + v_first + Fetch64(s + 8), 37) * k1;
|
||||
y = Rotate64(y + v_second + Fetch64(s + 48), 42) * k1;
|
||||
x ^= w_second;
|
||||
y += v_first + Fetch64(s + 40);
|
||||
z = Rotate64(z + w_first, 33) * k1;
|
||||
WeakHashLen32WithSeeds(s, v_second * k1, x + w_first, out v_first, out v_second);
|
||||
WeakHashLen32WithSeeds(s + 32, z + w_second, y + Fetch64(s + 16), out w_first, out w_second);
|
||||
swap(ref z, ref x);
|
||||
s += 64;
|
||||
}
|
||||
while (s != end);
|
||||
ulong mul = k1 + ((z & 0xff) << 1);
|
||||
|
||||
// Make s point to the last 64 bytes of input.
|
||||
s = last64;
|
||||
w_first += (len - 1) & 63;
|
||||
v_first += w_first;
|
||||
w_first += v_first;
|
||||
x = Rotate64(x + y + v_first + Fetch64(s + 8), 37) * mul;
|
||||
y = Rotate64(y + v_second + Fetch64(s + 48), 42) * mul;
|
||||
x ^= w_second * 9;
|
||||
y += (v_first * 9) + Fetch64(s + 40);
|
||||
z = Rotate64(z + w_first, 33) * mul;
|
||||
WeakHashLen32WithSeeds(s, v_second * mul, x + w_first, out v_first, out v_second);
|
||||
WeakHashLen32WithSeeds(s + 32, z + w_second, y + Fetch64(s + 16), out w_first, out w_second);
|
||||
swap(ref z, ref x);
|
||||
return HashLen16(HashLen16(v_first, w_first, mul) + (ShiftMix(y) * k0) + z, HashLen16(v_second, w_second, mul) + x, mul);
|
||||
}
|
||||
}
|
||||
|
||||
// farmhashuo.cc
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static ulong H(ulong x, ulong y, ulong mul, int r)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
ulong a = (x ^ y) * mul;
|
||||
a ^= a >> 47;
|
||||
ulong b = (y ^ a) * mul;
|
||||
return Rotate64(b, r) * mul;
|
||||
}
|
||||
}
|
||||
|
||||
// uo(257-) farmhashuo.cc, Hash64WithSeeds
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe ulong Hash64UO(byte* s, uint len)
|
||||
{
|
||||
const ulong seed0 = 81;
|
||||
const ulong seed1 = 0;
|
||||
|
||||
unchecked
|
||||
{
|
||||
// For strings over 64 bytes we loop. Internal state consists of
|
||||
// 64 bytes: u, v, w, x, y, and z.
|
||||
ulong x = seed0;
|
||||
ulong y = (seed1 * k2) + 113;
|
||||
ulong z = ShiftMix(y * k2) * k2;
|
||||
ulong v_first = seed0;
|
||||
ulong v_second = seed1;
|
||||
ulong w_first = 0;
|
||||
ulong w_second = 0;
|
||||
ulong u = x - z;
|
||||
x *= k2;
|
||||
ulong mul = k2 + (u & 0x82);
|
||||
|
||||
// Set end so that after the loop we have 1 to 64 bytes left to process.
|
||||
byte* end = s + ((len - 1) / 64 * 64);
|
||||
byte* last64 = end + ((len - 1) & 63) - 63;
|
||||
|
||||
do
|
||||
{
|
||||
ulong a0 = Fetch64(s);
|
||||
ulong a1 = Fetch64(s + 8);
|
||||
ulong a2 = Fetch64(s + 16);
|
||||
ulong a3 = Fetch64(s + 24);
|
||||
ulong a4 = Fetch64(s + 32);
|
||||
ulong a5 = Fetch64(s + 40);
|
||||
ulong a6 = Fetch64(s + 48);
|
||||
ulong a7 = Fetch64(s + 56);
|
||||
x += a0 + a1;
|
||||
y += a2;
|
||||
z += a3;
|
||||
v_first += a4;
|
||||
v_second += a5 + a1;
|
||||
w_first += a6;
|
||||
w_second += a7;
|
||||
|
||||
x = Rotate64(x, 26);
|
||||
x *= 9;
|
||||
y = Rotate64(y, 29);
|
||||
z *= mul;
|
||||
v_first = Rotate64(v_first, 33);
|
||||
v_second = Rotate64(v_second, 30);
|
||||
w_first ^= x;
|
||||
w_first *= 9;
|
||||
z = Rotate64(z, 32);
|
||||
z += w_second;
|
||||
w_second += z;
|
||||
z *= 9;
|
||||
swap(ref u, ref y);
|
||||
|
||||
z += a0 + a6;
|
||||
v_first += a2;
|
||||
v_second += a3;
|
||||
w_first += a4;
|
||||
w_second += a5 + a6;
|
||||
x += a1;
|
||||
y += a7;
|
||||
|
||||
y += v_first;
|
||||
v_first += x - y;
|
||||
v_second += w_first;
|
||||
w_first += v_second;
|
||||
w_second += x - y;
|
||||
x += w_second;
|
||||
w_second = Rotate64(w_second, 34);
|
||||
swap(ref u, ref z);
|
||||
s += 64;
|
||||
}
|
||||
while (s != end);
|
||||
|
||||
// Make s point to the last 64 bytes of input.
|
||||
s = last64;
|
||||
u *= 9;
|
||||
v_second = Rotate64(v_second, 28);
|
||||
v_first = Rotate64(v_first, 20);
|
||||
w_first += (len - 1) & 63;
|
||||
u += y;
|
||||
y += u;
|
||||
x = Rotate64(y - x + v_first + Fetch64(s + 8), 37) * mul;
|
||||
y = Rotate64(y ^ v_second ^ Fetch64(s + 48), 42) * mul;
|
||||
x ^= w_second * 9;
|
||||
y += v_first + Fetch64(s + 40);
|
||||
z = Rotate64(z + w_first, 33) * mul;
|
||||
WeakHashLen32WithSeeds(s, v_second * mul, x + w_first, out v_first, out v_second);
|
||||
WeakHashLen32WithSeeds(s + 32, z + w_second, y + Fetch64(s + 16), out w_first, out w_second);
|
||||
return H(HashLen16(v_first + x, w_first ^ y, mul) + z - u, H(v_second + y, w_second + z, k2, 30) ^ x, k2, 31);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/MessagePack/Internal/FarmHash.cs.meta
Normal file
11
Assets/Scripts/MessagePack/Internal/FarmHash.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fb5a050df97c09dd9b40337e9c0bbdcd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
391
Assets/Scripts/MessagePack/Internal/GuidBits.cs
Normal file
391
Assets/Scripts/MessagePack/Internal/GuidBits.cs
Normal file
|
|
@ -0,0 +1,391 @@
|
|||
// Copyright (c) All contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MessagePack.Internal
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit, Pack = 1)]
|
||||
internal struct GuidBits
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public readonly Guid Value;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public readonly byte Byte0;
|
||||
[FieldOffset(1)]
|
||||
public readonly byte Byte1;
|
||||
[FieldOffset(2)]
|
||||
public readonly byte Byte2;
|
||||
[FieldOffset(3)]
|
||||
public readonly byte Byte3;
|
||||
[FieldOffset(4)]
|
||||
public readonly byte Byte4;
|
||||
[FieldOffset(5)]
|
||||
public readonly byte Byte5;
|
||||
[FieldOffset(6)]
|
||||
public readonly byte Byte6;
|
||||
[FieldOffset(7)]
|
||||
public readonly byte Byte7;
|
||||
[FieldOffset(8)]
|
||||
public readonly byte Byte8;
|
||||
[FieldOffset(9)]
|
||||
public readonly byte Byte9;
|
||||
[FieldOffset(10)]
|
||||
public readonly byte Byte10;
|
||||
[FieldOffset(11)]
|
||||
public readonly byte Byte11;
|
||||
[FieldOffset(12)]
|
||||
public readonly byte Byte12;
|
||||
[FieldOffset(13)]
|
||||
public readonly byte Byte13;
|
||||
[FieldOffset(14)]
|
||||
public readonly byte Byte14;
|
||||
[FieldOffset(15)]
|
||||
public readonly byte Byte15;
|
||||
|
||||
// string.Join(", ", Enumerable.Range(0, 256).Select(x => (int)BitConverter.ToString(new byte[] { (byte)x }).ToLower()[0]))
|
||||
private static byte[] byteToHexStringHigh = new byte[256] { 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102 };
|
||||
|
||||
// string.Join(", ", Enumerable.Range(0, 256).Select(x => (int)BitConverter.ToString(new byte[] { (byte)x }).ToLower()[1]))
|
||||
private static byte[] byteToHexStringLow = new byte[256] { 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102 };
|
||||
|
||||
public GuidBits(ref Guid value)
|
||||
{
|
||||
this = default(GuidBits);
|
||||
this.Value = value;
|
||||
}
|
||||
|
||||
// 4-pattern, lower/upper and '-' or no
|
||||
public GuidBits(ReadOnlySpan<byte> utf8string)
|
||||
{
|
||||
this = default(GuidBits);
|
||||
|
||||
// 32
|
||||
if (utf8string.Length == 32)
|
||||
{
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
this.Byte0 = Parse(utf8string, 6);
|
||||
this.Byte1 = Parse(utf8string, 4);
|
||||
this.Byte2 = Parse(utf8string, 2);
|
||||
this.Byte3 = Parse(utf8string, 0);
|
||||
|
||||
this.Byte4 = Parse(utf8string, 10);
|
||||
this.Byte5 = Parse(utf8string, 8);
|
||||
|
||||
this.Byte6 = Parse(utf8string, 14);
|
||||
this.Byte7 = Parse(utf8string, 12);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Byte0 = Parse(utf8string, 0);
|
||||
this.Byte1 = Parse(utf8string, 2);
|
||||
this.Byte2 = Parse(utf8string, 4);
|
||||
this.Byte3 = Parse(utf8string, 6);
|
||||
|
||||
this.Byte4 = Parse(utf8string, 8);
|
||||
this.Byte5 = Parse(utf8string, 10);
|
||||
|
||||
this.Byte6 = Parse(utf8string, 12);
|
||||
this.Byte7 = Parse(utf8string, 14);
|
||||
}
|
||||
|
||||
this.Byte8 = Parse(utf8string, 16);
|
||||
this.Byte9 = Parse(utf8string, 18);
|
||||
|
||||
this.Byte10 = Parse(utf8string, 20);
|
||||
this.Byte11 = Parse(utf8string, 22);
|
||||
this.Byte12 = Parse(utf8string, 24);
|
||||
this.Byte13 = Parse(utf8string, 26);
|
||||
this.Byte14 = Parse(utf8string, 28);
|
||||
this.Byte15 = Parse(utf8string, 30);
|
||||
return;
|
||||
}
|
||||
else if (utf8string.Length == 36)
|
||||
{
|
||||
// '-' => 45
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
this.Byte0 = Parse(utf8string, 6);
|
||||
this.Byte1 = Parse(utf8string, 4);
|
||||
this.Byte2 = Parse(utf8string, 2);
|
||||
this.Byte3 = Parse(utf8string, 0);
|
||||
|
||||
if (utf8string[8] != '-')
|
||||
{
|
||||
goto ERROR;
|
||||
}
|
||||
|
||||
this.Byte4 = Parse(utf8string, 11);
|
||||
this.Byte5 = Parse(utf8string, 9);
|
||||
|
||||
if (utf8string[13] != '-')
|
||||
{
|
||||
goto ERROR;
|
||||
}
|
||||
|
||||
this.Byte6 = Parse(utf8string, 16);
|
||||
this.Byte7 = Parse(utf8string, 14);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Byte0 = Parse(utf8string, 0);
|
||||
this.Byte1 = Parse(utf8string, 2);
|
||||
this.Byte2 = Parse(utf8string, 4);
|
||||
this.Byte3 = Parse(utf8string, 6);
|
||||
|
||||
if (utf8string[8] != '-')
|
||||
{
|
||||
goto ERROR;
|
||||
}
|
||||
|
||||
this.Byte4 = Parse(utf8string, 9);
|
||||
this.Byte5 = Parse(utf8string, 11);
|
||||
|
||||
if (utf8string[13] != '-')
|
||||
{
|
||||
goto ERROR;
|
||||
}
|
||||
|
||||
this.Byte6 = Parse(utf8string, 14);
|
||||
this.Byte7 = Parse(utf8string, 16);
|
||||
}
|
||||
|
||||
if (utf8string[18] != '-')
|
||||
{
|
||||
goto ERROR;
|
||||
}
|
||||
|
||||
this.Byte8 = Parse(utf8string, 19);
|
||||
this.Byte9 = Parse(utf8string, 21);
|
||||
|
||||
if (utf8string[23] != '-')
|
||||
{
|
||||
goto ERROR;
|
||||
}
|
||||
|
||||
this.Byte10 = Parse(utf8string, 24);
|
||||
this.Byte11 = Parse(utf8string, 26);
|
||||
this.Byte12 = Parse(utf8string, 28);
|
||||
this.Byte13 = Parse(utf8string, 30);
|
||||
this.Byte14 = Parse(utf8string, 32);
|
||||
this.Byte15 = Parse(utf8string, 34);
|
||||
return;
|
||||
}
|
||||
|
||||
ERROR:
|
||||
throw new MessagePackSerializationException("Invalid Guid Pattern.");
|
||||
}
|
||||
|
||||
#if !UNITY_2018_3_OR_NEWER
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
private static byte Parse(ReadOnlySpan<byte> bytes, int highOffset)
|
||||
{
|
||||
return unchecked((byte)((SwitchParse(bytes[highOffset]) * 16) + SwitchParse(bytes[highOffset + 1])));
|
||||
}
|
||||
|
||||
#if !UNITY_2018_3_OR_NEWER
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
private static byte SwitchParse(byte b)
|
||||
{
|
||||
// '0'(48) ~ '9'(57) => -48
|
||||
// 'A'(65) ~ 'F'(70) => -55
|
||||
// 'a'(97) ~ 'f'(102) => -87
|
||||
switch (b)
|
||||
{
|
||||
case 48:
|
||||
case 49:
|
||||
case 50:
|
||||
case 51:
|
||||
case 52:
|
||||
case 53:
|
||||
case 54:
|
||||
case 55:
|
||||
case 56:
|
||||
case 57:
|
||||
return unchecked((byte)(b - 48));
|
||||
case 65:
|
||||
case 66:
|
||||
case 67:
|
||||
case 68:
|
||||
case 69:
|
||||
case 70:
|
||||
return unchecked((byte)(b - 55));
|
||||
case 97:
|
||||
case 98:
|
||||
case 99:
|
||||
case 100:
|
||||
case 101:
|
||||
case 102:
|
||||
return unchecked((byte)(b - 87));
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
case 9:
|
||||
case 10:
|
||||
case 11:
|
||||
case 12:
|
||||
case 13:
|
||||
case 14:
|
||||
case 15:
|
||||
case 16:
|
||||
case 17:
|
||||
case 18:
|
||||
case 19:
|
||||
case 20:
|
||||
case 21:
|
||||
case 22:
|
||||
case 23:
|
||||
case 24:
|
||||
case 25:
|
||||
case 26:
|
||||
case 27:
|
||||
case 28:
|
||||
case 29:
|
||||
case 30:
|
||||
case 31:
|
||||
case 32:
|
||||
case 33:
|
||||
case 34:
|
||||
case 35:
|
||||
case 36:
|
||||
case 37:
|
||||
case 38:
|
||||
case 39:
|
||||
case 40:
|
||||
case 41:
|
||||
case 42:
|
||||
case 43:
|
||||
case 44:
|
||||
case 45:
|
||||
case 46:
|
||||
case 47:
|
||||
case 58:
|
||||
case 59:
|
||||
case 60:
|
||||
case 61:
|
||||
case 62:
|
||||
case 63:
|
||||
case 64:
|
||||
case 71:
|
||||
case 72:
|
||||
case 73:
|
||||
case 74:
|
||||
case 75:
|
||||
case 76:
|
||||
case 77:
|
||||
case 78:
|
||||
case 79:
|
||||
case 80:
|
||||
case 81:
|
||||
case 82:
|
||||
case 83:
|
||||
case 84:
|
||||
case 85:
|
||||
case 86:
|
||||
case 87:
|
||||
case 88:
|
||||
case 89:
|
||||
case 90:
|
||||
case 91:
|
||||
case 92:
|
||||
case 93:
|
||||
case 94:
|
||||
case 95:
|
||||
case 96:
|
||||
default:
|
||||
throw new MessagePackSerializationException("Invalid Guid Pattern.");
|
||||
}
|
||||
}
|
||||
|
||||
// 4(x2) - 2(x2) - 2(x2) - 2(x2) - 6(x2)
|
||||
public void Write(Span<byte> buffer)
|
||||
{
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
// int(_a)
|
||||
buffer[6] = byteToHexStringHigh[this.Byte0];
|
||||
buffer[7] = byteToHexStringLow[this.Byte0];
|
||||
buffer[4] = byteToHexStringHigh[this.Byte1];
|
||||
buffer[5] = byteToHexStringLow[this.Byte1];
|
||||
buffer[2] = byteToHexStringHigh[this.Byte2];
|
||||
buffer[3] = byteToHexStringLow[this.Byte2];
|
||||
buffer[0] = byteToHexStringHigh[this.Byte3];
|
||||
buffer[1] = byteToHexStringLow[this.Byte3];
|
||||
|
||||
buffer[8] = (byte)'-';
|
||||
|
||||
// short(_b)
|
||||
buffer[11] = byteToHexStringHigh[this.Byte4];
|
||||
buffer[12] = byteToHexStringLow[this.Byte4];
|
||||
buffer[9] = byteToHexStringHigh[this.Byte5];
|
||||
buffer[10] = byteToHexStringLow[this.Byte5];
|
||||
|
||||
buffer[13] = (byte)'-';
|
||||
|
||||
// short(_c)
|
||||
buffer[16] = byteToHexStringHigh[this.Byte6];
|
||||
buffer[17] = byteToHexStringLow[this.Byte6];
|
||||
buffer[14] = byteToHexStringHigh[this.Byte7];
|
||||
buffer[15] = byteToHexStringLow[this.Byte7];
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer[0] = byteToHexStringHigh[this.Byte0];
|
||||
buffer[1] = byteToHexStringLow[this.Byte0];
|
||||
buffer[2] = byteToHexStringHigh[this.Byte1];
|
||||
buffer[3] = byteToHexStringLow[this.Byte1];
|
||||
buffer[4] = byteToHexStringHigh[this.Byte2];
|
||||
buffer[5] = byteToHexStringLow[this.Byte2];
|
||||
buffer[6] = byteToHexStringHigh[this.Byte3];
|
||||
buffer[7] = byteToHexStringLow[this.Byte3];
|
||||
|
||||
buffer[8] = (byte)'-';
|
||||
|
||||
buffer[9] = byteToHexStringHigh[this.Byte4];
|
||||
buffer[10] = byteToHexStringLow[this.Byte4];
|
||||
buffer[11] = byteToHexStringHigh[this.Byte5];
|
||||
buffer[12] = byteToHexStringLow[this.Byte5];
|
||||
|
||||
buffer[13] = (byte)'-';
|
||||
|
||||
buffer[14] = byteToHexStringHigh[this.Byte6];
|
||||
buffer[15] = byteToHexStringLow[this.Byte6];
|
||||
buffer[16] = byteToHexStringHigh[this.Byte7];
|
||||
buffer[17] = byteToHexStringLow[this.Byte7];
|
||||
}
|
||||
|
||||
buffer[18] = (byte)'-';
|
||||
|
||||
buffer[19] = byteToHexStringHigh[this.Byte8];
|
||||
buffer[20] = byteToHexStringLow[this.Byte8];
|
||||
buffer[21] = byteToHexStringHigh[this.Byte9];
|
||||
buffer[22] = byteToHexStringLow[this.Byte9];
|
||||
|
||||
buffer[23] = (byte)'-';
|
||||
|
||||
buffer[24] = byteToHexStringHigh[this.Byte10];
|
||||
buffer[25] = byteToHexStringLow[this.Byte10];
|
||||
buffer[26] = byteToHexStringHigh[this.Byte11];
|
||||
buffer[27] = byteToHexStringLow[this.Byte11];
|
||||
buffer[28] = byteToHexStringHigh[this.Byte12];
|
||||
buffer[29] = byteToHexStringLow[this.Byte12];
|
||||
buffer[30] = byteToHexStringHigh[this.Byte13];
|
||||
buffer[31] = byteToHexStringLow[this.Byte13];
|
||||
buffer[32] = byteToHexStringHigh[this.Byte14];
|
||||
buffer[33] = byteToHexStringLow[this.Byte14];
|
||||
buffer[34] = byteToHexStringHigh[this.Byte15];
|
||||
buffer[35] = byteToHexStringLow[this.Byte15];
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/MessagePack/Internal/GuidBits.cs.meta
Normal file
11
Assets/Scripts/MessagePack/Internal/GuidBits.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0bb83f98e290402db9fcb6da74f773ca
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
401
Assets/Scripts/MessagePack/Internal/ILGeneratorExtensions.cs
Normal file
401
Assets/Scripts/MessagePack/Internal/ILGeneratorExtensions.cs
Normal file
|
|
@ -0,0 +1,401 @@
|
|||
// Copyright (c) All contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#if !(UNITY_2018_3_OR_NEWER && NET_STANDARD_2_0)
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
#pragma warning disable SA1649 // File name should match first type name
|
||||
|
||||
namespace MessagePack.Internal
|
||||
{
|
||||
internal struct ArgumentField
|
||||
{
|
||||
private readonly int i;
|
||||
private readonly bool @ref;
|
||||
private readonly ILGenerator il;
|
||||
|
||||
public ArgumentField(ILGenerator il, int i, bool @ref = false)
|
||||
{
|
||||
this.il = il;
|
||||
this.i = i;
|
||||
this.@ref = @ref;
|
||||
}
|
||||
|
||||
public ArgumentField(ILGenerator il, int i, Type type)
|
||||
{
|
||||
this.il = il;
|
||||
this.i = i;
|
||||
TypeInfo ti = type.GetTypeInfo();
|
||||
this.@ref = (ti.IsClass || ti.IsInterface || ti.IsAbstract) ? false : true;
|
||||
}
|
||||
|
||||
public void EmitLoad()
|
||||
{
|
||||
if (this.@ref)
|
||||
{
|
||||
this.il.EmitLdarga(this.i);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.il.EmitLdarg(this.i);
|
||||
}
|
||||
}
|
||||
|
||||
public void EmitLdarg()
|
||||
{
|
||||
this.il.EmitLdarg(this.i);
|
||||
}
|
||||
|
||||
public void EmitLdarga()
|
||||
{
|
||||
this.il.EmitLdarga(this.i);
|
||||
}
|
||||
|
||||
public void EmitStore()
|
||||
{
|
||||
this.il.EmitStarg(this.i);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides optimized generation code and helpers.
|
||||
/// </summary>
|
||||
internal static class ILGeneratorExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads the local variable at a specific index onto the evaluation stack.
|
||||
/// </summary>
|
||||
public static void EmitLdloc(this ILGenerator il, int index)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
il.Emit(OpCodes.Ldloc_0);
|
||||
break;
|
||||
case 1:
|
||||
il.Emit(OpCodes.Ldloc_1);
|
||||
break;
|
||||
case 2:
|
||||
il.Emit(OpCodes.Ldloc_2);
|
||||
break;
|
||||
case 3:
|
||||
il.Emit(OpCodes.Ldloc_3);
|
||||
break;
|
||||
default:
|
||||
if (index <= 255)
|
||||
{
|
||||
il.Emit(OpCodes.Ldloc_S, (byte)index);
|
||||
}
|
||||
else
|
||||
{
|
||||
il.Emit(OpCodes.Ldloc, (short)index);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitLdloc(this ILGenerator il, LocalBuilder local)
|
||||
{
|
||||
EmitLdloc(il, local.LocalIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pops the current value from the top of the evaluation stack and stores it in a the local variable list at a specified index.
|
||||
/// </summary>
|
||||
public static void EmitStloc(this ILGenerator il, int index)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
il.Emit(OpCodes.Stloc_0);
|
||||
break;
|
||||
case 1:
|
||||
il.Emit(OpCodes.Stloc_1);
|
||||
break;
|
||||
case 2:
|
||||
il.Emit(OpCodes.Stloc_2);
|
||||
break;
|
||||
case 3:
|
||||
il.Emit(OpCodes.Stloc_3);
|
||||
break;
|
||||
default:
|
||||
if (index <= 255)
|
||||
{
|
||||
il.Emit(OpCodes.Stloc_S, (byte)index);
|
||||
}
|
||||
else
|
||||
{
|
||||
il.Emit(OpCodes.Stloc, (short)index);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitStloc(this ILGenerator il, LocalBuilder local)
|
||||
{
|
||||
EmitStloc(il, local.LocalIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the address of the local variable at a specific index onto the evaluation statck.
|
||||
/// </summary>
|
||||
public static void EmitLdloca(this ILGenerator il, int index)
|
||||
{
|
||||
if (index <= 255)
|
||||
{
|
||||
il.Emit(OpCodes.Ldloca_S, (byte)index);
|
||||
}
|
||||
else
|
||||
{
|
||||
il.Emit(OpCodes.Ldloca, (short)index);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitLdloca(this ILGenerator il, LocalBuilder local)
|
||||
{
|
||||
EmitLdloca(il, local.LocalIndex);
|
||||
}
|
||||
|
||||
public static void EmitTrue(this ILGenerator il)
|
||||
{
|
||||
EmitBoolean(il, true);
|
||||
}
|
||||
|
||||
public static void EmitFalse(this ILGenerator il)
|
||||
{
|
||||
EmitBoolean(il, false);
|
||||
}
|
||||
|
||||
public static void EmitBoolean(this ILGenerator il, bool value)
|
||||
{
|
||||
EmitLdc_I4(il, value ? 1 : 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes a supplied value of type int32 onto the evaluation stack as an int32.
|
||||
/// </summary>
|
||||
public static void EmitLdc_I4(this ILGenerator il, int value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case -1:
|
||||
il.Emit(OpCodes.Ldc_I4_M1);
|
||||
break;
|
||||
case 0:
|
||||
il.Emit(OpCodes.Ldc_I4_0);
|
||||
break;
|
||||
case 1:
|
||||
il.Emit(OpCodes.Ldc_I4_1);
|
||||
break;
|
||||
case 2:
|
||||
il.Emit(OpCodes.Ldc_I4_2);
|
||||
break;
|
||||
case 3:
|
||||
il.Emit(OpCodes.Ldc_I4_3);
|
||||
break;
|
||||
case 4:
|
||||
il.Emit(OpCodes.Ldc_I4_4);
|
||||
break;
|
||||
case 5:
|
||||
il.Emit(OpCodes.Ldc_I4_5);
|
||||
break;
|
||||
case 6:
|
||||
il.Emit(OpCodes.Ldc_I4_6);
|
||||
break;
|
||||
case 7:
|
||||
il.Emit(OpCodes.Ldc_I4_7);
|
||||
break;
|
||||
case 8:
|
||||
il.Emit(OpCodes.Ldc_I4_8);
|
||||
break;
|
||||
default:
|
||||
if (value >= -128 && value <= 127)
|
||||
{
|
||||
il.Emit(OpCodes.Ldc_I4_S, (sbyte)value);
|
||||
}
|
||||
else
|
||||
{
|
||||
il.Emit(OpCodes.Ldc_I4, value);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitUnboxOrCast(this ILGenerator il, Type type)
|
||||
{
|
||||
if (type.GetTypeInfo().IsValueType)
|
||||
{
|
||||
il.Emit(OpCodes.Unbox_Any, type);
|
||||
}
|
||||
else
|
||||
{
|
||||
il.Emit(OpCodes.Castclass, type);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitBoxOrDoNothing(this ILGenerator il, Type type)
|
||||
{
|
||||
if (type.GetTypeInfo().IsValueType)
|
||||
{
|
||||
il.Emit(OpCodes.Box, type);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitLdarg(this ILGenerator il, int index)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
break;
|
||||
case 1:
|
||||
il.Emit(OpCodes.Ldarg_1);
|
||||
break;
|
||||
case 2:
|
||||
il.Emit(OpCodes.Ldarg_2);
|
||||
break;
|
||||
case 3:
|
||||
il.Emit(OpCodes.Ldarg_3);
|
||||
break;
|
||||
default:
|
||||
if (index <= 255)
|
||||
{
|
||||
il.Emit(OpCodes.Ldarg_S, (byte)index);
|
||||
}
|
||||
else
|
||||
{
|
||||
il.Emit(OpCodes.Ldarg, index);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitLoadThis(this ILGenerator il)
|
||||
{
|
||||
EmitLdarg(il, 0);
|
||||
}
|
||||
|
||||
public static void EmitLdarga(this ILGenerator il, int index)
|
||||
{
|
||||
if (index <= 255)
|
||||
{
|
||||
il.Emit(OpCodes.Ldarga_S, (byte)index);
|
||||
}
|
||||
else
|
||||
{
|
||||
il.Emit(OpCodes.Ldarga, index);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitStarg(this ILGenerator il, int index)
|
||||
{
|
||||
if (index <= 255)
|
||||
{
|
||||
il.Emit(OpCodes.Starg_S, (byte)index);
|
||||
}
|
||||
else
|
||||
{
|
||||
il.Emit(OpCodes.Starg, index);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper for Pop op.
|
||||
/// </summary>
|
||||
public static void EmitPop(this ILGenerator il, int count)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
il.Emit(OpCodes.Pop);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitCall(this ILGenerator il, MethodInfo methodInfo)
|
||||
{
|
||||
if (methodInfo.IsFinal || !methodInfo.IsVirtual)
|
||||
{
|
||||
il.Emit(OpCodes.Call, methodInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
il.Emit(OpCodes.Callvirt, methodInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitLdfld(this ILGenerator il, FieldInfo fieldInfo)
|
||||
{
|
||||
il.Emit(OpCodes.Ldfld, fieldInfo);
|
||||
}
|
||||
|
||||
public static void EmitLdsfld(this ILGenerator il, FieldInfo fieldInfo)
|
||||
{
|
||||
il.Emit(OpCodes.Ldsfld, fieldInfo);
|
||||
}
|
||||
|
||||
public static void EmitRet(this ILGenerator il)
|
||||
{
|
||||
il.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
public static void EmitIntZeroReturn(this ILGenerator il)
|
||||
{
|
||||
il.EmitLdc_I4(0);
|
||||
il.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
public static void EmitNullReturn(this ILGenerator il)
|
||||
{
|
||||
il.Emit(OpCodes.Ldnull);
|
||||
il.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
public static void EmitULong(this ILGenerator il, ulong value)
|
||||
{
|
||||
il.Emit(OpCodes.Ldc_I8, unchecked((long)value));
|
||||
}
|
||||
|
||||
public static void EmitThrowNotimplemented(this ILGenerator il)
|
||||
{
|
||||
il.Emit(OpCodes.Newobj, typeof(System.NotImplementedException).GetTypeInfo().DeclaredConstructors.First(x => x.GetParameters().Length == 0));
|
||||
il.Emit(OpCodes.Throw);
|
||||
}
|
||||
|
||||
/// <summary>for var i = 0, i ..., i++. </summary>
|
||||
public static void EmitIncrementFor(this ILGenerator il, LocalBuilder conditionGreater, Action<LocalBuilder> emitBody)
|
||||
{
|
||||
Label loopBegin = il.DefineLabel();
|
||||
Label condtionLabel = il.DefineLabel();
|
||||
|
||||
// var i = 0
|
||||
LocalBuilder forI = il.DeclareLocal(typeof(int));
|
||||
il.EmitLdc_I4(0);
|
||||
il.EmitStloc(forI);
|
||||
il.Emit(OpCodes.Br, condtionLabel);
|
||||
|
||||
il.MarkLabel(loopBegin);
|
||||
emitBody(forI);
|
||||
|
||||
// i++
|
||||
il.EmitLdloc(forI);
|
||||
il.EmitLdc_I4(1);
|
||||
il.Emit(OpCodes.Add);
|
||||
il.EmitStloc(forI);
|
||||
|
||||
//// i < ***
|
||||
il.MarkLabel(condtionLabel);
|
||||
il.EmitLdloc(forI);
|
||||
il.EmitLdloc(conditionGreater);
|
||||
il.Emit(OpCodes.Blt, loopBegin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a7fcc89bc1ce33b46b7e5d8bcaa7f0fa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
49
Assets/Scripts/MessagePack/Internal/ReflectionExtensions.cs
Normal file
49
Assets/Scripts/MessagePack/Internal/ReflectionExtensions.cs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
// Copyright (c) All contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace MessagePack.Internal
|
||||
{
|
||||
internal static class ReflectionExtensions
|
||||
{
|
||||
public static bool IsNullable(this System.Reflection.TypeInfo type)
|
||||
{
|
||||
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(System.Nullable<>);
|
||||
}
|
||||
|
||||
public static bool IsPublic(this System.Reflection.TypeInfo type)
|
||||
{
|
||||
return type.IsPublic;
|
||||
}
|
||||
|
||||
public static bool IsAnonymous(this System.Reflection.TypeInfo type)
|
||||
{
|
||||
return type.GetCustomAttribute<CompilerGeneratedAttribute>() != null
|
||||
&& type.IsGenericType && type.Name.Contains("AnonymousType")
|
||||
&& (type.Name.StartsWith("<>") || type.Name.StartsWith("VB$"))
|
||||
&& (type.Attributes & TypeAttributes.NotPublic) == TypeAttributes.NotPublic;
|
||||
}
|
||||
|
||||
public static bool IsIndexer(this System.Reflection.PropertyInfo propertyInfo)
|
||||
{
|
||||
return propertyInfo.GetIndexParameters().Length > 0;
|
||||
}
|
||||
|
||||
public static bool IsConstructedGenericType(this System.Reflection.TypeInfo type)
|
||||
{
|
||||
return type.AsType().IsConstructedGenericType;
|
||||
}
|
||||
|
||||
public static MethodInfo GetGetMethod(this PropertyInfo propInfo)
|
||||
{
|
||||
return propInfo.GetMethod;
|
||||
}
|
||||
|
||||
public static MethodInfo GetSetMethod(this PropertyInfo propInfo)
|
||||
{
|
||||
return propInfo.SetMethod;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 82787207c36d0a4a98d7f53b49ba1576
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
573
Assets/Scripts/MessagePack/Internal/Sequence`1.cs
Normal file
573
Assets/Scripts/MessagePack/Internal/Sequence`1.cs
Normal file
|
|
@ -0,0 +1,573 @@
|
|||
// Copyright (c) All contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
/* Original license and copyright from file copied from https://github.com/AArnott/Nerdbank.Streams/blob/d656899be26d4d7c72c11c9232b4952c64a89bcb/src/Nerdbank.Streams/Sequence%601.cs
|
||||
* Copyright (c) Andrew Arnott. All rights reserved.
|
||||
* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Nerdbank.Streams
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages a sequence of elements, readily castable as a <see cref="ReadOnlySequence{T}"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of element stored by the sequence.</typeparam>
|
||||
/// <remarks>
|
||||
/// Instance members are not thread-safe.
|
||||
/// </remarks>
|
||||
[DebuggerDisplay("{" + nameof(DebuggerDisplay) + ",nq}")]
|
||||
internal class Sequence<T> : IBufferWriter<T>, IDisposable
|
||||
{
|
||||
private static readonly int DefaultLengthFromArrayPool = 1 + (4095 / Marshal.SizeOf<T>());
|
||||
|
||||
private readonly Stack<SequenceSegment> segmentPool = new Stack<SequenceSegment>();
|
||||
|
||||
private readonly MemoryPool<T> memoryPool;
|
||||
|
||||
private readonly ArrayPool<T> arrayPool;
|
||||
|
||||
private SequenceSegment first;
|
||||
|
||||
private SequenceSegment last;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Sequence{T}"/> class
|
||||
/// that uses a private <see cref="ArrayPool{T}"/> for recycling arrays.
|
||||
/// </summary>
|
||||
public Sequence()
|
||||
: this(ArrayPool<T>.Create())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Sequence{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="memoryPool">The pool to use for recycling backing arrays.</param>
|
||||
public Sequence(MemoryPool<T> memoryPool)
|
||||
{
|
||||
Requires.NotNull(memoryPool, nameof(memoryPool));
|
||||
this.memoryPool = memoryPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Sequence{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="arrayPool">The pool to use for recycling backing arrays.</param>
|
||||
public Sequence(ArrayPool<T> arrayPool)
|
||||
{
|
||||
Requires.NotNull(arrayPool, nameof(arrayPool));
|
||||
this.arrayPool = arrayPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum length for any array allocated as a segment in the sequence.
|
||||
/// Any non-positive value allows the pool to determine the length of the array.
|
||||
/// </summary>
|
||||
/// <value>The default value is 0.</value>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Each time <see cref="GetSpan(int)"/> or <see cref="GetMemory(int)"/> is called,
|
||||
/// previously allocated memory is used if it is large enough to satisfy the length demand.
|
||||
/// If new memory must be allocated, the argument to one of these methods typically dictate
|
||||
/// the length of array to allocate. When the caller uses very small values (just enough for its immediate need)
|
||||
/// but the high level scenario can predict that a large amount of memory will be ultimately required,
|
||||
/// it can be advisable to set this property to a value such that just a few larger arrays are allocated
|
||||
/// instead of many small ones.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The <see cref="MemoryPool{T}"/> in use may itself have a minimum array length as well,
|
||||
/// in which case the higher of the two minimums dictate the minimum array size that will be allocated.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public int MinimumSpanLength { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets this sequence expressed as a <see cref="ReadOnlySequence{T}"/>.
|
||||
/// </summary>
|
||||
/// <returns>A read only sequence representing the data in this object.</returns>
|
||||
public ReadOnlySequence<T> AsReadOnlySequence => this;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the sequence.
|
||||
/// </summary>
|
||||
public long Length => this.AsReadOnlySequence.Length;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value to display in a debugger datatip.
|
||||
/// </summary>
|
||||
private string DebuggerDisplay => $"Length: {this.AsReadOnlySequence.Length}";
|
||||
|
||||
/// <summary>
|
||||
/// Expresses this sequence as a <see cref="ReadOnlySequence{T}"/>.
|
||||
/// </summary>
|
||||
/// <param name="sequence">The sequence to convert.</param>
|
||||
public static implicit operator ReadOnlySequence<T>(Sequence<T> sequence)
|
||||
{
|
||||
return sequence.first != null
|
||||
? new ReadOnlySequence<T>(sequence.first, sequence.first.Start, sequence.last, sequence.last.End)
|
||||
: ReadOnlySequence<T>.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all elements from the sequence from its beginning to the specified position,
|
||||
/// considering that data to have been fully processed.
|
||||
/// </summary>
|
||||
/// <param name="position">
|
||||
/// The position of the first element that has not yet been processed.
|
||||
/// This is typically <see cref="ReadOnlySequence{T}.End"/> after reading all elements from that instance.
|
||||
/// </param>
|
||||
public void AdvanceTo(SequencePosition position)
|
||||
{
|
||||
var firstSegment = (SequenceSegment)position.GetObject();
|
||||
int firstIndex = position.GetInteger();
|
||||
|
||||
// Before making any mutations, confirm that the block specified belongs to this sequence.
|
||||
var current = this.first;
|
||||
while (current != firstSegment && current != null)
|
||||
{
|
||||
current = current.Next;
|
||||
}
|
||||
|
||||
Requires.Argument(current != null, nameof(position), "Position does not represent a valid position in this sequence.");
|
||||
|
||||
// Also confirm that the position is not a prior position in the block.
|
||||
Requires.Argument(firstIndex >= current.Start, nameof(position), "Position must not be earlier than current position.");
|
||||
|
||||
// Now repeat the loop, performing the mutations.
|
||||
current = this.first;
|
||||
while (current != firstSegment)
|
||||
{
|
||||
current = this.RecycleAndGetNext(current);
|
||||
}
|
||||
|
||||
firstSegment.AdvanceTo(firstIndex);
|
||||
|
||||
if (firstSegment.Length == 0)
|
||||
{
|
||||
firstSegment = this.RecycleAndGetNext(firstSegment);
|
||||
}
|
||||
|
||||
this.first = firstSegment;
|
||||
|
||||
if (this.first == null)
|
||||
{
|
||||
this.last = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Advances the sequence to include the specified number of elements initialized into memory
|
||||
/// returned by a prior call to <see cref="GetMemory(int)"/>.
|
||||
/// </summary>
|
||||
/// <param name="count">The number of elements written into memory.</param>
|
||||
public void Advance(int count)
|
||||
{
|
||||
SequenceSegment last = this.last;
|
||||
Verify.Operation(last != null, "Cannot advance before acquiring memory.");
|
||||
last.Advance(count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets writable memory that can be initialized and added to the sequence via a subsequent call to <see cref="Advance(int)"/>.
|
||||
/// </summary>
|
||||
/// <param name="sizeHint">The size of the memory required, or 0 to just get a convenient (non-empty) buffer.</param>
|
||||
/// <returns>The requested memory.</returns>
|
||||
public Memory<T> GetMemory(int sizeHint) => this.GetSegment(sizeHint).RemainingMemory;
|
||||
|
||||
/// <summary>
|
||||
/// Gets writable memory that can be initialized and added to the sequence via a subsequent call to <see cref="Advance(int)"/>.
|
||||
/// </summary>
|
||||
/// <param name="sizeHint">The size of the memory required, or 0 to just get a convenient (non-empty) buffer.</param>
|
||||
/// <returns>The requested memory.</returns>
|
||||
public Span<T> GetSpan(int sizeHint) => this.GetSegment(sizeHint).RemainingSpan;
|
||||
|
||||
/// <summary>
|
||||
/// Clears the entire sequence, recycles associated memory into pools,
|
||||
/// and resets this instance for reuse.
|
||||
/// This invalidates any <see cref="ReadOnlySequence{T}"/> previously produced by this instance.
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void Dispose() => this.Reset();
|
||||
|
||||
/// <summary>
|
||||
/// Clears the entire sequence and recycles associated memory into pools.
|
||||
/// This invalidates any <see cref="ReadOnlySequence{T}"/> previously produced by this instance.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
var current = this.first;
|
||||
while (current != null)
|
||||
{
|
||||
current = this.RecycleAndGetNext(current);
|
||||
}
|
||||
|
||||
this.first = this.last = null;
|
||||
}
|
||||
|
||||
private SequenceSegment GetSegment(int sizeHint)
|
||||
{
|
||||
Requires.Range(sizeHint >= 0, nameof(sizeHint));
|
||||
int? minBufferSize = null;
|
||||
if (sizeHint == 0)
|
||||
{
|
||||
if (this.last == null || this.last.WritableBytes == 0)
|
||||
{
|
||||
// We're going to need more memory. Take whatever size the pool wants to give us.
|
||||
minBufferSize = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sizeHint = Math.Max(this.MinimumSpanLength, sizeHint);
|
||||
if (this.last == null || this.last.WritableBytes < sizeHint)
|
||||
{
|
||||
minBufferSize = sizeHint;
|
||||
}
|
||||
}
|
||||
|
||||
if (minBufferSize.HasValue)
|
||||
{
|
||||
var segment = this.segmentPool.Count > 0 ? this.segmentPool.Pop() : new SequenceSegment();
|
||||
if (this.arrayPool != null)
|
||||
{
|
||||
segment.Assign(this.arrayPool.Rent(minBufferSize.Value == -1 ? DefaultLengthFromArrayPool : minBufferSize.Value));
|
||||
}
|
||||
else
|
||||
{
|
||||
segment.Assign(this.memoryPool.Rent(minBufferSize.Value));
|
||||
}
|
||||
|
||||
this.Append(segment);
|
||||
}
|
||||
|
||||
return this.last;
|
||||
}
|
||||
|
||||
private void Append(SequenceSegment segment)
|
||||
{
|
||||
if (this.last == null)
|
||||
{
|
||||
this.first = this.last = segment;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.last.Length > 0)
|
||||
{
|
||||
// Add a new block.
|
||||
this.last.SetNext(segment);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The last block is completely unused. Replace it instead of appending to it.
|
||||
var current = this.first;
|
||||
if (this.first != this.last)
|
||||
{
|
||||
while (current.Next != this.last)
|
||||
{
|
||||
current = current.Next;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.first = segment;
|
||||
}
|
||||
|
||||
current.SetNext(segment);
|
||||
this.RecycleAndGetNext(this.last);
|
||||
}
|
||||
|
||||
this.last = segment;
|
||||
}
|
||||
}
|
||||
|
||||
private SequenceSegment RecycleAndGetNext(SequenceSegment segment)
|
||||
{
|
||||
var recycledSegment = segment;
|
||||
segment = segment.Next;
|
||||
recycledSegment.ResetMemory(this.arrayPool);
|
||||
this.segmentPool.Push(recycledSegment);
|
||||
return segment;
|
||||
}
|
||||
|
||||
private class SequenceSegment : ReadOnlySequenceSegment<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// A value indicating whether the element is a value type.
|
||||
/// </summary>
|
||||
private static readonly bool IsValueTypeElement = typeof(T).GetTypeInfo().IsValueType;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the backing array, when using an <see cref="ArrayPool{T}"/> instead of a <see cref="MemoryPool{T}"/>.
|
||||
/// </summary>
|
||||
private T[] array;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the position within <see cref="ReadOnlySequenceSegment{T}.Memory"/> where the data starts.
|
||||
/// </summary>
|
||||
/// <remarks>This may be nonzero as a result of calling <see cref="Sequence{T}.AdvanceTo(SequencePosition)"/>.</remarks>
|
||||
internal int Start { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the position within <see cref="ReadOnlySequenceSegment{T}.Memory"/> where the data ends.
|
||||
/// </summary>
|
||||
internal int End { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tail of memory that has not yet been committed.
|
||||
/// </summary>
|
||||
internal Memory<T> RemainingMemory => this.AvailableMemory.Slice(this.End);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tail of memory that has not yet been committed.
|
||||
/// </summary>
|
||||
internal Span<T> RemainingSpan => this.AvailableMemory.Span.Slice(this.End);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tracker for the underlying array for this segment, which can be used to recycle the array when we're disposed of.
|
||||
/// Will be <c>null</c> if using an array pool, in which case the memory is held by <see cref="array"/>.
|
||||
/// </summary>
|
||||
internal IMemoryOwner<T> MemoryOwner { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full memory owned by the <see cref="MemoryOwner"/>.
|
||||
/// </summary>
|
||||
internal Memory<T> AvailableMemory => this.array ?? this.MemoryOwner?.Memory ?? default;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of elements that are committed in this segment.
|
||||
/// </summary>
|
||||
internal int Length => this.End - this.Start;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of writable bytes in this segment.
|
||||
/// It is the amount of bytes between <see cref="Length"/> and <see cref="End"/>.
|
||||
/// </summary>
|
||||
internal int WritableBytes => this.AvailableMemory.Length - this.End;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the next segment in the singly linked list of segments.
|
||||
/// </summary>
|
||||
internal new SequenceSegment Next
|
||||
{
|
||||
get => (SequenceSegment)base.Next;
|
||||
set => base.Next = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assigns this (recyclable) segment a new area in memory.
|
||||
/// </summary>
|
||||
/// <param name="memoryOwner">The memory and a means to recycle it.</param>
|
||||
internal void Assign(IMemoryOwner<T> memoryOwner)
|
||||
{
|
||||
this.MemoryOwner = memoryOwner;
|
||||
this.Memory = memoryOwner.Memory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assigns this (recyclable) segment a new area in memory.
|
||||
/// </summary>
|
||||
/// <param name="array">An array drawn from an <see cref="ArrayPool{T}"/>.</param>
|
||||
internal void Assign(T[] array)
|
||||
{
|
||||
this.array = array;
|
||||
this.Memory = array;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all fields in preparation to recycle this instance.
|
||||
/// </summary>
|
||||
internal void ResetMemory(ArrayPool<T> arrayPool)
|
||||
{
|
||||
this.ClearReferences(this.Start, this.End);
|
||||
this.Memory = default;
|
||||
this.Next = null;
|
||||
this.RunningIndex = 0;
|
||||
this.Start = 0;
|
||||
this.End = 0;
|
||||
if (this.array != null)
|
||||
{
|
||||
arrayPool.Return(this.array);
|
||||
this.array = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.MemoryOwner?.Dispose();
|
||||
this.MemoryOwner = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new segment after this one.
|
||||
/// </summary>
|
||||
/// <param name="segment">The next segment in the linked list.</param>
|
||||
internal void SetNext(SequenceSegment segment)
|
||||
{
|
||||
Debug.Assert(segment != null, "Null not allowed.");
|
||||
this.Next = segment;
|
||||
segment.RunningIndex = this.RunningIndex + this.Start + this.Length;
|
||||
|
||||
// When setting Memory, we start with index 0 instead of this.Start because
|
||||
// the first segment has an explicit index set anyway,
|
||||
// and we don't want to double-count it here.
|
||||
this.Memory = this.AvailableMemory.Slice(0, this.Start + this.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Commits more elements as written in this segment.
|
||||
/// </summary>
|
||||
/// <param name="count">The number of elements written.</param>
|
||||
internal void Advance(int count)
|
||||
{
|
||||
Requires.Range(count >= 0 && this.End + count <= this.Memory.Length, nameof(count));
|
||||
this.End += count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes some elements from the start of this segment.
|
||||
/// </summary>
|
||||
/// <param name="offset">The number of elements to ignore from the start of the underlying array.</param>
|
||||
internal void AdvanceTo(int offset)
|
||||
{
|
||||
Debug.Assert(offset >= this.Start, "Trying to rewind.");
|
||||
this.ClearReferences(this.Start, offset - this.Start);
|
||||
this.Start = offset;
|
||||
}
|
||||
|
||||
private void ClearReferences(int startIndex, int length)
|
||||
{
|
||||
// If we store references, clear them to allow the objects to be GC'd.
|
||||
if (!IsValueTypeElement)
|
||||
{
|
||||
this.AvailableMemory.Span.Slice(startIndex, length).Fill(default);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static class Requires
|
||||
{
|
||||
/// <summary>
|
||||
/// Throws an <see cref="ArgumentOutOfRangeException"/> if a condition does not evaluate to true.
|
||||
/// </summary>
|
||||
[DebuggerStepThrough]
|
||||
public static void Range(bool condition, string parameterName, string message = null)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
FailRange(parameterName, message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws an <see cref="ArgumentOutOfRangeException"/> if a condition does not evaluate to true.
|
||||
/// </summary>
|
||||
/// <returns>Nothing. This method always throws.</returns>
|
||||
[DebuggerStepThrough]
|
||||
public static Exception FailRange(string parameterName, string message = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(message))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(parameterName);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(parameterName, message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws an exception if the specified parameter's value is null.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the parameter.</typeparam>
|
||||
/// <param name="value">The value of the argument.</param>
|
||||
/// <param name="parameterName">The name of the parameter to include in any thrown exception.</param>
|
||||
/// <returns>The value of the parameter.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown if <paramref name="value"/> is <c>null</c>.</exception>
|
||||
[DebuggerStepThrough]
|
||||
public static T NotNull<T>(T value, string parameterName)
|
||||
where T : class // ensures value-types aren't passed to a null checking method
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(parameterName);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws an ArgumentException if a condition does not evaluate to true.
|
||||
/// </summary>
|
||||
[DebuggerStepThrough]
|
||||
public static void Argument(bool condition, string parameterName, string message)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
throw new ArgumentException(message, parameterName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws an ArgumentException if a condition does not evaluate to true.
|
||||
/// </summary>
|
||||
[DebuggerStepThrough]
|
||||
public static void Argument(bool condition, string parameterName, string message, object arg1)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
throw new ArgumentException(String.Format(message, arg1), parameterName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws an ArgumentException if a condition does not evaluate to true.
|
||||
/// </summary>
|
||||
[DebuggerStepThrough]
|
||||
public static void Argument(bool condition, string parameterName, string message, object arg1, object arg2)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
throw new ArgumentException(String.Format(message, arg1, arg2), parameterName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws an ArgumentException if a condition does not evaluate to true.
|
||||
/// </summary>
|
||||
[DebuggerStepThrough]
|
||||
public static void Argument(bool condition, string parameterName, string message, params object[] args)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
throw new ArgumentException(String.Format(message, args), parameterName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Common runtime checks that throw exceptions upon failure.
|
||||
/// </summary>
|
||||
internal static partial class Verify
|
||||
{
|
||||
/// <summary>
|
||||
/// Throws an <see cref="InvalidOperationException"/> if a condition is false.
|
||||
/// </summary>
|
||||
[DebuggerStepThrough]
|
||||
internal static void Operation(bool condition, string message)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/MessagePack/Internal/Sequence`1.cs.meta
Normal file
11
Assets/Scripts/MessagePack/Internal/Sequence`1.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 19e730b2bfc50b981bdcd41c703daab2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,246 @@
|
|||
// Copyright (c) All contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
#pragma warning disable SA1649 // File name should match first type name
|
||||
|
||||
namespace MessagePack.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// A dictionary where <see cref="Type"/> is the key, and a configurable <typeparamref name="TValue"/> type
|
||||
/// that is thread-safe to read and write, allowing concurrent reads and exclusive writes.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">The type of value stored in the dictionary.</typeparam>
|
||||
internal class ThreadsafeTypeKeyHashTable<TValue>
|
||||
{
|
||||
private Entry[] buckets;
|
||||
private int size; // only use in writer lock
|
||||
|
||||
private readonly object writerLock = new object();
|
||||
private readonly float loadFactor;
|
||||
|
||||
// IEqualityComparer.Equals is overhead if key only Type, don't use it.
|
||||
//// readonly IEqualityComparer<TKey> comparer;
|
||||
|
||||
public ThreadsafeTypeKeyHashTable(int capacity = 4, float loadFactor = 0.75f)
|
||||
{
|
||||
var tableSize = CalculateCapacity(capacity, loadFactor);
|
||||
this.buckets = new Entry[tableSize];
|
||||
this.loadFactor = loadFactor;
|
||||
}
|
||||
|
||||
public bool TryAdd(Type key, TValue value)
|
||||
{
|
||||
return this.TryAdd(key, _ => value); // create lambda capture
|
||||
}
|
||||
|
||||
public bool TryAdd(Type key, Func<Type, TValue> valueFactory)
|
||||
{
|
||||
return this.TryAddInternal(key, valueFactory, out TValue _);
|
||||
}
|
||||
|
||||
private bool TryAddInternal(Type key, Func<Type, TValue> valueFactory, out TValue resultingValue)
|
||||
{
|
||||
lock (this.writerLock)
|
||||
{
|
||||
var nextCapacity = CalculateCapacity(this.size + 1, this.loadFactor);
|
||||
|
||||
if (this.buckets.Length < nextCapacity)
|
||||
{
|
||||
// rehash
|
||||
var nextBucket = new Entry[nextCapacity];
|
||||
for (int i = 0; i < this.buckets.Length; i++)
|
||||
{
|
||||
Entry e = this.buckets[i];
|
||||
while (e != null)
|
||||
{
|
||||
var newEntry = new Entry { Key = e.Key, Value = e.Value, Hash = e.Hash };
|
||||
this.AddToBuckets(nextBucket, key, newEntry, null, out resultingValue);
|
||||
e = e.Next;
|
||||
}
|
||||
}
|
||||
|
||||
// add entry(if failed to add, only do resize)
|
||||
var successAdd = this.AddToBuckets(nextBucket, key, null, valueFactory, out resultingValue);
|
||||
|
||||
// replace field(threadsafe for read)
|
||||
VolatileWrite(ref this.buckets, nextBucket);
|
||||
|
||||
if (successAdd)
|
||||
{
|
||||
this.size++;
|
||||
}
|
||||
|
||||
return successAdd;
|
||||
}
|
||||
else
|
||||
{
|
||||
// add entry(insert last is thread safe for read)
|
||||
var successAdd = this.AddToBuckets(this.buckets, key, null, valueFactory, out resultingValue);
|
||||
if (successAdd)
|
||||
{
|
||||
this.size++;
|
||||
}
|
||||
|
||||
return successAdd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool AddToBuckets(Entry[] buckets, Type newKey, Entry newEntryOrNull, Func<Type, TValue> valueFactory, out TValue resultingValue)
|
||||
{
|
||||
var h = (newEntryOrNull != null) ? newEntryOrNull.Hash : newKey.GetHashCode();
|
||||
if (buckets[h & (buckets.Length - 1)] == null)
|
||||
{
|
||||
if (newEntryOrNull != null)
|
||||
{
|
||||
resultingValue = newEntryOrNull.Value;
|
||||
VolatileWrite(ref buckets[h & (buckets.Length - 1)], newEntryOrNull);
|
||||
}
|
||||
else
|
||||
{
|
||||
resultingValue = valueFactory(newKey);
|
||||
VolatileWrite(ref buckets[h & (buckets.Length - 1)], new Entry { Key = newKey, Value = resultingValue, Hash = h });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Entry searchLastEntry = buckets[h & (buckets.Length - 1)];
|
||||
while (true)
|
||||
{
|
||||
if (searchLastEntry.Key == newKey)
|
||||
{
|
||||
resultingValue = searchLastEntry.Value;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (searchLastEntry.Next == null)
|
||||
{
|
||||
if (newEntryOrNull != null)
|
||||
{
|
||||
resultingValue = newEntryOrNull.Value;
|
||||
VolatileWrite(ref searchLastEntry.Next, newEntryOrNull);
|
||||
}
|
||||
else
|
||||
{
|
||||
resultingValue = valueFactory(newKey);
|
||||
VolatileWrite(ref searchLastEntry.Next, new Entry { Key = newKey, Value = resultingValue, Hash = h });
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
searchLastEntry = searchLastEntry.Next;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryGetValue(Type key, out TValue value)
|
||||
{
|
||||
Entry[] table = this.buckets;
|
||||
var hash = key.GetHashCode();
|
||||
Entry entry = table[hash & table.Length - 1];
|
||||
|
||||
while (entry != null)
|
||||
{
|
||||
if (entry.Key == key)
|
||||
{
|
||||
value = entry.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
entry = entry.Next;
|
||||
}
|
||||
|
||||
value = default(TValue);
|
||||
return false;
|
||||
}
|
||||
|
||||
public TValue GetOrAdd(Type key, Func<Type, TValue> valueFactory)
|
||||
{
|
||||
TValue v;
|
||||
if (this.TryGetValue(key, out v))
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
this.TryAddInternal(key, valueFactory, out v);
|
||||
return v;
|
||||
}
|
||||
|
||||
private static int CalculateCapacity(int collectionSize, float loadFactor)
|
||||
{
|
||||
var initialCapacity = (int)(((float)collectionSize) / loadFactor);
|
||||
var capacity = 1;
|
||||
while (capacity < initialCapacity)
|
||||
{
|
||||
capacity <<= 1;
|
||||
}
|
||||
|
||||
if (capacity < 8)
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
return capacity;
|
||||
}
|
||||
|
||||
private static void VolatileWrite(ref Entry location, Entry value)
|
||||
{
|
||||
#if !UNITY_2018_3_OR_NEWER
|
||||
System.Threading.Volatile.Write(ref location, value);
|
||||
#elif UNITY_2018_3_OR_NEWER || NET_4_6
|
||||
System.Threading.Volatile.Write(ref location, value);
|
||||
#else
|
||||
System.Threading.Thread.MemoryBarrier();
|
||||
location = value;
|
||||
#endif
|
||||
}
|
||||
|
||||
private static void VolatileWrite(ref Entry[] location, Entry[] value)
|
||||
{
|
||||
#if !UNITY_2018_3_OR_NEWER
|
||||
System.Threading.Volatile.Write(ref location, value);
|
||||
#elif UNITY_2018_3_OR_NEWER || NET_4_6
|
||||
System.Threading.Volatile.Write(ref location, value);
|
||||
#else
|
||||
System.Threading.Thread.MemoryBarrier();
|
||||
location = value;
|
||||
#endif
|
||||
}
|
||||
|
||||
private class Entry
|
||||
{
|
||||
#pragma warning disable SA1401 // Fields should be private
|
||||
internal Type Key;
|
||||
internal TValue Value;
|
||||
internal int Hash;
|
||||
internal Entry Next;
|
||||
#pragma warning restore SA1401 // Fields should be private
|
||||
|
||||
// debug only
|
||||
public override string ToString()
|
||||
{
|
||||
return this.Key + "(" + this.Count() + ")";
|
||||
}
|
||||
|
||||
private int Count()
|
||||
{
|
||||
var count = 1;
|
||||
Entry n = this;
|
||||
while (n.Next != null)
|
||||
{
|
||||
count++;
|
||||
n = n.Next;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 517dd6b1707013015b06ebfbeeea1f2c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
430
Assets/Scripts/MessagePack/Internal/TinyJsonReader.cs
Normal file
430
Assets/Scripts/MessagePack/Internal/TinyJsonReader.cs
Normal file
|
|
@ -0,0 +1,430 @@
|
|||
// Copyright (c) All contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
#pragma warning disable SA1649 // File name should match first type name
|
||||
|
||||
namespace MessagePack
|
||||
{
|
||||
/* simple, tiny JSON reader for MessagePackSerializer.FromJson.
|
||||
* this is simple, compact and enough fast but not optimized extremely. */
|
||||
|
||||
internal enum TinyJsonToken
|
||||
{
|
||||
None,
|
||||
StartObject, // {
|
||||
EndObject, // }
|
||||
StartArray, // [
|
||||
EndArray, // ]
|
||||
Number, // -0~9
|
||||
String, // "___"
|
||||
True, // true
|
||||
False, // false
|
||||
Null, // null
|
||||
}
|
||||
|
||||
internal enum ValueType : byte
|
||||
{
|
||||
Null,
|
||||
True,
|
||||
False,
|
||||
Double,
|
||||
Long,
|
||||
ULong,
|
||||
Decimal,
|
||||
String,
|
||||
}
|
||||
|
||||
public class TinyJsonException : MessagePackSerializationException
|
||||
{
|
||||
public TinyJsonException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal class TinyJsonReader : IDisposable
|
||||
{
|
||||
private readonly TextReader reader;
|
||||
private readonly bool disposeInnerReader;
|
||||
private StringBuilder reusableBuilder;
|
||||
|
||||
public TinyJsonToken TokenType { get; private set; }
|
||||
|
||||
public ValueType ValueType { get; private set; }
|
||||
|
||||
public double DoubleValue { get; private set; }
|
||||
|
||||
public long LongValue { get; private set; }
|
||||
|
||||
public ulong ULongValue { get; private set; }
|
||||
|
||||
public decimal DecimalValue { get; private set; }
|
||||
|
||||
public string StringValue { get; private set; }
|
||||
|
||||
public TinyJsonReader(TextReader reader, bool disposeInnerReader = true)
|
||||
{
|
||||
this.reader = reader;
|
||||
this.disposeInnerReader = disposeInnerReader;
|
||||
}
|
||||
|
||||
public bool Read()
|
||||
{
|
||||
this.ReadNextToken();
|
||||
this.ReadValue();
|
||||
return this.TokenType != TinyJsonToken.None;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (this.reader != null && this.disposeInnerReader)
|
||||
{
|
||||
this.reader.Dispose();
|
||||
}
|
||||
|
||||
this.TokenType = TinyJsonToken.None;
|
||||
this.ValueType = ValueType.Null;
|
||||
}
|
||||
|
||||
private void SkipWhiteSpace()
|
||||
{
|
||||
var c = this.reader.Peek();
|
||||
while (c != -1 && Char.IsWhiteSpace((char)c))
|
||||
{
|
||||
this.reader.Read();
|
||||
c = this.reader.Peek();
|
||||
}
|
||||
}
|
||||
|
||||
private char ReadChar()
|
||||
{
|
||||
return (char)this.reader.Read();
|
||||
}
|
||||
|
||||
private static bool IsWordBreak(char c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case ' ':
|
||||
case '{':
|
||||
case '}':
|
||||
case '[':
|
||||
case ']':
|
||||
case ',':
|
||||
case ':':
|
||||
case '\"':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadNextToken()
|
||||
{
|
||||
this.SkipWhiteSpace();
|
||||
|
||||
var intChar = this.reader.Peek();
|
||||
if (intChar == -1)
|
||||
{
|
||||
this.TokenType = TinyJsonToken.None;
|
||||
return;
|
||||
}
|
||||
|
||||
var c = (char)intChar;
|
||||
switch (c)
|
||||
{
|
||||
case '{':
|
||||
this.TokenType = TinyJsonToken.StartObject;
|
||||
return;
|
||||
case '}':
|
||||
this.TokenType = TinyJsonToken.EndObject;
|
||||
return;
|
||||
case '[':
|
||||
this.TokenType = TinyJsonToken.StartArray;
|
||||
return;
|
||||
case ']':
|
||||
this.TokenType = TinyJsonToken.EndArray;
|
||||
return;
|
||||
case '"':
|
||||
this.TokenType = TinyJsonToken.String;
|
||||
return;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case '-':
|
||||
this.TokenType = TinyJsonToken.Number;
|
||||
return;
|
||||
case 't':
|
||||
this.TokenType = TinyJsonToken.True;
|
||||
return;
|
||||
case 'f':
|
||||
this.TokenType = TinyJsonToken.False;
|
||||
return;
|
||||
case 'n':
|
||||
this.TokenType = TinyJsonToken.Null;
|
||||
return;
|
||||
case ',':
|
||||
case ':':
|
||||
this.reader.Read();
|
||||
this.ReadNextToken();
|
||||
return;
|
||||
default:
|
||||
throw new TinyJsonException("Invalid String:" + c);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadValue()
|
||||
{
|
||||
this.ValueType = ValueType.Null;
|
||||
|
||||
switch (this.TokenType)
|
||||
{
|
||||
case TinyJsonToken.None:
|
||||
break;
|
||||
case TinyJsonToken.StartObject:
|
||||
case TinyJsonToken.EndObject:
|
||||
case TinyJsonToken.StartArray:
|
||||
case TinyJsonToken.EndArray:
|
||||
this.reader.Read();
|
||||
break;
|
||||
case TinyJsonToken.Number:
|
||||
this.ReadNumber();
|
||||
break;
|
||||
case TinyJsonToken.String:
|
||||
this.ReadString();
|
||||
break;
|
||||
case TinyJsonToken.True:
|
||||
if (this.ReadChar() != 't')
|
||||
{
|
||||
throw new TinyJsonException("Invalid Token");
|
||||
}
|
||||
|
||||
if (this.ReadChar() != 'r')
|
||||
{
|
||||
throw new TinyJsonException("Invalid Token");
|
||||
}
|
||||
|
||||
if (this.ReadChar() != 'u')
|
||||
{
|
||||
throw new TinyJsonException("Invalid Token");
|
||||
}
|
||||
|
||||
if (this.ReadChar() != 'e')
|
||||
{
|
||||
throw new TinyJsonException("Invalid Token");
|
||||
}
|
||||
|
||||
this.ValueType = ValueType.True;
|
||||
break;
|
||||
case TinyJsonToken.False:
|
||||
if (this.ReadChar() != 'f')
|
||||
{
|
||||
throw new TinyJsonException("Invalid Token");
|
||||
}
|
||||
|
||||
if (this.ReadChar() != 'a')
|
||||
{
|
||||
throw new TinyJsonException("Invalid Token");
|
||||
}
|
||||
|
||||
if (this.ReadChar() != 'l')
|
||||
{
|
||||
throw new TinyJsonException("Invalid Token");
|
||||
}
|
||||
|
||||
if (this.ReadChar() != 's')
|
||||
{
|
||||
throw new TinyJsonException("Invalid Token");
|
||||
}
|
||||
|
||||
if (this.ReadChar() != 'e')
|
||||
{
|
||||
throw new TinyJsonException("Invalid Token");
|
||||
}
|
||||
|
||||
this.ValueType = ValueType.False;
|
||||
break;
|
||||
case TinyJsonToken.Null:
|
||||
if (this.ReadChar() != 'n')
|
||||
{
|
||||
throw new TinyJsonException("Invalid Token");
|
||||
}
|
||||
|
||||
if (this.ReadChar() != 'u')
|
||||
{
|
||||
throw new TinyJsonException("Invalid Token");
|
||||
}
|
||||
|
||||
if (this.ReadChar() != 'l')
|
||||
{
|
||||
throw new TinyJsonException("Invalid Token");
|
||||
}
|
||||
|
||||
if (this.ReadChar() != 'l')
|
||||
{
|
||||
throw new TinyJsonException("Invalid Token");
|
||||
}
|
||||
|
||||
this.ValueType = ValueType.Null;
|
||||
break;
|
||||
default:
|
||||
throw new MessagePackSerializationException("InvalidTokenState:" + this.TokenType);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadNumber()
|
||||
{
|
||||
StringBuilder numberWord;
|
||||
if (this.reusableBuilder == null)
|
||||
{
|
||||
this.reusableBuilder = new StringBuilder();
|
||||
numberWord = this.reusableBuilder;
|
||||
}
|
||||
else
|
||||
{
|
||||
numberWord = this.reusableBuilder;
|
||||
numberWord.Length = 0; // Clear
|
||||
}
|
||||
|
||||
var isDouble = false;
|
||||
var intChar = this.reader.Peek();
|
||||
while (intChar != -1 && !IsWordBreak((char)intChar))
|
||||
{
|
||||
var c = this.ReadChar();
|
||||
numberWord.Append(c);
|
||||
if (c == '.' || c == 'e' || c == 'E')
|
||||
{
|
||||
isDouble = true;
|
||||
}
|
||||
|
||||
intChar = this.reader.Peek();
|
||||
}
|
||||
|
||||
var number = numberWord.ToString();
|
||||
if (isDouble)
|
||||
{
|
||||
double parsedDouble;
|
||||
Double.TryParse(number, NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite | NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint | NumberStyles.AllowThousands | NumberStyles.AllowExponent, System.Globalization.CultureInfo.InvariantCulture, out parsedDouble);
|
||||
this.ValueType = ValueType.Double;
|
||||
this.DoubleValue = parsedDouble;
|
||||
}
|
||||
else
|
||||
{
|
||||
long parsedInt;
|
||||
if (Int64.TryParse(number, NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out parsedInt))
|
||||
{
|
||||
this.ValueType = ValueType.Long;
|
||||
this.LongValue = parsedInt;
|
||||
return;
|
||||
}
|
||||
|
||||
ulong parsedULong;
|
||||
if (ulong.TryParse(number, NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out parsedULong))
|
||||
{
|
||||
this.ValueType = ValueType.ULong;
|
||||
this.ULongValue = parsedULong;
|
||||
return;
|
||||
}
|
||||
|
||||
Decimal parsedDecimal;
|
||||
if (decimal.TryParse(number, NumberStyles.Number, System.Globalization.CultureInfo.InvariantCulture, out parsedDecimal))
|
||||
{
|
||||
this.ValueType = ValueType.Decimal;
|
||||
this.DecimalValue = parsedDecimal;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadString()
|
||||
{
|
||||
this.reader.Read(); // skip ["]
|
||||
|
||||
StringBuilder sb;
|
||||
if (this.reusableBuilder == null)
|
||||
{
|
||||
this.reusableBuilder = new StringBuilder();
|
||||
sb = this.reusableBuilder;
|
||||
}
|
||||
else
|
||||
{
|
||||
sb = this.reusableBuilder;
|
||||
sb.Length = 0; // Clear
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (this.reader.Peek() == -1)
|
||||
{
|
||||
throw new TinyJsonException("Invalid Json String");
|
||||
}
|
||||
|
||||
var c = this.ReadChar();
|
||||
switch (c)
|
||||
{
|
||||
case '"': // endtoken
|
||||
goto END;
|
||||
case '\\': // escape character
|
||||
if (this.reader.Peek() == -1)
|
||||
{
|
||||
throw new TinyJsonException("Invalid Json String");
|
||||
}
|
||||
|
||||
c = this.ReadChar();
|
||||
switch (c)
|
||||
{
|
||||
case '"':
|
||||
case '\\':
|
||||
case '/':
|
||||
sb.Append(c);
|
||||
break;
|
||||
case 'b':
|
||||
sb.Append('\b');
|
||||
break;
|
||||
case 'f':
|
||||
sb.Append('\f');
|
||||
break;
|
||||
case 'n':
|
||||
sb.Append('\n');
|
||||
break;
|
||||
case 'r':
|
||||
sb.Append('\r');
|
||||
break;
|
||||
case 't':
|
||||
sb.Append('\t');
|
||||
break;
|
||||
case 'u':
|
||||
var hex = new char[4];
|
||||
hex[0] = this.ReadChar();
|
||||
hex[1] = this.ReadChar();
|
||||
hex[2] = this.ReadChar();
|
||||
hex[3] = this.ReadChar();
|
||||
sb.Append((char)Convert.ToInt32(new string(hex), 16));
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
default: // string
|
||||
sb.Append(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
END:
|
||||
this.ValueType = ValueType.String;
|
||||
this.StringValue = sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/MessagePack/Internal/TinyJsonReader.cs.meta
Normal file
11
Assets/Scripts/MessagePack/Internal/TinyJsonReader.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: adc1157b80c30cf2d8845336b9c1cfc2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
170
Assets/Scripts/MessagePack/Internal/UnsafeMemory.Low.cs
Normal file
170
Assets/Scripts/MessagePack/Internal/UnsafeMemory.Low.cs
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
// Copyright (c) All contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#if !UNITY_2018_3_OR_NEWER
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace MessagePack.Internal
|
||||
{
|
||||
/* for string key property name write optimization. */
|
||||
|
||||
public static class UnsafeMemory
|
||||
{
|
||||
public static readonly bool Is32Bit = IntPtr.Size == 4;
|
||||
}
|
||||
|
||||
public static partial class UnsafeMemory32
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe void WriteRaw1(ref MessagePackWriter writer, ReadOnlySpan<byte> src)
|
||||
{
|
||||
Span<byte> dst = writer.GetSpan(src.Length);
|
||||
|
||||
fixed (byte* pSrc = &src[0])
|
||||
fixed (byte* pDst = &dst[0])
|
||||
{
|
||||
*(byte*)pDst = *(byte*)pSrc;
|
||||
}
|
||||
|
||||
writer.Advance(src.Length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe void WriteRaw2(ref MessagePackWriter writer, ReadOnlySpan<byte> src)
|
||||
{
|
||||
Span<byte> dst = writer.GetSpan(src.Length);
|
||||
|
||||
fixed (byte* pSrc = &src[0])
|
||||
fixed (byte* pDst = &dst[0])
|
||||
{
|
||||
*(short*)pDst = *(short*)pSrc;
|
||||
}
|
||||
|
||||
writer.Advance(src.Length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe void WriteRaw3(ref MessagePackWriter writer, ReadOnlySpan<byte> src)
|
||||
{
|
||||
Span<byte> dst = writer.GetSpan(src.Length);
|
||||
|
||||
fixed (byte* pSrc = &src[0])
|
||||
fixed (byte* pDst = &dst[0])
|
||||
{
|
||||
*(byte*)pDst = *(byte*)pSrc;
|
||||
*(short*)(pDst + 1) = *(short*)(pSrc + 1);
|
||||
}
|
||||
|
||||
writer.Advance(src.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public static partial class UnsafeMemory64
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe void WriteRaw1(ref MessagePackWriter writer, ReadOnlySpan<byte> src)
|
||||
{
|
||||
Span<byte> dst = writer.GetSpan(src.Length);
|
||||
|
||||
fixed (byte* pSrc = &src[0])
|
||||
fixed (byte* pDst = &dst[0])
|
||||
{
|
||||
*(byte*)pDst = *(byte*)pSrc;
|
||||
}
|
||||
|
||||
writer.Advance(src.Length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe void WriteRaw2(ref MessagePackWriter writer, ReadOnlySpan<byte> src)
|
||||
{
|
||||
Span<byte> dst = writer.GetSpan(src.Length);
|
||||
|
||||
fixed (byte* pSrc = &src[0])
|
||||
fixed (byte* pDst = &dst[0])
|
||||
{
|
||||
*(short*)pDst = *(short*)pSrc;
|
||||
}
|
||||
|
||||
writer.Advance(src.Length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe void WriteRaw3(ref MessagePackWriter writer, ReadOnlySpan<byte> src)
|
||||
{
|
||||
Span<byte> dst = writer.GetSpan(src.Length);
|
||||
|
||||
fixed (byte* pSrc = &src[0])
|
||||
fixed (byte* pDst = &dst[0])
|
||||
{
|
||||
*(byte*)pDst = *(byte*)pSrc;
|
||||
*(short*)(pDst + 1) = *(short*)(pSrc + 1);
|
||||
}
|
||||
|
||||
writer.Advance(src.Length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe void WriteRaw4(ref MessagePackWriter writer, ReadOnlySpan<byte> src)
|
||||
{
|
||||
Span<byte> dst = writer.GetSpan(src.Length);
|
||||
|
||||
fixed (byte* pSrc = &src[0])
|
||||
fixed (byte* pDst = &dst[0])
|
||||
{
|
||||
*(int*)(pDst + 0) = *(int*)(pSrc + 0);
|
||||
}
|
||||
|
||||
writer.Advance(src.Length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe void WriteRaw5(ref MessagePackWriter writer, ReadOnlySpan<byte> src)
|
||||
{
|
||||
Span<byte> dst = writer.GetSpan(src.Length);
|
||||
|
||||
fixed (byte* pSrc = &src[0])
|
||||
fixed (byte* pDst = &dst[0])
|
||||
{
|
||||
*(int*)(pDst + 0) = *(int*)(pSrc + 0);
|
||||
*(int*)(pDst + 1) = *(int*)(pSrc + 1);
|
||||
}
|
||||
|
||||
writer.Advance(src.Length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe void WriteRaw6(ref MessagePackWriter writer, ReadOnlySpan<byte> src)
|
||||
{
|
||||
Span<byte> dst = writer.GetSpan(src.Length);
|
||||
|
||||
fixed (byte* pSrc = &src[0])
|
||||
fixed (byte* pDst = &dst[0])
|
||||
{
|
||||
*(int*)(pDst + 0) = *(int*)(pSrc + 0);
|
||||
*(int*)(pDst + 2) = *(int*)(pSrc + 2);
|
||||
}
|
||||
|
||||
writer.Advance(src.Length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe void WriteRaw7(ref MessagePackWriter writer, ReadOnlySpan<byte> src)
|
||||
{
|
||||
Span<byte> dst = writer.GetSpan(src.Length);
|
||||
|
||||
fixed (byte* pSrc = &src[0])
|
||||
fixed (byte* pDst = &dst[0])
|
||||
{
|
||||
*(int*)(pDst + 0) = *(int*)(pSrc + 0);
|
||||
*(int*)(pDst + 3) = *(int*)(pSrc + 3);
|
||||
}
|
||||
|
||||
writer.Advance(src.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
11
Assets/Scripts/MessagePack/Internal/UnsafeMemory.Low.cs.meta
Normal file
11
Assets/Scripts/MessagePack/Internal/UnsafeMemory.Low.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2126473180c3a7d3a8860c4fd8aa4183
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Loading…
Reference in a new issue