diff --git a/contracts/DLL.sol b/contracts/DLL.sol index 6408774..c5d343a 100644 --- a/contracts/DLL.sol +++ b/contracts/DLL.sol @@ -20,7 +20,7 @@ library DLL { function contains(Data storage self, uint _curr) public view returns (bool) { if (isEmpty(self) || _curr == NULL_NODE_ID) { return false; - } + } bool isSingleNode = (getStart(self) == _curr) && (getEnd(self) == _curr); bool isNullNode = (getNext(self, _curr) == NULL_NODE_ID) && (getPrev(self, _curr) == NULL_NODE_ID); @@ -44,7 +44,7 @@ library DLL { } /** - @dev Inserts a new node between _prev and _next. When inserting a node already existing in + @dev Inserts a new node between _prev and _next. When inserting a node already existing in the list it will be automatically removed from the old position. @param _prev the node which _new will be inserted after @param _curr the id of the new node being inserted @@ -53,11 +53,11 @@ library DLL { function insert(Data storage self, uint _prev, uint _curr, uint _next) public { require(_curr != NULL_NODE_ID); - remove(self, _curr); - require(_prev == NULL_NODE_ID || contains(self, _prev)); require(_next == NULL_NODE_ID || contains(self, _next)); + remove(self, _curr); + require(getNext(self, _prev) == _next); require(getPrev(self, _next) == _prev); @@ -68,6 +68,25 @@ library DLL { self.dll[_next].prev = _curr; } + function replace(Data storage self, uint _curr, uint _prev) public { + require(contains(self, _curr)); + if (self.dll[_curr].prev == _prev) { + return; + } + + require(_prev == NULL_NODE_ID || contains(self, _prev)); + + remove(self, _curr); + + uint next = self.dll[_prev].next; + + self.dll[_curr].prev = _prev; + self.dll[_curr].next = next; + + self.dll[_prev].next = _curr; + self.dll[next].prev = _curr; + } + function remove(Data storage self, uint _curr) public { if (!contains(self, _curr)) { return; diff --git a/contracts/TestDLL.sol b/contracts/TestDLL.sol index 8b47739..453b3ea 100644 --- a/contracts/TestDLL.sol +++ b/contracts/TestDLL.sol @@ -10,7 +10,7 @@ contract TestDLL { function isEmpty() public view returns (bool) { return dll.isEmpty(); } - + function contains(uint _curr) public view returns (bool) { return dll.contains(_curr); } @@ -18,15 +18,15 @@ contract TestDLL { function getNext(uint _curr) public view returns (uint) { return dll.getNext(_curr); } - + function getPrev(uint _curr) public view returns (uint) { return dll.getPrev(_curr); } - + function getStart() public view returns (uint) { return dll.getStart(); } - + function getEnd() public view returns (uint) { return dll.getEnd(); } @@ -38,4 +38,8 @@ contract TestDLL { function remove(uint _curr) public { dll.remove(_curr); } + + function replace(uint _curr, uint _prev) public { + dll.replace(_curr, _prev); + } } diff --git a/test/replace.js b/test/replace.js new file mode 100644 index 0000000..6acdee7 --- /dev/null +++ b/test/replace.js @@ -0,0 +1,52 @@ +const TestDLL = artifacts.require('TestDLL.sol'); +const utils = require('./utils.js'); + +contract('DLL', () => { + describe('Function: replace', () => { + it('Should throw error in non-existent node case', async () => { + const proxy = await TestDLL.deployed(); + + await proxy.insert(0, 1, 0); + try { + await proxy.replace(2, 0); + } catch (err) { + assert(utils.isEVMException(err), err.toString()); + + return; + } + + assert(false, 'replace a non-existent node'); + }); + + it('Should ignore replacing self-positioning node', async () => { + const proxy = await TestDLL.deployed(); + + await proxy.insert(0, 1, 0); + await proxy.replace(1, 0); + + const start = await proxy.getStart(); + const end = await proxy.getEnd(); + + assert.strictEqual(start.toString(10), '1', 'expected start to be 1'); + assert.strictEqual(end.toString(10), '1', 'expected end to be 1'); + }); + + it('Should replace a node', async () => { + const proxy = await TestDLL.deployed(); + + await proxy.insert(1, 2, 0); + await proxy.insert(2, 3, 0); + await proxy.replace(3, 1); + + const start = await proxy.getStart(); + const end = await proxy.getEnd(); + const next = await proxy.getNext(1); + const nextAfter = await proxy.getNext(3); + + assert.strictEqual(start.toString(10), '1', 'expected start to be 1'); + assert.strictEqual(end.toString(10), '2', 'expected end to be 2'); + assert.strictEqual(next.toString(10), '3', 'expected next to be 3'); + assert.strictEqual(nextAfter.toString(10), '2', 'expected next after 3 to be 2'); + }); + }); +});