Skip to content

Commit f48abd3

Browse files
authored
Merge pull request #1533 from OttoAllmendinger/add-buffer-writer
Add BufferWriter and BufferReader class
2 parents c06c372 + 5679a4b commit f48abd3

File tree

8 files changed

+852
-398
lines changed

8 files changed

+852
-398
lines changed

src/block.js

Lines changed: 21 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -26,43 +26,24 @@ class Block {
2626
}
2727
static fromBuffer(buffer) {
2828
if (buffer.length < 80) throw new Error('Buffer too small (< 80 bytes)');
29-
let offset = 0;
30-
const readSlice = n => {
31-
offset += n;
32-
return buffer.slice(offset - n, offset);
33-
};
34-
const readUInt32 = () => {
35-
const i = buffer.readUInt32LE(offset);
36-
offset += 4;
37-
return i;
38-
};
39-
const readInt32 = () => {
40-
const i = buffer.readInt32LE(offset);
41-
offset += 4;
42-
return i;
43-
};
29+
const bufferReader = new bufferutils_1.BufferReader(buffer);
4430
const block = new Block();
45-
block.version = readInt32();
46-
block.prevHash = readSlice(32);
47-
block.merkleRoot = readSlice(32);
48-
block.timestamp = readUInt32();
49-
block.bits = readUInt32();
50-
block.nonce = readUInt32();
31+
block.version = bufferReader.readInt32();
32+
block.prevHash = bufferReader.readSlice(32);
33+
block.merkleRoot = bufferReader.readSlice(32);
34+
block.timestamp = bufferReader.readUInt32();
35+
block.bits = bufferReader.readUInt32();
36+
block.nonce = bufferReader.readUInt32();
5137
if (buffer.length === 80) return block;
52-
const readVarInt = () => {
53-
const vi = varuint.decode(buffer, offset);
54-
offset += varuint.decode.bytes;
55-
return vi;
56-
};
5738
const readTransaction = () => {
5839
const tx = transaction_1.Transaction.fromBuffer(
59-
buffer.slice(offset),
40+
bufferReader.buffer.slice(bufferReader.offset),
6041
true,
6142
);
62-
offset += tx.byteLength();
43+
bufferReader.offset += tx.byteLength();
6344
return tx;
6445
};
65-
const nTransactions = readVarInt();
46+
const nTransactions = bufferReader.readVarInt();
6647
block.transactions = [];
6748
for (let i = 0; i < nTransactions; ++i) {
6849
const tx = readTransaction();
@@ -154,32 +135,20 @@ class Block {
154135
// TODO: buffer, offset compatibility
155136
toBuffer(headersOnly) {
156137
const buffer = Buffer.allocUnsafe(this.byteLength(headersOnly));
157-
let offset = 0;
158-
const writeSlice = slice => {
159-
slice.copy(buffer, offset);
160-
offset += slice.length;
161-
};
162-
const writeInt32 = i => {
163-
buffer.writeInt32LE(i, offset);
164-
offset += 4;
165-
};
166-
const writeUInt32 = i => {
167-
buffer.writeUInt32LE(i, offset);
168-
offset += 4;
169-
};
170-
writeInt32(this.version);
171-
writeSlice(this.prevHash);
172-
writeSlice(this.merkleRoot);
173-
writeUInt32(this.timestamp);
174-
writeUInt32(this.bits);
175-
writeUInt32(this.nonce);
138+
const bufferWriter = new bufferutils_1.BufferWriter(buffer);
139+
bufferWriter.writeInt32(this.version);
140+
bufferWriter.writeSlice(this.prevHash);
141+
bufferWriter.writeSlice(this.merkleRoot);
142+
bufferWriter.writeUInt32(this.timestamp);
143+
bufferWriter.writeUInt32(this.bits);
144+
bufferWriter.writeUInt32(this.nonce);
176145
if (headersOnly || !this.transactions) return buffer;
177-
varuint.encode(this.transactions.length, buffer, offset);
178-
offset += varuint.encode.bytes;
146+
varuint.encode(this.transactions.length, buffer, bufferWriter.offset);
147+
bufferWriter.offset += varuint.encode.bytes;
179148
this.transactions.forEach(tx => {
180149
const txSize = tx.byteLength(); // TODO: extract from toBuffer?
181-
tx.toBuffer(buffer, offset);
182-
offset += txSize;
150+
tx.toBuffer(buffer, bufferWriter.offset);
151+
bufferWriter.offset += txSize;
183152
});
184153
return buffer;
185154
}

src/bufferutils.js

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
'use strict';
22
Object.defineProperty(exports, '__esModule', { value: true });
3+
const types = require('./types');
4+
const typeforce = require('typeforce');
5+
const varuint = require('varuint-bitcoin');
36
// https://github.com/feross/buffer/blob/master/index.js#L1127
47
function verifuint(value, max) {
58
if (typeof value !== 'number')
@@ -38,3 +41,97 @@ function reverseBuffer(buffer) {
3841
return buffer;
3942
}
4043
exports.reverseBuffer = reverseBuffer;
44+
/**
45+
* Helper class for serialization of bitcoin data types into a pre-allocated buffer.
46+
*/
47+
class BufferWriter {
48+
constructor(buffer, offset = 0) {
49+
this.buffer = buffer;
50+
this.offset = offset;
51+
typeforce(types.tuple(types.Buffer, types.UInt32), [buffer, offset]);
52+
}
53+
writeUInt8(i) {
54+
this.offset = this.buffer.writeUInt8(i, this.offset);
55+
}
56+
writeInt32(i) {
57+
this.offset = this.buffer.writeInt32LE(i, this.offset);
58+
}
59+
writeUInt32(i) {
60+
this.offset = this.buffer.writeUInt32LE(i, this.offset);
61+
}
62+
writeUInt64(i) {
63+
this.offset = writeUInt64LE(this.buffer, i, this.offset);
64+
}
65+
writeVarInt(i) {
66+
varuint.encode(i, this.buffer, this.offset);
67+
this.offset += varuint.encode.bytes;
68+
}
69+
writeSlice(slice) {
70+
if (this.buffer.length < this.offset + slice.length) {
71+
throw new Error('Cannot write slice out of bounds');
72+
}
73+
this.offset += slice.copy(this.buffer, this.offset);
74+
}
75+
writeVarSlice(slice) {
76+
this.writeVarInt(slice.length);
77+
this.writeSlice(slice);
78+
}
79+
writeVector(vector) {
80+
this.writeVarInt(vector.length);
81+
vector.forEach(buf => this.writeVarSlice(buf));
82+
}
83+
}
84+
exports.BufferWriter = BufferWriter;
85+
/**
86+
* Helper class for reading of bitcoin data types from a buffer.
87+
*/
88+
class BufferReader {
89+
constructor(buffer, offset = 0) {
90+
this.buffer = buffer;
91+
this.offset = offset;
92+
typeforce(types.tuple(types.Buffer, types.UInt32), [buffer, offset]);
93+
}
94+
readUInt8() {
95+
const result = this.buffer.readUInt8(this.offset);
96+
this.offset++;
97+
return result;
98+
}
99+
readInt32() {
100+
const result = this.buffer.readInt32LE(this.offset);
101+
this.offset += 4;
102+
return result;
103+
}
104+
readUInt32() {
105+
const result = this.buffer.readUInt32LE(this.offset);
106+
this.offset += 4;
107+
return result;
108+
}
109+
readUInt64() {
110+
const result = readUInt64LE(this.buffer, this.offset);
111+
this.offset += 8;
112+
return result;
113+
}
114+
readVarInt() {
115+
const vi = varuint.decode(this.buffer, this.offset);
116+
this.offset += varuint.decode.bytes;
117+
return vi;
118+
}
119+
readSlice(n) {
120+
if (this.buffer.length < this.offset + n) {
121+
throw new Error('Cannot read slice out of bounds');
122+
}
123+
const result = this.buffer.slice(this.offset, this.offset + n);
124+
this.offset += n;
125+
return result;
126+
}
127+
readVarSlice() {
128+
return this.readSlice(this.readVarInt());
129+
}
130+
readVector() {
131+
const count = this.readVarInt();
132+
const vector = [];
133+
for (let i = 0; i < count; i++) vector.push(this.readVarSlice());
134+
return vector;
135+
}
136+
}
137+
exports.BufferReader = BufferReader;

0 commit comments

Comments
 (0)