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
						
					
					
				| 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;
 | |
| }
 |