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.
176 lines
6.4 KiB
176 lines
6.4 KiB
module.exports = BufferBuilder;
|
|
|
|
function BufferBuilder(initialCapacity) {
|
|
var buffer = Buffer.isBuffer(initialCapacity) ? initialCapacity : new Buffer(initialCapacity || 512);
|
|
this.buffers = [buffer];
|
|
|
|
this.writeIndex = 0;
|
|
this.length = 0;
|
|
}
|
|
|
|
/* Append a (subsequence of a) Buffer */
|
|
BufferBuilder.prototype.appendBuffer = function(source) {
|
|
if (source.length === 0) return this;
|
|
|
|
var tail = this.buffers[this.buffers.length-1];
|
|
|
|
var spaceInCurrent = tail.length - this.writeIndex;
|
|
if (source.length <= spaceInCurrent) {
|
|
// We can fit the whole thing in the current buffer
|
|
source.copy(tail, this.writeIndex);
|
|
this.writeIndex += source.length;
|
|
} else {
|
|
// Copy as much as we can into the current buffer
|
|
if (spaceInCurrent) { // Buffer.copy does not handle the degenerate case well
|
|
source.copy(tail, this.writeIndex);//, start, start + spaceInCurrent);
|
|
}
|
|
// Fit the rest into a new buffer. Make sure it is at least as big as
|
|
// what we're being asked to add, and also follow our double-previous-buffer pattern.
|
|
var newBuf = new Buffer(Math.max(tail.length*2, source.length));
|
|
|
|
this.buffers.push(newBuf);
|
|
this.writeIndex = source.copy(newBuf, 0, spaceInCurrent);
|
|
}
|
|
|
|
this.length += source.length;
|
|
|
|
return this;
|
|
};
|
|
|
|
function makeAppender(encoder, size) {
|
|
return function(x) {
|
|
var buf = this.buffers[this.buffers.length-1];
|
|
if (this.writeIndex + size <= buf.length) {
|
|
encoder.call(buf, x, this.writeIndex, true);
|
|
this.writeIndex += size;
|
|
this.length += size;
|
|
} else {
|
|
var scratchBuffer = new Buffer(size);
|
|
encoder.call(scratchBuffer, x, 0, true);
|
|
this.appendBuffer(scratchBuffer);
|
|
}
|
|
|
|
return this;
|
|
};
|
|
}
|
|
|
|
BufferBuilder.prototype.appendUInt8 = makeAppender(Buffer.prototype.writeUInt8, 1);
|
|
BufferBuilder.prototype.appendUInt16LE = makeAppender(Buffer.prototype.writeUInt16LE, 2);
|
|
BufferBuilder.prototype.appendUInt16BE = makeAppender(Buffer.prototype.writeUInt16BE, 2);
|
|
BufferBuilder.prototype.appendUInt32LE = makeAppender(Buffer.prototype.writeUInt32LE, 4);
|
|
BufferBuilder.prototype.appendUInt32BE = makeAppender(Buffer.prototype.writeUInt32BE, 4);
|
|
BufferBuilder.prototype.appendInt8 = makeAppender(Buffer.prototype.writeInt8, 1);
|
|
BufferBuilder.prototype.appendInt16LE = makeAppender(Buffer.prototype.writeInt16LE, 2);
|
|
BufferBuilder.prototype.appendInt16BE = makeAppender(Buffer.prototype.writeInt16BE, 2);
|
|
BufferBuilder.prototype.appendInt32LE = makeAppender(Buffer.prototype.writeInt32LE, 4);
|
|
BufferBuilder.prototype.appendInt32BE = makeAppender(Buffer.prototype.writeInt32BE, 4);
|
|
BufferBuilder.prototype.appendFloatLE = makeAppender(Buffer.prototype.writeFloatLE, 4);
|
|
BufferBuilder.prototype.appendFloatBE = makeAppender(Buffer.prototype.writeFloatBE, 4);
|
|
BufferBuilder.prototype.appendDoubleLE = makeAppender(Buffer.prototype.writeDoubleLE, 8);
|
|
BufferBuilder.prototype.appendDoubleBE = makeAppender(Buffer.prototype.writeDoubleBE, 8);
|
|
|
|
BufferBuilder.prototype.appendString = function(str, encoding) {
|
|
return this.appendBuffer(new Buffer(str, encoding));
|
|
};
|
|
|
|
BufferBuilder.prototype.appendStringZero = function(str, encoding) {
|
|
return this.appendString(str + '\0', encoding);
|
|
}
|
|
|
|
BufferBuilder.prototype.appendFill = function(value, count) {
|
|
if (!count) return;
|
|
|
|
var tail = this.buffers[this.buffers.length-1];
|
|
|
|
var spaceInCurrent = tail.length - this.writeIndex;
|
|
if (count <= spaceInCurrent) {
|
|
// We can fit the whole thing in the current buffer
|
|
tail.fill(value, this.writeIndex, this.writeIndex + count);
|
|
this.writeIndex += count;
|
|
} else {
|
|
// Copy as much as we can into the current buffer
|
|
if (spaceInCurrent) { // does not handle the degenerate case well
|
|
tail.fill(value, this.writeIndex);
|
|
}
|
|
// Fit the rest into a new buffer. Make sure it is at least as big as
|
|
// what we're being asked to add, and also follow our double-previous-buffer pattern.
|
|
var newBuf = new Buffer(Math.max(tail.length*2, count));
|
|
var couldNotFit = count - spaceInCurrent;
|
|
newBuf.fill(value, 0, couldNotFit);
|
|
this.buffers.push(newBuf);
|
|
this.writeIndex = couldNotFit;
|
|
}
|
|
|
|
this.length += count;
|
|
|
|
return this;
|
|
};
|
|
|
|
/* Convert to a plain Buffer */
|
|
BufferBuilder.prototype.get = function() {
|
|
var concatted = new Buffer(this.length);
|
|
this.copy(concatted);
|
|
return concatted;
|
|
};
|
|
|
|
/* Copy into targetBuffer */
|
|
BufferBuilder.prototype.copy = function(targetBuffer, targetStart, sourceStart, sourceEnd) {
|
|
targetStart || (targetStart = 0);
|
|
sourceStart || (sourceStart = 0);
|
|
sourceEnd !== undefined || (sourceEnd = this.length);
|
|
|
|
// Validation. Besides making us fail nicely, this makes it so we can skip checks below.
|
|
if (targetStart < 0 || (targetStart>0 && targetStart >= targetBuffer.length)) {
|
|
throw new Error('targetStart is out of bounds');
|
|
}
|
|
if (sourceEnd < sourceStart) {
|
|
throw new Error('sourceEnd < sourceStart');
|
|
}
|
|
if (sourceStart < 0 || (sourceStart>0 && sourceStart >= this.length)) {
|
|
throw new Error('sourceStart is out of bounds');
|
|
}
|
|
if (sourceEnd > this.length) {
|
|
throw new Error('sourceEnd out of bounds');
|
|
}
|
|
|
|
sourceEnd = Math.min(sourceEnd, sourceStart + (targetBuffer.length-targetStart));
|
|
var targetWriteIdx = targetStart;
|
|
var readBuffer = 0;
|
|
|
|
// Skip through our buffers until we get to where the copying should start.
|
|
var copyLength = sourceEnd - sourceStart;
|
|
var skipped = 0;
|
|
while (skipped < sourceStart) {
|
|
var buffer = this.buffers[readBuffer];
|
|
if (buffer.length + skipped < targetStart) {
|
|
skipped += buffer.length;
|
|
} else {
|
|
// Do the first copy. This one is different from the others in that it
|
|
// does not start from the beginning of one of our internal buffers.
|
|
var copyStart = sourceStart - skipped;
|
|
var inThisBuffer = Math.min(copyLength, buffer.length - copyStart);
|
|
|
|
buffer.copy(targetBuffer, targetWriteIdx, copyStart, copyStart + inThisBuffer);
|
|
targetWriteIdx += inThisBuffer;
|
|
copyLength -= inThisBuffer;
|
|
readBuffer++;
|
|
break;
|
|
}
|
|
readBuffer++;
|
|
}
|
|
|
|
// Copy the rest. Note that we can't run off of our end because we validated the range up above
|
|
while (copyLength > 0) {
|
|
var buffer = this.buffers[readBuffer];
|
|
var toCopy = Math.min(buffer.length, copyLength);
|
|
|
|
buffer.copy(targetBuffer, targetWriteIdx, 0, toCopy);
|
|
copyLength -= toCopy;
|
|
targetWriteIdx += toCopy;
|
|
readBuffer++;
|
|
}
|
|
|
|
// Return how many bytes were copied
|
|
return sourceEnd - sourceStart;
|
|
};
|