Skip to content

Commit

Permalink
Merge c9616d1 into b5c9ae1
Browse files Browse the repository at this point in the history
  • Loading branch information
yj-qin authored May 29, 2024
2 parents b5c9ae1 + c9616d1 commit cca0ef1
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 44 deletions.
57 changes: 31 additions & 26 deletions hashmap/hashmap.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub fn HashMap::new[K, V]() -> HashMap[K, V] {
size: 0,
capacity: default_init_capacity,
growAt: calc_grow_threshold(default_init_capacity),
entries: Array::make(default_init_capacity, Empty),
entries: FixedArray::make(default_init_capacity, None),
}
}

Expand All @@ -41,30 +41,35 @@ pub fn set[K : Hash + Eq, V](self : HashMap[K, V], key : K, value : V) -> Unit {
self.grow()
}
let hash = make_hash(key)
loop 0, self.index(hash), 0, hash, key, value {
i, idx, psl, hash, key, value => {
let insert_entry = { psl: 0, hash, key, value }
loop 0, self.index(hash), Option::Some(insert_entry) {
i, idx, Some(entry) => {
if i == self.capacity {
abort("HashMap is full")
panic()
}
match self.entries[idx] {
Empty => {
self.entries[idx] = Entry::Valid(~psl, ~hash, ~key, ~value)
None => {
self.entries[idx] = Some(entry)
self.size += 1
break
}
Valid(psl=d, hash=h, key=k, value=v) => {
if h == hash && k == key {
self.entries[idx] = Valid(psl=d, hash=h, key=k, ~value)
Some(curr_entry) => {
if curr_entry.hash == entry.hash && curr_entry.key == entry.key {
curr_entry.value = entry.value
break
}
if psl > d {
self.entries[idx] = Entry::Valid(~psl, ~hash, ~key, ~value)
continue i + 1, self.next_index(idx), d + 1, h, k, v
if entry.psl > curr_entry.psl {
self.entries[idx] = Some(entry)
curr_entry.psl += 1
continue i + 1, self.next_index(idx), Some(curr_entry)
} else {
entry.psl += 1
continue i + 1, self.next_index(idx), Some(entry)
}
continue i + 1, self.next_index(idx), psl + 1, hash, key, value
}
}
}
_, _, None => break
}
}

Expand All @@ -83,15 +88,15 @@ pub fn get[K : Hash + Eq, V](self : HashMap[K, V], key : K) -> Option[V] {
i < self.capacity
i = i + 1, idx = self.next_index(idx) {
match self.entries[idx] {
Valid(_) as entry => {
Some(entry) => {
if entry.hash == hash && entry.key == key {
break Some(entry.value)
}
if i > entry.psl {
break None
}
}
Empty => break None
None => break None
}
} else {
None
Expand Down Expand Up @@ -130,9 +135,9 @@ pub fn remove[K : Hash + Eq, V](self : HashMap[K, V], key : K) -> Unit {
i < self.capacity
i = i + 1, idx = self.next_index(idx) {
match self.entries[idx] {
Valid(_) as entry => {
Some(entry) => {
if entry.hash == hash && entry.key == key {
self.entries[idx] = Empty
self.entries[idx] = None
self.shift_back(idx)
self.size -= 1
break
Expand All @@ -141,7 +146,7 @@ pub fn remove[K : Hash + Eq, V](self : HashMap[K, V], key : K) -> Unit {
return
}
}
Empty => ()
None => ()
}
}
}
Expand All @@ -151,14 +156,14 @@ fn shift_back[K : Hash, V](self : HashMap[K, V], start_index : Int) -> Unit {
i < self.entries.length()
i = i + 1, prev = curr, curr = self.next_index(curr) {
match self.entries[curr] {
Valid(~psl, ~hash, ~key, ~value) => {
Some({ psl, hash, key, value, .. }) => {
if psl == 0 {
break
}
self.entries[prev] = Valid(psl=psl - 1, ~hash, ~key, ~value)
self.entries[curr] = Empty
self.entries[prev] = Some({ psl: psl - 1, hash, key, value })
self.entries[curr] = None
}
Empty => break
None => break
}
}
}
Expand All @@ -169,18 +174,18 @@ fn grow[K : Hash + Eq, V](self : HashMap[K, V]) -> Unit {
self.capacity = default_init_capacity
self.growAt = calc_grow_threshold(self.capacity)
self.size = 0
self.entries = Array::make(self.capacity, Empty)
self.entries = FixedArray::make(self.capacity, None)
return
}
let old_entries = self.entries
self.entries = Array::make(self.capacity * 2, Empty)
self.entries = FixedArray::make(self.capacity * 2, None)
self.capacity = self.capacity * 2
self.growAt = calc_grow_threshold(self.capacity)
self.size = 0
for i = 0; i < old_entries.length(); i = i + 1 {
match old_entries[i] {
Valid(~key, ~value, ..) => self.set(key, value)
_ => ()
Some({ key, value, .. }) => self.set(key, value)
None => ()
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion hashmap/hashmap_test.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ test "clear" {
@assertion.assert_eq(m.size, 0)?
@assertion.assert_eq(m.capacity, 8)?
for i = 0; i < m.capacity; i = i + 1 {
@assertion.assert_is(m.entries[i], Empty)?
@assertion.assert_is(m.entries[i], None)?
}
}

Expand Down
11 changes: 6 additions & 5 deletions hashmap/types.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.

priv enum Entry[K, V] {
Empty
// (Probe Sequence Length, Hash, Key, Value)
Valid(~psl : Int, ~hash : Int, ~key : K, ~value : V)
priv struct Entry[K, V] {
mut psl : Int
hash : Int
key : K
mut value : V
} derive(Debug)

/// A mutable hash map implements with Robin Hood hashing.
Expand All @@ -36,7 +37,7 @@ priv enum Entry[K, V] {
/// println(map.get(3)) // output: Some("updated")
/// ```
struct HashMap[K, V] {
mut entries : Array[Entry[K, V]]
mut entries : FixedArray[Option[Entry[K, V]]]
mut size : Int // active key-value pairs count
mut capacity : Int // current capacity
mut growAt : Int // threshold that triggers grow
Expand Down
24 changes: 12 additions & 12 deletions hashmap/utils.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ fn debug_entries[K : Show, V : Show](self : HashMap[K, V]) -> String {
for s = "", i = 0; i < self.entries.length(); {
let s = if i > 0 { s + "," } else { s }
match self.entries[i] {
Empty => continue s + "_", i + 1
Valid(~psl, ~key, ~value, ..) =>
None => continue s + "_", i + 1
Some({ psl, key, value, .. }) =>
continue s + "(\(psl),\(key),\(value))", i + 1
}
} else {
Expand All @@ -27,7 +27,7 @@ fn debug_entries[K : Show, V : Show](self : HashMap[K, V]) -> String {

/// Clears the map, removing all key-value pairs. Keeps the allocated space.
pub fn clear[K, V](self : HashMap[K, V]) -> Unit {
self.entries.fill(Empty)
self.entries.fill(None)
self.size = 0
}

Expand All @@ -37,11 +37,11 @@ pub fn as_iter[K, V](self : HashMap[K, V]) -> @iter.Iter[(K, V)] {
fn(yield) {
for i = 0, len = self.entries.length(); i < len; i = i + 1 {
match self.entries[i] {
Valid(~key, ~value, ..) =>
Some({ key, value, .. }) =>
if yield((key, value)).not() {
break false
}
_ => continue
None => continue
}
} else {
true
Expand All @@ -55,8 +55,8 @@ pub fn to_array[K, V](self : HashMap[K, V]) -> Array[(K, V)] {
let arr = []
for i = 0, len = self.entries.length(); i < len; i = i + 1 {
match self.entries[i] {
Valid(~key, ~value, ..) => arr.push((key, value))
_ => continue
Some({ key, value, .. }) => arr.push((key, value))
None => continue
}
}
arr
Expand All @@ -81,8 +81,8 @@ pub fn is_empty[K, V](self : HashMap[K, V]) -> Bool {
pub fn iter[K, V](self : HashMap[K, V], f : (K, V) -> Unit) -> Unit {
for i = 0; i < self.capacity; i = i + 1 {
match self.entries[i] {
Valid(_) as entry => f(entry.key, entry.value)
Empty => ()
Some({ key, value, .. }) => f(key, value)
None => ()
}
}
}
Expand All @@ -91,11 +91,11 @@ pub fn iter[K, V](self : HashMap[K, V], f : (K, V) -> Unit) -> Unit {
pub fn iteri[K, V](self : HashMap[K, V], f : (Int, K, V) -> Unit) -> Unit {
for i = 0, idx = 0; i < self.capacity; {
match self.entries[i] {
Valid(_) as entry => {
f(idx, entry.key, entry.value)
Some({ key, value, .. }) => {
f(idx, key, value)
continue i + 1, idx + 1
}
Empty => continue i + 1, idx
None => continue i + 1, idx
}
}
}

0 comments on commit cca0ef1

Please sign in to comment.