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.
		
		
		
		
		
			
		
			
				
					398 lines
				
				10 KiB
			
		
		
			
		
	
	
					398 lines
				
				10 KiB
			| 
											2 years ago
										 | var Chainsaw = require('chainsaw'); | ||
|  | var EventEmitter = require('events').EventEmitter; | ||
|  | var Buffers = require('buffers'); | ||
|  | var Vars = require('./lib/vars.js'); | ||
|  | var Stream = require('stream').Stream; | ||
|  | 
 | ||
|  | exports = module.exports = function (bufOrEm, eventName) { | ||
|  |     if (Buffer.isBuffer(bufOrEm)) { | ||
|  |         return exports.parse(bufOrEm); | ||
|  |     } | ||
|  |      | ||
|  |     var s = exports.stream(); | ||
|  |     if (bufOrEm && bufOrEm.pipe) { | ||
|  |         bufOrEm.pipe(s); | ||
|  |     } | ||
|  |     else if (bufOrEm) { | ||
|  |         bufOrEm.on(eventName || 'data', function (buf) { | ||
|  |             s.write(buf); | ||
|  |         }); | ||
|  |          | ||
|  |         bufOrEm.on('end', function () { | ||
|  |             s.end(); | ||
|  |         }); | ||
|  |     } | ||
|  |     return s; | ||
|  | }; | ||
|  | 
 | ||
|  | exports.stream = function (input) { | ||
|  |     if (input) return exports.apply(null, arguments); | ||
|  |      | ||
|  |     var pending = null; | ||
|  |     function getBytes (bytes, cb, skip) { | ||
|  |         pending = { | ||
|  |             bytes : bytes, | ||
|  |             skip : skip, | ||
|  |             cb : function (buf) { | ||
|  |                 pending = null; | ||
|  |                 cb(buf); | ||
|  |             }, | ||
|  |         }; | ||
|  |         dispatch(); | ||
|  |     } | ||
|  |      | ||
|  |     var offset = null; | ||
|  |     function dispatch () { | ||
|  |         if (!pending) { | ||
|  |             if (caughtEnd) done = true; | ||
|  |             return; | ||
|  |         } | ||
|  |         if (typeof pending === 'function') { | ||
|  |             pending(); | ||
|  |         } | ||
|  |         else { | ||
|  |             var bytes = offset + pending.bytes; | ||
|  |              | ||
|  |             if (buffers.length >= bytes) { | ||
|  |                 var buf; | ||
|  |                 if (offset == null) { | ||
|  |                     buf = buffers.splice(0, bytes); | ||
|  |                     if (!pending.skip) { | ||
|  |                         buf = buf.slice(); | ||
|  |                     } | ||
|  |                 } | ||
|  |                 else { | ||
|  |                     if (!pending.skip) { | ||
|  |                         buf = buffers.slice(offset, bytes); | ||
|  |                     } | ||
|  |                     offset = bytes; | ||
|  |                 } | ||
|  |                  | ||
|  |                 if (pending.skip) { | ||
|  |                     pending.cb(); | ||
|  |                 } | ||
|  |                 else { | ||
|  |                     pending.cb(buf); | ||
|  |                 } | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  |      | ||
|  |     function builder (saw) { | ||
|  |         function next () { if (!done) saw.next() } | ||
|  |          | ||
|  |         var self = words(function (bytes, cb) { | ||
|  |             return function (name) { | ||
|  |                 getBytes(bytes, function (buf) { | ||
|  |                     vars.set(name, cb(buf)); | ||
|  |                     next(); | ||
|  |                 }); | ||
|  |             }; | ||
|  |         }); | ||
|  |          | ||
|  |         self.tap = function (cb) { | ||
|  |             saw.nest(cb, vars.store); | ||
|  |         }; | ||
|  |          | ||
|  |         self.into = function (key, cb) { | ||
|  |             if (!vars.get(key)) vars.set(key, {}); | ||
|  |             var parent = vars; | ||
|  |             vars = Vars(parent.get(key)); | ||
|  |              | ||
|  |             saw.nest(function () { | ||
|  |                 cb.apply(this, arguments); | ||
|  |                 this.tap(function () { | ||
|  |                     vars = parent; | ||
|  |                 }); | ||
|  |             }, vars.store); | ||
|  |         }; | ||
|  |          | ||
|  |         self.flush = function () { | ||
|  |             vars.store = {}; | ||
|  |             next(); | ||
|  |         }; | ||
|  |          | ||
|  |         self.loop = function (cb) { | ||
|  |             var end = false; | ||
|  |              | ||
|  |             saw.nest(false, function loop () { | ||
|  |                 this.vars = vars.store; | ||
|  |                 cb.call(this, function () { | ||
|  |                     end = true; | ||
|  |                     next(); | ||
|  |                 }, vars.store); | ||
|  |                 this.tap(function () { | ||
|  |                     if (end) saw.next() | ||
|  |                     else loop.call(this) | ||
|  |                 }.bind(this)); | ||
|  |             }, vars.store); | ||
|  |         }; | ||
|  |          | ||
|  |         self.buffer = function (name, bytes) { | ||
|  |             if (typeof bytes === 'string') { | ||
|  |                 bytes = vars.get(bytes); | ||
|  |             } | ||
|  |              | ||
|  |             getBytes(bytes, function (buf) { | ||
|  |                 vars.set(name, buf); | ||
|  |                 next(); | ||
|  |             }); | ||
|  |         }; | ||
|  |          | ||
|  |         self.skip = function (bytes) { | ||
|  |             if (typeof bytes === 'string') { | ||
|  |                 bytes = vars.get(bytes); | ||
|  |             } | ||
|  |              | ||
|  |             getBytes(bytes, function () { | ||
|  |                 next(); | ||
|  |             }); | ||
|  |         }; | ||
|  |          | ||
|  |         self.scan = function find (name, search) { | ||
|  |             if (typeof search === 'string') { | ||
|  |                 search = new Buffer(search); | ||
|  |             } | ||
|  |             else if (!Buffer.isBuffer(search)) { | ||
|  |                 throw new Error('search must be a Buffer or a string'); | ||
|  |             } | ||
|  |              | ||
|  |             var taken = 0; | ||
|  |             pending = function () { | ||
|  |                 var pos = buffers.indexOf(search, offset + taken); | ||
|  |                 var i = pos-offset-taken; | ||
|  |                 if (pos !== -1) { | ||
|  |                     pending = null; | ||
|  |                     if (offset != null) { | ||
|  |                         vars.set( | ||
|  |                             name, | ||
|  |                             buffers.slice(offset, offset + taken + i) | ||
|  |                         ); | ||
|  |                         offset += taken + i + search.length; | ||
|  |                     } | ||
|  |                     else { | ||
|  |                         vars.set( | ||
|  |                             name, | ||
|  |                             buffers.slice(0, taken + i) | ||
|  |                         ); | ||
|  |                         buffers.splice(0, taken + i + search.length); | ||
|  |                     } | ||
|  |                     next(); | ||
|  |                     dispatch(); | ||
|  |                 } else { | ||
|  |                     i = Math.max(buffers.length - search.length - offset - taken, 0); | ||
|  | 				} | ||
|  |                 taken += i; | ||
|  |             }; | ||
|  |             dispatch(); | ||
|  |         }; | ||
|  |          | ||
|  |         self.peek = function (cb) { | ||
|  |             offset = 0; | ||
|  |             saw.nest(function () { | ||
|  |                 cb.call(this, vars.store); | ||
|  |                 this.tap(function () { | ||
|  |                     offset = null; | ||
|  |                 }); | ||
|  |             }); | ||
|  |         }; | ||
|  |          | ||
|  |         return self; | ||
|  |     }; | ||
|  |      | ||
|  |     var stream = Chainsaw.light(builder); | ||
|  |     stream.writable = true; | ||
|  |      | ||
|  |     var buffers = Buffers(); | ||
|  |      | ||
|  |     stream.write = function (buf) { | ||
|  |         buffers.push(buf); | ||
|  |         dispatch(); | ||
|  |     }; | ||
|  |      | ||
|  |     var vars = Vars(); | ||
|  |      | ||
|  |     var done = false, caughtEnd = false; | ||
|  |     stream.end = function () { | ||
|  |         caughtEnd = true; | ||
|  |     }; | ||
|  |      | ||
|  |     stream.pipe = Stream.prototype.pipe; | ||
|  |     Object.getOwnPropertyNames(EventEmitter.prototype).forEach(function (name) { | ||
|  |         stream[name] = EventEmitter.prototype[name]; | ||
|  |     }); | ||
|  |      | ||
|  |     return stream; | ||
|  | }; | ||
|  | 
 | ||
|  | exports.parse = function parse (buffer) { | ||
|  |     var self = words(function (bytes, cb) { | ||
|  |         return function (name) { | ||
|  |             if (offset + bytes <= buffer.length) { | ||
|  |                 var buf = buffer.slice(offset, offset + bytes); | ||
|  |                 offset += bytes; | ||
|  |                 vars.set(name, cb(buf)); | ||
|  |             } | ||
|  |             else { | ||
|  |                 vars.set(name, null); | ||
|  |             } | ||
|  |             return self; | ||
|  |         }; | ||
|  |     }); | ||
|  |      | ||
|  |     var offset = 0; | ||
|  |     var vars = Vars(); | ||
|  |     self.vars = vars.store; | ||
|  |      | ||
|  |     self.tap = function (cb) { | ||
|  |         cb.call(self, vars.store); | ||
|  |         return self; | ||
|  |     }; | ||
|  |      | ||
|  |     self.into = function (key, cb) { | ||
|  |         if (!vars.get(key)) { | ||
|  |             vars.set(key, {}); | ||
|  |         } | ||
|  |         var parent = vars; | ||
|  |         vars = Vars(parent.get(key)); | ||
|  |         cb.call(self, vars.store); | ||
|  |         vars = parent; | ||
|  |         return self; | ||
|  |     }; | ||
|  |      | ||
|  |     self.loop = function (cb) { | ||
|  |         var end = false; | ||
|  |         var ender = function () { end = true }; | ||
|  |         while (end === false) { | ||
|  |             cb.call(self, ender, vars.store); | ||
|  |         } | ||
|  |         return self; | ||
|  |     }; | ||
|  |      | ||
|  |     self.buffer = function (name, size) { | ||
|  |         if (typeof size === 'string') { | ||
|  |             size = vars.get(size); | ||
|  |         } | ||
|  |         var buf = buffer.slice(offset, Math.min(buffer.length, offset + size)); | ||
|  |         offset += size; | ||
|  |         vars.set(name, buf); | ||
|  |          | ||
|  |         return self; | ||
|  |     }; | ||
|  |      | ||
|  |     self.skip = function (bytes) { | ||
|  |         if (typeof bytes === 'string') { | ||
|  |             bytes = vars.get(bytes); | ||
|  |         } | ||
|  |         offset += bytes; | ||
|  |          | ||
|  |         return self; | ||
|  |     }; | ||
|  |      | ||
|  |     self.scan = function (name, search) { | ||
|  |         if (typeof search === 'string') { | ||
|  |             search = new Buffer(search); | ||
|  |         } | ||
|  |         else if (!Buffer.isBuffer(search)) { | ||
|  |             throw new Error('search must be a Buffer or a string'); | ||
|  |         } | ||
|  |         vars.set(name, null); | ||
|  |          | ||
|  |         // simple but slow string search
 | ||
|  |         for (var i = 0; i + offset <= buffer.length - search.length + 1; i++) { | ||
|  |             for ( | ||
|  |                 var j = 0; | ||
|  |                 j < search.length && buffer[offset+i+j] === search[j]; | ||
|  |                 j++ | ||
|  |             ); | ||
|  |             if (j === search.length) break; | ||
|  |         } | ||
|  |          | ||
|  |         vars.set(name, buffer.slice(offset, offset + i)); | ||
|  |         offset += i + search.length; | ||
|  |         return self; | ||
|  |     }; | ||
|  |      | ||
|  |     self.peek = function (cb) { | ||
|  |         var was = offset; | ||
|  |         cb.call(self, vars.store); | ||
|  |         offset = was; | ||
|  |         return self; | ||
|  |     }; | ||
|  |      | ||
|  |     self.flush = function () { | ||
|  |         vars.store = {}; | ||
|  |         return self; | ||
|  |     }; | ||
|  |      | ||
|  |     self.eof = function () { | ||
|  |         return offset >= buffer.length; | ||
|  |     }; | ||
|  |      | ||
|  |     return self; | ||
|  | }; | ||
|  | 
 | ||
|  | // convert byte strings to unsigned little endian numbers
 | ||
|  | function decodeLEu (bytes) { | ||
|  |     var acc = 0; | ||
|  |     for (var i = 0; i < bytes.length; i++) { | ||
|  |         acc += Math.pow(256,i) * bytes[i]; | ||
|  |     } | ||
|  |     return acc; | ||
|  | } | ||
|  | 
 | ||
|  | // convert byte strings to unsigned big endian numbers
 | ||
|  | function decodeBEu (bytes) { | ||
|  |     var acc = 0; | ||
|  |     for (var i = 0; i < bytes.length; i++) { | ||
|  |         acc += Math.pow(256, bytes.length - i - 1) * bytes[i]; | ||
|  |     } | ||
|  |     return acc; | ||
|  | } | ||
|  | 
 | ||
|  | // convert byte strings to signed big endian numbers
 | ||
|  | function decodeBEs (bytes) { | ||
|  |     var val = decodeBEu(bytes); | ||
|  |     if ((bytes[0] & 0x80) == 0x80) { | ||
|  |         val -= Math.pow(256, bytes.length); | ||
|  |     } | ||
|  |     return val; | ||
|  | } | ||
|  | 
 | ||
|  | // convert byte strings to signed little endian numbers
 | ||
|  | function decodeLEs (bytes) { | ||
|  |     var val = decodeLEu(bytes); | ||
|  |     if ((bytes[bytes.length - 1] & 0x80) == 0x80) { | ||
|  |         val -= Math.pow(256, bytes.length); | ||
|  |     } | ||
|  |     return val; | ||
|  | } | ||
|  | 
 | ||
|  | function words (decode) { | ||
|  |     var self = {}; | ||
|  |      | ||
|  |     [ 1, 2, 4, 8 ].forEach(function (bytes) { | ||
|  |         var bits = bytes * 8; | ||
|  |          | ||
|  |         self['word' + bits + 'le'] | ||
|  |         = self['word' + bits + 'lu'] | ||
|  |         = decode(bytes, decodeLEu); | ||
|  |          | ||
|  |         self['word' + bits + 'ls'] | ||
|  |         = decode(bytes, decodeLEs); | ||
|  |          | ||
|  |         self['word' + bits + 'be'] | ||
|  |         = self['word' + bits + 'bu'] | ||
|  |         = decode(bytes, decodeBEu); | ||
|  |          | ||
|  |         self['word' + bits + 'bs'] | ||
|  |         = decode(bytes, decodeBEs); | ||
|  |     }); | ||
|  |      | ||
|  |     // word8be(n) == word8le(n) for all n
 | ||
|  |     self.word8 = self.word8u = self.word8be; | ||
|  |     self.word8s = self.word8bs; | ||
|  |      | ||
|  |     return self; | ||
|  | } |