Recursive range iteration
First, I'd prefer to parse the int[] into a Node[] or somesuch - I'd even argue the name of node_value is a code smell indicating that it should be a member called value of a type called Node. I believe this is part of what causes your consternation with the header variable.

Speaking of names, D naming conventions generally avoids underscores except for constants, so more idiomatic names would be nodeValue and countMetadata (which would perhaps more correctly be called sumMetadata).

One of the reasons you should parse to Node[] is it factors out the common parts between node_value and count_metadata, making their responsibilities clearer and the code easier to follow - remember, you're writing code for the next person who looks at it; the computer is happy with a string of bits that nobody can read.

Now, you mention genericity. For the most part, replacing (ref int[] r) with (R)(ref R r) if (isForwardRange!R) will deal with this. This templates the functions on the type of the range passed. Your code otherwise uses range functions (front, popFront, take, etc), so so no other change is necessary.

However, due to the structure of the file format, you will need to read all child nodes before getting at the metadata of a node. This limits the possible laziness of the solution. Since you are always iterating over every node, this doesn't matter a whole lot, but since that's one of the great benefits of ranges, it's worth pointing out.

As for consuming the range, there is no very simple, unobtrusive solution. You need to either create a temporary variable (like p1), have a duplicate function for the non-ref case, or use inner functions:


int count_metadata(R)(R r0)
if (isForwardRange!R)
{
    int impl(ref R r) {
        // Same code as before, but call impl instead of count_metadata
    }
    return impl(r);
}


You also mentioned efficiency. I don't think I can improve much on that. Both count_metadata and node_value are O(n), and my proposed solution doesn't change that.