@@ -5,6 +5,8 @@ const { TYPES } = require('./EnumerableSet.opts');
5
5
const header = `\
6
6
pragma solidity ^0.8.20;
7
7
8
+ import {Hashes} from "../cryptography/Hashes.sol";
9
+
8
10
/**
9
11
* @dev Library for managing
10
12
* 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) {
233
235
}
234
236
` ;
235
237
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
+
236
353
// GENERATE
237
354
module . exports = format (
238
355
header . trimEnd ( ) ,
239
356
'library EnumerableSet {' ,
240
357
format (
241
358
[ ] . concat (
242
359
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 ,
244
363
) ,
245
364
) . trimEnd ( ) ,
246
365
'}' ,
0 commit comments