From b74bb857118fb84ec8ba3147229151eb75fbfcb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alberto=20Cuesta=20Ca=C3=B1ada?= Date: Wed, 15 Jan 2020 09:58:28 +0000 Subject: [PATCH] Drafted Enumerable.sol. --- contracts/drafts/Enumerable.sol | 192 ++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 contracts/drafts/Enumerable.sol diff --git a/contracts/drafts/Enumerable.sol b/contracts/drafts/Enumerable.sol new file mode 100644 index 000000000..c1a15d1b3 --- /dev/null +++ b/contracts/drafts/Enumerable.sol @@ -0,0 +1,192 @@ +pragma solidity ^0.5.10; + + +/** + * @title Enumerable + * @dev This contract implements an enumerable address set as a doubly linked list. + * @author Alberto Cuesta CaƱada + */ +library Enumerables { + + event ObjectCreated(uint256 id, address data); + event ObjectsLinked(uint256 prev, uint256 next); + event ObjectRemoved(uint256 id); + event NewHead(uint256 id); + event NewTail(uint256 id); + + struct Object{ + uint256 id; + uint256 next; + uint256 prev; + address data; + } + + struct Enumerable{ + uint256 head; + uint256 tail; + uint256 idCounter; + mapping (uint256 => Object) objects; + } + + /** + * @dev Retrieves the Object denoted by `id`. + */ + function get(Enumerable storage enumerable, uint256 id) + public + view + returns (uint256, uint256, uint256, address) + { + Object memory object = enumerable.objects[id]; + return (object.id, object.next, object.prev, object.data); + } + + /** + * @dev Insert a new Object as the new Head with `data` in the data field. + */ + function append(Enumerable storage enumerable, address data) + public + returns (bool) + { + uint256 objectId = _createObject(enumerable, data); + if (enumerable.head == 0) { + _setHead(enumerable, objectId); + } else { + _link(enumerable, enumerable.head, objectId); + _setTail(enumerable, objectId); + } + } + + /** + * @dev Remove the Object denoted by `id` from the List. + */ + function remove(Enumerable storage enumerable, uint256 id) + public + { + Object memory removeObject = enumerable.objects[id]; + if (enumerable.head == id && enumerable.tail == id) { + _setHead(enumerable, 0); + _setTail(enumerable, 0); + } + else if (enumerable.head == id) { + _setHead(enumerable, removeObject.next); + enumerable.objects[removeObject.next].prev = 0; + } + else if (enumerable.tail == id) { + _setTail(enumerable, removeObject.prev); + enumerable.objects[removeObject.prev].next = 0; + } + else { + _link(enumerable, removeObject.prev, removeObject.next); + } + delete enumerable.objects[removeObject.id]; + emit ObjectRemoved(id); + } + + /** + * @dev Returns true if at least one Object matches `data` in the data field. + * TODO: What happens with address(0) as data? + */ + function contains(Enumerable storage enumerable, address data) + public + view + returns (bool) + { + Object memory object = enumerable.objects[enumerable.head]; + while (object.data != data) { + object = enumerable.objects[object.next]; + } + return object.data == data; + } + + /** + * @dev Returns the length of the enumerable. + */ + function length(Enumerable storage enumerable) + public + view + returns (uint256) + { + uint256 count = 0; + if (enumerable.head != 0){ + count += 1; + Object memory object = enumerable.objects[enumerable.head]; + while (object.id != enumerable.tail) { + count += 1; + object = enumerable.objects[object.next]; + } + } + return count; + } + + /** + * @dev Returns all the data fields in the enumerable, in an array ordered from head to tail. + */ + function enumerate(Enumerable storage enumerable) + public + view + returns (address[] memory) + { + uint256 count = length(enumerable); + address[] memory data = new address[](count); + Object memory object = enumerable.objects[enumerable.head]; + for (uint256 i = 0; i < count; i += 1){ + data[i] = enumerable.objects[object.id].data; + object = enumerable.objects[object.next]; + } + return data; + } + + /** + * @dev Internal function to update the Head pointer. + */ + function _setHead(Enumerable storage enumerable, uint256 id) + internal + { + enumerable.head = id; + emit NewHead(id); + } + + /** + * @dev Internal function to update the Tail pointer. + */ + function _setTail(Enumerable storage enumerable, uint256 id) + internal + { + enumerable.tail = id; + emit NewTail(id); + } + + /** + * @dev Internal function to create an unlinked Object. + */ + function _createObject(Enumerable storage enumerable, address data) + internal + returns (uint256) + { + enumerable.idCounter += 1; + uint256 newId = enumerable.idCounter; + Object memory object = Object( + newId, + 0, + 0, + data + ); + enumerable.objects[object.id] = object; + emit ObjectCreated( + object.id, + object.data + ); + return object.id; + } + + /** + * @dev Internal function to link an Object to another. + */ + function _link(Enumerable storage enumerable, uint256 _prevId, uint256 _nextId) + internal + { + enumerable.objects[_prevId].next = _nextId; + enumerable.objects[_nextId].prev = _prevId; + emit ObjectsLinked(_prevId, _nextId); + } +}