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.
175 lines
6.4 KiB
175 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; |
|
};
|
|
|