Created
April 6, 2019 10:13
-
-
Save arturaz/7144da1830caa5c01cdd4e48b845cd35 to your computer and use it in GitHub Desktop.
JVM and .NET compatible GUID usable in Unity that can be converted back and forth without any allocations.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using com.tinylabproductions.TLPLib.Data; | |
using Quantum; | |
namespace Game.code.quantum_bind { | |
public static class Conversions { | |
public static unsafe QGuid toQGuid(this SerializableGUID guid) { | |
var srcBytes = stackalloc byte[16]; | |
var dstBytes = stackalloc byte[16]; | |
*((ulong*) srcBytes) = guid.long1; | |
*((ulong*) (srcBytes + 8)) = guid.long2; | |
// Some byte shuffling, because JVM and .NET decided to store those bytes differently for some | |
// reason. | |
dstBytes[0] = srcBytes[6]; | |
dstBytes[1] = srcBytes[7]; | |
dstBytes[2] = srcBytes[4]; | |
dstBytes[3] = srcBytes[5]; | |
dstBytes[4] = srcBytes[0]; | |
dstBytes[5] = srcBytes[1]; | |
dstBytes[6] = srcBytes[2]; | |
dstBytes[7] = srcBytes[3]; | |
dstBytes[8] = srcBytes[15]; | |
dstBytes[9] = srcBytes[14]; | |
dstBytes[10] = srcBytes[13]; | |
dstBytes[11] = srcBytes[12]; | |
dstBytes[12] = srcBytes[11]; | |
dstBytes[13] = srcBytes[10]; | |
dstBytes[14] = srcBytes[9]; | |
dstBytes[15] = srcBytes[8]; | |
return new QGuid(*((long*) dstBytes), *((long*) (dstBytes + 8))); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Runtime.InteropServices; | |
using System.Security.Cryptography; | |
using JetBrains.Annotations; | |
using Photon.Deterministic; | |
using Quantum.Core; | |
namespace Quantum { | |
/// <summary> | |
/// A runtime blittable, JVM compatible implementation of UUID. | |
/// </summary> | |
[StructLayout(LayoutKind.Sequential, Pack=CodeGenConstants.STRUCT_PACK), Serializable, PublicAPI] | |
public readonly struct QGuid : IEquatable<QGuid> { | |
public readonly long mostSigBits, leastSigBits; | |
public QGuid(long mostSigBits, long leastSigBits) { | |
this.mostSigBits = mostSigBits; | |
this.leastSigBits = leastSigBits; | |
} | |
public QGuid(byte[] data) { | |
#pragma warning disable 675 | |
long msb = 0; | |
long lsb = 0; | |
if (data.Length != 16) throw new Exception("data must be 16 bytes in length"); | |
for (var i=0; i<8; i++) | |
msb = (msb << 8) | (data[i] & 0xff); | |
for (var i=8; i<16; i++) | |
lsb = (lsb << 8) | (data[i] & 0xff); | |
mostSigBits = msb; | |
leastSigBits = lsb; | |
#pragma warning restore 675 | |
} | |
#region Equality | |
public bool Equals(QGuid other) => mostSigBits == other.mostSigBits && leastSigBits == other.leastSigBits; | |
public override bool Equals(object obj) { | |
if (ReferenceEquals(null, obj)) return false; | |
return obj is QGuid other && Equals(other); | |
} | |
public override int GetHashCode() { | |
unchecked { | |
return (mostSigBits.GetHashCode() * 397) ^ leastSigBits.GetHashCode(); | |
} | |
} | |
public static bool operator ==(QGuid left, QGuid right) => left.Equals(right); | |
public static bool operator !=(QGuid left, QGuid right) => !left.Equals(right); | |
#endregion | |
public override string ToString() => | |
digits(mostSigBits >> 32, 8) + "-" + | |
digits(mostSigBits >> 16, 4) + "-" + | |
digits(mostSigBits, 4) + "-" + | |
digits(leastSigBits >> 48, 4) + "-" + | |
digits(leastSigBits, 12); | |
public string asString => ToString(); | |
/** Returns val represented by the specified number of hex digits. */ | |
static string digits(long val, int digits) { | |
var hi = 1L << (digits * 4); | |
return (hi | (val & (hi - 1))).ToString("x").Substring(1); | |
} | |
static class RngProvider { | |
static readonly RNGCryptoServiceProvider provider = new RNGCryptoServiceProvider(); | |
static readonly byte[] bytes = new byte[16]; | |
public static QGuid random() { | |
provider.GetBytes(bytes); | |
bytes[6] &= 0x0f; /* clear version */ | |
bytes[6] |= 0x40; /* set to version 4 */ | |
bytes[8] &= 0x3f; /* clear variant */ | |
bytes[8] |= 0x80; /* set to IETF variant */ | |
return new QGuid(bytes); | |
} | |
} | |
public static QGuid random() => RngProvider.random(); | |
public static bool tryParseAllocating(string name, out QGuid guid) { | |
try { | |
guid = fromStringAllocatingThrowing(name); | |
return true; | |
} | |
catch { | |
guid = default; | |
return false; | |
} | |
} | |
public static QGuid fromStringAllocatingThrowing(string name) { | |
var components = name.Split('-'); | |
if (components.Length != 5) | |
throw new Exception("Invalid UUID string: " + name); | |
var mostSigBits = Convert.ToInt64(components[0], 16); | |
mostSigBits <<= 16; | |
mostSigBits |= Convert.ToInt64(components[1], 16); | |
mostSigBits <<= 16; | |
mostSigBits |= Convert.ToInt64(components[2], 16); | |
var leastSigBits = Convert.ToInt64(components[3], 16); | |
leastSigBits <<= 48; | |
leastSigBits |= Convert.ToInt64(components[4], 16); | |
return new QGuid(mostSigBits, leastSigBits); | |
} | |
public static unsafe void Serialize(QGuid* guid, BitStream stream) { | |
if (stream.Writing) { | |
stream.WriteLong(guid->mostSigBits); | |
stream.WriteLong(guid->leastSigBits); | |
} | |
else { | |
*guid = new QGuid(stream.ReadLong(), stream.ReadLong()); | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using GenerationAttributes; | |
using JetBrains.Annotations; | |
using Sirenix.OdinInspector; | |
using UnityEngine; | |
using UnityEngine.Serialization; | |
namespace com.tinylabproductions.TLPLib.Data { | |
[Serializable, InlineProperty, Record(GenerateConstructor = GeneratedConstructor.None, GenerateToString = false)] | |
public partial class SerializableGUID { | |
[SerializeField, FormerlySerializedAs("long1"), HideInInspector, PublicAccessor] ulong _long1; | |
[SerializeField, FormerlySerializedAs("long2"), HideInInspector, PublicAccessor] ulong _long2; | |
[CustomContextMenu("Generate new GUID", nameof(generate)), ShowInInspector, HideLabel] | |
string GUID { | |
get => guid.ToString(); | |
set => guid = new Guid(value); | |
} | |
[PublicAPI] public void generate() => guid = Guid.NewGuid(); | |
public SerializableGUID(Guid guid) { | |
this.guid = guid; | |
} | |
[PublicAPI] public Guid guid { | |
get => new Guid( | |
(uint) _long1, | |
(ushort) (_long1 >> 32), | |
(ushort) (_long1 >> (32 + 16)), | |
(byte) _long2, | |
(byte) (_long2 >> 8), | |
(byte) (_long2 >> (8 * 2)), | |
(byte) (_long2 >> (8 * 3)), | |
(byte) (_long2 >> (8 * 4)), | |
(byte) (_long2 >> (8 * 5)), | |
(byte) (_long2 >> (8 * 6)), | |
(byte) (_long2 >> (8 * 7)) | |
); | |
private set { | |
var bytes = value.ToByteArray(); | |
_long1 = BitConverter.ToUInt64(bytes, 0); | |
_long2 = BitConverter.ToUInt64(bytes, 8); | |
} | |
} | |
[PublicAPI] public bool isZero => _long1 == 0 && _long2 == 0; | |
public override string ToString() => guid.ToString(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment