<commit_before>!(function(name, definition){
    let hasDefine = typeof define === 'function';
    let hasModule = typeof module !== 'undefined' && module.exports;

    if(hasDefine){
        // AMD module or CMD module
        define('eventhelper_dubug', function(){return function(){}});
        define(['eventhelper_dubug'], definition);
    }else if(hasModule){
        //CommonJS, Node.js module
        module.exports = definition(require('debug')('eventhelper'))
    }else {
        this[name] = definition();
    }
})('EventHelper', function(debug){
    debug = debug || function(){};

    const ALL_EVENT = '__all__';

    /**
     * EventHelper is an implementation of task/event mode based asynchronous pattern.
     * EventHelper is Inspired by EventProxy, but implement totally different with it.
     * You can use `on` or `bind` to listen a customer event with a callback function;
     * `emit`-- an event fires all callback in succession.
     * Examples:
     * ```js
     * let render = function(template, resource){};
     * var eventHelper = new EventHelper();
     * eventHelper.all('template', 'resource', render);
     * eventHelper.emit('template', template);
     * eventHelper.emit('resource', resource);
     * ```
     */
    function EventHelper(){
        if(!(this instanceof EventHelper)){
            return new EventHelper();
        }
        this._eventEmitter = {};
    }

    /**
     * EventHelper.prototype
     *
     */
    EventHelper.prototype = {
        constructor: EventHelper,
        on: function(eventType, handler){
            debug('Add listener for %s', eventType);
            this._eventEmitter[eventType] = this._eventEmitter[eventType] || [];
            handler = typeof handler=='function' ? handler:function(){};
            this._eventEmitter[eventType].push(handler);
            return this;
        },
        emit: function(eventType, ...args){
            let eventList = this._eventEmitter[eventType];
            if(eventList && eventList.length>0){
                eventList.map((handler)=> handler(...args));
            }
            return this;
        },
        all: function(...args){
            let that = this;
            let callback = args.pop();
            let counter = args.length;
            let resultValue = [];
            let handler = (index)=>(value)=>{
                resultValue[index] = value;
                counter--;
                if(counter==0){
                    callback.apply(this, resultValue);
                }
            };
            args.map((eventType, index)=> that.once(eventType, handler(index)));
            return this;
        },
        race: function(...args){
            let that = this;
            let callback = args.pop();
            let resultValue = [];
            let resolve = false;
            let handler = (value)=>{
                if(!resolve){
                    resolve = true;
                    callback(value);
                }
            }
            args.map((arg)=> that.on(arg, handler));
            return this;
        },
        tail: function(...args){
            let that = this;
            let callback = args.pop();
            let counter = args.length;
            let resultValue = [];
            let fired = {};
            let handler = (index, eventType)=>(value)=>{
                resultValue[index] = value;
                if(counter>0){
                    if(!fired[eventType]){
                        fired[eventType] = true;
                        counter--;
                    }
                    if(counter==0){
                        callback.apply(null, resultValue);
                    }
                }else {
                    callback.apply(null, resultValue);
                }
            };
            args.map((eventType, index)=> that.on(eventType, handler(index, eventType)));
            return this;
        }
    };

    /**
     * Bind an event, the first argument is eventType, a String name,
     * second argument is callback function.
     * `addListener` is alias for `on`
     * @param {String} eventType
     * @param {Function} handler
     */
    EventHelper.prototype.addListener = EventHelper.prototype.on;

    /**
     * `subscribe` is alias for `on`
     */
    EventHelper.prototype.subscribe = EventHelper.prototype.on;

     /**
     * `bind` is alias for `on`
     */
    EventHelper.prototype.bind = EventHelper.prototype.on;

    /**
     * headbind: Bind an event, but put the callback function into head of all callbacks
     * @param {String} eventType
     * @param {Function} handler
     */
    EventHelper.prototype.headbind = function(eventType, handler){
        debug(`HeadBind for event: ${eventType}`)
        this._eventEmitter[eventType] = this._eventEmitter[eventType] || [];
        this._eventEmitter.unshift(handler);
        return this;
    };

    /**
     * Remove one or many callbacks.
     *
     * if `callback` is null, removes all callbacks for the event.
     * if `eventType` is null, removes all bound callbacks for all events.
     * @param {String} eventType
     * @param {Function} handler
     */
    EventHelper.prototype.un = function(eventType, handler){
        let eventEmitter = this._eventEmitter;
        if(!eventType || typeof eventType=='function'){
            debug('Remove all events');
            this._eventEmitter = {};
        }else {
            if(!handler) {
                debug(`Remove all listeners of ${eventType}`)
                this._eventEmitter[eventType] = [];
            }else {
                debug(`Remove a listener of ${eventType}`);
                let index = this._eventEmitter[eventType].indexOf(handler);
                this._eventEmitter[eventType].splice(index ,1);
            }
        }
        return this;
    };

    /**
     * `removeListener` is the alias of `un`
     */
    EventHelper.prototype.removeListener = EventHelper.prototype.un;

    /**
     * `unsubscribe` is the alias of `un`
     */
    EventHelper.prototype.unsubscribe = EventHelper.prototype.un;

    /**
     * `unbind` is the alias of `un`
     */
    EventHelper.prototype.unbind = EventHelper.prototype.un;

    /**
     * Remove all listeners of an event
     * @param {String} eventType
     */
    EventHelper.prototype.unAllListeners = function(eventType){
        debug(`Unbind all listeners of event: ${eventType}`);
        return this.un(eventType);
    }

    /**
     * `removeAllListeners` is alias of `unAllListeners`
     */
    EventHelper.prototype.removeAllListeners = EventHelper.prototype.unAllListeners;

    /**
     * `unbindAllListeners` is alias of 'unAllListeners'
     */
    EventHelper.prototype.unbindAllListeners = EventHelper.prototype.unAllListeners;

    /**
     * bind the ALL_EVENT event
     * @param {Function} handler
     */
    EventHelper.prototype.bindForAll = function(handler){
        return this.on(ALL_EVENT, handler);
    }

    /**
     * unbind the ALL_EVENT event
     * @param {Function} handler
     */
    EventHelper.prototype.unbindForAll = function(){
        return this.un(ALL_EVENT, handler);
    };

    /**
     * trigger an event, firing all bound callbacks,
     * callbacks will be passed the same arguments as `trigger`, apart from the event name
     * Listening for `"all"` pass the eventType: `true` as the first argument
     * `trigger` is alias of `emit`
     * @param {String} eventType
     * @param {Mix} data
     */
    EventHelper.prototype.trigger = EventHelper.prototype.emit;

    /**
     * `fire` is alias of `emit`
     */
    EventHelper.prototype.fire = EventHelper.prototype.emit;

    /**
     * Bind an event like the bind method, but will remove the listener after it was fired
     * @param {String} eventType
     * @param {Function} handler
     */
    EventHelper.prototype.once = function(eventType, handler){
        let that = this;
        let wrapper = function(...args){
            handler.apply(that, ...args);
            that.un(eventType, wrapper);
        };
        return that.on(eventType, wrapper);
    };

    /**
     * `emitLater`: emit the eventType async
     */
    let later = (typeof setImmediate !== 'undefined' && setImmediate) || (typeof process !== 'undefined' && process.nextTick) || function(fn){setTimeout(fn, 0)};
    EventHelper.prototype.emitLater = function(...args){
        let that = this;
        later(function(){
            that.emit.apply(that, ...args);
        });
        return that;
    };

    /**
     * `immediate`: Bind an eventType and trigger it immediate
     * @param {String} eventType
     * @param {Function} handler
     * @param {Mix} data
     */
    EventHelper.prototype.immediate = function(eventType, handler, ...args){
        this.on(eventType, handler);
        this.emit(eventType, ...args);
        return this;
    };

    /**
     * `asap` is alias of `immediate`
     */
    EventHelper.prototype.asap = EventHelper.prototype.immediate;

    /**
     * Assign some events, after all events has been fired, the callback function will be executed once.
     * ```
     * Example:
     * eventHelper.all(event1, event2, event3, callback);
     * @param {String} eventType1
     * @param {String} eventType2
     * @param {String} eventType3
     * @param {Function} handler
     */
    EventHelper.prototype.assign = EventHelper.prototype.all;

    /**
     * Assign the only one event handler
     * `errorMap` is an object, such as:
     * {
     *  eventType1: handler1,
     *  eventType2: handler2
     * }
     * @param {Object} errorMap
     */
    EventHelper.prototype.fail = function(errorMap){
        let that = this;
        let wrapper = function(eventType, ...args){
            let handler = errorMap[eventType] || function(){throw new Error(...args)};
            handler.apply(that, args);
        };
        return that.on('error', wrapper);
    };

    /**
     * `assignAll` is alias for `tail`
     */
    EventHelper.prototype.assignAll = EventHelper.prototype.tail;

    /**
     * `combineLastest` is alias for `tail`
     */
    EventHelper.prototype.combineLastest = EventHelper.prototype.tail;

    /**
     * the callback will be executed after the event has ben fired N times
     * @param {String} eventType
     * @param {Number} times
     * @param {Function} handler
     */
    EventHelper.prototype.after = function(eventType, times, handler){
        let that = this;
        let count = 1;
        let resultArr = [];
        let wrapper = function(data){
            count++;
            resultArr.push(data);
            if(count==times){
                handler.apply(that, [resultArr]);
                that.un(eventType, wrapper);
            }
        };
        return that.on(eventType, wrapper);
    };

    EventHelper.EventHelper = EventHelper;

    return EventHelper;
});
<commit_msg>add group method<commit_after>!(function(name, definition){
    let hasDefine = typeof define === 'function';
    let hasModule = typeof module !== 'undefined' && module.exports;

    if(hasDefine){
        // AMD module or CMD module
        define('eventhelper_dubug', function(){return function(){}});
        define(['eventhelper_dubug'], definition);
    }else if(hasModule){
        //CommonJS, Node.js module
        module.exports = definition(require('debug')('eventhelper'))
    }else {
        this[name] = definition();
    }
})('EventHelper', function(debug){
    debug = debug || function(){};

    const ALL_EVENT = '__all__';

    /**
     * EventHelper is an implementation of task/event mode based asynchronous pattern.
     * EventHelper is Inspired by EventProxy, but implement totally different with it.
     * You can use `on` or `bind` to listen a customer event with a callback function;
     * `emit`-- an event fires all callback in succession.
     * Examples:
     * ```js
     * let render = function(template, resource){};
     * var eventHelper = new EventHelper();
     * eventHelper.all('template', 'resource', render);
     * eventHelper.emit('template', template);
     * eventHelper.emit('resource', resource);
     * ```
     */
    function EventHelper(){
        if(!(this instanceof EventHelper)){
            return new EventHelper();
        }
        this._eventEmitter = {};
    }

    /**
     * EventHelper.prototype
     *
     */
    EventHelper.prototype = {
        constructor: EventHelper,
        on: function(eventType, handler){
            debug('Add listener for %s', eventType);
            this._eventEmitter[eventType] = this._eventEmitter[eventType] || [];
            handler = typeof handler=='function' ? handler:function(){};
            this._eventEmitter[eventType].push(handler);
            return this;
        },
        emit: function(eventType, ...args){
            let eventList = this._eventEmitter[eventType];
            if(eventList && eventList.length>0){
                eventList.map((handler)=> handler(...args));
            }
            return this;
        },
        all: function(...args){
            let that = this;
            let callback = args.pop();
            let counter = args.length;
            let resultValue = [];
            let handler = (index)=>(value)=>{
                resultValue[index] = value;
                counter--;
                if(counter==0){
                    callback.apply(this, resultValue);
                }
            };
            args.map((eventType, index)=> that.once(eventType, handler(index)));
            return this;
        },
        race: function(...args){
            let that = this;
            let callback = args.pop();
            let resultValue = [];
            let resolve = false;
            let handler = (value)=>{
                if(!resolve){
                    resolve = true;
                    callback(value);
                }
            }
            args.map((arg)=> that.on(arg, handler));
            return this;
        },
        tail: function(...args){
            let that = this;
            let callback = args.pop();
            let counter = args.length;
            let resultValue = [];
            let fired = {};
            let handler = (index, eventType)=>(value)=>{
                resultValue[index] = value;
                if(counter>0){
                    if(!fired[eventType]){
                        fired[eventType] = true;
                        counter--;
                    }
                    if(counter==0){
                        callback.apply(null, resultValue);
                    }
                }else {
                    callback.apply(null, resultValue);
                }
            };
            args.map((eventType, index)=> that.on(eventType, handler(index, eventType)));
            return this;
        }
    };

    /**
     * Bind an event, the first argument is eventType, a String name,
     * second argument is callback function.
     * `addListener` is alias for `on`
     * @param {String} eventType
     * @param {Function} handler
     */
    EventHelper.prototype.addListener = EventHelper.prototype.on;

    /**
     * `subscribe` is alias for `on`
     */
    EventHelper.prototype.subscribe = EventHelper.prototype.on;

     /**
     * `bind` is alias for `on`
     */
    EventHelper.prototype.bind = EventHelper.prototype.on;

    /**
     * headbind: Bind an event, but put the callback function into head of all callbacks
     * @param {String} eventType
     * @param {Function} handler
     */
    EventHelper.prototype.headbind = function(eventType, handler){
        debug(`HeadBind for event: ${eventType}`)
        this._eventEmitter[eventType] = this._eventEmitter[eventType] || [];
        this._eventEmitter.unshift(handler);
        return this;
    };

    /**
     * Remove one or many callbacks.
     *
     * if `callback` is null, removes all callbacks for the event.
     * if `eventType` is null, removes all bound callbacks for all events.
     * @param {String} eventType
     * @param {Function} handler
     */
    EventHelper.prototype.un = function(eventType, handler){
        let eventEmitter = this._eventEmitter;
        if(!eventType || typeof eventType=='function'){
            debug('Remove all events');
            this._eventEmitter = {};
        }else {
            if(!handler) {
                debug(`Remove all listeners of ${eventType}`)
                this._eventEmitter[eventType] = [];
            }else {
                debug(`Remove a listener of ${eventType}`);
                let index = this._eventEmitter[eventType].indexOf(handler);
                this._eventEmitter[eventType].splice(index ,1);
            }
        }
        return this;
    };

    /**
     * `removeListener` is the alias of `un`
     */
    EventHelper.prototype.removeListener = EventHelper.prototype.un;

    /**
     * `unsubscribe` is the alias of `un`
     */
    EventHelper.prototype.unsubscribe = EventHelper.prototype.un;

    /**
     * `unbind` is the alias of `un`
     */
    EventHelper.prototype.unbind = EventHelper.prototype.un;

    /**
     * Remove all listeners of an event
     * @param {String} eventType
     */
    EventHelper.prototype.unAllListeners = function(eventType){
        debug(`Unbind all listeners of event: ${eventType}`);
        return this.un(eventType);
    }

    /**
     * `removeAllListeners` is alias of `unAllListeners`
     */
    EventHelper.prototype.removeAllListeners = EventHelper.prototype.unAllListeners;

    /**
     * `unbindAllListeners` is alias of 'unAllListeners'
     */
    EventHelper.prototype.unbindAllListeners = EventHelper.prototype.unAllListeners;

    /**
     * bind the ALL_EVENT event
     * @param {Function} handler
     */
    EventHelper.prototype.bindForAll = function(handler){
        return this.on(ALL_EVENT, handler);
    }

    /**
     * unbind the ALL_EVENT event
     * @param {Function} handler
     */
    EventHelper.prototype.unbindForAll = function(){
        return this.un(ALL_EVENT, handler);
    };

    /**
     * trigger an event, firing all bound callbacks,
     * callbacks will be passed the same arguments as `trigger`, apart from the event name
     * Listening for `"all"` pass the eventType: `true` as the first argument
     * `trigger` is alias of `emit`
     * @param {String} eventType
     * @param {Mix} data
     */
    EventHelper.prototype.trigger = EventHelper.prototype.emit;

    /**
     * `fire` is alias of `emit`
     */
    EventHelper.prototype.fire = EventHelper.prototype.emit;

    /**
     * Bind an event like the bind method, but will remove the listener after it was fired
     * @param {String} eventType
     * @param {Function} handler
     */
    EventHelper.prototype.once = function(eventType, handler){
        let that = this;
        let wrapper = function(...args){
            handler.apply(that, ...args);
            that.un(eventType, wrapper);
        };
        return that.on(eventType, wrapper);
    };

    /**
     * `emitLater`: emit the eventType async
     */
    let later = (typeof setImmediate !== 'undefined' && setImmediate) || (typeof process !== 'undefined' && process.nextTick) || function(fn){setTimeout(fn, 0)};
    EventHelper.prototype.emitLater = function(...args){
        let that = this;
        later(function(){
            that.emit.apply(that, ...args);
        });
        return that;
    };

    /**
     * `immediate`: Bind an eventType and trigger it immediate
     * @param {String} eventType
     * @param {Function} handler
     * @param {Mix} data
     */
    EventHelper.prototype.immediate = function(eventType, handler, ...args){
        this.on(eventType, handler);
        this.emit(eventType, ...args);
        return this;
    };

    /**
     * `asap` is alias of `immediate`
     */
    EventHelper.prototype.asap = EventHelper.prototype.immediate;

    /**
     * Assign some events, after all events has been fired, the callback function will be executed once.
     * ```
     * Example:
     * eventHelper.all(event1, event2, event3, callback);
     * @param {String} eventType1
     * @param {String} eventType2
     * @param {String} eventType3
     * @param {Function} handler
     */
    EventHelper.prototype.assign = EventHelper.prototype.all;

    /**
     * Assign the only one event handler
     * `errorMap` is an object, such as:
     * {
     *  eventType1: handler1,
     *  eventType2: handler2
     * }
     * @param {Object} errorMap
     */
    EventHelper.prototype.fail = function(errorMap){
        let that = this;
        let wrapper = function(eventType, ...args){
            let handler = errorMap[eventType] || function(){throw new Error(...args)};
            handler.apply(that, args);
        };
        return that.on('error', wrapper);
    };

    /**
     * `assignAll` is alias for `tail`
     */
    EventHelper.prototype.assignAll = EventHelper.prototype.tail;

    /**
     * `combineLastest` is alias for `tail`
     */
    EventHelper.prototype.combineLastest = EventHelper.prototype.tail;

    /**
     * the callback will be executed after the event has ben fired N times
     * @param {String} eventType
     * @param {Number} times
     * @param {Function} handler
     */
    EventHelper.prototype.after = function(eventType, times, handler){
        let that = this;
        let count = 1;
        let resultArr = [];
        let wrapper = function({index, result}){
            count++;
            resultArr[index] = result;
            if(count==times){
                handler.apply(that, [resultArr]);
                that.un(eventType, wrapper);
            }
        };
        return that.on(eventType, wrapper);
    };

    /**
   * The `after` method's helper. Use it will return ordered results.
   * If you need manipulate result, you need callback
   * Examples:
   * ```js
   * var ep = new EventProxy();
   * ep.after('file', files.length, function (list) {
   *   // Ordered results
   * });
   * for (var i = 0; i < files.length; i++) {
   *   fs.readFile(files[i], 'utf-8', ep.group('file'));
   * }
   * ```
   * @param {String} eventname Event name, shoule keep consistent with `after`.
   * @param {Function} callback Callback function, should return the final result.
   */
  EventProxy.prototype.group = function (eventname, callback) {
    var that = this;
    var group = eventname + '_group';
    var index = that._after[group].index;
    that._after[group].index++;
    return function (err, data) {
      if (err) {
        // put all arguments to the error handler
        return that.emit.apply(that, ['error'].concat(SLICE.call(arguments)));
      }
      that.emit(group, {
        index: index,
        // callback(err, args1, args2, ...)
        result: callback ? callback.apply(null, SLICE.call(arguments, 1)) : data
      });
    };
  };

    EventHelper.EventHelper = EventHelper;

    return EventHelper;
});
<|endoftext|>


<commit_before>"use strict";

const {EventEmitter} = require("events");

class EventSource extends EventEmitter {
	constructor (...args) {
		super();
		this.initial = [...args];
	}

	send (data, event, id) {
		this.emit("data", {data, event, id});

		return this;
	}

	stream (req, res) {
		let id = -1;
		const fn = arg => this.transmit(res, arg, ++id);

		req.socket.setTimeout(0);
		req.socket.setNoDelay(true);
		req.socket.setKeepAlive(true);

		res.statusCode = 200;
		res.header("content-type", "text/event-stream");
		res.header("cache-control", "no-cache");
		res.header("connection", "keep-alive");

		this.on("data", fn);
		req.on("close", () => this.removeListener("data", fn));
		this.initial.forEach(i => this.send(i));

		return this;
	}

	transmit (res, arg, id) {
		if (arg.id !== void 0) {
			res.write(`id: ${arg.id}\n`);
		} else {
			res.write(`id: ${++id}\n`);
		}

		if (arg.event !== void 0) {
			res.write(`event: ${arg.event}\n`);
		}

		res.write(`data: ${JSON.stringify(arg.data)}\n\n`);
	}
}

module.exports = (...args) => new EventSource(...args);
<commit_msg>Reducing `id` handling<commit_after>"use strict";

const {EventEmitter} = require("events");

class EventSource extends EventEmitter {
	constructor (...args) {
		super();
		this.initial = [...args];
	}

	send (data, event, id) {
		this.emit("data", {data, event, id});

		return this;
	}

	stream (req, res) {
		let id = -1;
		const fn = arg => this.transmit(res, arg, ++id);

		req.socket.setTimeout(0);
		req.socket.setNoDelay(true);
		req.socket.setKeepAlive(true);

		res.statusCode = 200;
		res.header("content-type", "text/event-stream");
		res.header("cache-control", "no-cache");
		res.header("connection", "keep-alive");

		this.on("data", fn);
		req.on("close", () => this.removeListener("data", fn));
		this.initial.forEach(i => this.send(i));

		return this;
	}

	transmit (res, arg, id) {
		res.write(`id: ${arg.id || id}\n`);

		if (arg.event !== void 0) {
			res.write(`event: ${arg.event}\n`);
		}

		res.write(`data: ${JSON.stringify(arg.data)}\n\n`);
	}
}

module.exports = (...args) => new EventSource(...args);
<|endoftext|>


<commit_before>
// == Extend Node primitives to use iconv-lite =================================

module.exports = function (iconv) {
    var original = undefined; // Place to keep original methods.

    iconv.extendNodeEncodings = function extendNodeEncodings() {
        if (original)
            throw new Error("require('iconv-lite').extendNodeEncodings() is already called.")
        original = {};

        var nodeNativeEncodings = {
            'hex': true, 'utf8': true, 'utf-8': true, 'ascii': true, 'binary': true,
            'base64': true, 'ucs2': true, 'ucs-2': true, 'utf16le': true, 'utf-16le': true,
        };

        Buffer.isNativeEncoding = function(enc) {
            return nodeNativeEncodings[enc && enc.toLowerCase()];
        }

        // -- SlowBuffer -----------------------------------------------------------
        var SlowBuffer = require('buffer').SlowBuffer;

        original.SlowBufferToString = SlowBuffer.prototype.toString;
        SlowBuffer.prototype.toString = function(encoding, start, end) {
            encoding = String(encoding || 'utf8').toLowerCase();
            start = +start || 0;
            if (typeof end !== 'number') end = this.length;

            // Fastpath empty strings
            if (+end == start)
                return '';

            // Use native conversion when possible
            if (Buffer.isNativeEncoding(encoding))
                return original.SlowBufferToString.call(this, encoding, start, end);

            // Otherwise, use our decoding method.
            if (typeof start == 'undefined') start = 0;
            if (typeof end == 'undefined') end = this.length;
            return iconv.decode(this.slice(start, end), encoding);
        }

        original.SlowBufferWrite = SlowBuffer.prototype.write;
        SlowBuffer.prototype.write = function(string, offset, length, encoding) {
            // Support both (string, offset, length, encoding)
            // and the legacy (string, encoding, offset, length)
            if (isFinite(offset)) {
                if (!isFinite(length)) {
                    encoding = length;
                    length = undefined;
                }
            } else {  // legacy
                var swap = encoding;
                encoding = offset;
                offset = length;
                length = swap;
            }

            offset = +offset || 0;
            var remaining = this.length - offset;
            if (!length) {
                length = remaining;
            } else {
                length = +length;
                if (length > remaining) {
                    length = remaining;
                }
            }
            encoding = String(encoding || 'utf8').toLowerCase();

            // Use native conversion when possible
            if (Buffer.isNativeEncoding(encoding))
                return original.SlowBufferWrite.call(this, string, offset, length, encoding);

            if (string.length > 0 && (length < 0 || offset < 0))
                throw new RangeError('attempt to write beyond buffer bounds');

            // Otherwise, use our encoding method.
            var buf = iconv.encode(string, encoding);
            if (buf.length < length) length = buf.length;
            buf.copy(this, offset, 0, length);
            return length;
        }

        // -- Buffer ---------------------------------------------------------------

        original.BufferIsEncoding = Buffer.isEncoding;
        Buffer.isEncoding = function(encoding) {
            return Buffer.isNativeEncoding(encoding) || iconv.encodingExists(encoding);
        }

        original.BufferByteLength = Buffer.byteLength;
        Buffer.byteLength = SlowBuffer.byteLength = function(str, encoding) {
            encoding = String(encoding || 'utf8').toLowerCase();

            // Use native conversion when possible
            if (Buffer.isNativeEncoding(encoding))
                return original.BufferByteLength.call(this, str, encoding);

            // Slow, I know, but we don't have a better way yet.
            return iconv.encode(str, encoding).length;
        }

        original.BufferToString = Buffer.prototype.toString;
        Buffer.prototype.toString = function(encoding, start, end) {
            encoding = String(encoding || 'utf8').toLowerCase();

            // Use native conversion when possible
            if (Buffer.isNativeEncoding(encoding))
                return original.BufferToString.call(this, encoding, start, end);

            // Otherwise, use our decoding method.
            if (typeof start == 'undefined') start = 0;
            if (typeof end == 'undefined') end = this.length;
            return iconv.decode(this.slice(start, end), encoding);
        }

        original.BufferWrite = Buffer.prototype.write;
        Buffer.prototype.write = function(string, offset, length, encoding) {
            var _offset = offset, _length = length, _encoding = encoding;
            // Support both (string, offset, length, encoding)
            // and the legacy (string, encoding, offset, length)
            if (isFinite(offset)) {
                if (!isFinite(length)) {
                    encoding = length;
                    length = undefined;
                }
            } else {  // legacy
                var swap = encoding;
                encoding = offset;
                offset = length;
                length = swap;
            }

            encoding = String(encoding || 'utf8').toLowerCase();

            // Use native conversion when possible
            if (Buffer.isNativeEncoding(encoding))
                return original.BufferWrite.call(this, string, _offset, _length, _encoding);

            offset = +offset || 0;
            var remaining = this.length - offset;
            if (!length) {
                length = remaining;
            } else {
                length = +length;
                if (length > remaining) {
                    length = remaining;
                }
            }

            if (string.length > 0 && (length < 0 || offset < 0))
                throw new RangeError('attempt to write beyond buffer bounds');

            // Otherwise, use our encoding method.
            var buf = iconv.encode(string, encoding);
            if (buf.length < length) length = buf.length;
            buf.copy(this, offset, 0, length);
            return length;

            // TODO: Set _charsWritten.
        }

        // -- Readable -------------------------------------------------------------
        if (iconv.supportsStreams) {
            var Readable = require('stream').Readable;

            original.ReadableSetEncoding = Readable.prototype.setEncoding;
            Readable.prototype.setEncoding = function setEncoding(enc, options) {
                // Try to use original function when possible.
                if (Buffer.isNativeEncoding(enc))
                    return original.ReadableSetEncoding.call(this, enc);

                // Try to use our own decoder, it has the same interface.
                this._readableState.decoder = iconv.getCodec(enc).decoder(options);
                this._readableState.encoding = enc;
            }

            Readable.prototype.collect = iconv._collect;
        }
    }

    // Remove iconv-lite Node primitive extensions.
    iconv.undoExtendNodeEncodings = function undoExtendNodeEncodings() {
        if (!original)
            throw new Error("require('iconv-lite').undoExtendNodeEncodings(): Nothing to undo; extendNodeEncodings() is not called.")

        delete Buffer.isNativeEncoding;

        var SlowBuffer = require('buffer').SlowBuffer;

        SlowBuffer.prototype.toString = original.SlowBufferToString;
        SlowBuffer.prototype.write = original.SlowBufferWrite;

        Buffer.isEncoding = original.BufferIsEncoding;
        Buffer.byteLength = original.BufferByteLength;
        Buffer.prototype.toString = original.BufferToString;
        Buffer.prototype.write = original.BufferWrite;

        if (iconv.supportsStreams) {
            var Readable = require('stream').Readable;

            Readable.prototype.setEncoding = original.ReadableSetEncoding;
            delete Readable.prototype.collect;
        }

        original = undefined;
    }
}
<commit_msg>do not throw an error if `extendNodeEncodings()` is called again<commit_after>
// == Extend Node primitives to use iconv-lite =================================

module.exports = function (iconv) {
    var original = undefined; // Place to keep original methods.

    iconv.extendNodeEncodings = function extendNodeEncodings() {
        if (original) return;
        original = {};

        var nodeNativeEncodings = {
            'hex': true, 'utf8': true, 'utf-8': true, 'ascii': true, 'binary': true,
            'base64': true, 'ucs2': true, 'ucs-2': true, 'utf16le': true, 'utf-16le': true,
        };

        Buffer.isNativeEncoding = function(enc) {
            return nodeNativeEncodings[enc && enc.toLowerCase()];
        }

        // -- SlowBuffer -----------------------------------------------------------
        var SlowBuffer = require('buffer').SlowBuffer;

        original.SlowBufferToString = SlowBuffer.prototype.toString;
        SlowBuffer.prototype.toString = function(encoding, start, end) {
            encoding = String(encoding || 'utf8').toLowerCase();
            start = +start || 0;
            if (typeof end !== 'number') end = this.length;

            // Fastpath empty strings
            if (+end == start)
                return '';

            // Use native conversion when possible
            if (Buffer.isNativeEncoding(encoding))
                return original.SlowBufferToString.call(this, encoding, start, end);

            // Otherwise, use our decoding method.
            if (typeof start == 'undefined') start = 0;
            if (typeof end == 'undefined') end = this.length;
            return iconv.decode(this.slice(start, end), encoding);
        }

        original.SlowBufferWrite = SlowBuffer.prototype.write;
        SlowBuffer.prototype.write = function(string, offset, length, encoding) {
            // Support both (string, offset, length, encoding)
            // and the legacy (string, encoding, offset, length)
            if (isFinite(offset)) {
                if (!isFinite(length)) {
                    encoding = length;
                    length = undefined;
                }
            } else {  // legacy
                var swap = encoding;
                encoding = offset;
                offset = length;
                length = swap;
            }

            offset = +offset || 0;
            var remaining = this.length - offset;
            if (!length) {
                length = remaining;
            } else {
                length = +length;
                if (length > remaining) {
                    length = remaining;
                }
            }
            encoding = String(encoding || 'utf8').toLowerCase();

            // Use native conversion when possible
            if (Buffer.isNativeEncoding(encoding))
                return original.SlowBufferWrite.call(this, string, offset, length, encoding);

            if (string.length > 0 && (length < 0 || offset < 0))
                throw new RangeError('attempt to write beyond buffer bounds');

            // Otherwise, use our encoding method.
            var buf = iconv.encode(string, encoding);
            if (buf.length < length) length = buf.length;
            buf.copy(this, offset, 0, length);
            return length;
        }

        // -- Buffer ---------------------------------------------------------------

        original.BufferIsEncoding = Buffer.isEncoding;
        Buffer.isEncoding = function(encoding) {
            return Buffer.isNativeEncoding(encoding) || iconv.encodingExists(encoding);
        }

        original.BufferByteLength = Buffer.byteLength;
        Buffer.byteLength = SlowBuffer.byteLength = function(str, encoding) {
            encoding = String(encoding || 'utf8').toLowerCase();

            // Use native conversion when possible
            if (Buffer.isNativeEncoding(encoding))
                return original.BufferByteLength.call(this, str, encoding);

            // Slow, I know, but we don't have a better way yet.
            return iconv.encode(str, encoding).length;
        }

        original.BufferToString = Buffer.prototype.toString;
        Buffer.prototype.toString = function(encoding, start, end) {
            encoding = String(encoding || 'utf8').toLowerCase();

            // Use native conversion when possible
            if (Buffer.isNativeEncoding(encoding))
                return original.BufferToString.call(this, encoding, start, end);

            // Otherwise, use our decoding method.
            if (typeof start == 'undefined') start = 0;
            if (typeof end == 'undefined') end = this.length;
            return iconv.decode(this.slice(start, end), encoding);
        }

        original.BufferWrite = Buffer.prototype.write;
        Buffer.prototype.write = function(string, offset, length, encoding) {
            var _offset = offset, _length = length, _encoding = encoding;
            // Support both (string, offset, length, encoding)
            // and the legacy (string, encoding, offset, length)
            if (isFinite(offset)) {
                if (!isFinite(length)) {
                    encoding = length;
                    length = undefined;
                }
            } else {  // legacy
                var swap = encoding;
                encoding = offset;
                offset = length;
                length = swap;
            }

            encoding = String(encoding || 'utf8').toLowerCase();

            // Use native conversion when possible
            if (Buffer.isNativeEncoding(encoding))
                return original.BufferWrite.call(this, string, _offset, _length, _encoding);

            offset = +offset || 0;
            var remaining = this.length - offset;
            if (!length) {
                length = remaining;
            } else {
                length = +length;
                if (length > remaining) {
                    length = remaining;
                }
            }

            if (string.length > 0 && (length < 0 || offset < 0))
                throw new RangeError('attempt to write beyond buffer bounds');

            // Otherwise, use our encoding method.
            var buf = iconv.encode(string, encoding);
            if (buf.length < length) length = buf.length;
            buf.copy(this, offset, 0, length);
            return length;

            // TODO: Set _charsWritten.
        }

        // -- Readable -------------------------------------------------------------
        if (iconv.supportsStreams) {
            var Readable = require('stream').Readable;

            original.ReadableSetEncoding = Readable.prototype.setEncoding;
            Readable.prototype.setEncoding = function setEncoding(enc, options) {
                // Try to use original function when possible.
                if (Buffer.isNativeEncoding(enc))
                    return original.ReadableSetEncoding.call(this, enc);

                // Try to use our own decoder, it has the same interface.
                this._readableState.decoder = iconv.getCodec(enc).decoder(options);
                this._readableState.encoding = enc;
            }

            Readable.prototype.collect = iconv._collect;
        }
    }

    // Remove iconv-lite Node primitive extensions.
    iconv.undoExtendNodeEncodings = function undoExtendNodeEncodings() {
        if (!original)
            throw new Error("require('iconv-lite').undoExtendNodeEncodings(): Nothing to undo; extendNodeEncodings() is not called.")

        delete Buffer.isNativeEncoding;

        var SlowBuffer = require('buffer').SlowBuffer;

        SlowBuffer.prototype.toString = original.SlowBufferToString;
        SlowBuffer.prototype.write = original.SlowBufferWrite;

        Buffer.isEncoding = original.BufferIsEncoding;
        Buffer.byteLength = original.BufferByteLength;
        Buffer.prototype.toString = original.BufferToString;
        Buffer.prototype.write = original.BufferWrite;

        if (iconv.supportsStreams) {
            var Readable = require('stream').Readable;

            Readable.prototype.setEncoding = original.ReadableSetEncoding;
            delete Readable.prototype.collect;
        }

        original = undefined;
    }
}
<|endoftext|>


<commit_before>const debug = require('debug')('webtorrent:file-stream')
const stream = require('readable-stream')

/**
 * Readable stream of a torrent file
 *
 * @param {File} file
 * @param {Object} opts
 * @param {number} opts.start stream slice of file, starting from this byte (inclusive)
 * @param {number} opts.end stream slice of file, ending with this byte (inclusive)
 */
class FileStream extends stream.Readable {
  constructor (file, opts) {
    super(opts)

    this.destroyed = false
    this._torrent = file._torrent

    const start = (opts && opts.start) || 0
    const end = (opts && opts.end && opts.end < file.length)
      ? opts.end
      : file.length - 1

    const pieceLength = file._torrent.pieceLength

    this._startPiece = (start + file.offset) / pieceLength | 0
    this._endPiece = (end + file.offset) / pieceLength | 0

    this._piece = this._startPiece
    this._offset = (start + file.offset) - (this._startPiece * pieceLength)

    this._missing = end - start + 1
    this._reading = false
    this._notifying = false
    this._criticalLength = Math.min((1024 * 1024 / pieceLength) | 0, 2)
  }

  _read () {
    if (this._reading) return
    this._reading = true
    this._notify()
  }

  _notify () {
    if (!this._reading || this._missing === 0) return
    if (!this._torrent.bitfield.get(this._piece)) {
      return this._torrent.critical(this._piece, this._piece + this._criticalLength)
    }

    if (this._notifying) return
    this._notifying = true

    if (this._torrent.destroyed) return this._destroy(new Error('Torrent removed'))

    const p = this._piece

    const getOpts = {}
    // Specify length for the last piece in case it is zero-padded
    if (p === this._torrent.pieces.length - 1) {
      getOpts.length = this._torrent.lastPieceLength
    }
    this._torrent.store.get(p, getOpts, (err, buffer) => {
      this._notifying = false
      if (this.destroyed) return
      debug('read %s (length %s) (err %s)', p, buffer.length, err && err.message)

      if (err) return this._destroy(err)

      if (this._offset) {
        buffer = buffer.slice(this._offset)
        this._offset = 0
      }

      if (this._missing < buffer.length) {
        buffer = buffer.slice(0, this._missing)
      }
      this._missing -= buffer.length

      debug('pushing buffer of length %s', buffer.length)
      this._reading = false
      this.push(buffer)

      if (this._missing === 0) this.push(null)
    })
    this._piece += 1
  }

  destroy (onclose) {
    this._destroy(null, onclose)
  }

  _destroy (err, onclose) {
    if (this.destroyed) return
    this.destroyed = true

    if (!this._torrent.destroyed) {
      this._torrent.deselect(this._startPiece, this._endPiece, true)
    }

    if (err) this.emit('error', err)
    this.emit('close')
    if (onclose) onclose()
  }
}

module.exports = FileStream
<commit_msg>guard against exception from buffer being undefined<commit_after>const debug = require('debug')('webtorrent:file-stream')
const stream = require('readable-stream')

/**
 * Readable stream of a torrent file
 *
 * @param {File} file
 * @param {Object} opts
 * @param {number} opts.start stream slice of file, starting from this byte (inclusive)
 * @param {number} opts.end stream slice of file, ending with this byte (inclusive)
 */
class FileStream extends stream.Readable {
  constructor (file, opts) {
    super(opts)

    this.destroyed = false
    this._torrent = file._torrent

    const start = (opts && opts.start) || 0
    const end = (opts && opts.end && opts.end < file.length)
      ? opts.end
      : file.length - 1

    const pieceLength = file._torrent.pieceLength

    this._startPiece = (start + file.offset) / pieceLength | 0
    this._endPiece = (end + file.offset) / pieceLength | 0

    this._piece = this._startPiece
    this._offset = (start + file.offset) - (this._startPiece * pieceLength)

    this._missing = end - start + 1
    this._reading = false
    this._notifying = false
    this._criticalLength = Math.min((1024 * 1024 / pieceLength) | 0, 2)
  }

  _read () {
    if (this._reading) return
    this._reading = true
    this._notify()
  }

  _notify () {
    if (!this._reading || this._missing === 0) return
    if (!this._torrent.bitfield.get(this._piece)) {
      return this._torrent.critical(this._piece, this._piece + this._criticalLength)
    }

    if (this._notifying) return
    this._notifying = true

    if (this._torrent.destroyed) return this._destroy(new Error('Torrent removed'))

    const p = this._piece

    const getOpts = {}
    // Specify length for the last piece in case it is zero-padded
    if (p === this._torrent.pieces.length - 1) {
      getOpts.length = this._torrent.lastPieceLength
    }
    this._torrent.store.get(p, getOpts, (err, buffer) => {
      this._notifying = false
      if (this.destroyed) return
      debug('read %s (length %s) (err %s)', p, buffer && buffer.length, err && err.message)

      if (err) return this._destroy(err)

      if (this._offset) {
        buffer = buffer.slice(this._offset)
        this._offset = 0
      }

      if (this._missing < buffer.length) {
        buffer = buffer.slice(0, this._missing)
      }
      this._missing -= buffer.length

      debug('pushing buffer of length %s', buffer.length)
      this._reading = false
      this.push(buffer)

      if (this._missing === 0) this.push(null)
    })
    this._piece += 1
  }

  destroy (onclose) {
    this._destroy(null, onclose)
  }

  _destroy (err, onclose) {
    if (this.destroyed) return
    this.destroyed = true

    if (!this._torrent.destroyed) {
      this._torrent.deselect(this._startPiece, this._endPiece, true)
    }

    if (err) this.emit('error', err)
    this.emit('close')
    if (onclose) onclose()
  }
}

module.exports = FileStream
<|endoftext|>


<commit_before>/*
 * decaffeinate suggestions:
 * DS102: Remove unnecessary code created because of implicit returns
 * DS103: Rewrite code to no longer use __guard__
 * DS207: Consider shorter variations of null checks
 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
 */
const os = require("os");
const yn = require("yn");

const STACKDRIVER_LOGGING = yn(process.env['STACKDRIVER_LOGGING']);

module.exports.monitor = logger => (function(req, res, next) {
    const Metrics = require("./metrics");
    const startTime = process.hrtime();
    const {
        end
    } = res;
    res.end = function() {
        let info;
        end.apply(this, arguments);
        const responseTime = process.hrtime(startTime);
        const responseTimeMs = Math.round((responseTime[0] * 1000) + (responseTime[1] / 1000000));
        const requestSize = parseInt(req.headers["content-length"], 10);
        if ((req.route != null ? req.route.path : undefined) != null) {
            const routePath = req.route.path.toString().replace(/\//g, '_').replace(/\:/g, '').slice(1);
            Metrics.timing("http_request", responseTimeMs, null, {method:req.method, status_code: res.statusCode, path:routePath});
            if (requestSize) {
                Metrics.summary("http_request_size_bytes", requestSize, {method:req.method, status_code: res.statusCode, path:routePath});
            }
        }
        const remoteIp = req.ip || __guard__(req.socket != null ? req.socket.socket : undefined, x => x.remoteAddress) || (req.socket != null ? req.socket.remoteAddress : undefined);
        const reqUrl = req.originalUrl || req.url;
        const referrer = req.headers['referer'] || req.headers['referrer'];
        if (STACKDRIVER_LOGGING) {
            info = {
                httpRequest: {
                    requestMethod: req.method,
                    requestUrl: reqUrl,
                    requestSize,
                    status: res.statusCode,
                    responseSize: (res._headers != null ? res._headers["content-length"] : undefined),
                    userAgent: req.headers["user-agent"],
                    remoteIp,
                    referer: referrer,
                    latency: {
                        seconds: responseTime[0],
                        nanos: responseTime[1]
                    },
                    protocol: req.protocol
                }
            };
        } else {
            info = {
                req: {
                    url: reqUrl,
                    method: req.method,
                    referrer,
                    "remote-addr": remoteIp,
                    "user-agent": req.headers["user-agent"],
                    "content-length": req.headers["content-length"]
                },
                res: {
                    "content-length": (res._headers != null ? res._headers["content-length"] : undefined),
                    statusCode: res.statusCode
                },
                "response-time": responseTimeMs
            };
        }
        return logger.info(info, "%s %s", req.method, reqUrl);
    };
    return next();
});

function __guard__(value, transform) {
  return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined;
}<commit_msg>[http] use public node api for getting the response content-length<commit_after>/*
 * decaffeinate suggestions:
 * DS102: Remove unnecessary code created because of implicit returns
 * DS103: Rewrite code to no longer use __guard__
 * DS207: Consider shorter variations of null checks
 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
 */
const os = require("os");
const yn = require("yn");

const STACKDRIVER_LOGGING = yn(process.env['STACKDRIVER_LOGGING']);

module.exports.monitor = logger => (function(req, res, next) {
    const Metrics = require("./metrics");
    const startTime = process.hrtime();
    const {
        end
    } = res;
    res.end = function() {
        let info;
        end.apply(this, arguments);
        const responseTime = process.hrtime(startTime);
        const responseTimeMs = Math.round((responseTime[0] * 1000) + (responseTime[1] / 1000000));
        const requestSize = parseInt(req.headers["content-length"], 10);
        if ((req.route != null ? req.route.path : undefined) != null) {
            const routePath = req.route.path.toString().replace(/\//g, '_').replace(/\:/g, '').slice(1);
            Metrics.timing("http_request", responseTimeMs, null, {method:req.method, status_code: res.statusCode, path:routePath});
            if (requestSize) {
                Metrics.summary("http_request_size_bytes", requestSize, {method:req.method, status_code: res.statusCode, path:routePath});
            }
        }
        const remoteIp = req.ip || __guard__(req.socket != null ? req.socket.socket : undefined, x => x.remoteAddress) || (req.socket != null ? req.socket.remoteAddress : undefined);
        const reqUrl = req.originalUrl || req.url;
        const referrer = req.headers['referer'] || req.headers['referrer'];
        if (STACKDRIVER_LOGGING) {
            info = {
                httpRequest: {
                    requestMethod: req.method,
                    requestUrl: reqUrl,
                    requestSize,
                    status: res.statusCode,
                    responseSize: res.getHeader("content-length"),
                    userAgent: req.headers["user-agent"],
                    remoteIp,
                    referer: referrer,
                    latency: {
                        seconds: responseTime[0],
                        nanos: responseTime[1]
                    },
                    protocol: req.protocol
                }
            };
        } else {
            info = {
                req: {
                    url: reqUrl,
                    method: req.method,
                    referrer,
                    "remote-addr": remoteIp,
                    "user-agent": req.headers["user-agent"],
                    "content-length": req.headers["content-length"]
                },
                res: {
                    "content-length": res.getHeader("content-length"),
                    statusCode: res.statusCode
                },
                "response-time": responseTimeMs
            };
        }
        return logger.info(info, "%s %s", req.method, reqUrl);
    };
    return next();
});

function __guard__(value, transform) {
  return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined;
}<|endoftext|>


<commit_before>const debug = require('debug')('webtorrent:file-stream')
const stream = require('readable-stream')

/**
 * Readable stream of a torrent file
 *
 * @param {File} file
 * @param {Object} opts
 * @param {number} opts.start stream slice of file, starting from this byte (inclusive)
 * @param {number} opts.end stream slice of file, ending with this byte (inclusive)
 */
class FileStream extends stream.Readable {
  constructor (file, opts) {
    super(opts)

    this.destroyed = false
    this._torrent = file._torrent

    const start = (opts && opts.start) || 0
    const end = (opts && opts.end && opts.end < file.length)
      ? opts.end
      : file.length - 1

    const pieceLength = file._torrent.pieceLength

    this._startPiece = (start + file.offset) / pieceLength | 0
    this._endPiece = (end + file.offset) / pieceLength | 0

    this._piece = this._startPiece
    this._offset = (start + file.offset) - (this._startPiece * pieceLength)

    this._missing = end - start + 1
    this._reading = false
    this._notifying = false
    this._criticalLength = Math.min((1024 * 1024 / pieceLength) | 0, 2)
  }

  _read () {
    if (this._reading) return
    this._reading = true
    this._notify()
  }

  _notify () {
    if (!this._reading || this._missing === 0) return
    if (!this._torrent.bitfield.get(this._piece)) {
      return this._torrent.critical(this._piece, this._piece + this._criticalLength)
    }

    if (this._notifying) return
    this._notifying = true

    if (this._torrent.destroyed) return this._destroy(new Error('Torrent removed'))

    const p = this._piece

    const getOpts = {}
    // Specify length for the last piece in case it is zero-padded
    if (p === this._torrent.pieces.length - 1) {
      getOpts.length = this._torrent.lastPieceLength
    }
    this._torrent.store.get(p, getOpts, (err, buffer) => {
      this._notifying = false
      if (this.destroyed) return
      debug('read %s (length %s) (err %s)', p, buffer && buffer.length, err && err.message)

      if (err) return this._destroy(err)

      if (this._offset) {
        buffer = buffer.slice(this._offset)
        this._offset = 0
      }

      if (this._missing < buffer.length) {
        buffer = buffer.slice(0, this._missing)
      }
      this._missing -= buffer.length

      debug('pushing buffer of length %s', buffer.length)
      this._reading = false
      this.push(buffer)

      if (this._missing === 0) this.push(null)
    })
    this._piece += 1
  }

  destroy (onclose) {
    this._destroy(null, onclose)
  }

  _destroy (err, onclose) {
    if (this.destroyed) return
    this.destroyed = true

    if (!this._torrent.destroyed) {
      this._torrent.deselect(this._startPiece, this._endPiece, true)
    }

    if (err) this.emit('error', err)
    this.emit('close')
    if (onclose) onclose()
  }
}

module.exports = FileStream
<commit_msg>FileStream: remove onclose argument to destroy()<commit_after>const debug = require('debug')('webtorrent:file-stream')
const stream = require('readable-stream')

/**
 * Readable stream of a torrent file
 *
 * @param {File} file
 * @param {Object} opts
 * @param {number} opts.start stream slice of file, starting from this byte (inclusive)
 * @param {number} opts.end stream slice of file, ending with this byte (inclusive)
 */
class FileStream extends stream.Readable {
  constructor (file, opts) {
    super(opts)

    this.destroyed = false
    this._torrent = file._torrent

    const start = (opts && opts.start) || 0
    const end = (opts && opts.end && opts.end < file.length)
      ? opts.end
      : file.length - 1

    const pieceLength = file._torrent.pieceLength

    this._startPiece = (start + file.offset) / pieceLength | 0
    this._endPiece = (end + file.offset) / pieceLength | 0

    this._piece = this._startPiece
    this._offset = (start + file.offset) - (this._startPiece * pieceLength)

    this._missing = end - start + 1
    this._reading = false
    this._notifying = false
    this._criticalLength = Math.min((1024 * 1024 / pieceLength) | 0, 2)
  }

  _read () {
    if (this._reading) return
    this._reading = true
    this._notify()
  }

  _notify () {
    if (!this._reading || this._missing === 0) return
    if (!this._torrent.bitfield.get(this._piece)) {
      return this._torrent.critical(this._piece, this._piece + this._criticalLength)
    }

    if (this._notifying) return
    this._notifying = true

    if (this._torrent.destroyed) return this._destroy(new Error('Torrent removed'))

    const p = this._piece

    const getOpts = {}
    // Specify length for the last piece in case it is zero-padded
    if (p === this._torrent.pieces.length - 1) {
      getOpts.length = this._torrent.lastPieceLength
    }
    this._torrent.store.get(p, getOpts, (err, buffer) => {
      this._notifying = false
      if (this.destroyed) return
      debug('read %s (length %s) (err %s)', p, buffer && buffer.length, err && err.message)

      if (err) return this._destroy(err)

      if (this._offset) {
        buffer = buffer.slice(this._offset)
        this._offset = 0
      }

      if (this._missing < buffer.length) {
        buffer = buffer.slice(0, this._missing)
      }
      this._missing -= buffer.length

      debug('pushing buffer of length %s', buffer.length)
      this._reading = false
      this.push(buffer)

      if (this._missing === 0) this.push(null)
    })
    this._piece += 1
  }

  _destroy (err) {
    if (this.destroyed) return
    this.destroyed = true

    if (!this._torrent.destroyed) {
      this._torrent.deselect(this._startPiece, this._endPiece, true)
    }

    if (err) this.emit('error', err)
    this.emit('close')
  }
}

module.exports = FileStream
<|endoftext|>


<commit_before>/*
 * cli.js: Handlers for the forever CLI commands.
 *
 * (C) 2010 Nodejitsu Inc.
 * MIT LICENCE
 *
 */

var fs = require('fs'),
    util = require('util'),
    path = require('path'),
    flatiron = require('flatiron'),
    cliff = require('cliff'),
    daemon = require('daemon'),
    forever = require('../forever');

var cli = exports;

var help = [
  'usage: forever [action] [options] SCRIPT [script-options]',
  '',
  'Monitors the script specified in the current process or as a daemon',
  '',
  'actions:',
  '  start               Start SCRIPT as a daemon',
  '  stop                Stop the daemon SCRIPT',
  '  stopall             Stop all running forever scripts',
  '  restart             Restart the daemon SCRIPT',
  '  list                List all running forever scripts',
  '  config              Lists all forever user configuration',
  '  set <key> <val>     Sets the specified forever config <key>',
  '  clear <key>         Clears the specified forever config <key>',
  '  logs                Lists log files for all forever processes',
  '  logs <script|index> Tails the logs for <script|index>',
  '  columns add <col>   Adds the specified column to the output in `forever list`',
  '  columns rm <col>    Removed the specified column from the output in `forever list`',
  '  columns set <cols>  Set all columns for the output in `forever list`',
  '  cleanlogs           [CAREFUL] Deletes all historical forever log files',
  '',
  'options:',
  '  -m  MAX          Only run the specified script MAX times',
  '  -l  LOGFILE      Logs the forever output to LOGFILE',
  '  -o  OUTFILE      Logs stdout from child script to OUTFILE',
  '  -e  ERRFILE      Logs stderr from child script to ERRFILE',
  '  -p  PATH         Base path for all forever related files (pid files, etc.)',
  '  -c  COMMAND      COMMAND to execute (defaults to node)',
  '  -a, --append     Append logs',
  '  --pidfile        The pid file',
  '  --sourceDir      The source directory for which SCRIPT is relative to',
  '  --minUptime      Minimum uptime (millis) for a script to not be considered "spinning"',
  '  --spinSleepTime  Time to wait (millis) between launches of a spinning script.',
  '  --plain          Disable command line colors',
  '  -d, --debug      Forces forever to log debug output',
  '  -v, --verbose    Turns on the verbose messages from Forever',
  '  -s, --silent     Run the child script silencing stdout and stderr',
  '  -w, --watch      Watch for file changes',
  '  -h, --help       You\'re staring at it',
  '',
  '[Long Running Process]',
  '  The forever process will continue to run outputting log messages to the console.',
  '  ex. forever -o out.log -e err.log my-script.js',
  '',
  '[Daemon]',
  '  The forever process will run as a daemon which will make the target process start',
  '  in the background. This is extremely useful for remote starting simple node.js scripts',
  '  without using nohup. It is recommended to run start with -o -l, & -e.',
  '  ex. forever start -l forever.log -o out.log -e err.log my-daemon.js',
  '      forever stop my-daemon.js',
  ''
];

var app = flatiron.app;

app.use(flatiron.plugins.cli, {
  usage: help
});

var argvOptions = {
  'command':   {alias: 'c'},
  'errFile':   {alias: 'e'},
  'logFile':   {alias: 'l'},
  'appendLog': {alias: ['a', 'append'], boolean: true},
  'max':       {alias: 'm'},
  'outFile':   {alias: 'o'},
  'path':      {alias: 'p'},
  'silent':    {alias: 's', boolean: true},
  'verbose':   {alias: 'v', boolean: true},
  'watch':     {alias: 'w', boolean: true},
  'debug':     {alias: 'd', boolean: true},
  'plain':     {boolean: true}
};

app.use(flatiron.plugins.config, {argv: argvOptions});

var reserved = ['root', 'pidPath'];

//
// ### @private function (file, options, callback)
// #### @file {string} Target script to start
// #### @options {Object} Options to start the script with
// #### @callback {function} Continuation to respond to when complete.
// Helper function that sets up the pathing for the specified `file`
// then stats the appropriate files and responds.
//
function tryStart(file, options, callback) {
  var fullLog, fullScript;

  fullLog = forever.logFilePath(options.logFile, options.uid);
  fullScript = path.join(options.sourceDir, file);

  forever.stat(fullLog, fullScript, options.appendLog, function (err) {
    if (err) {
      forever.log.error('Cannot start forever');
      forever.log.error(err.message);
      process.exit(-1);
    }

    callback();
  });
}

//
// ### @private function updateConfig (updater)
// #### @updater {function} Function which updates the forever config
// Helper which runs the specified `updater` and then saves the forever
// config to `forever.config.get('root')`.
//
function updateConfig(updater) {
  updater();
  forever.config.save(function (err) {
    if (err) {
      return forever.log.error('Error saving config: ' + err.message);
    }

    cli.config();
    var configFile = path.join(forever.config.get('root'), 'config.json');
    forever.log.info('Forever config saved: ' + configFile.yellow);
  });
}

//
// ### @private function checkColumn (name)
// #### @name {string} Column to check
// Checks if column `name` exists
//
function checkColumn(name) {
  if (forever.columns[name]) {
    forever.log.error('Unknown column: ' + name.magenta);
    return false;
  }
  return true;
}

//
// ### @private function getOptions (file)
// #### @file {string} File to run. **Optional**
// Returns `options` object for use with `forever.start` and
// `forever.startDaemon`
//
function getOptions(file) {
  var options = {};
  //
  // First isolate options which should be passed to file
  //
  options.options = process.argv.splice(process.argv.indexOf(file) + 1);

  //
  // Now we have to force optimist to reparse command line options because
  // we've removed some before.
  //
  app.config.system.loadArgv();

  [
    'pidFile', 'logFile', 'errFile', 'watch', 'minUptime', 'appendLog',
    'silent', 'max', 'command', 'path', 'spinSleepTime', 'sourceDir',
    'uid'
  ].forEach(function (key) {
    options[key] = app.config.get(key);
  });

  options.sourceDir = file && file[0] !== '/' ? process.cwd() : '/';
  if (options.sourceDir) {
    options.spawnWith = {cwd: options.sourceDir};
  }

  return options;
}

//
// ### function cleanLogs
// Deletes all historical forever log files
//
app.cmd('cleanlogs', cli.cleanLogs = function () {
  forever.log.silly('Tidying ' + forever.config.get('root'));
  forever.cleanUp(true).on('cleanUp', function () {
    forever.log.silly(forever.config.get('root') + ' tidied.');
  });
});

//
// ### function start (file)
// #### @file {string} Location of the script to spawn with forever
// Starts a forever process for the script located at `file` as daemon
// process.
//
app.cmd(/start (.+)/, cli.startDaemon = function (file) {
  forever.log.info('Forever processing file: ' + file.grey);
  var options = getOptions(file);
  tryStart(file, options, function () {
    var monitor = forever.startDaemon(file, options);
    monitor.on('start', function () {
      forever.startServer(monitor);
    });
  });
});

//
// ### function stop (file)
// #### @file {string} Target forever process to stop
// Stops the forever process specified by `file`.
//
app.cmd(/stop (.+)/, cli.stop = function (file) {
  var runner = forever.stop(file, true);

  runner.on('stop', function (process) {
    forever.log.info('Forever stopped process:');
    forever.log.data(process);
  });

  runner.on('error', function (err) {
    forever.log.error('Forever cannot find process with index: ' + file);
  });
});

//
// ### function stopall ()
// Stops all currently running forever processes.
//
app.cmd('stopall', cli.stopall = function () {
  var runner = forever.stopAll(true);
  runner.on('stopAll', function (processes) {
    if (processes) {
      forever.log.info('Forever stopped processes:');
      processes.split('\n').forEach(function (line) {
        forever.log.data(line);
      });
    }
    else {
      forever.log.info('No forever processes running');
    }
  });
});

//
// ### function restart (file)
// #### @file {string} Target process to restart
// Restarts the forever process specified by `file`.
//
app.cmd(/restart (.+)/, cli.restart = function (file) {
  var runner = forever.restart(file, true);
  runner.on('restart', function (processes) {
    if (processes) {
      forever.log.info('Forever restarted process(es):');
      processes.split('\n').forEach(function (line) {
        forever.log.data(line);
      });
    }
    else {
      forever.log.info('No forever processes running');
    }
  });

  runner.on('error', function (err) {
    forever.log.error('Error restarting process: ' + file.grey);
    forever.log.error(err.message);
  });
});

//
// ### function list ()
// Lists all currently running forever processes.
//
app.cmd('list', cli.list = function () {
  forever.list(true, function (err, processes) {
    if (processes) {
      forever.log.info('Forever processes running');
      processes.split('\n').forEach(function (line) {
        forever.log.data(line);
      });
    }
    else {
      forever.log.info('No forever processes running');
    }
  });
});

//
// ### function config ()
// Lists all of the configuration in `~/.forever/config.json`.
//
app.cmd('config', cli.config = function () {
  var keys = Object.keys(forever.config.store),
      conf = cliff.inspect(forever.config.store);

  if (keys.length <= 2) {
    conf = conf.replace(/\{\s/, '{ \n')
               .replace(/\}/, '\n}')
               .replace('\\033[90m', '  \\033[90m')
               .replace(/, /ig, ',\n  ');
  }
  else {
    conf = conf.replace(/\n\s{4}/ig, '\n  ');
  }

  conf.split('\n').forEach(function (line) {
    forever.log.data(line);
  });
});

//
// ### function set (key, value)
// #### @key {string} Key to set in forever config
// #### @value {string} Value to set for `key`
// Sets the specified `key` / `value` pair in the
// forever user config.
//
app.cmd('set :key :value', cli.set = function (key, value) {
  updateConfig(function () {
    forever.log.info('Setting forever config: ' + key.grey);
    forever.config.set(key, value);
  });
});

//
// ### function clear (key)
// #### @key {string} Key to remove from `~/.forever/config.json`
// Removes the specified `key` from the forever user config.
//
app.cmd('clear :key', cli.clear = function (key) {
  if (reserved.indexOf(key) !== -1) {
    forever.log.warn('Cannot clear reserved config: ' + key.grey);
    forever.log.warn('Use `forever set ' + key + '` instead');
    return;
  }

  updateConfig(function () {
    forever.log.info('Clearing forever config: ' + key.grey);
    forever.config.clear(key);
  });
});

//
// ### function logs (target)
// #### @target {string} **Optional** Target script or index to list logs for
// Displays the logs using `tail` for the specified `target`. If no `target` is
// specified then it will display log files for all running forever processes.
//
app.cmd('logs :index', cli.logs = function (index) {
  //
  // Helper function for listing all log files
  //
  function listFiles() {
    var index = 0,
        rows = [['   ', 'script', 'logfile']];

    forever.list(false, function (err, processes) {
      forever.log.info('Logs for running Forever processes');
      rows = rows.concat(processes.map(function (proc) {
        return ['[' + index++ + ']', proc.file.grey, proc.logFile.magenta];
      }));

      cliff.putRows('data', rows, ['white', 'grey', 'magenta']);
    });
  }

  if (typeof index === 'undefined') {
    return listFiles();
  }

  forever.tail(index, function (err, logs) {
    if (err) {
      return forever.log.error(err.message);
    }

    logs.forEach(function (proc) {
      forever.log.info('Showing logs for ' + proc.file.magenta);
      proc.logs.forEach(function (line) {
        forever.log.data(line);
      });
    });
  });
});

app.cmd('columns add :name', cli.addColumn = function (name) {
  if (checkColumn(name)) {
    var columns = forever.config.get('columns');

    if (~columns.indexOf(value)) {
      return forever.log.warn(value.magenta + ' already exists in forever');
    }

    forever.log.info('Adding column: ' + value.magenta);
    columns.push(value);

    forever.config.set('columns', columns);
    forever.config.saveSync();
  }
});

app.cmd('columns rm :name', cli.rmColumn = function (name) {
  if (checkColumn(name)) {
    var columns = forever.config.get('columns');

    if (!~columns.indexOf(value)) {
      return forever.log.warn(value.magenta + ' doesn\'t exist in forever');
    }

    forever.log.info('Removing column: ' + value.magenta);
    columns.splice(columns.indexOf(value), 1);

    forever.config.set('columns', columns);
    forever.config.saveSync();
  }
});

app.cmd('columns set :value', cli.setColumns = function (value) {
  forever.log.info('Setting columns: ' + value.join(' ').magenta);
  columns = value;

  forever.config.set('columns', columns);
  forever.config.saveSync();
});

//
// ### function start (file)
// #### @file {string} Location of the script to spawn with forever
// Starts a forever process for the script located at `file` as non-daemon
// process.
//
// Remark: this regex matches everything. It has to be added at the end to
// make executing other commands possible.
//
app.cmd(/(.+)/, cli.start = function (file) {
  tryStart(file, {}, function () {
    var monitor = forever.start(file, {});
    monitor.on('start', function () {
      forever.startServer(monitor);
    });
  });
});

cli.startCLI = function () {
  app.init(function () {
    app.start();
  });
};

<commit_msg>[fix] Make column operations work<commit_after>/*
 * cli.js: Handlers for the forever CLI commands.
 *
 * (C) 2010 Nodejitsu Inc.
 * MIT LICENCE
 *
 */

var fs = require('fs'),
    util = require('util'),
    path = require('path'),
    flatiron = require('flatiron'),
    cliff = require('cliff'),
    daemon = require('daemon'),
    forever = require('../forever');

var cli = exports;

var help = [
  'usage: forever [action] [options] SCRIPT [script-options]',
  '',
  'Monitors the script specified in the current process or as a daemon',
  '',
  'actions:',
  '  start               Start SCRIPT as a daemon',
  '  stop                Stop the daemon SCRIPT',
  '  stopall             Stop all running forever scripts',
  '  restart             Restart the daemon SCRIPT',
  '  list                List all running forever scripts',
  '  config              Lists all forever user configuration',
  '  set <key> <val>     Sets the specified forever config <key>',
  '  clear <key>         Clears the specified forever config <key>',
  '  logs                Lists log files for all forever processes',
  '  logs <script|index> Tails the logs for <script|index>',
  '  columns add <col>   Adds the specified column to the output in `forever list`',
  '  columns rm <col>    Removed the specified column from the output in `forever list`',
  '  columns set <cols>  Set all columns for the output in `forever list`',
  '  cleanlogs           [CAREFUL] Deletes all historical forever log files',
  '',
  'options:',
  '  -m  MAX          Only run the specified script MAX times',
  '  -l  LOGFILE      Logs the forever output to LOGFILE',
  '  -o  OUTFILE      Logs stdout from child script to OUTFILE',
  '  -e  ERRFILE      Logs stderr from child script to ERRFILE',
  '  -p  PATH         Base path for all forever related files (pid files, etc.)',
  '  -c  COMMAND      COMMAND to execute (defaults to node)',
  '  -a, --append     Append logs',
  '  --pidfile        The pid file',
  '  --sourceDir      The source directory for which SCRIPT is relative to',
  '  --minUptime      Minimum uptime (millis) for a script to not be considered "spinning"',
  '  --spinSleepTime  Time to wait (millis) between launches of a spinning script.',
  '  --plain          Disable command line colors',
  '  -d, --debug      Forces forever to log debug output',
  '  -v, --verbose    Turns on the verbose messages from Forever',
  '  -s, --silent     Run the child script silencing stdout and stderr',
  '  -w, --watch      Watch for file changes',
  '  -h, --help       You\'re staring at it',
  '',
  '[Long Running Process]',
  '  The forever process will continue to run outputting log messages to the console.',
  '  ex. forever -o out.log -e err.log my-script.js',
  '',
  '[Daemon]',
  '  The forever process will run as a daemon which will make the target process start',
  '  in the background. This is extremely useful for remote starting simple node.js scripts',
  '  without using nohup. It is recommended to run start with -o -l, & -e.',
  '  ex. forever start -l forever.log -o out.log -e err.log my-daemon.js',
  '      forever stop my-daemon.js',
  ''
];

var app = flatiron.app;

app.use(flatiron.plugins.cli, {
  usage: help
});

var argvOptions = {
  'command':   {alias: 'c'},
  'errFile':   {alias: 'e'},
  'logFile':   {alias: 'l'},
  'appendLog': {alias: ['a', 'append'], boolean: true},
  'max':       {alias: 'm'},
  'outFile':   {alias: 'o'},
  'path':      {alias: 'p'},
  'silent':    {alias: 's', boolean: true},
  'verbose':   {alias: 'v', boolean: true},
  'watch':     {alias: 'w', boolean: true},
  'debug':     {alias: 'd', boolean: true},
  'plain':     {boolean: true}
};

app.use(flatiron.plugins.config, {argv: argvOptions});

var reserved = ['root', 'pidPath'];

//
// ### @private function (file, options, callback)
// #### @file {string} Target script to start
// #### @options {Object} Options to start the script with
// #### @callback {function} Continuation to respond to when complete.
// Helper function that sets up the pathing for the specified `file`
// then stats the appropriate files and responds.
//
function tryStart(file, options, callback) {
  var fullLog, fullScript;

  fullLog = forever.logFilePath(options.logFile, options.uid);
  fullScript = path.join(options.sourceDir, file);

  forever.stat(fullLog, fullScript, options.appendLog, function (err) {
    if (err) {
      forever.log.error('Cannot start forever');
      forever.log.error(err.message);
      process.exit(-1);
    }

    callback();
  });
}

//
// ### @private function updateConfig (updater)
// #### @updater {function} Function which updates the forever config
// Helper which runs the specified `updater` and then saves the forever
// config to `forever.config.get('root')`.
//
function updateConfig(updater) {
  updater();
  forever.config.save(function (err) {
    if (err) {
      return forever.log.error('Error saving config: ' + err.message);
    }

    cli.config();
    var configFile = path.join(forever.config.get('root'), 'config.json');
    forever.log.info('Forever config saved: ' + configFile.yellow);
  });
}

//
// ### @private function checkColumn (name)
// #### @name {string} Column to check
// Checks if column `name` exists
//
function checkColumn(name) {
  if (!forever.columns[name]) {
    forever.log.error('Unknown column: ' + name.magenta);
    return false;
  }
  return true;
}

//
// ### @private function getOptions (file)
// #### @file {string} File to run. **Optional**
// Returns `options` object for use with `forever.start` and
// `forever.startDaemon`
//
function getOptions(file) {
  var options = {};
  //
  // First isolate options which should be passed to file
  //
  options.options = process.argv.splice(process.argv.indexOf(file) + 1);

  //
  // Now we have to force optimist to reparse command line options because
  // we've removed some before.
  //
  app.config.system.loadArgv();

  [
    'pidFile', 'logFile', 'errFile', 'watch', 'minUptime', 'appendLog',
    'silent', 'max', 'command', 'path', 'spinSleepTime', 'sourceDir',
    'uid'
  ].forEach(function (key) {
    options[key] = app.config.get(key);
  });

  options.sourceDir = file && file[0] !== '/' ? process.cwd() : '/';
  if (options.sourceDir) {
    options.spawnWith = {cwd: options.sourceDir};
  }

  return options;
}

//
// ### function cleanLogs
// Deletes all historical forever log files
//
app.cmd('cleanlogs', cli.cleanLogs = function () {
  forever.log.silly('Tidying ' + forever.config.get('root'));
  forever.cleanUp(true).on('cleanUp', function () {
    forever.log.silly(forever.config.get('root') + ' tidied.');
  });
});

//
// ### function start (file)
// #### @file {string} Location of the script to spawn with forever
// Starts a forever process for the script located at `file` as daemon
// process.
//
app.cmd(/start (.+)/, cli.startDaemon = function (file) {
  forever.log.info('Forever processing file: ' + file.grey);
  var options = getOptions(file);
  tryStart(file, options, function () {
    var monitor = forever.startDaemon(file, options);
    monitor.on('start', function () {
      forever.startServer(monitor);
    });
  });
});

//
// ### function stop (file)
// #### @file {string} Target forever process to stop
// Stops the forever process specified by `file`.
//
app.cmd(/stop (.+)/, cli.stop = function (file) {
  var runner = forever.stop(file, true);

  runner.on('stop', function (process) {
    forever.log.info('Forever stopped process:');
    forever.log.data(process);
  });

  runner.on('error', function (err) {
    forever.log.error('Forever cannot find process with index: ' + file);
  });
});

//
// ### function stopall ()
// Stops all currently running forever processes.
//
app.cmd('stopall', cli.stopall = function () {
  var runner = forever.stopAll(true);
  runner.on('stopAll', function (processes) {
    if (processes) {
      forever.log.info('Forever stopped processes:');
      processes.split('\n').forEach(function (line) {
        forever.log.data(line);
      });
    }
    else {
      forever.log.info('No forever processes running');
    }
  });
});

//
// ### function restart (file)
// #### @file {string} Target process to restart
// Restarts the forever process specified by `file`.
//
app.cmd(/restart (.+)/, cli.restart = function (file) {
  var runner = forever.restart(file, true);
  runner.on('restart', function (processes) {
    if (processes) {
      forever.log.info('Forever restarted process(es):');
      processes.split('\n').forEach(function (line) {
        forever.log.data(line);
      });
    }
    else {
      forever.log.info('No forever processes running');
    }
  });

  runner.on('error', function (err) {
    forever.log.error('Error restarting process: ' + file.grey);
    forever.log.error(err.message);
  });
});

//
// ### function list ()
// Lists all currently running forever processes.
//
app.cmd('list', cli.list = function () {
  forever.list(true, function (err, processes) {
    if (processes) {
      forever.log.info('Forever processes running');
      processes.split('\n').forEach(function (line) {
        forever.log.data(line);
      });
    }
    else {
      forever.log.info('No forever processes running');
    }
  });
});

//
// ### function config ()
// Lists all of the configuration in `~/.forever/config.json`.
//
app.cmd('config', cli.config = function () {
  var keys = Object.keys(forever.config.store),
      conf = cliff.inspect(forever.config.store);

  if (keys.length <= 2) {
    conf = conf.replace(/\{\s/, '{ \n')
               .replace(/\}/, '\n}')
               .replace('\\033[90m', '  \\033[90m')
               .replace(/, /ig, ',\n  ');
  }
  else {
    conf = conf.replace(/\n\s{4}/ig, '\n  ');
  }

  conf.split('\n').forEach(function (line) {
    forever.log.data(line);
  });
});

//
// ### function set (key, value)
// #### @key {string} Key to set in forever config
// #### @value {string} Value to set for `key`
// Sets the specified `key` / `value` pair in the
// forever user config.
//
app.cmd('set :key :value', cli.set = function (key, value) {
  updateConfig(function () {
    forever.log.info('Setting forever config: ' + key.grey);
    forever.config.set(key, value);
  });
});

//
// ### function clear (key)
// #### @key {string} Key to remove from `~/.forever/config.json`
// Removes the specified `key` from the forever user config.
//
app.cmd('clear :key', cli.clear = function (key) {
  if (reserved.indexOf(key) !== -1) {
    forever.log.warn('Cannot clear reserved config: ' + key.grey);
    forever.log.warn('Use `forever set ' + key + '` instead');
    return;
  }

  updateConfig(function () {
    forever.log.info('Clearing forever config: ' + key.grey);
    forever.config.clear(key);
  });
});

//
// ### function logs (target)
// #### @target {string} **Optional** Target script or index to list logs for
// Displays the logs using `tail` for the specified `target`. If no `target` is
// specified then it will display log files for all running forever processes.
//
app.cmd('logs :index', cli.logs = function (index) {
  //
  // Helper function for listing all log files
  //
  function listFiles() {
    var index = 0,
        rows = [['   ', 'script', 'logfile']];

    forever.list(false, function (err, processes) {
      forever.log.info('Logs for running Forever processes');
      rows = rows.concat(processes.map(function (proc) {
        return ['[' + index++ + ']', proc.file.grey, proc.logFile.magenta];
      }));

      cliff.putRows('data', rows, ['white', 'grey', 'magenta']);
    });
  }

  if (typeof index === 'undefined') {
    return listFiles();
  }

  forever.tail(index, function (err, logs) {
    if (err) {
      return forever.log.error(err.message);
    }

    logs.forEach(function (proc) {
      forever.log.info('Showing logs for ' + proc.file.magenta);
      proc.logs.forEach(function (line) {
        forever.log.data(line);
      });
    });
  });
});

app.cmd('columns add :name', cli.addColumn = function (name) {
  if (checkColumn(name)) {
    var columns = forever.config.get('columns');

    if (~columns.indexOf(name)) {
      return forever.log.warn(name.magenta + ' already exists in forever');
    }

    forever.log.info('Adding column: ' + name.magenta);
    columns.push(name);

    forever.config.set('columns', columns);
    forever.config.saveSync();
  }
});

app.cmd('columns rm :name', cli.rmColumn = function (name) {
  if (checkColumn(name)) {
    var columns = forever.config.get('columns');

    if (!~columns.indexOf(name)) {
      return forever.log.warn(name.magenta + ' doesn\'t exist in forever');
    }

    forever.log.info('Removing column: ' + name.magenta);
    columns.splice(columns.indexOf(name), 1);

    forever.config.set('columns', columns);
    forever.config.saveSync();
  }
});

app.cmd('columns set :value', cli.setColumns = function (columns) {
  forever.log.info('Setting columns: ' + columns.join(' ').magenta);

  forever.config.set('columns', columns);
  forever.config.saveSync();
});

//
// ### function start (file)
// #### @file {string} Location of the script to spawn with forever
// Starts a forever process for the script located at `file` as non-daemon
// process.
//
// Remark: this regex matches everything. It has to be added at the end to
// make executing other commands possible.
//
app.cmd(/(.+)/, cli.start = function (file) {
  tryStart(file, {}, function () {
    var monitor = forever.start(file, {});
    monitor.on('start', function () {
      forever.startServer(monitor);
    });
  });
});

cli.startCLI = function () {
  app.init(function () {
    app.start();
  });
};

<|endoftext|>


<commit_before>/**
 * Get a travel time surface and accessibility results for a particular origin.
 * Pass in references to the query (the JS object stored in query.json), the stopTreeCache, the origin file, the
 * x and y coordinates of the origin relative to the query, what parameter you want (BEST_CASE, WORST_CASE or AVERAGE),
 * and a grid. Returns a travel time/accessibility surface which can be used by isochoroneTile and accessibilityForCutoff
 */
export default function getSurface ({grid, origin, query, stopTreeCache, which}) {
  let surface = new Uint8Array(query.width * query.height)

  let transitOffset = getTransitOffset(origin.data[0])

  // how many departure minutes are there. skip number of stops
  let nMinutes = origin.data[transitOffset + 1]

  let travelTimes = new Uint8Array(nMinutes)

  // store the accessibility at each departure minute for every possible cutoff 1 - 120
  let access = new Float64Array(nMinutes * 120)

  // x and y refer to pixel coordinates not origins here
  // loop over rows first
  for (let y = 0, pixelIdx = 0, stcOffset = 0; y < query.height; y++) {
    for (let x = 0; x < query.width; x++, pixelIdx++) {
      let nStops = stopTreeCache.data[stcOffset++]

      // can we reach this pixel without riding transit?
      let nonTransitTime = origin.getNonTransitTime(x, y)

      // fill with unreachable, or the walk distance
      travelTimes.fill(nonTransitTime)

      for (let stopIdx = 0; stopIdx < nStops; stopIdx++) {
        // read the stop ID
        let stopId = stopTreeCache.data[stcOffset++]

        // read the time (minutes)
        let time = stopTreeCache.data[stcOffset++]

        // de-delta-code times
        for (let minute = 0, previous = 0; minute < nMinutes; minute++) {
          let offset = origin.index[stopId] + minute * 2 // * 2 because path info is included as well
          let travelTimeToStop = origin.data[offset] + previous
          previous = travelTimeToStop

          if (travelTimeToStop === -1) continue

          let travelTimeToPixel = (travelTimeToStop / 60 + time) | 0

          if (travelTimeToPixel > 254) continue

          if (travelTimes[minute] > travelTimeToPixel) travelTimes[minute] = travelTimeToPixel
        }
      }

      // compute and set value for pixel
      surface[pixelIdx] = computePixelValue(which, travelTimes)

      // compute access values
      computeAccessValues(x, y, query, grid, travelTimes, access)
    }
  }

  return {
    surface,
    access,
    nMinutes
  }
}

/**
 * Where is the transit portion of the origin data there are a certain number of pixels in each direction aroudn the origin with times in them. read the radius, multiply by two to get diameter, add one because there is a pixel in the center, square to get number of pixels, add one to skip the first value which gives radius, and or with to convert to 32-bit int.
 *
 * @param {Number} radius
 * @return {Number}
 */

export function getTransitOffset (radius) {
  return (Math.pow(radius * 2 + 1, 2) + 1) | 0
}

/**
 * Get the pixel value
 *
 * @param {String} which
 * @param {Uint8Array} travelTimes
 * @return {Number} pixelValue
 */

export function computePixelValue (which, travelTimes) {
  switch (which) {
    case 'BEST_CASE':
      return computeBestPixelValue(travelTimes)
    case 'AVERAGE':
      return computeAveragePixelValue(travelTimes)
    case 'WORST_CASE':
      return computeWorstPixelValue(travelTimes)
  }
}

/**
 * Compute best pixel value
 *
 * @param {Uint8Array} travelTimes
 * @return {Number} pixel
 */

export function computeBestPixelValue (travelTimes) {
  let pixel = 255
  for (let i = 0; i < travelTimes.length; i++) {
    pixel = Math.min(pixel, travelTimes[i])
  }
  return pixel
}

/**
 * Compute average pixel value
 *
 * @param {Uint8Array} travelTimes
 * @return {Number} pixel
 */

export function computeAveragePixelValue (travelTimes) {
  let sum = 0
  let count = 0

  for (let i = 0; i < travelTimes.length; i++) {
    if (travelTimes[i] !== 255) {
      sum += travelTimes[i]
      count++
    }
  }

  // coerce to int
  if (count > travelTimes.length / 2) return (sum / count) | 0
  else return 255
}

/**
 * Compute worst pixel value
 *
 * @param {Uint8Array} travelTimes
 * @return {Number} pixel
 */

export function computeWorstPixelValue (travelTimes) {
  let pixel = 0
  for (let i = 0; i < travelTimes.length; i++) {
    pixel = Math.max(pixel, travelTimes[i])
  }
  return pixel
}

/**
 * Compute access values for x, y
 *
 * @param {Number} x
 * @param {Number} y
 * @param {Object} query
 * @param {Object} grid
 * @param {Uint8Array} travelTimes
 * @param {Float64Array} accessPerMinute
 */

export function computeAccessValues (x, y, query, grid, travelTimes, access) {
  // get value of this pixel from grid
  let gridx = (x | 0 + query.west | 0 - grid.west | 0) | 0
  let gridy = (y | 0 + query.north | 0 - grid.north | 0) | 0

  // if condition below fails we're off the grid, value is zero, don't bother with calculations
  if (gridx >= 0 && gridx < grid.width && gridy >= 0 && gridy < grid.height) {
    let val = grid.data[gridy * grid.width + gridx]

    for (let minute = 0; minute < travelTimes.length; minute++) {
      let travelTime = travelTimes[minute]

      if (travelTime !== 255) {
        // put this in all of the correct cutoff categories for this minute
        for (let cutoff = 119; cutoff >= travelTime - 1; cutoff--) {
          // TODO roll off smoothly
          access[cutoff * travelTimes.length + minute] += val
        }
      }
    }
  }
}
<commit_msg>comment<commit_after>/**
 * Get a travel time surface and accessibility results for a particular origin.
 * Pass in references to the query (the JS object stored in query.json), the stopTreeCache, the origin file, the
 * x and y coordinates of the origin relative to the query, what parameter you want (BEST_CASE, WORST_CASE or AVERAGE),
 * and a grid. Returns a travel time/accessibility surface which can be used by isochoroneTile and accessibilityForCutoff
 */
export default function getSurface ({grid, origin, query, stopTreeCache, which}) {
  let surface = new Uint8Array(query.width * query.height)

  let transitOffset = getTransitOffset(origin.data[0])

  // how many departure minutes are there. skip number of stops
  let nMinutes = origin.data[transitOffset + 1]

  let travelTimes = new Uint8Array(nMinutes)

  // store the accessibility at each departure minute for every possible cutoff 1 - 120
  let access = new Float64Array(nMinutes * 120)

  // x and y refer to pixel coordinates not origins here
  // loop over rows first
  for (let y = 0, pixelIdx = 0, stcOffset = 0; y < query.height; y++) {
    for (let x = 0; x < query.width; x++, pixelIdx++) {
      let nStops = stopTreeCache.data[stcOffset++]

      // can we reach this pixel without riding transit?
      let nonTransitTime = origin.getNonTransitTime(x, y)

      // fill with unreachable, or the walk distance
      travelTimes.fill(nonTransitTime)

      for (let stopIdx = 0; stopIdx < nStops; stopIdx++) {
        // read the stop ID
        let stopId = stopTreeCache.data[stcOffset++]

        // read the time (minutes)
        let time = stopTreeCache.data[stcOffset++]

        // de-delta-code times
        for (let minute = 0, previous = 0; minute < nMinutes; minute++) {
          let offset = origin.index[stopId] + minute * 2 // * 2 because path info is included as well
          let travelTimeToStop = origin.data[offset] + previous
          previous = travelTimeToStop

          if (travelTimeToStop === -1) continue

          let travelTimeToPixel = (travelTimeToStop / 60 + time) | 0

          if (travelTimeToPixel > 254) continue

          if (travelTimes[minute] > travelTimeToPixel) travelTimes[minute] = travelTimeToPixel
        }
      }

      // compute and set value for pixel
      surface[pixelIdx] = computePixelValue(which, travelTimes)

      // compute access values
      computeAccessValues(x, y, query, grid, travelTimes, access)
    }
  }

  return {
    surface,
    access,
    nMinutes
  }
}

/**
 * Where is the transit portion of the origin data there are a certain number of pixels in each direction aroudn the origin with times in them. read the radius, multiply by two to get diameter, add one because there is a pixel in the center, square to get number of pixels, add one to skip the first value which gives radius, and or with to convert to 32-bit int.
 *
 * @param {Number} radius
 * @return {Number}
 */

export function getTransitOffset (radius) {
  return (Math.pow(radius * 2 + 1, 2) + 1) | 0
}

/**
 * Get the pixel value
 *
 * @param {String} which
 * @param {Uint8Array} travelTimes
 * @return {Number} pixelValue
 */

export function computePixelValue (which, travelTimes) {
  switch (which) {
    case 'BEST_CASE':
      return computeBestPixelValue(travelTimes)
    case 'AVERAGE':
      return computeAveragePixelValue(travelTimes)
    case 'WORST_CASE':
      return computeWorstPixelValue(travelTimes)
  }
}

/**
 * Compute best pixel value
 *
 * @param {Uint8Array} travelTimes
 * @return {Number} pixel
 */

export function computeBestPixelValue (travelTimes) {
  let pixel = 255
  for (let i = 0; i < travelTimes.length; i++) {
    pixel = Math.min(pixel, travelTimes[i])
  }
  return pixel
}

/**
 * Compute average pixel value
 *
 * @param {Uint8Array} travelTimes
 * @return {Number} pixel
 */

export function computeAveragePixelValue (travelTimes) {
  let sum = 0
  let count = 0

  for (let i = 0; i < travelTimes.length; i++) {
    if (travelTimes[i] !== 255) {
      sum += travelTimes[i]
      count++
    }
  }

  // coerce to int
  if (count > travelTimes.length / 2) return (sum / count) | 0
  else return 255
}

/**
 * Compute worst pixel value
 *
 * @param {Uint8Array} travelTimes
 * @return {Number} pixel
 */

export function computeWorstPixelValue (travelTimes) {
  let pixel = 0
  for (let i = 0; i < travelTimes.length; i++) {
    pixel = Math.max(pixel, travelTimes[i])
  }
  return pixel
}

/**
 * Compute access values for x, y
 *
 * @param {Number} x
 * @param {Number} y
 * @param {Object} query
 * @param {Object} grid
 * @param {Uint8Array} travelTimes
 * @param {Float64Array} accessPerMinute
 */

export function computeAccessValues (x, y, query, grid, travelTimes, access) {
  // get value of this pixel from grid
  // or everything with 0 so that v8 can do type inference
  let gridx = (x | 0 + query.west | 0 - grid.west | 0) | 0
  let gridy = (y | 0 + query.north | 0 - grid.north | 0) | 0

  // if condition below fails we're off the grid, value is zero, don't bother with calculations
  if (gridx >= 0 && gridx < grid.width && gridy >= 0 && gridy < grid.height) {
    let val = grid.data[gridy * grid.width + gridx]

    for (let minute = 0; minute < travelTimes.length; minute++) {
      let travelTime = travelTimes[minute]

      if (travelTime !== 255) {
        // put this in all of the correct cutoff categories for this minute
        for (let cutoff = 119; cutoff >= travelTime - 1; cutoff--) {
          // TODO roll off smoothly
          access[cutoff * travelTimes.length + minute] += val
        }
      }
    }
  }
}
<|endoftext|>