| function FrameParser() { |
| this.readFrameBytes = 0 |
| this.frameBodyLength = 0 |
| this.frameBody = null |
| this.cursor = 0 |
| this.chunk = null |
| } |
| |
| FrameParser.prototype.push = function(chunk) { |
| if (this.chunk) { |
| throw new Error('Must consume pending frames before pushing more chunks') |
| } |
| |
| this.chunk = chunk |
| } |
| |
| FrameParser.prototype.nextFrame = function() { |
| if (!this.chunk) { |
| return null |
| } |
| |
| for (var len = this.chunk.length; this.cursor < len;) { |
| if (this.readFrameBytes < 4) { |
| this.frameBodyLength += |
| (this.chunk[this.cursor] << (this.readFrameBytes * 8)) >>> 0 |
| this.cursor += 1 |
| this.readFrameBytes += 1 |
| } |
| else { |
| var bytesLeft = len - this.cursor |
| |
| if (bytesLeft >= this.frameBodyLength) { |
| var completeBody |
| if (this.frameBody) { |
| completeBody = Buffer.concat([ |
| this.frameBody |
| , this.chunk.slice(this.cursor, this.cursor + this.frameBodyLength) |
| ]) |
| } |
| else { |
| completeBody = this.chunk.slice(this.cursor, |
| this.cursor + this.frameBodyLength) |
| } |
| |
| this.cursor += this.frameBodyLength |
| this.frameBodyLength = this.readFrameBytes = 0 |
| this.frameBody = null |
| |
| return completeBody |
| } |
| else { |
| // @todo Consider/benchmark continuation frames to prevent |
| // potential Buffer thrashing. |
| if (this.frameBody) { |
| this.frameBody = |
| Buffer.concat([this.frameBody, this.chunk.slice(this.cursor, len)]) |
| } |
| else { |
| this.frameBody = this.chunk.slice(this.cursor, len) |
| } |
| |
| this.frameBodyLength -= bytesLeft |
| this.readFrameBytes += bytesLeft |
| this.cursor = len |
| } |
| } |
| } |
| |
| this.cursor = 0 |
| this.chunk = null |
| |
| return null |
| } |
| |
| module.exports = FrameParser |