diff --git a/dist/perf/indexeddb-main-thread-worker-072c4623.js b/dist/perf/indexeddb-main-thread-worker-072c4623.js new file mode 100644 index 0000000..31e0819 --- /dev/null +++ b/dist/perf/indexeddb-main-thread-worker-072c4623.js @@ -0,0 +1,34 @@ +function decodeBase64(base64, enableUnicode) { + var binaryString = atob(base64); + if (enableUnicode) { + var binaryView = new Uint8Array(binaryString.length); + for (var i = 0, n = binaryString.length; i < n; ++i) { + binaryView[i] = binaryString.charCodeAt(i); + } + return String.fromCharCode.apply(null, new Uint16Array(binaryView.buffer)); + } + return binaryString; +} + +function createURL(base64, sourcemapArg, enableUnicodeArg) { + var sourcemap = sourcemapArg === undefined ? null : sourcemapArg; + var enableUnicode = enableUnicodeArg === undefined ? false : enableUnicodeArg; + var source = decodeBase64(base64, enableUnicode); + var start = source.indexOf('\n', 10) + 1; + var body = source.substring(start) + (sourcemap ? '\/\/# sourceMappingURL=' + sourcemap : ''); + var blob = new Blob([body], { type: 'application/javascript' }); + return URL.createObjectURL(blob); +} + +function createBase64WorkerFactory(base64, sourcemapArg, enableUnicodeArg) { + var url; + return function WorkerFactory(options) { + url = url || createURL(base64, sourcemapArg, enableUnicodeArg); + return new Worker(url, options); + }; +} + +var WorkerFactory = createBase64WorkerFactory('Lyogcm9sbHVwLXBsdWdpbi13ZWItd29ya2VyLWxvYWRlciAqLwohZnVuY3Rpb24oKXsidXNlIHN0cmljdCI7bGV0IHQ9MzczNTkyODU1OTtjbGFzcyBle2NvbnN0cnVjdG9yKHQse2luaXRpYWxPZmZzZXQ6ZT00LHVzZUF0b21pY3M6aT0hMCxzdHJlYW06cz0hMCxkZWJ1ZzpyLG5hbWU6bn09e30pe3RoaXMuYnVmZmVyPXQsdGhpcy5hdG9taWNWaWV3PW5ldyBJbnQzMkFycmF5KHQpLHRoaXMub2Zmc2V0PWUsdGhpcy51c2VBdG9taWNzPWksdGhpcy5zdHJlYW09cyx0aGlzLmRlYnVnPXIsdGhpcy5uYW1lPW59bG9nKC4uLnQpe3RoaXMuZGVidWcmJmNvbnNvbGUubG9nKGBbcmVhZGVyOiAke3RoaXMubmFtZX1dYCwuLi50KX13YWl0V3JpdGUodCl7aWYodGhpcy51c2VBdG9taWNzKXtmb3IodGhpcy5sb2coYHdhaXRpbmcgZm9yICR7dH1gKTswPT09QXRvbWljcy5sb2FkKHRoaXMuYXRvbWljVmlldywwKTspQXRvbWljcy53YWl0KHRoaXMuYXRvbWljVmlldywwLDAsNTAwKTt0aGlzLmxvZyhgcmVzdW1lZCBmb3IgJHt0fWApfWVsc2UgaWYoMSE9PXRoaXMuYXRvbWljVmlld1swXSl0aHJvdyBuZXcgRXJyb3IoImB3YWl0V3JpdGVgIGV4cGVjdGVkIGFycmF5IHRvIGJlIHJlYWRhYmxlIil9ZmxpcCgpe2lmKHRoaXMubG9nKCJmbGlwIiksdGhpcy51c2VBdG9taWNzKXtpZigxIT09QXRvbWljcy5jb21wYXJlRXhjaGFuZ2UodGhpcy5hdG9taWNWaWV3LDAsMSwwKSl0aHJvdyBuZXcgRXJyb3IoIlJlYWQgZGF0YSBvdXQgb2Ygc3luYyEgVGhpcyBpcyBkaXNhc3Ryb3VzIik7QXRvbWljcy5ub3RpZnkodGhpcy5hdG9taWNWaWV3LDApfWVsc2UgdGhpcy5hdG9taWNWaWV3WzBdPTA7dGhpcy5vZmZzZXQ9NH1kb25lKCl7dGhpcy53YWl0V3JpdGUoImRvbmUiKTtsZXQgZT1uZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsdGhpcy5vZmZzZXQpLmdldFVpbnQzMigwKT09PXQ7cmV0dXJuIGUmJih0aGlzLmxvZygiZG9uZSIpLHRoaXMuZmxpcCgpKSxlfXBlZWsodCl7dGhpcy5wZWVrT2Zmc2V0PXRoaXMub2Zmc2V0O2xldCBlPXQoKTtyZXR1cm4gdGhpcy5vZmZzZXQ9dGhpcy5wZWVrT2Zmc2V0LHRoaXMucGVla09mZnNldD1udWxsLGV9c3RyaW5nKCl7dGhpcy53YWl0V3JpdGUoInN0cmluZyIpO2xldCB0PXRoaXMuX2ludDMyKCksZT10LzIsaT1uZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsdGhpcy5vZmZzZXQsdCkscz1bXTtmb3IobGV0IHQ9MDt0PGU7dCsrKXMucHVzaChpLmdldFVpbnQxNigyKnQpKTtsZXQgcj1TdHJpbmcuZnJvbUNoYXJDb2RlLmFwcGx5KG51bGwscyk7cmV0dXJuIHRoaXMubG9nKCJzdHJpbmciLHIpLHRoaXMub2Zmc2V0Kz10LG51bGw9PXRoaXMucGVla09mZnNldCYmdGhpcy5mbGlwKCkscn1faW50MzIoKXtsZXQgdD1uZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsdGhpcy5vZmZzZXQpLmdldEludDMyKCk7cmV0dXJuIHRoaXMubG9nKCJfaW50MzIiLHQpLHRoaXMub2Zmc2V0Kz00LHR9aW50MzIoKXt0aGlzLndhaXRXcml0ZSgiaW50MzIiKTtsZXQgdD10aGlzLl9pbnQzMigpO3JldHVybiB0aGlzLmxvZygiaW50MzIiLHQpLG51bGw9PXRoaXMucGVla09mZnNldCYmdGhpcy5mbGlwKCksdH1ieXRlcygpe3RoaXMud2FpdFdyaXRlKCJieXRlcyIpO2xldCB0PXRoaXMuX2ludDMyKCksZT1uZXcgQXJyYXlCdWZmZXIodCk7cmV0dXJuIG5ldyBVaW50OEFycmF5KGUpLnNldChuZXcgVWludDhBcnJheSh0aGlzLmJ1ZmZlcix0aGlzLm9mZnNldCx0KSksdGhpcy5sb2coImJ5dGVzIixlKSx0aGlzLm9mZnNldCs9dCxudWxsPT10aGlzLnBlZWtPZmZzZXQmJnRoaXMuZmxpcCgpLGV9fWNsYXNzIGl7Y29uc3RydWN0b3IodCx7aW5pdGlhbE9mZnNldDplPTQsdXNlQXRvbWljczppPSEwLHN0cmVhbTpzPSEwLGRlYnVnOnIsbmFtZTpufT17fSl7dGhpcy5idWZmZXI9dCx0aGlzLmF0b21pY1ZpZXc9bmV3IEludDMyQXJyYXkodCksdGhpcy5vZmZzZXQ9ZSx0aGlzLnVzZUF0b21pY3M9aSx0aGlzLnN0cmVhbT1zLHRoaXMuZGVidWc9cix0aGlzLm5hbWU9bix0aGlzLnVzZUF0b21pY3M/QXRvbWljcy5zdG9yZSh0aGlzLmF0b21pY1ZpZXcsMCwwKTp0aGlzLmF0b21pY1ZpZXdbMF09MH1sb2coLi4udCl7dGhpcy5kZWJ1ZyYmY29uc29sZS5sb2coYFt3cml0ZXI6ICR7dGhpcy5uYW1lfV1gLC4uLnQpfXdhaXRSZWFkKHQpe2lmKHRoaXMudXNlQXRvbWljcyl7aWYodGhpcy5sb2coYHdhaXRpbmcgZm9yICR7dH1gKSwwIT09QXRvbWljcy5jb21wYXJlRXhjaGFuZ2UodGhpcy5hdG9taWNWaWV3LDAsMCwxKSl0aHJvdyBuZXcgRXJyb3IoIldyb3RlIHNvbWV0aGluZyBpbnRvIHVud3JpdGFibGUgYnVmZmVyISBUaGlzIGlzIGRpc2FzdHJvdXMiKTtmb3IoQXRvbWljcy5ub3RpZnkodGhpcy5hdG9taWNWaWV3LDApOzE9PT1BdG9taWNzLmxvYWQodGhpcy5hdG9taWNWaWV3LDApOylBdG9taWNzLndhaXQodGhpcy5hdG9taWNWaWV3LDAsMSw1MDApO3RoaXMubG9nKGByZXN1bWVkIGZvciAke3R9YCl9ZWxzZSB0aGlzLmF0b21pY1ZpZXdbMF09MTt0aGlzLm9mZnNldD00fWZpbmFsaXplKCl7dGhpcy5sb2coImZpbmFsaXppbmciKSxuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsdGhpcy5vZmZzZXQpLnNldFVpbnQzMigwLHQpLHRoaXMud2FpdFJlYWQoImZpbmFsaXplIil9c3RyaW5nKHQpe3RoaXMubG9nKCJzdHJpbmciLHQpO2xldCBlPTIqdC5sZW5ndGg7dGhpcy5faW50MzIoZSk7bGV0IGk9bmV3IERhdGFWaWV3KHRoaXMuYnVmZmVyLHRoaXMub2Zmc2V0LGUpO2ZvcihsZXQgZT0wO2U8dC5sZW5ndGg7ZSsrKWkuc2V0VWludDE2KDIqZSx0LmNoYXJDb2RlQXQoZSkpO3RoaXMub2Zmc2V0Kz1lLHRoaXMud2FpdFJlYWQoInN0cmluZyIpfV9pbnQzMih0KXtuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsdGhpcy5vZmZzZXQpLnNldEludDMyKDAsdCksdGhpcy5vZmZzZXQrPTR9aW50MzIodCl7dGhpcy5sb2coImludDMyIix0KSx0aGlzLl9pbnQzMih0KSx0aGlzLndhaXRSZWFkKCJpbnQzMiIpfWJ5dGVzKHQpe3RoaXMubG9nKCJieXRlcyIsdCk7bGV0IGU9dC5ieXRlTGVuZ3RoO3RoaXMuX2ludDMyKGUpLG5ldyBVaW50OEFycmF5KHRoaXMuYnVmZmVyLHRoaXMub2Zmc2V0KS5zZXQobmV3IFVpbnQ4QXJyYXkodCkpLHRoaXMub2Zmc2V0Kz1lLHRoaXMud2FpdFJlYWQoImJ5dGVzIil9fWxldCBzLHI9e30sbj17fTthc3luYyBmdW5jdGlvbiBhKHQsZSxpKXtnbG9iYWxUaGlzLnBvc3RNZXNzYWdlKHt0eXBlOiJfX3BlcmYtZGVldHM6bG9nLXBlcmYiLGRhdGFUeXBlOnQsbmFtZTplLGRhdGE6aSxhcGlWZXJzaW9uOjF9KX1mdW5jdGlvbiBvKCl7Z2xvYmFsVGhpcy5wb3N0TWVzc2FnZSh7dHlwZToiX19wZXJmLWRlZXRzOmNsZWFyLXBlcmYifSkscj17fSxuPXt9LHM9cGVyZm9ybWFuY2Uubm93KCl9YXN5bmMgZnVuY3Rpb24gbCgpe09iamVjdC5rZXlzKHIpLm1hcCgodD0+e2EoInRpbWluZyIsdCxyW3RdLmRhdGEubWFwKCh0PT4oe3g6dC5zdGFydCt0LnRvb2sseTp0LnRvb2t9KSkpKX0pKSxPYmplY3Qua2V5cyhuKS5tYXAoKHQ9PnthKCJjb3VudCIsdCxuW3RdLm1hcCgoKHQsZSk9Pih7eDp0LnRpbWUseTplfSkpKSl9KSl9ZnVuY3Rpb24gYyh0KXtudWxsPT1yW3RdJiYoclt0XT17c3RhcnQ6bnVsbCxkYXRhOltdfSk7bGV0IGU9clt0XTtpZihudWxsIT1lLnN0YXJ0KXRocm93IG5ldyBFcnJvcihgdGltZXIgYWxyZWFkeSBzdGFydGVkICR7dH1gKTtlLnN0YXJ0PXBlcmZvcm1hbmNlLm5vdygpfWZ1bmN0aW9uIGgodCl7bGV0IGU9cGVyZm9ybWFuY2Uubm93KCksaT1yW3RdO2lmKGkmJm51bGwhPWkuc3RhcnQpe2xldCB0PWUtaS5zdGFydCxyPWkuc3RhcnQtcztpLnN0YXJ0PW51bGwsaS5kYXRhLmxlbmd0aDw0ZTQmJmkuZGF0YS5wdXNoKHtzdGFydDpyLHRvb2s6dH0pfX1mdW5jdGlvbiBmKHQpe251bGw9PW5bdF0mJihuW3RdPVtdKSxuW3RdLnB1c2goe3RpbWU6cGVyZm9ybWFuY2Uubm93KCl9KX1nbG9iYWxUaGlzLmFkZEV2ZW50TGlzdGVuZXIoIm1lc3NhZ2UiLCh0PT57c3dpdGNoKHQuZGF0YS50eXBlKXtjYXNlIl9fcGVyZi1kZWV0czpzdGFydC1wcm9maWxlIjpvKCk7YnJlYWs7Y2FzZSJfX3BlcmYtZGVldHM6c3RvcC1wcm9maWxlIjpsKCk7YnJlYWs7Y2FzZSJfX3BlcmYtZGVldHM6Y2xlYXItcGVyZiI6Y2FzZSJfX3BlcmYtZGVldHM6bG9nLXBlcmYiOiJ1bmRlZmluZWQiPT10eXBlb2Ygd2luZG93JiZzZWxmLnBvc3RNZXNzYWdlKHQuZGF0YSl9fSkpO2xldCB1PS9eKCg/IWNocm9tZXxhbmRyb2lkKS4pKnNhZmFyaS9pLnRlc3QobmF2aWdhdG9yLnVzZXJBZ2VudCksZD1uZXcgTWFwLHc9bmV3IE1hcDtmdW5jdGlvbiBnKHQsZSl7aWYoIXQpdGhyb3cgbmV3IEVycm9yKGUpfWxldCBtPTAscD0xLHk9MixiPTQ7Y2xhc3Mga3tjb25zdHJ1Y3Rvcih0LGU9InJlYWRvbmx5Iil7dGhpcy5kYj10LGYoInRyYW5zYWN0aW9ucyIpLHRoaXMudHJhbnM9dGhpcy5kYi50cmFuc2FjdGlvbihbImRhdGEiXSxlKSx0aGlzLnN0b3JlPXRoaXMudHJhbnMub2JqZWN0U3RvcmUoImRhdGEiKSx0aGlzLmxvY2tUeXBlPSJyZWFkb25seSI9PT1lP3A6Yix0aGlzLmNhY2hlZEZpcnN0QmxvY2s9bnVsbCx0aGlzLmN1cnNvcj1udWxsLHRoaXMucHJldlJlYWRzPW51bGx9YXN5bmMgcHJlZmV0Y2hGaXJzdEJsb2NrKHQpe2xldCBlPWF3YWl0IHRoaXMuZ2V0KDApO3JldHVybiB0aGlzLmNhY2hlZEZpcnN0QmxvY2s9ZSxlfWFzeW5jIHdhaXRDb21wbGV0ZSgpe3JldHVybiBuZXcgUHJvbWlzZSgoKHQsZSk9Pnt0aGlzLmNvbW1pdCgpLHRoaXMubG9ja1R5cGU9PT1iPyh0aGlzLnRyYW5zLm9uY29tcGxldGU9ZT0+dCgpLHRoaXMudHJhbnMub25lcnJvcj10PT5lKHQpKTp1P3RoaXMudHJhbnMub25jb21wbGV0ZT1lPT50KCk6dCgpfSkpfWNvbW1pdCgpe3RoaXMudHJhbnMuY29tbWl0JiZ0aGlzLnRyYW5zLmNvbW1pdCgpfWFzeW5jIHVwZ3JhZGVFeGNsdXNpdmUoKXt0aGlzLmNvbW1pdCgpLGYoInRyYW5zYWN0aW9ucyIpLHRoaXMudHJhbnM9dGhpcy5kYi50cmFuc2FjdGlvbihbImRhdGEiXSwicmVhZHdyaXRlIiksdGhpcy5zdG9yZT10aGlzLnRyYW5zLm9iamVjdFN0b3JlKCJkYXRhIiksdGhpcy5sb2NrVHlwZT1iO2xldCB0PXRoaXMuY2FjaGVkRmlyc3RCbG9jayxlPWF3YWl0IHRoaXMucHJlZmV0Y2hGaXJzdEJsb2NrKDUwMCk7aWYobnVsbD09dCYmbnVsbD09ZSlyZXR1cm4hMDtmb3IobGV0IGk9MjQ7aTw0MDtpKyspaWYoZVtpXSE9PXRbaV0pcmV0dXJuITE7cmV0dXJuITB9ZG93bmdyYWRlU2hhcmVkKCl7dGhpcy5jb21taXQoKSxmKCJ0cmFuc2FjdGlvbnMiKSx0aGlzLnRyYW5zPXRoaXMuZGIudHJhbnNhY3Rpb24oWyJkYXRhIl0sInJlYWRvbmx5IiksdGhpcy5zdG9yZT10aGlzLnRyYW5zLm9iamVjdFN0b3JlKCJkYXRhIiksdGhpcy5sb2NrVHlwZT1wfWFzeW5jIGdldCh0KXtyZXR1cm4gbmV3IFByb21pc2UoKChlLGkpPT57YygiZ2V0Iik7bGV0IHM9dGhpcy5zdG9yZS5nZXQodCk7cy5vbnN1Y2Nlc3M9dD0+e2goImdldCIpLGUocy5yZXN1bHQpfSxzLm9uZXJyb3I9dD0+aSh0KX0pKX1nZXRSZWFkRGlyZWN0aW9uKCl7bGV0IHQ9dGhpcy5wcmV2UmVhZHM7aWYodCl7aWYodFswXTx0WzFdJiZ0WzFdPHRbMl0mJnRbMl0tdFswXTwxMClyZXR1cm4ibmV4dCI7aWYodFswXT50WzFdJiZ0WzFdPnRbMl0mJnRbMF0tdFsyXTwxMClyZXR1cm4icHJldiJ9cmV0dXJuIG51bGx9cmVhZCh0KXtsZXQgZT0oKT0+bmV3IFByb21pc2UoKCh0LGUpPT57aWYobnVsbCE9dGhpcy5jdXJzb3JQcm9taXNlKXRocm93IG5ldyBFcnJvcigid2FpdEN1cnNvcigpIGNhbGxlZCBidXQgc29tZXRoaW5nIGVsc2UgaXMgYWxyZWFkeSB3YWl0aW5nIik7dGhpcy5jdXJzb3JQcm9taXNlPXtyZXNvbHZlOnQscmVqZWN0OmV9fSkpO2lmKHRoaXMuY3Vyc29yKXtsZXQgaT10aGlzLmN1cnNvcjtyZXR1cm4ibmV4dCI9PT1pLmRpcmVjdGlvbiYmdD5pLmtleSYmdDxpLmtleSsxMDA/KGMoInN0cmVhbS1uZXh0IiksaS5hZHZhbmNlKHQtaS5rZXkpLGUoKSk6InByZXYiPT09aS5kaXJlY3Rpb24mJnQ8aS5rZXkmJnQ+aS5rZXktMTAwPyhjKCJzdHJlYW0tbmV4dCIpLGkuYWR2YW5jZShpLmtleS10KSxlKCkpOih0aGlzLmN1cnNvcj1udWxsLHRoaXMucmVhZCh0KSl9e2xldCBpPXRoaXMuZ2V0UmVhZERpcmVjdGlvbigpO2lmKGkpe2xldCBzO3RoaXMucHJldlJlYWRzPW51bGwscz0icHJldiI9PT1pP0lEQktleVJhbmdlLnVwcGVyQm91bmQodCk6SURCS2V5UmFuZ2UubG93ZXJCb3VuZCh0KTtsZXQgcj10aGlzLnN0b3JlLm9wZW5DdXJzb3IocyxpKTtyZXR1cm4gYygic3RyZWFtIiksci5vbnN1Y2Nlc3M9dD0+e2goInN0cmVhbSIpLGgoInN0cmVhbS1uZXh0Iik7bGV0IGU9dC50YXJnZXQucmVzdWx0O2lmKHRoaXMuY3Vyc29yPWUsbnVsbD09dGhpcy5jdXJzb3JQcm9taXNlKXRocm93IG5ldyBFcnJvcigiR290IGRhdGEgZnJvbSBjdXJzb3IgYnV0IG5vdGhpbmcgaXMgd2FpdGluZyBpdCIpO3RoaXMuY3Vyc29yUHJvbWlzZS5yZXNvbHZlKGU/ZS52YWx1ZTpudWxsKSx0aGlzLmN1cnNvclByb21pc2U9bnVsbH0sci5vbmVycm9yPXQ9PntpZihjb25zb2xlLmxvZygiQ3Vyc29yIGZhaWx1cmU6Iix0KSxudWxsPT10aGlzLmN1cnNvclByb21pc2UpdGhyb3cgbmV3IEVycm9yKCJHb3QgZGF0YSBmcm9tIGN1cnNvciBidXQgbm90aGluZyBpcyB3YWl0aW5nIGl0Iik7dGhpcy5jdXJzb3JQcm9taXNlLnJlamVjdCh0KSx0aGlzLmN1cnNvclByb21pc2U9bnVsbH0sZSgpfXJldHVybiBudWxsPT10aGlzLnByZXZSZWFkcyYmKHRoaXMucHJldlJlYWRzPVswLDAsMF0pLHRoaXMucHJldlJlYWRzLnB1c2godCksdGhpcy5wcmV2UmVhZHMuc2hpZnQoKSx0aGlzLmdldCh0KX19YXN5bmMgc2V0KHQpe3JldHVybiB0aGlzLnByZXZSZWFkcz1udWxsLG5ldyBQcm9taXNlKCgoZSxpKT0+e2xldCBzPXRoaXMuc3RvcmUucHV0KHQudmFsdWUsdC5rZXkpO3Mub25zdWNjZXNzPXQ9PmUocy5yZXN1bHQpLHMub25lcnJvcj10PT5pKHQpfSkpfWFzeW5jIGJ1bGtTZXQodCl7dGhpcy5wcmV2UmVhZHM9bnVsbDtmb3IobGV0IGUgb2YgdCl0aGlzLnN0b3JlLnB1dChlLnZhbHVlLGUua2V5KX19YXN5bmMgZnVuY3Rpb24gdih0KXtyZXR1cm4gbmV3IFByb21pc2UoKChlLGkpPT57aWYoZC5nZXQodCkpcmV0dXJuIHZvaWQgZShkLmdldCh0KSk7Y29uc29sZS5sb2coIm9wZW5pbmciLHQpO2xldCBzPWdsb2JhbFRoaXMuaW5kZXhlZERCLm9wZW4odCwxKTtzLm9uc3VjY2Vzcz1pPT57Y29uc29sZS5sb2coImRiIGlzIG9wZW4hIix0KTtsZXQgcz1pLnRhcmdldC5yZXN1bHQ7cy5vbnZlcnNpb25jaGFuZ2U9KCk9Pntjb25zb2xlLmxvZygiY2xvc2luZyBiZWNhdXNlIHZlcnNpb24gY2hhbmdlZCIpLHMuY2xvc2UoKSxkLmRlbGV0ZSh0KX0scy5vbmNsb3NlPSgpPT57ZC5kZWxldGUodCl9LGQuc2V0KHQscyksZShzKX0scy5vbnVwZ3JhZGVuZWVkZWQ9dD0+e2xldCBlPXQudGFyZ2V0LnJlc3VsdDtlLm9iamVjdFN0b3JlTmFtZXMuY29udGFpbnMoImRhdGEiKXx8ZS5jcmVhdGVPYmplY3RTdG9yZSgiZGF0YSIpfSxzLm9uYmxvY2tlZD10PT5jb25zb2xlLmxvZygiYmxvY2tlZCIsdCkscy5vbmVycm9yPXMub25hYm9ydD10PT5pKHQudGFyZ2V0LmVycm9yKX0pKX1mdW5jdGlvbiBBKHQpe2xldCBlPWQuZ2V0KHQpO2UmJihjb25zb2xlLmxvZygiY2xvc2luZyBkYiIpLGUuY2xvc2UoKSxkLmRlbGV0ZSh0KSl9YXN5bmMgZnVuY3Rpb24gUih0LGUsaSl7bGV0IHM9dy5nZXQodCk7aWYocyl7aWYoInJlYWR3cml0ZSI9PT1lJiZzLmxvY2tUeXBlPT09cCl0aHJvdyBuZXcgRXJyb3IoIkF0dGVtcHRlZCB3cml0ZSBidXQgb25seSBoYXMgU0hBUkVEIGxvY2siKTtyZXR1cm4gaShzKX1zPW5ldyBrKGF3YWl0IHYodCksZSksYXdhaXQgaShzKSxhd2FpdCBzLndhaXRDb21wbGV0ZSgpfWFzeW5jIGZ1bmN0aW9uIF8odCxlLGkpe2xldCBzPWZ1bmN0aW9uKHQpe3JldHVybiB3LmdldCh0KX0oZSk7aWYoaT09PXApe2lmKG51bGw9PXMpdGhyb3cgbmV3IEVycm9yKCJVbmxvY2sgZXJyb3IgKFNIQVJFRCk6IG5vIHRyYW5zYWN0aW9uIHJ1bm5pbmciKTtzLmxvY2tUeXBlPT09YiYmcy5kb3duZ3JhZGVTaGFyZWQoKX1lbHNlIGk9PT1tJiZzJiYoYXdhaXQgcy53YWl0Q29tcGxldGUoKSx3LmRlbGV0ZShlKSk7dC5pbnQzMigwKSx0LmZpbmFsaXplKCl9YXN5bmMgZnVuY3Rpb24geih0LGUpe2xldCBpPXQuc3RyaW5nKCk7c3dpdGNoKGkpe2Nhc2UicHJvZmlsZS1zdGFydCI6dC5kb25lKCksbygpLGUuaW50MzIoMCksZS5maW5hbGl6ZSgpLHoodCxlKTticmVhaztjYXNlInByb2ZpbGUtc3RvcCI6dC5kb25lKCksbCgpLGF3YWl0IG5ldyBQcm9taXNlKCh0PT5zZXRUaW1lb3V0KHQsMWUzKSkpLGUuaW50MzIoMCksZS5maW5hbGl6ZSgpLHoodCxlKTticmVhaztjYXNlIndyaXRlQmxvY2tzIjp7bGV0IGk9dC5zdHJpbmcoKSxzPVtdO2Zvcig7IXQuZG9uZSgpOyl7bGV0IGU9dC5pbnQzMigpLGk9dC5ieXRlcygpO3MucHVzaCh7cG9zOmUsZGF0YTppfSl9YXdhaXQgYXN5bmMgZnVuY3Rpb24odCxlLGkpe3JldHVybiBSKGUsInJlYWR3cml0ZSIsKGFzeW5jIGU9Pnthd2FpdCBlLmJ1bGtTZXQoaS5tYXAoKHQ9Pih7a2V5OnQucG9zLHZhbHVlOnQuZGF0YX0pKSkpLHQuaW50MzIoMCksdC5maW5hbGl6ZSgpfSkpfShlLGkscykseih0LGUpO2JyZWFrfWNhc2UicmVhZEJsb2NrIjp7bGV0IGk9dC5zdHJpbmcoKSxzPXQuaW50MzIoKTt0LmRvbmUoKSxhd2FpdCBhc3luYyBmdW5jdGlvbih0LGUsaSl7cmV0dXJuIFIoZSwicmVhZG9ubHkiLChhc3luYyBlPT57bGV0IHM9YXdhaXQgZS5yZWFkKGkpO251bGw9PXM/dC5ieXRlcyhuZXcgQXJyYXlCdWZmZXIoMCkpOnQuYnl0ZXMocyksdC5maW5hbGl6ZSgpfSkpfShlLGkscykseih0LGUpO2JyZWFrfWNhc2UicmVhZE1ldGEiOntsZXQgaT10LnN0cmluZygpO3QuZG9uZSgpLGF3YWl0IGFzeW5jIGZ1bmN0aW9uKHQsZSl7cmV0dXJuIFIoZSwicmVhZG9ubHkiLChhc3luYyBlPT57dHJ5e2NvbnNvbGUubG9nKCJSZWFkaW5nIG1ldGEiKTtsZXQgaT1hd2FpdCBlLmdldCgtMSk7Y29uc29sZS5sb2coIlJlYWRpbmcgbWV0YSAoZG9uZSkiLGkpO2xldCBzPWk7dC5pbnQzMihzP3Muc2l6ZTotMSksdC5pbnQzMihzP3MuYmxvY2tTaXplOi0xKSx0LmZpbmFsaXplKCl9Y2F0Y2goZSl7Y29uc29sZS5sb2coZSksdC5pbnQzMigtMSksdC5pbnQzMigtMSksdC5maW5hbGl6ZSgpfX0pKX0oZSxpKSx6KHQsZSk7YnJlYWt9Y2FzZSJ3cml0ZU1ldGEiOntsZXQgaT10LnN0cmluZygpLHM9dC5pbnQzMigpLHI9dC5pbnQzMigpO3QuZG9uZSgpLGF3YWl0IGFzeW5jIGZ1bmN0aW9uKHQsZSxpKXtyZXR1cm4gUihlLCJyZWFkd3JpdGUiLChhc3luYyBlPT57dHJ5e2F3YWl0IGUuc2V0KHtrZXk6LTEsdmFsdWU6aX0pLHQuaW50MzIoMCksdC5maW5hbGl6ZSgpfWNhdGNoKGUpe2NvbnNvbGUubG9nKGUpLHQuaW50MzIoLTEpLHQuZmluYWxpemUoKX19KSl9KGUsaSx7c2l6ZTpzLGJsb2NrU2l6ZTpyfSkseih0LGUpO2JyZWFrfWNhc2UiZGVsZXRlRmlsZSI6e2xldCBpPXQuc3RyaW5nKCk7dC5kb25lKCksYXdhaXQgYXN5bmMgZnVuY3Rpb24odCxlKXt0cnl7QShlKSxhd2FpdCBuZXcgUHJvbWlzZSgoKHQsaSk9PntsZXQgcz1nbG9iYWxUaGlzLmluZGV4ZWREQi5kZWxldGVEYXRhYmFzZShlKTtzLm9uc3VjY2Vzcz10LHMub25lcnJvcj1pfSkpLHQuaW50MzIoMCksdC5maW5hbGl6ZSgpfWNhdGNoKGUpe3QuaW50MzIoLTEpLHQuZmluYWxpemUoKX19KGUsaSkseih0LGUpO2JyZWFrfWNhc2UiY2xvc2VGaWxlIjp7bGV0IGk9dC5zdHJpbmcoKTt0LmRvbmUoKSxhd2FpdCBhc3luYyBmdW5jdGlvbih0LGUpe0EoZSksdC5pbnQzMigwKSx0LmZpbmFsaXplKCl9KGUsaSkseih0LGUpO2JyZWFrfWNhc2UibG9ja0ZpbGUiOntsZXQgaT10LnN0cmluZygpLHM9dC5pbnQzMigpO3QuZG9uZSgpLGF3YWl0IGFzeW5jIGZ1bmN0aW9uKHQsZSxpKXtsZXQgcz13LmdldChlKTtpZihzKWlmKGk+cy5sb2NrVHlwZSl7ZyhzLmxvY2tUeXBlPT09cCxgVXByYWRpbmcgbG9jayB0eXBlIGZyb20gJHtzLmxvY2tUeXBlfSBpcyBpbnZhbGlkYCksZyhpPT09eXx8aT09PWIsYFVwZ3JhZGluZyBsb2NrIHR5cGUgdG8gJHtpfSBpcyBpbnZhbGlkYCk7bGV0IGU9YXdhaXQgcy51cGdyYWRlRXhjbHVzaXZlKCk7dC5pbnQzMihlPzA6LTEpLHQuZmluYWxpemUoKX1lbHNlIGcocy5sb2NrVHlwZT09PWksYERvd25ncmFkaW5nIGxvY2sgdG8gJHtpfSBpcyBpbnZhbGlkYCksdC5pbnQzMigwKSx0LmZpbmFsaXplKCk7ZWxzZXtnKGk9PT1wLGBOZXcgbG9ja3MgbXVzdCBzdGFydCBhcyBTSEFSRUQgaW5zdGVhZCBvZiAke2l9YCk7bGV0IHM9bmV3IGsoYXdhaXQgdihlKSk7YXdhaXQgcy5wcmVmZXRjaEZpcnN0QmxvY2soNTAwKSx3LnNldChlLHMpLHQuaW50MzIoMCksdC5maW5hbGl6ZSgpfX0oZSxpLHMpLHoodCxlKTticmVha31jYXNlInVubG9ja0ZpbGUiOntsZXQgaT10LnN0cmluZygpLHM9dC5pbnQzMigpO3QuZG9uZSgpLGF3YWl0IF8oZSxpLHMpLHoodCxlKTticmVha31kZWZhdWx0OnRocm93IG5ldyBFcnJvcigiVW5rbm93biBtZXRob2Q6ICIraSl9fXNlbGYub25tZXNzYWdlPXQ9Pntzd2l0Y2godC5kYXRhLnR5cGUpe2Nhc2UiaW5pdCI6e3Bvc3RNZXNzYWdlKHt0eXBlOiJfX2Fic3VyZDp3b3JrZXItcmVhZHkifSk7bGV0W3Mscl09dC5kYXRhLmJ1ZmZlcnM7eihuZXcgZShzLHtuYW1lOiJhcmdzIixkZWJ1ZzohMX0pLG5ldyBpKHIse25hbWU6InJlc3VsdHMiLGRlYnVnOiExfSkpO2JyZWFrfX19fSgpOwoK', null, false); +/* eslint-enable */ + +export default WorkerFactory; diff --git a/dist/perf/indexeddb-main-thread.js b/dist/perf/indexeddb-main-thread.js index a9950e2..334e0ca 100644 --- a/dist/perf/indexeddb-main-thread.js +++ b/dist/perf/indexeddb-main-thread.js @@ -69,7 +69,7 @@ function makeInitBackend(spawnEventName, getModule) { // Use the generic main thread module to create our indexeddb worker // proxy const initBackend = makeInitBackend('__absurd:spawn-idb-worker', () => - import('./indexeddb-main-thread-worker-8819e539.js') + import('./indexeddb-main-thread-worker-072c4623.js') ); export { initBackend }; diff --git a/dist2/index.js b/dist2/index.js deleted file mode 100644 index e990d6a..0000000 --- a/dist2/index.js +++ /dev/null @@ -1,194 +0,0 @@ -const ERRNO_CODES = { - EPERM: 63, - ENOENT: 44 -}; - -// This implements an emscripten-compatible filesystem that is means -// to be mounted to the one from `sql.js`. Example: -// -// let BFS = new BlockedFS(SQL.FS, idbBackend); -// SQL.FS.mount(BFS, {}, '/blocked'); -// -// Now any files created under '/blocked' will be handled by this -// filesystem, which creates a special file that handles read/writes -// in the way that we want. -class BlockedFS$1 { - constructor(FS, backend) { - this.FS = FS; - this.backend = backend; - - this.node_ops = { - getattr: node => { - let fileattr = FS.isFile(node.mode) ? node.contents.getattr() : null; - - let attr = {}; - attr.dev = 1; - attr.ino = node.id; - attr.mode = fileattr ? fileattr.mode : node.mode; - attr.nlink = 1; - attr.uid = 0; - attr.gid = 0; - attr.rdev = node.rdev; - attr.size = fileattr ? fileattr.size : FS.isDir(node.mode) ? 4096 : 0; - attr.atime = new Date(0); - attr.mtime = new Date(0); - attr.ctime = new Date(0); - attr.blksize = fileattr ? fileattr.blockSize : 4096; - attr.blocks = Math.ceil(attr.size / attr.blksize); - return attr; - }, - setattr: (node, attr) => { - if (FS.isFile(node)) { - node.contents.setattr(attr); - } else { - if (attr.mode != null) { - node.mode = attr.mode; - } - if (attr.size != null) { - node.size = attr.size; - } - } - }, - lookup: (parent, name) => { - throw new this.FS.ErrnoError(ERRNO_CODES.ENOENT); - }, - mknod: (parent, name, mode, dev) => { - if (name.endsWith('.lock')) { - throw new Error('Locking via lockfiles is not supported'); - } - - return this.createNode(parent, name, mode, dev); - }, - rename: (old_node, new_dir, new_name) => { - throw new Error('rename not implemented'); - }, - unlink: (parent, name) => { - let node = this.FS.lookupNode(parent, name); - node.contents.delete(name); - }, - readdir: node => { - // We could list all the available databases here if `node` is - // the root directory. However Firefox does not implemented - // such a methods. Other browsers do, but since it's not - // supported on all browsers users will need to track it - // separate anyway right now - - throw new Error('readdir not implemented'); - }, - symlink: (parent, newname, oldpath) => { - throw new Error('symlink not implemented'); - }, - readlink: node => { - throw new Error('symlink not implemented'); - } - }; - - this.stream_ops = { - open: stream => { - if (this.FS.isFile(stream.node.mode)) { - stream.node.contents.open(); - } - }, - - close: stream => { - if (this.FS.isFile(stream.node.mode)) { - stream.node.contents.close(); - } - }, - - read: (stream, buffer, offset, length, position) => { - // console.log('read', offset, length, position) - return stream.node.contents.read(buffer, offset, length, position); - }, - - write: (stream, buffer, offset, length, position) => { - // console.log('write', offset, length, position); - return stream.node.contents.write(buffer, offset, length, position); - }, - - llseek: (stream, offset, whence) => { - // Copied from MEMFS - var position = offset; - if (whence === 1) { - position += stream.position; - } else if (whence === 2) { - if (FS.isFile(stream.node.mode)) { - position += stream.node.contents.getattr().size; - } - } - if (position < 0) { - throw new this.FS.ErrnoError(28); - } - return position; - }, - allocate: (stream, offset, length) => { - stream.node.contents.setattr({ size: offset + length }); - }, - mmap: (stream, address, length, position, prot, flags) => { - throw new Error('mmap not implemented'); - }, - msync: (stream, buffer, offset, length, mmapFlags) => { - throw new Error('msync not implemented'); - }, - fsync: (stream, buffer, offset, length, mmapFlags) => { - stream.node.contents.fsync(); - } - }; - } - - async init() { - await this.backend.init(); - } - - mount() { - return this.createNode(null, '/', 16384 /* dir */ | 511 /* 0777 */, 0); - } - - lock(path, lockType) { - let { node } = this.FS.lookupPath(path); - return node.contents.lock(lockType); - } - - unlock(path, lockType) { - let { node } = this.FS.lookupPath(path); - return node.contents.unlock(lockType); - } - - createNode(parent, name, mode, dev) { - // Only files and directories supported - if (!(this.FS.isDir(mode) || this.FS.isFile(mode))) { - throw new this.FS.ErrnoError(ERRNO_CODES.EPERM); - } - - var node = this.FS.createNode(parent, name, mode, dev); - if (this.FS.isDir(node.mode)) { - node.node_ops = { - mknod: this.node_ops.mknod, - lookup: this.node_ops.lookup, - unlink: this.node_ops.unlink, - setattr: this.node_ops.setattr - }; - node.stream_ops = {}; - node.contents = {}; - } else if (this.FS.isFile(node.mode)) { - node.node_ops = this.node_ops; - node.stream_ops = this.stream_ops; - - // Create file! - node.contents = this.backend.createFile(name); - } - - // add the new node to the parent - if (parent) { - parent.contents[name] = node; - parent.timestamp = node.timestamp; - } - - return node; - } -} - -// Right now we don't support `export from` so we do this manually -const BlockedFS = BlockedFS$1; - -export { BlockedFS }; diff --git a/dist2/indexeddb-backend.js b/dist2/indexeddb-backend.js deleted file mode 100644 index 8bd0b60..0000000 --- a/dist2/indexeddb-backend.js +++ /dev/null @@ -1,855 +0,0 @@ -let FINALIZED = 0xdeadbeef; - -let WRITEABLE = 0; -let READABLE = 1; - -class Reader { - constructor( - buffer, - { initialOffset = 4, useAtomics = true, stream = true, debug, name } = {} - ) { - this.buffer = buffer; - this.atomicView = new Int32Array(buffer); - this.offset = initialOffset; - this.useAtomics = useAtomics; - this.stream = stream; - this.debug = debug; - this.name = name; - } - - log(...args) { - if (this.debug) { - console.log(`[reader: ${this.name}]`, ...args); - } - } - - waitWrite(name) { - if (this.useAtomics) { - this.log(`waiting for ${name}`); - - while (Atomics.load(this.atomicView, 0) === WRITEABLE) { - // console.log('waiting for write...'); - Atomics.wait(this.atomicView, 0, WRITEABLE, 500); - } - - this.log(`resumed for ${name}`); - } else { - if (this.atomicView[0] !== READABLE) { - throw new Error('`waitWrite` expected array to be readable'); - } - } - } - - flip() { - this.log('flip'); - if (this.useAtomics) { - let prev = Atomics.compareExchange( - this.atomicView, - 0, - READABLE, - WRITEABLE - ); - - if (prev !== READABLE) { - throw new Error('Read data out of sync! This is disastrous'); - } - - Atomics.notify(this.atomicView, 0); - } else { - this.atomicView[0] = WRITEABLE; - } - - this.offset = 4; - } - - done() { - this.waitWrite('done'); - - let dataView = new DataView(this.buffer, this.offset); - let done = dataView.getUint32(0) === FINALIZED; - - if (done) { - this.log('done'); - this.flip(); - } - - return done; - } - - peek(fn) { - this.peekOffset = this.offset; - let res = fn(); - this.offset = this.peekOffset; - this.peekOffset = null; - return res; - } - - string() { - this.waitWrite('string'); - - let byteLength = this._int32(); - let length = byteLength / 2; - - let dataView = new DataView(this.buffer, this.offset, byteLength); - let chars = []; - for (let i = 0; i < length; i++) { - chars.push(dataView.getUint16(i * 2)); - } - let str = String.fromCharCode.apply(null, chars); - this.log('string', str); - - this.offset += byteLength; - - if (this.peekOffset == null) { - this.flip(); - } - return str; - } - - _int32() { - let byteLength = 4; - - let dataView = new DataView(this.buffer, this.offset); - let num = dataView.getInt32(); - this.log('_int32', num); - - this.offset += byteLength; - return num; - } - - int32() { - this.waitWrite('int32'); - let num = this._int32(); - this.log('int32', num); - - if (this.peekOffset == null) { - this.flip(); - } - return num; - } - - bytes() { - this.waitWrite('bytes'); - - let byteLength = this._int32(); - - let bytes = new ArrayBuffer(byteLength); - new Uint8Array(bytes).set( - new Uint8Array(this.buffer, this.offset, byteLength) - ); - this.log('bytes', bytes); - - this.offset += byteLength; - - if (this.peekOffset == null) { - this.flip(); - } - return bytes; - } -} - -class Writer { - constructor( - buffer, - { initialOffset = 4, useAtomics = true, stream = true, debug, name } = {} - ) { - this.buffer = buffer; - this.atomicView = new Int32Array(buffer); - this.offset = initialOffset; - this.useAtomics = useAtomics; - this.stream = stream; - - this.debug = debug; - this.name = name; - - if (this.useAtomics) { - // The buffer starts out as writeable - Atomics.store(this.atomicView, 0, WRITEABLE); - } else { - this.atomicView[0] = WRITEABLE; - } - } - - log(...args) { - if (this.debug) { - console.log(`[writer: ${this.name}]`, ...args); - } - } - - waitRead(name) { - if (this.useAtomics) { - this.log(`waiting for ${name}`); - // Switch to writable - // Atomics.store(this.atomicView, 0, 1); - - let prev = Atomics.compareExchange( - this.atomicView, - 0, - WRITEABLE, - READABLE - ); - - if (prev !== WRITEABLE) { - throw new Error( - 'Wrote something into unwritable buffer! This is disastrous' - ); - } - - Atomics.notify(this.atomicView, 0); - - while (Atomics.load(this.atomicView, 0) === READABLE) { - // console.log('waiting to be read...'); - Atomics.wait(this.atomicView, 0, READABLE, 500); - } - - this.log(`resumed for ${name}`); - } else { - this.atomicView[0] = READABLE; - } - - this.offset = 4; - } - - finalize() { - this.log('finalizing'); - let dataView = new DataView(this.buffer, this.offset); - dataView.setUint32(0, FINALIZED); - this.waitRead('finalize'); - } - - string(str) { - this.log('string', str); - - let byteLength = str.length * 2; - this._int32(byteLength); - - let dataView = new DataView(this.buffer, this.offset, byteLength); - for (let i = 0; i < str.length; i++) { - dataView.setUint16(i * 2, str.charCodeAt(i)); - } - - this.offset += byteLength; - this.waitRead('string'); - } - - _int32(num) { - let byteLength = 4; - - let dataView = new DataView(this.buffer, this.offset); - dataView.setInt32(0, num); - - this.offset += byteLength; - } - - int32(num) { - this.log('int32', num); - this._int32(num); - this.waitRead('int32'); - } - - bytes(buffer) { - this.log('bytes', buffer); - - let byteLength = buffer.byteLength; - this._int32(byteLength); - new Uint8Array(this.buffer, this.offset).set(new Uint8Array(buffer)); - - this.offset += byteLength; - this.waitRead('bytes'); - } -} - -function range(start, end, step) { - let r = []; - for (let i = start; i <= end; i += step) { - r.push(i); - } - return r; -} - -function getBoundaryIndexes(blockSize, start, end) { - let startC = start - (start % blockSize); - let endC = end - 1 - ((end - 1) % blockSize); - - return range(startC, endC, blockSize); -} - -function readChunks(chunks, start, end) { - let buffer = new ArrayBuffer(end - start); - let bufferView = new Uint8Array(buffer); - for (let i = 0; i < chunks.length; i++) { - let chunk = chunks[i]; - - // TODO: jest has a bug where we can't do `instanceof ArrayBuffer` - if (chunk.data.constructor.name !== 'ArrayBuffer') { - throw new Error('Chunk data is not an ArrayBuffer'); - } - - let cstart = 0; - let cend = chunk.data.byteLength; - - if (start > chunk.pos) { - cstart = start - chunk.pos; - } - if (end < chunk.pos + chunk.data.byteLength) { - cend = end - chunk.pos; - } - - if (cstart > chunk.data.byteLength || cend < 0) { - continue; - } - - let len = cend - cstart; - - bufferView.set( - new Uint8Array(chunk.data, cstart, len), - chunk.pos - start + cstart - ); - } - - return buffer; -} - -function writeChunks(bufferView, blockSize, start, end) { - let indexes = getBoundaryIndexes(blockSize, start, end); - let cursor = 0; - - return indexes - .map(index => { - let cstart = 0; - let cend = blockSize; - if (start > index && start < index + blockSize) { - cstart = start - index; - } - if (end > index && end < index + blockSize) { - cend = end - index; - } - - let len = cend - cstart; - let chunkBuffer = new ArrayBuffer(blockSize); - - if (start > index + blockSize || end <= index) { - return null; - } - - let off = bufferView.byteOffset + cursor; - - let available = bufferView.buffer.byteLength - off; - if (available <= 0) { - return null; - } - - let readLength = Math.min(len, available); - - new Uint8Array(chunkBuffer).set( - new Uint8Array(bufferView.buffer, off, readLength), - cstart - ); - cursor += readLength; - - return { - pos: index, - data: chunkBuffer, - offset: cstart, - length: readLength - }; - }) - .filter(Boolean); -} - -class File { - constructor(filename, defaultBlockSize, ops, meta = null) { - this.filename = filename; - this.defaultBlockSize = defaultBlockSize; - this.buffer = new Map(); - this.ops = ops; - this.meta = meta; - this._metaDirty = false; - } - - bufferChunks(chunks) { - for (let i = 0; i < chunks.length; i++) { - let chunk = chunks[i]; - this.buffer.set(chunk.pos, chunk); - } - } - - open() { - this.meta = this.ops.readMeta(); - - if (this.meta == null) { - this.meta = {}; - - // New file - this.setattr({ - size: 0, - blockSize: this.defaultBlockSize - }); - - this.fsync(); - } - } - - close() { - this.fsync(); - this.ops.close(); - } - - delete() { - this.ops.delete(); - } - - load(indexes) { - let status = indexes.reduce( - (acc, b) => { - let inMemory = this.buffer.get(b); - if (inMemory) { - acc.chunks.push(inMemory); - } else { - acc.missing.push(b); - } - return acc; - }, - { chunks: [], missing: [] } - ); - - let missingChunks = []; - if (status.missing.length > 0) { - missingChunks = this.ops.readBlocks(status.missing, this.meta.blockSize); - } - return status.chunks.concat(missingChunks); - } - - read(bufferView, offset, length, position) { - // console.log('reading', this.filename, offset, length, position); - let buffer = bufferView.buffer; - - if (length <= 0) { - return 0; - } - if (position < 0) { - // TODO: is this right? - return 0; - } - if (position >= this.meta.size) { - let view = new Uint8Array(buffer, offset); - for (let i = 0; i < length; i++) { - view[i] = 0; - } - - return length; - } - - position = Math.max(position, 0); - let dataLength = Math.min(length, this.meta.size - position); - - let start = position; - let end = position + dataLength; - - let indexes = getBoundaryIndexes(this.meta.blockSize, start, end); - - let chunks = this.load(indexes); - let readBuffer = readChunks(chunks, start, end); - - if (buffer.byteLength - offset < readBuffer.byteLength) { - throw new Error('Buffer given to `read` is too small'); - } - let view = new Uint8Array(buffer); - view.set(new Uint8Array(readBuffer), offset); - - // TODO: I don't need to do this. `unixRead` does this for us. - for (let i = dataLength; i < length; i++) { - view[offset + i] = 0; - } - - return length; - } - - write(bufferView, offset, length, position) { - // console.log('writing', this.filename, offset, length, position); - let buffer = bufferView.buffer; - - if (length <= 0) { - return 0; - } - if (position < 0) { - return 0; - } - if (buffer.byteLength === 0) { - return 0; - } - - length = Math.min(length, buffer.byteLength - offset); - - let writes = writeChunks( - new Uint8Array(buffer, offset, length), - this.meta.blockSize, - position, - position + length - ); - - // Find any partial chunks and read them in and merge with - // existing data - let { partialWrites, fullWrites } = writes.reduce( - (state, write) => { - if (write.length !== this.meta.blockSize) { - state.partialWrites.push(write); - } else { - state.fullWrites.push({ - pos: write.pos, - data: write.data - }); - } - return state; - }, - { fullWrites: [], partialWrites: [] } - ); - - let reads = []; - if (partialWrites.length > 0) { - reads = this.load(partialWrites.map(w => w.pos)); - } - - let allWrites = fullWrites.concat( - reads.map(read => { - let write = partialWrites.find(w => w.pos === read.pos); - - // MuTatIoN! - new Uint8Array(read.data).set( - new Uint8Array(write.data, write.offset, write.length), - write.offset, - write.length - ); - - return read; - }) - ); - - this.bufferChunks(allWrites); - - if (position + length > this.meta.size) { - this.setattr({ size: position + length }); - } - - return length; - } - - lock(lockType) { - return this.ops.lock(lockType); - } - - unlock(lockType) { - return this.ops.unlock(lockType); - } - - fsync() { - if (this.buffer.size > 0) { - this.ops.writeBlocks([...this.buffer.values()], this.meta.blockSize); - } - - if (this._metaDirty) { - this.ops.writeMeta(this.meta); - this._metaDirty = false; - } - - this.buffer = new Map(); - } - - setattr(attr) { - if (attr.mode !== undefined) { - this.meta.mode = attr.mode; - this._metaDirty = true; - } - - if (attr.timestamp !== undefined) { - this.meta.timestamp = attr.timestamp; - this._metaDirty = true; - } - - if (attr.size !== undefined) { - this.meta.size = attr.size; - this._metaDirty = true; - } - - if (attr.blockSize !== undefined) { - if (this.meta.blockSize != null) { - throw new Error('Changing blockSize is not allowed yet'); - } - this.meta.blockSize = attr.blockSize; - this._metaDirty = true; - } - } - - getattr() { - return this.meta; - } -} - -// These are temporarily global, but will be easy to clean up later -let reader, writer; - -function positionToKey(pos, blockSize) { - // We are forced to round because of floating point error. `pos` - // should always be divisible by `blockSize` - return Math.round(pos / blockSize); -} - -function invokeWorker(method, args) { - switch (method) { - case 'readBlocks': { - let { name, positions, blockSize } = args; - - let res = []; - for (let pos of positions) { - writer.string('readBlock'); - writer.string(name); - writer.int32(positionToKey(pos, blockSize)); - writer.finalize(); - - let data = reader.bytes(); - reader.done(); - res.push({ - pos, - // If th length is 0, the block didn't exist. We return a - // blank block in that case - data: data.byteLength === 0 ? new ArrayBuffer(blockSize) : data - }); - } - - return res; - } - - case 'writeBlocks': { - let { name, writes, blockSize } = args; - writer.string('writeBlocks'); - writer.string(name); - for (let write of writes) { - writer.int32(positionToKey(write.pos, blockSize)); - writer.bytes(write.data); - } - writer.finalize(); - - // Block for empty response - - let res = reader.int32(); - reader.done(); - return res; - } - - case 'readMeta': { - writer.string('readMeta'); - writer.string(args.name); - writer.finalize(); - - let size = reader.int32(); - let blockSize = reader.int32(); - reader.done(); - return size === -1 ? null : { size, blockSize }; - } - - case 'writeMeta': { - let { name, meta } = args; - writer.string('writeMeta'); - writer.string(name); - writer.int32(meta.size); - writer.int32(meta.blockSize); - writer.finalize(); - - let res = reader.int32(); - reader.done(); - return res; - } - - case 'deleteFile': { - writer.string('deleteFile'); - writer.string(args.name); - writer.finalize(); - - let res = reader.int32(); - reader.done(); - return res; - } - - case 'closeFile': { - writer.string('closeFile'); - writer.string(args.name); - writer.finalize(); - - let res = reader.int32(); - reader.done(); - return res; - } - - case 'lockFile': { - writer.string('lockFile'); - writer.string(args.name); - writer.int32(args.lockType); - writer.finalize(); - - let res = reader.int32(); - reader.done(); - return res === 0; - } - - case 'unlockFile': { - writer.string('unlockFile'); - writer.string(args.name); - writer.int32(args.lockType); - writer.finalize(); - - let res = reader.int32(); - reader.done(); - return res === 0; - } - } -} - -class FileOps { - constructor(filename) { - this.filename = filename; - } - - getStoreName() { - return this.filename.replace(/\//g, '-'); - } - - lock(lockType) { - return invokeWorker('lockFile', { name: this.getStoreName(), lockType }); - } - - unlock(lockType) { - return invokeWorker('unlockFile', { name: this.getStoreName(), lockType }); - } - - delete() { - return invokeWorker('deleteFile', { name: this.getStoreName() }); - } - - close() { - return invokeWorker('closeFile', { name: this.getStoreName() }); - } - - readMeta() { - return invokeWorker('readMeta', { name: this.getStoreName() }); - } - - writeMeta(meta) { - return invokeWorker('writeMeta', { name: this.getStoreName(), meta }); - } - - readBlocks(positions, blockSize) { - // if (Math.random() < 0.005) { - // console.log('reading', positions); - // } - - if (this.stats) { - this.stats.read += positions.length; - } - - return invokeWorker('readBlocks', { - name: this.getStoreName(), - positions, - blockSize - }); - } - - writeBlocks(writes, blockSize) { - // console.log('_writing', this.filename, writes); - if (this.stats) { - this.stats.writes += writes.length; - } - - return invokeWorker('writeBlocks', { - name: this.getStoreName(), - writes, - blockSize - }); - } -} - -function startWorker(reader, writer) { - let onReady; - let workerReady = new Promise(resolve => (onReady = resolve)); - - self.postMessage({ - type: '__absurd:spawn-idb-worker', - argBuffer: writer.buffer, - resultBuffer: reader.buffer - }); - - self.addEventListener('message', e => { - switch (e.data.type) { - case '__absurd:worker-ready': - onReady(); - break; - - // Normally you would use `postMessage` control the profiler in - // a worker (just like this worker go those events), and the - // perf library automatically handles those events. We don't do - // that for the special backend worker though because it's - // always blocked when it's not processing. Instead we forward - // these events by going through the atomics layer to unblock it - // to make sure it starts immediately - case '__perf-deets:start-profile': - writer.string('profile-start'); - writer.finalize(); - reader.int32(); - reader.done(); - break; - - case '__perf-deets:end-profile': - writer.string('profile-end'); - writer.finalize(); - reader.int32(); - reader.done(); - break; - } - }); - - return workerReady; -} - -class IndexedDBBackend { - constructor(defaultBlockSize) { - this.defaultBlockSize = defaultBlockSize; - } - - async init() { - let argBuffer = new SharedArrayBuffer(4096 * 9); - writer = this.writer = new Writer(argBuffer, { - name: 'args (backend)', - debug: false - }); - - let resultBuffer = new SharedArrayBuffer(4096 * 9); - reader = new Reader(resultBuffer, { - name: 'results', - debug: false - }); - - await startWorker(reader, writer); - } - - createFile(filename) { - return new File(filename, this.defaultBlockSize, new FileOps(filename)); - } - - // Instead of controlling the profiler from the main thread by - // posting a message to this worker, you can control it inside the - // worker manually with these methods - startProfile() { - writer.string('profile-start'); - writer.finalize(); - reader.int32(); - reader.done(); - } - - stopProfile() { - writer.string('profile-stop'); - writer.finalize(); - reader.int32(); - reader.done(); - } -} - -export default IndexedDBBackend; diff --git a/dist2/indexeddb-main-thread-worker-14b63d9a.js b/dist2/indexeddb-main-thread-worker-14b63d9a.js deleted file mode 100644 index bddc5de..0000000 --- a/dist2/indexeddb-main-thread-worker-14b63d9a.js +++ /dev/null @@ -1,34 +0,0 @@ -function decodeBase64(base64, enableUnicode) { - var binaryString = atob(base64); - if (enableUnicode) { - var binaryView = new Uint8Array(binaryString.length); - for (var i = 0, n = binaryString.length; i < n; ++i) { - binaryView[i] = binaryString.charCodeAt(i); - } - return String.fromCharCode.apply(null, new Uint16Array(binaryView.buffer)); - } - return binaryString; -} - -function createURL(base64, sourcemapArg, enableUnicodeArg) { - var sourcemap = sourcemapArg === undefined ? null : sourcemapArg; - var enableUnicode = enableUnicodeArg === undefined ? false : enableUnicodeArg; - var source = decodeBase64(base64, enableUnicode); - var start = source.indexOf('\n', 10) + 1; - var body = source.substring(start) + (sourcemap ? '\/\/# sourceMappingURL=' + sourcemap : ''); - var blob = new Blob([body], { type: 'application/javascript' }); - return URL.createObjectURL(blob); -} - -function createBase64WorkerFactory(base64, sourcemapArg, enableUnicodeArg) { - var url; - return function WorkerFactory(options) { - url = url || createURL(base64, sourcemapArg, enableUnicodeArg); - return new Worker(url, options); - }; -} - -var WorkerFactory = createBase64WorkerFactory('Lyogcm9sbHVwLXBsdWdpbi13ZWItd29ya2VyLWxvYWRlciAqLwooZnVuY3Rpb24gKCkgewogICd1c2Ugc3RyaWN0JzsKCiAgbGV0IEZJTkFMSVpFRCA9IDB4ZGVhZGJlZWY7CgogIGxldCBXUklURUFCTEUgPSAwOwogIGxldCBSRUFEQUJMRSA9IDE7CgogIGNsYXNzIFJlYWRlciB7CiAgICBjb25zdHJ1Y3RvcigKICAgICAgYnVmZmVyLAogICAgICB7IGluaXRpYWxPZmZzZXQgPSA0LCB1c2VBdG9taWNzID0gdHJ1ZSwgc3RyZWFtID0gdHJ1ZSwgZGVidWcsIG5hbWUgfSA9IHt9CiAgICApIHsKICAgICAgdGhpcy5idWZmZXIgPSBidWZmZXI7CiAgICAgIHRoaXMuYXRvbWljVmlldyA9IG5ldyBJbnQzMkFycmF5KGJ1ZmZlcik7CiAgICAgIHRoaXMub2Zmc2V0ID0gaW5pdGlhbE9mZnNldDsKICAgICAgdGhpcy51c2VBdG9taWNzID0gdXNlQXRvbWljczsKICAgICAgdGhpcy5zdHJlYW0gPSBzdHJlYW07CiAgICAgIHRoaXMuZGVidWcgPSBkZWJ1ZzsKICAgICAgdGhpcy5uYW1lID0gbmFtZTsKICAgIH0KCiAgICBsb2coLi4uYXJncykgewogICAgICBpZiAodGhpcy5kZWJ1ZykgewogICAgICAgIGNvbnNvbGUubG9nKGBbcmVhZGVyOiAke3RoaXMubmFtZX1dYCwgLi4uYXJncyk7CiAgICAgIH0KICAgIH0KCiAgICB3YWl0V3JpdGUobmFtZSkgewogICAgICBpZiAodGhpcy51c2VBdG9taWNzKSB7CiAgICAgICAgdGhpcy5sb2coYHdhaXRpbmcgZm9yICR7bmFtZX1gKTsKCiAgICAgICAgd2hpbGUgKEF0b21pY3MubG9hZCh0aGlzLmF0b21pY1ZpZXcsIDApID09PSBXUklURUFCTEUpIHsKICAgICAgICAgIC8vIGNvbnNvbGUubG9nKCd3YWl0aW5nIGZvciB3cml0ZS4uLicpOwogICAgICAgICAgQXRvbWljcy53YWl0KHRoaXMuYXRvbWljVmlldywgMCwgV1JJVEVBQkxFLCA1MDApOwogICAgICAgIH0KCiAgICAgICAgdGhpcy5sb2coYHJlc3VtZWQgZm9yICR7bmFtZX1gKTsKICAgICAgfSBlbHNlIHsKICAgICAgICBpZiAodGhpcy5hdG9taWNWaWV3WzBdICE9PSBSRUFEQUJMRSkgewogICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdgd2FpdFdyaXRlYCBleHBlY3RlZCBhcnJheSB0byBiZSByZWFkYWJsZScpOwogICAgICAgIH0KICAgICAgfQogICAgfQoKICAgIGZsaXAoKSB7CiAgICAgIHRoaXMubG9nKCdmbGlwJyk7CiAgICAgIGlmICh0aGlzLnVzZUF0b21pY3MpIHsKICAgICAgICBsZXQgcHJldiA9IEF0b21pY3MuY29tcGFyZUV4Y2hhbmdlKAogICAgICAgICAgdGhpcy5hdG9taWNWaWV3LAogICAgICAgICAgMCwKICAgICAgICAgIFJFQURBQkxFLAogICAgICAgICAgV1JJVEVBQkxFCiAgICAgICAgKTsKCiAgICAgICAgaWYgKHByZXYgIT09IFJFQURBQkxFKSB7CiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1JlYWQgZGF0YSBvdXQgb2Ygc3luYyEgVGhpcyBpcyBkaXNhc3Ryb3VzJyk7CiAgICAgICAgfQoKICAgICAgICBBdG9taWNzLm5vdGlmeSh0aGlzLmF0b21pY1ZpZXcsIDApOwogICAgICB9IGVsc2UgewogICAgICAgIHRoaXMuYXRvbWljVmlld1swXSA9IFdSSVRFQUJMRTsKICAgICAgfQoKICAgICAgdGhpcy5vZmZzZXQgPSA0OwogICAgfQoKICAgIGRvbmUoKSB7CiAgICAgIHRoaXMud2FpdFdyaXRlKCdkb25lJyk7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0KTsKICAgICAgbGV0IGRvbmUgPSBkYXRhVmlldy5nZXRVaW50MzIoMCkgPT09IEZJTkFMSVpFRDsKCiAgICAgIGlmIChkb25lKSB7CiAgICAgICAgdGhpcy5sb2coJ2RvbmUnKTsKICAgICAgICB0aGlzLmZsaXAoKTsKICAgICAgfQoKICAgICAgcmV0dXJuIGRvbmU7CiAgICB9CgogICAgcGVlayhmbikgewogICAgICB0aGlzLnBlZWtPZmZzZXQgPSB0aGlzLm9mZnNldDsKICAgICAgbGV0IHJlcyA9IGZuKCk7CiAgICAgIHRoaXMub2Zmc2V0ID0gdGhpcy5wZWVrT2Zmc2V0OwogICAgICB0aGlzLnBlZWtPZmZzZXQgPSBudWxsOwogICAgICByZXR1cm4gcmVzOwogICAgfQoKICAgIHN0cmluZygpIHsKICAgICAgdGhpcy53YWl0V3JpdGUoJ3N0cmluZycpOwoKICAgICAgbGV0IGJ5dGVMZW5ndGggPSB0aGlzLl9pbnQzMigpOwogICAgICBsZXQgbGVuZ3RoID0gYnl0ZUxlbmd0aCAvIDI7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0LCBieXRlTGVuZ3RoKTsKICAgICAgbGV0IGNoYXJzID0gW107CiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbGVuZ3RoOyBpKyspIHsKICAgICAgICBjaGFycy5wdXNoKGRhdGFWaWV3LmdldFVpbnQxNihpICogMikpOwogICAgICB9CiAgICAgIGxldCBzdHIgPSBTdHJpbmcuZnJvbUNoYXJDb2RlLmFwcGx5KG51bGwsIGNoYXJzKTsKICAgICAgdGhpcy5sb2coJ3N0cmluZycsIHN0cik7CgogICAgICB0aGlzLm9mZnNldCArPSBieXRlTGVuZ3RoOwoKICAgICAgaWYgKHRoaXMucGVla09mZnNldCA9PSBudWxsKSB7CiAgICAgICAgdGhpcy5mbGlwKCk7CiAgICAgIH0KICAgICAgcmV0dXJuIHN0cjsKICAgIH0KCiAgICBfaW50MzIoKSB7CiAgICAgIGxldCBieXRlTGVuZ3RoID0gNDsKCiAgICAgIGxldCBkYXRhVmlldyA9IG5ldyBEYXRhVmlldyh0aGlzLmJ1ZmZlciwgdGhpcy5vZmZzZXQpOwogICAgICBsZXQgbnVtID0gZGF0YVZpZXcuZ2V0SW50MzIoKTsKICAgICAgdGhpcy5sb2coJ19pbnQzMicsIG51bSk7CgogICAgICB0aGlzLm9mZnNldCArPSBieXRlTGVuZ3RoOwogICAgICByZXR1cm4gbnVtOwogICAgfQoKICAgIGludDMyKCkgewogICAgICB0aGlzLndhaXRXcml0ZSgnaW50MzInKTsKICAgICAgbGV0IG51bSA9IHRoaXMuX2ludDMyKCk7CiAgICAgIHRoaXMubG9nKCdpbnQzMicsIG51bSk7CgogICAgICBpZiAodGhpcy5wZWVrT2Zmc2V0ID09IG51bGwpIHsKICAgICAgICB0aGlzLmZsaXAoKTsKICAgICAgfQogICAgICByZXR1cm4gbnVtOwogICAgfQoKICAgIGJ5dGVzKCkgewogICAgICB0aGlzLndhaXRXcml0ZSgnYnl0ZXMnKTsKCiAgICAgIGxldCBieXRlTGVuZ3RoID0gdGhpcy5faW50MzIoKTsKCiAgICAgIGxldCBieXRlcyA9IG5ldyBBcnJheUJ1ZmZlcihieXRlTGVuZ3RoKTsKICAgICAgbmV3IFVpbnQ4QXJyYXkoYnl0ZXMpLnNldCgKICAgICAgICBuZXcgVWludDhBcnJheSh0aGlzLmJ1ZmZlciwgdGhpcy5vZmZzZXQsIGJ5dGVMZW5ndGgpCiAgICAgICk7CiAgICAgIHRoaXMubG9nKCdieXRlcycsIGJ5dGVzKTsKCiAgICAgIHRoaXMub2Zmc2V0ICs9IGJ5dGVMZW5ndGg7CgogICAgICBpZiAodGhpcy5wZWVrT2Zmc2V0ID09IG51bGwpIHsKICAgICAgICB0aGlzLmZsaXAoKTsKICAgICAgfQogICAgICByZXR1cm4gYnl0ZXM7CiAgICB9CiAgfQoKICBjbGFzcyBXcml0ZXIgewogICAgY29uc3RydWN0b3IoCiAgICAgIGJ1ZmZlciwKICAgICAgeyBpbml0aWFsT2Zmc2V0ID0gNCwgdXNlQXRvbWljcyA9IHRydWUsIHN0cmVhbSA9IHRydWUsIGRlYnVnLCBuYW1lIH0gPSB7fQogICAgKSB7CiAgICAgIHRoaXMuYnVmZmVyID0gYnVmZmVyOwogICAgICB0aGlzLmF0b21pY1ZpZXcgPSBuZXcgSW50MzJBcnJheShidWZmZXIpOwogICAgICB0aGlzLm9mZnNldCA9IGluaXRpYWxPZmZzZXQ7CiAgICAgIHRoaXMudXNlQXRvbWljcyA9IHVzZUF0b21pY3M7CiAgICAgIHRoaXMuc3RyZWFtID0gc3RyZWFtOwoKICAgICAgdGhpcy5kZWJ1ZyA9IGRlYnVnOwogICAgICB0aGlzLm5hbWUgPSBuYW1lOwoKICAgICAgaWYgKHRoaXMudXNlQXRvbWljcykgewogICAgICAgIC8vIFRoZSBidWZmZXIgc3RhcnRzIG91dCBhcyB3cml0ZWFibGUKICAgICAgICBBdG9taWNzLnN0b3JlKHRoaXMuYXRvbWljVmlldywgMCwgV1JJVEVBQkxFKTsKICAgICAgfSBlbHNlIHsKICAgICAgICB0aGlzLmF0b21pY1ZpZXdbMF0gPSBXUklURUFCTEU7CiAgICAgIH0KICAgIH0KCiAgICBsb2coLi4uYXJncykgewogICAgICBpZiAodGhpcy5kZWJ1ZykgewogICAgICAgIGNvbnNvbGUubG9nKGBbd3JpdGVyOiAke3RoaXMubmFtZX1dYCwgLi4uYXJncyk7CiAgICAgIH0KICAgIH0KCiAgICB3YWl0UmVhZChuYW1lKSB7CiAgICAgIGlmICh0aGlzLnVzZUF0b21pY3MpIHsKICAgICAgICB0aGlzLmxvZyhgd2FpdGluZyBmb3IgJHtuYW1lfWApOwogICAgICAgIC8vIFN3aXRjaCB0byB3cml0YWJsZQogICAgICAgIC8vIEF0b21pY3Muc3RvcmUodGhpcy5hdG9taWNWaWV3LCAwLCAxKTsKCiAgICAgICAgbGV0IHByZXYgPSBBdG9taWNzLmNvbXBhcmVFeGNoYW5nZSgKICAgICAgICAgIHRoaXMuYXRvbWljVmlldywKICAgICAgICAgIDAsCiAgICAgICAgICBXUklURUFCTEUsCiAgICAgICAgICBSRUFEQUJMRQogICAgICAgICk7CgogICAgICAgIGlmIChwcmV2ICE9PSBXUklURUFCTEUpIHsKICAgICAgICAgIHRocm93IG5ldyBFcnJvcigKICAgICAgICAgICAgJ1dyb3RlIHNvbWV0aGluZyBpbnRvIHVud3JpdGFibGUgYnVmZmVyISBUaGlzIGlzIGRpc2FzdHJvdXMnCiAgICAgICAgICApOwogICAgICAgIH0KCiAgICAgICAgQXRvbWljcy5ub3RpZnkodGhpcy5hdG9taWNWaWV3LCAwKTsKCiAgICAgICAgd2hpbGUgKEF0b21pY3MubG9hZCh0aGlzLmF0b21pY1ZpZXcsIDApID09PSBSRUFEQUJMRSkgewogICAgICAgICAgLy8gY29uc29sZS5sb2coJ3dhaXRpbmcgdG8gYmUgcmVhZC4uLicpOwogICAgICAgICAgQXRvbWljcy53YWl0KHRoaXMuYXRvbWljVmlldywgMCwgUkVBREFCTEUsIDUwMCk7CiAgICAgICAgfQoKICAgICAgICB0aGlzLmxvZyhgcmVzdW1lZCBmb3IgJHtuYW1lfWApOwogICAgICB9IGVsc2UgewogICAgICAgIHRoaXMuYXRvbWljVmlld1swXSA9IFJFQURBQkxFOwogICAgICB9CgogICAgICB0aGlzLm9mZnNldCA9IDQ7CiAgICB9CgogICAgZmluYWxpemUoKSB7CiAgICAgIHRoaXMubG9nKCdmaW5hbGl6aW5nJyk7CiAgICAgIGxldCBkYXRhVmlldyA9IG5ldyBEYXRhVmlldyh0aGlzLmJ1ZmZlciwgdGhpcy5vZmZzZXQpOwogICAgICBkYXRhVmlldy5zZXRVaW50MzIoMCwgRklOQUxJWkVEKTsKICAgICAgdGhpcy53YWl0UmVhZCgnZmluYWxpemUnKTsKICAgIH0KCiAgICBzdHJpbmcoc3RyKSB7CiAgICAgIHRoaXMubG9nKCdzdHJpbmcnLCBzdHIpOwoKICAgICAgbGV0IGJ5dGVMZW5ndGggPSBzdHIubGVuZ3RoICogMjsKICAgICAgdGhpcy5faW50MzIoYnl0ZUxlbmd0aCk7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0LCBieXRlTGVuZ3RoKTsKICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBzdHIubGVuZ3RoOyBpKyspIHsKICAgICAgICBkYXRhVmlldy5zZXRVaW50MTYoaSAqIDIsIHN0ci5jaGFyQ29kZUF0KGkpKTsKICAgICAgfQoKICAgICAgdGhpcy5vZmZzZXQgKz0gYnl0ZUxlbmd0aDsKICAgICAgdGhpcy53YWl0UmVhZCgnc3RyaW5nJyk7CiAgICB9CgogICAgX2ludDMyKG51bSkgewogICAgICBsZXQgYnl0ZUxlbmd0aCA9IDQ7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0KTsKICAgICAgZGF0YVZpZXcuc2V0SW50MzIoMCwgbnVtKTsKCiAgICAgIHRoaXMub2Zmc2V0ICs9IGJ5dGVMZW5ndGg7CiAgICB9CgogICAgaW50MzIobnVtKSB7CiAgICAgIHRoaXMubG9nKCdpbnQzMicsIG51bSk7CiAgICAgIHRoaXMuX2ludDMyKG51bSk7CiAgICAgIHRoaXMud2FpdFJlYWQoJ2ludDMyJyk7CiAgICB9CgogICAgYnl0ZXMoYnVmZmVyKSB7CiAgICAgIHRoaXMubG9nKCdieXRlcycsIGJ1ZmZlcik7CgogICAgICBsZXQgYnl0ZUxlbmd0aCA9IGJ1ZmZlci5ieXRlTGVuZ3RoOwogICAgICB0aGlzLl9pbnQzMihieXRlTGVuZ3RoKTsKICAgICAgbmV3IFVpbnQ4QXJyYXkodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0KS5zZXQobmV3IFVpbnQ4QXJyYXkoYnVmZmVyKSk7CgogICAgICB0aGlzLm9mZnNldCArPSBieXRlTGVuZ3RoOwogICAgICB0aGlzLndhaXRSZWFkKCdieXRlcycpOwogICAgfQogIH0KCiAgbGV0IGlzUHJvYmFibHlTYWZhcmkgPSAvXigoPyFjaHJvbWV8YW5kcm9pZCkuKSpzYWZhcmkvaS50ZXN0KAogICAgbmF2aWdhdG9yLnVzZXJBZ2VudAogICk7CgogIGxldCBvcGVuRGJzID0gbmV3IE1hcCgpOwogIGxldCB0cmFuc2FjdGlvbnMgPSBuZXcgTWFwKCk7CgogIGZ1bmN0aW9uIGFzc2VydChjb25kLCBtc2cpIHsKICAgIGlmICghY29uZCkgewogICAgICB0aHJvdyBuZXcgRXJyb3IobXNnKTsKICAgIH0KICB9CgogIGxldCBMT0NLX1RZUEVTID0gewogICAgTk9ORTogMCwKICAgIFNIQVJFRDogMSwKICAgIFJFU0VSVkVEOiAyLAogICAgUEVORElORzogMywKICAgIEVYQ0xVU0lWRTogNAogIH07CgogIC8vIFdlIHVzZSBsb25nLWxpdmVkIHRyYW5zYWN0aW9ucywgYW5kIGBUcmFuc2FjdGlvbmAga2VlcHMgdGhlCiAgLy8gdHJhbnNhY3Rpb24gc3RhdGUuIEl0IGltcGxlbWVudHMgYW4gb3B0aW1hbCB3YXkgdG8gcGVyZm9ybQogIC8vIHJlYWQvd3JpdGVzIHdpdGgga25vd2xlZGdlIG9mIGhvdyBzcWxpdGUgYXNrcyBmb3IgdGhlbSwgYW5kIGFsc28KICAvLyBpbXBsZW1lbnRzIGEgbG9ja2luZyBtZWNoYW5pc20gdGhhdCBtYXBzIHRvIGhvdyBzcWxpdGUgbG9ja3Mgd29yay4KICBjbGFzcyBUcmFuc2FjdGlvbiB7CiAgICBjb25zdHJ1Y3RvcihkYiwgaW5pdGlhbE1vZGUgPSAncmVhZG9ubHknKSB7CiAgICAgIHRoaXMuZGIgPSBkYjsKICAgICAgdGhpcy50cmFucyA9IHRoaXMuZGIudHJhbnNhY3Rpb24oWydkYXRhJ10sIGluaXRpYWxNb2RlKTsKICAgICAgdGhpcy5zdG9yZSA9IHRoaXMudHJhbnMub2JqZWN0U3RvcmUoJ2RhdGEnKTsKICAgICAgdGhpcy5sb2NrVHlwZSA9CiAgICAgICAgaW5pdGlhbE1vZGUgPT09ICdyZWFkb25seScgPyBMT0NLX1RZUEVTLlNIQVJFRCA6IExPQ0tfVFlQRVMuRVhDTFVTSVZFOwoKICAgICAgLy8gVGhlcmUgaXMgbm8gbmVlZCBmb3IgdXMgdG8gY2FjaGUgYmxvY2tzLiBVc2Ugc3FsaXRlJ3MKICAgICAgLy8gYGNhY2hlX3NpemVgIGZvciB0aGF0IGFuZCBpdCB3aWxsIGF1dG9tYXRpY2FsbHkgZG8gaXQuIEhvd2V2ZXIsCiAgICAgIC8vIHdlIGRvIHN0aWxsIGtlZXAgYSBjYWNoZSBvZiB0aGUgZmlyc3QgYmxvY2sgZm9yIHRoZSBkdXJhdGlvbiBvZgogICAgICAvLyB0aGlzIHRyYW5zYWN0aW9uIGJlY2F1c2Ugb2YgaG93IGxvY2tpbmcgd29ya3M7IHRoaXMgYXZvaWRzIGEKICAgICAgLy8gZmV3IGV4dHJhIHJlYWRzIGFuZCBhbGxvd3MgdXMgdG8gZGV0ZWN0IGNoYW5nZXMgZHVyaW5nCiAgICAgIC8vIHVwZ3JhZGluZyAoc2VlIGB1cGdyYWRlRXhjbHVzaXZlYCkKICAgICAgdGhpcy5jYWNoZWRGaXJzdEJsb2NrID0gbnVsbDsKCiAgICAgIHRoaXMuY3Vyc29yID0gbnVsbDsKICAgICAgdGhpcy5wcmV2UmVhZHMgPSBudWxsOwogICAgfQoKICAgIGFzeW5jIHByZWZldGNoRmlyc3RCbG9jayh0aW1lb3V0KSB7CiAgICAgIC8vIFRPRE86IGltcGxlbWVudCB0aW1lb3V0CgogICAgICAvLyBHZXQgdGhlIGZpcnN0IGJsb2NrIGFuZCBjYWNoZSBpdAogICAgICBsZXQgYmxvY2sgPSBhd2FpdCB0aGlzLmdldCgwKTsKICAgICAgdGhpcy5jYWNoZWRGaXJzdEJsb2NrID0gYmxvY2s7CiAgICAgIHJldHVybiBibG9jazsKICAgIH0KCiAgICBhc3luYyB3YWl0Q29tcGxldGUoKSB7CiAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7CiAgICAgICAgLy8gRWFnZXJseSBjb21taXQgaXQgZm9yIGJldHRlciBwZXJmLiBOb3RlIHRoYXQgKip0aGlzIGFzc3VtZXMKICAgICAgICAvLyB0aGUgdHJhbnNhY3Rpb24gaXMgb3BlbioqIGFzIGBjb21taXRgIHdpbGwgdGhyb3cgYW4gZXJyb3IgaWYKICAgICAgICAvLyBpdCdzIGFscmVhZHkgY2xvc2VkICh3aGljaCBzaG91bGQgbmV2ZXIgYmUgdGhlIGNhc2UgZm9yIHVzKQogICAgICAgIHRoaXMuY29tbWl0KCk7CgogICAgICAgIGlmICh0aGlzLmxvY2tUeXBlID09PSBMT0NLX1RZUEVTLkVYQ0xVU0lWRSkgewogICAgICAgICAgLy8gV2FpdCB1bnRpbCBhbGwgd3JpdGVzIGFyZSBjb21taXR0ZWQKICAgICAgICAgIHRoaXMudHJhbnMub25jb21wbGV0ZSA9IGUgPT4gcmVzb2x2ZSgpOwoKICAgICAgICAgIC8vIFRPRE86IElzIGl0IE9LIHRvIGFkZCB0aGlzIGxhdGVyLCBhZnRlciBhbiBlcnJvciBtaWdodCBoYXZlCiAgICAgICAgICAvLyBoYXBwZW5lZD8gV2lsbCBpdCBob2xkIHRoZSBlcnJvciBhbmQgZmlyZSB0aGlzIHdoZW4gd2UKICAgICAgICAgIC8vIGF0dGFjaGVkIGl0PyBXZSBtaWdodCB3YW50IHRvIGVhZ2VybHkgY3JlYXRlIHRoZSBwcm9taXNlCiAgICAgICAgICAvLyB3aGVuIGNyZWF0aW5nIHRoZSB0cmFuc2FjdGlvbiBhbmQgcmV0dXJuIGl0IGhlcmUKICAgICAgICAgIHRoaXMudHJhbnMub25lcnJvciA9IGUgPT4gcmVqZWN0KGUpOwogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICBpZiAoaXNQcm9iYWJseVNhZmFyaSkgewogICAgICAgICAgICAvLyBTYWZhcmkgaGFzIGEgYnVnIHdoZXJlIHNvbWV0aW1lcyB0aGUgSURCIGdldHMgYmxvY2tlZAogICAgICAgICAgICAvLyBwZXJtYW5lbnRseSBpZiB5b3UgcmVmcmVzaCB0aGUgcGFnZSB3aXRoIGFuIG9wZW4KICAgICAgICAgICAgLy8gdHJhbnNhY3Rpb24uIFlvdSBoYXZlIHRvIHJlc3RhcnQgdGhlIGJyb3dzZXIgdG8gZml4IGl0LgogICAgICAgICAgICAvLyBXZSB3YWl0IGZvciByZWFkb25seSB0cmFuc2FjdGlvbnMgdG8gZmluaXNoIHRvbywgYnV0IHRoaXMKICAgICAgICAgICAgLy8gaXMgYSBwZXJmIGhpdAogICAgICAgICAgICB0aGlzLnRyYW5zLm9uY29tcGxldGUgPSBlID0+IHJlc29sdmUoKTsKICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIC8vIE5vIG5lZWQgdG8gd2FpdCBvbiBhbnl0aGluZyBpbiBhIHJlYWQtb25seSB0cmFuc2FjdGlvbi4KICAgICAgICAgICAgLy8gTm90ZSB0aGF0IGVycm9ycyBkdXJpbmcgcmVhZHMgYXJlYSBhbHdheXMgaGFuZGxlZCBieSB0aGUKICAgICAgICAgICAgLy8gcmVhZCByZXF1ZXN0LgogICAgICAgICAgICByZXNvbHZlKCk7CiAgICAgICAgICB9CiAgICAgICAgfQogICAgICB9KTsKICAgIH0KCiAgICBjb21taXQoKSB7CiAgICAgIC8vIFNhZmFyaSBkb2Vzbid0IHN1cHBvcnQgdGhpcyBtZXRob2QgeWV0ICh0aGlzIGlzIGp1c3QgYW4KICAgICAgLy8gb3B0aW1pemF0aW9uKQogICAgICBpZiAodGhpcy50cmFucy5jb21taXQpIHsKICAgICAgICB0aGlzLnRyYW5zLmNvbW1pdCgpOwogICAgICB9CiAgICB9CgogICAgYXN5bmMgdXBncmFkZUV4Y2x1c2l2ZSgpIHsKICAgICAgdGhpcy5jb21taXQoKTsKICAgICAgdGhpcy50cmFucyA9IHRoaXMuZGIudHJhbnNhY3Rpb24oWydkYXRhJ10sICdyZWFkd3JpdGUnKTsKICAgICAgdGhpcy5zdG9yZSA9IHRoaXMudHJhbnMub2JqZWN0U3RvcmUoJ2RhdGEnKTsKICAgICAgdGhpcy5sb2NrVHlwZSA9IExPQ0tfVFlQRVMuRVhDTFVTSVZFOwoKICAgICAgbGV0IGNhY2hlZDAgPSB0aGlzLmNhY2hlZEZpcnN0QmxvY2s7CgogICAgICAvLyBEbyBhIHJlYWQKICAgICAgbGV0IGJsb2NrID0gYXdhaXQgdGhpcy5wcmVmZXRjaEZpcnN0QmxvY2soNTAwKTsKICAgICAgLy8gVE9ETzogd2hlbiB0aW1lb3V0cyBhcmUgaW1wbGVtZW50ZWQsIGRldGVjdCB0aW1lb3V0IGFuZCByZXR1cm4gQlVTWQoKICAgICAgaWYgKGNhY2hlZDAgPT0gbnVsbCAmJiBibG9jayA9PSBudWxsKSB7CiAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgZm9yIChsZXQgaSA9IDI0OyBpIDwgNDA7IGkrKykgewogICAgICAgICAgaWYgKGJsb2NrW2ldICE9PSBjYWNoZWQwW2ldKSB7CiAgICAgICAgICAgIHJldHVybiBmYWxzZTsKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIH0KCiAgICAgIHJldHVybiB0cnVlOwogICAgfQoKICAgIGRvd25ncmFkZVNoYXJlZCgpIHsKICAgICAgdGhpcy5jb21taXQoKTsKICAgICAgdGhpcy50cmFucyA9IHRoaXMuZGIudHJhbnNhY3Rpb24oWydkYXRhJ10sICdyZWFkb25seScpOwogICAgICB0aGlzLnN0b3JlID0gdGhpcy50cmFucy5vYmplY3RTdG9yZSgnZGF0YScpOwogICAgICB0aGlzLmxvY2tUeXBlID0gTE9DS19UWVBFUy5TSEFSRUQ7CiAgICB9CgogICAgYXN5bmMgZ2V0KGtleSkgewogICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4gewogICAgICAgIGxldCByZXEgPSB0aGlzLnN0b3JlLmdldChrZXkpOwogICAgICAgIHJlcS5vbnN1Y2Nlc3MgPSBlID0+IHsKICAgICAgICAgIHJlc29sdmUocmVxLnJlc3VsdCk7CiAgICAgICAgfTsKICAgICAgICByZXEub25lcnJvciA9IGUgPT4gcmVqZWN0KGUpOwogICAgICB9KTsKICAgIH0KCiAgICBnZXRSZWFkRGlyZWN0aW9uKCkgewogICAgICAvLyBUaGVyZSBhcmUgYSB0d28gd2F5cyB3ZSBjYW4gcmVhZCBkYXRhOiBhIGRpcmVjdCBgZ2V0YCByZXF1ZXN0CiAgICAgIC8vIG9yIG9wZW5pbmcgYSBjdXJzb3IgYW5kIGl0ZXJhdGluZyB0aHJvdWdoIGRhdGEuIFdlIGRvbid0IGtub3cKICAgICAgLy8gd2hhdCBmdXR1cmUgcmVhZHMgbG9vayBsaWtlLCBzbyB3ZSBkb24ndCBrbm93IHRoZSBiZXN0IHN0cmF0ZWd5CiAgICAgIC8vIHRvIHBpY2suIEFsd2F5cyBjaG9vc2luZyBvbmUgc3RyYXRlZ3kgZm9yZ29lcyBhIGxvdCBvZgogICAgICAvLyBvcHRpbWl6YXRpb24sIGJlY2F1c2UgaXRlcmF0aW5nIHdpdGggYSBjdXJzb3IgaXMgYSBsb3QgZmFzdGVyCiAgICAgIC8vIHRoYW4gbWFueSBgZ2V0YCBjYWxscy4gT24gdGhlIG90aGVyIGhhbmQsIG9wZW5pbmcgYSBjdXJzb3IgaXMKICAgICAgLy8gc2xvdywgYW5kIHNvIGlzIGNhbGxpbmcgYGFkdmFuY2VgIHRvIG1vdmUgYSBjdXJzb3Igb3ZlciBhIGh1Z2UKICAgICAgLy8gcmFuZ2UgKGxpa2UgbW92aW5nIGl0IDEwMDAgaXRlbXMgbGF0ZXIpLCBzbyBtYW55IGBnZXRgIGNhbGxzIHdvdWxkCiAgICAgIC8vIGJlIGZhc3Rlci4gSW4gZ2VuZXJhbDoKICAgICAgLy8KICAgICAgLy8gKiBNYW55IGBnZXRgIGNhbGxzIGFyZSBmYXN0ZXIgd2hlbiBkb2luZyByYW5kb20gYWNjZXNzZXMKICAgICAgLy8gKiBJdGVyYXRpbmcgd2l0aCBhIGN1cnNvciBpcyBmYXN0ZXIgaWYgZG9pbmcgbW9zdGx5IHNlcXVlbnRpYWwKICAgICAgLy8gICBhY2Nlc3NlcwogICAgICAvLwogICAgICAvLyBXZSBpbXBsZW1lbnQgYSBoZXVyaXN0aWMgYW5kIGtlZXBzIHRyYWNrIG9mIHRoZSBsYXN0IDMgcmVhZHMKICAgICAgLy8gYW5kIGRldGVjdHMgd2hlbiB0aGV5IGFyZSBtb3N0bHkgc2VxdWVudGlhbC4gSWYgdGhleSBhcmUsIHdlCiAgICAgIC8vIG9wZW4gYSBjdXJzb3IgYW5kIHN0YXJ0IHJlYWRpbmcgYnkgaXRlcmF0aW5nIGl0LiBJZiBub3QsIHdlIGRvCiAgICAgIC8vIGRpcmVjdCBgZ2V0YCBjYWxscy4KICAgICAgLy8KICAgICAgLy8gT24gdG9wIG9mIGFsbCBvZiB0aGlzLCBlYWNoIGJyb3dzZXIgaGFzIGRpZmZlcmVudCBwZXJmCiAgICAgIC8vIGNoYXJhY3RlcmlzdGljcy4gV2Ugd2lsbCBwcm9iYWJseSB3YW50IHRvIG1ha2UgdGhlc2UgdGhyZXNob2xkcwogICAgICAvLyBjb25maWd1cmFibGUgc28gdGhlIHVzZXIgY2FuIGNoYW5nZSB0aGVtIHBlci1icm93c2VyIGlmIG5lZWRlZCwKICAgICAgLy8gYXMgd2VsbCBhcyBmaW5lLXR1bmluZyB0aGVtIGZvciB0aGVpciB1c2FnZSBvZiBzcWxpdGUuCgogICAgICBsZXQgcHJldlJlYWRzID0gdGhpcy5wcmV2UmVhZHM7CiAgICAgIGlmIChwcmV2UmVhZHMpIHsKICAgICAgICAvLyBIYXMgdGhlcmUgYmVlbiAzIGZvcndhcmQgc2VxdWVudGlhbCByZWFkcyB3aXRoaW4gMTAgYmxvY2tzPwogICAgICAgIGlmICgKICAgICAgICAgIHByZXZSZWFkc1swXSA8IHByZXZSZWFkc1sxXSAmJgogICAgICAgICAgcHJldlJlYWRzWzFdIDwgcHJldlJlYWRzWzJdICYmCiAgICAgICAgICBwcmV2UmVhZHNbMl0gLSBwcmV2UmVhZHNbMF0gPCAxMAogICAgICAgICkgewogICAgICAgICAgcmV0dXJuICduZXh0JzsKICAgICAgICB9CgogICAgICAgIC8vIEhhcyB0aGVyZSBiZWVuIDMgYmFja3dhcmRzIHNlcXVlbnRpYWwgcmVhZHMgd2l0aGluIDEwIGJsb2Nrcz8KICAgICAgICBpZiAoCiAgICAgICAgICBwcmV2UmVhZHNbMF0gPiBwcmV2UmVhZHNbMV0gJiYKICAgICAgICAgIHByZXZSZWFkc1sxXSA+IHByZXZSZWFkc1syXSAmJgogICAgICAgICAgcHJldlJlYWRzWzBdIC0gcHJldlJlYWRzWzJdIDwgMTAKICAgICAgICApIHsKICAgICAgICAgIHJldHVybiAncHJldic7CiAgICAgICAgfQogICAgICB9CgogICAgICByZXR1cm4gbnVsbDsKICAgIH0KCiAgICByZWFkKHBvc2l0aW9uKSB7CiAgICAgIGxldCB3YWl0Q3Vyc29yID0gKCkgPT4gewogICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7CiAgICAgICAgICBpZiAodGhpcy5jdXJzb3JQcm9taXNlICE9IG51bGwpIHsKICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKAogICAgICAgICAgICAgICd3YWl0Q3Vyc29yKCkgY2FsbGVkIGJ1dCBzb21ldGhpbmcgZWxzZSBpcyBhbHJlYWR5IHdhaXRpbmcnCiAgICAgICAgICAgICk7CiAgICAgICAgICB9CiAgICAgICAgICB0aGlzLmN1cnNvclByb21pc2UgPSB7IHJlc29sdmUsIHJlamVjdCB9OwogICAgICAgIH0pOwogICAgICB9OwoKICAgICAgaWYgKHRoaXMuY3Vyc29yKSB7CiAgICAgICAgbGV0IGN1cnNvciA9IHRoaXMuY3Vyc29yOwoKICAgICAgICBpZiAoCiAgICAgICAgICBjdXJzb3IuZGlyZWN0aW9uID09PSAnbmV4dCcgJiYKICAgICAgICAgIHBvc2l0aW9uID4gY3Vyc29yLmtleSAmJgogICAgICAgICAgcG9zaXRpb24gPCBjdXJzb3Iua2V5ICsgMTAwCiAgICAgICAgKSB7CgogICAgICAgICAgY3Vyc29yLmFkdmFuY2UocG9zaXRpb24gLSBjdXJzb3Iua2V5KTsKICAgICAgICAgIHJldHVybiB3YWl0Q3Vyc29yKCk7CiAgICAgICAgfSBlbHNlIGlmICgKICAgICAgICAgIGN1cnNvci5kaXJlY3Rpb24gPT09ICdwcmV2JyAmJgogICAgICAgICAgcG9zaXRpb24gPCBjdXJzb3Iua2V5ICYmCiAgICAgICAgICBwb3NpdGlvbiA+IGN1cnNvci5rZXkgLSAxMDAKICAgICAgICApIHsKCiAgICAgICAgICBjdXJzb3IuYWR2YW5jZShjdXJzb3Iua2V5IC0gcG9zaXRpb24pOwogICAgICAgICAgcmV0dXJuIHdhaXRDdXJzb3IoKTsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgLy8gRGl0Y2ggdGhlIGN1cnNvcgogICAgICAgICAgdGhpcy5jdXJzb3IgPSBudWxsOwogICAgICAgICAgcmV0dXJuIHRoaXMucmVhZChwb3NpdGlvbik7CiAgICAgICAgfQogICAgICB9IGVsc2UgewogICAgICAgIC8vIFdlIGRvbid0IGFscmVhZHkgaGF2ZSBhIGN1cnNvci4gV2UgbmVlZCB0byBhIGZyZXNoIHJlYWQ7CiAgICAgICAgLy8gc2hvdWxkIHdlIG9wZW4gYSBjdXJzb3Igb3IgY2FsbCBgZ2V0YD8KCiAgICAgICAgbGV0IGRpciA9IHRoaXMuZ2V0UmVhZERpcmVjdGlvbigpOwogICAgICAgIGlmIChkaXIpIHsKICAgICAgICAgIC8vIE9wZW4gYSBjdXJzb3IKICAgICAgICAgIHRoaXMucHJldlJlYWRzID0gbnVsbDsKCiAgICAgICAgICBsZXQga2V5UmFuZ2U7CiAgICAgICAgICBpZiAoZGlyID09PSAncHJldicpIHsKICAgICAgICAgICAga2V5UmFuZ2UgPSBJREJLZXlSYW5nZS51cHBlckJvdW5kKHBvc2l0aW9uKTsKICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIGtleVJhbmdlID0gSURCS2V5UmFuZ2UubG93ZXJCb3VuZChwb3NpdGlvbik7CiAgICAgICAgICB9CgogICAgICAgICAgbGV0IHJlcSA9IHRoaXMuc3RvcmUub3BlbkN1cnNvcihrZXlSYW5nZSwgZGlyKTsKCiAgICAgICAgICByZXEub25zdWNjZXNzID0gZSA9PiB7CgogICAgICAgICAgICBsZXQgY3Vyc29yID0gZS50YXJnZXQucmVzdWx0OwogICAgICAgICAgICB0aGlzLmN1cnNvciA9IGN1cnNvcjsKCiAgICAgICAgICAgIGlmICh0aGlzLmN1cnNvclByb21pc2UgPT0gbnVsbCkgewogICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignR290IGRhdGEgZnJvbSBjdXJzb3IgYnV0IG5vdGhpbmcgaXMgd2FpdGluZyBpdCcpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHRoaXMuY3Vyc29yUHJvbWlzZS5yZXNvbHZlKGN1cnNvciA/IGN1cnNvci52YWx1ZSA6IG51bGwpOwogICAgICAgICAgICB0aGlzLmN1cnNvclByb21pc2UgPSBudWxsOwogICAgICAgICAgfTsKICAgICAgICAgIHJlcS5vbmVycm9yID0gZSA9PiB7CiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdDdXJzb3IgZmFpbHVyZTonLCBlKTsKCiAgICAgICAgICAgIGlmICh0aGlzLmN1cnNvclByb21pc2UgPT0gbnVsbCkgewogICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignR290IGRhdGEgZnJvbSBjdXJzb3IgYnV0IG5vdGhpbmcgaXMgd2FpdGluZyBpdCcpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHRoaXMuY3Vyc29yUHJvbWlzZS5yZWplY3QoZSk7CiAgICAgICAgICAgIHRoaXMuY3Vyc29yUHJvbWlzZSA9IG51bGw7CiAgICAgICAgICB9OwoKICAgICAgICAgIHJldHVybiB3YWl0Q3Vyc29yKCk7CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgIGlmICh0aGlzLnByZXZSZWFkcyA9PSBudWxsKSB7CiAgICAgICAgICAgIHRoaXMucHJldlJlYWRzID0gWzAsIDAsIDBdOwogICAgICAgICAgfQogICAgICAgICAgdGhpcy5wcmV2UmVhZHMucHVzaChwb3NpdGlvbik7CiAgICAgICAgICB0aGlzLnByZXZSZWFkcy5zaGlmdCgpOwoKICAgICAgICAgIHJldHVybiB0aGlzLmdldChwb3NpdGlvbik7CiAgICAgICAgfQogICAgICB9CiAgICB9CgogICAgYXN5bmMgc2V0KGl0ZW0pIHsKICAgICAgdGhpcy5wcmV2UmVhZHMgPSBudWxsOwoKICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHsKICAgICAgICBsZXQgcmVxID0gdGhpcy5zdG9yZS5wdXQoaXRlbS52YWx1ZSwgaXRlbS5rZXkpOwogICAgICAgIHJlcS5vbnN1Y2Nlc3MgPSBlID0+IHJlc29sdmUocmVxLnJlc3VsdCk7CiAgICAgICAgcmVxLm9uZXJyb3IgPSBlID0+IHJlamVjdChlKTsKICAgICAgfSk7CiAgICB9CgogICAgYXN5bmMgYnVsa1NldChpdGVtcykgewogICAgICB0aGlzLnByZXZSZWFkcyA9IG51bGw7CgogICAgICBmb3IgKGxldCBpdGVtIG9mIGl0ZW1zKSB7CiAgICAgICAgdGhpcy5zdG9yZS5wdXQoaXRlbS52YWx1ZSwgaXRlbS5rZXkpOwogICAgICB9CiAgICB9CiAgfQoKICBhc3luYyBmdW5jdGlvbiBsb2FkRGIobmFtZSkgewogICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHsKICAgICAgaWYgKG9wZW5EYnMuZ2V0KG5hbWUpKSB7CiAgICAgICAgcmVzb2x2ZShvcGVuRGJzLmdldChuYW1lKSk7CiAgICAgICAgcmV0dXJuOwogICAgICB9CgogICAgICBjb25zb2xlLmxvZygnb3BlbmluZycsIG5hbWUpOwoKICAgICAgbGV0IHJlcSA9IGdsb2JhbFRoaXMuaW5kZXhlZERCLm9wZW4obmFtZSwgMSk7CiAgICAgIHJlcS5vbnN1Y2Nlc3MgPSBldmVudCA9PiB7CiAgICAgICAgY29uc29sZS5sb2coJ2RiIGlzIG9wZW4hJywgbmFtZSk7CiAgICAgICAgbGV0IGRiID0gZXZlbnQudGFyZ2V0LnJlc3VsdDsKCiAgICAgICAgZGIub252ZXJzaW9uY2hhbmdlID0gKCkgPT4gewogICAgICAgICAgLy8gVE9ETzogTm90aWZ5IHRoZSB1c2VyIHNvbWVob3cKICAgICAgICAgIGNvbnNvbGUubG9nKCdjbG9zaW5nIGJlY2F1c2UgdmVyc2lvbiBjaGFuZ2VkJyk7CiAgICAgICAgICBkYi5jbG9zZSgpOwogICAgICAgICAgb3BlbkRicy5kZWxldGUobmFtZSk7CiAgICAgICAgfTsKCiAgICAgICAgZGIub25jbG9zZSA9ICgpID0+IHsKICAgICAgICAgIG9wZW5EYnMuZGVsZXRlKG5hbWUpOwogICAgICAgIH07CgogICAgICAgIG9wZW5EYnMuc2V0KG5hbWUsIGRiKTsKICAgICAgICByZXNvbHZlKGRiKTsKICAgICAgfTsKICAgICAgcmVxLm9udXBncmFkZW5lZWRlZCA9IGV2ZW50ID0+IHsKICAgICAgICBsZXQgZGIgPSBldmVudC50YXJnZXQucmVzdWx0OwogICAgICAgIGlmICghZGIub2JqZWN0U3RvcmVOYW1lcy5jb250YWlucygnZGF0YScpKSB7CiAgICAgICAgICBkYi5jcmVhdGVPYmplY3RTdG9yZSgnZGF0YScpOwogICAgICAgIH0KICAgICAgfTsKICAgICAgcmVxLm9uYmxvY2tlZCA9IGUgPT4gY29uc29sZS5sb2coJ2Jsb2NrZWQnLCBlKTsKICAgICAgcmVxLm9uZXJyb3IgPSByZXEub25hYm9ydCA9IGUgPT4gcmVqZWN0KGUudGFyZ2V0LmVycm9yKTsKICAgIH0pOwogIH0KCiAgZnVuY3Rpb24gY2xvc2VEYihuYW1lKSB7CiAgICBsZXQgb3BlbkRiID0gb3BlbkRicy5nZXQobmFtZSk7CiAgICBpZiAob3BlbkRiKSB7CiAgICAgIGNvbnNvbGUubG9nKCdjbG9zaW5nIGRiJyk7CiAgICAgIG9wZW5EYi5jbG9zZSgpOwogICAgICBvcGVuRGJzLmRlbGV0ZShuYW1lKTsKICAgIH0KICB9CgogIGZ1bmN0aW9uIGdldFRyYW5zYWN0aW9uKG5hbWUpIHsKICAgIHJldHVybiB0cmFuc2FjdGlvbnMuZ2V0KG5hbWUpOwogIH0KCiAgYXN5bmMgZnVuY3Rpb24gd2l0aFRyYW5zYWN0aW9uKG5hbWUsIG1vZGUsIGZ1bmMpIHsKICAgIGxldCB0cmFucyA9IHRyYW5zYWN0aW9ucy5nZXQobmFtZSk7CiAgICBpZiAodHJhbnMpIHsKICAgICAgLy8gSWYgYSB0cmFuc2FjdGlvbiBhbHJlYWR5IGV4aXN0cywgdGhhdCBtZWFucyB0aGUgZmlsZSBoYXMgYmVlbgogICAgICAvLyBsb2NrZWQuIFdlIGRvbid0IGZ1bGx5IHN1cHBvcnQgYXJiaXRyYXJ5IG5lc3RlZCB0cmFuc2FjdGlvbnMsCiAgICAgIC8vIGFzIHNlZW4gYmVsb3cgKHdlIHdvbid0IHVwZ3JhZGUgYSBgcmVhZG9ubHlgIHRvIGByZWFkd3JpdGVgCiAgICAgIC8vIGF1dG9tYXRpY2FsbHkpIGFuZCB0aGlzIGlzIG1haW5seSBmb3IgdGhlIHVzZSBjYXNlIHdoZXJlIHNxbGl0ZQogICAgICAvLyBsb2NrcyB0aGUgZGIgYW5kIGNyZWF0ZXMgYSB0cmFuc2FjdGlvbiBmb3IgdGhlIGR1cmFjdGlvbiBvZiB0aGUKICAgICAgLy8gbG9jay4gV2UgZG9uJ3QgYWN0dWFsbHkgd3JpdGUgY29kZSBpbiBhIHdheSB0aGF0IGFzc3VtZXMgbmVzdGVkCiAgICAgIC8vIHRyYW5zYWN0aW9ucywgc28ganVzdCBlcnJvciBoZXJlCiAgICAgIGlmIChtb2RlID09PSAncmVhZHdyaXRlJyAmJiB0cmFucy5sb2NrVHlwZSA9PT0gTE9DS19UWVBFUy5TSEFSRUQpIHsKICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0F0dGVtcHRlZCB3cml0ZSBidXQgb25seSBoYXMgU0hBUkVEIGxvY2snKTsKICAgICAgfQogICAgICByZXR1cm4gZnVuYyh0cmFucyk7CiAgICB9CgogICAgLy8gT3V0c2lkZSB0aGUgc2NvcGUgb2YgYSBsb2NrLCBjcmVhdGUgYSB0ZW1wb3JhcnkgdHJhbnNhY3Rpb24KICAgIHRyYW5zID0gbmV3IFRyYW5zYWN0aW9uKGF3YWl0IGxvYWREYihuYW1lKSwgbW9kZSk7CiAgICBhd2FpdCBmdW5jKHRyYW5zKTsKICAgIGF3YWl0IHRyYW5zLndhaXRDb21wbGV0ZSgpOwogIH0KCiAgLy8gTG9ja2luZyBzdHJhdGVneToKICAvLwogIC8vICogV2UgbWFwIHNxbGl0ZSdzIGxvY2tzIG9udG8gSW5kZXhlZERCJ3MgdHJhbnNhY3Rpb24gc2VtYW50aWNzLgogIC8vICAgUmVhZCB0cmFuc2FjdGlvbnMgbWF5IGV4ZWN1dGUgaW4gcGFyYWxsZWwuIFJlYWQvd3JpdGUKICAvLyAgIHRyYW5zYWN0aW9ucyBhcmUgcXVldWVkIHVwIGFuZCB3YWl0IHVudGlsIGFsbCBwcmVjZWRpbmcKICAvLyAgIHJlYWQgdHJhbnNhY3Rpb25zIGZpbmlzaCBleGVjdXRpbmcuIFJlYWQgdHJhbnNhY3Rpb25zIHN0YXJ0ZWQKICAvLyAgIGFmdGVyIGEgcmVhZC93cml0ZSB0cmFuc2FjdGlvbiB3YWl0IHVudGlsIGl0IGlzIGZpbmlzaGVkLgogIC8vCiAgLy8gKiBJREIgdHJhbnNhY3Rpb25zIHdpbGwgd2FpdCBmb3JldmVyIHVudGlsIHRoZXkgY2FuIGV4ZWN1dGUgKGZvcgogIC8vICAgZXhhbXBsZSwgdGhleSBtYXkgYmUgYmxvY2tlZCBvbiBhIHJlYWQvd3JpdGUgdHJhbnNhY3Rpb24pLiBXZQogIC8vICAgZG9uJ3Qgd2FudCB0byBhbGxvdyBzcWxpdGUgdHJhbnNhY3Rpb25zIHRvIHdhaXQgZm9yZXZlciwgc28KICAvLyAgIHdlIG1hbnVhbGx5IHRpbWVvdXQgaWYgYSB0cmFuc2FjdGlvbiB0YWtlcyB0b28gbG9uZyB0bwogIC8vICAgc3RhcnQgZXhlY3V0aW5nLiBUaGlzIHNpbXVsYXRlcyB0aGUgYmVoYXZpb3Igb2YgYSBzcWxpdGUKICAvLyAgIGJhaWxpbmcgaWYgaXQgY2FuJ3QgcmVxdWlyZSBhIGxvY2suCiAgLy8KICAvLyAqIEEgU0hBUkVEIGxvY2sgd2FudHMgdG8gcmVhZCBmcm9tIHRoZSBkYi4gV2Ugc3RhcnQgYSByZWFkCiAgLy8gICB0cmFuc2FjdGlvbiBhbmQgcmVhZCB0aGUgZmlyc3QgYmxvY2ssIGFuZCBpZiB3ZSByZWFkIGl0IHdpdGhpbgogIC8vICAgNTAwbXMgd2UgY29uc2lkZXIgdGhlIGxvY2sgc3VjY2Vzc2Z1bC4gT3RoZXJ3aXNlIHRoZSBsb2NrCiAgLy8gICBmYWlsZWQgYW5kIHdlIHJldHVybiBTUUxJVEVfQlVTWS4gKFRoZXJlJ3Mgbm8gcGVyZiBkb3duc2lkZQogIC8vICAgdG8gcmVhZGluZyB0aGUgZmlyc3QgYmxvY2sgLSBpdCBoYXMgdG8gYmUgcmVhZCBhbnl3YXkgdG8gY2hlY2sKICAvLyAgIGJ5dGVzIDI0LTM5IGZvciB0aGUgY2hhbmdlIGNvdW50ZXIpCiAgLy8KICAvLyAqIEEgUkVTRVJWRUQgbG9jayBtZWFucyB0aGUgZGIgd2FudHMgdG8gc3RhcnQgd3JpdGluZyAodGhpbmsgb2YKICAvLyAgIGBCRUdJTiBUUkFOU0FDVElPTmApLiBPbmx5IG9uZSBwcm9jZXNzIGNhbiBvYnRhaW4gYSBSRVNFUlZFRAogIC8vICAgbG9jayBhdCBhIHRpbWUsIGJ1dCBub3JtYWxseSBzcWxpdGUgc3RpbGwgbGVhZHMgbmV3IHJlYWQgbG9ja3MKICAvLyAgIGhhcHBlbi4gSXQgaXNuJ3QgdW50aWwgYW4gRVhDTFVTSVZFIGxvY2sgaXMgaGVsZCB0aGF0IHJlYWRzIGFyZQogIC8vICAgYmxvY2tlZC4gSG93ZXZlciwgc2luY2Ugd2UgbmVlZCB0byBndWFyYW50ZWUgb25seSBvbmUgUkVTRVJWRUQKICAvLyAgIGxvY2sgYXQgb25jZSAob3RoZXJ3aXNlIGRhdGEgY291bGQgY2hhbmdlIGZyb20gYW5vdGhlciBwcm9jZXNzCiAgLy8gICB3aXRoaW4gYSB0cmFuc2FjdGlvbiwgY2F1c2luZyBmYXVsdHkgY2FjaGVzIGV0YykgdGhlIHNpbXBsZXN0CiAgLy8gICB0aGluZyB0byBkbyBpcyBnbyBhaGVhZCBhbmQgZ3JhYiBhIHJlYWQvd3JpdGUgdHJhbnNhY3Rpb24gdGhhdAogIC8vICAgcmVwcmVzZW50cyB0aGUgUkVTRVJWRUQgbG9jay4gVGhpcyB3aWxsIGJsb2NrIGFsbCByZWFkcyBmcm9tCiAgLy8gICBoYXBwZW5pbmcsIGFuZCBpcyBlc3NlbnRpYWxseSB0aGUgc2FtZSBhcyBhbiBFWENMVVNJVkUgbG9jay4KICAvLwogIC8vICAgICAqIFRoZSBtYWluIHByb2JsZW0gaGVyZSBpcyB3ZSBjYW4ndCAidXBncmFkZSIgYSBgcmVhZG9ubHlgCiAgLy8gICAgICAgdHJhbnNhY3Rpb24gdG8gYHJlYWR3cml0ZWAsIGJ1dCBuYXRpdmUgc3FsaXRlIGNhbiB1cGdyYWRlIGEKICAvLyAgICAgICBsb2NrIGZyb20gU0hBUkVEIHRvIFJFU0VSVkVELiBXZSBuZWVkIHRvIHN0YXJ0IGEgbmV3CiAgLy8gICAgICAgdHJhbnNhY3Rpb24gdG8gZG8gc28sIGFuZCBiZWNhdXNlIG9mIHRoYXQgdGhlcmUgbWlnaHQgYmUKICAvLyAgICAgICBvdGhlciBgcmVhZHdyaXRlYCB0cmFuc2FjdGlvbnMgdGhhdCBnZXQgcnVuIGR1cmluZyB0aGUKICAvLyAgICAgICAidXBncmFkZSIgd2hpY2ggaW52YWxpZGF0ZXMgdGhlIHdob2xlIGxvY2tpbmcgcHJvY2VzcyBhbmQKICAvLyAgICAgICBhbmQgY29ycnVwdHMgZGF0YS4KICAvLwogIC8vICogSWRlYWxseSwgd2UgY291bGQgdGVsbCBzcWxpdGUgdG8gc2tpcCBTSEFSRUQgbG9ja3MgZW50aXJlbHkuIFdlCiAgLy8gICBkb24ndCBuZWVkIHRoZW0gc2luY2Ugd2UgY2FuIHJlbHkgb24gSW5kZXhlZERCJ3Mgc2VtYW50aWNzLgogIC8vICAgVGhlbiB3aGVuIGl0IHdhbnRzIHRvIHN0YXJ0IHdyaXRpbmcsIHdlIGdldCBhIFJFU0VSVkVEIGxvY2sKICAvLyAgIHdpdGhvdXQgaGF2aW5nIHRvIHVwZ3JhZGUgZnJvbSBTSEFSRUQuIFRoaXMgd291bGQgc2F2ZSB1cwogIC8vICAgdGhlIGNvc3Qgb2YgYSBgcmVhZG9ubHlgIHRyYW5zYWN0aW9uIHdoZW4gd3JpdGluZzsgcmlnaHQgbm93CiAgLy8gICBpdCBtdXN0IG9wZW4gYSBgcmVhZG9ubHlgIHRyYW5zYWN0aW9uIGFuZCB0aGVuIGltbWVkaWF0ZWx5IG9wZW4KICAvLyAgIGEgYHJlYWR3cml0ZWAgdG8gdXBncmFkZSBpdC4gSSB0aG91Z2h0IG9mIGRlZmVycmluZyBvcGVuaW5nIHRoZQogIC8vICAgYHJlYWRvbmx5YCB0cmFuc2FjdGlvbiB1bnRpbCBzb21ldGhpbmcgaXMgYWN0dWFsbHkgcmVhZCwgYnV0CiAgLy8gICB1bmZvcnR1bmF0ZWx5IHNxbGl0ZSBvcGVucyBpdCwgcmVhZHMgdGhlIGZpcnN0IGJsb2NrLCBhbmQgdGhlbgogIC8vICAgdXBncmFkZXMgaXQuIFNvIHRoZXJlJ3Mgbm8gd2F5IGFyb3VuZCBpdC4gKFdlIGNhbid0IGFzc3VtZSBpdCdzCiAgLy8gICBhIGByZWFkd3JpdGVgIHRyYW5zYWN0aW9uIGF0IHRoYXQgcG9pbnQgc2luY2UgdGhhdCB3b3VsZCBhc3N1bWUKICAvLyAgIGFsbCBTSEFSRUQgbG9ja3MgYXJlIGByZWFkd3JpdGVgLCByZW1vdmluZyB0aGUgcG9zc2liaWxpdHkgb2YKICAvLyAgIGNvbmN1cnJlbnQgcmVhZHMpLgogIC8vCiAgLy8gKiBVcGdyYWRpbmcgdG8gYW4gRVhDTFVTSVZFIGxvY2sgaXMgYSBub29wLCBzaW5jZSB3ZSB0cmVhdCBSRVNFUlZFRAogIC8vICAgbG9ja3MgYXMgRVhDTFVTSVZFLgogIGFzeW5jIGZ1bmN0aW9uIGhhbmRsZUxvY2sod3JpdGVyLCBuYW1lLCBsb2NrVHlwZSkgewogICAgLy8gY29uc29sZS5sb2coJ2xvY2tpbmcnLCBuYW1lLCBsb2NrVHlwZSwgcGVyZm9ybWFuY2Uubm93KCkpOwoKICAgIGxldCB0cmFucyA9IHRyYW5zYWN0aW9ucy5nZXQobmFtZSk7CiAgICBpZiAodHJhbnMpIHsKICAgICAgaWYgKGxvY2tUeXBlID4gdHJhbnMubG9ja1R5cGUpIHsKICAgICAgICAvLyBVcGdyYWRlIFNIQVJFRCB0byBFWENMVVNJVkUKICAgICAgICBhc3NlcnQoCiAgICAgICAgICB0cmFucy5sb2NrVHlwZSA9PT0gTE9DS19UWVBFUy5TSEFSRUQsCiAgICAgICAgICBgVXByYWRpbmcgbG9jayB0eXBlIGZyb20gJHt0cmFucy5sb2NrVHlwZX0gaXMgaW52YWxpZGAKICAgICAgICApOwogICAgICAgIGFzc2VydCgKICAgICAgICAgIGxvY2tUeXBlID09PSBMT0NLX1RZUEVTLlJFU0VSVkVEIHx8IGxvY2tUeXBlID09PSBMT0NLX1RZUEVTLkVYQ0xVU0lWRSwKICAgICAgICAgIGBVcGdyYWRpbmcgbG9jayB0eXBlIHRvICR7bG9ja1R5cGV9IGlzIGludmFsaWRgCiAgICAgICAgKTsKCiAgICAgICAgbGV0IHN1Y2Nlc3MgPSBhd2FpdCB0cmFucy51cGdyYWRlRXhjbHVzaXZlKCk7CiAgICAgICAgd3JpdGVyLmludDMyKHN1Y2Nlc3MgPyAwIDogLTEpOwogICAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgICB9IGVsc2UgewogICAgICAgIC8vIElmIG5vdCB1cGdyYWRpbmcgYW5kIHdlIGFscmVhZHkgaGF2ZSBhIGxvY2ssIG1ha2Ugc3VyZSB0aGlzCiAgICAgICAgLy8gaXNuJ3QgYSBkb3duZ3JhZGUKICAgICAgICBhc3NlcnQoCiAgICAgICAgICB0cmFucy5sb2NrVHlwZSA9PT0gbG9ja1R5cGUsCiAgICAgICAgICBgRG93bmdyYWRpbmcgbG9jayB0byAke2xvY2tUeXBlfSBpcyBpbnZhbGlkYAogICAgICAgICk7CgogICAgICAgIHdyaXRlci5pbnQzMigwKTsKICAgICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgICAgfQogICAgfSBlbHNlIHsKICAgICAgYXNzZXJ0KAogICAgICAgIGxvY2tUeXBlID09PSBMT0NLX1RZUEVTLlNIQVJFRCwKICAgICAgICBgTmV3IGxvY2tzIG11c3Qgc3RhcnQgYXMgU0hBUkVEIGluc3RlYWQgb2YgJHtsb2NrVHlwZX1gCiAgICAgICk7CgogICAgICBsZXQgdHJhbnMgPSBuZXcgVHJhbnNhY3Rpb24oYXdhaXQgbG9hZERiKG5hbWUpKTsKICAgICAgaWYgKChhd2FpdCB0cmFucy5wcmVmZXRjaEZpcnN0QmxvY2soNTAwKSkgPT0gbnVsbCkgOwoKICAgICAgdHJhbnNhY3Rpb25zLnNldChuYW1lLCB0cmFucyk7CgogICAgICB3cml0ZXIuaW50MzIoMCk7CiAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgfQogIH0KCiAgYXN5bmMgZnVuY3Rpb24gaGFuZGxlVW5sb2NrKHdyaXRlciwgbmFtZSwgbG9ja1R5cGUpIHsKICAgIC8vIGNvbnNvbGUubG9nKCd1bmxvY2tpbmcnLCBuYW1lLCBsb2NrVHlwZSwgcGVyZm9ybWFuY2Uubm93KCkpOwoKICAgIGxldCB0cmFucyA9IGdldFRyYW5zYWN0aW9uKG5hbWUpOwoKICAgIGlmIChsb2NrVHlwZSA9PT0gTE9DS19UWVBFUy5TSEFSRUQpIHsKICAgICAgaWYgKHRyYW5zID09IG51bGwpIHsKICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1VubG9jayBlcnJvciAoU0hBUkVEKTogbm8gdHJhbnNhY3Rpb24gcnVubmluZycpOwogICAgICB9CgogICAgICBpZiAodHJhbnMubG9ja1R5cGUgPT09IExPQ0tfVFlQRVMuRVhDTFVTSVZFKSB7CiAgICAgICAgdHJhbnMuZG93bmdyYWRlU2hhcmVkKCk7CiAgICAgIH0KICAgIH0gZWxzZSBpZiAobG9ja1R5cGUgPT09IExPQ0tfVFlQRVMuTk9ORSkgewogICAgICAvLyBJIHRob3VnaHQgd2UgY291bGQgYXNzdW1lIGEgbG9jayBpcyBhbHdheXMgb3BlbiB3aGVuIGB1bmxvY2tgCiAgICAgIC8vIGlzIGNhbGxlZCwgYnV0IGl0IGFsc28gY2FsbHMgYHVubG9ja2Agd2hlbiBjbG9zaW5nIHRoZSBmaWxlIG5vCiAgICAgIC8vIG1hdHRlciB3aGF0LiBEbyBub3RoaW5nIGlmIHRoZXJlJ3Mgbm8gbG9jayBjdXJyZW50bHkKICAgICAgaWYgKHRyYW5zKSB7CiAgICAgICAgLy8gVE9ETzogdGhpcyBpcyB3aGVyZSBhbiBlcnJvciBjb3VsZCBidWJibGUgdXAuIEhhbmRsZSBpdAogICAgICAgIGF3YWl0IHRyYW5zLndhaXRDb21wbGV0ZSgpOwogICAgICAgIHRyYW5zYWN0aW9ucy5kZWxldGUobmFtZSk7CiAgICAgIH0KICAgIH0KCiAgICB3cml0ZXIuaW50MzIoMCk7CiAgICB3cml0ZXIuZmluYWxpemUoKTsKICB9CgogIGFzeW5jIGZ1bmN0aW9uIGhhbmRsZVJlYWQod3JpdGVyLCBuYW1lLCBwb3NpdGlvbikgewogICAgcmV0dXJuIHdpdGhUcmFuc2FjdGlvbihuYW1lLCAncmVhZG9ubHknLCBhc3luYyB0cmFucyA9PiB7CiAgICAgIGxldCBkYXRhID0gYXdhaXQgdHJhbnMucmVhZChwb3NpdGlvbik7CgogICAgICBpZiAoZGF0YSA9PSBudWxsKSB7CiAgICAgICAgd3JpdGVyLmJ5dGVzKG5ldyBBcnJheUJ1ZmZlcigwKSk7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgd3JpdGVyLmJ5dGVzKGRhdGEpOwogICAgICB9CiAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgfSk7CiAgfQoKICBhc3luYyBmdW5jdGlvbiBoYW5kbGVXcml0ZXMod3JpdGVyLCBuYW1lLCB3cml0ZXMpIHsKICAgIHJldHVybiB3aXRoVHJhbnNhY3Rpb24obmFtZSwgJ3JlYWR3cml0ZScsIGFzeW5jIHRyYW5zID0+IHsKICAgICAgYXdhaXQgdHJhbnMuYnVsa1NldCh3cml0ZXMubWFwKHcgPT4gKHsga2V5OiB3LnBvcywgdmFsdWU6IHcuZGF0YSB9KSkpOwoKICAgICAgd3JpdGVyLmludDMyKDApOwogICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgIH0pOwogIH0KCiAgYXN5bmMgZnVuY3Rpb24gaGFuZGxlUmVhZE1ldGEod3JpdGVyLCBuYW1lKSB7CiAgICByZXR1cm4gd2l0aFRyYW5zYWN0aW9uKG5hbWUsICdyZWFkb25seScsIGFzeW5jIHRyYW5zID0+IHsKICAgICAgdHJ5IHsKICAgICAgICBjb25zb2xlLmxvZygnUmVhZGluZyBtZXRhJyk7CiAgICAgICAgbGV0IHJlcyA9IGF3YWl0IHRyYW5zLmdldCgtMSk7CiAgICAgICAgY29uc29sZS5sb2coJ1JlYWRpbmcgbWV0YSAoZG9uZSknLCByZXMpOwoKICAgICAgICBsZXQgbWV0YSA9IHJlczsKICAgICAgICB3cml0ZXIuaW50MzIobWV0YSA/IG1ldGEuc2l6ZSA6IC0xKTsKICAgICAgICB3cml0ZXIuaW50MzIobWV0YSA/IG1ldGEuYmxvY2tTaXplIDogLTEpOwogICAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgICB9IGNhdGNoIChlcnIpIHsKICAgICAgICBjb25zb2xlLmxvZyhlcnIpOwogICAgICAgIHdyaXRlci5pbnQzMigtMSk7CiAgICAgICAgd3JpdGVyLmludDMyKC0xKTsKICAgICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgICAgfQogICAgfSk7CiAgfQoKICBhc3luYyBmdW5jdGlvbiBoYW5kbGVXcml0ZU1ldGEod3JpdGVyLCBuYW1lLCBtZXRhKSB7CiAgICByZXR1cm4gd2l0aFRyYW5zYWN0aW9uKG5hbWUsICdyZWFkd3JpdGUnLCBhc3luYyB0cmFucyA9PiB7CiAgICAgIHRyeSB7CiAgICAgICAgYXdhaXQgdHJhbnMuc2V0KHsga2V5OiAtMSwgdmFsdWU6IG1ldGEgfSk7CgogICAgICAgIHdyaXRlci5pbnQzMigwKTsKICAgICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgICAgfSBjYXRjaCAoZXJyKSB7CiAgICAgICAgY29uc29sZS5sb2coZXJyKTsKICAgICAgICB3cml0ZXIuaW50MzIoLTEpOwogICAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgICB9CiAgICB9KTsKICB9CgogIGFzeW5jIGZ1bmN0aW9uIGhhbmRsZURlbGV0ZUZpbGUod3JpdGVyLCBuYW1lKSB7CiAgICB0cnkgewogICAgICBjbG9zZURiKG5hbWUpOwoKICAgICAgYXdhaXQgbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4gewogICAgICAgIGxldCByZXEgPSBnbG9iYWxUaGlzLmluZGV4ZWREQi5kZWxldGVEYXRhYmFzZShuYW1lKTsKICAgICAgICByZXEub25zdWNjZXNzID0gcmVzb2x2ZTsKICAgICAgICByZXEub25lcnJvciA9IHJlamVjdDsKICAgICAgfSk7CgogICAgICB3cml0ZXIuaW50MzIoMCk7CiAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgfSBjYXRjaCAoZXJyKSB7CiAgICAgIHdyaXRlci5pbnQzMigtMSk7CiAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgfQogIH0KCiAgYXN5bmMgZnVuY3Rpb24gaGFuZGxlQ2xvc2VGaWxlKHdyaXRlciwgbmFtZSkgewogICAgY2xvc2VEYihuYW1lKTsKCiAgICB3cml0ZXIuaW50MzIoMCk7CiAgICB3cml0ZXIuZmluYWxpemUoKTsKICB9CgogIC8vIGBsaXN0ZW5gIGNvbnRpbnVhbGx5IGxpc3RlbnMgZm9yIHJlcXVlc3RzIHZpYSB0aGUgc2hhcmVkIGJ1ZmZlci4KICAvLyBSaWdodCBub3cgaXQncyBpbXBsZW1lbnRlZCBpbiBhIHRhaWwtY2FsbCBzdHlsZSAoYGxpc3RlbmAgaXMKICAvLyByZWN1cnNpdmVseSBjYWxsZWQpIGJlY2F1c2UgSSB0aG91Z2h0IHRoYXQgd2FzIG5lY2Vzc2FyeSBmb3IKICAvLyB2YXJpb3VzIHJlYXNvbnMuIFdlIGNhbiBjb252ZXJ0IHRoaXMgdG8gYSBgd2hpbGUoMSlgIGxvb3Agd2l0aAogIC8vIGFuZCB1c2UgYGF3YWl0YCB0aG91Z2gKICBhc3luYyBmdW5jdGlvbiBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpIHsKICAgIGxldCBtZXRob2QgPSByZWFkZXIuc3RyaW5nKCk7CgogICAgc3dpdGNoIChtZXRob2QpIHsKICAgICAgY2FzZSAncHJvZmlsZS1zdGFydCc6IHsKICAgICAgICByZWFkZXIuZG9uZSgpOwoKICAgICAgICB3cml0ZXIuaW50MzIoMCk7CiAgICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICAgICAgbGlzdGVuKHJlYWRlciwgd3JpdGVyKTsKICAgICAgICBicmVhazsKICAgICAgfQoKICAgICAgY2FzZSAncHJvZmlsZS1zdG9wJzogewogICAgICAgIHJlYWRlci5kb25lKCk7CiAgICAgICAgLy8gVGhlIHBlcmYgbGlicmFyeSBwb3N0cyBhIG1lc3NhZ2U7IG1ha2Ugc3VyZSBpdCBoYXMgdGltZSB0bwogICAgICAgIC8vIGFjdHVhbGx5IHBvc3QgaXQgYmVmb3JlIGJsb2NraW5nIHRoZSB0aHJlYWQgYWdhaW4KICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZShyZXNvbHZlID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgMTAwMCkpOwoKICAgICAgICB3cml0ZXIuaW50MzIoMCk7CiAgICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICAgICAgbGlzdGVuKHJlYWRlciwgd3JpdGVyKTsKICAgICAgICBicmVhazsKICAgICAgfQoKICAgICAgY2FzZSAnd3JpdGVCbG9ja3MnOiB7CiAgICAgICAgbGV0IG5hbWUgPSByZWFkZXIuc3RyaW5nKCk7CiAgICAgICAgbGV0IHdyaXRlcyA9IFtdOwogICAgICAgIHdoaWxlICghcmVhZGVyLmRvbmUoKSkgewogICAgICAgICAgbGV0IHBvcyA9IHJlYWRlci5pbnQzMigpOwogICAgICAgICAgbGV0IGRhdGEgPSByZWFkZXIuYnl0ZXMoKTsKICAgICAgICAgIHdyaXRlcy5wdXNoKHsgcG9zLCBkYXRhIH0pOwogICAgICAgIH0KCiAgICAgICAgYXdhaXQgaGFuZGxlV3JpdGVzKHdyaXRlciwgbmFtZSwgd3JpdGVzKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICdyZWFkQmxvY2snOiB7CiAgICAgICAgbGV0IG5hbWUgPSByZWFkZXIuc3RyaW5nKCk7CiAgICAgICAgbGV0IHBvcyA9IHJlYWRlci5pbnQzMigpOwogICAgICAgIHJlYWRlci5kb25lKCk7CgogICAgICAgIGF3YWl0IGhhbmRsZVJlYWQod3JpdGVyLCBuYW1lLCBwb3MpOwogICAgICAgIGxpc3RlbihyZWFkZXIsIHdyaXRlcik7CiAgICAgICAgYnJlYWs7CiAgICAgIH0KCiAgICAgIGNhc2UgJ3JlYWRNZXRhJzogewogICAgICAgIGxldCBuYW1lID0gcmVhZGVyLnN0cmluZygpOwogICAgICAgIHJlYWRlci5kb25lKCk7CiAgICAgICAgYXdhaXQgaGFuZGxlUmVhZE1ldGEod3JpdGVyLCBuYW1lKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICd3cml0ZU1ldGEnOiB7CiAgICAgICAgbGV0IG5hbWUgPSByZWFkZXIuc3RyaW5nKCk7CiAgICAgICAgbGV0IHNpemUgPSByZWFkZXIuaW50MzIoKTsKICAgICAgICBsZXQgYmxvY2tTaXplID0gcmVhZGVyLmludDMyKCk7CiAgICAgICAgcmVhZGVyLmRvbmUoKTsKICAgICAgICBhd2FpdCBoYW5kbGVXcml0ZU1ldGEod3JpdGVyLCBuYW1lLCB7IHNpemUsIGJsb2NrU2l6ZSB9KTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICdkZWxldGVGaWxlJzogewogICAgICAgIGxldCBuYW1lID0gcmVhZGVyLnN0cmluZygpOwogICAgICAgIHJlYWRlci5kb25lKCk7CgogICAgICAgIGF3YWl0IGhhbmRsZURlbGV0ZUZpbGUod3JpdGVyLCBuYW1lKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICdjbG9zZUZpbGUnOiB7CiAgICAgICAgbGV0IG5hbWUgPSByZWFkZXIuc3RyaW5nKCk7CiAgICAgICAgcmVhZGVyLmRvbmUoKTsKCiAgICAgICAgYXdhaXQgaGFuZGxlQ2xvc2VGaWxlKHdyaXRlciwgbmFtZSk7CiAgICAgICAgbGlzdGVuKHJlYWRlciwgd3JpdGVyKTsKICAgICAgICBicmVhazsKICAgICAgfQoKICAgICAgY2FzZSAnbG9ja0ZpbGUnOiB7CiAgICAgICAgbGV0IG5hbWUgPSByZWFkZXIuc3RyaW5nKCk7CiAgICAgICAgbGV0IGxvY2tUeXBlID0gcmVhZGVyLmludDMyKCk7CiAgICAgICAgcmVhZGVyLmRvbmUoKTsKCiAgICAgICAgYXdhaXQgaGFuZGxlTG9jayh3cml0ZXIsIG5hbWUsIGxvY2tUeXBlKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICd1bmxvY2tGaWxlJzogewogICAgICAgIGxldCBuYW1lID0gcmVhZGVyLnN0cmluZygpOwogICAgICAgIGxldCBsb2NrVHlwZSA9IHJlYWRlci5pbnQzMigpOwogICAgICAgIHJlYWRlci5kb25lKCk7CgogICAgICAgIGF3YWl0IGhhbmRsZVVubG9jayh3cml0ZXIsIG5hbWUsIGxvY2tUeXBlKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBkZWZhdWx0OgogICAgICAgIHRocm93IG5ldyBFcnJvcignVW5rbm93biBtZXRob2Q6ICcgKyBtZXRob2QpOwogICAgfQogIH0KCiAgc2VsZi5vbm1lc3NhZ2UgPSBtc2cgPT4gewogICAgc3dpdGNoIChtc2cuZGF0YS50eXBlKSB7CiAgICAgIGNhc2UgJ2luaXQnOiB7CiAgICAgICAgcG9zdE1lc3NhZ2UoeyB0eXBlOiAnX19hYnN1cmQ6d29ya2VyLXJlYWR5JyB9KTsKICAgICAgICBsZXQgW2FyZ0J1ZmZlciwgcmVzdWx0QnVmZmVyXSA9IG1zZy5kYXRhLmJ1ZmZlcnM7CiAgICAgICAgbGV0IHJlYWRlciA9IG5ldyBSZWFkZXIoYXJnQnVmZmVyLCB7IG5hbWU6ICdhcmdzJywgZGVidWc6IGZhbHNlIH0pOwogICAgICAgIGxldCB3cml0ZXIgPSBuZXcgV3JpdGVyKHJlc3VsdEJ1ZmZlciwgeyBuYW1lOiAncmVzdWx0cycsIGRlYnVnOiBmYWxzZSB9KTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CiAgICB9CiAgfTsKCn0oKSk7Cgo=', null, false); -/* eslint-enable */ - -export default WorkerFactory; diff --git a/dist2/indexeddb-main-thread.js b/dist2/indexeddb-main-thread.js deleted file mode 100644 index 8605cf1..0000000 --- a/dist2/indexeddb-main-thread.js +++ /dev/null @@ -1,73 +0,0 @@ -// The reason for this strange abstraction is because we can't rely on -// nested worker support (Safari doesn't support it). We need to proxy -// creating a child worker through the main thread, and this requires -// a bit of glue code. We don't want to duplicate this code in each -// backend that needs it, so this module abstracts it out. It has to -// have a strange shape because we don't want to eagerly bundle the -// backend code, so users of this code need to pass an `() => -// import('worker.js')` expression to get the worker module to run. - -function isWorker() { - return ( - typeof WorkerGlobalScope !== 'undefined' && - self instanceof WorkerGlobalScope - ); -} - -function makeStartWorkerFromMain(getModule) { - return (argBuffer, resultBuffer, parentWorker) => { - if (isWorker()) { - throw new Error( - '`startWorkerFromMain` should only be called from the main thread' - ); - } - - if (typeof Worker === 'undefined') { - // We're on the main thread? Weird: it doesn't have workers - throw new Error( - 'Web workers not available. sqlite3 requires web workers to work.' - ); - } - - getModule().then(({ default: BackendWorker }) => { - let worker = new BackendWorker(); - - worker.postMessage({ type: 'init', buffers: [argBuffer, resultBuffer] }); - - worker.addEventListener('message', msg => { - // Forward any messages to the worker that's supposed - // to be the parent - parentWorker.postMessage(msg.data); - }); - }); - }; -} - -let hasInitialized = false; - -function makeInitBackend(spawnEventName, getModule) { - const startWorkerFromMain = makeStartWorkerFromMain(getModule); - - return worker => { - if (hasInitialized) { - return; - } - hasInitialized = true; - - worker.addEventListener('message', e => { - switch (e.data.type) { - case spawnEventName: - startWorkerFromMain(e.data.argBuffer, e.data.resultBuffer, worker); - break; - } - }); - }; -} - -// Use the generic main thread module to create our indexeddb worker -// proxy -const initBackend = makeInitBackend('__absurd:spawn-idb-worker', () => - import('./indexeddb-main-thread-worker-14b63d9a.js') -); - -export { initBackend }; diff --git a/dist2/memory-backend.js b/dist2/memory-backend.js deleted file mode 100644 index 7a2352c..0000000 --- a/dist2/memory-backend.js +++ /dev/null @@ -1,448 +0,0 @@ -function range(start, end, step) { - let r = []; - for (let i = start; i <= end; i += step) { - r.push(i); - } - return r; -} - -function getBoundaryIndexes(blockSize, start, end) { - let startC = start - (start % blockSize); - let endC = end - 1 - ((end - 1) % blockSize); - - return range(startC, endC, blockSize); -} - -function readChunks(chunks, start, end) { - let buffer = new ArrayBuffer(end - start); - let bufferView = new Uint8Array(buffer); - for (let i = 0; i < chunks.length; i++) { - let chunk = chunks[i]; - - // TODO: jest has a bug where we can't do `instanceof ArrayBuffer` - if (chunk.data.constructor.name !== 'ArrayBuffer') { - throw new Error('Chunk data is not an ArrayBuffer'); - } - - let cstart = 0; - let cend = chunk.data.byteLength; - - if (start > chunk.pos) { - cstart = start - chunk.pos; - } - if (end < chunk.pos + chunk.data.byteLength) { - cend = end - chunk.pos; - } - - if (cstart > chunk.data.byteLength || cend < 0) { - continue; - } - - let len = cend - cstart; - - bufferView.set( - new Uint8Array(chunk.data, cstart, len), - chunk.pos - start + cstart - ); - } - - return buffer; -} - -function writeChunks(bufferView, blockSize, start, end) { - let indexes = getBoundaryIndexes(blockSize, start, end); - let cursor = 0; - - return indexes - .map(index => { - let cstart = 0; - let cend = blockSize; - if (start > index && start < index + blockSize) { - cstart = start - index; - } - if (end > index && end < index + blockSize) { - cend = end - index; - } - - let len = cend - cstart; - let chunkBuffer = new ArrayBuffer(blockSize); - - if (start > index + blockSize || end <= index) { - return null; - } - - let off = bufferView.byteOffset + cursor; - - let available = bufferView.buffer.byteLength - off; - if (available <= 0) { - return null; - } - - let readLength = Math.min(len, available); - - new Uint8Array(chunkBuffer).set( - new Uint8Array(bufferView.buffer, off, readLength), - cstart - ); - cursor += readLength; - - return { - pos: index, - data: chunkBuffer, - offset: cstart, - length: readLength - }; - }) - .filter(Boolean); -} - -class File { - constructor(filename, defaultBlockSize, ops, meta = null) { - this.filename = filename; - this.defaultBlockSize = defaultBlockSize; - this.buffer = new Map(); - this.ops = ops; - this.meta = meta; - this._metaDirty = false; - } - - bufferChunks(chunks) { - for (let i = 0; i < chunks.length; i++) { - let chunk = chunks[i]; - this.buffer.set(chunk.pos, chunk); - } - } - - open() { - this.meta = this.ops.readMeta(); - - if (this.meta == null) { - this.meta = {}; - - // New file - this.setattr({ - size: 0, - blockSize: this.defaultBlockSize - }); - - this.fsync(); - } - } - - close() { - this.fsync(); - this.ops.close(); - } - - delete() { - this.ops.delete(); - } - - load(indexes) { - let status = indexes.reduce( - (acc, b) => { - let inMemory = this.buffer.get(b); - if (inMemory) { - acc.chunks.push(inMemory); - } else { - acc.missing.push(b); - } - return acc; - }, - { chunks: [], missing: [] } - ); - - let missingChunks = []; - if (status.missing.length > 0) { - missingChunks = this.ops.readBlocks(status.missing, this.meta.blockSize); - } - return status.chunks.concat(missingChunks); - } - - read(bufferView, offset, length, position) { - // console.log('reading', this.filename, offset, length, position); - let buffer = bufferView.buffer; - - if (length <= 0) { - return 0; - } - if (position < 0) { - // TODO: is this right? - return 0; - } - if (position >= this.meta.size) { - let view = new Uint8Array(buffer, offset); - for (let i = 0; i < length; i++) { - view[i] = 0; - } - - return length; - } - - position = Math.max(position, 0); - let dataLength = Math.min(length, this.meta.size - position); - - let start = position; - let end = position + dataLength; - - let indexes = getBoundaryIndexes(this.meta.blockSize, start, end); - - let chunks = this.load(indexes); - let readBuffer = readChunks(chunks, start, end); - - if (buffer.byteLength - offset < readBuffer.byteLength) { - throw new Error('Buffer given to `read` is too small'); - } - let view = new Uint8Array(buffer); - view.set(new Uint8Array(readBuffer), offset); - - // TODO: I don't need to do this. `unixRead` does this for us. - for (let i = dataLength; i < length; i++) { - view[offset + i] = 0; - } - - return length; - } - - write(bufferView, offset, length, position) { - // console.log('writing', this.filename, offset, length, position); - let buffer = bufferView.buffer; - - if (length <= 0) { - return 0; - } - if (position < 0) { - return 0; - } - if (buffer.byteLength === 0) { - return 0; - } - - length = Math.min(length, buffer.byteLength - offset); - - let writes = writeChunks( - new Uint8Array(buffer, offset, length), - this.meta.blockSize, - position, - position + length - ); - - // Find any partial chunks and read them in and merge with - // existing data - let { partialWrites, fullWrites } = writes.reduce( - (state, write) => { - if (write.length !== this.meta.blockSize) { - state.partialWrites.push(write); - } else { - state.fullWrites.push({ - pos: write.pos, - data: write.data - }); - } - return state; - }, - { fullWrites: [], partialWrites: [] } - ); - - let reads = []; - if (partialWrites.length > 0) { - reads = this.load(partialWrites.map(w => w.pos)); - } - - let allWrites = fullWrites.concat( - reads.map(read => { - let write = partialWrites.find(w => w.pos === read.pos); - - // MuTatIoN! - new Uint8Array(read.data).set( - new Uint8Array(write.data, write.offset, write.length), - write.offset, - write.length - ); - - return read; - }) - ); - - this.bufferChunks(allWrites); - - if (position + length > this.meta.size) { - this.setattr({ size: position + length }); - } - - return length; - } - - lock(lockType) { - return this.ops.lock(lockType); - } - - unlock(lockType) { - return this.ops.unlock(lockType); - } - - fsync() { - if (this.buffer.size > 0) { - this.ops.writeBlocks([...this.buffer.values()], this.meta.blockSize); - } - - if (this._metaDirty) { - this.ops.writeMeta(this.meta); - this._metaDirty = false; - } - - this.buffer = new Map(); - } - - setattr(attr) { - if (attr.mode !== undefined) { - this.meta.mode = attr.mode; - this._metaDirty = true; - } - - if (attr.timestamp !== undefined) { - this.meta.timestamp = attr.timestamp; - this._metaDirty = true; - } - - if (attr.size !== undefined) { - this.meta.size = attr.size; - this._metaDirty = true; - } - - if (attr.blockSize !== undefined) { - if (this.meta.blockSize != null) { - throw new Error('Changing blockSize is not allowed yet'); - } - this.meta.blockSize = attr.blockSize; - this._metaDirty = true; - } - } - - getattr() { - return this.meta; - } -} - -class FileOps { - constructor(filename, meta = null, data) { - this.filename = filename; - this.locked = false; - this.meta = meta; - this.data = data || new ArrayBuffer(0); - } - - lock() { - return true; - } - - unlock() { - return true; - } - - close() { - return true; - } - - delete() { - // in-memory noop - } - - startStats() {} - stats() {} - - readMeta() { - return this.meta; - } - - writeMeta(meta) { - if (this.meta == null) { - this.meta = {}; - } - this.meta.size = meta.size; - this.meta.blockSize = meta.blockSize; - } - - readBlocks(positions, blockSize) { - // console.log('_reading', this.filename, positions); - let data = this.data; - - return positions.map(pos => { - let buffer = new ArrayBuffer(blockSize); - - if (pos < data.byteLength) { - new Uint8Array(buffer).set( - new Uint8Array(data, pos, Math.min(blockSize, data.byteLength - pos)) - ); - } - - return { pos, data: buffer }; - }); - } - - writeBlocks(writes, blockSize) { - // console.log('_writing', this.filename, writes); - let data = this.data; - - console.log('writes', writes.length); - let i = 0; - for (let write of writes) { - if (i % 1000 === 0) { - console.log('write'); - } - i++; - let fullLength = write.pos + write.data.byteLength; - - if (fullLength > data.byteLength) { - // Resize file - let buffer = new ArrayBuffer(fullLength); - new Uint8Array(buffer).set(new Uint8Array(data)); - this.data = data = buffer; - } - - new Uint8Array(data).set(new Uint8Array(write.data), write.pos); - } - } -} - -class MemoryBackend { - constructor(defaultBlockSize, fileData) { - this.fileData = Object.fromEntries( - Object.entries(fileData).map(([name, data]) => { - return [name, data]; - }) - ); - this.files = {}; - this.defaultBlockSize = defaultBlockSize; - } - - async init() {} - - createFile(filename) { - console.log('creating', filename); - if (this.files[filename] == null) { - let data = this.fileData[filename]; - - this.files[filename] = new File( - filename, - this.defaultBlockSize, - new FileOps( - filename, - data - ? { - size: data.byteLength, - blockSize: this.defaultBlockSize - } - : null - ) - ); - } - return this.files[filename]; - } - - getFile(filename) { - return this.files[filename]; - } -} - -export default MemoryBackend; diff --git a/dist2/perf/indexeddb-backend.js b/dist2/perf/indexeddb-backend.js deleted file mode 100644 index fe8b821..0000000 --- a/dist2/perf/indexeddb-backend.js +++ /dev/null @@ -1,863 +0,0 @@ -import * as perf from 'perf-deets'; - -let FINALIZED = 0xdeadbeef; - -let WRITEABLE = 0; -let READABLE = 1; - -class Reader { - constructor( - buffer, - { initialOffset = 4, useAtomics = true, stream = true, debug, name } = {} - ) { - this.buffer = buffer; - this.atomicView = new Int32Array(buffer); - this.offset = initialOffset; - this.useAtomics = useAtomics; - this.stream = stream; - this.debug = debug; - this.name = name; - } - - log(...args) { - if (this.debug) { - console.log(`[reader: ${this.name}]`, ...args); - } - } - - waitWrite(name) { - if (this.useAtomics) { - this.log(`waiting for ${name}`); - - while (Atomics.load(this.atomicView, 0) === WRITEABLE) { - // console.log('waiting for write...'); - Atomics.wait(this.atomicView, 0, WRITEABLE, 500); - } - - this.log(`resumed for ${name}`); - } else { - if (this.atomicView[0] !== READABLE) { - throw new Error('`waitWrite` expected array to be readable'); - } - } - } - - flip() { - this.log('flip'); - if (this.useAtomics) { - let prev = Atomics.compareExchange( - this.atomicView, - 0, - READABLE, - WRITEABLE - ); - - if (prev !== READABLE) { - throw new Error('Read data out of sync! This is disastrous'); - } - - Atomics.notify(this.atomicView, 0); - } else { - this.atomicView[0] = WRITEABLE; - } - - this.offset = 4; - } - - done() { - this.waitWrite('done'); - - let dataView = new DataView(this.buffer, this.offset); - let done = dataView.getUint32(0) === FINALIZED; - - if (done) { - this.log('done'); - this.flip(); - } - - return done; - } - - peek(fn) { - this.peekOffset = this.offset; - let res = fn(); - this.offset = this.peekOffset; - this.peekOffset = null; - return res; - } - - string() { - this.waitWrite('string'); - - let byteLength = this._int32(); - let length = byteLength / 2; - - let dataView = new DataView(this.buffer, this.offset, byteLength); - let chars = []; - for (let i = 0; i < length; i++) { - chars.push(dataView.getUint16(i * 2)); - } - let str = String.fromCharCode.apply(null, chars); - this.log('string', str); - - this.offset += byteLength; - - if (this.peekOffset == null) { - this.flip(); - } - return str; - } - - _int32() { - let byteLength = 4; - - let dataView = new DataView(this.buffer, this.offset); - let num = dataView.getInt32(); - this.log('_int32', num); - - this.offset += byteLength; - return num; - } - - int32() { - this.waitWrite('int32'); - let num = this._int32(); - this.log('int32', num); - - if (this.peekOffset == null) { - this.flip(); - } - return num; - } - - bytes() { - this.waitWrite('bytes'); - - let byteLength = this._int32(); - - let bytes = new ArrayBuffer(byteLength); - new Uint8Array(bytes).set( - new Uint8Array(this.buffer, this.offset, byteLength) - ); - this.log('bytes', bytes); - - this.offset += byteLength; - - if (this.peekOffset == null) { - this.flip(); - } - return bytes; - } -} - -class Writer { - constructor( - buffer, - { initialOffset = 4, useAtomics = true, stream = true, debug, name } = {} - ) { - this.buffer = buffer; - this.atomicView = new Int32Array(buffer); - this.offset = initialOffset; - this.useAtomics = useAtomics; - this.stream = stream; - - this.debug = debug; - this.name = name; - - if (this.useAtomics) { - // The buffer starts out as writeable - Atomics.store(this.atomicView, 0, WRITEABLE); - } else { - this.atomicView[0] = WRITEABLE; - } - } - - log(...args) { - if (this.debug) { - console.log(`[writer: ${this.name}]`, ...args); - } - } - - waitRead(name) { - if (this.useAtomics) { - this.log(`waiting for ${name}`); - // Switch to writable - // Atomics.store(this.atomicView, 0, 1); - - let prev = Atomics.compareExchange( - this.atomicView, - 0, - WRITEABLE, - READABLE - ); - - if (prev !== WRITEABLE) { - throw new Error( - 'Wrote something into unwritable buffer! This is disastrous' - ); - } - - Atomics.notify(this.atomicView, 0); - - while (Atomics.load(this.atomicView, 0) === READABLE) { - // console.log('waiting to be read...'); - Atomics.wait(this.atomicView, 0, READABLE, 500); - } - - this.log(`resumed for ${name}`); - } else { - this.atomicView[0] = READABLE; - } - - this.offset = 4; - } - - finalize() { - this.log('finalizing'); - let dataView = new DataView(this.buffer, this.offset); - dataView.setUint32(0, FINALIZED); - this.waitRead('finalize'); - } - - string(str) { - this.log('string', str); - - let byteLength = str.length * 2; - this._int32(byteLength); - - let dataView = new DataView(this.buffer, this.offset, byteLength); - for (let i = 0; i < str.length; i++) { - dataView.setUint16(i * 2, str.charCodeAt(i)); - } - - this.offset += byteLength; - this.waitRead('string'); - } - - _int32(num) { - let byteLength = 4; - - let dataView = new DataView(this.buffer, this.offset); - dataView.setInt32(0, num); - - this.offset += byteLength; - } - - int32(num) { - this.log('int32', num); - this._int32(num); - this.waitRead('int32'); - } - - bytes(buffer) { - this.log('bytes', buffer); - - let byteLength = buffer.byteLength; - this._int32(byteLength); - new Uint8Array(this.buffer, this.offset).set(new Uint8Array(buffer)); - - this.offset += byteLength; - this.waitRead('bytes'); - } -} - -function range(start, end, step) { - let r = []; - for (let i = start; i <= end; i += step) { - r.push(i); - } - return r; -} - -function getBoundaryIndexes(blockSize, start, end) { - let startC = start - (start % blockSize); - let endC = end - 1 - ((end - 1) % blockSize); - - return range(startC, endC, blockSize); -} - -function readChunks(chunks, start, end) { - let buffer = new ArrayBuffer(end - start); - let bufferView = new Uint8Array(buffer); - for (let i = 0; i < chunks.length; i++) { - let chunk = chunks[i]; - - // TODO: jest has a bug where we can't do `instanceof ArrayBuffer` - if (chunk.data.constructor.name !== 'ArrayBuffer') { - throw new Error('Chunk data is not an ArrayBuffer'); - } - - let cstart = 0; - let cend = chunk.data.byteLength; - - if (start > chunk.pos) { - cstart = start - chunk.pos; - } - if (end < chunk.pos + chunk.data.byteLength) { - cend = end - chunk.pos; - } - - if (cstart > chunk.data.byteLength || cend < 0) { - continue; - } - - let len = cend - cstart; - - bufferView.set( - new Uint8Array(chunk.data, cstart, len), - chunk.pos - start + cstart - ); - } - - return buffer; -} - -function writeChunks(bufferView, blockSize, start, end) { - let indexes = getBoundaryIndexes(blockSize, start, end); - let cursor = 0; - - return indexes - .map(index => { - let cstart = 0; - let cend = blockSize; - if (start > index && start < index + blockSize) { - cstart = start - index; - } - if (end > index && end < index + blockSize) { - cend = end - index; - } - - let len = cend - cstart; - let chunkBuffer = new ArrayBuffer(blockSize); - - if (start > index + blockSize || end <= index) { - return null; - } - - let off = bufferView.byteOffset + cursor; - - let available = bufferView.buffer.byteLength - off; - if (available <= 0) { - return null; - } - - let readLength = Math.min(len, available); - - new Uint8Array(chunkBuffer).set( - new Uint8Array(bufferView.buffer, off, readLength), - cstart - ); - cursor += readLength; - - return { - pos: index, - data: chunkBuffer, - offset: cstart, - length: readLength - }; - }) - .filter(Boolean); -} - -class File { - constructor(filename, defaultBlockSize, ops, meta = null) { - this.filename = filename; - this.defaultBlockSize = defaultBlockSize; - this.buffer = new Map(); - this.ops = ops; - this.meta = meta; - this._metaDirty = false; - } - - bufferChunks(chunks) { - for (let i = 0; i < chunks.length; i++) { - let chunk = chunks[i]; - this.buffer.set(chunk.pos, chunk); - } - } - - open() { - this.meta = this.ops.readMeta(); - - if (this.meta == null) { - this.meta = {}; - - // New file - this.setattr({ - size: 0, - blockSize: this.defaultBlockSize - }); - - this.fsync(); - } - } - - close() { - this.fsync(); - this.ops.close(); - } - - delete() { - this.ops.delete(); - } - - load(indexes) { - let status = indexes.reduce( - (acc, b) => { - let inMemory = this.buffer.get(b); - if (inMemory) { - acc.chunks.push(inMemory); - } else { - acc.missing.push(b); - } - return acc; - }, - { chunks: [], missing: [] } - ); - - let missingChunks = []; - if (status.missing.length > 0) { - missingChunks = this.ops.readBlocks(status.missing, this.meta.blockSize); - } - return status.chunks.concat(missingChunks); - } - - read(bufferView, offset, length, position) { - // console.log('reading', this.filename, offset, length, position); - let buffer = bufferView.buffer; - - if (length <= 0) { - return 0; - } - if (position < 0) { - // TODO: is this right? - return 0; - } - if (position >= this.meta.size) { - let view = new Uint8Array(buffer, offset); - for (let i = 0; i < length; i++) { - view[i] = 0; - } - - return length; - } - - perf.record('read'); - - position = Math.max(position, 0); - let dataLength = Math.min(length, this.meta.size - position); - - let start = position; - let end = position + dataLength; - - let indexes = getBoundaryIndexes(this.meta.blockSize, start, end); - - let chunks = this.load(indexes); - let readBuffer = readChunks(chunks, start, end); - - if (buffer.byteLength - offset < readBuffer.byteLength) { - throw new Error('Buffer given to `read` is too small'); - } - let view = new Uint8Array(buffer); - view.set(new Uint8Array(readBuffer), offset); - - // TODO: I don't need to do this. `unixRead` does this for us. - for (let i = dataLength; i < length; i++) { - view[offset + i] = 0; - } - - perf.endRecording('read'); - - return length; - } - - write(bufferView, offset, length, position) { - // console.log('writing', this.filename, offset, length, position); - let buffer = bufferView.buffer; - - if (length <= 0) { - return 0; - } - if (position < 0) { - return 0; - } - if (buffer.byteLength === 0) { - return 0; - } - - length = Math.min(length, buffer.byteLength - offset); - - let writes = writeChunks( - new Uint8Array(buffer, offset, length), - this.meta.blockSize, - position, - position + length - ); - - // Find any partial chunks and read them in and merge with - // existing data - let { partialWrites, fullWrites } = writes.reduce( - (state, write) => { - if (write.length !== this.meta.blockSize) { - state.partialWrites.push(write); - } else { - state.fullWrites.push({ - pos: write.pos, - data: write.data - }); - } - return state; - }, - { fullWrites: [], partialWrites: [] } - ); - - let reads = []; - if (partialWrites.length > 0) { - reads = this.load(partialWrites.map(w => w.pos)); - } - - let allWrites = fullWrites.concat( - reads.map(read => { - let write = partialWrites.find(w => w.pos === read.pos); - - // MuTatIoN! - new Uint8Array(read.data).set( - new Uint8Array(write.data, write.offset, write.length), - write.offset, - write.length - ); - - return read; - }) - ); - - this.bufferChunks(allWrites); - - if (position + length > this.meta.size) { - this.setattr({ size: position + length }); - } - - return length; - } - - lock(lockType) { - return this.ops.lock(lockType); - } - - unlock(lockType) { - return this.ops.unlock(lockType); - } - - fsync() { - if (this.buffer.size > 0) { - this.ops.writeBlocks([...this.buffer.values()], this.meta.blockSize); - } - - if (this._metaDirty) { - this.ops.writeMeta(this.meta); - this._metaDirty = false; - } - - this.buffer = new Map(); - } - - setattr(attr) { - if (attr.mode !== undefined) { - this.meta.mode = attr.mode; - this._metaDirty = true; - } - - if (attr.timestamp !== undefined) { - this.meta.timestamp = attr.timestamp; - this._metaDirty = true; - } - - if (attr.size !== undefined) { - this.meta.size = attr.size; - this._metaDirty = true; - } - - if (attr.blockSize !== undefined) { - if (this.meta.blockSize != null) { - throw new Error('Changing blockSize is not allowed yet'); - } - this.meta.blockSize = attr.blockSize; - this._metaDirty = true; - } - } - - getattr() { - return this.meta; - } -} - -// These are temporarily global, but will be easy to clean up later -let reader, writer; - -function positionToKey(pos, blockSize) { - // We are forced to round because of floating point error. `pos` - // should always be divisible by `blockSize` - return Math.round(pos / blockSize); -} - -function invokeWorker(method, args) { - switch (method) { - case 'readBlocks': { - let { name, positions, blockSize } = args; - - let res = []; - for (let pos of positions) { - writer.string('readBlock'); - writer.string(name); - writer.int32(positionToKey(pos, blockSize)); - writer.finalize(); - - let data = reader.bytes(); - reader.done(); - res.push({ - pos, - // If th length is 0, the block didn't exist. We return a - // blank block in that case - data: data.byteLength === 0 ? new ArrayBuffer(blockSize) : data - }); - } - - return res; - } - - case 'writeBlocks': { - let { name, writes, blockSize } = args; - writer.string('writeBlocks'); - writer.string(name); - for (let write of writes) { - writer.int32(positionToKey(write.pos, blockSize)); - writer.bytes(write.data); - } - writer.finalize(); - - // Block for empty response - - let res = reader.int32(); - reader.done(); - return res; - } - - case 'readMeta': { - writer.string('readMeta'); - writer.string(args.name); - writer.finalize(); - - let size = reader.int32(); - let blockSize = reader.int32(); - reader.done(); - return size === -1 ? null : { size, blockSize }; - } - - case 'writeMeta': { - let { name, meta } = args; - writer.string('writeMeta'); - writer.string(name); - writer.int32(meta.size); - writer.int32(meta.blockSize); - writer.finalize(); - - let res = reader.int32(); - reader.done(); - return res; - } - - case 'deleteFile': { - writer.string('deleteFile'); - writer.string(args.name); - writer.finalize(); - - let res = reader.int32(); - reader.done(); - return res; - } - - case 'closeFile': { - writer.string('closeFile'); - writer.string(args.name); - writer.finalize(); - - let res = reader.int32(); - reader.done(); - return res; - } - - case 'lockFile': { - writer.string('lockFile'); - writer.string(args.name); - writer.int32(args.lockType); - writer.finalize(); - - let res = reader.int32(); - reader.done(); - return res === 0; - } - - case 'unlockFile': { - writer.string('unlockFile'); - writer.string(args.name); - writer.int32(args.lockType); - writer.finalize(); - - let res = reader.int32(); - reader.done(); - return res === 0; - } - } -} - -class FileOps { - constructor(filename) { - this.filename = filename; - } - - getStoreName() { - return this.filename.replace(/\//g, '-'); - } - - lock(lockType) { - return invokeWorker('lockFile', { name: this.getStoreName(), lockType }); - } - - unlock(lockType) { - return invokeWorker('unlockFile', { name: this.getStoreName(), lockType }); - } - - delete() { - return invokeWorker('deleteFile', { name: this.getStoreName() }); - } - - close() { - return invokeWorker('closeFile', { name: this.getStoreName() }); - } - - readMeta() { - return invokeWorker('readMeta', { name: this.getStoreName() }); - } - - writeMeta(meta) { - return invokeWorker('writeMeta', { name: this.getStoreName(), meta }); - } - - readBlocks(positions, blockSize) { - // if (Math.random() < 0.005) { - // console.log('reading', positions); - // } - - if (this.stats) { - this.stats.read += positions.length; - } - - return invokeWorker('readBlocks', { - name: this.getStoreName(), - positions, - blockSize - }); - } - - writeBlocks(writes, blockSize) { - // console.log('_writing', this.filename, writes); - if (this.stats) { - this.stats.writes += writes.length; - } - - return invokeWorker('writeBlocks', { - name: this.getStoreName(), - writes, - blockSize - }); - } -} - -function startWorker(reader, writer) { - let onReady; - let workerReady = new Promise(resolve => (onReady = resolve)); - - self.postMessage({ - type: '__absurd:spawn-idb-worker', - argBuffer: writer.buffer, - resultBuffer: reader.buffer - }); - - self.addEventListener('message', e => { - switch (e.data.type) { - case '__absurd:worker-ready': - onReady(); - break; - - // Normally you would use `postMessage` control the profiler in - // a worker (just like this worker go those events), and the - // perf library automatically handles those events. We don't do - // that for the special backend worker though because it's - // always blocked when it's not processing. Instead we forward - // these events by going through the atomics layer to unblock it - // to make sure it starts immediately - case '__perf-deets:start-profile': - writer.string('profile-start'); - writer.finalize(); - reader.int32(); - reader.done(); - break; - - case '__perf-deets:end-profile': - writer.string('profile-end'); - writer.finalize(); - reader.int32(); - reader.done(); - break; - } - }); - - return workerReady; -} - -class IndexedDBBackend { - constructor(defaultBlockSize) { - this.defaultBlockSize = defaultBlockSize; - } - - async init() { - let argBuffer = new SharedArrayBuffer(4096 * 9); - writer = this.writer = new Writer(argBuffer, { - name: 'args (backend)', - debug: false - }); - - let resultBuffer = new SharedArrayBuffer(4096 * 9); - reader = new Reader(resultBuffer, { - name: 'results', - debug: false - }); - - await startWorker(reader, writer); - } - - createFile(filename) { - return new File(filename, this.defaultBlockSize, new FileOps(filename)); - } - - // Instead of controlling the profiler from the main thread by - // posting a message to this worker, you can control it inside the - // worker manually with these methods - startProfile() { - perf.start(); - writer.string('profile-start'); - writer.finalize(); - reader.int32(); - reader.done(); - } - - stopProfile() { - perf.stop(); - writer.string('profile-stop'); - writer.finalize(); - reader.int32(); - reader.done(); - } -} - -export default IndexedDBBackend; diff --git a/dist2/perf/indexeddb-main-thread-worker-754bfead.js b/dist2/perf/indexeddb-main-thread-worker-754bfead.js deleted file mode 100644 index 92aafaf..0000000 --- a/dist2/perf/indexeddb-main-thread-worker-754bfead.js +++ /dev/null @@ -1,34 +0,0 @@ -function decodeBase64(base64, enableUnicode) { - var binaryString = atob(base64); - if (enableUnicode) { - var binaryView = new Uint8Array(binaryString.length); - for (var i = 0, n = binaryString.length; i < n; ++i) { - binaryView[i] = binaryString.charCodeAt(i); - } - return String.fromCharCode.apply(null, new Uint16Array(binaryView.buffer)); - } - return binaryString; -} - -function createURL(base64, sourcemapArg, enableUnicodeArg) { - var sourcemap = sourcemapArg === undefined ? null : sourcemapArg; - var enableUnicode = enableUnicodeArg === undefined ? false : enableUnicodeArg; - var source = decodeBase64(base64, enableUnicode); - var start = source.indexOf('\n', 10) + 1; - var body = source.substring(start) + (sourcemap ? '\/\/# sourceMappingURL=' + sourcemap : ''); - var blob = new Blob([body], { type: 'application/javascript' }); - return URL.createObjectURL(blob); -} - -function createBase64WorkerFactory(base64, sourcemapArg, enableUnicodeArg) { - var url; - return function WorkerFactory(options) { - url = url || createURL(base64, sourcemapArg, enableUnicodeArg); - return new Worker(url, options); - }; -} - -var WorkerFactory = createBase64WorkerFactory('Lyogcm9sbHVwLXBsdWdpbi13ZWItd29ya2VyLWxvYWRlciAqLwooZnVuY3Rpb24gKCkgewogICd1c2Ugc3RyaWN0JzsKCiAgbGV0IEZJTkFMSVpFRCA9IDB4ZGVhZGJlZWY7CgogIGxldCBXUklURUFCTEUgPSAwOwogIGxldCBSRUFEQUJMRSA9IDE7CgogIGNsYXNzIFJlYWRlciB7CiAgICBjb25zdHJ1Y3RvcigKICAgICAgYnVmZmVyLAogICAgICB7IGluaXRpYWxPZmZzZXQgPSA0LCB1c2VBdG9taWNzID0gdHJ1ZSwgc3RyZWFtID0gdHJ1ZSwgZGVidWcsIG5hbWUgfSA9IHt9CiAgICApIHsKICAgICAgdGhpcy5idWZmZXIgPSBidWZmZXI7CiAgICAgIHRoaXMuYXRvbWljVmlldyA9IG5ldyBJbnQzMkFycmF5KGJ1ZmZlcik7CiAgICAgIHRoaXMub2Zmc2V0ID0gaW5pdGlhbE9mZnNldDsKICAgICAgdGhpcy51c2VBdG9taWNzID0gdXNlQXRvbWljczsKICAgICAgdGhpcy5zdHJlYW0gPSBzdHJlYW07CiAgICAgIHRoaXMuZGVidWcgPSBkZWJ1ZzsKICAgICAgdGhpcy5uYW1lID0gbmFtZTsKICAgIH0KCiAgICBsb2coLi4uYXJncykgewogICAgICBpZiAodGhpcy5kZWJ1ZykgewogICAgICAgIGNvbnNvbGUubG9nKGBbcmVhZGVyOiAke3RoaXMubmFtZX1dYCwgLi4uYXJncyk7CiAgICAgIH0KICAgIH0KCiAgICB3YWl0V3JpdGUobmFtZSkgewogICAgICBpZiAodGhpcy51c2VBdG9taWNzKSB7CiAgICAgICAgdGhpcy5sb2coYHdhaXRpbmcgZm9yICR7bmFtZX1gKTsKCiAgICAgICAgd2hpbGUgKEF0b21pY3MubG9hZCh0aGlzLmF0b21pY1ZpZXcsIDApID09PSBXUklURUFCTEUpIHsKICAgICAgICAgIC8vIGNvbnNvbGUubG9nKCd3YWl0aW5nIGZvciB3cml0ZS4uLicpOwogICAgICAgICAgQXRvbWljcy53YWl0KHRoaXMuYXRvbWljVmlldywgMCwgV1JJVEVBQkxFLCA1MDApOwogICAgICAgIH0KCiAgICAgICAgdGhpcy5sb2coYHJlc3VtZWQgZm9yICR7bmFtZX1gKTsKICAgICAgfSBlbHNlIHsKICAgICAgICBpZiAodGhpcy5hdG9taWNWaWV3WzBdICE9PSBSRUFEQUJMRSkgewogICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdgd2FpdFdyaXRlYCBleHBlY3RlZCBhcnJheSB0byBiZSByZWFkYWJsZScpOwogICAgICAgIH0KICAgICAgfQogICAgfQoKICAgIGZsaXAoKSB7CiAgICAgIHRoaXMubG9nKCdmbGlwJyk7CiAgICAgIGlmICh0aGlzLnVzZUF0b21pY3MpIHsKICAgICAgICBsZXQgcHJldiA9IEF0b21pY3MuY29tcGFyZUV4Y2hhbmdlKAogICAgICAgICAgdGhpcy5hdG9taWNWaWV3LAogICAgICAgICAgMCwKICAgICAgICAgIFJFQURBQkxFLAogICAgICAgICAgV1JJVEVBQkxFCiAgICAgICAgKTsKCiAgICAgICAgaWYgKHByZXYgIT09IFJFQURBQkxFKSB7CiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1JlYWQgZGF0YSBvdXQgb2Ygc3luYyEgVGhpcyBpcyBkaXNhc3Ryb3VzJyk7CiAgICAgICAgfQoKICAgICAgICBBdG9taWNzLm5vdGlmeSh0aGlzLmF0b21pY1ZpZXcsIDApOwogICAgICB9IGVsc2UgewogICAgICAgIHRoaXMuYXRvbWljVmlld1swXSA9IFdSSVRFQUJMRTsKICAgICAgfQoKICAgICAgdGhpcy5vZmZzZXQgPSA0OwogICAgfQoKICAgIGRvbmUoKSB7CiAgICAgIHRoaXMud2FpdFdyaXRlKCdkb25lJyk7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0KTsKICAgICAgbGV0IGRvbmUgPSBkYXRhVmlldy5nZXRVaW50MzIoMCkgPT09IEZJTkFMSVpFRDsKCiAgICAgIGlmIChkb25lKSB7CiAgICAgICAgdGhpcy5sb2coJ2RvbmUnKTsKICAgICAgICB0aGlzLmZsaXAoKTsKICAgICAgfQoKICAgICAgcmV0dXJuIGRvbmU7CiAgICB9CgogICAgcGVlayhmbikgewogICAgICB0aGlzLnBlZWtPZmZzZXQgPSB0aGlzLm9mZnNldDsKICAgICAgbGV0IHJlcyA9IGZuKCk7CiAgICAgIHRoaXMub2Zmc2V0ID0gdGhpcy5wZWVrT2Zmc2V0OwogICAgICB0aGlzLnBlZWtPZmZzZXQgPSBudWxsOwogICAgICByZXR1cm4gcmVzOwogICAgfQoKICAgIHN0cmluZygpIHsKICAgICAgdGhpcy53YWl0V3JpdGUoJ3N0cmluZycpOwoKICAgICAgbGV0IGJ5dGVMZW5ndGggPSB0aGlzLl9pbnQzMigpOwogICAgICBsZXQgbGVuZ3RoID0gYnl0ZUxlbmd0aCAvIDI7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0LCBieXRlTGVuZ3RoKTsKICAgICAgbGV0IGNoYXJzID0gW107CiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbGVuZ3RoOyBpKyspIHsKICAgICAgICBjaGFycy5wdXNoKGRhdGFWaWV3LmdldFVpbnQxNihpICogMikpOwogICAgICB9CiAgICAgIGxldCBzdHIgPSBTdHJpbmcuZnJvbUNoYXJDb2RlLmFwcGx5KG51bGwsIGNoYXJzKTsKICAgICAgdGhpcy5sb2coJ3N0cmluZycsIHN0cik7CgogICAgICB0aGlzLm9mZnNldCArPSBieXRlTGVuZ3RoOwoKICAgICAgaWYgKHRoaXMucGVla09mZnNldCA9PSBudWxsKSB7CiAgICAgICAgdGhpcy5mbGlwKCk7CiAgICAgIH0KICAgICAgcmV0dXJuIHN0cjsKICAgIH0KCiAgICBfaW50MzIoKSB7CiAgICAgIGxldCBieXRlTGVuZ3RoID0gNDsKCiAgICAgIGxldCBkYXRhVmlldyA9IG5ldyBEYXRhVmlldyh0aGlzLmJ1ZmZlciwgdGhpcy5vZmZzZXQpOwogICAgICBsZXQgbnVtID0gZGF0YVZpZXcuZ2V0SW50MzIoKTsKICAgICAgdGhpcy5sb2coJ19pbnQzMicsIG51bSk7CgogICAgICB0aGlzLm9mZnNldCArPSBieXRlTGVuZ3RoOwogICAgICByZXR1cm4gbnVtOwogICAgfQoKICAgIGludDMyKCkgewogICAgICB0aGlzLndhaXRXcml0ZSgnaW50MzInKTsKICAgICAgbGV0IG51bSA9IHRoaXMuX2ludDMyKCk7CiAgICAgIHRoaXMubG9nKCdpbnQzMicsIG51bSk7CgogICAgICBpZiAodGhpcy5wZWVrT2Zmc2V0ID09IG51bGwpIHsKICAgICAgICB0aGlzLmZsaXAoKTsKICAgICAgfQogICAgICByZXR1cm4gbnVtOwogICAgfQoKICAgIGJ5dGVzKCkgewogICAgICB0aGlzLndhaXRXcml0ZSgnYnl0ZXMnKTsKCiAgICAgIGxldCBieXRlTGVuZ3RoID0gdGhpcy5faW50MzIoKTsKCiAgICAgIGxldCBieXRlcyA9IG5ldyBBcnJheUJ1ZmZlcihieXRlTGVuZ3RoKTsKICAgICAgbmV3IFVpbnQ4QXJyYXkoYnl0ZXMpLnNldCgKICAgICAgICBuZXcgVWludDhBcnJheSh0aGlzLmJ1ZmZlciwgdGhpcy5vZmZzZXQsIGJ5dGVMZW5ndGgpCiAgICAgICk7CiAgICAgIHRoaXMubG9nKCdieXRlcycsIGJ5dGVzKTsKCiAgICAgIHRoaXMub2Zmc2V0ICs9IGJ5dGVMZW5ndGg7CgogICAgICBpZiAodGhpcy5wZWVrT2Zmc2V0ID09IG51bGwpIHsKICAgICAgICB0aGlzLmZsaXAoKTsKICAgICAgfQogICAgICByZXR1cm4gYnl0ZXM7CiAgICB9CiAgfQoKICBjbGFzcyBXcml0ZXIgewogICAgY29uc3RydWN0b3IoCiAgICAgIGJ1ZmZlciwKICAgICAgeyBpbml0aWFsT2Zmc2V0ID0gNCwgdXNlQXRvbWljcyA9IHRydWUsIHN0cmVhbSA9IHRydWUsIGRlYnVnLCBuYW1lIH0gPSB7fQogICAgKSB7CiAgICAgIHRoaXMuYnVmZmVyID0gYnVmZmVyOwogICAgICB0aGlzLmF0b21pY1ZpZXcgPSBuZXcgSW50MzJBcnJheShidWZmZXIpOwogICAgICB0aGlzLm9mZnNldCA9IGluaXRpYWxPZmZzZXQ7CiAgICAgIHRoaXMudXNlQXRvbWljcyA9IHVzZUF0b21pY3M7CiAgICAgIHRoaXMuc3RyZWFtID0gc3RyZWFtOwoKICAgICAgdGhpcy5kZWJ1ZyA9IGRlYnVnOwogICAgICB0aGlzLm5hbWUgPSBuYW1lOwoKICAgICAgaWYgKHRoaXMudXNlQXRvbWljcykgewogICAgICAgIC8vIFRoZSBidWZmZXIgc3RhcnRzIG91dCBhcyB3cml0ZWFibGUKICAgICAgICBBdG9taWNzLnN0b3JlKHRoaXMuYXRvbWljVmlldywgMCwgV1JJVEVBQkxFKTsKICAgICAgfSBlbHNlIHsKICAgICAgICB0aGlzLmF0b21pY1ZpZXdbMF0gPSBXUklURUFCTEU7CiAgICAgIH0KICAgIH0KCiAgICBsb2coLi4uYXJncykgewogICAgICBpZiAodGhpcy5kZWJ1ZykgewogICAgICAgIGNvbnNvbGUubG9nKGBbd3JpdGVyOiAke3RoaXMubmFtZX1dYCwgLi4uYXJncyk7CiAgICAgIH0KICAgIH0KCiAgICB3YWl0UmVhZChuYW1lKSB7CiAgICAgIGlmICh0aGlzLnVzZUF0b21pY3MpIHsKICAgICAgICB0aGlzLmxvZyhgd2FpdGluZyBmb3IgJHtuYW1lfWApOwogICAgICAgIC8vIFN3aXRjaCB0byB3cml0YWJsZQogICAgICAgIC8vIEF0b21pY3Muc3RvcmUodGhpcy5hdG9taWNWaWV3LCAwLCAxKTsKCiAgICAgICAgbGV0IHByZXYgPSBBdG9taWNzLmNvbXBhcmVFeGNoYW5nZSgKICAgICAgICAgIHRoaXMuYXRvbWljVmlldywKICAgICAgICAgIDAsCiAgICAgICAgICBXUklURUFCTEUsCiAgICAgICAgICBSRUFEQUJMRQogICAgICAgICk7CgogICAgICAgIGlmIChwcmV2ICE9PSBXUklURUFCTEUpIHsKICAgICAgICAgIHRocm93IG5ldyBFcnJvcigKICAgICAgICAgICAgJ1dyb3RlIHNvbWV0aGluZyBpbnRvIHVud3JpdGFibGUgYnVmZmVyISBUaGlzIGlzIGRpc2FzdHJvdXMnCiAgICAgICAgICApOwogICAgICAgIH0KCiAgICAgICAgQXRvbWljcy5ub3RpZnkodGhpcy5hdG9taWNWaWV3LCAwKTsKCiAgICAgICAgd2hpbGUgKEF0b21pY3MubG9hZCh0aGlzLmF0b21pY1ZpZXcsIDApID09PSBSRUFEQUJMRSkgewogICAgICAgICAgLy8gY29uc29sZS5sb2coJ3dhaXRpbmcgdG8gYmUgcmVhZC4uLicpOwogICAgICAgICAgQXRvbWljcy53YWl0KHRoaXMuYXRvbWljVmlldywgMCwgUkVBREFCTEUsIDUwMCk7CiAgICAgICAgfQoKICAgICAgICB0aGlzLmxvZyhgcmVzdW1lZCBmb3IgJHtuYW1lfWApOwogICAgICB9IGVsc2UgewogICAgICAgIHRoaXMuYXRvbWljVmlld1swXSA9IFJFQURBQkxFOwogICAgICB9CgogICAgICB0aGlzLm9mZnNldCA9IDQ7CiAgICB9CgogICAgZmluYWxpemUoKSB7CiAgICAgIHRoaXMubG9nKCdmaW5hbGl6aW5nJyk7CiAgICAgIGxldCBkYXRhVmlldyA9IG5ldyBEYXRhVmlldyh0aGlzLmJ1ZmZlciwgdGhpcy5vZmZzZXQpOwogICAgICBkYXRhVmlldy5zZXRVaW50MzIoMCwgRklOQUxJWkVEKTsKICAgICAgdGhpcy53YWl0UmVhZCgnZmluYWxpemUnKTsKICAgIH0KCiAgICBzdHJpbmcoc3RyKSB7CiAgICAgIHRoaXMubG9nKCdzdHJpbmcnLCBzdHIpOwoKICAgICAgbGV0IGJ5dGVMZW5ndGggPSBzdHIubGVuZ3RoICogMjsKICAgICAgdGhpcy5faW50MzIoYnl0ZUxlbmd0aCk7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0LCBieXRlTGVuZ3RoKTsKICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBzdHIubGVuZ3RoOyBpKyspIHsKICAgICAgICBkYXRhVmlldy5zZXRVaW50MTYoaSAqIDIsIHN0ci5jaGFyQ29kZUF0KGkpKTsKICAgICAgfQoKICAgICAgdGhpcy5vZmZzZXQgKz0gYnl0ZUxlbmd0aDsKICAgICAgdGhpcy53YWl0UmVhZCgnc3RyaW5nJyk7CiAgICB9CgogICAgX2ludDMyKG51bSkgewogICAgICBsZXQgYnl0ZUxlbmd0aCA9IDQ7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0KTsKICAgICAgZGF0YVZpZXcuc2V0SW50MzIoMCwgbnVtKTsKCiAgICAgIHRoaXMub2Zmc2V0ICs9IGJ5dGVMZW5ndGg7CiAgICB9CgogICAgaW50MzIobnVtKSB7CiAgICAgIHRoaXMubG9nKCdpbnQzMicsIG51bSk7CiAgICAgIHRoaXMuX2ludDMyKG51bSk7CiAgICAgIHRoaXMud2FpdFJlYWQoJ2ludDMyJyk7CiAgICB9CgogICAgYnl0ZXMoYnVmZmVyKSB7CiAgICAgIHRoaXMubG9nKCdieXRlcycsIGJ1ZmZlcik7CgogICAgICBsZXQgYnl0ZUxlbmd0aCA9IGJ1ZmZlci5ieXRlTGVuZ3RoOwogICAgICB0aGlzLl9pbnQzMihieXRlTGVuZ3RoKTsKICAgICAgbmV3IFVpbnQ4QXJyYXkodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0KS5zZXQobmV3IFVpbnQ4QXJyYXkoYnVmZmVyKSk7CgogICAgICB0aGlzLm9mZnNldCArPSBieXRlTGVuZ3RoOwogICAgICB0aGlzLndhaXRSZWFkKCdieXRlcycpOwogICAgfQogIH0KCiAgbGV0IGJ1ZmZlciA9IDQwMDAwOwogIGxldCBiYXNlVGltZTsKICBsZXQgdGltaW5ncyA9IHt9OwogIGxldCBjb3VudHMgPSB7fTsKCiAgYXN5bmMgZnVuY3Rpb24gd3JpdGVEYXRhKHR5cGUsIG5hbWUsIGRhdGEpIHsKICAgIGdsb2JhbFRoaXMucG9zdE1lc3NhZ2UoewogICAgICB0eXBlOiAnX19wZXJmLWRlZXRzOmxvZy1wZXJmJywKICAgICAgZGF0YVR5cGU6IHR5cGUsCiAgICAgIG5hbWUsCiAgICAgIGRhdGEKICAgIH0pOwogIH0KCiAgZnVuY3Rpb24gc3RhcnQoKSB7CiAgICBnbG9iYWxUaGlzLnBvc3RNZXNzYWdlKHsgdHlwZTogJ19fcGVyZi1kZWV0czpjbGVhci1wZXJmJyB9KTsKCiAgICB0aW1pbmdzID0ge307CiAgICBjb3VudHMgPSB7fTsKICAgIGJhc2VUaW1lID0gcGVyZm9ybWFuY2Uubm93KCk7CiAgfQoKICBhc3luYyBmdW5jdGlvbiBzdG9wKCkgewogICAgT2JqZWN0LmtleXModGltaW5ncykubWFwKG5hbWUgPT4gewogICAgICBsZXQgdGltaW5nID0gdGltaW5nc1tuYW1lXTsKICAgICAgd3JpdGVEYXRhKAogICAgICAgICd0aW1pbmcnLAogICAgICAgIG5hbWUsCiAgICAgICAgdGltaW5nLmRhdGEubWFwKHggPT4gKHsgeDogeC5zdGFydCArIHgudG9vaywgeTogeC50b29rIH0pKQogICAgICApOwogICAgfSk7CgogICAgT2JqZWN0LmtleXMoY291bnRzKS5tYXAobmFtZSA9PiB7CiAgICAgIGxldCBjb3VudCA9IGNvdW50c1tuYW1lXTsKICAgICAgd3JpdGVEYXRhKCdjb3VudCcsIG5hbWUsIGNvdW50Lm1hcCgoYywgaSkgPT4gKHsgeDogYy50aW1lLCB5OiBpIH0pKSk7CiAgICB9KTsKICB9CgogIGZ1bmN0aW9uIHJlY29yZChuYW1lKSB7CiAgICBpZiAodGltaW5nc1tuYW1lXSA9PSBudWxsKSB7CiAgICAgIHRpbWluZ3NbbmFtZV0gPSB7IHN0YXJ0OiBudWxsLCBkYXRhOiBbXSB9OwogICAgfQogICAgbGV0IHRpbWVyID0gdGltaW5nc1tuYW1lXTsKCiAgICBpZiAodGltZXIuc3RhcnQgIT0gbnVsbCkgewogICAgICB0aHJvdyBuZXcgRXJyb3IoYHRpbWVyIGFscmVhZHkgc3RhcnRlZCAke25hbWV9YCk7CiAgICB9CiAgICB0aW1lci5zdGFydCA9IHBlcmZvcm1hbmNlLm5vdygpOwogIH0KCiAgZnVuY3Rpb24gZW5kUmVjb3JkaW5nKG5hbWUpIHsKICAgIGxldCBub3cgPSBwZXJmb3JtYW5jZS5ub3coKTsKICAgIGxldCB0aW1lciA9IHRpbWluZ3NbbmFtZV07CgogICAgaWYgKHRpbWVyICYmIHRpbWVyLnN0YXJ0ICE9IG51bGwpIHsKICAgICAgbGV0IHRvb2sgPSBub3cgLSB0aW1lci5zdGFydDsKICAgICAgbGV0IHN0YXJ0ID0gdGltZXIuc3RhcnQgLSBiYXNlVGltZTsKICAgICAgdGltZXIuc3RhcnQgPSBudWxsOwoKICAgICAgaWYgKHRpbWVyLmRhdGEubGVuZ3RoIDwgYnVmZmVyKSB7CiAgICAgICAgdGltZXIuZGF0YS5wdXNoKHsgc3RhcnQsIHRvb2sgfSk7CiAgICAgIH0KICAgIH0KICB9CgogIGZ1bmN0aW9uIGNvdW50KG5hbWUpIHsKICAgIGlmIChjb3VudHNbbmFtZV0gPT0gbnVsbCkgewogICAgICBjb3VudHNbbmFtZV0gPSBbXTsKICAgIH0KICAgIGNvdW50c1tuYW1lXS5wdXNoKHsgdGltZTogcGVyZm9ybWFuY2Uubm93KCkgfSk7CiAgfQoKICAvLyBBZGQgYSBsaXN0ZW5lciB0byBoYW5kbGUgc3RhcnQvc3RvcCBldmVudHMKICBnbG9iYWxUaGlzLmFkZEV2ZW50TGlzdGVuZXIoJ21lc3NhZ2UnLCBlID0+IHsKICAgIHN3aXRjaCAoZS5kYXRhLnR5cGUpIHsKICAgICAgY2FzZSAnX19wZXJmLWRlZXRzOnN0YXJ0LXByb2ZpbGUnOgogICAgICAgIHN0YXJ0KCk7CiAgICAgICAgYnJlYWs7CiAgICAgIGNhc2UgJ19fcGVyZi1kZWV0czpzdG9wLXByb2ZpbGUnOgogICAgICAgIHN0b3AoKTsKICAgICAgICBicmVhazsKICAgICAgLy8gSW4gdGhlIGNhc2Ugb2YgbmVzdGVkIHdvcmtlcnMsIHdlIHdhbnQgdG8gcHJvcGFnYXRlIHRoZXNlCiAgICAgIC8vIGV2ZW50cyB1cCB0byB0aGUgbWFpbiB0aHJlYWQuIE5vdGUgdGhhdCB0aGlzIGFzc3VtZXMgdGhlIHBlcmYKICAgICAgLy8gbGlicmFyeSBpcyBsb2FkZWQgdGhyb3VnaG91dCB0aGUgd2hvbGUgd29ya2VyIHRyZWUuIElmIG9uZSBvZgogICAgICAvLyB0aGUgY2hpbGQgd29ya2VycyBkb2Vzbid0IGxvYWQsIHRoaXMgbGlzdGVuZXIgd29uJ3QgcnVuIGFuZAogICAgICAvLyBkYXRhIHdpbGwgYmUgbG9zdAogICAgICBjYXNlICdfX3BlcmYtZGVldHM6Y2xlYXItcGVyZic6CiAgICAgIGNhc2UgJ19fcGVyZi1kZWV0czpsb2ctcGVyZic6CiAgICAgICAgc2VsZi5wb3N0TWVzc2FnZShlLmRhdGEpOwogICAgfQogIH0pOwoKICBsZXQgaXNQcm9iYWJseVNhZmFyaSA9IC9eKCg/IWNocm9tZXxhbmRyb2lkKS4pKnNhZmFyaS9pLnRlc3QoCiAgICBuYXZpZ2F0b3IudXNlckFnZW50CiAgKTsKCiAgbGV0IG9wZW5EYnMgPSBuZXcgTWFwKCk7CiAgbGV0IHRyYW5zYWN0aW9ucyA9IG5ldyBNYXAoKTsKCiAgZnVuY3Rpb24gYXNzZXJ0KGNvbmQsIG1zZykgewogICAgaWYgKCFjb25kKSB7CiAgICAgIHRocm93IG5ldyBFcnJvcihtc2cpOwogICAgfQogIH0KCiAgbGV0IExPQ0tfVFlQRVMgPSB7CiAgICBOT05FOiAwLAogICAgU0hBUkVEOiAxLAogICAgUkVTRVJWRUQ6IDIsCiAgICBQRU5ESU5HOiAzLAogICAgRVhDTFVTSVZFOiA0CiAgfTsKCiAgLy8gV2UgdXNlIGxvbmctbGl2ZWQgdHJhbnNhY3Rpb25zLCBhbmQgYFRyYW5zYWN0aW9uYCBrZWVwcyB0aGUKICAvLyB0cmFuc2FjdGlvbiBzdGF0ZS4gSXQgaW1wbGVtZW50cyBhbiBvcHRpbWFsIHdheSB0byBwZXJmb3JtCiAgLy8gcmVhZC93cml0ZXMgd2l0aCBrbm93bGVkZ2Ugb2YgaG93IHNxbGl0ZSBhc2tzIGZvciB0aGVtLCBhbmQgYWxzbwogIC8vIGltcGxlbWVudHMgYSBsb2NraW5nIG1lY2hhbmlzbSB0aGF0IG1hcHMgdG8gaG93IHNxbGl0ZSBsb2NrcyB3b3JrLgogIGNsYXNzIFRyYW5zYWN0aW9uIHsKICAgIGNvbnN0cnVjdG9yKGRiLCBpbml0aWFsTW9kZSA9ICdyZWFkb25seScpIHsKICAgICAgdGhpcy5kYiA9IGRiOwogICAgICBjb3VudCgndHJhbnNhY3Rpb25zJyk7CiAgICAgIHRoaXMudHJhbnMgPSB0aGlzLmRiLnRyYW5zYWN0aW9uKFsnZGF0YSddLCBpbml0aWFsTW9kZSk7CiAgICAgIHRoaXMuc3RvcmUgPSB0aGlzLnRyYW5zLm9iamVjdFN0b3JlKCdkYXRhJyk7CiAgICAgIHRoaXMubG9ja1R5cGUgPQogICAgICAgIGluaXRpYWxNb2RlID09PSAncmVhZG9ubHknID8gTE9DS19UWVBFUy5TSEFSRUQgOiBMT0NLX1RZUEVTLkVYQ0xVU0lWRTsKCiAgICAgIC8vIFRoZXJlIGlzIG5vIG5lZWQgZm9yIHVzIHRvIGNhY2hlIGJsb2Nrcy4gVXNlIHNxbGl0ZSdzCiAgICAgIC8vIGBjYWNoZV9zaXplYCBmb3IgdGhhdCBhbmQgaXQgd2lsbCBhdXRvbWF0aWNhbGx5IGRvIGl0LiBIb3dldmVyLAogICAgICAvLyB3ZSBkbyBzdGlsbCBrZWVwIGEgY2FjaGUgb2YgdGhlIGZpcnN0IGJsb2NrIGZvciB0aGUgZHVyYXRpb24gb2YKICAgICAgLy8gdGhpcyB0cmFuc2FjdGlvbiBiZWNhdXNlIG9mIGhvdyBsb2NraW5nIHdvcmtzOyB0aGlzIGF2b2lkcyBhCiAgICAgIC8vIGZldyBleHRyYSByZWFkcyBhbmQgYWxsb3dzIHVzIHRvIGRldGVjdCBjaGFuZ2VzIGR1cmluZwogICAgICAvLyB1cGdyYWRpbmcgKHNlZSBgdXBncmFkZUV4Y2x1c2l2ZWApCiAgICAgIHRoaXMuY2FjaGVkRmlyc3RCbG9jayA9IG51bGw7CgogICAgICB0aGlzLmN1cnNvciA9IG51bGw7CiAgICAgIHRoaXMucHJldlJlYWRzID0gbnVsbDsKICAgIH0KCiAgICBhc3luYyBwcmVmZXRjaEZpcnN0QmxvY2sodGltZW91dCkgewogICAgICAvLyBUT0RPOiBpbXBsZW1lbnQgdGltZW91dAoKICAgICAgLy8gR2V0IHRoZSBmaXJzdCBibG9jayBhbmQgY2FjaGUgaXQKICAgICAgbGV0IGJsb2NrID0gYXdhaXQgdGhpcy5nZXQoMCk7CiAgICAgIHRoaXMuY2FjaGVkRmlyc3RCbG9jayA9IGJsb2NrOwogICAgICByZXR1cm4gYmxvY2s7CiAgICB9CgogICAgYXN5bmMgd2FpdENvbXBsZXRlKCkgewogICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4gewogICAgICAgIC8vIEVhZ2VybHkgY29tbWl0IGl0IGZvciBiZXR0ZXIgcGVyZi4gTm90ZSB0aGF0ICoqdGhpcyBhc3N1bWVzCiAgICAgICAgLy8gdGhlIHRyYW5zYWN0aW9uIGlzIG9wZW4qKiBhcyBgY29tbWl0YCB3aWxsIHRocm93IGFuIGVycm9yIGlmCiAgICAgICAgLy8gaXQncyBhbHJlYWR5IGNsb3NlZCAod2hpY2ggc2hvdWxkIG5ldmVyIGJlIHRoZSBjYXNlIGZvciB1cykKICAgICAgICB0aGlzLmNvbW1pdCgpOwoKICAgICAgICBpZiAodGhpcy5sb2NrVHlwZSA9PT0gTE9DS19UWVBFUy5FWENMVVNJVkUpIHsKICAgICAgICAgIC8vIFdhaXQgdW50aWwgYWxsIHdyaXRlcyBhcmUgY29tbWl0dGVkCiAgICAgICAgICB0aGlzLnRyYW5zLm9uY29tcGxldGUgPSBlID0+IHJlc29sdmUoKTsKCiAgICAgICAgICAvLyBUT0RPOiBJcyBpdCBPSyB0byBhZGQgdGhpcyBsYXRlciwgYWZ0ZXIgYW4gZXJyb3IgbWlnaHQgaGF2ZQogICAgICAgICAgLy8gaGFwcGVuZWQ/IFdpbGwgaXQgaG9sZCB0aGUgZXJyb3IgYW5kIGZpcmUgdGhpcyB3aGVuIHdlCiAgICAgICAgICAvLyBhdHRhY2hlZCBpdD8gV2UgbWlnaHQgd2FudCB0byBlYWdlcmx5IGNyZWF0ZSB0aGUgcHJvbWlzZQogICAgICAgICAgLy8gd2hlbiBjcmVhdGluZyB0aGUgdHJhbnNhY3Rpb24gYW5kIHJldHVybiBpdCBoZXJlCiAgICAgICAgICB0aGlzLnRyYW5zLm9uZXJyb3IgPSBlID0+IHJlamVjdChlKTsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgaWYgKGlzUHJvYmFibHlTYWZhcmkpIHsKICAgICAgICAgICAgLy8gU2FmYXJpIGhhcyBhIGJ1ZyB3aGVyZSBzb21ldGltZXMgdGhlIElEQiBnZXRzIGJsb2NrZWQKICAgICAgICAgICAgLy8gcGVybWFuZW50bHkgaWYgeW91IHJlZnJlc2ggdGhlIHBhZ2Ugd2l0aCBhbiBvcGVuCiAgICAgICAgICAgIC8vIHRyYW5zYWN0aW9uLiBZb3UgaGF2ZSB0byByZXN0YXJ0IHRoZSBicm93c2VyIHRvIGZpeCBpdC4KICAgICAgICAgICAgLy8gV2Ugd2FpdCBmb3IgcmVhZG9ubHkgdHJhbnNhY3Rpb25zIHRvIGZpbmlzaCB0b28sIGJ1dCB0aGlzCiAgICAgICAgICAgIC8vIGlzIGEgcGVyZiBoaXQKICAgICAgICAgICAgdGhpcy50cmFucy5vbmNvbXBsZXRlID0gZSA9PiByZXNvbHZlKCk7CiAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAvLyBObyBuZWVkIHRvIHdhaXQgb24gYW55dGhpbmcgaW4gYSByZWFkLW9ubHkgdHJhbnNhY3Rpb24uCiAgICAgICAgICAgIC8vIE5vdGUgdGhhdCBlcnJvcnMgZHVyaW5nIHJlYWRzIGFyZWEgYWx3YXlzIGhhbmRsZWQgYnkgdGhlCiAgICAgICAgICAgIC8vIHJlYWQgcmVxdWVzdC4KICAgICAgICAgICAgcmVzb2x2ZSgpOwogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSk7CiAgICB9CgogICAgY29tbWl0KCkgewogICAgICAvLyBTYWZhcmkgZG9lc24ndCBzdXBwb3J0IHRoaXMgbWV0aG9kIHlldCAodGhpcyBpcyBqdXN0IGFuCiAgICAgIC8vIG9wdGltaXphdGlvbikKICAgICAgaWYgKHRoaXMudHJhbnMuY29tbWl0KSB7CiAgICAgICAgdGhpcy50cmFucy5jb21taXQoKTsKICAgICAgfQogICAgfQoKICAgIGFzeW5jIHVwZ3JhZGVFeGNsdXNpdmUoKSB7CiAgICAgIHRoaXMuY29tbWl0KCk7CgogICAgICAvLyBjb25zb2xlLmxvZygndXBkYXRpbmcgdHJhbnNhY3Rpb24gcmVhZHdyaXRlJyk7CiAgICAgIGNvdW50KCd0cmFuc2FjdGlvbnMnKTsKICAgICAgdGhpcy50cmFucyA9IHRoaXMuZGIudHJhbnNhY3Rpb24oWydkYXRhJ10sICdyZWFkd3JpdGUnKTsKICAgICAgdGhpcy5zdG9yZSA9IHRoaXMudHJhbnMub2JqZWN0U3RvcmUoJ2RhdGEnKTsKICAgICAgdGhpcy5sb2NrVHlwZSA9IExPQ0tfVFlQRVMuRVhDTFVTSVZFOwoKICAgICAgbGV0IGNhY2hlZDAgPSB0aGlzLmNhY2hlZEZpcnN0QmxvY2s7CgogICAgICAvLyBEbyBhIHJlYWQKICAgICAgbGV0IGJsb2NrID0gYXdhaXQgdGhpcy5wcmVmZXRjaEZpcnN0QmxvY2soNTAwKTsKICAgICAgLy8gVE9ETzogd2hlbiB0aW1lb3V0cyBhcmUgaW1wbGVtZW50ZWQsIGRldGVjdCB0aW1lb3V0IGFuZCByZXR1cm4gQlVTWQoKICAgICAgaWYgKGNhY2hlZDAgPT0gbnVsbCAmJiBibG9jayA9PSBudWxsKSB7CiAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgZm9yIChsZXQgaSA9IDI0OyBpIDwgNDA7IGkrKykgewogICAgICAgICAgaWYgKGJsb2NrW2ldICE9PSBjYWNoZWQwW2ldKSB7CiAgICAgICAgICAgIHJldHVybiBmYWxzZTsKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIH0KCiAgICAgIHJldHVybiB0cnVlOwogICAgfQoKICAgIGRvd25ncmFkZVNoYXJlZCgpIHsKICAgICAgdGhpcy5jb21taXQoKTsKCiAgICAgIC8vIGNvbnNvbGUubG9nKCdkb3duZ3JhZGluZyB0cmFuc2FjdGlvbiByZWFkb25seScpOwogICAgICBjb3VudCgndHJhbnNhY3Rpb25zJyk7CiAgICAgIHRoaXMudHJhbnMgPSB0aGlzLmRiLnRyYW5zYWN0aW9uKFsnZGF0YSddLCAncmVhZG9ubHknKTsKICAgICAgdGhpcy5zdG9yZSA9IHRoaXMudHJhbnMub2JqZWN0U3RvcmUoJ2RhdGEnKTsKICAgICAgdGhpcy5sb2NrVHlwZSA9IExPQ0tfVFlQRVMuU0hBUkVEOwogICAgfQoKICAgIGFzeW5jIGdldChrZXkpIHsKICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHsKICAgICAgICByZWNvcmQoJ2dldCcpOwogICAgICAgIGxldCByZXEgPSB0aGlzLnN0b3JlLmdldChrZXkpOwogICAgICAgIHJlcS5vbnN1Y2Nlc3MgPSBlID0+IHsKICAgICAgICAgIGVuZFJlY29yZGluZygnZ2V0Jyk7CiAgICAgICAgICByZXNvbHZlKHJlcS5yZXN1bHQpOwogICAgICAgIH07CiAgICAgICAgcmVxLm9uZXJyb3IgPSBlID0+IHJlamVjdChlKTsKICAgICAgfSk7CiAgICB9CgogICAgZ2V0UmVhZERpcmVjdGlvbigpIHsKICAgICAgLy8gVGhlcmUgYXJlIGEgdHdvIHdheXMgd2UgY2FuIHJlYWQgZGF0YTogYSBkaXJlY3QgYGdldGAgcmVxdWVzdAogICAgICAvLyBvciBvcGVuaW5nIGEgY3Vyc29yIGFuZCBpdGVyYXRpbmcgdGhyb3VnaCBkYXRhLiBXZSBkb24ndCBrbm93CiAgICAgIC8vIHdoYXQgZnV0dXJlIHJlYWRzIGxvb2sgbGlrZSwgc28gd2UgZG9uJ3Qga25vdyB0aGUgYmVzdCBzdHJhdGVneQogICAgICAvLyB0byBwaWNrLiBBbHdheXMgY2hvb3Npbmcgb25lIHN0cmF0ZWd5IGZvcmdvZXMgYSBsb3Qgb2YKICAgICAgLy8gb3B0aW1pemF0aW9uLCBiZWNhdXNlIGl0ZXJhdGluZyB3aXRoIGEgY3Vyc29yIGlzIGEgbG90IGZhc3RlcgogICAgICAvLyB0aGFuIG1hbnkgYGdldGAgY2FsbHMuIE9uIHRoZSBvdGhlciBoYW5kLCBvcGVuaW5nIGEgY3Vyc29yIGlzCiAgICAgIC8vIHNsb3csIGFuZCBzbyBpcyBjYWxsaW5nIGBhZHZhbmNlYCB0byBtb3ZlIGEgY3Vyc29yIG92ZXIgYSBodWdlCiAgICAgIC8vIHJhbmdlIChsaWtlIG1vdmluZyBpdCAxMDAwIGl0ZW1zIGxhdGVyKSwgc28gbWFueSBgZ2V0YCBjYWxscyB3b3VsZAogICAgICAvLyBiZSBmYXN0ZXIuIEluIGdlbmVyYWw6CiAgICAgIC8vCiAgICAgIC8vICogTWFueSBgZ2V0YCBjYWxscyBhcmUgZmFzdGVyIHdoZW4gZG9pbmcgcmFuZG9tIGFjY2Vzc2VzCiAgICAgIC8vICogSXRlcmF0aW5nIHdpdGggYSBjdXJzb3IgaXMgZmFzdGVyIGlmIGRvaW5nIG1vc3RseSBzZXF1ZW50aWFsCiAgICAgIC8vICAgYWNjZXNzZXMKICAgICAgLy8KICAgICAgLy8gV2UgaW1wbGVtZW50IGEgaGV1cmlzdGljIGFuZCBrZWVwcyB0cmFjayBvZiB0aGUgbGFzdCAzIHJlYWRzCiAgICAgIC8vIGFuZCBkZXRlY3RzIHdoZW4gdGhleSBhcmUgbW9zdGx5IHNlcXVlbnRpYWwuIElmIHRoZXkgYXJlLCB3ZQogICAgICAvLyBvcGVuIGEgY3Vyc29yIGFuZCBzdGFydCByZWFkaW5nIGJ5IGl0ZXJhdGluZyBpdC4gSWYgbm90LCB3ZSBkbwogICAgICAvLyBkaXJlY3QgYGdldGAgY2FsbHMuCiAgICAgIC8vCiAgICAgIC8vIE9uIHRvcCBvZiBhbGwgb2YgdGhpcywgZWFjaCBicm93c2VyIGhhcyBkaWZmZXJlbnQgcGVyZgogICAgICAvLyBjaGFyYWN0ZXJpc3RpY3MuIFdlIHdpbGwgcHJvYmFibHkgd2FudCB0byBtYWtlIHRoZXNlIHRocmVzaG9sZHMKICAgICAgLy8gY29uZmlndXJhYmxlIHNvIHRoZSB1c2VyIGNhbiBjaGFuZ2UgdGhlbSBwZXItYnJvd3NlciBpZiBuZWVkZWQsCiAgICAgIC8vIGFzIHdlbGwgYXMgZmluZS10dW5pbmcgdGhlbSBmb3IgdGhlaXIgdXNhZ2Ugb2Ygc3FsaXRlLgoKICAgICAgbGV0IHByZXZSZWFkcyA9IHRoaXMucHJldlJlYWRzOwogICAgICBpZiAocHJldlJlYWRzKSB7CiAgICAgICAgLy8gSGFzIHRoZXJlIGJlZW4gMyBmb3J3YXJkIHNlcXVlbnRpYWwgcmVhZHMgd2l0aGluIDEwIGJsb2Nrcz8KICAgICAgICBpZiAoCiAgICAgICAgICBwcmV2UmVhZHNbMF0gPCBwcmV2UmVhZHNbMV0gJiYKICAgICAgICAgIHByZXZSZWFkc1sxXSA8IHByZXZSZWFkc1syXSAmJgogICAgICAgICAgcHJldlJlYWRzWzJdIC0gcHJldlJlYWRzWzBdIDwgMTAKICAgICAgICApIHsKICAgICAgICAgIHJldHVybiAnbmV4dCc7CiAgICAgICAgfQoKICAgICAgICAvLyBIYXMgdGhlcmUgYmVlbiAzIGJhY2t3YXJkcyBzZXF1ZW50aWFsIHJlYWRzIHdpdGhpbiAxMCBibG9ja3M/CiAgICAgICAgaWYgKAogICAgICAgICAgcHJldlJlYWRzWzBdID4gcHJldlJlYWRzWzFdICYmCiAgICAgICAgICBwcmV2UmVhZHNbMV0gPiBwcmV2UmVhZHNbMl0gJiYKICAgICAgICAgIHByZXZSZWFkc1swXSAtIHByZXZSZWFkc1syXSA8IDEwCiAgICAgICAgKSB7CiAgICAgICAgICByZXR1cm4gJ3ByZXYnOwogICAgICAgIH0KICAgICAgfQoKICAgICAgcmV0dXJuIG51bGw7CiAgICB9CgogICAgcmVhZChwb3NpdGlvbikgewogICAgICBsZXQgd2FpdEN1cnNvciA9ICgpID0+IHsKICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4gewogICAgICAgICAgaWYgKHRoaXMuY3Vyc29yUHJvbWlzZSAhPSBudWxsKSB7CiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcigKICAgICAgICAgICAgICAnd2FpdEN1cnNvcigpIGNhbGxlZCBidXQgc29tZXRoaW5nIGVsc2UgaXMgYWxyZWFkeSB3YWl0aW5nJwogICAgICAgICAgICApOwogICAgICAgICAgfQogICAgICAgICAgdGhpcy5jdXJzb3JQcm9taXNlID0geyByZXNvbHZlLCByZWplY3QgfTsKICAgICAgICB9KTsKICAgICAgfTsKCiAgICAgIGlmICh0aGlzLmN1cnNvcikgewogICAgICAgIGxldCBjdXJzb3IgPSB0aGlzLmN1cnNvcjsKCiAgICAgICAgaWYgKAogICAgICAgICAgY3Vyc29yLmRpcmVjdGlvbiA9PT0gJ25leHQnICYmCiAgICAgICAgICBwb3NpdGlvbiA+IGN1cnNvci5rZXkgJiYKICAgICAgICAgIHBvc2l0aW9uIDwgY3Vyc29yLmtleSArIDEwMAogICAgICAgICkgewogICAgICAgICAgcmVjb3JkKCdzdHJlYW0tbmV4dCcpOwoKICAgICAgICAgIGN1cnNvci5hZHZhbmNlKHBvc2l0aW9uIC0gY3Vyc29yLmtleSk7CiAgICAgICAgICByZXR1cm4gd2FpdEN1cnNvcigpOwogICAgICAgIH0gZWxzZSBpZiAoCiAgICAgICAgICBjdXJzb3IuZGlyZWN0aW9uID09PSAncHJldicgJiYKICAgICAgICAgIHBvc2l0aW9uIDwgY3Vyc29yLmtleSAmJgogICAgICAgICAgcG9zaXRpb24gPiBjdXJzb3Iua2V5IC0gMTAwCiAgICAgICAgKSB7CiAgICAgICAgICByZWNvcmQoJ3N0cmVhbS1uZXh0Jyk7CgogICAgICAgICAgY3Vyc29yLmFkdmFuY2UoY3Vyc29yLmtleSAtIHBvc2l0aW9uKTsKICAgICAgICAgIHJldHVybiB3YWl0Q3Vyc29yKCk7CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgIC8vIERpdGNoIHRoZSBjdXJzb3IKICAgICAgICAgIHRoaXMuY3Vyc29yID0gbnVsbDsKICAgICAgICAgIHJldHVybiB0aGlzLnJlYWQocG9zaXRpb24pOwogICAgICAgIH0KICAgICAgfSBlbHNlIHsKICAgICAgICAvLyBXZSBkb24ndCBhbHJlYWR5IGhhdmUgYSBjdXJzb3IuIFdlIG5lZWQgdG8gYSBmcmVzaCByZWFkOwogICAgICAgIC8vIHNob3VsZCB3ZSBvcGVuIGEgY3Vyc29yIG9yIGNhbGwgYGdldGA/CgogICAgICAgIGxldCBkaXIgPSB0aGlzLmdldFJlYWREaXJlY3Rpb24oKTsKICAgICAgICBpZiAoZGlyKSB7CiAgICAgICAgICAvLyBPcGVuIGEgY3Vyc29yCiAgICAgICAgICB0aGlzLnByZXZSZWFkcyA9IG51bGw7CgogICAgICAgICAgbGV0IGtleVJhbmdlOwogICAgICAgICAgaWYgKGRpciA9PT0gJ3ByZXYnKSB7CiAgICAgICAgICAgIGtleVJhbmdlID0gSURCS2V5UmFuZ2UudXBwZXJCb3VuZChwb3NpdGlvbik7CiAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICBrZXlSYW5nZSA9IElEQktleVJhbmdlLmxvd2VyQm91bmQocG9zaXRpb24pOwogICAgICAgICAgfQoKICAgICAgICAgIGxldCByZXEgPSB0aGlzLnN0b3JlLm9wZW5DdXJzb3Ioa2V5UmFuZ2UsIGRpcik7CiAgICAgICAgICByZWNvcmQoJ3N0cmVhbScpOwoKICAgICAgICAgIHJlcS5vbnN1Y2Nlc3MgPSBlID0+IHsKICAgICAgICAgICAgZW5kUmVjb3JkaW5nKCdzdHJlYW0nKTsKICAgICAgICAgICAgZW5kUmVjb3JkaW5nKCdzdHJlYW0tbmV4dCcpOwoKICAgICAgICAgICAgbGV0IGN1cnNvciA9IGUudGFyZ2V0LnJlc3VsdDsKICAgICAgICAgICAgdGhpcy5jdXJzb3IgPSBjdXJzb3I7CgogICAgICAgICAgICBpZiAodGhpcy5jdXJzb3JQcm9taXNlID09IG51bGwpIHsKICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0dvdCBkYXRhIGZyb20gY3Vyc29yIGJ1dCBub3RoaW5nIGlzIHdhaXRpbmcgaXQnKTsKICAgICAgICAgICAgfQogICAgICAgICAgICB0aGlzLmN1cnNvclByb21pc2UucmVzb2x2ZShjdXJzb3IgPyBjdXJzb3IudmFsdWUgOiBudWxsKTsKICAgICAgICAgICAgdGhpcy5jdXJzb3JQcm9taXNlID0gbnVsbDsKICAgICAgICAgIH07CiAgICAgICAgICByZXEub25lcnJvciA9IGUgPT4gewogICAgICAgICAgICBjb25zb2xlLmxvZygnQ3Vyc29yIGZhaWx1cmU6JywgZSk7CgogICAgICAgICAgICBpZiAodGhpcy5jdXJzb3JQcm9taXNlID09IG51bGwpIHsKICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0dvdCBkYXRhIGZyb20gY3Vyc29yIGJ1dCBub3RoaW5nIGlzIHdhaXRpbmcgaXQnKTsKICAgICAgICAgICAgfQogICAgICAgICAgICB0aGlzLmN1cnNvclByb21pc2UucmVqZWN0KGUpOwogICAgICAgICAgICB0aGlzLmN1cnNvclByb21pc2UgPSBudWxsOwogICAgICAgICAgfTsKCiAgICAgICAgICByZXR1cm4gd2FpdEN1cnNvcigpOwogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICBpZiAodGhpcy5wcmV2UmVhZHMgPT0gbnVsbCkgewogICAgICAgICAgICB0aGlzLnByZXZSZWFkcyA9IFswLCAwLCAwXTsKICAgICAgICAgIH0KICAgICAgICAgIHRoaXMucHJldlJlYWRzLnB1c2gocG9zaXRpb24pOwogICAgICAgICAgdGhpcy5wcmV2UmVhZHMuc2hpZnQoKTsKCiAgICAgICAgICByZXR1cm4gdGhpcy5nZXQocG9zaXRpb24pOwogICAgICAgIH0KICAgICAgfQogICAgfQoKICAgIGFzeW5jIHNldChpdGVtKSB7CiAgICAgIHRoaXMucHJldlJlYWRzID0gbnVsbDsKCiAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7CiAgICAgICAgbGV0IHJlcSA9IHRoaXMuc3RvcmUucHV0KGl0ZW0udmFsdWUsIGl0ZW0ua2V5KTsKICAgICAgICByZXEub25zdWNjZXNzID0gZSA9PiByZXNvbHZlKHJlcS5yZXN1bHQpOwogICAgICAgIHJlcS5vbmVycm9yID0gZSA9PiByZWplY3QoZSk7CiAgICAgIH0pOwogICAgfQoKICAgIGFzeW5jIGJ1bGtTZXQoaXRlbXMpIHsKICAgICAgdGhpcy5wcmV2UmVhZHMgPSBudWxsOwoKICAgICAgZm9yIChsZXQgaXRlbSBvZiBpdGVtcykgewogICAgICAgIHRoaXMuc3RvcmUucHV0KGl0ZW0udmFsdWUsIGl0ZW0ua2V5KTsKICAgICAgfQogICAgfQogIH0KCiAgYXN5bmMgZnVuY3Rpb24gbG9hZERiKG5hbWUpIHsKICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7CiAgICAgIGlmIChvcGVuRGJzLmdldChuYW1lKSkgewogICAgICAgIHJlc29sdmUob3BlbkRicy5nZXQobmFtZSkpOwogICAgICAgIHJldHVybjsKICAgICAgfQoKICAgICAgY29uc29sZS5sb2coJ29wZW5pbmcnLCBuYW1lKTsKCiAgICAgIGxldCByZXEgPSBnbG9iYWxUaGlzLmluZGV4ZWREQi5vcGVuKG5hbWUsIDEpOwogICAgICByZXEub25zdWNjZXNzID0gZXZlbnQgPT4gewogICAgICAgIGNvbnNvbGUubG9nKCdkYiBpcyBvcGVuIScsIG5hbWUpOwogICAgICAgIGxldCBkYiA9IGV2ZW50LnRhcmdldC5yZXN1bHQ7CgogICAgICAgIGRiLm9udmVyc2lvbmNoYW5nZSA9ICgpID0+IHsKICAgICAgICAgIC8vIFRPRE86IE5vdGlmeSB0aGUgdXNlciBzb21laG93CiAgICAgICAgICBjb25zb2xlLmxvZygnY2xvc2luZyBiZWNhdXNlIHZlcnNpb24gY2hhbmdlZCcpOwogICAgICAgICAgZGIuY2xvc2UoKTsKICAgICAgICAgIG9wZW5EYnMuZGVsZXRlKG5hbWUpOwogICAgICAgIH07CgogICAgICAgIGRiLm9uY2xvc2UgPSAoKSA9PiB7CiAgICAgICAgICBvcGVuRGJzLmRlbGV0ZShuYW1lKTsKICAgICAgICB9OwoKICAgICAgICBvcGVuRGJzLnNldChuYW1lLCBkYik7CiAgICAgICAgcmVzb2x2ZShkYik7CiAgICAgIH07CiAgICAgIHJlcS5vbnVwZ3JhZGVuZWVkZWQgPSBldmVudCA9PiB7CiAgICAgICAgbGV0IGRiID0gZXZlbnQudGFyZ2V0LnJlc3VsdDsKICAgICAgICBpZiAoIWRiLm9iamVjdFN0b3JlTmFtZXMuY29udGFpbnMoJ2RhdGEnKSkgewogICAgICAgICAgZGIuY3JlYXRlT2JqZWN0U3RvcmUoJ2RhdGEnKTsKICAgICAgICB9CiAgICAgIH07CiAgICAgIHJlcS5vbmJsb2NrZWQgPSBlID0+IGNvbnNvbGUubG9nKCdibG9ja2VkJywgZSk7CiAgICAgIHJlcS5vbmVycm9yID0gcmVxLm9uYWJvcnQgPSBlID0+IHJlamVjdChlLnRhcmdldC5lcnJvcik7CiAgICB9KTsKICB9CgogIGZ1bmN0aW9uIGNsb3NlRGIobmFtZSkgewogICAgbGV0IG9wZW5EYiA9IG9wZW5EYnMuZ2V0KG5hbWUpOwogICAgaWYgKG9wZW5EYikgewogICAgICBjb25zb2xlLmxvZygnY2xvc2luZyBkYicpOwogICAgICBvcGVuRGIuY2xvc2UoKTsKICAgICAgb3BlbkRicy5kZWxldGUobmFtZSk7CiAgICB9CiAgfQoKICBmdW5jdGlvbiBnZXRUcmFuc2FjdGlvbihuYW1lKSB7CiAgICByZXR1cm4gdHJhbnNhY3Rpb25zLmdldChuYW1lKTsKICB9CgogIGFzeW5jIGZ1bmN0aW9uIHdpdGhUcmFuc2FjdGlvbihuYW1lLCBtb2RlLCBmdW5jKSB7CiAgICBsZXQgdHJhbnMgPSB0cmFuc2FjdGlvbnMuZ2V0KG5hbWUpOwogICAgaWYgKHRyYW5zKSB7CiAgICAgIC8vIElmIGEgdHJhbnNhY3Rpb24gYWxyZWFkeSBleGlzdHMsIHRoYXQgbWVhbnMgdGhlIGZpbGUgaGFzIGJlZW4KICAgICAgLy8gbG9ja2VkLiBXZSBkb24ndCBmdWxseSBzdXBwb3J0IGFyYml0cmFyeSBuZXN0ZWQgdHJhbnNhY3Rpb25zLAogICAgICAvLyBhcyBzZWVuIGJlbG93ICh3ZSB3b24ndCB1cGdyYWRlIGEgYHJlYWRvbmx5YCB0byBgcmVhZHdyaXRlYAogICAgICAvLyBhdXRvbWF0aWNhbGx5KSBhbmQgdGhpcyBpcyBtYWlubHkgZm9yIHRoZSB1c2UgY2FzZSB3aGVyZSBzcWxpdGUKICAgICAgLy8gbG9ja3MgdGhlIGRiIGFuZCBjcmVhdGVzIGEgdHJhbnNhY3Rpb24gZm9yIHRoZSBkdXJhY3Rpb24gb2YgdGhlCiAgICAgIC8vIGxvY2suIFdlIGRvbid0IGFjdHVhbGx5IHdyaXRlIGNvZGUgaW4gYSB3YXkgdGhhdCBhc3N1bWVzIG5lc3RlZAogICAgICAvLyB0cmFuc2FjdGlvbnMsIHNvIGp1c3QgZXJyb3IgaGVyZQogICAgICBpZiAobW9kZSA9PT0gJ3JlYWR3cml0ZScgJiYgdHJhbnMubG9ja1R5cGUgPT09IExPQ0tfVFlQRVMuU0hBUkVEKSB7CiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdBdHRlbXB0ZWQgd3JpdGUgYnV0IG9ubHkgaGFzIFNIQVJFRCBsb2NrJyk7CiAgICAgIH0KICAgICAgcmV0dXJuIGZ1bmModHJhbnMpOwogICAgfQoKICAgIC8vIE91dHNpZGUgdGhlIHNjb3BlIG9mIGEgbG9jaywgY3JlYXRlIGEgdGVtcG9yYXJ5IHRyYW5zYWN0aW9uCiAgICB0cmFucyA9IG5ldyBUcmFuc2FjdGlvbihhd2FpdCBsb2FkRGIobmFtZSksIG1vZGUpOwogICAgYXdhaXQgZnVuYyh0cmFucyk7CiAgICBhd2FpdCB0cmFucy53YWl0Q29tcGxldGUoKTsKICB9CgogIC8vIExvY2tpbmcgc3RyYXRlZ3k6CiAgLy8KICAvLyAqIFdlIG1hcCBzcWxpdGUncyBsb2NrcyBvbnRvIEluZGV4ZWREQidzIHRyYW5zYWN0aW9uIHNlbWFudGljcy4KICAvLyAgIFJlYWQgdHJhbnNhY3Rpb25zIG1heSBleGVjdXRlIGluIHBhcmFsbGVsLiBSZWFkL3dyaXRlCiAgLy8gICB0cmFuc2FjdGlvbnMgYXJlIHF1ZXVlZCB1cCBhbmQgd2FpdCB1bnRpbCBhbGwgcHJlY2VkaW5nCiAgLy8gICByZWFkIHRyYW5zYWN0aW9ucyBmaW5pc2ggZXhlY3V0aW5nLiBSZWFkIHRyYW5zYWN0aW9ucyBzdGFydGVkCiAgLy8gICBhZnRlciBhIHJlYWQvd3JpdGUgdHJhbnNhY3Rpb24gd2FpdCB1bnRpbCBpdCBpcyBmaW5pc2hlZC4KICAvLwogIC8vICogSURCIHRyYW5zYWN0aW9ucyB3aWxsIHdhaXQgZm9yZXZlciB1bnRpbCB0aGV5IGNhbiBleGVjdXRlIChmb3IKICAvLyAgIGV4YW1wbGUsIHRoZXkgbWF5IGJlIGJsb2NrZWQgb24gYSByZWFkL3dyaXRlIHRyYW5zYWN0aW9uKS4gV2UKICAvLyAgIGRvbid0IHdhbnQgdG8gYWxsb3cgc3FsaXRlIHRyYW5zYWN0aW9ucyB0byB3YWl0IGZvcmV2ZXIsIHNvCiAgLy8gICB3ZSBtYW51YWxseSB0aW1lb3V0IGlmIGEgdHJhbnNhY3Rpb24gdGFrZXMgdG9vIGxvbmcgdG8KICAvLyAgIHN0YXJ0IGV4ZWN1dGluZy4gVGhpcyBzaW11bGF0ZXMgdGhlIGJlaGF2aW9yIG9mIGEgc3FsaXRlCiAgLy8gICBiYWlsaW5nIGlmIGl0IGNhbid0IHJlcXVpcmUgYSBsb2NrLgogIC8vCiAgLy8gKiBBIFNIQVJFRCBsb2NrIHdhbnRzIHRvIHJlYWQgZnJvbSB0aGUgZGIuIFdlIHN0YXJ0IGEgcmVhZAogIC8vICAgdHJhbnNhY3Rpb24gYW5kIHJlYWQgdGhlIGZpcnN0IGJsb2NrLCBhbmQgaWYgd2UgcmVhZCBpdCB3aXRoaW4KICAvLyAgIDUwMG1zIHdlIGNvbnNpZGVyIHRoZSBsb2NrIHN1Y2Nlc3NmdWwuIE90aGVyd2lzZSB0aGUgbG9jawogIC8vICAgZmFpbGVkIGFuZCB3ZSByZXR1cm4gU1FMSVRFX0JVU1kuIChUaGVyZSdzIG5vIHBlcmYgZG93bnNpZGUKICAvLyAgIHRvIHJlYWRpbmcgdGhlIGZpcnN0IGJsb2NrIC0gaXQgaGFzIHRvIGJlIHJlYWQgYW55d2F5IHRvIGNoZWNrCiAgLy8gICBieXRlcyAyNC0zOSBmb3IgdGhlIGNoYW5nZSBjb3VudGVyKQogIC8vCiAgLy8gKiBBIFJFU0VSVkVEIGxvY2sgbWVhbnMgdGhlIGRiIHdhbnRzIHRvIHN0YXJ0IHdyaXRpbmcgKHRoaW5rIG9mCiAgLy8gICBgQkVHSU4gVFJBTlNBQ1RJT05gKS4gT25seSBvbmUgcHJvY2VzcyBjYW4gb2J0YWluIGEgUkVTRVJWRUQKICAvLyAgIGxvY2sgYXQgYSB0aW1lLCBidXQgbm9ybWFsbHkgc3FsaXRlIHN0aWxsIGxlYWRzIG5ldyByZWFkIGxvY2tzCiAgLy8gICBoYXBwZW4uIEl0IGlzbid0IHVudGlsIGFuIEVYQ0xVU0lWRSBsb2NrIGlzIGhlbGQgdGhhdCByZWFkcyBhcmUKICAvLyAgIGJsb2NrZWQuIEhvd2V2ZXIsIHNpbmNlIHdlIG5lZWQgdG8gZ3VhcmFudGVlIG9ubHkgb25lIFJFU0VSVkVECiAgLy8gICBsb2NrIGF0IG9uY2UgKG90aGVyd2lzZSBkYXRhIGNvdWxkIGNoYW5nZSBmcm9tIGFub3RoZXIgcHJvY2VzcwogIC8vICAgd2l0aGluIGEgdHJhbnNhY3Rpb24sIGNhdXNpbmcgZmF1bHR5IGNhY2hlcyBldGMpIHRoZSBzaW1wbGVzdAogIC8vICAgdGhpbmcgdG8gZG8gaXMgZ28gYWhlYWQgYW5kIGdyYWIgYSByZWFkL3dyaXRlIHRyYW5zYWN0aW9uIHRoYXQKICAvLyAgIHJlcHJlc2VudHMgdGhlIFJFU0VSVkVEIGxvY2suIFRoaXMgd2lsbCBibG9jayBhbGwgcmVhZHMgZnJvbQogIC8vICAgaGFwcGVuaW5nLCBhbmQgaXMgZXNzZW50aWFsbHkgdGhlIHNhbWUgYXMgYW4gRVhDTFVTSVZFIGxvY2suCiAgLy8KICAvLyAgICAgKiBUaGUgbWFpbiBwcm9ibGVtIGhlcmUgaXMgd2UgY2FuJ3QgInVwZ3JhZGUiIGEgYHJlYWRvbmx5YAogIC8vICAgICAgIHRyYW5zYWN0aW9uIHRvIGByZWFkd3JpdGVgLCBidXQgbmF0aXZlIHNxbGl0ZSBjYW4gdXBncmFkZSBhCiAgLy8gICAgICAgbG9jayBmcm9tIFNIQVJFRCB0byBSRVNFUlZFRC4gV2UgbmVlZCB0byBzdGFydCBhIG5ldwogIC8vICAgICAgIHRyYW5zYWN0aW9uIHRvIGRvIHNvLCBhbmQgYmVjYXVzZSBvZiB0aGF0IHRoZXJlIG1pZ2h0IGJlCiAgLy8gICAgICAgb3RoZXIgYHJlYWR3cml0ZWAgdHJhbnNhY3Rpb25zIHRoYXQgZ2V0IHJ1biBkdXJpbmcgdGhlCiAgLy8gICAgICAgInVwZ3JhZGUiIHdoaWNoIGludmFsaWRhdGVzIHRoZSB3aG9sZSBsb2NraW5nIHByb2Nlc3MgYW5kCiAgLy8gICAgICAgYW5kIGNvcnJ1cHRzIGRhdGEuCiAgLy8KICAvLyAqIElkZWFsbHksIHdlIGNvdWxkIHRlbGwgc3FsaXRlIHRvIHNraXAgU0hBUkVEIGxvY2tzIGVudGlyZWx5LiBXZQogIC8vICAgZG9uJ3QgbmVlZCB0aGVtIHNpbmNlIHdlIGNhbiByZWx5IG9uIEluZGV4ZWREQidzIHNlbWFudGljcy4KICAvLyAgIFRoZW4gd2hlbiBpdCB3YW50cyB0byBzdGFydCB3cml0aW5nLCB3ZSBnZXQgYSBSRVNFUlZFRCBsb2NrCiAgLy8gICB3aXRob3V0IGhhdmluZyB0byB1cGdyYWRlIGZyb20gU0hBUkVELiBUaGlzIHdvdWxkIHNhdmUgdXMKICAvLyAgIHRoZSBjb3N0IG9mIGEgYHJlYWRvbmx5YCB0cmFuc2FjdGlvbiB3aGVuIHdyaXRpbmc7IHJpZ2h0IG5vdwogIC8vICAgaXQgbXVzdCBvcGVuIGEgYHJlYWRvbmx5YCB0cmFuc2FjdGlvbiBhbmQgdGhlbiBpbW1lZGlhdGVseSBvcGVuCiAgLy8gICBhIGByZWFkd3JpdGVgIHRvIHVwZ3JhZGUgaXQuIEkgdGhvdWdodCBvZiBkZWZlcnJpbmcgb3BlbmluZyB0aGUKICAvLyAgIGByZWFkb25seWAgdHJhbnNhY3Rpb24gdW50aWwgc29tZXRoaW5nIGlzIGFjdHVhbGx5IHJlYWQsIGJ1dAogIC8vICAgdW5mb3J0dW5hdGVseSBzcWxpdGUgb3BlbnMgaXQsIHJlYWRzIHRoZSBmaXJzdCBibG9jaywgYW5kIHRoZW4KICAvLyAgIHVwZ3JhZGVzIGl0LiBTbyB0aGVyZSdzIG5vIHdheSBhcm91bmQgaXQuIChXZSBjYW4ndCBhc3N1bWUgaXQncwogIC8vICAgYSBgcmVhZHdyaXRlYCB0cmFuc2FjdGlvbiBhdCB0aGF0IHBvaW50IHNpbmNlIHRoYXQgd291bGQgYXNzdW1lCiAgLy8gICBhbGwgU0hBUkVEIGxvY2tzIGFyZSBgcmVhZHdyaXRlYCwgcmVtb3ZpbmcgdGhlIHBvc3NpYmlsaXR5IG9mCiAgLy8gICBjb25jdXJyZW50IHJlYWRzKS4KICAvLwogIC8vICogVXBncmFkaW5nIHRvIGFuIEVYQ0xVU0lWRSBsb2NrIGlzIGEgbm9vcCwgc2luY2Ugd2UgdHJlYXQgUkVTRVJWRUQKICAvLyAgIGxvY2tzIGFzIEVYQ0xVU0lWRS4KICBhc3luYyBmdW5jdGlvbiBoYW5kbGVMb2NrKHdyaXRlciwgbmFtZSwgbG9ja1R5cGUpIHsKICAgIC8vIGNvbnNvbGUubG9nKCdsb2NraW5nJywgbmFtZSwgbG9ja1R5cGUsIHBlcmZvcm1hbmNlLm5vdygpKTsKCiAgICBsZXQgdHJhbnMgPSB0cmFuc2FjdGlvbnMuZ2V0KG5hbWUpOwogICAgaWYgKHRyYW5zKSB7CiAgICAgIGlmIChsb2NrVHlwZSA+IHRyYW5zLmxvY2tUeXBlKSB7CiAgICAgICAgLy8gVXBncmFkZSBTSEFSRUQgdG8gRVhDTFVTSVZFCiAgICAgICAgYXNzZXJ0KAogICAgICAgICAgdHJhbnMubG9ja1R5cGUgPT09IExPQ0tfVFlQRVMuU0hBUkVELAogICAgICAgICAgYFVwcmFkaW5nIGxvY2sgdHlwZSBmcm9tICR7dHJhbnMubG9ja1R5cGV9IGlzIGludmFsaWRgCiAgICAgICAgKTsKICAgICAgICBhc3NlcnQoCiAgICAgICAgICBsb2NrVHlwZSA9PT0gTE9DS19UWVBFUy5SRVNFUlZFRCB8fCBsb2NrVHlwZSA9PT0gTE9DS19UWVBFUy5FWENMVVNJVkUsCiAgICAgICAgICBgVXBncmFkaW5nIGxvY2sgdHlwZSB0byAke2xvY2tUeXBlfSBpcyBpbnZhbGlkYAogICAgICAgICk7CgogICAgICAgIGxldCBzdWNjZXNzID0gYXdhaXQgdHJhbnMudXBncmFkZUV4Y2x1c2l2ZSgpOwogICAgICAgIHdyaXRlci5pbnQzMihzdWNjZXNzID8gMCA6IC0xKTsKICAgICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgICAgfSBlbHNlIHsKICAgICAgICAvLyBJZiBub3QgdXBncmFkaW5nIGFuZCB3ZSBhbHJlYWR5IGhhdmUgYSBsb2NrLCBtYWtlIHN1cmUgdGhpcwogICAgICAgIC8vIGlzbid0IGEgZG93bmdyYWRlCiAgICAgICAgYXNzZXJ0KAogICAgICAgICAgdHJhbnMubG9ja1R5cGUgPT09IGxvY2tUeXBlLAogICAgICAgICAgYERvd25ncmFkaW5nIGxvY2sgdG8gJHtsb2NrVHlwZX0gaXMgaW52YWxpZGAKICAgICAgICApOwoKICAgICAgICB3cml0ZXIuaW50MzIoMCk7CiAgICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICAgIH0KICAgIH0gZWxzZSB7CiAgICAgIGFzc2VydCgKICAgICAgICBsb2NrVHlwZSA9PT0gTE9DS19UWVBFUy5TSEFSRUQsCiAgICAgICAgYE5ldyBsb2NrcyBtdXN0IHN0YXJ0IGFzIFNIQVJFRCBpbnN0ZWFkIG9mICR7bG9ja1R5cGV9YAogICAgICApOwoKICAgICAgbGV0IHRyYW5zID0gbmV3IFRyYW5zYWN0aW9uKGF3YWl0IGxvYWREYihuYW1lKSk7CiAgICAgIGlmICgoYXdhaXQgdHJhbnMucHJlZmV0Y2hGaXJzdEJsb2NrKDUwMCkpID09IG51bGwpIDsKCiAgICAgIHRyYW5zYWN0aW9ucy5zZXQobmFtZSwgdHJhbnMpOwoKICAgICAgd3JpdGVyLmludDMyKDApOwogICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgIH0KICB9CgogIGFzeW5jIGZ1bmN0aW9uIGhhbmRsZVVubG9jayh3cml0ZXIsIG5hbWUsIGxvY2tUeXBlKSB7CiAgICAvLyBjb25zb2xlLmxvZygndW5sb2NraW5nJywgbmFtZSwgbG9ja1R5cGUsIHBlcmZvcm1hbmNlLm5vdygpKTsKCiAgICBsZXQgdHJhbnMgPSBnZXRUcmFuc2FjdGlvbihuYW1lKTsKCiAgICBpZiAobG9ja1R5cGUgPT09IExPQ0tfVFlQRVMuU0hBUkVEKSB7CiAgICAgIGlmICh0cmFucyA9PSBudWxsKSB7CiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdVbmxvY2sgZXJyb3IgKFNIQVJFRCk6IG5vIHRyYW5zYWN0aW9uIHJ1bm5pbmcnKTsKICAgICAgfQoKICAgICAgaWYgKHRyYW5zLmxvY2tUeXBlID09PSBMT0NLX1RZUEVTLkVYQ0xVU0lWRSkgewogICAgICAgIHRyYW5zLmRvd25ncmFkZVNoYXJlZCgpOwogICAgICB9CiAgICB9IGVsc2UgaWYgKGxvY2tUeXBlID09PSBMT0NLX1RZUEVTLk5PTkUpIHsKICAgICAgLy8gSSB0aG91Z2h0IHdlIGNvdWxkIGFzc3VtZSBhIGxvY2sgaXMgYWx3YXlzIG9wZW4gd2hlbiBgdW5sb2NrYAogICAgICAvLyBpcyBjYWxsZWQsIGJ1dCBpdCBhbHNvIGNhbGxzIGB1bmxvY2tgIHdoZW4gY2xvc2luZyB0aGUgZmlsZSBubwogICAgICAvLyBtYXR0ZXIgd2hhdC4gRG8gbm90aGluZyBpZiB0aGVyZSdzIG5vIGxvY2sgY3VycmVudGx5CiAgICAgIGlmICh0cmFucykgewogICAgICAgIC8vIFRPRE86IHRoaXMgaXMgd2hlcmUgYW4gZXJyb3IgY291bGQgYnViYmxlIHVwLiBIYW5kbGUgaXQKICAgICAgICBhd2FpdCB0cmFucy53YWl0Q29tcGxldGUoKTsKICAgICAgICB0cmFuc2FjdGlvbnMuZGVsZXRlKG5hbWUpOwogICAgICB9CiAgICB9CgogICAgd3JpdGVyLmludDMyKDApOwogICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgfQoKICBhc3luYyBmdW5jdGlvbiBoYW5kbGVSZWFkKHdyaXRlciwgbmFtZSwgcG9zaXRpb24pIHsKICAgIHJldHVybiB3aXRoVHJhbnNhY3Rpb24obmFtZSwgJ3JlYWRvbmx5JywgYXN5bmMgdHJhbnMgPT4gewogICAgICBsZXQgZGF0YSA9IGF3YWl0IHRyYW5zLnJlYWQocG9zaXRpb24pOwoKICAgICAgaWYgKGRhdGEgPT0gbnVsbCkgewogICAgICAgIHdyaXRlci5ieXRlcyhuZXcgQXJyYXlCdWZmZXIoMCkpOwogICAgICB9IGVsc2UgewogICAgICAgIHdyaXRlci5ieXRlcyhkYXRhKTsKICAgICAgfQogICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgIH0pOwogIH0KCiAgYXN5bmMgZnVuY3Rpb24gaGFuZGxlV3JpdGVzKHdyaXRlciwgbmFtZSwgd3JpdGVzKSB7CiAgICByZXR1cm4gd2l0aFRyYW5zYWN0aW9uKG5hbWUsICdyZWFkd3JpdGUnLCBhc3luYyB0cmFucyA9PiB7CiAgICAgIGF3YWl0IHRyYW5zLmJ1bGtTZXQod3JpdGVzLm1hcCh3ID0+ICh7IGtleTogdy5wb3MsIHZhbHVlOiB3LmRhdGEgfSkpKTsKCiAgICAgIHdyaXRlci5pbnQzMigwKTsKICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICB9KTsKICB9CgogIGFzeW5jIGZ1bmN0aW9uIGhhbmRsZVJlYWRNZXRhKHdyaXRlciwgbmFtZSkgewogICAgcmV0dXJuIHdpdGhUcmFuc2FjdGlvbihuYW1lLCAncmVhZG9ubHknLCBhc3luYyB0cmFucyA9PiB7CiAgICAgIHRyeSB7CiAgICAgICAgY29uc29sZS5sb2coJ1JlYWRpbmcgbWV0YScpOwogICAgICAgIGxldCByZXMgPSBhd2FpdCB0cmFucy5nZXQoLTEpOwogICAgICAgIGNvbnNvbGUubG9nKCdSZWFkaW5nIG1ldGEgKGRvbmUpJywgcmVzKTsKCiAgICAgICAgbGV0IG1ldGEgPSByZXM7CiAgICAgICAgd3JpdGVyLmludDMyKG1ldGEgPyBtZXRhLnNpemUgOiAtMSk7CiAgICAgICAgd3JpdGVyLmludDMyKG1ldGEgPyBtZXRhLmJsb2NrU2l6ZSA6IC0xKTsKICAgICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgICAgfSBjYXRjaCAoZXJyKSB7CiAgICAgICAgY29uc29sZS5sb2coZXJyKTsKICAgICAgICB3cml0ZXIuaW50MzIoLTEpOwogICAgICAgIHdyaXRlci5pbnQzMigtMSk7CiAgICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICAgIH0KICAgIH0pOwogIH0KCiAgYXN5bmMgZnVuY3Rpb24gaGFuZGxlV3JpdGVNZXRhKHdyaXRlciwgbmFtZSwgbWV0YSkgewogICAgcmV0dXJuIHdpdGhUcmFuc2FjdGlvbihuYW1lLCAncmVhZHdyaXRlJywgYXN5bmMgdHJhbnMgPT4gewogICAgICB0cnkgewogICAgICAgIGF3YWl0IHRyYW5zLnNldCh7IGtleTogLTEsIHZhbHVlOiBtZXRhIH0pOwoKICAgICAgICB3cml0ZXIuaW50MzIoMCk7CiAgICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICAgIH0gY2F0Y2ggKGVycikgewogICAgICAgIGNvbnNvbGUubG9nKGVycik7CiAgICAgICAgd3JpdGVyLmludDMyKC0xKTsKICAgICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgICAgfQogICAgfSk7CiAgfQoKICBhc3luYyBmdW5jdGlvbiBoYW5kbGVEZWxldGVGaWxlKHdyaXRlciwgbmFtZSkgewogICAgdHJ5IHsKICAgICAgY2xvc2VEYihuYW1lKTsKCiAgICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHsKICAgICAgICBsZXQgcmVxID0gZ2xvYmFsVGhpcy5pbmRleGVkREIuZGVsZXRlRGF0YWJhc2UobmFtZSk7CiAgICAgICAgcmVxLm9uc3VjY2VzcyA9IHJlc29sdmU7CiAgICAgICAgcmVxLm9uZXJyb3IgPSByZWplY3Q7CiAgICAgIH0pOwoKICAgICAgd3JpdGVyLmludDMyKDApOwogICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgIH0gY2F0Y2ggKGVycikgewogICAgICB3cml0ZXIuaW50MzIoLTEpOwogICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgIH0KICB9CgogIGFzeW5jIGZ1bmN0aW9uIGhhbmRsZUNsb3NlRmlsZSh3cml0ZXIsIG5hbWUpIHsKICAgIGNsb3NlRGIobmFtZSk7CgogICAgd3JpdGVyLmludDMyKDApOwogICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgfQoKICAvLyBgbGlzdGVuYCBjb250aW51YWxseSBsaXN0ZW5zIGZvciByZXF1ZXN0cyB2aWEgdGhlIHNoYXJlZCBidWZmZXIuCiAgLy8gUmlnaHQgbm93IGl0J3MgaW1wbGVtZW50ZWQgaW4gYSB0YWlsLWNhbGwgc3R5bGUgKGBsaXN0ZW5gIGlzCiAgLy8gcmVjdXJzaXZlbHkgY2FsbGVkKSBiZWNhdXNlIEkgdGhvdWdodCB0aGF0IHdhcyBuZWNlc3NhcnkgZm9yCiAgLy8gdmFyaW91cyByZWFzb25zLiBXZSBjYW4gY29udmVydCB0aGlzIHRvIGEgYHdoaWxlKDEpYCBsb29wIHdpdGgKICAvLyBhbmQgdXNlIGBhd2FpdGAgdGhvdWdoCiAgYXN5bmMgZnVuY3Rpb24gbGlzdGVuKHJlYWRlciwgd3JpdGVyKSB7CiAgICBsZXQgbWV0aG9kID0gcmVhZGVyLnN0cmluZygpOwoKICAgIHN3aXRjaCAobWV0aG9kKSB7CiAgICAgIGNhc2UgJ3Byb2ZpbGUtc3RhcnQnOiB7CiAgICAgICAgcmVhZGVyLmRvbmUoKTsKCiAgICAgICAgc3RhcnQoKTsKCiAgICAgICAgd3JpdGVyLmludDMyKDApOwogICAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgICAgIGxpc3RlbihyZWFkZXIsIHdyaXRlcik7CiAgICAgICAgYnJlYWs7CiAgICAgIH0KCiAgICAgIGNhc2UgJ3Byb2ZpbGUtc3RvcCc6IHsKICAgICAgICByZWFkZXIuZG9uZSgpOwoKICAgICAgICBzdG9wKCk7CiAgICAgICAgLy8gVGhlIHBlcmYgbGlicmFyeSBwb3N0cyBhIG1lc3NhZ2U7IG1ha2Ugc3VyZSBpdCBoYXMgdGltZSB0bwogICAgICAgIC8vIGFjdHVhbGx5IHBvc3QgaXQgYmVmb3JlIGJsb2NraW5nIHRoZSB0aHJlYWQgYWdhaW4KICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZShyZXNvbHZlID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgMTAwMCkpOwoKICAgICAgICB3cml0ZXIuaW50MzIoMCk7CiAgICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICAgICAgbGlzdGVuKHJlYWRlciwgd3JpdGVyKTsKICAgICAgICBicmVhazsKICAgICAgfQoKICAgICAgY2FzZSAnd3JpdGVCbG9ja3MnOiB7CiAgICAgICAgbGV0IG5hbWUgPSByZWFkZXIuc3RyaW5nKCk7CiAgICAgICAgbGV0IHdyaXRlcyA9IFtdOwogICAgICAgIHdoaWxlICghcmVhZGVyLmRvbmUoKSkgewogICAgICAgICAgbGV0IHBvcyA9IHJlYWRlci5pbnQzMigpOwogICAgICAgICAgbGV0IGRhdGEgPSByZWFkZXIuYnl0ZXMoKTsKICAgICAgICAgIHdyaXRlcy5wdXNoKHsgcG9zLCBkYXRhIH0pOwogICAgICAgIH0KCiAgICAgICAgYXdhaXQgaGFuZGxlV3JpdGVzKHdyaXRlciwgbmFtZSwgd3JpdGVzKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICdyZWFkQmxvY2snOiB7CiAgICAgICAgbGV0IG5hbWUgPSByZWFkZXIuc3RyaW5nKCk7CiAgICAgICAgbGV0IHBvcyA9IHJlYWRlci5pbnQzMigpOwogICAgICAgIHJlYWRlci5kb25lKCk7CgogICAgICAgIGF3YWl0IGhhbmRsZVJlYWQod3JpdGVyLCBuYW1lLCBwb3MpOwogICAgICAgIGxpc3RlbihyZWFkZXIsIHdyaXRlcik7CiAgICAgICAgYnJlYWs7CiAgICAgIH0KCiAgICAgIGNhc2UgJ3JlYWRNZXRhJzogewogICAgICAgIGxldCBuYW1lID0gcmVhZGVyLnN0cmluZygpOwogICAgICAgIHJlYWRlci5kb25lKCk7CiAgICAgICAgYXdhaXQgaGFuZGxlUmVhZE1ldGEod3JpdGVyLCBuYW1lKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICd3cml0ZU1ldGEnOiB7CiAgICAgICAgbGV0IG5hbWUgPSByZWFkZXIuc3RyaW5nKCk7CiAgICAgICAgbGV0IHNpemUgPSByZWFkZXIuaW50MzIoKTsKICAgICAgICBsZXQgYmxvY2tTaXplID0gcmVhZGVyLmludDMyKCk7CiAgICAgICAgcmVhZGVyLmRvbmUoKTsKICAgICAgICBhd2FpdCBoYW5kbGVXcml0ZU1ldGEod3JpdGVyLCBuYW1lLCB7IHNpemUsIGJsb2NrU2l6ZSB9KTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICdkZWxldGVGaWxlJzogewogICAgICAgIGxldCBuYW1lID0gcmVhZGVyLnN0cmluZygpOwogICAgICAgIHJlYWRlci5kb25lKCk7CgogICAgICAgIGF3YWl0IGhhbmRsZURlbGV0ZUZpbGUod3JpdGVyLCBuYW1lKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICdjbG9zZUZpbGUnOiB7CiAgICAgICAgbGV0IG5hbWUgPSByZWFkZXIuc3RyaW5nKCk7CiAgICAgICAgcmVhZGVyLmRvbmUoKTsKCiAgICAgICAgYXdhaXQgaGFuZGxlQ2xvc2VGaWxlKHdyaXRlciwgbmFtZSk7CiAgICAgICAgbGlzdGVuKHJlYWRlciwgd3JpdGVyKTsKICAgICAgICBicmVhazsKICAgICAgfQoKICAgICAgY2FzZSAnbG9ja0ZpbGUnOiB7CiAgICAgICAgbGV0IG5hbWUgPSByZWFkZXIuc3RyaW5nKCk7CiAgICAgICAgbGV0IGxvY2tUeXBlID0gcmVhZGVyLmludDMyKCk7CiAgICAgICAgcmVhZGVyLmRvbmUoKTsKCiAgICAgICAgYXdhaXQgaGFuZGxlTG9jayh3cml0ZXIsIG5hbWUsIGxvY2tUeXBlKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICd1bmxvY2tGaWxlJzogewogICAgICAgIGxldCBuYW1lID0gcmVhZGVyLnN0cmluZygpOwogICAgICAgIGxldCBsb2NrVHlwZSA9IHJlYWRlci5pbnQzMigpOwogICAgICAgIHJlYWRlci5kb25lKCk7CgogICAgICAgIGF3YWl0IGhhbmRsZVVubG9jayh3cml0ZXIsIG5hbWUsIGxvY2tUeXBlKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBkZWZhdWx0OgogICAgICAgIHRocm93IG5ldyBFcnJvcignVW5rbm93biBtZXRob2Q6ICcgKyBtZXRob2QpOwogICAgfQogIH0KCiAgc2VsZi5vbm1lc3NhZ2UgPSBtc2cgPT4gewogICAgc3dpdGNoIChtc2cuZGF0YS50eXBlKSB7CiAgICAgIGNhc2UgJ2luaXQnOiB7CiAgICAgICAgcG9zdE1lc3NhZ2UoeyB0eXBlOiAnX19hYnN1cmQ6d29ya2VyLXJlYWR5JyB9KTsKICAgICAgICBsZXQgW2FyZ0J1ZmZlciwgcmVzdWx0QnVmZmVyXSA9IG1zZy5kYXRhLmJ1ZmZlcnM7CiAgICAgICAgbGV0IHJlYWRlciA9IG5ldyBSZWFkZXIoYXJnQnVmZmVyLCB7IG5hbWU6ICdhcmdzJywgZGVidWc6IGZhbHNlIH0pOwogICAgICAgIGxldCB3cml0ZXIgPSBuZXcgV3JpdGVyKHJlc3VsdEJ1ZmZlciwgeyBuYW1lOiAncmVzdWx0cycsIGRlYnVnOiBmYWxzZSB9KTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CiAgICB9CiAgfTsKCn0oKSk7Cgo=', null, false); -/* eslint-enable */ - -export default WorkerFactory; diff --git a/dist2/perf/indexeddb-main-thread.js b/dist2/perf/indexeddb-main-thread.js deleted file mode 100644 index 341b7d3..0000000 --- a/dist2/perf/indexeddb-main-thread.js +++ /dev/null @@ -1,73 +0,0 @@ -// The reason for this strange abstraction is because we can't rely on -// nested worker support (Safari doesn't support it). We need to proxy -// creating a child worker through the main thread, and this requires -// a bit of glue code. We don't want to duplicate this code in each -// backend that needs it, so this module abstracts it out. It has to -// have a strange shape because we don't want to eagerly bundle the -// backend code, so users of this code need to pass an `() => -// import('worker.js')` expression to get the worker module to run. - -function isWorker() { - return ( - typeof WorkerGlobalScope !== 'undefined' && - self instanceof WorkerGlobalScope - ); -} - -function makeStartWorkerFromMain(getModule) { - return (argBuffer, resultBuffer, parentWorker) => { - if (isWorker()) { - throw new Error( - '`startWorkerFromMain` should only be called from the main thread' - ); - } - - if (typeof Worker === 'undefined') { - // We're on the main thread? Weird: it doesn't have workers - throw new Error( - 'Web workers not available. sqlite3 requires web workers to work.' - ); - } - - getModule().then(({ default: BackendWorker }) => { - let worker = new BackendWorker(); - - worker.postMessage({ type: 'init', buffers: [argBuffer, resultBuffer] }); - - worker.addEventListener('message', msg => { - // Forward any messages to the worker that's supposed - // to be the parent - parentWorker.postMessage(msg.data); - }); - }); - }; -} - -let hasInitialized = false; - -function makeInitBackend(spawnEventName, getModule) { - const startWorkerFromMain = makeStartWorkerFromMain(getModule); - - return worker => { - if (hasInitialized) { - return; - } - hasInitialized = true; - - worker.addEventListener('message', e => { - switch (e.data.type) { - case spawnEventName: - startWorkerFromMain(e.data.argBuffer, e.data.resultBuffer, worker); - break; - } - }); - }; -} - -// Use the generic main thread module to create our indexeddb worker -// proxy -const initBackend = makeInitBackend('__absurd:spawn-idb-worker', () => - import('./indexeddb-main-thread-worker-754bfead.js') -); - -export { initBackend }; diff --git a/package.json b/package.json index f8d0d24..b75ee5b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "absurd-sql.js-backend", - "version": "0.0.23", + "version": "0.0.27", "main": "./dist/index.js", "scripts": { "build": "rm -rf dist && rollup -c rollup.config.js", @@ -19,7 +19,7 @@ "fast-check": "^2.17.0", "html-webpack-plugin": "^5.3.2", "jest": "^27.0.5", - "perf-deets": "^1.0.10", + "perf-deets": "^1.0.12", "rollup": "^2.53.1", "rollup-plugin-extensions": "^0.1.0", "rollup-plugin-web-worker-loader": "^1.6.1", @@ -29,5 +29,10 @@ "webpack-dev-server": "^3.11.2", "worker-loader": "^3.0.8", "rollup-plugin-terser": "^7.0.2" - } + }, + "files": [ + "README.md", + "dist/**/*", + "src/**/*" + ] } diff --git a/rollup.config.js b/rollup.config.js index 8feb12e..492e425 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -21,7 +21,7 @@ function getConfig(entry, filename, perf) { !perf && alias({ entries: { - 'perf-deets': path.resolve(__dirname, './src/perf-deets-noop.js') + 'perf-deets': path.resolve(__dirname, './node_modules/perf-deets/noop.js') } }), webWorkerLoader({ diff --git a/src/perf-deets-noop.js b/src/perf-deets-noop.js deleted file mode 100644 index d92d786..0000000 --- a/src/perf-deets-noop.js +++ /dev/null @@ -1,5 +0,0 @@ -export function start() {} -export function stop() {} -export function record() {} -export function endRecording() {} -export function count() {} diff --git a/yarn.lock b/yarn.lock index 84cdfba..531b952 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4568,10 +4568,10 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= -perf-deets@^1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/perf-deets/-/perf-deets-1.0.10.tgz#daaf134692a2577e2cb4575da7cd2ab87deb08a3" - integrity sha512-7lCJX4LFFIl2o7Ckgq0YvsWyU5ZCABDKXMIr3Da/DpnOft/DBZLmKQSZV9CCeR7yFUbtROM/cQl8btOnT5j0nw== +perf-deets@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/perf-deets/-/perf-deets-1.0.12.tgz#5144c11692f49ebd7891c55989bfbb31c693906a" + integrity sha512-yCMG6M8noEZeKri/+IeTTH3VkXwmPF69Z9bxWK5gbeCyCZUwQ9DXUtL0XytYC1Mge/CJXpA4k0dIpVSZKjqJZA== dependencies: "@observablehq/plot" "^0.1.0"