diff options
author | Greg Hysen <greg.hysen@gmail.com> | 2018-11-26 12:05:41 +0800 |
---|---|---|
committer | Greg Hysen <greg.hysen@gmail.com> | 2018-11-29 08:38:11 +0800 |
commit | 8f73f53c95d8ba887558863b8b726a2b3f5b7e2b (patch) | |
tree | d819b2412b59e484fffea54701f5441108ce122d /packages/utils/src/abi_encoder/calldata | |
parent | 9a51af46ee4a35b3d1ce2fcdc6f561aa68307cf0 (diff) | |
download | dexon-0x-contracts-8f73f53c95d8ba887558863b8b726a2b3f5b7e2b.tar dexon-0x-contracts-8f73f53c95d8ba887558863b8b726a2b3f5b7e2b.tar.gz dexon-0x-contracts-8f73f53c95d8ba887558863b8b726a2b3f5b7e2b.tar.bz2 dexon-0x-contracts-8f73f53c95d8ba887558863b8b726a2b3f5b7e2b.tar.lz dexon-0x-contracts-8f73f53c95d8ba887558863b8b726a2b3f5b7e2b.tar.xz dexon-0x-contracts-8f73f53c95d8ba887558863b8b726a2b3f5b7e2b.tar.zst dexon-0x-contracts-8f73f53c95d8ba887558863b8b726a2b3f5b7e2b.zip |
Moved calldata iterator logic into its own iterator clas
Diffstat (limited to 'packages/utils/src/abi_encoder/calldata')
-rw-r--r-- | packages/utils/src/abi_encoder/calldata/calldata.ts | 56 | ||||
-rw-r--r-- | packages/utils/src/abi_encoder/calldata/iterator.ts | 94 |
2 files changed, 104 insertions, 46 deletions
diff --git a/packages/utils/src/abi_encoder/calldata/calldata.ts b/packages/utils/src/abi_encoder/calldata/calldata.ts index b2396ee8f..dd9d47def 100644 --- a/packages/utils/src/abi_encoder/calldata/calldata.ts +++ b/packages/utils/src/abi_encoder/calldata/calldata.ts @@ -2,11 +2,11 @@ import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; import * as Constants from '../utils/constants'; -import { Queue } from '../utils/queue'; import { EncodingRules } from '../utils/rules'; import * as CalldataBlocks from './blocks'; import { CalldataBlock } from './calldata_block'; +import { CalldataIterator, ReverseCalldataIterator } from './iterator'; export class Calldata { private readonly _rules: EncodingRules; @@ -14,41 +14,6 @@ export class Calldata { private _sizeInBytes: number; private _root: CalldataBlock | undefined; - private static _createQueue(block: CalldataBlock): Queue<CalldataBlock> { - const blockQueue = new Queue<CalldataBlock>(); - - // Base Case - if (!(block instanceof CalldataBlocks.Set)) { - blockQueue.pushBack(block); - return blockQueue; - } - - // This is a Member Block - const memberBlock = block; - _.eachRight(memberBlock.getMembers(), (member: CalldataBlock) => { - if (member instanceof CalldataBlocks.Set) { - blockQueue.mergeFront(Calldata._createQueue(member)); - } else { - blockQueue.pushFront(member); - } - }); - - // Children - _.each(memberBlock.getMembers(), (member: CalldataBlock) => { - if (member instanceof CalldataBlocks.Pointer && member.getAlias() === undefined) { - const dependency = member.getDependency(); - if (dependency instanceof CalldataBlocks.Set) { - blockQueue.mergeBack(Calldata._createQueue(dependency)); - } else { - blockQueue.pushBack(dependency); - } - } - }); - - blockQueue.pushFront(memberBlock); - return blockQueue; - } - public constructor(rules: EncodingRules) { this._rules = rules; this._selector = ''; @@ -65,9 +30,9 @@ export class Calldata { // 1. Create a queue of subtrees by hash // Note that they are ordered the same as - const subtreeQueue = Calldata._createQueue(this._root); + const iterator = new ReverseCalldataIterator(this._root); let block: CalldataBlock | undefined; - for (block = subtreeQueue.popBack(); block !== undefined; block = subtreeQueue.popBack()) { + while (block = iterator.next()) { if (block instanceof CalldataBlocks.Pointer) { const dependencyBlockHashBuf = block.getDependency().computeHash(); const dependencyBlockHash = ethUtil.bufferToHex(dependencyBlockHashBuf); @@ -97,10 +62,10 @@ export class Calldata { this.optimize(); } - const offsetQueue = Calldata._createQueue(this._root); + const iterator = new CalldataIterator(this._root); let block: CalldataBlock | undefined; let offset = 0; - for (block = offsetQueue.popFront(); block !== undefined; block = offsetQueue.popFront()) { + while (block = iterator.next()) { block.setOffset(offset); offset += block.getSizeInBytes(); } @@ -136,13 +101,12 @@ export class Calldata { throw new Error('expected root'); } - const valueQueue = Calldata._createQueue(this._root); + const iterator = new CalldataIterator(this._root); let block: CalldataBlock | undefined; let offset = 0; - const functionBlock = valueQueue.peekFront(); - const functionName: string = functionBlock === undefined ? '' : functionBlock.getName(); - for (block = valueQueue.popFront(); block !== undefined; block = valueQueue.popFront()) { + const functionName: string = this._root.getName(); + while (block = iterator.next()) { // Process each block 1 word at a time const size = block.getSizeInBytes(); const name = block.getName(); @@ -209,10 +173,10 @@ export class Calldata { throw new Error('expected root'); } - const valueQueue = Calldata._createQueue(this._root); + const iterator = new CalldataIterator(this._root); const valueBufs: Buffer[] = [selectorBuffer]; let block: CalldataBlock | undefined; - for (block = valueQueue.popFront(); block !== undefined; block = valueQueue.popFront()) { + while (block = iterator.next()) { valueBufs.push(block.toBuffer()); } diff --git a/packages/utils/src/abi_encoder/calldata/iterator.ts b/packages/utils/src/abi_encoder/calldata/iterator.ts new file mode 100644 index 000000000..3e3367e10 --- /dev/null +++ b/packages/utils/src/abi_encoder/calldata/iterator.ts @@ -0,0 +1,94 @@ +/* tslint:disable max-classes-per-file */ +import * as _ from 'lodash'; + +import { Queue } from '../utils/queue'; + +import * as CalldataBlocks from './blocks'; +import { CalldataBlock } from './calldata_block'; + +/** + * Iterator class for Calldata Blocks. Blocks follows the order + * they should be put into calldata that is passed to he EVM. + * + * Example #1: + * Let root = Set { + * Blob{} A, + * Pointer { + * Blob{} a + * } B, + * Blob{} C + * } + * It will iterate as follows: [A, B, C, B.a] + * + * Example #2: + * Let root = Set { + * Blob{} A, + * Pointer { + * Blob{} a + * Pointer { + * Blob{} b + * } + * } B, + * Pointer { + * Blob{} c + * } C + * } + * It will iterate as follows: [A, B, C, B.a, B.b, C.c] + */ +abstract class BaseIterator { + protected readonly _root: CalldataBlock; + protected readonly _queue: Queue<CalldataBlock>; + + private static _createQueue(block: CalldataBlock): Queue<CalldataBlock> { + const queue = new Queue<CalldataBlock>(); + // Base case + if (!(block instanceof CalldataBlocks.Set)) { + queue.pushBack(block); + return queue; + } + // This is a set; add members + const set = block; + _.eachRight(set.getMembers(), (member: CalldataBlock) => { + queue.mergeFront(BaseIterator._createQueue(member)); + }); + // Add children + _.each(set.getMembers(), (member: CalldataBlock) => { + // Traverse child if it is a unique pointer. + // A pointer that is an alias for another pointer is ignored. + if (member instanceof CalldataBlocks.Pointer && member.getAlias() === undefined) { + const dependency = member.getDependency(); + queue.mergeBack(BaseIterator._createQueue(dependency)); + } + }); + // Put set block at the front of the queue + queue.pushFront(set); + return queue; + } + + public constructor(root: CalldataBlock) { + this._root = root; + this._queue = BaseIterator._createQueue(root); + } + + public abstract next(): CalldataBlock | undefined; +} + +export class CalldataIterator extends BaseIterator { + public constructor(root: CalldataBlock) { + super(root); + } + + public next(): CalldataBlock | undefined { + return this._queue.popFront(); + } +} + +export class ReverseCalldataIterator extends BaseIterator { + public constructor(root: CalldataBlock) { + super(root); + } + + public next(): CalldataBlock | undefined { + return this._queue.popBack(); + } +} |