| var Buffer = require('buffer').Buffer; |
| |
| function OffsetBuffer() { |
| this.offset = 0; |
| this.size = 0; |
| this.buffers = []; |
| } |
| module.exports = OffsetBuffer; |
| |
| OffsetBuffer.prototype.isEmpty = function isEmpty() { |
| return this.size === 0; |
| }; |
| |
| OffsetBuffer.prototype.clone = function clone(size) { |
| var r = new OffsetBuffer(); |
| r.offset = this.offset; |
| r.size = size; |
| r.buffers = this.buffers.slice(); |
| return r; |
| }; |
| |
| OffsetBuffer.prototype.toChunks = function toChunks() { |
| if (this.size === 0) |
| return []; |
| |
| // We are going to slice it anyway |
| if (this.offset !== 0) { |
| this.buffers[0] = this.buffers[0].slice(this.offset); |
| this.offset = 0; |
| } |
| |
| var chunks = [ ]; |
| var off = 0; |
| for (var i = 0; off <= this.size && i < this.buffers.length; i++) { |
| var buf = this.buffers[i]; |
| off += buf.length; |
| |
| // Slice off last buffer |
| if (off > this.size) { |
| buf = buf.slice(0, buf.length - (off - this.size)); |
| this.buffers[i] = buf; |
| } |
| |
| chunks.push(buf); |
| } |
| |
| // If some buffers were skipped - trim length |
| if (i < this.buffers.length) |
| this.buffers.length = i; |
| |
| return chunks; |
| }; |
| |
| OffsetBuffer.prototype.toString = function toString(enc) { |
| return this.toChunks().map(function(c) { |
| return c.toString(enc); |
| }).join(''); |
| }; |
| |
| OffsetBuffer.prototype.use = function use(buf, off, n) { |
| this.buffers = [ buf ]; |
| this.offset = off; |
| this.size = n; |
| }; |
| |
| OffsetBuffer.prototype.push = function push(data) { |
| // Ignore empty writes |
| if (data.length === 0) |
| return; |
| |
| this.size += data.length; |
| this.buffers.push(data); |
| }; |
| |
| OffsetBuffer.prototype.has = function has(n) { |
| return this.size >= n; |
| }; |
| |
| OffsetBuffer.prototype.skip = function skip(n) { |
| if (this.size === 0) |
| return; |
| |
| this.size -= n; |
| |
| // Fast case, skip bytes in a first buffer |
| if (this.offset + n < this.buffers[0].length) { |
| this.offset += n; |
| return; |
| } |
| |
| var left = n - (this.buffers[0].length - this.offset); |
| this.offset = 0; |
| |
| for (var shift = 1; left > 0 && shift < this.buffers.length; shift++) { |
| var buf = this.buffers[shift]; |
| if (buf.length > left) { |
| this.offset = left; |
| break; |
| } |
| left -= buf.length; |
| } |
| this.buffers = this.buffers.slice(shift); |
| }; |
| |
| OffsetBuffer.prototype.copy = function copy(target, targetOff, off, n) { |
| if (this.size === 0) |
| return; |
| if (off !== 0) |
| throw new Error('Unsupported offset in .copy()'); |
| |
| var toff = targetOff; |
| var first = this.buffers[0]; |
| var toCopy = Math.min(n, first.length - this.offset); |
| first.copy(target, toff, this.offset, this.offset + toCopy); |
| |
| toff += toCopy; |
| var left = n - toCopy; |
| for (var i = 1; left > 0 && i < this.buffers.length; i++) { |
| var buf = this.buffers[i]; |
| var toCopy = Math.min(left, buf.length); |
| |
| buf.copy(target, toff, 0, toCopy); |
| |
| toff += toCopy; |
| left -= toCopy; |
| } |
| }; |
| |
| OffsetBuffer.prototype.take = function take(n) { |
| if (n === 0) |
| return new Buffer(0); |
| |
| this.size -= n; |
| |
| // Fast cases |
| var first = this.buffers[0].length - this.offset; |
| if (first === n) { |
| var r = this.buffers.shift(); |
| if (this.offset !== 0) { |
| r = r.slice(this.offset); |
| this.offset = 0; |
| } |
| return r; |
| } else if (first > n) { |
| var r = this.buffers[0].slice(this.offset, this.offset + n); |
| this.offset += n; |
| return r; |
| } |
| |
| // Allocate and fill buffer |
| var out = new Buffer(n); |
| var toOff = 0; |
| var startOff = this.offset; |
| for (var i = 0; toOff !== n && i < this.buffers.length; i++) { |
| var buf = this.buffers[i]; |
| var toCopy = Math.min(buf.length - startOff, n - toOff); |
| |
| buf.copy(out, toOff, startOff, startOff + toCopy); |
| if (startOff + toCopy < buf.length) { |
| this.offset = startOff + toCopy; |
| break; |
| } else { |
| toOff += toCopy; |
| startOff = 0; |
| } |
| } |
| |
| this.buffers = this.buffers.slice(i); |
| if (this.buffers.length === 0) |
| this.offset = 0; |
| |
| return out; |
| }; |
| |
| OffsetBuffer.prototype.peekUInt8 = function peekUInt8() { |
| return this.buffers[0][this.offset]; |
| }; |
| |
| OffsetBuffer.prototype.readUInt8 = function readUInt8() { |
| this.size -= 1; |
| var first = this.buffers[0]; |
| var r = first[this.offset]; |
| if (++this.offset === first.length) { |
| this.offset = 0; |
| this.buffers.shift(); |
| } |
| |
| return r; |
| }; |
| |
| OffsetBuffer.prototype.readUInt16LE = function readUInt16LE() { |
| var first = this.buffers[0]; |
| this.size -= 2; |
| |
| var r; |
| var shift; |
| |
| // Fast case - first buffer has all bytes |
| if (first.length - this.offset >= 2) { |
| r = first.readUInt16LE(this.offset); |
| shift = 0; |
| this.offset += 2; |
| |
| // One byte here - one byte there |
| } else { |
| r = first[this.offset] | (this.buffers[1][0] << 8); |
| shift = 1; |
| this.offset = 1; |
| } |
| |
| if (this.offset === this.buffers[shift].length) { |
| this.offset = 0; |
| shift++; |
| } |
| if (shift !== 0) |
| this.buffers = this.buffers.slice(shift); |
| |
| return r; |
| }; |
| |
| OffsetBuffer.prototype.readUInt24LE = function readUInt24LE() { |
| var first = this.buffers[0]; |
| |
| var r; |
| var shift; |
| var firstHas = first.length - this.offset; |
| |
| // Fast case - first buffer has all bytes |
| if (firstHas >= 3) { |
| r = first.readUInt16LE(this.offset) | (first[this.offset + 2] << 16); |
| shift = 0; |
| this.offset += 3; |
| |
| // First buffer has 2 of 3 bytes |
| } else if (firstHas >= 2) { |
| r = first.readUInt16LE(this.offset) | (this.buffers[1][0] << 16); |
| shift = 1; |
| this.offset = 1; |
| |
| // Slow case: First buffer has 1 of 3 bytes |
| } else { |
| r = first[this.offset]; |
| this.offset = 0; |
| this.buffers.shift(); |
| this.size -= 1; |
| |
| r |= this.readUInt16LE() << 8; |
| return r; |
| } |
| |
| this.size -= 3; |
| if (this.offset === this.buffers[shift].length) { |
| this.offset = 0; |
| shift++; |
| } |
| if (shift !== 0) |
| this.buffers = this.buffers.slice(shift); |
| |
| return r; |
| }; |
| |
| OffsetBuffer.prototype.readUInt32LE = function readUInt32LE() { |
| var first = this.buffers[0]; |
| |
| var r; |
| var shift; |
| var firstHas = first.length - this.offset; |
| |
| // Fast case - first buffer has all bytes |
| if (firstHas >= 4) { |
| r = first.readUInt32LE(this.offset); |
| shift = 0; |
| this.offset += 4; |
| |
| // First buffer has 3 of 4 bytes |
| } else if (firstHas >= 3) { |
| r = (first.readUInt16LE(this.offset) | |
| (first[this.offset + 2] << 16)) + |
| (this.buffers[1][0] * 0x1000000); |
| shift = 1; |
| this.offset = 1; |
| |
| // Slow case: First buffer has 2 of 4 bytes |
| } else if (firstHas >= 2) { |
| r = first.readUInt16LE(this.offset); |
| this.offset = 0; |
| this.buffers.shift(); |
| this.size -= 2; |
| |
| r += this.readUInt16LE() * 0x10000; |
| return r; |
| |
| // Slow case: First buffer has 1 of 4 bytes |
| } else { |
| r = first[this.offset]; |
| this.offset = 0; |
| this.buffers.shift(); |
| this.size -= 1; |
| |
| r += this.readUInt24LE() * 0x100; |
| return r; |
| } |
| |
| this.size -= 4; |
| if (this.offset === this.buffers[shift].length) { |
| this.offset = 0; |
| shift++; |
| } |
| if (shift !== 0) |
| this.buffers = this.buffers.slice(shift); |
| |
| return r; |
| }; |
| |
| OffsetBuffer.prototype.readUInt16BE = function readUInt16BE() { |
| var r = this.readUInt16LE(); |
| |
| return ((r & 0xff) << 8) | (r >> 8); |
| }; |
| |
| OffsetBuffer.prototype.readUInt24BE = function readUInt24BE() { |
| var r = this.readUInt24LE(); |
| |
| return ((r & 0xff) << 16) | (((r >> 8) & 0xff) << 8) | (r >> 16); |
| }; |
| |
| OffsetBuffer.prototype.readUInt32BE = function readUInt32BE() { |
| var r = this.readUInt32LE(); |
| |
| return (((r & 0xff) << 24) | |
| (((r >>> 8) & 0xff) << 16) | |
| (((r >>> 16) & 0xff) << 8) | |
| (r >>> 24)) >>> 0; |
| }; |
| |
| // Signed number APIs |
| |
| function signedInt8(num) { |
| if (num >= 0x80) |
| return -(0xff ^ num) - 1; |
| else |
| return num; |
| } |
| |
| OffsetBuffer.prototype.peekInt8 = function peekInt8() { |
| return signedInt8(this.peekUInt8()); |
| }; |
| |
| OffsetBuffer.prototype.readInt8 = function readInt8() { |
| return signedInt8(this.readUInt8()); |
| }; |
| |
| function signedInt16(num) { |
| if (num >= 0x8000) |
| return -(0xffff ^ num) - 1; |
| else |
| return num; |
| } |
| |
| OffsetBuffer.prototype.readInt16BE = function readInt16BE() { |
| return signedInt16(this.readUInt16BE()); |
| }; |
| |
| OffsetBuffer.prototype.readInt16LE = function readInt16LE() { |
| return signedInt16(this.readUInt16LE()); |
| }; |
| |
| function signedInt24(num) { |
| if (num >= 0x800000) |
| return -(0xffffff ^ num) - 1; |
| else |
| return num; |
| } |
| |
| OffsetBuffer.prototype.readInt24BE = function readInt24BE() { |
| return signedInt24(this.readUInt24BE()); |
| }; |
| |
| OffsetBuffer.prototype.readInt24LE = function readInt24LE() { |
| return signedInt24(this.readUInt24LE()); |
| }; |
| |
| function signedInt32(num) { |
| if (num >= 0x80000000) |
| return -(0xffffffff ^ num) - 1; |
| else |
| return num; |
| } |
| |
| OffsetBuffer.prototype.readInt32BE = function readInt32BE() { |
| return signedInt32(this.readUInt32BE()); |
| }; |
| |
| OffsetBuffer.prototype.readInt32LE = function readInt32LE() { |
| return signedInt32(this.readUInt32LE()); |
| }; |