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