Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finish unordered map std #495

Merged
merged 2 commits into from Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .run/spice.run.xml
@@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="spice" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="run -O0 -d ../../media/test-project/test.spice" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="Spice" TARGET_NAME="spice" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="Spice" RUN_TARGET_NAME="spice">
<configuration default="false" name="spice" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="run -O3 -d ../../media/test-project/test.spice" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="Spice" TARGET_NAME="spice" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="Spice" RUN_TARGET_NAME="spice">
<envs>
<env name="LLVM_BUILD_INCLUDE_DIR" value="D:/LLVM/build-release/include" />
<env name="LLVM_INCLUDE_DIR" value="D:/LLVM/llvm/include" />
Expand Down
112 changes: 40 additions & 72 deletions media/test-project/test.spice
@@ -1,77 +1,45 @@
type Speak interface {
p sayHello(string);
}

type MakeSound interface {
p makeSound();
}

type Human struct : MakeSound, Speak {
string firstName
string lastName
unsigned int age
}

p Human.ctor(string firstName, string lastName, unsigned int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}

p Human.makeSound() {
printf("Sigh...\n");
}

p Human.sayHello(string name) {
printf("Hi, %s!\n", name);
}

type Car struct : MakeSound {
string brand
string model
unsigned int seats
}

p Car.ctor(string brand, string model, unsigned int seats) {
this.brand = brand;
this.model = model;
this.seats = seats;
}

p Car.makeSound() {
printf("Wroom, wroom!\n");
}

type Parrot struct : MakeSound, Speak {
string name
unsigned int age
}

p Parrot.ctor(string name, unsigned int age) {
this.name = name;
this.age = age;
}

p Parrot.makeSound() {
printf("Sqawk!\n");
}

p Parrot.sayHello(string name) {
printf("Hello %s, squawk!\n", name);
}
import "std/data/unordered-map";
import "std/time/timer";

f<int> main() {
Human human = Human("John", "Doe", 25);
Car car = Car("Toyota", "Corolla", 5);
Parrot parrot = Parrot("Polly", 3);

human.makeSound();
car.makeSound();
parrot.makeSound();

human.sayHello("Jane");
parrot.sayHello("Jane");
return 0;
Timer timer = Timer();
timer.start();

UnorderedMap<int, string> map = UnorderedMap<int, string>(3l);
assert map.getSize() == 0;
assert map.isEmpty();
map.upsert(1, "one");
map.upsert(2, "two");
map.upsert(3, "three");
map.upsert(4, "four");
assert map.getSize() == 4;
assert !map.isEmpty();
assert map.contains(1);
assert map.contains(2);
assert map.contains(3);
assert map.contains(4);
assert !map.contains(5);
assert map.get(1) == "one";
assert map.get(2) == "two";
assert map.get(3) == "three";
assert map.get(4) == "four";
const Result<string> item5 = map.getSafe(5);
assert item5.isErr();
map.remove(2);
assert !map.contains(2);
assert map.getSize() == 3;
map.clear();
assert map.getSize() == 0;
assert map.isEmpty();
map.upsert(1, "one");
map.upsert(1, "one new");
assert map.getSize() == 1;
assert map.get(1) == "one new";
map.remove(1);
assert map.isEmpty();

timer.stop();
printf("All assertions passed in %d microseconds!\n", timer.getDurationInMicros());
}

/*import "bootstrap/util/block-allocator";
Expand Down
121 changes: 104 additions & 17 deletions std/data/hash-table.spice
@@ -1,6 +1,7 @@
import "std/data/vector";
import "std/data/linked-list";
import "std/data/optional";
import "std/type/result";
import "std/type/error";
import "std/math/hash";

// Generic types for key and value
Expand All @@ -13,50 +14,136 @@ type HashEntry<K, V> struct {
}

public type HashTable<K, V> struct {
Vector<LinkedList<HashEntry<K, V>>> table
Vector<LinkedList<HashEntry<K, V>>> buckets
}

public p HashTable.ctor(unsigned long bucketCount = 100l) {
this.table = Vector<LinkedList<HashEntry<K, V>>>(bucketCount);
this.buckets = Vector<LinkedList<HashEntry<K, V>>>(bucketCount);
for unsigned long i = 0l; i < bucketCount; i++ {
this.table.pushBack(LinkedList<HashEntry<K, V>>());
this.buckets.pushBack(LinkedList<HashEntry<K, V>>());
}
}

/**
* Insert a key-value pair into the hash table.
* If the key already exists, the value is updated.
*
* @param key The key to insert
* @param value The value to insert
*/
public p HashTable.upsert(const K& key, const V& value) {
const unsigned long index = this.hash(key);
const LinkedList<HashEntry<K, V>>& list = this.table.get(index);
foreach const HashEntry<K, V>& entry : list {
const LinkedList<HashEntry<K, V>>& bucket = this.buckets.get(index);
foreach const HashEntry<K, V>& entry : bucket {
if (entry.key == key) {
entry.value = value;
return;
}
}
bucket.pushBack(HashEntry<K, V>{key, value});
}

public f<Optional<V>> HashTable.get(const K& key) {
/**
* Retrieve the value associated with the given key.
* If the key is not found, panic.
*
* @param key The key to look up
* @return The value associated with the key
*/
public f<V&> HashTable.get(const K& key) {
const unsigned long index = this.hash(key);
const LinkedList<HashEntry<K, V>>& list = this.table.get(index);
foreach const HashEntry<K, V>& entry : list {
const LinkedList<HashEntry<K, V>>& bucket = this.buckets.get(index);
foreach const HashEntry<K, V>& entry : bucket {
if (entry.key == key) {
return Optional<V>(entry.value);
return entry.value;
}
}
return Optional<V>();
panic(Error("The provided key was not found"));
}

/**
* Retrieve the value associated with the given key as Optional<T>.
* If the key is not found, return an empty optional.
*
* @param key The key to look up
* @return Optional<T>, containing the value associated with the key or empty if the key is not found
*/
public f<Result<V>> HashTable.getSafe(const K& key) {
const unsigned long index = this.hash(key);
const LinkedList<HashEntry<K, V>>& bucket = this.buckets.get(index);
foreach const HashEntry<K, V>& entry : bucket {
if (entry.key == key) {
return ok(entry.value);
}
}
return err<V>(Error("The provided key was not found"));
}

/**
* Remove the key-value pair associated with the given key.
* If the key is not found, do nothing.
*
* @param key The key to remove
*/
public p HashTable.remove(const K& key) {
const unsigned long index = this.hash(key);
LinkedList<HashEntry<K, V>>& list = this.table.get(index);
for (unsigned long i = 0l; i < list.getSize(); i++) {
if (list.get(i).key == key) {
list.remove(i);
LinkedList<HashEntry<K, V>>& bucket = this.buckets.get(index);
for unsigned long i = 0l; i < bucket.getSize(); i++ {
if (bucket.get(i).key == key) {
bucket.removeAt(i);
return;
}
}
}

/**
* Check if the hash table contains the given key.
*
* @param key The key to check for
* @return True if the key is found, false otherwise
*/
public f<bool> HashTable.contains(const K& key) {
const unsigned long index = this.hash(key);
const LinkedList<HashEntry<K, V>>& bucket = this.buckets.get(index);
foreach const HashEntry<K, V>& entry : bucket {
if (entry.key == key) {
return true;
}
}
return false;
}

/**
* Get the size of the hash table.
*
* @return The number of key-value pairs in the hash table
*/
public inline f<unsigned long> HashTable.getSize() {
result = 0l;
foreach LinkedList<HashEntry<K, V>>& bucket : this.buckets {
result += bucket.getSize();
}
}

/**
* Checks if the hash table is empty.
*
* @return True if empty, false otherwise.
*/
public inline f<bool> HashTable.isEmpty() {
return this.getSize() == 0l;
}

/**
* Clear the hash table, removing all key-value pairs.
*/
public inline p HashTable.clear() {
foreach LinkedList<HashEntry<K, V>>& bucket : this.buckets {
bucket.clear();
}
}

inline f<unsigned long> HashTable.hash(const K& key) {
K keyCopy = key;
return hash(keyCopy) % this.table.getSize();
const K keyCopy = key;
return hash(keyCopy) % this.buckets.getSize();
}