back

handle.cs - hashed handle reference

I use this in c# projects for double referencing all assets. Referencing by string name rather than pointers allows for hot swapping assets, reduces coupling, and allows for default asset replacement if data has not yet been loaded. If reusing the same Handle multiple times, just do a copy of the Handle, rather than rehashing the same string.

Handle.cs

/// Handle.cs
/// Hashed handle reference.
///
/// Copyright (c) 2010 Eric Itomura
///
/// This software is provided 'as-is', without any express or implied
/// warranty. In no event will the authors be held liable for any damages
/// arising from the use of this software.
///
/// Permission is granted to anyone to use this software for any purpose,
/// including commercial applications, and to alter it and redistribute it
/// freely.
#if DEBUG
#define HANDLEVERIFY
#endif
using System;
using System.Diagnostics;
using System.Collections.Generic;

namespace ProjectB
{
    /// <summary>
    ///  Handle is a string reference with basic type safety. It uses a case insensitive
    ///   quick hash algorithm (fnv1a http://www.isthe.com/chongo/tech/comp/fnv/index.html)
    /// </summary>
    /// <typeparam name="T">The type value is only used for type checking
    ///     and excluding cross type name collisions.</typeparam>
    public struct Handle<T> : IEquatable<Handle<T>>
    {
        public static readonly HandleComparer<T> Comparer = new HandleComparer<T>();
        public uint ID;
#if HANDLEVERIFY
        public string DebugString;
        public uint IDVerify;
#endif

        public Handle(string name)
        {
            Debug.Assert(name != null);

            uint hash = 2166136261;
            int charCount = name.Length;
            for (int i = 0; i < charCount; ++i)
            {
                char c = char.ToLowerInvariant(name[i]);
                hash = hash ^ c;
                hash = hash * 16777619;
            }
            ID = hash;
#if HANDLEVERIFY
            DebugString = name;
            for (int i = 0; i < charCount; ++i)
            {
                char c = char.ToLowerInvariant(name[i]);
                hash = hash ^ c;
                hash = hash * 16777619;
            }
            IDVerify = hash;
#endif
        }

        public Handle(uint prehash)
        {
            ID = prehash;
#if HANDLEVERIFY
            DebugString = null;
            IDVerify = 0;
#endif
        }

        public override string ToString()
        {
            var id = " id" + ID;
#if HANDLEVERIFY
            id += " " + DebugString;
#endif
            return typeof(T).ToString() + id;
        }

        public static bool operator ==(Handle<T> a, Handle<T> b)
        {
            Handle<T>.VerifyNoHashCollision(a, b);
            return a.ID == b.ID;
        }

        public static bool operator !=(Handle<T> a, Handle<T> b)
        {
            Handle<T>.VerifyNoHashCollision(a, b);
            return a.ID != b.ID;
        }

        public bool Equals(Handle<T> handle)
        {
            Handle<T>.VerifyNoHashCollision(this, handle);
            return ID == handle.ID;
        }

        public override bool Equals(Object o)
        {
            return o != null && GetHashCode() == (uint)o.GetHashCode();
        }

        public override int GetHashCode()
        {
            return (int)ID;
        }

        public bool IsValid()
        {
            return ID != 0;
        }

        [Conditional("HANDLEVERIFY")]
        public static void VerifyNoHashCollision(Handle<T> a, Handle<T> b)
        {
#if HANDLEVERIFY
            Debug.Assert(a.ID != b.ID || a.IDVerify == b.IDVerify || a.IDVerify * b.IDVerify == 0);
#endif
        }
    }

    public class HandleComparer<T> : EqualityComparer<Handle<T>>
    {
        public override bool Equals(Handle<T> a, Handle<T> b)
        {
            Handle<T>.VerifyNoHashCollision(a, b);
            return a.ID == b.ID;
        }

        public override int GetHashCode(Handle<T> handle)
        {
            return (int)handle.ID;
        }
    }
}