diff --git a/src/libs/karm-base/buf.h b/src/libs/karm-base/buf.h index dc3cd2568d..86a2974136 100644 --- a/src/libs/karm-base/buf.h +++ b/src/libs/karm-base/buf.h @@ -27,11 +27,10 @@ struct Buf { return buf; } - Buf() = default; - - Buf(usize cap) + Buf(usize cap = 0) : _cap(cap) { - _buf = new Inert[cap]; + if (cap) + _buf = new Inert[cap]; } Buf(std::initializer_list other) { diff --git a/src/libs/karm-base/hash.h b/src/libs/karm-base/hash.h index ef673ba18b..a7e1ec8b11 100644 --- a/src/libs/karm-base/hash.h +++ b/src/libs/karm-base/hash.h @@ -1,21 +1,11 @@ #pragma once -#include -#include +#include "checked.h" +#include "slice.h" namespace Karm { -struct Hash { - usize _value; - - constexpr explicit operator usize() const { - return _value; - } - constexpr auto operator<=>(Hash const &) const = default; - constexpr auto operator+(Hash const &o) const { - return Hash(_value + o._value + 0x9e3779b9 + (_value << 6) + (_value >> 2)); - } -}; +using Hash = usize; template struct Hasher; @@ -30,11 +20,11 @@ struct Hasher { template <> struct Hasher { static constexpr Hash hash(Bytes bytes) { - usize hash = 0; + Hash hash = 0; for (auto &b : bytes) hash = (1000003 * hash) ^ b; hash ^= bytes.len(); - return {hash}; + return hash; } }; diff --git a/src/libs/karm-base/inert.h b/src/libs/karm-base/inert.h index 9d7e716327..164ea94aa4 100644 --- a/src/libs/karm-base/inert.h +++ b/src/libs/karm-base/inert.h @@ -8,7 +8,7 @@ namespace Karm { template -union Inert { +struct Inert { alignas(alignof(T)) char _inner[sizeof(T)]; template diff --git a/src/libs/karm-base/map.h b/src/libs/karm-base/map.h index 9a69418f2c..0cad58883e 100644 --- a/src/libs/karm-base/map.h +++ b/src/libs/karm-base/map.h @@ -1,6 +1,5 @@ #pragma once -#include "cons.h" #include "std.h" #include "vec.h" diff --git a/src/libs/karm-base/set.h b/src/libs/karm-base/set.h new file mode 100644 index 0000000000..eabd52aa2c --- /dev/null +++ b/src/libs/karm-base/set.h @@ -0,0 +1,139 @@ +#pragma once + +#include "clamp.h" +#include "hash.h" +#include "inert.h" + +namespace Karm { + +template +struct Set { + enum State {}; + + struct Slot : public Inert { + enum struct State : u8 { + FREE, + USED, + DEAD, + }; + + using enum State; + State state = State::FREE; + }; + + Slot *_slots = nullptr; + usize _cap = 0; + usize _len = 0; + + Set(usize cap = 0) : _cap(cap) { + if (cap) + _slots = new Slot[cap]; + } + + ~Set() { + clear(); + } + + void ensure(usize desired) { + if (desired <= _cap) + return; + + if (not _slots) { + _slots = new Slot[desired]; + _cap = desired; + return; + } + + auto *oldSlots = _slots; + usize oldCap = _cap; + + _slots = new Slot[desired]; + _cap = desired; + _len = 0; + + for (usize i = 0; i < _cap; i++) + _slots[i].state = Slot::FREE; + + for (usize i = 0; i < oldCap; i++) { + if (oldSlots[i].state != Slot::USED) + continue; + _put(oldSlots[i].unwrap()); + } + + delete[] oldSlots; + } + + usize _usage() const { + if (not _cap) + return 100; + return (_len * 100) / _cap; + } + + void _put(T const &t) { + usize i = hash(t) % _cap; + while (_slots[i].state != Slot::FREE) { + if (_slots[i].unwrap() == t) + return; + i = (i + 1) % _cap; + } + + _slots[i].ctor(t); + _slots[i].state = Slot::USED; + _len++; + } + + void put(T const &t) { + if (_usage() > 80) + ensure(max(_cap * 2, 16uz)); + + _put(t); + } + + Slot *_lookup(T const &t) const { + if (_len == 0) + return nullptr; + + usize i = hash(t) % _cap; + while (_slots[i].state != Slot::FREE) { + auto &s = _slots[i]; + if (s.state == Slot::USED and + s.unwrap() == t) + return &s; + i = (i + 1) % _cap; + } + return nullptr; + } + + bool has(T const &t) const { + return _lookup(t); + } + + void del(T const &t) { + auto *slot = _lookup(t); + if (not slot) + return; + + slot->state = Slot::DEAD; + slot->dtor(); + _len--; + } + + void clear() { + if (not _slots) + return; + for (usize i = 0; i < _cap; i++) + if (_slots[i].state == Slot::USED) + _slots[i].dtor(); + delete[] _slots; + + _slots = nullptr; + _cap = 0; + _len = 0; + } + + usize len() const { + return _len; + } +}; + +} // namespace Karm diff --git a/src/libs/karm-base/sieve.h b/src/libs/karm-base/sieve.h index 02bad7329a..a928a5f533 100644 --- a/src/libs/karm-base/sieve.h +++ b/src/libs/karm-base/sieve.h @@ -77,7 +77,7 @@ struct Sieve { return NONE; } - bool contains(K const &key) { + bool has(K const &key) { return _lookup(key) != nullptr; } diff --git a/src/libs/karm-base/tests/test-set.cpp b/src/libs/karm-base/tests/test-set.cpp new file mode 100644 index 0000000000..86f4be9638 --- /dev/null +++ b/src/libs/karm-base/tests/test-set.cpp @@ -0,0 +1,96 @@ +#include +#include + +namespace Karm::Base::Tests { + +test$("set-put") { + Set set{}; + set.put(420); + expect$(set.has(420)); + + return Ok(); +} + +test$("set-remove") { + Set set{}; + set.put(420); + expect$(set.has(420)); + set.del(420); + expect$(not set.has(420)); + + return Ok(); +} + +test$("set-clear") { + Set set{}; + set.put(420); + set.put(69); + expect$(set.has(420)); + expect$(set.has(69)); + set.clear(); + expect$(not set.has(420)); + expect$(not set.has(69)); + + return Ok(); +} + +test$("set-len") { + Set set{}; + expectEq$(set.len(), 0uz); + set.put(420); + expectEq$(set.len(), 1uz); + set.put(69); + expectEq$(set.len(), 2uz); + set.del(420); + expectEq$(set.len(), 1uz); + + return Ok(); +} + +test$("set-usage") { + Set set{10}; + expectEq$(set._usage(), 0uz); + set.put(420); + expectEq$(set._usage(), 10uz); + set.put(69); + expectEq$(set._usage(), 20uz); + set.del(420); + expectEq$(set._usage(), 10uz); + + return Ok(); +} + +test$("set-ensure") { + Set set{10}; + expectEq$(set._cap, 10uz); + set.ensure(20); + expectEq$(set._cap, 20uz); + set.ensure(10); + expectEq$(set._cap, 20uz); + + return Ok(); +} + +test$("set-put-collision") { + Set set{10}; + set.put(420); + set.put(69); + set.put(420); + expectEq$(set.len(), 2uz); + + return Ok(); +} + +test$("set-put-resize") { + Set set{10}; + for (int i = 0; i < 10; i++) { + set.put(i); + } + expectEq$(set.len(), 10uz); + set.put(420); + expect$(set.has(420)); + + return Ok(); +} + +} // namespace Karm::Base::Tests diff --git a/src/libs/karm-base/tests/test-sieve.cpp b/src/libs/karm-base/tests/test-sieve.cpp index 713f5b0cb8..09671092e2 100644 --- a/src/libs/karm-base/tests/test-sieve.cpp +++ b/src/libs/karm-base/tests/test-sieve.cpp @@ -22,11 +22,11 @@ test$("sieve-access") { test$("sieve-contains") { Sieve cache{10}; - expect$(not cache.contains(0)); + expect$(not cache.has(0)); (void)cache.access(0, [&] { return 0; }); - expect$(cache.contains(0)); + expect$(cache.has(0)); return Ok(); } @@ -66,7 +66,7 @@ test$("seive-evict") { return 10 * 10; }); - expect$(not cache.contains(0)); + expect$(not cache.has(0)); expectEq$(cache.len(), 10uz); return Ok();