@@ -37,12 +37,20 @@ struct TBytes {
3737 uint256 _spacer;
3838}
3939
40+ /// @dev Pointer struct to a stack pointer generator in transient storage.
41+ /// This stack does not directly take in values. Instead, it generates pointers
42+ /// that can be casted to any of the other transient storage pointer struct.
43+ struct TStack {
44+ uint256 _spacer;
45+ }
46+
4047using LibTransient for TUint256 global ;
4148using LibTransient for TInt256 global ;
4249using LibTransient for TBytes32 global ;
4350using LibTransient for TAddress global ;
4451using LibTransient for TBool global ;
4552using LibTransient for TBytes global ;
53+ using LibTransient for TStack global ;
4654
4755/// @notice Library for transient storage operations.
4856/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/g/LibTransient.sol)
@@ -52,6 +60,13 @@ using LibTransient for TBytes global;
5260/// L2s are super cheap anyway.
5361/// For best safety, always clear the storage after use.
5462library LibTransient {
63+ /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
64+ /* CUSTOM ERRORS */
65+ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
66+
67+ /// @dev The transient stack is empty.
68+ error StackIsEmpty ();
69+
5570 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
5671 /* CONSTANTS */
5772 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
@@ -60,6 +75,10 @@ library LibTransient {
6075 /// `bytes4(keccak256("_LIB_TRANSIENT_COMPAT_SLOT_SEED"))`.
6176 uint256 private constant _LIB_TRANSIENT_COMPAT_SLOT_SEED = 0x5a0b45f2 ;
6277
78+ /// @dev Multiplier to stack base slot, so that in the case where two stacks
79+ /// share consecutive base slots, their pointers will likely not overlap. A prime.
80+ uint256 private constant _STACK_BASE_SALT = 0x9e076501211e1371b ;
81+
6382 /// @dev The canonical address of the transient registry.
6483 /// See: https://gist.github.com/Vectorized/4ab665d7a234ef5aaaff2e5091ec261f
6584 address internal constant REGISTRY = 0x000000000000297f64C7F8d9595e43257908F170 ;
@@ -703,6 +722,95 @@ library LibTransient {
703722 _compat (ptr)._spacer = 0 ;
704723 }
705724
725+ /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
726+ /* STACK OPERATIONS */
727+ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
728+
729+ /// @dev Returns a pointer to a stack in transient storage.
730+ function tStack (bytes32 tSlot ) internal pure returns (TStack storage ptr ) {
731+ /// @solidity memory-safe-assembly
732+ assembly {
733+ ptr.slot := tSlot
734+ }
735+ }
736+
737+ /// @dev Returns a pointer to a stack in transient storage.
738+ function tStack (uint256 tSlot ) internal pure returns (TStack storage ptr ) {
739+ /// @solidity memory-safe-assembly
740+ assembly {
741+ ptr.slot := tSlot
742+ }
743+ }
744+
745+ /// @dev Returns the number of elements in the stack.
746+ function length (TStack storage ptr ) internal view returns (uint256 result ) {
747+ /// @solidity memory-safe-assembly
748+ assembly {
749+ result := shr (160 , shl (128 , tload (ptr.slot))) // Removes the base offset and stride.
750+ }
751+ }
752+
753+ /// @dev Clears the stack at `ptr`.
754+ /// Note: Future usage of the stack will point to a fresh transient storage region.
755+ function clear (TStack storage ptr ) internal {
756+ /// @solidity memory-safe-assembly
757+ assembly {
758+ // Clears the length and increments the base pointer by `1 << 128`.
759+ tstore (ptr.slot, shl (128 , add (1 , shr (128 , tload (ptr.slot)))))
760+ }
761+ }
762+
763+ /// @dev Increments the stack length by 1, and returns a pointer to the top element.
764+ /// We don't want to call this `push` as it does not take in an element value.
765+ /// Note: The value pointed to might not be cleared from previous usage.
766+ function place (TStack storage ptr ) internal returns (bytes32 topPtr ) {
767+ /// @solidity memory-safe-assembly
768+ assembly {
769+ topPtr := add (0x100000000 , tload (ptr.slot)) // Increments by a stride.
770+ tstore (ptr.slot, topPtr)
771+ topPtr := add (mul (_STACK_BASE_SALT, ptr.slot), topPtr)
772+ }
773+ }
774+
775+ /// @dev Returns a pointer to the top element. Returns zero if the stack is empty.
776+ /// This method can help avoid an additional `TLOAD`.
777+ function peek (TStack storage ptr ) internal view returns (bytes32 topPtr ) {
778+ /// @solidity memory-safe-assembly
779+ assembly {
780+ let t := tload (ptr.slot)
781+ topPtr := mul (iszero (iszero (shl (128 , t))), add (mul (_STACK_BASE_SALT, ptr.slot), t))
782+ }
783+ }
784+
785+ /// @dev Returns a pointer to the top element. Reverts if the stack is empty.
786+ function top (TStack storage ptr ) internal view returns (bytes32 topPtr ) {
787+ /// @solidity memory-safe-assembly
788+ assembly {
789+ topPtr := tload (ptr.slot)
790+ if iszero (topPtr) {
791+ mstore (0x00 , 0xbb704e21 ) // `StackIsEmpty()`.
792+ revert (0x1c , 0x04 )
793+ }
794+ topPtr := add (mul (_STACK_BASE_SALT, ptr.slot), topPtr)
795+ }
796+ }
797+
798+ /// @dev Decrements the stack length by 1, returns a pointer to the top element
799+ /// before the popping. Reverts if the stack is empty.
800+ /// Note: Popping from the stack does NOT auto-clear the top value.
801+ function pop (TStack storage ptr ) internal returns (bytes32 lastTopPtr ) {
802+ /// @solidity memory-safe-assembly
803+ assembly {
804+ lastTopPtr := tload (ptr.slot)
805+ if iszero (lastTopPtr) {
806+ mstore (0x00 , 0xbb704e21 ) // `StackIsEmpty()`.
807+ revert (0x1c , 0x04 )
808+ }
809+ tstore (ptr.slot, sub (lastTopPtr, 0x100000000 )) // Decrements by a stride.
810+ lastTopPtr := add (mul (_STACK_BASE_SALT, ptr.slot), lastTopPtr)
811+ }
812+ }
813+
706814 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
707815 /* TRANSIENT REGISTRY OPERATIONS */
708816 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
0 commit comments