diff --git a/lib/glacier/container/hash_map.h b/lib/glacier/container/hash_map.h new file mode 100644 index 0000000..1c066b4 --- /dev/null +++ b/lib/glacier/container/hash_map.h @@ -0,0 +1,222 @@ +#pragma once + +#include + +#include "glacier/container/array.h" +#include "glacier/container/linked_list.h" +#include "glacier/container/pair.h" +#include "glacier/status/error.h" + +namespace glcr { + +template +struct HashFunc { + uint64_t operator()(const T&); +}; + +template <> +struct HashFunc { + uint64_t operator()(const uint64_t& value) { + // FIXME: Write a real hash function. + return 0xABBAABBAABBAABBA ^ value; + } +}; + +template > +class HashMap { + public: + HashMap() = default; + HashMap(const HashMap&) = delete; + HashMap& operator=(const HashMap&) = delete; + // TODO: Implement Move. + HashMap(HashMap&&) = delete; + HashMap& operator=(HashMap&&) = delete; + + // Accessors. + uint64_t size() { return size_; } + uint64_t empty() { return size_ == 0; } + + // Returns load as a percentage (i.e. 60 means the load is 0.6). + // + // If data is a zero-size array, return load as 100 so it will be flagged for + // resize. + // TODO: Return a double here once FPE is enabled. + uint64_t load() { + if (data_.size() == 0) { + return 100; + } + return size_ * 100 / data_.size(); + } + + V& at(const K&); + const V& at(const K&) const; + + bool Contains(const K&) const; + + // Setters. + [[nodiscard]] ErrorCode Insert(const K&, const V&); + [[nodiscard]] ErrorCode Insert(K&&, V&&); + + [[nodiscard]] ErrorCode Update(const K&, const V&); + [[nodiscard]] ErrorCode Update(const K&, V&&); + + [[nodiscard]] ErrorCode Delete(const K&); + + void Resize(uint64_t new_size); + + private: + Array>> data_; + uint64_t size_ = 0; + + void ResizeIfNecessary(); +}; + +template +V& HashMap::at(const K& key) { + uint64_t hc = H()(key); + auto& ll = data_[hc % data_.size()]; + + for (auto& pair : ll) { + if (pair.first() == key) { + return pair.second(); + } + } + // TODO: Add a failure mode here instead of constructing an object. + ll.PushFront({key, {}}); + return ll.PeekFront().second(); +} + +template +const V& HashMap::at(const K& key) const { + uint64_t hc = H()(key); + auto& ll = data_[hc % data_.size()]; + + for (auto& pair : ll) { + if (pair.first() == key) { + return pair.second(); + } + } + // TODO: Add a failure mode here instead of constructing an object. + ll.PushFront({key, {}}); + return ll.PeekFront().second(); +} + +template +bool HashMap::Contains(const K& key) const { + uint64_t hc = H()(key); + auto& ll = data_[hc % data_.size()]; + + for (auto& pair : ll) { + if (pair.first() == key) { + return true; + } + } + return false; +} + +template +ErrorCode HashMap::Insert(const K& key, const V& value) { + ResizeIfNecessary(); + + uint64_t hc = H()(key); + auto& ll = data_[hc % data_.size()]; + + for (auto& pair : ll) { + if (pair.first() == key) { + return ALREADY_EXISTS; + } + } + ll.PushFront({Move(key), Move(value)}); + size_++; + return OK; +} + +template +ErrorCode HashMap::Insert(K&& key, V&& value) { + ResizeIfNecessary(); + + uint64_t hc = H()(key); + auto& ll = data_[hc % data_.size()]; + + for (auto& pair : ll) { + if (pair.first() == key) { + return ALREADY_EXISTS; + } + } + ll.PushFront({Move(key), Move(value)}); + size_++; + return OK; +} + +template +ErrorCode HashMap::Update(const K& key, const V& value) { + ResizeIfNecessary(); + + uint64_t hc = H()(key); + auto& ll = data_[hc % data_.size()]; + + for (auto& pair : ll) { + if (pair.first() == key) { + pair.second() = value; + return OK; + } + } + return NOT_FOUND; +} + +template +ErrorCode HashMap::Update(const K& key, V&& value) { + ResizeIfNecessary(); + + uint64_t hc = H()(key); + auto& ll = data_[hc % data_.size()]; + + for (auto& pair : ll) { + if (pair.first() == key) { + pair.second() = Move(value); + return OK; + } + } + return NOT_FOUND; +} + +template +ErrorCode HashMap::Delete(const K& key) { + uint64_t hc = H()(key); + auto& ll = data_[hc % data_.size()]; + + for (auto& pair : ll) { + if (pair.first() == key) { + ll.Remove(pair); + size_--; + return OK; + } + } + return NOT_FOUND; +} + +template +void HashMap::Resize(uint64_t new_size) { + Array>> new_data(new_size); + + for (uint64_t i = 0; i < data_.size(); i++) { + auto& ll = data_[i]; + while (!ll.empty()) { + auto pair = ll.PopFront(); + uint64_t hc = H()(pair.first()); + new_data[hc % new_size].PushFront(Move(pair)); + } + } + data_ = glcr::Move(new_data); +} + +template +void HashMap::ResizeIfNecessary() { + if (data_.size() == 0) { + Resize(8); + } else if (load() > 75) { + Resize(data_.size() * 2); + } +} + +} // namespace glcr diff --git a/lib/glacier/container/linked_list.h b/lib/glacier/container/linked_list.h index e5fd24f..be1d2cc 100644 --- a/lib/glacier/container/linked_list.h +++ b/lib/glacier/container/linked_list.h @@ -23,6 +23,8 @@ class LinkedList { T PopFront(); + void Remove(const T& item); + void PushFront(const T& item); void PushFront(T&& item); @@ -53,7 +55,9 @@ class LinkedList { }; Iterator begin() { return {front_}; } + const Iterator begin() const { return {front_}; } Iterator end() { return {nullptr}; } + const Iterator end() const { return {nullptr}; } private: uint64_t size_ = 0; @@ -123,4 +127,21 @@ T LinkedList::PopFront() { return Move(ret); } +template +void LinkedList::Remove(const T& item) { + if (front_->item == item) { + PopFront(); + return; + } + ListItem* iter = front_; + while (iter != nullptr) { + if (iter->next != nullptr && iter->next->item == item) { + iter->next = iter->next->next; + size_--; + return; + } + iter = iter->next; + } +} + } // namespace glcr diff --git a/lib/glacier/container/pair.h b/lib/glacier/container/pair.h index e633fef..9ad2753 100644 --- a/lib/glacier/container/pair.h +++ b/lib/glacier/container/pair.h @@ -1,14 +1,23 @@ #pragma once +#include + +#include "glacier/memory/move.h" + namespace glcr { template class Pair { public: Pair(const T& first, const U& second) : first_(first), second_(second) {} + Pair(T&& first, U&& second) : first_(Move(first)), second_(Move(second)) {} T& first() { return first_; } U& second() { return second_; } + bool operator==(const Pair& other) { + return other.first_ == first_ && other.second_ == second_; + } + private: T first_; U second_; diff --git a/zion/capability/capability_table.cpp b/zion/capability/capability_table.cpp index babda9a..4725389 100644 --- a/zion/capability/capability_table.cpp +++ b/zion/capability/capability_table.cpp @@ -8,37 +8,26 @@ uint64_t CapabilityTable::AddExistingCapability( const glcr::RefPtr& cap) { MutexHolder h(lock_); uint64_t id = next_cap_id_++; - capabilities_.PushBack({.id = id, .cap = cap}); + if (capabilities_.Insert(id, cap) != glcr::OK) { + panic("Reusing capability id."); + } return id; } glcr::RefPtr CapabilityTable::GetCapability(uint64_t id) { MutexHolder h(lock_); - auto iter = capabilities_.begin(); - while (iter != capabilities_.end()) { - if (iter->cap && iter->id == id) { - return iter->cap; - } - ++iter; + if (!capabilities_.Contains(id)) { + panic("Bad cap access {}", id); } - dbgln("Bad cap access {}", id); - dbgln("Num caps: {}", capabilities_.size()); - return {}; + return capabilities_.at(id); } glcr::RefPtr CapabilityTable::ReleaseCapability(uint64_t id) { MutexHolder h(lock_); - auto iter = capabilities_.begin(); - while (iter != capabilities_.end()) { - if (iter->cap && iter->id == id) { - // FIXME: Do an actual release here. - auto cap = iter->cap; - iter->cap = {nullptr}; - return cap; - } - ++iter; + if (!capabilities_.Contains(id)) { + panic("Bad cap release {}", id); } - dbgln("Bad cap release: {}", id); - dbgln("Num caps: {}", capabilities_.size()); - return {}; + auto cap = capabilities_.at(id); + (void)capabilities_.Delete(id); + return cap; } diff --git a/zion/capability/capability_table.h b/zion/capability/capability_table.h index 78cf85b..a67112c 100644 --- a/zion/capability/capability_table.h +++ b/zion/capability/capability_table.h @@ -1,9 +1,10 @@ #pragma once -#include +#include #include #include "capability/capability.h" +#include "debug/debug.h" #include "object/mutex.h" class CapabilityTable { @@ -28,12 +29,8 @@ class CapabilityTable { glcr::RefPtr lock_ = Mutex::Create(); // TODO: Do some randomization. uint64_t next_cap_id_ = 0x100; - // FIXME: use a map data structure. - struct CapEntry { - uint64_t id; - glcr::RefPtr cap; - }; - glcr::LinkedList capabilities_; + // TODO: Consider not holding a uniqueptr here instead of a refptr? + glcr::HashMap> capabilities_; }; template @@ -41,7 +38,9 @@ uint64_t CapabilityTable::AddNewCapability(const glcr::RefPtr& object, uint64_t permissions) { MutexHolder h(lock_); uint64_t id = next_cap_id_++; - capabilities_.PushBack( - {.id = id, .cap = MakeRefCounted(object, permissions)}); + if (capabilities_.Insert( + id, MakeRefCounted(object, permissions)) != glcr::OK) { + panic("Reusing capability id {}", id); + } return id; }