From 787d34a8b5891b0b191575fd27e2da25f2eb9630 Mon Sep 17 00:00:00 2001 From: nocturn9x Date: Wed, 27 Jan 2021 15:51:15 +0100 Subject: [PATCH] Added WIP implementation of a generic hashmap that should behave better with JAPL's own memory manager --- build.py | 4 +- src/japl.nim | 10 +- src/lexer.nim | 2 +- src/types/_hashmap.nim | 88 ---------------- src/types/hashmap | Bin 0 -> 132832 bytes src/types/hashmap.nim | 226 +++++++++++++++++++++++++++++++++++++++++ src/types/methods.nim | 4 + 7 files changed, 239 insertions(+), 95 deletions(-) delete mode 100644 src/types/_hashmap.nim create mode 100755 src/types/hashmap create mode 100644 src/types/hashmap.nim diff --git a/build.py b/build.py index 191d167..121aaa1 100755 --- a/build.py +++ b/build.py @@ -45,8 +45,8 @@ CONFIG_TEMPLATE = '''# Copyright 2020 Mattia Giambirtone import strformat -const MAP_LOAD_FACTOR* = {map_load_factor} # Load factor for builtin hashmaps (TODO) -const ARRAY_GROW_FACTOR* = {array_grow_factor} # How much extra memory to allocate for dynamic arrays (TODO) +const MAP_LOAD_FACTOR* = {map_load_factor} # Load factor for builtin hashmaps +const ARRAY_GROW_FACTOR* = {array_grow_factor} # How much extra memory to allocate for dynamic arrays const FRAMES_MAX* = {frames_max} # The maximum recursion limit const JAPL_VERSION* = "0.3.0" const JAPL_RELEASE* = "alpha" diff --git a/src/japl.nim b/src/japl.nim index 1b772d4..d8c0efa 100644 --- a/src/japl.nim +++ b/src/japl.nim @@ -26,8 +26,10 @@ import types/typeutils import types/methods -proc repl() = - var bytecodeVM = initVM() +proc repl(bytecodeVM: VM) = + var bytecodeVM = bytecodeVM + if bytecodeVM == nil: + bytecodeVM = initVM() echo JAPL_VERSION_STRING echo &"[Nim {NimVersion} on {hostOs} ({hostCPU})]" var source = "" @@ -58,7 +60,7 @@ proc repl() = proc main(file: var string = "", fromString: bool = false, interactive: bool = false) = var source: string if file == "" and not fromString: - repl() + repl(nil) return # We exit after the REPL has ran if not fromString: var sourceFile: File @@ -77,7 +79,7 @@ proc main(file: var string = "", fromString: bool = false, interactive: bool = f var bytecodeVM = initVM() discard bytecodeVM.interpret(source, file) if interactive: - repl() + repl(bytecodeVM) bytecodeVM.freeVM() diff --git a/src/lexer.nim b/src/lexer.nim index 8110e13..163d33b 100644 --- a/src/lexer.nim +++ b/src/lexer.nim @@ -257,7 +257,7 @@ when isMainModule: stdout.write("Lexer output: [") var lexed = lexer.lex() for i, el in lexed: - stdout.write($el[]) + stdout.write($el) if i < lexed.high(): stdout.write(", ") stdout.write("]\n") diff --git a/src/types/_hashmap.nim b/src/types/_hashmap.nim deleted file mode 100644 index 972de2d..0000000 --- a/src/types/_hashmap.nim +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright 2020 Mattia Giambirtone -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -# WIP - Not working - -import ../memory -import lenientops -import baseObject -import methods -import japlNil -import japlString - - -const HASH_MAP_LOAD_MAX = 0.75 # Hash map max load factor - - -type - Entry = ref object - key*: ptr Obj - value*: ptr Obj - HashMap = object of Obj - size*: int - capacity*: int - entries*: ptr UncheckedArray[Entry] - - -proc newHashMap(): HashMap = - result = HashMap(size: 0, capacity: 0, entries: nil) - - -proc freeHashMap(self: var HashMap) = - discard freeArray(self, self.entries, self.capacity) - self.size = 0 - self.capacity = 0 - self.entries = nil - - -proc findEntry(self: HashMap, key: ptr Obj): Entry = - var idx = (int key.hashValue) mod self.capacity - var entry: Entry - while true: - entry = self.entries[idx] - if entry.key.eq(key) or entry.key == nil: - result = entry - break - idx = idx + 1 mod self.capacity - - -proc adjustCapacity(self: var HashMap, capacity: int) = - var entries = allocate(UncheckedArray[Entry], Entry, capacity) - var i = 0 - while i < capacity: - entries[i].key = nil - entries[i].value = cast[ptr Obj](asNil()) - i += 1 - self.entries = entries - self.capacity = capacity - - -proc setEntry(self: var HashMap, key: ptr Obj, value: ptr Obj): bool = - if self.size + 1 > self.capacity * HASH_MAP_LOAD_MAX: - var capacity = growCapacity(self.capacity) - self.adjustCapacity(capacity) - var entry: Entry = self.findEntry(key) - var isNewKey: bool = entry.key == nil - if isNewKey: - self.size += 1 - entry.key = key - entry.value = value - result = isNewKey - - -when isMainModule: - var dictionary = newHashMap() - discard dictionary.setEntry(asObj[String]("helo".asStr()), asObj[String]("world".asStr())) - echo dictionary.findEntry(asObj[String]("helo".asStr())).value.toStr() diff --git a/src/types/hashmap b/src/types/hashmap new file mode 100755 index 0000000000000000000000000000000000000000..a74448a94be2d05711ac55435cb1c54478bba2e6 GIT binary patch literal 132832 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 b`?%5j(sM*zKcoNi|H`lY(G$VbVd=jCy>7t8 literal 0 HcmV?d00001 diff --git a/src/types/hashmap.nim b/src/types/hashmap.nim new file mode 100644 index 0000000..1b34d4c --- /dev/null +++ b/src/types/hashmap.nim @@ -0,0 +1,226 @@ +# Copyright 2020 Mattia Giambirtone +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + + +import ../memory +import ../config +import baseObject +import methods +import iterable + + +import lenientops +import options +import hashes +import strformat + + + +type + Entry*[K, V] = object + ## Low-level object to store key/value pairs + key*: Option[K] + value*: Option[V] + tombstone*: bool + HashMap*[K, V] = object of Iterable + ## An associative array with O(1) lookup time, + ## similar to nim's Table type, but using raw + ## memory to be more compatible with JAPL's runtime + ## memory management + entries*: ptr UncheckedArray[ptr Entry[K, V]] + # This attribute counts *only* non-deleted entries + actual_length*: int + + +proc newHashMap*[K, V](): ptr HashMap[K, V] = + ## Initializes a new, empty hashmap + result = allocateObj(HashMap[K, V], ObjectType.Dict) + result.actual_length = 0 + result.entries = nil + result.capacity = 0 + result.length = 0 + + +proc freeHashMap*[K, V](self: ptr HashMap[K, V]) = + ## Frees the memory associated with the hashmap + discard freeArray(UncheckedArray[ptr Entry[K, V]], self.entries, self.capacity) + self.length = 0 + self.actual_length = 0 + self.capacity = 0 + self.entries = nil + + +proc findEntry[K, V](self: ptr UncheckedArray[ptr Entry[K, V]], key: K, capacity: int): ptr Entry[K, V] = + ## Low-level method used to find entries in the underlying + ## array, returns a pointer to an entry + var capacity = uint64(capacity) + var idx = uint64(key.hash()) mod capacity + var tombstone: ptr Entry[K, V] = nil + while true: + result = self[idx] + if result.key.isNone() and result.key.isSome(): + # We found a tombstone + tombstone = result + elif result.key.isNone() or result.key.get() == key: + if tombstone != nil: + result = tombstone + result.tombstone = true + break + # If none of these conditions match, we have a collision! + # This means we can just move on to the next slot in our probe + # sequence until we find an empty slot. The way our resizing + # mechanism works makes the empty slot invariant easy to + # maintain since we increase the underlying array's size + # before we are actually full + idx += 1 mod capacity + + +proc adjustCapacity[K, V](self: ptr HashMap[K, V]) = + ## Adjusts the capacity of the underlying array to make room + ## for more entries. Low-level method, not recommended + var newCapacity = growCapacity(self.capacity) + var entries = allocate(UncheckedArray[ptr Entry[K, V]], Entry[K, V], newCapacity) + self.length = 0 + var temp: ptr Entry[K, V] + for x in countup(0, newCapacity - 1): + entries[x] = allocate(Entry[K, V], Entry[K, V], 1) + temp = entries[x] + temp.key = none(K) + temp.value = none(V) + temp.tombstone = false + for x in countdown(self.capacity - 1, 0): + temp = self.entries[x] + if temp.key.isSome(): + entries[x] = temp + self.length += 1 + discard freeArray(UncheckedArray[ptr Entry[K, V]], self.entries, self.capacity) + self.entries = entries + self.capacity = newCapacity + + +proc setEntry[K, V](self: ptr HashMap[K, V], key: K, value: V): bool = + ## Low-level method to set/replace an entry with a value + if self.length + 1 > self.capacity * MAP_LOAD_FACTOR: + # Since we always need at least some empty slots + # for our probe sequences to work properly, we + # always resize our underlying array before we're full. + # MAP_LOAD_FACTOR is a constant float between 0.0 and 1.0 + # which determines the percentage of full buckets that's + # needed to start a resize operation + self.adjustCapacity() + var entry = findEntry(self.entries, key, self.capacity) + result = entry.key.isNone() + if result: + self.actual_length += 1 + self.length += 1 + entry.key = some(key) + entry.value = some(value) + + +proc `[]`*[K, V](self: ptr HashMap[K, V], key: K): V = + ## Retrieves a value by key + var entry = findEntry(self.entries, key, self.capacity) + if entry.key.isNone(): + raise newException(KeyError, &"Key not found: {key}") + result = entry.value.get() + + +proc `[]=`*[K, V](self: ptr HashMap[K, V], key: K, value: V) = + ## Sets a value with the given key. If the key already + ## exists it will be overwritten + discard self.setEntry(key, value) + + +proc del*[K, V](self: ptr HashMap[K, V], key: K) = + ## Deletes an entry in the hashmap + if self.len() == 0: + raise newException(KeyError, &"delete from empty hashmap") + var entry = findEntry(self.entries, key, self.capacity) + if entry.key.isSome(): + ## We don't reset the value of the + ## 'value' attribute because that + ## makes us understand that this + ## entry is a tombstone and not + ## a truly full bucket + self.actual_length -= 1 + entry.key = none(K) + else: + raise newException(KeyError, &"Key not found: {key}") + + +proc contains*[K, V](self: ptr HashMap[K, V], key: K): bool = + ## Checks if key is in the hashmap + var entry = findEntry(self.entries, key, self.capacity) + if entry.key.isSome(): + result = true + else: + result = false + + +iterator keys*[K, V](self: ptr HashMap[K, V]): K = + ## Yields all the keys in the hashmap + var entry: ptr Entry[K, V] + for i in countup(0, self.capacity - 1): + entry = self.entries[i] + if entry.key.isSome(): + yield entry.key.get() + + +iterator values*[K, V](self: ptr HashMap[K, V]): V = + ## Yields all the values in the hashmap + for key in self.keys(): + yield self[key] + + +iterator pairs*[K, V](self: ptr HashMap[K, V]): tuple[key: K, val: V] = + ## Yields all the key/value pairs in the hashmap + for key in self.keys(): + yield (key: key, val: self[key]) + + +iterator items*[K, V](self: ptr HashMap[K, V]): K = + ## Yields all the keys in the hashmap (for iteration) + for k in self.keys(): + yield k + + +proc len*[K, V](self: ptr HashMap[K, V]): int = + ## Returns the length of the hashmap + result = self.actual_length + + +proc `$`*[K, V](self: ptr HashMap[K, V]): string = + ## Returns a string representation of the hashmap + var i = 0 + result &= "{" + for key, value in self.pairs(): + result &= &"{key}: {value}" + if i < self.len() - 1: + result &= ", " + i += 1 + result &= "}" + + +var d = newHashMap[int, int]() +d[1] = 55 +d[2] = 876 +d[3] = 7890 +d[4] = 55 +d[5] = 435 +d[6] = 567 +d[7] = 21334 ## Adjust capacity (75% full) +d[8] = 9768 +d[9] = 235 +echo d diff --git a/src/types/methods.nim b/src/types/methods.nim index de31178..a7e96bd 100644 --- a/src/types/methods.nim +++ b/src/types/methods.nim @@ -205,6 +205,10 @@ proc eq*(self, other: ptr Obj): bool = discard +proc `==`*(self, other: ptr Obj): bool = + result = self.eq(other) + + proc negate*(self: ptr Obj): returnType = ## Returns the result of -self or ## raises an error if the operation