You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
202 lines
5.1 KiB
202 lines
5.1 KiB
'use strict';
|
|
|
|
var _ = require('lodash');
|
|
var $ = require('../util/preconditions');
|
|
var BufferUtil = require('../util/buffer');
|
|
var BN = require('../crypto/bn');
|
|
|
|
var BufferReader = function BufferReader(buf) {
|
|
if (!(this instanceof BufferReader)) {
|
|
return new BufferReader(buf);
|
|
}
|
|
if (_.isUndefined(buf)) {
|
|
return;
|
|
}
|
|
if (Buffer.isBuffer(buf)) {
|
|
this.set({
|
|
buf: buf
|
|
});
|
|
} else if (_.isString(buf)) {
|
|
var b = new Buffer(buf, 'hex');
|
|
if (b.length * 2 != buf.length)
|
|
throw new TypeError('Invalid hex string');
|
|
|
|
this.set({
|
|
buf: b,
|
|
});
|
|
} else if (_.isObject(buf)) {
|
|
var obj = buf;
|
|
this.set(obj);
|
|
} else {
|
|
throw new TypeError('Unrecognized argument for BufferReader');
|
|
}
|
|
};
|
|
|
|
BufferReader.prototype.set = function(obj) {
|
|
this.buf = obj.buf || this.buf || undefined;
|
|
this.pos = obj.pos || this.pos || 0;
|
|
return this;
|
|
};
|
|
|
|
BufferReader.prototype.eof = function() {
|
|
return this.pos >= this.buf.length;
|
|
};
|
|
|
|
BufferReader.prototype.finished = BufferReader.prototype.eof;
|
|
|
|
BufferReader.prototype.read = function(len) {
|
|
$.checkArgument(!_.isUndefined(len), 'Must specify a length');
|
|
var buf = this.buf.slice(this.pos, this.pos + len);
|
|
this.pos = this.pos + len;
|
|
return buf;
|
|
};
|
|
|
|
BufferReader.prototype.readAll = function() {
|
|
var buf = this.buf.slice(this.pos, this.buf.length);
|
|
this.pos = this.buf.length;
|
|
return buf;
|
|
};
|
|
|
|
BufferReader.prototype.readUInt8 = function() {
|
|
var val = this.buf.readUInt8(this.pos);
|
|
this.pos = this.pos + 1;
|
|
return val;
|
|
};
|
|
|
|
BufferReader.prototype.readUInt16BE = function() {
|
|
var val = this.buf.readUInt16BE(this.pos);
|
|
this.pos = this.pos + 2;
|
|
return val;
|
|
};
|
|
|
|
BufferReader.prototype.readUInt16LE = function() {
|
|
var val = this.buf.readUInt16LE(this.pos);
|
|
this.pos = this.pos + 2;
|
|
return val;
|
|
};
|
|
|
|
BufferReader.prototype.readUInt32BE = function() {
|
|
var val = this.buf.readUInt32BE(this.pos);
|
|
this.pos = this.pos + 4;
|
|
return val;
|
|
};
|
|
|
|
BufferReader.prototype.readUInt32LE = function() {
|
|
var val = this.buf.readUInt32LE(this.pos);
|
|
this.pos = this.pos + 4;
|
|
return val;
|
|
};
|
|
|
|
BufferReader.prototype.readInt32LE = function() {
|
|
var val = this.buf.readInt32LE(this.pos);
|
|
this.pos = this.pos + 4;
|
|
return val;
|
|
};
|
|
|
|
BufferReader.prototype.readUInt64BEBN = function() {
|
|
var buf = this.buf.slice(this.pos, this.pos + 8);
|
|
var bn = BN.fromBuffer(buf);
|
|
this.pos = this.pos + 8;
|
|
return bn;
|
|
};
|
|
|
|
BufferReader.prototype.readUInt64LEBN = function() {
|
|
var second = this.buf.readUInt32LE(this.pos);
|
|
var first = this.buf.readUInt32LE(this.pos + 4);
|
|
var combined = (first * 0x100000000) + second;
|
|
// Instantiating an instance of BN with a number is faster than with an
|
|
// array or string. However, the maximum safe number for a double precision
|
|
// floating point is 2 ^ 52 - 1 (0x1fffffffffffff), thus we can safely use
|
|
// non-floating point numbers less than this amount (52 bits). And in the case
|
|
// that the number is larger, we can instatiate an instance of BN by passing
|
|
// an array from the buffer (slower) and specifying the endianness.
|
|
var bn;
|
|
if (combined <= 0x1fffffffffffff) {
|
|
bn = new BN(combined);
|
|
} else {
|
|
var data = Array.prototype.slice.call(this.buf, this.pos, this.pos + 8);
|
|
bn = new BN(data, 10, 'le');
|
|
}
|
|
this.pos = this.pos + 8;
|
|
return bn;
|
|
};
|
|
|
|
BufferReader.prototype.readVarintNum = function() {
|
|
var first = this.readUInt8();
|
|
switch (first) {
|
|
case 0xFD:
|
|
return this.readUInt16LE();
|
|
case 0xFE:
|
|
return this.readUInt32LE();
|
|
case 0xFF:
|
|
var bn = this.readUInt64LEBN();
|
|
var n = bn.toNumber();
|
|
if (n <= Math.pow(2, 53)) {
|
|
return n;
|
|
} else {
|
|
throw new Error('number too large to retain precision - use readVarintBN');
|
|
}
|
|
break;
|
|
default:
|
|
return first;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* reads a length prepended buffer
|
|
*/
|
|
BufferReader.prototype.readVarLengthBuffer = function() {
|
|
var len = this.readVarintNum();
|
|
var buf = this.read(len);
|
|
$.checkState(buf.length === len, 'Invalid length while reading varlength buffer. ' +
|
|
'Expected to read: ' + len + ' and read ' + buf.length);
|
|
return buf;
|
|
};
|
|
|
|
BufferReader.prototype.readVarintBuf = function() {
|
|
var first = this.buf.readUInt8(this.pos);
|
|
switch (first) {
|
|
case 0xFD:
|
|
return this.read(1 + 2);
|
|
case 0xFE:
|
|
return this.read(1 + 4);
|
|
case 0xFF:
|
|
return this.read(1 + 8);
|
|
default:
|
|
return this.read(1);
|
|
}
|
|
};
|
|
|
|
BufferReader.prototype.readVarintBN = function() {
|
|
var first = this.readUInt8();
|
|
switch (first) {
|
|
case 0xFD:
|
|
return new BN(this.readUInt16LE());
|
|
case 0xFE:
|
|
return new BN(this.readUInt32LE());
|
|
case 0xFF:
|
|
return this.readUInt64LEBN();
|
|
default:
|
|
return new BN(first);
|
|
}
|
|
};
|
|
|
|
BufferReader.prototype.reverse = function() {
|
|
var buf = new Buffer(this.buf.length);
|
|
for (var i = 0; i < buf.length; i++) {
|
|
buf[i] = this.buf[this.buf.length - 1 - i];
|
|
}
|
|
this.buf = buf;
|
|
return this;
|
|
};
|
|
|
|
BufferReader.prototype.readReverse = function(len) {
|
|
if (_.isUndefined(len)) {
|
|
len = this.buf.length;
|
|
}
|
|
var buf = this.buf.slice(this.pos, this.pos + len);
|
|
this.pos = this.pos + len;
|
|
return BufferUtil.reverse(buf);
|
|
};
|
|
|
|
module.exports = BufferReader;
|
|
|