aboutsummaryrefslogtreecommitdiffstats
path: root/packages/utils/src/abi_encoder/calldata/iterator.ts
blob: 8e2b16a5ae58207c82ff2da02be1ff14fb3b7b79 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
/* 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 implements Iterable<CalldataBlock> {
    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 [Symbol.iterator](): { next: () => IteratorResult<CalldataBlock> } {
        return {
            next: () => {
                const nextBlock = this.nextBlock();
                if (nextBlock !== undefined) {
                    return {
                        value: nextBlock,
                        done: false,
                    };
                }
                return {
                    done: true,
                    value: new CalldataBlocks.Blob('', '', '', new Buffer('')),
                };
            },
        };
    }

    public abstract nextBlock(): CalldataBlock | undefined;
}

export class CalldataIterator extends BaseIterator {
    public constructor(root: CalldataBlock) {
        super(root);
    }

    public nextBlock(): CalldataBlock | undefined {
        return this._queue.popFront();
    }
}

export class ReverseCalldataIterator extends BaseIterator {
    public constructor(root: CalldataBlock) {
        super(root);
    }

    public nextBlock(): CalldataBlock | undefined {
        return this._queue.popBack();
    }
}