Skip to content

Commit

Permalink
add library TWStringSet (#331)
Browse files Browse the repository at this point in the history
  • Loading branch information
nkrishang authored Feb 6, 2023
1 parent c220bc0 commit 50b4122
Show file tree
Hide file tree
Showing 2 changed files with 184 additions and 0 deletions.
172 changes: 172 additions & 0 deletions contracts/lib/TWStringSet.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// SPDX-License-Identifier: Apache 2.0
// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol)

pragma solidity ^0.8.0;

library TWStringSet {
struct Set {
// Storage of set values
string[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(string => uint256) _indexes;
}

/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, string memory value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}

/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, string memory value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];

if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.

uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;

if (lastIndex != toDeleteIndex) {
string memory lastValue = set._values[lastIndex];

// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
}

// Delete the slot where the moved value was stored
set._values.pop();

// Delete the index for the deleted slot
delete set._indexes[value];

return true;
} else {
return false;
}
}

/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, string memory value) private view returns (bool) {
return set._indexes[value] != 0;
}

/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}

/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (string memory) {
return set._values[index];
}

/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (string[] memory) {
return set._values;
}

/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Set storage set, string memory value) internal returns (bool) {
return _add(set, value);
}

/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Set storage set, string memory value) internal returns (bool) {
return _remove(set, value);
}

/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Set storage set, string memory value) internal view returns (bool) {
return _contains(set, value);
}

/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Set storage set) internal view returns (uint256) {
return _length(set);
}

/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Set storage set, uint256 index) internal view returns (string memory) {
return _at(set, index);
}

/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Set storage set) internal view returns (string[] memory) {
return _values(set);
}
}
12 changes: 12 additions & 0 deletions docs/TWStringSet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# TWStringSet











0 comments on commit 50b4122

Please sign in to comment.