Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Span<T> range operator doesn't make sense

I cannot for the life of me understand the logic behind the Range operator of a Span.

I have a the following byte array which contains an IP Protocol header:

---------------------------------------------------------------------------------
|   |   |   |   |   |   |   |   |   |   | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 9 |
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---------------------------------------------------------------------------------
|                   other data                  |    src ip     |    dest ip    |
---------------------------------------------------------------------------------

Using the range operator, I need to use the following values to extract the proper 4 byte arrays:

Span<byte> ipHeader = ethernetFrame.IPHeader;
byte[] sourceIpAddress = ipHeader[12..16].ToArray();
byte[] destinationIpAddress = ipHeader[16..20].ToArray();

If instead, I use the Slice method, I end up with:

Span<byte> ipHeader = ethernetFrame.IPHeader;
byte[] sourceIpAddress = ipHeader.Slice(12, 4).ToArray();
byte[] destinationIpAddress = ipHeader.Slice(16, 4).ToArray();

What is the logic behind the range operator that requires me to give an end value that's 1 past the actual ending index value?

I'm also curious from a performance perspective (because this particular code block runs millions of times per minute), would using Slice be more efficient anyway?

like image 757
jscarle Avatar asked Jun 09 '26 21:06

jscarle


1 Answers

I cannot for the life of me understand the logic behind the Range operator of a Span.

It's worth noting that this isn't specific to Span - ranges in C# are always expected to have an exclusive upper bound. (See the tutorial for more.)

What is the logic behind the range operator that requires me to give an end value that's 1 past the actual ending index value?

It's just an exclusive upper bound. Upper bounds are very often exclusive, and that has multiple benefits. For example, notice how you have two | dividers in your picture? Those are at index 12 and 16 - that's the upper bound of the range before, and the lower bound of the range after. The same number is useful in both places, so you don't need to start adding or subtracting one.

Note that exclusive upper bounds are really common in for loops as well. If you wanted to manually copy that src IP address, I suspect you'd do something like:

for (int index = 12; index < 16; index++)
{
    // Copy item at index
}

I consider that much more idiomatic than using an inclusive upper bound:

for (int index = 12; index <= 15; index++)
{
    // Copy item at index
}

It also works naturally with ranges specified as start and length:

for (int index = start; index < start + length; index++)
{
    // Copy item at index
}

(Or conversely, with exclusive upper bounds you can find the length just by subtracting the start from the end, without any "add one" parts.)

I'm also curious from a performance perspective (because this particular code block runs millions of times per minute), would using Slice be more efficient anyway?

The range operator will slice the span already. What's potentially inefficient is constructing new byte arrays - if you can avoid that, and use the spans directly, that will be more efficient.

like image 142
Jon Skeet Avatar answered Jun 12 '26 10:06

Jon Skeet



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!