-
Notifications
You must be signed in to change notification settings - Fork 4.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Overlaps/Within extension methods for ReadOnlySpan<T> #23580
Comments
A few thoughts:
Another idea that might be worth considering: public static bool Overlaps<T>(this ReadOnlySpan<T> first, ReadOnlySpan<T> second);
public static bool Overlaps<T>(this ReadOnlySpan<T> first, ReadOnlySpan<T> second,
out int startDifference);
public static bool Overlaps<T>(this ReadOnlySpan<T> first, ReadOnlySpan<T> second,
out int startDifference, out int endDifference); where
|
@ektrah, what is the value of having the following: public static bool Overlaps<T>(this ReadOnlySpan<T> first, ReadOnlySpan<T> second,
out int startDifference); Also, there is no way to distinguish between scenario A & E based on the output of Overlaps (from your design) but I don't think we need to. Regarding:
Can you please provide an example where we would throw an exception? Similar to @ektrah, I don't think Contains would work as a name. I think of contains the same way as Array.contains or List.Contains: I am not sure about ContainsSlice either. I think of ContainsSlice as follows: Span<byte> src = new byte [1, 2, 3, 4, 5];
Span<byte> items = new byte [2, 3, 4];
src.ContainsSlice(items); // returns true; Maybe IsContainedWithin? src.IsContainedWithin(dest) @ektrah's proposal avoids the naming issue altogether. |
You don't have to type I don't have a strong opinion if this overload should be there or not. Since
If the spans overlap, then it's reasonable to assume that the spans refer to the same object in managed memory. (Unsafe code can defeat that assumption.) If the spans do not overlap (A & E), then it's quite likely that they do not refer to the same object in managed memory. Since there is no defined distance or relative order between two arbitrary objects in managed memory, there is no meaningful
var bytes = new byte[16];
var first = new Span<byte>(bytes, 0, 8).NonPortableCast<byte, int>();
var second = new Span<byte>(bytes, 7, 8).NonPortableCast<byte, int>();
var overlaps = first.Overlaps(second, out int elementIndex); What should the value of Ignoring this corner case may actually not be that bad. An exception would be fine as well though (IMO). |
@nietras, any thoughts or updates for this API proposal (fix the TODOs)? I would consider @ektrah's idea which removes the need of a Contains method: public static bool Overlaps<T>(this ReadOnlySpan<T> first, ReadOnlySpan<T> second);
public static bool Overlaps<T>(this ReadOnlySpan<T> first, ReadOnlySpan<T> second,
out int startDifference, out int endDifference); You mentioned previously (from https://github.com/dotnet/corefx/issues/18750#issuecomment-296601237):
Can you explain why they would have different optimization paths? |
@ektrah thanks for the feedback and answering questions :)
They cannot be greater than
If a user knows the spans come from the same memory segment this could have utility, but it is an edge case, and I agree. It makes the API/implementation more straightforward too by removing this case.
I think
There are edge cases here, an empty span has
It is not ideal I agree, but there is prior work especially from Below examples of
This also suggests that an There are other examples and one I would then pitch instead, namely if (child.Within(parent, out var childElementOffset))
{
// Return to pool
}
if (child.Within(parent))
{
// Return to pool
}
@ahsonkhan the So to sum I would change the proposal to: public static class SpanExtensions
{
public static bool Overlaps<T>(this ReadOnlySpan<T> first, ReadOnlySpan<T> second);
public static bool Overlaps<T>(this ReadOnlySpan<T> first, ReadOnlySpan<T> second,
out int elementOffset);
public static bool Within<T>(this ReadOnlySpan<T> first, ReadOnlySpan<T> second);
public static bool Within<T>(this ReadOnlySpan<T> first, ReadOnlySpan<T> second,
out int elementOffset);}
} Let me know what you think. And I will try to update the proposal as soon as feedback comes in, sorry for the long reply here. If we still cannot agree on Possible other extensions inspired by public static class SpanExtensions
{
public static Span<T> Intersect<T>(this Span<T> first, Span<T> second);
public static ReadOnlySpan<T> Intersect<T>(this ReadOnlySpan<T> first, ReadOnlySpan<T> second);
} There is also the |
@nietras, I have updated the original post with the method rename (Contains -> Within) and parameter rename (elementOffset -> elementIndex). If you get a chance, please update (or remove) the TODOs and remove the WIP. Marking API ready for review. I will solicit additional feedback (if any) during API review. |
@ahsonkhan I have revised the proposal and added new cases and checks to remove the last edge cases. Didn't have time to add more examples, hope this is good enough for review. |
We seem to believe public static class SpanExtensions
{
public static bool Overlaps<T>(this ReadOnlySpan<T> first, ReadOnlySpan<T> second);
public static bool Overlaps<T>(this ReadOnlySpan<T> first, ReadOnlySpan<T> second, out int elementOffset);
} |
|
Should there also be overloads with Span<T> as the first parameter? public static class SpanExtensions
{
public static bool Overlaps<T>(this Span<T> first, ReadOnlySpan<T> second);
public static bool Overlaps<T>(this Span<T> first, ReadOnlySpan<T> second, out int elementOffset);
} Otherwise users have to cast every Span<T> to ReadOnlySpan<T> first when using extension method syntax: Span<byte> first = ...;
Span<byte> second = ...;
bool result = ((ReadOnlySpan<byte>)first).Overlaps(second); |
@ektrah public static implicit operator ReadOnlySpan<T>(Span<T> span) UPDATE: Sorry, you are right that for as an extension method this has issues...
@terrajobst fair enough. Guess we´ll have to prove that wrong if not 😉 Shall I update the proposal to show only
@benaadams cast to |
@nietras Yes, except if you use extension method syntax. |
@ektrah yeah I updated my comment two seconds after realizing that. My
mistake. I think it would be good to add these overloads too, otherwise
they become too cumbersome.
…On Oct 31, 2017 8:08 PM, "ektrah" ***@***.***> wrote:
@nietras <https://github.com/nietras> Yes, except if you use extension
method syntax.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<https://github.com/dotnet/corefx/issues/24103#issuecomment-340875103>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AKTG73GwXCkJhZ6__5Kz7Z1__HRnrTB5ks5sx3BEgaJpZM4PaGZ9>
.
|
@mgravell, FYI |
FYI: The API review discussion was recorded - see https://youtu.be/bHwCPVNQLwo?t=2568 (33 min duration) |
@nietras, feel free to revisit this if it turns out we need Within. |
This proposal adds utility methods for checking spans for overlaps or if a span is within in another span. For previous discussions see https://github.com/dotnet/corefx/issues/17793, dotnet/corefxlab#827 and https://github.com/dotnet/corefx/issues/18750. And a closed PR dotnet/corefx#18731.
Proposed API
Add two sets of extension methods
Overlaps
andWithin
forReadOnlySpan<T>
inSpanExtensions
:second
span does not match the alignment offirst
span anArgumentException
is thrown.out int elementOffset
is the relative offset or index of the start of thesecond
span to the start of thefirst
span.elementOffset
is only valid if the check in question returnstrue
.Rationale and Usage
For
Overlaps
the scenario can involve algorithms that transform elements fromfirst
tosecond
span,if the spans overlap the result might be wrong depending on the algorithm. To be able to detect this
the
Overlaps
method can be called.For
Within
the scenario can involve buffer pool management, where spans are leased from a buffer and later needs to be returned to a given buffer. CallingWithin
allows checking for whichbuffer the given span is within and thus where it must be returned to.
Expected Results
first
entirely beforesecond
, no overlapfirst
starts beforesecond
and ends insidesecond
first
is entirely withinsecond
first
starts inside targetsecond
and ends aftersecond
endfirst
entirely aftersecond
, no overlapfirst
starts beforesecond
andends after
second
, i.e.second
is contained infirst
first
is same assecond
first
is empty withinsecond
first
is empty beforesecond
first
is empty aftersecond
In table below
Overlaps => first.Overlaps(second)
orWithin => first.Within(second)
.x => first
andy => second
.Overlaps
Within
false
false
true
false
elementOffset > 0
true
true
true
false
elementOffset < 0
true
false
elementOffset >= 0 && xLength >= (elementOffset + yLength)
true
true
elementOffset == 0 && xLength == yLength
true
true
xLength == 0
false
false
Examples
Open Questions
Could this API be expressed differently, perhaps with an all encompassing
Overlap
method returning an enum? Performance downsides?Naming of the methods should be reviewed.
Updates
UPDATE 1: Rename
Contains
toWithin
with reversedfirst
/second
relationship, due to naming confusion. RenameelementIndex
toelementOffset
latter considered better.elementOffset
only valid if check returnstrue
. Revise table and add empty cases.cc: @ektrah @mgravell @ahsonkhan @shiftylogic @jkotas
The text was updated successfully, but these errors were encountered: