Skip to content

Commit

Permalink
Avoid copy of key for hash table bucket lookup
Browse files Browse the repository at this point in the history
  • Loading branch information
marcauberer committed Feb 15, 2025
1 parent 0962e4f commit 7cae238
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 33 deletions.
7 changes: 4 additions & 3 deletions src/typechecker/TypeCheckerExpressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -490,9 +490,6 @@ std::any TypeChecker::visitPostfixUnaryExpr(PostfixUnaryExprNode *node) {
AssignExprNode *indexAssignExpr = node->subscriptIndexExpr;
const auto index = std::any_cast<ExprResult>(visit(indexAssignExpr));
HANDLE_UNRESOLVED_TYPE_ER(index.type)
// Check if the index is of the right type
if (!index.type.isOneOf({TY_INT, TY_LONG}))
SOFT_ERROR_ER(node, ARRAY_INDEX_NOT_INT_OR_LONG, "Array index must be of type int or long")

// Check is there is an overloaded operator function available, if yes accept it
const auto [type, _] = opRuleManager.isOperatorOverloadingFctAvailable<2>(node, OP_FCT_SUBSCRIPT, {operand, index}, 0);
Expand All @@ -503,6 +500,10 @@ std::any TypeChecker::visitPostfixUnaryExpr(PostfixUnaryExprNode *node) {

operandType = operandType.removeReferenceWrapper();

// Check if the index is of the right type
if (!index.type.isOneOf({TY_INT, TY_LONG}))
SOFT_ERROR_ER(node, ARRAY_INDEX_NOT_INT_OR_LONG, "Array index must be of type int or long")

// Check if we can apply the subscript operator on the lhs type
if (!operandType.isOneOf({TY_ARRAY, TY_PTR, TY_STRING}))
SOFT_ERROR_ER(node, OPERATOR_WRONG_DATA_TYPE,
Expand Down
3 changes: 1 addition & 2 deletions std/data/hash-table.spice
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,7 @@ inline f<LinkedList<HashEntry<K, V>>&> HashTable.getBucket(const K& key) {
}

inline f<unsigned long> HashTable.hash(const K& key) {
const K keyCopy = key; // ToDo: Avoid copy of key
return hash(keyCopy) % this.buckets.getSize();
return hash(key) % this.buckets.getSize();
}

/**
Expand Down
71 changes: 57 additions & 14 deletions std/math/hash.spice
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,77 @@ type TT int|long|short|bool; // Trivially hashable types
type TC byte|char; // Trivially hashable types with one additional cast
type T dyn; // Any type

// Aliases
public type Hash alias unsigned long;

public type IHashable interface {
public f<unsigned long> hash<T>(const T&);
public f<Hash> hash();
}

public f<unsigned long> hash<TT>(TT input) {
return cast<unsigned long>(input);
/**
* Hash primitive numeric type
*
* @param input Primitieve numeric to hash
* @return Hash of primitive numeric
*/
public f<Hash> hash<TT>(TT input) {
return cast<Hash>(input);
}

public f<unsigned long> hash<TC>(TC input) {
return cast<unsigned long>(input);
/**
* Hash primitive numeric type
*
* @param input Primitieve numeric to hash
* @return Hash of primitive numeric
*/
public f<Hash> hash<TC>(TC input) {
return cast<Hash>(cast<unsigned int>(input));
}

public f<unsigned long> hash(double input) {
/**
* Hash a double value
*
* @param input Double to hash
* @return Hash of double
*/
public f<Hash> hash(double input) {
unsafe {
return *(cast<unsigned long*>(&input));
return *(cast<Hash*>(&input));
}
}

// djb2 hash
public f<unsigned long> hash(string input) {
unsigned long hash = 5381l;
/**
* Hash contents of an immutable string
*
* @param input String to hash
* @return Hash of string
*/
public f<Hash> hash(string input) {
// Hash using djb2 algorithm
Hash hash = 5381l;
for unsigned long i = 0l; i < len(input); i++ {
unsigned long c = cast<unsigned long>(cast<unsigned int>(input[i]));
Hash c = cast<Hash>(cast<unsigned int>(input[i]));
hash = ((hash << 5l) + hash) + c; // hash * 33 + c
}
return hash;
}

/*public f<unsigned long> hash<T>(const T& input) {
return input.hash();
}*/
/**
* Hash contents of a mutable String object
*
* @param input String to hash
* @return Hash of string
*/
public f<Hash> hash(const String& input) {
return hash(input.getRaw());
}

/**
* Dispatch function for structs, that implement the IHashable interface
*
* @param hashable Hashable object
* @return Hash of the struct
*/
public f<Hash> hash(const IHashable& hashable) {
return hashable.hash();
}
38 changes: 38 additions & 0 deletions test/test-files/std/data/unordered-map-complex-key/source.spice
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import "std/data/unordered-map";

f<int> main() {
UnorderedMap<String, int> map = UnorderedMap<String, int>();
assert map.getSize() == 0;
assert map.isEmpty();
map.upsert(String("one"), 1);
map.upsert(String("two"), 2);
map.upsert(String("three"), 3);
map.upsert(String("four"), 4);
assert map.getSize() == 4;
assert !map.isEmpty();
assert map.contains(String("one"));
assert map.contains(String("two"));
assert map.contains(String("three"));
assert map.contains(String("four"));
assert !map.contains(String("five"));
assert map.get(String("one")) == 1;
assert map[String("two")] == 2;
assert map.get(String("three")) == 3;
assert map.get(String("four")) == 4;
const Result<int> item5 = map.getSafe(String("five"));
assert item5.isErr();
map.remove(String("two"));
assert !map.contains(String("two"));
assert map.getSize() == 3;
map.clear();
assert map.getSize() == 0;
assert map.isEmpty();
map.upsert(String("one"), 1);
map.upsert(String("one"), 11);
assert map.getSize() == 1;
assert map[String("one")] == 11;
map.remove(String("one"));
assert map.isEmpty();

printf("All assertions passed!\n");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
All assertions passed!
12 changes: 7 additions & 5 deletions test/test-files/std/math/hash-basic/cout.out
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
Hash (double): 4638355772470722560
Hash (int): 123
Hash (long): 123
Hash (short): 123
Hash (char): 0
Hash (byte): 0
Hash (string): -1763540338
Hash (double): 0
Hash (long): 123
Hash (char): 123
Hash (byte): 123
Hash (string): 5904905660241445518
Hash (String): 5904905660241445518
Hash (HashableType): 124
29 changes: 20 additions & 9 deletions test/test-files/std/math/hash-basic/source.spice
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
import "std/math/hash";

type HashableType struct : IHashable {
unsigned long content = 124l
}

f<Hash> HashableType.hash() {
return this.content;
}

f<int> main() {
// Trivial hashes
printf("Hash (int): %d\n", hash(123));
printf("Hash (long): %d\n", hash(123l));
printf("Hash (short): %d\n", hash(123s));
printf("Hash (char): %d\n", hash(123.0));
printf("Hash (byte): %d\n", hash(123.0));
printf("Hash (string): %d\n", hash("Hello, World!"));
// Complex hashes
printf("Hash (double): %d\n", hash(123.0));
// Commonly used hash functions
printf("Hash (double): %lu\n", hash(123.0));
printf("Hash (int): %lu\n", hash(123));
printf("Hash (short): %lu\n", hash(123s));
printf("Hash (long): %lu\n", hash(123l));
printf("Hash (char): %lu\n", hash(cast<char>(123)));
printf("Hash (byte): %lu\n", hash(cast<byte>(123)));
printf("Hash (string): %lu\n", hash("Hello, World!"));
printf("Hash (String): %lu\n", hash(String("Hello, World!")));
// Custom hash implementation
const HashableType ht;
printf("Hash (HashableType): %lu\n", hash(ht));
}

0 comments on commit 7cae238

Please sign in to comment.