cached

A _range algorithm that caches the elements of a _range by evaluating them only once.

As a natural benefit of caching, the returned _range is a ForwardRange even if the source _range is an InputRange. Although the returned _range provides opIndex, it is a RandomAccessRange only if the source _range hasLength.

The version that takes buffers may never need to allocate memory if the number of elements never exceed the capacity of the underlying CircularBlocks. (See CircularBlocks.)

  1. auto cached(R range, size_t heapBlockCapacity)
    cached
    (
    R
    )
    (,
    size_t heapBlockCapacity = pageSize / ElementType!R.sizeof
    )
  2. auto cached(R range, ubyte[N][M] buffers)

Parameters

range R

the source _range to cache elements of

heapBlockCapacity size_t

the minimum capacity to use for CircularBlocks, which is used as storage for the cached elements; the default value attemps to use one page of memory

Bugs

Although opIndex is provided, isRandomAccessRange produces false because this implementation is not a BidirectionalRange at this time.

Todo:

Add opApplyReverse support and BidirectionalRange functions

Examples

// In the following typical usage, even though `slide(2)` would visit most
// elements more than once, `cached` guarantees that the lambda function
// executed by `map` will be executed only once per range element.

auto r = iota(4)
         .map!(i => i * i)    // [0, 1, 4, 9]
         .cached
         .slide(2)
         .array;

// There are 3 sliding windows of width 2 over 4 elements
r.length.shouldBe(3);
assert(r[0].equal([0, 1]));
assert(r[1].equal([1, 4]));
assert(r[2].equal([4, 9]));
// Random access over an InputRange

import std.algorithm : splitter;
import std.range : popFrontN;

auto lines = "monday,tuesday,wednesday,thursday";

auto s = lines.splitter(',');
auto c = s.cached;

// The source range does not provide random access:
static assert(!__traits(compiles, s[0]));

// The cached range does:
assert(c[2] == "wednesday");
assert(c[1] == "tuesday");

c.popFrontN(3);

// Now index 0 is another element
assert(c[0] == "thursday");
// This version uses an existing buffer. It may never need to allocate
// additional heap memory.

ubyte[1024][2] buffers;
auto r = iota(4)
         .map!(i => i * i)
         .cached(buffers)    // Will use the provided buffers
         .slide(2)
         .array;

r.length.shouldBe(3);

Meta