-
Notifications
You must be signed in to change notification settings - Fork 56
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
Refactor: optimize Hashmap lib with BALLs #139
base: main
Are you sure you want to change the base?
Changes from 2 commits
9e9ac85
3ccca49
af93705
5c7b296
8f6cd1a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
/// @title HashMap | ||
/// @notice SPDX-License-Identifier: MIT | ||
/// @author asnared <https://github.com/abigger87> | ||
|
||
/// @notice A Module Encapsulating HashMap Methods | ||
/// @notice Adapted from <https://github.com/huff-language/huff-examples/blob/main/erc20/contracts/utils/HashMap.huff> | ||
|
||
/// @notice Given a piece of data (ie an address), hash it, generating the storage slot for a hashmap. | ||
/// @notice Port to BALLs by eugenioclrc taken from https://github.com/huff-language/huffmate/blob/4e2c9bd3412ab8cc65f6ceadafc01a1ff1815796/src/data-structures/Hashmap.huff | ||
|
||
/// @notice Given a piece of data (ie an address), hash it, generating the storage slot for a hashmap. | ||
fn GET_SLOT_FROM_KEY<mem_ptr>(key) -> (hashed) { | ||
// Load the data into memory and hash it, while preserving the memory location. | ||
mstore(mem_ptr, key) | ||
// Hash the data, generating a key. | ||
hashed = sha3(mem_ptr, 0x20) | ||
} | ||
|
||
/// @notice Given two keys (ie a slot and a key), hash them together, generating a slot for a secondary hashmap. | ||
fn GET_SLOT_FROM_KEYS<mem_ptr>(slot, key) -> (hashed) { | ||
// Load the data into memory. | ||
mstore(add(mem_ptr, 0x20), slot) | ||
mstore(mem_ptr, key) | ||
|
||
|
||
// Hash the data, generating a slot. | ||
hashed = sha3(mem_ptr, 0x40) | ||
} | ||
|
||
|
||
/// @notice Calculate the slot from two keys | ||
fn GET_SLOT_FROM_KEYS_2D<mem_ptr>(slot, key1, key2) -> (hashed) { | ||
// Load the data into memory. | ||
mstore(add(mem_ptr, 0x20), slot) | ||
mstore(mem_ptr, key1) | ||
sha3(mem_ptr, 0x40) | ||
|
||
mstore(add(mem_ptr, 0x20), key2) | ||
|
||
// Hash the data, generating a slot. | ||
hashed = sha3(mem_ptr, 0x40) | ||
} | ||
|
||
/// @notice Calculate the slot from three keys | ||
fn GET_SLOT_FROM_KEYS_3D<mem_ptr>(slot, key1, key2, key3) -> (hashed) { | ||
// Load the data into memory | ||
mstore(add(mem_ptr, 0x20), slot) | ||
// concat the first two keys | ||
mstore(mem_ptr, key1) | ||
sha3(mem_ptr, 0x40) | ||
|
||
// put key2 in memory, before slot1 | ||
mstore(add(mem_ptr, 0x20), key2) | ||
sha3(mem_ptr, 0x40) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similarly here you forget to save and write the result of the hash to memory There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems that i mess up, gonna change current test to also predict the expected slot :D |
||
|
||
// put key3 in memory, before slot2 | ||
mstore(add(mem_ptr, 0x20), key3) | ||
// Hash the data, generating the final slot3 | ||
hashed = sha3(mem_ptr, 0x40) | ||
} | ||
|
||
/// @notice Load an element onto the stack from a key | ||
fn LOAD_ELEMENT<mem_ptr>(key) -> (value) { | ||
value = sload(GET_SLOT_FROM_KEY<mem_ptr>(key)) | ||
} | ||
|
||
/// @notice Load an element onto the stack from two keys | ||
fn LOAD_ELEMENT_FROM_KEYS<mem_ptr>(key1, key2) -> (value) { | ||
value = sload(GET_SLOT_FROM_KEYS<mem_ptr>(key1, key2)) | ||
} | ||
|
||
/// @notice Load an element onto the stack from a slot and two keys | ||
fn LOAD_ELEMENT_FROM_KEYS_2D<mem_ptr>(slot, key1, key2) -> (value) { | ||
value = sload(GET_SLOT_FROM_KEYS_2D<mem_ptr>(slot, key1, key2)) | ||
} | ||
|
||
/// @notice Load an element onto the stack from a slot and three keys | ||
fn LOAD_ELEMENT_FROM_KEYS_3D<mem_ptr>(slot, key1, key2, key3) -> (value) { | ||
value = sload(GET_SLOT_FROM_KEYS_3D<mem_ptr>(slot, key1, key2, key3)) | ||
} | ||
|
||
/// @notice Store an element from a key | ||
fn STORE_ELEMENT<mem_ptr>(key, value) { | ||
sstore(GET_SLOT_FROM_KEY<mem_ptr>(key), value) | ||
} | ||
|
||
/// @notice Store an element from two keys | ||
fn STORE_ELEMENT_FROM_KEYS<mem_ptr>(key1, key2, value) { | ||
sstore(GET_SLOT_FROM_KEYS<mem_ptr>(key1, key2), value) | ||
} | ||
|
||
/// @notice Store an element from a slot and two keys | ||
fn STORE_ELEMENT_FROM_KEYS_2D<mem_ptr>(slot, key1, key2, value) { | ||
sstore(GET_SLOT_FROM_KEYS_2D<mem_ptr>(slot, key1, key2), value) | ||
} | ||
|
||
/// @notice Store an element from a slot and three keys | ||
fn STORE_ELEMENT_FROM_KEYS_3D<mem_ptr>(slot, key1, key2, key3, value) { | ||
sstore(GET_SLOT_FROM_KEYS_3D<mem_ptr>(slot, key1, key2, key3), value) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,160 +1,163 @@ | ||
/// @title HashMap | ||
/// @notice SPDX-License-Identifier: MIT | ||
/// @author asnared <https://github.com/abigger87> | ||
|
||
/// @notice A Module Encapsulating HashMap Methods | ||
/// @notice Adapted from <https://github.com/huff-language/huff-examples/blob/main/erc20/contracts/utils/HashMap.huff> | ||
|
||
/// @notice Given a piece of data (ie an address), hash it, generating the storage slot for a hashmap. | ||
#define macro GET_SLOT_FROM_KEY(mem_ptr) = takes(1) returns (1) { | ||
// Input stack: [key] | ||
// Load the data into memory and hash it, while preserving the memory location. | ||
<mem_ptr> // [<mem_ptr>, key] | ||
mstore // [] | ||
/// @notice Port to BALLs by eugenioclrc taken from https://github.com/huff-language/huffmate/blob/4e2c9bd3412ab8cc65f6ceadafc01a1ff1815796/src/data-structures/Hashmap.huff | ||
|
||
/// @notice Given a piece of data (ie an address), hash it, generating the storage slot for a hashmap. | ||
|
||
// Hash the data, generating a key. | ||
0x20 // [32] | ||
<mem_ptr> // [<mem_ptr>, 32] | ||
sha3 // [slot] | ||
/// @notice Given a piece of data (ie an address), hash it, generating the storage slot for a hashmap. | ||
#define macro GET_SLOT_FROM_KEY(mem_ptr) = takes(1) returns(1) { | ||
// takes: [key] | ||
<mem_ptr> // [key, mem_ptr] | ||
mstore // [] | ||
0x20 // [0x20] | ||
<mem_ptr> // [0x20, mem_ptr] | ||
sha3 // [hashed] | ||
// returns: [hashed] | ||
} | ||
|
||
/// @notice Given two keys (ie a slot and a key), hash them together, generating a slot for a secondary hashmap. | ||
#define macro GET_SLOT_FROM_KEYS(mem_ptr) = takes(2) returns (1) { | ||
// Input stack: [slot, key] | ||
// Load the data into memory. | ||
<mem_ptr> 0x20 add // [<mem_ptr> + 32, slot, key] | ||
mstore // [key] | ||
<mem_ptr> // [<mem_ptr>, key] | ||
mstore // [] | ||
|
||
// Hash the data, generating a slot. | ||
0x40 // [64] | ||
<mem_ptr> // [<mem_ptr>, 64] | ||
sha3 // [slot] | ||
#define macro GET_SLOT_FROM_KEYS(mem_ptr) = takes(2) returns(1) { | ||
// takes: [key, slot] | ||
<mem_ptr> // [key, slot, mem_ptr] | ||
0x20 // [key, slot, mem_ptr, 0x20] | ||
add // [key, slot, add(0x20, mem_ptr)] | ||
mstore // [key] | ||
<mem_ptr> // [key, mem_ptr] | ||
mstore // [] | ||
0x40 // [0x40] | ||
<mem_ptr> // [0x40, mem_ptr] | ||
sha3 // [hashed] | ||
// returns: [hashed] | ||
} | ||
|
||
/// @notice Calculate the slot from two keys | ||
#define macro GET_SLOT_FROM_KEYS_2D(mem_ptr) = takes(3) returns (1) { | ||
// Input stack: [slot, key1, key2] | ||
// Load the data into memory | ||
<mem_ptr> 0x20 add // [<mem_ptr> + 32, slot, key1, key2] | ||
mstore // [key1, key2] | ||
|
||
// next byte | ||
<mem_ptr> // [<mem_ptr>, key1, key2] | ||
mstore // [key2] | ||
|
||
0x40 // [0x40, key2] | ||
<mem_ptr> // [<mem_ptr>, 0x40, key2] | ||
sha3 // [hash, key2] | ||
|
||
// concat the two keys | ||
<mem_ptr> 0x20 add // [<mem_ptr> + 32, hash, key2] put hash in memory | ||
mstore // [key2] | ||
|
||
// next byte | ||
<mem_ptr> // [<mem_ptr>, key2] | ||
mstore // [] | ||
|
||
// Hash the data, generating a slot. | ||
0x40 // [0x40] | ||
<mem_ptr> // [<mem_ptr>, 0x40] | ||
sha3 // [slot] | ||
#define macro GET_SLOT_FROM_KEYS_2D(mem_ptr) = takes(3) returns(1) { | ||
// takes: [key2, key1, slot] | ||
0x20 // [key2, key1, slot, 0x20] | ||
<mem_ptr> // [key2, key1, slot, 0x20, mem_ptr] | ||
add // [key2, key1, slot, add(mem_ptr, 0x20)] | ||
mstore // [key2, key1] | ||
<mem_ptr> // [key2, key1, mem_ptr] | ||
mstore // [key2] | ||
0x20 // [key2, 0x20] | ||
<mem_ptr> // [key2, 0x20, mem_ptr] | ||
0x40 // [key2, 0x20, mem_ptr, 0x40] | ||
<mem_ptr> // [key2, 0x20, mem_ptr, 0x40, mem_ptr] | ||
sha3 // [key2, 0x20, mem_ptr, sha3(mem_ptr, 0x40)] | ||
pop // [key2, 0x20, mem_ptr] | ||
add // [key2, add(mem_ptr, 0x20)] | ||
mstore // [] | ||
0x40 // [0x40] | ||
<mem_ptr> // [0x40, mem_ptr] | ||
sha3 // [hashed] | ||
// returns: [hashed] | ||
} | ||
|
||
/// @notice Calculate the slot from three keys | ||
#define macro GET_SLOT_FROM_KEYS_3D(mem_ptr) = takes(4) returns (1) { | ||
// Input stack: [slot, key1, key2, key3] | ||
// Load the data into memory | ||
<mem_ptr> 0x20 add // [<mem_ptr> + 32, slot, key1, key2, key3] | ||
swap1 dup2 // [<mem_ptr> + 32, slot, <mem_ptr> + 32, key1, key2, key3] | ||
mstore // [<mem_ptr> + 32, key1, key2, key3] | ||
|
||
swap1 <mem_ptr> // [<mem_ptr>, key1, <mem_ptr> + 32, key2, key3] | ||
mstore // [<mem_ptr> + 32, key2, key3] | ||
|
||
0x40 // [0x40, <mem_ptr> + 32, key2, key3] | ||
<mem_ptr> // [<mem_ptr>, 0x40, <mem_ptr> + 32, key2, key3] | ||
sha3 // [slot1, <mem_ptr> + 32, key2, key3] | ||
|
||
// concat the first two keys | ||
dup2 // [<mem_ptr> + 32, slot1, <mem_ptr> + 32, key2, key3] put slot1 in memory | ||
mstore // [<mem_ptr> + 32, key2, key3] | ||
|
||
// put key2 in memory, before slot1 | ||
swap1 // [key2, <mem_ptr> + 32, key3] | ||
<mem_ptr> // [<mem_ptr>, key2, <mem_ptr> + 32, key3] | ||
mstore // [key3] | ||
|
||
0x40 // [0x40, <mem_ptr> + 32, key3] | ||
<mem_ptr> // [<mem_ptr>, 0x40, <mem_ptr> + 32, key3] | ||
sha3 // [slot2, <mem_ptr> + 32, key3] | ||
|
||
// concat with the third key | ||
swap1 // [<mem_ptr> + 32, slot2, key3] put slot2 in memory | ||
mstore // [key3] | ||
|
||
// put key3 in memory, before slot2 | ||
<mem_ptr> // [<mem_ptr>, key3] | ||
mstore // [] | ||
|
||
// Hash the data, generating the final slot3 | ||
0x40 // [0x40] | ||
<mem_ptr> // [<mem_ptr>, 0x40] | ||
sha3 // [slot3] | ||
#define macro GET_SLOT_FROM_KEYS_3D(mem_ptr) = takes(4) returns(1) { | ||
// takes: [key3, key2, key1, slot] | ||
0x20 // [key3, key2, key1, slot, 0x20] | ||
<mem_ptr> // [key3, key2, key1, slot, 0x20, mem_ptr] | ||
add // [key3, key2, key1, slot, add(mem_ptr, 0x20)] | ||
mstore // [key3, key2, key1] | ||
<mem_ptr> // [key3, key2, key1, mem_ptr] | ||
mstore // [key3, key2] | ||
0x20 // [key3, key2, 0x20] | ||
0x40 // [key3, key2, 0x20, 0x40] | ||
<mem_ptr> // [key3, key2, 0x20, 0x40, mem_ptr] | ||
sha3 // [key3, key2, 0x20, sha3(mem_ptr, 0x40)] | ||
pop // [key3, key2, 0x20] | ||
<mem_ptr> // [key3, key2, 0x20, mem_ptr] | ||
add // [key3, key2, add(mem_ptr, 0x20)] | ||
mstore // [key3] | ||
0x20 // [key3, 0x20] | ||
<mem_ptr> // [key3, 0x20, mem_ptr] | ||
add // [key3, add(mem_ptr, 0x20)] | ||
0x40 // [key3, add(mem_ptr, 0x20), 0x40] | ||
<mem_ptr> // [key3, add(mem_ptr, 0x20), 0x40, mem_ptr] | ||
sha3 // [key3, add(mem_ptr, 0x20), sha3(mem_ptr, 0x40)] | ||
pop // [key3, add(mem_ptr, 0x20)] | ||
mstore // [] | ||
0x40 // [0x40] | ||
<mem_ptr> // [0x40, mem_ptr] | ||
sha3 // [hashed] | ||
// returns: [hashed] | ||
} | ||
|
||
/// @notice Load an element onto the stack from a key | ||
#define macro LOAD_ELEMENT(mem_ptr) = takes(1) returns(1) { | ||
// Input stack: [key] | ||
GET_SLOT_FROM_KEY(<mem_ptr>) // [slot] | ||
sload // [value] | ||
// takes: [key] | ||
GET_SLOT_FROM_KEY(<mem_ptr>) | ||
// [GET_SLOT_FROM_KEY<mem_ptr>(key)] | ||
sload // [value] | ||
// returns: [value] | ||
} | ||
|
||
/// @notice Load an element onto the stack from two keys | ||
#define macro LOAD_ELEMENT_FROM_KEYS(mem_ptr) = takes(2) returns(1) { | ||
// Input stack: [key1, key2] | ||
GET_SLOT_FROM_KEYS(<mem_ptr>) // [slot] | ||
sload // [value] | ||
// takes: [key2, key1] | ||
GET_SLOT_FROM_KEYS(<mem_ptr>) | ||
// [GET_SLOT_FROM_KEYS<mem_ptr>(key1, key2)] | ||
sload // [value] | ||
// returns: [value] | ||
} | ||
|
||
/// @notice Load an element onto the stack from a slot and two keys | ||
#define macro LOAD_ELEMENT_FROM_KEYS_2D(mem_ptr) = takes(3) returns(1) { | ||
// Input stack: [slot, key1, key2] | ||
GET_SLOT_FROM_KEYS_2D(<mem_ptr>) // [slot] | ||
sload // [value] | ||
// takes: [key2, key1, slot] | ||
GET_SLOT_FROM_KEYS_2D(<mem_ptr>) | ||
// [GET_SLOT_FROM_KEYS_2D<mem_ptr>(slot, key1, key2)] | ||
sload // [value] | ||
// returns: [value] | ||
} | ||
|
||
/// @notice Load an element onto the stack from a slot and three keys | ||
#define macro LOAD_ELEMENT_FROM_KEYS_3D(mem_ptr) = takes(4) returns(1) { | ||
// Input stack: [slot, key1, key2, key3] | ||
GET_SLOT_FROM_KEYS_3D(<mem_ptr>) // [slot] | ||
sload // [value] | ||
// takes: [key3, key2, key1, slot] | ||
GET_SLOT_FROM_KEYS_3D(<mem_ptr>) | ||
// [GET_SLOT_FROM_KEYS_3D<mem_ptr>(slot, key1, key2, key3)] | ||
sload // [value] | ||
// returns: [value] | ||
} | ||
|
||
/// @notice Store an element from a key | ||
#define macro STORE_ELEMENT(mem_ptr) = takes(2) returns(0) { | ||
// Input stack: [key, value] | ||
GET_SLOT_FROM_KEY(<mem_ptr>) // [slot, value] | ||
sstore // [] | ||
// takes: [value, key] | ||
GET_SLOT_FROM_KEY(<mem_ptr>) | ||
// [value, GET_SLOT_FROM_KEY<mem_ptr>(key)] | ||
sstore // [] | ||
// returns: [] | ||
} | ||
|
||
/// @notice Store an element from two keys | ||
#define macro STORE_ELEMENT_FROM_KEYS(mem_ptr) = takes(3) returns (0) { | ||
// Input stack: [key1, key2, value] | ||
GET_SLOT_FROM_KEYS(<mem_ptr>) // [slot, value] | ||
sstore // [] | ||
#define macro STORE_ELEMENT_FROM_KEYS(mem_ptr) = takes(3) returns(0) { | ||
// takes: [value, key2, key1] | ||
GET_SLOT_FROM_KEYS(<mem_ptr>) | ||
// [value, GET_SLOT_FROM_KEYS<mem_ptr>(key1, key2)] | ||
sstore // [] | ||
// returns: [] | ||
} | ||
|
||
/// @notice Store an element from a slot and two keys | ||
#define macro STORE_ELEMENT_FROM_KEYS_2D(mem_ptr) = takes(4) returns (0) { | ||
// Input stack: [slot, key1, key2, value] | ||
GET_SLOT_FROM_KEYS_2D(<mem_ptr>) // [slot, value] | ||
sstore // [] | ||
#define macro STORE_ELEMENT_FROM_KEYS_2D(mem_ptr) = takes(4) returns(0) { | ||
// takes: [value, key2, key1, slot] | ||
GET_SLOT_FROM_KEYS_2D(<mem_ptr>) | ||
// [value, GET_SLOT_FROM_KEYS_2D<mem_ptr>(slot, key1, key2)] | ||
sstore // [] | ||
// returns: [] | ||
} | ||
|
||
/// @notice Store an element from a slot and three keys | ||
#define macro STORE_ELEMENT_FROM_KEYS_3D(mem_ptr) = takes(5) returns (0) { | ||
// Input stack: [slot, key1, key2, key3, value] | ||
GET_SLOT_FROM_KEYS_3D(<mem_ptr>) // [slot, value] | ||
sstore // [] | ||
#define macro STORE_ELEMENT_FROM_KEYS_3D(mem_ptr) = takes(5) returns(0) { | ||
// takes: [value, key3, key2, key1, slot] | ||
GET_SLOT_FROM_KEYS_3D(<mem_ptr>) | ||
// [value, GET_SLOT_FROM_KEYS_3D<mem_ptr>(slot, key1, key2, key3)] | ||
sstore // [] | ||
// returns: [] | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the result of this hash needs to be stored and set into memory. Also wonder if you could save gas by storing
add(mem_ptr, 0x20)
in a var and just referencing that again