Skip to content

[Proposal] std/mem: add a more generic eql function#7848

Closed
ifreund wants to merge 1 commit intoziglang:masterfrom
ifreund:generic-mem-eql
Closed

[Proposal] std/mem: add a more generic eql function#7848
ifreund wants to merge 1 commit intoziglang:masterfrom
ifreund:generic-mem-eql

Conversation

@ifreund
Copy link
Member

@ifreund ifreund commented Jan 21, 2021

I had to write some very ugly code recently to compare two optional, sentinel-terminated pointers in river: https://github.com/ifreund/river/blob/cd005e15f8bcb6852b35fd3d89248e72611a5d5f/river/Option.zig#L87-L90

    } else if (self.value == .string and
        // TODO: std.mem needs a good way to compare optional sentinel pointers
        ((self.value.string == null and value.string == null) or
        (self.value.string != null and value.string != null and
        std.cstr.cmp(self.value.string.?, value.string.?) != 0)))
    {

As I wrote in the comment, this made me feel like we needed a function to do a better job of this in std.mem. I've Implemented such a function in this patch as a proposal. I'm not sure if this function should replace the current std.mem.eql() as that would be a widely breaking change and the anytype based API is not as simple when working with slices. However, this would match the API of std.meta.eql() so perhaps simply replacing std.mem.eql() is worth considering.

The new function is temporarily named eqlGeneric(), but if we decide not to replace current mem.eql() the new function should have a better name. Naming suggestions welcome.

Edit: std.meta.deepEql() or similar would also be a reasonable place to put this function.

@ifreund
Copy link
Member Author

ifreund commented Jan 22, 2021

One other feature we should consider here is easy and efficient comparison of sentinel terminated pointers and slices. The current pattern is to use for example:

mem.eql(u8, my_slice, mem.span(my_sentinel_terminated_ptr));

However this is inefficient as the full length of the sentinel terminated pointer is always iterated even when the slice is shorter. Ideally the comparison would look something like this:

fn better(a: []const u8, b: [*:0]const u8) bool {
    for (a) |a_elem, idx| {
        if (b[idx] == 0 or b[idx] != a_elem) return false;
    }
    return b[a.len] == 0;
}

Comparison in godbolt
I'm no expert in asm, but I've been led to believe that fewer instructions are better

@andrewrk
Copy link
Member

andrewrk commented Apr 2, 2021

Closing old drafts; please feel free to re-open if you decide to pick this back up.

@andrewrk
Copy link
Member

andrewrk commented Apr 2, 2021

I see this was intended to be a proposal; filed #8414 on your behalf

IOKG04 added a commit to IOKG04/zig that referenced this pull request Aug 3, 2025
probably still gonna change the naming.
TODO: more functions, maybe link to `std.mem.len()` from there

I am happy to report though, that I tested the speed of these,
even if in a very bad way, but still conclusive enough,
and the versions I wrote/stole from ifreund are faster :3
note that I didn't test the `allEquals()` function,
but I'll just assume it's faster too..

ziglang#7848 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants