/* * 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;