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:
hyperion51 2020-02-26 17:48:40 +00:00
commit a0c8dbee72
493 changed files with 57790 additions and 3 deletions

View 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

View file

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

View 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;
}
}
}
}

View file

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

View 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 + ")";
}
}
}
}

View file

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

View 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;
}
}
}

View file

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

View 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);
}
}

View file

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

View 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

View file

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

View 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;
}
}
}

View file

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

View 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
}
}

View file

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

View 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];
}
}
}

View file

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

View 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

View file

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

View 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;
}
}
}

View file

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

View 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);
}
}
}
}

View file

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

View file

@ -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;
}
}
}
}

View file

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

View 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();
}
}
}

View file

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

View 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

View file

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