#region Copyright & License Information /* * Copyright 2007-2015 The OpenRA Developers (see AUTHORS) * This file is part of OpenRA, which is free software. It is made * available to you under the terms of the GNU General Public License * as published by the Free Software Foundation. For more information, * see COPYING. */ #endregion using System; using System.Collections.Generic; using System.Linq; using OpenRA.Primitives; namespace OpenRA { static class ListExts { public static int BinarySearchMany(this List list, uint searchFor) { var start = 0; var end = list.Count; while (start != end) { var mid = (start + end) / 2; if (list[mid].ActorID < searchFor) start = mid + 1; else end = mid; } return start; } } class TraitDictionary { // construct this delegate once. static Func doCreateTraitContainer = CreateTraitContainer; static ITraitContainer CreateTraitContainer(Type t) { return (ITraitContainer)typeof(TraitContainer<>).MakeGenericType(t) .GetConstructor(Type.EmptyTypes).Invoke(null); } Dictionary traits = new Dictionary(); ITraitContainer InnerGet(Type t) { return traits.GetOrAdd(t, doCreateTraitContainer); } TraitContainer InnerGet() { return (TraitContainer)InnerGet(typeof(T)); } public void PrintReport() { Log.AddChannel("traitreport", "traitreport.log"); foreach (var t in traits.OrderByDescending(t => t.Value.Queries).TakeWhile(t => t.Value.Queries > 0)) Log.Write("traitreport", "{0}: {1}", t.Key.Name, t.Value.Queries); } public void AddTrait(Actor actor, object val) { var t = val.GetType(); foreach (var i in t.GetInterfaces()) InnerAdd(actor, i, val); foreach (var tt in t.BaseTypes()) InnerAdd(actor, tt, val); } void InnerAdd(Actor actor, Type t, object val) { InnerGet(t).Add(actor, val); } static void CheckDestroyed(Actor actor) { if (actor.Destroyed) throw new InvalidOperationException("Attempted to get trait from destroyed object ({0})".F(actor)); } public bool Contains(Actor actor) { CheckDestroyed(actor); return InnerGet().GetMultiple(actor.ActorID).Any(); } public T Get(Actor actor) { CheckDestroyed(actor); return InnerGet().Get(actor.ActorID); } public T GetOrDefault(Actor actor) { CheckDestroyed(actor); return InnerGet().GetOrDefault(actor.ActorID); } public IEnumerable WithInterface(Actor actor) { CheckDestroyed(actor); return InnerGet().GetMultiple(actor.ActorID); } public IEnumerable> ActorsWithTrait() { return InnerGet().All(); } public void RemoveActor(Actor a) { foreach (var t in traits) t.Value.RemoveActor(a.ActorID); } interface ITraitContainer { void Add(Actor actor, object trait); void RemoveActor(uint actor); int Queries { get; } } class TraitContainer : ITraitContainer { readonly List actors = new List(); readonly List traits = new List(); public int Queries { get; private set; } public void Add(Actor actor, object trait) { var insertIndex = actors.BinarySearchMany(actor.ActorID + 1); actors.Insert(insertIndex, actor); traits.Insert(insertIndex, (T)trait); } public T Get(uint actor) { var result = GetOrDefault(actor); if (result == null) throw new InvalidOperationException("Actor does not have trait of type `{0}`".F(typeof(T))); return result; } public T GetOrDefault(uint actor) { ++Queries; var index = actors.BinarySearchMany(actor); if (index >= actors.Count || actors[index].ActorID != actor) return default(T); else if (index + 1 < actors.Count && actors[index + 1].ActorID == actor) throw new InvalidOperationException("Actor has multiple traits of type `{0}`".F(typeof(T))); else return traits[index]; } public IEnumerable GetMultiple(uint actor) { ++Queries; return new MultipleEnumerable(this, actor); } class MultipleEnumerable : IEnumerable { readonly TraitContainer container; readonly uint actor; public MultipleEnumerable(TraitContainer container, uint actor) { this.container = container; this.actor = actor; } public IEnumerator GetEnumerator() { return new MultipleEnumerator(container, actor); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } } class MultipleEnumerator : IEnumerator { readonly List actors; readonly List traits; readonly uint actor; int index; public MultipleEnumerator(TraitContainer container, uint actor) { actors = container.actors; traits = container.traits; this.actor = actor; Reset(); } public void Reset() { index = actors.BinarySearchMany(actor) - 1; } public bool MoveNext() { return ++index < actors.Count && actors[index].ActorID == actor; } public T Current { get { return traits[index]; } } object System.Collections.IEnumerator.Current { get { return Current; } } public void Dispose() { } } public IEnumerable> All() { ++Queries; return new AllEnumerable(this); } class AllEnumerable : IEnumerable> { readonly TraitContainer container; public AllEnumerable(TraitContainer container) { this.container = container; } public IEnumerator> GetEnumerator() { return new AllEnumerator(container); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } } class AllEnumerator : IEnumerator> { readonly List actors; readonly List traits; int index; public AllEnumerator(TraitContainer container) { actors = container.actors; traits = container.traits; Reset(); } public void Reset() { index = -1; } public bool MoveNext() { return ++index < actors.Count; } public TraitPair Current { get { return new TraitPair { Actor = actors[index], Trait = traits[index] }; } } object System.Collections.IEnumerator.Current { get { return Current; } } public void Dispose() { } } public void RemoveActor(uint actor) { var startIndex = actors.BinarySearchMany(actor); if (startIndex >= actors.Count || actors[startIndex].ActorID != actor) return; var endIndex = startIndex + 1; while (endIndex < actors.Count && actors[endIndex].ActorID == actor) endIndex++; var count = endIndex - startIndex; actors.RemoveRange(startIndex, count); traits.RemoveRange(startIndex, count); } } } }