/* * Copyright (c) 2006-2014, openmetaverse.org * 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.org 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; namespace OpenMetaverse { /// /// A 128-bit Universally Unique Identifier, used throughout the Second /// Life networking protocol /// [Serializable] public struct UUID : IComparable, IEquatable { /// The System.Guid object this struct wraps around public Guid Guid; #region Constructors /// /// Constructor that takes a string UUID representation /// /// A string representation of a UUID, case /// insensitive and can either be hyphenated or non-hyphenated /// UUID("11f8aa9c-b071-4242-836b-13b7abe0d489") public UUID(string val) { if (String.IsNullOrEmpty(val)) Guid = new Guid(); else Guid = new Guid(val); } /// /// Constructor that takes a System.Guid object /// /// A Guid object that contains the unique identifier /// to be represented by this UUID public UUID(Guid val) { Guid = val; } /// /// Constructor that takes a byte array containing a UUID /// /// Byte array containing a 16 byte UUID /// Beginning offset in the array public UUID(byte[] source, int pos) { Guid = UUID.Zero.Guid; FromBytes(source, pos); } /// /// Constructor that takes an unsigned 64-bit unsigned integer to /// convert to a UUID /// /// 64-bit unsigned integer to convert to a UUID public UUID(ulong val) { byte[] end = BitConverter.GetBytes(val); if (!BitConverter.IsLittleEndian) Array.Reverse(end); Guid = new Guid(0, 0, 0, end); } /// /// Copy constructor /// /// UUID to copy public UUID(UUID val) { Guid = val.Guid; } #endregion Constructors #region Public Methods /// /// IComparable.CompareTo implementation /// public int CompareTo(UUID id) { return Guid.CompareTo(id.Guid); } /// /// Assigns this UUID from 16 bytes out of a byte array /// /// Byte array containing the UUID to assign this UUID to /// Starting position of the UUID in the byte array 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]); } /// /// Returns a copy of the raw bytes for this UUID /// /// A 16 byte array containing this UUID public byte[] GetBytes() { byte[] output = new byte[16]; ToBytes(output, 0); return output; } /// /// Writes the raw bytes for this UUID to a byte array /// /// Destination byte array /// Position in the destination array to start /// writing. Must be at least 16 bytes before the end of the array 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); } /// /// Calculate an LLCRC (cyclic redundancy check) for this UUID /// /// The CRC checksum for this UUID 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; } /// /// Create a 64-bit integer representation from the second half of this UUID /// /// An integer created from the last eight bytes of this UUID public ulong GetULong() { byte[] bytes = Guid.ToByteArray(); return (ulong) ((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 /// /// Generate a UUID from a string /// /// A string representation of a UUID, case /// insensitive and can either be hyphenated or non-hyphenated /// UUID.Parse("11f8aa9c-b071-4242-836b-13b7abe0d489") public static UUID Parse(string val) { return new UUID(val); } /// /// Generate a UUID from a string /// /// A string representation of a UUID, case /// insensitive and can either be hyphenated or non-hyphenated /// Will contain the parsed UUID if successful, /// otherwise null /// True if the string was successfully parse, otherwise false /// UUID.TryParse("11f8aa9c-b071-4242-836b-13b7abe0d489", result) 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; } } /// /// Combine two UUIDs together by taking the MD5 hash of a byte array /// containing both UUIDs /// /// First UUID to combine /// Second UUID to combine /// The UUID product of the combination 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); } /// /// /// /// public static UUID Random() { return new UUID(Guid.NewGuid()); } #endregion Static Methods #region Overrides /// /// Return a hash code for this UUID, used by .NET for hash tables /// /// An integer composed of all the UUID bytes XORed together public override int GetHashCode() { return Guid.GetHashCode(); } /// /// Comparison function /// /// An object to compare to this UUID /// True if the object is a UUID and both UUIDs are equal public override bool Equals(object o) { if (!(o is UUID)) return false; UUID uuid = (UUID)o; return Guid == uuid.Guid; } /// /// Comparison function /// /// UUID to compare to /// True if the UUIDs are equal, otherwise false public bool Equals(UUID uuid) { return Guid == uuid.Guid; } /// /// Get a hyphenated string representation of this UUID /// /// A string representation of this UUID, lowercase and /// with hyphens /// 11f8aa9c-b071-4242-836b-13b7abe0d489 public override string ToString() { if (Guid == Guid.Empty) return ZeroString; else return Guid.ToString(); } #endregion Overrides #region Operators /// /// Equals operator /// /// First UUID for comparison /// Second UUID for comparison /// True if the UUIDs are byte for byte equal, otherwise false public static bool operator ==(UUID lhs, UUID rhs) { return lhs.Guid == rhs.Guid; } /// /// Not equals operator /// /// First UUID for comparison /// Second UUID for comparison /// True if the UUIDs are not equal, otherwise true public static bool operator !=(UUID lhs, UUID rhs) { return !(lhs == rhs); } /// /// XOR operator /// /// First UUID /// Second UUID /// A UUID that is a XOR combination of the two input UUIDs public static UUID operator ^(UUID lhs, UUID rhs) { byte[] lhsbytes = lhs.GetBytes(); byte[] rhsbytes = rhs.GetBytes(); byte[] output = new byte[16]; for (int i = 0; i < 16; i++) { output[i] = (byte)(lhsbytes[i] ^ rhsbytes[i]); } return new UUID(output, 0); } /// /// String typecasting operator /// /// A UUID in string form. Case insensitive, /// hyphenated or non-hyphenated /// A UUID built from the string representation public static explicit operator UUID(string val) { return new UUID(val); } #endregion Operators /// An UUID with a value of all zeroes public static readonly UUID Zero = new UUID(); /// A cache of UUID.Zero as a string to optimize a common path private static readonly string ZeroString = Guid.Empty.ToString(); } }