-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathbitstream.ts
147 lines (123 loc) · 4.08 KB
/
bitstream.ts
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import { BigNumber } from "bignumber.js";
import BN = require("bn.js");
import abi = require("ethereumjs-abi");
export class Bitstream {
private data: string;
constructor(private initialData: string = "") {
this.data = initialData;
if (this.data.startsWith("0x")) {
this.data = this.data.slice(2);
}
}
public getData() {
if (this.data.length === 0) {
return "0x0";
} else {
return "0x" + this.data;
}
}
public getBytes32Array() {
if (this.data.length === 0) {
return [];
} else {
assert.equal(this.length() % 32, 0, "Bitstream not compatible with bytes32[]");
return this.data.match(/.{1,64}/g).map((element) => "0x" + element);
}
}
public addBigNumber(x: BigNumber, numBytes = 32, forceAppend = true) {
const formattedData = this.padString(x.toString(16), numBytes * 2);
return this.insert(formattedData, forceAppend);
}
public addBN(x: BN, numBytes = 32, forceAppend = true) {
const formattedData = this.padString(x.toString(16), numBytes * 2);
return this.insert(formattedData, forceAppend);
}
public addNumber(x: number, numBytes = 4, forceAppend = true) {
// Check if we need to encode this number as negative
if (x < 0) {
const encoded = abi.rawEncode(["int256"], [x.toString(10)]);
const hex = encoded.toString("hex").slice(-(numBytes * 2));
return this.addHex(hex, forceAppend);
} else {
return this.addBigNumber(new BigNumber(x), numBytes, forceAppend);
}
}
public addAddress(x: string, numBytes = 20, forceAppend = true) {
const formattedData = this.padString(x.slice(2), numBytes * 2);
return this.insert(formattedData, forceAppend);
}
public addHex(x: string, forceAppend = true) {
if (x.startsWith("0x")) {
return this.insert(x.slice(2), forceAppend);
} else {
return this.insert(x, forceAppend);
}
}
public addRawBytes(bs: string, forceAppend = true) {
return this.insert(bs.slice(2), forceAppend);
}
public extractUint8(offset: number) {
return parseInt(this.extractBytes1(offset).toString("hex"), 16);
}
public extractUint16(offset: number) {
return parseInt(this.extractBytesX(offset, 2).toString("hex"), 16);
}
public extractUint32(offset: number) {
return parseInt(this.extractBytesX(offset, 4).toString("hex"), 16);
}
public extractUint(offset: number) {
return new BigNumber(this.extractBytesX(offset, 32).toString("hex"), 16);
}
public extractAddress(offset: number) {
return "0x" + this.extractBytesX(offset, 20).toString("hex");
}
public extractBytes1(offset: number) {
return this.extractBytesX(offset, 1);
}
public extractBytes32(offset: number) {
return this.extractBytesX(offset, 32);
}
public extractBytesX(offset: number, length: number) {
const start = offset * 2;
const end = start + length * 2;
if (this.data.length < end) {
throw new Error("substring index out of range:[" + start + ", " + end + "]");
}
return new Buffer(this.data.substring(start, end), "hex");
}
// Returns the number of bytes of data
public length() {
return this.data.length / 2;
}
private insert(x: string, forceAppend: boolean) {
const offset = this.length();
if (!forceAppend) {
// Check if the data we're inserting is already available in the bitstream.
// If so, return the offset to the location.
let start = 0;
while (start !== -1) {
start = this.data.indexOf(x, start);
if (start !== -1) {
// The offset should be a multiple of 4 bytes
if ((start % (2 * 4)) === 0) {
// logDebug("++ Reused " + x + " at location " + start / 2);
return start / 2;
} else {
start++;
}
}
}
}
this.data += x;
return offset;
}
private padString(x: string, targetLength: number) {
if (x.length > targetLength) {
throw Error("0x" + x + " is too big to fit in the requested length (" + targetLength + ")");
}
while (x.length < targetLength) {
x = "0" + x;
}
return x;
}
}