From aaa6ca4175e046dfa40faf14148a49c488dad072 Mon Sep 17 00:00:00 2001 From: Runxi Yu Date: Wed, 17 Sep 2025 03:20:49 +0800 Subject: [PATCH] Add swiss FNV wrapper --- ds/map/swiss_fnv/README | 1 + ds/map/swiss_fnv/del.ha | 9 +++++++++ ds/map/swiss_fnv/finish.ha | 10 ++++++++++ ds/map/swiss_fnv/get.ha | 9 +++++++++ ds/map/swiss_fnv/internal.ha | 12 ++++++++++++ ds/map/swiss_fnv/map.ha | 24 ++++++++++++++++++++++++ ds/map/swiss_fnv/new.ha | 35 +++++++++++++++++++++++++++++++++++ ds/map/swiss_fnv/set.ha | 9 +++++++++ ds/map/swiss_fnv/test.ha | 16 ++++++++++++++++ diff --git a/ds/map/swiss_fnv/README b/ds/map/swiss_fnv/README new file mode 100644 index 0000000000000000000000000000000000000000..b6c239c958cc6282c6f59735bd96a72d7d1cbb59 --- /dev/null +++ b/ds/map/swiss_fnv/README @@ -0,0 +1 @@ +swiss_fnv: FNV swiss tables diff --git a/ds/map/swiss_fnv/del.ha b/ds/map/swiss_fnv/del.ha new file mode 100644 index 0000000000000000000000000000000000000000..150b37c0bb9224ccc89612fb9e09181fecb92ef5 --- /dev/null +++ b/ds/map/swiss_fnv/del.ha @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: Apache-2.0 AND MPL-2.0 +// SPDX-FileCopyrightText: 2025 Runxi Yu + +use ds::map; + +// Deletes an item from a [[map]]. +export fn del(m: *map, key: []u8) (*opaque | void) = { + return map::del(m.inner, key); +}; diff --git a/ds/map/swiss_fnv/finish.ha b/ds/map/swiss_fnv/finish.ha new file mode 100644 index 0000000000000000000000000000000000000000..7581aa0b20455d4b983494ba0b30e4b0f570ee22 --- /dev/null +++ b/ds/map/swiss_fnv/finish.ha @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: Apache-2.0 AND MPL-2.0 +// SPDX-FileCopyrightText: 2025 Runxi Yu + +use ds::map; + +// Frees resources associated with a [[map]]. +export fn finish(m: *map) void = { + map::finish(m.inner); + free(m); +}; diff --git a/ds/map/swiss_fnv/get.ha b/ds/map/swiss_fnv/get.ha new file mode 100644 index 0000000000000000000000000000000000000000..591eb2b19845bfc74f8c1ce01ebb8c69e99b0761 --- /dev/null +++ b/ds/map/swiss_fnv/get.ha @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: Apache-2.0 AND MPL-2.0 +// SPDX-FileCopyrightText: 2025 Runxi Yu + +use ds::map; + +// Gets an item from a [[map]] by key, returning void if not found. +export fn get(m: *map, key: []u8) (*opaque | void) = { + return map::get(m.inner, key); +}; diff --git a/ds/map/swiss_fnv/internal.ha b/ds/map/swiss_fnv/internal.ha new file mode 100644 index 0000000000000000000000000000000000000000..87d291f1e403b22950ce97a96c9de44ff7f8e3cd --- /dev/null +++ b/ds/map/swiss_fnv/internal.ha @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: Apache-2.0 AND MPL-2.0 +// SPDX-FileCopyrightText: 2025 Runxi Yu + +use hash; +use hash::fnv; + +fn hash64(_params: nullable *opaque, key: []u8) size = { + let h = fnv::fnv64a(); + defer hash::close(&h); + hash::write(&h, key); + return fnv::sum64(&h): size; +}; diff --git a/ds/map/swiss_fnv/map.ha b/ds/map/swiss_fnv/map.ha new file mode 100644 index 0000000000000000000000000000000000000000..79b85f7030431200e4352d518c6709db292cd67f --- /dev/null +++ b/ds/map/swiss_fnv/map.ha @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: Apache-2.0 AND MPL-2.0 +// SPDX-FileCopyrightText: 2025 Runxi Yu + +use ds::map; + +// Swiss-table based map from []u8 to *opaque, using FNV-1a. +// +// You are advised to create these with [[new]]. +export type map = struct { + vt: map::map, + inner: *map::map, +}; + +const _vt: map::vtable = map::vtable { + getter = &vt_get, + setter = &vt_set, + deleter = &vt_del, + finisher = &vt_finish, +}; + +fn vt_get(m: *map::map, key: []u8) (*opaque | void) = get(m: *map, key); +fn vt_set(m: *map::map, key: []u8, v: *opaque) (void | nomem) = set(m: *map, key, v); +fn vt_del(m: *map::map, key: []u8) (*opaque | void) = del(m: *map, key); +fn vt_finish(m: *map::map) void = finish(m: *map); diff --git a/ds/map/swiss_fnv/new.ha b/ds/map/swiss_fnv/new.ha new file mode 100644 index 0000000000000000000000000000000000000000..2a5f848a70702825e3477aad4631ee3861a8ffc2 --- /dev/null +++ b/ds/map/swiss_fnv/new.ha @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: Apache-2.0 AND MPL-2.0 +// SPDX-FileCopyrightText: 2025 Runxi Yu + +use errors; +use ds::map; +use ds::map::swiss; + +// Creates a new [[map]] with an initial number of groups using FNV. +// +// n_groups must be greater than zero. +export fn new( + n_groups: size, +) (*map | errors::invalid | nomem) = { + let inner = match (swiss::new( + n_groups, &hash64, null, + )) { + case let sm: *swiss::map => + yield (sm: *map::map); + case errors::invalid => + return errors::invalid; + case nomem => + return nomem; + }; + + let m = match (alloc(map { + vt = &_vt, + inner = inner, + })) { + case let p: *map => yield p; + case nomem => + map::finish(inner); + return nomem; + }; + return m; +}; diff --git a/ds/map/swiss_fnv/set.ha b/ds/map/swiss_fnv/set.ha new file mode 100644 index 0000000000000000000000000000000000000000..7751c5afba1129d964fb208049f6961372712004 --- /dev/null +++ b/ds/map/swiss_fnv/set.ha @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: Apache-2.0 AND MPL-2.0 +// SPDX-FileCopyrightText: 2025 Runxi Yu + +use ds::map; + +// Sets an item in a [[map]], replacing any existing item with the same key. +export fn set(m: *map, key: []u8, value: *opaque) (void | nomem) = { + return map::set(m.inner, key, value); +}; diff --git a/ds/map/swiss_fnv/test.ha b/ds/map/swiss_fnv/test.ha new file mode 100644 index 0000000000000000000000000000000000000000..3ca1486057682bf1e83b6fc3ed20f4ae38876238 --- /dev/null +++ b/ds/map/swiss_fnv/test.ha @@ -0,0 +1,16 @@ +use ds::map; +use errors; + +@test fn test() void = { + const groups: [2]size = [1z, 32z]; + + for (let gi = 0z; gi < len(groups); gi += 1) { + let m: *map = match (new(groups[gi])) { + case let p: *map => yield p; + case errors::invalid => abort("swiss_fnv: invalid groups"); + case nomem => abort("swiss_fnv: nomem"); + }; + defer finish(m); + map::stress_test(m, 200000); + }; +}; -- 2.48.1