From 88da477edd61f77fd912f30b5c6b4f88ec061039 Mon Sep 17 00:00:00 2001 From: James Long Date: Mon, 2 Aug 2021 21:23:02 -0400 Subject: [PATCH] Handle closing/deleting; make perf builds --- dist/index.js | 2 +- dist/indexeddb-backend.js | 19 +- dist/memory-backend.js | 5 + dist/perf/index.js | 303 +++++++ dist/perf/indexeddb-backend.js | 1028 +++++++++++++++++++++++ dist/perf/memory-backend.js | 570 +++++++++++++ package.json | 6 +- rollup.config.js | 11 +- src/blocked-file.js | 1 + src/examples/large-data/index.html | 1 + src/examples/large-data/main.js | 8 +- src/examples/large-data/main.worker.js | 24 +- src/examples/webpack.config.js | 2 +- src/index.js | 5 - src/indexeddb/backend.js | 36 +- src/indexeddb/main-thread.js | 267 ++++++ src/indexeddb/start-indexeddb-worker.js | 73 -- src/indexeddb/worker.js | 41 +- src/memory/backend.js | 4 + src/perf-frontend.dev.js | 202 +++++ src/perf-frontend.js | 2 + src/perf.dev.js | 73 +- yarn.lock | 328 +++++++- 23 files changed, 2850 insertions(+), 161 deletions(-) create mode 100644 dist/perf/index.js create mode 100644 dist/perf/indexeddb-backend.js create mode 100644 dist/perf/memory-backend.js create mode 100644 src/indexeddb/main-thread.js delete mode 100644 src/indexeddb/start-indexeddb-worker.js create mode 100644 src/perf-frontend.dev.js create mode 100644 src/perf-frontend.js diff --git a/dist/index.js b/dist/index.js index bd01c67..83903a8 100644 --- a/dist/index.js +++ b/dist/index.js @@ -218,7 +218,7 @@ function createBase64WorkerFactory(base64, sourcemapArg, enableUnicodeArg) { }; } -var WorkerFactory = createBase64WorkerFactory('Lyogcm9sbHVwLXBsdWdpbi13ZWItd29ya2VyLWxvYWRlciAqLwooZnVuY3Rpb24gKCkgewogICd1c2Ugc3RyaWN0JzsKCiAgbGV0IEZJTkFMSVpFRCA9IDB4ZGVhZGJlZWY7CgogIGxldCBXUklURUFCTEUgPSAwOwogIGxldCBSRUFEQUJMRSA9IDE7CgogIGNsYXNzIFJlYWRlciB7CiAgICBjb25zdHJ1Y3RvcigKICAgICAgYnVmZmVyLAogICAgICB7IGluaXRpYWxPZmZzZXQgPSA0LCB1c2VBdG9taWNzID0gdHJ1ZSwgc3RyZWFtID0gdHJ1ZSwgZGVidWcsIG5hbWUgfSA9IHt9CiAgICApIHsKICAgICAgdGhpcy5idWZmZXIgPSBidWZmZXI7CiAgICAgIHRoaXMuYXRvbWljVmlldyA9IG5ldyBJbnQzMkFycmF5KGJ1ZmZlcik7CiAgICAgIHRoaXMub2Zmc2V0ID0gaW5pdGlhbE9mZnNldDsKICAgICAgdGhpcy51c2VBdG9taWNzID0gdXNlQXRvbWljczsKICAgICAgdGhpcy5zdHJlYW0gPSBzdHJlYW07CiAgICAgIHRoaXMuZGVidWcgPSBkZWJ1ZzsKICAgICAgdGhpcy5uYW1lID0gbmFtZTsKICAgIH0KCiAgICBsb2coLi4uYXJncykgewogICAgICBpZiAodGhpcy5kZWJ1ZykgewogICAgICAgIGNvbnNvbGUubG9nKGBbcmVhZGVyOiAke3RoaXMubmFtZX1dYCwgLi4uYXJncyk7CiAgICAgIH0KICAgIH0KCiAgICB3YWl0V3JpdGUobmFtZSkgewogICAgICBpZiAodGhpcy51c2VBdG9taWNzKSB7CiAgICAgICAgdGhpcy5sb2coYHdhaXRpbmcgZm9yICR7bmFtZX1gKTsKCiAgICAgICAgd2hpbGUgKEF0b21pY3MubG9hZCh0aGlzLmF0b21pY1ZpZXcsIDApID09PSBXUklURUFCTEUpIHsKICAgICAgICAgIC8vIGNvbnNvbGUubG9nKCd3YWl0aW5nIGZvciB3cml0ZS4uLicpOwogICAgICAgICAgQXRvbWljcy53YWl0KHRoaXMuYXRvbWljVmlldywgMCwgV1JJVEVBQkxFLCA1MDApOwogICAgICAgIH0KCiAgICAgICAgdGhpcy5sb2coYHJlc3VtZWQgZm9yICR7bmFtZX1gKTsKICAgICAgfSBlbHNlIHsKICAgICAgICBpZiAodGhpcy5hdG9taWNWaWV3WzBdICE9PSBSRUFEQUJMRSkgewogICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdgd2FpdFdyaXRlYCBleHBlY3RlZCBhcnJheSB0byBiZSByZWFkYWJsZScpOwogICAgICAgIH0KICAgICAgfQogICAgfQoKICAgIGZsaXAoKSB7CiAgICAgIHRoaXMubG9nKCdmbGlwJyk7CiAgICAgIGlmICh0aGlzLnVzZUF0b21pY3MpIHsKICAgICAgICBsZXQgcHJldiA9IEF0b21pY3MuY29tcGFyZUV4Y2hhbmdlKAogICAgICAgICAgdGhpcy5hdG9taWNWaWV3LAogICAgICAgICAgMCwKICAgICAgICAgIFJFQURBQkxFLAogICAgICAgICAgV1JJVEVBQkxFCiAgICAgICAgKTsKCiAgICAgICAgaWYgKHByZXYgIT09IFJFQURBQkxFKSB7CiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1JlYWQgZGF0YSBvdXQgb2Ygc3luYyEgVGhpcyBpcyBkaXNhc3Ryb3VzJyk7CiAgICAgICAgfQoKICAgICAgICBBdG9taWNzLm5vdGlmeSh0aGlzLmF0b21pY1ZpZXcsIDApOwogICAgICB9IGVsc2UgewogICAgICAgIHRoaXMuYXRvbWljVmlld1swXSA9IFdSSVRFQUJMRTsKICAgICAgfQoKICAgICAgdGhpcy5vZmZzZXQgPSA0OwogICAgfQoKICAgIGRvbmUoKSB7CiAgICAgIHRoaXMud2FpdFdyaXRlKCdkb25lJyk7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0KTsKICAgICAgbGV0IGRvbmUgPSBkYXRhVmlldy5nZXRVaW50MzIoMCkgPT09IEZJTkFMSVpFRDsKCiAgICAgIGlmIChkb25lKSB7CiAgICAgICAgdGhpcy5sb2coJ2RvbmUnKTsKICAgICAgICB0aGlzLmZsaXAoKTsKICAgICAgfQoKICAgICAgcmV0dXJuIGRvbmU7CiAgICB9CgogICAgcGVlayhmbikgewogICAgICB0aGlzLnBlZWtPZmZzZXQgPSB0aGlzLm9mZnNldDsKICAgICAgbGV0IHJlcyA9IGZuKCk7CiAgICAgIHRoaXMub2Zmc2V0ID0gdGhpcy5wZWVrT2Zmc2V0OwogICAgICB0aGlzLnBlZWtPZmZzZXQgPSBudWxsOwogICAgICByZXR1cm4gcmVzOwogICAgfQoKICAgIHN0cmluZygpIHsKICAgICAgdGhpcy53YWl0V3JpdGUoJ3N0cmluZycpOwoKICAgICAgbGV0IGJ5dGVMZW5ndGggPSB0aGlzLl9pbnQzMigpOwogICAgICBsZXQgbGVuZ3RoID0gYnl0ZUxlbmd0aCAvIDI7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0LCBieXRlTGVuZ3RoKTsKICAgICAgbGV0IGNoYXJzID0gW107CiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbGVuZ3RoOyBpKyspIHsKICAgICAgICBjaGFycy5wdXNoKGRhdGFWaWV3LmdldFVpbnQxNihpICogMikpOwogICAgICB9CiAgICAgIGxldCBzdHIgPSBTdHJpbmcuZnJvbUNoYXJDb2RlLmFwcGx5KG51bGwsIGNoYXJzKTsKICAgICAgdGhpcy5sb2coJ3N0cmluZycsIHN0cik7CgogICAgICB0aGlzLm9mZnNldCArPSBieXRlTGVuZ3RoOwoKICAgICAgaWYgKHRoaXMucGVla09mZnNldCA9PSBudWxsKSB7CiAgICAgICAgdGhpcy5mbGlwKCk7CiAgICAgIH0KICAgICAgcmV0dXJuIHN0cjsKICAgIH0KCiAgICBfaW50MzIoKSB7CiAgICAgIGxldCBieXRlTGVuZ3RoID0gNDsKCiAgICAgIGxldCBkYXRhVmlldyA9IG5ldyBEYXRhVmlldyh0aGlzLmJ1ZmZlciwgdGhpcy5vZmZzZXQpOwogICAgICBsZXQgbnVtID0gZGF0YVZpZXcuZ2V0SW50MzIoKTsKICAgICAgdGhpcy5sb2coJ19pbnQzMicsIG51bSk7CgogICAgICB0aGlzLm9mZnNldCArPSBieXRlTGVuZ3RoOwogICAgICByZXR1cm4gbnVtOwogICAgfQoKICAgIGludDMyKCkgewogICAgICB0aGlzLndhaXRXcml0ZSgnaW50MzInKTsKICAgICAgbGV0IG51bSA9IHRoaXMuX2ludDMyKCk7CiAgICAgIHRoaXMubG9nKCdpbnQzMicsIG51bSk7CgogICAgICBpZiAodGhpcy5wZWVrT2Zmc2V0ID09IG51bGwpIHsKICAgICAgICB0aGlzLmZsaXAoKTsKICAgICAgfQogICAgICByZXR1cm4gbnVtOwogICAgfQoKICAgIGJ5dGVzKCkgewogICAgICB0aGlzLndhaXRXcml0ZSgnYnl0ZXMnKTsKCiAgICAgIGxldCBieXRlTGVuZ3RoID0gdGhpcy5faW50MzIoKTsKCiAgICAgIGxldCBieXRlcyA9IG5ldyBBcnJheUJ1ZmZlcihieXRlTGVuZ3RoKTsKICAgICAgbmV3IFVpbnQ4QXJyYXkoYnl0ZXMpLnNldCgKICAgICAgICBuZXcgVWludDhBcnJheSh0aGlzLmJ1ZmZlciwgdGhpcy5vZmZzZXQsIGJ5dGVMZW5ndGgpCiAgICAgICk7CiAgICAgIHRoaXMubG9nKCdieXRlcycsIGJ5dGVzKTsKCiAgICAgIHRoaXMub2Zmc2V0ICs9IGJ5dGVMZW5ndGg7CgogICAgICBpZiAodGhpcy5wZWVrT2Zmc2V0ID09IG51bGwpIHsKICAgICAgICB0aGlzLmZsaXAoKTsKICAgICAgfQogICAgICByZXR1cm4gYnl0ZXM7CiAgICB9CiAgfQoKICBjbGFzcyBXcml0ZXIgewogICAgY29uc3RydWN0b3IoCiAgICAgIGJ1ZmZlciwKICAgICAgeyBpbml0aWFsT2Zmc2V0ID0gNCwgdXNlQXRvbWljcyA9IHRydWUsIHN0cmVhbSA9IHRydWUsIGRlYnVnLCBuYW1lIH0gPSB7fQogICAgKSB7CiAgICAgIHRoaXMuYnVmZmVyID0gYnVmZmVyOwogICAgICB0aGlzLmF0b21pY1ZpZXcgPSBuZXcgSW50MzJBcnJheShidWZmZXIpOwogICAgICB0aGlzLm9mZnNldCA9IGluaXRpYWxPZmZzZXQ7CiAgICAgIHRoaXMudXNlQXRvbWljcyA9IHVzZUF0b21pY3M7CiAgICAgIHRoaXMuc3RyZWFtID0gc3RyZWFtOwoKICAgICAgdGhpcy5kZWJ1ZyA9IGRlYnVnOwogICAgICB0aGlzLm5hbWUgPSBuYW1lOwoKICAgICAgaWYgKHRoaXMudXNlQXRvbWljcykgewogICAgICAgIC8vIFRoZSBidWZmZXIgc3RhcnRzIG91dCBhcyB3cml0ZWFibGUKICAgICAgICBBdG9taWNzLnN0b3JlKHRoaXMuYXRvbWljVmlldywgMCwgV1JJVEVBQkxFKTsKICAgICAgfSBlbHNlIHsKICAgICAgICB0aGlzLmF0b21pY1ZpZXdbMF0gPSBXUklURUFCTEU7CiAgICAgIH0KICAgIH0KCiAgICBsb2coLi4uYXJncykgewogICAgICBpZiAodGhpcy5kZWJ1ZykgewogICAgICAgIGNvbnNvbGUubG9nKGBbd3JpdGVyOiAke3RoaXMubmFtZX1dYCwgLi4uYXJncyk7CiAgICAgIH0KICAgIH0KCiAgICB3YWl0UmVhZChuYW1lKSB7CiAgICAgIGlmICh0aGlzLnVzZUF0b21pY3MpIHsKICAgICAgICB0aGlzLmxvZyhgd2FpdGluZyBmb3IgJHtuYW1lfWApOwogICAgICAgIC8vIFN3aXRjaCB0byB3cml0YWJsZQogICAgICAgIC8vIEF0b21pY3Muc3RvcmUodGhpcy5hdG9taWNWaWV3LCAwLCAxKTsKCiAgICAgICAgbGV0IHByZXYgPSBBdG9taWNzLmNvbXBhcmVFeGNoYW5nZSgKICAgICAgICAgIHRoaXMuYXRvbWljVmlldywKICAgICAgICAgIDAsCiAgICAgICAgICBXUklURUFCTEUsCiAgICAgICAgICBSRUFEQUJMRQogICAgICAgICk7CgogICAgICAgIGlmIChwcmV2ICE9PSBXUklURUFCTEUpIHsKICAgICAgICAgIHRocm93IG5ldyBFcnJvcigKICAgICAgICAgICAgJ1dyb3RlIHNvbWV0aGluZyBpbnRvIHVud3JpdGFibGUgYnVmZmVyISBUaGlzIGlzIGRpc2FzdHJvdXMnCiAgICAgICAgICApOwogICAgICAgIH0KCiAgICAgICAgQXRvbWljcy5ub3RpZnkodGhpcy5hdG9taWNWaWV3LCAwKTsKCiAgICAgICAgd2hpbGUgKEF0b21pY3MubG9hZCh0aGlzLmF0b21pY1ZpZXcsIDApID09PSBSRUFEQUJMRSkgewogICAgICAgICAgLy8gY29uc29sZS5sb2coJ3dhaXRpbmcgdG8gYmUgcmVhZC4uLicpOwogICAgICAgICAgQXRvbWljcy53YWl0KHRoaXMuYXRvbWljVmlldywgMCwgUkVBREFCTEUsIDUwMCk7CiAgICAgICAgfQoKICAgICAgICB0aGlzLmxvZyhgcmVzdW1lZCBmb3IgJHtuYW1lfWApOwogICAgICB9IGVsc2UgewogICAgICAgIHRoaXMuYXRvbWljVmlld1swXSA9IFJFQURBQkxFOwogICAgICB9CgogICAgICB0aGlzLm9mZnNldCA9IDQ7CiAgICB9CgogICAgZmluYWxpemUoKSB7CiAgICAgIHRoaXMubG9nKCdmaW5hbGl6aW5nJyk7CiAgICAgIGxldCBkYXRhVmlldyA9IG5ldyBEYXRhVmlldyh0aGlzLmJ1ZmZlciwgdGhpcy5vZmZzZXQpOwogICAgICBkYXRhVmlldy5zZXRVaW50MzIoMCwgRklOQUxJWkVEKTsKICAgICAgdGhpcy53YWl0UmVhZCgnZmluYWxpemUnKTsKICAgIH0KCiAgICBzdHJpbmcoc3RyKSB7CiAgICAgIHRoaXMubG9nKCdzdHJpbmcnLCBzdHIpOwoKICAgICAgbGV0IGJ5dGVMZW5ndGggPSBzdHIubGVuZ3RoICogMjsKICAgICAgdGhpcy5faW50MzIoYnl0ZUxlbmd0aCk7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0LCBieXRlTGVuZ3RoKTsKICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBzdHIubGVuZ3RoOyBpKyspIHsKICAgICAgICBkYXRhVmlldy5zZXRVaW50MTYoaSAqIDIsIHN0ci5jaGFyQ29kZUF0KGkpKTsKICAgICAgfQoKICAgICAgdGhpcy5vZmZzZXQgKz0gYnl0ZUxlbmd0aDsKICAgICAgdGhpcy53YWl0UmVhZCgnc3RyaW5nJyk7CiAgICB9CgogICAgX2ludDMyKG51bSkgewogICAgICBsZXQgYnl0ZUxlbmd0aCA9IDQ7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0KTsKICAgICAgZGF0YVZpZXcuc2V0SW50MzIoMCwgbnVtKTsKCiAgICAgIHRoaXMub2Zmc2V0ICs9IGJ5dGVMZW5ndGg7CiAgICB9CgogICAgaW50MzIobnVtKSB7CiAgICAgIHRoaXMubG9nKCdpbnQzMicsIG51bSk7CiAgICAgIHRoaXMuX2ludDMyKG51bSk7CiAgICAgIHRoaXMud2FpdFJlYWQoJ2ludDMyJyk7CiAgICB9CgogICAgYnl0ZXMoYnVmZmVyKSB7CiAgICAgIHRoaXMubG9nKCdieXRlcycsIGJ1ZmZlcik7CgogICAgICBsZXQgYnl0ZUxlbmd0aCA9IGJ1ZmZlci5ieXRlTGVuZ3RoOwogICAgICB0aGlzLl9pbnQzMihieXRlTGVuZ3RoKTsKICAgICAgbmV3IFVpbnQ4QXJyYXkodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0KS5zZXQobmV3IFVpbnQ4QXJyYXkoYnVmZmVyKSk7CgogICAgICB0aGlzLm9mZnNldCArPSBieXRlTGVuZ3RoOwogICAgICB0aGlzLndhaXRSZWFkKCdieXRlcycpOwogICAgfQogIH0KCiAgLy8gTm9vcHMgaW4gcHJvZAogIGFzeW5jIGZ1bmN0aW9uIGVuZCgpIHt9CgogIGxldCBpc1Byb2JhYmx5U2FmYXJpID0gL14oKD8hY2hyb21lfGFuZHJvaWQpLikqc2FmYXJpL2kudGVzdCgKICAgIG5hdmlnYXRvci51c2VyQWdlbnQKICApOwoKICBsZXQgb3BlbkRicyA9IG5ldyBNYXAoKTsKICBsZXQgdHJhbnNhY3Rpb25zID0gbmV3IE1hcCgpOwoKICBmdW5jdGlvbiBhc3NlcnQoY29uZCwgbXNnKSB7CiAgICBpZiAoIWNvbmQpIHsKICAgICAgdGhyb3cgbmV3IEVycm9yKG1zZyk7CiAgICB9CiAgfQoKICBsZXQgTE9DS19UWVBFUyA9IHsKICAgIE5PTkU6IDAsCiAgICBTSEFSRUQ6IDEsCiAgICBSRVNFUlZFRDogMiwKICAgIFBFTkRJTkc6IDMsCiAgICBFWENMVVNJVkU6IDQKICB9OwoKICAvLyBXZSB1c2UgbG9uZy1saXZlZCB0cmFuc2FjdGlvbnMsIGFuZCBgVHJhbnNhY3Rpb25gIGtlZXBzIHRoZQogIC8vIHRyYW5zYWN0aW9uIHN0YXRlLiBJdCBpbXBsZW1lbnRzIGFuIG9wdGltYWwgd2F5IHRvIHBlcmZvcm0KICAvLyByZWFkL3dyaXRlcyB3aXRoIGtub3dsZWRnZSBvZiBob3cgc3FsaXRlIGFza3MgZm9yIHRoZW0sIGFuZCBhbHNvCiAgLy8gaW1wbGVtZW50cyBhIGxvY2tpbmcgbWVjaGFuaXNtIHRoYXQgbWFwcyB0byBob3cgc3FsaXRlIGxvY2tzIHdvcmsuCiAgY2xhc3MgVHJhbnNhY3Rpb24gewogICAgY29uc3RydWN0b3IoZGIsIGluaXRpYWxNb2RlID0gJ3JlYWRvbmx5JykgewogICAgICB0aGlzLmRiID0gZGI7CiAgICAgIHRoaXMudHJhbnMgPSB0aGlzLmRiLnRyYW5zYWN0aW9uKFsnZGF0YSddLCBpbml0aWFsTW9kZSk7CiAgICAgIHRoaXMuc3RvcmUgPSB0aGlzLnRyYW5zLm9iamVjdFN0b3JlKCdkYXRhJyk7CiAgICAgIHRoaXMubG9ja1R5cGUgPQogICAgICAgIGluaXRpYWxNb2RlID09PSAncmVhZG9ubHknID8gTE9DS19UWVBFUy5TSEFSRUQgOiBMT0NLX1RZUEVTLkVYQ0xVU0lWRTsKCiAgICAgIC8vIFRoZXJlIGlzIG5vIG5lZWQgZm9yIHVzIHRvIGNhY2hlIGJsb2Nrcy4gVXNlIHNxbGl0ZSdzCiAgICAgIC8vIGBjYWNoZV9zaXplYCBmb3IgdGhhdCBhbmQgaXQgd2lsbCBhdXRvbWF0aWNhbGx5IGRvIGl0LiBIb3dldmVyLAogICAgICAvLyB3ZSBkbyBzdGlsbCBrZWVwIGEgY2FjaGUgb2YgdGhlIGZpcnN0IGJsb2NrIGZvciB0aGUgZHVyYXRpb24gb2YKICAgICAgLy8gdGhpcyB0cmFuc2FjdGlvbiBiZWNhdXNlIG9mIGhvdyBsb2NraW5nIHdvcmtzOyB0aGlzIGF2b2lkcyBhCiAgICAgIC8vIGZldyBleHRyYSByZWFkcyBhbmQgYWxsb3dzIHVzIHRvIGRldGVjdCBjaGFuZ2VzIGR1cmluZwogICAgICAvLyB1cGdyYWRpbmcgKHNlZSBgdXBncmFkZUV4Y2x1c2l2ZWApCiAgICAgIHRoaXMuY2FjaGVkRmlyc3RCbG9jayA9IG51bGw7CgogICAgICB0aGlzLmN1cnNvciA9IG51bGw7CiAgICAgIHRoaXMucHJldlJlYWRzID0gbnVsbDsKICAgIH0KCiAgICBhc3luYyBwcmVmZXRjaEZpcnN0QmxvY2sodGltZW91dCkgewogICAgICAvLyBUT0RPOiBpbXBsZW1lbnQgdGltZW91dAoKICAgICAgLy8gR2V0IHRoZSBmaXJzdCBibG9jayBhbmQgY2FjaGUgaXQKICAgICAgbGV0IGJsb2NrID0gYXdhaXQgdGhpcy5nZXQoMCk7CiAgICAgIHRoaXMuY2FjaGVkRmlyc3RCbG9jayA9IGJsb2NrOwogICAgICByZXR1cm4gYmxvY2s7CiAgICB9CgogICAgYXN5bmMgd2FpdENvbXBsZXRlKCkgewogICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4gewogICAgICAgIC8vIEVhZ2VybHkgY29tbWl0IGl0IGZvciBiZXR0ZXIgcGVyZi4gTm90ZSB0aGF0ICoqdGhpcyBhc3N1bWVzCiAgICAgICAgLy8gdGhlIHRyYW5zYWN0aW9uIGlzIG9wZW4qKiBhcyBgY29tbWl0YCB3aWxsIHRocm93IGFuIGVycm9yIGlmCiAgICAgICAgLy8gaXQncyBhbHJlYWR5IGNsb3NlZCAod2hpY2ggc2hvdWxkIG5ldmVyIGJlIHRoZSBjYXNlIGZvciB1cykKICAgICAgICB0aGlzLmNvbW1pdCgpOwoKICAgICAgICBpZiAodGhpcy5sb2NrVHlwZSA9PT0gTE9DS19UWVBFUy5FWENMVVNJVkUpIHsKICAgICAgICAgIC8vIFdhaXQgdW50aWwgYWxsIHdyaXRlcyBhcmUgY29tbWl0dGVkCiAgICAgICAgICB0aGlzLnRyYW5zLm9uY29tcGxldGUgPSBlID0+IHJlc29sdmUoKTsKCiAgICAgICAgICAvLyBUT0RPOiBJcyBpdCBPSyB0byBhZGQgdGhpcyBsYXRlciwgYWZ0ZXIgYW4gZXJyb3IgbWlnaHQgaGF2ZQogICAgICAgICAgLy8gaGFwcGVuZWQ/IFdpbGwgaXQgaG9sZCB0aGUgZXJyb3IgYW5kIGZpcmUgdGhpcyB3aGVuIHdlCiAgICAgICAgICAvLyBhdHRhY2hlZCBpdD8gV2UgbWlnaHQgd2FudCB0byBlYWdlcmx5IGNyZWF0ZSB0aGUgcHJvbWlzZQogICAgICAgICAgLy8gd2hlbiBjcmVhdGluZyB0aGUgdHJhbnNhY3Rpb24gYW5kIHJldHVybiBpdCBoZXJlCiAgICAgICAgICB0aGlzLnRyYW5zLm9uZXJyb3IgPSBlID0+IHJlamVjdChlKTsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgaWYgKGlzUHJvYmFibHlTYWZhcmkpIHsKICAgICAgICAgICAgLy8gU2FmYXJpIGhhcyBhIGJ1ZyB3aGVyZSBzb21ldGltZXMgdGhlIElEQiBnZXRzIGJsb2NrZWQKICAgICAgICAgICAgLy8gcGVybWFuZW50bHkgaWYgeW91IHJlZnJlc2ggdGhlIHBhZ2Ugd2l0aCBhbiBvcGVuCiAgICAgICAgICAgIC8vIHRyYW5zYWN0aW9uLiBZb3UgaGF2ZSB0byByZXN0YXJ0IHRoZSBicm93c2VyIHRvIGZpeCBpdC4KICAgICAgICAgICAgLy8gV2Ugd2FpdCBmb3IgcmVhZG9ubHkgdHJhbnNhY3Rpb25zIHRvIGZpbmlzaCB0b28sIGJ1dCB0aGlzCiAgICAgICAgICAgIC8vIGlzIGEgcGVyZiBoaXQKICAgICAgICAgICAgdGhpcy50cmFucy5vbmNvbXBsZXRlID0gZSA9PiByZXNvbHZlKCk7CiAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAvLyBObyBuZWVkIHRvIHdhaXQgb24gYW55dGhpbmcgaW4gYSByZWFkLW9ubHkgdHJhbnNhY3Rpb24uCiAgICAgICAgICAgIC8vIE5vdGUgdGhhdCBlcnJvcnMgZHVyaW5nIHJlYWRzIGFyZWEgYWx3YXlzIGhhbmRsZWQgYnkgdGhlCiAgICAgICAgICAgIC8vIHJlYWQgcmVxdWVzdC4KICAgICAgICAgICAgcmVzb2x2ZSgpOwogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSk7CiAgICB9CgogICAgY29tbWl0KCkgewogICAgICAvLyBTYWZhcmkgZG9lc24ndCBzdXBwb3J0IHRoaXMgbWV0aG9kIHlldCAodGhpcyBpcyBqdXN0IGFuCiAgICAgIC8vIG9wdGltaXphdGlvbikKICAgICAgaWYgKHRoaXMudHJhbnMuY29tbWl0KSB7CiAgICAgICAgdGhpcy50cmFucy5jb21taXQoKTsKICAgICAgfQogICAgfQoKICAgIGFzeW5jIHVwZ3JhZGVFeGNsdXNpdmUoKSB7CiAgICAgIHRoaXMuY29tbWl0KCk7CgogICAgICAvLyBjb25zb2xlLmxvZygndXBkYXRpbmcgdHJhbnNhY3Rpb24gcmVhZHdyaXRlJyk7CiAgICAgIHRoaXMudHJhbnMgPSB0aGlzLmRiLnRyYW5zYWN0aW9uKFsnZGF0YSddLCAncmVhZHdyaXRlJyk7CiAgICAgIHRoaXMuc3RvcmUgPSB0aGlzLnRyYW5zLm9iamVjdFN0b3JlKCdkYXRhJyk7CiAgICAgIHRoaXMubG9ja1R5cGUgPSBMT0NLX1RZUEVTLkVYQ0xVU0lWRTsKCiAgICAgIGxldCBjYWNoZWQwID0gdGhpcy5jYWNoZWRGaXJzdEJsb2NrOwoKICAgICAgLy8gRG8gYSByZWFkCiAgICAgIGxldCBibG9jayA9IGF3YWl0IHRoaXMucHJlZmV0Y2hGaXJzdEJsb2NrKDUwMCk7CiAgICAgIC8vIFRPRE86IHdoZW4gdGltZW91dHMgYXJlIGltcGxlbWVudGVkLCBkZXRlY3QgdGltZW91dCBhbmQgcmV0dXJuIEJVU1kKCiAgICAgIGlmIChjYWNoZWQwID09IG51bGwgJiYgYmxvY2sgPT0gbnVsbCkgewogICAgICAgIHJldHVybiB0cnVlOwogICAgICB9IGVsc2UgewogICAgICAgIGZvciAobGV0IGkgPSAyNDsgaSA8IDQwOyBpKyspIHsKICAgICAgICAgIGlmIChibG9ja1tpXSAhPT0gY2FjaGVkMFtpXSkgewogICAgICAgICAgICByZXR1cm4gZmFsc2U7CiAgICAgICAgICB9CiAgICAgICAgfQogICAgICB9CgogICAgICByZXR1cm4gdHJ1ZTsKICAgIH0KCiAgICBkb3duZ3JhZGVTaGFyZWQoKSB7CiAgICAgIHRoaXMuY29tbWl0KCk7CgogICAgICAvLyBjb25zb2xlLmxvZygnZG93bmdyYWRpbmcgdHJhbnNhY3Rpb24gcmVhZG9ubHknKTsKICAgICAgdGhpcy50cmFucyA9IHRoaXMuZGIudHJhbnNhY3Rpb24oWydkYXRhJ10sICdyZWFkb25seScpOwogICAgICB0aGlzLnN0b3JlID0gdGhpcy50cmFucy5vYmplY3RTdG9yZSgnZGF0YScpOwogICAgICB0aGlzLmxvY2tUeXBlID0gTE9DS19UWVBFUy5TSEFSRUQ7CiAgICB9CgogICAgYXN5bmMgZ2V0KGtleSkgewogICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4gewogICAgICAgIGxldCByZXEgPSB0aGlzLnN0b3JlLmdldChrZXkpOwogICAgICAgIHJlcS5vbnN1Y2Nlc3MgPSBlID0+IHsKICAgICAgICAgIHJlc29sdmUocmVxLnJlc3VsdCk7CiAgICAgICAgfTsKICAgICAgICByZXEub25lcnJvciA9IGUgPT4gcmVqZWN0KGUpOwogICAgICB9KTsKICAgIH0KCiAgICBnZXRSZWFkRGlyZWN0aW9uKCkgewogICAgICAvLyBUaGVyZSBhcmUgYSB0d28gd2F5cyB3ZSBjYW4gcmVhZCBkYXRhOiBhIGRpcmVjdCBgZ2V0YCByZXF1ZXN0CiAgICAgIC8vIG9yIG9wZW5pbmcgYSBjdXJzb3IgYW5kIGl0ZXJhdGluZyB0aHJvdWdoIGRhdGEuIFdlIGRvbid0IGtub3cKICAgICAgLy8gd2hhdCBmdXR1cmUgcmVhZHMgbG9vayBsaWtlLCBzbyB3ZSBkb24ndCBrbm93IHRoZSBiZXN0IHN0cmF0ZWd5CiAgICAgIC8vIHRvIHBpY2suIEFsd2F5cyBjaG9vc2luZyBvbmUgc3RyYXRlZ3kgZm9yZ29lcyBhIGxvdCBvZgogICAgICAvLyBvcHRpbWl6YXRpb24sIGJlY2F1c2UgaXRlcmF0aW5nIHdpdGggYSBjdXJzb3IgaXMgYSBsb3QgZmFzdGVyCiAgICAgIC8vIHRoYW4gbWFueSBgZ2V0YCBjYWxscy4gT24gdGhlIG90aGVyIGhhbmQsIG9wZW5pbmcgYSBjdXJzb3IgaXMKICAgICAgLy8gc2xvdywgYW5kIHNvIGlzIGNhbGxpbmcgYGFkdmFuY2VgIHRvIG1vdmUgYSBjdXJzb3Igb3ZlciBhIGh1Z2UKICAgICAgLy8gcmFuZ2UgKGxpa2UgbW92aW5nIGl0IDEwMDAgaXRlbXMgbGF0ZXIpLCBzbyBtYW55IGBnZXRgIGNhbGxzIHdvdWxkCiAgICAgIC8vIGJlIGZhc3Rlci4gSW4gZ2VuZXJhbDoKICAgICAgLy8KICAgICAgLy8gKiBNYW55IGBnZXRgIGNhbGxzIGFyZSBmYXN0ZXIgd2hlbiBkb2luZyByYW5kb20gYWNjZXNzZXMKICAgICAgLy8gKiBJdGVyYXRpbmcgd2l0aCBhIGN1cnNvciBpcyBmYXN0ZXIgaWYgZG9pbmcgbW9zdGx5IHNlcXVlbnRpYWwKICAgICAgLy8gICBhY2Nlc3NlcwogICAgICAvLwogICAgICAvLyBXZSBpbXBsZW1lbnQgYSBoZXVyaXN0aWMgYW5kIGtlZXBzIHRyYWNrIG9mIHRoZSBsYXN0IDMgcmVhZHMKICAgICAgLy8gYW5kIGRldGVjdHMgd2hlbiB0aGV5IGFyZSBtb3N0bHkgc2VxdWVudGlhbC4gSWYgdGhleSBhcmUsIHdlCiAgICAgIC8vIG9wZW4gYSBjdXJzb3IgYW5kIHN0YXJ0IHJlYWRpbmcgYnkgaXRlcmF0aW5nIGl0LiBJZiBub3QsIHdlIGRvCiAgICAgIC8vIGRpcmVjdCBgZ2V0YCBjYWxscy4KICAgICAgLy8KICAgICAgLy8gT24gdG9wIG9mIGFsbCBvZiB0aGlzLCBlYWNoIGJyb3dzZXIgaGFzIGRpZmZlcmVudCBwZXJmCiAgICAgIC8vIGNoYXJhY3RlcmlzdGljcy4gV2Ugd2lsbCBwcm9iYWJseSB3YW50IHRvIG1ha2UgdGhlc2UgdGhyZXNob2xkcwogICAgICAvLyBjb25maWd1cmFibGUgc28gdGhlIHVzZXIgY2FuIGNoYW5nZSB0aGVtIHBlci1icm93c2VyIGlmIG5lZWRlZCwKICAgICAgLy8gYXMgd2VsbCBhcyBmaW5lLXR1bmluZyB0aGVtIGZvciB0aGVpciB1c2FnZSBvZiBzcWxpdGUuCgogICAgICBsZXQgcHJldlJlYWRzID0gdGhpcy5wcmV2UmVhZHM7CiAgICAgIGlmIChwcmV2UmVhZHMpIHsKICAgICAgICAvLyBIYXMgdGhlcmUgYmVlbiAzIGZvcndhcmQgc2VxdWVudGlhbCByZWFkcyB3aXRoaW4gMTAgYmxvY2tzPwogICAgICAgIGlmICgKICAgICAgICAgIHByZXZSZWFkc1swXSA8IHByZXZSZWFkc1sxXSAmJgogICAgICAgICAgcHJldlJlYWRzWzFdIDwgcHJldlJlYWRzWzJdICYmCiAgICAgICAgICBwcmV2UmVhZHNbMl0gLSBwcmV2UmVhZHNbMF0gPCAxMAogICAgICAgICkgewogICAgICAgICAgcmV0dXJuICduZXh0JzsKICAgICAgICB9CgogICAgICAgIC8vIEhhcyB0aGVyZSBiZWVuIDMgYmFja3dhcmRzIHNlcXVlbnRpYWwgcmVhZHMgd2l0aGluIDEwIGJsb2Nrcz8KICAgICAgICBpZiAoCiAgICAgICAgICBwcmV2UmVhZHNbMF0gPiBwcmV2UmVhZHNbMV0gJiYKICAgICAgICAgIHByZXZSZWFkc1sxXSA+IHByZXZSZWFkc1syXSAmJgogICAgICAgICAgcHJldlJlYWRzWzBdIC0gcHJldlJlYWRzWzJdIDwgMTAKICAgICAgICApIHsKICAgICAgICAgIHJldHVybiAncHJldic7CiAgICAgICAgfQogICAgICB9CgogICAgICByZXR1cm4gbnVsbDsKICAgIH0KCiAgICByZWFkKHBvc2l0aW9uKSB7CiAgICAgIGxldCB3YWl0Q3Vyc29yID0gKCkgPT4gewogICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7CiAgICAgICAgICBpZiAodGhpcy5jdXJzb3JQcm9taXNlICE9IG51bGwpIHsKICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKAogICAgICAgICAgICAgICd3YWl0Q3Vyc29yKCkgY2FsbGVkIGJ1dCBzb21ldGhpbmcgZWxzZSBpcyBhbHJlYWR5IHdhaXRpbmcnCiAgICAgICAgICAgICk7CiAgICAgICAgICB9CiAgICAgICAgICB0aGlzLmN1cnNvclByb21pc2UgPSB7IHJlc29sdmUsIHJlamVjdCB9OwogICAgICAgIH0pOwogICAgICB9OwoKICAgICAgaWYgKHRoaXMuY3Vyc29yKSB7CiAgICAgICAgbGV0IGN1cnNvciA9IHRoaXMuY3Vyc29yOwoKICAgICAgICBpZiAoCiAgICAgICAgICBjdXJzb3IuZGlyZWN0aW9uID09PSAnbmV4dCcgJiYKICAgICAgICAgIHBvc2l0aW9uID4gY3Vyc29yLmtleSAmJgogICAgICAgICAgcG9zaXRpb24gPCBjdXJzb3Iua2V5ICsgMTAwCiAgICAgICAgKSB7CgogICAgICAgICAgY3Vyc29yLmFkdmFuY2UocG9zaXRpb24gLSBjdXJzb3Iua2V5KTsKICAgICAgICAgIHJldHVybiB3YWl0Q3Vyc29yKCk7CiAgICAgICAgfSBlbHNlIGlmICgKICAgICAgICAgIGN1cnNvci5kaXJlY3Rpb24gPT09ICdwcmV2JyAmJgogICAgICAgICAgcG9zaXRpb24gPCBjdXJzb3Iua2V5ICYmCiAgICAgICAgICBwb3NpdGlvbiA+IGN1cnNvci5rZXkgLSAxMDAKICAgICAgICApIHsKCiAgICAgICAgICBjdXJzb3IuYWR2YW5jZShjdXJzb3Iua2V5IC0gcG9zaXRpb24pOwogICAgICAgICAgcmV0dXJuIHdhaXRDdXJzb3IoKTsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgLy8gRGl0Y2ggdGhlIGN1cnNvcgogICAgICAgICAgdGhpcy5jdXJzb3IgPSBudWxsOwogICAgICAgICAgcmV0dXJuIHRoaXMucmVhZChwb3NpdGlvbik7CiAgICAgICAgfQogICAgICB9IGVsc2UgewogICAgICAgIC8vIFdlIGRvbid0IGFscmVhZHkgaGF2ZSBhIGN1cnNvci4gV2UgbmVlZCB0byBhIGZyZXNoIHJlYWQ7CiAgICAgICAgLy8gc2hvdWxkIHdlIG9wZW4gYSBjdXJzb3Igb3IgY2FsbCBgZ2V0YD8KCiAgICAgICAgbGV0IGRpciA9IHRoaXMuZ2V0UmVhZERpcmVjdGlvbigpOwogICAgICAgIGlmIChkaXIpIHsKICAgICAgICAgIC8vIE9wZW4gYSBjdXJzb3IKICAgICAgICAgIHRoaXMucHJldlJlYWRzID0gbnVsbDsKCiAgICAgICAgICBsZXQga2V5UmFuZ2U7CiAgICAgICAgICBpZiAoZGlyID09PSAncHJldicpIHsKICAgICAgICAgICAga2V5UmFuZ2UgPSBJREJLZXlSYW5nZS51cHBlckJvdW5kKHBvc2l0aW9uKTsKICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIGtleVJhbmdlID0gSURCS2V5UmFuZ2UubG93ZXJCb3VuZChwb3NpdGlvbik7CiAgICAgICAgICB9CgogICAgICAgICAgbGV0IHJlcSA9IHRoaXMuc3RvcmUub3BlbkN1cnNvcihrZXlSYW5nZSwgZGlyKTsKCiAgICAgICAgICByZXEub25zdWNjZXNzID0gZSA9PiB7CgogICAgICAgICAgICBsZXQgY3Vyc29yID0gZS50YXJnZXQucmVzdWx0OwogICAgICAgICAgICB0aGlzLmN1cnNvciA9IGN1cnNvcjsKCiAgICAgICAgICAgIGlmICh0aGlzLmN1cnNvclByb21pc2UgPT0gbnVsbCkgewogICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignR290IGRhdGEgZnJvbSBjdXJzb3IgYnV0IG5vdGhpbmcgaXMgd2FpdGluZyBpdCcpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHRoaXMuY3Vyc29yUHJvbWlzZS5yZXNvbHZlKGN1cnNvciA/IGN1cnNvci52YWx1ZSA6IG51bGwpOwogICAgICAgICAgICB0aGlzLmN1cnNvclByb21pc2UgPSBudWxsOwogICAgICAgICAgfTsKICAgICAgICAgIHJlcS5vbmVycm9yID0gZSA9PiB7CiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdDdXJzb3IgZmFpbHVyZTonLCBlKTsKCiAgICAgICAgICAgIGlmICh0aGlzLmN1cnNvclByb21pc2UgPT0gbnVsbCkgewogICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignR290IGRhdGEgZnJvbSBjdXJzb3IgYnV0IG5vdGhpbmcgaXMgd2FpdGluZyBpdCcpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHRoaXMuY3Vyc29yUHJvbWlzZS5yZWplY3QoZSk7CiAgICAgICAgICAgIHRoaXMuY3Vyc29yUHJvbWlzZSA9IG51bGw7CiAgICAgICAgICB9OwoKICAgICAgICAgIHJldHVybiB3YWl0Q3Vyc29yKCk7CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgIGlmICh0aGlzLnByZXZSZWFkcyA9PSBudWxsKSB7CiAgICAgICAgICAgIHRoaXMucHJldlJlYWRzID0gWzAsIDAsIDBdOwogICAgICAgICAgfQogICAgICAgICAgdGhpcy5wcmV2UmVhZHMucHVzaChwb3NpdGlvbik7CiAgICAgICAgICB0aGlzLnByZXZSZWFkcy5zaGlmdCgpOwoKICAgICAgICAgIHJldHVybiB0aGlzLmdldChwb3NpdGlvbik7CiAgICAgICAgfQogICAgICB9CiAgICB9CgogICAgYXN5bmMgc2V0KGl0ZW0pIHsKICAgICAgdGhpcy5wcmV2UmVhZHMgPSBudWxsOwoKICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHsKICAgICAgICBsZXQgcmVxID0gdGhpcy5zdG9yZS5wdXQoaXRlbS52YWx1ZSwgaXRlbS5rZXkpOwogICAgICAgIHJlcS5vbnN1Y2Nlc3MgPSBlID0+IHJlc29sdmUocmVxLnJlc3VsdCk7CiAgICAgICAgcmVxLm9uZXJyb3IgPSBlID0+IHJlamVjdChlKTsKICAgICAgfSk7CiAgICB9CgogICAgYXN5bmMgYnVsa1NldChpdGVtcykgewogICAgICB0aGlzLnByZXZSZWFkcyA9IG51bGw7CgogICAgICBmb3IgKGxldCBpdGVtIG9mIGl0ZW1zKSB7CiAgICAgICAgdGhpcy5zdG9yZS5wdXQoaXRlbS52YWx1ZSwgaXRlbS5rZXkpOwogICAgICB9CiAgICB9CiAgfQoKICBhc3luYyBmdW5jdGlvbiBsb2FkRGIobmFtZSkgewogICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHsKICAgICAgaWYgKG9wZW5EYnMuZ2V0KG5hbWUpKSB7CiAgICAgICAgcmVzb2x2ZShvcGVuRGJzLmdldChuYW1lKSk7CiAgICAgICAgcmV0dXJuOwogICAgICB9CgogICAgICBsZXQgcmVxID0gZ2xvYmFsVGhpcy5pbmRleGVkREIub3BlbihuYW1lLCAxKTsKICAgICAgcmVxLm9uc3VjY2VzcyA9IGV2ZW50ID0+IHsKICAgICAgICBjb25zb2xlLmxvZygnZGIgaXMgb3BlbiEnLCBuYW1lKTsKICAgICAgICBsZXQgZGIgPSBldmVudC50YXJnZXQucmVzdWx0OwoKICAgICAgICBkYi5vbnZlcnNpb25jaGFuZ2UgPSAoKSA9PiB7CiAgICAgICAgICAvLyBUT0RPOiBOb3RpZnkgdGhlIHVzZXIgc29tZWhvdwogICAgICAgICAgY29uc29sZS5sb2coJ2Nsb3NpbmcgYmVjYXVzZSB2ZXJzaW9uIGNoYW5nZWQnKTsKICAgICAgICAgIGRiLmNsb3NlKCk7CiAgICAgICAgfTsKCiAgICAgICAgZGIub25jbG9zZSA9ICgpID0+IHsKICAgICAgICAgIG9wZW5EYnMuZGVsZXRlKG5hbWUpOwogICAgICAgIH07CgogICAgICAgIG9wZW5EYnMuc2V0KG5hbWUsIGRiKTsKICAgICAgICByZXNvbHZlKGRiKTsKICAgICAgfTsKICAgICAgcmVxLm9udXBncmFkZW5lZWRlZCA9IGV2ZW50ID0+IHsKICAgICAgICBsZXQgZGIgPSBldmVudC50YXJnZXQucmVzdWx0OwogICAgICAgIGlmICghZGIub2JqZWN0U3RvcmVOYW1lcy5jb250YWlucygnZGF0YScpKSB7CiAgICAgICAgICBkYi5jcmVhdGVPYmplY3RTdG9yZSgnZGF0YScpOwogICAgICAgIH0KICAgICAgfTsKICAgICAgcmVxLm9uYmxvY2tlZCA9IGUgPT4gY29uc29sZS5sb2coJ2Jsb2NrZWQnLCBlKTsKICAgICAgcmVxLm9uZXJyb3IgPSByZXEub25hYm9ydCA9IGUgPT4gcmVqZWN0KGUudGFyZ2V0LmVycm9yKTsKICAgIH0pOwogIH0KCiAgZnVuY3Rpb24gZ2V0VHJhbnNhY3Rpb24obmFtZSkgewogICAgcmV0dXJuIHRyYW5zYWN0aW9ucy5nZXQobmFtZSk7CiAgfQoKICBhc3luYyBmdW5jdGlvbiB3aXRoVHJhbnNhY3Rpb24obmFtZSwgbW9kZSwgZnVuYykgewogICAgbGV0IHRyYW5zID0gdHJhbnNhY3Rpb25zLmdldChuYW1lKTsKICAgIGlmICh0cmFucykgewogICAgICAvLyBJZiBhIHRyYW5zYWN0aW9uIGFscmVhZHkgZXhpc3RzLCB0aGF0IG1lYW5zIHRoZSBmaWxlIGhhcyBiZWVuCiAgICAgIC8vIGxvY2tlZC4gV2UgZG9uJ3QgZnVsbHkgc3VwcG9ydCBhcmJpdHJhcnkgbmVzdGVkIHRyYW5zYWN0aW9ucywKICAgICAgLy8gYXMgc2VlbiBiZWxvdyAod2Ugd29uJ3QgdXBncmFkZSBhIGByZWFkb25seWAgdG8gYHJlYWR3cml0ZWAKICAgICAgLy8gYXV0b21hdGljYWxseSkgYW5kIHRoaXMgaXMgbWFpbmx5IGZvciB0aGUgdXNlIGNhc2Ugd2hlcmUgc3FsaXRlCiAgICAgIC8vIGxvY2tzIHRoZSBkYiBhbmQgY3JlYXRlcyBhIHRyYW5zYWN0aW9uIGZvciB0aGUgZHVyYWN0aW9uIG9mIHRoZQogICAgICAvLyBsb2NrLiBXZSBkb24ndCBhY3R1YWxseSB3cml0ZSBjb2RlIGluIGEgd2F5IHRoYXQgYXNzdW1lcyBuZXN0ZWQKICAgICAgLy8gdHJhbnNhY3Rpb25zLCBzbyBqdXN0IGVycm9yIGhlcmUKICAgICAgaWYgKG1vZGUgPT09ICdyZWFkd3JpdGUnICYmIHRyYW5zLmxvY2tUeXBlID09PSBMT0NLX1RZUEVTLlNIQVJFRCkgewogICAgICAgIHRocm93IG5ldyBFcnJvcignQXR0ZW1wdGVkIHdyaXRlIGJ1dCBvbmx5IGhhcyBTSEFSRUQgbG9jaycpOwogICAgICB9CiAgICAgIHJldHVybiBmdW5jKHRyYW5zKTsKICAgIH0KCiAgICAvLyBPdXRzaWRlIHRoZSBzY29wZSBvZiBhIGxvY2ssIGNyZWF0ZSBhIHRlbXBvcmFyeSB0cmFuc2FjdGlvbgogICAgdHJhbnMgPSBuZXcgVHJhbnNhY3Rpb24oYXdhaXQgbG9hZERiKG5hbWUpLCBtb2RlKTsKICAgIGF3YWl0IGZ1bmModHJhbnMpOwogICAgYXdhaXQgdHJhbnMud2FpdENvbXBsZXRlKCk7CiAgfQoKICAvLyBMb2NraW5nIHN0cmF0ZWd5OgogIC8vCiAgLy8gKiBXZSBtYXAgc3FsaXRlJ3MgbG9ja3Mgb250byBJbmRleGVkREIncyB0cmFuc2FjdGlvbiBzZW1hbnRpY3MuCiAgLy8gICBSZWFkIHRyYW5zYWN0aW9ucyBtYXkgZXhlY3V0ZSBpbiBwYXJhbGxlbC4gUmVhZC93cml0ZQogIC8vICAgdHJhbnNhY3Rpb25zIGFyZSBxdWV1ZWQgdXAgYW5kIHdhaXQgdW50aWwgYWxsIHByZWNlZGluZwogIC8vICAgcmVhZCB0cmFuc2FjdGlvbnMgZmluaXNoIGV4ZWN1dGluZy4gUmVhZCB0cmFuc2FjdGlvbnMgc3RhcnRlZAogIC8vICAgYWZ0ZXIgYSByZWFkL3dyaXRlIHRyYW5zYWN0aW9uIHdhaXQgdW50aWwgaXQgaXMgZmluaXNoZWQuCiAgLy8KICAvLyAqIElEQiB0cmFuc2FjdGlvbnMgd2lsbCB3YWl0IGZvcmV2ZXIgdW50aWwgdGhleSBjYW4gZXhlY3V0ZSAoZm9yCiAgLy8gICBleGFtcGxlLCB0aGV5IG1heSBiZSBibG9ja2VkIG9uIGEgcmVhZC93cml0ZSB0cmFuc2FjdGlvbikuIFdlCiAgLy8gICBkb24ndCB3YW50IHRvIGFsbG93IHNxbGl0ZSB0cmFuc2FjdGlvbnMgdG8gd2FpdCBmb3JldmVyLCBzbwogIC8vICAgd2UgbWFudWFsbHkgdGltZW91dCBpZiBhIHRyYW5zYWN0aW9uIHRha2VzIHRvbyBsb25nIHRvCiAgLy8gICBzdGFydCBleGVjdXRpbmcuIFRoaXMgc2ltdWxhdGVzIHRoZSBiZWhhdmlvciBvZiBhIHNxbGl0ZQogIC8vICAgYmFpbGluZyBpZiBpdCBjYW4ndCByZXF1aXJlIGEgbG9jay4KICAvLwogIC8vICogQSBTSEFSRUQgbG9jayB3YW50cyB0byByZWFkIGZyb20gdGhlIGRiLiBXZSBzdGFydCBhIHJlYWQKICAvLyAgIHRyYW5zYWN0aW9uIGFuZCByZWFkIHRoZSBmaXJzdCBibG9jaywgYW5kIGlmIHdlIHJlYWQgaXQgd2l0aGluCiAgLy8gICA1MDBtcyB3ZSBjb25zaWRlciB0aGUgbG9jayBzdWNjZXNzZnVsLiBPdGhlcndpc2UgdGhlIGxvY2sKICAvLyAgIGZhaWxlZCBhbmQgd2UgcmV0dXJuIFNRTElURV9CVVNZLiAoVGhlcmUncyBubyBwZXJmIGRvd25zaWRlCiAgLy8gICB0byByZWFkaW5nIHRoZSBmaXJzdCBibG9jayAtIGl0IGhhcyB0byBiZSByZWFkIGFueXdheSB0byBjaGVjawogIC8vICAgYnl0ZXMgMjQtMzkgZm9yIHRoZSBjaGFuZ2UgY291bnRlcikKICAvLwogIC8vICogQSBSRVNFUlZFRCBsb2NrIG1lYW5zIHRoZSBkYiB3YW50cyB0byBzdGFydCB3cml0aW5nICh0aGluayBvZgogIC8vICAgYEJFR0lOIFRSQU5TQUNUSU9OYCkuIE9ubHkgb25lIHByb2Nlc3MgY2FuIG9idGFpbiBhIFJFU0VSVkVECiAgLy8gICBsb2NrIGF0IGEgdGltZSwgYnV0IG5vcm1hbGx5IHNxbGl0ZSBzdGlsbCBsZWFkcyBuZXcgcmVhZCBsb2NrcwogIC8vICAgaGFwcGVuLiBJdCBpc24ndCB1bnRpbCBhbiBFWENMVVNJVkUgbG9jayBpcyBoZWxkIHRoYXQgcmVhZHMgYXJlCiAgLy8gICBibG9ja2VkLiBIb3dldmVyLCBzaW5jZSB3ZSBuZWVkIHRvIGd1YXJhbnRlZSBvbmx5IG9uZSBSRVNFUlZFRAogIC8vICAgbG9jayBhdCBvbmNlIChvdGhlcndpc2UgZGF0YSBjb3VsZCBjaGFuZ2UgZnJvbSBhbm90aGVyIHByb2Nlc3MKICAvLyAgIHdpdGhpbiBhIHRyYW5zYWN0aW9uLCBjYXVzaW5nIGZhdWx0eSBjYWNoZXMgZXRjKSB0aGUgc2ltcGxlc3QKICAvLyAgIHRoaW5nIHRvIGRvIGlzIGdvIGFoZWFkIGFuZCBncmFiIGEgcmVhZC93cml0ZSB0cmFuc2FjdGlvbiB0aGF0CiAgLy8gICByZXByZXNlbnRzIHRoZSBSRVNFUlZFRCBsb2NrLiBUaGlzIHdpbGwgYmxvY2sgYWxsIHJlYWRzIGZyb20KICAvLyAgIGhhcHBlbmluZywgYW5kIGlzIGVzc2VudGlhbGx5IHRoZSBzYW1lIGFzIGFuIEVYQ0xVU0lWRSBsb2NrLgogIC8vCiAgLy8gICAgICogVGhlIG1haW4gcHJvYmxlbSBoZXJlIGlzIHdlIGNhbid0ICJ1cGdyYWRlIiBhIGByZWFkb25seWAKICAvLyAgICAgICB0cmFuc2FjdGlvbiB0byBgcmVhZHdyaXRlYCwgYnV0IG5hdGl2ZSBzcWxpdGUgY2FuIHVwZ3JhZGUgYQogIC8vICAgICAgIGxvY2sgZnJvbSBTSEFSRUQgdG8gUkVTRVJWRUQuIFdlIG5lZWQgdG8gc3RhcnQgYSBuZXcKICAvLyAgICAgICB0cmFuc2FjdGlvbiB0byBkbyBzbywgYW5kIGJlY2F1c2Ugb2YgdGhhdCB0aGVyZSBtaWdodCBiZQogIC8vICAgICAgIG90aGVyIGByZWFkd3JpdGVgIHRyYW5zYWN0aW9ucyB0aGF0IGdldCBydW4gZHVyaW5nIHRoZQogIC8vICAgICAgICJ1cGdyYWRlIiB3aGljaCBpbnZhbGlkYXRlcyB0aGUgd2hvbGUgbG9ja2luZyBwcm9jZXNzIGFuZAogIC8vICAgICAgIGFuZCBjb3JydXB0cyBkYXRhLgogIC8vCiAgLy8gKiBJZGVhbGx5LCB3ZSBjb3VsZCB0ZWxsIHNxbGl0ZSB0byBza2lwIFNIQVJFRCBsb2NrcyBlbnRpcmVseS4gV2UKICAvLyAgIGRvbid0IG5lZWQgdGhlbSBzaW5jZSB3ZSBjYW4gcmVseSBvbiBJbmRleGVkREIncyBzZW1hbnRpY3MuCiAgLy8gICBUaGVuIHdoZW4gaXQgd2FudHMgdG8gc3RhcnQgd3JpdGluZywgd2UgZ2V0IGEgUkVTRVJWRUQgbG9jawogIC8vICAgd2l0aG91dCBoYXZpbmcgdG8gdXBncmFkZSBmcm9tIFNIQVJFRC4gVGhpcyB3b3VsZCBzYXZlIHVzCiAgLy8gICB0aGUgY29zdCBvZiBhIGByZWFkb25seWAgdHJhbnNhY3Rpb24gd2hlbiB3cml0aW5nOyByaWdodCBub3cKICAvLyAgIGl0IG11c3Qgb3BlbiBhIGByZWFkb25seWAgdHJhbnNhY3Rpb24gYW5kIHRoZW4gaW1tZWRpYXRlbHkgb3BlbgogIC8vICAgYSBgcmVhZHdyaXRlYCB0byB1cGdyYWRlIGl0LiBJIHRob3VnaHQgb2YgZGVmZXJyaW5nIG9wZW5pbmcgdGhlCiAgLy8gICBgcmVhZG9ubHlgIHRyYW5zYWN0aW9uIHVudGlsIHNvbWV0aGluZyBpcyBhY3R1YWxseSByZWFkLCBidXQKICAvLyAgIHVuZm9ydHVuYXRlbHkgc3FsaXRlIG9wZW5zIGl0LCByZWFkcyB0aGUgZmlyc3QgYmxvY2ssIGFuZCB0aGVuCiAgLy8gICB1cGdyYWRlcyBpdC4gU28gdGhlcmUncyBubyB3YXkgYXJvdW5kIGl0LiAoV2UgY2FuJ3QgYXNzdW1lIGl0J3MKICAvLyAgIGEgYHJlYWR3cml0ZWAgdHJhbnNhY3Rpb24gYXQgdGhhdCBwb2ludCBzaW5jZSB0aGF0IHdvdWxkIGFzc3VtZQogIC8vICAgYWxsIFNIQVJFRCBsb2NrcyBhcmUgYHJlYWR3cml0ZWAsIHJlbW92aW5nIHRoZSBwb3NzaWJpbGl0eSBvZgogIC8vICAgY29uY3VycmVudCByZWFkcykuCiAgLy8KICAvLyAqIFVwZ3JhZGluZyB0byBhbiBFWENMVVNJVkUgbG9jayBpcyBhIG5vb3AsIHNpbmNlIHdlIHRyZWF0IFJFU0VSVkVECiAgLy8gICBsb2NrcyBhcyBFWENMVVNJVkUuCiAgYXN5bmMgZnVuY3Rpb24gaGFuZGxlTG9jayh3cml0ZXIsIG5hbWUsIGxvY2tUeXBlKSB7CiAgICAvLyBjb25zb2xlLmxvZygnbG9ja2luZycsIG5hbWUsIGxvY2tUeXBlLCBwZXJmb3JtYW5jZS5ub3coKSk7CgogICAgbGV0IHRyYW5zID0gdHJhbnNhY3Rpb25zLmdldChuYW1lKTsKICAgIGlmICh0cmFucykgewogICAgICBpZiAobG9ja1R5cGUgPiB0cmFucy5sb2NrVHlwZSkgewogICAgICAgIC8vIFVwZ3JhZGUgU0hBUkVEIHRvIEVYQ0xVU0lWRQogICAgICAgIGFzc2VydCgKICAgICAgICAgIHRyYW5zLmxvY2tUeXBlID09PSBMT0NLX1RZUEVTLlNIQVJFRCwKICAgICAgICAgIGBVcHJhZGluZyBsb2NrIHR5cGUgZnJvbSAke3RyYW5zLmxvY2tUeXBlfSBpcyBpbnZhbGlkYAogICAgICAgICk7CiAgICAgICAgYXNzZXJ0KAogICAgICAgICAgbG9ja1R5cGUgPT09IExPQ0tfVFlQRVMuUkVTRVJWRUQgfHwgbG9ja1R5cGUgPT09IExPQ0tfVFlQRVMuRVhDTFVTSVZFLAogICAgICAgICAgYFVwZ3JhZGluZyBsb2NrIHR5cGUgdG8gJHtsb2NrVHlwZX0gaXMgaW52YWxpZGAKICAgICAgICApOwoKICAgICAgICBsZXQgc3VjY2VzcyA9IGF3YWl0IHRyYW5zLnVwZ3JhZGVFeGNsdXNpdmUoKTsKICAgICAgICB3cml0ZXIuaW50MzIoc3VjY2VzcyA/IDAgOiAtMSk7CiAgICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgLy8gSWYgbm90IHVwZ3JhZGluZyBhbmQgd2UgYWxyZWFkeSBoYXZlIGEgbG9jaywgbWFrZSBzdXJlIHRoaXMKICAgICAgICAvLyBpc24ndCBhIGRvd25ncmFkZQogICAgICAgIGFzc2VydCgKICAgICAgICAgIHRyYW5zLmxvY2tUeXBlID09PSBsb2NrVHlwZSwKICAgICAgICAgIGBEb3duZ3JhZGluZyBsb2NrIHRvICR7bG9ja1R5cGV9IGlzIGludmFsaWRgCiAgICAgICAgKTsKCiAgICAgICAgd3JpdGVyLmludDMyKDApOwogICAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgICB9CiAgICB9IGVsc2UgewogICAgICBhc3NlcnQoCiAgICAgICAgbG9ja1R5cGUgPT09IExPQ0tfVFlQRVMuU0hBUkVELAogICAgICAgIGBOZXcgbG9ja3MgbXVzdCBzdGFydCBhcyBTSEFSRUQgaW5zdGVhZCBvZiAke2xvY2tUeXBlfWAKICAgICAgKTsKCiAgICAgIGxldCB0cmFucyA9IG5ldyBUcmFuc2FjdGlvbihhd2FpdCBsb2FkRGIobmFtZSkpOwogICAgICBpZiAoKGF3YWl0IHRyYW5zLnByZWZldGNoRmlyc3RCbG9jayg1MDApKSA9PSBudWxsKSA7CgogICAgICB0cmFuc2FjdGlvbnMuc2V0KG5hbWUsIHRyYW5zKTsKCiAgICAgIHdyaXRlci5pbnQzMigwKTsKICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICB9CiAgfQoKICBhc3luYyBmdW5jdGlvbiBoYW5kbGVVbmxvY2sod3JpdGVyLCBuYW1lLCBsb2NrVHlwZSkgewogICAgLy8gY29uc29sZS5sb2coJ3VubG9ja2luZycsIG5hbWUsIGxvY2tUeXBlLCBwZXJmb3JtYW5jZS5ub3coKSk7CgogICAgbGV0IHRyYW5zID0gZ2V0VHJhbnNhY3Rpb24obmFtZSk7CgogICAgaWYgKGxvY2tUeXBlID09PSBMT0NLX1RZUEVTLlNIQVJFRCkgewogICAgICBpZiAodHJhbnMgPT0gbnVsbCkgewogICAgICAgIHRocm93IG5ldyBFcnJvcignVW5sb2NrIGVycm9yIChTSEFSRUQpOiBubyB0cmFuc2FjdGlvbiBydW5uaW5nJyk7CiAgICAgIH0KCiAgICAgIGlmICh0cmFucy5sb2NrVHlwZSA9PT0gTE9DS19UWVBFUy5FWENMVVNJVkUpIHsKICAgICAgICB0cmFucy5kb3duZ3JhZGVTaGFyZWQoKTsKICAgICAgfQogICAgfSBlbHNlIGlmIChsb2NrVHlwZSA9PT0gTE9DS19UWVBFUy5OT05FKSB7CiAgICAgIC8vIEkgdGhvdWdodCB3ZSBjb3VsZCBhc3N1bWUgYSBsb2NrIGlzIGFsd2F5cyBvcGVuIHdoZW4gYHVubG9ja2AKICAgICAgLy8gaXMgY2FsbGVkLCBidXQgaXQgYWxzbyBjYWxscyBgdW5sb2NrYCB3aGVuIGNsb3NpbmcgdGhlIGZpbGUgbm8KICAgICAgLy8gbWF0dGVyIHdoYXQuIERvIG5vdGhpbmcgaWYgdGhlcmUncyBubyBsb2NrIGN1cnJlbnRseQogICAgICBpZiAodHJhbnMpIHsKICAgICAgICAvLyBUT0RPOiB0aGlzIGlzIHdoZXJlIGFuIGVycm9yIGNvdWxkIGJ1YmJsZSB1cC4gSGFuZGxlIGl0CiAgICAgICAgYXdhaXQgdHJhbnMud2FpdENvbXBsZXRlKCk7CiAgICAgICAgdHJhbnNhY3Rpb25zLmRlbGV0ZShuYW1lKTsKICAgICAgfQogICAgfQoKICAgIHdyaXRlci5pbnQzMigwKTsKICAgIHdyaXRlci5maW5hbGl6ZSgpOwogIH0KCiAgYXN5bmMgZnVuY3Rpb24gaGFuZGxlUmVhZCh3cml0ZXIsIG5hbWUsIHBvc2l0aW9uKSB7CiAgICByZXR1cm4gd2l0aFRyYW5zYWN0aW9uKG5hbWUsICdyZWFkb25seScsIGFzeW5jIHRyYW5zID0+IHsKICAgICAgbGV0IGRhdGEgPSBhd2FpdCB0cmFucy5yZWFkKHBvc2l0aW9uKTsKCiAgICAgIGlmIChkYXRhID09IG51bGwpIHsKICAgICAgICB3cml0ZXIuYnl0ZXMobmV3IEFycmF5QnVmZmVyKDApKTsKICAgICAgfSBlbHNlIHsKICAgICAgICB3cml0ZXIuYnl0ZXMoZGF0YSk7CiAgICAgIH0KICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICB9KTsKICB9CgogIGFzeW5jIGZ1bmN0aW9uIGhhbmRsZVdyaXRlcyh3cml0ZXIsIG5hbWUsIHdyaXRlcykgewogICAgcmV0dXJuIHdpdGhUcmFuc2FjdGlvbihuYW1lLCAncmVhZHdyaXRlJywgYXN5bmMgdHJhbnMgPT4gewogICAgICBhd2FpdCB0cmFucy5idWxrU2V0KHdyaXRlcy5tYXAodyA9PiAoeyBrZXk6IHcucG9zLCB2YWx1ZTogdy5kYXRhIH0pKSk7CgogICAgICB3cml0ZXIuaW50MzIoMCk7CiAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgfSk7CiAgfQoKICBhc3luYyBmdW5jdGlvbiBoYW5kbGVSZWFkTWV0YSh3cml0ZXIsIG5hbWUpIHsKICAgIHJldHVybiB3aXRoVHJhbnNhY3Rpb24obmFtZSwgJ3JlYWRvbmx5JywgYXN5bmMgdHJhbnMgPT4gewogICAgICB0cnkgewogICAgICAgIGNvbnNvbGUubG9nKCdSZWFkaW5nIG1ldGEnKTsKICAgICAgICBsZXQgcmVzID0gYXdhaXQgdHJhbnMuZ2V0KC0xKTsKICAgICAgICBjb25zb2xlLmxvZygnUmVhZGluZyBtZXRhIChkb25lKScsIHJlcyk7CgogICAgICAgIGxldCBtZXRhID0gcmVzOwogICAgICAgIHdyaXRlci5pbnQzMihtZXRhID8gbWV0YS5zaXplIDogLTEpOwogICAgICAgIHdyaXRlci5pbnQzMihtZXRhID8gbWV0YS5ibG9ja1NpemUgOiAtMSk7CiAgICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICAgIH0gY2F0Y2ggKGVycikgewogICAgICAgIGNvbnNvbGUubG9nKGVycik7CiAgICAgICAgd3JpdGVyLmludDMyKC0xKTsKICAgICAgICB3cml0ZXIuaW50MzIoLTEpOwogICAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgICB9CiAgICB9KTsKICB9CgogIGFzeW5jIGZ1bmN0aW9uIGhhbmRsZVdyaXRlTWV0YSh3cml0ZXIsIG5hbWUsIG1ldGEpIHsKICAgIHJldHVybiB3aXRoVHJhbnNhY3Rpb24obmFtZSwgJ3JlYWR3cml0ZScsIGFzeW5jIHRyYW5zID0+IHsKICAgICAgdHJ5IHsKICAgICAgICBhd2FpdCB0cmFucy5zZXQoeyBrZXk6IC0xLCB2YWx1ZTogbWV0YSB9KTsKCiAgICAgICAgd3JpdGVyLmludDMyKDApOwogICAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgICB9IGNhdGNoIChlcnIpIHsKICAgICAgICBjb25zb2xlLmxvZyhlcnIpOwogICAgICAgIHdyaXRlci5pbnQzMigtMSk7CiAgICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICAgIH0KICAgIH0pOwogIH0KCiAgYXN5bmMgZnVuY3Rpb24gaGFuZGxlRGVsZXRlRmlsZSh3cml0ZXIsIG5hbWUpIHsKICAgIC8vIFRPRE86IEhhbmRsZSB0aGlzCiAgICB3cml0ZXIuaW50MzIoMCk7CiAgICB3cml0ZXIuZmluYWxpemUoKTsKICB9CgogIC8vIGBsaXN0ZW5gIGNvbnRpbnVhbGx5IGxpc3RlbnMgZm9yIHJlcXVlc3RzIHZpYSB0aGUgc2hhcmVkIGJ1ZmZlci4KICAvLyBSaWdodCBub3cgaXQncyBpbXBsZW1lbnRlZCBpbiBhIHRhaWwtY2FsbCBzdHlsZSAoYGxpc3RlbmAgaXMKICAvLyByZWN1cnNpdmVseSBjYWxsZWQpIGJlY2F1c2UgSSB0aG91Z2h0IHRoYXQgd2FzIG5lY2Vzc2FyeSBmb3IKICAvLyB2YXJpb3VzIHJlYXNvbnMuIFdlIGNhbiBjb252ZXJ0IHRoaXMgdG8gYSBgd2hpbGUoMSlgIGxvb3Agd2l0aAogIC8vIGFuZCB1c2UgYGF3YWl0YCB0aG91Z2gKICBhc3luYyBmdW5jdGlvbiBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpIHsKICAgIGxldCBtZXRob2QgPSByZWFkZXIuc3RyaW5nKCk7CgogICAgc3dpdGNoIChtZXRob2QpIHsKICAgICAgY2FzZSAnc3RhdHMtc3RhcnQnOiB7CiAgICAgICAgcmVhZGVyLmRvbmUoKTsKCiAgICAgICAgd3JpdGVyLmludDMyKDApOwogICAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgICAgIGxpc3RlbihyZWFkZXIsIHdyaXRlcik7CiAgICAgICAgYnJlYWs7CiAgICAgIH0KCiAgICAgIGNhc2UgJ3N0YXRzJzogewogICAgICAgIHJlYWRlci5kb25lKCk7CgogICAgICAgIGF3YWl0IGVuZCgpOwoKICAgICAgICB3cml0ZXIuaW50MzIoMCk7CiAgICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICAgICAgbGlzdGVuKHJlYWRlciwgd3JpdGVyKTsKICAgICAgICBicmVhazsKICAgICAgfQoKICAgICAgY2FzZSAnd3JpdGVCbG9ja3MnOiB7CiAgICAgICAgbGV0IG5hbWUgPSByZWFkZXIuc3RyaW5nKCk7CiAgICAgICAgbGV0IHdyaXRlcyA9IFtdOwogICAgICAgIHdoaWxlICghcmVhZGVyLmRvbmUoKSkgewogICAgICAgICAgbGV0IHBvcyA9IHJlYWRlci5pbnQzMigpOwogICAgICAgICAgbGV0IGRhdGEgPSByZWFkZXIuYnl0ZXMoKTsKICAgICAgICAgIHdyaXRlcy5wdXNoKHsgcG9zLCBkYXRhIH0pOwogICAgICAgIH0KCiAgICAgICAgYXdhaXQgaGFuZGxlV3JpdGVzKHdyaXRlciwgbmFtZSwgd3JpdGVzKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICdyZWFkQmxvY2snOiB7CiAgICAgICAgbGV0IG5hbWUgPSByZWFkZXIuc3RyaW5nKCk7CiAgICAgICAgbGV0IHBvcyA9IHJlYWRlci5pbnQzMigpOwogICAgICAgIHJlYWRlci5kb25lKCk7CgogICAgICAgIGF3YWl0IGhhbmRsZVJlYWQod3JpdGVyLCBuYW1lLCBwb3MpOwogICAgICAgIGxpc3RlbihyZWFkZXIsIHdyaXRlcik7CiAgICAgICAgYnJlYWs7CiAgICAgIH0KCiAgICAgIGNhc2UgJ3JlYWRNZXRhJzogewogICAgICAgIGxldCBuYW1lID0gcmVhZGVyLnN0cmluZygpOwogICAgICAgIHJlYWRlci5kb25lKCk7CiAgICAgICAgYXdhaXQgaGFuZGxlUmVhZE1ldGEod3JpdGVyLCBuYW1lKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICd3cml0ZU1ldGEnOiB7CiAgICAgICAgbGV0IG5hbWUgPSByZWFkZXIuc3RyaW5nKCk7CiAgICAgICAgbGV0IHNpemUgPSByZWFkZXIuaW50MzIoKTsKICAgICAgICBsZXQgYmxvY2tTaXplID0gcmVhZGVyLmludDMyKCk7CiAgICAgICAgcmVhZGVyLmRvbmUoKTsKICAgICAgICBhd2FpdCBoYW5kbGVXcml0ZU1ldGEod3JpdGVyLCBuYW1lLCB7IHNpemUsIGJsb2NrU2l6ZSB9KTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICdkZWxldGVGaWxlJzogewogICAgICAgIHJlYWRlci5zdHJpbmcoKTsKICAgICAgICByZWFkZXIuZG9uZSgpOwoKICAgICAgICBhd2FpdCBoYW5kbGVEZWxldGVGaWxlKHdyaXRlcik7CiAgICAgICAgbGlzdGVuKHJlYWRlciwgd3JpdGVyKTsKICAgICAgICBicmVhazsKICAgICAgfQoKICAgICAgY2FzZSAnbG9ja0ZpbGUnOiB7CiAgICAgICAgbGV0IG5hbWUgPSByZWFkZXIuc3RyaW5nKCk7CiAgICAgICAgbGV0IGxvY2tUeXBlID0gcmVhZGVyLmludDMyKCk7CiAgICAgICAgcmVhZGVyLmRvbmUoKTsKCiAgICAgICAgYXdhaXQgaGFuZGxlTG9jayh3cml0ZXIsIG5hbWUsIGxvY2tUeXBlKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICd1bmxvY2tGaWxlJzogewogICAgICAgIGxldCBuYW1lID0gcmVhZGVyLnN0cmluZygpOwogICAgICAgIGxldCBsb2NrVHlwZSA9IHJlYWRlci5pbnQzMigpOwogICAgICAgIHJlYWRlci5kb25lKCk7CgogICAgICAgIGF3YWl0IGhhbmRsZVVubG9jayh3cml0ZXIsIG5hbWUsIGxvY2tUeXBlKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBkZWZhdWx0OgogICAgICAgIHRocm93IG5ldyBFcnJvcignVW5rbm93biBtZXRob2Q6ICcgKyBtZXRob2QpOwogICAgfQogIH0KCiAgc2VsZi5vbm1lc3NhZ2UgPSBtc2cgPT4gewogICAgc3dpdGNoIChtc2cuZGF0YS50eXBlKSB7CiAgICAgIGNhc2UgJ2luaXQnOiB7CiAgICAgICAgcG9zdE1lc3NhZ2UoeyB0eXBlOiAnd29ya2VyLXJlYWR5JyB9KTsKICAgICAgICBsZXQgW2FyZ0J1ZmZlciwgcmVzdWx0QnVmZmVyXSA9IG1zZy5kYXRhLmJ1ZmZlcnM7CiAgICAgICAgbGV0IHJlYWRlciA9IG5ldyBSZWFkZXIoYXJnQnVmZmVyLCB7IG5hbWU6ICdhcmdzJywgZGVidWc6IGZhbHNlIH0pOwogICAgICAgIGxldCB3cml0ZXIgPSBuZXcgV3JpdGVyKHJlc3VsdEJ1ZmZlciwgeyBuYW1lOiAncmVzdWx0cycsIGRlYnVnOiBmYWxzZSB9KTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CiAgICB9CiAgfTsKCn0oKSk7Cgo=', null, false); +var WorkerFactory = createBase64WorkerFactory('Lyogcm9sbHVwLXBsdWdpbi13ZWItd29ya2VyLWxvYWRlciAqLwooZnVuY3Rpb24gKCkgewogICd1c2Ugc3RyaWN0JzsKCiAgbGV0IEZJTkFMSVpFRCA9IDB4ZGVhZGJlZWY7CgogIGxldCBXUklURUFCTEUgPSAwOwogIGxldCBSRUFEQUJMRSA9IDE7CgogIGNsYXNzIFJlYWRlciB7CiAgICBjb25zdHJ1Y3RvcigKICAgICAgYnVmZmVyLAogICAgICB7IGluaXRpYWxPZmZzZXQgPSA0LCB1c2VBdG9taWNzID0gdHJ1ZSwgc3RyZWFtID0gdHJ1ZSwgZGVidWcsIG5hbWUgfSA9IHt9CiAgICApIHsKICAgICAgdGhpcy5idWZmZXIgPSBidWZmZXI7CiAgICAgIHRoaXMuYXRvbWljVmlldyA9IG5ldyBJbnQzMkFycmF5KGJ1ZmZlcik7CiAgICAgIHRoaXMub2Zmc2V0ID0gaW5pdGlhbE9mZnNldDsKICAgICAgdGhpcy51c2VBdG9taWNzID0gdXNlQXRvbWljczsKICAgICAgdGhpcy5zdHJlYW0gPSBzdHJlYW07CiAgICAgIHRoaXMuZGVidWcgPSBkZWJ1ZzsKICAgICAgdGhpcy5uYW1lID0gbmFtZTsKICAgIH0KCiAgICBsb2coLi4uYXJncykgewogICAgICBpZiAodGhpcy5kZWJ1ZykgewogICAgICAgIGNvbnNvbGUubG9nKGBbcmVhZGVyOiAke3RoaXMubmFtZX1dYCwgLi4uYXJncyk7CiAgICAgIH0KICAgIH0KCiAgICB3YWl0V3JpdGUobmFtZSkgewogICAgICBpZiAodGhpcy51c2VBdG9taWNzKSB7CiAgICAgICAgdGhpcy5sb2coYHdhaXRpbmcgZm9yICR7bmFtZX1gKTsKCiAgICAgICAgd2hpbGUgKEF0b21pY3MubG9hZCh0aGlzLmF0b21pY1ZpZXcsIDApID09PSBXUklURUFCTEUpIHsKICAgICAgICAgIC8vIGNvbnNvbGUubG9nKCd3YWl0aW5nIGZvciB3cml0ZS4uLicpOwogICAgICAgICAgQXRvbWljcy53YWl0KHRoaXMuYXRvbWljVmlldywgMCwgV1JJVEVBQkxFLCA1MDApOwogICAgICAgIH0KCiAgICAgICAgdGhpcy5sb2coYHJlc3VtZWQgZm9yICR7bmFtZX1gKTsKICAgICAgfSBlbHNlIHsKICAgICAgICBpZiAodGhpcy5hdG9taWNWaWV3WzBdICE9PSBSRUFEQUJMRSkgewogICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdgd2FpdFdyaXRlYCBleHBlY3RlZCBhcnJheSB0byBiZSByZWFkYWJsZScpOwogICAgICAgIH0KICAgICAgfQogICAgfQoKICAgIGZsaXAoKSB7CiAgICAgIHRoaXMubG9nKCdmbGlwJyk7CiAgICAgIGlmICh0aGlzLnVzZUF0b21pY3MpIHsKICAgICAgICBsZXQgcHJldiA9IEF0b21pY3MuY29tcGFyZUV4Y2hhbmdlKAogICAgICAgICAgdGhpcy5hdG9taWNWaWV3LAogICAgICAgICAgMCwKICAgICAgICAgIFJFQURBQkxFLAogICAgICAgICAgV1JJVEVBQkxFCiAgICAgICAgKTsKCiAgICAgICAgaWYgKHByZXYgIT09IFJFQURBQkxFKSB7CiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1JlYWQgZGF0YSBvdXQgb2Ygc3luYyEgVGhpcyBpcyBkaXNhc3Ryb3VzJyk7CiAgICAgICAgfQoKICAgICAgICBBdG9taWNzLm5vdGlmeSh0aGlzLmF0b21pY1ZpZXcsIDApOwogICAgICB9IGVsc2UgewogICAgICAgIHRoaXMuYXRvbWljVmlld1swXSA9IFdSSVRFQUJMRTsKICAgICAgfQoKICAgICAgdGhpcy5vZmZzZXQgPSA0OwogICAgfQoKICAgIGRvbmUoKSB7CiAgICAgIHRoaXMud2FpdFdyaXRlKCdkb25lJyk7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0KTsKICAgICAgbGV0IGRvbmUgPSBkYXRhVmlldy5nZXRVaW50MzIoMCkgPT09IEZJTkFMSVpFRDsKCiAgICAgIGlmIChkb25lKSB7CiAgICAgICAgdGhpcy5sb2coJ2RvbmUnKTsKICAgICAgICB0aGlzLmZsaXAoKTsKICAgICAgfQoKICAgICAgcmV0dXJuIGRvbmU7CiAgICB9CgogICAgcGVlayhmbikgewogICAgICB0aGlzLnBlZWtPZmZzZXQgPSB0aGlzLm9mZnNldDsKICAgICAgbGV0IHJlcyA9IGZuKCk7CiAgICAgIHRoaXMub2Zmc2V0ID0gdGhpcy5wZWVrT2Zmc2V0OwogICAgICB0aGlzLnBlZWtPZmZzZXQgPSBudWxsOwogICAgICByZXR1cm4gcmVzOwogICAgfQoKICAgIHN0cmluZygpIHsKICAgICAgdGhpcy53YWl0V3JpdGUoJ3N0cmluZycpOwoKICAgICAgbGV0IGJ5dGVMZW5ndGggPSB0aGlzLl9pbnQzMigpOwogICAgICBsZXQgbGVuZ3RoID0gYnl0ZUxlbmd0aCAvIDI7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0LCBieXRlTGVuZ3RoKTsKICAgICAgbGV0IGNoYXJzID0gW107CiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbGVuZ3RoOyBpKyspIHsKICAgICAgICBjaGFycy5wdXNoKGRhdGFWaWV3LmdldFVpbnQxNihpICogMikpOwogICAgICB9CiAgICAgIGxldCBzdHIgPSBTdHJpbmcuZnJvbUNoYXJDb2RlLmFwcGx5KG51bGwsIGNoYXJzKTsKICAgICAgdGhpcy5sb2coJ3N0cmluZycsIHN0cik7CgogICAgICB0aGlzLm9mZnNldCArPSBieXRlTGVuZ3RoOwoKICAgICAgaWYgKHRoaXMucGVla09mZnNldCA9PSBudWxsKSB7CiAgICAgICAgdGhpcy5mbGlwKCk7CiAgICAgIH0KICAgICAgcmV0dXJuIHN0cjsKICAgIH0KCiAgICBfaW50MzIoKSB7CiAgICAgIGxldCBieXRlTGVuZ3RoID0gNDsKCiAgICAgIGxldCBkYXRhVmlldyA9IG5ldyBEYXRhVmlldyh0aGlzLmJ1ZmZlciwgdGhpcy5vZmZzZXQpOwogICAgICBsZXQgbnVtID0gZGF0YVZpZXcuZ2V0SW50MzIoKTsKICAgICAgdGhpcy5sb2coJ19pbnQzMicsIG51bSk7CgogICAgICB0aGlzLm9mZnNldCArPSBieXRlTGVuZ3RoOwogICAgICByZXR1cm4gbnVtOwogICAgfQoKICAgIGludDMyKCkgewogICAgICB0aGlzLndhaXRXcml0ZSgnaW50MzInKTsKICAgICAgbGV0IG51bSA9IHRoaXMuX2ludDMyKCk7CiAgICAgIHRoaXMubG9nKCdpbnQzMicsIG51bSk7CgogICAgICBpZiAodGhpcy5wZWVrT2Zmc2V0ID09IG51bGwpIHsKICAgICAgICB0aGlzLmZsaXAoKTsKICAgICAgfQogICAgICByZXR1cm4gbnVtOwogICAgfQoKICAgIGJ5dGVzKCkgewogICAgICB0aGlzLndhaXRXcml0ZSgnYnl0ZXMnKTsKCiAgICAgIGxldCBieXRlTGVuZ3RoID0gdGhpcy5faW50MzIoKTsKCiAgICAgIGxldCBieXRlcyA9IG5ldyBBcnJheUJ1ZmZlcihieXRlTGVuZ3RoKTsKICAgICAgbmV3IFVpbnQ4QXJyYXkoYnl0ZXMpLnNldCgKICAgICAgICBuZXcgVWludDhBcnJheSh0aGlzLmJ1ZmZlciwgdGhpcy5vZmZzZXQsIGJ5dGVMZW5ndGgpCiAgICAgICk7CiAgICAgIHRoaXMubG9nKCdieXRlcycsIGJ5dGVzKTsKCiAgICAgIHRoaXMub2Zmc2V0ICs9IGJ5dGVMZW5ndGg7CgogICAgICBpZiAodGhpcy5wZWVrT2Zmc2V0ID09IG51bGwpIHsKICAgICAgICB0aGlzLmZsaXAoKTsKICAgICAgfQogICAgICByZXR1cm4gYnl0ZXM7CiAgICB9CiAgfQoKICBjbGFzcyBXcml0ZXIgewogICAgY29uc3RydWN0b3IoCiAgICAgIGJ1ZmZlciwKICAgICAgeyBpbml0aWFsT2Zmc2V0ID0gNCwgdXNlQXRvbWljcyA9IHRydWUsIHN0cmVhbSA9IHRydWUsIGRlYnVnLCBuYW1lIH0gPSB7fQogICAgKSB7CiAgICAgIHRoaXMuYnVmZmVyID0gYnVmZmVyOwogICAgICB0aGlzLmF0b21pY1ZpZXcgPSBuZXcgSW50MzJBcnJheShidWZmZXIpOwogICAgICB0aGlzLm9mZnNldCA9IGluaXRpYWxPZmZzZXQ7CiAgICAgIHRoaXMudXNlQXRvbWljcyA9IHVzZUF0b21pY3M7CiAgICAgIHRoaXMuc3RyZWFtID0gc3RyZWFtOwoKICAgICAgdGhpcy5kZWJ1ZyA9IGRlYnVnOwogICAgICB0aGlzLm5hbWUgPSBuYW1lOwoKICAgICAgaWYgKHRoaXMudXNlQXRvbWljcykgewogICAgICAgIC8vIFRoZSBidWZmZXIgc3RhcnRzIG91dCBhcyB3cml0ZWFibGUKICAgICAgICBBdG9taWNzLnN0b3JlKHRoaXMuYXRvbWljVmlldywgMCwgV1JJVEVBQkxFKTsKICAgICAgfSBlbHNlIHsKICAgICAgICB0aGlzLmF0b21pY1ZpZXdbMF0gPSBXUklURUFCTEU7CiAgICAgIH0KICAgIH0KCiAgICBsb2coLi4uYXJncykgewogICAgICBpZiAodGhpcy5kZWJ1ZykgewogICAgICAgIGNvbnNvbGUubG9nKGBbd3JpdGVyOiAke3RoaXMubmFtZX1dYCwgLi4uYXJncyk7CiAgICAgIH0KICAgIH0KCiAgICB3YWl0UmVhZChuYW1lKSB7CiAgICAgIGlmICh0aGlzLnVzZUF0b21pY3MpIHsKICAgICAgICB0aGlzLmxvZyhgd2FpdGluZyBmb3IgJHtuYW1lfWApOwogICAgICAgIC8vIFN3aXRjaCB0byB3cml0YWJsZQogICAgICAgIC8vIEF0b21pY3Muc3RvcmUodGhpcy5hdG9taWNWaWV3LCAwLCAxKTsKCiAgICAgICAgbGV0IHByZXYgPSBBdG9taWNzLmNvbXBhcmVFeGNoYW5nZSgKICAgICAgICAgIHRoaXMuYXRvbWljVmlldywKICAgICAgICAgIDAsCiAgICAgICAgICBXUklURUFCTEUsCiAgICAgICAgICBSRUFEQUJMRQogICAgICAgICk7CgogICAgICAgIGlmIChwcmV2ICE9PSBXUklURUFCTEUpIHsKICAgICAgICAgIHRocm93IG5ldyBFcnJvcigKICAgICAgICAgICAgJ1dyb3RlIHNvbWV0aGluZyBpbnRvIHVud3JpdGFibGUgYnVmZmVyISBUaGlzIGlzIGRpc2FzdHJvdXMnCiAgICAgICAgICApOwogICAgICAgIH0KCiAgICAgICAgQXRvbWljcy5ub3RpZnkodGhpcy5hdG9taWNWaWV3LCAwKTsKCiAgICAgICAgd2hpbGUgKEF0b21pY3MubG9hZCh0aGlzLmF0b21pY1ZpZXcsIDApID09PSBSRUFEQUJMRSkgewogICAgICAgICAgLy8gY29uc29sZS5sb2coJ3dhaXRpbmcgdG8gYmUgcmVhZC4uLicpOwogICAgICAgICAgQXRvbWljcy53YWl0KHRoaXMuYXRvbWljVmlldywgMCwgUkVBREFCTEUsIDUwMCk7CiAgICAgICAgfQoKICAgICAgICB0aGlzLmxvZyhgcmVzdW1lZCBmb3IgJHtuYW1lfWApOwogICAgICB9IGVsc2UgewogICAgICAgIHRoaXMuYXRvbWljVmlld1swXSA9IFJFQURBQkxFOwogICAgICB9CgogICAgICB0aGlzLm9mZnNldCA9IDQ7CiAgICB9CgogICAgZmluYWxpemUoKSB7CiAgICAgIHRoaXMubG9nKCdmaW5hbGl6aW5nJyk7CiAgICAgIGxldCBkYXRhVmlldyA9IG5ldyBEYXRhVmlldyh0aGlzLmJ1ZmZlciwgdGhpcy5vZmZzZXQpOwogICAgICBkYXRhVmlldy5zZXRVaW50MzIoMCwgRklOQUxJWkVEKTsKICAgICAgdGhpcy53YWl0UmVhZCgnZmluYWxpemUnKTsKICAgIH0KCiAgICBzdHJpbmcoc3RyKSB7CiAgICAgIHRoaXMubG9nKCdzdHJpbmcnLCBzdHIpOwoKICAgICAgbGV0IGJ5dGVMZW5ndGggPSBzdHIubGVuZ3RoICogMjsKICAgICAgdGhpcy5faW50MzIoYnl0ZUxlbmd0aCk7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0LCBieXRlTGVuZ3RoKTsKICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBzdHIubGVuZ3RoOyBpKyspIHsKICAgICAgICBkYXRhVmlldy5zZXRVaW50MTYoaSAqIDIsIHN0ci5jaGFyQ29kZUF0KGkpKTsKICAgICAgfQoKICAgICAgdGhpcy5vZmZzZXQgKz0gYnl0ZUxlbmd0aDsKICAgICAgdGhpcy53YWl0UmVhZCgnc3RyaW5nJyk7CiAgICB9CgogICAgX2ludDMyKG51bSkgewogICAgICBsZXQgYnl0ZUxlbmd0aCA9IDQ7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0KTsKICAgICAgZGF0YVZpZXcuc2V0SW50MzIoMCwgbnVtKTsKCiAgICAgIHRoaXMub2Zmc2V0ICs9IGJ5dGVMZW5ndGg7CiAgICB9CgogICAgaW50MzIobnVtKSB7CiAgICAgIHRoaXMubG9nKCdpbnQzMicsIG51bSk7CiAgICAgIHRoaXMuX2ludDMyKG51bSk7CiAgICAgIHRoaXMud2FpdFJlYWQoJ2ludDMyJyk7CiAgICB9CgogICAgYnl0ZXMoYnVmZmVyKSB7CiAgICAgIHRoaXMubG9nKCdieXRlcycsIGJ1ZmZlcik7CgogICAgICBsZXQgYnl0ZUxlbmd0aCA9IGJ1ZmZlci5ieXRlTGVuZ3RoOwogICAgICB0aGlzLl9pbnQzMihieXRlTGVuZ3RoKTsKICAgICAgbmV3IFVpbnQ4QXJyYXkodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0KS5zZXQobmV3IFVpbnQ4QXJyYXkoYnVmZmVyKSk7CgogICAgICB0aGlzLm9mZnNldCArPSBieXRlTGVuZ3RoOwogICAgICB0aGlzLndhaXRSZWFkKCdieXRlcycpOwogICAgfQogIH0KCiAgLy8gTm9vcHMgaW4gcHJvZAogIGFzeW5jIGZ1bmN0aW9uIGVuZCgpIHt9CgogIGxldCBpc1Byb2JhYmx5U2FmYXJpID0gL14oKD8hY2hyb21lfGFuZHJvaWQpLikqc2FmYXJpL2kudGVzdCgKICAgIG5hdmlnYXRvci51c2VyQWdlbnQKICApOwoKICBsZXQgb3BlbkRicyA9IG5ldyBNYXAoKTsKICBsZXQgdHJhbnNhY3Rpb25zID0gbmV3IE1hcCgpOwoKICBmdW5jdGlvbiBhc3NlcnQoY29uZCwgbXNnKSB7CiAgICBpZiAoIWNvbmQpIHsKICAgICAgdGhyb3cgbmV3IEVycm9yKG1zZyk7CiAgICB9CiAgfQoKICBsZXQgTE9DS19UWVBFUyA9IHsKICAgIE5PTkU6IDAsCiAgICBTSEFSRUQ6IDEsCiAgICBSRVNFUlZFRDogMiwKICAgIFBFTkRJTkc6IDMsCiAgICBFWENMVVNJVkU6IDQKICB9OwoKICAvLyBXZSB1c2UgbG9uZy1saXZlZCB0cmFuc2FjdGlvbnMsIGFuZCBgVHJhbnNhY3Rpb25gIGtlZXBzIHRoZQogIC8vIHRyYW5zYWN0aW9uIHN0YXRlLiBJdCBpbXBsZW1lbnRzIGFuIG9wdGltYWwgd2F5IHRvIHBlcmZvcm0KICAvLyByZWFkL3dyaXRlcyB3aXRoIGtub3dsZWRnZSBvZiBob3cgc3FsaXRlIGFza3MgZm9yIHRoZW0sIGFuZCBhbHNvCiAgLy8gaW1wbGVtZW50cyBhIGxvY2tpbmcgbWVjaGFuaXNtIHRoYXQgbWFwcyB0byBob3cgc3FsaXRlIGxvY2tzIHdvcmsuCiAgY2xhc3MgVHJhbnNhY3Rpb24gewogICAgY29uc3RydWN0b3IoZGIsIGluaXRpYWxNb2RlID0gJ3JlYWRvbmx5JykgewogICAgICB0aGlzLmRiID0gZGI7CiAgICAgIHRoaXMudHJhbnMgPSB0aGlzLmRiLnRyYW5zYWN0aW9uKFsnZGF0YSddLCBpbml0aWFsTW9kZSk7CiAgICAgIHRoaXMuc3RvcmUgPSB0aGlzLnRyYW5zLm9iamVjdFN0b3JlKCdkYXRhJyk7CiAgICAgIHRoaXMubG9ja1R5cGUgPQogICAgICAgIGluaXRpYWxNb2RlID09PSAncmVhZG9ubHknID8gTE9DS19UWVBFUy5TSEFSRUQgOiBMT0NLX1RZUEVTLkVYQ0xVU0lWRTsKCiAgICAgIC8vIFRoZXJlIGlzIG5vIG5lZWQgZm9yIHVzIHRvIGNhY2hlIGJsb2Nrcy4gVXNlIHNxbGl0ZSdzCiAgICAgIC8vIGBjYWNoZV9zaXplYCBmb3IgdGhhdCBhbmQgaXQgd2lsbCBhdXRvbWF0aWNhbGx5IGRvIGl0LiBIb3dldmVyLAogICAgICAvLyB3ZSBkbyBzdGlsbCBrZWVwIGEgY2FjaGUgb2YgdGhlIGZpcnN0IGJsb2NrIGZvciB0aGUgZHVyYXRpb24gb2YKICAgICAgLy8gdGhpcyB0cmFuc2FjdGlvbiBiZWNhdXNlIG9mIGhvdyBsb2NraW5nIHdvcmtzOyB0aGlzIGF2b2lkcyBhCiAgICAgIC8vIGZldyBleHRyYSByZWFkcyBhbmQgYWxsb3dzIHVzIHRvIGRldGVjdCBjaGFuZ2VzIGR1cmluZwogICAgICAvLyB1cGdyYWRpbmcgKHNlZSBgdXBncmFkZUV4Y2x1c2l2ZWApCiAgICAgIHRoaXMuY2FjaGVkRmlyc3RCbG9jayA9IG51bGw7CgogICAgICB0aGlzLmN1cnNvciA9IG51bGw7CiAgICAgIHRoaXMucHJldlJlYWRzID0gbnVsbDsKICAgIH0KCiAgICBhc3luYyBwcmVmZXRjaEZpcnN0QmxvY2sodGltZW91dCkgewogICAgICAvLyBUT0RPOiBpbXBsZW1lbnQgdGltZW91dAoKICAgICAgLy8gR2V0IHRoZSBmaXJzdCBibG9jayBhbmQgY2FjaGUgaXQKICAgICAgbGV0IGJsb2NrID0gYXdhaXQgdGhpcy5nZXQoMCk7CiAgICAgIHRoaXMuY2FjaGVkRmlyc3RCbG9jayA9IGJsb2NrOwogICAgICByZXR1cm4gYmxvY2s7CiAgICB9CgogICAgYXN5bmMgd2FpdENvbXBsZXRlKCkgewogICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4gewogICAgICAgIC8vIEVhZ2VybHkgY29tbWl0IGl0IGZvciBiZXR0ZXIgcGVyZi4gTm90ZSB0aGF0ICoqdGhpcyBhc3N1bWVzCiAgICAgICAgLy8gdGhlIHRyYW5zYWN0aW9uIGlzIG9wZW4qKiBhcyBgY29tbWl0YCB3aWxsIHRocm93IGFuIGVycm9yIGlmCiAgICAgICAgLy8gaXQncyBhbHJlYWR5IGNsb3NlZCAod2hpY2ggc2hvdWxkIG5ldmVyIGJlIHRoZSBjYXNlIGZvciB1cykKICAgICAgICB0aGlzLmNvbW1pdCgpOwoKICAgICAgICBpZiAodGhpcy5sb2NrVHlwZSA9PT0gTE9DS19UWVBFUy5FWENMVVNJVkUpIHsKICAgICAgICAgIC8vIFdhaXQgdW50aWwgYWxsIHdyaXRlcyBhcmUgY29tbWl0dGVkCiAgICAgICAgICB0aGlzLnRyYW5zLm9uY29tcGxldGUgPSBlID0+IHJlc29sdmUoKTsKCiAgICAgICAgICAvLyBUT0RPOiBJcyBpdCBPSyB0byBhZGQgdGhpcyBsYXRlciwgYWZ0ZXIgYW4gZXJyb3IgbWlnaHQgaGF2ZQogICAgICAgICAgLy8gaGFwcGVuZWQ/IFdpbGwgaXQgaG9sZCB0aGUgZXJyb3IgYW5kIGZpcmUgdGhpcyB3aGVuIHdlCiAgICAgICAgICAvLyBhdHRhY2hlZCBpdD8gV2UgbWlnaHQgd2FudCB0byBlYWdlcmx5IGNyZWF0ZSB0aGUgcHJvbWlzZQogICAgICAgICAgLy8gd2hlbiBjcmVhdGluZyB0aGUgdHJhbnNhY3Rpb24gYW5kIHJldHVybiBpdCBoZXJlCiAgICAgICAgICB0aGlzLnRyYW5zLm9uZXJyb3IgPSBlID0+IHJlamVjdChlKTsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgaWYgKGlzUHJvYmFibHlTYWZhcmkpIHsKICAgICAgICAgICAgLy8gU2FmYXJpIGhhcyBhIGJ1ZyB3aGVyZSBzb21ldGltZXMgdGhlIElEQiBnZXRzIGJsb2NrZWQKICAgICAgICAgICAgLy8gcGVybWFuZW50bHkgaWYgeW91IHJlZnJlc2ggdGhlIHBhZ2Ugd2l0aCBhbiBvcGVuCiAgICAgICAgICAgIC8vIHRyYW5zYWN0aW9uLiBZb3UgaGF2ZSB0byByZXN0YXJ0IHRoZSBicm93c2VyIHRvIGZpeCBpdC4KICAgICAgICAgICAgLy8gV2Ugd2FpdCBmb3IgcmVhZG9ubHkgdHJhbnNhY3Rpb25zIHRvIGZpbmlzaCB0b28sIGJ1dCB0aGlzCiAgICAgICAgICAgIC8vIGlzIGEgcGVyZiBoaXQKICAgICAgICAgICAgdGhpcy50cmFucy5vbmNvbXBsZXRlID0gZSA9PiByZXNvbHZlKCk7CiAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAvLyBObyBuZWVkIHRvIHdhaXQgb24gYW55dGhpbmcgaW4gYSByZWFkLW9ubHkgdHJhbnNhY3Rpb24uCiAgICAgICAgICAgIC8vIE5vdGUgdGhhdCBlcnJvcnMgZHVyaW5nIHJlYWRzIGFyZWEgYWx3YXlzIGhhbmRsZWQgYnkgdGhlCiAgICAgICAgICAgIC8vIHJlYWQgcmVxdWVzdC4KICAgICAgICAgICAgcmVzb2x2ZSgpOwogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSk7CiAgICB9CgogICAgY29tbWl0KCkgewogICAgICAvLyBTYWZhcmkgZG9lc24ndCBzdXBwb3J0IHRoaXMgbWV0aG9kIHlldCAodGhpcyBpcyBqdXN0IGFuCiAgICAgIC8vIG9wdGltaXphdGlvbikKICAgICAgaWYgKHRoaXMudHJhbnMuY29tbWl0KSB7CiAgICAgICAgdGhpcy50cmFucy5jb21taXQoKTsKICAgICAgfQogICAgfQoKICAgIGFzeW5jIHVwZ3JhZGVFeGNsdXNpdmUoKSB7CiAgICAgIHRoaXMuY29tbWl0KCk7CgogICAgICAvLyBjb25zb2xlLmxvZygndXBkYXRpbmcgdHJhbnNhY3Rpb24gcmVhZHdyaXRlJyk7CiAgICAgIHRoaXMudHJhbnMgPSB0aGlzLmRiLnRyYW5zYWN0aW9uKFsnZGF0YSddLCAncmVhZHdyaXRlJyk7CiAgICAgIHRoaXMuc3RvcmUgPSB0aGlzLnRyYW5zLm9iamVjdFN0b3JlKCdkYXRhJyk7CiAgICAgIHRoaXMubG9ja1R5cGUgPSBMT0NLX1RZUEVTLkVYQ0xVU0lWRTsKCiAgICAgIGxldCBjYWNoZWQwID0gdGhpcy5jYWNoZWRGaXJzdEJsb2NrOwoKICAgICAgLy8gRG8gYSByZWFkCiAgICAgIGxldCBibG9jayA9IGF3YWl0IHRoaXMucHJlZmV0Y2hGaXJzdEJsb2NrKDUwMCk7CiAgICAgIC8vIFRPRE86IHdoZW4gdGltZW91dHMgYXJlIGltcGxlbWVudGVkLCBkZXRlY3QgdGltZW91dCBhbmQgcmV0dXJuIEJVU1kKCiAgICAgIGlmIChjYWNoZWQwID09IG51bGwgJiYgYmxvY2sgPT0gbnVsbCkgewogICAgICAgIHJldHVybiB0cnVlOwogICAgICB9IGVsc2UgewogICAgICAgIGZvciAobGV0IGkgPSAyNDsgaSA8IDQwOyBpKyspIHsKICAgICAgICAgIGlmIChibG9ja1tpXSAhPT0gY2FjaGVkMFtpXSkgewogICAgICAgICAgICByZXR1cm4gZmFsc2U7CiAgICAgICAgICB9CiAgICAgICAgfQogICAgICB9CgogICAgICByZXR1cm4gdHJ1ZTsKICAgIH0KCiAgICBkb3duZ3JhZGVTaGFyZWQoKSB7CiAgICAgIHRoaXMuY29tbWl0KCk7CgogICAgICAvLyBjb25zb2xlLmxvZygnZG93bmdyYWRpbmcgdHJhbnNhY3Rpb24gcmVhZG9ubHknKTsKICAgICAgdGhpcy50cmFucyA9IHRoaXMuZGIudHJhbnNhY3Rpb24oWydkYXRhJ10sICdyZWFkb25seScpOwogICAgICB0aGlzLnN0b3JlID0gdGhpcy50cmFucy5vYmplY3RTdG9yZSgnZGF0YScpOwogICAgICB0aGlzLmxvY2tUeXBlID0gTE9DS19UWVBFUy5TSEFSRUQ7CiAgICB9CgogICAgYXN5bmMgZ2V0KGtleSkgewogICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4gewogICAgICAgIGxldCByZXEgPSB0aGlzLnN0b3JlLmdldChrZXkpOwogICAgICAgIHJlcS5vbnN1Y2Nlc3MgPSBlID0+IHsKICAgICAgICAgIHJlc29sdmUocmVxLnJlc3VsdCk7CiAgICAgICAgfTsKICAgICAgICByZXEub25lcnJvciA9IGUgPT4gcmVqZWN0KGUpOwogICAgICB9KTsKICAgIH0KCiAgICBnZXRSZWFkRGlyZWN0aW9uKCkgewogICAgICAvLyBUaGVyZSBhcmUgYSB0d28gd2F5cyB3ZSBjYW4gcmVhZCBkYXRhOiBhIGRpcmVjdCBgZ2V0YCByZXF1ZXN0CiAgICAgIC8vIG9yIG9wZW5pbmcgYSBjdXJzb3IgYW5kIGl0ZXJhdGluZyB0aHJvdWdoIGRhdGEuIFdlIGRvbid0IGtub3cKICAgICAgLy8gd2hhdCBmdXR1cmUgcmVhZHMgbG9vayBsaWtlLCBzbyB3ZSBkb24ndCBrbm93IHRoZSBiZXN0IHN0cmF0ZWd5CiAgICAgIC8vIHRvIHBpY2suIEFsd2F5cyBjaG9vc2luZyBvbmUgc3RyYXRlZ3kgZm9yZ29lcyBhIGxvdCBvZgogICAgICAvLyBvcHRpbWl6YXRpb24sIGJlY2F1c2UgaXRlcmF0aW5nIHdpdGggYSBjdXJzb3IgaXMgYSBsb3QgZmFzdGVyCiAgICAgIC8vIHRoYW4gbWFueSBgZ2V0YCBjYWxscy4gT24gdGhlIG90aGVyIGhhbmQsIG9wZW5pbmcgYSBjdXJzb3IgaXMKICAgICAgLy8gc2xvdywgYW5kIHNvIGlzIGNhbGxpbmcgYGFkdmFuY2VgIHRvIG1vdmUgYSBjdXJzb3Igb3ZlciBhIGh1Z2UKICAgICAgLy8gcmFuZ2UgKGxpa2UgbW92aW5nIGl0IDEwMDAgaXRlbXMgbGF0ZXIpLCBzbyBtYW55IGBnZXRgIGNhbGxzIHdvdWxkCiAgICAgIC8vIGJlIGZhc3Rlci4gSW4gZ2VuZXJhbDoKICAgICAgLy8KICAgICAgLy8gKiBNYW55IGBnZXRgIGNhbGxzIGFyZSBmYXN0ZXIgd2hlbiBkb2luZyByYW5kb20gYWNjZXNzZXMKICAgICAgLy8gKiBJdGVyYXRpbmcgd2l0aCBhIGN1cnNvciBpcyBmYXN0ZXIgaWYgZG9pbmcgbW9zdGx5IHNlcXVlbnRpYWwKICAgICAgLy8gICBhY2Nlc3NlcwogICAgICAvLwogICAgICAvLyBXZSBpbXBsZW1lbnQgYSBoZXVyaXN0aWMgYW5kIGtlZXBzIHRyYWNrIG9mIHRoZSBsYXN0IDMgcmVhZHMKICAgICAgLy8gYW5kIGRldGVjdHMgd2hlbiB0aGV5IGFyZSBtb3N0bHkgc2VxdWVudGlhbC4gSWYgdGhleSBhcmUsIHdlCiAgICAgIC8vIG9wZW4gYSBjdXJzb3IgYW5kIHN0YXJ0IHJlYWRpbmcgYnkgaXRlcmF0aW5nIGl0LiBJZiBub3QsIHdlIGRvCiAgICAgIC8vIGRpcmVjdCBgZ2V0YCBjYWxscy4KICAgICAgLy8KICAgICAgLy8gT24gdG9wIG9mIGFsbCBvZiB0aGlzLCBlYWNoIGJyb3dzZXIgaGFzIGRpZmZlcmVudCBwZXJmCiAgICAgIC8vIGNoYXJhY3RlcmlzdGljcy4gV2Ugd2lsbCBwcm9iYWJseSB3YW50IHRvIG1ha2UgdGhlc2UgdGhyZXNob2xkcwogICAgICAvLyBjb25maWd1cmFibGUgc28gdGhlIHVzZXIgY2FuIGNoYW5nZSB0aGVtIHBlci1icm93c2VyIGlmIG5lZWRlZCwKICAgICAgLy8gYXMgd2VsbCBhcyBmaW5lLXR1bmluZyB0aGVtIGZvciB0aGVpciB1c2FnZSBvZiBzcWxpdGUuCgogICAgICBsZXQgcHJldlJlYWRzID0gdGhpcy5wcmV2UmVhZHM7CiAgICAgIGlmIChwcmV2UmVhZHMpIHsKICAgICAgICAvLyBIYXMgdGhlcmUgYmVlbiAzIGZvcndhcmQgc2VxdWVudGlhbCByZWFkcyB3aXRoaW4gMTAgYmxvY2tzPwogICAgICAgIGlmICgKICAgICAgICAgIHByZXZSZWFkc1swXSA8IHByZXZSZWFkc1sxXSAmJgogICAgICAgICAgcHJldlJlYWRzWzFdIDwgcHJldlJlYWRzWzJdICYmCiAgICAgICAgICBwcmV2UmVhZHNbMl0gLSBwcmV2UmVhZHNbMF0gPCAxMAogICAgICAgICkgewogICAgICAgICAgcmV0dXJuICduZXh0JzsKICAgICAgICB9CgogICAgICAgIC8vIEhhcyB0aGVyZSBiZWVuIDMgYmFja3dhcmRzIHNlcXVlbnRpYWwgcmVhZHMgd2l0aGluIDEwIGJsb2Nrcz8KICAgICAgICBpZiAoCiAgICAgICAgICBwcmV2UmVhZHNbMF0gPiBwcmV2UmVhZHNbMV0gJiYKICAgICAgICAgIHByZXZSZWFkc1sxXSA+IHByZXZSZWFkc1syXSAmJgogICAgICAgICAgcHJldlJlYWRzWzBdIC0gcHJldlJlYWRzWzJdIDwgMTAKICAgICAgICApIHsKICAgICAgICAgIHJldHVybiAncHJldic7CiAgICAgICAgfQogICAgICB9CgogICAgICByZXR1cm4gbnVsbDsKICAgIH0KCiAgICByZWFkKHBvc2l0aW9uKSB7CiAgICAgIGxldCB3YWl0Q3Vyc29yID0gKCkgPT4gewogICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7CiAgICAgICAgICBpZiAodGhpcy5jdXJzb3JQcm9taXNlICE9IG51bGwpIHsKICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKAogICAgICAgICAgICAgICd3YWl0Q3Vyc29yKCkgY2FsbGVkIGJ1dCBzb21ldGhpbmcgZWxzZSBpcyBhbHJlYWR5IHdhaXRpbmcnCiAgICAgICAgICAgICk7CiAgICAgICAgICB9CiAgICAgICAgICB0aGlzLmN1cnNvclByb21pc2UgPSB7IHJlc29sdmUsIHJlamVjdCB9OwogICAgICAgIH0pOwogICAgICB9OwoKICAgICAgaWYgKHRoaXMuY3Vyc29yKSB7CiAgICAgICAgbGV0IGN1cnNvciA9IHRoaXMuY3Vyc29yOwoKICAgICAgICBpZiAoCiAgICAgICAgICBjdXJzb3IuZGlyZWN0aW9uID09PSAnbmV4dCcgJiYKICAgICAgICAgIHBvc2l0aW9uID4gY3Vyc29yLmtleSAmJgogICAgICAgICAgcG9zaXRpb24gPCBjdXJzb3Iua2V5ICsgMTAwCiAgICAgICAgKSB7CgogICAgICAgICAgY3Vyc29yLmFkdmFuY2UocG9zaXRpb24gLSBjdXJzb3Iua2V5KTsKICAgICAgICAgIHJldHVybiB3YWl0Q3Vyc29yKCk7CiAgICAgICAgfSBlbHNlIGlmICgKICAgICAgICAgIGN1cnNvci5kaXJlY3Rpb24gPT09ICdwcmV2JyAmJgogICAgICAgICAgcG9zaXRpb24gPCBjdXJzb3Iua2V5ICYmCiAgICAgICAgICBwb3NpdGlvbiA+IGN1cnNvci5rZXkgLSAxMDAKICAgICAgICApIHsKCiAgICAgICAgICBjdXJzb3IuYWR2YW5jZShjdXJzb3Iua2V5IC0gcG9zaXRpb24pOwogICAgICAgICAgcmV0dXJuIHdhaXRDdXJzb3IoKTsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgLy8gRGl0Y2ggdGhlIGN1cnNvcgogICAgICAgICAgdGhpcy5jdXJzb3IgPSBudWxsOwogICAgICAgICAgcmV0dXJuIHRoaXMucmVhZChwb3NpdGlvbik7CiAgICAgICAgfQogICAgICB9IGVsc2UgewogICAgICAgIC8vIFdlIGRvbid0IGFscmVhZHkgaGF2ZSBhIGN1cnNvci4gV2UgbmVlZCB0byBhIGZyZXNoIHJlYWQ7CiAgICAgICAgLy8gc2hvdWxkIHdlIG9wZW4gYSBjdXJzb3Igb3IgY2FsbCBgZ2V0YD8KCiAgICAgICAgbGV0IGRpciA9IHRoaXMuZ2V0UmVhZERpcmVjdGlvbigpOwogICAgICAgIGlmIChkaXIpIHsKICAgICAgICAgIC8vIE9wZW4gYSBjdXJzb3IKICAgICAgICAgIHRoaXMucHJldlJlYWRzID0gbnVsbDsKCiAgICAgICAgICBsZXQga2V5UmFuZ2U7CiAgICAgICAgICBpZiAoZGlyID09PSAncHJldicpIHsKICAgICAgICAgICAga2V5UmFuZ2UgPSBJREJLZXlSYW5nZS51cHBlckJvdW5kKHBvc2l0aW9uKTsKICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIGtleVJhbmdlID0gSURCS2V5UmFuZ2UubG93ZXJCb3VuZChwb3NpdGlvbik7CiAgICAgICAgICB9CgogICAgICAgICAgbGV0IHJlcSA9IHRoaXMuc3RvcmUub3BlbkN1cnNvcihrZXlSYW5nZSwgZGlyKTsKCiAgICAgICAgICByZXEub25zdWNjZXNzID0gZSA9PiB7CgogICAgICAgICAgICBsZXQgY3Vyc29yID0gZS50YXJnZXQucmVzdWx0OwogICAgICAgICAgICB0aGlzLmN1cnNvciA9IGN1cnNvcjsKCiAgICAgICAgICAgIGlmICh0aGlzLmN1cnNvclByb21pc2UgPT0gbnVsbCkgewogICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignR290IGRhdGEgZnJvbSBjdXJzb3IgYnV0IG5vdGhpbmcgaXMgd2FpdGluZyBpdCcpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHRoaXMuY3Vyc29yUHJvbWlzZS5yZXNvbHZlKGN1cnNvciA/IGN1cnNvci52YWx1ZSA6IG51bGwpOwogICAgICAgICAgICB0aGlzLmN1cnNvclByb21pc2UgPSBudWxsOwogICAgICAgICAgfTsKICAgICAgICAgIHJlcS5vbmVycm9yID0gZSA9PiB7CiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdDdXJzb3IgZmFpbHVyZTonLCBlKTsKCiAgICAgICAgICAgIGlmICh0aGlzLmN1cnNvclByb21pc2UgPT0gbnVsbCkgewogICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignR290IGRhdGEgZnJvbSBjdXJzb3IgYnV0IG5vdGhpbmcgaXMgd2FpdGluZyBpdCcpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHRoaXMuY3Vyc29yUHJvbWlzZS5yZWplY3QoZSk7CiAgICAgICAgICAgIHRoaXMuY3Vyc29yUHJvbWlzZSA9IG51bGw7CiAgICAgICAgICB9OwoKICAgICAgICAgIHJldHVybiB3YWl0Q3Vyc29yKCk7CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgIGlmICh0aGlzLnByZXZSZWFkcyA9PSBudWxsKSB7CiAgICAgICAgICAgIHRoaXMucHJldlJlYWRzID0gWzAsIDAsIDBdOwogICAgICAgICAgfQogICAgICAgICAgdGhpcy5wcmV2UmVhZHMucHVzaChwb3NpdGlvbik7CiAgICAgICAgICB0aGlzLnByZXZSZWFkcy5zaGlmdCgpOwoKICAgICAgICAgIHJldHVybiB0aGlzLmdldChwb3NpdGlvbik7CiAgICAgICAgfQogICAgICB9CiAgICB9CgogICAgYXN5bmMgc2V0KGl0ZW0pIHsKICAgICAgdGhpcy5wcmV2UmVhZHMgPSBudWxsOwoKICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHsKICAgICAgICBsZXQgcmVxID0gdGhpcy5zdG9yZS5wdXQoaXRlbS52YWx1ZSwgaXRlbS5rZXkpOwogICAgICAgIHJlcS5vbnN1Y2Nlc3MgPSBlID0+IHJlc29sdmUocmVxLnJlc3VsdCk7CiAgICAgICAgcmVxLm9uZXJyb3IgPSBlID0+IHJlamVjdChlKTsKICAgICAgfSk7CiAgICB9CgogICAgYXN5bmMgYnVsa1NldChpdGVtcykgewogICAgICB0aGlzLnByZXZSZWFkcyA9IG51bGw7CgogICAgICBmb3IgKGxldCBpdGVtIG9mIGl0ZW1zKSB7CiAgICAgICAgdGhpcy5zdG9yZS5wdXQoaXRlbS52YWx1ZSwgaXRlbS5rZXkpOwogICAgICB9CiAgICB9CiAgfQoKICBhc3luYyBmdW5jdGlvbiBsb2FkRGIobmFtZSkgewogICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHsKICAgICAgaWYgKG9wZW5EYnMuZ2V0KG5hbWUpKSB7CiAgICAgICAgcmVzb2x2ZShvcGVuRGJzLmdldChuYW1lKSk7CiAgICAgICAgcmV0dXJuOwogICAgICB9CgogICAgICBjb25zb2xlLmxvZygnb3BlbmluZycsIG5hbWUpOwoKICAgICAgbGV0IHJlcSA9IGdsb2JhbFRoaXMuaW5kZXhlZERCLm9wZW4obmFtZSwgMSk7CiAgICAgIHJlcS5vbnN1Y2Nlc3MgPSBldmVudCA9PiB7CiAgICAgICAgY29uc29sZS5sb2coJ2RiIGlzIG9wZW4hJywgbmFtZSk7CiAgICAgICAgbGV0IGRiID0gZXZlbnQudGFyZ2V0LnJlc3VsdDsKCiAgICAgICAgZGIub252ZXJzaW9uY2hhbmdlID0gKCkgPT4gewogICAgICAgICAgLy8gVE9ETzogTm90aWZ5IHRoZSB1c2VyIHNvbWVob3cKICAgICAgICAgIGNvbnNvbGUubG9nKCdjbG9zaW5nIGJlY2F1c2UgdmVyc2lvbiBjaGFuZ2VkJyk7CiAgICAgICAgICBkYi5jbG9zZSgpOwogICAgICAgICAgb3BlbkRicy5kZWxldGUobmFtZSk7CiAgICAgICAgfTsKCiAgICAgICAgZGIub25jbG9zZSA9ICgpID0+IHsKICAgICAgICAgIG9wZW5EYnMuZGVsZXRlKG5hbWUpOwogICAgICAgIH07CgogICAgICAgIG9wZW5EYnMuc2V0KG5hbWUsIGRiKTsKICAgICAgICByZXNvbHZlKGRiKTsKICAgICAgfTsKICAgICAgcmVxLm9udXBncmFkZW5lZWRlZCA9IGV2ZW50ID0+IHsKICAgICAgICBsZXQgZGIgPSBldmVudC50YXJnZXQucmVzdWx0OwogICAgICAgIGlmICghZGIub2JqZWN0U3RvcmVOYW1lcy5jb250YWlucygnZGF0YScpKSB7CiAgICAgICAgICBkYi5jcmVhdGVPYmplY3RTdG9yZSgnZGF0YScpOwogICAgICAgIH0KICAgICAgfTsKICAgICAgcmVxLm9uYmxvY2tlZCA9IGUgPT4gY29uc29sZS5sb2coJ2Jsb2NrZWQnLCBlKTsKICAgICAgcmVxLm9uZXJyb3IgPSByZXEub25hYm9ydCA9IGUgPT4gcmVqZWN0KGUudGFyZ2V0LmVycm9yKTsKICAgIH0pOwogIH0KCiAgZnVuY3Rpb24gY2xvc2VEYihuYW1lKSB7CiAgICBsZXQgb3BlbkRiID0gb3BlbkRicy5nZXQobmFtZSk7CiAgICBpZiAob3BlbkRiKSB7CiAgICAgIG9wZW5EYi5jbG9zZSgpOwogICAgICBvcGVuRGJzLmRlbGV0ZShuYW1lKTsKICAgIH0KICB9CgogIGZ1bmN0aW9uIGdldFRyYW5zYWN0aW9uKG5hbWUpIHsKICAgIHJldHVybiB0cmFuc2FjdGlvbnMuZ2V0KG5hbWUpOwogIH0KCiAgYXN5bmMgZnVuY3Rpb24gd2l0aFRyYW5zYWN0aW9uKG5hbWUsIG1vZGUsIGZ1bmMpIHsKICAgIGxldCB0cmFucyA9IHRyYW5zYWN0aW9ucy5nZXQobmFtZSk7CiAgICBpZiAodHJhbnMpIHsKICAgICAgLy8gSWYgYSB0cmFuc2FjdGlvbiBhbHJlYWR5IGV4aXN0cywgdGhhdCBtZWFucyB0aGUgZmlsZSBoYXMgYmVlbgogICAgICAvLyBsb2NrZWQuIFdlIGRvbid0IGZ1bGx5IHN1cHBvcnQgYXJiaXRyYXJ5IG5lc3RlZCB0cmFuc2FjdGlvbnMsCiAgICAgIC8vIGFzIHNlZW4gYmVsb3cgKHdlIHdvbid0IHVwZ3JhZGUgYSBgcmVhZG9ubHlgIHRvIGByZWFkd3JpdGVgCiAgICAgIC8vIGF1dG9tYXRpY2FsbHkpIGFuZCB0aGlzIGlzIG1haW5seSBmb3IgdGhlIHVzZSBjYXNlIHdoZXJlIHNxbGl0ZQogICAgICAvLyBsb2NrcyB0aGUgZGIgYW5kIGNyZWF0ZXMgYSB0cmFuc2FjdGlvbiBmb3IgdGhlIGR1cmFjdGlvbiBvZiB0aGUKICAgICAgLy8gbG9jay4gV2UgZG9uJ3QgYWN0dWFsbHkgd3JpdGUgY29kZSBpbiBhIHdheSB0aGF0IGFzc3VtZXMgbmVzdGVkCiAgICAgIC8vIHRyYW5zYWN0aW9ucywgc28ganVzdCBlcnJvciBoZXJlCiAgICAgIGlmIChtb2RlID09PSAncmVhZHdyaXRlJyAmJiB0cmFucy5sb2NrVHlwZSA9PT0gTE9DS19UWVBFUy5TSEFSRUQpIHsKICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0F0dGVtcHRlZCB3cml0ZSBidXQgb25seSBoYXMgU0hBUkVEIGxvY2snKTsKICAgICAgfQogICAgICByZXR1cm4gZnVuYyh0cmFucyk7CiAgICB9CgogICAgLy8gT3V0c2lkZSB0aGUgc2NvcGUgb2YgYSBsb2NrLCBjcmVhdGUgYSB0ZW1wb3JhcnkgdHJhbnNhY3Rpb24KICAgIHRyYW5zID0gbmV3IFRyYW5zYWN0aW9uKGF3YWl0IGxvYWREYihuYW1lKSwgbW9kZSk7CiAgICBhd2FpdCBmdW5jKHRyYW5zKTsKICAgIGF3YWl0IHRyYW5zLndhaXRDb21wbGV0ZSgpOwogIH0KCiAgLy8gTG9ja2luZyBzdHJhdGVneToKICAvLwogIC8vICogV2UgbWFwIHNxbGl0ZSdzIGxvY2tzIG9udG8gSW5kZXhlZERCJ3MgdHJhbnNhY3Rpb24gc2VtYW50aWNzLgogIC8vICAgUmVhZCB0cmFuc2FjdGlvbnMgbWF5IGV4ZWN1dGUgaW4gcGFyYWxsZWwuIFJlYWQvd3JpdGUKICAvLyAgIHRyYW5zYWN0aW9ucyBhcmUgcXVldWVkIHVwIGFuZCB3YWl0IHVudGlsIGFsbCBwcmVjZWRpbmcKICAvLyAgIHJlYWQgdHJhbnNhY3Rpb25zIGZpbmlzaCBleGVjdXRpbmcuIFJlYWQgdHJhbnNhY3Rpb25zIHN0YXJ0ZWQKICAvLyAgIGFmdGVyIGEgcmVhZC93cml0ZSB0cmFuc2FjdGlvbiB3YWl0IHVudGlsIGl0IGlzIGZpbmlzaGVkLgogIC8vCiAgLy8gKiBJREIgdHJhbnNhY3Rpb25zIHdpbGwgd2FpdCBmb3JldmVyIHVudGlsIHRoZXkgY2FuIGV4ZWN1dGUgKGZvcgogIC8vICAgZXhhbXBsZSwgdGhleSBtYXkgYmUgYmxvY2tlZCBvbiBhIHJlYWQvd3JpdGUgdHJhbnNhY3Rpb24pLiBXZQogIC8vICAgZG9uJ3Qgd2FudCB0byBhbGxvdyBzcWxpdGUgdHJhbnNhY3Rpb25zIHRvIHdhaXQgZm9yZXZlciwgc28KICAvLyAgIHdlIG1hbnVhbGx5IHRpbWVvdXQgaWYgYSB0cmFuc2FjdGlvbiB0YWtlcyB0b28gbG9uZyB0bwogIC8vICAgc3RhcnQgZXhlY3V0aW5nLiBUaGlzIHNpbXVsYXRlcyB0aGUgYmVoYXZpb3Igb2YgYSBzcWxpdGUKICAvLyAgIGJhaWxpbmcgaWYgaXQgY2FuJ3QgcmVxdWlyZSBhIGxvY2suCiAgLy8KICAvLyAqIEEgU0hBUkVEIGxvY2sgd2FudHMgdG8gcmVhZCBmcm9tIHRoZSBkYi4gV2Ugc3RhcnQgYSByZWFkCiAgLy8gICB0cmFuc2FjdGlvbiBhbmQgcmVhZCB0aGUgZmlyc3QgYmxvY2ssIGFuZCBpZiB3ZSByZWFkIGl0IHdpdGhpbgogIC8vICAgNTAwbXMgd2UgY29uc2lkZXIgdGhlIGxvY2sgc3VjY2Vzc2Z1bC4gT3RoZXJ3aXNlIHRoZSBsb2NrCiAgLy8gICBmYWlsZWQgYW5kIHdlIHJldHVybiBTUUxJVEVfQlVTWS4gKFRoZXJlJ3Mgbm8gcGVyZiBkb3duc2lkZQogIC8vICAgdG8gcmVhZGluZyB0aGUgZmlyc3QgYmxvY2sgLSBpdCBoYXMgdG8gYmUgcmVhZCBhbnl3YXkgdG8gY2hlY2sKICAvLyAgIGJ5dGVzIDI0LTM5IGZvciB0aGUgY2hhbmdlIGNvdW50ZXIpCiAgLy8KICAvLyAqIEEgUkVTRVJWRUQgbG9jayBtZWFucyB0aGUgZGIgd2FudHMgdG8gc3RhcnQgd3JpdGluZyAodGhpbmsgb2YKICAvLyAgIGBCRUdJTiBUUkFOU0FDVElPTmApLiBPbmx5IG9uZSBwcm9jZXNzIGNhbiBvYnRhaW4gYSBSRVNFUlZFRAogIC8vICAgbG9jayBhdCBhIHRpbWUsIGJ1dCBub3JtYWxseSBzcWxpdGUgc3RpbGwgbGVhZHMgbmV3IHJlYWQgbG9ja3MKICAvLyAgIGhhcHBlbi4gSXQgaXNuJ3QgdW50aWwgYW4gRVhDTFVTSVZFIGxvY2sgaXMgaGVsZCB0aGF0IHJlYWRzIGFyZQogIC8vICAgYmxvY2tlZC4gSG93ZXZlciwgc2luY2Ugd2UgbmVlZCB0byBndWFyYW50ZWUgb25seSBvbmUgUkVTRVJWRUQKICAvLyAgIGxvY2sgYXQgb25jZSAob3RoZXJ3aXNlIGRhdGEgY291bGQgY2hhbmdlIGZyb20gYW5vdGhlciBwcm9jZXNzCiAgLy8gICB3aXRoaW4gYSB0cmFuc2FjdGlvbiwgY2F1c2luZyBmYXVsdHkgY2FjaGVzIGV0YykgdGhlIHNpbXBsZXN0CiAgLy8gICB0aGluZyB0byBkbyBpcyBnbyBhaGVhZCBhbmQgZ3JhYiBhIHJlYWQvd3JpdGUgdHJhbnNhY3Rpb24gdGhhdAogIC8vICAgcmVwcmVzZW50cyB0aGUgUkVTRVJWRUQgbG9jay4gVGhpcyB3aWxsIGJsb2NrIGFsbCByZWFkcyBmcm9tCiAgLy8gICBoYXBwZW5pbmcsIGFuZCBpcyBlc3NlbnRpYWxseSB0aGUgc2FtZSBhcyBhbiBFWENMVVNJVkUgbG9jay4KICAvLwogIC8vICAgICAqIFRoZSBtYWluIHByb2JsZW0gaGVyZSBpcyB3ZSBjYW4ndCAidXBncmFkZSIgYSBgcmVhZG9ubHlgCiAgLy8gICAgICAgdHJhbnNhY3Rpb24gdG8gYHJlYWR3cml0ZWAsIGJ1dCBuYXRpdmUgc3FsaXRlIGNhbiB1cGdyYWRlIGEKICAvLyAgICAgICBsb2NrIGZyb20gU0hBUkVEIHRvIFJFU0VSVkVELiBXZSBuZWVkIHRvIHN0YXJ0IGEgbmV3CiAgLy8gICAgICAgdHJhbnNhY3Rpb24gdG8gZG8gc28sIGFuZCBiZWNhdXNlIG9mIHRoYXQgdGhlcmUgbWlnaHQgYmUKICAvLyAgICAgICBvdGhlciBgcmVhZHdyaXRlYCB0cmFuc2FjdGlvbnMgdGhhdCBnZXQgcnVuIGR1cmluZyB0aGUKICAvLyAgICAgICAidXBncmFkZSIgd2hpY2ggaW52YWxpZGF0ZXMgdGhlIHdob2xlIGxvY2tpbmcgcHJvY2VzcyBhbmQKICAvLyAgICAgICBhbmQgY29ycnVwdHMgZGF0YS4KICAvLwogIC8vICogSWRlYWxseSwgd2UgY291bGQgdGVsbCBzcWxpdGUgdG8gc2tpcCBTSEFSRUQgbG9ja3MgZW50aXJlbHkuIFdlCiAgLy8gICBkb24ndCBuZWVkIHRoZW0gc2luY2Ugd2UgY2FuIHJlbHkgb24gSW5kZXhlZERCJ3Mgc2VtYW50aWNzLgogIC8vICAgVGhlbiB3aGVuIGl0IHdhbnRzIHRvIHN0YXJ0IHdyaXRpbmcsIHdlIGdldCBhIFJFU0VSVkVEIGxvY2sKICAvLyAgIHdpdGhvdXQgaGF2aW5nIHRvIHVwZ3JhZGUgZnJvbSBTSEFSRUQuIFRoaXMgd291bGQgc2F2ZSB1cwogIC8vICAgdGhlIGNvc3Qgb2YgYSBgcmVhZG9ubHlgIHRyYW5zYWN0aW9uIHdoZW4gd3JpdGluZzsgcmlnaHQgbm93CiAgLy8gICBpdCBtdXN0IG9wZW4gYSBgcmVhZG9ubHlgIHRyYW5zYWN0aW9uIGFuZCB0aGVuIGltbWVkaWF0ZWx5IG9wZW4KICAvLyAgIGEgYHJlYWR3cml0ZWAgdG8gdXBncmFkZSBpdC4gSSB0aG91Z2h0IG9mIGRlZmVycmluZyBvcGVuaW5nIHRoZQogIC8vICAgYHJlYWRvbmx5YCB0cmFuc2FjdGlvbiB1bnRpbCBzb21ldGhpbmcgaXMgYWN0dWFsbHkgcmVhZCwgYnV0CiAgLy8gICB1bmZvcnR1bmF0ZWx5IHNxbGl0ZSBvcGVucyBpdCwgcmVhZHMgdGhlIGZpcnN0IGJsb2NrLCBhbmQgdGhlbgogIC8vICAgdXBncmFkZXMgaXQuIFNvIHRoZXJlJ3Mgbm8gd2F5IGFyb3VuZCBpdC4gKFdlIGNhbid0IGFzc3VtZSBpdCdzCiAgLy8gICBhIGByZWFkd3JpdGVgIHRyYW5zYWN0aW9uIGF0IHRoYXQgcG9pbnQgc2luY2UgdGhhdCB3b3VsZCBhc3N1bWUKICAvLyAgIGFsbCBTSEFSRUQgbG9ja3MgYXJlIGByZWFkd3JpdGVgLCByZW1vdmluZyB0aGUgcG9zc2liaWxpdHkgb2YKICAvLyAgIGNvbmN1cnJlbnQgcmVhZHMpLgogIC8vCiAgLy8gKiBVcGdyYWRpbmcgdG8gYW4gRVhDTFVTSVZFIGxvY2sgaXMgYSBub29wLCBzaW5jZSB3ZSB0cmVhdCBSRVNFUlZFRAogIC8vICAgbG9ja3MgYXMgRVhDTFVTSVZFLgogIGFzeW5jIGZ1bmN0aW9uIGhhbmRsZUxvY2sod3JpdGVyLCBuYW1lLCBsb2NrVHlwZSkgewogICAgLy8gY29uc29sZS5sb2coJ2xvY2tpbmcnLCBuYW1lLCBsb2NrVHlwZSwgcGVyZm9ybWFuY2Uubm93KCkpOwoKICAgIGxldCB0cmFucyA9IHRyYW5zYWN0aW9ucy5nZXQobmFtZSk7CiAgICBpZiAodHJhbnMpIHsKICAgICAgaWYgKGxvY2tUeXBlID4gdHJhbnMubG9ja1R5cGUpIHsKICAgICAgICAvLyBVcGdyYWRlIFNIQVJFRCB0byBFWENMVVNJVkUKICAgICAgICBhc3NlcnQoCiAgICAgICAgICB0cmFucy5sb2NrVHlwZSA9PT0gTE9DS19UWVBFUy5TSEFSRUQsCiAgICAgICAgICBgVXByYWRpbmcgbG9jayB0eXBlIGZyb20gJHt0cmFucy5sb2NrVHlwZX0gaXMgaW52YWxpZGAKICAgICAgICApOwogICAgICAgIGFzc2VydCgKICAgICAgICAgIGxvY2tUeXBlID09PSBMT0NLX1RZUEVTLlJFU0VSVkVEIHx8IGxvY2tUeXBlID09PSBMT0NLX1RZUEVTLkVYQ0xVU0lWRSwKICAgICAgICAgIGBVcGdyYWRpbmcgbG9jayB0eXBlIHRvICR7bG9ja1R5cGV9IGlzIGludmFsaWRgCiAgICAgICAgKTsKCiAgICAgICAgbGV0IHN1Y2Nlc3MgPSBhd2FpdCB0cmFucy51cGdyYWRlRXhjbHVzaXZlKCk7CiAgICAgICAgd3JpdGVyLmludDMyKHN1Y2Nlc3MgPyAwIDogLTEpOwogICAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgICB9IGVsc2UgewogICAgICAgIC8vIElmIG5vdCB1cGdyYWRpbmcgYW5kIHdlIGFscmVhZHkgaGF2ZSBhIGxvY2ssIG1ha2Ugc3VyZSB0aGlzCiAgICAgICAgLy8gaXNuJ3QgYSBkb3duZ3JhZGUKICAgICAgICBhc3NlcnQoCiAgICAgICAgICB0cmFucy5sb2NrVHlwZSA9PT0gbG9ja1R5cGUsCiAgICAgICAgICBgRG93bmdyYWRpbmcgbG9jayB0byAke2xvY2tUeXBlfSBpcyBpbnZhbGlkYAogICAgICAgICk7CgogICAgICAgIHdyaXRlci5pbnQzMigwKTsKICAgICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgICAgfQogICAgfSBlbHNlIHsKICAgICAgYXNzZXJ0KAogICAgICAgIGxvY2tUeXBlID09PSBMT0NLX1RZUEVTLlNIQVJFRCwKICAgICAgICBgTmV3IGxvY2tzIG11c3Qgc3RhcnQgYXMgU0hBUkVEIGluc3RlYWQgb2YgJHtsb2NrVHlwZX1gCiAgICAgICk7CgogICAgICBsZXQgdHJhbnMgPSBuZXcgVHJhbnNhY3Rpb24oYXdhaXQgbG9hZERiKG5hbWUpKTsKICAgICAgaWYgKChhd2FpdCB0cmFucy5wcmVmZXRjaEZpcnN0QmxvY2soNTAwKSkgPT0gbnVsbCkgOwoKICAgICAgdHJhbnNhY3Rpb25zLnNldChuYW1lLCB0cmFucyk7CgogICAgICB3cml0ZXIuaW50MzIoMCk7CiAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgfQogIH0KCiAgYXN5bmMgZnVuY3Rpb24gaGFuZGxlVW5sb2NrKHdyaXRlciwgbmFtZSwgbG9ja1R5cGUpIHsKICAgIC8vIGNvbnNvbGUubG9nKCd1bmxvY2tpbmcnLCBuYW1lLCBsb2NrVHlwZSwgcGVyZm9ybWFuY2Uubm93KCkpOwoKICAgIGxldCB0cmFucyA9IGdldFRyYW5zYWN0aW9uKG5hbWUpOwoKICAgIGlmIChsb2NrVHlwZSA9PT0gTE9DS19UWVBFUy5TSEFSRUQpIHsKICAgICAgaWYgKHRyYW5zID09IG51bGwpIHsKICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1VubG9jayBlcnJvciAoU0hBUkVEKTogbm8gdHJhbnNhY3Rpb24gcnVubmluZycpOwogICAgICB9CgogICAgICBpZiAodHJhbnMubG9ja1R5cGUgPT09IExPQ0tfVFlQRVMuRVhDTFVTSVZFKSB7CiAgICAgICAgdHJhbnMuZG93bmdyYWRlU2hhcmVkKCk7CiAgICAgIH0KICAgIH0gZWxzZSBpZiAobG9ja1R5cGUgPT09IExPQ0tfVFlQRVMuTk9ORSkgewogICAgICAvLyBJIHRob3VnaHQgd2UgY291bGQgYXNzdW1lIGEgbG9jayBpcyBhbHdheXMgb3BlbiB3aGVuIGB1bmxvY2tgCiAgICAgIC8vIGlzIGNhbGxlZCwgYnV0IGl0IGFsc28gY2FsbHMgYHVubG9ja2Agd2hlbiBjbG9zaW5nIHRoZSBmaWxlIG5vCiAgICAgIC8vIG1hdHRlciB3aGF0LiBEbyBub3RoaW5nIGlmIHRoZXJlJ3Mgbm8gbG9jayBjdXJyZW50bHkKICAgICAgaWYgKHRyYW5zKSB7CiAgICAgICAgLy8gVE9ETzogdGhpcyBpcyB3aGVyZSBhbiBlcnJvciBjb3VsZCBidWJibGUgdXAuIEhhbmRsZSBpdAogICAgICAgIGF3YWl0IHRyYW5zLndhaXRDb21wbGV0ZSgpOwogICAgICAgIHRyYW5zYWN0aW9ucy5kZWxldGUobmFtZSk7CiAgICAgIH0KICAgIH0KCiAgICB3cml0ZXIuaW50MzIoMCk7CiAgICB3cml0ZXIuZmluYWxpemUoKTsKICB9CgogIGFzeW5jIGZ1bmN0aW9uIGhhbmRsZVJlYWQod3JpdGVyLCBuYW1lLCBwb3NpdGlvbikgewogICAgcmV0dXJuIHdpdGhUcmFuc2FjdGlvbihuYW1lLCAncmVhZG9ubHknLCBhc3luYyB0cmFucyA9PiB7CiAgICAgIGxldCBkYXRhID0gYXdhaXQgdHJhbnMucmVhZChwb3NpdGlvbik7CgogICAgICBpZiAoZGF0YSA9PSBudWxsKSB7CiAgICAgICAgd3JpdGVyLmJ5dGVzKG5ldyBBcnJheUJ1ZmZlcigwKSk7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgd3JpdGVyLmJ5dGVzKGRhdGEpOwogICAgICB9CiAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgfSk7CiAgfQoKICBhc3luYyBmdW5jdGlvbiBoYW5kbGVXcml0ZXMod3JpdGVyLCBuYW1lLCB3cml0ZXMpIHsKICAgIHJldHVybiB3aXRoVHJhbnNhY3Rpb24obmFtZSwgJ3JlYWR3cml0ZScsIGFzeW5jIHRyYW5zID0+IHsKICAgICAgYXdhaXQgdHJhbnMuYnVsa1NldCh3cml0ZXMubWFwKHcgPT4gKHsga2V5OiB3LnBvcywgdmFsdWU6IHcuZGF0YSB9KSkpOwoKICAgICAgd3JpdGVyLmludDMyKDApOwogICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgIH0pOwogIH0KCiAgYXN5bmMgZnVuY3Rpb24gaGFuZGxlUmVhZE1ldGEod3JpdGVyLCBuYW1lKSB7CiAgICByZXR1cm4gd2l0aFRyYW5zYWN0aW9uKG5hbWUsICdyZWFkb25seScsIGFzeW5jIHRyYW5zID0+IHsKICAgICAgdHJ5IHsKICAgICAgICBjb25zb2xlLmxvZygnUmVhZGluZyBtZXRhJyk7CiAgICAgICAgbGV0IHJlcyA9IGF3YWl0IHRyYW5zLmdldCgtMSk7CiAgICAgICAgY29uc29sZS5sb2coJ1JlYWRpbmcgbWV0YSAoZG9uZSknLCByZXMpOwoKICAgICAgICBsZXQgbWV0YSA9IHJlczsKICAgICAgICB3cml0ZXIuaW50MzIobWV0YSA/IG1ldGEuc2l6ZSA6IC0xKTsKICAgICAgICB3cml0ZXIuaW50MzIobWV0YSA/IG1ldGEuYmxvY2tTaXplIDogLTEpOwogICAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgICB9IGNhdGNoIChlcnIpIHsKICAgICAgICBjb25zb2xlLmxvZyhlcnIpOwogICAgICAgIHdyaXRlci5pbnQzMigtMSk7CiAgICAgICAgd3JpdGVyLmludDMyKC0xKTsKICAgICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgICAgfQogICAgfSk7CiAgfQoKICBhc3luYyBmdW5jdGlvbiBoYW5kbGVXcml0ZU1ldGEod3JpdGVyLCBuYW1lLCBtZXRhKSB7CiAgICByZXR1cm4gd2l0aFRyYW5zYWN0aW9uKG5hbWUsICdyZWFkd3JpdGUnLCBhc3luYyB0cmFucyA9PiB7CiAgICAgIHRyeSB7CiAgICAgICAgYXdhaXQgdHJhbnMuc2V0KHsga2V5OiAtMSwgdmFsdWU6IG1ldGEgfSk7CgogICAgICAgIHdyaXRlci5pbnQzMigwKTsKICAgICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgICAgfSBjYXRjaCAoZXJyKSB7CiAgICAgICAgY29uc29sZS5sb2coZXJyKTsKICAgICAgICB3cml0ZXIuaW50MzIoLTEpOwogICAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgICB9CiAgICB9KTsKICB9CgogIGFzeW5jIGZ1bmN0aW9uIGhhbmRsZURlbGV0ZUZpbGUod3JpdGVyLCBuYW1lKSB7CiAgICB0cnkgewogICAgICBjbG9zZURiKG5hbWUpOwoKICAgICAgYXdhaXQgbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4gewogICAgICAgIGxldCByZXEgPSBnbG9iYWxUaGlzLmluZGV4ZWREQi5kZWxldGVEYXRhYmFzZShuYW1lKTsKICAgICAgICByZXEub25zdWNjZXNzID0gcmVzb2x2ZTsKICAgICAgICByZXEub25lcnJvciA9IHJlamVjdDsKICAgICAgfSk7CgogICAgICB3cml0ZXIuaW50MzIoMCk7CiAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgfSBjYXRjaCAoZXJyKSB7CiAgICAgIHdyaXRlci5pbnQzMigtMSk7CiAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgfQogIH0KCiAgYXN5bmMgZnVuY3Rpb24gaGFuZGxlQ2xvc2VGaWxlKHdyaXRlciwgbmFtZSkgewogICAgY2xvc2VEYihuYW1lKTsKCiAgICB3cml0ZXIuaW50MzIoMCk7CiAgICB3cml0ZXIuZmluYWxpemUoKTsKICB9CgogIC8vIGBsaXN0ZW5gIGNvbnRpbnVhbGx5IGxpc3RlbnMgZm9yIHJlcXVlc3RzIHZpYSB0aGUgc2hhcmVkIGJ1ZmZlci4KICAvLyBSaWdodCBub3cgaXQncyBpbXBsZW1lbnRlZCBpbiBhIHRhaWwtY2FsbCBzdHlsZSAoYGxpc3RlbmAgaXMKICAvLyByZWN1cnNpdmVseSBjYWxsZWQpIGJlY2F1c2UgSSB0aG91Z2h0IHRoYXQgd2FzIG5lY2Vzc2FyeSBmb3IKICAvLyB2YXJpb3VzIHJlYXNvbnMuIFdlIGNhbiBjb252ZXJ0IHRoaXMgdG8gYSBgd2hpbGUoMSlgIGxvb3Agd2l0aAogIC8vIGFuZCB1c2UgYGF3YWl0YCB0aG91Z2gKICBhc3luYyBmdW5jdGlvbiBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpIHsKICAgIGxldCBtZXRob2QgPSByZWFkZXIuc3RyaW5nKCk7CgogICAgc3dpdGNoIChtZXRob2QpIHsKICAgICAgY2FzZSAnc3RhdHMtc3RhcnQnOiB7CiAgICAgICAgcmVhZGVyLmRvbmUoKTsKCiAgICAgICAgd3JpdGVyLmludDMyKDApOwogICAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgICAgIGxpc3RlbihyZWFkZXIsIHdyaXRlcik7CiAgICAgICAgYnJlYWs7CiAgICAgIH0KCiAgICAgIGNhc2UgJ3N0YXRzJzogewogICAgICAgIHJlYWRlci5kb25lKCk7CgogICAgICAgIGF3YWl0IGVuZCgpOwoKICAgICAgICB3cml0ZXIuaW50MzIoMCk7CiAgICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICAgICAgbGlzdGVuKHJlYWRlciwgd3JpdGVyKTsKICAgICAgICBicmVhazsKICAgICAgfQoKICAgICAgY2FzZSAnd3JpdGVCbG9ja3MnOiB7CiAgICAgICAgbGV0IG5hbWUgPSByZWFkZXIuc3RyaW5nKCk7CiAgICAgICAgbGV0IHdyaXRlcyA9IFtdOwogICAgICAgIHdoaWxlICghcmVhZGVyLmRvbmUoKSkgewogICAgICAgICAgbGV0IHBvcyA9IHJlYWRlci5pbnQzMigpOwogICAgICAgICAgbGV0IGRhdGEgPSByZWFkZXIuYnl0ZXMoKTsKICAgICAgICAgIHdyaXRlcy5wdXNoKHsgcG9zLCBkYXRhIH0pOwogICAgICAgIH0KCiAgICAgICAgYXdhaXQgaGFuZGxlV3JpdGVzKHdyaXRlciwgbmFtZSwgd3JpdGVzKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICdyZWFkQmxvY2snOiB7CiAgICAgICAgbGV0IG5hbWUgPSByZWFkZXIuc3RyaW5nKCk7CiAgICAgICAgbGV0IHBvcyA9IHJlYWRlci5pbnQzMigpOwogICAgICAgIHJlYWRlci5kb25lKCk7CgogICAgICAgIGF3YWl0IGhhbmRsZVJlYWQod3JpdGVyLCBuYW1lLCBwb3MpOwogICAgICAgIGxpc3RlbihyZWFkZXIsIHdyaXRlcik7CiAgICAgICAgYnJlYWs7CiAgICAgIH0KCiAgICAgIGNhc2UgJ3JlYWRNZXRhJzogewogICAgICAgIGxldCBuYW1lID0gcmVhZGVyLnN0cmluZygpOwogICAgICAgIHJlYWRlci5kb25lKCk7CiAgICAgICAgYXdhaXQgaGFuZGxlUmVhZE1ldGEod3JpdGVyLCBuYW1lKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICd3cml0ZU1ldGEnOiB7CiAgICAgICAgbGV0IG5hbWUgPSByZWFkZXIuc3RyaW5nKCk7CiAgICAgICAgbGV0IHNpemUgPSByZWFkZXIuaW50MzIoKTsKICAgICAgICBsZXQgYmxvY2tTaXplID0gcmVhZGVyLmludDMyKCk7CiAgICAgICAgcmVhZGVyLmRvbmUoKTsKICAgICAgICBhd2FpdCBoYW5kbGVXcml0ZU1ldGEod3JpdGVyLCBuYW1lLCB7IHNpemUsIGJsb2NrU2l6ZSB9KTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICdkZWxldGVGaWxlJzogewogICAgICAgIGxldCBuYW1lID0gcmVhZGVyLnN0cmluZygpOwogICAgICAgIHJlYWRlci5kb25lKCk7CgogICAgICAgIGF3YWl0IGhhbmRsZURlbGV0ZUZpbGUod3JpdGVyLCBuYW1lKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICdjbG9zZUZpbGUnOiB7CiAgICAgICAgbGV0IG5hbWUgPSByZWFkZXIuc3RyaW5nKCk7CiAgICAgICAgcmVhZGVyLmRvbmUoKTsKCiAgICAgICAgYXdhaXQgaGFuZGxlQ2xvc2VGaWxlKHdyaXRlciwgbmFtZSk7CiAgICAgICAgbGlzdGVuKHJlYWRlciwgd3JpdGVyKTsKICAgICAgICBicmVhazsKICAgICAgfQoKICAgICAgY2FzZSAnbG9ja0ZpbGUnOiB7CiAgICAgICAgbGV0IG5hbWUgPSByZWFkZXIuc3RyaW5nKCk7CiAgICAgICAgbGV0IGxvY2tUeXBlID0gcmVhZGVyLmludDMyKCk7CiAgICAgICAgcmVhZGVyLmRvbmUoKTsKCiAgICAgICAgYXdhaXQgaGFuZGxlTG9jayh3cml0ZXIsIG5hbWUsIGxvY2tUeXBlKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICd1bmxvY2tGaWxlJzogewogICAgICAgIGxldCBuYW1lID0gcmVhZGVyLnN0cmluZygpOwogICAgICAgIGxldCBsb2NrVHlwZSA9IHJlYWRlci5pbnQzMigpOwogICAgICAgIHJlYWRlci5kb25lKCk7CgogICAgICAgIGF3YWl0IGhhbmRsZVVubG9jayh3cml0ZXIsIG5hbWUsIGxvY2tUeXBlKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBkZWZhdWx0OgogICAgICAgIHRocm93IG5ldyBFcnJvcignVW5rbm93biBtZXRob2Q6ICcgKyBtZXRob2QpOwogICAgfQogIH0KCiAgc2VsZi5vbm1lc3NhZ2UgPSBtc2cgPT4gewogICAgc3dpdGNoIChtc2cuZGF0YS50eXBlKSB7CiAgICAgIGNhc2UgJ2luaXQnOiB7CiAgICAgICAgcG9zdE1lc3NhZ2UoeyB0eXBlOiAnd29ya2VyLXJlYWR5JyB9KTsKICAgICAgICBsZXQgW2FyZ0J1ZmZlciwgcmVzdWx0QnVmZmVyXSA9IG1zZy5kYXRhLmJ1ZmZlcnM7CiAgICAgICAgbGV0IHJlYWRlciA9IG5ldyBSZWFkZXIoYXJnQnVmZmVyLCB7IG5hbWU6ICdhcmdzJywgZGVidWc6IGZhbHNlIH0pOwogICAgICAgIGxldCB3cml0ZXIgPSBuZXcgV3JpdGVyKHJlc3VsdEJ1ZmZlciwgeyBuYW1lOiAncmVzdWx0cycsIGRlYnVnOiBmYWxzZSB9KTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CiAgICB9CiAgfTsKCn0oKSk7Cgo=', null, false); /* eslint-enable */ let workerReady = null; diff --git a/dist/indexeddb-backend.js b/dist/indexeddb-backend.js index 2800ef8..650cc13 100644 --- a/dist/indexeddb-backend.js +++ b/dist/indexeddb-backend.js @@ -395,6 +395,7 @@ class File { close() { this.fsync(); + this.ops.close(); } delete() { @@ -626,7 +627,7 @@ function createBase64WorkerFactory(base64, sourcemapArg, enableUnicodeArg) { }; } -var WorkerFactory = createBase64WorkerFactory('Lyogcm9sbHVwLXBsdWdpbi13ZWItd29ya2VyLWxvYWRlciAqLwooZnVuY3Rpb24gKCkgewogICd1c2Ugc3RyaWN0JzsKCiAgbGV0IEZJTkFMSVpFRCA9IDB4ZGVhZGJlZWY7CgogIGxldCBXUklURUFCTEUgPSAwOwogIGxldCBSRUFEQUJMRSA9IDE7CgogIGNsYXNzIFJlYWRlciB7CiAgICBjb25zdHJ1Y3RvcigKICAgICAgYnVmZmVyLAogICAgICB7IGluaXRpYWxPZmZzZXQgPSA0LCB1c2VBdG9taWNzID0gdHJ1ZSwgc3RyZWFtID0gdHJ1ZSwgZGVidWcsIG5hbWUgfSA9IHt9CiAgICApIHsKICAgICAgdGhpcy5idWZmZXIgPSBidWZmZXI7CiAgICAgIHRoaXMuYXRvbWljVmlldyA9IG5ldyBJbnQzMkFycmF5KGJ1ZmZlcik7CiAgICAgIHRoaXMub2Zmc2V0ID0gaW5pdGlhbE9mZnNldDsKICAgICAgdGhpcy51c2VBdG9taWNzID0gdXNlQXRvbWljczsKICAgICAgdGhpcy5zdHJlYW0gPSBzdHJlYW07CiAgICAgIHRoaXMuZGVidWcgPSBkZWJ1ZzsKICAgICAgdGhpcy5uYW1lID0gbmFtZTsKICAgIH0KCiAgICBsb2coLi4uYXJncykgewogICAgICBpZiAodGhpcy5kZWJ1ZykgewogICAgICAgIGNvbnNvbGUubG9nKGBbcmVhZGVyOiAke3RoaXMubmFtZX1dYCwgLi4uYXJncyk7CiAgICAgIH0KICAgIH0KCiAgICB3YWl0V3JpdGUobmFtZSkgewogICAgICBpZiAodGhpcy51c2VBdG9taWNzKSB7CiAgICAgICAgdGhpcy5sb2coYHdhaXRpbmcgZm9yICR7bmFtZX1gKTsKCiAgICAgICAgd2hpbGUgKEF0b21pY3MubG9hZCh0aGlzLmF0b21pY1ZpZXcsIDApID09PSBXUklURUFCTEUpIHsKICAgICAgICAgIC8vIGNvbnNvbGUubG9nKCd3YWl0aW5nIGZvciB3cml0ZS4uLicpOwogICAgICAgICAgQXRvbWljcy53YWl0KHRoaXMuYXRvbWljVmlldywgMCwgV1JJVEVBQkxFLCA1MDApOwogICAgICAgIH0KCiAgICAgICAgdGhpcy5sb2coYHJlc3VtZWQgZm9yICR7bmFtZX1gKTsKICAgICAgfSBlbHNlIHsKICAgICAgICBpZiAodGhpcy5hdG9taWNWaWV3WzBdICE9PSBSRUFEQUJMRSkgewogICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdgd2FpdFdyaXRlYCBleHBlY3RlZCBhcnJheSB0byBiZSByZWFkYWJsZScpOwogICAgICAgIH0KICAgICAgfQogICAgfQoKICAgIGZsaXAoKSB7CiAgICAgIHRoaXMubG9nKCdmbGlwJyk7CiAgICAgIGlmICh0aGlzLnVzZUF0b21pY3MpIHsKICAgICAgICBsZXQgcHJldiA9IEF0b21pY3MuY29tcGFyZUV4Y2hhbmdlKAogICAgICAgICAgdGhpcy5hdG9taWNWaWV3LAogICAgICAgICAgMCwKICAgICAgICAgIFJFQURBQkxFLAogICAgICAgICAgV1JJVEVBQkxFCiAgICAgICAgKTsKCiAgICAgICAgaWYgKHByZXYgIT09IFJFQURBQkxFKSB7CiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1JlYWQgZGF0YSBvdXQgb2Ygc3luYyEgVGhpcyBpcyBkaXNhc3Ryb3VzJyk7CiAgICAgICAgfQoKICAgICAgICBBdG9taWNzLm5vdGlmeSh0aGlzLmF0b21pY1ZpZXcsIDApOwogICAgICB9IGVsc2UgewogICAgICAgIHRoaXMuYXRvbWljVmlld1swXSA9IFdSSVRFQUJMRTsKICAgICAgfQoKICAgICAgdGhpcy5vZmZzZXQgPSA0OwogICAgfQoKICAgIGRvbmUoKSB7CiAgICAgIHRoaXMud2FpdFdyaXRlKCdkb25lJyk7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0KTsKICAgICAgbGV0IGRvbmUgPSBkYXRhVmlldy5nZXRVaW50MzIoMCkgPT09IEZJTkFMSVpFRDsKCiAgICAgIGlmIChkb25lKSB7CiAgICAgICAgdGhpcy5sb2coJ2RvbmUnKTsKICAgICAgICB0aGlzLmZsaXAoKTsKICAgICAgfQoKICAgICAgcmV0dXJuIGRvbmU7CiAgICB9CgogICAgcGVlayhmbikgewogICAgICB0aGlzLnBlZWtPZmZzZXQgPSB0aGlzLm9mZnNldDsKICAgICAgbGV0IHJlcyA9IGZuKCk7CiAgICAgIHRoaXMub2Zmc2V0ID0gdGhpcy5wZWVrT2Zmc2V0OwogICAgICB0aGlzLnBlZWtPZmZzZXQgPSBudWxsOwogICAgICByZXR1cm4gcmVzOwogICAgfQoKICAgIHN0cmluZygpIHsKICAgICAgdGhpcy53YWl0V3JpdGUoJ3N0cmluZycpOwoKICAgICAgbGV0IGJ5dGVMZW5ndGggPSB0aGlzLl9pbnQzMigpOwogICAgICBsZXQgbGVuZ3RoID0gYnl0ZUxlbmd0aCAvIDI7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0LCBieXRlTGVuZ3RoKTsKICAgICAgbGV0IGNoYXJzID0gW107CiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbGVuZ3RoOyBpKyspIHsKICAgICAgICBjaGFycy5wdXNoKGRhdGFWaWV3LmdldFVpbnQxNihpICogMikpOwogICAgICB9CiAgICAgIGxldCBzdHIgPSBTdHJpbmcuZnJvbUNoYXJDb2RlLmFwcGx5KG51bGwsIGNoYXJzKTsKICAgICAgdGhpcy5sb2coJ3N0cmluZycsIHN0cik7CgogICAgICB0aGlzLm9mZnNldCArPSBieXRlTGVuZ3RoOwoKICAgICAgaWYgKHRoaXMucGVla09mZnNldCA9PSBudWxsKSB7CiAgICAgICAgdGhpcy5mbGlwKCk7CiAgICAgIH0KICAgICAgcmV0dXJuIHN0cjsKICAgIH0KCiAgICBfaW50MzIoKSB7CiAgICAgIGxldCBieXRlTGVuZ3RoID0gNDsKCiAgICAgIGxldCBkYXRhVmlldyA9IG5ldyBEYXRhVmlldyh0aGlzLmJ1ZmZlciwgdGhpcy5vZmZzZXQpOwogICAgICBsZXQgbnVtID0gZGF0YVZpZXcuZ2V0SW50MzIoKTsKICAgICAgdGhpcy5sb2coJ19pbnQzMicsIG51bSk7CgogICAgICB0aGlzLm9mZnNldCArPSBieXRlTGVuZ3RoOwogICAgICByZXR1cm4gbnVtOwogICAgfQoKICAgIGludDMyKCkgewogICAgICB0aGlzLndhaXRXcml0ZSgnaW50MzInKTsKICAgICAgbGV0IG51bSA9IHRoaXMuX2ludDMyKCk7CiAgICAgIHRoaXMubG9nKCdpbnQzMicsIG51bSk7CgogICAgICBpZiAodGhpcy5wZWVrT2Zmc2V0ID09IG51bGwpIHsKICAgICAgICB0aGlzLmZsaXAoKTsKICAgICAgfQogICAgICByZXR1cm4gbnVtOwogICAgfQoKICAgIGJ5dGVzKCkgewogICAgICB0aGlzLndhaXRXcml0ZSgnYnl0ZXMnKTsKCiAgICAgIGxldCBieXRlTGVuZ3RoID0gdGhpcy5faW50MzIoKTsKCiAgICAgIGxldCBieXRlcyA9IG5ldyBBcnJheUJ1ZmZlcihieXRlTGVuZ3RoKTsKICAgICAgbmV3IFVpbnQ4QXJyYXkoYnl0ZXMpLnNldCgKICAgICAgICBuZXcgVWludDhBcnJheSh0aGlzLmJ1ZmZlciwgdGhpcy5vZmZzZXQsIGJ5dGVMZW5ndGgpCiAgICAgICk7CiAgICAgIHRoaXMubG9nKCdieXRlcycsIGJ5dGVzKTsKCiAgICAgIHRoaXMub2Zmc2V0ICs9IGJ5dGVMZW5ndGg7CgogICAgICBpZiAodGhpcy5wZWVrT2Zmc2V0ID09IG51bGwpIHsKICAgICAgICB0aGlzLmZsaXAoKTsKICAgICAgfQogICAgICByZXR1cm4gYnl0ZXM7CiAgICB9CiAgfQoKICBjbGFzcyBXcml0ZXIgewogICAgY29uc3RydWN0b3IoCiAgICAgIGJ1ZmZlciwKICAgICAgeyBpbml0aWFsT2Zmc2V0ID0gNCwgdXNlQXRvbWljcyA9IHRydWUsIHN0cmVhbSA9IHRydWUsIGRlYnVnLCBuYW1lIH0gPSB7fQogICAgKSB7CiAgICAgIHRoaXMuYnVmZmVyID0gYnVmZmVyOwogICAgICB0aGlzLmF0b21pY1ZpZXcgPSBuZXcgSW50MzJBcnJheShidWZmZXIpOwogICAgICB0aGlzLm9mZnNldCA9IGluaXRpYWxPZmZzZXQ7CiAgICAgIHRoaXMudXNlQXRvbWljcyA9IHVzZUF0b21pY3M7CiAgICAgIHRoaXMuc3RyZWFtID0gc3RyZWFtOwoKICAgICAgdGhpcy5kZWJ1ZyA9IGRlYnVnOwogICAgICB0aGlzLm5hbWUgPSBuYW1lOwoKICAgICAgaWYgKHRoaXMudXNlQXRvbWljcykgewogICAgICAgIC8vIFRoZSBidWZmZXIgc3RhcnRzIG91dCBhcyB3cml0ZWFibGUKICAgICAgICBBdG9taWNzLnN0b3JlKHRoaXMuYXRvbWljVmlldywgMCwgV1JJVEVBQkxFKTsKICAgICAgfSBlbHNlIHsKICAgICAgICB0aGlzLmF0b21pY1ZpZXdbMF0gPSBXUklURUFCTEU7CiAgICAgIH0KICAgIH0KCiAgICBsb2coLi4uYXJncykgewogICAgICBpZiAodGhpcy5kZWJ1ZykgewogICAgICAgIGNvbnNvbGUubG9nKGBbd3JpdGVyOiAke3RoaXMubmFtZX1dYCwgLi4uYXJncyk7CiAgICAgIH0KICAgIH0KCiAgICB3YWl0UmVhZChuYW1lKSB7CiAgICAgIGlmICh0aGlzLnVzZUF0b21pY3MpIHsKICAgICAgICB0aGlzLmxvZyhgd2FpdGluZyBmb3IgJHtuYW1lfWApOwogICAgICAgIC8vIFN3aXRjaCB0byB3cml0YWJsZQogICAgICAgIC8vIEF0b21pY3Muc3RvcmUodGhpcy5hdG9taWNWaWV3LCAwLCAxKTsKCiAgICAgICAgbGV0IHByZXYgPSBBdG9taWNzLmNvbXBhcmVFeGNoYW5nZSgKICAgICAgICAgIHRoaXMuYXRvbWljVmlldywKICAgICAgICAgIDAsCiAgICAgICAgICBXUklURUFCTEUsCiAgICAgICAgICBSRUFEQUJMRQogICAgICAgICk7CgogICAgICAgIGlmIChwcmV2ICE9PSBXUklURUFCTEUpIHsKICAgICAgICAgIHRocm93IG5ldyBFcnJvcigKICAgICAgICAgICAgJ1dyb3RlIHNvbWV0aGluZyBpbnRvIHVud3JpdGFibGUgYnVmZmVyISBUaGlzIGlzIGRpc2FzdHJvdXMnCiAgICAgICAgICApOwogICAgICAgIH0KCiAgICAgICAgQXRvbWljcy5ub3RpZnkodGhpcy5hdG9taWNWaWV3LCAwKTsKCiAgICAgICAgd2hpbGUgKEF0b21pY3MubG9hZCh0aGlzLmF0b21pY1ZpZXcsIDApID09PSBSRUFEQUJMRSkgewogICAgICAgICAgLy8gY29uc29sZS5sb2coJ3dhaXRpbmcgdG8gYmUgcmVhZC4uLicpOwogICAgICAgICAgQXRvbWljcy53YWl0KHRoaXMuYXRvbWljVmlldywgMCwgUkVBREFCTEUsIDUwMCk7CiAgICAgICAgfQoKICAgICAgICB0aGlzLmxvZyhgcmVzdW1lZCBmb3IgJHtuYW1lfWApOwogICAgICB9IGVsc2UgewogICAgICAgIHRoaXMuYXRvbWljVmlld1swXSA9IFJFQURBQkxFOwogICAgICB9CgogICAgICB0aGlzLm9mZnNldCA9IDQ7CiAgICB9CgogICAgZmluYWxpemUoKSB7CiAgICAgIHRoaXMubG9nKCdmaW5hbGl6aW5nJyk7CiAgICAgIGxldCBkYXRhVmlldyA9IG5ldyBEYXRhVmlldyh0aGlzLmJ1ZmZlciwgdGhpcy5vZmZzZXQpOwogICAgICBkYXRhVmlldy5zZXRVaW50MzIoMCwgRklOQUxJWkVEKTsKICAgICAgdGhpcy53YWl0UmVhZCgnZmluYWxpemUnKTsKICAgIH0KCiAgICBzdHJpbmcoc3RyKSB7CiAgICAgIHRoaXMubG9nKCdzdHJpbmcnLCBzdHIpOwoKICAgICAgbGV0IGJ5dGVMZW5ndGggPSBzdHIubGVuZ3RoICogMjsKICAgICAgdGhpcy5faW50MzIoYnl0ZUxlbmd0aCk7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0LCBieXRlTGVuZ3RoKTsKICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBzdHIubGVuZ3RoOyBpKyspIHsKICAgICAgICBkYXRhVmlldy5zZXRVaW50MTYoaSAqIDIsIHN0ci5jaGFyQ29kZUF0KGkpKTsKICAgICAgfQoKICAgICAgdGhpcy5vZmZzZXQgKz0gYnl0ZUxlbmd0aDsKICAgICAgdGhpcy53YWl0UmVhZCgnc3RyaW5nJyk7CiAgICB9CgogICAgX2ludDMyKG51bSkgewogICAgICBsZXQgYnl0ZUxlbmd0aCA9IDQ7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0KTsKICAgICAgZGF0YVZpZXcuc2V0SW50MzIoMCwgbnVtKTsKCiAgICAgIHRoaXMub2Zmc2V0ICs9IGJ5dGVMZW5ndGg7CiAgICB9CgogICAgaW50MzIobnVtKSB7CiAgICAgIHRoaXMubG9nKCdpbnQzMicsIG51bSk7CiAgICAgIHRoaXMuX2ludDMyKG51bSk7CiAgICAgIHRoaXMud2FpdFJlYWQoJ2ludDMyJyk7CiAgICB9CgogICAgYnl0ZXMoYnVmZmVyKSB7CiAgICAgIHRoaXMubG9nKCdieXRlcycsIGJ1ZmZlcik7CgogICAgICBsZXQgYnl0ZUxlbmd0aCA9IGJ1ZmZlci5ieXRlTGVuZ3RoOwogICAgICB0aGlzLl9pbnQzMihieXRlTGVuZ3RoKTsKICAgICAgbmV3IFVpbnQ4QXJyYXkodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0KS5zZXQobmV3IFVpbnQ4QXJyYXkoYnVmZmVyKSk7CgogICAgICB0aGlzLm9mZnNldCArPSBieXRlTGVuZ3RoOwogICAgICB0aGlzLndhaXRSZWFkKCdieXRlcycpOwogICAgfQogIH0KCiAgLy8gTm9vcHMgaW4gcHJvZAogIGFzeW5jIGZ1bmN0aW9uIGVuZCgpIHt9CgogIGxldCBpc1Byb2JhYmx5U2FmYXJpID0gL14oKD8hY2hyb21lfGFuZHJvaWQpLikqc2FmYXJpL2kudGVzdCgKICAgIG5hdmlnYXRvci51c2VyQWdlbnQKICApOwoKICBsZXQgb3BlbkRicyA9IG5ldyBNYXAoKTsKICBsZXQgdHJhbnNhY3Rpb25zID0gbmV3IE1hcCgpOwoKICBmdW5jdGlvbiBhc3NlcnQoY29uZCwgbXNnKSB7CiAgICBpZiAoIWNvbmQpIHsKICAgICAgdGhyb3cgbmV3IEVycm9yKG1zZyk7CiAgICB9CiAgfQoKICBsZXQgTE9DS19UWVBFUyA9IHsKICAgIE5PTkU6IDAsCiAgICBTSEFSRUQ6IDEsCiAgICBSRVNFUlZFRDogMiwKICAgIFBFTkRJTkc6IDMsCiAgICBFWENMVVNJVkU6IDQKICB9OwoKICAvLyBXZSB1c2UgbG9uZy1saXZlZCB0cmFuc2FjdGlvbnMsIGFuZCBgVHJhbnNhY3Rpb25gIGtlZXBzIHRoZQogIC8vIHRyYW5zYWN0aW9uIHN0YXRlLiBJdCBpbXBsZW1lbnRzIGFuIG9wdGltYWwgd2F5IHRvIHBlcmZvcm0KICAvLyByZWFkL3dyaXRlcyB3aXRoIGtub3dsZWRnZSBvZiBob3cgc3FsaXRlIGFza3MgZm9yIHRoZW0sIGFuZCBhbHNvCiAgLy8gaW1wbGVtZW50cyBhIGxvY2tpbmcgbWVjaGFuaXNtIHRoYXQgbWFwcyB0byBob3cgc3FsaXRlIGxvY2tzIHdvcmsuCiAgY2xhc3MgVHJhbnNhY3Rpb24gewogICAgY29uc3RydWN0b3IoZGIsIGluaXRpYWxNb2RlID0gJ3JlYWRvbmx5JykgewogICAgICB0aGlzLmRiID0gZGI7CiAgICAgIHRoaXMudHJhbnMgPSB0aGlzLmRiLnRyYW5zYWN0aW9uKFsnZGF0YSddLCBpbml0aWFsTW9kZSk7CiAgICAgIHRoaXMuc3RvcmUgPSB0aGlzLnRyYW5zLm9iamVjdFN0b3JlKCdkYXRhJyk7CiAgICAgIHRoaXMubG9ja1R5cGUgPQogICAgICAgIGluaXRpYWxNb2RlID09PSAncmVhZG9ubHknID8gTE9DS19UWVBFUy5TSEFSRUQgOiBMT0NLX1RZUEVTLkVYQ0xVU0lWRTsKCiAgICAgIC8vIFRoZXJlIGlzIG5vIG5lZWQgZm9yIHVzIHRvIGNhY2hlIGJsb2Nrcy4gVXNlIHNxbGl0ZSdzCiAgICAgIC8vIGBjYWNoZV9zaXplYCBmb3IgdGhhdCBhbmQgaXQgd2lsbCBhdXRvbWF0aWNhbGx5IGRvIGl0LiBIb3dldmVyLAogICAgICAvLyB3ZSBkbyBzdGlsbCBrZWVwIGEgY2FjaGUgb2YgdGhlIGZpcnN0IGJsb2NrIGZvciB0aGUgZHVyYXRpb24gb2YKICAgICAgLy8gdGhpcyB0cmFuc2FjdGlvbiBiZWNhdXNlIG9mIGhvdyBsb2NraW5nIHdvcmtzOyB0aGlzIGF2b2lkcyBhCiAgICAgIC8vIGZldyBleHRyYSByZWFkcyBhbmQgYWxsb3dzIHVzIHRvIGRldGVjdCBjaGFuZ2VzIGR1cmluZwogICAgICAvLyB1cGdyYWRpbmcgKHNlZSBgdXBncmFkZUV4Y2x1c2l2ZWApCiAgICAgIHRoaXMuY2FjaGVkRmlyc3RCbG9jayA9IG51bGw7CgogICAgICB0aGlzLmN1cnNvciA9IG51bGw7CiAgICAgIHRoaXMucHJldlJlYWRzID0gbnVsbDsKICAgIH0KCiAgICBhc3luYyBwcmVmZXRjaEZpcnN0QmxvY2sodGltZW91dCkgewogICAgICAvLyBUT0RPOiBpbXBsZW1lbnQgdGltZW91dAoKICAgICAgLy8gR2V0IHRoZSBmaXJzdCBibG9jayBhbmQgY2FjaGUgaXQKICAgICAgbGV0IGJsb2NrID0gYXdhaXQgdGhpcy5nZXQoMCk7CiAgICAgIHRoaXMuY2FjaGVkRmlyc3RCbG9jayA9IGJsb2NrOwogICAgICByZXR1cm4gYmxvY2s7CiAgICB9CgogICAgYXN5bmMgd2FpdENvbXBsZXRlKCkgewogICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4gewogICAgICAgIC8vIEVhZ2VybHkgY29tbWl0IGl0IGZvciBiZXR0ZXIgcGVyZi4gTm90ZSB0aGF0ICoqdGhpcyBhc3N1bWVzCiAgICAgICAgLy8gdGhlIHRyYW5zYWN0aW9uIGlzIG9wZW4qKiBhcyBgY29tbWl0YCB3aWxsIHRocm93IGFuIGVycm9yIGlmCiAgICAgICAgLy8gaXQncyBhbHJlYWR5IGNsb3NlZCAod2hpY2ggc2hvdWxkIG5ldmVyIGJlIHRoZSBjYXNlIGZvciB1cykKICAgICAgICB0aGlzLmNvbW1pdCgpOwoKICAgICAgICBpZiAodGhpcy5sb2NrVHlwZSA9PT0gTE9DS19UWVBFUy5FWENMVVNJVkUpIHsKICAgICAgICAgIC8vIFdhaXQgdW50aWwgYWxsIHdyaXRlcyBhcmUgY29tbWl0dGVkCiAgICAgICAgICB0aGlzLnRyYW5zLm9uY29tcGxldGUgPSBlID0+IHJlc29sdmUoKTsKCiAgICAgICAgICAvLyBUT0RPOiBJcyBpdCBPSyB0byBhZGQgdGhpcyBsYXRlciwgYWZ0ZXIgYW4gZXJyb3IgbWlnaHQgaGF2ZQogICAgICAgICAgLy8gaGFwcGVuZWQ/IFdpbGwgaXQgaG9sZCB0aGUgZXJyb3IgYW5kIGZpcmUgdGhpcyB3aGVuIHdlCiAgICAgICAgICAvLyBhdHRhY2hlZCBpdD8gV2UgbWlnaHQgd2FudCB0byBlYWdlcmx5IGNyZWF0ZSB0aGUgcHJvbWlzZQogICAgICAgICAgLy8gd2hlbiBjcmVhdGluZyB0aGUgdHJhbnNhY3Rpb24gYW5kIHJldHVybiBpdCBoZXJlCiAgICAgICAgICB0aGlzLnRyYW5zLm9uZXJyb3IgPSBlID0+IHJlamVjdChlKTsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgaWYgKGlzUHJvYmFibHlTYWZhcmkpIHsKICAgICAgICAgICAgLy8gU2FmYXJpIGhhcyBhIGJ1ZyB3aGVyZSBzb21ldGltZXMgdGhlIElEQiBnZXRzIGJsb2NrZWQKICAgICAgICAgICAgLy8gcGVybWFuZW50bHkgaWYgeW91IHJlZnJlc2ggdGhlIHBhZ2Ugd2l0aCBhbiBvcGVuCiAgICAgICAgICAgIC8vIHRyYW5zYWN0aW9uLiBZb3UgaGF2ZSB0byByZXN0YXJ0IHRoZSBicm93c2VyIHRvIGZpeCBpdC4KICAgICAgICAgICAgLy8gV2Ugd2FpdCBmb3IgcmVhZG9ubHkgdHJhbnNhY3Rpb25zIHRvIGZpbmlzaCB0b28sIGJ1dCB0aGlzCiAgICAgICAgICAgIC8vIGlzIGEgcGVyZiBoaXQKICAgICAgICAgICAgdGhpcy50cmFucy5vbmNvbXBsZXRlID0gZSA9PiByZXNvbHZlKCk7CiAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAvLyBObyBuZWVkIHRvIHdhaXQgb24gYW55dGhpbmcgaW4gYSByZWFkLW9ubHkgdHJhbnNhY3Rpb24uCiAgICAgICAgICAgIC8vIE5vdGUgdGhhdCBlcnJvcnMgZHVyaW5nIHJlYWRzIGFyZWEgYWx3YXlzIGhhbmRsZWQgYnkgdGhlCiAgICAgICAgICAgIC8vIHJlYWQgcmVxdWVzdC4KICAgICAgICAgICAgcmVzb2x2ZSgpOwogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSk7CiAgICB9CgogICAgY29tbWl0KCkgewogICAgICAvLyBTYWZhcmkgZG9lc24ndCBzdXBwb3J0IHRoaXMgbWV0aG9kIHlldCAodGhpcyBpcyBqdXN0IGFuCiAgICAgIC8vIG9wdGltaXphdGlvbikKICAgICAgaWYgKHRoaXMudHJhbnMuY29tbWl0KSB7CiAgICAgICAgdGhpcy50cmFucy5jb21taXQoKTsKICAgICAgfQogICAgfQoKICAgIGFzeW5jIHVwZ3JhZGVFeGNsdXNpdmUoKSB7CiAgICAgIHRoaXMuY29tbWl0KCk7CgogICAgICAvLyBjb25zb2xlLmxvZygndXBkYXRpbmcgdHJhbnNhY3Rpb24gcmVhZHdyaXRlJyk7CiAgICAgIHRoaXMudHJhbnMgPSB0aGlzLmRiLnRyYW5zYWN0aW9uKFsnZGF0YSddLCAncmVhZHdyaXRlJyk7CiAgICAgIHRoaXMuc3RvcmUgPSB0aGlzLnRyYW5zLm9iamVjdFN0b3JlKCdkYXRhJyk7CiAgICAgIHRoaXMubG9ja1R5cGUgPSBMT0NLX1RZUEVTLkVYQ0xVU0lWRTsKCiAgICAgIGxldCBjYWNoZWQwID0gdGhpcy5jYWNoZWRGaXJzdEJsb2NrOwoKICAgICAgLy8gRG8gYSByZWFkCiAgICAgIGxldCBibG9jayA9IGF3YWl0IHRoaXMucHJlZmV0Y2hGaXJzdEJsb2NrKDUwMCk7CiAgICAgIC8vIFRPRE86IHdoZW4gdGltZW91dHMgYXJlIGltcGxlbWVudGVkLCBkZXRlY3QgdGltZW91dCBhbmQgcmV0dXJuIEJVU1kKCiAgICAgIGlmIChjYWNoZWQwID09IG51bGwgJiYgYmxvY2sgPT0gbnVsbCkgewogICAgICAgIHJldHVybiB0cnVlOwogICAgICB9IGVsc2UgewogICAgICAgIGZvciAobGV0IGkgPSAyNDsgaSA8IDQwOyBpKyspIHsKICAgICAgICAgIGlmIChibG9ja1tpXSAhPT0gY2FjaGVkMFtpXSkgewogICAgICAgICAgICByZXR1cm4gZmFsc2U7CiAgICAgICAgICB9CiAgICAgICAgfQogICAgICB9CgogICAgICByZXR1cm4gdHJ1ZTsKICAgIH0KCiAgICBkb3duZ3JhZGVTaGFyZWQoKSB7CiAgICAgIHRoaXMuY29tbWl0KCk7CgogICAgICAvLyBjb25zb2xlLmxvZygnZG93bmdyYWRpbmcgdHJhbnNhY3Rpb24gcmVhZG9ubHknKTsKICAgICAgdGhpcy50cmFucyA9IHRoaXMuZGIudHJhbnNhY3Rpb24oWydkYXRhJ10sICdyZWFkb25seScpOwogICAgICB0aGlzLnN0b3JlID0gdGhpcy50cmFucy5vYmplY3RTdG9yZSgnZGF0YScpOwogICAgICB0aGlzLmxvY2tUeXBlID0gTE9DS19UWVBFUy5TSEFSRUQ7CiAgICB9CgogICAgYXN5bmMgZ2V0KGtleSkgewogICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4gewogICAgICAgIGxldCByZXEgPSB0aGlzLnN0b3JlLmdldChrZXkpOwogICAgICAgIHJlcS5vbnN1Y2Nlc3MgPSBlID0+IHsKICAgICAgICAgIHJlc29sdmUocmVxLnJlc3VsdCk7CiAgICAgICAgfTsKICAgICAgICByZXEub25lcnJvciA9IGUgPT4gcmVqZWN0KGUpOwogICAgICB9KTsKICAgIH0KCiAgICBnZXRSZWFkRGlyZWN0aW9uKCkgewogICAgICAvLyBUaGVyZSBhcmUgYSB0d28gd2F5cyB3ZSBjYW4gcmVhZCBkYXRhOiBhIGRpcmVjdCBgZ2V0YCByZXF1ZXN0CiAgICAgIC8vIG9yIG9wZW5pbmcgYSBjdXJzb3IgYW5kIGl0ZXJhdGluZyB0aHJvdWdoIGRhdGEuIFdlIGRvbid0IGtub3cKICAgICAgLy8gd2hhdCBmdXR1cmUgcmVhZHMgbG9vayBsaWtlLCBzbyB3ZSBkb24ndCBrbm93IHRoZSBiZXN0IHN0cmF0ZWd5CiAgICAgIC8vIHRvIHBpY2suIEFsd2F5cyBjaG9vc2luZyBvbmUgc3RyYXRlZ3kgZm9yZ29lcyBhIGxvdCBvZgogICAgICAvLyBvcHRpbWl6YXRpb24sIGJlY2F1c2UgaXRlcmF0aW5nIHdpdGggYSBjdXJzb3IgaXMgYSBsb3QgZmFzdGVyCiAgICAgIC8vIHRoYW4gbWFueSBgZ2V0YCBjYWxscy4gT24gdGhlIG90aGVyIGhhbmQsIG9wZW5pbmcgYSBjdXJzb3IgaXMKICAgICAgLy8gc2xvdywgYW5kIHNvIGlzIGNhbGxpbmcgYGFkdmFuY2VgIHRvIG1vdmUgYSBjdXJzb3Igb3ZlciBhIGh1Z2UKICAgICAgLy8gcmFuZ2UgKGxpa2UgbW92aW5nIGl0IDEwMDAgaXRlbXMgbGF0ZXIpLCBzbyBtYW55IGBnZXRgIGNhbGxzIHdvdWxkCiAgICAgIC8vIGJlIGZhc3Rlci4gSW4gZ2VuZXJhbDoKICAgICAgLy8KICAgICAgLy8gKiBNYW55IGBnZXRgIGNhbGxzIGFyZSBmYXN0ZXIgd2hlbiBkb2luZyByYW5kb20gYWNjZXNzZXMKICAgICAgLy8gKiBJdGVyYXRpbmcgd2l0aCBhIGN1cnNvciBpcyBmYXN0ZXIgaWYgZG9pbmcgbW9zdGx5IHNlcXVlbnRpYWwKICAgICAgLy8gICBhY2Nlc3NlcwogICAgICAvLwogICAgICAvLyBXZSBpbXBsZW1lbnQgYSBoZXVyaXN0aWMgYW5kIGtlZXBzIHRyYWNrIG9mIHRoZSBsYXN0IDMgcmVhZHMKICAgICAgLy8gYW5kIGRldGVjdHMgd2hlbiB0aGV5IGFyZSBtb3N0bHkgc2VxdWVudGlhbC4gSWYgdGhleSBhcmUsIHdlCiAgICAgIC8vIG9wZW4gYSBjdXJzb3IgYW5kIHN0YXJ0IHJlYWRpbmcgYnkgaXRlcmF0aW5nIGl0LiBJZiBub3QsIHdlIGRvCiAgICAgIC8vIGRpcmVjdCBgZ2V0YCBjYWxscy4KICAgICAgLy8KICAgICAgLy8gT24gdG9wIG9mIGFsbCBvZiB0aGlzLCBlYWNoIGJyb3dzZXIgaGFzIGRpZmZlcmVudCBwZXJmCiAgICAgIC8vIGNoYXJhY3RlcmlzdGljcy4gV2Ugd2lsbCBwcm9iYWJseSB3YW50IHRvIG1ha2UgdGhlc2UgdGhyZXNob2xkcwogICAgICAvLyBjb25maWd1cmFibGUgc28gdGhlIHVzZXIgY2FuIGNoYW5nZSB0aGVtIHBlci1icm93c2VyIGlmIG5lZWRlZCwKICAgICAgLy8gYXMgd2VsbCBhcyBmaW5lLXR1bmluZyB0aGVtIGZvciB0aGVpciB1c2FnZSBvZiBzcWxpdGUuCgogICAgICBsZXQgcHJldlJlYWRzID0gdGhpcy5wcmV2UmVhZHM7CiAgICAgIGlmIChwcmV2UmVhZHMpIHsKICAgICAgICAvLyBIYXMgdGhlcmUgYmVlbiAzIGZvcndhcmQgc2VxdWVudGlhbCByZWFkcyB3aXRoaW4gMTAgYmxvY2tzPwogICAgICAgIGlmICgKICAgICAgICAgIHByZXZSZWFkc1swXSA8IHByZXZSZWFkc1sxXSAmJgogICAgICAgICAgcHJldlJlYWRzWzFdIDwgcHJldlJlYWRzWzJdICYmCiAgICAgICAgICBwcmV2UmVhZHNbMl0gLSBwcmV2UmVhZHNbMF0gPCAxMAogICAgICAgICkgewogICAgICAgICAgcmV0dXJuICduZXh0JzsKICAgICAgICB9CgogICAgICAgIC8vIEhhcyB0aGVyZSBiZWVuIDMgYmFja3dhcmRzIHNlcXVlbnRpYWwgcmVhZHMgd2l0aGluIDEwIGJsb2Nrcz8KICAgICAgICBpZiAoCiAgICAgICAgICBwcmV2UmVhZHNbMF0gPiBwcmV2UmVhZHNbMV0gJiYKICAgICAgICAgIHByZXZSZWFkc1sxXSA+IHByZXZSZWFkc1syXSAmJgogICAgICAgICAgcHJldlJlYWRzWzBdIC0gcHJldlJlYWRzWzJdIDwgMTAKICAgICAgICApIHsKICAgICAgICAgIHJldHVybiAncHJldic7CiAgICAgICAgfQogICAgICB9CgogICAgICByZXR1cm4gbnVsbDsKICAgIH0KCiAgICByZWFkKHBvc2l0aW9uKSB7CiAgICAgIGxldCB3YWl0Q3Vyc29yID0gKCkgPT4gewogICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7CiAgICAgICAgICBpZiAodGhpcy5jdXJzb3JQcm9taXNlICE9IG51bGwpIHsKICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKAogICAgICAgICAgICAgICd3YWl0Q3Vyc29yKCkgY2FsbGVkIGJ1dCBzb21ldGhpbmcgZWxzZSBpcyBhbHJlYWR5IHdhaXRpbmcnCiAgICAgICAgICAgICk7CiAgICAgICAgICB9CiAgICAgICAgICB0aGlzLmN1cnNvclByb21pc2UgPSB7IHJlc29sdmUsIHJlamVjdCB9OwogICAgICAgIH0pOwogICAgICB9OwoKICAgICAgaWYgKHRoaXMuY3Vyc29yKSB7CiAgICAgICAgbGV0IGN1cnNvciA9IHRoaXMuY3Vyc29yOwoKICAgICAgICBpZiAoCiAgICAgICAgICBjdXJzb3IuZGlyZWN0aW9uID09PSAnbmV4dCcgJiYKICAgICAgICAgIHBvc2l0aW9uID4gY3Vyc29yLmtleSAmJgogICAgICAgICAgcG9zaXRpb24gPCBjdXJzb3Iua2V5ICsgMTAwCiAgICAgICAgKSB7CgogICAgICAgICAgY3Vyc29yLmFkdmFuY2UocG9zaXRpb24gLSBjdXJzb3Iua2V5KTsKICAgICAgICAgIHJldHVybiB3YWl0Q3Vyc29yKCk7CiAgICAgICAgfSBlbHNlIGlmICgKICAgICAgICAgIGN1cnNvci5kaXJlY3Rpb24gPT09ICdwcmV2JyAmJgogICAgICAgICAgcG9zaXRpb24gPCBjdXJzb3Iua2V5ICYmCiAgICAgICAgICBwb3NpdGlvbiA+IGN1cnNvci5rZXkgLSAxMDAKICAgICAgICApIHsKCiAgICAgICAgICBjdXJzb3IuYWR2YW5jZShjdXJzb3Iua2V5IC0gcG9zaXRpb24pOwogICAgICAgICAgcmV0dXJuIHdhaXRDdXJzb3IoKTsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgLy8gRGl0Y2ggdGhlIGN1cnNvcgogICAgICAgICAgdGhpcy5jdXJzb3IgPSBudWxsOwogICAgICAgICAgcmV0dXJuIHRoaXMucmVhZChwb3NpdGlvbik7CiAgICAgICAgfQogICAgICB9IGVsc2UgewogICAgICAgIC8vIFdlIGRvbid0IGFscmVhZHkgaGF2ZSBhIGN1cnNvci4gV2UgbmVlZCB0byBhIGZyZXNoIHJlYWQ7CiAgICAgICAgLy8gc2hvdWxkIHdlIG9wZW4gYSBjdXJzb3Igb3IgY2FsbCBgZ2V0YD8KCiAgICAgICAgbGV0IGRpciA9IHRoaXMuZ2V0UmVhZERpcmVjdGlvbigpOwogICAgICAgIGlmIChkaXIpIHsKICAgICAgICAgIC8vIE9wZW4gYSBjdXJzb3IKICAgICAgICAgIHRoaXMucHJldlJlYWRzID0gbnVsbDsKCiAgICAgICAgICBsZXQga2V5UmFuZ2U7CiAgICAgICAgICBpZiAoZGlyID09PSAncHJldicpIHsKICAgICAgICAgICAga2V5UmFuZ2UgPSBJREJLZXlSYW5nZS51cHBlckJvdW5kKHBvc2l0aW9uKTsKICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIGtleVJhbmdlID0gSURCS2V5UmFuZ2UubG93ZXJCb3VuZChwb3NpdGlvbik7CiAgICAgICAgICB9CgogICAgICAgICAgbGV0IHJlcSA9IHRoaXMuc3RvcmUub3BlbkN1cnNvcihrZXlSYW5nZSwgZGlyKTsKCiAgICAgICAgICByZXEub25zdWNjZXNzID0gZSA9PiB7CgogICAgICAgICAgICBsZXQgY3Vyc29yID0gZS50YXJnZXQucmVzdWx0OwogICAgICAgICAgICB0aGlzLmN1cnNvciA9IGN1cnNvcjsKCiAgICAgICAgICAgIGlmICh0aGlzLmN1cnNvclByb21pc2UgPT0gbnVsbCkgewogICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignR290IGRhdGEgZnJvbSBjdXJzb3IgYnV0IG5vdGhpbmcgaXMgd2FpdGluZyBpdCcpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHRoaXMuY3Vyc29yUHJvbWlzZS5yZXNvbHZlKGN1cnNvciA/IGN1cnNvci52YWx1ZSA6IG51bGwpOwogICAgICAgICAgICB0aGlzLmN1cnNvclByb21pc2UgPSBudWxsOwogICAgICAgICAgfTsKICAgICAgICAgIHJlcS5vbmVycm9yID0gZSA9PiB7CiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdDdXJzb3IgZmFpbHVyZTonLCBlKTsKCiAgICAgICAgICAgIGlmICh0aGlzLmN1cnNvclByb21pc2UgPT0gbnVsbCkgewogICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignR290IGRhdGEgZnJvbSBjdXJzb3IgYnV0IG5vdGhpbmcgaXMgd2FpdGluZyBpdCcpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHRoaXMuY3Vyc29yUHJvbWlzZS5yZWplY3QoZSk7CiAgICAgICAgICAgIHRoaXMuY3Vyc29yUHJvbWlzZSA9IG51bGw7CiAgICAgICAgICB9OwoKICAgICAgICAgIHJldHVybiB3YWl0Q3Vyc29yKCk7CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgIGlmICh0aGlzLnByZXZSZWFkcyA9PSBudWxsKSB7CiAgICAgICAgICAgIHRoaXMucHJldlJlYWRzID0gWzAsIDAsIDBdOwogICAgICAgICAgfQogICAgICAgICAgdGhpcy5wcmV2UmVhZHMucHVzaChwb3NpdGlvbik7CiAgICAgICAgICB0aGlzLnByZXZSZWFkcy5zaGlmdCgpOwoKICAgICAgICAgIHJldHVybiB0aGlzLmdldChwb3NpdGlvbik7CiAgICAgICAgfQogICAgICB9CiAgICB9CgogICAgYXN5bmMgc2V0KGl0ZW0pIHsKICAgICAgdGhpcy5wcmV2UmVhZHMgPSBudWxsOwoKICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHsKICAgICAgICBsZXQgcmVxID0gdGhpcy5zdG9yZS5wdXQoaXRlbS52YWx1ZSwgaXRlbS5rZXkpOwogICAgICAgIHJlcS5vbnN1Y2Nlc3MgPSBlID0+IHJlc29sdmUocmVxLnJlc3VsdCk7CiAgICAgICAgcmVxLm9uZXJyb3IgPSBlID0+IHJlamVjdChlKTsKICAgICAgfSk7CiAgICB9CgogICAgYXN5bmMgYnVsa1NldChpdGVtcykgewogICAgICB0aGlzLnByZXZSZWFkcyA9IG51bGw7CgogICAgICBmb3IgKGxldCBpdGVtIG9mIGl0ZW1zKSB7CiAgICAgICAgdGhpcy5zdG9yZS5wdXQoaXRlbS52YWx1ZSwgaXRlbS5rZXkpOwogICAgICB9CiAgICB9CiAgfQoKICBhc3luYyBmdW5jdGlvbiBsb2FkRGIobmFtZSkgewogICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHsKICAgICAgaWYgKG9wZW5EYnMuZ2V0KG5hbWUpKSB7CiAgICAgICAgcmVzb2x2ZShvcGVuRGJzLmdldChuYW1lKSk7CiAgICAgICAgcmV0dXJuOwogICAgICB9CgogICAgICBsZXQgcmVxID0gZ2xvYmFsVGhpcy5pbmRleGVkREIub3BlbihuYW1lLCAxKTsKICAgICAgcmVxLm9uc3VjY2VzcyA9IGV2ZW50ID0+IHsKICAgICAgICBjb25zb2xlLmxvZygnZGIgaXMgb3BlbiEnLCBuYW1lKTsKICAgICAgICBsZXQgZGIgPSBldmVudC50YXJnZXQucmVzdWx0OwoKICAgICAgICBkYi5vbnZlcnNpb25jaGFuZ2UgPSAoKSA9PiB7CiAgICAgICAgICAvLyBUT0RPOiBOb3RpZnkgdGhlIHVzZXIgc29tZWhvdwogICAgICAgICAgY29uc29sZS5sb2coJ2Nsb3NpbmcgYmVjYXVzZSB2ZXJzaW9uIGNoYW5nZWQnKTsKICAgICAgICAgIGRiLmNsb3NlKCk7CiAgICAgICAgfTsKCiAgICAgICAgZGIub25jbG9zZSA9ICgpID0+IHsKICAgICAgICAgIG9wZW5EYnMuZGVsZXRlKG5hbWUpOwogICAgICAgIH07CgogICAgICAgIG9wZW5EYnMuc2V0KG5hbWUsIGRiKTsKICAgICAgICByZXNvbHZlKGRiKTsKICAgICAgfTsKICAgICAgcmVxLm9udXBncmFkZW5lZWRlZCA9IGV2ZW50ID0+IHsKICAgICAgICBsZXQgZGIgPSBldmVudC50YXJnZXQucmVzdWx0OwogICAgICAgIGlmICghZGIub2JqZWN0U3RvcmVOYW1lcy5jb250YWlucygnZGF0YScpKSB7CiAgICAgICAgICBkYi5jcmVhdGVPYmplY3RTdG9yZSgnZGF0YScpOwogICAgICAgIH0KICAgICAgfTsKICAgICAgcmVxLm9uYmxvY2tlZCA9IGUgPT4gY29uc29sZS5sb2coJ2Jsb2NrZWQnLCBlKTsKICAgICAgcmVxLm9uZXJyb3IgPSByZXEub25hYm9ydCA9IGUgPT4gcmVqZWN0KGUudGFyZ2V0LmVycm9yKTsKICAgIH0pOwogIH0KCiAgZnVuY3Rpb24gZ2V0VHJhbnNhY3Rpb24obmFtZSkgewogICAgcmV0dXJuIHRyYW5zYWN0aW9ucy5nZXQobmFtZSk7CiAgfQoKICBhc3luYyBmdW5jdGlvbiB3aXRoVHJhbnNhY3Rpb24obmFtZSwgbW9kZSwgZnVuYykgewogICAgbGV0IHRyYW5zID0gdHJhbnNhY3Rpb25zLmdldChuYW1lKTsKICAgIGlmICh0cmFucykgewogICAgICAvLyBJZiBhIHRyYW5zYWN0aW9uIGFscmVhZHkgZXhpc3RzLCB0aGF0IG1lYW5zIHRoZSBmaWxlIGhhcyBiZWVuCiAgICAgIC8vIGxvY2tlZC4gV2UgZG9uJ3QgZnVsbHkgc3VwcG9ydCBhcmJpdHJhcnkgbmVzdGVkIHRyYW5zYWN0aW9ucywKICAgICAgLy8gYXMgc2VlbiBiZWxvdyAod2Ugd29uJ3QgdXBncmFkZSBhIGByZWFkb25seWAgdG8gYHJlYWR3cml0ZWAKICAgICAgLy8gYXV0b21hdGljYWxseSkgYW5kIHRoaXMgaXMgbWFpbmx5IGZvciB0aGUgdXNlIGNhc2Ugd2hlcmUgc3FsaXRlCiAgICAgIC8vIGxvY2tzIHRoZSBkYiBhbmQgY3JlYXRlcyBhIHRyYW5zYWN0aW9uIGZvciB0aGUgZHVyYWN0aW9uIG9mIHRoZQogICAgICAvLyBsb2NrLiBXZSBkb24ndCBhY3R1YWxseSB3cml0ZSBjb2RlIGluIGEgd2F5IHRoYXQgYXNzdW1lcyBuZXN0ZWQKICAgICAgLy8gdHJhbnNhY3Rpb25zLCBzbyBqdXN0IGVycm9yIGhlcmUKICAgICAgaWYgKG1vZGUgPT09ICdyZWFkd3JpdGUnICYmIHRyYW5zLmxvY2tUeXBlID09PSBMT0NLX1RZUEVTLlNIQVJFRCkgewogICAgICAgIHRocm93IG5ldyBFcnJvcignQXR0ZW1wdGVkIHdyaXRlIGJ1dCBvbmx5IGhhcyBTSEFSRUQgbG9jaycpOwogICAgICB9CiAgICAgIHJldHVybiBmdW5jKHRyYW5zKTsKICAgIH0KCiAgICAvLyBPdXRzaWRlIHRoZSBzY29wZSBvZiBhIGxvY2ssIGNyZWF0ZSBhIHRlbXBvcmFyeSB0cmFuc2FjdGlvbgogICAgdHJhbnMgPSBuZXcgVHJhbnNhY3Rpb24oYXdhaXQgbG9hZERiKG5hbWUpLCBtb2RlKTsKICAgIGF3YWl0IGZ1bmModHJhbnMpOwogICAgYXdhaXQgdHJhbnMud2FpdENvbXBsZXRlKCk7CiAgfQoKICAvLyBMb2NraW5nIHN0cmF0ZWd5OgogIC8vCiAgLy8gKiBXZSBtYXAgc3FsaXRlJ3MgbG9ja3Mgb250byBJbmRleGVkREIncyB0cmFuc2FjdGlvbiBzZW1hbnRpY3MuCiAgLy8gICBSZWFkIHRyYW5zYWN0aW9ucyBtYXkgZXhlY3V0ZSBpbiBwYXJhbGxlbC4gUmVhZC93cml0ZQogIC8vICAgdHJhbnNhY3Rpb25zIGFyZSBxdWV1ZWQgdXAgYW5kIHdhaXQgdW50aWwgYWxsIHByZWNlZGluZwogIC8vICAgcmVhZCB0cmFuc2FjdGlvbnMgZmluaXNoIGV4ZWN1dGluZy4gUmVhZCB0cmFuc2FjdGlvbnMgc3RhcnRlZAogIC8vICAgYWZ0ZXIgYSByZWFkL3dyaXRlIHRyYW5zYWN0aW9uIHdhaXQgdW50aWwgaXQgaXMgZmluaXNoZWQuCiAgLy8KICAvLyAqIElEQiB0cmFuc2FjdGlvbnMgd2lsbCB3YWl0IGZvcmV2ZXIgdW50aWwgdGhleSBjYW4gZXhlY3V0ZSAoZm9yCiAgLy8gICBleGFtcGxlLCB0aGV5IG1heSBiZSBibG9ja2VkIG9uIGEgcmVhZC93cml0ZSB0cmFuc2FjdGlvbikuIFdlCiAgLy8gICBkb24ndCB3YW50IHRvIGFsbG93IHNxbGl0ZSB0cmFuc2FjdGlvbnMgdG8gd2FpdCBmb3JldmVyLCBzbwogIC8vICAgd2UgbWFudWFsbHkgdGltZW91dCBpZiBhIHRyYW5zYWN0aW9uIHRha2VzIHRvbyBsb25nIHRvCiAgLy8gICBzdGFydCBleGVjdXRpbmcuIFRoaXMgc2ltdWxhdGVzIHRoZSBiZWhhdmlvciBvZiBhIHNxbGl0ZQogIC8vICAgYmFpbGluZyBpZiBpdCBjYW4ndCByZXF1aXJlIGEgbG9jay4KICAvLwogIC8vICogQSBTSEFSRUQgbG9jayB3YW50cyB0byByZWFkIGZyb20gdGhlIGRiLiBXZSBzdGFydCBhIHJlYWQKICAvLyAgIHRyYW5zYWN0aW9uIGFuZCByZWFkIHRoZSBmaXJzdCBibG9jaywgYW5kIGlmIHdlIHJlYWQgaXQgd2l0aGluCiAgLy8gICA1MDBtcyB3ZSBjb25zaWRlciB0aGUgbG9jayBzdWNjZXNzZnVsLiBPdGhlcndpc2UgdGhlIGxvY2sKICAvLyAgIGZhaWxlZCBhbmQgd2UgcmV0dXJuIFNRTElURV9CVVNZLiAoVGhlcmUncyBubyBwZXJmIGRvd25zaWRlCiAgLy8gICB0byByZWFkaW5nIHRoZSBmaXJzdCBibG9jayAtIGl0IGhhcyB0byBiZSByZWFkIGFueXdheSB0byBjaGVjawogIC8vICAgYnl0ZXMgMjQtMzkgZm9yIHRoZSBjaGFuZ2UgY291bnRlcikKICAvLwogIC8vICogQSBSRVNFUlZFRCBsb2NrIG1lYW5zIHRoZSBkYiB3YW50cyB0byBzdGFydCB3cml0aW5nICh0aGluayBvZgogIC8vICAgYEJFR0lOIFRSQU5TQUNUSU9OYCkuIE9ubHkgb25lIHByb2Nlc3MgY2FuIG9idGFpbiBhIFJFU0VSVkVECiAgLy8gICBsb2NrIGF0IGEgdGltZSwgYnV0IG5vcm1hbGx5IHNxbGl0ZSBzdGlsbCBsZWFkcyBuZXcgcmVhZCBsb2NrcwogIC8vICAgaGFwcGVuLiBJdCBpc24ndCB1bnRpbCBhbiBFWENMVVNJVkUgbG9jayBpcyBoZWxkIHRoYXQgcmVhZHMgYXJlCiAgLy8gICBibG9ja2VkLiBIb3dldmVyLCBzaW5jZSB3ZSBuZWVkIHRvIGd1YXJhbnRlZSBvbmx5IG9uZSBSRVNFUlZFRAogIC8vICAgbG9jayBhdCBvbmNlIChvdGhlcndpc2UgZGF0YSBjb3VsZCBjaGFuZ2UgZnJvbSBhbm90aGVyIHByb2Nlc3MKICAvLyAgIHdpdGhpbiBhIHRyYW5zYWN0aW9uLCBjYXVzaW5nIGZhdWx0eSBjYWNoZXMgZXRjKSB0aGUgc2ltcGxlc3QKICAvLyAgIHRoaW5nIHRvIGRvIGlzIGdvIGFoZWFkIGFuZCBncmFiIGEgcmVhZC93cml0ZSB0cmFuc2FjdGlvbiB0aGF0CiAgLy8gICByZXByZXNlbnRzIHRoZSBSRVNFUlZFRCBsb2NrLiBUaGlzIHdpbGwgYmxvY2sgYWxsIHJlYWRzIGZyb20KICAvLyAgIGhhcHBlbmluZywgYW5kIGlzIGVzc2VudGlhbGx5IHRoZSBzYW1lIGFzIGFuIEVYQ0xVU0lWRSBsb2NrLgogIC8vCiAgLy8gICAgICogVGhlIG1haW4gcHJvYmxlbSBoZXJlIGlzIHdlIGNhbid0ICJ1cGdyYWRlIiBhIGByZWFkb25seWAKICAvLyAgICAgICB0cmFuc2FjdGlvbiB0byBgcmVhZHdyaXRlYCwgYnV0IG5hdGl2ZSBzcWxpdGUgY2FuIHVwZ3JhZGUgYQogIC8vICAgICAgIGxvY2sgZnJvbSBTSEFSRUQgdG8gUkVTRVJWRUQuIFdlIG5lZWQgdG8gc3RhcnQgYSBuZXcKICAvLyAgICAgICB0cmFuc2FjdGlvbiB0byBkbyBzbywgYW5kIGJlY2F1c2Ugb2YgdGhhdCB0aGVyZSBtaWdodCBiZQogIC8vICAgICAgIG90aGVyIGByZWFkd3JpdGVgIHRyYW5zYWN0aW9ucyB0aGF0IGdldCBydW4gZHVyaW5nIHRoZQogIC8vICAgICAgICJ1cGdyYWRlIiB3aGljaCBpbnZhbGlkYXRlcyB0aGUgd2hvbGUgbG9ja2luZyBwcm9jZXNzIGFuZAogIC8vICAgICAgIGFuZCBjb3JydXB0cyBkYXRhLgogIC8vCiAgLy8gKiBJZGVhbGx5LCB3ZSBjb3VsZCB0ZWxsIHNxbGl0ZSB0byBza2lwIFNIQVJFRCBsb2NrcyBlbnRpcmVseS4gV2UKICAvLyAgIGRvbid0IG5lZWQgdGhlbSBzaW5jZSB3ZSBjYW4gcmVseSBvbiBJbmRleGVkREIncyBzZW1hbnRpY3MuCiAgLy8gICBUaGVuIHdoZW4gaXQgd2FudHMgdG8gc3RhcnQgd3JpdGluZywgd2UgZ2V0IGEgUkVTRVJWRUQgbG9jawogIC8vICAgd2l0aG91dCBoYXZpbmcgdG8gdXBncmFkZSBmcm9tIFNIQVJFRC4gVGhpcyB3b3VsZCBzYXZlIHVzCiAgLy8gICB0aGUgY29zdCBvZiBhIGByZWFkb25seWAgdHJhbnNhY3Rpb24gd2hlbiB3cml0aW5nOyByaWdodCBub3cKICAvLyAgIGl0IG11c3Qgb3BlbiBhIGByZWFkb25seWAgdHJhbnNhY3Rpb24gYW5kIHRoZW4gaW1tZWRpYXRlbHkgb3BlbgogIC8vICAgYSBgcmVhZHdyaXRlYCB0byB1cGdyYWRlIGl0LiBJIHRob3VnaHQgb2YgZGVmZXJyaW5nIG9wZW5pbmcgdGhlCiAgLy8gICBgcmVhZG9ubHlgIHRyYW5zYWN0aW9uIHVudGlsIHNvbWV0aGluZyBpcyBhY3R1YWxseSByZWFkLCBidXQKICAvLyAgIHVuZm9ydHVuYXRlbHkgc3FsaXRlIG9wZW5zIGl0LCByZWFkcyB0aGUgZmlyc3QgYmxvY2ssIGFuZCB0aGVuCiAgLy8gICB1cGdyYWRlcyBpdC4gU28gdGhlcmUncyBubyB3YXkgYXJvdW5kIGl0LiAoV2UgY2FuJ3QgYXNzdW1lIGl0J3MKICAvLyAgIGEgYHJlYWR3cml0ZWAgdHJhbnNhY3Rpb24gYXQgdGhhdCBwb2ludCBzaW5jZSB0aGF0IHdvdWxkIGFzc3VtZQogIC8vICAgYWxsIFNIQVJFRCBsb2NrcyBhcmUgYHJlYWR3cml0ZWAsIHJlbW92aW5nIHRoZSBwb3NzaWJpbGl0eSBvZgogIC8vICAgY29uY3VycmVudCByZWFkcykuCiAgLy8KICAvLyAqIFVwZ3JhZGluZyB0byBhbiBFWENMVVNJVkUgbG9jayBpcyBhIG5vb3AsIHNpbmNlIHdlIHRyZWF0IFJFU0VSVkVECiAgLy8gICBsb2NrcyBhcyBFWENMVVNJVkUuCiAgYXN5bmMgZnVuY3Rpb24gaGFuZGxlTG9jayh3cml0ZXIsIG5hbWUsIGxvY2tUeXBlKSB7CiAgICAvLyBjb25zb2xlLmxvZygnbG9ja2luZycsIG5hbWUsIGxvY2tUeXBlLCBwZXJmb3JtYW5jZS5ub3coKSk7CgogICAgbGV0IHRyYW5zID0gdHJhbnNhY3Rpb25zLmdldChuYW1lKTsKICAgIGlmICh0cmFucykgewogICAgICBpZiAobG9ja1R5cGUgPiB0cmFucy5sb2NrVHlwZSkgewogICAgICAgIC8vIFVwZ3JhZGUgU0hBUkVEIHRvIEVYQ0xVU0lWRQogICAgICAgIGFzc2VydCgKICAgICAgICAgIHRyYW5zLmxvY2tUeXBlID09PSBMT0NLX1RZUEVTLlNIQVJFRCwKICAgICAgICAgIGBVcHJhZGluZyBsb2NrIHR5cGUgZnJvbSAke3RyYW5zLmxvY2tUeXBlfSBpcyBpbnZhbGlkYAogICAgICAgICk7CiAgICAgICAgYXNzZXJ0KAogICAgICAgICAgbG9ja1R5cGUgPT09IExPQ0tfVFlQRVMuUkVTRVJWRUQgfHwgbG9ja1R5cGUgPT09IExPQ0tfVFlQRVMuRVhDTFVTSVZFLAogICAgICAgICAgYFVwZ3JhZGluZyBsb2NrIHR5cGUgdG8gJHtsb2NrVHlwZX0gaXMgaW52YWxpZGAKICAgICAgICApOwoKICAgICAgICBsZXQgc3VjY2VzcyA9IGF3YWl0IHRyYW5zLnVwZ3JhZGVFeGNsdXNpdmUoKTsKICAgICAgICB3cml0ZXIuaW50MzIoc3VjY2VzcyA/IDAgOiAtMSk7CiAgICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgLy8gSWYgbm90IHVwZ3JhZGluZyBhbmQgd2UgYWxyZWFkeSBoYXZlIGEgbG9jaywgbWFrZSBzdXJlIHRoaXMKICAgICAgICAvLyBpc24ndCBhIGRvd25ncmFkZQogICAgICAgIGFzc2VydCgKICAgICAgICAgIHRyYW5zLmxvY2tUeXBlID09PSBsb2NrVHlwZSwKICAgICAgICAgIGBEb3duZ3JhZGluZyBsb2NrIHRvICR7bG9ja1R5cGV9IGlzIGludmFsaWRgCiAgICAgICAgKTsKCiAgICAgICAgd3JpdGVyLmludDMyKDApOwogICAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgICB9CiAgICB9IGVsc2UgewogICAgICBhc3NlcnQoCiAgICAgICAgbG9ja1R5cGUgPT09IExPQ0tfVFlQRVMuU0hBUkVELAogICAgICAgIGBOZXcgbG9ja3MgbXVzdCBzdGFydCBhcyBTSEFSRUQgaW5zdGVhZCBvZiAke2xvY2tUeXBlfWAKICAgICAgKTsKCiAgICAgIGxldCB0cmFucyA9IG5ldyBUcmFuc2FjdGlvbihhd2FpdCBsb2FkRGIobmFtZSkpOwogICAgICBpZiAoKGF3YWl0IHRyYW5zLnByZWZldGNoRmlyc3RCbG9jayg1MDApKSA9PSBudWxsKSA7CgogICAgICB0cmFuc2FjdGlvbnMuc2V0KG5hbWUsIHRyYW5zKTsKCiAgICAgIHdyaXRlci5pbnQzMigwKTsKICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICB9CiAgfQoKICBhc3luYyBmdW5jdGlvbiBoYW5kbGVVbmxvY2sod3JpdGVyLCBuYW1lLCBsb2NrVHlwZSkgewogICAgLy8gY29uc29sZS5sb2coJ3VubG9ja2luZycsIG5hbWUsIGxvY2tUeXBlLCBwZXJmb3JtYW5jZS5ub3coKSk7CgogICAgbGV0IHRyYW5zID0gZ2V0VHJhbnNhY3Rpb24obmFtZSk7CgogICAgaWYgKGxvY2tUeXBlID09PSBMT0NLX1RZUEVTLlNIQVJFRCkgewogICAgICBpZiAodHJhbnMgPT0gbnVsbCkgewogICAgICAgIHRocm93IG5ldyBFcnJvcignVW5sb2NrIGVycm9yIChTSEFSRUQpOiBubyB0cmFuc2FjdGlvbiBydW5uaW5nJyk7CiAgICAgIH0KCiAgICAgIGlmICh0cmFucy5sb2NrVHlwZSA9PT0gTE9DS19UWVBFUy5FWENMVVNJVkUpIHsKICAgICAgICB0cmFucy5kb3duZ3JhZGVTaGFyZWQoKTsKICAgICAgfQogICAgfSBlbHNlIGlmIChsb2NrVHlwZSA9PT0gTE9DS19UWVBFUy5OT05FKSB7CiAgICAgIC8vIEkgdGhvdWdodCB3ZSBjb3VsZCBhc3N1bWUgYSBsb2NrIGlzIGFsd2F5cyBvcGVuIHdoZW4gYHVubG9ja2AKICAgICAgLy8gaXMgY2FsbGVkLCBidXQgaXQgYWxzbyBjYWxscyBgdW5sb2NrYCB3aGVuIGNsb3NpbmcgdGhlIGZpbGUgbm8KICAgICAgLy8gbWF0dGVyIHdoYXQuIERvIG5vdGhpbmcgaWYgdGhlcmUncyBubyBsb2NrIGN1cnJlbnRseQogICAgICBpZiAodHJhbnMpIHsKICAgICAgICAvLyBUT0RPOiB0aGlzIGlzIHdoZXJlIGFuIGVycm9yIGNvdWxkIGJ1YmJsZSB1cC4gSGFuZGxlIGl0CiAgICAgICAgYXdhaXQgdHJhbnMud2FpdENvbXBsZXRlKCk7CiAgICAgICAgdHJhbnNhY3Rpb25zLmRlbGV0ZShuYW1lKTsKICAgICAgfQogICAgfQoKICAgIHdyaXRlci5pbnQzMigwKTsKICAgIHdyaXRlci5maW5hbGl6ZSgpOwogIH0KCiAgYXN5bmMgZnVuY3Rpb24gaGFuZGxlUmVhZCh3cml0ZXIsIG5hbWUsIHBvc2l0aW9uKSB7CiAgICByZXR1cm4gd2l0aFRyYW5zYWN0aW9uKG5hbWUsICdyZWFkb25seScsIGFzeW5jIHRyYW5zID0+IHsKICAgICAgbGV0IGRhdGEgPSBhd2FpdCB0cmFucy5yZWFkKHBvc2l0aW9uKTsKCiAgICAgIGlmIChkYXRhID09IG51bGwpIHsKICAgICAgICB3cml0ZXIuYnl0ZXMobmV3IEFycmF5QnVmZmVyKDApKTsKICAgICAgfSBlbHNlIHsKICAgICAgICB3cml0ZXIuYnl0ZXMoZGF0YSk7CiAgICAgIH0KICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICB9KTsKICB9CgogIGFzeW5jIGZ1bmN0aW9uIGhhbmRsZVdyaXRlcyh3cml0ZXIsIG5hbWUsIHdyaXRlcykgewogICAgcmV0dXJuIHdpdGhUcmFuc2FjdGlvbihuYW1lLCAncmVhZHdyaXRlJywgYXN5bmMgdHJhbnMgPT4gewogICAgICBhd2FpdCB0cmFucy5idWxrU2V0KHdyaXRlcy5tYXAodyA9PiAoeyBrZXk6IHcucG9zLCB2YWx1ZTogdy5kYXRhIH0pKSk7CgogICAgICB3cml0ZXIuaW50MzIoMCk7CiAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgfSk7CiAgfQoKICBhc3luYyBmdW5jdGlvbiBoYW5kbGVSZWFkTWV0YSh3cml0ZXIsIG5hbWUpIHsKICAgIHJldHVybiB3aXRoVHJhbnNhY3Rpb24obmFtZSwgJ3JlYWRvbmx5JywgYXN5bmMgdHJhbnMgPT4gewogICAgICB0cnkgewogICAgICAgIGNvbnNvbGUubG9nKCdSZWFkaW5nIG1ldGEnKTsKICAgICAgICBsZXQgcmVzID0gYXdhaXQgdHJhbnMuZ2V0KC0xKTsKICAgICAgICBjb25zb2xlLmxvZygnUmVhZGluZyBtZXRhIChkb25lKScsIHJlcyk7CgogICAgICAgIGxldCBtZXRhID0gcmVzOwogICAgICAgIHdyaXRlci5pbnQzMihtZXRhID8gbWV0YS5zaXplIDogLTEpOwogICAgICAgIHdyaXRlci5pbnQzMihtZXRhID8gbWV0YS5ibG9ja1NpemUgOiAtMSk7CiAgICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICAgIH0gY2F0Y2ggKGVycikgewogICAgICAgIGNvbnNvbGUubG9nKGVycik7CiAgICAgICAgd3JpdGVyLmludDMyKC0xKTsKICAgICAgICB3cml0ZXIuaW50MzIoLTEpOwogICAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgICB9CiAgICB9KTsKICB9CgogIGFzeW5jIGZ1bmN0aW9uIGhhbmRsZVdyaXRlTWV0YSh3cml0ZXIsIG5hbWUsIG1ldGEpIHsKICAgIHJldHVybiB3aXRoVHJhbnNhY3Rpb24obmFtZSwgJ3JlYWR3cml0ZScsIGFzeW5jIHRyYW5zID0+IHsKICAgICAgdHJ5IHsKICAgICAgICBhd2FpdCB0cmFucy5zZXQoeyBrZXk6IC0xLCB2YWx1ZTogbWV0YSB9KTsKCiAgICAgICAgd3JpdGVyLmludDMyKDApOwogICAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgICB9IGNhdGNoIChlcnIpIHsKICAgICAgICBjb25zb2xlLmxvZyhlcnIpOwogICAgICAgIHdyaXRlci5pbnQzMigtMSk7CiAgICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICAgIH0KICAgIH0pOwogIH0KCiAgYXN5bmMgZnVuY3Rpb24gaGFuZGxlRGVsZXRlRmlsZSh3cml0ZXIsIG5hbWUpIHsKICAgIC8vIFRPRE86IEhhbmRsZSB0aGlzCiAgICB3cml0ZXIuaW50MzIoMCk7CiAgICB3cml0ZXIuZmluYWxpemUoKTsKICB9CgogIC8vIGBsaXN0ZW5gIGNvbnRpbnVhbGx5IGxpc3RlbnMgZm9yIHJlcXVlc3RzIHZpYSB0aGUgc2hhcmVkIGJ1ZmZlci4KICAvLyBSaWdodCBub3cgaXQncyBpbXBsZW1lbnRlZCBpbiBhIHRhaWwtY2FsbCBzdHlsZSAoYGxpc3RlbmAgaXMKICAvLyByZWN1cnNpdmVseSBjYWxsZWQpIGJlY2F1c2UgSSB0aG91Z2h0IHRoYXQgd2FzIG5lY2Vzc2FyeSBmb3IKICAvLyB2YXJpb3VzIHJlYXNvbnMuIFdlIGNhbiBjb252ZXJ0IHRoaXMgdG8gYSBgd2hpbGUoMSlgIGxvb3Agd2l0aAogIC8vIGFuZCB1c2UgYGF3YWl0YCB0aG91Z2gKICBhc3luYyBmdW5jdGlvbiBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpIHsKICAgIGxldCBtZXRob2QgPSByZWFkZXIuc3RyaW5nKCk7CgogICAgc3dpdGNoIChtZXRob2QpIHsKICAgICAgY2FzZSAnc3RhdHMtc3RhcnQnOiB7CiAgICAgICAgcmVhZGVyLmRvbmUoKTsKCiAgICAgICAgd3JpdGVyLmludDMyKDApOwogICAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgICAgIGxpc3RlbihyZWFkZXIsIHdyaXRlcik7CiAgICAgICAgYnJlYWs7CiAgICAgIH0KCiAgICAgIGNhc2UgJ3N0YXRzJzogewogICAgICAgIHJlYWRlci5kb25lKCk7CgogICAgICAgIGF3YWl0IGVuZCgpOwoKICAgICAgICB3cml0ZXIuaW50MzIoMCk7CiAgICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICAgICAgbGlzdGVuKHJlYWRlciwgd3JpdGVyKTsKICAgICAgICBicmVhazsKICAgICAgfQoKICAgICAgY2FzZSAnd3JpdGVCbG9ja3MnOiB7CiAgICAgICAgbGV0IG5hbWUgPSByZWFkZXIuc3RyaW5nKCk7CiAgICAgICAgbGV0IHdyaXRlcyA9IFtdOwogICAgICAgIHdoaWxlICghcmVhZGVyLmRvbmUoKSkgewogICAgICAgICAgbGV0IHBvcyA9IHJlYWRlci5pbnQzMigpOwogICAgICAgICAgbGV0IGRhdGEgPSByZWFkZXIuYnl0ZXMoKTsKICAgICAgICAgIHdyaXRlcy5wdXNoKHsgcG9zLCBkYXRhIH0pOwogICAgICAgIH0KCiAgICAgICAgYXdhaXQgaGFuZGxlV3JpdGVzKHdyaXRlciwgbmFtZSwgd3JpdGVzKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICdyZWFkQmxvY2snOiB7CiAgICAgICAgbGV0IG5hbWUgPSByZWFkZXIuc3RyaW5nKCk7CiAgICAgICAgbGV0IHBvcyA9IHJlYWRlci5pbnQzMigpOwogICAgICAgIHJlYWRlci5kb25lKCk7CgogICAgICAgIGF3YWl0IGhhbmRsZVJlYWQod3JpdGVyLCBuYW1lLCBwb3MpOwogICAgICAgIGxpc3RlbihyZWFkZXIsIHdyaXRlcik7CiAgICAgICAgYnJlYWs7CiAgICAgIH0KCiAgICAgIGNhc2UgJ3JlYWRNZXRhJzogewogICAgICAgIGxldCBuYW1lID0gcmVhZGVyLnN0cmluZygpOwogICAgICAgIHJlYWRlci5kb25lKCk7CiAgICAgICAgYXdhaXQgaGFuZGxlUmVhZE1ldGEod3JpdGVyLCBuYW1lKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICd3cml0ZU1ldGEnOiB7CiAgICAgICAgbGV0IG5hbWUgPSByZWFkZXIuc3RyaW5nKCk7CiAgICAgICAgbGV0IHNpemUgPSByZWFkZXIuaW50MzIoKTsKICAgICAgICBsZXQgYmxvY2tTaXplID0gcmVhZGVyLmludDMyKCk7CiAgICAgICAgcmVhZGVyLmRvbmUoKTsKICAgICAgICBhd2FpdCBoYW5kbGVXcml0ZU1ldGEod3JpdGVyLCBuYW1lLCB7IHNpemUsIGJsb2NrU2l6ZSB9KTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICdkZWxldGVGaWxlJzogewogICAgICAgIHJlYWRlci5zdHJpbmcoKTsKICAgICAgICByZWFkZXIuZG9uZSgpOwoKICAgICAgICBhd2FpdCBoYW5kbGVEZWxldGVGaWxlKHdyaXRlcik7CiAgICAgICAgbGlzdGVuKHJlYWRlciwgd3JpdGVyKTsKICAgICAgICBicmVhazsKICAgICAgfQoKICAgICAgY2FzZSAnbG9ja0ZpbGUnOiB7CiAgICAgICAgbGV0IG5hbWUgPSByZWFkZXIuc3RyaW5nKCk7CiAgICAgICAgbGV0IGxvY2tUeXBlID0gcmVhZGVyLmludDMyKCk7CiAgICAgICAgcmVhZGVyLmRvbmUoKTsKCiAgICAgICAgYXdhaXQgaGFuZGxlTG9jayh3cml0ZXIsIG5hbWUsIGxvY2tUeXBlKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICd1bmxvY2tGaWxlJzogewogICAgICAgIGxldCBuYW1lID0gcmVhZGVyLnN0cmluZygpOwogICAgICAgIGxldCBsb2NrVHlwZSA9IHJlYWRlci5pbnQzMigpOwogICAgICAgIHJlYWRlci5kb25lKCk7CgogICAgICAgIGF3YWl0IGhhbmRsZVVubG9jayh3cml0ZXIsIG5hbWUsIGxvY2tUeXBlKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBkZWZhdWx0OgogICAgICAgIHRocm93IG5ldyBFcnJvcignVW5rbm93biBtZXRob2Q6ICcgKyBtZXRob2QpOwogICAgfQogIH0KCiAgc2VsZi5vbm1lc3NhZ2UgPSBtc2cgPT4gewogICAgc3dpdGNoIChtc2cuZGF0YS50eXBlKSB7CiAgICAgIGNhc2UgJ2luaXQnOiB7CiAgICAgICAgcG9zdE1lc3NhZ2UoeyB0eXBlOiAnd29ya2VyLXJlYWR5JyB9KTsKICAgICAgICBsZXQgW2FyZ0J1ZmZlciwgcmVzdWx0QnVmZmVyXSA9IG1zZy5kYXRhLmJ1ZmZlcnM7CiAgICAgICAgbGV0IHJlYWRlciA9IG5ldyBSZWFkZXIoYXJnQnVmZmVyLCB7IG5hbWU6ICdhcmdzJywgZGVidWc6IGZhbHNlIH0pOwogICAgICAgIGxldCB3cml0ZXIgPSBuZXcgV3JpdGVyKHJlc3VsdEJ1ZmZlciwgeyBuYW1lOiAncmVzdWx0cycsIGRlYnVnOiBmYWxzZSB9KTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CiAgICB9CiAgfTsKCn0oKSk7Cgo=', null, false); +var WorkerFactory = createBase64WorkerFactory('Lyogcm9sbHVwLXBsdWdpbi13ZWItd29ya2VyLWxvYWRlciAqLwooZnVuY3Rpb24gKCkgewogICd1c2Ugc3RyaWN0JzsKCiAgbGV0IEZJTkFMSVpFRCA9IDB4ZGVhZGJlZWY7CgogIGxldCBXUklURUFCTEUgPSAwOwogIGxldCBSRUFEQUJMRSA9IDE7CgogIGNsYXNzIFJlYWRlciB7CiAgICBjb25zdHJ1Y3RvcigKICAgICAgYnVmZmVyLAogICAgICB7IGluaXRpYWxPZmZzZXQgPSA0LCB1c2VBdG9taWNzID0gdHJ1ZSwgc3RyZWFtID0gdHJ1ZSwgZGVidWcsIG5hbWUgfSA9IHt9CiAgICApIHsKICAgICAgdGhpcy5idWZmZXIgPSBidWZmZXI7CiAgICAgIHRoaXMuYXRvbWljVmlldyA9IG5ldyBJbnQzMkFycmF5KGJ1ZmZlcik7CiAgICAgIHRoaXMub2Zmc2V0ID0gaW5pdGlhbE9mZnNldDsKICAgICAgdGhpcy51c2VBdG9taWNzID0gdXNlQXRvbWljczsKICAgICAgdGhpcy5zdHJlYW0gPSBzdHJlYW07CiAgICAgIHRoaXMuZGVidWcgPSBkZWJ1ZzsKICAgICAgdGhpcy5uYW1lID0gbmFtZTsKICAgIH0KCiAgICBsb2coLi4uYXJncykgewogICAgICBpZiAodGhpcy5kZWJ1ZykgewogICAgICAgIGNvbnNvbGUubG9nKGBbcmVhZGVyOiAke3RoaXMubmFtZX1dYCwgLi4uYXJncyk7CiAgICAgIH0KICAgIH0KCiAgICB3YWl0V3JpdGUobmFtZSkgewogICAgICBpZiAodGhpcy51c2VBdG9taWNzKSB7CiAgICAgICAgdGhpcy5sb2coYHdhaXRpbmcgZm9yICR7bmFtZX1gKTsKCiAgICAgICAgd2hpbGUgKEF0b21pY3MubG9hZCh0aGlzLmF0b21pY1ZpZXcsIDApID09PSBXUklURUFCTEUpIHsKICAgICAgICAgIC8vIGNvbnNvbGUubG9nKCd3YWl0aW5nIGZvciB3cml0ZS4uLicpOwogICAgICAgICAgQXRvbWljcy53YWl0KHRoaXMuYXRvbWljVmlldywgMCwgV1JJVEVBQkxFLCA1MDApOwogICAgICAgIH0KCiAgICAgICAgdGhpcy5sb2coYHJlc3VtZWQgZm9yICR7bmFtZX1gKTsKICAgICAgfSBlbHNlIHsKICAgICAgICBpZiAodGhpcy5hdG9taWNWaWV3WzBdICE9PSBSRUFEQUJMRSkgewogICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdgd2FpdFdyaXRlYCBleHBlY3RlZCBhcnJheSB0byBiZSByZWFkYWJsZScpOwogICAgICAgIH0KICAgICAgfQogICAgfQoKICAgIGZsaXAoKSB7CiAgICAgIHRoaXMubG9nKCdmbGlwJyk7CiAgICAgIGlmICh0aGlzLnVzZUF0b21pY3MpIHsKICAgICAgICBsZXQgcHJldiA9IEF0b21pY3MuY29tcGFyZUV4Y2hhbmdlKAogICAgICAgICAgdGhpcy5hdG9taWNWaWV3LAogICAgICAgICAgMCwKICAgICAgICAgIFJFQURBQkxFLAogICAgICAgICAgV1JJVEVBQkxFCiAgICAgICAgKTsKCiAgICAgICAgaWYgKHByZXYgIT09IFJFQURBQkxFKSB7CiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1JlYWQgZGF0YSBvdXQgb2Ygc3luYyEgVGhpcyBpcyBkaXNhc3Ryb3VzJyk7CiAgICAgICAgfQoKICAgICAgICBBdG9taWNzLm5vdGlmeSh0aGlzLmF0b21pY1ZpZXcsIDApOwogICAgICB9IGVsc2UgewogICAgICAgIHRoaXMuYXRvbWljVmlld1swXSA9IFdSSVRFQUJMRTsKICAgICAgfQoKICAgICAgdGhpcy5vZmZzZXQgPSA0OwogICAgfQoKICAgIGRvbmUoKSB7CiAgICAgIHRoaXMud2FpdFdyaXRlKCdkb25lJyk7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0KTsKICAgICAgbGV0IGRvbmUgPSBkYXRhVmlldy5nZXRVaW50MzIoMCkgPT09IEZJTkFMSVpFRDsKCiAgICAgIGlmIChkb25lKSB7CiAgICAgICAgdGhpcy5sb2coJ2RvbmUnKTsKICAgICAgICB0aGlzLmZsaXAoKTsKICAgICAgfQoKICAgICAgcmV0dXJuIGRvbmU7CiAgICB9CgogICAgcGVlayhmbikgewogICAgICB0aGlzLnBlZWtPZmZzZXQgPSB0aGlzLm9mZnNldDsKICAgICAgbGV0IHJlcyA9IGZuKCk7CiAgICAgIHRoaXMub2Zmc2V0ID0gdGhpcy5wZWVrT2Zmc2V0OwogICAgICB0aGlzLnBlZWtPZmZzZXQgPSBudWxsOwogICAgICByZXR1cm4gcmVzOwogICAgfQoKICAgIHN0cmluZygpIHsKICAgICAgdGhpcy53YWl0V3JpdGUoJ3N0cmluZycpOwoKICAgICAgbGV0IGJ5dGVMZW5ndGggPSB0aGlzLl9pbnQzMigpOwogICAgICBsZXQgbGVuZ3RoID0gYnl0ZUxlbmd0aCAvIDI7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0LCBieXRlTGVuZ3RoKTsKICAgICAgbGV0IGNoYXJzID0gW107CiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbGVuZ3RoOyBpKyspIHsKICAgICAgICBjaGFycy5wdXNoKGRhdGFWaWV3LmdldFVpbnQxNihpICogMikpOwogICAgICB9CiAgICAgIGxldCBzdHIgPSBTdHJpbmcuZnJvbUNoYXJDb2RlLmFwcGx5KG51bGwsIGNoYXJzKTsKICAgICAgdGhpcy5sb2coJ3N0cmluZycsIHN0cik7CgogICAgICB0aGlzLm9mZnNldCArPSBieXRlTGVuZ3RoOwoKICAgICAgaWYgKHRoaXMucGVla09mZnNldCA9PSBudWxsKSB7CiAgICAgICAgdGhpcy5mbGlwKCk7CiAgICAgIH0KICAgICAgcmV0dXJuIHN0cjsKICAgIH0KCiAgICBfaW50MzIoKSB7CiAgICAgIGxldCBieXRlTGVuZ3RoID0gNDsKCiAgICAgIGxldCBkYXRhVmlldyA9IG5ldyBEYXRhVmlldyh0aGlzLmJ1ZmZlciwgdGhpcy5vZmZzZXQpOwogICAgICBsZXQgbnVtID0gZGF0YVZpZXcuZ2V0SW50MzIoKTsKICAgICAgdGhpcy5sb2coJ19pbnQzMicsIG51bSk7CgogICAgICB0aGlzLm9mZnNldCArPSBieXRlTGVuZ3RoOwogICAgICByZXR1cm4gbnVtOwogICAgfQoKICAgIGludDMyKCkgewogICAgICB0aGlzLndhaXRXcml0ZSgnaW50MzInKTsKICAgICAgbGV0IG51bSA9IHRoaXMuX2ludDMyKCk7CiAgICAgIHRoaXMubG9nKCdpbnQzMicsIG51bSk7CgogICAgICBpZiAodGhpcy5wZWVrT2Zmc2V0ID09IG51bGwpIHsKICAgICAgICB0aGlzLmZsaXAoKTsKICAgICAgfQogICAgICByZXR1cm4gbnVtOwogICAgfQoKICAgIGJ5dGVzKCkgewogICAgICB0aGlzLndhaXRXcml0ZSgnYnl0ZXMnKTsKCiAgICAgIGxldCBieXRlTGVuZ3RoID0gdGhpcy5faW50MzIoKTsKCiAgICAgIGxldCBieXRlcyA9IG5ldyBBcnJheUJ1ZmZlcihieXRlTGVuZ3RoKTsKICAgICAgbmV3IFVpbnQ4QXJyYXkoYnl0ZXMpLnNldCgKICAgICAgICBuZXcgVWludDhBcnJheSh0aGlzLmJ1ZmZlciwgdGhpcy5vZmZzZXQsIGJ5dGVMZW5ndGgpCiAgICAgICk7CiAgICAgIHRoaXMubG9nKCdieXRlcycsIGJ5dGVzKTsKCiAgICAgIHRoaXMub2Zmc2V0ICs9IGJ5dGVMZW5ndGg7CgogICAgICBpZiAodGhpcy5wZWVrT2Zmc2V0ID09IG51bGwpIHsKICAgICAgICB0aGlzLmZsaXAoKTsKICAgICAgfQogICAgICByZXR1cm4gYnl0ZXM7CiAgICB9CiAgfQoKICBjbGFzcyBXcml0ZXIgewogICAgY29uc3RydWN0b3IoCiAgICAgIGJ1ZmZlciwKICAgICAgeyBpbml0aWFsT2Zmc2V0ID0gNCwgdXNlQXRvbWljcyA9IHRydWUsIHN0cmVhbSA9IHRydWUsIGRlYnVnLCBuYW1lIH0gPSB7fQogICAgKSB7CiAgICAgIHRoaXMuYnVmZmVyID0gYnVmZmVyOwogICAgICB0aGlzLmF0b21pY1ZpZXcgPSBuZXcgSW50MzJBcnJheShidWZmZXIpOwogICAgICB0aGlzLm9mZnNldCA9IGluaXRpYWxPZmZzZXQ7CiAgICAgIHRoaXMudXNlQXRvbWljcyA9IHVzZUF0b21pY3M7CiAgICAgIHRoaXMuc3RyZWFtID0gc3RyZWFtOwoKICAgICAgdGhpcy5kZWJ1ZyA9IGRlYnVnOwogICAgICB0aGlzLm5hbWUgPSBuYW1lOwoKICAgICAgaWYgKHRoaXMudXNlQXRvbWljcykgewogICAgICAgIC8vIFRoZSBidWZmZXIgc3RhcnRzIG91dCBhcyB3cml0ZWFibGUKICAgICAgICBBdG9taWNzLnN0b3JlKHRoaXMuYXRvbWljVmlldywgMCwgV1JJVEVBQkxFKTsKICAgICAgfSBlbHNlIHsKICAgICAgICB0aGlzLmF0b21pY1ZpZXdbMF0gPSBXUklURUFCTEU7CiAgICAgIH0KICAgIH0KCiAgICBsb2coLi4uYXJncykgewogICAgICBpZiAodGhpcy5kZWJ1ZykgewogICAgICAgIGNvbnNvbGUubG9nKGBbd3JpdGVyOiAke3RoaXMubmFtZX1dYCwgLi4uYXJncyk7CiAgICAgIH0KICAgIH0KCiAgICB3YWl0UmVhZChuYW1lKSB7CiAgICAgIGlmICh0aGlzLnVzZUF0b21pY3MpIHsKICAgICAgICB0aGlzLmxvZyhgd2FpdGluZyBmb3IgJHtuYW1lfWApOwogICAgICAgIC8vIFN3aXRjaCB0byB3cml0YWJsZQogICAgICAgIC8vIEF0b21pY3Muc3RvcmUodGhpcy5hdG9taWNWaWV3LCAwLCAxKTsKCiAgICAgICAgbGV0IHByZXYgPSBBdG9taWNzLmNvbXBhcmVFeGNoYW5nZSgKICAgICAgICAgIHRoaXMuYXRvbWljVmlldywKICAgICAgICAgIDAsCiAgICAgICAgICBXUklURUFCTEUsCiAgICAgICAgICBSRUFEQUJMRQogICAgICAgICk7CgogICAgICAgIGlmIChwcmV2ICE9PSBXUklURUFCTEUpIHsKICAgICAgICAgIHRocm93IG5ldyBFcnJvcigKICAgICAgICAgICAgJ1dyb3RlIHNvbWV0aGluZyBpbnRvIHVud3JpdGFibGUgYnVmZmVyISBUaGlzIGlzIGRpc2FzdHJvdXMnCiAgICAgICAgICApOwogICAgICAgIH0KCiAgICAgICAgQXRvbWljcy5ub3RpZnkodGhpcy5hdG9taWNWaWV3LCAwKTsKCiAgICAgICAgd2hpbGUgKEF0b21pY3MubG9hZCh0aGlzLmF0b21pY1ZpZXcsIDApID09PSBSRUFEQUJMRSkgewogICAgICAgICAgLy8gY29uc29sZS5sb2coJ3dhaXRpbmcgdG8gYmUgcmVhZC4uLicpOwogICAgICAgICAgQXRvbWljcy53YWl0KHRoaXMuYXRvbWljVmlldywgMCwgUkVBREFCTEUsIDUwMCk7CiAgICAgICAgfQoKICAgICAgICB0aGlzLmxvZyhgcmVzdW1lZCBmb3IgJHtuYW1lfWApOwogICAgICB9IGVsc2UgewogICAgICAgIHRoaXMuYXRvbWljVmlld1swXSA9IFJFQURBQkxFOwogICAgICB9CgogICAgICB0aGlzLm9mZnNldCA9IDQ7CiAgICB9CgogICAgZmluYWxpemUoKSB7CiAgICAgIHRoaXMubG9nKCdmaW5hbGl6aW5nJyk7CiAgICAgIGxldCBkYXRhVmlldyA9IG5ldyBEYXRhVmlldyh0aGlzLmJ1ZmZlciwgdGhpcy5vZmZzZXQpOwogICAgICBkYXRhVmlldy5zZXRVaW50MzIoMCwgRklOQUxJWkVEKTsKICAgICAgdGhpcy53YWl0UmVhZCgnZmluYWxpemUnKTsKICAgIH0KCiAgICBzdHJpbmcoc3RyKSB7CiAgICAgIHRoaXMubG9nKCdzdHJpbmcnLCBzdHIpOwoKICAgICAgbGV0IGJ5dGVMZW5ndGggPSBzdHIubGVuZ3RoICogMjsKICAgICAgdGhpcy5faW50MzIoYnl0ZUxlbmd0aCk7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0LCBieXRlTGVuZ3RoKTsKICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBzdHIubGVuZ3RoOyBpKyspIHsKICAgICAgICBkYXRhVmlldy5zZXRVaW50MTYoaSAqIDIsIHN0ci5jaGFyQ29kZUF0KGkpKTsKICAgICAgfQoKICAgICAgdGhpcy5vZmZzZXQgKz0gYnl0ZUxlbmd0aDsKICAgICAgdGhpcy53YWl0UmVhZCgnc3RyaW5nJyk7CiAgICB9CgogICAgX2ludDMyKG51bSkgewogICAgICBsZXQgYnl0ZUxlbmd0aCA9IDQ7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0KTsKICAgICAgZGF0YVZpZXcuc2V0SW50MzIoMCwgbnVtKTsKCiAgICAgIHRoaXMub2Zmc2V0ICs9IGJ5dGVMZW5ndGg7CiAgICB9CgogICAgaW50MzIobnVtKSB7CiAgICAgIHRoaXMubG9nKCdpbnQzMicsIG51bSk7CiAgICAgIHRoaXMuX2ludDMyKG51bSk7CiAgICAgIHRoaXMud2FpdFJlYWQoJ2ludDMyJyk7CiAgICB9CgogICAgYnl0ZXMoYnVmZmVyKSB7CiAgICAgIHRoaXMubG9nKCdieXRlcycsIGJ1ZmZlcik7CgogICAgICBsZXQgYnl0ZUxlbmd0aCA9IGJ1ZmZlci5ieXRlTGVuZ3RoOwogICAgICB0aGlzLl9pbnQzMihieXRlTGVuZ3RoKTsKICAgICAgbmV3IFVpbnQ4QXJyYXkodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0KS5zZXQobmV3IFVpbnQ4QXJyYXkoYnVmZmVyKSk7CgogICAgICB0aGlzLm9mZnNldCArPSBieXRlTGVuZ3RoOwogICAgICB0aGlzLndhaXRSZWFkKCdieXRlcycpOwogICAgfQogIH0KCiAgLy8gTm9vcHMgaW4gcHJvZAogIGFzeW5jIGZ1bmN0aW9uIGVuZCgpIHt9CgogIGxldCBpc1Byb2JhYmx5U2FmYXJpID0gL14oKD8hY2hyb21lfGFuZHJvaWQpLikqc2FmYXJpL2kudGVzdCgKICAgIG5hdmlnYXRvci51c2VyQWdlbnQKICApOwoKICBsZXQgb3BlbkRicyA9IG5ldyBNYXAoKTsKICBsZXQgdHJhbnNhY3Rpb25zID0gbmV3IE1hcCgpOwoKICBmdW5jdGlvbiBhc3NlcnQoY29uZCwgbXNnKSB7CiAgICBpZiAoIWNvbmQpIHsKICAgICAgdGhyb3cgbmV3IEVycm9yKG1zZyk7CiAgICB9CiAgfQoKICBsZXQgTE9DS19UWVBFUyA9IHsKICAgIE5PTkU6IDAsCiAgICBTSEFSRUQ6IDEsCiAgICBSRVNFUlZFRDogMiwKICAgIFBFTkRJTkc6IDMsCiAgICBFWENMVVNJVkU6IDQKICB9OwoKICAvLyBXZSB1c2UgbG9uZy1saXZlZCB0cmFuc2FjdGlvbnMsIGFuZCBgVHJhbnNhY3Rpb25gIGtlZXBzIHRoZQogIC8vIHRyYW5zYWN0aW9uIHN0YXRlLiBJdCBpbXBsZW1lbnRzIGFuIG9wdGltYWwgd2F5IHRvIHBlcmZvcm0KICAvLyByZWFkL3dyaXRlcyB3aXRoIGtub3dsZWRnZSBvZiBob3cgc3FsaXRlIGFza3MgZm9yIHRoZW0sIGFuZCBhbHNvCiAgLy8gaW1wbGVtZW50cyBhIGxvY2tpbmcgbWVjaGFuaXNtIHRoYXQgbWFwcyB0byBob3cgc3FsaXRlIGxvY2tzIHdvcmsuCiAgY2xhc3MgVHJhbnNhY3Rpb24gewogICAgY29uc3RydWN0b3IoZGIsIGluaXRpYWxNb2RlID0gJ3JlYWRvbmx5JykgewogICAgICB0aGlzLmRiID0gZGI7CiAgICAgIHRoaXMudHJhbnMgPSB0aGlzLmRiLnRyYW5zYWN0aW9uKFsnZGF0YSddLCBpbml0aWFsTW9kZSk7CiAgICAgIHRoaXMuc3RvcmUgPSB0aGlzLnRyYW5zLm9iamVjdFN0b3JlKCdkYXRhJyk7CiAgICAgIHRoaXMubG9ja1R5cGUgPQogICAgICAgIGluaXRpYWxNb2RlID09PSAncmVhZG9ubHknID8gTE9DS19UWVBFUy5TSEFSRUQgOiBMT0NLX1RZUEVTLkVYQ0xVU0lWRTsKCiAgICAgIC8vIFRoZXJlIGlzIG5vIG5lZWQgZm9yIHVzIHRvIGNhY2hlIGJsb2Nrcy4gVXNlIHNxbGl0ZSdzCiAgICAgIC8vIGBjYWNoZV9zaXplYCBmb3IgdGhhdCBhbmQgaXQgd2lsbCBhdXRvbWF0aWNhbGx5IGRvIGl0LiBIb3dldmVyLAogICAgICAvLyB3ZSBkbyBzdGlsbCBrZWVwIGEgY2FjaGUgb2YgdGhlIGZpcnN0IGJsb2NrIGZvciB0aGUgZHVyYXRpb24gb2YKICAgICAgLy8gdGhpcyB0cmFuc2FjdGlvbiBiZWNhdXNlIG9mIGhvdyBsb2NraW5nIHdvcmtzOyB0aGlzIGF2b2lkcyBhCiAgICAgIC8vIGZldyBleHRyYSByZWFkcyBhbmQgYWxsb3dzIHVzIHRvIGRldGVjdCBjaGFuZ2VzIGR1cmluZwogICAgICAvLyB1cGdyYWRpbmcgKHNlZSBgdXBncmFkZUV4Y2x1c2l2ZWApCiAgICAgIHRoaXMuY2FjaGVkRmlyc3RCbG9jayA9IG51bGw7CgogICAgICB0aGlzLmN1cnNvciA9IG51bGw7CiAgICAgIHRoaXMucHJldlJlYWRzID0gbnVsbDsKICAgIH0KCiAgICBhc3luYyBwcmVmZXRjaEZpcnN0QmxvY2sodGltZW91dCkgewogICAgICAvLyBUT0RPOiBpbXBsZW1lbnQgdGltZW91dAoKICAgICAgLy8gR2V0IHRoZSBmaXJzdCBibG9jayBhbmQgY2FjaGUgaXQKICAgICAgbGV0IGJsb2NrID0gYXdhaXQgdGhpcy5nZXQoMCk7CiAgICAgIHRoaXMuY2FjaGVkRmlyc3RCbG9jayA9IGJsb2NrOwogICAgICByZXR1cm4gYmxvY2s7CiAgICB9CgogICAgYXN5bmMgd2FpdENvbXBsZXRlKCkgewogICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4gewogICAgICAgIC8vIEVhZ2VybHkgY29tbWl0IGl0IGZvciBiZXR0ZXIgcGVyZi4gTm90ZSB0aGF0ICoqdGhpcyBhc3N1bWVzCiAgICAgICAgLy8gdGhlIHRyYW5zYWN0aW9uIGlzIG9wZW4qKiBhcyBgY29tbWl0YCB3aWxsIHRocm93IGFuIGVycm9yIGlmCiAgICAgICAgLy8gaXQncyBhbHJlYWR5IGNsb3NlZCAod2hpY2ggc2hvdWxkIG5ldmVyIGJlIHRoZSBjYXNlIGZvciB1cykKICAgICAgICB0aGlzLmNvbW1pdCgpOwoKICAgICAgICBpZiAodGhpcy5sb2NrVHlwZSA9PT0gTE9DS19UWVBFUy5FWENMVVNJVkUpIHsKICAgICAgICAgIC8vIFdhaXQgdW50aWwgYWxsIHdyaXRlcyBhcmUgY29tbWl0dGVkCiAgICAgICAgICB0aGlzLnRyYW5zLm9uY29tcGxldGUgPSBlID0+IHJlc29sdmUoKTsKCiAgICAgICAgICAvLyBUT0RPOiBJcyBpdCBPSyB0byBhZGQgdGhpcyBsYXRlciwgYWZ0ZXIgYW4gZXJyb3IgbWlnaHQgaGF2ZQogICAgICAgICAgLy8gaGFwcGVuZWQ/IFdpbGwgaXQgaG9sZCB0aGUgZXJyb3IgYW5kIGZpcmUgdGhpcyB3aGVuIHdlCiAgICAgICAgICAvLyBhdHRhY2hlZCBpdD8gV2UgbWlnaHQgd2FudCB0byBlYWdlcmx5IGNyZWF0ZSB0aGUgcHJvbWlzZQogICAgICAgICAgLy8gd2hlbiBjcmVhdGluZyB0aGUgdHJhbnNhY3Rpb24gYW5kIHJldHVybiBpdCBoZXJlCiAgICAgICAgICB0aGlzLnRyYW5zLm9uZXJyb3IgPSBlID0+IHJlamVjdChlKTsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgaWYgKGlzUHJvYmFibHlTYWZhcmkpIHsKICAgICAgICAgICAgLy8gU2FmYXJpIGhhcyBhIGJ1ZyB3aGVyZSBzb21ldGltZXMgdGhlIElEQiBnZXRzIGJsb2NrZWQKICAgICAgICAgICAgLy8gcGVybWFuZW50bHkgaWYgeW91IHJlZnJlc2ggdGhlIHBhZ2Ugd2l0aCBhbiBvcGVuCiAgICAgICAgICAgIC8vIHRyYW5zYWN0aW9uLiBZb3UgaGF2ZSB0byByZXN0YXJ0IHRoZSBicm93c2VyIHRvIGZpeCBpdC4KICAgICAgICAgICAgLy8gV2Ugd2FpdCBmb3IgcmVhZG9ubHkgdHJhbnNhY3Rpb25zIHRvIGZpbmlzaCB0b28sIGJ1dCB0aGlzCiAgICAgICAgICAgIC8vIGlzIGEgcGVyZiBoaXQKICAgICAgICAgICAgdGhpcy50cmFucy5vbmNvbXBsZXRlID0gZSA9PiByZXNvbHZlKCk7CiAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAvLyBObyBuZWVkIHRvIHdhaXQgb24gYW55dGhpbmcgaW4gYSByZWFkLW9ubHkgdHJhbnNhY3Rpb24uCiAgICAgICAgICAgIC8vIE5vdGUgdGhhdCBlcnJvcnMgZHVyaW5nIHJlYWRzIGFyZWEgYWx3YXlzIGhhbmRsZWQgYnkgdGhlCiAgICAgICAgICAgIC8vIHJlYWQgcmVxdWVzdC4KICAgICAgICAgICAgcmVzb2x2ZSgpOwogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSk7CiAgICB9CgogICAgY29tbWl0KCkgewogICAgICAvLyBTYWZhcmkgZG9lc24ndCBzdXBwb3J0IHRoaXMgbWV0aG9kIHlldCAodGhpcyBpcyBqdXN0IGFuCiAgICAgIC8vIG9wdGltaXphdGlvbikKICAgICAgaWYgKHRoaXMudHJhbnMuY29tbWl0KSB7CiAgICAgICAgdGhpcy50cmFucy5jb21taXQoKTsKICAgICAgfQogICAgfQoKICAgIGFzeW5jIHVwZ3JhZGVFeGNsdXNpdmUoKSB7CiAgICAgIHRoaXMuY29tbWl0KCk7CgogICAgICAvLyBjb25zb2xlLmxvZygndXBkYXRpbmcgdHJhbnNhY3Rpb24gcmVhZHdyaXRlJyk7CiAgICAgIHRoaXMudHJhbnMgPSB0aGlzLmRiLnRyYW5zYWN0aW9uKFsnZGF0YSddLCAncmVhZHdyaXRlJyk7CiAgICAgIHRoaXMuc3RvcmUgPSB0aGlzLnRyYW5zLm9iamVjdFN0b3JlKCdkYXRhJyk7CiAgICAgIHRoaXMubG9ja1R5cGUgPSBMT0NLX1RZUEVTLkVYQ0xVU0lWRTsKCiAgICAgIGxldCBjYWNoZWQwID0gdGhpcy5jYWNoZWRGaXJzdEJsb2NrOwoKICAgICAgLy8gRG8gYSByZWFkCiAgICAgIGxldCBibG9jayA9IGF3YWl0IHRoaXMucHJlZmV0Y2hGaXJzdEJsb2NrKDUwMCk7CiAgICAgIC8vIFRPRE86IHdoZW4gdGltZW91dHMgYXJlIGltcGxlbWVudGVkLCBkZXRlY3QgdGltZW91dCBhbmQgcmV0dXJuIEJVU1kKCiAgICAgIGlmIChjYWNoZWQwID09IG51bGwgJiYgYmxvY2sgPT0gbnVsbCkgewogICAgICAgIHJldHVybiB0cnVlOwogICAgICB9IGVsc2UgewogICAgICAgIGZvciAobGV0IGkgPSAyNDsgaSA8IDQwOyBpKyspIHsKICAgICAgICAgIGlmIChibG9ja1tpXSAhPT0gY2FjaGVkMFtpXSkgewogICAgICAgICAgICByZXR1cm4gZmFsc2U7CiAgICAgICAgICB9CiAgICAgICAgfQogICAgICB9CgogICAgICByZXR1cm4gdHJ1ZTsKICAgIH0KCiAgICBkb3duZ3JhZGVTaGFyZWQoKSB7CiAgICAgIHRoaXMuY29tbWl0KCk7CgogICAgICAvLyBjb25zb2xlLmxvZygnZG93bmdyYWRpbmcgdHJhbnNhY3Rpb24gcmVhZG9ubHknKTsKICAgICAgdGhpcy50cmFucyA9IHRoaXMuZGIudHJhbnNhY3Rpb24oWydkYXRhJ10sICdyZWFkb25seScpOwogICAgICB0aGlzLnN0b3JlID0gdGhpcy50cmFucy5vYmplY3RTdG9yZSgnZGF0YScpOwogICAgICB0aGlzLmxvY2tUeXBlID0gTE9DS19UWVBFUy5TSEFSRUQ7CiAgICB9CgogICAgYXN5bmMgZ2V0KGtleSkgewogICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4gewogICAgICAgIGxldCByZXEgPSB0aGlzLnN0b3JlLmdldChrZXkpOwogICAgICAgIHJlcS5vbnN1Y2Nlc3MgPSBlID0+IHsKICAgICAgICAgIHJlc29sdmUocmVxLnJlc3VsdCk7CiAgICAgICAgfTsKICAgICAgICByZXEub25lcnJvciA9IGUgPT4gcmVqZWN0KGUpOwogICAgICB9KTsKICAgIH0KCiAgICBnZXRSZWFkRGlyZWN0aW9uKCkgewogICAgICAvLyBUaGVyZSBhcmUgYSB0d28gd2F5cyB3ZSBjYW4gcmVhZCBkYXRhOiBhIGRpcmVjdCBgZ2V0YCByZXF1ZXN0CiAgICAgIC8vIG9yIG9wZW5pbmcgYSBjdXJzb3IgYW5kIGl0ZXJhdGluZyB0aHJvdWdoIGRhdGEuIFdlIGRvbid0IGtub3cKICAgICAgLy8gd2hhdCBmdXR1cmUgcmVhZHMgbG9vayBsaWtlLCBzbyB3ZSBkb24ndCBrbm93IHRoZSBiZXN0IHN0cmF0ZWd5CiAgICAgIC8vIHRvIHBpY2suIEFsd2F5cyBjaG9vc2luZyBvbmUgc3RyYXRlZ3kgZm9yZ29lcyBhIGxvdCBvZgogICAgICAvLyBvcHRpbWl6YXRpb24sIGJlY2F1c2UgaXRlcmF0aW5nIHdpdGggYSBjdXJzb3IgaXMgYSBsb3QgZmFzdGVyCiAgICAgIC8vIHRoYW4gbWFueSBgZ2V0YCBjYWxscy4gT24gdGhlIG90aGVyIGhhbmQsIG9wZW5pbmcgYSBjdXJzb3IgaXMKICAgICAgLy8gc2xvdywgYW5kIHNvIGlzIGNhbGxpbmcgYGFkdmFuY2VgIHRvIG1vdmUgYSBjdXJzb3Igb3ZlciBhIGh1Z2UKICAgICAgLy8gcmFuZ2UgKGxpa2UgbW92aW5nIGl0IDEwMDAgaXRlbXMgbGF0ZXIpLCBzbyBtYW55IGBnZXRgIGNhbGxzIHdvdWxkCiAgICAgIC8vIGJlIGZhc3Rlci4gSW4gZ2VuZXJhbDoKICAgICAgLy8KICAgICAgLy8gKiBNYW55IGBnZXRgIGNhbGxzIGFyZSBmYXN0ZXIgd2hlbiBkb2luZyByYW5kb20gYWNjZXNzZXMKICAgICAgLy8gKiBJdGVyYXRpbmcgd2l0aCBhIGN1cnNvciBpcyBmYXN0ZXIgaWYgZG9pbmcgbW9zdGx5IHNlcXVlbnRpYWwKICAgICAgLy8gICBhY2Nlc3NlcwogICAgICAvLwogICAgICAvLyBXZSBpbXBsZW1lbnQgYSBoZXVyaXN0aWMgYW5kIGtlZXBzIHRyYWNrIG9mIHRoZSBsYXN0IDMgcmVhZHMKICAgICAgLy8gYW5kIGRldGVjdHMgd2hlbiB0aGV5IGFyZSBtb3N0bHkgc2VxdWVudGlhbC4gSWYgdGhleSBhcmUsIHdlCiAgICAgIC8vIG9wZW4gYSBjdXJzb3IgYW5kIHN0YXJ0IHJlYWRpbmcgYnkgaXRlcmF0aW5nIGl0LiBJZiBub3QsIHdlIGRvCiAgICAgIC8vIGRpcmVjdCBgZ2V0YCBjYWxscy4KICAgICAgLy8KICAgICAgLy8gT24gdG9wIG9mIGFsbCBvZiB0aGlzLCBlYWNoIGJyb3dzZXIgaGFzIGRpZmZlcmVudCBwZXJmCiAgICAgIC8vIGNoYXJhY3RlcmlzdGljcy4gV2Ugd2lsbCBwcm9iYWJseSB3YW50IHRvIG1ha2UgdGhlc2UgdGhyZXNob2xkcwogICAgICAvLyBjb25maWd1cmFibGUgc28gdGhlIHVzZXIgY2FuIGNoYW5nZSB0aGVtIHBlci1icm93c2VyIGlmIG5lZWRlZCwKICAgICAgLy8gYXMgd2VsbCBhcyBmaW5lLXR1bmluZyB0aGVtIGZvciB0aGVpciB1c2FnZSBvZiBzcWxpdGUuCgogICAgICBsZXQgcHJldlJlYWRzID0gdGhpcy5wcmV2UmVhZHM7CiAgICAgIGlmIChwcmV2UmVhZHMpIHsKICAgICAgICAvLyBIYXMgdGhlcmUgYmVlbiAzIGZvcndhcmQgc2VxdWVudGlhbCByZWFkcyB3aXRoaW4gMTAgYmxvY2tzPwogICAgICAgIGlmICgKICAgICAgICAgIHByZXZSZWFkc1swXSA8IHByZXZSZWFkc1sxXSAmJgogICAgICAgICAgcHJldlJlYWRzWzFdIDwgcHJldlJlYWRzWzJdICYmCiAgICAgICAgICBwcmV2UmVhZHNbMl0gLSBwcmV2UmVhZHNbMF0gPCAxMAogICAgICAgICkgewogICAgICAgICAgcmV0dXJuICduZXh0JzsKICAgICAgICB9CgogICAgICAgIC8vIEhhcyB0aGVyZSBiZWVuIDMgYmFja3dhcmRzIHNlcXVlbnRpYWwgcmVhZHMgd2l0aGluIDEwIGJsb2Nrcz8KICAgICAgICBpZiAoCiAgICAgICAgICBwcmV2UmVhZHNbMF0gPiBwcmV2UmVhZHNbMV0gJiYKICAgICAgICAgIHByZXZSZWFkc1sxXSA+IHByZXZSZWFkc1syXSAmJgogICAgICAgICAgcHJldlJlYWRzWzBdIC0gcHJldlJlYWRzWzJdIDwgMTAKICAgICAgICApIHsKICAgICAgICAgIHJldHVybiAncHJldic7CiAgICAgICAgfQogICAgICB9CgogICAgICByZXR1cm4gbnVsbDsKICAgIH0KCiAgICByZWFkKHBvc2l0aW9uKSB7CiAgICAgIGxldCB3YWl0Q3Vyc29yID0gKCkgPT4gewogICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7CiAgICAgICAgICBpZiAodGhpcy5jdXJzb3JQcm9taXNlICE9IG51bGwpIHsKICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKAogICAgICAgICAgICAgICd3YWl0Q3Vyc29yKCkgY2FsbGVkIGJ1dCBzb21ldGhpbmcgZWxzZSBpcyBhbHJlYWR5IHdhaXRpbmcnCiAgICAgICAgICAgICk7CiAgICAgICAgICB9CiAgICAgICAgICB0aGlzLmN1cnNvclByb21pc2UgPSB7IHJlc29sdmUsIHJlamVjdCB9OwogICAgICAgIH0pOwogICAgICB9OwoKICAgICAgaWYgKHRoaXMuY3Vyc29yKSB7CiAgICAgICAgbGV0IGN1cnNvciA9IHRoaXMuY3Vyc29yOwoKICAgICAgICBpZiAoCiAgICAgICAgICBjdXJzb3IuZGlyZWN0aW9uID09PSAnbmV4dCcgJiYKICAgICAgICAgIHBvc2l0aW9uID4gY3Vyc29yLmtleSAmJgogICAgICAgICAgcG9zaXRpb24gPCBjdXJzb3Iua2V5ICsgMTAwCiAgICAgICAgKSB7CgogICAgICAgICAgY3Vyc29yLmFkdmFuY2UocG9zaXRpb24gLSBjdXJzb3Iua2V5KTsKICAgICAgICAgIHJldHVybiB3YWl0Q3Vyc29yKCk7CiAgICAgICAgfSBlbHNlIGlmICgKICAgICAgICAgIGN1cnNvci5kaXJlY3Rpb24gPT09ICdwcmV2JyAmJgogICAgICAgICAgcG9zaXRpb24gPCBjdXJzb3Iua2V5ICYmCiAgICAgICAgICBwb3NpdGlvbiA+IGN1cnNvci5rZXkgLSAxMDAKICAgICAgICApIHsKCiAgICAgICAgICBjdXJzb3IuYWR2YW5jZShjdXJzb3Iua2V5IC0gcG9zaXRpb24pOwogICAgICAgICAgcmV0dXJuIHdhaXRDdXJzb3IoKTsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgLy8gRGl0Y2ggdGhlIGN1cnNvcgogICAgICAgICAgdGhpcy5jdXJzb3IgPSBudWxsOwogICAgICAgICAgcmV0dXJuIHRoaXMucmVhZChwb3NpdGlvbik7CiAgICAgICAgfQogICAgICB9IGVsc2UgewogICAgICAgIC8vIFdlIGRvbid0IGFscmVhZHkgaGF2ZSBhIGN1cnNvci4gV2UgbmVlZCB0byBhIGZyZXNoIHJlYWQ7CiAgICAgICAgLy8gc2hvdWxkIHdlIG9wZW4gYSBjdXJzb3Igb3IgY2FsbCBgZ2V0YD8KCiAgICAgICAgbGV0IGRpciA9IHRoaXMuZ2V0UmVhZERpcmVjdGlvbigpOwogICAgICAgIGlmIChkaXIpIHsKICAgICAgICAgIC8vIE9wZW4gYSBjdXJzb3IKICAgICAgICAgIHRoaXMucHJldlJlYWRzID0gbnVsbDsKCiAgICAgICAgICBsZXQga2V5UmFuZ2U7CiAgICAgICAgICBpZiAoZGlyID09PSAncHJldicpIHsKICAgICAgICAgICAga2V5UmFuZ2UgPSBJREJLZXlSYW5nZS51cHBlckJvdW5kKHBvc2l0aW9uKTsKICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIGtleVJhbmdlID0gSURCS2V5UmFuZ2UubG93ZXJCb3VuZChwb3NpdGlvbik7CiAgICAgICAgICB9CgogICAgICAgICAgbGV0IHJlcSA9IHRoaXMuc3RvcmUub3BlbkN1cnNvcihrZXlSYW5nZSwgZGlyKTsKCiAgICAgICAgICByZXEub25zdWNjZXNzID0gZSA9PiB7CgogICAgICAgICAgICBsZXQgY3Vyc29yID0gZS50YXJnZXQucmVzdWx0OwogICAgICAgICAgICB0aGlzLmN1cnNvciA9IGN1cnNvcjsKCiAgICAgICAgICAgIGlmICh0aGlzLmN1cnNvclByb21pc2UgPT0gbnVsbCkgewogICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignR290IGRhdGEgZnJvbSBjdXJzb3IgYnV0IG5vdGhpbmcgaXMgd2FpdGluZyBpdCcpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHRoaXMuY3Vyc29yUHJvbWlzZS5yZXNvbHZlKGN1cnNvciA/IGN1cnNvci52YWx1ZSA6IG51bGwpOwogICAgICAgICAgICB0aGlzLmN1cnNvclByb21pc2UgPSBudWxsOwogICAgICAgICAgfTsKICAgICAgICAgIHJlcS5vbmVycm9yID0gZSA9PiB7CiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdDdXJzb3IgZmFpbHVyZTonLCBlKTsKCiAgICAgICAgICAgIGlmICh0aGlzLmN1cnNvclByb21pc2UgPT0gbnVsbCkgewogICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignR290IGRhdGEgZnJvbSBjdXJzb3IgYnV0IG5vdGhpbmcgaXMgd2FpdGluZyBpdCcpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHRoaXMuY3Vyc29yUHJvbWlzZS5yZWplY3QoZSk7CiAgICAgICAgICAgIHRoaXMuY3Vyc29yUHJvbWlzZSA9IG51bGw7CiAgICAgICAgICB9OwoKICAgICAgICAgIHJldHVybiB3YWl0Q3Vyc29yKCk7CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgIGlmICh0aGlzLnByZXZSZWFkcyA9PSBudWxsKSB7CiAgICAgICAgICAgIHRoaXMucHJldlJlYWRzID0gWzAsIDAsIDBdOwogICAgICAgICAgfQogICAgICAgICAgdGhpcy5wcmV2UmVhZHMucHVzaChwb3NpdGlvbik7CiAgICAgICAgICB0aGlzLnByZXZSZWFkcy5zaGlmdCgpOwoKICAgICAgICAgIHJldHVybiB0aGlzLmdldChwb3NpdGlvbik7CiAgICAgICAgfQogICAgICB9CiAgICB9CgogICAgYXN5bmMgc2V0KGl0ZW0pIHsKICAgICAgdGhpcy5wcmV2UmVhZHMgPSBudWxsOwoKICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHsKICAgICAgICBsZXQgcmVxID0gdGhpcy5zdG9yZS5wdXQoaXRlbS52YWx1ZSwgaXRlbS5rZXkpOwogICAgICAgIHJlcS5vbnN1Y2Nlc3MgPSBlID0+IHJlc29sdmUocmVxLnJlc3VsdCk7CiAgICAgICAgcmVxLm9uZXJyb3IgPSBlID0+IHJlamVjdChlKTsKICAgICAgfSk7CiAgICB9CgogICAgYXN5bmMgYnVsa1NldChpdGVtcykgewogICAgICB0aGlzLnByZXZSZWFkcyA9IG51bGw7CgogICAgICBmb3IgKGxldCBpdGVtIG9mIGl0ZW1zKSB7CiAgICAgICAgdGhpcy5zdG9yZS5wdXQoaXRlbS52YWx1ZSwgaXRlbS5rZXkpOwogICAgICB9CiAgICB9CiAgfQoKICBhc3luYyBmdW5jdGlvbiBsb2FkRGIobmFtZSkgewogICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHsKICAgICAgaWYgKG9wZW5EYnMuZ2V0KG5hbWUpKSB7CiAgICAgICAgcmVzb2x2ZShvcGVuRGJzLmdldChuYW1lKSk7CiAgICAgICAgcmV0dXJuOwogICAgICB9CgogICAgICBjb25zb2xlLmxvZygnb3BlbmluZycsIG5hbWUpOwoKICAgICAgbGV0IHJlcSA9IGdsb2JhbFRoaXMuaW5kZXhlZERCLm9wZW4obmFtZSwgMSk7CiAgICAgIHJlcS5vbnN1Y2Nlc3MgPSBldmVudCA9PiB7CiAgICAgICAgY29uc29sZS5sb2coJ2RiIGlzIG9wZW4hJywgbmFtZSk7CiAgICAgICAgbGV0IGRiID0gZXZlbnQudGFyZ2V0LnJlc3VsdDsKCiAgICAgICAgZGIub252ZXJzaW9uY2hhbmdlID0gKCkgPT4gewogICAgICAgICAgLy8gVE9ETzogTm90aWZ5IHRoZSB1c2VyIHNvbWVob3cKICAgICAgICAgIGNvbnNvbGUubG9nKCdjbG9zaW5nIGJlY2F1c2UgdmVyc2lvbiBjaGFuZ2VkJyk7CiAgICAgICAgICBkYi5jbG9zZSgpOwogICAgICAgICAgb3BlbkRicy5kZWxldGUobmFtZSk7CiAgICAgICAgfTsKCiAgICAgICAgZGIub25jbG9zZSA9ICgpID0+IHsKICAgICAgICAgIG9wZW5EYnMuZGVsZXRlKG5hbWUpOwogICAgICAgIH07CgogICAgICAgIG9wZW5EYnMuc2V0KG5hbWUsIGRiKTsKICAgICAgICByZXNvbHZlKGRiKTsKICAgICAgfTsKICAgICAgcmVxLm9udXBncmFkZW5lZWRlZCA9IGV2ZW50ID0+IHsKICAgICAgICBsZXQgZGIgPSBldmVudC50YXJnZXQucmVzdWx0OwogICAgICAgIGlmICghZGIub2JqZWN0U3RvcmVOYW1lcy5jb250YWlucygnZGF0YScpKSB7CiAgICAgICAgICBkYi5jcmVhdGVPYmplY3RTdG9yZSgnZGF0YScpOwogICAgICAgIH0KICAgICAgfTsKICAgICAgcmVxLm9uYmxvY2tlZCA9IGUgPT4gY29uc29sZS5sb2coJ2Jsb2NrZWQnLCBlKTsKICAgICAgcmVxLm9uZXJyb3IgPSByZXEub25hYm9ydCA9IGUgPT4gcmVqZWN0KGUudGFyZ2V0LmVycm9yKTsKICAgIH0pOwogIH0KCiAgZnVuY3Rpb24gY2xvc2VEYihuYW1lKSB7CiAgICBsZXQgb3BlbkRiID0gb3BlbkRicy5nZXQobmFtZSk7CiAgICBpZiAob3BlbkRiKSB7CiAgICAgIG9wZW5EYi5jbG9zZSgpOwogICAgICBvcGVuRGJzLmRlbGV0ZShuYW1lKTsKICAgIH0KICB9CgogIGZ1bmN0aW9uIGdldFRyYW5zYWN0aW9uKG5hbWUpIHsKICAgIHJldHVybiB0cmFuc2FjdGlvbnMuZ2V0KG5hbWUpOwogIH0KCiAgYXN5bmMgZnVuY3Rpb24gd2l0aFRyYW5zYWN0aW9uKG5hbWUsIG1vZGUsIGZ1bmMpIHsKICAgIGxldCB0cmFucyA9IHRyYW5zYWN0aW9ucy5nZXQobmFtZSk7CiAgICBpZiAodHJhbnMpIHsKICAgICAgLy8gSWYgYSB0cmFuc2FjdGlvbiBhbHJlYWR5IGV4aXN0cywgdGhhdCBtZWFucyB0aGUgZmlsZSBoYXMgYmVlbgogICAgICAvLyBsb2NrZWQuIFdlIGRvbid0IGZ1bGx5IHN1cHBvcnQgYXJiaXRyYXJ5IG5lc3RlZCB0cmFuc2FjdGlvbnMsCiAgICAgIC8vIGFzIHNlZW4gYmVsb3cgKHdlIHdvbid0IHVwZ3JhZGUgYSBgcmVhZG9ubHlgIHRvIGByZWFkd3JpdGVgCiAgICAgIC8vIGF1dG9tYXRpY2FsbHkpIGFuZCB0aGlzIGlzIG1haW5seSBmb3IgdGhlIHVzZSBjYXNlIHdoZXJlIHNxbGl0ZQogICAgICAvLyBsb2NrcyB0aGUgZGIgYW5kIGNyZWF0ZXMgYSB0cmFuc2FjdGlvbiBmb3IgdGhlIGR1cmFjdGlvbiBvZiB0aGUKICAgICAgLy8gbG9jay4gV2UgZG9uJ3QgYWN0dWFsbHkgd3JpdGUgY29kZSBpbiBhIHdheSB0aGF0IGFzc3VtZXMgbmVzdGVkCiAgICAgIC8vIHRyYW5zYWN0aW9ucywgc28ganVzdCBlcnJvciBoZXJlCiAgICAgIGlmIChtb2RlID09PSAncmVhZHdyaXRlJyAmJiB0cmFucy5sb2NrVHlwZSA9PT0gTE9DS19UWVBFUy5TSEFSRUQpIHsKICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0F0dGVtcHRlZCB3cml0ZSBidXQgb25seSBoYXMgU0hBUkVEIGxvY2snKTsKICAgICAgfQogICAgICByZXR1cm4gZnVuYyh0cmFucyk7CiAgICB9CgogICAgLy8gT3V0c2lkZSB0aGUgc2NvcGUgb2YgYSBsb2NrLCBjcmVhdGUgYSB0ZW1wb3JhcnkgdHJhbnNhY3Rpb24KICAgIHRyYW5zID0gbmV3IFRyYW5zYWN0aW9uKGF3YWl0IGxvYWREYihuYW1lKSwgbW9kZSk7CiAgICBhd2FpdCBmdW5jKHRyYW5zKTsKICAgIGF3YWl0IHRyYW5zLndhaXRDb21wbGV0ZSgpOwogIH0KCiAgLy8gTG9ja2luZyBzdHJhdGVneToKICAvLwogIC8vICogV2UgbWFwIHNxbGl0ZSdzIGxvY2tzIG9udG8gSW5kZXhlZERCJ3MgdHJhbnNhY3Rpb24gc2VtYW50aWNzLgogIC8vICAgUmVhZCB0cmFuc2FjdGlvbnMgbWF5IGV4ZWN1dGUgaW4gcGFyYWxsZWwuIFJlYWQvd3JpdGUKICAvLyAgIHRyYW5zYWN0aW9ucyBhcmUgcXVldWVkIHVwIGFuZCB3YWl0IHVudGlsIGFsbCBwcmVjZWRpbmcKICAvLyAgIHJlYWQgdHJhbnNhY3Rpb25zIGZpbmlzaCBleGVjdXRpbmcuIFJlYWQgdHJhbnNhY3Rpb25zIHN0YXJ0ZWQKICAvLyAgIGFmdGVyIGEgcmVhZC93cml0ZSB0cmFuc2FjdGlvbiB3YWl0IHVudGlsIGl0IGlzIGZpbmlzaGVkLgogIC8vCiAgLy8gKiBJREIgdHJhbnNhY3Rpb25zIHdpbGwgd2FpdCBmb3JldmVyIHVudGlsIHRoZXkgY2FuIGV4ZWN1dGUgKGZvcgogIC8vICAgZXhhbXBsZSwgdGhleSBtYXkgYmUgYmxvY2tlZCBvbiBhIHJlYWQvd3JpdGUgdHJhbnNhY3Rpb24pLiBXZQogIC8vICAgZG9uJ3Qgd2FudCB0byBhbGxvdyBzcWxpdGUgdHJhbnNhY3Rpb25zIHRvIHdhaXQgZm9yZXZlciwgc28KICAvLyAgIHdlIG1hbnVhbGx5IHRpbWVvdXQgaWYgYSB0cmFuc2FjdGlvbiB0YWtlcyB0b28gbG9uZyB0bwogIC8vICAgc3RhcnQgZXhlY3V0aW5nLiBUaGlzIHNpbXVsYXRlcyB0aGUgYmVoYXZpb3Igb2YgYSBzcWxpdGUKICAvLyAgIGJhaWxpbmcgaWYgaXQgY2FuJ3QgcmVxdWlyZSBhIGxvY2suCiAgLy8KICAvLyAqIEEgU0hBUkVEIGxvY2sgd2FudHMgdG8gcmVhZCBmcm9tIHRoZSBkYi4gV2Ugc3RhcnQgYSByZWFkCiAgLy8gICB0cmFuc2FjdGlvbiBhbmQgcmVhZCB0aGUgZmlyc3QgYmxvY2ssIGFuZCBpZiB3ZSByZWFkIGl0IHdpdGhpbgogIC8vICAgNTAwbXMgd2UgY29uc2lkZXIgdGhlIGxvY2sgc3VjY2Vzc2Z1bC4gT3RoZXJ3aXNlIHRoZSBsb2NrCiAgLy8gICBmYWlsZWQgYW5kIHdlIHJldHVybiBTUUxJVEVfQlVTWS4gKFRoZXJlJ3Mgbm8gcGVyZiBkb3duc2lkZQogIC8vICAgdG8gcmVhZGluZyB0aGUgZmlyc3QgYmxvY2sgLSBpdCBoYXMgdG8gYmUgcmVhZCBhbnl3YXkgdG8gY2hlY2sKICAvLyAgIGJ5dGVzIDI0LTM5IGZvciB0aGUgY2hhbmdlIGNvdW50ZXIpCiAgLy8KICAvLyAqIEEgUkVTRVJWRUQgbG9jayBtZWFucyB0aGUgZGIgd2FudHMgdG8gc3RhcnQgd3JpdGluZyAodGhpbmsgb2YKICAvLyAgIGBCRUdJTiBUUkFOU0FDVElPTmApLiBPbmx5IG9uZSBwcm9jZXNzIGNhbiBvYnRhaW4gYSBSRVNFUlZFRAogIC8vICAgbG9jayBhdCBhIHRpbWUsIGJ1dCBub3JtYWxseSBzcWxpdGUgc3RpbGwgbGVhZHMgbmV3IHJlYWQgbG9ja3MKICAvLyAgIGhhcHBlbi4gSXQgaXNuJ3QgdW50aWwgYW4gRVhDTFVTSVZFIGxvY2sgaXMgaGVsZCB0aGF0IHJlYWRzIGFyZQogIC8vICAgYmxvY2tlZC4gSG93ZXZlciwgc2luY2Ugd2UgbmVlZCB0byBndWFyYW50ZWUgb25seSBvbmUgUkVTRVJWRUQKICAvLyAgIGxvY2sgYXQgb25jZSAob3RoZXJ3aXNlIGRhdGEgY291bGQgY2hhbmdlIGZyb20gYW5vdGhlciBwcm9jZXNzCiAgLy8gICB3aXRoaW4gYSB0cmFuc2FjdGlvbiwgY2F1c2luZyBmYXVsdHkgY2FjaGVzIGV0YykgdGhlIHNpbXBsZXN0CiAgLy8gICB0aGluZyB0byBkbyBpcyBnbyBhaGVhZCBhbmQgZ3JhYiBhIHJlYWQvd3JpdGUgdHJhbnNhY3Rpb24gdGhhdAogIC8vICAgcmVwcmVzZW50cyB0aGUgUkVTRVJWRUQgbG9jay4gVGhpcyB3aWxsIGJsb2NrIGFsbCByZWFkcyBmcm9tCiAgLy8gICBoYXBwZW5pbmcsIGFuZCBpcyBlc3NlbnRpYWxseSB0aGUgc2FtZSBhcyBhbiBFWENMVVNJVkUgbG9jay4KICAvLwogIC8vICAgICAqIFRoZSBtYWluIHByb2JsZW0gaGVyZSBpcyB3ZSBjYW4ndCAidXBncmFkZSIgYSBgcmVhZG9ubHlgCiAgLy8gICAgICAgdHJhbnNhY3Rpb24gdG8gYHJlYWR3cml0ZWAsIGJ1dCBuYXRpdmUgc3FsaXRlIGNhbiB1cGdyYWRlIGEKICAvLyAgICAgICBsb2NrIGZyb20gU0hBUkVEIHRvIFJFU0VSVkVELiBXZSBuZWVkIHRvIHN0YXJ0IGEgbmV3CiAgLy8gICAgICAgdHJhbnNhY3Rpb24gdG8gZG8gc28sIGFuZCBiZWNhdXNlIG9mIHRoYXQgdGhlcmUgbWlnaHQgYmUKICAvLyAgICAgICBvdGhlciBgcmVhZHdyaXRlYCB0cmFuc2FjdGlvbnMgdGhhdCBnZXQgcnVuIGR1cmluZyB0aGUKICAvLyAgICAgICAidXBncmFkZSIgd2hpY2ggaW52YWxpZGF0ZXMgdGhlIHdob2xlIGxvY2tpbmcgcHJvY2VzcyBhbmQKICAvLyAgICAgICBhbmQgY29ycnVwdHMgZGF0YS4KICAvLwogIC8vICogSWRlYWxseSwgd2UgY291bGQgdGVsbCBzcWxpdGUgdG8gc2tpcCBTSEFSRUQgbG9ja3MgZW50aXJlbHkuIFdlCiAgLy8gICBkb24ndCBuZWVkIHRoZW0gc2luY2Ugd2UgY2FuIHJlbHkgb24gSW5kZXhlZERCJ3Mgc2VtYW50aWNzLgogIC8vICAgVGhlbiB3aGVuIGl0IHdhbnRzIHRvIHN0YXJ0IHdyaXRpbmcsIHdlIGdldCBhIFJFU0VSVkVEIGxvY2sKICAvLyAgIHdpdGhvdXQgaGF2aW5nIHRvIHVwZ3JhZGUgZnJvbSBTSEFSRUQuIFRoaXMgd291bGQgc2F2ZSB1cwogIC8vICAgdGhlIGNvc3Qgb2YgYSBgcmVhZG9ubHlgIHRyYW5zYWN0aW9uIHdoZW4gd3JpdGluZzsgcmlnaHQgbm93CiAgLy8gICBpdCBtdXN0IG9wZW4gYSBgcmVhZG9ubHlgIHRyYW5zYWN0aW9uIGFuZCB0aGVuIGltbWVkaWF0ZWx5IG9wZW4KICAvLyAgIGEgYHJlYWR3cml0ZWAgdG8gdXBncmFkZSBpdC4gSSB0aG91Z2h0IG9mIGRlZmVycmluZyBvcGVuaW5nIHRoZQogIC8vICAgYHJlYWRvbmx5YCB0cmFuc2FjdGlvbiB1bnRpbCBzb21ldGhpbmcgaXMgYWN0dWFsbHkgcmVhZCwgYnV0CiAgLy8gICB1bmZvcnR1bmF0ZWx5IHNxbGl0ZSBvcGVucyBpdCwgcmVhZHMgdGhlIGZpcnN0IGJsb2NrLCBhbmQgdGhlbgogIC8vICAgdXBncmFkZXMgaXQuIFNvIHRoZXJlJ3Mgbm8gd2F5IGFyb3VuZCBpdC4gKFdlIGNhbid0IGFzc3VtZSBpdCdzCiAgLy8gICBhIGByZWFkd3JpdGVgIHRyYW5zYWN0aW9uIGF0IHRoYXQgcG9pbnQgc2luY2UgdGhhdCB3b3VsZCBhc3N1bWUKICAvLyAgIGFsbCBTSEFSRUQgbG9ja3MgYXJlIGByZWFkd3JpdGVgLCByZW1vdmluZyB0aGUgcG9zc2liaWxpdHkgb2YKICAvLyAgIGNvbmN1cnJlbnQgcmVhZHMpLgogIC8vCiAgLy8gKiBVcGdyYWRpbmcgdG8gYW4gRVhDTFVTSVZFIGxvY2sgaXMgYSBub29wLCBzaW5jZSB3ZSB0cmVhdCBSRVNFUlZFRAogIC8vICAgbG9ja3MgYXMgRVhDTFVTSVZFLgogIGFzeW5jIGZ1bmN0aW9uIGhhbmRsZUxvY2sod3JpdGVyLCBuYW1lLCBsb2NrVHlwZSkgewogICAgLy8gY29uc29sZS5sb2coJ2xvY2tpbmcnLCBuYW1lLCBsb2NrVHlwZSwgcGVyZm9ybWFuY2Uubm93KCkpOwoKICAgIGxldCB0cmFucyA9IHRyYW5zYWN0aW9ucy5nZXQobmFtZSk7CiAgICBpZiAodHJhbnMpIHsKICAgICAgaWYgKGxvY2tUeXBlID4gdHJhbnMubG9ja1R5cGUpIHsKICAgICAgICAvLyBVcGdyYWRlIFNIQVJFRCB0byBFWENMVVNJVkUKICAgICAgICBhc3NlcnQoCiAgICAgICAgICB0cmFucy5sb2NrVHlwZSA9PT0gTE9DS19UWVBFUy5TSEFSRUQsCiAgICAgICAgICBgVXByYWRpbmcgbG9jayB0eXBlIGZyb20gJHt0cmFucy5sb2NrVHlwZX0gaXMgaW52YWxpZGAKICAgICAgICApOwogICAgICAgIGFzc2VydCgKICAgICAgICAgIGxvY2tUeXBlID09PSBMT0NLX1RZUEVTLlJFU0VSVkVEIHx8IGxvY2tUeXBlID09PSBMT0NLX1RZUEVTLkVYQ0xVU0lWRSwKICAgICAgICAgIGBVcGdyYWRpbmcgbG9jayB0eXBlIHRvICR7bG9ja1R5cGV9IGlzIGludmFsaWRgCiAgICAgICAgKTsKCiAgICAgICAgbGV0IHN1Y2Nlc3MgPSBhd2FpdCB0cmFucy51cGdyYWRlRXhjbHVzaXZlKCk7CiAgICAgICAgd3JpdGVyLmludDMyKHN1Y2Nlc3MgPyAwIDogLTEpOwogICAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgICB9IGVsc2UgewogICAgICAgIC8vIElmIG5vdCB1cGdyYWRpbmcgYW5kIHdlIGFscmVhZHkgaGF2ZSBhIGxvY2ssIG1ha2Ugc3VyZSB0aGlzCiAgICAgICAgLy8gaXNuJ3QgYSBkb3duZ3JhZGUKICAgICAgICBhc3NlcnQoCiAgICAgICAgICB0cmFucy5sb2NrVHlwZSA9PT0gbG9ja1R5cGUsCiAgICAgICAgICBgRG93bmdyYWRpbmcgbG9jayB0byAke2xvY2tUeXBlfSBpcyBpbnZhbGlkYAogICAgICAgICk7CgogICAgICAgIHdyaXRlci5pbnQzMigwKTsKICAgICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgICAgfQogICAgfSBlbHNlIHsKICAgICAgYXNzZXJ0KAogICAgICAgIGxvY2tUeXBlID09PSBMT0NLX1RZUEVTLlNIQVJFRCwKICAgICAgICBgTmV3IGxvY2tzIG11c3Qgc3RhcnQgYXMgU0hBUkVEIGluc3RlYWQgb2YgJHtsb2NrVHlwZX1gCiAgICAgICk7CgogICAgICBsZXQgdHJhbnMgPSBuZXcgVHJhbnNhY3Rpb24oYXdhaXQgbG9hZERiKG5hbWUpKTsKICAgICAgaWYgKChhd2FpdCB0cmFucy5wcmVmZXRjaEZpcnN0QmxvY2soNTAwKSkgPT0gbnVsbCkgOwoKICAgICAgdHJhbnNhY3Rpb25zLnNldChuYW1lLCB0cmFucyk7CgogICAgICB3cml0ZXIuaW50MzIoMCk7CiAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgfQogIH0KCiAgYXN5bmMgZnVuY3Rpb24gaGFuZGxlVW5sb2NrKHdyaXRlciwgbmFtZSwgbG9ja1R5cGUpIHsKICAgIC8vIGNvbnNvbGUubG9nKCd1bmxvY2tpbmcnLCBuYW1lLCBsb2NrVHlwZSwgcGVyZm9ybWFuY2Uubm93KCkpOwoKICAgIGxldCB0cmFucyA9IGdldFRyYW5zYWN0aW9uKG5hbWUpOwoKICAgIGlmIChsb2NrVHlwZSA9PT0gTE9DS19UWVBFUy5TSEFSRUQpIHsKICAgICAgaWYgKHRyYW5zID09IG51bGwpIHsKICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1VubG9jayBlcnJvciAoU0hBUkVEKTogbm8gdHJhbnNhY3Rpb24gcnVubmluZycpOwogICAgICB9CgogICAgICBpZiAodHJhbnMubG9ja1R5cGUgPT09IExPQ0tfVFlQRVMuRVhDTFVTSVZFKSB7CiAgICAgICAgdHJhbnMuZG93bmdyYWRlU2hhcmVkKCk7CiAgICAgIH0KICAgIH0gZWxzZSBpZiAobG9ja1R5cGUgPT09IExPQ0tfVFlQRVMuTk9ORSkgewogICAgICAvLyBJIHRob3VnaHQgd2UgY291bGQgYXNzdW1lIGEgbG9jayBpcyBhbHdheXMgb3BlbiB3aGVuIGB1bmxvY2tgCiAgICAgIC8vIGlzIGNhbGxlZCwgYnV0IGl0IGFsc28gY2FsbHMgYHVubG9ja2Agd2hlbiBjbG9zaW5nIHRoZSBmaWxlIG5vCiAgICAgIC8vIG1hdHRlciB3aGF0LiBEbyBub3RoaW5nIGlmIHRoZXJlJ3Mgbm8gbG9jayBjdXJyZW50bHkKICAgICAgaWYgKHRyYW5zKSB7CiAgICAgICAgLy8gVE9ETzogdGhpcyBpcyB3aGVyZSBhbiBlcnJvciBjb3VsZCBidWJibGUgdXAuIEhhbmRsZSBpdAogICAgICAgIGF3YWl0IHRyYW5zLndhaXRDb21wbGV0ZSgpOwogICAgICAgIHRyYW5zYWN0aW9ucy5kZWxldGUobmFtZSk7CiAgICAgIH0KICAgIH0KCiAgICB3cml0ZXIuaW50MzIoMCk7CiAgICB3cml0ZXIuZmluYWxpemUoKTsKICB9CgogIGFzeW5jIGZ1bmN0aW9uIGhhbmRsZVJlYWQod3JpdGVyLCBuYW1lLCBwb3NpdGlvbikgewogICAgcmV0dXJuIHdpdGhUcmFuc2FjdGlvbihuYW1lLCAncmVhZG9ubHknLCBhc3luYyB0cmFucyA9PiB7CiAgICAgIGxldCBkYXRhID0gYXdhaXQgdHJhbnMucmVhZChwb3NpdGlvbik7CgogICAgICBpZiAoZGF0YSA9PSBudWxsKSB7CiAgICAgICAgd3JpdGVyLmJ5dGVzKG5ldyBBcnJheUJ1ZmZlcigwKSk7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgd3JpdGVyLmJ5dGVzKGRhdGEpOwogICAgICB9CiAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgfSk7CiAgfQoKICBhc3luYyBmdW5jdGlvbiBoYW5kbGVXcml0ZXMod3JpdGVyLCBuYW1lLCB3cml0ZXMpIHsKICAgIHJldHVybiB3aXRoVHJhbnNhY3Rpb24obmFtZSwgJ3JlYWR3cml0ZScsIGFzeW5jIHRyYW5zID0+IHsKICAgICAgYXdhaXQgdHJhbnMuYnVsa1NldCh3cml0ZXMubWFwKHcgPT4gKHsga2V5OiB3LnBvcywgdmFsdWU6IHcuZGF0YSB9KSkpOwoKICAgICAgd3JpdGVyLmludDMyKDApOwogICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgIH0pOwogIH0KCiAgYXN5bmMgZnVuY3Rpb24gaGFuZGxlUmVhZE1ldGEod3JpdGVyLCBuYW1lKSB7CiAgICByZXR1cm4gd2l0aFRyYW5zYWN0aW9uKG5hbWUsICdyZWFkb25seScsIGFzeW5jIHRyYW5zID0+IHsKICAgICAgdHJ5IHsKICAgICAgICBjb25zb2xlLmxvZygnUmVhZGluZyBtZXRhJyk7CiAgICAgICAgbGV0IHJlcyA9IGF3YWl0IHRyYW5zLmdldCgtMSk7CiAgICAgICAgY29uc29sZS5sb2coJ1JlYWRpbmcgbWV0YSAoZG9uZSknLCByZXMpOwoKICAgICAgICBsZXQgbWV0YSA9IHJlczsKICAgICAgICB3cml0ZXIuaW50MzIobWV0YSA/IG1ldGEuc2l6ZSA6IC0xKTsKICAgICAgICB3cml0ZXIuaW50MzIobWV0YSA/IG1ldGEuYmxvY2tTaXplIDogLTEpOwogICAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgICB9IGNhdGNoIChlcnIpIHsKICAgICAgICBjb25zb2xlLmxvZyhlcnIpOwogICAgICAgIHdyaXRlci5pbnQzMigtMSk7CiAgICAgICAgd3JpdGVyLmludDMyKC0xKTsKICAgICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgICAgfQogICAgfSk7CiAgfQoKICBhc3luYyBmdW5jdGlvbiBoYW5kbGVXcml0ZU1ldGEod3JpdGVyLCBuYW1lLCBtZXRhKSB7CiAgICByZXR1cm4gd2l0aFRyYW5zYWN0aW9uKG5hbWUsICdyZWFkd3JpdGUnLCBhc3luYyB0cmFucyA9PiB7CiAgICAgIHRyeSB7CiAgICAgICAgYXdhaXQgdHJhbnMuc2V0KHsga2V5OiAtMSwgdmFsdWU6IG1ldGEgfSk7CgogICAgICAgIHdyaXRlci5pbnQzMigwKTsKICAgICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgICAgfSBjYXRjaCAoZXJyKSB7CiAgICAgICAgY29uc29sZS5sb2coZXJyKTsKICAgICAgICB3cml0ZXIuaW50MzIoLTEpOwogICAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgICB9CiAgICB9KTsKICB9CgogIGFzeW5jIGZ1bmN0aW9uIGhhbmRsZURlbGV0ZUZpbGUod3JpdGVyLCBuYW1lKSB7CiAgICB0cnkgewogICAgICBjbG9zZURiKG5hbWUpOwoKICAgICAgYXdhaXQgbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4gewogICAgICAgIGxldCByZXEgPSBnbG9iYWxUaGlzLmluZGV4ZWREQi5kZWxldGVEYXRhYmFzZShuYW1lKTsKICAgICAgICByZXEub25zdWNjZXNzID0gcmVzb2x2ZTsKICAgICAgICByZXEub25lcnJvciA9IHJlamVjdDsKICAgICAgfSk7CgogICAgICB3cml0ZXIuaW50MzIoMCk7CiAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgfSBjYXRjaCAoZXJyKSB7CiAgICAgIHdyaXRlci5pbnQzMigtMSk7CiAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgfQogIH0KCiAgYXN5bmMgZnVuY3Rpb24gaGFuZGxlQ2xvc2VGaWxlKHdyaXRlciwgbmFtZSkgewogICAgY2xvc2VEYihuYW1lKTsKCiAgICB3cml0ZXIuaW50MzIoMCk7CiAgICB3cml0ZXIuZmluYWxpemUoKTsKICB9CgogIC8vIGBsaXN0ZW5gIGNvbnRpbnVhbGx5IGxpc3RlbnMgZm9yIHJlcXVlc3RzIHZpYSB0aGUgc2hhcmVkIGJ1ZmZlci4KICAvLyBSaWdodCBub3cgaXQncyBpbXBsZW1lbnRlZCBpbiBhIHRhaWwtY2FsbCBzdHlsZSAoYGxpc3RlbmAgaXMKICAvLyByZWN1cnNpdmVseSBjYWxsZWQpIGJlY2F1c2UgSSB0aG91Z2h0IHRoYXQgd2FzIG5lY2Vzc2FyeSBmb3IKICAvLyB2YXJpb3VzIHJlYXNvbnMuIFdlIGNhbiBjb252ZXJ0IHRoaXMgdG8gYSBgd2hpbGUoMSlgIGxvb3Agd2l0aAogIC8vIGFuZCB1c2UgYGF3YWl0YCB0aG91Z2gKICBhc3luYyBmdW5jdGlvbiBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpIHsKICAgIGxldCBtZXRob2QgPSByZWFkZXIuc3RyaW5nKCk7CgogICAgc3dpdGNoIChtZXRob2QpIHsKICAgICAgY2FzZSAnc3RhdHMtc3RhcnQnOiB7CiAgICAgICAgcmVhZGVyLmRvbmUoKTsKCiAgICAgICAgd3JpdGVyLmludDMyKDApOwogICAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgICAgIGxpc3RlbihyZWFkZXIsIHdyaXRlcik7CiAgICAgICAgYnJlYWs7CiAgICAgIH0KCiAgICAgIGNhc2UgJ3N0YXRzJzogewogICAgICAgIHJlYWRlci5kb25lKCk7CgogICAgICAgIGF3YWl0IGVuZCgpOwoKICAgICAgICB3cml0ZXIuaW50MzIoMCk7CiAgICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICAgICAgbGlzdGVuKHJlYWRlciwgd3JpdGVyKTsKICAgICAgICBicmVhazsKICAgICAgfQoKICAgICAgY2FzZSAnd3JpdGVCbG9ja3MnOiB7CiAgICAgICAgbGV0IG5hbWUgPSByZWFkZXIuc3RyaW5nKCk7CiAgICAgICAgbGV0IHdyaXRlcyA9IFtdOwogICAgICAgIHdoaWxlICghcmVhZGVyLmRvbmUoKSkgewogICAgICAgICAgbGV0IHBvcyA9IHJlYWRlci5pbnQzMigpOwogICAgICAgICAgbGV0IGRhdGEgPSByZWFkZXIuYnl0ZXMoKTsKICAgICAgICAgIHdyaXRlcy5wdXNoKHsgcG9zLCBkYXRhIH0pOwogICAgICAgIH0KCiAgICAgICAgYXdhaXQgaGFuZGxlV3JpdGVzKHdyaXRlciwgbmFtZSwgd3JpdGVzKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICdyZWFkQmxvY2snOiB7CiAgICAgICAgbGV0IG5hbWUgPSByZWFkZXIuc3RyaW5nKCk7CiAgICAgICAgbGV0IHBvcyA9IHJlYWRlci5pbnQzMigpOwogICAgICAgIHJlYWRlci5kb25lKCk7CgogICAgICAgIGF3YWl0IGhhbmRsZVJlYWQod3JpdGVyLCBuYW1lLCBwb3MpOwogICAgICAgIGxpc3RlbihyZWFkZXIsIHdyaXRlcik7CiAgICAgICAgYnJlYWs7CiAgICAgIH0KCiAgICAgIGNhc2UgJ3JlYWRNZXRhJzogewogICAgICAgIGxldCBuYW1lID0gcmVhZGVyLnN0cmluZygpOwogICAgICAgIHJlYWRlci5kb25lKCk7CiAgICAgICAgYXdhaXQgaGFuZGxlUmVhZE1ldGEod3JpdGVyLCBuYW1lKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICd3cml0ZU1ldGEnOiB7CiAgICAgICAgbGV0IG5hbWUgPSByZWFkZXIuc3RyaW5nKCk7CiAgICAgICAgbGV0IHNpemUgPSByZWFkZXIuaW50MzIoKTsKICAgICAgICBsZXQgYmxvY2tTaXplID0gcmVhZGVyLmludDMyKCk7CiAgICAgICAgcmVhZGVyLmRvbmUoKTsKICAgICAgICBhd2FpdCBoYW5kbGVXcml0ZU1ldGEod3JpdGVyLCBuYW1lLCB7IHNpemUsIGJsb2NrU2l6ZSB9KTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICdkZWxldGVGaWxlJzogewogICAgICAgIGxldCBuYW1lID0gcmVhZGVyLnN0cmluZygpOwogICAgICAgIHJlYWRlci5kb25lKCk7CgogICAgICAgIGF3YWl0IGhhbmRsZURlbGV0ZUZpbGUod3JpdGVyLCBuYW1lKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICdjbG9zZUZpbGUnOiB7CiAgICAgICAgbGV0IG5hbWUgPSByZWFkZXIuc3RyaW5nKCk7CiAgICAgICAgcmVhZGVyLmRvbmUoKTsKCiAgICAgICAgYXdhaXQgaGFuZGxlQ2xvc2VGaWxlKHdyaXRlciwgbmFtZSk7CiAgICAgICAgbGlzdGVuKHJlYWRlciwgd3JpdGVyKTsKICAgICAgICBicmVhazsKICAgICAgfQoKICAgICAgY2FzZSAnbG9ja0ZpbGUnOiB7CiAgICAgICAgbGV0IG5hbWUgPSByZWFkZXIuc3RyaW5nKCk7CiAgICAgICAgbGV0IGxvY2tUeXBlID0gcmVhZGVyLmludDMyKCk7CiAgICAgICAgcmVhZGVyLmRvbmUoKTsKCiAgICAgICAgYXdhaXQgaGFuZGxlTG9jayh3cml0ZXIsIG5hbWUsIGxvY2tUeXBlKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICd1bmxvY2tGaWxlJzogewogICAgICAgIGxldCBuYW1lID0gcmVhZGVyLnN0cmluZygpOwogICAgICAgIGxldCBsb2NrVHlwZSA9IHJlYWRlci5pbnQzMigpOwogICAgICAgIHJlYWRlci5kb25lKCk7CgogICAgICAgIGF3YWl0IGhhbmRsZVVubG9jayh3cml0ZXIsIG5hbWUsIGxvY2tUeXBlKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBkZWZhdWx0OgogICAgICAgIHRocm93IG5ldyBFcnJvcignVW5rbm93biBtZXRob2Q6ICcgKyBtZXRob2QpOwogICAgfQogIH0KCiAgc2VsZi5vbm1lc3NhZ2UgPSBtc2cgPT4gewogICAgc3dpdGNoIChtc2cuZGF0YS50eXBlKSB7CiAgICAgIGNhc2UgJ2luaXQnOiB7CiAgICAgICAgcG9zdE1lc3NhZ2UoeyB0eXBlOiAnd29ya2VyLXJlYWR5JyB9KTsKICAgICAgICBsZXQgW2FyZ0J1ZmZlciwgcmVzdWx0QnVmZmVyXSA9IG1zZy5kYXRhLmJ1ZmZlcnM7CiAgICAgICAgbGV0IHJlYWRlciA9IG5ldyBSZWFkZXIoYXJnQnVmZmVyLCB7IG5hbWU6ICdhcmdzJywgZGVidWc6IGZhbHNlIH0pOwogICAgICAgIGxldCB3cml0ZXIgPSBuZXcgV3JpdGVyKHJlc3VsdEJ1ZmZlciwgeyBuYW1lOiAncmVzdWx0cycsIGRlYnVnOiBmYWxzZSB9KTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CiAgICB9CiAgfTsKCn0oKSk7Cgo=', null, false); /* eslint-enable */ let workerReady = null; @@ -789,6 +790,16 @@ function invokeWorker(method, args) { 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); @@ -839,7 +850,11 @@ class FileOps { } delete() { - invokeWorker('deleteFile', { name: this.getStoreName() }); + return invokeWorker('deleteFile', { name: this.getStoreName() }); + } + + close() { + return invokeWorker('closeFile', { name: this.getStoreName() }); } readMeta() { diff --git a/dist/memory-backend.js b/dist/memory-backend.js index dbca2cf..86824cb 100644 --- a/dist/memory-backend.js +++ b/dist/memory-backend.js @@ -134,6 +134,7 @@ class File { close() { this.fsync(); + this.ops.close(); } delete() { @@ -351,6 +352,10 @@ class FileOps { return true; } + close() { + return true; + } + delete() { // in-memory noop } diff --git a/dist/perf/index.js b/dist/perf/index.js new file mode 100644 index 0000000..25ca0a1 --- /dev/null +++ b/dist/perf/index.js @@ -0,0 +1,303 @@ +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; + } +} + +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('Lyogcm9sbHVwLXBsdWdpbi13ZWItd29ya2VyLWxvYWRlciAqLwooZnVuY3Rpb24gKCkgewogICd1c2Ugc3RyaWN0JzsKCiAgbGV0IEZJTkFMSVpFRCA9IDB4ZGVhZGJlZWY7CgogIGxldCBXUklURUFCTEUgPSAwOwogIGxldCBSRUFEQUJMRSA9IDE7CgogIGNsYXNzIFJlYWRlciB7CiAgICBjb25zdHJ1Y3RvcigKICAgICAgYnVmZmVyLAogICAgICB7IGluaXRpYWxPZmZzZXQgPSA0LCB1c2VBdG9taWNzID0gdHJ1ZSwgc3RyZWFtID0gdHJ1ZSwgZGVidWcsIG5hbWUgfSA9IHt9CiAgICApIHsKICAgICAgdGhpcy5idWZmZXIgPSBidWZmZXI7CiAgICAgIHRoaXMuYXRvbWljVmlldyA9IG5ldyBJbnQzMkFycmF5KGJ1ZmZlcik7CiAgICAgIHRoaXMub2Zmc2V0ID0gaW5pdGlhbE9mZnNldDsKICAgICAgdGhpcy51c2VBdG9taWNzID0gdXNlQXRvbWljczsKICAgICAgdGhpcy5zdHJlYW0gPSBzdHJlYW07CiAgICAgIHRoaXMuZGVidWcgPSBkZWJ1ZzsKICAgICAgdGhpcy5uYW1lID0gbmFtZTsKICAgIH0KCiAgICBsb2coLi4uYXJncykgewogICAgICBpZiAodGhpcy5kZWJ1ZykgewogICAgICAgIGNvbnNvbGUubG9nKGBbcmVhZGVyOiAke3RoaXMubmFtZX1dYCwgLi4uYXJncyk7CiAgICAgIH0KICAgIH0KCiAgICB3YWl0V3JpdGUobmFtZSkgewogICAgICBpZiAodGhpcy51c2VBdG9taWNzKSB7CiAgICAgICAgdGhpcy5sb2coYHdhaXRpbmcgZm9yICR7bmFtZX1gKTsKCiAgICAgICAgd2hpbGUgKEF0b21pY3MubG9hZCh0aGlzLmF0b21pY1ZpZXcsIDApID09PSBXUklURUFCTEUpIHsKICAgICAgICAgIC8vIGNvbnNvbGUubG9nKCd3YWl0aW5nIGZvciB3cml0ZS4uLicpOwogICAgICAgICAgQXRvbWljcy53YWl0KHRoaXMuYXRvbWljVmlldywgMCwgV1JJVEVBQkxFLCA1MDApOwogICAgICAgIH0KCiAgICAgICAgdGhpcy5sb2coYHJlc3VtZWQgZm9yICR7bmFtZX1gKTsKICAgICAgfSBlbHNlIHsKICAgICAgICBpZiAodGhpcy5hdG9taWNWaWV3WzBdICE9PSBSRUFEQUJMRSkgewogICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdgd2FpdFdyaXRlYCBleHBlY3RlZCBhcnJheSB0byBiZSByZWFkYWJsZScpOwogICAgICAgIH0KICAgICAgfQogICAgfQoKICAgIGZsaXAoKSB7CiAgICAgIHRoaXMubG9nKCdmbGlwJyk7CiAgICAgIGlmICh0aGlzLnVzZUF0b21pY3MpIHsKICAgICAgICBsZXQgcHJldiA9IEF0b21pY3MuY29tcGFyZUV4Y2hhbmdlKAogICAgICAgICAgdGhpcy5hdG9taWNWaWV3LAogICAgICAgICAgMCwKICAgICAgICAgIFJFQURBQkxFLAogICAgICAgICAgV1JJVEVBQkxFCiAgICAgICAgKTsKCiAgICAgICAgaWYgKHByZXYgIT09IFJFQURBQkxFKSB7CiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1JlYWQgZGF0YSBvdXQgb2Ygc3luYyEgVGhpcyBpcyBkaXNhc3Ryb3VzJyk7CiAgICAgICAgfQoKICAgICAgICBBdG9taWNzLm5vdGlmeSh0aGlzLmF0b21pY1ZpZXcsIDApOwogICAgICB9IGVsc2UgewogICAgICAgIHRoaXMuYXRvbWljVmlld1swXSA9IFdSSVRFQUJMRTsKICAgICAgfQoKICAgICAgdGhpcy5vZmZzZXQgPSA0OwogICAgfQoKICAgIGRvbmUoKSB7CiAgICAgIHRoaXMud2FpdFdyaXRlKCdkb25lJyk7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0KTsKICAgICAgbGV0IGRvbmUgPSBkYXRhVmlldy5nZXRVaW50MzIoMCkgPT09IEZJTkFMSVpFRDsKCiAgICAgIGlmIChkb25lKSB7CiAgICAgICAgdGhpcy5sb2coJ2RvbmUnKTsKICAgICAgICB0aGlzLmZsaXAoKTsKICAgICAgfQoKICAgICAgcmV0dXJuIGRvbmU7CiAgICB9CgogICAgcGVlayhmbikgewogICAgICB0aGlzLnBlZWtPZmZzZXQgPSB0aGlzLm9mZnNldDsKICAgICAgbGV0IHJlcyA9IGZuKCk7CiAgICAgIHRoaXMub2Zmc2V0ID0gdGhpcy5wZWVrT2Zmc2V0OwogICAgICB0aGlzLnBlZWtPZmZzZXQgPSBudWxsOwogICAgICByZXR1cm4gcmVzOwogICAgfQoKICAgIHN0cmluZygpIHsKICAgICAgdGhpcy53YWl0V3JpdGUoJ3N0cmluZycpOwoKICAgICAgbGV0IGJ5dGVMZW5ndGggPSB0aGlzLl9pbnQzMigpOwogICAgICBsZXQgbGVuZ3RoID0gYnl0ZUxlbmd0aCAvIDI7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0LCBieXRlTGVuZ3RoKTsKICAgICAgbGV0IGNoYXJzID0gW107CiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbGVuZ3RoOyBpKyspIHsKICAgICAgICBjaGFycy5wdXNoKGRhdGFWaWV3LmdldFVpbnQxNihpICogMikpOwogICAgICB9CiAgICAgIGxldCBzdHIgPSBTdHJpbmcuZnJvbUNoYXJDb2RlLmFwcGx5KG51bGwsIGNoYXJzKTsKICAgICAgdGhpcy5sb2coJ3N0cmluZycsIHN0cik7CgogICAgICB0aGlzLm9mZnNldCArPSBieXRlTGVuZ3RoOwoKICAgICAgaWYgKHRoaXMucGVla09mZnNldCA9PSBudWxsKSB7CiAgICAgICAgdGhpcy5mbGlwKCk7CiAgICAgIH0KICAgICAgcmV0dXJuIHN0cjsKICAgIH0KCiAgICBfaW50MzIoKSB7CiAgICAgIGxldCBieXRlTGVuZ3RoID0gNDsKCiAgICAgIGxldCBkYXRhVmlldyA9IG5ldyBEYXRhVmlldyh0aGlzLmJ1ZmZlciwgdGhpcy5vZmZzZXQpOwogICAgICBsZXQgbnVtID0gZGF0YVZpZXcuZ2V0SW50MzIoKTsKICAgICAgdGhpcy5sb2coJ19pbnQzMicsIG51bSk7CgogICAgICB0aGlzLm9mZnNldCArPSBieXRlTGVuZ3RoOwogICAgICByZXR1cm4gbnVtOwogICAgfQoKICAgIGludDMyKCkgewogICAgICB0aGlzLndhaXRXcml0ZSgnaW50MzInKTsKICAgICAgbGV0IG51bSA9IHRoaXMuX2ludDMyKCk7CiAgICAgIHRoaXMubG9nKCdpbnQzMicsIG51bSk7CgogICAgICBpZiAodGhpcy5wZWVrT2Zmc2V0ID09IG51bGwpIHsKICAgICAgICB0aGlzLmZsaXAoKTsKICAgICAgfQogICAgICByZXR1cm4gbnVtOwogICAgfQoKICAgIGJ5dGVzKCkgewogICAgICB0aGlzLndhaXRXcml0ZSgnYnl0ZXMnKTsKCiAgICAgIGxldCBieXRlTGVuZ3RoID0gdGhpcy5faW50MzIoKTsKCiAgICAgIGxldCBieXRlcyA9IG5ldyBBcnJheUJ1ZmZlcihieXRlTGVuZ3RoKTsKICAgICAgbmV3IFVpbnQ4QXJyYXkoYnl0ZXMpLnNldCgKICAgICAgICBuZXcgVWludDhBcnJheSh0aGlzLmJ1ZmZlciwgdGhpcy5vZmZzZXQsIGJ5dGVMZW5ndGgpCiAgICAgICk7CiAgICAgIHRoaXMubG9nKCdieXRlcycsIGJ5dGVzKTsKCiAgICAgIHRoaXMub2Zmc2V0ICs9IGJ5dGVMZW5ndGg7CgogICAgICBpZiAodGhpcy5wZWVrT2Zmc2V0ID09IG51bGwpIHsKICAgICAgICB0aGlzLmZsaXAoKTsKICAgICAgfQogICAgICByZXR1cm4gYnl0ZXM7CiAgICB9CiAgfQoKICBjbGFzcyBXcml0ZXIgewogICAgY29uc3RydWN0b3IoCiAgICAgIGJ1ZmZlciwKICAgICAgeyBpbml0aWFsT2Zmc2V0ID0gNCwgdXNlQXRvbWljcyA9IHRydWUsIHN0cmVhbSA9IHRydWUsIGRlYnVnLCBuYW1lIH0gPSB7fQogICAgKSB7CiAgICAgIHRoaXMuYnVmZmVyID0gYnVmZmVyOwogICAgICB0aGlzLmF0b21pY1ZpZXcgPSBuZXcgSW50MzJBcnJheShidWZmZXIpOwogICAgICB0aGlzLm9mZnNldCA9IGluaXRpYWxPZmZzZXQ7CiAgICAgIHRoaXMudXNlQXRvbWljcyA9IHVzZUF0b21pY3M7CiAgICAgIHRoaXMuc3RyZWFtID0gc3RyZWFtOwoKICAgICAgdGhpcy5kZWJ1ZyA9IGRlYnVnOwogICAgICB0aGlzLm5hbWUgPSBuYW1lOwoKICAgICAgaWYgKHRoaXMudXNlQXRvbWljcykgewogICAgICAgIC8vIFRoZSBidWZmZXIgc3RhcnRzIG91dCBhcyB3cml0ZWFibGUKICAgICAgICBBdG9taWNzLnN0b3JlKHRoaXMuYXRvbWljVmlldywgMCwgV1JJVEVBQkxFKTsKICAgICAgfSBlbHNlIHsKICAgICAgICB0aGlzLmF0b21pY1ZpZXdbMF0gPSBXUklURUFCTEU7CiAgICAgIH0KICAgIH0KCiAgICBsb2coLi4uYXJncykgewogICAgICBpZiAodGhpcy5kZWJ1ZykgewogICAgICAgIGNvbnNvbGUubG9nKGBbd3JpdGVyOiAke3RoaXMubmFtZX1dYCwgLi4uYXJncyk7CiAgICAgIH0KICAgIH0KCiAgICB3YWl0UmVhZChuYW1lKSB7CiAgICAgIGlmICh0aGlzLnVzZUF0b21pY3MpIHsKICAgICAgICB0aGlzLmxvZyhgd2FpdGluZyBmb3IgJHtuYW1lfWApOwogICAgICAgIC8vIFN3aXRjaCB0byB3cml0YWJsZQogICAgICAgIC8vIEF0b21pY3Muc3RvcmUodGhpcy5hdG9taWNWaWV3LCAwLCAxKTsKCiAgICAgICAgbGV0IHByZXYgPSBBdG9taWNzLmNvbXBhcmVFeGNoYW5nZSgKICAgICAgICAgIHRoaXMuYXRvbWljVmlldywKICAgICAgICAgIDAsCiAgICAgICAgICBXUklURUFCTEUsCiAgICAgICAgICBSRUFEQUJMRQogICAgICAgICk7CgogICAgICAgIGlmIChwcmV2ICE9PSBXUklURUFCTEUpIHsKICAgICAgICAgIHRocm93IG5ldyBFcnJvcigKICAgICAgICAgICAgJ1dyb3RlIHNvbWV0aGluZyBpbnRvIHVud3JpdGFibGUgYnVmZmVyISBUaGlzIGlzIGRpc2FzdHJvdXMnCiAgICAgICAgICApOwogICAgICAgIH0KCiAgICAgICAgQXRvbWljcy5ub3RpZnkodGhpcy5hdG9taWNWaWV3LCAwKTsKCiAgICAgICAgd2hpbGUgKEF0b21pY3MubG9hZCh0aGlzLmF0b21pY1ZpZXcsIDApID09PSBSRUFEQUJMRSkgewogICAgICAgICAgLy8gY29uc29sZS5sb2coJ3dhaXRpbmcgdG8gYmUgcmVhZC4uLicpOwogICAgICAgICAgQXRvbWljcy53YWl0KHRoaXMuYXRvbWljVmlldywgMCwgUkVBREFCTEUsIDUwMCk7CiAgICAgICAgfQoKICAgICAgICB0aGlzLmxvZyhgcmVzdW1lZCBmb3IgJHtuYW1lfWApOwogICAgICB9IGVsc2UgewogICAgICAgIHRoaXMuYXRvbWljVmlld1swXSA9IFJFQURBQkxFOwogICAgICB9CgogICAgICB0aGlzLm9mZnNldCA9IDQ7CiAgICB9CgogICAgZmluYWxpemUoKSB7CiAgICAgIHRoaXMubG9nKCdmaW5hbGl6aW5nJyk7CiAgICAgIGxldCBkYXRhVmlldyA9IG5ldyBEYXRhVmlldyh0aGlzLmJ1ZmZlciwgdGhpcy5vZmZzZXQpOwogICAgICBkYXRhVmlldy5zZXRVaW50MzIoMCwgRklOQUxJWkVEKTsKICAgICAgdGhpcy53YWl0UmVhZCgnZmluYWxpemUnKTsKICAgIH0KCiAgICBzdHJpbmcoc3RyKSB7CiAgICAgIHRoaXMubG9nKCdzdHJpbmcnLCBzdHIpOwoKICAgICAgbGV0IGJ5dGVMZW5ndGggPSBzdHIubGVuZ3RoICogMjsKICAgICAgdGhpcy5faW50MzIoYnl0ZUxlbmd0aCk7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0LCBieXRlTGVuZ3RoKTsKICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBzdHIubGVuZ3RoOyBpKyspIHsKICAgICAgICBkYXRhVmlldy5zZXRVaW50MTYoaSAqIDIsIHN0ci5jaGFyQ29kZUF0KGkpKTsKICAgICAgfQoKICAgICAgdGhpcy5vZmZzZXQgKz0gYnl0ZUxlbmd0aDsKICAgICAgdGhpcy53YWl0UmVhZCgnc3RyaW5nJyk7CiAgICB9CgogICAgX2ludDMyKG51bSkgewogICAgICBsZXQgYnl0ZUxlbmd0aCA9IDQ7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0KTsKICAgICAgZGF0YVZpZXcuc2V0SW50MzIoMCwgbnVtKTsKCiAgICAgIHRoaXMub2Zmc2V0ICs9IGJ5dGVMZW5ndGg7CiAgICB9CgogICAgaW50MzIobnVtKSB7CiAgICAgIHRoaXMubG9nKCdpbnQzMicsIG51bSk7CiAgICAgIHRoaXMuX2ludDMyKG51bSk7CiAgICAgIHRoaXMud2FpdFJlYWQoJ2ludDMyJyk7CiAgICB9CgogICAgYnl0ZXMoYnVmZmVyKSB7CiAgICAgIHRoaXMubG9nKCdieXRlcycsIGJ1ZmZlcik7CgogICAgICBsZXQgYnl0ZUxlbmd0aCA9IGJ1ZmZlci5ieXRlTGVuZ3RoOwogICAgICB0aGlzLl9pbnQzMihieXRlTGVuZ3RoKTsKICAgICAgbmV3IFVpbnQ4QXJyYXkodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0KS5zZXQobmV3IFVpbnQ4QXJyYXkoYnVmZmVyKSk7CgogICAgICB0aGlzLm9mZnNldCArPSBieXRlTGVuZ3RoOwogICAgICB0aGlzLndhaXRSZWFkKCdieXRlcycpOwogICAgfQogIH0KCiAgbGV0IHRva2VuID0gJyc7CiAgbGV0IHNoZWV0SWQgPSAnMXAxaXNVWmtXZThvYzEyTEwwa3FhVDNVRlRfTVI4dkVvRWllRXJ1SFcteEUnOwoKICBsZXQgYnVmZmVyID0gNDAwMDA7CiAgbGV0IGJhc2VUaW1lOwogIGxldCB0aW1pbmdzID0ge307CgogIGxldCByYW5nZSA9ICdBMyc7CgogIGNvbnN0IGRlc2NyaXB0aW9ucyA9IHsKICAgIGdldDogJ0NhbGxzIHRvIGBzdG9yZS5nZXRgJywKICAgICdzdHJlYW0tbmV4dCc6ICdBZHZhbmNpbmcgYSBjdXJzb3InLAogICAgc3RyZWFtOiAnT3BlbmluZyBhIGN1cnNvcicsCiAgICByZWFkOiAnRnVsbCBwcm9jZXNzIGZvciByZWFkaW5nIGEgYmxvY2snCiAgfTsKCiAgZnVuY3Rpb24gbGFzdChhcnIpIHsKICAgIHJldHVybiBhcnIubGVuZ3RoID09PSAwID8gbnVsbCA6IGFyclthcnIubGVuZ3RoIC0gMV07CiAgfQoKICBmdW5jdGlvbiBwZXJjZW50aWxlKGRhdGEsIHApIHsKICAgIGxldCBzb3J0ZWQgPSBbLi4uZGF0YV07CiAgICBzb3J0ZWQuc29ydCgobjEsIG4yKSA9PiBuMVsxXSAtIG4yWzFdKTsKICAgIHJldHVybiBzb3J0ZWQuc2xpY2UoMCwgTWF0aC5jZWlsKHNvcnRlZC5sZW5ndGggKiBwKSB8IDApOwogIH0KCiAgbGV0IHNob3dXYXJuaW5nID0gdHJ1ZTsKCiAgYXN5bmMgZnVuY3Rpb24gd3JpdGVEYXRhKHNoZWV0TmFtZSwgZGF0YSkgewogICAgbGV0IGFyciA9IHBlcmNlbnRpbGUoZGF0YSwgMC45NSk7CgogICAgaWYgKGFyci5sZW5ndGggPiBidWZmZXIpIHsKICAgICAgYXJyID0gYXJyLnNsaWNlKC1idWZmZXIpOwogICAgfSBlbHNlIHsKICAgICAgd2hpbGUgKGFyci5sZW5ndGggPCBidWZmZXIpIHsKICAgICAgICBhcnIucHVzaChbJycsICcnXSk7CiAgICAgIH0KICAgIH0KCiAgICBsZXQgcmVzID0gYXdhaXQgZmV0Y2goCiAgICAgIGBodHRwczovL3NoZWV0cy5nb29nbGVhcGlzLmNvbS92NC9zcHJlYWRzaGVldHMvJHtzaGVldElkfS92YWx1ZXMvJHtzaGVldE5hbWV9ISR7cmFuZ2V9P3ZhbHVlSW5wdXRPcHRpb249VVNFUl9FTlRFUkVEYCwKICAgICAgewogICAgICAgIG1ldGhvZDogJ1BVVCcsCiAgICAgICAgaGVhZGVyczogewogICAgICAgICAgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJywKICAgICAgICAgIEF1dGhvcml6YXRpb246IGBCZWFyZXIgJHt0b2tlbn1gCiAgICAgICAgfSwKICAgICAgICBib2R5OiBKU09OLnN0cmluZ2lmeSh7IHZhbHVlczogYXJyIH0pCiAgICAgIH0KICAgICk7CiAgICBpZiAocmVzLnN0YXR1cyA9PSAyMDApIHsKICAgICAgY29uc29sZS5sb2coYExvZ2dlZCB0aW1pbmdzIHRvIHNwcmVhZHNoZWV0ICgke3NoZWV0TmFtZX0pKWApOwogICAgfSBlbHNlIHsKICAgICAgaWYgKHNob3dXYXJuaW5nKSB7CiAgICAgICAgc2hvd1dhcm5pbmcgPSBmYWxzZTsKICAgICAgICBjb25zb2xlLndhcm4oCiAgICAgICAgICAnVW5hYmxlIHRvIGxvZyBwZXJmIGRhdGEgdG8gc3ByZWFkc2hlZXQuIElzIHRoZSBPQXV0aCB0b2tlbiBleHBpcmVkPycKICAgICAgICApOwogICAgICB9CgogICAgICBjb25zb2xlLmxvZyhgLS0tICR7c2hlZXROYW1lfSAoJHtkZXNjcmlwdGlvbnNbc2hlZXROYW1lXX0pIC0tLWApOwogICAgICBjb25zb2xlLmxvZyhgQ291bnQ6ICR7ZGF0YS5sZW5ndGh9YCk7CiAgICAgIGNvbnNvbGUubG9nKGBwNTA6ICR7bGFzdChwZXJjZW50aWxlKGRhdGEsIDAuNSkpWzFdfWApOwogICAgICBjb25zb2xlLmxvZyhgcDk1OiAke2xhc3QocGVyY2VudGlsZShkYXRhLCAwLjk1KSlbMV19YCk7CiAgICB9CiAgfQoKICBhc3luYyBmdW5jdGlvbiBlbmQoKSB7CiAgICBhd2FpdCBQcm9taXNlLmFsbCgKICAgICAgT2JqZWN0LmtleXModGltaW5ncykubWFwKG5hbWUgPT4gewogICAgICAgIGxldCB0aW1pbmcgPSB0aW1pbmdzW25hbWVdOwogICAgICAgIHJldHVybiB3cml0ZURhdGEobmFtZSwgdGltaW5nLmRhdGEubWFwKHggPT4gW3guc3RhcnQgKyB4LnRvb2ssIHgudG9va10pKTsKICAgICAgfSkKICAgICk7CiAgfQoKICBmdW5jdGlvbiBzdGFydCgpIHsKICAgIHRpbWluZ3MgPSB7fTsKICAgIGJhc2VUaW1lID0gcGVyZm9ybWFuY2Uubm93KCk7CiAgfQoKICBmdW5jdGlvbiByZWNvcmQobmFtZSkgewogICAgaWYgKHRpbWluZ3NbbmFtZV0gPT0gbnVsbCkgewogICAgICB0aW1pbmdzW25hbWVdID0geyBzdGFydDogbnVsbCwgZGF0YTogW10gfTsKICAgIH0KICAgIGxldCB0aW1lciA9IHRpbWluZ3NbbmFtZV07CgogICAgaWYgKHRpbWVyLnN0YXJ0ICE9IG51bGwpIHsKICAgICAgdGhyb3cgbmV3IEVycm9yKGB0aW1lciBhbHJlYWR5IHN0YXJ0ZWQgJHtuYW1lfWApOwogICAgfQogICAgdGltZXIuc3RhcnQgPSBwZXJmb3JtYW5jZS5ub3coKTsKICB9CgogIGZ1bmN0aW9uIGVuZFJlY29yZGluZyhuYW1lKSB7CiAgICBsZXQgbm93ID0gcGVyZm9ybWFuY2Uubm93KCk7CiAgICBsZXQgdGltZXIgPSB0aW1pbmdzW25hbWVdOwoKICAgIGlmICh0aW1lciAmJiB0aW1lci5zdGFydCAhPSBudWxsKSB7CiAgICAgIGxldCB0b29rID0gbm93IC0gdGltZXIuc3RhcnQ7CiAgICAgIGxldCBzdGFydCA9IHRpbWVyLnN0YXJ0IC0gYmFzZVRpbWU7CiAgICAgIHRpbWVyLnN0YXJ0ID0gbnVsbDsKCiAgICAgIGlmICh0aW1lci5kYXRhLmxlbmd0aCA8IGJ1ZmZlcikgewogICAgICAgIHRpbWVyLmRhdGEucHVzaCh7IHN0YXJ0LCB0b29rIH0pOwogICAgICB9CiAgICB9CiAgfQoKICBsZXQgaXNQcm9iYWJseVNhZmFyaSA9IC9eKCg/IWNocm9tZXxhbmRyb2lkKS4pKnNhZmFyaS9pLnRlc3QoCiAgICBuYXZpZ2F0b3IudXNlckFnZW50CiAgKTsKCiAgbGV0IG9wZW5EYnMgPSBuZXcgTWFwKCk7CiAgbGV0IHRyYW5zYWN0aW9ucyA9IG5ldyBNYXAoKTsKCiAgZnVuY3Rpb24gYXNzZXJ0KGNvbmQsIG1zZykgewogICAgaWYgKCFjb25kKSB7CiAgICAgIHRocm93IG5ldyBFcnJvcihtc2cpOwogICAgfQogIH0KCiAgbGV0IExPQ0tfVFlQRVMgPSB7CiAgICBOT05FOiAwLAogICAgU0hBUkVEOiAxLAogICAgUkVTRVJWRUQ6IDIsCiAgICBQRU5ESU5HOiAzLAogICAgRVhDTFVTSVZFOiA0CiAgfTsKCiAgLy8gV2UgdXNlIGxvbmctbGl2ZWQgdHJhbnNhY3Rpb25zLCBhbmQgYFRyYW5zYWN0aW9uYCBrZWVwcyB0aGUKICAvLyB0cmFuc2FjdGlvbiBzdGF0ZS4gSXQgaW1wbGVtZW50cyBhbiBvcHRpbWFsIHdheSB0byBwZXJmb3JtCiAgLy8gcmVhZC93cml0ZXMgd2l0aCBrbm93bGVkZ2Ugb2YgaG93IHNxbGl0ZSBhc2tzIGZvciB0aGVtLCBhbmQgYWxzbwogIC8vIGltcGxlbWVudHMgYSBsb2NraW5nIG1lY2hhbmlzbSB0aGF0IG1hcHMgdG8gaG93IHNxbGl0ZSBsb2NrcyB3b3JrLgogIGNsYXNzIFRyYW5zYWN0aW9uIHsKICAgIGNvbnN0cnVjdG9yKGRiLCBpbml0aWFsTW9kZSA9ICdyZWFkb25seScpIHsKICAgICAgdGhpcy5kYiA9IGRiOwogICAgICB0aGlzLnRyYW5zID0gdGhpcy5kYi50cmFuc2FjdGlvbihbJ2RhdGEnXSwgaW5pdGlhbE1vZGUpOwogICAgICB0aGlzLnN0b3JlID0gdGhpcy50cmFucy5vYmplY3RTdG9yZSgnZGF0YScpOwogICAgICB0aGlzLmxvY2tUeXBlID0KICAgICAgICBpbml0aWFsTW9kZSA9PT0gJ3JlYWRvbmx5JyA/IExPQ0tfVFlQRVMuU0hBUkVEIDogTE9DS19UWVBFUy5FWENMVVNJVkU7CgogICAgICAvLyBUaGVyZSBpcyBubyBuZWVkIGZvciB1cyB0byBjYWNoZSBibG9ja3MuIFVzZSBzcWxpdGUncwogICAgICAvLyBgY2FjaGVfc2l6ZWAgZm9yIHRoYXQgYW5kIGl0IHdpbGwgYXV0b21hdGljYWxseSBkbyBpdC4gSG93ZXZlciwKICAgICAgLy8gd2UgZG8gc3RpbGwga2VlcCBhIGNhY2hlIG9mIHRoZSBmaXJzdCBibG9jayBmb3IgdGhlIGR1cmF0aW9uIG9mCiAgICAgIC8vIHRoaXMgdHJhbnNhY3Rpb24gYmVjYXVzZSBvZiBob3cgbG9ja2luZyB3b3JrczsgdGhpcyBhdm9pZHMgYQogICAgICAvLyBmZXcgZXh0cmEgcmVhZHMgYW5kIGFsbG93cyB1cyB0byBkZXRlY3QgY2hhbmdlcyBkdXJpbmcKICAgICAgLy8gdXBncmFkaW5nIChzZWUgYHVwZ3JhZGVFeGNsdXNpdmVgKQogICAgICB0aGlzLmNhY2hlZEZpcnN0QmxvY2sgPSBudWxsOwoKICAgICAgdGhpcy5jdXJzb3IgPSBudWxsOwogICAgICB0aGlzLnByZXZSZWFkcyA9IG51bGw7CiAgICB9CgogICAgYXN5bmMgcHJlZmV0Y2hGaXJzdEJsb2NrKHRpbWVvdXQpIHsKICAgICAgLy8gVE9ETzogaW1wbGVtZW50IHRpbWVvdXQKCiAgICAgIC8vIEdldCB0aGUgZmlyc3QgYmxvY2sgYW5kIGNhY2hlIGl0CiAgICAgIGxldCBibG9jayA9IGF3YWl0IHRoaXMuZ2V0KDApOwogICAgICB0aGlzLmNhY2hlZEZpcnN0QmxvY2sgPSBibG9jazsKICAgICAgcmV0dXJuIGJsb2NrOwogICAgfQoKICAgIGFzeW5jIHdhaXRDb21wbGV0ZSgpIHsKICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHsKICAgICAgICAvLyBFYWdlcmx5IGNvbW1pdCBpdCBmb3IgYmV0dGVyIHBlcmYuIE5vdGUgdGhhdCAqKnRoaXMgYXNzdW1lcwogICAgICAgIC8vIHRoZSB0cmFuc2FjdGlvbiBpcyBvcGVuKiogYXMgYGNvbW1pdGAgd2lsbCB0aHJvdyBhbiBlcnJvciBpZgogICAgICAgIC8vIGl0J3MgYWxyZWFkeSBjbG9zZWQgKHdoaWNoIHNob3VsZCBuZXZlciBiZSB0aGUgY2FzZSBmb3IgdXMpCiAgICAgICAgdGhpcy5jb21taXQoKTsKCiAgICAgICAgaWYgKHRoaXMubG9ja1R5cGUgPT09IExPQ0tfVFlQRVMuRVhDTFVTSVZFKSB7CiAgICAgICAgICAvLyBXYWl0IHVudGlsIGFsbCB3cml0ZXMgYXJlIGNvbW1pdHRlZAogICAgICAgICAgdGhpcy50cmFucy5vbmNvbXBsZXRlID0gZSA9PiByZXNvbHZlKCk7CgogICAgICAgICAgLy8gVE9ETzogSXMgaXQgT0sgdG8gYWRkIHRoaXMgbGF0ZXIsIGFmdGVyIGFuIGVycm9yIG1pZ2h0IGhhdmUKICAgICAgICAgIC8vIGhhcHBlbmVkPyBXaWxsIGl0IGhvbGQgdGhlIGVycm9yIGFuZCBmaXJlIHRoaXMgd2hlbiB3ZQogICAgICAgICAgLy8gYXR0YWNoZWQgaXQ/IFdlIG1pZ2h0IHdhbnQgdG8gZWFnZXJseSBjcmVhdGUgdGhlIHByb21pc2UKICAgICAgICAgIC8vIHdoZW4gY3JlYXRpbmcgdGhlIHRyYW5zYWN0aW9uIGFuZCByZXR1cm4gaXQgaGVyZQogICAgICAgICAgdGhpcy50cmFucy5vbmVycm9yID0gZSA9PiByZWplY3QoZSk7CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgIGlmIChpc1Byb2JhYmx5U2FmYXJpKSB7CiAgICAgICAgICAgIC8vIFNhZmFyaSBoYXMgYSBidWcgd2hlcmUgc29tZXRpbWVzIHRoZSBJREIgZ2V0cyBibG9ja2VkCiAgICAgICAgICAgIC8vIHBlcm1hbmVudGx5IGlmIHlvdSByZWZyZXNoIHRoZSBwYWdlIHdpdGggYW4gb3BlbgogICAgICAgICAgICAvLyB0cmFuc2FjdGlvbi4gWW91IGhhdmUgdG8gcmVzdGFydCB0aGUgYnJvd3NlciB0byBmaXggaXQuCiAgICAgICAgICAgIC8vIFdlIHdhaXQgZm9yIHJlYWRvbmx5IHRyYW5zYWN0aW9ucyB0byBmaW5pc2ggdG9vLCBidXQgdGhpcwogICAgICAgICAgICAvLyBpcyBhIHBlcmYgaGl0CiAgICAgICAgICAgIHRoaXMudHJhbnMub25jb21wbGV0ZSA9IGUgPT4gcmVzb2x2ZSgpOwogICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgLy8gTm8gbmVlZCB0byB3YWl0IG9uIGFueXRoaW5nIGluIGEgcmVhZC1vbmx5IHRyYW5zYWN0aW9uLgogICAgICAgICAgICAvLyBOb3RlIHRoYXQgZXJyb3JzIGR1cmluZyByZWFkcyBhcmVhIGFsd2F5cyBoYW5kbGVkIGJ5IHRoZQogICAgICAgICAgICAvLyByZWFkIHJlcXVlc3QuCiAgICAgICAgICAgIHJlc29sdmUoKTsKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIH0pOwogICAgfQoKICAgIGNvbW1pdCgpIHsKICAgICAgLy8gU2FmYXJpIGRvZXNuJ3Qgc3VwcG9ydCB0aGlzIG1ldGhvZCB5ZXQgKHRoaXMgaXMganVzdCBhbgogICAgICAvLyBvcHRpbWl6YXRpb24pCiAgICAgIGlmICh0aGlzLnRyYW5zLmNvbW1pdCkgewogICAgICAgIHRoaXMudHJhbnMuY29tbWl0KCk7CiAgICAgIH0KICAgIH0KCiAgICBhc3luYyB1cGdyYWRlRXhjbHVzaXZlKCkgewogICAgICB0aGlzLmNvbW1pdCgpOwoKICAgICAgLy8gY29uc29sZS5sb2coJ3VwZGF0aW5nIHRyYW5zYWN0aW9uIHJlYWR3cml0ZScpOwogICAgICB0aGlzLnRyYW5zID0gdGhpcy5kYi50cmFuc2FjdGlvbihbJ2RhdGEnXSwgJ3JlYWR3cml0ZScpOwogICAgICB0aGlzLnN0b3JlID0gdGhpcy50cmFucy5vYmplY3RTdG9yZSgnZGF0YScpOwogICAgICB0aGlzLmxvY2tUeXBlID0gTE9DS19UWVBFUy5FWENMVVNJVkU7CgogICAgICBsZXQgY2FjaGVkMCA9IHRoaXMuY2FjaGVkRmlyc3RCbG9jazsKCiAgICAgIC8vIERvIGEgcmVhZAogICAgICBsZXQgYmxvY2sgPSBhd2FpdCB0aGlzLnByZWZldGNoRmlyc3RCbG9jayg1MDApOwogICAgICAvLyBUT0RPOiB3aGVuIHRpbWVvdXRzIGFyZSBpbXBsZW1lbnRlZCwgZGV0ZWN0IHRpbWVvdXQgYW5kIHJldHVybiBCVVNZCgogICAgICBpZiAoY2FjaGVkMCA9PSBudWxsICYmIGJsb2NrID09IG51bGwpIHsKICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgfSBlbHNlIHsKICAgICAgICBmb3IgKGxldCBpID0gMjQ7IGkgPCA0MDsgaSsrKSB7CiAgICAgICAgICBpZiAoYmxvY2tbaV0gIT09IGNhY2hlZDBbaV0pIHsKICAgICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfQoKICAgICAgcmV0dXJuIHRydWU7CiAgICB9CgogICAgZG93bmdyYWRlU2hhcmVkKCkgewogICAgICB0aGlzLmNvbW1pdCgpOwoKICAgICAgLy8gY29uc29sZS5sb2coJ2Rvd25ncmFkaW5nIHRyYW5zYWN0aW9uIHJlYWRvbmx5Jyk7CiAgICAgIHRoaXMudHJhbnMgPSB0aGlzLmRiLnRyYW5zYWN0aW9uKFsnZGF0YSddLCAncmVhZG9ubHknKTsKICAgICAgdGhpcy5zdG9yZSA9IHRoaXMudHJhbnMub2JqZWN0U3RvcmUoJ2RhdGEnKTsKICAgICAgdGhpcy5sb2NrVHlwZSA9IExPQ0tfVFlQRVMuU0hBUkVEOwogICAgfQoKICAgIGFzeW5jIGdldChrZXkpIHsKICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHsKICAgICAgICByZWNvcmQoJ2dldCcpOwogICAgICAgIGxldCByZXEgPSB0aGlzLnN0b3JlLmdldChrZXkpOwogICAgICAgIHJlcS5vbnN1Y2Nlc3MgPSBlID0+IHsKICAgICAgICAgIGVuZFJlY29yZGluZygnZ2V0Jyk7CiAgICAgICAgICByZXNvbHZlKHJlcS5yZXN1bHQpOwogICAgICAgIH07CiAgICAgICAgcmVxLm9uZXJyb3IgPSBlID0+IHJlamVjdChlKTsKICAgICAgfSk7CiAgICB9CgogICAgZ2V0UmVhZERpcmVjdGlvbigpIHsKICAgICAgLy8gVGhlcmUgYXJlIGEgdHdvIHdheXMgd2UgY2FuIHJlYWQgZGF0YTogYSBkaXJlY3QgYGdldGAgcmVxdWVzdAogICAgICAvLyBvciBvcGVuaW5nIGEgY3Vyc29yIGFuZCBpdGVyYXRpbmcgdGhyb3VnaCBkYXRhLiBXZSBkb24ndCBrbm93CiAgICAgIC8vIHdoYXQgZnV0dXJlIHJlYWRzIGxvb2sgbGlrZSwgc28gd2UgZG9uJ3Qga25vdyB0aGUgYmVzdCBzdHJhdGVneQogICAgICAvLyB0byBwaWNrLiBBbHdheXMgY2hvb3Npbmcgb25lIHN0cmF0ZWd5IGZvcmdvZXMgYSBsb3Qgb2YKICAgICAgLy8gb3B0aW1pemF0aW9uLCBiZWNhdXNlIGl0ZXJhdGluZyB3aXRoIGEgY3Vyc29yIGlzIGEgbG90IGZhc3RlcgogICAgICAvLyB0aGFuIG1hbnkgYGdldGAgY2FsbHMuIE9uIHRoZSBvdGhlciBoYW5kLCBvcGVuaW5nIGEgY3Vyc29yIGlzCiAgICAgIC8vIHNsb3csIGFuZCBzbyBpcyBjYWxsaW5nIGBhZHZhbmNlYCB0byBtb3ZlIGEgY3Vyc29yIG92ZXIgYSBodWdlCiAgICAgIC8vIHJhbmdlIChsaWtlIG1vdmluZyBpdCAxMDAwIGl0ZW1zIGxhdGVyKSwgc28gbWFueSBgZ2V0YCBjYWxscyB3b3VsZAogICAgICAvLyBiZSBmYXN0ZXIuIEluIGdlbmVyYWw6CiAgICAgIC8vCiAgICAgIC8vICogTWFueSBgZ2V0YCBjYWxscyBhcmUgZmFzdGVyIHdoZW4gZG9pbmcgcmFuZG9tIGFjY2Vzc2VzCiAgICAgIC8vICogSXRlcmF0aW5nIHdpdGggYSBjdXJzb3IgaXMgZmFzdGVyIGlmIGRvaW5nIG1vc3RseSBzZXF1ZW50aWFsCiAgICAgIC8vICAgYWNjZXNzZXMKICAgICAgLy8KICAgICAgLy8gV2UgaW1wbGVtZW50IGEgaGV1cmlzdGljIGFuZCBrZWVwcyB0cmFjayBvZiB0aGUgbGFzdCAzIHJlYWRzCiAgICAgIC8vIGFuZCBkZXRlY3RzIHdoZW4gdGhleSBhcmUgbW9zdGx5IHNlcXVlbnRpYWwuIElmIHRoZXkgYXJlLCB3ZQogICAgICAvLyBvcGVuIGEgY3Vyc29yIGFuZCBzdGFydCByZWFkaW5nIGJ5IGl0ZXJhdGluZyBpdC4gSWYgbm90LCB3ZSBkbwogICAgICAvLyBkaXJlY3QgYGdldGAgY2FsbHMuCiAgICAgIC8vCiAgICAgIC8vIE9uIHRvcCBvZiBhbGwgb2YgdGhpcywgZWFjaCBicm93c2VyIGhhcyBkaWZmZXJlbnQgcGVyZgogICAgICAvLyBjaGFyYWN0ZXJpc3RpY3MuIFdlIHdpbGwgcHJvYmFibHkgd2FudCB0byBtYWtlIHRoZXNlIHRocmVzaG9sZHMKICAgICAgLy8gY29uZmlndXJhYmxlIHNvIHRoZSB1c2VyIGNhbiBjaGFuZ2UgdGhlbSBwZXItYnJvd3NlciBpZiBuZWVkZWQsCiAgICAgIC8vIGFzIHdlbGwgYXMgZmluZS10dW5pbmcgdGhlbSBmb3IgdGhlaXIgdXNhZ2Ugb2Ygc3FsaXRlLgoKICAgICAgbGV0IHByZXZSZWFkcyA9IHRoaXMucHJldlJlYWRzOwogICAgICBpZiAocHJldlJlYWRzKSB7CiAgICAgICAgLy8gSGFzIHRoZXJlIGJlZW4gMyBmb3J3YXJkIHNlcXVlbnRpYWwgcmVhZHMgd2l0aGluIDEwIGJsb2Nrcz8KICAgICAgICBpZiAoCiAgICAgICAgICBwcmV2UmVhZHNbMF0gPCBwcmV2UmVhZHNbMV0gJiYKICAgICAgICAgIHByZXZSZWFkc1sxXSA8IHByZXZSZWFkc1syXSAmJgogICAgICAgICAgcHJldlJlYWRzWzJdIC0gcHJldlJlYWRzWzBdIDwgMTAKICAgICAgICApIHsKICAgICAgICAgIHJldHVybiAnbmV4dCc7CiAgICAgICAgfQoKICAgICAgICAvLyBIYXMgdGhlcmUgYmVlbiAzIGJhY2t3YXJkcyBzZXF1ZW50aWFsIHJlYWRzIHdpdGhpbiAxMCBibG9ja3M/CiAgICAgICAgaWYgKAogICAgICAgICAgcHJldlJlYWRzWzBdID4gcHJldlJlYWRzWzFdICYmCiAgICAgICAgICBwcmV2UmVhZHNbMV0gPiBwcmV2UmVhZHNbMl0gJiYKICAgICAgICAgIHByZXZSZWFkc1swXSAtIHByZXZSZWFkc1syXSA8IDEwCiAgICAgICAgKSB7CiAgICAgICAgICByZXR1cm4gJ3ByZXYnOwogICAgICAgIH0KICAgICAgfQoKICAgICAgcmV0dXJuIG51bGw7CiAgICB9CgogICAgcmVhZChwb3NpdGlvbikgewogICAgICBsZXQgd2FpdEN1cnNvciA9ICgpID0+IHsKICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4gewogICAgICAgICAgaWYgKHRoaXMuY3Vyc29yUHJvbWlzZSAhPSBudWxsKSB7CiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcigKICAgICAgICAgICAgICAnd2FpdEN1cnNvcigpIGNhbGxlZCBidXQgc29tZXRoaW5nIGVsc2UgaXMgYWxyZWFkeSB3YWl0aW5nJwogICAgICAgICAgICApOwogICAgICAgICAgfQogICAgICAgICAgdGhpcy5jdXJzb3JQcm9taXNlID0geyByZXNvbHZlLCByZWplY3QgfTsKICAgICAgICB9KTsKICAgICAgfTsKCiAgICAgIGlmICh0aGlzLmN1cnNvcikgewogICAgICAgIGxldCBjdXJzb3IgPSB0aGlzLmN1cnNvcjsKCiAgICAgICAgaWYgKAogICAgICAgICAgY3Vyc29yLmRpcmVjdGlvbiA9PT0gJ25leHQnICYmCiAgICAgICAgICBwb3NpdGlvbiA+IGN1cnNvci5rZXkgJiYKICAgICAgICAgIHBvc2l0aW9uIDwgY3Vyc29yLmtleSArIDEwMAogICAgICAgICkgewogICAgICAgICAgcmVjb3JkKCdzdHJlYW0tbmV4dCcpOwoKICAgICAgICAgIGN1cnNvci5hZHZhbmNlKHBvc2l0aW9uIC0gY3Vyc29yLmtleSk7CiAgICAgICAgICByZXR1cm4gd2FpdEN1cnNvcigpOwogICAgICAgIH0gZWxzZSBpZiAoCiAgICAgICAgICBjdXJzb3IuZGlyZWN0aW9uID09PSAncHJldicgJiYKICAgICAgICAgIHBvc2l0aW9uIDwgY3Vyc29yLmtleSAmJgogICAgICAgICAgcG9zaXRpb24gPiBjdXJzb3Iua2V5IC0gMTAwCiAgICAgICAgKSB7CiAgICAgICAgICByZWNvcmQoJ3N0cmVhbS1uZXh0Jyk7CgogICAgICAgICAgY3Vyc29yLmFkdmFuY2UoY3Vyc29yLmtleSAtIHBvc2l0aW9uKTsKICAgICAgICAgIHJldHVybiB3YWl0Q3Vyc29yKCk7CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgIC8vIERpdGNoIHRoZSBjdXJzb3IKICAgICAgICAgIHRoaXMuY3Vyc29yID0gbnVsbDsKICAgICAgICAgIHJldHVybiB0aGlzLnJlYWQocG9zaXRpb24pOwogICAgICAgIH0KICAgICAgfSBlbHNlIHsKICAgICAgICAvLyBXZSBkb24ndCBhbHJlYWR5IGhhdmUgYSBjdXJzb3IuIFdlIG5lZWQgdG8gYSBmcmVzaCByZWFkOwogICAgICAgIC8vIHNob3VsZCB3ZSBvcGVuIGEgY3Vyc29yIG9yIGNhbGwgYGdldGA/CgogICAgICAgIGxldCBkaXIgPSB0aGlzLmdldFJlYWREaXJlY3Rpb24oKTsKICAgICAgICBpZiAoZGlyKSB7CiAgICAgICAgICAvLyBPcGVuIGEgY3Vyc29yCiAgICAgICAgICB0aGlzLnByZXZSZWFkcyA9IG51bGw7CgogICAgICAgICAgbGV0IGtleVJhbmdlOwogICAgICAgICAgaWYgKGRpciA9PT0gJ3ByZXYnKSB7CiAgICAgICAgICAgIGtleVJhbmdlID0gSURCS2V5UmFuZ2UudXBwZXJCb3VuZChwb3NpdGlvbik7CiAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICBrZXlSYW5nZSA9IElEQktleVJhbmdlLmxvd2VyQm91bmQocG9zaXRpb24pOwogICAgICAgICAgfQoKICAgICAgICAgIGxldCByZXEgPSB0aGlzLnN0b3JlLm9wZW5DdXJzb3Ioa2V5UmFuZ2UsIGRpcik7CiAgICAgICAgICByZWNvcmQoJ3N0cmVhbScpOwoKICAgICAgICAgIHJlcS5vbnN1Y2Nlc3MgPSBlID0+IHsKICAgICAgICAgICAgZW5kUmVjb3JkaW5nKCdzdHJlYW0nKTsKICAgICAgICAgICAgZW5kUmVjb3JkaW5nKCdzdHJlYW0tbmV4dCcpOwoKICAgICAgICAgICAgbGV0IGN1cnNvciA9IGUudGFyZ2V0LnJlc3VsdDsKICAgICAgICAgICAgdGhpcy5jdXJzb3IgPSBjdXJzb3I7CgogICAgICAgICAgICBpZiAodGhpcy5jdXJzb3JQcm9taXNlID09IG51bGwpIHsKICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0dvdCBkYXRhIGZyb20gY3Vyc29yIGJ1dCBub3RoaW5nIGlzIHdhaXRpbmcgaXQnKTsKICAgICAgICAgICAgfQogICAgICAgICAgICB0aGlzLmN1cnNvclByb21pc2UucmVzb2x2ZShjdXJzb3IgPyBjdXJzb3IudmFsdWUgOiBudWxsKTsKICAgICAgICAgICAgdGhpcy5jdXJzb3JQcm9taXNlID0gbnVsbDsKICAgICAgICAgIH07CiAgICAgICAgICByZXEub25lcnJvciA9IGUgPT4gewogICAgICAgICAgICBjb25zb2xlLmxvZygnQ3Vyc29yIGZhaWx1cmU6JywgZSk7CgogICAgICAgICAgICBpZiAodGhpcy5jdXJzb3JQcm9taXNlID09IG51bGwpIHsKICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0dvdCBkYXRhIGZyb20gY3Vyc29yIGJ1dCBub3RoaW5nIGlzIHdhaXRpbmcgaXQnKTsKICAgICAgICAgICAgfQogICAgICAgICAgICB0aGlzLmN1cnNvclByb21pc2UucmVqZWN0KGUpOwogICAgICAgICAgICB0aGlzLmN1cnNvclByb21pc2UgPSBudWxsOwogICAgICAgICAgfTsKCiAgICAgICAgICByZXR1cm4gd2FpdEN1cnNvcigpOwogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICBpZiAodGhpcy5wcmV2UmVhZHMgPT0gbnVsbCkgewogICAgICAgICAgICB0aGlzLnByZXZSZWFkcyA9IFswLCAwLCAwXTsKICAgICAgICAgIH0KICAgICAgICAgIHRoaXMucHJldlJlYWRzLnB1c2gocG9zaXRpb24pOwogICAgICAgICAgdGhpcy5wcmV2UmVhZHMuc2hpZnQoKTsKCiAgICAgICAgICByZXR1cm4gdGhpcy5nZXQocG9zaXRpb24pOwogICAgICAgIH0KICAgICAgfQogICAgfQoKICAgIGFzeW5jIHNldChpdGVtKSB7CiAgICAgIHRoaXMucHJldlJlYWRzID0gbnVsbDsKCiAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7CiAgICAgICAgbGV0IHJlcSA9IHRoaXMuc3RvcmUucHV0KGl0ZW0udmFsdWUsIGl0ZW0ua2V5KTsKICAgICAgICByZXEub25zdWNjZXNzID0gZSA9PiByZXNvbHZlKHJlcS5yZXN1bHQpOwogICAgICAgIHJlcS5vbmVycm9yID0gZSA9PiByZWplY3QoZSk7CiAgICAgIH0pOwogICAgfQoKICAgIGFzeW5jIGJ1bGtTZXQoaXRlbXMpIHsKICAgICAgdGhpcy5wcmV2UmVhZHMgPSBudWxsOwoKICAgICAgZm9yIChsZXQgaXRlbSBvZiBpdGVtcykgewogICAgICAgIHRoaXMuc3RvcmUucHV0KGl0ZW0udmFsdWUsIGl0ZW0ua2V5KTsKICAgICAgfQogICAgfQogIH0KCiAgYXN5bmMgZnVuY3Rpb24gbG9hZERiKG5hbWUpIHsKICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7CiAgICAgIGlmIChvcGVuRGJzLmdldChuYW1lKSkgewogICAgICAgIHJlc29sdmUob3BlbkRicy5nZXQobmFtZSkpOwogICAgICAgIHJldHVybjsKICAgICAgfQoKICAgICAgY29uc29sZS5sb2coJ29wZW5pbmcnLCBuYW1lKTsKCiAgICAgIGxldCByZXEgPSBnbG9iYWxUaGlzLmluZGV4ZWREQi5vcGVuKG5hbWUsIDEpOwogICAgICByZXEub25zdWNjZXNzID0gZXZlbnQgPT4gewogICAgICAgIGNvbnNvbGUubG9nKCdkYiBpcyBvcGVuIScsIG5hbWUpOwogICAgICAgIGxldCBkYiA9IGV2ZW50LnRhcmdldC5yZXN1bHQ7CgogICAgICAgIGRiLm9udmVyc2lvbmNoYW5nZSA9ICgpID0+IHsKICAgICAgICAgIC8vIFRPRE86IE5vdGlmeSB0aGUgdXNlciBzb21laG93CiAgICAgICAgICBjb25zb2xlLmxvZygnY2xvc2luZyBiZWNhdXNlIHZlcnNpb24gY2hhbmdlZCcpOwogICAgICAgICAgZGIuY2xvc2UoKTsKICAgICAgICAgIG9wZW5EYnMuZGVsZXRlKG5hbWUpOwogICAgICAgIH07CgogICAgICAgIGRiLm9uY2xvc2UgPSAoKSA9PiB7CiAgICAgICAgICBvcGVuRGJzLmRlbGV0ZShuYW1lKTsKICAgICAgICB9OwoKICAgICAgICBvcGVuRGJzLnNldChuYW1lLCBkYik7CiAgICAgICAgcmVzb2x2ZShkYik7CiAgICAgIH07CiAgICAgIHJlcS5vbnVwZ3JhZGVuZWVkZWQgPSBldmVudCA9PiB7CiAgICAgICAgbGV0IGRiID0gZXZlbnQudGFyZ2V0LnJlc3VsdDsKICAgICAgICBpZiAoIWRiLm9iamVjdFN0b3JlTmFtZXMuY29udGFpbnMoJ2RhdGEnKSkgewogICAgICAgICAgZGIuY3JlYXRlT2JqZWN0U3RvcmUoJ2RhdGEnKTsKICAgICAgICB9CiAgICAgIH07CiAgICAgIHJlcS5vbmJsb2NrZWQgPSBlID0+IGNvbnNvbGUubG9nKCdibG9ja2VkJywgZSk7CiAgICAgIHJlcS5vbmVycm9yID0gcmVxLm9uYWJvcnQgPSBlID0+IHJlamVjdChlLnRhcmdldC5lcnJvcik7CiAgICB9KTsKICB9CgogIGZ1bmN0aW9uIGNsb3NlRGIobmFtZSkgewogICAgbGV0IG9wZW5EYiA9IG9wZW5EYnMuZ2V0KG5hbWUpOwogICAgaWYgKG9wZW5EYikgewogICAgICBvcGVuRGIuY2xvc2UoKTsKICAgICAgb3BlbkRicy5kZWxldGUobmFtZSk7CiAgICB9CiAgfQoKICBmdW5jdGlvbiBnZXRUcmFuc2FjdGlvbihuYW1lKSB7CiAgICByZXR1cm4gdHJhbnNhY3Rpb25zLmdldChuYW1lKTsKICB9CgogIGFzeW5jIGZ1bmN0aW9uIHdpdGhUcmFuc2FjdGlvbihuYW1lLCBtb2RlLCBmdW5jKSB7CiAgICBsZXQgdHJhbnMgPSB0cmFuc2FjdGlvbnMuZ2V0KG5hbWUpOwogICAgaWYgKHRyYW5zKSB7CiAgICAgIC8vIElmIGEgdHJhbnNhY3Rpb24gYWxyZWFkeSBleGlzdHMsIHRoYXQgbWVhbnMgdGhlIGZpbGUgaGFzIGJlZW4KICAgICAgLy8gbG9ja2VkLiBXZSBkb24ndCBmdWxseSBzdXBwb3J0IGFyYml0cmFyeSBuZXN0ZWQgdHJhbnNhY3Rpb25zLAogICAgICAvLyBhcyBzZWVuIGJlbG93ICh3ZSB3b24ndCB1cGdyYWRlIGEgYHJlYWRvbmx5YCB0byBgcmVhZHdyaXRlYAogICAgICAvLyBhdXRvbWF0aWNhbGx5KSBhbmQgdGhpcyBpcyBtYWlubHkgZm9yIHRoZSB1c2UgY2FzZSB3aGVyZSBzcWxpdGUKICAgICAgLy8gbG9ja3MgdGhlIGRiIGFuZCBjcmVhdGVzIGEgdHJhbnNhY3Rpb24gZm9yIHRoZSBkdXJhY3Rpb24gb2YgdGhlCiAgICAgIC8vIGxvY2suIFdlIGRvbid0IGFjdHVhbGx5IHdyaXRlIGNvZGUgaW4gYSB3YXkgdGhhdCBhc3N1bWVzIG5lc3RlZAogICAgICAvLyB0cmFuc2FjdGlvbnMsIHNvIGp1c3QgZXJyb3IgaGVyZQogICAgICBpZiAobW9kZSA9PT0gJ3JlYWR3cml0ZScgJiYgdHJhbnMubG9ja1R5cGUgPT09IExPQ0tfVFlQRVMuU0hBUkVEKSB7CiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdBdHRlbXB0ZWQgd3JpdGUgYnV0IG9ubHkgaGFzIFNIQVJFRCBsb2NrJyk7CiAgICAgIH0KICAgICAgcmV0dXJuIGZ1bmModHJhbnMpOwogICAgfQoKICAgIC8vIE91dHNpZGUgdGhlIHNjb3BlIG9mIGEgbG9jaywgY3JlYXRlIGEgdGVtcG9yYXJ5IHRyYW5zYWN0aW9uCiAgICB0cmFucyA9IG5ldyBUcmFuc2FjdGlvbihhd2FpdCBsb2FkRGIobmFtZSksIG1vZGUpOwogICAgYXdhaXQgZnVuYyh0cmFucyk7CiAgICBhd2FpdCB0cmFucy53YWl0Q29tcGxldGUoKTsKICB9CgogIC8vIExvY2tpbmcgc3RyYXRlZ3k6CiAgLy8KICAvLyAqIFdlIG1hcCBzcWxpdGUncyBsb2NrcyBvbnRvIEluZGV4ZWREQidzIHRyYW5zYWN0aW9uIHNlbWFudGljcy4KICAvLyAgIFJlYWQgdHJhbnNhY3Rpb25zIG1heSBleGVjdXRlIGluIHBhcmFsbGVsLiBSZWFkL3dyaXRlCiAgLy8gICB0cmFuc2FjdGlvbnMgYXJlIHF1ZXVlZCB1cCBhbmQgd2FpdCB1bnRpbCBhbGwgcHJlY2VkaW5nCiAgLy8gICByZWFkIHRyYW5zYWN0aW9ucyBmaW5pc2ggZXhlY3V0aW5nLiBSZWFkIHRyYW5zYWN0aW9ucyBzdGFydGVkCiAgLy8gICBhZnRlciBhIHJlYWQvd3JpdGUgdHJhbnNhY3Rpb24gd2FpdCB1bnRpbCBpdCBpcyBmaW5pc2hlZC4KICAvLwogIC8vICogSURCIHRyYW5zYWN0aW9ucyB3aWxsIHdhaXQgZm9yZXZlciB1bnRpbCB0aGV5IGNhbiBleGVjdXRlIChmb3IKICAvLyAgIGV4YW1wbGUsIHRoZXkgbWF5IGJlIGJsb2NrZWQgb24gYSByZWFkL3dyaXRlIHRyYW5zYWN0aW9uKS4gV2UKICAvLyAgIGRvbid0IHdhbnQgdG8gYWxsb3cgc3FsaXRlIHRyYW5zYWN0aW9ucyB0byB3YWl0IGZvcmV2ZXIsIHNvCiAgLy8gICB3ZSBtYW51YWxseSB0aW1lb3V0IGlmIGEgdHJhbnNhY3Rpb24gdGFrZXMgdG9vIGxvbmcgdG8KICAvLyAgIHN0YXJ0IGV4ZWN1dGluZy4gVGhpcyBzaW11bGF0ZXMgdGhlIGJlaGF2aW9yIG9mIGEgc3FsaXRlCiAgLy8gICBiYWlsaW5nIGlmIGl0IGNhbid0IHJlcXVpcmUgYSBsb2NrLgogIC8vCiAgLy8gKiBBIFNIQVJFRCBsb2NrIHdhbnRzIHRvIHJlYWQgZnJvbSB0aGUgZGIuIFdlIHN0YXJ0IGEgcmVhZAogIC8vICAgdHJhbnNhY3Rpb24gYW5kIHJlYWQgdGhlIGZpcnN0IGJsb2NrLCBhbmQgaWYgd2UgcmVhZCBpdCB3aXRoaW4KICAvLyAgIDUwMG1zIHdlIGNvbnNpZGVyIHRoZSBsb2NrIHN1Y2Nlc3NmdWwuIE90aGVyd2lzZSB0aGUgbG9jawogIC8vICAgZmFpbGVkIGFuZCB3ZSByZXR1cm4gU1FMSVRFX0JVU1kuIChUaGVyZSdzIG5vIHBlcmYgZG93bnNpZGUKICAvLyAgIHRvIHJlYWRpbmcgdGhlIGZpcnN0IGJsb2NrIC0gaXQgaGFzIHRvIGJlIHJlYWQgYW55d2F5IHRvIGNoZWNrCiAgLy8gICBieXRlcyAyNC0zOSBmb3IgdGhlIGNoYW5nZSBjb3VudGVyKQogIC8vCiAgLy8gKiBBIFJFU0VSVkVEIGxvY2sgbWVhbnMgdGhlIGRiIHdhbnRzIHRvIHN0YXJ0IHdyaXRpbmcgKHRoaW5rIG9mCiAgLy8gICBgQkVHSU4gVFJBTlNBQ1RJT05gKS4gT25seSBvbmUgcHJvY2VzcyBjYW4gb2J0YWluIGEgUkVTRVJWRUQKICAvLyAgIGxvY2sgYXQgYSB0aW1lLCBidXQgbm9ybWFsbHkgc3FsaXRlIHN0aWxsIGxlYWRzIG5ldyByZWFkIGxvY2tzCiAgLy8gICBoYXBwZW4uIEl0IGlzbid0IHVudGlsIGFuIEVYQ0xVU0lWRSBsb2NrIGlzIGhlbGQgdGhhdCByZWFkcyBhcmUKICAvLyAgIGJsb2NrZWQuIEhvd2V2ZXIsIHNpbmNlIHdlIG5lZWQgdG8gZ3VhcmFudGVlIG9ubHkgb25lIFJFU0VSVkVECiAgLy8gICBsb2NrIGF0IG9uY2UgKG90aGVyd2lzZSBkYXRhIGNvdWxkIGNoYW5nZSBmcm9tIGFub3RoZXIgcHJvY2VzcwogIC8vICAgd2l0aGluIGEgdHJhbnNhY3Rpb24sIGNhdXNpbmcgZmF1bHR5IGNhY2hlcyBldGMpIHRoZSBzaW1wbGVzdAogIC8vICAgdGhpbmcgdG8gZG8gaXMgZ28gYWhlYWQgYW5kIGdyYWIgYSByZWFkL3dyaXRlIHRyYW5zYWN0aW9uIHRoYXQKICAvLyAgIHJlcHJlc2VudHMgdGhlIFJFU0VSVkVEIGxvY2suIFRoaXMgd2lsbCBibG9jayBhbGwgcmVhZHMgZnJvbQogIC8vICAgaGFwcGVuaW5nLCBhbmQgaXMgZXNzZW50aWFsbHkgdGhlIHNhbWUgYXMgYW4gRVhDTFVTSVZFIGxvY2suCiAgLy8KICAvLyAgICAgKiBUaGUgbWFpbiBwcm9ibGVtIGhlcmUgaXMgd2UgY2FuJ3QgInVwZ3JhZGUiIGEgYHJlYWRvbmx5YAogIC8vICAgICAgIHRyYW5zYWN0aW9uIHRvIGByZWFkd3JpdGVgLCBidXQgbmF0aXZlIHNxbGl0ZSBjYW4gdXBncmFkZSBhCiAgLy8gICAgICAgbG9jayBmcm9tIFNIQVJFRCB0byBSRVNFUlZFRC4gV2UgbmVlZCB0byBzdGFydCBhIG5ldwogIC8vICAgICAgIHRyYW5zYWN0aW9uIHRvIGRvIHNvLCBhbmQgYmVjYXVzZSBvZiB0aGF0IHRoZXJlIG1pZ2h0IGJlCiAgLy8gICAgICAgb3RoZXIgYHJlYWR3cml0ZWAgdHJhbnNhY3Rpb25zIHRoYXQgZ2V0IHJ1biBkdXJpbmcgdGhlCiAgLy8gICAgICAgInVwZ3JhZGUiIHdoaWNoIGludmFsaWRhdGVzIHRoZSB3aG9sZSBsb2NraW5nIHByb2Nlc3MgYW5kCiAgLy8gICAgICAgYW5kIGNvcnJ1cHRzIGRhdGEuCiAgLy8KICAvLyAqIElkZWFsbHksIHdlIGNvdWxkIHRlbGwgc3FsaXRlIHRvIHNraXAgU0hBUkVEIGxvY2tzIGVudGlyZWx5LiBXZQogIC8vICAgZG9uJ3QgbmVlZCB0aGVtIHNpbmNlIHdlIGNhbiByZWx5IG9uIEluZGV4ZWREQidzIHNlbWFudGljcy4KICAvLyAgIFRoZW4gd2hlbiBpdCB3YW50cyB0byBzdGFydCB3cml0aW5nLCB3ZSBnZXQgYSBSRVNFUlZFRCBsb2NrCiAgLy8gICB3aXRob3V0IGhhdmluZyB0byB1cGdyYWRlIGZyb20gU0hBUkVELiBUaGlzIHdvdWxkIHNhdmUgdXMKICAvLyAgIHRoZSBjb3N0IG9mIGEgYHJlYWRvbmx5YCB0cmFuc2FjdGlvbiB3aGVuIHdyaXRpbmc7IHJpZ2h0IG5vdwogIC8vICAgaXQgbXVzdCBvcGVuIGEgYHJlYWRvbmx5YCB0cmFuc2FjdGlvbiBhbmQgdGhlbiBpbW1lZGlhdGVseSBvcGVuCiAgLy8gICBhIGByZWFkd3JpdGVgIHRvIHVwZ3JhZGUgaXQuIEkgdGhvdWdodCBvZiBkZWZlcnJpbmcgb3BlbmluZyB0aGUKICAvLyAgIGByZWFkb25seWAgdHJhbnNhY3Rpb24gdW50aWwgc29tZXRoaW5nIGlzIGFjdHVhbGx5IHJlYWQsIGJ1dAogIC8vICAgdW5mb3J0dW5hdGVseSBzcWxpdGUgb3BlbnMgaXQsIHJlYWRzIHRoZSBmaXJzdCBibG9jaywgYW5kIHRoZW4KICAvLyAgIHVwZ3JhZGVzIGl0LiBTbyB0aGVyZSdzIG5vIHdheSBhcm91bmQgaXQuIChXZSBjYW4ndCBhc3N1bWUgaXQncwogIC8vICAgYSBgcmVhZHdyaXRlYCB0cmFuc2FjdGlvbiBhdCB0aGF0IHBvaW50IHNpbmNlIHRoYXQgd291bGQgYXNzdW1lCiAgLy8gICBhbGwgU0hBUkVEIGxvY2tzIGFyZSBgcmVhZHdyaXRlYCwgcmVtb3ZpbmcgdGhlIHBvc3NpYmlsaXR5IG9mCiAgLy8gICBjb25jdXJyZW50IHJlYWRzKS4KICAvLwogIC8vICogVXBncmFkaW5nIHRvIGFuIEVYQ0xVU0lWRSBsb2NrIGlzIGEgbm9vcCwgc2luY2Ugd2UgdHJlYXQgUkVTRVJWRUQKICAvLyAgIGxvY2tzIGFzIEVYQ0xVU0lWRS4KICBhc3luYyBmdW5jdGlvbiBoYW5kbGVMb2NrKHdyaXRlciwgbmFtZSwgbG9ja1R5cGUpIHsKICAgIC8vIGNvbnNvbGUubG9nKCdsb2NraW5nJywgbmFtZSwgbG9ja1R5cGUsIHBlcmZvcm1hbmNlLm5vdygpKTsKCiAgICBsZXQgdHJhbnMgPSB0cmFuc2FjdGlvbnMuZ2V0KG5hbWUpOwogICAgaWYgKHRyYW5zKSB7CiAgICAgIGlmIChsb2NrVHlwZSA+IHRyYW5zLmxvY2tUeXBlKSB7CiAgICAgICAgLy8gVXBncmFkZSBTSEFSRUQgdG8gRVhDTFVTSVZFCiAgICAgICAgYXNzZXJ0KAogICAgICAgICAgdHJhbnMubG9ja1R5cGUgPT09IExPQ0tfVFlQRVMuU0hBUkVELAogICAgICAgICAgYFVwcmFkaW5nIGxvY2sgdHlwZSBmcm9tICR7dHJhbnMubG9ja1R5cGV9IGlzIGludmFsaWRgCiAgICAgICAgKTsKICAgICAgICBhc3NlcnQoCiAgICAgICAgICBsb2NrVHlwZSA9PT0gTE9DS19UWVBFUy5SRVNFUlZFRCB8fCBsb2NrVHlwZSA9PT0gTE9DS19UWVBFUy5FWENMVVNJVkUsCiAgICAgICAgICBgVXBncmFkaW5nIGxvY2sgdHlwZSB0byAke2xvY2tUeXBlfSBpcyBpbnZhbGlkYAogICAgICAgICk7CgogICAgICAgIGxldCBzdWNjZXNzID0gYXdhaXQgdHJhbnMudXBncmFkZUV4Y2x1c2l2ZSgpOwogICAgICAgIHdyaXRlci5pbnQzMihzdWNjZXNzID8gMCA6IC0xKTsKICAgICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgICAgfSBlbHNlIHsKICAgICAgICAvLyBJZiBub3QgdXBncmFkaW5nIGFuZCB3ZSBhbHJlYWR5IGhhdmUgYSBsb2NrLCBtYWtlIHN1cmUgdGhpcwogICAgICAgIC8vIGlzbid0IGEgZG93bmdyYWRlCiAgICAgICAgYXNzZXJ0KAogICAgICAgICAgdHJhbnMubG9ja1R5cGUgPT09IGxvY2tUeXBlLAogICAgICAgICAgYERvd25ncmFkaW5nIGxvY2sgdG8gJHtsb2NrVHlwZX0gaXMgaW52YWxpZGAKICAgICAgICApOwoKICAgICAgICB3cml0ZXIuaW50MzIoMCk7CiAgICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICAgIH0KICAgIH0gZWxzZSB7CiAgICAgIGFzc2VydCgKICAgICAgICBsb2NrVHlwZSA9PT0gTE9DS19UWVBFUy5TSEFSRUQsCiAgICAgICAgYE5ldyBsb2NrcyBtdXN0IHN0YXJ0IGFzIFNIQVJFRCBpbnN0ZWFkIG9mICR7bG9ja1R5cGV9YAogICAgICApOwoKICAgICAgbGV0IHRyYW5zID0gbmV3IFRyYW5zYWN0aW9uKGF3YWl0IGxvYWREYihuYW1lKSk7CiAgICAgIGlmICgoYXdhaXQgdHJhbnMucHJlZmV0Y2hGaXJzdEJsb2NrKDUwMCkpID09IG51bGwpIDsKCiAgICAgIHRyYW5zYWN0aW9ucy5zZXQobmFtZSwgdHJhbnMpOwoKICAgICAgd3JpdGVyLmludDMyKDApOwogICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgIH0KICB9CgogIGFzeW5jIGZ1bmN0aW9uIGhhbmRsZVVubG9jayh3cml0ZXIsIG5hbWUsIGxvY2tUeXBlKSB7CiAgICAvLyBjb25zb2xlLmxvZygndW5sb2NraW5nJywgbmFtZSwgbG9ja1R5cGUsIHBlcmZvcm1hbmNlLm5vdygpKTsKCiAgICBsZXQgdHJhbnMgPSBnZXRUcmFuc2FjdGlvbihuYW1lKTsKCiAgICBpZiAobG9ja1R5cGUgPT09IExPQ0tfVFlQRVMuU0hBUkVEKSB7CiAgICAgIGlmICh0cmFucyA9PSBudWxsKSB7CiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdVbmxvY2sgZXJyb3IgKFNIQVJFRCk6IG5vIHRyYW5zYWN0aW9uIHJ1bm5pbmcnKTsKICAgICAgfQoKICAgICAgaWYgKHRyYW5zLmxvY2tUeXBlID09PSBMT0NLX1RZUEVTLkVYQ0xVU0lWRSkgewogICAgICAgIHRyYW5zLmRvd25ncmFkZVNoYXJlZCgpOwogICAgICB9CiAgICB9IGVsc2UgaWYgKGxvY2tUeXBlID09PSBMT0NLX1RZUEVTLk5PTkUpIHsKICAgICAgLy8gSSB0aG91Z2h0IHdlIGNvdWxkIGFzc3VtZSBhIGxvY2sgaXMgYWx3YXlzIG9wZW4gd2hlbiBgdW5sb2NrYAogICAgICAvLyBpcyBjYWxsZWQsIGJ1dCBpdCBhbHNvIGNhbGxzIGB1bmxvY2tgIHdoZW4gY2xvc2luZyB0aGUgZmlsZSBubwogICAgICAvLyBtYXR0ZXIgd2hhdC4gRG8gbm90aGluZyBpZiB0aGVyZSdzIG5vIGxvY2sgY3VycmVudGx5CiAgICAgIGlmICh0cmFucykgewogICAgICAgIC8vIFRPRE86IHRoaXMgaXMgd2hlcmUgYW4gZXJyb3IgY291bGQgYnViYmxlIHVwLiBIYW5kbGUgaXQKICAgICAgICBhd2FpdCB0cmFucy53YWl0Q29tcGxldGUoKTsKICAgICAgICB0cmFuc2FjdGlvbnMuZGVsZXRlKG5hbWUpOwogICAgICB9CiAgICB9CgogICAgd3JpdGVyLmludDMyKDApOwogICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgfQoKICBhc3luYyBmdW5jdGlvbiBoYW5kbGVSZWFkKHdyaXRlciwgbmFtZSwgcG9zaXRpb24pIHsKICAgIHJldHVybiB3aXRoVHJhbnNhY3Rpb24obmFtZSwgJ3JlYWRvbmx5JywgYXN5bmMgdHJhbnMgPT4gewogICAgICBsZXQgZGF0YSA9IGF3YWl0IHRyYW5zLnJlYWQocG9zaXRpb24pOwoKICAgICAgaWYgKGRhdGEgPT0gbnVsbCkgewogICAgICAgIHdyaXRlci5ieXRlcyhuZXcgQXJyYXlCdWZmZXIoMCkpOwogICAgICB9IGVsc2UgewogICAgICAgIHdyaXRlci5ieXRlcyhkYXRhKTsKICAgICAgfQogICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgIH0pOwogIH0KCiAgYXN5bmMgZnVuY3Rpb24gaGFuZGxlV3JpdGVzKHdyaXRlciwgbmFtZSwgd3JpdGVzKSB7CiAgICByZXR1cm4gd2l0aFRyYW5zYWN0aW9uKG5hbWUsICdyZWFkd3JpdGUnLCBhc3luYyB0cmFucyA9PiB7CiAgICAgIGF3YWl0IHRyYW5zLmJ1bGtTZXQod3JpdGVzLm1hcCh3ID0+ICh7IGtleTogdy5wb3MsIHZhbHVlOiB3LmRhdGEgfSkpKTsKCiAgICAgIHdyaXRlci5pbnQzMigwKTsKICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICB9KTsKICB9CgogIGFzeW5jIGZ1bmN0aW9uIGhhbmRsZVJlYWRNZXRhKHdyaXRlciwgbmFtZSkgewogICAgcmV0dXJuIHdpdGhUcmFuc2FjdGlvbihuYW1lLCAncmVhZG9ubHknLCBhc3luYyB0cmFucyA9PiB7CiAgICAgIHRyeSB7CiAgICAgICAgY29uc29sZS5sb2coJ1JlYWRpbmcgbWV0YScpOwogICAgICAgIGxldCByZXMgPSBhd2FpdCB0cmFucy5nZXQoLTEpOwogICAgICAgIGNvbnNvbGUubG9nKCdSZWFkaW5nIG1ldGEgKGRvbmUpJywgcmVzKTsKCiAgICAgICAgbGV0IG1ldGEgPSByZXM7CiAgICAgICAgd3JpdGVyLmludDMyKG1ldGEgPyBtZXRhLnNpemUgOiAtMSk7CiAgICAgICAgd3JpdGVyLmludDMyKG1ldGEgPyBtZXRhLmJsb2NrU2l6ZSA6IC0xKTsKICAgICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgICAgfSBjYXRjaCAoZXJyKSB7CiAgICAgICAgY29uc29sZS5sb2coZXJyKTsKICAgICAgICB3cml0ZXIuaW50MzIoLTEpOwogICAgICAgIHdyaXRlci5pbnQzMigtMSk7CiAgICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICAgIH0KICAgIH0pOwogIH0KCiAgYXN5bmMgZnVuY3Rpb24gaGFuZGxlV3JpdGVNZXRhKHdyaXRlciwgbmFtZSwgbWV0YSkgewogICAgcmV0dXJuIHdpdGhUcmFuc2FjdGlvbihuYW1lLCAncmVhZHdyaXRlJywgYXN5bmMgdHJhbnMgPT4gewogICAgICB0cnkgewogICAgICAgIGF3YWl0IHRyYW5zLnNldCh7IGtleTogLTEsIHZhbHVlOiBtZXRhIH0pOwoKICAgICAgICB3cml0ZXIuaW50MzIoMCk7CiAgICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICAgIH0gY2F0Y2ggKGVycikgewogICAgICAgIGNvbnNvbGUubG9nKGVycik7CiAgICAgICAgd3JpdGVyLmludDMyKC0xKTsKICAgICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgICAgfQogICAgfSk7CiAgfQoKICBhc3luYyBmdW5jdGlvbiBoYW5kbGVEZWxldGVGaWxlKHdyaXRlciwgbmFtZSkgewogICAgdHJ5IHsKICAgICAgY2xvc2VEYihuYW1lKTsKCiAgICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHsKICAgICAgICBsZXQgcmVxID0gZ2xvYmFsVGhpcy5pbmRleGVkREIuZGVsZXRlRGF0YWJhc2UobmFtZSk7CiAgICAgICAgcmVxLm9uc3VjY2VzcyA9IHJlc29sdmU7CiAgICAgICAgcmVxLm9uZXJyb3IgPSByZWplY3Q7CiAgICAgIH0pOwoKICAgICAgd3JpdGVyLmludDMyKDApOwogICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgIH0gY2F0Y2ggKGVycikgewogICAgICB3cml0ZXIuaW50MzIoLTEpOwogICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgIH0KICB9CgogIGFzeW5jIGZ1bmN0aW9uIGhhbmRsZUNsb3NlRmlsZSh3cml0ZXIsIG5hbWUpIHsKICAgIGNsb3NlRGIobmFtZSk7CgogICAgd3JpdGVyLmludDMyKDApOwogICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgfQoKICAvLyBgbGlzdGVuYCBjb250aW51YWxseSBsaXN0ZW5zIGZvciByZXF1ZXN0cyB2aWEgdGhlIHNoYXJlZCBidWZmZXIuCiAgLy8gUmlnaHQgbm93IGl0J3MgaW1wbGVtZW50ZWQgaW4gYSB0YWlsLWNhbGwgc3R5bGUgKGBsaXN0ZW5gIGlzCiAgLy8gcmVjdXJzaXZlbHkgY2FsbGVkKSBiZWNhdXNlIEkgdGhvdWdodCB0aGF0IHdhcyBuZWNlc3NhcnkgZm9yCiAgLy8gdmFyaW91cyByZWFzb25zLiBXZSBjYW4gY29udmVydCB0aGlzIHRvIGEgYHdoaWxlKDEpYCBsb29wIHdpdGgKICAvLyBhbmQgdXNlIGBhd2FpdGAgdGhvdWdoCiAgYXN5bmMgZnVuY3Rpb24gbGlzdGVuKHJlYWRlciwgd3JpdGVyKSB7CiAgICBsZXQgbWV0aG9kID0gcmVhZGVyLnN0cmluZygpOwoKICAgIHN3aXRjaCAobWV0aG9kKSB7CiAgICAgIGNhc2UgJ3N0YXRzLXN0YXJ0JzogewogICAgICAgIHJlYWRlci5kb25lKCk7CgogICAgICAgIHN0YXJ0KCk7CgogICAgICAgIHdyaXRlci5pbnQzMigwKTsKICAgICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICdzdGF0cyc6IHsKICAgICAgICByZWFkZXIuZG9uZSgpOwoKICAgICAgICBhd2FpdCBlbmQoKTsKCiAgICAgICAgd3JpdGVyLmludDMyKDApOwogICAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgICAgIGxpc3RlbihyZWFkZXIsIHdyaXRlcik7CiAgICAgICAgYnJlYWs7CiAgICAgIH0KCiAgICAgIGNhc2UgJ3dyaXRlQmxvY2tzJzogewogICAgICAgIGxldCBuYW1lID0gcmVhZGVyLnN0cmluZygpOwogICAgICAgIGxldCB3cml0ZXMgPSBbXTsKICAgICAgICB3aGlsZSAoIXJlYWRlci5kb25lKCkpIHsKICAgICAgICAgIGxldCBwb3MgPSByZWFkZXIuaW50MzIoKTsKICAgICAgICAgIGxldCBkYXRhID0gcmVhZGVyLmJ5dGVzKCk7CiAgICAgICAgICB3cml0ZXMucHVzaCh7IHBvcywgZGF0YSB9KTsKICAgICAgICB9CgogICAgICAgIGF3YWl0IGhhbmRsZVdyaXRlcyh3cml0ZXIsIG5hbWUsIHdyaXRlcyk7CiAgICAgICAgbGlzdGVuKHJlYWRlciwgd3JpdGVyKTsKICAgICAgICBicmVhazsKICAgICAgfQoKICAgICAgY2FzZSAncmVhZEJsb2NrJzogewogICAgICAgIGxldCBuYW1lID0gcmVhZGVyLnN0cmluZygpOwogICAgICAgIGxldCBwb3MgPSByZWFkZXIuaW50MzIoKTsKICAgICAgICByZWFkZXIuZG9uZSgpOwoKICAgICAgICBhd2FpdCBoYW5kbGVSZWFkKHdyaXRlciwgbmFtZSwgcG9zKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICdyZWFkTWV0YSc6IHsKICAgICAgICBsZXQgbmFtZSA9IHJlYWRlci5zdHJpbmcoKTsKICAgICAgICByZWFkZXIuZG9uZSgpOwogICAgICAgIGF3YWl0IGhhbmRsZVJlYWRNZXRhKHdyaXRlciwgbmFtZSk7CiAgICAgICAgbGlzdGVuKHJlYWRlciwgd3JpdGVyKTsKICAgICAgICBicmVhazsKICAgICAgfQoKICAgICAgY2FzZSAnd3JpdGVNZXRhJzogewogICAgICAgIGxldCBuYW1lID0gcmVhZGVyLnN0cmluZygpOwogICAgICAgIGxldCBzaXplID0gcmVhZGVyLmludDMyKCk7CiAgICAgICAgbGV0IGJsb2NrU2l6ZSA9IHJlYWRlci5pbnQzMigpOwogICAgICAgIHJlYWRlci5kb25lKCk7CiAgICAgICAgYXdhaXQgaGFuZGxlV3JpdGVNZXRhKHdyaXRlciwgbmFtZSwgeyBzaXplLCBibG9ja1NpemUgfSk7CiAgICAgICAgbGlzdGVuKHJlYWRlciwgd3JpdGVyKTsKICAgICAgICBicmVhazsKICAgICAgfQoKICAgICAgY2FzZSAnZGVsZXRlRmlsZSc6IHsKICAgICAgICBsZXQgbmFtZSA9IHJlYWRlci5zdHJpbmcoKTsKICAgICAgICByZWFkZXIuZG9uZSgpOwoKICAgICAgICBhd2FpdCBoYW5kbGVEZWxldGVGaWxlKHdyaXRlciwgbmFtZSk7CiAgICAgICAgbGlzdGVuKHJlYWRlciwgd3JpdGVyKTsKICAgICAgICBicmVhazsKICAgICAgfQoKICAgICAgY2FzZSAnY2xvc2VGaWxlJzogewogICAgICAgIGxldCBuYW1lID0gcmVhZGVyLnN0cmluZygpOwogICAgICAgIHJlYWRlci5kb25lKCk7CgogICAgICAgIGF3YWl0IGhhbmRsZUNsb3NlRmlsZSh3cml0ZXIsIG5hbWUpOwogICAgICAgIGxpc3RlbihyZWFkZXIsIHdyaXRlcik7CiAgICAgICAgYnJlYWs7CiAgICAgIH0KCiAgICAgIGNhc2UgJ2xvY2tGaWxlJzogewogICAgICAgIGxldCBuYW1lID0gcmVhZGVyLnN0cmluZygpOwogICAgICAgIGxldCBsb2NrVHlwZSA9IHJlYWRlci5pbnQzMigpOwogICAgICAgIHJlYWRlci5kb25lKCk7CgogICAgICAgIGF3YWl0IGhhbmRsZUxvY2sod3JpdGVyLCBuYW1lLCBsb2NrVHlwZSk7CiAgICAgICAgbGlzdGVuKHJlYWRlciwgd3JpdGVyKTsKICAgICAgICBicmVhazsKICAgICAgfQoKICAgICAgY2FzZSAndW5sb2NrRmlsZSc6IHsKICAgICAgICBsZXQgbmFtZSA9IHJlYWRlci5zdHJpbmcoKTsKICAgICAgICBsZXQgbG9ja1R5cGUgPSByZWFkZXIuaW50MzIoKTsKICAgICAgICByZWFkZXIuZG9uZSgpOwoKICAgICAgICBhd2FpdCBoYW5kbGVVbmxvY2sod3JpdGVyLCBuYW1lLCBsb2NrVHlwZSk7CiAgICAgICAgbGlzdGVuKHJlYWRlciwgd3JpdGVyKTsKICAgICAgICBicmVhazsKICAgICAgfQoKICAgICAgZGVmYXVsdDoKICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1Vua25vd24gbWV0aG9kOiAnICsgbWV0aG9kKTsKICAgIH0KICB9CgogIHNlbGYub25tZXNzYWdlID0gbXNnID0+IHsKICAgIHN3aXRjaCAobXNnLmRhdGEudHlwZSkgewogICAgICBjYXNlICdpbml0JzogewogICAgICAgIHBvc3RNZXNzYWdlKHsgdHlwZTogJ3dvcmtlci1yZWFkeScgfSk7CiAgICAgICAgbGV0IFthcmdCdWZmZXIsIHJlc3VsdEJ1ZmZlcl0gPSBtc2cuZGF0YS5idWZmZXJzOwogICAgICAgIGxldCByZWFkZXIgPSBuZXcgUmVhZGVyKGFyZ0J1ZmZlciwgeyBuYW1lOiAnYXJncycsIGRlYnVnOiBmYWxzZSB9KTsKICAgICAgICBsZXQgd3JpdGVyID0gbmV3IFdyaXRlcihyZXN1bHRCdWZmZXIsIHsgbmFtZTogJ3Jlc3VsdHMnLCBkZWJ1ZzogZmFsc2UgfSk7CiAgICAgICAgbGlzdGVuKHJlYWRlciwgd3JpdGVyKTsKICAgICAgICBicmVhazsKICAgICAgfQogICAgfQogIH07Cgp9KCkpOwoK', null, false); +/* eslint-enable */ + +let workerReady = null; + +function isWorker() { + return ( + typeof WorkerGlobalScope !== 'undefined' && + self instanceof WorkerGlobalScope + ); +} + +function startWorker(argBuffer, resultBuffer) { + if (workerReady) { + return workerReady; + } + + let onReady; + workerReady = new Promise(resolve => (onReady = resolve)); + + if (typeof Worker === 'undefined') { + // No `Worker` available - this context does not support nested + // workers sadly. We need to proxy creating a worker to the main + // thread. + if (!isWorker()) { + // We're on the main thread? Weird: it doesn't have workers + throw new Error( + 'Web workers not available, even from the main thread. sqlite3 requires web workers to work.' + ); + } + + self.postMessage({ + type: 'spawn-idb-worker', + argBuffer, + resultBuffer + }); + + self.addEventListener('message', e => { + if (e.data.type === 'worker-ready') { + onReady(); + } + }); + } else { + let worker = new WorkerFactory(); + + // This is another way to load the worker. It won't be inlined + // into the script, which might be better for debugging, but makes + // it more difficult to distribute. + // let worker = new Worker(new URL('./indexeddb.worker.js', import.meta.url)); + + worker.postMessage({ type: 'init', buffers: [argBuffer, resultBuffer] }); + + worker.onmessage = msg => { + if (msg.data.type === 'worker-ready') { + onReady(); + } + }; + + return workerReady; + } +} + +// This is called from the main thread to setup a proxy for spawning +// workers. It's necessary for browsers that don't support spawning +// workers from workers (only Safari). +function supportNestedWorkers$1(worker) { + worker.addEventListener('message', e => { + if (e.data.type === 'spawn-idb-worker') { + startWorker(e.data.argBuffer, e.data.resultBuffer).then(() => { + worker.postMessage({ type: 'worker-ready' }); + }); + } + }); +} + +// Right now we don't support `export from` so we do this manually +// +// TODO: This isn't packaged up the best. There will be duplicate code +// across bundles and we need to separate things better +const BlockedFS = BlockedFS$1; +const supportNestedWorkers = supportNestedWorkers$1; + +export { BlockedFS, supportNestedWorkers }; diff --git a/dist/perf/indexeddb-backend.js b/dist/perf/indexeddb-backend.js new file mode 100644 index 0000000..53acfc5 --- /dev/null +++ b/dist/perf/indexeddb-backend.js @@ -0,0 +1,1028 @@ +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'); + } +} + +let token = ''; +let sheetId = '1p1isUZkWe8oc12LL0kqaT3UFT_MR8vEoEieEruHW-xE'; + +let buffer = 40000; +let baseTime; +let timings = {}; + +let range$1 = 'A3'; + +const descriptions = { + get: 'Calls to `store.get`', + 'stream-next': 'Advancing a cursor', + stream: 'Opening a cursor', + read: 'Full process for reading a block' +}; + +function last(arr) { + return arr.length === 0 ? null : arr[arr.length - 1]; +} + +function percentile(data, p) { + let sorted = [...data]; + sorted.sort((n1, n2) => n1[1] - n2[1]); + return sorted.slice(0, Math.ceil(sorted.length * p) | 0); +} + +let showWarning = true; + +async function writeData(sheetName, data) { + let arr = percentile(data, 0.95); + + if (arr.length > buffer) { + arr = arr.slice(-buffer); + } else { + while (arr.length < buffer) { + arr.push(['', '']); + } + } + + let res = await fetch( + `https://sheets.googleapis.com/v4/spreadsheets/${sheetId}/values/${sheetName}!${range$1}?valueInputOption=USER_ENTERED`, + { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}` + }, + body: JSON.stringify({ values: arr }) + } + ); + if (res.status == 200) { + console.log(`Logged timings to spreadsheet (${sheetName}))`); + } else { + if (showWarning) { + showWarning = false; + console.warn( + 'Unable to log perf data to spreadsheet. Is the OAuth token expired?' + ); + } + + console.log(`--- ${sheetName} (${descriptions[sheetName]}) ---`); + console.log(`Count: ${data.length}`); + console.log(`p50: ${last(percentile(data, 0.5))[1]}`); + console.log(`p95: ${last(percentile(data, 0.95))[1]}`); + } +} + +async function end() { + await Promise.all( + Object.keys(timings).map(name => { + let timing = timings[name]; + return writeData(name, timing.data.map(x => [x.start + x.took, x.took])); + }) + ); +} + +function start() { + timings = {}; + baseTime = performance.now(); +} + +function record(name) { + if (timings[name] == null) { + timings[name] = { start: null, data: [] }; + } + let timer = timings[name]; + + if (timer.start != null) { + throw new Error(`timer already started ${name}`); + } + timer.start = performance.now(); +} + +function endRecording(name) { + let now = performance.now(); + let timer = timings[name]; + + if (timer && timer.start != null) { + let took = now - timer.start; + let start = timer.start - baseTime; + timer.start = null; + + if (timer.data.length < buffer) { + timer.data.push({ start, took }); + } + } +} + +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; + } + + 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; + } + + 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; + } + + startStats() { + start(); + this.ops.startStats(); + } + + stats() { + end(); + this.ops.stats(); + } +} + +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('Lyogcm9sbHVwLXBsdWdpbi13ZWItd29ya2VyLWxvYWRlciAqLwooZnVuY3Rpb24gKCkgewogICd1c2Ugc3RyaWN0JzsKCiAgbGV0IEZJTkFMSVpFRCA9IDB4ZGVhZGJlZWY7CgogIGxldCBXUklURUFCTEUgPSAwOwogIGxldCBSRUFEQUJMRSA9IDE7CgogIGNsYXNzIFJlYWRlciB7CiAgICBjb25zdHJ1Y3RvcigKICAgICAgYnVmZmVyLAogICAgICB7IGluaXRpYWxPZmZzZXQgPSA0LCB1c2VBdG9taWNzID0gdHJ1ZSwgc3RyZWFtID0gdHJ1ZSwgZGVidWcsIG5hbWUgfSA9IHt9CiAgICApIHsKICAgICAgdGhpcy5idWZmZXIgPSBidWZmZXI7CiAgICAgIHRoaXMuYXRvbWljVmlldyA9IG5ldyBJbnQzMkFycmF5KGJ1ZmZlcik7CiAgICAgIHRoaXMub2Zmc2V0ID0gaW5pdGlhbE9mZnNldDsKICAgICAgdGhpcy51c2VBdG9taWNzID0gdXNlQXRvbWljczsKICAgICAgdGhpcy5zdHJlYW0gPSBzdHJlYW07CiAgICAgIHRoaXMuZGVidWcgPSBkZWJ1ZzsKICAgICAgdGhpcy5uYW1lID0gbmFtZTsKICAgIH0KCiAgICBsb2coLi4uYXJncykgewogICAgICBpZiAodGhpcy5kZWJ1ZykgewogICAgICAgIGNvbnNvbGUubG9nKGBbcmVhZGVyOiAke3RoaXMubmFtZX1dYCwgLi4uYXJncyk7CiAgICAgIH0KICAgIH0KCiAgICB3YWl0V3JpdGUobmFtZSkgewogICAgICBpZiAodGhpcy51c2VBdG9taWNzKSB7CiAgICAgICAgdGhpcy5sb2coYHdhaXRpbmcgZm9yICR7bmFtZX1gKTsKCiAgICAgICAgd2hpbGUgKEF0b21pY3MubG9hZCh0aGlzLmF0b21pY1ZpZXcsIDApID09PSBXUklURUFCTEUpIHsKICAgICAgICAgIC8vIGNvbnNvbGUubG9nKCd3YWl0aW5nIGZvciB3cml0ZS4uLicpOwogICAgICAgICAgQXRvbWljcy53YWl0KHRoaXMuYXRvbWljVmlldywgMCwgV1JJVEVBQkxFLCA1MDApOwogICAgICAgIH0KCiAgICAgICAgdGhpcy5sb2coYHJlc3VtZWQgZm9yICR7bmFtZX1gKTsKICAgICAgfSBlbHNlIHsKICAgICAgICBpZiAodGhpcy5hdG9taWNWaWV3WzBdICE9PSBSRUFEQUJMRSkgewogICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdgd2FpdFdyaXRlYCBleHBlY3RlZCBhcnJheSB0byBiZSByZWFkYWJsZScpOwogICAgICAgIH0KICAgICAgfQogICAgfQoKICAgIGZsaXAoKSB7CiAgICAgIHRoaXMubG9nKCdmbGlwJyk7CiAgICAgIGlmICh0aGlzLnVzZUF0b21pY3MpIHsKICAgICAgICBsZXQgcHJldiA9IEF0b21pY3MuY29tcGFyZUV4Y2hhbmdlKAogICAgICAgICAgdGhpcy5hdG9taWNWaWV3LAogICAgICAgICAgMCwKICAgICAgICAgIFJFQURBQkxFLAogICAgICAgICAgV1JJVEVBQkxFCiAgICAgICAgKTsKCiAgICAgICAgaWYgKHByZXYgIT09IFJFQURBQkxFKSB7CiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1JlYWQgZGF0YSBvdXQgb2Ygc3luYyEgVGhpcyBpcyBkaXNhc3Ryb3VzJyk7CiAgICAgICAgfQoKICAgICAgICBBdG9taWNzLm5vdGlmeSh0aGlzLmF0b21pY1ZpZXcsIDApOwogICAgICB9IGVsc2UgewogICAgICAgIHRoaXMuYXRvbWljVmlld1swXSA9IFdSSVRFQUJMRTsKICAgICAgfQoKICAgICAgdGhpcy5vZmZzZXQgPSA0OwogICAgfQoKICAgIGRvbmUoKSB7CiAgICAgIHRoaXMud2FpdFdyaXRlKCdkb25lJyk7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0KTsKICAgICAgbGV0IGRvbmUgPSBkYXRhVmlldy5nZXRVaW50MzIoMCkgPT09IEZJTkFMSVpFRDsKCiAgICAgIGlmIChkb25lKSB7CiAgICAgICAgdGhpcy5sb2coJ2RvbmUnKTsKICAgICAgICB0aGlzLmZsaXAoKTsKICAgICAgfQoKICAgICAgcmV0dXJuIGRvbmU7CiAgICB9CgogICAgcGVlayhmbikgewogICAgICB0aGlzLnBlZWtPZmZzZXQgPSB0aGlzLm9mZnNldDsKICAgICAgbGV0IHJlcyA9IGZuKCk7CiAgICAgIHRoaXMub2Zmc2V0ID0gdGhpcy5wZWVrT2Zmc2V0OwogICAgICB0aGlzLnBlZWtPZmZzZXQgPSBudWxsOwogICAgICByZXR1cm4gcmVzOwogICAgfQoKICAgIHN0cmluZygpIHsKICAgICAgdGhpcy53YWl0V3JpdGUoJ3N0cmluZycpOwoKICAgICAgbGV0IGJ5dGVMZW5ndGggPSB0aGlzLl9pbnQzMigpOwogICAgICBsZXQgbGVuZ3RoID0gYnl0ZUxlbmd0aCAvIDI7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0LCBieXRlTGVuZ3RoKTsKICAgICAgbGV0IGNoYXJzID0gW107CiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbGVuZ3RoOyBpKyspIHsKICAgICAgICBjaGFycy5wdXNoKGRhdGFWaWV3LmdldFVpbnQxNihpICogMikpOwogICAgICB9CiAgICAgIGxldCBzdHIgPSBTdHJpbmcuZnJvbUNoYXJDb2RlLmFwcGx5KG51bGwsIGNoYXJzKTsKICAgICAgdGhpcy5sb2coJ3N0cmluZycsIHN0cik7CgogICAgICB0aGlzLm9mZnNldCArPSBieXRlTGVuZ3RoOwoKICAgICAgaWYgKHRoaXMucGVla09mZnNldCA9PSBudWxsKSB7CiAgICAgICAgdGhpcy5mbGlwKCk7CiAgICAgIH0KICAgICAgcmV0dXJuIHN0cjsKICAgIH0KCiAgICBfaW50MzIoKSB7CiAgICAgIGxldCBieXRlTGVuZ3RoID0gNDsKCiAgICAgIGxldCBkYXRhVmlldyA9IG5ldyBEYXRhVmlldyh0aGlzLmJ1ZmZlciwgdGhpcy5vZmZzZXQpOwogICAgICBsZXQgbnVtID0gZGF0YVZpZXcuZ2V0SW50MzIoKTsKICAgICAgdGhpcy5sb2coJ19pbnQzMicsIG51bSk7CgogICAgICB0aGlzLm9mZnNldCArPSBieXRlTGVuZ3RoOwogICAgICByZXR1cm4gbnVtOwogICAgfQoKICAgIGludDMyKCkgewogICAgICB0aGlzLndhaXRXcml0ZSgnaW50MzInKTsKICAgICAgbGV0IG51bSA9IHRoaXMuX2ludDMyKCk7CiAgICAgIHRoaXMubG9nKCdpbnQzMicsIG51bSk7CgogICAgICBpZiAodGhpcy5wZWVrT2Zmc2V0ID09IG51bGwpIHsKICAgICAgICB0aGlzLmZsaXAoKTsKICAgICAgfQogICAgICByZXR1cm4gbnVtOwogICAgfQoKICAgIGJ5dGVzKCkgewogICAgICB0aGlzLndhaXRXcml0ZSgnYnl0ZXMnKTsKCiAgICAgIGxldCBieXRlTGVuZ3RoID0gdGhpcy5faW50MzIoKTsKCiAgICAgIGxldCBieXRlcyA9IG5ldyBBcnJheUJ1ZmZlcihieXRlTGVuZ3RoKTsKICAgICAgbmV3IFVpbnQ4QXJyYXkoYnl0ZXMpLnNldCgKICAgICAgICBuZXcgVWludDhBcnJheSh0aGlzLmJ1ZmZlciwgdGhpcy5vZmZzZXQsIGJ5dGVMZW5ndGgpCiAgICAgICk7CiAgICAgIHRoaXMubG9nKCdieXRlcycsIGJ5dGVzKTsKCiAgICAgIHRoaXMub2Zmc2V0ICs9IGJ5dGVMZW5ndGg7CgogICAgICBpZiAodGhpcy5wZWVrT2Zmc2V0ID09IG51bGwpIHsKICAgICAgICB0aGlzLmZsaXAoKTsKICAgICAgfQogICAgICByZXR1cm4gYnl0ZXM7CiAgICB9CiAgfQoKICBjbGFzcyBXcml0ZXIgewogICAgY29uc3RydWN0b3IoCiAgICAgIGJ1ZmZlciwKICAgICAgeyBpbml0aWFsT2Zmc2V0ID0gNCwgdXNlQXRvbWljcyA9IHRydWUsIHN0cmVhbSA9IHRydWUsIGRlYnVnLCBuYW1lIH0gPSB7fQogICAgKSB7CiAgICAgIHRoaXMuYnVmZmVyID0gYnVmZmVyOwogICAgICB0aGlzLmF0b21pY1ZpZXcgPSBuZXcgSW50MzJBcnJheShidWZmZXIpOwogICAgICB0aGlzLm9mZnNldCA9IGluaXRpYWxPZmZzZXQ7CiAgICAgIHRoaXMudXNlQXRvbWljcyA9IHVzZUF0b21pY3M7CiAgICAgIHRoaXMuc3RyZWFtID0gc3RyZWFtOwoKICAgICAgdGhpcy5kZWJ1ZyA9IGRlYnVnOwogICAgICB0aGlzLm5hbWUgPSBuYW1lOwoKICAgICAgaWYgKHRoaXMudXNlQXRvbWljcykgewogICAgICAgIC8vIFRoZSBidWZmZXIgc3RhcnRzIG91dCBhcyB3cml0ZWFibGUKICAgICAgICBBdG9taWNzLnN0b3JlKHRoaXMuYXRvbWljVmlldywgMCwgV1JJVEVBQkxFKTsKICAgICAgfSBlbHNlIHsKICAgICAgICB0aGlzLmF0b21pY1ZpZXdbMF0gPSBXUklURUFCTEU7CiAgICAgIH0KICAgIH0KCiAgICBsb2coLi4uYXJncykgewogICAgICBpZiAodGhpcy5kZWJ1ZykgewogICAgICAgIGNvbnNvbGUubG9nKGBbd3JpdGVyOiAke3RoaXMubmFtZX1dYCwgLi4uYXJncyk7CiAgICAgIH0KICAgIH0KCiAgICB3YWl0UmVhZChuYW1lKSB7CiAgICAgIGlmICh0aGlzLnVzZUF0b21pY3MpIHsKICAgICAgICB0aGlzLmxvZyhgd2FpdGluZyBmb3IgJHtuYW1lfWApOwogICAgICAgIC8vIFN3aXRjaCB0byB3cml0YWJsZQogICAgICAgIC8vIEF0b21pY3Muc3RvcmUodGhpcy5hdG9taWNWaWV3LCAwLCAxKTsKCiAgICAgICAgbGV0IHByZXYgPSBBdG9taWNzLmNvbXBhcmVFeGNoYW5nZSgKICAgICAgICAgIHRoaXMuYXRvbWljVmlldywKICAgICAgICAgIDAsCiAgICAgICAgICBXUklURUFCTEUsCiAgICAgICAgICBSRUFEQUJMRQogICAgICAgICk7CgogICAgICAgIGlmIChwcmV2ICE9PSBXUklURUFCTEUpIHsKICAgICAgICAgIHRocm93IG5ldyBFcnJvcigKICAgICAgICAgICAgJ1dyb3RlIHNvbWV0aGluZyBpbnRvIHVud3JpdGFibGUgYnVmZmVyISBUaGlzIGlzIGRpc2FzdHJvdXMnCiAgICAgICAgICApOwogICAgICAgIH0KCiAgICAgICAgQXRvbWljcy5ub3RpZnkodGhpcy5hdG9taWNWaWV3LCAwKTsKCiAgICAgICAgd2hpbGUgKEF0b21pY3MubG9hZCh0aGlzLmF0b21pY1ZpZXcsIDApID09PSBSRUFEQUJMRSkgewogICAgICAgICAgLy8gY29uc29sZS5sb2coJ3dhaXRpbmcgdG8gYmUgcmVhZC4uLicpOwogICAgICAgICAgQXRvbWljcy53YWl0KHRoaXMuYXRvbWljVmlldywgMCwgUkVBREFCTEUsIDUwMCk7CiAgICAgICAgfQoKICAgICAgICB0aGlzLmxvZyhgcmVzdW1lZCBmb3IgJHtuYW1lfWApOwogICAgICB9IGVsc2UgewogICAgICAgIHRoaXMuYXRvbWljVmlld1swXSA9IFJFQURBQkxFOwogICAgICB9CgogICAgICB0aGlzLm9mZnNldCA9IDQ7CiAgICB9CgogICAgZmluYWxpemUoKSB7CiAgICAgIHRoaXMubG9nKCdmaW5hbGl6aW5nJyk7CiAgICAgIGxldCBkYXRhVmlldyA9IG5ldyBEYXRhVmlldyh0aGlzLmJ1ZmZlciwgdGhpcy5vZmZzZXQpOwogICAgICBkYXRhVmlldy5zZXRVaW50MzIoMCwgRklOQUxJWkVEKTsKICAgICAgdGhpcy53YWl0UmVhZCgnZmluYWxpemUnKTsKICAgIH0KCiAgICBzdHJpbmcoc3RyKSB7CiAgICAgIHRoaXMubG9nKCdzdHJpbmcnLCBzdHIpOwoKICAgICAgbGV0IGJ5dGVMZW5ndGggPSBzdHIubGVuZ3RoICogMjsKICAgICAgdGhpcy5faW50MzIoYnl0ZUxlbmd0aCk7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0LCBieXRlTGVuZ3RoKTsKICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBzdHIubGVuZ3RoOyBpKyspIHsKICAgICAgICBkYXRhVmlldy5zZXRVaW50MTYoaSAqIDIsIHN0ci5jaGFyQ29kZUF0KGkpKTsKICAgICAgfQoKICAgICAgdGhpcy5vZmZzZXQgKz0gYnl0ZUxlbmd0aDsKICAgICAgdGhpcy53YWl0UmVhZCgnc3RyaW5nJyk7CiAgICB9CgogICAgX2ludDMyKG51bSkgewogICAgICBsZXQgYnl0ZUxlbmd0aCA9IDQ7CgogICAgICBsZXQgZGF0YVZpZXcgPSBuZXcgRGF0YVZpZXcodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0KTsKICAgICAgZGF0YVZpZXcuc2V0SW50MzIoMCwgbnVtKTsKCiAgICAgIHRoaXMub2Zmc2V0ICs9IGJ5dGVMZW5ndGg7CiAgICB9CgogICAgaW50MzIobnVtKSB7CiAgICAgIHRoaXMubG9nKCdpbnQzMicsIG51bSk7CiAgICAgIHRoaXMuX2ludDMyKG51bSk7CiAgICAgIHRoaXMud2FpdFJlYWQoJ2ludDMyJyk7CiAgICB9CgogICAgYnl0ZXMoYnVmZmVyKSB7CiAgICAgIHRoaXMubG9nKCdieXRlcycsIGJ1ZmZlcik7CgogICAgICBsZXQgYnl0ZUxlbmd0aCA9IGJ1ZmZlci5ieXRlTGVuZ3RoOwogICAgICB0aGlzLl9pbnQzMihieXRlTGVuZ3RoKTsKICAgICAgbmV3IFVpbnQ4QXJyYXkodGhpcy5idWZmZXIsIHRoaXMub2Zmc2V0KS5zZXQobmV3IFVpbnQ4QXJyYXkoYnVmZmVyKSk7CgogICAgICB0aGlzLm9mZnNldCArPSBieXRlTGVuZ3RoOwogICAgICB0aGlzLndhaXRSZWFkKCdieXRlcycpOwogICAgfQogIH0KCiAgbGV0IHRva2VuID0gJyc7CiAgbGV0IHNoZWV0SWQgPSAnMXAxaXNVWmtXZThvYzEyTEwwa3FhVDNVRlRfTVI4dkVvRWllRXJ1SFcteEUnOwoKICBsZXQgYnVmZmVyID0gNDAwMDA7CiAgbGV0IGJhc2VUaW1lOwogIGxldCB0aW1pbmdzID0ge307CgogIGxldCByYW5nZSA9ICdBMyc7CgogIGNvbnN0IGRlc2NyaXB0aW9ucyA9IHsKICAgIGdldDogJ0NhbGxzIHRvIGBzdG9yZS5nZXRgJywKICAgICdzdHJlYW0tbmV4dCc6ICdBZHZhbmNpbmcgYSBjdXJzb3InLAogICAgc3RyZWFtOiAnT3BlbmluZyBhIGN1cnNvcicsCiAgICByZWFkOiAnRnVsbCBwcm9jZXNzIGZvciByZWFkaW5nIGEgYmxvY2snCiAgfTsKCiAgZnVuY3Rpb24gbGFzdChhcnIpIHsKICAgIHJldHVybiBhcnIubGVuZ3RoID09PSAwID8gbnVsbCA6IGFyclthcnIubGVuZ3RoIC0gMV07CiAgfQoKICBmdW5jdGlvbiBwZXJjZW50aWxlKGRhdGEsIHApIHsKICAgIGxldCBzb3J0ZWQgPSBbLi4uZGF0YV07CiAgICBzb3J0ZWQuc29ydCgobjEsIG4yKSA9PiBuMVsxXSAtIG4yWzFdKTsKICAgIHJldHVybiBzb3J0ZWQuc2xpY2UoMCwgTWF0aC5jZWlsKHNvcnRlZC5sZW5ndGggKiBwKSB8IDApOwogIH0KCiAgbGV0IHNob3dXYXJuaW5nID0gdHJ1ZTsKCiAgYXN5bmMgZnVuY3Rpb24gd3JpdGVEYXRhKHNoZWV0TmFtZSwgZGF0YSkgewogICAgbGV0IGFyciA9IHBlcmNlbnRpbGUoZGF0YSwgMC45NSk7CgogICAgaWYgKGFyci5sZW5ndGggPiBidWZmZXIpIHsKICAgICAgYXJyID0gYXJyLnNsaWNlKC1idWZmZXIpOwogICAgfSBlbHNlIHsKICAgICAgd2hpbGUgKGFyci5sZW5ndGggPCBidWZmZXIpIHsKICAgICAgICBhcnIucHVzaChbJycsICcnXSk7CiAgICAgIH0KICAgIH0KCiAgICBsZXQgcmVzID0gYXdhaXQgZmV0Y2goCiAgICAgIGBodHRwczovL3NoZWV0cy5nb29nbGVhcGlzLmNvbS92NC9zcHJlYWRzaGVldHMvJHtzaGVldElkfS92YWx1ZXMvJHtzaGVldE5hbWV9ISR7cmFuZ2V9P3ZhbHVlSW5wdXRPcHRpb249VVNFUl9FTlRFUkVEYCwKICAgICAgewogICAgICAgIG1ldGhvZDogJ1BVVCcsCiAgICAgICAgaGVhZGVyczogewogICAgICAgICAgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJywKICAgICAgICAgIEF1dGhvcml6YXRpb246IGBCZWFyZXIgJHt0b2tlbn1gCiAgICAgICAgfSwKICAgICAgICBib2R5OiBKU09OLnN0cmluZ2lmeSh7IHZhbHVlczogYXJyIH0pCiAgICAgIH0KICAgICk7CiAgICBpZiAocmVzLnN0YXR1cyA9PSAyMDApIHsKICAgICAgY29uc29sZS5sb2coYExvZ2dlZCB0aW1pbmdzIHRvIHNwcmVhZHNoZWV0ICgke3NoZWV0TmFtZX0pKWApOwogICAgfSBlbHNlIHsKICAgICAgaWYgKHNob3dXYXJuaW5nKSB7CiAgICAgICAgc2hvd1dhcm5pbmcgPSBmYWxzZTsKICAgICAgICBjb25zb2xlLndhcm4oCiAgICAgICAgICAnVW5hYmxlIHRvIGxvZyBwZXJmIGRhdGEgdG8gc3ByZWFkc2hlZXQuIElzIHRoZSBPQXV0aCB0b2tlbiBleHBpcmVkPycKICAgICAgICApOwogICAgICB9CgogICAgICBjb25zb2xlLmxvZyhgLS0tICR7c2hlZXROYW1lfSAoJHtkZXNjcmlwdGlvbnNbc2hlZXROYW1lXX0pIC0tLWApOwogICAgICBjb25zb2xlLmxvZyhgQ291bnQ6ICR7ZGF0YS5sZW5ndGh9YCk7CiAgICAgIGNvbnNvbGUubG9nKGBwNTA6ICR7bGFzdChwZXJjZW50aWxlKGRhdGEsIDAuNSkpWzFdfWApOwogICAgICBjb25zb2xlLmxvZyhgcDk1OiAke2xhc3QocGVyY2VudGlsZShkYXRhLCAwLjk1KSlbMV19YCk7CiAgICB9CiAgfQoKICBhc3luYyBmdW5jdGlvbiBlbmQoKSB7CiAgICBhd2FpdCBQcm9taXNlLmFsbCgKICAgICAgT2JqZWN0LmtleXModGltaW5ncykubWFwKG5hbWUgPT4gewogICAgICAgIGxldCB0aW1pbmcgPSB0aW1pbmdzW25hbWVdOwogICAgICAgIHJldHVybiB3cml0ZURhdGEobmFtZSwgdGltaW5nLmRhdGEubWFwKHggPT4gW3guc3RhcnQgKyB4LnRvb2ssIHgudG9va10pKTsKICAgICAgfSkKICAgICk7CiAgfQoKICBmdW5jdGlvbiBzdGFydCgpIHsKICAgIHRpbWluZ3MgPSB7fTsKICAgIGJhc2VUaW1lID0gcGVyZm9ybWFuY2Uubm93KCk7CiAgfQoKICBmdW5jdGlvbiByZWNvcmQobmFtZSkgewogICAgaWYgKHRpbWluZ3NbbmFtZV0gPT0gbnVsbCkgewogICAgICB0aW1pbmdzW25hbWVdID0geyBzdGFydDogbnVsbCwgZGF0YTogW10gfTsKICAgIH0KICAgIGxldCB0aW1lciA9IHRpbWluZ3NbbmFtZV07CgogICAgaWYgKHRpbWVyLnN0YXJ0ICE9IG51bGwpIHsKICAgICAgdGhyb3cgbmV3IEVycm9yKGB0aW1lciBhbHJlYWR5IHN0YXJ0ZWQgJHtuYW1lfWApOwogICAgfQogICAgdGltZXIuc3RhcnQgPSBwZXJmb3JtYW5jZS5ub3coKTsKICB9CgogIGZ1bmN0aW9uIGVuZFJlY29yZGluZyhuYW1lKSB7CiAgICBsZXQgbm93ID0gcGVyZm9ybWFuY2Uubm93KCk7CiAgICBsZXQgdGltZXIgPSB0aW1pbmdzW25hbWVdOwoKICAgIGlmICh0aW1lciAmJiB0aW1lci5zdGFydCAhPSBudWxsKSB7CiAgICAgIGxldCB0b29rID0gbm93IC0gdGltZXIuc3RhcnQ7CiAgICAgIGxldCBzdGFydCA9IHRpbWVyLnN0YXJ0IC0gYmFzZVRpbWU7CiAgICAgIHRpbWVyLnN0YXJ0ID0gbnVsbDsKCiAgICAgIGlmICh0aW1lci5kYXRhLmxlbmd0aCA8IGJ1ZmZlcikgewogICAgICAgIHRpbWVyLmRhdGEucHVzaCh7IHN0YXJ0LCB0b29rIH0pOwogICAgICB9CiAgICB9CiAgfQoKICBsZXQgaXNQcm9iYWJseVNhZmFyaSA9IC9eKCg/IWNocm9tZXxhbmRyb2lkKS4pKnNhZmFyaS9pLnRlc3QoCiAgICBuYXZpZ2F0b3IudXNlckFnZW50CiAgKTsKCiAgbGV0IG9wZW5EYnMgPSBuZXcgTWFwKCk7CiAgbGV0IHRyYW5zYWN0aW9ucyA9IG5ldyBNYXAoKTsKCiAgZnVuY3Rpb24gYXNzZXJ0KGNvbmQsIG1zZykgewogICAgaWYgKCFjb25kKSB7CiAgICAgIHRocm93IG5ldyBFcnJvcihtc2cpOwogICAgfQogIH0KCiAgbGV0IExPQ0tfVFlQRVMgPSB7CiAgICBOT05FOiAwLAogICAgU0hBUkVEOiAxLAogICAgUkVTRVJWRUQ6IDIsCiAgICBQRU5ESU5HOiAzLAogICAgRVhDTFVTSVZFOiA0CiAgfTsKCiAgLy8gV2UgdXNlIGxvbmctbGl2ZWQgdHJhbnNhY3Rpb25zLCBhbmQgYFRyYW5zYWN0aW9uYCBrZWVwcyB0aGUKICAvLyB0cmFuc2FjdGlvbiBzdGF0ZS4gSXQgaW1wbGVtZW50cyBhbiBvcHRpbWFsIHdheSB0byBwZXJmb3JtCiAgLy8gcmVhZC93cml0ZXMgd2l0aCBrbm93bGVkZ2Ugb2YgaG93IHNxbGl0ZSBhc2tzIGZvciB0aGVtLCBhbmQgYWxzbwogIC8vIGltcGxlbWVudHMgYSBsb2NraW5nIG1lY2hhbmlzbSB0aGF0IG1hcHMgdG8gaG93IHNxbGl0ZSBsb2NrcyB3b3JrLgogIGNsYXNzIFRyYW5zYWN0aW9uIHsKICAgIGNvbnN0cnVjdG9yKGRiLCBpbml0aWFsTW9kZSA9ICdyZWFkb25seScpIHsKICAgICAgdGhpcy5kYiA9IGRiOwogICAgICB0aGlzLnRyYW5zID0gdGhpcy5kYi50cmFuc2FjdGlvbihbJ2RhdGEnXSwgaW5pdGlhbE1vZGUpOwogICAgICB0aGlzLnN0b3JlID0gdGhpcy50cmFucy5vYmplY3RTdG9yZSgnZGF0YScpOwogICAgICB0aGlzLmxvY2tUeXBlID0KICAgICAgICBpbml0aWFsTW9kZSA9PT0gJ3JlYWRvbmx5JyA/IExPQ0tfVFlQRVMuU0hBUkVEIDogTE9DS19UWVBFUy5FWENMVVNJVkU7CgogICAgICAvLyBUaGVyZSBpcyBubyBuZWVkIGZvciB1cyB0byBjYWNoZSBibG9ja3MuIFVzZSBzcWxpdGUncwogICAgICAvLyBgY2FjaGVfc2l6ZWAgZm9yIHRoYXQgYW5kIGl0IHdpbGwgYXV0b21hdGljYWxseSBkbyBpdC4gSG93ZXZlciwKICAgICAgLy8gd2UgZG8gc3RpbGwga2VlcCBhIGNhY2hlIG9mIHRoZSBmaXJzdCBibG9jayBmb3IgdGhlIGR1cmF0aW9uIG9mCiAgICAgIC8vIHRoaXMgdHJhbnNhY3Rpb24gYmVjYXVzZSBvZiBob3cgbG9ja2luZyB3b3JrczsgdGhpcyBhdm9pZHMgYQogICAgICAvLyBmZXcgZXh0cmEgcmVhZHMgYW5kIGFsbG93cyB1cyB0byBkZXRlY3QgY2hhbmdlcyBkdXJpbmcKICAgICAgLy8gdXBncmFkaW5nIChzZWUgYHVwZ3JhZGVFeGNsdXNpdmVgKQogICAgICB0aGlzLmNhY2hlZEZpcnN0QmxvY2sgPSBudWxsOwoKICAgICAgdGhpcy5jdXJzb3IgPSBudWxsOwogICAgICB0aGlzLnByZXZSZWFkcyA9IG51bGw7CiAgICB9CgogICAgYXN5bmMgcHJlZmV0Y2hGaXJzdEJsb2NrKHRpbWVvdXQpIHsKICAgICAgLy8gVE9ETzogaW1wbGVtZW50IHRpbWVvdXQKCiAgICAgIC8vIEdldCB0aGUgZmlyc3QgYmxvY2sgYW5kIGNhY2hlIGl0CiAgICAgIGxldCBibG9jayA9IGF3YWl0IHRoaXMuZ2V0KDApOwogICAgICB0aGlzLmNhY2hlZEZpcnN0QmxvY2sgPSBibG9jazsKICAgICAgcmV0dXJuIGJsb2NrOwogICAgfQoKICAgIGFzeW5jIHdhaXRDb21wbGV0ZSgpIHsKICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHsKICAgICAgICAvLyBFYWdlcmx5IGNvbW1pdCBpdCBmb3IgYmV0dGVyIHBlcmYuIE5vdGUgdGhhdCAqKnRoaXMgYXNzdW1lcwogICAgICAgIC8vIHRoZSB0cmFuc2FjdGlvbiBpcyBvcGVuKiogYXMgYGNvbW1pdGAgd2lsbCB0aHJvdyBhbiBlcnJvciBpZgogICAgICAgIC8vIGl0J3MgYWxyZWFkeSBjbG9zZWQgKHdoaWNoIHNob3VsZCBuZXZlciBiZSB0aGUgY2FzZSBmb3IgdXMpCiAgICAgICAgdGhpcy5jb21taXQoKTsKCiAgICAgICAgaWYgKHRoaXMubG9ja1R5cGUgPT09IExPQ0tfVFlQRVMuRVhDTFVTSVZFKSB7CiAgICAgICAgICAvLyBXYWl0IHVudGlsIGFsbCB3cml0ZXMgYXJlIGNvbW1pdHRlZAogICAgICAgICAgdGhpcy50cmFucy5vbmNvbXBsZXRlID0gZSA9PiByZXNvbHZlKCk7CgogICAgICAgICAgLy8gVE9ETzogSXMgaXQgT0sgdG8gYWRkIHRoaXMgbGF0ZXIsIGFmdGVyIGFuIGVycm9yIG1pZ2h0IGhhdmUKICAgICAgICAgIC8vIGhhcHBlbmVkPyBXaWxsIGl0IGhvbGQgdGhlIGVycm9yIGFuZCBmaXJlIHRoaXMgd2hlbiB3ZQogICAgICAgICAgLy8gYXR0YWNoZWQgaXQ/IFdlIG1pZ2h0IHdhbnQgdG8gZWFnZXJseSBjcmVhdGUgdGhlIHByb21pc2UKICAgICAgICAgIC8vIHdoZW4gY3JlYXRpbmcgdGhlIHRyYW5zYWN0aW9uIGFuZCByZXR1cm4gaXQgaGVyZQogICAgICAgICAgdGhpcy50cmFucy5vbmVycm9yID0gZSA9PiByZWplY3QoZSk7CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgIGlmIChpc1Byb2JhYmx5U2FmYXJpKSB7CiAgICAgICAgICAgIC8vIFNhZmFyaSBoYXMgYSBidWcgd2hlcmUgc29tZXRpbWVzIHRoZSBJREIgZ2V0cyBibG9ja2VkCiAgICAgICAgICAgIC8vIHBlcm1hbmVudGx5IGlmIHlvdSByZWZyZXNoIHRoZSBwYWdlIHdpdGggYW4gb3BlbgogICAgICAgICAgICAvLyB0cmFuc2FjdGlvbi4gWW91IGhhdmUgdG8gcmVzdGFydCB0aGUgYnJvd3NlciB0byBmaXggaXQuCiAgICAgICAgICAgIC8vIFdlIHdhaXQgZm9yIHJlYWRvbmx5IHRyYW5zYWN0aW9ucyB0byBmaW5pc2ggdG9vLCBidXQgdGhpcwogICAgICAgICAgICAvLyBpcyBhIHBlcmYgaGl0CiAgICAgICAgICAgIHRoaXMudHJhbnMub25jb21wbGV0ZSA9IGUgPT4gcmVzb2x2ZSgpOwogICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgLy8gTm8gbmVlZCB0byB3YWl0IG9uIGFueXRoaW5nIGluIGEgcmVhZC1vbmx5IHRyYW5zYWN0aW9uLgogICAgICAgICAgICAvLyBOb3RlIHRoYXQgZXJyb3JzIGR1cmluZyByZWFkcyBhcmVhIGFsd2F5cyBoYW5kbGVkIGJ5IHRoZQogICAgICAgICAgICAvLyByZWFkIHJlcXVlc3QuCiAgICAgICAgICAgIHJlc29sdmUoKTsKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIH0pOwogICAgfQoKICAgIGNvbW1pdCgpIHsKICAgICAgLy8gU2FmYXJpIGRvZXNuJ3Qgc3VwcG9ydCB0aGlzIG1ldGhvZCB5ZXQgKHRoaXMgaXMganVzdCBhbgogICAgICAvLyBvcHRpbWl6YXRpb24pCiAgICAgIGlmICh0aGlzLnRyYW5zLmNvbW1pdCkgewogICAgICAgIHRoaXMudHJhbnMuY29tbWl0KCk7CiAgICAgIH0KICAgIH0KCiAgICBhc3luYyB1cGdyYWRlRXhjbHVzaXZlKCkgewogICAgICB0aGlzLmNvbW1pdCgpOwoKICAgICAgLy8gY29uc29sZS5sb2coJ3VwZGF0aW5nIHRyYW5zYWN0aW9uIHJlYWR3cml0ZScpOwogICAgICB0aGlzLnRyYW5zID0gdGhpcy5kYi50cmFuc2FjdGlvbihbJ2RhdGEnXSwgJ3JlYWR3cml0ZScpOwogICAgICB0aGlzLnN0b3JlID0gdGhpcy50cmFucy5vYmplY3RTdG9yZSgnZGF0YScpOwogICAgICB0aGlzLmxvY2tUeXBlID0gTE9DS19UWVBFUy5FWENMVVNJVkU7CgogICAgICBsZXQgY2FjaGVkMCA9IHRoaXMuY2FjaGVkRmlyc3RCbG9jazsKCiAgICAgIC8vIERvIGEgcmVhZAogICAgICBsZXQgYmxvY2sgPSBhd2FpdCB0aGlzLnByZWZldGNoRmlyc3RCbG9jayg1MDApOwogICAgICAvLyBUT0RPOiB3aGVuIHRpbWVvdXRzIGFyZSBpbXBsZW1lbnRlZCwgZGV0ZWN0IHRpbWVvdXQgYW5kIHJldHVybiBCVVNZCgogICAgICBpZiAoY2FjaGVkMCA9PSBudWxsICYmIGJsb2NrID09IG51bGwpIHsKICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgfSBlbHNlIHsKICAgICAgICBmb3IgKGxldCBpID0gMjQ7IGkgPCA0MDsgaSsrKSB7CiAgICAgICAgICBpZiAoYmxvY2tbaV0gIT09IGNhY2hlZDBbaV0pIHsKICAgICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfQoKICAgICAgcmV0dXJuIHRydWU7CiAgICB9CgogICAgZG93bmdyYWRlU2hhcmVkKCkgewogICAgICB0aGlzLmNvbW1pdCgpOwoKICAgICAgLy8gY29uc29sZS5sb2coJ2Rvd25ncmFkaW5nIHRyYW5zYWN0aW9uIHJlYWRvbmx5Jyk7CiAgICAgIHRoaXMudHJhbnMgPSB0aGlzLmRiLnRyYW5zYWN0aW9uKFsnZGF0YSddLCAncmVhZG9ubHknKTsKICAgICAgdGhpcy5zdG9yZSA9IHRoaXMudHJhbnMub2JqZWN0U3RvcmUoJ2RhdGEnKTsKICAgICAgdGhpcy5sb2NrVHlwZSA9IExPQ0tfVFlQRVMuU0hBUkVEOwogICAgfQoKICAgIGFzeW5jIGdldChrZXkpIHsKICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHsKICAgICAgICByZWNvcmQoJ2dldCcpOwogICAgICAgIGxldCByZXEgPSB0aGlzLnN0b3JlLmdldChrZXkpOwogICAgICAgIHJlcS5vbnN1Y2Nlc3MgPSBlID0+IHsKICAgICAgICAgIGVuZFJlY29yZGluZygnZ2V0Jyk7CiAgICAgICAgICByZXNvbHZlKHJlcS5yZXN1bHQpOwogICAgICAgIH07CiAgICAgICAgcmVxLm9uZXJyb3IgPSBlID0+IHJlamVjdChlKTsKICAgICAgfSk7CiAgICB9CgogICAgZ2V0UmVhZERpcmVjdGlvbigpIHsKICAgICAgLy8gVGhlcmUgYXJlIGEgdHdvIHdheXMgd2UgY2FuIHJlYWQgZGF0YTogYSBkaXJlY3QgYGdldGAgcmVxdWVzdAogICAgICAvLyBvciBvcGVuaW5nIGEgY3Vyc29yIGFuZCBpdGVyYXRpbmcgdGhyb3VnaCBkYXRhLiBXZSBkb24ndCBrbm93CiAgICAgIC8vIHdoYXQgZnV0dXJlIHJlYWRzIGxvb2sgbGlrZSwgc28gd2UgZG9uJ3Qga25vdyB0aGUgYmVzdCBzdHJhdGVneQogICAgICAvLyB0byBwaWNrLiBBbHdheXMgY2hvb3Npbmcgb25lIHN0cmF0ZWd5IGZvcmdvZXMgYSBsb3Qgb2YKICAgICAgLy8gb3B0aW1pemF0aW9uLCBiZWNhdXNlIGl0ZXJhdGluZyB3aXRoIGEgY3Vyc29yIGlzIGEgbG90IGZhc3RlcgogICAgICAvLyB0aGFuIG1hbnkgYGdldGAgY2FsbHMuIE9uIHRoZSBvdGhlciBoYW5kLCBvcGVuaW5nIGEgY3Vyc29yIGlzCiAgICAgIC8vIHNsb3csIGFuZCBzbyBpcyBjYWxsaW5nIGBhZHZhbmNlYCB0byBtb3ZlIGEgY3Vyc29yIG92ZXIgYSBodWdlCiAgICAgIC8vIHJhbmdlIChsaWtlIG1vdmluZyBpdCAxMDAwIGl0ZW1zIGxhdGVyKSwgc28gbWFueSBgZ2V0YCBjYWxscyB3b3VsZAogICAgICAvLyBiZSBmYXN0ZXIuIEluIGdlbmVyYWw6CiAgICAgIC8vCiAgICAgIC8vICogTWFueSBgZ2V0YCBjYWxscyBhcmUgZmFzdGVyIHdoZW4gZG9pbmcgcmFuZG9tIGFjY2Vzc2VzCiAgICAgIC8vICogSXRlcmF0aW5nIHdpdGggYSBjdXJzb3IgaXMgZmFzdGVyIGlmIGRvaW5nIG1vc3RseSBzZXF1ZW50aWFsCiAgICAgIC8vICAgYWNjZXNzZXMKICAgICAgLy8KICAgICAgLy8gV2UgaW1wbGVtZW50IGEgaGV1cmlzdGljIGFuZCBrZWVwcyB0cmFjayBvZiB0aGUgbGFzdCAzIHJlYWRzCiAgICAgIC8vIGFuZCBkZXRlY3RzIHdoZW4gdGhleSBhcmUgbW9zdGx5IHNlcXVlbnRpYWwuIElmIHRoZXkgYXJlLCB3ZQogICAgICAvLyBvcGVuIGEgY3Vyc29yIGFuZCBzdGFydCByZWFkaW5nIGJ5IGl0ZXJhdGluZyBpdC4gSWYgbm90LCB3ZSBkbwogICAgICAvLyBkaXJlY3QgYGdldGAgY2FsbHMuCiAgICAgIC8vCiAgICAgIC8vIE9uIHRvcCBvZiBhbGwgb2YgdGhpcywgZWFjaCBicm93c2VyIGhhcyBkaWZmZXJlbnQgcGVyZgogICAgICAvLyBjaGFyYWN0ZXJpc3RpY3MuIFdlIHdpbGwgcHJvYmFibHkgd2FudCB0byBtYWtlIHRoZXNlIHRocmVzaG9sZHMKICAgICAgLy8gY29uZmlndXJhYmxlIHNvIHRoZSB1c2VyIGNhbiBjaGFuZ2UgdGhlbSBwZXItYnJvd3NlciBpZiBuZWVkZWQsCiAgICAgIC8vIGFzIHdlbGwgYXMgZmluZS10dW5pbmcgdGhlbSBmb3IgdGhlaXIgdXNhZ2Ugb2Ygc3FsaXRlLgoKICAgICAgbGV0IHByZXZSZWFkcyA9IHRoaXMucHJldlJlYWRzOwogICAgICBpZiAocHJldlJlYWRzKSB7CiAgICAgICAgLy8gSGFzIHRoZXJlIGJlZW4gMyBmb3J3YXJkIHNlcXVlbnRpYWwgcmVhZHMgd2l0aGluIDEwIGJsb2Nrcz8KICAgICAgICBpZiAoCiAgICAgICAgICBwcmV2UmVhZHNbMF0gPCBwcmV2UmVhZHNbMV0gJiYKICAgICAgICAgIHByZXZSZWFkc1sxXSA8IHByZXZSZWFkc1syXSAmJgogICAgICAgICAgcHJldlJlYWRzWzJdIC0gcHJldlJlYWRzWzBdIDwgMTAKICAgICAgICApIHsKICAgICAgICAgIHJldHVybiAnbmV4dCc7CiAgICAgICAgfQoKICAgICAgICAvLyBIYXMgdGhlcmUgYmVlbiAzIGJhY2t3YXJkcyBzZXF1ZW50aWFsIHJlYWRzIHdpdGhpbiAxMCBibG9ja3M/CiAgICAgICAgaWYgKAogICAgICAgICAgcHJldlJlYWRzWzBdID4gcHJldlJlYWRzWzFdICYmCiAgICAgICAgICBwcmV2UmVhZHNbMV0gPiBwcmV2UmVhZHNbMl0gJiYKICAgICAgICAgIHByZXZSZWFkc1swXSAtIHByZXZSZWFkc1syXSA8IDEwCiAgICAgICAgKSB7CiAgICAgICAgICByZXR1cm4gJ3ByZXYnOwogICAgICAgIH0KICAgICAgfQoKICAgICAgcmV0dXJuIG51bGw7CiAgICB9CgogICAgcmVhZChwb3NpdGlvbikgewogICAgICBsZXQgd2FpdEN1cnNvciA9ICgpID0+IHsKICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4gewogICAgICAgICAgaWYgKHRoaXMuY3Vyc29yUHJvbWlzZSAhPSBudWxsKSB7CiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcigKICAgICAgICAgICAgICAnd2FpdEN1cnNvcigpIGNhbGxlZCBidXQgc29tZXRoaW5nIGVsc2UgaXMgYWxyZWFkeSB3YWl0aW5nJwogICAgICAgICAgICApOwogICAgICAgICAgfQogICAgICAgICAgdGhpcy5jdXJzb3JQcm9taXNlID0geyByZXNvbHZlLCByZWplY3QgfTsKICAgICAgICB9KTsKICAgICAgfTsKCiAgICAgIGlmICh0aGlzLmN1cnNvcikgewogICAgICAgIGxldCBjdXJzb3IgPSB0aGlzLmN1cnNvcjsKCiAgICAgICAgaWYgKAogICAgICAgICAgY3Vyc29yLmRpcmVjdGlvbiA9PT0gJ25leHQnICYmCiAgICAgICAgICBwb3NpdGlvbiA+IGN1cnNvci5rZXkgJiYKICAgICAgICAgIHBvc2l0aW9uIDwgY3Vyc29yLmtleSArIDEwMAogICAgICAgICkgewogICAgICAgICAgcmVjb3JkKCdzdHJlYW0tbmV4dCcpOwoKICAgICAgICAgIGN1cnNvci5hZHZhbmNlKHBvc2l0aW9uIC0gY3Vyc29yLmtleSk7CiAgICAgICAgICByZXR1cm4gd2FpdEN1cnNvcigpOwogICAgICAgIH0gZWxzZSBpZiAoCiAgICAgICAgICBjdXJzb3IuZGlyZWN0aW9uID09PSAncHJldicgJiYKICAgICAgICAgIHBvc2l0aW9uIDwgY3Vyc29yLmtleSAmJgogICAgICAgICAgcG9zaXRpb24gPiBjdXJzb3Iua2V5IC0gMTAwCiAgICAgICAgKSB7CiAgICAgICAgICByZWNvcmQoJ3N0cmVhbS1uZXh0Jyk7CgogICAgICAgICAgY3Vyc29yLmFkdmFuY2UoY3Vyc29yLmtleSAtIHBvc2l0aW9uKTsKICAgICAgICAgIHJldHVybiB3YWl0Q3Vyc29yKCk7CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgIC8vIERpdGNoIHRoZSBjdXJzb3IKICAgICAgICAgIHRoaXMuY3Vyc29yID0gbnVsbDsKICAgICAgICAgIHJldHVybiB0aGlzLnJlYWQocG9zaXRpb24pOwogICAgICAgIH0KICAgICAgfSBlbHNlIHsKICAgICAgICAvLyBXZSBkb24ndCBhbHJlYWR5IGhhdmUgYSBjdXJzb3IuIFdlIG5lZWQgdG8gYSBmcmVzaCByZWFkOwogICAgICAgIC8vIHNob3VsZCB3ZSBvcGVuIGEgY3Vyc29yIG9yIGNhbGwgYGdldGA/CgogICAgICAgIGxldCBkaXIgPSB0aGlzLmdldFJlYWREaXJlY3Rpb24oKTsKICAgICAgICBpZiAoZGlyKSB7CiAgICAgICAgICAvLyBPcGVuIGEgY3Vyc29yCiAgICAgICAgICB0aGlzLnByZXZSZWFkcyA9IG51bGw7CgogICAgICAgICAgbGV0IGtleVJhbmdlOwogICAgICAgICAgaWYgKGRpciA9PT0gJ3ByZXYnKSB7CiAgICAgICAgICAgIGtleVJhbmdlID0gSURCS2V5UmFuZ2UudXBwZXJCb3VuZChwb3NpdGlvbik7CiAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICBrZXlSYW5nZSA9IElEQktleVJhbmdlLmxvd2VyQm91bmQocG9zaXRpb24pOwogICAgICAgICAgfQoKICAgICAgICAgIGxldCByZXEgPSB0aGlzLnN0b3JlLm9wZW5DdXJzb3Ioa2V5UmFuZ2UsIGRpcik7CiAgICAgICAgICByZWNvcmQoJ3N0cmVhbScpOwoKICAgICAgICAgIHJlcS5vbnN1Y2Nlc3MgPSBlID0+IHsKICAgICAgICAgICAgZW5kUmVjb3JkaW5nKCdzdHJlYW0nKTsKICAgICAgICAgICAgZW5kUmVjb3JkaW5nKCdzdHJlYW0tbmV4dCcpOwoKICAgICAgICAgICAgbGV0IGN1cnNvciA9IGUudGFyZ2V0LnJlc3VsdDsKICAgICAgICAgICAgdGhpcy5jdXJzb3IgPSBjdXJzb3I7CgogICAgICAgICAgICBpZiAodGhpcy5jdXJzb3JQcm9taXNlID09IG51bGwpIHsKICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0dvdCBkYXRhIGZyb20gY3Vyc29yIGJ1dCBub3RoaW5nIGlzIHdhaXRpbmcgaXQnKTsKICAgICAgICAgICAgfQogICAgICAgICAgICB0aGlzLmN1cnNvclByb21pc2UucmVzb2x2ZShjdXJzb3IgPyBjdXJzb3IudmFsdWUgOiBudWxsKTsKICAgICAgICAgICAgdGhpcy5jdXJzb3JQcm9taXNlID0gbnVsbDsKICAgICAgICAgIH07CiAgICAgICAgICByZXEub25lcnJvciA9IGUgPT4gewogICAgICAgICAgICBjb25zb2xlLmxvZygnQ3Vyc29yIGZhaWx1cmU6JywgZSk7CgogICAgICAgICAgICBpZiAodGhpcy5jdXJzb3JQcm9taXNlID09IG51bGwpIHsKICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0dvdCBkYXRhIGZyb20gY3Vyc29yIGJ1dCBub3RoaW5nIGlzIHdhaXRpbmcgaXQnKTsKICAgICAgICAgICAgfQogICAgICAgICAgICB0aGlzLmN1cnNvclByb21pc2UucmVqZWN0KGUpOwogICAgICAgICAgICB0aGlzLmN1cnNvclByb21pc2UgPSBudWxsOwogICAgICAgICAgfTsKCiAgICAgICAgICByZXR1cm4gd2FpdEN1cnNvcigpOwogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICBpZiAodGhpcy5wcmV2UmVhZHMgPT0gbnVsbCkgewogICAgICAgICAgICB0aGlzLnByZXZSZWFkcyA9IFswLCAwLCAwXTsKICAgICAgICAgIH0KICAgICAgICAgIHRoaXMucHJldlJlYWRzLnB1c2gocG9zaXRpb24pOwogICAgICAgICAgdGhpcy5wcmV2UmVhZHMuc2hpZnQoKTsKCiAgICAgICAgICByZXR1cm4gdGhpcy5nZXQocG9zaXRpb24pOwogICAgICAgIH0KICAgICAgfQogICAgfQoKICAgIGFzeW5jIHNldChpdGVtKSB7CiAgICAgIHRoaXMucHJldlJlYWRzID0gbnVsbDsKCiAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7CiAgICAgICAgbGV0IHJlcSA9IHRoaXMuc3RvcmUucHV0KGl0ZW0udmFsdWUsIGl0ZW0ua2V5KTsKICAgICAgICByZXEub25zdWNjZXNzID0gZSA9PiByZXNvbHZlKHJlcS5yZXN1bHQpOwogICAgICAgIHJlcS5vbmVycm9yID0gZSA9PiByZWplY3QoZSk7CiAgICAgIH0pOwogICAgfQoKICAgIGFzeW5jIGJ1bGtTZXQoaXRlbXMpIHsKICAgICAgdGhpcy5wcmV2UmVhZHMgPSBudWxsOwoKICAgICAgZm9yIChsZXQgaXRlbSBvZiBpdGVtcykgewogICAgICAgIHRoaXMuc3RvcmUucHV0KGl0ZW0udmFsdWUsIGl0ZW0ua2V5KTsKICAgICAgfQogICAgfQogIH0KCiAgYXN5bmMgZnVuY3Rpb24gbG9hZERiKG5hbWUpIHsKICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7CiAgICAgIGlmIChvcGVuRGJzLmdldChuYW1lKSkgewogICAgICAgIHJlc29sdmUob3BlbkRicy5nZXQobmFtZSkpOwogICAgICAgIHJldHVybjsKICAgICAgfQoKICAgICAgY29uc29sZS5sb2coJ29wZW5pbmcnLCBuYW1lKTsKCiAgICAgIGxldCByZXEgPSBnbG9iYWxUaGlzLmluZGV4ZWREQi5vcGVuKG5hbWUsIDEpOwogICAgICByZXEub25zdWNjZXNzID0gZXZlbnQgPT4gewogICAgICAgIGNvbnNvbGUubG9nKCdkYiBpcyBvcGVuIScsIG5hbWUpOwogICAgICAgIGxldCBkYiA9IGV2ZW50LnRhcmdldC5yZXN1bHQ7CgogICAgICAgIGRiLm9udmVyc2lvbmNoYW5nZSA9ICgpID0+IHsKICAgICAgICAgIC8vIFRPRE86IE5vdGlmeSB0aGUgdXNlciBzb21laG93CiAgICAgICAgICBjb25zb2xlLmxvZygnY2xvc2luZyBiZWNhdXNlIHZlcnNpb24gY2hhbmdlZCcpOwogICAgICAgICAgZGIuY2xvc2UoKTsKICAgICAgICAgIG9wZW5EYnMuZGVsZXRlKG5hbWUpOwogICAgICAgIH07CgogICAgICAgIGRiLm9uY2xvc2UgPSAoKSA9PiB7CiAgICAgICAgICBvcGVuRGJzLmRlbGV0ZShuYW1lKTsKICAgICAgICB9OwoKICAgICAgICBvcGVuRGJzLnNldChuYW1lLCBkYik7CiAgICAgICAgcmVzb2x2ZShkYik7CiAgICAgIH07CiAgICAgIHJlcS5vbnVwZ3JhZGVuZWVkZWQgPSBldmVudCA9PiB7CiAgICAgICAgbGV0IGRiID0gZXZlbnQudGFyZ2V0LnJlc3VsdDsKICAgICAgICBpZiAoIWRiLm9iamVjdFN0b3JlTmFtZXMuY29udGFpbnMoJ2RhdGEnKSkgewogICAgICAgICAgZGIuY3JlYXRlT2JqZWN0U3RvcmUoJ2RhdGEnKTsKICAgICAgICB9CiAgICAgIH07CiAgICAgIHJlcS5vbmJsb2NrZWQgPSBlID0+IGNvbnNvbGUubG9nKCdibG9ja2VkJywgZSk7CiAgICAgIHJlcS5vbmVycm9yID0gcmVxLm9uYWJvcnQgPSBlID0+IHJlamVjdChlLnRhcmdldC5lcnJvcik7CiAgICB9KTsKICB9CgogIGZ1bmN0aW9uIGNsb3NlRGIobmFtZSkgewogICAgbGV0IG9wZW5EYiA9IG9wZW5EYnMuZ2V0KG5hbWUpOwogICAgaWYgKG9wZW5EYikgewogICAgICBvcGVuRGIuY2xvc2UoKTsKICAgICAgb3BlbkRicy5kZWxldGUobmFtZSk7CiAgICB9CiAgfQoKICBmdW5jdGlvbiBnZXRUcmFuc2FjdGlvbihuYW1lKSB7CiAgICByZXR1cm4gdHJhbnNhY3Rpb25zLmdldChuYW1lKTsKICB9CgogIGFzeW5jIGZ1bmN0aW9uIHdpdGhUcmFuc2FjdGlvbihuYW1lLCBtb2RlLCBmdW5jKSB7CiAgICBsZXQgdHJhbnMgPSB0cmFuc2FjdGlvbnMuZ2V0KG5hbWUpOwogICAgaWYgKHRyYW5zKSB7CiAgICAgIC8vIElmIGEgdHJhbnNhY3Rpb24gYWxyZWFkeSBleGlzdHMsIHRoYXQgbWVhbnMgdGhlIGZpbGUgaGFzIGJlZW4KICAgICAgLy8gbG9ja2VkLiBXZSBkb24ndCBmdWxseSBzdXBwb3J0IGFyYml0cmFyeSBuZXN0ZWQgdHJhbnNhY3Rpb25zLAogICAgICAvLyBhcyBzZWVuIGJlbG93ICh3ZSB3b24ndCB1cGdyYWRlIGEgYHJlYWRvbmx5YCB0byBgcmVhZHdyaXRlYAogICAgICAvLyBhdXRvbWF0aWNhbGx5KSBhbmQgdGhpcyBpcyBtYWlubHkgZm9yIHRoZSB1c2UgY2FzZSB3aGVyZSBzcWxpdGUKICAgICAgLy8gbG9ja3MgdGhlIGRiIGFuZCBjcmVhdGVzIGEgdHJhbnNhY3Rpb24gZm9yIHRoZSBkdXJhY3Rpb24gb2YgdGhlCiAgICAgIC8vIGxvY2suIFdlIGRvbid0IGFjdHVhbGx5IHdyaXRlIGNvZGUgaW4gYSB3YXkgdGhhdCBhc3N1bWVzIG5lc3RlZAogICAgICAvLyB0cmFuc2FjdGlvbnMsIHNvIGp1c3QgZXJyb3IgaGVyZQogICAgICBpZiAobW9kZSA9PT0gJ3JlYWR3cml0ZScgJiYgdHJhbnMubG9ja1R5cGUgPT09IExPQ0tfVFlQRVMuU0hBUkVEKSB7CiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdBdHRlbXB0ZWQgd3JpdGUgYnV0IG9ubHkgaGFzIFNIQVJFRCBsb2NrJyk7CiAgICAgIH0KICAgICAgcmV0dXJuIGZ1bmModHJhbnMpOwogICAgfQoKICAgIC8vIE91dHNpZGUgdGhlIHNjb3BlIG9mIGEgbG9jaywgY3JlYXRlIGEgdGVtcG9yYXJ5IHRyYW5zYWN0aW9uCiAgICB0cmFucyA9IG5ldyBUcmFuc2FjdGlvbihhd2FpdCBsb2FkRGIobmFtZSksIG1vZGUpOwogICAgYXdhaXQgZnVuYyh0cmFucyk7CiAgICBhd2FpdCB0cmFucy53YWl0Q29tcGxldGUoKTsKICB9CgogIC8vIExvY2tpbmcgc3RyYXRlZ3k6CiAgLy8KICAvLyAqIFdlIG1hcCBzcWxpdGUncyBsb2NrcyBvbnRvIEluZGV4ZWREQidzIHRyYW5zYWN0aW9uIHNlbWFudGljcy4KICAvLyAgIFJlYWQgdHJhbnNhY3Rpb25zIG1heSBleGVjdXRlIGluIHBhcmFsbGVsLiBSZWFkL3dyaXRlCiAgLy8gICB0cmFuc2FjdGlvbnMgYXJlIHF1ZXVlZCB1cCBhbmQgd2FpdCB1bnRpbCBhbGwgcHJlY2VkaW5nCiAgLy8gICByZWFkIHRyYW5zYWN0aW9ucyBmaW5pc2ggZXhlY3V0aW5nLiBSZWFkIHRyYW5zYWN0aW9ucyBzdGFydGVkCiAgLy8gICBhZnRlciBhIHJlYWQvd3JpdGUgdHJhbnNhY3Rpb24gd2FpdCB1bnRpbCBpdCBpcyBmaW5pc2hlZC4KICAvLwogIC8vICogSURCIHRyYW5zYWN0aW9ucyB3aWxsIHdhaXQgZm9yZXZlciB1bnRpbCB0aGV5IGNhbiBleGVjdXRlIChmb3IKICAvLyAgIGV4YW1wbGUsIHRoZXkgbWF5IGJlIGJsb2NrZWQgb24gYSByZWFkL3dyaXRlIHRyYW5zYWN0aW9uKS4gV2UKICAvLyAgIGRvbid0IHdhbnQgdG8gYWxsb3cgc3FsaXRlIHRyYW5zYWN0aW9ucyB0byB3YWl0IGZvcmV2ZXIsIHNvCiAgLy8gICB3ZSBtYW51YWxseSB0aW1lb3V0IGlmIGEgdHJhbnNhY3Rpb24gdGFrZXMgdG9vIGxvbmcgdG8KICAvLyAgIHN0YXJ0IGV4ZWN1dGluZy4gVGhpcyBzaW11bGF0ZXMgdGhlIGJlaGF2aW9yIG9mIGEgc3FsaXRlCiAgLy8gICBiYWlsaW5nIGlmIGl0IGNhbid0IHJlcXVpcmUgYSBsb2NrLgogIC8vCiAgLy8gKiBBIFNIQVJFRCBsb2NrIHdhbnRzIHRvIHJlYWQgZnJvbSB0aGUgZGIuIFdlIHN0YXJ0IGEgcmVhZAogIC8vICAgdHJhbnNhY3Rpb24gYW5kIHJlYWQgdGhlIGZpcnN0IGJsb2NrLCBhbmQgaWYgd2UgcmVhZCBpdCB3aXRoaW4KICAvLyAgIDUwMG1zIHdlIGNvbnNpZGVyIHRoZSBsb2NrIHN1Y2Nlc3NmdWwuIE90aGVyd2lzZSB0aGUgbG9jawogIC8vICAgZmFpbGVkIGFuZCB3ZSByZXR1cm4gU1FMSVRFX0JVU1kuIChUaGVyZSdzIG5vIHBlcmYgZG93bnNpZGUKICAvLyAgIHRvIHJlYWRpbmcgdGhlIGZpcnN0IGJsb2NrIC0gaXQgaGFzIHRvIGJlIHJlYWQgYW55d2F5IHRvIGNoZWNrCiAgLy8gICBieXRlcyAyNC0zOSBmb3IgdGhlIGNoYW5nZSBjb3VudGVyKQogIC8vCiAgLy8gKiBBIFJFU0VSVkVEIGxvY2sgbWVhbnMgdGhlIGRiIHdhbnRzIHRvIHN0YXJ0IHdyaXRpbmcgKHRoaW5rIG9mCiAgLy8gICBgQkVHSU4gVFJBTlNBQ1RJT05gKS4gT25seSBvbmUgcHJvY2VzcyBjYW4gb2J0YWluIGEgUkVTRVJWRUQKICAvLyAgIGxvY2sgYXQgYSB0aW1lLCBidXQgbm9ybWFsbHkgc3FsaXRlIHN0aWxsIGxlYWRzIG5ldyByZWFkIGxvY2tzCiAgLy8gICBoYXBwZW4uIEl0IGlzbid0IHVudGlsIGFuIEVYQ0xVU0lWRSBsb2NrIGlzIGhlbGQgdGhhdCByZWFkcyBhcmUKICAvLyAgIGJsb2NrZWQuIEhvd2V2ZXIsIHNpbmNlIHdlIG5lZWQgdG8gZ3VhcmFudGVlIG9ubHkgb25lIFJFU0VSVkVECiAgLy8gICBsb2NrIGF0IG9uY2UgKG90aGVyd2lzZSBkYXRhIGNvdWxkIGNoYW5nZSBmcm9tIGFub3RoZXIgcHJvY2VzcwogIC8vICAgd2l0aGluIGEgdHJhbnNhY3Rpb24sIGNhdXNpbmcgZmF1bHR5IGNhY2hlcyBldGMpIHRoZSBzaW1wbGVzdAogIC8vICAgdGhpbmcgdG8gZG8gaXMgZ28gYWhlYWQgYW5kIGdyYWIgYSByZWFkL3dyaXRlIHRyYW5zYWN0aW9uIHRoYXQKICAvLyAgIHJlcHJlc2VudHMgdGhlIFJFU0VSVkVEIGxvY2suIFRoaXMgd2lsbCBibG9jayBhbGwgcmVhZHMgZnJvbQogIC8vICAgaGFwcGVuaW5nLCBhbmQgaXMgZXNzZW50aWFsbHkgdGhlIHNhbWUgYXMgYW4gRVhDTFVTSVZFIGxvY2suCiAgLy8KICAvLyAgICAgKiBUaGUgbWFpbiBwcm9ibGVtIGhlcmUgaXMgd2UgY2FuJ3QgInVwZ3JhZGUiIGEgYHJlYWRvbmx5YAogIC8vICAgICAgIHRyYW5zYWN0aW9uIHRvIGByZWFkd3JpdGVgLCBidXQgbmF0aXZlIHNxbGl0ZSBjYW4gdXBncmFkZSBhCiAgLy8gICAgICAgbG9jayBmcm9tIFNIQVJFRCB0byBSRVNFUlZFRC4gV2UgbmVlZCB0byBzdGFydCBhIG5ldwogIC8vICAgICAgIHRyYW5zYWN0aW9uIHRvIGRvIHNvLCBhbmQgYmVjYXVzZSBvZiB0aGF0IHRoZXJlIG1pZ2h0IGJlCiAgLy8gICAgICAgb3RoZXIgYHJlYWR3cml0ZWAgdHJhbnNhY3Rpb25zIHRoYXQgZ2V0IHJ1biBkdXJpbmcgdGhlCiAgLy8gICAgICAgInVwZ3JhZGUiIHdoaWNoIGludmFsaWRhdGVzIHRoZSB3aG9sZSBsb2NraW5nIHByb2Nlc3MgYW5kCiAgLy8gICAgICAgYW5kIGNvcnJ1cHRzIGRhdGEuCiAgLy8KICAvLyAqIElkZWFsbHksIHdlIGNvdWxkIHRlbGwgc3FsaXRlIHRvIHNraXAgU0hBUkVEIGxvY2tzIGVudGlyZWx5LiBXZQogIC8vICAgZG9uJ3QgbmVlZCB0aGVtIHNpbmNlIHdlIGNhbiByZWx5IG9uIEluZGV4ZWREQidzIHNlbWFudGljcy4KICAvLyAgIFRoZW4gd2hlbiBpdCB3YW50cyB0byBzdGFydCB3cml0aW5nLCB3ZSBnZXQgYSBSRVNFUlZFRCBsb2NrCiAgLy8gICB3aXRob3V0IGhhdmluZyB0byB1cGdyYWRlIGZyb20gU0hBUkVELiBUaGlzIHdvdWxkIHNhdmUgdXMKICAvLyAgIHRoZSBjb3N0IG9mIGEgYHJlYWRvbmx5YCB0cmFuc2FjdGlvbiB3aGVuIHdyaXRpbmc7IHJpZ2h0IG5vdwogIC8vICAgaXQgbXVzdCBvcGVuIGEgYHJlYWRvbmx5YCB0cmFuc2FjdGlvbiBhbmQgdGhlbiBpbW1lZGlhdGVseSBvcGVuCiAgLy8gICBhIGByZWFkd3JpdGVgIHRvIHVwZ3JhZGUgaXQuIEkgdGhvdWdodCBvZiBkZWZlcnJpbmcgb3BlbmluZyB0aGUKICAvLyAgIGByZWFkb25seWAgdHJhbnNhY3Rpb24gdW50aWwgc29tZXRoaW5nIGlzIGFjdHVhbGx5IHJlYWQsIGJ1dAogIC8vICAgdW5mb3J0dW5hdGVseSBzcWxpdGUgb3BlbnMgaXQsIHJlYWRzIHRoZSBmaXJzdCBibG9jaywgYW5kIHRoZW4KICAvLyAgIHVwZ3JhZGVzIGl0LiBTbyB0aGVyZSdzIG5vIHdheSBhcm91bmQgaXQuIChXZSBjYW4ndCBhc3N1bWUgaXQncwogIC8vICAgYSBgcmVhZHdyaXRlYCB0cmFuc2FjdGlvbiBhdCB0aGF0IHBvaW50IHNpbmNlIHRoYXQgd291bGQgYXNzdW1lCiAgLy8gICBhbGwgU0hBUkVEIGxvY2tzIGFyZSBgcmVhZHdyaXRlYCwgcmVtb3ZpbmcgdGhlIHBvc3NpYmlsaXR5IG9mCiAgLy8gICBjb25jdXJyZW50IHJlYWRzKS4KICAvLwogIC8vICogVXBncmFkaW5nIHRvIGFuIEVYQ0xVU0lWRSBsb2NrIGlzIGEgbm9vcCwgc2luY2Ugd2UgdHJlYXQgUkVTRVJWRUQKICAvLyAgIGxvY2tzIGFzIEVYQ0xVU0lWRS4KICBhc3luYyBmdW5jdGlvbiBoYW5kbGVMb2NrKHdyaXRlciwgbmFtZSwgbG9ja1R5cGUpIHsKICAgIC8vIGNvbnNvbGUubG9nKCdsb2NraW5nJywgbmFtZSwgbG9ja1R5cGUsIHBlcmZvcm1hbmNlLm5vdygpKTsKCiAgICBsZXQgdHJhbnMgPSB0cmFuc2FjdGlvbnMuZ2V0KG5hbWUpOwogICAgaWYgKHRyYW5zKSB7CiAgICAgIGlmIChsb2NrVHlwZSA+IHRyYW5zLmxvY2tUeXBlKSB7CiAgICAgICAgLy8gVXBncmFkZSBTSEFSRUQgdG8gRVhDTFVTSVZFCiAgICAgICAgYXNzZXJ0KAogICAgICAgICAgdHJhbnMubG9ja1R5cGUgPT09IExPQ0tfVFlQRVMuU0hBUkVELAogICAgICAgICAgYFVwcmFkaW5nIGxvY2sgdHlwZSBmcm9tICR7dHJhbnMubG9ja1R5cGV9IGlzIGludmFsaWRgCiAgICAgICAgKTsKICAgICAgICBhc3NlcnQoCiAgICAgICAgICBsb2NrVHlwZSA9PT0gTE9DS19UWVBFUy5SRVNFUlZFRCB8fCBsb2NrVHlwZSA9PT0gTE9DS19UWVBFUy5FWENMVVNJVkUsCiAgICAgICAgICBgVXBncmFkaW5nIGxvY2sgdHlwZSB0byAke2xvY2tUeXBlfSBpcyBpbnZhbGlkYAogICAgICAgICk7CgogICAgICAgIGxldCBzdWNjZXNzID0gYXdhaXQgdHJhbnMudXBncmFkZUV4Y2x1c2l2ZSgpOwogICAgICAgIHdyaXRlci5pbnQzMihzdWNjZXNzID8gMCA6IC0xKTsKICAgICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgICAgfSBlbHNlIHsKICAgICAgICAvLyBJZiBub3QgdXBncmFkaW5nIGFuZCB3ZSBhbHJlYWR5IGhhdmUgYSBsb2NrLCBtYWtlIHN1cmUgdGhpcwogICAgICAgIC8vIGlzbid0IGEgZG93bmdyYWRlCiAgICAgICAgYXNzZXJ0KAogICAgICAgICAgdHJhbnMubG9ja1R5cGUgPT09IGxvY2tUeXBlLAogICAgICAgICAgYERvd25ncmFkaW5nIGxvY2sgdG8gJHtsb2NrVHlwZX0gaXMgaW52YWxpZGAKICAgICAgICApOwoKICAgICAgICB3cml0ZXIuaW50MzIoMCk7CiAgICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICAgIH0KICAgIH0gZWxzZSB7CiAgICAgIGFzc2VydCgKICAgICAgICBsb2NrVHlwZSA9PT0gTE9DS19UWVBFUy5TSEFSRUQsCiAgICAgICAgYE5ldyBsb2NrcyBtdXN0IHN0YXJ0IGFzIFNIQVJFRCBpbnN0ZWFkIG9mICR7bG9ja1R5cGV9YAogICAgICApOwoKICAgICAgbGV0IHRyYW5zID0gbmV3IFRyYW5zYWN0aW9uKGF3YWl0IGxvYWREYihuYW1lKSk7CiAgICAgIGlmICgoYXdhaXQgdHJhbnMucHJlZmV0Y2hGaXJzdEJsb2NrKDUwMCkpID09IG51bGwpIDsKCiAgICAgIHRyYW5zYWN0aW9ucy5zZXQobmFtZSwgdHJhbnMpOwoKICAgICAgd3JpdGVyLmludDMyKDApOwogICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgIH0KICB9CgogIGFzeW5jIGZ1bmN0aW9uIGhhbmRsZVVubG9jayh3cml0ZXIsIG5hbWUsIGxvY2tUeXBlKSB7CiAgICAvLyBjb25zb2xlLmxvZygndW5sb2NraW5nJywgbmFtZSwgbG9ja1R5cGUsIHBlcmZvcm1hbmNlLm5vdygpKTsKCiAgICBsZXQgdHJhbnMgPSBnZXRUcmFuc2FjdGlvbihuYW1lKTsKCiAgICBpZiAobG9ja1R5cGUgPT09IExPQ0tfVFlQRVMuU0hBUkVEKSB7CiAgICAgIGlmICh0cmFucyA9PSBudWxsKSB7CiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdVbmxvY2sgZXJyb3IgKFNIQVJFRCk6IG5vIHRyYW5zYWN0aW9uIHJ1bm5pbmcnKTsKICAgICAgfQoKICAgICAgaWYgKHRyYW5zLmxvY2tUeXBlID09PSBMT0NLX1RZUEVTLkVYQ0xVU0lWRSkgewogICAgICAgIHRyYW5zLmRvd25ncmFkZVNoYXJlZCgpOwogICAgICB9CiAgICB9IGVsc2UgaWYgKGxvY2tUeXBlID09PSBMT0NLX1RZUEVTLk5PTkUpIHsKICAgICAgLy8gSSB0aG91Z2h0IHdlIGNvdWxkIGFzc3VtZSBhIGxvY2sgaXMgYWx3YXlzIG9wZW4gd2hlbiBgdW5sb2NrYAogICAgICAvLyBpcyBjYWxsZWQsIGJ1dCBpdCBhbHNvIGNhbGxzIGB1bmxvY2tgIHdoZW4gY2xvc2luZyB0aGUgZmlsZSBubwogICAgICAvLyBtYXR0ZXIgd2hhdC4gRG8gbm90aGluZyBpZiB0aGVyZSdzIG5vIGxvY2sgY3VycmVudGx5CiAgICAgIGlmICh0cmFucykgewogICAgICAgIC8vIFRPRE86IHRoaXMgaXMgd2hlcmUgYW4gZXJyb3IgY291bGQgYnViYmxlIHVwLiBIYW5kbGUgaXQKICAgICAgICBhd2FpdCB0cmFucy53YWl0Q29tcGxldGUoKTsKICAgICAgICB0cmFuc2FjdGlvbnMuZGVsZXRlKG5hbWUpOwogICAgICB9CiAgICB9CgogICAgd3JpdGVyLmludDMyKDApOwogICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgfQoKICBhc3luYyBmdW5jdGlvbiBoYW5kbGVSZWFkKHdyaXRlciwgbmFtZSwgcG9zaXRpb24pIHsKICAgIHJldHVybiB3aXRoVHJhbnNhY3Rpb24obmFtZSwgJ3JlYWRvbmx5JywgYXN5bmMgdHJhbnMgPT4gewogICAgICBsZXQgZGF0YSA9IGF3YWl0IHRyYW5zLnJlYWQocG9zaXRpb24pOwoKICAgICAgaWYgKGRhdGEgPT0gbnVsbCkgewogICAgICAgIHdyaXRlci5ieXRlcyhuZXcgQXJyYXlCdWZmZXIoMCkpOwogICAgICB9IGVsc2UgewogICAgICAgIHdyaXRlci5ieXRlcyhkYXRhKTsKICAgICAgfQogICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgIH0pOwogIH0KCiAgYXN5bmMgZnVuY3Rpb24gaGFuZGxlV3JpdGVzKHdyaXRlciwgbmFtZSwgd3JpdGVzKSB7CiAgICByZXR1cm4gd2l0aFRyYW5zYWN0aW9uKG5hbWUsICdyZWFkd3JpdGUnLCBhc3luYyB0cmFucyA9PiB7CiAgICAgIGF3YWl0IHRyYW5zLmJ1bGtTZXQod3JpdGVzLm1hcCh3ID0+ICh7IGtleTogdy5wb3MsIHZhbHVlOiB3LmRhdGEgfSkpKTsKCiAgICAgIHdyaXRlci5pbnQzMigwKTsKICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICB9KTsKICB9CgogIGFzeW5jIGZ1bmN0aW9uIGhhbmRsZVJlYWRNZXRhKHdyaXRlciwgbmFtZSkgewogICAgcmV0dXJuIHdpdGhUcmFuc2FjdGlvbihuYW1lLCAncmVhZG9ubHknLCBhc3luYyB0cmFucyA9PiB7CiAgICAgIHRyeSB7CiAgICAgICAgY29uc29sZS5sb2coJ1JlYWRpbmcgbWV0YScpOwogICAgICAgIGxldCByZXMgPSBhd2FpdCB0cmFucy5nZXQoLTEpOwogICAgICAgIGNvbnNvbGUubG9nKCdSZWFkaW5nIG1ldGEgKGRvbmUpJywgcmVzKTsKCiAgICAgICAgbGV0IG1ldGEgPSByZXM7CiAgICAgICAgd3JpdGVyLmludDMyKG1ldGEgPyBtZXRhLnNpemUgOiAtMSk7CiAgICAgICAgd3JpdGVyLmludDMyKG1ldGEgPyBtZXRhLmJsb2NrU2l6ZSA6IC0xKTsKICAgICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgICAgfSBjYXRjaCAoZXJyKSB7CiAgICAgICAgY29uc29sZS5sb2coZXJyKTsKICAgICAgICB3cml0ZXIuaW50MzIoLTEpOwogICAgICAgIHdyaXRlci5pbnQzMigtMSk7CiAgICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICAgIH0KICAgIH0pOwogIH0KCiAgYXN5bmMgZnVuY3Rpb24gaGFuZGxlV3JpdGVNZXRhKHdyaXRlciwgbmFtZSwgbWV0YSkgewogICAgcmV0dXJuIHdpdGhUcmFuc2FjdGlvbihuYW1lLCAncmVhZHdyaXRlJywgYXN5bmMgdHJhbnMgPT4gewogICAgICB0cnkgewogICAgICAgIGF3YWl0IHRyYW5zLnNldCh7IGtleTogLTEsIHZhbHVlOiBtZXRhIH0pOwoKICAgICAgICB3cml0ZXIuaW50MzIoMCk7CiAgICAgICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgICAgIH0gY2F0Y2ggKGVycikgewogICAgICAgIGNvbnNvbGUubG9nKGVycik7CiAgICAgICAgd3JpdGVyLmludDMyKC0xKTsKICAgICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgICAgfQogICAgfSk7CiAgfQoKICBhc3luYyBmdW5jdGlvbiBoYW5kbGVEZWxldGVGaWxlKHdyaXRlciwgbmFtZSkgewogICAgdHJ5IHsKICAgICAgY2xvc2VEYihuYW1lKTsKCiAgICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHsKICAgICAgICBsZXQgcmVxID0gZ2xvYmFsVGhpcy5pbmRleGVkREIuZGVsZXRlRGF0YWJhc2UobmFtZSk7CiAgICAgICAgcmVxLm9uc3VjY2VzcyA9IHJlc29sdmU7CiAgICAgICAgcmVxLm9uZXJyb3IgPSByZWplY3Q7CiAgICAgIH0pOwoKICAgICAgd3JpdGVyLmludDMyKDApOwogICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgIH0gY2F0Y2ggKGVycikgewogICAgICB3cml0ZXIuaW50MzIoLTEpOwogICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgIH0KICB9CgogIGFzeW5jIGZ1bmN0aW9uIGhhbmRsZUNsb3NlRmlsZSh3cml0ZXIsIG5hbWUpIHsKICAgIGNsb3NlRGIobmFtZSk7CgogICAgd3JpdGVyLmludDMyKDApOwogICAgd3JpdGVyLmZpbmFsaXplKCk7CiAgfQoKICAvLyBgbGlzdGVuYCBjb250aW51YWxseSBsaXN0ZW5zIGZvciByZXF1ZXN0cyB2aWEgdGhlIHNoYXJlZCBidWZmZXIuCiAgLy8gUmlnaHQgbm93IGl0J3MgaW1wbGVtZW50ZWQgaW4gYSB0YWlsLWNhbGwgc3R5bGUgKGBsaXN0ZW5gIGlzCiAgLy8gcmVjdXJzaXZlbHkgY2FsbGVkKSBiZWNhdXNlIEkgdGhvdWdodCB0aGF0IHdhcyBuZWNlc3NhcnkgZm9yCiAgLy8gdmFyaW91cyByZWFzb25zLiBXZSBjYW4gY29udmVydCB0aGlzIHRvIGEgYHdoaWxlKDEpYCBsb29wIHdpdGgKICAvLyBhbmQgdXNlIGBhd2FpdGAgdGhvdWdoCiAgYXN5bmMgZnVuY3Rpb24gbGlzdGVuKHJlYWRlciwgd3JpdGVyKSB7CiAgICBsZXQgbWV0aG9kID0gcmVhZGVyLnN0cmluZygpOwoKICAgIHN3aXRjaCAobWV0aG9kKSB7CiAgICAgIGNhc2UgJ3N0YXRzLXN0YXJ0JzogewogICAgICAgIHJlYWRlci5kb25lKCk7CgogICAgICAgIHN0YXJ0KCk7CgogICAgICAgIHdyaXRlci5pbnQzMigwKTsKICAgICAgICB3cml0ZXIuZmluYWxpemUoKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICdzdGF0cyc6IHsKICAgICAgICByZWFkZXIuZG9uZSgpOwoKICAgICAgICBhd2FpdCBlbmQoKTsKCiAgICAgICAgd3JpdGVyLmludDMyKDApOwogICAgICAgIHdyaXRlci5maW5hbGl6ZSgpOwogICAgICAgIGxpc3RlbihyZWFkZXIsIHdyaXRlcik7CiAgICAgICAgYnJlYWs7CiAgICAgIH0KCiAgICAgIGNhc2UgJ3dyaXRlQmxvY2tzJzogewogICAgICAgIGxldCBuYW1lID0gcmVhZGVyLnN0cmluZygpOwogICAgICAgIGxldCB3cml0ZXMgPSBbXTsKICAgICAgICB3aGlsZSAoIXJlYWRlci5kb25lKCkpIHsKICAgICAgICAgIGxldCBwb3MgPSByZWFkZXIuaW50MzIoKTsKICAgICAgICAgIGxldCBkYXRhID0gcmVhZGVyLmJ5dGVzKCk7CiAgICAgICAgICB3cml0ZXMucHVzaCh7IHBvcywgZGF0YSB9KTsKICAgICAgICB9CgogICAgICAgIGF3YWl0IGhhbmRsZVdyaXRlcyh3cml0ZXIsIG5hbWUsIHdyaXRlcyk7CiAgICAgICAgbGlzdGVuKHJlYWRlciwgd3JpdGVyKTsKICAgICAgICBicmVhazsKICAgICAgfQoKICAgICAgY2FzZSAncmVhZEJsb2NrJzogewogICAgICAgIGxldCBuYW1lID0gcmVhZGVyLnN0cmluZygpOwogICAgICAgIGxldCBwb3MgPSByZWFkZXIuaW50MzIoKTsKICAgICAgICByZWFkZXIuZG9uZSgpOwoKICAgICAgICBhd2FpdCBoYW5kbGVSZWFkKHdyaXRlciwgbmFtZSwgcG9zKTsKICAgICAgICBsaXN0ZW4ocmVhZGVyLCB3cml0ZXIpOwogICAgICAgIGJyZWFrOwogICAgICB9CgogICAgICBjYXNlICdyZWFkTWV0YSc6IHsKICAgICAgICBsZXQgbmFtZSA9IHJlYWRlci5zdHJpbmcoKTsKICAgICAgICByZWFkZXIuZG9uZSgpOwogICAgICAgIGF3YWl0IGhhbmRsZVJlYWRNZXRhKHdyaXRlciwgbmFtZSk7CiAgICAgICAgbGlzdGVuKHJlYWRlciwgd3JpdGVyKTsKICAgICAgICBicmVhazsKICAgICAgfQoKICAgICAgY2FzZSAnd3JpdGVNZXRhJzogewogICAgICAgIGxldCBuYW1lID0gcmVhZGVyLnN0cmluZygpOwogICAgICAgIGxldCBzaXplID0gcmVhZGVyLmludDMyKCk7CiAgICAgICAgbGV0IGJsb2NrU2l6ZSA9IHJlYWRlci5pbnQzMigpOwogICAgICAgIHJlYWRlci5kb25lKCk7CiAgICAgICAgYXdhaXQgaGFuZGxlV3JpdGVNZXRhKHdyaXRlciwgbmFtZSwgeyBzaXplLCBibG9ja1NpemUgfSk7CiAgICAgICAgbGlzdGVuKHJlYWRlciwgd3JpdGVyKTsKICAgICAgICBicmVhazsKICAgICAgfQoKICAgICAgY2FzZSAnZGVsZXRlRmlsZSc6IHsKICAgICAgICBsZXQgbmFtZSA9IHJlYWRlci5zdHJpbmcoKTsKICAgICAgICByZWFkZXIuZG9uZSgpOwoKICAgICAgICBhd2FpdCBoYW5kbGVEZWxldGVGaWxlKHdyaXRlciwgbmFtZSk7CiAgICAgICAgbGlzdGVuKHJlYWRlciwgd3JpdGVyKTsKICAgICAgICBicmVhazsKICAgICAgfQoKICAgICAgY2FzZSAnY2xvc2VGaWxlJzogewogICAgICAgIGxldCBuYW1lID0gcmVhZGVyLnN0cmluZygpOwogICAgICAgIHJlYWRlci5kb25lKCk7CgogICAgICAgIGF3YWl0IGhhbmRsZUNsb3NlRmlsZSh3cml0ZXIsIG5hbWUpOwogICAgICAgIGxpc3RlbihyZWFkZXIsIHdyaXRlcik7CiAgICAgICAgYnJlYWs7CiAgICAgIH0KCiAgICAgIGNhc2UgJ2xvY2tGaWxlJzogewogICAgICAgIGxldCBuYW1lID0gcmVhZGVyLnN0cmluZygpOwogICAgICAgIGxldCBsb2NrVHlwZSA9IHJlYWRlci5pbnQzMigpOwogICAgICAgIHJlYWRlci5kb25lKCk7CgogICAgICAgIGF3YWl0IGhhbmRsZUxvY2sod3JpdGVyLCBuYW1lLCBsb2NrVHlwZSk7CiAgICAgICAgbGlzdGVuKHJlYWRlciwgd3JpdGVyKTsKICAgICAgICBicmVhazsKICAgICAgfQoKICAgICAgY2FzZSAndW5sb2NrRmlsZSc6IHsKICAgICAgICBsZXQgbmFtZSA9IHJlYWRlci5zdHJpbmcoKTsKICAgICAgICBsZXQgbG9ja1R5cGUgPSByZWFkZXIuaW50MzIoKTsKICAgICAgICByZWFkZXIuZG9uZSgpOwoKICAgICAgICBhd2FpdCBoYW5kbGVVbmxvY2sod3JpdGVyLCBuYW1lLCBsb2NrVHlwZSk7CiAgICAgICAgbGlzdGVuKHJlYWRlciwgd3JpdGVyKTsKICAgICAgICBicmVhazsKICAgICAgfQoKICAgICAgZGVmYXVsdDoKICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1Vua25vd24gbWV0aG9kOiAnICsgbWV0aG9kKTsKICAgIH0KICB9CgogIHNlbGYub25tZXNzYWdlID0gbXNnID0+IHsKICAgIHN3aXRjaCAobXNnLmRhdGEudHlwZSkgewogICAgICBjYXNlICdpbml0JzogewogICAgICAgIHBvc3RNZXNzYWdlKHsgdHlwZTogJ3dvcmtlci1yZWFkeScgfSk7CiAgICAgICAgbGV0IFthcmdCdWZmZXIsIHJlc3VsdEJ1ZmZlcl0gPSBtc2cuZGF0YS5idWZmZXJzOwogICAgICAgIGxldCByZWFkZXIgPSBuZXcgUmVhZGVyKGFyZ0J1ZmZlciwgeyBuYW1lOiAnYXJncycsIGRlYnVnOiBmYWxzZSB9KTsKICAgICAgICBsZXQgd3JpdGVyID0gbmV3IFdyaXRlcihyZXN1bHRCdWZmZXIsIHsgbmFtZTogJ3Jlc3VsdHMnLCBkZWJ1ZzogZmFsc2UgfSk7CiAgICAgICAgbGlzdGVuKHJlYWRlciwgd3JpdGVyKTsKICAgICAgICBicmVhazsKICAgICAgfQogICAgfQogIH07Cgp9KCkpOwoK', null, false); +/* eslint-enable */ + +let workerReady = null; + +function isWorker() { + return ( + typeof WorkerGlobalScope !== 'undefined' && + self instanceof WorkerGlobalScope + ); +} + +function startWorker(argBuffer, resultBuffer) { + if (workerReady) { + return workerReady; + } + + let onReady; + workerReady = new Promise(resolve => (onReady = resolve)); + + if (typeof Worker === 'undefined') { + // No `Worker` available - this context does not support nested + // workers sadly. We need to proxy creating a worker to the main + // thread. + if (!isWorker()) { + // We're on the main thread? Weird: it doesn't have workers + throw new Error( + 'Web workers not available, even from the main thread. sqlite3 requires web workers to work.' + ); + } + + self.postMessage({ + type: 'spawn-idb-worker', + argBuffer, + resultBuffer + }); + + self.addEventListener('message', e => { + if (e.data.type === 'worker-ready') { + onReady(); + } + }); + } else { + let worker = new WorkerFactory(); + + // This is another way to load the worker. It won't be inlined + // into the script, which might be better for debugging, but makes + // it more difficult to distribute. + // let worker = new Worker(new URL('./indexeddb.worker.js', import.meta.url)); + + worker.postMessage({ type: 'init', buffers: [argBuffer, resultBuffer] }); + + worker.onmessage = msg => { + if (msg.data.type === 'worker-ready') { + onReady(); + } + }; + + return workerReady; + } +} + +// 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 'stats-start': { + writer.string('stats-start'); + writer.finalize(); + reader.int32(); + reader.done(); + break; + } + + case 'stats': { + writer.string('stats'); + writer.finalize(); + reader.int32(); + reader.done(); + break; + } + + 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; + } + + startStats() { + return invokeWorker('stats-start'); + } + + stats() { + return invokeWorker('stats'); + } + + 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 + }); + } +} + +class IndexedDBBackend { + constructor(defaultBlockSize) { + this.defaultBlockSize = defaultBlockSize; + } + + async init() { + let argBuffer = new SharedArrayBuffer(4096 * 9); + 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(argBuffer, resultBuffer); + } + + createFile(filename) { + return new File(filename, this.defaultBlockSize, new FileOps(filename)); + } +} + +export default IndexedDBBackend; diff --git a/dist/perf/memory-backend.js b/dist/perf/memory-backend.js new file mode 100644 index 0000000..84ee511 --- /dev/null +++ b/dist/perf/memory-backend.js @@ -0,0 +1,570 @@ +let token = ''; +let sheetId = '1p1isUZkWe8oc12LL0kqaT3UFT_MR8vEoEieEruHW-xE'; + +let buffer = 40000; +let baseTime; +let timings = {}; + +let range$1 = 'A3'; + +const descriptions = { + get: 'Calls to `store.get`', + 'stream-next': 'Advancing a cursor', + stream: 'Opening a cursor', + read: 'Full process for reading a block' +}; + +function last(arr) { + return arr.length === 0 ? null : arr[arr.length - 1]; +} + +function percentile(data, p) { + let sorted = [...data]; + sorted.sort((n1, n2) => n1[1] - n2[1]); + return sorted.slice(0, Math.ceil(sorted.length * p) | 0); +} + +let showWarning = true; + +async function writeData(sheetName, data) { + let arr = percentile(data, 0.95); + + if (arr.length > buffer) { + arr = arr.slice(-buffer); + } else { + while (arr.length < buffer) { + arr.push(['', '']); + } + } + + let res = await fetch( + `https://sheets.googleapis.com/v4/spreadsheets/${sheetId}/values/${sheetName}!${range$1}?valueInputOption=USER_ENTERED`, + { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}` + }, + body: JSON.stringify({ values: arr }) + } + ); + if (res.status == 200) { + console.log(`Logged timings to spreadsheet (${sheetName}))`); + } else { + if (showWarning) { + showWarning = false; + console.warn( + 'Unable to log perf data to spreadsheet. Is the OAuth token expired?' + ); + } + + console.log(`--- ${sheetName} (${descriptions[sheetName]}) ---`); + console.log(`Count: ${data.length}`); + console.log(`p50: ${last(percentile(data, 0.5))[1]}`); + console.log(`p95: ${last(percentile(data, 0.95))[1]}`); + } +} + +async function end() { + await Promise.all( + Object.keys(timings).map(name => { + let timing = timings[name]; + return writeData(name, timing.data.map(x => [x.start + x.took, x.took])); + }) + ); +} + +function start() { + timings = {}; + baseTime = performance.now(); +} + +function record(name) { + if (timings[name] == null) { + timings[name] = { start: null, data: [] }; + } + let timer = timings[name]; + + if (timer.start != null) { + throw new Error(`timer already started ${name}`); + } + timer.start = performance.now(); +} + +function endRecording(name) { + let now = performance.now(); + let timer = timings[name]; + + if (timer && timer.start != null) { + let took = now - timer.start; + let start = timer.start - baseTime; + timer.start = null; + + if (timer.data.length < buffer) { + timer.data.push({ start, took }); + } + } +} + +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; + } + + 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; + } + + 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; + } + + startStats() { + start(); + this.ops.startStats(); + } + + stats() { + end(); + this.ops.stats(); + } +} + +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/package.json b/package.json index df95862..fad5c1e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "absurd-sql.js-backend", - "version": "0.0.7", + "version": "0.0.8", "main": "./dist/index.js", "scripts": { "build": "rm -r dist && rollup -c rollup.config.js", @@ -11,6 +11,9 @@ "dependencies": {}, "devDependencies": { "@babel/plugin-transform-modules-commonjs": "^7.14.5", + "@jlongster/sql.js": "1.6.0", + "@observablehq/plot": "^0.1.0", + "@rollup/plugin-node-resolve": "^13.0.4", "babel": "^6.23.0", "detect-browser": "^5.2.0", "eslint": "^7.29.0", @@ -20,7 +23,6 @@ "rollup": "^2.53.1", "rollup-plugin-extensions": "^0.1.0", "rollup-plugin-web-worker-loader": "^1.6.1", - "@jlongster/sql.js": "1.6.0", "uuid": "^8.3.2", "webpack": "^5.41.1", "webpack-cli": "^4.7.2", diff --git a/rollup.config.js b/rollup.config.js index 8188a5a..745dd32 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,6 +1,7 @@ import webWorkerLoader from 'rollup-plugin-web-worker-loader'; +import { nodeResolve } from '@rollup/plugin-node-resolve'; -function getConfig(entry, filename) { +function getConfig(entry, filename, perf) { return { input: entry, output: { @@ -12,6 +13,9 @@ function getConfig(entry, filename) { webWorkerLoader({ pattern: /.*\/worker\.js/, targetPlatform: 'browser' + }), + nodeResolve({ + extensions: (perf ? ['.dev.js'] : []).concat(['.js']) }) ] }; @@ -20,5 +24,8 @@ function getConfig(entry, filename) { export default [ getConfig('src/index.js', 'index.js'), getConfig('src/memory/backend.js', 'memory-backend.js'), - getConfig('src/indexeddb/backend.js', 'indexeddb-backend.js') + getConfig('src/indexeddb/backend.js', 'indexeddb-backend.js'), + getConfig('src/index.js', 'perf/index.js', true), + getConfig('src/memory/backend.js', 'perf/memory-backend.js', true), + getConfig('src/indexeddb/backend.js', 'perf/indexeddb-backend.js', true) ]; diff --git a/src/blocked-file.js b/src/blocked-file.js index 9d643a9..f82f58b 100644 --- a/src/blocked-file.js +++ b/src/blocked-file.js @@ -136,6 +136,7 @@ export class File { close() { this.fsync(); + this.ops.close() } delete() { diff --git a/src/examples/large-data/index.html b/src/examples/large-data/index.html index 7612da2..2785476 100644 --- a/src/examples/large-data/index.html +++ b/src/examples/large-data/index.html @@ -72,6 +72,7 @@ +
diff --git a/src/examples/large-data/main.js b/src/examples/large-data/main.js index 899baed..2607787 100644 --- a/src/examples/large-data/main.js +++ b/src/examples/large-data/main.js @@ -1,9 +1,11 @@ -import { supportNestedWorkers } from '../..'; +import { initBackend } from '../../indexeddb/main-thread'; let worker; function init() { worker = new Worker(new URL('./main.worker.js', import.meta.url)); + initBackend(worker); + worker.postMessage({ type: 'ui-invoke', name: 'init' }); let output = document.querySelector('.output'); @@ -25,11 +27,9 @@ function init() { worker.postMessage({ type: 'options', name, value }); }); } - - supportNestedWorkers(worker); } -let methods = ['init', 'populate', 'countAll', 'randomReads']; +let methods = ['init', 'populate', 'countAll', 'randomReads', 'deleteFile']; for (let method of methods) { let btn = document.querySelector(`#${method}`); diff --git a/src/examples/large-data/main.worker.js b/src/examples/large-data/main.worker.js index 899db0a..9e59848 100644 --- a/src/examples/large-data/main.worker.js +++ b/src/examples/large-data/main.worker.js @@ -1,4 +1,4 @@ -import initSqlJs from '@jlongster/sql.js'; +import initSqlJs from '@jlongster/sql.js/dist/sql-wasm-debug.js'; import { BlockedFS } from '../..'; import * as uuid from 'uuid'; import MemoryBackend from '../../memory/backend'; @@ -197,7 +197,7 @@ async function randomReads() { throw err; } - for (let i = 0; i < 20; i++) { + for (let i = 0; i < 8; i++) { let off = i * 10000; stmt.bind([off]); output('Using offset: ' + formatNumber(off)); @@ -227,11 +227,29 @@ async function randomReads() { file.stats(); } +async function deleteFile() { + await init(); + let filepath = `/blocked/${getDBName()}`; + + let exists = true; + try { + SQL.FS.stat(filepath); + } catch (e) { + exists = false; + } + + if (exists) { + SQL.FS.unlink(filepath); + } + _db = null; +} + let methods = { init, populate, countAll, - randomReads + randomReads, + deleteFile }; if (typeof self !== 'undefined') { diff --git a/src/examples/webpack.config.js b/src/examples/webpack.config.js index d7d50ea..1e117de 100644 --- a/src/examples/webpack.config.js +++ b/src/examples/webpack.config.js @@ -36,4 +36,4 @@ function getConfig(name, entry, html) { module.exports = [ getConfig('large-data', './large-data/main.js', './large-data/index.html'), getConfig('fts', './fts/main.js', './fts/index.html') - ] +]; diff --git a/src/index.js b/src/index.js index 2c09b18..ed7f8cd 100644 --- a/src/index.js +++ b/src/index.js @@ -1,9 +1,4 @@ import _BlockedFS from './blocked-fs'; -import { supportNestedWorkers as _supportNestedWorkers } from './indexeddb/start-indexeddb-worker'; // Right now we don't support `export from` so we do this manually -// -// TODO: This isn't packaged up the best. There will be duplicate code -// across bundles and we need to separate things better export const BlockedFS = _BlockedFS; -export const supportNestedWorkers = _supportNestedWorkers; diff --git a/src/indexeddb/backend.js b/src/indexeddb/backend.js index ffdf1da..60ce449 100644 --- a/src/indexeddb/backend.js +++ b/src/indexeddb/backend.js @@ -1,6 +1,5 @@ import { Reader, Writer } from './shared-channel'; import { File } from '../blocked-file'; -import { startWorker } from './start-indexeddb-worker'; // These are temporarily global, but will be easy to clean up later let reader, writer; @@ -103,6 +102,16 @@ function invokeWorker(method, args) { 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); @@ -153,7 +162,11 @@ class FileOps { } delete() { - invokeWorker('deleteFile', { name: this.getStoreName() }); + return invokeWorker('deleteFile', { name: this.getStoreName() }); + } + + close() { + return invokeWorker('closeFile', { name: this.getStoreName() }); } readMeta() { @@ -194,6 +207,25 @@ class FileOps { } } +function startWorker(argBuffer, resultBuffer) { + let onReady; + let workerReady = new Promise(resolve => (onReady = resolve)); + + self.postMessage({ + type: 'spawn-idb-worker', + argBuffer, + resultBuffer + }); + + self.addEventListener('message', e => { + if (e.data.type === 'worker-ready') { + onReady(); + } + }); + + return workerReady; +} + export default class IndexedDBBackend { constructor(defaultBlockSize) { this.defaultBlockSize = defaultBlockSize; diff --git a/src/indexeddb/main-thread.js b/src/indexeddb/main-thread.js new file mode 100644 index 0000000..1bb12a0 --- /dev/null +++ b/src/indexeddb/main-thread.js @@ -0,0 +1,267 @@ +import IndexedDBWorker from './worker.js'; +import * as Plot from '@observablehq/plot'; + +let workerReady = null; + +function isWorker() { + return ( + typeof WorkerGlobalScope !== 'undefined' && + self instanceof WorkerGlobalScope + ); +} + +function percentile(data, p) { + let sorted = [...data]; + sorted.sort((n1, n2) => n1.y - n2.y); + return sorted.slice(0, Math.ceil(sorted.length * p) | 0); +} + +function percentilePoint(data, p) { + let result = percentile(data, p); + return result[result.length - 1]; +} + +function fixed(num, places) { + let factor = Math.pow(10, places); + let clipped = (num * factor) | 0; + return clipped / factor; +} + +function makeDataPoint(label, value) { + let p = document.createElement('div'); + p.innerHTML = `${label} ${value}`; + p.style.marginRight = '10px'; + p.style.border = '1px solid #C3D0FF'; + p.style.borderRadius = '6px'; + p.style.padding = '1px 5px'; + return p; +} + +let loggedResults = []; +let cleanupTimer; + +function clearPerfResults() { + let m = document.querySelector('.perf-results .ready'); + if (m) { + m.style.display = 'none'; + } + loggedResults = []; +} + +function appendPerfResults(name, data) { + // We track which data has been output, and after a certain time + // assue things have settled and remove any stale data. We do this + // instead of clearing everything when a new recording starts so + // that it keeps the current state (like scroll position) which is + // nice if you are watching and graph and wanting to compare it + // across runs + loggedResults.push(name); + clearTimeout(cleanupTimer); + cleanupTimer = setTimeout(() => { + for (let el of document.querySelectorAll('.data')) { + if (!loggedResults.includes(el.dataset.dataName)) { + el.remove(); + } + } + }, 500); + + let c = document.querySelector('.perf-results'); + if (!c) { + c = document.createElement('div'); + c.className = 'perf-results'; + c.style.maxHeight = 'calc(100vh - 10px)'; + c.style.padding = '15px'; + c.style.margin = '5px'; + c.style.boxSizing = 'border-box'; + c.style.position = 'fixed'; + c.style.top = 0; + c.style.right = 0; + c.style.display = 'flex'; + c.style.flexDirection = 'column'; + c.style.alignItems = 'flex-end'; + c.style.backgroundColor = '#E3F0FF'; + + let btnDiv = document.createElement('div'); + let msg = document.createElement('span'); + msg.className = 'ready'; + msg.textContent = 'Perf results are ready!'; + msg.style.marginRight = '15px'; + btnDiv.appendChild(msg); + + let btn = document.createElement('button'); + btn.textContent = 'open'; + + btn.addEventListener('click', e => { + let r = document.querySelector('.perf-results .results'); + if (r.style.display === 'none') { + r.style.display = 'flex'; + e.target.textContent = 'close'; + } else { + r.style.display = 'none'; + e.target.textContent = 'open'; + } + }); + + btnDiv.appendChild(btn); + c.appendChild(btnDiv); + + document.body.appendChild(c); + } + + let r = c.querySelector('.results'); + if (!r) { + let rc = document.createElement('div'); + rc.style.overflow = 'auto'; + + r = document.createElement('div'); + r.className = 'results'; + r.style.display = 'none'; + r.style.flex = '1'; + r.style.flexDirection = 'column'; + + rc.appendChild(r); + c.appendChild(rc); + } + + let m = document.querySelector('.perf-results .ready'); + m.style.display = 'inline'; + + let svgCont = document.createElement('div'); + svgCont.className = 'data ' + name; + svgCont.dataset.dataName = name; + svgCont.style.marginTop = '15px'; + + let text = document.createElement('div'); + text.style.font = '13px system-ui, sans-serif'; + text.style.display = 'flex'; + text.style.justifyContent = 'space-between'; + + let label = document.createElement('div'); + label.textContent = name; + text.appendChild(label); + + let spacer = document.createElement('div'); + spacer.style.flex = '1'; + text.appendChild(spacer); + + text.appendChild( + makeDataPoint('total', fixed(data.reduce((t, n) => t + n.y, 0), 3)) + ); + text.appendChild(makeDataPoint('count', data.length)); + text.appendChild( + makeDataPoint('p50', fixed(percentilePoint(data, 0.5).y, 3)) + ); + text.appendChild( + makeDataPoint('p95', fixed(percentilePoint(data, 0.95).y, 3)) + ); + + svgCont.appendChild(text); + + let svg = Plot.plot({ + y: { grid: true, label: 'took (ms)', labelOffset: 40, inset: 10 }, + x: { grid: true, label: 'run time (ms)', labelOffset: 40 }, + marginTop: 30, + marginLeft: 50, + marginRight: 30, + marginBottom: 50, + marks: [ + Plot.dot(percentile(data, 0.95), { + x: 'x', + y: 'y', + r: 2, + fill: '#1271BF', + fillOpacity: Math.max(1 - Math.min(data.length / 500, 0.8), 0.1) + }) + ] + }); + svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); + svg.setAttribute('version', '1.1'); + svg.setAttribute('preserveAspectRatio', 'none'); + svg.style.font = '13px system-ui, sans-serif'; + svg.style.backgroundColor = 'white'; + svg.style.marginTop = '5px'; + + let scale = 1.5; + svg.setAttribute('width', 640 * (scale / 2)); + svg.setAttribute('height', 400 * (scale / 2)); + + svgCont.appendChild(svg); + + let existing = r.querySelector('.data.' + name); + console.log(name, existing); + if (existing) { + existing.parentNode.replaceChild(svgCont, existing); + } else { + r.appendChild(svgCont); + } +} + +function listenForPerfData(worker) { + worker.addEventListener('message', msg => { + switch (msg.data.type) { + case 'clear-perf': { + clearPerfResults(); + break; + } + case 'log-perf': { + appendPerfResults(msg.data.name, msg.data.data); + break; + } + } + }); +} + +function startWorkerFromMain(argBuffer, resultBuffer) { + if (workerReady) { + return workerReady; + } + + 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.' + ); + } + + let onReady; + workerReady = new Promise(resolve => (onReady = resolve)); + + let worker = new IndexedDBWorker(); + + // This is another way to load the worker. It won't be inlined + // into the script, which might be better for debugging, but makes + // it more difficult to distribute. + // let worker = new Worker(new URL('./indexeddb.worker.js', import.meta.url)); + + worker.postMessage({ type: 'init', buffers: [argBuffer, resultBuffer] }); + + worker.addEventListener('message', msg => { + switch (msg.data.type) { + case 'worker-ready': + onReady(); + break; + } + }); + + listenForPerfData(worker); + + return workerReady; +} + +export function initBackend(worker) { + listenForPerfData(worker); + + worker.addEventListener('message', e => { + if (e.data.type === 'spawn-idb-worker') { + startWorkerFromMain(e.data.argBuffer, e.data.resultBuffer).then(() => { + worker.postMessage({ type: 'worker-ready' }); + }); + } + }); +} diff --git a/src/indexeddb/start-indexeddb-worker.js b/src/indexeddb/start-indexeddb-worker.js deleted file mode 100644 index 72a6d0d..0000000 --- a/src/indexeddb/start-indexeddb-worker.js +++ /dev/null @@ -1,73 +0,0 @@ -import IndexedDBWorker from './worker.js'; - -let workerReady = null; - -function isWorker() { - return ( - typeof WorkerGlobalScope !== 'undefined' && - self instanceof WorkerGlobalScope - ); -} - -export function startWorker(argBuffer, resultBuffer) { - if (workerReady) { - return workerReady; - } - - let onReady; - workerReady = new Promise(resolve => (onReady = resolve)); - - if (typeof Worker === 'undefined') { - // No `Worker` available - this context does not support nested - // workers sadly. We need to proxy creating a worker to the main - // thread. - if (!isWorker()) { - // We're on the main thread? Weird: it doesn't have workers - throw new Error( - 'Web workers not available, even from the main thread. sqlite3 requires web workers to work.' - ); - } - - self.postMessage({ - type: 'spawn-idb-worker', - argBuffer, - resultBuffer - }); - - self.addEventListener('message', e => { - if (e.data.type === 'worker-ready') { - onReady(); - } - }); - } else { - let worker = new IndexedDBWorker(); - - // This is another way to load the worker. It won't be inlined - // into the script, which might be better for debugging, but makes - // it more difficult to distribute. - // let worker = new Worker(new URL('./indexeddb.worker.js', import.meta.url)); - - worker.postMessage({ type: 'init', buffers: [argBuffer, resultBuffer] }); - - worker.onmessage = msg => { - if (msg.data.type === 'worker-ready') { - onReady(); - } - }; - - return workerReady; - } -} - -// This is called from the main thread to setup a proxy for spawning -// workers. It's necessary for browsers that don't support spawning -// workers from workers (only Safari). -export function supportNestedWorkers(worker) { - worker.addEventListener('message', e => { - if (e.data.type === 'spawn-idb-worker') { - startWorker(e.data.argBuffer, e.data.resultBuffer).then(() => { - worker.postMessage({ type: 'worker-ready' }); - }); - } - }); -} diff --git a/src/indexeddb/worker.js b/src/indexeddb/worker.js index e90c48d..0913fb6 100644 --- a/src/indexeddb/worker.js +++ b/src/indexeddb/worker.js @@ -313,6 +313,8 @@ async function loadDb(name) { return; } + console.log('opening', name); + let req = globalThis.indexedDB.open(name, 1); req.onsuccess = event => { console.log('db is open!', name); @@ -322,6 +324,7 @@ async function loadDb(name) { // TODO: Notify the user somehow console.log('closing because version changed'); db.close(); + openDbs.delete(name); }; db.onclose = () => { @@ -342,6 +345,14 @@ async function loadDb(name) { }); } +function closeDb(name) { + let openDb = openDbs.get(name); + if (openDb) { + openDb.close(); + openDbs.delete(name); + } +} + function getTransaction(name) { return transactions.get(name); } @@ -559,7 +570,26 @@ async function handleWriteMeta(writer, name, meta) { } async function handleDeleteFile(writer, name) { - // TODO: Handle this + try { + closeDb(name); + + await new Promise((resolve, reject) => { + let req = globalThis.indexedDB.deleteDatabase(name); + req.onsuccess = resolve; + req.onerror = reject; + }); + + writer.int32(0); + writer.finalize(); + } catch (err) { + writer.int32(-1); + writer.finalize(); + } +} + +async function handleCloseFile(writer, name) { + closeDb(name); + writer.int32(0); writer.finalize(); } @@ -646,6 +676,15 @@ async function listen(reader, writer) { break; } + case 'closeFile': { + let name = reader.string(); + reader.done(); + + await handleCloseFile(writer, name); + listen(reader, writer); + break; + } + case 'lockFile': { let name = reader.string(); let lockType = reader.int32(); diff --git a/src/memory/backend.js b/src/memory/backend.js index cd982de..de0e069 100644 --- a/src/memory/backend.js +++ b/src/memory/backend.js @@ -16,6 +16,10 @@ class FileOps { return true; } + close() { + return true; + } + delete() { // in-memory noop } diff --git a/src/perf-frontend.dev.js b/src/perf-frontend.dev.js new file mode 100644 index 0000000..6065142 --- /dev/null +++ b/src/perf-frontend.dev.js @@ -0,0 +1,202 @@ +import * as Plot from '@observablehq/plot'; + +function percentile(data, p) { + let sorted = [...data]; + sorted.sort((n1, n2) => n1.y - n2.y); + return sorted.slice(0, Math.ceil(sorted.length * p) | 0); +} + +function percentilePoint(data, p) { + let result = percentile(data, p); + return result[result.length - 1]; +} + +function fixed(num, places) { + let factor = Math.pow(10, places); + let clipped = (num * factor) | 0; + return clipped / factor; +} + +function makeDataPoint(label, value) { + let p = document.createElement('div'); + p.innerHTML = `${label} ${value}`; + p.style.marginRight = '10px'; + p.style.border = '1px solid #C3D0FF'; + p.style.borderRadius = '6px'; + p.style.padding = '1px 5px'; + return p; +} + +let loggedResults = []; +let cleanupTimer; + +function clearPerfResults() { + let m = document.querySelector('.perf-results .ready'); + if (m) { + m.style.display = 'none'; + } + loggedResults = []; +} + +function appendPerfResults(name, data) { + // We track which data has been output, and after a certain time + // assue things have settled and remove any stale data. We do this + // instead of clearing everything when a new recording starts so + // that it keeps the current state (like scroll position) which is + // nice if you are watching and graph and wanting to compare it + // across runs + loggedResults.push(name); + clearTimeout(cleanupTimer); + cleanupTimer = setTimeout(() => { + for (let el of document.querySelectorAll('.data')) { + if (!loggedResults.includes(el.dataset.dataName)) { + el.remove(); + } + } + }, 500); + + let c = document.querySelector('.perf-results'); + if (!c) { + c = document.createElement('div'); + c.className = 'perf-results'; + c.style.maxHeight = 'calc(100vh - 10px)'; + c.style.padding = '15px'; + c.style.margin = '5px'; + c.style.boxSizing = 'border-box'; + c.style.position = 'fixed'; + c.style.top = 0; + c.style.right = 0; + c.style.display = 'flex'; + c.style.flexDirection = 'column'; + c.style.alignItems = 'flex-end'; + c.style.backgroundColor = '#E3F0FF'; + + let btnDiv = document.createElement('div'); + let msg = document.createElement('span'); + msg.className = 'ready'; + msg.textContent = 'Perf results are ready!'; + msg.style.marginRight = '15px'; + btnDiv.appendChild(msg); + + let btn = document.createElement('button'); + btn.textContent = 'open'; + + btn.addEventListener('click', e => { + let r = document.querySelector('.perf-results .results'); + if (r.style.display === 'none') { + r.style.display = 'flex'; + e.target.textContent = 'close'; + } else { + r.style.display = 'none'; + e.target.textContent = 'open'; + } + }); + + btnDiv.appendChild(btn); + c.appendChild(btnDiv); + + document.body.appendChild(c); + } + + let r = c.querySelector('.results'); + if (!r) { + let rc = document.createElement('div'); + rc.style.overflow = 'auto'; + + r = document.createElement('div'); + r.className = 'results'; + r.style.display = 'none'; + r.style.flex = '1'; + r.style.flexDirection = 'column'; + + rc.appendChild(r); + c.appendChild(rc); + } + + let m = document.querySelector('.perf-results .ready'); + m.style.display = 'inline'; + + let svgCont = document.createElement('div'); + svgCont.className = 'data ' + name; + svgCont.dataset.dataName = name; + svgCont.style.marginTop = '15px'; + + let text = document.createElement('div'); + text.style.font = '13px system-ui, sans-serif'; + text.style.display = 'flex'; + text.style.justifyContent = 'space-between'; + + let label = document.createElement('div'); + label.textContent = name; + text.appendChild(label); + + let spacer = document.createElement('div'); + spacer.style.flex = '1'; + text.appendChild(spacer); + + text.appendChild( + makeDataPoint('total', fixed(data.reduce((t, n) => t + n.y, 0), 3)) + ); + text.appendChild(makeDataPoint('count', data.length)); + text.appendChild( + makeDataPoint('p50', fixed(percentilePoint(data, 0.5).y, 3)) + ); + text.appendChild( + makeDataPoint('p95', fixed(percentilePoint(data, 0.95).y, 3)) + ); + + svgCont.appendChild(text); + + let svg = Plot.plot({ + y: { grid: true, label: 'took (ms)', labelOffset: 40, inset: 10 }, + x: { grid: true, label: 'run time (ms)', labelOffset: 40 }, + marginTop: 30, + marginLeft: 50, + marginRight: 30, + marginBottom: 50, + marks: [ + Plot.dot(percentile(data, 0.95), { + x: 'x', + y: 'y', + r: 2, + fill: '#1271BF', + fillOpacity: Math.max(1 - Math.min(data.length / 500, 0.8), 0.1) + }) + ] + }); + svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); + svg.setAttribute('version', '1.1'); + svg.setAttribute('preserveAspectRatio', 'none'); + svg.style.font = '13px system-ui, sans-serif'; + svg.style.backgroundColor = 'white'; + svg.style.marginTop = '5px'; + + let scale = 1.5; + svg.setAttribute('width', 640 * (scale / 2)); + svg.setAttribute('height', 400 * (scale / 2)); + + svgCont.appendChild(svg); + + let existing = r.querySelector('.data.' + name); + console.log(name, existing); + if (existing) { + existing.parentNode.replaceChild(svgCont, existing); + } else { + r.appendChild(svgCont); + } +} + +function listenForPerfData(worker) { + worker.addEventListener('message', msg => { + switch (msg.data.type) { + case 'clear-perf': { + clearPerfResults(); + break; + } + case 'log-perf': { + appendPerfResults(msg.data.name, msg.data.data); + break; + } + } + }); +} diff --git a/src/perf-frontend.js b/src/perf-frontend.js new file mode 100644 index 0000000..2c95f25 --- /dev/null +++ b/src/perf-frontend.js @@ -0,0 +1,2 @@ +// Noops in prod +export function listenForPerfData(worker) {} diff --git a/src/perf.dev.js b/src/perf.dev.js index ef049cd..63d6685 100644 --- a/src/perf.dev.js +++ b/src/perf.dev.js @@ -1,25 +1,7 @@ -import { detect } from 'detect-browser'; - -const browser = detect(); - -let token = ''; -let sheetId = '1p1isUZkWe8oc12LL0kqaT3UFT_MR8vEoEieEruHW-xE'; - let buffer = 40000; let baseTime; let timings = {}; -let range; -if (browser.name === 'chrome') { - range = 'A3'; -} else if (browser.name === 'safari') { - range = 'D3'; -} else if (browser.name === 'firefox') { - range = 'G3'; -} else { - throw new Error('Unknown browser: ' + browser.name); -} - const descriptions = { get: 'Calls to `store.get`', 'stream-next': 'Advancing a cursor', @@ -31,63 +13,32 @@ function last(arr) { return arr.length === 0 ? null : arr[arr.length - 1]; } -function percentile(data, p) { - let sorted = [...data]; - sorted.sort((n1, n2) => n1[1] - n2[1]); - return sorted.slice(0, Math.ceil(sorted.length * p) | 0); -} - let showWarning = true; -async function writeData(sheetName, data) { - let arr = percentile(data, 0.95); - - if (arr.length > buffer) { - arr = arr.slice(-buffer); - } else { - while (arr.length < buffer) { - arr.push(['', '']); - } - } - - let res = await fetch( - `https://sheets.googleapis.com/v4/spreadsheets/${sheetId}/values/${sheetName}!${range}?valueInputOption=USER_ENTERED`, - { - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${token}` - }, - body: JSON.stringify({ values: arr }) - } - ); - if (res.status == 200) { - console.log(`Logged timings to spreadsheet (${sheetName}))`); - } else { - if (showWarning) { - showWarning = false; - console.warn( - 'Unable to log perf data to spreadsheet. Is the OAuth token expired?' - ); - } +async function writeData(name, data) { + self.postMessage({ type: 'log-perf', name, data }); - console.log(`--- ${sheetName} (${descriptions[sheetName]}) ---`); - console.log(`Count: ${data.length}`); - console.log(`p50: ${last(percentile(data, 0.5))[1]}`); - console.log(`p95: ${last(percentile(data, 0.95))[1]}`); - } + // console.log(`--- ${sheetName} (${descriptions[sheetName]}) ---`); + // console.log(`Count: ${data.length}`); + // console.log(`p50: ${last(percentile(data, 0.5))[1]}`); + // console.log(`p95: ${last(percentile(data, 0.95))[1]}`); } export async function end() { await Promise.all( Object.keys(timings).map(name => { let timing = timings[name]; - return writeData(name, timing.data.map(x => [x.start + x.took, x.took])); + return writeData( + name, + timing.data.map(x => ({ x: x.start + x.took, y: x.took })) + ); }) ); } export function start() { + self.postMessage({ type: 'clear-perf' }); + timings = {}; baseTime = performance.now(); } diff --git a/yarn.lock b/yarn.lock index d6707b7..41a5295 100644 --- a/yarn.lock +++ b/yarn.lock @@ -529,6 +529,35 @@ resolved "https://registry.yarnpkg.com/@jlongster/sql.js/-/sql.js-1.6.0.tgz#fad6d5a4fec07c4ef835a4ad5724394ec1304998" integrity sha512-WWUuQkTA8B18tx2rv9vINDc7jnU1rTMzfjonyMqdXAejwSH8mhyw/vGNqxXLQxMVgZH+Lo2wVm+0Wb6rfWLNJA== +"@observablehq/plot@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@observablehq/plot/-/plot-0.1.0.tgz#d55b4c26b2982557af13db48bf0c802342cbfeac" + integrity sha512-jHlEGT02qjsbu/tCJjTeVybQkDx33jn20C1UtIB4ecwyDOgdo28QMKVhFHLbDkjrGYZEpTjuMq+N2x5sxRi2lw== + dependencies: + d3 "^6.7.0" + isoformat "^0.1.0" + +"@rollup/plugin-node-resolve@^13.0.4": + version "13.0.4" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.0.4.tgz#b10222f4145a019740acb7738402130d848660c0" + integrity sha512-eYq4TFy40O8hjeDs+sIxEH/jc9lyuI2k9DM557WN6rO5OpnC2qXMBNj4IKH1oHrnAazL49C5p0tgP0/VpqJ+/w== + dependencies: + "@rollup/pluginutils" "^3.1.0" + "@types/resolve" "1.17.1" + builtin-modules "^3.1.0" + deepmerge "^4.2.2" + is-module "^1.0.0" + resolve "^1.19.0" + +"@rollup/pluginutils@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" + integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== + dependencies: + "@types/estree" "0.0.39" + estree-walker "^1.0.1" + picomatch "^2.2.2" + "@sinonjs/commons@^1.7.0": version "1.8.3" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" @@ -602,6 +631,11 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.48.tgz#18dc8091b285df90db2f25aa7d906cfc394b7f74" integrity sha512-LfZwXoGUDo0C3me81HXgkBg5CTQYb6xzEl+fNmbO4JdRiSKQ8A0GD1OBBvKAIsbCUgoyAty7m99GqqMQe784ew== +"@types/estree@0.0.39": + version "0.0.39" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" + integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== + "@types/glob@^7.1.1": version "7.1.3" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183" @@ -661,6 +695,13 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.3.0.tgz#2e8332cc7363f887d32ec5496b207d26ba8052bb" integrity sha512-hkc1DATxFLQo4VxPDpMH1gCkPpBbpOoJ/4nhuXw4n63/0R6bCpQECj4+K226UJ4JO/eJQz+1mC2I7JsWanAdQw== +"@types/resolve@1.17.1": + version "1.17.1" + resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" + integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw== + dependencies: + "@types/node" "*" + "@types/stack-utils@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff" @@ -1272,6 +1313,11 @@ buffer-indexof@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== +builtin-modules@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.2.0.tgz#45d5db99e7ee5e6bc4f362e008bf917ab5049887" + integrity sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA== + bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" @@ -1487,7 +1533,7 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" -commander@^2.20.0: +commander@2, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -1629,6 +1675,250 @@ cssstyle@^2.3.0: dependencies: cssom "~0.3.6" +d3-array@2, d3-array@^2.3.0, d3-array@^2.5.0: + version "2.12.1" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.12.1.tgz#e20b41aafcdffdf5d50928004ececf815a465e81" + integrity sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ== + dependencies: + internmap "^1.0.0" + +d3-axis@2: + version "2.1.0" + resolved "https://registry.yarnpkg.com/d3-axis/-/d3-axis-2.1.0.tgz#978db534092711117d032fad5d733d206307f6a0" + integrity sha512-z/G2TQMyuf0X3qP+Mh+2PimoJD41VOCjViJzT0BHeL/+JQAofkiWZbWxlwFGb1N8EN+Cl/CW+MUKbVzr1689Cw== + +d3-brush@2: + version "2.1.0" + resolved "https://registry.yarnpkg.com/d3-brush/-/d3-brush-2.1.0.tgz#adadfbb104e8937af142e9a6e2028326f0471065" + integrity sha512-cHLLAFatBATyIKqZOkk/mDHUbzne2B3ZwxkzMHvFTCZCmLaXDpZRihQSn8UNXTkGD/3lb/W2sQz0etAftmHMJQ== + dependencies: + d3-dispatch "1 - 2" + d3-drag "2" + d3-interpolate "1 - 2" + d3-selection "2" + d3-transition "2" + +d3-chord@2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-chord/-/d3-chord-2.0.0.tgz#32491b5665391180560f738e5c1ccd1e3c47ebae" + integrity sha512-D5PZb7EDsRNdGU4SsjQyKhja8Zgu+SHZfUSO5Ls8Wsn+jsAKUUGkcshLxMg9HDFxG3KqavGWaWkJ8EpU8ojuig== + dependencies: + d3-path "1 - 2" + +"d3-color@1 - 2", d3-color@2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-2.0.0.tgz#8d625cab42ed9b8f601a1760a389f7ea9189d62e" + integrity sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ== + +d3-contour@2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-contour/-/d3-contour-2.0.0.tgz#80ee834988563e3bea9d99ddde72c0f8c089ea40" + integrity sha512-9unAtvIaNk06UwqBmvsdHX7CZ+NPDZnn8TtNH1myW93pWJkhsV25JcgnYAu0Ck5Veb1DHiCv++Ic5uvJ+h50JA== + dependencies: + d3-array "2" + +d3-delaunay@5: + version "5.3.0" + resolved "https://registry.yarnpkg.com/d3-delaunay/-/d3-delaunay-5.3.0.tgz#b47f05c38f854a4e7b3cea80e0bb12e57398772d" + integrity sha512-amALSrOllWVLaHTnDLHwMIiz0d1bBu9gZXd1FiLfXf8sHcX9jrcj81TVZOqD4UX7MgBZZ07c8GxzEgBpJqc74w== + dependencies: + delaunator "4" + +"d3-dispatch@1 - 2", d3-dispatch@2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-2.0.0.tgz#8a18e16f76dd3fcaef42163c97b926aa9b55e7cf" + integrity sha512-S/m2VsXI7gAti2pBoLClFFTMOO1HTtT0j99AuXLoGFKO6deHDdnv6ZGTxSTTUTgO1zVcv82fCOtDjYK4EECmWA== + +d3-drag@2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-2.0.0.tgz#9eaf046ce9ed1c25c88661911c1d5a4d8eb7ea6d" + integrity sha512-g9y9WbMnF5uqB9qKqwIIa/921RYWzlUDv9Jl1/yONQwxbOfszAWTCm8u7HOTgJgRDXiRZN56cHT9pd24dmXs8w== + dependencies: + d3-dispatch "1 - 2" + d3-selection "2" + +"d3-dsv@1 - 2", d3-dsv@2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-2.0.0.tgz#b37b194b6df42da513a120d913ad1be22b5fe7c5" + integrity sha512-E+Pn8UJYx9mViuIUkoc93gJGGYut6mSDKy2+XaPwccwkRGlR+LO97L2VCCRjQivTwLHkSnAJG7yo00BWY6QM+w== + dependencies: + commander "2" + iconv-lite "0.4" + rw "1" + +"d3-ease@1 - 2", d3-ease@2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-2.0.0.tgz#fd1762bfca00dae4bacea504b1d628ff290ac563" + integrity sha512-68/n9JWarxXkOWMshcT5IcjbB+agblQUaIsbnXmrzejn2O82n3p2A9R2zEB9HIEFWKFwPAEDDN8gR0VdSAyyAQ== + +d3-fetch@2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-fetch/-/d3-fetch-2.0.0.tgz#ecd7ef2128d9847a3b41b548fec80918d645c064" + integrity sha512-TkYv/hjXgCryBeNKiclrwqZH7Nb+GaOwo3Neg24ZVWA3MKB+Rd+BY84Nh6tmNEMcjUik1CSUWjXYndmeO6F7sw== + dependencies: + d3-dsv "1 - 2" + +d3-force@2: + version "2.1.1" + resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-2.1.1.tgz#f20ccbf1e6c9e80add1926f09b51f686a8bc0937" + integrity sha512-nAuHEzBqMvpFVMf9OX75d00OxvOXdxY+xECIXjW6Gv8BRrXu6gAWbv/9XKrvfJ5i5DCokDW7RYE50LRoK092ew== + dependencies: + d3-dispatch "1 - 2" + d3-quadtree "1 - 2" + d3-timer "1 - 2" + +"d3-format@1 - 2", d3-format@2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-2.0.0.tgz#a10bcc0f986c372b729ba447382413aabf5b0767" + integrity sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA== + +d3-geo@2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-2.0.2.tgz#c065c1b71fe8c5f1be657e5f43d9bdd010383c40" + integrity sha512-8pM1WGMLGFuhq9S+FpPURxic+gKzjluCD/CHTuUF3mXMeiCo0i6R0tO1s4+GArRFde96SLcW/kOFRjoAosPsFA== + dependencies: + d3-array "^2.5.0" + +d3-hierarchy@2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-2.0.0.tgz#dab88a58ca3e7a1bc6cab390e89667fcc6d20218" + integrity sha512-SwIdqM3HxQX2214EG9GTjgmCc/mbSx4mQBn+DuEETubhOw6/U3fmnji4uCVrmzOydMHSO1nZle5gh6HB/wdOzw== + +"d3-interpolate@1 - 2", "d3-interpolate@1.2.0 - 2", d3-interpolate@2: + version "2.0.1" + resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-2.0.1.tgz#98be499cfb8a3b94d4ff616900501a64abc91163" + integrity sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ== + dependencies: + d3-color "1 - 2" + +"d3-path@1 - 2", d3-path@2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-2.0.0.tgz#55d86ac131a0548adae241eebfb56b4582dd09d8" + integrity sha512-ZwZQxKhBnv9yHaiWd6ZU4x5BtCQ7pXszEV9CU6kRgwIQVQGLMv1oiL4M+MK/n79sYzsj+gcgpPQSctJUsLN7fA== + +d3-polygon@2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-polygon/-/d3-polygon-2.0.0.tgz#13608ef042fbec625ba1598327564f03c0396d8e" + integrity sha512-MsexrCK38cTGermELs0cO1d79DcTsQRN7IWMJKczD/2kBjzNXxLUWP33qRF6VDpiLV/4EI4r6Gs0DAWQkE8pSQ== + +"d3-quadtree@1 - 2", d3-quadtree@2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-2.0.0.tgz#edbad045cef88701f6fee3aee8e93fb332d30f9d" + integrity sha512-b0Ed2t1UUalJpc3qXzKi+cPGxeXRr4KU9YSlocN74aTzp6R/Ud43t79yLLqxHRWZfsvWXmbDWPpoENK1K539xw== + +d3-random@2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/d3-random/-/d3-random-2.2.2.tgz#5eebd209ef4e45a2b362b019c1fb21c2c98cbb6e" + integrity sha512-0D9P8TRj6qDAtHhRQn6EfdOtHMfsUWanl3yb/84C4DqpZ+VsgfI5iTVRNRbELCfNvRfpMr8OrqqUTQ6ANGCijw== + +d3-scale-chromatic@2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-scale-chromatic/-/d3-scale-chromatic-2.0.0.tgz#c13f3af86685ff91323dc2f0ebd2dabbd72d8bab" + integrity sha512-LLqy7dJSL8yDy7NRmf6xSlsFZ6zYvJ4BcWFE4zBrOPnQERv9zj24ohnXKRbyi9YHnYV+HN1oEO3iFK971/gkzA== + dependencies: + d3-color "1 - 2" + d3-interpolate "1 - 2" + +d3-scale@3: + version "3.3.0" + resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-3.3.0.tgz#28c600b29f47e5b9cd2df9749c206727966203f3" + integrity sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ== + dependencies: + d3-array "^2.3.0" + d3-format "1 - 2" + d3-interpolate "1.2.0 - 2" + d3-time "^2.1.1" + d3-time-format "2 - 3" + +d3-selection@2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-2.0.0.tgz#94a11638ea2141b7565f883780dabc7ef6a61066" + integrity sha512-XoGGqhLUN/W14NmaqcO/bb1nqjDAw5WtSYb2X8wiuQWvSZUsUVYsOSkOybUrNvcBjaywBdYPy03eXHMXjk9nZA== + +d3-shape@2: + version "2.1.0" + resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-2.1.0.tgz#3b6a82ccafbc45de55b57fcf956c584ded3b666f" + integrity sha512-PnjUqfM2PpskbSLTJvAzp2Wv4CZsnAgTfcVRTwW03QR3MkXF8Uo7B1y/lWkAsmbKwuecto++4NlsYcvYpXpTHA== + dependencies: + d3-path "1 - 2" + +"d3-time-format@2 - 3", d3-time-format@3: + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-3.0.0.tgz#df8056c83659e01f20ac5da5fdeae7c08d5f1bb6" + integrity sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag== + dependencies: + d3-time "1 - 2" + +"d3-time@1 - 2", d3-time@2, d3-time@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-2.1.1.tgz#e9d8a8a88691f4548e68ca085e5ff956724a6682" + integrity sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ== + dependencies: + d3-array "2" + +"d3-timer@1 - 2", d3-timer@2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-2.0.0.tgz#055edb1d170cfe31ab2da8968deee940b56623e6" + integrity sha512-TO4VLh0/420Y/9dO3+f9abDEFYeCUr2WZRlxJvbp4HPTQcSylXNiL6yZa9FIUvV1yRiFufl1bszTCLDqv9PWNA== + +d3-transition@2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-2.0.0.tgz#366ef70c22ef88d1e34105f507516991a291c94c" + integrity sha512-42ltAGgJesfQE3u9LuuBHNbGrI/AJjNL2OAUdclE70UE6Vy239GCBEYD38uBPoLeNsOhFStGpPI0BAOV+HMxog== + dependencies: + d3-color "1 - 2" + d3-dispatch "1 - 2" + d3-ease "1 - 2" + d3-interpolate "1 - 2" + d3-timer "1 - 2" + +d3-zoom@2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-2.0.0.tgz#f04d0afd05518becce879d04709c47ecd93fba54" + integrity sha512-fFg7aoaEm9/jf+qfstak0IYpnesZLiMX6GZvXtUSdv8RH2o4E2qeelgdU09eKS6wGuiGMfcnMI0nTIqWzRHGpw== + dependencies: + d3-dispatch "1 - 2" + d3-drag "2" + d3-interpolate "1 - 2" + d3-selection "2" + d3-transition "2" + +d3@^6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/d3/-/d3-6.7.0.tgz#adac458597b4a2cafe8e08cf30948af0c95cd61f" + integrity sha512-hNHRhe+yCDLUG6Q2LwvR/WdNFPOJQ5VWqsJcwIYVeI401+d2/rrCjxSXkiAdIlpx7/73eApFB4Olsmh3YN7a6g== + dependencies: + d3-array "2" + d3-axis "2" + d3-brush "2" + d3-chord "2" + d3-color "2" + d3-contour "2" + d3-delaunay "5" + d3-dispatch "2" + d3-drag "2" + d3-dsv "2" + d3-ease "2" + d3-fetch "2" + d3-force "2" + d3-format "2" + d3-geo "2" + d3-hierarchy "2" + d3-interpolate "2" + d3-path "2" + d3-polygon "2" + d3-quadtree "2" + d3-random "2" + d3-scale "3" + d3-scale-chromatic "2" + d3-selection "2" + d3-shape "2" + d3-time "2" + d3-time-format "3" + d3-timer "2" + d3-transition "2" + d3-zoom "2" + data-urls@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" @@ -1751,6 +2041,11 @@ del@^4.1.1: pify "^4.0.1" rimraf "^2.6.3" +delaunator@4: + version "4.0.1" + resolved "https://registry.yarnpkg.com/delaunator/-/delaunator-4.0.1.tgz#3d779687f57919a7a418f8ab947d3bddb6846957" + integrity sha512-WNPWi1IRKZfCt/qIDMfERkDp93+iZEmOxN2yy4Jg+Xhv8SLk2UTqqbe1sfiipn0and9QrE914/ihdx82Y/Giag== + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -2089,6 +2384,11 @@ estraverse@^5.1.0, estraverse@^5.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== +estree-walker@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" + integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -2722,7 +3022,7 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -iconv-lite@0.4.24: +iconv-lite@0.4, iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -2789,6 +3089,11 @@ internal-ip@^4.3.0: default-gateway "^4.2.0" ipaddr.js "^1.9.0" +internmap@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/internmap/-/internmap-1.0.1.tgz#0017cc8a3b99605f0302f2b198d272e015e5df95" + integrity sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw== + interpret@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" @@ -2944,6 +3249,11 @@ is-glob@^4.0.0, is-glob@^4.0.1: dependencies: is-extglob "^2.1.1" +is-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" + integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= + is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -3042,6 +3352,11 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= +isoformat@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/isoformat/-/isoformat-0.1.0.tgz#b693c1c9ee9ab02f1af5af41ceeae52bf501b233" + integrity sha512-4wCSk50Ov1PKbZ2m+YN0rUgQfF4NRkIavbhpW1mANEqD9HxBZ+j/fWk8hERq1yxn+CfWqvOac4m9axLuF0NfEw== + istanbul-lib-coverage@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" @@ -4237,7 +4552,7 @@ 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= -picomatch@^2.0.4, picomatch@^2.2.3: +picomatch@^2.0.4, picomatch@^2.2.2, picomatch@^2.2.3: version "2.3.0" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== @@ -4572,7 +4887,7 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.20.0, resolve@^1.9.0: +resolve@^1.19.0, resolve@^1.20.0, resolve@^1.9.0: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -4621,6 +4936,11 @@ rollup@^2.53.1: optionalDependencies: fsevents "~2.3.2" +rw@1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4" + integrity sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q= + safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"