@@ -5,6 +5,8 @@ const { TYPES } = require('./EnumerableSet.opts');
55const header = `\
66pragma solidity ^0.8.20;
77
8+ import {Hashes} from "../cryptography/Hashes.sol";
9+
810/**
911 * @dev Library for managing
1012 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
@@ -233,14 +235,131 @@ function values(${name} storage set) internal view returns (${type}[] memory) {
233235}
234236` ;
235237
238+ const memorySet = ( { name, type } ) => `\
239+ struct ${ name } {
240+ // Storage of set values
241+ ${ type } [] _values;
242+ // Position is the index of the value in the \`values\` array plus 1.
243+ // Position 0 is used to mean a value is not in the self.
244+ mapping(bytes32 valueHash => uint256) _positions;
245+ }
246+
247+ /**
248+ * @dev Add a value to a self. O(1).
249+ *
250+ * Returns true if the value was added to the set, that is if it was not
251+ * already present.
252+ */
253+ function add(${ name } storage self, ${ type } memory value) internal returns (bool) {
254+ if (!contains(self, value)) {
255+ self._values.push(value);
256+ // The value is stored at length-1, but we add 1 to all indexes
257+ // and use 0 as a sentinel value
258+ self._positions[_hash(value)] = self._values.length;
259+ return true;
260+ } else {
261+ return false;
262+ }
263+ }
264+
265+ /**
266+ * @dev Removes a value from a self. O(1).
267+ *
268+ * Returns true if the value was removed from the set, that is if it was
269+ * present.
270+ */
271+ function remove(${ name } storage self, ${ type } memory value) internal returns (bool) {
272+ // We cache the value's position to prevent multiple reads from the same storage slot
273+ bytes32 valueHash = _hash(value);
274+ uint256 position = self._positions[valueHash];
275+
276+ if (position != 0) {
277+ // Equivalent to contains(self, value)
278+ // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
279+ // the array, and then remove the last element (sometimes called as 'swap and pop').
280+ // This modifies the order of the array, as noted in {at}.
281+
282+ uint256 valueIndex = position - 1;
283+ uint256 lastIndex = self._values.length - 1;
284+
285+ if (valueIndex != lastIndex) {
286+ ${ type } memory lastValue = self._values[lastIndex];
287+
288+ // Move the lastValue to the index where the value to delete is
289+ self._values[valueIndex] = lastValue;
290+ // Update the tracked position of the lastValue (that was just moved)
291+ self._positions[_hash(lastValue)] = position;
292+ }
293+
294+ // Delete the slot where the moved value was stored
295+ self._values.pop();
296+
297+ // Delete the tracked position for the deleted slot
298+ delete self._positions[valueHash];
299+
300+ return true;
301+ } else {
302+ return false;
303+ }
304+ }
305+
306+ /**
307+ * @dev Returns true if the value is in the self. O(1).
308+ */
309+ function contains(${ name } storage self, ${ type } memory value) internal view returns (bool) {
310+ return self._positions[_hash(value)] != 0;
311+ }
312+
313+ /**
314+ * @dev Returns the number of values on the self. O(1).
315+ */
316+ function length(${ name } storage self) internal view returns (uint256) {
317+ return self._values.length;
318+ }
319+
320+ /**
321+ * @dev Returns the value stored at position \`index\` in the self. O(1).
322+ *
323+ * Note that there are no guarantees on the ordering of values inside the
324+ * array, and it may change when more values are added or removed.
325+ *
326+ * Requirements:
327+ *
328+ * - \`index\` must be strictly less than {length}.
329+ */
330+ function at(${ name } storage self, uint256 index) internal view returns (${ type } memory) {
331+ return self._values[index];
332+ }
333+
334+ /**
335+ * @dev Return the entire set in an array
336+ *
337+ * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
338+ * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
339+ * this function has an unbounded cost, and using it as part of a state-changing function may render the function
340+ * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
341+ */
342+ function values(${ name } storage self) internal view returns (${ type } [] memory) {
343+ return self._values;
344+ }
345+ ` ;
346+
347+ const hashes = `\
348+ function _hash(bytes32[2] memory value) private pure returns (bytes32) {
349+ return Hashes.efficientKeccak256(value[0], value[1]);
350+ }
351+ ` ;
352+
236353// GENERATE
237354module . exports = format (
238355 header . trimEnd ( ) ,
239356 'library EnumerableSet {' ,
240357 format (
241358 [ ] . concat (
242359 defaultSet ,
243- TYPES . map ( details => customSet ( details ) ) ,
360+ TYPES . filter ( ( { size } ) => size == undefined ) . map ( details => customSet ( details ) ) ,
361+ TYPES . filter ( ( { size } ) => size != undefined ) . map ( details => memorySet ( details ) ) ,
362+ hashes ,
244363 ) ,
245364 ) . trimEnd ( ) ,
246365 '}' ,
0 commit comments