buffer: improve read/write speed with assert

Improved assert check order of execution and added additional checks on
parameters to ensure no bad values make it through (e.g. negative offset
values).
This commit is contained in:
Trevor Norris 2013-01-10 16:59:39 -08:00 committed by isaacs
parent 22b84e6216
commit 7393740c7b

View File

@ -616,35 +616,28 @@ Buffer.prototype.asciiWrite = function(string, offset) {
return this.write(string, offset, 'ascii');
};
/*
* Need to make sure that buffer isn't trying to write out of bounds.
* This check is far too slow internally for fast buffers.
*/
function checkOffset(offset, ext, length) {
if ((offset % 1) !== 0 || offset < 0)
throw new RangeError('offset is not uint');
if (offset + ext > length)
throw new RangeError('Trying to access beyond buffer length');
}
Buffer.prototype.readUInt8 = function(offset, noAssert) {
var buffer = this;
if (!noAssert) {
assert.ok(offset !== undefined && offset !== null,
'missing offset');
assert.ok(offset < buffer.length,
'Trying to read beyond buffer length');
}
return buffer[offset];
if (!noAssert)
checkOffset(offset, 1, this.length);
return this[offset];
};
function readUInt16(buffer, offset, isBigEndian, noAssert) {
function readUInt16(buffer, offset, isBigEndian) {
var val = 0;
if (!noAssert) {
assert.ok(typeof (isBigEndian) === 'boolean',
'missing or invalid endian');
assert.ok(offset !== undefined && offset !== null,
'missing offset');
assert.ok(offset + 1 < buffer.length,
'Trying to read beyond buffer length');
}
if (isBigEndian) {
val = buffer[offset] << 8;
val |= buffer[offset + 1];
@ -656,28 +649,24 @@ function readUInt16(buffer, offset, isBigEndian, noAssert) {
return val;
}
Buffer.prototype.readUInt16LE = function(offset, noAssert) {
if (!noAssert)
checkOffset(offset, 2, this.length);
return readUInt16(this, offset, false, noAssert);
};
Buffer.prototype.readUInt16BE = function(offset, noAssert) {
if (!noAssert)
checkOffset(offset, 2, this.length);
return readUInt16(this, offset, true, noAssert);
};
function readUInt32(buffer, offset, isBigEndian, noAssert) {
var val = 0;
if (!noAssert) {
assert.ok(typeof (isBigEndian) === 'boolean',
'missing or invalid endian');
assert.ok(offset !== undefined && offset !== null,
'missing offset');
assert.ok(offset + 3 < buffer.length,
'Trying to read beyond buffer length');
}
if (isBigEndian) {
val = buffer[offset + 1] << 16;
val |= buffer[offset + 2] << 8;
@ -693,11 +682,17 @@ function readUInt32(buffer, offset, isBigEndian, noAssert) {
return val;
}
Buffer.prototype.readUInt32LE = function(offset, noAssert) {
if (!noAssert)
checkOffset(offset, 4, this.length);
return readUInt32(this, offset, false, noAssert);
};
Buffer.prototype.readUInt32BE = function(offset, noAssert) {
if (!noAssert)
checkOffset(offset, 4, this.length);
return readUInt32(this, offset, true, noAssert);
};
@ -747,113 +742,82 @@ Buffer.prototype.readUInt32BE = function(offset, noAssert) {
* (0x007f + 1) * -1
* (0x0080) * -1
*/
Buffer.prototype.readInt8 = function(offset, noAssert) {
var buffer = this;
var neg;
if (!noAssert) {
assert.ok(offset !== undefined && offset !== null,
'missing offset');
assert.ok(offset < buffer.length,
'Trying to read beyond buffer length');
}
neg = buffer[offset] & 0x80;
if (!neg) {
return (buffer[offset]);
}
return ((0xff - buffer[offset] + 1) * -1);
if (!noAssert)
checkOffset(offset, 1, this.length);
if (!(this[offset] & 0x80))
return (this[offset]);
return ((0xff - this[offset] + 1) * -1);
};
function readInt16(buffer, offset, isBigEndian, noAssert) {
var neg, val;
if (!noAssert) {
assert.ok(typeof (isBigEndian) === 'boolean',
'missing or invalid endian');
function readInt16(buffer, offset, isBigEndian) {
var val = readUInt16(buffer, offset, isBigEndian);
assert.ok(offset !== undefined && offset !== null,
'missing offset');
assert.ok(offset + 1 < buffer.length,
'Trying to read beyond buffer length');
}
val = readUInt16(buffer, offset, isBigEndian, noAssert);
neg = val & 0x8000;
if (!neg) {
if (!(val & 0x8000))
return val;
}
return (0xffff - val + 1) * -1;
}
Buffer.prototype.readInt16LE = function(offset, noAssert) {
return readInt16(this, offset, false, noAssert);
if (!noAssert)
checkOffset(offset, 2, this.length);
return readInt16(this, offset, false);
};
Buffer.prototype.readInt16BE = function(offset, noAssert) {
return readInt16(this, offset, true, noAssert);
if (!noAssert)
checkOffset(offset, 2, this.length);
return readInt16(this, offset, true);
};
function readInt32(buffer, offset, isBigEndian, noAssert) {
var neg, val;
if (!noAssert) {
assert.ok(typeof (isBigEndian) === 'boolean',
'missing or invalid endian');
function readInt32(buffer, offset, isBigEndian) {
var val = readUInt32(buffer, offset, isBigEndian);
assert.ok(offset !== undefined && offset !== null,
'missing offset');
assert.ok(offset + 3 < buffer.length,
'Trying to read beyond buffer length');
}
val = readUInt32(buffer, offset, isBigEndian, noAssert);
neg = val & 0x80000000;
if (!neg) {
if (!(val & 0x80000000))
return (val);
}
return (0xffffffff - val + 1) * -1;
}
Buffer.prototype.readInt32LE = function(offset, noAssert) {
return readInt32(this, offset, false, noAssert);
if (!noAssert)
checkOffset(offset, 2, this.length);
return readInt32(this, offset, false);
};
Buffer.prototype.readInt32BE = function(offset, noAssert) {
return readInt32(this, offset, true, noAssert);
if (!noAssert)
checkOffset(offset, 2, this.length);
return readInt32(this, offset, true);
};
function checkOffset(offset, ext, length) {
if ((offset % 1) !== 0 || offset < 0)
throw new RangeError('offset is not uint');
if (offset + ext > length)
throw new RangeError('Trying to access beyond buffer length');
}
Buffer.prototype.readFloatLE = function(offset, noAssert) {
if (!noAssert)
checkOffset(offset, 4, this.length);
return this.parent.readFloatLE(this.offset + offset, !!noAssert);
};
Buffer.prototype.readFloatBE = function(offset, noAssert) {
if (!noAssert)
checkOffset(offset, 4, this.length);
return this.parent.readFloatBE(this.offset + offset, !!noAssert);
};
Buffer.prototype.readDoubleLE = function(offset, noAssert) {
if (!noAssert)
checkOffset(offset, 8, this.length);
return this.parent.readDoubleLE(this.offset + offset, !!noAssert);
};
Buffer.prototype.readDoubleBE = function(offset, noAssert) {
if (!noAssert)
checkOffset(offset, 8, this.length);
@ -861,63 +825,24 @@ Buffer.prototype.readDoubleBE = function(offset, noAssert) {
};
/*
* We have to make sure that the value is a valid integer. This means that it is
* non-negative. It has no fractional component and that it does not exceed the
* maximum allowed value.
*
* value The number to check for validity
*
* max The maximum value
*/
function verifuint(value, max) {
assert.ok(typeof (value) == 'number',
'cannot write a non-number as a number');
assert.ok(value >= 0,
'specified a negative value for writing an unsigned value');
assert.ok(value <= max, 'value is larger than maximum value for type');
assert.ok(Math.floor(value) === value, 'value has a fractional component');
function checkInt(buffer, value, offset, ext, max, min) {
if ((value % 1) !== 0 || value > max || value < min)
throw TypeError("value is out of bounds");
if ((offset % 1) !== 0 || offset < 0)
throw TypeError("offset is not uint");
if (offset + ext > buffer.length || buffer.length + offset < 0)
throw RangeError("Trying to write outside buffer length");
}
Buffer.prototype.writeUInt8 = function(value, offset, noAssert) {
var buffer = this;
if (!noAssert) {
assert.ok(value !== undefined && value !== null,
'missing value');
assert.ok(offset !== undefined && offset !== null,
'missing offset');
assert.ok(offset < buffer.length,
'trying to write beyond buffer length');
verifuint(value, 0xff);
}
buffer[offset] = value;
if (!noAssert)
checkInt(this, value, offset, 1, 0xff, 0);
this[offset] = value;
};
function writeUInt16(buffer, value, offset, isBigEndian, noAssert) {
if (!noAssert) {
assert.ok(value !== undefined && value !== null,
'missing value');
assert.ok(typeof (isBigEndian) === 'boolean',
'missing or invalid endian');
assert.ok(offset !== undefined && offset !== null,
'missing offset');
assert.ok(offset + 1 < buffer.length,
'trying to write beyond buffer length');
verifuint(value, 0xffff);
}
function writeUInt16(buffer, value, offset, isBigEndian) {
if (isBigEndian) {
buffer[offset] = (value & 0xff00) >>> 8;
buffer[offset + 1] = value & 0x00ff;
@ -927,31 +852,22 @@ function writeUInt16(buffer, value, offset, isBigEndian, noAssert) {
}
}
Buffer.prototype.writeUInt16LE = function(value, offset, noAssert) {
writeUInt16(this, value, offset, false, noAssert);
if (!noAssert)
checkInt(this, value, offset, 2, 0xffff, 0);
writeUInt16(this, value, offset, false);
};
Buffer.prototype.writeUInt16BE = function(value, offset, noAssert) {
writeUInt16(this, value, offset, true, noAssert);
if (!noAssert)
checkInt(this, value, offset, 2, 0xffff, 0);
writeUInt16(this, value, offset, true);
};
function writeUInt32(buffer, value, offset, isBigEndian, noAssert) {
if (!noAssert) {
assert.ok(value !== undefined && value !== null,
'missing value');
assert.ok(typeof (isBigEndian) === 'boolean',
'missing or invalid endian');
assert.ok(offset !== undefined && offset !== null,
'missing offset');
assert.ok(offset + 3 < buffer.length,
'trying to write beyond buffer length');
verifuint(value, 0xffffffff);
}
function writeUInt32(buffer, value, offset, isBigEndian) {
if (isBigEndian) {
buffer[offset] = (value >>> 24) & 0xff;
buffer[offset + 1] = (value >>> 16) & 0xff;
@ -965,12 +881,18 @@ function writeUInt32(buffer, value, offset, isBigEndian, noAssert) {
}
}
Buffer.prototype.writeUInt32LE = function(value, offset, noAssert) {
writeUInt32(this, value, offset, false, noAssert);
if (!noAssert)
checkInt(this, value, offset, 4, 0xffffffff, 0);
writeUInt32(this, value, offset, false);
};
Buffer.prototype.writeUInt32BE = function(value, offset, noAssert) {
writeUInt32(this, value, offset, true, noAssert);
if (!noAssert)
checkInt(this, value, offset, 4, 0xffffffff, 0);
writeUInt32(this, value, offset, true);
};
@ -1011,92 +933,67 @@ Buffer.prototype.writeUInt32BE = function(value, offset, noAssert) {
* hacky, but it should work and get the job done which is our goal here.
*/
/*
* A series of checks to make sure we actually have a signed 32-bit number
*/
function verifsint(value, max, min) {
assert.ok(typeof (value) == 'number',
'cannot write a non-number as a number');
assert.ok(value <= max, 'value larger than maximum allowed value');
assert.ok(value >= min, 'value smaller than minimum allowed value');
assert.ok(Math.floor(value) === value, 'value has a fractional component');
}
Buffer.prototype.writeInt8 = function(value, offset, noAssert) {
var buffer = this;
if (!noAssert) {
verifsint(value, 0x7f, -0x80);
}
if (value >= 0) {
buffer.writeUInt8(value, offset, noAssert);
} else {
buffer.writeUInt8(0xff + value + 1, offset, noAssert);
}
if (!noAssert)
checkInt(this, value, offset, 1, 0x7f, -0x80);
if (value < 0) value = 0xff + value + 1;
this[offset] = value;
};
function writeInt16(buffer, value, offset, isBigEndian, noAssert) {
if (!noAssert) {
verifsint(value, 0x7fff, -0x8000);
}
if (value >= 0) {
writeUInt16(buffer, value, offset, isBigEndian, noAssert);
} else {
writeUInt16(buffer, 0xffff + value + 1, offset, isBigEndian, noAssert);
}
}
Buffer.prototype.writeInt16LE = function(value, offset, noAssert) {
writeInt16(this, value, offset, false, noAssert);
if (!noAssert)
checkInt(this, value, offset, 2, 0x7fff, -0x8000);
if (value < 0) value = 0xffff + value + 1;
writeUInt16(this, value, offset, false);
};
Buffer.prototype.writeInt16BE = function(value, offset, noAssert) {
writeInt16(this, value, offset, true, noAssert);
if (!noAssert)
checkInt(this, value, offset, 2, 0x7fff, -0x8000);
if (value < 0) value = 0xffff + value + 1;
writeUInt16(this, value, offset, true);
};
function writeInt32(buffer, value, offset, isBigEndian, noAssert) {
if (!noAssert) {
verifsint(value, 0x7fffffff, -0x80000000);
}
if (value >= 0) {
writeUInt32(buffer, value, offset, isBigEndian, noAssert);
} else {
writeUInt32(buffer, 0xffffffff + value + 1, offset, isBigEndian, noAssert);
}
}
Buffer.prototype.writeInt32LE = function(value, offset, noAssert) {
writeInt32(this, value, offset, false, noAssert);
if (!noAssert)
checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000);
if (value < 0) value = 0xffffffff + value + 1;
writeUInt32(this, value, offset, false);
};
Buffer.prototype.writeInt32BE = function(value, offset, noAssert) {
writeInt32(this, value, offset, true, noAssert);
if (!noAssert)
checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000);
if (value < 0) value = 0xffffffff + value + 1;
writeUInt32(this, value, offset, true);
};
Buffer.prototype.writeFloatLE = function(value, offset, noAssert) {
if (!noAssert)
checkOffset(offset, 4, this.length);
this.parent.writeFloatLE(value, this.offset + offset, !!noAssert);
};
Buffer.prototype.writeFloatBE = function(value, offset, noAssert) {
if (!noAssert)
checkOffset(offset, 4, this.length);
this.parent.writeFloatBE(value, this.offset + offset, !!noAssert);
};
Buffer.prototype.writeDoubleLE = function(value, offset, noAssert) {
if (!noAssert)
checkOffset(offset, 8, this.length);
this.parent.writeDoubleLE(value, this.offset + offset, !!noAssert);
};
Buffer.prototype.writeDoubleBE = function(value, offset, noAssert) {
if (!noAssert)
checkOffset(offset, 8, this.length);