Created
March 25, 2014 21:57
-
-
Save juandopazo/9772321 to your computer and use it in GitHub Desktop.
This file contains 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
(function (global) { | |
"use strict"; | |
function Queue(capacity) { | |
this.capacity = this.snap(capacity); | |
this.length = 0; | |
this.front = 0; | |
this.initialize(); | |
} | |
Queue.prototype.push = function (value) { | |
var length = this.length; | |
if (this.capacity <= length) { | |
this.grow(this.snap(this.capacity * this.growFactor)); | |
} | |
var index = (this.front + length) & (this.capacity - 1); | |
this[index] = value; | |
this.length = length + 1; | |
}; | |
Queue.prototype.shift = function () { | |
var front = this.front; | |
var result = this[front]; | |
this[front] = void 0; | |
this.front = (front + 1) & (this.capacity - 1); | |
this.length--; | |
return result; | |
}; | |
Queue.prototype.grow = function (capacity) { | |
var oldFront = this.front; | |
var oldCapacity = this.capacity; | |
var oldQueue = new Array(oldCapacity); | |
var length = this.length; | |
copy(this, 0, oldQueue, 0, oldCapacity); | |
this.capacity = capacity; | |
this.initialize(); | |
this.front = 0; | |
if (oldFront + length <= oldCapacity) { | |
// Can perform direct linear copy | |
copy(oldQueue, oldFront, this, 0, length); | |
} else { | |
// Cannot perform copy directly, perform as much as possible at the | |
// end, and then copy the rest to the beginning of the buffer | |
var lengthBeforeWrapping = | |
length - ((oldFront + length) & (oldCapacity - 1)); | |
copy( | |
oldQueue, | |
oldFront, | |
this, | |
0, | |
lengthBeforeWrapping | |
); | |
copy( | |
oldQueue, | |
0, | |
this, | |
lengthBeforeWrapping, | |
length - lengthBeforeWrapping | |
); | |
} | |
}; | |
Queue.prototype.initialize = function () { | |
var length = this.capacity; | |
for (var i = 0; i < length; ++i) { | |
this[i] = void 0; | |
} | |
}; | |
Queue.prototype.snap = function (capacity) { | |
if (typeof capacity !== "number") { | |
return this.minCapacity; | |
} | |
return pow2AtLeast( | |
Math.min(this.maxCapacity, Math.max(this.minCapacity, capacity)) | |
); | |
}; | |
Queue.prototype.maxCapacity = (1 << 30) | 0; | |
Queue.prototype.minCapacity = 16; | |
Queue.prototype.growFactor = 8; | |
function copy(source, sourceIndex, target, targetIndex, length) { | |
for (var index = 0; index < length; ++index) { | |
target[index + targetIndex] = source[index + sourceIndex]; | |
} | |
} | |
function pow2AtLeast(n) { | |
n = n >>> 0; | |
n = n - 1; | |
n = n | (n >> 1); | |
n = n | (n >> 2); | |
n = n | (n >> 4); | |
n = n | (n >> 8); | |
n = n | (n >> 16); | |
return n + 1; | |
} | |
// Use the fastest possible means to execute a task in a future turn | |
// of the event loop. | |
// Queue is a circular buffer with good locality of reference and doesn't | |
// allocate new memory unless there are more than `InitialCapacity` parallel | |
// tasks in which case it will resize itself generously to x8 more capacity. | |
// The use case of asap should require no or few amount of resizes during | |
// runtime. | |
// Calling a task frees a slot immediately so if the calling | |
// has a side effect of queuing itself again, it can be sustained | |
// without additional memory | |
// Queue specifically uses | |
// http://en.wikipedia.org/wiki/Circular_buffer#Use_a_Fill_Count | |
// Because: | |
// 1. We need fast .length operation, since queue | |
// could have changed after every iteration | |
// 2. Modulus can be negated by using power-of-two | |
// capacities and replacing it with bitwise AND | |
// 3. It will not be used in a multi-threaded situation. | |
//1024 = InitialCapacity | |
var queue = new Queue(1024); | |
var flushing = false; | |
var requestFlush = void 0; | |
var hasSetImmediate = typeof setImmediate === "function"; | |
var domain; | |
// Avoid shims from browserify. | |
// The existence of `global` in browsers is guaranteed by browserify. | |
var process = global.process; | |
// Note that some fake-Node environments, | |
// like the Mocha test runner, introduce a `process` global. | |
var isNodeJS = !!process && ({}).toString.call(process) === "[object process]"; | |
function flush() { | |
/* jshint loopfunc: true */ | |
while (queue.length > 0) { | |
var task = queue.shift(); | |
try { | |
task.call(); | |
} catch (e) { | |
if (isNodeJS) { | |
// In node, uncaught exceptions are considered fatal errors. | |
// Re-throw them to interrupt flushing! | |
// Ensure continuation if an uncaught exception is suppressed | |
// listening process.on("uncaughtException") or domain("error"). | |
requestFlush(); | |
throw e; | |
} else { | |
// In browsers, uncaught exceptions are not fatal. | |
// Re-throw them asynchronously to avoid slow-downs. | |
setTimeout(function () { | |
throw e; | |
}, 0); | |
} | |
} | |
} | |
flushing = false; | |
} | |
if (isNodeJS) { | |
// Node.js | |
requestFlush = function () { | |
// Ensure flushing is not bound to any domain. | |
var currentDomain = process.domain; | |
if (currentDomain) { | |
domain = domain || (1,require)("domain"); | |
domain.active = process.domain = null; | |
} | |
// Avoid tick recursion - use setImmediate if it exists. | |
if (flushing && hasSetImmediate) { | |
setImmediate(flush); | |
} else { | |
process.nextTick(flush); | |
} | |
if (currentDomain) { | |
domain.active = process.domain = currentDomain; | |
} | |
}; | |
} else if (hasSetImmediate) { | |
// In IE10, or https://github.com/NobleJS/setImmediate | |
requestFlush = function () { | |
setImmediate(flush); | |
}; | |
} else if (typeof MessageChannel !== "undefined") { | |
// modern browsers | |
// http://www.nonblocking.io/2011/06/windownexttick.html | |
var channel = new MessageChannel(); | |
// At least Safari Version 6.0.5 (8536.30.1) intermittently cannot create | |
// working message ports the first time a page loads. | |
channel.port1.onmessage = function () { | |
requestFlush = requestPortFlush; | |
channel.port1.onmessage = flush; | |
flush(); | |
}; | |
var requestPortFlush = function () { | |
// Opera requires us to provide a message payload, regardless of | |
// whether we use it. | |
channel.port2.postMessage(0); | |
}; | |
requestFlush = function () { | |
setTimeout(flush, 0); | |
requestPortFlush(); | |
}; | |
} else { | |
// old browsers | |
requestFlush = function () { | |
setTimeout(flush, 0); | |
}; | |
} | |
function asap(task) { | |
if (isNodeJS && process.domain) { | |
task = process.domain.bind(task); | |
} | |
queue.push(task); | |
if (!flushing) { | |
requestFlush(); | |
flushing = true; | |
} | |
}; | |
global.asap = asap; | |
}(this)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment