Skip to content
Prev Previous commit
Next Next commit
Split sort helpers
  • Loading branch information
ernestognw committed Mar 26, 2024
commit 67aec22f08cab210daaff5101e46f15dc41d0658
42 changes: 21 additions & 21 deletions contracts/utils/Arrays.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,22 +40,6 @@ library Arrays {
return array;
}

/// @dev Helper: low level cast address memory array to uint256 memory array
function _castToBytes32Array(address[] memory input) private pure returns (bytes32[] memory output) {
assembly {
output := input
}
}

/// @dev Helper: low level cast address comp function to bytes32 comp function
function _castToBytes32Comp(
function(address, address) pure returns (bool) input
) private pure returns (function(bytes32, bytes32) pure returns (bool) output) {
assembly {
output := input
}
}

/**
* @dev Sort an array of bytes32 (in memory) following the provided comparator function.
*
Expand All @@ -82,11 +66,6 @@ library Arrays {
return sort(array, _defaultComp);
}

/// @dev Comparator for sorting arrays in increasing order.
function _defaultComp(bytes32 a, bytes32 b) private pure returns (bool) {
return a < b;
}

/**
* @dev Sort an array of uint256 (in memory) following the provided comparator function.
*
Expand Down Expand Up @@ -114,6 +93,27 @@ library Arrays {
return array;
}

/// @dev Comparator for sorting arrays in increasing order.
function _defaultComp(bytes32 a, bytes32 b) private pure returns (bool) {
return a < b;
}

/// @dev Helper: low level cast address memory array to uint256 memory array
function _castToBytes32Array(address[] memory input) private pure returns (bytes32[] memory output) {
assembly {
output := input
}
}

/// @dev Helper: low level cast address comp function to bytes32 comp function
function _castToBytes32Comp(
function(address, address) pure returns (bool) input
) private pure returns (function(bytes32, bytes32) pure returns (bool) output) {
assembly {
output := input
}
}

/// @dev Helper: low level cast uint256 memory array to uint256 memory array
function _castToBytes32Array(uint256[] memory input) private pure returns (bytes32[] memory output) {
assembly {
Expand Down
113 changes: 58 additions & 55 deletions scripts/generate/templates/Arrays.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,70 +13,71 @@ import {Math} from "./math/Math.sol";
*/
`;

const sort = type => `
/**
* @dev Sort an array of ${type} (in memory) following the provided comparator function.
*
* This function does the sorting "in place", meaning that it overrides the input. The object is returned for
* convenience, but that returned value can be discarded safely if the caller has a memory pointer to the array.
*
* NOTE: this function's cost is \`O(n · log(n))\` in average and \`O(n²)\` in the worst case, with n the length of the
* array. Using it in view functions that are executed through \`eth_call\` is safe, but one should be very careful
* when executing this as part of a transaction. If the array being sorted is too large, the sort operation may
* consume more gas than is available in a block, leading to potential DoS.
*/
function sort(
${type}[] memory array,
function(${type}, ${type}) pure returns (bool) comp
) internal pure returns (${type}[] memory) {
${
type === 'bytes32'
? '_quickSort(_begin(array), _end(array), comp);'
: 'sort(_castToBytes32Array(array), _castToBytes32Comp(comp));'
const sort = type => {
const sortLine =
type === 'bytes32'
? '_quickSort(_begin(array), _end(array), comp);'
: 'sort(_castToBytes32Array(array), _castToBytes32Comp(comp));';
const ascendingSortLine =
type === 'bytes32'
? 'return sort(array, _defaultComp);'
: 'sort(_castToBytes32Array(array), _defaultComp);\nreturn array;';

return `
/**
* @dev Sort an array of ${type} (in memory) following the provided comparator function.
*
* This function does the sorting "in place", meaning that it overrides the input. The object is returned for
* convenience, but that returned value can be discarded safely if the caller has a memory pointer to the array.
*
* NOTE: this function's cost is \`O(n · log(n))\` in average and \`O(n²)\` in the worst case, with n the length of the
* array. Using it in view functions that are executed through \`eth_call\` is safe, but one should be very careful
* when executing this as part of a transaction. If the array being sorted is too large, the sort operation may
* consume more gas than is available in a block, leading to potential DoS.
*/
function sort(
${type}[] memory array,
function(${type}, ${type}) pure returns (bool) comp
) internal pure returns (${type}[] memory) {
${sortLine}
return array;
}
return array;
}

/**
* @dev Variant of {sort} that sorts an array of ${type} in increasing order.
*/
function sort(${type}[] memory array) internal pure returns (${type}[] memory) {
${
type === 'bytes32'
? 'return sort(array, _defaultComp);'
: `sort(_castToBytes32Array(array), _defaultComp);
return array;`

/**
* @dev Variant of {sort} that sorts an array of ${type} in increasing order.
*/
function sort(${type}[] memory array) internal pure returns (${type}[] memory) {
${ascendingSortLine}
}
}
`;
};

${
type === 'bytes32'
? `
const defaultComparator = `
/// @dev Comparator for sorting arrays in increasing order.
function _defaultComp(bytes32 a, bytes32 b) private pure returns (bool) {
return a < b;
}
`
: `
/// @dev Helper: low level cast ${type} memory array to uint256 memory array
function _castToBytes32Array(${type}[] memory input) private pure returns (bytes32[] memory output) {
assembly {
output := input
}
}

/// @dev Helper: low level cast ${type} comp function to bytes32 comp function
function _castToBytes32Comp(
function(${type}, ${type}) pure returns (bool) input
) private pure returns (function(bytes32, bytes32) pure returns (bool) output) {
assembly {
output := input
}
}
`
}
`;

const sortHelpers = type => {
if (type === 'bytes32') return '';
return `/// @dev Helper: low level cast ${type} memory array to uint256 memory array
function _castToBytes32Array(${type}[] memory input) private pure returns (bytes32[] memory output) {
assembly {
output := input
}
}

/// @dev Helper: low level cast ${type} comp function to bytes32 comp function
function _castToBytes32Comp(
function(${type}, ${type}) pure returns (bool) input
) private pure returns (function(bytes32, bytes32) pure returns (bool) output) {
assembly {
output := input
}
}`;
};

const pointers = `
/**
* @dev Pointer to the memory location of the first element of \`array\`.
Expand Down Expand Up @@ -376,6 +377,8 @@ module.exports = format(
'library Arrays {',
'using StorageSlot for bytes32;',
...TYPES.map(sort),
defaultComparator,
...TYPES.map(sortHelpers),
pointers,
quickSort,
bounds,
Expand Down