Skip to content

Instantly share code, notes, and snippets.

@mburbea
Last active August 29, 2015 14:19
Show Gist options
  • Save mburbea/f7188d9c09d2a269bada to your computer and use it in GitHub Desktop.
Save mburbea/f7188d9c09d2a269bada to your computer and use it in GitHub Desktop.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
namespace ConsoleApplication2
{
public static class CachingIEnumerable
{
public static IEnumerable<T> ToCachedEnumerable<T>(this IEnumerable<T> enumerable)
{
return new CachingEnumerable<T>(enumerable);
}
}
public class CachingEnumerable<T> : IEnumerable<T>
{
readonly StrongBox<IEnumerable<T>> _cache = new StrongBox<IEnumerable<T>>();
readonly IEnumerator<T> _producer;
readonly List<T> _storage;
public CachingEnumerable(IEnumerable<T> enumerable)
{
if (enumerable == null) throw new ArgumentNullException("enumerable");
if (enumerable is ICollection<T>)
{
_cache.Value = enumerable;
}
else
{
_producer = enumerable.GetEnumerator();
_storage = new List<T>();
}
}
public IEnumerator<T> GetEnumerator()
{
var cacheValue = Volatile.Read(ref _cache.Value);
if (cacheValue != null)
{
return cacheValue.GetEnumerator();
}
else
{
return new Walker(_cache, _producer, _storage);
}
}
IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
struct Walker : IEnumerator<T>
{
readonly StrongBox<IEnumerable<T>> _cache;
readonly IEnumerator<T> _producer;
readonly List<T> _storage;
int _n;
T _current;
public Walker(StrongBox<IEnumerable<T>> cache, IEnumerator<T> producerEnumerator, List<T> storage)
{
this = default(Walker);
_cache = cache;
_producer = producerEnumerator;
_storage = storage;
}
public T Current { get { return _current; } }
object IEnumerator.Current
{
get { return _current; }
}
public bool MoveNext()
{
if (_n < _storage.Count)
{
_current = _storage[_n++];
return true;
}
lock (_cache)
{
if (_n < _storage.Count)
{
_current = _storage[_n++];
return true;
}
if (_cache.Value != null)
{
return false;
}
if (_producer.MoveNext())
{
_current = _producer.Current;
_storage.Add(Current);
_n++;
return true;
}
_cache.Value = _storage;
using (_producer) { }
return false;
}
}
public void Reset()
{
_n = 0;
}
public void Dispose()
{
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment