/******************************************************************************
* Copyright (C) Ultraleap, Inc. 2011-2021. *
* *
* Use subject to the terms of the Apache License 2.0 available at *
* http://www.apache.org/licenses/LICENSE-2.0, or another agreement *
* between Ultraleap and you, your company or other organization. *
******************************************************************************/
using Leap.Unity.Query;
using System;
namespace Leap.Unity
{
///
/// This is a definition-friendly interface that new "indexable" struct definitions can
/// implement to make it a little easier to implement foreach and Query() operations
/// for their struct. (You can use the IndexableStructEnumerator for this purpose, you
/// just have to pass it type arguments that correspond to your struct type.)
///
/// Unlike IIndexable, IIndexableStruct cannot utilize extension methods to
/// automatically give consumers of the interface access to foreach and Query
/// operations because consumption of a struct via an interface parameter forces the
/// struct to be boxed, which causes allocation. As such, IIndexableStruct does not
/// directly implement IIndexable.
///
/// (This all may change in C# 8 when we get traits, but Unity is still in the C# 4
/// stone age.)
///
public interface IIndexableStruct
where ThisIndexableType : struct,
IIndexableStruct
{
T this[int idx] { get; }
int Count { get; }
}
///
/// Explicit boxing class for IIndexableStructs that implements IIndexable.
///
/// This is useful when you need to pass an IIndexableStruct into a context that
/// requires an IIndexable and you also need to avoid allocating any garbage. To avoid
/// allocation, you can use the generic Pool to pool instances of this class and pass
/// it around as an IIndexable.
///
public class BoxedIndexableStruct
: IIndexable,
IPoolable
where IndexableStruct : struct,
IIndexableStruct
{
///
/// The wrapped indexable struct, or null.
///
public IndexableStruct? maybeIndexableStruct = null;
public Element this[int idx]
{
get
{
if (!maybeIndexableStruct.HasValue)
{
throw new NullReferenceException(
"PooledIndexableStructWrapper failed to index missing "
+ typeof(IndexableStruct).Name
+ "; did you assign its maybeIndexableStruct field?");
}
return maybeIndexableStruct.Value[idx];
}
}
public int Count
{
get
{
if (!maybeIndexableStruct.HasValue) { return 0; }
return maybeIndexableStruct.Value.Count;
}
}
public void OnSpawn() { }
public void OnRecycle()
{
maybeIndexableStruct = null;
}
}
public static class BoxedIndexableStructExtensions
{
///
/// If you spawned this BoxedIndexableStruct from a Pool, you can call this method
/// to recycle it back into the pool.
///
/// If you want to send an IIndexableStruct into a context that expects an
/// IIndexable without boxing, you can "convert" it to an IIndexable without
/// allocating by pooling the wrapper objects instead.
///
/// This extension method is short-hand for recycling a pooled wrapper around a
/// struct. It should be called in the finally block after a
/// try block uses the wrapper as an IIndexable. Be sure to use
/// the Pool for the BoxedIndexableStruct to spawn the wrapper in the first place.
///
public static void Recycle(this BoxedIndexableStruct pooledWrapper)
where IndexableStruct : struct,
IIndexableStruct
{
Pool>.Recycle(pooledWrapper);
}
}
///
/// A two-generic-argument variant of an enumerator that allows an IIndexableStruct
/// to quickly define an Enumerator that avoids allocation.
///
public struct IndexableStructEnumerator
where IndexableStruct : struct, IIndexableStruct
{
IndexableStruct? maybeIndexable;
int index;
public IndexableStructEnumerator(IndexableStruct indexable)
{
this.maybeIndexable = indexable;
index = -1;
}
public IndexableStructEnumerator GetEnumerator()
{
return this;
}
public bool MoveNext()
{
if (!maybeIndexable.HasValue) return false;
index++; return index < maybeIndexable.Value.Count;
}
public void Reset()
{
index = -1;
}
public Element Current { get { return maybeIndexable.Value[index]; } }
}
}