Commit 1a14b811 authored by Karl Floersch's avatar Karl Floersch

Add missing features to RingBuffer

parent 3ec7c358
...@@ -50,6 +50,28 @@ library Lib_TimeboundRingBuffer { ...@@ -50,6 +50,28 @@ library Lib_TimeboundRingBuffer {
_self.context = makeContext(uint32(length+1), _extraData); _self.context = makeContext(uint32(length+1), _extraData);
} }
function push2(
TimeboundRingBuffer storage _self,
bytes32 _ele1,
bytes32 _ele2,
bytes28 _extraData
)
internal
{
uint length = _getLength(_self.context);
uint maxSize = _self.maxSize;
if (length + 1 >= maxSize) {
if (block.timestamp < _self.firstElementTimestamp + _self.timeout) {
// Because this is a push2 we need to at least increment by 2
_self.maxSize += _self.maxSizeIncrementAmount > 1 ? _self.maxSizeIncrementAmount : 2;
maxSize = _self.maxSize;
}
}
_self.elements[length % maxSize] = _ele1;
_self.elements[(length + 1) % maxSize] = _ele2;
_self.context = makeContext(uint32(length+2), _extraData);
}
function makeContext( function makeContext(
uint32 _length, uint32 _length,
bytes28 _extraData bytes28 _extraData
...@@ -60,7 +82,7 @@ library Lib_TimeboundRingBuffer { ...@@ -60,7 +82,7 @@ library Lib_TimeboundRingBuffer {
bytes32 bytes32
) )
{ {
return bytes32(bytes4(_length)); return bytes32(_extraData) | bytes32(uint256(_length));
} }
function getLength( function getLength(
...@@ -84,7 +106,21 @@ library Lib_TimeboundRingBuffer { ...@@ -84,7 +106,21 @@ library Lib_TimeboundRingBuffer {
uint32 uint32
) )
{ {
return uint32(bytes4(context)); bytes32 lengthMask = 0x00000000000000000000000000000000000000000000000000000000ffffffff;
return uint32(uint256(context & lengthMask));
}
function getExtraData(
TimeboundRingBuffer storage _self
)
internal
view
returns(
bytes28
)
{
bytes32 extraDataMask = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000;
return bytes28(_self.context & extraDataMask);
} }
function get( function get(
......
...@@ -30,6 +30,10 @@ contract TestLib_TimeboundRingBuffer { ...@@ -30,6 +30,10 @@ contract TestLib_TimeboundRingBuffer {
list.push(_ele, _extraData); list.push(_ele, _extraData);
} }
function push2(bytes32 _ele1, bytes32 _ele2, bytes28 _extraData) public {
list.push2(_ele1, _ele2, _extraData);
}
function get(uint32 index) public view returns(bytes32) { function get(uint32 index) public view returns(bytes32) {
return list.get(index); return list.get(index);
} }
...@@ -38,6 +42,10 @@ contract TestLib_TimeboundRingBuffer { ...@@ -38,6 +42,10 @@ contract TestLib_TimeboundRingBuffer {
return list.getLength(); return list.getLength();
} }
function getExtraData() public view returns(bytes28) {
return list.getExtraData();
}
function getMaxSize() public view returns(uint32) { function getMaxSize() public view returns(uint32) {
return list.maxSize; return list.maxSize;
} }
......
...@@ -9,12 +9,10 @@ import { Contract, Signer } from 'ethers' ...@@ -9,12 +9,10 @@ import { Contract, Signer } from 'ethers'
import { import {
NON_NULL_BYTES32, NON_NULL_BYTES32,
makeHexString, makeHexString,
fromHexString,
getHexSlice,
increaseEthTime increaseEthTime
} from '../../../helpers' } from '../../../helpers'
const numToBytes32 = (num: Number) => { const numToBytes32 = (num: Number): string => {
if (num < 0 || num > 255) { if (num < 0 || num > 255) {
throw new Error('Unsupported number.') throw new Error('Unsupported number.')
} }
...@@ -22,7 +20,7 @@ const numToBytes32 = (num: Number) => { ...@@ -22,7 +20,7 @@ const numToBytes32 = (num: Number) => {
return '0x' + '00'.repeat(31) + strNum return '0x' + '00'.repeat(31) + strNum
} }
describe.only('Lib_TimeboundRingBuffer', () => { describe('Lib_TimeboundRingBuffer', () => {
let signer: Signer let signer: Signer
before(async () => { before(async () => {
;[signer] = await ethers.getSigners() ;[signer] = await ethers.getSigners()
...@@ -32,7 +30,7 @@ describe.only('Lib_TimeboundRingBuffer', () => { ...@@ -32,7 +30,7 @@ describe.only('Lib_TimeboundRingBuffer', () => {
const NON_NULL_BYTES28 = makeHexString('01', 28) const NON_NULL_BYTES28 = makeHexString('01', 28)
describe('[0,1,2,3] with no timeout', () => { describe('push with no timeout', () => {
beforeEach(async () => { beforeEach(async () => {
Lib_TimeboundRingBuffer = await ( Lib_TimeboundRingBuffer = await (
await ethers.getContractFactory('TestLib_TimeboundRingBuffer') await ethers.getContractFactory('TestLib_TimeboundRingBuffer')
...@@ -57,27 +55,48 @@ describe.only('Lib_TimeboundRingBuffer', () => { ...@@ -57,27 +55,48 @@ describe.only('Lib_TimeboundRingBuffer', () => {
}) })
}) })
describe('get()', () => {
before(async () => {
Lib_TimeboundRingBuffer = await (
await ethers.getContractFactory('TestLib_TimeboundRingBuffer')
).deploy(2, 1, 10_000)
await increaseEthTime(ethers.provider, 20_000)
for (let i = 0; i < 4; i++) {
await Lib_TimeboundRingBuffer.push(numToBytes32(i), NON_NULL_BYTES28)
}
})
it('should revert if index is too old', async () => {
await expect(Lib_TimeboundRingBuffer.get(0)).to.be.revertedWith("Index too old & has been overridden.")
})
it('should revert if index is greater than length', async () => {
await expect(Lib_TimeboundRingBuffer.get(5)).to.be.revertedWith("Index too large.")
})
})
describe('push with timeout', () => { describe('push with timeout', () => {
const startSize = 2 const startSize = 2
const pushNum = (num: Number) => Lib_TimeboundRingBuffer.push(numToBytes32(num), NON_NULL_BYTES28)
beforeEach(async () => { beforeEach(async () => {
Lib_TimeboundRingBuffer = await ( Lib_TimeboundRingBuffer = await (
await ethers.getContractFactory('TestLib_TimeboundRingBuffer') await ethers.getContractFactory('TestLib_TimeboundRingBuffer')
).deploy(startSize, 1, 10_000) ).deploy(startSize, 1, 10_000)
for (let i = 0; i < startSize; i++) { for (let i = 0; i < startSize; i++) {
await Lib_TimeboundRingBuffer.push(numToBytes32(i), NON_NULL_BYTES28) await pushNum(i)
} }
}) })
const pushNum = (num: Number) => Lib_TimeboundRingBuffer.push(numToBytes32(num), NON_NULL_BYTES28)
const pushJunk = () => Lib_TimeboundRingBuffer.push(NON_NULL_BYTES32, NON_NULL_BYTES28) const pushJunk = () => Lib_TimeboundRingBuffer.push(NON_NULL_BYTES32, NON_NULL_BYTES28)
it('should push a single value which extends the array', async () => { it('should push a single value which extends the array', async () => {
await Lib_TimeboundRingBuffer.push(numToBytes32(2), NON_NULL_BYTES28) await pushNum(2)
const increasedSize = startSize + 1 const increasedSize = startSize + 1
expect(await Lib_TimeboundRingBuffer.getMaxSize()).to.equal(increasedSize) expect(await Lib_TimeboundRingBuffer.getMaxSize()).to.equal(increasedSize)
await increaseEthTime(ethers.provider, 20_000) await increaseEthTime(ethers.provider, 20_000)
await Lib_TimeboundRingBuffer.push(numToBytes32(3), NON_NULL_BYTES28) await pushNum(3)
expect(await Lib_TimeboundRingBuffer.getMaxSize()).to.equal(increasedSize) // Shouldn't increase the size this time expect(await Lib_TimeboundRingBuffer.getMaxSize()).to.equal(increasedSize) // Shouldn't increase the size this time
expect(await Lib_TimeboundRingBuffer.get(2)).to.equal(numToBytes32(2)) expect(await Lib_TimeboundRingBuffer.get(2)).to.equal(numToBytes32(2))
...@@ -96,4 +115,45 @@ describe.only('Lib_TimeboundRingBuffer', () => { ...@@ -96,4 +115,45 @@ describe.only('Lib_TimeboundRingBuffer', () => {
expect(await Lib_TimeboundRingBuffer.getMaxSize()).to.equal(increasedSize) expect(await Lib_TimeboundRingBuffer.getMaxSize()).to.equal(increasedSize)
}) })
}) })
describe('push2 with timeout', () => {
const startSize = 2
const push2Nums = (num1: Number, num2: Number) => Lib_TimeboundRingBuffer.push2(numToBytes32(num1), numToBytes32(num2), NON_NULL_BYTES28)
beforeEach(async () => {
Lib_TimeboundRingBuffer = await (
await ethers.getContractFactory('TestLib_TimeboundRingBuffer')
).deploy(startSize, 1, 10_000)
})
// it('should push two values which extends the array', async () => {
// })
it('should push a single value which extends the array', async () => {
await push2Nums(0, 1)
await push2Nums(2, 3)
const increasedSize = startSize + 2
expect(await Lib_TimeboundRingBuffer.getMaxSize()).to.equal(increasedSize)
await increaseEthTime(ethers.provider, 20_000)
await push2Nums(4, 5)
expect(await Lib_TimeboundRingBuffer.getMaxSize()).to.equal(increasedSize) // Shouldn't increase the size this time
for (let i = 2; i < 6; i++) {
expect(await Lib_TimeboundRingBuffer.get(i)).to.equal(numToBytes32(i))
}
})
})
describe('getExtraData', () => {
beforeEach(async () => {
Lib_TimeboundRingBuffer = await (
await ethers.getContractFactory('TestLib_TimeboundRingBuffer')
).deploy(2, 1, 10_000)
})
it('should return the expected extra data', async () => {
await Lib_TimeboundRingBuffer.push(NON_NULL_BYTES32, NON_NULL_BYTES28)
expect(await Lib_TimeboundRingBuffer.getExtraData()).to.equal(NON_NULL_BYTES28)
})
})
}) })
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment