Files
libremetaverse/LibreMetaverse.Types/UUID.cs
Cinder 6bc7c7ad45 Revert "Reintroduce ProtoBuf and use it for inventory caching too. ZeroFormatter wasn't working out."
This reverts commit 0ce9e654ec.
Revert "Silly Cinder. Don't serialize InventoryNodeDictionary"

This reverts commit 30f3bf0ea8.
Revert "Serialize Permissions and one more ProtoBuf fix for InventoryNode"

This reverts commit 26992406c5.
2021-09-25 19:50:57 -05:00

393 lines
14 KiB
C#

/*
* Copyright (c) 2006-2016, openmetaverse.co
* All rights reserved.
*
* - Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* - Neither the name of the openmetaverse.co nor the names
* of its contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Security.Cryptography;
namespace OpenMetaverse
{
/// <summary>
/// A 128-bit Universally Unique Identifier, used throughout the Second
/// Life networking protocol
/// </summary>
[Serializable]
public struct UUID : IComparable<UUID>, IEquatable<UUID>
{
/// <summary>The System.Guid object this struct wraps around</summary>
public Guid Guid { get; set; }
#region Constructors
/// <summary>
/// Constructor that takes a string UUID representation
/// </summary>
/// <param name="val">A string representation of a UUID, case
/// insensitive and can either be hyphenated or non-hyphenated</param>
/// <example>UUID("11f8aa9c-b071-4242-836b-13b7abe0d489")</example>
public UUID(string val)
{
Guid = string.IsNullOrEmpty(val) ? new Guid() : new Guid(val);
}
/// <summary>
/// Constructor that takes a System.Guid object
/// </summary>
/// <param name="val">A Guid object that contains the unique identifier
/// to be represented by this UUID</param>
public UUID(Guid val)
{
Guid = val;
}
/// <summary>
/// Constructor that takes a byte array containing a UUID
/// </summary>
/// <param name="source">Byte array containing a 16 byte UUID</param>
/// <param name="pos">Beginning offset in the array</param>
public UUID(byte[] source, int pos)
{
Guid = UUID.Zero.Guid;
FromBytes(source, pos);
}
/// <summary>
/// Constructor that takes an unsigned 64-bit unsigned integer to
/// convert to a UUID
/// </summary>
/// <param name="val">64-bit unsigned integer to convert to a UUID</param>
public UUID(ulong val)
{
byte[] end = BitConverter.GetBytes(val);
if (!BitConverter.IsLittleEndian)
Array.Reverse(end);
Guid = new Guid(0, 0, 0, end);
}
/// <summary>
/// Copy constructor
/// </summary>
/// <param name="val">UUID to copy</param>
public UUID(UUID val)
{
Guid = val.Guid;
}
#endregion Constructors
#region Public Methods
/// <summary>
/// IComparable.CompareTo implementation
/// </summary>
public int CompareTo(UUID id)
{
return Guid.CompareTo(id.Guid);
}
/// <summary>
/// Assigns this UUID from 16 bytes out of a byte array
/// </summary>
/// <param name="source">Byte array containing the UUID to assign this UUID to</param>
/// <param name="pos">Starting position of the UUID in the byte array</param>
public void FromBytes(byte[] source, int pos)
{
int a = (source[pos + 0] << 24) | (source[pos + 1] << 16) | (source[pos + 2] << 8) | source[pos + 3];
short b = (short)((source[pos + 4] << 8) | source[pos + 5]);
short c = (short)((source[pos + 6] << 8) | source[pos + 7]);
Guid = new Guid(a, b, c, source[pos + 8], source[pos + 9], source[pos + 10], source[pos + 11],
source[pos + 12], source[pos + 13], source[pos + 14], source[pos + 15]);
}
/// <summary>
/// Returns a copy of the raw bytes for this UUID
/// </summary>
/// <returns>A 16 byte array containing this UUID</returns>
public byte[] GetBytes()
{
byte[] output = new byte[16];
ToBytes(output, 0);
return output;
}
/// <summary>
/// Writes the raw bytes for this UUID to a byte array
/// </summary>
/// <param name="dest">Destination byte array</param>
/// <param name="pos">Position in the destination array to start
/// writing. Must be at least 16 bytes before the end of the array</param>
public void ToBytes(byte[] dest, int pos)
{
byte[] bytes = Guid.ToByteArray();
dest[pos + 0] = bytes[3];
dest[pos + 1] = bytes[2];
dest[pos + 2] = bytes[1];
dest[pos + 3] = bytes[0];
dest[pos + 4] = bytes[5];
dest[pos + 5] = bytes[4];
dest[pos + 6] = bytes[7];
dest[pos + 7] = bytes[6];
Buffer.BlockCopy(bytes, 8, dest, pos + 8, 8);
}
/// <summary>
/// Calculate an LLCRC (cyclic redundancy check) for this UUID
/// </summary>
/// <returns>The CRC checksum for this UUID</returns>
public uint CRC()
{
uint retval = 0;
byte[] bytes = GetBytes();
retval += (uint)((bytes[3] << 24) + (bytes[2] << 16) + (bytes[1] << 8) + bytes[0]);
retval += (uint)((bytes[7] << 24) + (bytes[6] << 16) + (bytes[5] << 8) + bytes[4]);
retval += (uint)((bytes[11] << 24) + (bytes[10] << 16) + (bytes[9] << 8) + bytes[8]);
retval += (uint)((bytes[15] << 24) + (bytes[14] << 16) + (bytes[13] << 8) + bytes[12]);
return retval;
}
/// <summary>
/// Create a 64-bit integer representation from the second half of this UUID
/// </summary>
/// <returns>An integer created from the last eight bytes of this UUID</returns>
public ulong GetULong()
{
byte[] bytes = Guid.ToByteArray();
return (ulong)bytes[8] +
((ulong)bytes[9] << 8) +
((ulong)bytes[10] << 16) +
((ulong)bytes[12] << 24) +
((ulong)bytes[13] << 32) +
((ulong)bytes[13] << 40) +
((ulong)bytes[14] << 48) +
((ulong)bytes[15] << 56);
}
#endregion Public Methods
#region Static Methods
/// <summary>
/// Generate a UUID from a string
/// </summary>
/// <param name="val">A string representation of a UUID, case
/// insensitive and can either be hyphenated or non-hyphenated</param>
/// <example>UUID.Parse("11f8aa9c-b071-4242-836b-13b7abe0d489")</example>
public static UUID Parse(string val)
{
return new UUID(val);
}
/// <summary>
/// Generate a UUID from a string
/// </summary>
/// <param name="val">A string representation of a UUID, case
/// insensitive and can either be hyphenated or non-hyphenated</param>
/// <param name="result">Will contain the parsed UUID if successful,
/// otherwise null</param>
/// <returns>True if the string was successfully parse, otherwise false</returns>
/// <example>UUID.TryParse("11f8aa9c-b071-4242-836b-13b7abe0d489", result)</example>
public static bool TryParse(string val, out UUID result)
{
if (string.IsNullOrEmpty(val) ||
(val[0] == '{' && val.Length != 38) ||
(val.Length != 36 && val.Length != 32))
{
result = UUID.Zero;
return false;
}
try
{
result = Parse(val);
return true;
}
catch (Exception)
{
result = UUID.Zero;
return false;
}
}
/// <summary>
/// Combine two UUIDs together by taking the MD5 hash of a byte array
/// containing both UUIDs
/// </summary>
/// <param name="first">First UUID to combine</param>
/// <param name="second">Second UUID to combine</param>
/// <returns>The UUID product of the combination</returns>
public static UUID Combine(UUID first, UUID second)
{
// Construct the buffer that MD5ed
byte[] input = new byte[32];
Buffer.BlockCopy(first.GetBytes(), 0, input, 0, 16);
Buffer.BlockCopy(second.GetBytes(), 0, input, 16, 16);
return new UUID(Utils.MD5(input), 0);
}
/// <summary>
///
/// </summary>
/// <returns>UUID</returns>
public static UUID Random()
{
return new UUID(Guid.NewGuid());
}
/// <summary>
/// Slower than Random(), but generates a cryptographically secure UUID
/// </summary>
/// <returns>UUID</returns>
public static UUID SecureRandom()
{
var rng = new RNGCryptoServiceProvider();
byte[] data = new byte[16];
rng.GetBytes(data);
// Mark as UUIDv4
data[7] = (byte)((data[7] | 0x40) & 0x4f);
data[8] = (byte)((data[8] | 0x80) & 0xbf);
return new UUID(new Guid(data));
}
#endregion Static Methods
#region Overrides
/// <summary>
/// Return a hash code for this UUID, used by .NET for hash tables
/// </summary>
/// <returns>An integer composed of all the UUID bytes XORed together</returns>
public override int GetHashCode()
{
return Guid.GetHashCode();
}
/// <summary>
/// Comparison function
/// </summary>
/// <param name="o">An object to compare to this UUID</param>
/// <returns>True if the object is a UUID and both UUIDs are equal</returns>
public override bool Equals(object o)
{
if (!(o is UUID)) return false;
UUID uuid = (UUID)o;
return Guid == uuid.Guid;
}
/// <summary>
/// Comparison function
/// </summary>
/// <param name="uuid">UUID to compare to</param>
/// <returns>True if the UUIDs are equal, otherwise false</returns>
public bool Equals(UUID uuid)
{
return Guid == uuid.Guid;
}
/// <summary>
/// Get a hyphenated string representation of this UUID
/// </summary>
/// <returns>A string representation of this UUID, lowercase and
/// with hyphens</returns>
/// <example>11f8aa9c-b071-4242-836b-13b7abe0d489</example>
public override string ToString()
{
return Guid == Guid.Empty ? ZeroString : Guid.ToString();
}
#endregion Overrides
#region Operators
/// <summary>
/// Equals operator
/// </summary>
/// <param name="lhs">First UUID for comparison</param>
/// <param name="rhs">Second UUID for comparison</param>
/// <returns>True if the UUIDs are byte for byte equal, otherwise false</returns>
public static bool operator ==(UUID lhs, UUID rhs)
{
return lhs.Guid == rhs.Guid;
}
/// <summary>
/// Not equals operator
/// </summary>
/// <param name="lhs">First UUID for comparison</param>
/// <param name="rhs">Second UUID for comparison</param>
/// <returns>True if the UUIDs are not equal, otherwise true</returns>
public static bool operator !=(UUID lhs, UUID rhs)
{
return !(lhs == rhs);
}
/// <summary>
/// XOR operator
/// </summary>
/// <param name="lhs">First UUID</param>
/// <param name="rhs">Second UUID</param>
/// <returns>A UUID that is a XOR combination of the two input UUIDs</returns>
public static UUID operator ^(UUID lhs, UUID rhs)
{
byte[] lhsbytes = lhs.GetBytes();
byte[] rhsbytes = rhs.GetBytes();
byte[] output = new byte[16];
for (var i = 0; i < 16; i++)
{
output[i] = (byte)(lhsbytes[i] ^ rhsbytes[i]);
}
return new UUID(output, 0);
}
/// <summary>
/// String typecasting operator
/// </summary>
/// <param name="val">A UUID in string form. Case insensitive,
/// hyphenated or non-hyphenated</param>
/// <returns>A UUID built from the string representation</returns>
public static explicit operator UUID(string val)
{
return new UUID(val);
}
#endregion Operators
/// <summary>An UUID with a value of all zeroes</summary>
public static readonly UUID Zero = new UUID();
/// <summary>A cache of UUID.Zero as a string to optimize a common path</summary>
private static readonly string ZeroString = Guid.Empty.ToString();
}
}