182 lines
5.1 KiB
JavaScript
182 lines
5.1 KiB
JavaScript
|
/*
|
||
|
* MICROUTILS.US (MODULE)
|
||
|
*
|
||
|
* Version: 1.3.0
|
||
|
* Author: VideoPlayerCode
|
||
|
* URL: https://github.com/VideoPlayerCode/mpv-tools
|
||
|
* License: Apache License, Version 2.0
|
||
|
*/
|
||
|
|
||
|
/* jshint -W097 */
|
||
|
/* global mp, module, require */
|
||
|
|
||
|
'use strict';
|
||
|
|
||
|
var Utils = {};
|
||
|
|
||
|
// NOTE: This is an implementation of a non-recursive quicksort, which doesn't
|
||
|
// risk any stack overflows. This function is necessary because of a MuJS <=
|
||
|
// 1.0.1 bug which causes a stack overflow when running its built-in sort() on
|
||
|
// any large array. See: https://github.com/ccxvii/mujs/issues/55
|
||
|
// Furthermore, this performs optimized case-insensitive sorting.
|
||
|
Utils.quickSort = function(arr, options)
|
||
|
{
|
||
|
options = options || {};
|
||
|
|
||
|
var i, sortRef,
|
||
|
caseInsensitive = !!options.caseInsensitive;
|
||
|
|
||
|
if (caseInsensitive) {
|
||
|
sortRef = arr.slice(0);
|
||
|
for (i = sortRef.length - 1; i >= 0; --i)
|
||
|
if (typeof sortRef[i] === 'string')
|
||
|
sortRef[i] = sortRef[i].toLowerCase();
|
||
|
|
||
|
return Utils.quickSort_Run(arr, sortRef);
|
||
|
}
|
||
|
|
||
|
return Utils.quickSort_Run(arr);
|
||
|
};
|
||
|
|
||
|
Utils.quickSort_Run = function(arr, sortRef)
|
||
|
{
|
||
|
if (arr.length <= 1)
|
||
|
return arr;
|
||
|
|
||
|
var hasSortRef = !!sortRef;
|
||
|
if (!hasSortRef)
|
||
|
sortRef = arr; // Use arr instead. Makes a direct reference (no copy).
|
||
|
|
||
|
if (arr.length !== sortRef.length)
|
||
|
throw 'Array and sort-reference length must be identical';
|
||
|
|
||
|
// Adapted from a great, public-domain C algorithm by Darel Rex Finley.
|
||
|
// Original implementation: http://alienryderflex.com/quicksort/
|
||
|
// Ported by VideoPlayerCode and extended to sort via a 2nd reference array,
|
||
|
// to allow sorting the main array by _any_ criteria via the 2nd array.
|
||
|
var refPiv, arrPiv, beg = [], end = [], stackMax = -1, stackPtr = 0, L, R;
|
||
|
|
||
|
beg.push(0); end.push(sortRef.length);
|
||
|
++stackMax; // Tracks highest available stack index.
|
||
|
while (stackPtr >= 0) {
|
||
|
L = beg[stackPtr]; R = end[stackPtr] - 1;
|
||
|
if (L < R) {
|
||
|
if (hasSortRef) // If we have a SEPARATE sort-ref, mirror actions!
|
||
|
arrPiv = arr[L];
|
||
|
refPiv = sortRef[L]; // Left-pivot is fastest, no MuJS math needed!
|
||
|
|
||
|
while (L < R) {
|
||
|
while (sortRef[R] >= refPiv && L < R) R--;
|
||
|
if (L < R) {
|
||
|
if (hasSortRef)
|
||
|
arr[L] = arr[R];
|
||
|
sortRef[L++] = sortRef[R];
|
||
|
}
|
||
|
while (sortRef[L] <= refPiv && L < R) L++;
|
||
|
if (L < R) {
|
||
|
if (hasSortRef)
|
||
|
arr[R] = arr[L];
|
||
|
sortRef[R--] = sortRef[L];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (hasSortRef)
|
||
|
arr[L] = arrPiv;
|
||
|
sortRef[L] = refPiv;
|
||
|
|
||
|
if (stackPtr === stackMax) {
|
||
|
beg.push(0); end.push(0); // Grow stacks to fit next elem.
|
||
|
++stackMax;
|
||
|
}
|
||
|
|
||
|
beg[stackPtr + 1] = L + 1;
|
||
|
end[stackPtr + 1] = end[stackPtr];
|
||
|
end[stackPtr++] = L;
|
||
|
} else {
|
||
|
stackPtr--;
|
||
|
// NOTE: No need to shrink stack here. Size-reqs GROW until sorted!
|
||
|
// (Anyway, MuJS is slow at splice() and wastes time if we shrink.)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return arr;
|
||
|
};
|
||
|
|
||
|
Utils.isInt = function(value)
|
||
|
{
|
||
|
// Verify that the input is an integer (whole number).
|
||
|
return (typeof value !== 'number' || isNaN(value)) ?
|
||
|
false :
|
||
|
(value | 0) === value;
|
||
|
};
|
||
|
|
||
|
Utils._hexSymbols = [
|
||
|
'0', '1', '2', '3', '4', '5', '6', '7',
|
||
|
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
|
||
|
];
|
||
|
|
||
|
Utils.toHex = function(num, outputLength)
|
||
|
{
|
||
|
// Generates a fixed-length output, and handles negative numbers properly.
|
||
|
var result = '';
|
||
|
while (outputLength--) {
|
||
|
result = Utils._hexSymbols[num & 0xF] + result;
|
||
|
num >>= 4;
|
||
|
}
|
||
|
return result;
|
||
|
};
|
||
|
|
||
|
Utils.shuffle = function(arr)
|
||
|
{
|
||
|
var m = arr.length, tmp, i;
|
||
|
|
||
|
while (m) { // While items remain to shuffle...
|
||
|
// Pick a remaining element...
|
||
|
i = Math.floor(Math.random() * m--);
|
||
|
|
||
|
// And swap it with the current element.
|
||
|
tmp = arr[m];
|
||
|
arr[m] = arr[i];
|
||
|
arr[i] = tmp;
|
||
|
}
|
||
|
|
||
|
return arr;
|
||
|
};
|
||
|
|
||
|
Utils.trim = function(str)
|
||
|
{
|
||
|
return str.replace(/(?:^\s+|\s+$)/g, ''); // Trim left and right whitespace.
|
||
|
};
|
||
|
|
||
|
Utils.ltrim = function(str)
|
||
|
{
|
||
|
return str.replace(/^\s+/, ''); // Trim left whitespace.
|
||
|
};
|
||
|
|
||
|
Utils.rtrim = function(str)
|
||
|
{
|
||
|
return str.replace(/\s+$/, ''); // Trim right whitespace.
|
||
|
};
|
||
|
|
||
|
Utils.dump = function(value)
|
||
|
{
|
||
|
mp.msg.error(JSON.stringify(value));
|
||
|
};
|
||
|
|
||
|
Utils.benchmarkStart = function(textLabel)
|
||
|
{
|
||
|
Utils.benchmarkTimestamp = mp.get_time();
|
||
|
Utils.benchmarkTextLabel = textLabel;
|
||
|
};
|
||
|
|
||
|
Utils.benchmarkEnd = function()
|
||
|
{
|
||
|
var now = mp.get_time(),
|
||
|
start = Utils.benchmarkTimestamp ? Utils.benchmarkTimestamp : now,
|
||
|
elapsed = now - start,
|
||
|
label = typeof Utils.benchmarkTextLabel === 'string' ? Utils.benchmarkTextLabel : '';
|
||
|
mp.msg.info('Time Elapsed (Benchmark'+(label.length ? ': '+label : '')+'): '+elapsed+' seconds.');
|
||
|
};
|
||
|
|
||
|
module.exports = Utils;
|