Skip to content

✨ Add set and delete_ to native bytes/string in storage #1371

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions src/utils/LibBytes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,114 @@ library LibBytes {
/// @dev The constant returned when the `search` is not found in the bytes.
uint256 internal constant NOT_FOUND = type(uint256).max;

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* NATIVE BYTES OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

/// @dev Sets the value of the bytes array storage reference `$` to `s`.
/// A bytes array in memory cannot be assigned to a local bytes array storage reference directly.
function set(bytes storage $, bytes memory s) internal {
/// @solidity memory-safe-assembly
assembly {
let len := mload(s)
let packed := shl(1, len)
for {} 1 {} {
if iszero(gt(len, 0x1f)) {
let right_aligned := mload(add(s, len))
let zeros := shl(3, sub(0x20, len))
let left_aligned := shl(zeros, right_aligned)
packed := or(packed, left_aligned)
break
}
packed := or(packed, 1)
mstore(0x00, $.slot)
let ptr := keccak256(0x00, 0x20)
// the number of words minus 1
let words := shr(5, sub(len, 1))
let end := add(ptr, words)
let o := add(s, 0x20)
for {} 1 {} {
if eq(ptr, end) { break }
sstore(ptr, mload(o))
ptr := add(ptr, 1)
o := add(o, 0x20)
}
// clean and store the last word
let right_aligned := mload(add(s, len))
let zeros := shl(3, sub(sub(o, s), len))
let left_aligned := shl(zeros, right_aligned)
sstore(ptr, left_aligned)
break
}
sstore($.slot, packed)
}
}

/// @dev Sets the value of the bytes array storage reference `$` to `s`.
/// A bytes array in calldata cannot be assigned to a local bytes array storage reference directly.
function setCalldata(bytes storage $, bytes calldata s) internal {
/// @solidity memory-safe-assembly
assembly {
let len_ptr := sub(s.offset, 0x20)
let packed := shl(1, s.length)
for {} 1 {} {
if iszero(gt(s.length, 0x1f)) {
let right_aligned := calldataload(add(len_ptr, s.length))
let zeros := shl(3, sub(0x20, s.length))
let left_aligned := shl(zeros, right_aligned)
packed := or(packed, left_aligned)
break
}
packed := or(packed, 1)
mstore(0x00, $.slot)
let ptr := keccak256(0x00, 0x20)
// the number of words minus 1
let words := shr(5, sub(s.length, 1))
let end := add(ptr, words)
let o := s.offset
for {} 1 {} {
if eq(ptr, end) { break }
sstore(ptr, calldataload(o))
ptr := add(ptr, 1)
o := add(o, 0x20)
}
// clean and store the last word
let right_aligned := calldataload(add(len_ptr, s.length))
let zeros := shl(3, sub(sub(o, len_ptr), s.length))
let left_aligned := shl(zeros, right_aligned)
sstore(ptr, left_aligned)
break
}
sstore($.slot, packed)
}
}

/// @dev Deletes a bytes array from storage.
/// The `delete` keyword is not applicable to local bytes array storage references.
function delete_(bytes storage $) internal {
/// @solidity memory-safe-assembly
assembly {
let packed := sload($.slot)
let is_long_string := and(packed, 1)
for {} 1 {} {
sstore($.slot, 0)
if iszero(is_long_string) { break }
mstore(0, $.slot)
let ptr := keccak256(0x00, 0x20)
let len := shr(1, packed)
// the number of words used to store the string
let words := shr(5, add(len, 0x1f))
let end := add(ptr, words)
for {} 1 {} {
sstore(ptr, 0)
ptr := add(ptr, 1)
if eq(ptr, end) { break }
}
break
}
}
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BYTE STORAGE OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
Expand Down
30 changes: 30 additions & 0 deletions src/utils/LibString.sol
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,36 @@ library LibString {
/// @dev Lookup for ' \t\n\r\x0b\x0c'.
uint128 internal constant WHITESPACE_7_BIT_ASCII = 0x100003e00;

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* NATIVE STRING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

/// @dev Sets the value of the string storage reference `$` to `s`.
/// A string in memory cannot be assigned to a local string storage reference directly.
function set(string storage $, string memory s) internal {
LibBytes.set(bytesStorage($), bytes(s));
}

/// @dev Sets the value of the string storage reference `$` to `s`.
/// A string in calldata cannot be assigned to a local string storage reference directly.
function setCalldata(string storage $, string calldata s) internal {
LibBytes.setCalldata(bytesStorage($), bytes(s));
}

/// @dev Deletes a string from storage.
/// The `delete` keyword is not applicable to local string storage references.
function delete_(string storage $) internal {
LibBytes.delete_(bytesStorage($));
}

/// @dev Helper to cast `$` to a `bytes`.
function bytesStorage(string storage $) internal pure returns (bytes storage casted) {
/// @solidity memory-safe-assembly
assembly {
casted.slot := $.slot
}
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STRING STORAGE OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
Expand Down
108 changes: 108 additions & 0 deletions src/utils/g/LibBytes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,114 @@ library LibBytes {
/// @dev The constant returned when the `search` is not found in the bytes.
uint256 internal constant NOT_FOUND = type(uint256).max;

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* NATIVE BYTES OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

/// @dev Sets the value of the bytes array storage reference `$` to `s`.
/// A bytes array in memory cannot be assigned to a local bytes array storage reference directly.
function set(bytes storage $, bytes memory s) internal {
/// @solidity memory-safe-assembly
assembly {
let len := mload(s)
let packed := shl(1, len)
for {} 1 {} {
if iszero(gt(len, 0x1f)) {
let right_aligned := mload(add(s, len))
let zeros := shl(3, sub(0x20, len))
let left_aligned := shl(zeros, right_aligned)
packed := or(packed, left_aligned)
break
}
packed := or(packed, 1)
mstore(0x00, $.slot)
let ptr := keccak256(0x00, 0x20)
// the number of words minus 1
let words := shr(5, sub(len, 1))
let end := add(ptr, words)
let o := add(s, 0x20)
for {} 1 {} {
if eq(ptr, end) { break }
sstore(ptr, mload(o))
ptr := add(ptr, 1)
o := add(o, 0x20)
}
// clean and store the last word
let right_aligned := mload(add(s, len))
let zeros := shl(3, sub(sub(o, s), len))
let left_aligned := shl(zeros, right_aligned)
sstore(ptr, left_aligned)
break
}
sstore($.slot, packed)
}
}

/// @dev Sets the value of the bytes array storage reference `$` to `s`.
/// A bytes array in calldata cannot be assigned to a local bytes array storage reference directly.
function setCalldata(bytes storage $, bytes calldata s) internal {
/// @solidity memory-safe-assembly
assembly {
let len_ptr := sub(s.offset, 0x20)
let packed := shl(1, s.length)
for {} 1 {} {
if iszero(gt(s.length, 0x1f)) {
let right_aligned := calldataload(add(len_ptr, s.length))
let zeros := shl(3, sub(0x20, s.length))
let left_aligned := shl(zeros, right_aligned)
packed := or(packed, left_aligned)
break
}
packed := or(packed, 1)
mstore(0x00, $.slot)
let ptr := keccak256(0x00, 0x20)
// the number of words minus 1
let words := shr(5, sub(s.length, 1))
let end := add(ptr, words)
let o := s.offset
for {} 1 {} {
if eq(ptr, end) { break }
sstore(ptr, calldataload(o))
ptr := add(ptr, 1)
o := add(o, 0x20)
}
// clean and store the last word
let right_aligned := calldataload(add(len_ptr, s.length))
let zeros := shl(3, sub(sub(o, len_ptr), s.length))
let left_aligned := shl(zeros, right_aligned)
sstore(ptr, left_aligned)
break
}
sstore($.slot, packed)
}
}

/// @dev Deletes a bytes array from storage.
/// The `delete` keyword is not applicable to local bytes array storage references.
function delete_(bytes storage $) internal {
/// @solidity memory-safe-assembly
assembly {
let packed := sload($.slot)
let is_long_string := and(packed, 1)
for {} 1 {} {
sstore($.slot, 0)
if iszero(is_long_string) { break }
mstore(0, $.slot)
let ptr := keccak256(0x00, 0x20)
let len := shr(1, packed)
// the number of words used to store the string
let words := shr(5, add(len, 0x1f))
let end := add(ptr, words)
for {} 1 {} {
sstore(ptr, 0)
ptr := add(ptr, 1)
if eq(ptr, end) { break }
}
break
}
}
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BYTE STORAGE OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
Expand Down
30 changes: 30 additions & 0 deletions src/utils/g/LibString.sol
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,36 @@ library LibString {
/// @dev Lookup for ' \t\n\r\x0b\x0c'.
uint128 internal constant WHITESPACE_7_BIT_ASCII = 0x100003e00;

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* NATIVE STRING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

/// @dev Sets the value of the string storage reference `$` to `s`.
/// A string in memory cannot be assigned to a local string storage reference directly.
function set(string storage $, string memory s) internal {
LibBytes.set(bytesStorage($), bytes(s));
}

/// @dev Sets the value of the string storage reference `$` to `s`.
/// A string in calldata cannot be assigned to a local string storage reference directly.
function setCalldata(string storage $, string calldata s) internal {
LibBytes.setCalldata(bytesStorage($), bytes(s));
}

/// @dev Deletes a string from storage.
/// The `delete` keyword is not applicable to local string storage references.
function delete_(string storage $) internal {
LibBytes.delete_(bytesStorage($));
}

/// @dev Helper to cast `$` to a `bytes`.
function bytesStorage(string storage $) internal pure returns (bytes storage casted) {
/// @solidity memory-safe-assembly
assembly {
casted.slot := $.slot
}
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STRING STORAGE OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
Expand Down
Loading
Loading