Created
December 9, 2014 12:07
-
-
Save skrat/044f2390db5a011cd5c6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
require = (function e(t, n, r) { | |
function s(o, u) { | |
if (!n[o]) { | |
if (!t[o]) { | |
var a = typeof require == "function" && require;if (!u && a) return a(o, !0);if (i) return i(o, !0);throw new Error("Cannot find module '" + o + "'"); | |
}var f = n[o] = { exports: {} };t[o][0].call(f.exports, function (e) { | |
var n = t[o][1][e];return s(n ? n : e); | |
}, f, f.exports, e, t, n, r); | |
}return n[o].exports; | |
}var i = typeof require == "function" && require;for (var o = 0; o < r.length; o++) s(r[o]);return s; | |
})({ 1: [function (require, module, exports) { | |
"use strict"; | |
var buffers = require("./impl/buffers"); | |
var channels = require("./impl/channels"); | |
var select = require("./impl/select"); | |
var process = require("./impl/process"); | |
var timers = require("./impl/timers"); | |
function spawn(gen, returnChannel) { | |
if (returnChannel) { | |
var ch = channels.chan(buffers.fixed(1)); | |
(new process.Process(gen, function (value) { | |
process.put_then_callback(ch, value, function (ok) { | |
ch.close(); | |
}); | |
})).run(); | |
return ch; | |
} else { | |
(new process.Process(gen)).run(); | |
return null; | |
} | |
}; | |
function go(f, args, returnChannel) { | |
var gen = f.apply(null, args); | |
return spawn(gen, returnChannel); | |
}; | |
function chan(bufferOrNumber) { | |
var buf; | |
if (bufferOrNumber === 0) { | |
bufferOrNumber = null; | |
} | |
if (typeof bufferOrNumber === "number") { | |
buf = buffers.fixed(bufferOrNumber); | |
} else { | |
buf = bufferOrNumber; | |
} | |
return channels.chan(buf); | |
}; | |
module.exports = { | |
buffers: { | |
fixed: buffers.fixed, | |
dropping: buffers.dropping, | |
sliding: buffers.sliding | |
}, | |
spawn: spawn, | |
go: go, | |
chan: chan, | |
DEFAULT: select.DEFAULT, | |
CLOSED: channels.CLOSED, | |
put: process.put, | |
take: process.take, | |
sleep: process.sleep, | |
alts: process.alts, | |
putAsync: process.put_then_callback, | |
takeAsync: process.take_then_callback, | |
timeout: timers.timeout | |
}; | |
}, { "./impl/buffers": 5, "./impl/channels": 6, "./impl/process": 8, "./impl/select": 9, "./impl/timers": 10 }], i2jwt0: [function (require, module, exports) { | |
"use strict"; | |
var csp = require("./csp.core"); | |
var operations = require("./csp.operations"); | |
csp.operations = operations; | |
module.exports = csp; | |
}, { "./csp.core": 1, "./csp.operations": 4 }], csp: [function (require, module, exports) { | |
module.exports = require("i2jwt0"); | |
}, {}], 4: [function (require, module, exports) { | |
var mapcat = regeneratorRuntime.mark(function mapcat(f, src, dst) { | |
var value, seq, length, i; | |
return regeneratorRuntime.wrap(function mapcat$(_context2) { | |
while (true) switch (_context2.prev = _context2.next) { | |
case 0: | |
if (!true) { | |
_context2.next = 22; | |
break; | |
} | |
_context2.next = 3; | |
return take(src); | |
case 3: value = _context2.sent; | |
if (!(value === CLOSED)) { | |
_context2.next = 9; | |
break; | |
} | |
dst.close(); | |
return _context2.abrupt("break", 22); | |
case 9: seq = f(value); | |
length = seq.length; | |
i = 0; | |
case 12: | |
if (!(i < length)) { | |
_context2.next = 18; | |
break; | |
} | |
_context2.next = 15; | |
return put(dst, seq[i]); | |
case 15: i++; | |
_context2.next = 12; | |
break; | |
case 18: | |
if (!dst.is_closed()) { | |
_context2.next = 20; | |
break; | |
} | |
return _context2.abrupt("break", 22); | |
case 20: _context2.next = 0; | |
break; | |
case 22: | |
case "end": return _context2.stop(); | |
} | |
}, mapcat, this); | |
}); | |
"use strict"; | |
var Box = require("./impl/channels").Box; | |
var csp = require("./csp.core"), go = csp.go, take = csp.take, put = csp.put, takeAsync = csp.takeAsync, putAsync = csp.putAsync, alts = csp.alts, chan = csp.chan, CLOSED = csp.CLOSED; | |
function noOp(v) {} | |
function mapFrom(f, ch) { | |
return { | |
is_closed: function () { | |
return ch.is_closed(); | |
}, | |
close: function () { | |
ch.close(); | |
}, | |
_put: function (value, handler) { | |
return ch._put(value, handler); | |
}, | |
_take: function (handler) { | |
var result = ch._take({ | |
is_active: function () { | |
return handler.is_active(); | |
}, | |
commit: function () { | |
var take_cb = handler.commit(); | |
return function (value) { | |
return take_cb(value === CLOSED ? CLOSED : f(value)); | |
}; | |
} | |
}); | |
if (result) { | |
var value = result.value; | |
return new Box(value === CLOSED ? CLOSED : f(value)); | |
} else { | |
return null; | |
} | |
} | |
}; | |
} | |
function mapInto(f, ch) { | |
return { | |
is_closed: function () { | |
return ch.is_closed(); | |
}, | |
close: function () { | |
ch.close(); | |
}, | |
_put: function (value, handler) { | |
return ch._put(f(value), handler); | |
}, | |
_take: function (handler) { | |
return ch._take(handler); | |
} | |
}; | |
} | |
function filterFrom(p, ch, bufferOrN) { | |
var out = chan(bufferOrN); | |
go(regeneratorRuntime.mark(function _callee() { | |
var value; | |
return regeneratorRuntime.wrap(function _callee$(_context) { | |
while (true) switch (_context.prev = _context.next) { | |
case 0: | |
if (!true) { | |
_context.next = 12; | |
break; | |
} | |
_context.next = 3; | |
return take(ch); | |
case 3: value = _context.sent; | |
if (!(value === CLOSED)) { | |
_context.next = 7; | |
break; | |
} | |
out.close(); | |
return _context.abrupt("break", 12); | |
case 7: | |
if (!p(value)) { | |
_context.next = 10; | |
break; | |
} | |
_context.next = 10; | |
return put(out, value); | |
case 10: _context.next = 0; | |
break; | |
case 12: | |
case "end": return _context.stop(); | |
} | |
}, _callee, this); | |
})); | |
return out; | |
} | |
function filterInto(p, ch) { | |
return { | |
is_closed: function () { | |
return ch.is_closed(); | |
}, | |
close: function () { | |
ch.close(); | |
}, | |
_put: function (value, handler) { | |
if (p(value)) { | |
return ch._put(value, handler); | |
} else { | |
return new Box(!ch.is_closed()); | |
} | |
}, | |
_take: function (handler) { | |
return ch._take(handler); | |
} | |
}; | |
} | |
function removeFrom(p, ch) { | |
return filterFrom(function (value) { | |
return !p(value); | |
}, ch); | |
} | |
function removeInto(p, ch) { | |
return filterInto(function (value) { | |
return !p(value); | |
}, ch); | |
} | |
function mapcatFrom(f, ch, bufferOrN) { | |
var out = chan(bufferOrN); | |
go(mapcat, [f, ch, out]); | |
return out; | |
} | |
function mapcatInto(f, ch, bufferOrN) { | |
var src = chan(bufferOrN); | |
go(mapcat, [f, src, ch]); | |
return src; | |
} | |
function pipe(src, dst, keepOpen) { | |
go(regeneratorRuntime.mark(function _callee2() { | |
var value; | |
return regeneratorRuntime.wrap(function _callee2$(_context3) { | |
while (true) switch (_context3.prev = _context3.next) { | |
case 0: | |
if (!true) { | |
_context3.next = 13; | |
break; | |
} | |
_context3.next = 3; | |
return take(src); | |
case 3: value = _context3.sent; | |
if (!(value === CLOSED)) { | |
_context3.next = 7; | |
break; | |
} | |
if (!keepOpen) { | |
dst.close(); | |
} | |
return _context3.abrupt("break", 13); | |
case 7: _context3.next = 9; | |
return put(dst, value); | |
case 9: | |
if (_context3.sent) { | |
_context3.next = 11; | |
break; | |
} | |
return _context3.abrupt("break", 13); | |
case 11: _context3.next = 0; | |
break; | |
case 13: | |
case "end": return _context3.stop(); | |
} | |
}, _callee2, this); | |
})); | |
return dst; | |
} | |
function split(p, ch, trueBufferOrN, falseBufferOrN) { | |
var tch = chan(trueBufferOrN); | |
var fch = chan(falseBufferOrN); | |
go(regeneratorRuntime.mark(function _callee3() { | |
var value; | |
return regeneratorRuntime.wrap(function _callee3$(_context4) { | |
while (true) switch (_context4.prev = _context4.next) { | |
case 0: | |
if (!true) { | |
_context4.next = 12; | |
break; | |
} | |
_context4.next = 3; | |
return take(ch); | |
case 3: value = _context4.sent; | |
if (!(value === CLOSED)) { | |
_context4.next = 8; | |
break; | |
} | |
tch.close(); | |
fch.close(); | |
return _context4.abrupt("break", 12); | |
case 8: _context4.next = 10; | |
return put(p(value) ? tch : fch, value); | |
case 10: _context4.next = 0; | |
break; | |
case 12: | |
case "end": return _context4.stop(); | |
} | |
}, _callee3, this); | |
})); | |
return [tch, fch]; | |
} | |
function reduce(f, init, ch) { | |
return go(regeneratorRuntime.mark(function _callee4() { | |
var result, value; | |
return regeneratorRuntime.wrap(function _callee4$(_context5) { | |
while (true) switch (_context5.prev = _context5.next) { | |
case 0: result = init; | |
case 1: | |
if (!true) { | |
_context5.next = 12; | |
break; | |
} | |
_context5.next = 4; | |
return take(ch); | |
case 4: value = _context5.sent; | |
if (!(value === CLOSED)) { | |
_context5.next = 9; | |
break; | |
} | |
return _context5.abrupt("return", result); | |
case 9: | |
result = f(result, value); | |
case 10: _context5.next = 1; | |
break; | |
case 12: | |
case "end": return _context5.stop(); | |
} | |
}, _callee4, this); | |
}), [], true); | |
} | |
function onto(ch, coll, keepOpen) { | |
return go(regeneratorRuntime.mark(function _callee5() { | |
var length, i; | |
return regeneratorRuntime.wrap(function _callee5$(_context6) { | |
while (true) switch (_context6.prev = _context6.next) { | |
case 0: length = coll.length; | |
i = 0; | |
case 2: | |
if (!(i < length)) { | |
_context6.next = 8; | |
break; | |
} | |
_context6.next = 5; | |
return put(ch, coll[i]); | |
case 5: i++; | |
_context6.next = 2; | |
break; | |
case 8: | |
if (!keepOpen) { | |
ch.close(); | |
} | |
case 9: | |
case "end": return _context6.stop(); | |
} | |
}, _callee5, this); | |
})); | |
} | |
// TODO: Bounded? | |
function fromColl(coll) { | |
var ch = chan(coll.length); | |
onto(ch, coll); | |
return ch; | |
} | |
function map(f, chs, bufferOrN) { | |
var out = chan(bufferOrN); | |
var length = chs.length; | |
// Array holding 1 round of values | |
var values = new Array(length); | |
// TODO: Not sure why we need a size-1 buffer here | |
var dchan = chan(1); | |
// How many more items this round | |
var dcount; | |
// put callbacks for each channel | |
var dcallbacks = new Array(length); | |
for (var i = 0; i < length; i++) { | |
dcallbacks[i] = (function (i) { | |
return function (value) { | |
values[i] = value; | |
dcount--; | |
if (dcount === 0) { | |
putAsync(dchan, values.slice(0), noOp); | |
} | |
}; | |
}(i)); | |
} | |
go(regeneratorRuntime.mark(function _callee6() { | |
var i, values; | |
return regeneratorRuntime.wrap(function _callee6$(_context7) { | |
while (true) switch (_context7.prev = _context7.next) { | |
case 0: | |
if (!true) { | |
_context7.next = 18; | |
break; | |
} | |
dcount = length; | |
// We could just launch n goroutines here, but for effciency we | |
// don't | |
for (i = 0; i < length; i++) { | |
try { | |
takeAsync(chs[i], dcallbacks[i]); | |
} catch (e) { | |
// FIX: Hmm why catching here? | |
dcount--; | |
} | |
} | |
_context7.next = 5; | |
return take(dchan); | |
case 5: values = _context7.sent; | |
i = 0; | |
case 7: | |
if (!(i < length)) { | |
_context7.next = 14; | |
break; | |
} | |
if (!(values[i] === CLOSED)) { | |
_context7.next = 11; | |
break; | |
} | |
out.close(); | |
return _context7.abrupt("return"); | |
case 11: i++; | |
_context7.next = 7; | |
break; | |
case 14: _context7.next = 16; | |
return put(out, f.apply(null, values)); | |
case 16: _context7.next = 0; | |
break; | |
case 18: | |
case "end": return _context7.stop(); | |
} | |
}, _callee6, this); | |
})); | |
return out; | |
} | |
function merge(chs, bufferOrN) { | |
var out = chan(bufferOrN); | |
var actives = chs.slice(0); | |
go(regeneratorRuntime.mark(function _callee7() { | |
var r, value, i; | |
return regeneratorRuntime.wrap(function _callee7$(_context8) { | |
while (true) switch (_context8.prev = _context8.next) { | |
case 0: | |
if (!true) { | |
_context8.next = 15; | |
break; | |
} | |
if (!(actives.length === 0)) { | |
_context8.next = 3; | |
break; | |
} | |
return _context8.abrupt("break", 15); | |
case 3: _context8.next = 5; | |
return alts(actives); | |
case 5: r = _context8.sent; | |
value = r.value; | |
if (!(value === CLOSED)) { | |
_context8.next = 11; | |
break; | |
} | |
i = actives.indexOf(r.channel); | |
actives.splice(i, 1); | |
return _context8.abrupt("continue", 0); | |
case 11: _context8.next = 13; | |
return put(out, value); | |
case 13: _context8.next = 0; | |
break; | |
case 15: | |
out.close(); | |
case 16: | |
case "end": return _context8.stop(); | |
} | |
}, _callee7, this); | |
})); | |
return out; | |
} | |
function into(coll, ch) { | |
var result = coll.slice(0); | |
return reduce(function (result, item) { | |
result.push(item); | |
return result; | |
}, result, ch); | |
} | |
function takeN(n, ch, bufferOrN) { | |
var out = chan(bufferOrN); | |
go(regeneratorRuntime.mark(function _callee8() { | |
var i, value; | |
return regeneratorRuntime.wrap(function _callee8$(_context9) { | |
while (true) switch (_context9.prev = _context9.next) { | |
case 0: i = 0; | |
case 1: | |
if (!(i < n)) { | |
_context9.next = 12; | |
break; | |
} | |
_context9.next = 4; | |
return take(ch); | |
case 4: value = _context9.sent; | |
if (!(value === CLOSED)) { | |
_context9.next = 7; | |
break; | |
} | |
return _context9.abrupt("break", 12); | |
case 7: _context9.next = 9; | |
return put(out, value); | |
case 9: i++; | |
_context9.next = 1; | |
break; | |
case 12: | |
out.close(); | |
case 13: | |
case "end": return _context9.stop(); | |
} | |
}, _callee8, this); | |
})); | |
return out; | |
} | |
var NOTHING = {}; | |
function unique(ch, bufferOrN) { | |
var out = chan(bufferOrN); | |
var last = NOTHING; | |
go(regeneratorRuntime.mark(function _callee9() { | |
var value; | |
return regeneratorRuntime.wrap(function _callee9$(_context10) { | |
while (true) switch (_context10.prev = _context10.next) { | |
case 0: | |
if (!true) { | |
_context10.next = 13; | |
break; | |
} | |
_context10.next = 3; | |
return take(ch); | |
case 3: value = _context10.sent; | |
if (!(value === CLOSED)) { | |
_context10.next = 6; | |
break; | |
} | |
return _context10.abrupt("break", 13); | |
case 6: | |
if (!(value === last)) { | |
_context10.next = 8; | |
break; | |
} | |
return _context10.abrupt("continue", 0); | |
case 8: | |
last = value; | |
_context10.next = 11; | |
return put(out, value); | |
case 11: _context10.next = 0; | |
break; | |
case 13: | |
out.close(); | |
case 14: | |
case "end": return _context10.stop(); | |
} | |
}, _callee9, this); | |
})); | |
return out; | |
} | |
function partitionBy(f, ch, bufferOrN) { | |
var out = chan(bufferOrN); | |
var part = []; | |
var last = NOTHING; | |
go(regeneratorRuntime.mark(function _callee10() { | |
var value, newItem; | |
return regeneratorRuntime.wrap(function _callee10$(_context11) { | |
while (true) switch (_context11.prev = _context11.next) { | |
case 0: | |
if (!true) { | |
_context11.next = 23; | |
break; | |
} | |
_context11.next = 3; | |
return take(ch); | |
case 3: value = _context11.sent; | |
if (!(value === CLOSED)) { | |
_context11.next = 12; | |
break; | |
} | |
if (!(part.length > 0)) { | |
_context11.next = 8; | |
break; | |
} | |
_context11.next = 8; | |
return put(out, part); | |
case 8: | |
out.close(); | |
return _context11.abrupt("break", 23); | |
case 12: newItem = f(value); | |
if (!(newItem === last || last === NOTHING)) { | |
_context11.next = 17; | |
break; | |
} | |
part.push(value); | |
_context11.next = 20; | |
break; | |
case 17: _context11.next = 19; | |
return put(out, part); | |
case 19: | |
part = [value]; | |
case 20: | |
last = newItem; | |
case 21: _context11.next = 0; | |
break; | |
case 23: | |
case "end": return _context11.stop(); | |
} | |
}, _callee10, this); | |
})); | |
return out; | |
} | |
function partition(n, ch, bufferOrN) { | |
var out = chan(bufferOrN); | |
go(regeneratorRuntime.mark(function _callee11() { | |
var part, i, value; | |
return regeneratorRuntime.wrap(function _callee11$(_context12) { | |
while (true) switch (_context12.prev = _context12.next) { | |
case 0: | |
if (!true) { | |
_context12.next = 21; | |
break; | |
} | |
part = new Array(n); | |
i = 0; | |
case 3: | |
if (!(i < n)) { | |
_context12.next = 17; | |
break; | |
} | |
_context12.next = 6; | |
return take(ch); | |
case 6: value = _context12.sent; | |
if (!(value === CLOSED)) { | |
_context12.next = 13; | |
break; | |
} | |
if (!(i > 0)) { | |
_context12.next = 11; | |
break; | |
} | |
_context12.next = 11; | |
return put(out, part.slice(0, i)); | |
case 11: | |
out.close(); | |
return _context12.abrupt("return"); | |
case 13: | |
part[i] = value; | |
case 14: i++; | |
_context12.next = 3; | |
break; | |
case 17: _context12.next = 19; | |
return put(out, part); | |
case 19: _context12.next = 0; | |
break; | |
case 21: | |
case "end": return _context12.stop(); | |
} | |
}, _callee11, this); | |
})); | |
return out; | |
} | |
module.exports = { | |
mapFrom: mapFrom, | |
mapInto: mapInto, | |
filterFrom: filterFrom, | |
filterInto: filterInto, | |
removeFrom: removeFrom, | |
removeInto: removeInto, | |
mapcatFrom: mapcatFrom, | |
mapcatInto: mapcatInto, | |
pipe: pipe, | |
split: split, | |
reduce: reduce, | |
onto: onto, | |
fromColl: fromColl, | |
map: map, | |
merge: merge, | |
into: into, | |
take: takeN, | |
unique: unique, | |
partition: partition, | |
partitionBy: partitionBy | |
}; | |
// Possible "fluid" interfaces: | |
// thread( | |
// [fromColl, [1, 2, 3, 4]], | |
// [mapFrom, inc], | |
// [into, []] | |
// ) | |
// thread( | |
// [fromColl, [1, 2, 3, 4]], | |
// [mapFrom, inc, _], | |
// [into, [], _] | |
// ) | |
// wrap() | |
// .fromColl([1, 2, 3, 4]) | |
// .mapFrom(inc) | |
// .into([]) | |
// .unwrap(); | |
}, { "./csp.core": 1, "./impl/channels": 6 }], 5: [function (require, module, exports) { | |
"use strict"; | |
// TODO: Consider EmptyError & FullError to avoid redundant bound | |
// checks, to improve performance (may need benchmarks) | |
function acopy(src, src_start, dst, dst_start, length) { | |
var count = 0; | |
while (true) { | |
if (count >= length) { | |
break; | |
} | |
dst[dst_start + count] = src[src_start + count]; | |
count++; | |
} | |
} | |
var EMPTY = { | |
toString: function () { | |
return "[object EMPTY]"; | |
} | |
}; | |
var RingBuffer = function (head, tail, length, array) { | |
this.length = length; | |
this.array = array; | |
this.head = head; | |
this.tail = tail; | |
}; | |
// Internal method, callers must do bound check | |
RingBuffer.prototype._unshift = function (item) { | |
var array = this.array; | |
var head = this.head; | |
array[head] = item; | |
this.head = (head + 1) % array.length; | |
this.length++; | |
}; | |
RingBuffer.prototype._resize = function () { | |
var array = this.array; | |
var new_length = 2 * array.length; | |
var new_array = new Array(new_length); | |
var head = this.head; | |
var tail = this.tail; | |
var length = this.length; | |
if (tail < head) { | |
acopy(array, tail, new_array, 0, length); | |
this.tail = 0; | |
this.head = length; | |
this.array = new_array; | |
} else if (tail > head) { | |
acopy(array, tail, new_array, 0, array.length - tail); | |
acopy(array, 0, new_array, array.length - tail, head); | |
this.tail = 0; | |
this.head = length; | |
this.array = new_array; | |
} else if (tail === head) { | |
this.tail = 0; | |
this.head = 0; | |
this.array = new_array; | |
} | |
}; | |
RingBuffer.prototype.unbounded_unshift = function (item) { | |
if (this.length + 1 === this.array.length) { | |
this._resize(); | |
} | |
this._unshift(item); | |
}; | |
RingBuffer.prototype.pop = function () { | |
if (this.length === 0) { | |
return EMPTY; | |
} | |
var array = this.array; | |
var tail = this.tail; | |
var item = array[tail]; | |
array[tail] = null; | |
this.tail = (tail + 1) % array.length; | |
this.length--; | |
return item; | |
}; | |
RingBuffer.prototype.cleanup = function (predicate) { | |
var length = this.length; | |
for (var i = 0; i < length; i++) { | |
var item = this.pop(); | |
if (predicate(item)) { | |
this._unshift(item); | |
} | |
} | |
}; | |
var FixedBuffer = function (buf, n) { | |
this.buf = buf; | |
this.n = n; | |
}; | |
FixedBuffer.prototype.is_full = function () { | |
return this.buf.length == this.n; | |
}; | |
FixedBuffer.prototype.remove = function () { | |
return this.buf.pop(); | |
}; | |
FixedBuffer.prototype.add = function (item) { | |
if (this.is_full()) { | |
throw new Error("Can't add to a full buffer"); | |
} | |
this.buf._unshift(item); | |
}; | |
FixedBuffer.prototype.count = function () { | |
return this.buf.length; | |
}; | |
var DroppingBuffer = function (buf, n) { | |
this.buf = buf; | |
this.n = n; | |
}; | |
DroppingBuffer.prototype.is_full = function () { | |
return false; | |
}; | |
DroppingBuffer.prototype.remove = function () { | |
return this.buf.pop(); | |
}; | |
DroppingBuffer.prototype.add = function (item) { | |
if (this.buf.length < this.n) { | |
this.buf._unshift(item); | |
} | |
}; | |
DroppingBuffer.prototype.count = function () { | |
return this.buf.length; | |
}; | |
var SlidingBuffer = function (buf, n) { | |
this.buf = buf; | |
this.n = n; | |
}; | |
SlidingBuffer.prototype.is_full = function () { | |
return false; | |
}; | |
SlidingBuffer.prototype.remove = function () { | |
return this.buf.pop(); | |
}; | |
SlidingBuffer.prototype.add = function (item) { | |
if (this.buf.length === this.n) { | |
this.buf.pop(); | |
} | |
this.buf._unshift(item); | |
}; | |
SlidingBuffer.prototype.count = function () { | |
return this.buf.length; | |
}; | |
var ring = exports.ring = function ring_buffer(n) { | |
return new RingBuffer(0, 0, 0, new Array(n)); | |
}; | |
exports.fixed = function fixed_buffer(n) { | |
return new FixedBuffer(ring(n), n); | |
}; | |
exports.dropping = function dropping_buffer(n) { | |
return new DroppingBuffer(ring(n), n); | |
}; | |
exports.sliding = function sliding_buffer(n) { | |
return new SlidingBuffer(ring(n), n); | |
}; | |
exports.EMPTY = EMPTY; | |
}, {}], 6: [function (require, module, exports) { | |
"use strict"; | |
var buffers = require("./buffers"); | |
var dispatch = require("./dispatch"); | |
var MAX_DIRTY = 64; | |
var MAX_QUEUE_SIZE = 1024; | |
var CLOSED = null; | |
var Box = function (value) { | |
this.value = value; | |
}; | |
var PutBox = function (handler, value) { | |
this.handler = handler; | |
this.value = value; | |
}; | |
var Channel = function (takes, puts, buf) { | |
this.buf = buf; | |
this.takes = takes; | |
this.puts = puts; | |
this.dirty_takes = 0; | |
this.dirty_puts = 0; | |
this.closed = false; | |
}; | |
Channel.prototype._put = function (value, handler) { | |
if (value === CLOSED) { | |
throw new Error("Cannot put CLOSED on a channel."); | |
} | |
if (this.closed || !handler.is_active()) { | |
return new Box(!this.closed); | |
} | |
while (true) { | |
var taker = this.takes.pop(); | |
if (taker !== buffers.EMPTY) { | |
if (taker.is_active()) { | |
var callback = taker.commit(); | |
handler.commit(); | |
dispatch.run(function () { | |
callback(value); | |
}); | |
return new Box(true); | |
} else { | |
continue; | |
} | |
} else { | |
if (this.buf && !this.buf.is_full()) { | |
handler.commit(); | |
this.buf.add(value); | |
return new Box(true); | |
} else { | |
if (this.dirty_puts > MAX_DIRTY) { | |
this.puts.cleanup(function (putter) { | |
return putter.handler.is_active(); | |
}); | |
this.dirty_puts = 0; | |
} else { | |
this.dirty_puts++; | |
} | |
if (this.puts.length >= MAX_QUEUE_SIZE) { | |
throw new Error("No more than " + MAX_QUEUE_SIZE + " pending puts are allowed on a single channel."); | |
} | |
this.puts.unbounded_unshift(new PutBox(handler, value)); | |
} | |
} | |
break; | |
} | |
return null; | |
}; | |
Channel.prototype._take = function (handler) { | |
if (!handler.is_active()) { | |
return null; | |
} | |
if (this.buf && this.buf.count() > 0) { | |
handler.commit(); | |
return new Box(this.buf.remove()); | |
} | |
while (true) { | |
var putter = this.puts.pop(); | |
if (putter !== buffers.EMPTY) { | |
var put_handler = putter.handler; | |
if (put_handler.is_active()) { | |
handler.commit(); | |
var callback = put_handler.commit(); | |
dispatch.run(function () { | |
callback(true); | |
}); | |
return new Box(putter.value); | |
} else { | |
continue; | |
} | |
} else { | |
if (this.closed) { | |
handler.commit(); | |
return new Box(CLOSED); | |
} else { | |
if (this.dirty_takes > MAX_DIRTY) { | |
this.takes.cleanup(function (handler) { | |
return handler.is_active(); | |
}); | |
this.dirty_takes = 0; | |
} else { | |
this.dirty_takes++; | |
} | |
if (this.takes.length >= MAX_QUEUE_SIZE) { | |
throw new Error("No more than " + MAX_QUEUE_SIZE + " pending takes are allowed on a single channel."); | |
} | |
this.takes.unbounded_unshift(handler); | |
} | |
} | |
break; | |
} | |
return null; | |
}; | |
Channel.prototype.close = function () { | |
if (this.closed) { | |
return; | |
} | |
this.closed = true; | |
while (true) { | |
var taker = this.takes.pop(); | |
if (taker === buffers.EMPTY) { | |
break; | |
} | |
if (taker.is_active()) { | |
var callback = taker.commit(); | |
dispatch.run(function () { | |
callback(CLOSED); | |
}); | |
} | |
} | |
// TODO: Tests | |
while (true) { | |
var putter = this.puts.pop(); | |
if (putter === buffers.EMPTY) { | |
break; | |
} | |
if (putter.handler.is_active()) { | |
var put_callback = putter.handler.commit(); | |
dispatch.run(function () { | |
put_callback(false); | |
}); | |
} | |
} | |
}; | |
Channel.prototype.is_closed = function () { | |
return this.closed; | |
}; | |
exports.chan = function (buf) { | |
return new Channel(buffers.ring(32), buffers.ring(32), buf); | |
}; | |
exports.Box = Box; | |
exports.CLOSED = CLOSED; | |
}, { "./buffers": 5, "./dispatch": 7 }], 7: [function (require, module, exports) { | |
"use strict"; | |
// TODO: Use process.nextTick if it's available since it's more | |
// efficient | |
// http://howtonode.org/understanding-process-next-tick | |
// Maybe we don't even need to queue ourselves in that case? | |
// XXX: But http://blog.nodejs.org/2013/03/11/node-v0-10-0-stable/ | |
// Looks like it will blow up the stack (or is that just about | |
// pre-empting IO (but that's already bad enough IMO)?) | |
// Looks like | |
// http://nodejs.org/api/process.html#process_process_nexttick_callback | |
// is the equivalent of our TASK_BATCH_SIZE | |
var buffers = require("./buffers"); | |
var TASK_BATCH_SIZE = 1024; | |
var tasks = buffers.ring(32); | |
var running = false; | |
var queued = false; | |
var queue_dispatcher; | |
function process_messages() { | |
running = true; | |
queued = false; | |
var count = 0; | |
while (true) { | |
var task = tasks.pop(); | |
if (task === buffers.EMPTY) { | |
break; | |
} | |
// TODO: Don't we need a try/finally here? | |
task(); | |
if (count >= TASK_BATCH_SIZE) { | |
break; | |
} | |
count++; | |
} | |
running = false; | |
if (tasks.length > 0) { | |
queue_dispatcher(); | |
} | |
} | |
if (typeof MessageChannel !== "undefined") { | |
var message_channel = new MessageChannel(); | |
message_channel.port1.onmessage = function (_) { | |
process_messages(); | |
}; | |
queue_dispatcher = function () { | |
if (!(queued && running)) { | |
queued = true; | |
message_channel.port2.postMessage(0); | |
} | |
}; | |
} else if (typeof setImmediate !== "undefined") { | |
queue_dispatcher = function () { | |
if (!(queued && running)) { | |
queued = true; | |
setImmediate(process_messages); | |
} | |
}; | |
} else { | |
queue_dispatcher = function () { | |
if (!(queued && running)) { | |
queued = true; | |
setTimeout(process_messages, 0); | |
} | |
}; | |
} | |
exports.run = function (f) { | |
tasks.unbounded_unshift(f); | |
queue_dispatcher(); | |
}; | |
exports.queue_delay = function (f, delay) { | |
setTimeout(f, delay); | |
}; | |
}, { "./buffers": 5 }], 8: [function (require, module, exports) { | |
"use strict"; | |
var dispatch = require("./dispatch"); | |
var select = require("./select"); | |
var FnHandler = function (f) { | |
this.f = f; | |
}; | |
FnHandler.prototype.is_active = function () { | |
return true; | |
}; | |
FnHandler.prototype.commit = function () { | |
return this.f; | |
}; | |
function put_then_callback(channel, value, callback) { | |
var result = channel._put(value, new FnHandler(callback)); | |
if (result) { | |
callback(result.value); | |
} | |
} | |
function take_then_callback(channel, callback) { | |
var result = channel._take(new FnHandler(callback)); | |
if (result) { | |
callback(result.value); | |
} | |
} | |
var Process = function (gen, onFinish) { | |
this.gen = gen; | |
this.finished = false; | |
this.onFinish = onFinish; | |
}; | |
var Instruction = function (op, data) { | |
this.op = op; | |
this.data = data; | |
}; | |
var TAKE = "take"; | |
var PUT = "put"; | |
var SLEEP = "sleep"; | |
var ALTS = "alts"; | |
// TODO FIX XXX: This is a (probably) temporary hack to avoid blowing | |
// up the stack, but it means double queueing when the value is not | |
// immediately available | |
Process.prototype._continue = function (response) { | |
var self = this; | |
dispatch.run(function () { | |
self.run(response); | |
}); | |
}; | |
Process.prototype._done = function (value) { | |
if (!this.finished) { | |
this.finished = true; | |
var onFinish = this.onFinish; | |
if (typeof onFinish === "function") { | |
dispatch.run(function () { | |
onFinish(value); | |
}); | |
} | |
} | |
}; | |
Process.prototype.run = function (response) { | |
if (this.finished) { | |
return; | |
} | |
// TODO: Shouldn't we (optionally) stop error propagation here (and | |
// signal the error through a channel or something)? Otherwise the | |
// uncaught exception will crash some runtimes (e.g. Node) | |
var iter = this.gen.next(response); | |
if (iter.done) { | |
this._done(iter.value); | |
return; | |
} | |
var ins = iter.value; | |
if (ins instanceof Instruction) { | |
var self = this; | |
switch (ins.op) { | |
case PUT: | |
var data = ins.data; | |
put_then_callback(data.channel, data.value, function (ok) { | |
self._continue(ok); | |
}); | |
break; | |
case TAKE: | |
var channel = ins.data; | |
take_then_callback(channel, function (value) { | |
self._continue(value); | |
}); | |
break; | |
case SLEEP: | |
var msecs = ins.data; | |
dispatch.queue_delay(function () { | |
self.run(null); | |
}, msecs); | |
break; | |
case ALTS: | |
select.do_alts(ins.data.operations, function (result) { | |
self._continue(result); | |
}, ins.data.options); | |
break; | |
} | |
} else { | |
this._continue(ins); | |
} | |
}; | |
function take(channel) { | |
return new Instruction(TAKE, channel); | |
} | |
function put(channel, value) { | |
return new Instruction(PUT, { | |
channel: channel, | |
value: value | |
}); | |
} | |
function sleep(msecs) { | |
return new Instruction(SLEEP, msecs); | |
} | |
function alts(operations, options) { | |
return new Instruction(ALTS, { | |
operations: operations, | |
options: options | |
}); | |
} | |
exports.put_then_callback = put_then_callback; | |
exports.take_then_callback = take_then_callback; | |
exports.put = put; | |
exports.take = take; | |
exports.sleep = sleep; | |
exports.alts = alts; | |
exports.Process = Process; | |
}, { "./dispatch": 7, "./select": 9 }], 9: [function (require, module, exports) { | |
"use strict"; | |
var Box = require("./channels").Box; | |
var AltHandler = function (flag, f) { | |
this.f = f; | |
this.flag = flag; | |
}; | |
AltHandler.prototype.is_active = function () { | |
return this.flag.value; | |
}; | |
AltHandler.prototype.commit = function () { | |
this.flag.value = false; | |
return this.f; | |
}; | |
var AltResult = function (value, channel) { | |
this.value = value; | |
this.channel = channel; | |
}; | |
function rand_int(n) { | |
return Math.floor(Math.random() * (n + 1)); | |
} | |
function random_array(n) { | |
var a = new Array(n); | |
var i; | |
for (i = 0; i < n; i++) { | |
a[i] = 0; | |
} | |
for (i = 1; i < n; i++) { | |
var j = rand_int(i); | |
a[i] = a[j]; | |
a[j] = i; | |
} | |
return a; | |
} | |
var hasOwnProperty = Object.prototype.hasOwnProperty; | |
var DEFAULT = { | |
toString: function () { | |
return "[object DEFAULT]"; | |
} | |
}; | |
// TODO: Accept a priority function or something | |
exports.do_alts = function (operations, callback, options) { | |
var length = operations.length; | |
// XXX Hmm | |
if (length === 0) { | |
throw new Error("Empty alt list"); | |
} | |
var priority = (options && options.priority) ? true : false; | |
if (!priority) { | |
var indexes = random_array(length); | |
} | |
var flag = new Box(true); | |
for (var i = 0; i < length; i++) { | |
var operation = operations[priority ? i : indexes[i]]; | |
var port, result; | |
// XXX Hmm | |
if (operation instanceof Array) { | |
var value = operation[1]; | |
port = operation[0]; | |
result = port._put(value, (function (port) { | |
return new AltHandler(flag, function (ok) { | |
callback(new AltResult(ok, port)); | |
}); | |
})(port)); | |
} else { | |
port = operation; | |
result = port._take((function (port) { | |
return new AltHandler(flag, function (value) { | |
callback(new AltResult(value, port)); | |
}); | |
})(port)); | |
} | |
// XXX Hmm | |
if (result instanceof Box) { | |
callback(new AltResult(result.value, port)); | |
break; | |
} | |
} | |
if (!(result instanceof Box) && options && hasOwnProperty.call(options, "default")) { | |
if (flag.value) { | |
flag.value = false; | |
callback(new AltResult(options["default"], DEFAULT)); | |
} | |
} | |
}; | |
exports.DEFAULT = DEFAULT; | |
}, { "./channels": 6 }], 10: [function (require, module, exports) { | |
"use strict"; | |
var dispatch = require("./dispatch"); | |
var channels = require("./channels"); | |
exports.timeout = function timeout_channel(msecs) { | |
var chan = channels.chan(); | |
dispatch.queue_delay(function () { | |
chan.close(); | |
}, msecs); | |
return chan; | |
}; | |
}, { "./channels": 6, "./dispatch": 7 }] }, {}, ["i2jwt0"]); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment