Skip to content

Commit a69298e

Browse files
Add hashing support compatible with std::hash (#11)
1 parent 4f2a521 commit a69298e

File tree

4 files changed

+50
-2
lines changed

4 files changed

+50
-2
lines changed

c_include/fine.hpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,16 @@ inline ERL_NIF_TERM make_atom(ErlNifEnv *env, const char *msg) {
6565
// A representation of an atom term.
6666
class Atom {
6767
public:
68-
Atom(std::string name) : name(name), term(std::nullopt) {
68+
Atom(std::string name) : name(std::move(name)), term(std::nullopt) {
6969
if (!Atom::initialized) {
7070
Atom::atoms.push_back(this);
7171
}
7272
}
7373

74-
std::string to_string() const { return this->name; }
74+
public:
75+
const std::string &to_string() const & noexcept { return this->name; }
76+
77+
std::string to_string() && noexcept { return this->name; }
7578

7679
bool operator==(const Atom &other) const { return this->name == other.name; }
7780

@@ -90,6 +93,8 @@ class Atom {
9093
}
9194

9295
friend struct Encoder<Atom>;
96+
friend struct Decoder<Atom>;
97+
friend struct ::std::hash<Atom>;
9398

9499
friend int __private__::load(ErlNifEnv *env, void **priv_data,
95100
ERL_NIF_TERM load_info);
@@ -1182,4 +1187,18 @@ inline int load(ErlNifEnv *env, void **, ERL_NIF_TERM) {
11821187

11831188
} // namespace fine
11841189

1190+
namespace std {
1191+
template <> struct hash<::fine::Term> {
1192+
size_t operator()(const ::fine::Term &term) noexcept {
1193+
return enif_hash(ERL_NIF_INTERNAL_HASH, term, 0);
1194+
}
1195+
};
1196+
1197+
template <> struct hash<::fine::Atom> {
1198+
size_t operator()(const ::fine::Atom &atom) noexcept {
1199+
return std::hash<std::string_view>{}(atom.to_string());
1200+
}
1201+
};
1202+
} // namespace std
1203+
11851204
#endif

test/c_src/finest.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include <cstring>
22
#include <exception>
3+
#include <functional>
34
#include <memory_resource>
45
#include <optional>
56
#include <stdexcept>
@@ -388,6 +389,17 @@ bool compare_ge(ErlNifEnv *, fine::Term lhs, fine::Term rhs) noexcept {
388389
return lhs >= rhs;
389390
}
390391
FINE_NIF(compare_ge, 0);
392+
393+
std::uint64_t hash_term(ErlNifEnv *, fine::Term term) noexcept {
394+
return std::invoke(std::hash<fine::Term>{}, term);
395+
}
396+
FINE_NIF(hash_term, 0);
397+
398+
std::uint64_t hash_atom(ErlNifEnv *, fine::Atom atom) noexcept {
399+
return std::invoke(std::hash<fine::Atom>{}, atom);
400+
}
401+
FINE_NIF(hash_atom, 0);
402+
391403
} // namespace finest
392404

393405
FINE_INIT("Elixir.Finest.NIF");

test/lib/finest/nif.ex

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,5 +71,8 @@ defmodule Finest.NIF do
7171
def compare_gt(_lhs, _rhs), do: err!()
7272
def compare_ge(_lhs, _rhs), do: err!()
7373

74+
def hash_term(_term), do: err!()
75+
def hash_atom(_term), do: err!()
76+
7477
defp err!(), do: :erlang.nif_error(:not_loaded)
7578
end

test/test/finest_test.exs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,4 +359,18 @@ defmodule FinestTest do
359359
assert NIF.compare_ge("fine", "fine")
360360
end
361361
end
362+
363+
describe "hash" do
364+
test "term" do
365+
for value <- [42, "fine", ["it", %{"should" => {"just", "work"}}], :atom] do
366+
assert NIF.hash_term(value) == NIF.hash_term(value)
367+
end
368+
end
369+
370+
test "atom" do
371+
for value <- [:ok, :error, :"with spaces", Enum, nil, true, false] do
372+
assert NIF.hash_atom(value) == NIF.hash_atom(value)
373+
end
374+
end
375+
end
362376
end

0 commit comments

Comments
 (0)