Lindenii Project Forge
Login

hare-ds

Data structures for Hare
Commit info
ID
aaa6ca4175e046dfa40faf14148a49c488dad072
Author
Runxi Yu <me@runxiyu.org>
Author date
Wed, 17 Sep 2025 03:20:49 +0800
Committer
Runxi Yu <me@runxiyu.org>
Committer date
Wed, 17 Sep 2025 03:20:49 +0800
Actions
Add swiss FNV wrapper
swiss_fnv: FNV swiss tables
// SPDX-License-Identifier: Apache-2.0 AND MPL-2.0
// SPDX-FileCopyrightText: 2025 Runxi Yu <me@runxiyu.org>

use ds::map;

// Deletes an item from a [[map]].
export fn del(m: *map, key: []u8) (*opaque | void) = {
	return map::del(m.inner, key);
};
// SPDX-License-Identifier: Apache-2.0 AND MPL-2.0
// SPDX-FileCopyrightText: 2025 Runxi Yu <me@runxiyu.org>

use ds::map;

// Frees resources associated with a [[map]].
export fn finish(m: *map) void = {
	map::finish(m.inner);
	free(m);
};
// SPDX-License-Identifier: Apache-2.0 AND MPL-2.0
// SPDX-FileCopyrightText: 2025 Runxi Yu <me@runxiyu.org>

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);
};
// SPDX-License-Identifier: Apache-2.0 AND MPL-2.0
// SPDX-FileCopyrightText: 2025 Runxi Yu <me@runxiyu.org>

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;
};
// SPDX-License-Identifier: Apache-2.0 AND MPL-2.0
// SPDX-FileCopyrightText: 2025 Runxi Yu <me@runxiyu.org>

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);
// SPDX-License-Identifier: Apache-2.0 AND MPL-2.0
// SPDX-FileCopyrightText: 2025 Runxi Yu <me@runxiyu.org>

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;
};
// SPDX-License-Identifier: Apache-2.0 AND MPL-2.0
// SPDX-FileCopyrightText: 2025 Runxi Yu <me@runxiyu.org>

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);
};
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);
	};
};