UNPKG

wal

Version:

An implementation of write-ahead logging (WAL) for nodejs.

327 lines (291 loc) 31.9 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _assertPlus = require('assert-plus'); var _assertPlus2 = _interopRequireDefault(_assertPlus); var _ranfile = require('ranfile'); var _ranfile2 = _interopRequireDefault(_ranfile); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /****************************************************************************** The log index file is a side-file that tracks the byte offsets to each distinct log entry. Using the side-file indexing scheme, we are able to efficiently store and retrieve each distinct log entry without having to care about the entry's byte content. In this scheme, log-serial-numbers (LSNs) are the natural integer index at which the log entry was written, as if being written to an array. Layout: marker -- 4 marker bytes identifying the file's type: IDX$ base -- 4 bytes integer (big-endian); an index offset, usually 0 (zero). Offsets are only meaningful when indexes are split across many files so are a placeholder for future work. head -- 4 bytes integer (big-endian); the write head (next unused entry) commit -- 4 bytes integer (big-endian); the commit head (may trail head) In this layout there are 16 header bytes (hlen), therefore each subsequent 4 bytes contains a pointer to a log entry's starting byte in the indexed write- ahead-log (WAL). hlen + ((index - base) * sizeof(int)) Example (octets)... Base Head Commit Index 0 Index 1 v v v V v 49 44 58 24 00 00 00 00 00 00 00 02 00 00 00 01 00 00 00 00 00 00 00 58 \ Index 2 v 00 00 02 1f Log Entry 0 begins at byte 0 Log Entry 1 begins at byte 88 Log Entry 2 begins at byte 543 ******************************************************************************/ var MARKER = 'IDX$'; var OFS_MARKER = 0; var OFS_BASEINDEX = 4; var OFS_HEAD = 8; var OFS_COMMIT = 12; var HLEN = 16; var SIZEOF_INT = 4; var DEF_COMMIT = -1; var $file = Symbol('file'); var $header = Symbol('header'); function allocFill(len) { return Buffer.alloc ? Buffer.alloc(len) : new Buffer(len); // deprecated in 5.x+ } var LogIndexFile = function () { function LogIndexFile(file) { _classCallCheck(this, LogIndexFile); _assertPlus2.default.object(file, 'file'); _assertPlus2.default.func(file.read, 'file.read'); _assertPlus2.default.func(file.write, 'file.write'); _assertPlus2.default.func(file.close, 'file.close'); if (file.size < HLEN) { throw new Error('File too small to be an index file.'); } this[$file] = file; } _createClass(LogIndexFile, [{ key: 'isCommitted', value: function isCommitted(index) { _assertPlus2.default.ok(this[$header], 'index must be open'); return index < this.commitHead; } }, { key: 'commit', value: function commit(index) { _assertPlus2.default.ok(this[$header], 'index must be open'); _assertPlus2.default.number(index, 'index'); var expected = this.commitHead + 1; if (index < expected) { return Promise.resolve(index); } if (index !== expected) { return Promise.reject(new Error('Out of order commit; expected ' + expected + ' but received ' + index + '.')); } this[$header].writeInt32BE(index, OFS_COMMIT, true); return this[$file].write(OFS_COMMIT, this[$header], OFS_COMMIT, SIZEOF_INT).then(function () { return index; }); } }, { key: 'localizeIndex', value: function localizeIndex(index) { _assertPlus2.default.number(index, 'index'); index = index - this.baseIndex; return index; } }, { key: 'globalizeIndex', value: function globalizeIndex(index) { _assertPlus2.default.number(index, 'index'); index = index + this.baseIndex; return index; } }, { key: 'offset', value: function offset(index) { _assertPlus2.default.ok(index <= this.head, 'index out of range'); _assertPlus2.default.ok(this[$header], 'index must be open'); var ofs = HLEN + this.localizeIndex(index) * SIZEOF_INT; return this[$file].read(ofs, SIZEOF_INT).then(function (data) { var offset = data.readInt32BE(0, SIZEOF_INT); return offset; }); } }, { key: 'get', value: function get(index) { _assertPlus2.default.number(index, 'index'); _assertPlus2.default.ok(index < this.head, 'index out of range'); _assertPlus2.default.ok(this[$header], 'index must be open'); var ofs = HLEN + this.localizeIndex(index) * SIZEOF_INT; return this[$file].read(ofs, 2 * SIZEOF_INT).then(function (data) { var offset = data.readInt32BE(0, SIZEOF_INT); return { offset: offset, length: data.readInt32BE(SIZEOF_INT, SIZEOF_INT) - offset }; }); } }, { key: 'getRange', value: function getRange(index, count) { _assertPlus2.default.number(index, 'index'); _assertPlus2.default.ok(index < this.head, 'index out of range'); _assertPlus2.default.number(count, 'count'); _assertPlus2.default.ok(this[$header], 'index must be open'); // head points at the next entry var limit = this.head - index; _assertPlus2.default.ok(count <= limit, 'count puts index out of range'); var ofs = HLEN + this.localizeIndex(index) * SIZEOF_INT; return this[$file].read(ofs, (count + 1) * SIZEOF_INT).then(function (data) { var i = -1; var result = []; while (++i < count) { var offset = data.readInt32BE(i * SIZEOF_INT, SIZEOF_INT); result.push({ index: index + i, offset: offset, length: data.readInt32BE((i + 1) * SIZEOF_INT, SIZEOF_INT) - offset }); } return result; }); } }, { key: 'truncate', value: function truncate(from) { var _this = this; _assertPlus2.default.number(from, 'from'); _assertPlus2.default.ok(from > this.commitHead, 'cannot truncate a committed log entry'); _assertPlus2.default.ok(from < this.head, 'index out of range'); var header = this[$header]; header.writeInt32BE(from, OFS_HEAD, SIZEOF_INT); return this[$file].write(OFS_HEAD, header, OFS_HEAD, SIZEOF_INT).then(function () { if (_this.head === _this.baseIndex) { return _this[$file].read(HLEN, SIZEOF_INT).then(function (data) { return { offset: data.readInt32BE(0, SIZEOF_INT), length: 0 }; }); } return _this.get(_this.head - 1); }).then(function (rec) { return rec.offset + rec.length; }); } }, { key: 'increment', value: function increment(offset) { var _this2 = this; _assertPlus2.default.ok(this[$header], 'index must be open'); // write the offset into the next index location (typically at EOF), // then update and write the header reference, which completes the // increment. var header = this[$header]; var current = this.head; var next = current + 1; var ofs = HLEN + this.localizeIndex(next) * SIZEOF_INT; var data = allocFill(SIZEOF_INT); data.writeInt32BE(offset, 0, SIZEOF_INT); return this[$file].write(ofs, data).then(function () { header.writeInt32BE(next, OFS_HEAD, SIZEOF_INT); return _this2[$file].write(OFS_HEAD, header, OFS_HEAD, SIZEOF_INT); }).then(function () { return current; }); } }, { key: 'open', value: function open() { var _this3 = this; _assertPlus2.default.ok(!this[$header], 'index already open'); var file = this[$file]; return file.read(OFS_MARKER, HLEN).then(function (data) { var marker = data.toString('ascii', OFS_MARKER, OFS_BASEINDEX); if (marker !== MARKER) { throw new Error('Invalid index file; expected file marker to be ' + MARKER + ' but found ' + marker + '.'); } _this3[$header] = data; return _this3; }); } }, { key: 'close', value: function close() { var _this4 = this; return this[$file].close().then(function () { _this4[$header] = null; }); } }, { key: 'name', get: function get() { return this[$file].name; } }, { key: 'writable', get: function get() { return this[$file].writable; } }, { key: 'marker', get: function get() { _assertPlus2.default.ok(this[$header], 'index must be open'); return this[$header].toString('ascii', OFS_MARKER, OFS_BASEINDEX); } }, { key: 'baseIndex', get: function get() { _assertPlus2.default.ok(this[$header], 'index must be open'); return this[$header].readInt32BE(OFS_BASEINDEX, true); } }, { key: 'head', get: function get() { _assertPlus2.default.ok(this[$header], 'index must be open'); return this[$header].readInt32BE(OFS_HEAD, true); } }, { key: 'commitHead', get: function get() { _assertPlus2.default.ok(this[$header], 'index must be open'); return this[$header].readInt32BE(OFS_COMMIT, true); } }]); return LogIndexFile; }(); function open(path, writable) { _assertPlus2.default.string(path, 'path'); _assertPlus2.default.optionalBool(writable, 'writable'); writable = writable === true; return _ranfile2.default.open(path, writable).then(function (file) { return new LogIndexFile(file).open(); }); } function create(path, baseIndex, byteOffset) { _assertPlus2.default.string(path, 'path'); _assertPlus2.default.optionalNumber(baseIndex, 'baseIndex'); _assertPlus2.default.optionalNumber(byteOffset, 'byteOffset'); if (baseIndex === undefined) { baseIndex = 0; } if (byteOffset === undefined) { byteOffset = 0; } var data = allocFill(HLEN + SIZEOF_INT); data.write(MARKER, 'ascii'); data.writeInt32BE(baseIndex, OFS_BASEINDEX, true); data.writeInt32BE(baseIndex, OFS_HEAD, true); data.writeInt32BE(DEF_COMMIT, OFS_COMMIT, true); data.writeInt32BE(byteOffset, HLEN, true); return _ranfile2.default.create(path).then(function (file) { return file.write(OFS_MARKER, data).then(function () { return new LogIndexFile(file).open(); }); }); } LogIndexFile.open = open; LogIndexFile.create = create; exports.default = LogIndexFile; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9sb2ctaW5kZXgtZmlsZS5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7QUFFQTs7OztBQUNBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQTRDQSxJQUFNLFNBQVMsTUFBZjtBQUNBLElBQU0sYUFBYSxDQUFuQjtBQUNBLElBQU0sZ0JBQWdCLENBQXRCO0FBQ0EsSUFBTSxXQUFXLENBQWpCO0FBQ0EsSUFBTSxhQUFhLEVBQW5CO0FBQ0EsSUFBTSxPQUFPLEVBQWI7QUFDQSxJQUFNLGFBQWEsQ0FBbkI7QUFDQSxJQUFNLGFBQWEsQ0FBQyxDQUFwQjs7QUFFQSxJQUFNLFFBQVEsT0FBTyxNQUFQLENBQWQ7QUFDQSxJQUFNLFVBQVUsT0FBTyxRQUFQLENBQWhCOztBQUVBLFNBQVMsU0FBVCxDQUFtQixHQUFuQixFQUF3QjtBQUN0QixTQUFRLE9BQU8sS0FBUixHQUNMLE9BQU8sS0FBUCxDQUFhLEdBQWIsQ0FESyxHQUVMLElBQUksTUFBSixDQUFXLEdBQVgsQ0FGRixDO0FBR0Q7O0lBRUssWTtBQUVKLHdCQUFZLElBQVosRUFBa0I7QUFBQTs7QUFDaEIseUJBQU8sTUFBUCxDQUFjLElBQWQsRUFBb0IsTUFBcEI7QUFDQSx5QkFBTyxJQUFQLENBQVksS0FBSyxJQUFqQixFQUF1QixXQUF2QjtBQUNBLHlCQUFPLElBQVAsQ0FBWSxLQUFLLEtBQWpCLEVBQXdCLFlBQXhCO0FBQ0EseUJBQU8sSUFBUCxDQUFZLEtBQUssS0FBakIsRUFBd0IsWUFBeEI7QUFDQSxRQUFJLEtBQUssSUFBTCxHQUFZLElBQWhCLEVBQXNCO0FBQ3BCLFlBQU0sSUFBSSxLQUFKLENBQVUscUNBQVYsQ0FBTjtBQUNEO0FBQ0QsU0FBSyxLQUFMLElBQWMsSUFBZDtBQUNEOzs7O2dDQTRCVyxLLEVBQU87QUFDakIsMkJBQU8sRUFBUCxDQUFVLEtBQUssT0FBTCxDQUFWLEVBQXlCLG9CQUF6QjtBQUNBLGFBQU8s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Q0EsU0FBTyxrQkFBaUIsTUFBakIsQ0FBd0IsSUFBeEIsRUFDSixJQURJLENBQ0M7QUFBQSxXQUFRLEtBQUssS0FBTCxDQUFXLFVBQVgsRUFBdUIsSUFBdkIsRUFDWCxJQURXLENBQ047QUFBQSxhQUFNLElBQUksWUFBSixDQUFpQixJQUFqQixFQUF1QixJQUF2QixFQUFOO0FBQUEsS0FETSxDQUFSO0FBQUEsR0FERCxDQUFQO0FBR0Q7O0FBRUQsYUFBYSxJQUFiLEdBQW9CLElBQXBCO0FBQ0EsYUFBYSxNQUFiLEdBQXNCLE1BQXRCOztrQkFFZSxZIiwiZmlsZSI6ImxvZy1pbmRleC1maWxlLmpzIiwic291cmNlc0NvbnRlbnQiOlsiJ3VzZSBzdHJpY3QnO1xuXG5pbXBvcnQgYXNzZXJ0IGZyb20gJ2Fzc2VydC1wbHVzJztcbmltcG9ydCBSYW5kb21BY2Nlc3NGaWxlIGZyb20gJ3JhbmZpbGUnO1xuXG4vKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG5UaGUgbG9nIGluZGV4IGZpbGUgaXMgYSBzaWRlLWZpbGUgdGhhdCB0cmFja3MgdGhlIGJ5dGUgb2Zmc2V0cyB0byBlYWNoIGRpc3RpbmN0XG5sb2cgZW50cnkuIFVzaW5nIHRoZSBzaWRlLWZpbGUgaW5kZXhpbmcgc2NoZW1lLCB3ZSBhcmUgYWJsZSB0byBlZmZpY2llbnRseVxuc3RvcmUgYW5kIHJldHJpZXZlIGVhY2ggZGlzdGluY3QgbG9nIGVudHJ5IHdpdGhvdXQgaGF2aW5nIHRvIGNhcmUgYWJvdXQgdGhlXG5lbnRyeSdzIGJ5dGUgY29udGVudC4gSW4gdGhpcyBzY2hlbWUsIGxvZy1zZXJpYWwtbnVtYmVycyAoTFNOcykgYXJlIHRoZSBuYXR1cmFsXG5pbnRlZ2VyIGluZGV4IGF0IHdoaWNoIHRoZSBsb2cgZW50cnkgd2FzIHdyaXR0ZW4sIGFzIGlmIGJlaW5nIHdyaXR0ZW4gdG8gYW5cbmFycmF5LlxuXG5MYXlvdXQ6XG5cbm1hcmtlciAgIC0tIDQgbWFya2VyIGJ5dGVzIGlkZW50aWZ5aW5nIHRoZSBmaWxlJ3MgdHlwZTogSURYJFxuYmFzZSAgICAgLS0gNCBieXRlcyBpbnRlZ2VyIChiaWctZW5kaWFuKTsgYW4gaW5kZXggb2Zmc2V0LCB1c3VhbGx5IDAgKHplcm8pLlxuICAgICAgICAgICAgT2Zmc2V0cyBhcmUgb25seSBtZWFuaW5nZnVsIHdoZW4gaW5kZXhlcyBhcmUgc3BsaXQgYWNyb3NzIG1hbnlcbiAgICAgICAgICAgIGZpbGVzIHNvIGFyZSBhIHBsYWNlaG9sZGVyIGZvciBmdXR1cmUgd29yay5cbmhlYWQgICAgIC0tIDQgYnl0ZXMgaW50ZWdlciAoYmlnLWVuZGlhbik7IHRoZSB3cml0ZSBoZWFkIChuZXh0IHVudXNlZCBlbnRyeSlcbmNvbW1pdCAgIC0tIDQgYnl0ZXMgaW50ZWdlciAoYmlnLWVuZGlhbik7IHRoZSBjb21taXQgaGVhZCAobWF5IHRyYWlsIGhlYWQpXG5cbkluIHRoaXMgbGF5b3V0IHRoZXJlIGFyZSAxNiBoZWFkZXIgYnl0ZXMgKGhsZW4pLCB0aGVyZWZvcmUgZWFjaCBzdWJzZXF1ZW50IDRcbmJ5dGVzIGNvbnRhaW5zIGEgcG9pbnRlciB0byBhIGxvZyBlbnRyeSdzIHN0YXJ0aW5nIGJ5dGUgaW4gdGhlIGluZGV4ZWQgd3JpdGUtXG5haGVhZC1sb2cgKFdBTCkuXG5cbmhsZW4gKyAoKGluZGV4IC0gYmFzZSkgKiBzaXplb2YoaW50KSlcblxuXG5FeGFtcGxlIChvY3RldHMpLi4uXG5cbiAgICAgICAgICAgIEJhc2UgICAgICBIZWFkICAgICAgICBDb21taXQgICAgICBJbmRleCAwICAgICBJbmRleCAxXG4gICAgICAgICAgICB2ICAgICAgICAgICB2ICAgICAgICAgICB2ICAgICAgICAgICBWICAgICAgICAgICB2XG40OSA0NCA1OCAyNCAwMCAwMCAwMCAwMCAwMCAwMCAwMCAwMiAwMCAwMCAwMCAwMSAwMCAwMCAwMCAwMCAwMCAwMCAwMCA1OCBcXFxuXG5JbmRleCAyXG52XG4wMCAwMCAwMiAxZlxuXG5Mb2cgRW50cnkgMCBiZWdpbnMgYXQgYnl0ZSAwXG5Mb2cgRW50cnkgMSBiZWdpbnMgYXQgYnl0ZSA4OFxuTG9nIEVudHJ5IDIgYmVnaW5zIGF0IGJ5dGUgNTQzXG5cblxuXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG5cbmNvbnN0IE1BUktFUiA9ICdJRFgkJztcbmNvbnN0IE9GU19NQVJLRVIgPSAwO1xuY29uc3QgT0ZTX0JBU0VJTkRFWCA9IDQ7XG5jb25zdCBPRlNfSEVBRCA9IDg7XG5jb25zdCBPRlNfQ09NTUlUID0gMTI7XG5jb25zdCBITEVOID0gMTY7XG5jb25zdCBTSVpFT0ZfSU5UID0gNDtcbmNvbnN0IERFRl9DT01NSVQgPSAtMTtcblxuY29uc3QgJGZpbGUgPSBTeW1ib2woJ2ZpbGUnKTtcbmNvbnN0ICRoZWFkZXIgPSBTeW1ib2woJ2hlYWRlcicpO1xuXG5mdW5jdGlvbiBhbGxvY0ZpbGwobGVuKSB7XG4gIHJldHVybiAoQnVmZmVyLmFsbG9jKSA/XG4gICAgQnVmZmVyLmFsbG9jKGxlbikgOlxuICAgIG5ldyBCdWZmZXIobGVuKTsgLy8gZGVwcmVjYXRlZCBpbiA1LngrXG59XG5cbmNsYXNzIExvZ0luZGV4RmlsZSB7XG5cbiAgY29uc3RydWN0b3IoZmlsZSkge1xuICAgIGFzc2VydC5vYmplY3QoZmlsZSwgJ2ZpbGUnKTtcbiAgICBhc3NlcnQuZnVuYyhmaWxlLnJlYWQsICdmaWxlLnJlYWQnKTtcbiAgICBhc3NlcnQuZnVuYyhmaWxlLndyaXRlLCAnZmlsZS53cml0ZScpO1xuICAgIGFzc2VydC5mdW5jKGZpbGUuY2xvc2UsICdmaWxlLmNsb3NlJyk7XG4gICAgaWYgKGZpbGUuc2l6ZSA8IEhMRU4pIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignRmlsZSB0b28gc21hbGwgdG8gYmUgYW4gaW5kZXggZmlsZS4nKTtcbiAgICB9XG4gICAgdGhpc1skZmlsZV0gPSBmaWxlO1xuICB9XG5cbiAgZ2V0IG5hbWUoKSB7IHJldHVybiB0aGlzWyRmaWxlXS5uYW1lOyB9XG5cbiAgZ2V0IHdyaXRhYmxlKCkge1xuICAgIHJldHVybiB0aGlzWyRmaWxlXS53cml0YWJsZTtcbiAgfVxuXG4gIGdldCBtYXJrZXIoKSB7XG4gICAgYXNzZXJ0Lm9rKHRoaXNbJGhlYWRlcl0sICdpbmRleCBtdXN0IGJlIG9wZW4nKTtcbiAgICByZXR1cm4gdGhpc1skaGVhZGVyXS50b1N0cmluZygnYXNjaWknLCBPRlNfTUFSS0VSLCBPRlNfQkFTRUlOREVYKTtcbiAgfVxuXG4gIGdldCBiYXNlSW5kZXgoKSB7XG4gICAgYXNzZXJ0Lm9rKHRoaXNbJGhlYWRlcl0sICdpbmRleCBtdXN0IGJlIG9wZW4nKTtcbiAgICByZXR1cm4gdGhpc1skaGVhZGVyXS5yZWFkSW50MzJCRShPRlNfQkFTRUlOREVYLCB0cnVlKTtcbiAgfVxuXG4gIGdldCBoZWFkKCkge1xuICAgIGFzc2VydC5vayh0aGlzWyRoZWFkZXJdLCAnaW5kZXggbXVzdCBiZSBvcGVuJyk7XG4gICAgcmV0dXJuIHRoaXNbJGhlYWRlcl0ucmVhZEludDMyQkUoT0ZTX0hFQUQsIHRydWUpO1xuICB9XG5cbiAgZ2V0IGNvbW1pdEhlYWQoKSB7XG4gICAgYXNzZXJ0Lm9rKHRoaXNbJGhlYWRlcl0sICdpbmRleCBtdXN0IGJlIG9wZW4nKTtcbiAgICByZXR1cm4gdGhpc1skaGVhZGVyXS5yZWFkSW50MzJCRShPRlNfQ09NTUlULCB0cnVlKTtcbiAgfVxuXG4gIGlzQ29tbWl0dGVkKGluZGV4KSB7XG4gICAgYXNzZXJ0Lm9rKHRoaXNbJGhlYWRlcl0sICdpbmRleCBtdXN0IGJlIG9wZW4nKTtcbiAgICByZXR1cm4gaW5kZXggPCB0aGlzLmNvbW1pdEhlYWQ7XG4gIH1cblxuICBjb21taXQoaW5kZXgpIHtcbiAgICBhc3NlcnQub2sodGhpc1skaGVhZGVyXSwgJ2luZGV4IG11c3QgYmUgb3BlbicpO1xuICAgIGFzc2VydC5udW1iZXIoaW5kZXgsICdpbmRleCcpO1xuICAgIGxldCBleHBlY3RlZCA9IHRoaXMuY29tbWl0SGVhZCArIDE7XG4gICAgaWYgKGluZGV4IDwgZXhwZWN0ZWQpIHtcbiAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoaW5kZXgpO1xuICAgIH1cbiAgICBpZiAoaW5kZXggIT09IGV4cGVjdGVkKSB7XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKGBPdXQgb2Ygb3JkZXIgY29tbWl0OyBleHBlY3RlZCAke2V4cGVjdGVkfSBidXQgcmVjZWl2ZWQgJHtpbmRleH0uYCkpO1xuICAgIH1cbiAgICB0aGlzWyRoZWFkZXJdLndyaXRlSW50MzJCRShpbmRleCwgT0ZTX0NPTU1JVCwgdHJ1ZSk7XG4gICAgcmV0dXJuIHRoaXNbJGZpbGVdLndyaXRlKE9GU19DT01NSVQsIHRoaXNbJGhlYWRlcl0sIE9GU19DT01NSVQsIFNJWkVPRl9JTlQpXG4gICAgICAudGhlbigoKSA9PiBpbmRleCk7XG4gIH1cblxuICBsb2NhbGl6ZUluZGV4KGluZGV4KSB7XG4gICAgYXNzZXJ0Lm51bWJlcihpbmRleCwgJ2luZGV4Jyk7XG4gICAgaW5kZXggPSBpbmRleCAtIHRoaXMuYmFzZUluZGV4O1xuICAgIHJldHVybiBpbmRleDtcbiAgfVxuXG4gIGdsb2JhbGl6ZUluZGV4KGluZGV4KSB7XG4gICAgYXNzZXJ0Lm51bWJlcihpbmRleCwgJ2luZGV4Jyk7XG4gICAgaW5kZXggPSBpbmRleCArIHRoaXMuYmFzZUluZGV4O1xuICAgIHJldHVybiBpbmRleDtcbiAgfVxuXG4gIG9mZnNldChpbmRleCkge1xuICAgIGFzc2VydC5vayhpbmRleCA8PSB0aGlzLmhlYWQsICdpbmRleCBvdXQgb2YgcmFuZ2UnKTtcbiAgICBhc3NlcnQub2sodGhpc1skaGVhZGVyXSwgJ2luZGV4IG11c3QgYmUgb3BlbicpO1xuICAgIGxldCBvZnMgPSBITEVOICsgKHRoaXMubG9jYWxpemVJbmRleChpbmRleCkgKiBTSVpFT0ZfSU5UKTtcbiAgICByZXR1cm4gdGhpc1skZmlsZV0ucmVhZChvZnMsIFNJWkVPRl9JTlQpXG4gICAgICAudGhlbihkYXRhID0+IHtcbiAgICAgICAgbGV0IG9mZnNldCA9IGRhdGEucmVhZEludDMyQkUoMCwgU0laRU9GX0lOVCk7XG4gICAgICAgIHJldHVybiBvZmZzZXQ7XG4gICAgICB9KTtcbiAgfVxuXG4gIGdldChpbmRleCkge1xuICAgIGFzc2VydC5udW1iZXIoaW5kZXgsICdpbmRleCcpO1xuICAgIGFzc2VydC5vayhpbmRleCA8IHRoaXMuaGVhZCwgJ2luZGV4IG91dCBvZiByYW5nZScpO1xuICAgIGFzc2VydC5vayh0aGlzWyRoZWFkZXJdLCAnaW5kZXggbXVzdCBiZSBvcGVuJyk7XG4gICAgbGV0IG9mcyA9IEhMRU4gKyAodGhpcy5sb2NhbGl6ZUluZGV4KGluZGV4KSAqIFNJWkVPRl9JTlQpO1xuICAgIHJldHVybiB0aGlzWyRmaWxlXS5yZWFkKG9mcywgMiAqIFNJWkVPRl9JTlQpXG4gICAgICAudGhlbihkYXRhID0+IHtcbiAgICAgICAgbGV0IG9mZnNldCA9IGRhdGEucmVhZEludDMyQkUoMCwgU0laRU9GX0lOVCk7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgb2Zmc2V0LFxuICAgICAgICAgIGxlbmd0aDogZGF0YS5yZWFkSW50MzJCRShTSVpFT0ZfSU5ULCBTSVpFT0ZfSU5UKSAtIG9mZnNldFxuICAgICAgICB9O1xuICAgICAgfSk7XG4gIH1cblxuICBnZXRSYW5nZShpbmRleCwgY291bnQpIHtcbiAgICBhc3NlcnQubnVtYmVyKGluZGV4LCAnaW5kZXgnKTtcbiAgICBhc3NlcnQub2soaW5kZXggPCB0aGlzLmhlYWQsICdpbmRleCBvdXQgb2YgcmFuZ2UnKTtcbiAgICBhc3NlcnQubnVtYmVyKGNvdW50LCAnY291bnQnKTtcbiAgICBhc3NlcnQub2sodGhpc1skaGVhZGVyXSwgJ2luZGV4IG11c3QgYmUgb3BlbicpO1xuICAgIC8vIGhlYWQgcG9pbnRzIGF0IHRoZSBuZXh0IGVudHJ5XG4gICAgbGV0IGxpbWl0ID0gKHRoaXMuaGVhZCAtIGluZGV4KTtcbiAgICBhc3NlcnQub2soY291bnQgPD0gbGltaXQsICdjb3VudCBwdXRzIGluZGV4IG91dCBvZiByYW5nZScpO1xuICAgIGxldCBvZnMgPSBITEVOICsgKHRoaXMubG9jYWxpemVJbmRleChpbmRleCkgKiBTSVpFT0ZfSU5UKTtcbiAgICByZXR1cm4gdGhpc1skZmlsZV0ucmVhZChvZnMsIChjb3VudCArIDEpICogU0laRU9GX0lOVClcbiAgICAgIC50aGVuKGRhdGEgPT4ge1xuICAgICAgICBsZXQgaSA9IC0xO1xuICAgICAgICBsZXQgcmVzdWx0ID0gW107XG4gICAgICAgIHdoaWxlICgrK2kgPCBjb3VudCkge1xuICAgICAgICAgIGxldCBvZmZzZXQgPSBkYXRhLnJlYWRJbnQzMkJFKGkgKiBTSVpFT0ZfSU5ULCBTSVpFT0ZfSU5UKTtcbiAgICAgICAgICByZXN1bHQucHVzaCh7XG4gICAgICAgICAgICBpbmRleDogaW5kZXggKyBpLFxuICAgICAgICAgICAgb2Zmc2V0LFxuICAgICAgICAgICAgbGVuZ3RoOiBkYXRhLnJlYWRJbnQzMkJFKChpICsgMSkgKiBTSVpFT0ZfSU5ULCBTSVpFT0ZfSU5UKSAtIG9mZnNldFxuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgICB9KTtcbiAgfVxuXG4gIHRydW5jYXRlKGZyb20pIHtcbiAgICBhc3NlcnQubnVtYmVyKGZyb20sICdmcm9tJyk7XG4gICAgYXNzZXJ0Lm9rKGZyb20gPiB0aGlzLmNvbW1pdEhlYWQsICdjYW5ub3QgdHJ1bmNhdGUgYSBjb21taXR0ZWQgbG9nIGVudHJ5Jyk7XG4gICAgYXNzZXJ0Lm9rKGZyb20gPCB0aGlzLmhlYWQsICdpbmRleCBvdXQgb2YgcmFuZ2UnKTtcbiAgICBsZXQgaGVhZGVyID0gdGhpc1skaGVhZGVyXTtcbiAgICBoZWFkZXIud3JpdGVJbnQzMkJFKGZyb20sIE9GU19IRUFELCBTSVpFT0ZfSU5UKTtcbiAgICByZXR1cm4gdGhpc1skZmlsZV0ud3JpdGUoT0ZTX0hFQUQsIGhlYWRlciwgT0ZTX0hFQUQsIFNJWkVPRl9JTlQpXG4gICAgICAudGhlbigoKSA9PiB7XG4gICAgICAgIGlmICh0aGlzLmhlYWQgPT09IHRoaXMuYmFzZUluZGV4KSB7XG4gICAgICAgICAgcmV0dXJuIHRoaXNbJGZpbGVdLnJlYWQoSExFTiwgU0laRU9GX0lOVClcbiAgICAgICAgICAgIC50aGVuKGRhdGEgPT4ge1xuICAgICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgIG9mZnNldDogZGF0YS5yZWFkSW50MzJCRSgwLCBTSVpFT0ZfSU5UKSxcbiAgICAgICAgICAgICAgICBsZW5ndGg6IDBcbiAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzLmdldCh0aGlzLmhlYWQgLSAxKTtcbiAgICAgIH0pXG4gICAgICAudGhlbihyZWMgPT4gcmVjLm9mZnNldCArIHJlYy5sZW5ndGgpO1xuICB9XG5cbiAgaW5jcmVtZW50KG9mZnNldCkge1xuICAgIGFzc2VydC5vayh0aGlzWyRoZWFkZXJdLCAnaW5kZXggbXVzdCBiZSBvcGVuJyk7XG4gICAgLy8gd3JpdGUgdGhlIG9mZnNldCBpbnRvIHRoZSBuZXh0IGluZGV4IGxvY2F0aW9uICh0eXBpY2FsbHkgYXQgRU9GKSxcbiAgICAvLyB0aGVuIHVwZGF0ZSBhbmQgd3JpdGUgdGhlIGhlYWRlciByZWZlcmVuY2UsIHdoaWNoIGNvbXBsZXRlcyB0aGVcbiAgICAvLyBpbmNyZW1lbnQuXG4gICAgbGV0IGhlYWRlciA9IHRoaXNbJGhlYWRlcl07XG4gICAgbGV0IGN1cnJlbnQgPSB0aGlzLmhlYWQ7XG4gICAgbGV0IG5leHQgPSBjdXJyZW50ICsgMTtcbiAgICBsZXQgb2ZzID0gSExFTiArICh0aGlzLmxvY2FsaXplSW5kZXgobmV4dCkgKiBTSVpFT0ZfSU5UKTtcbiAgICBsZXQgZGF0YSA9IGFsbG9jRmlsbChTSVpFT0ZfSU5UKTtcbiAgICBkYXRhLndyaXRlSW50MzJCRShvZmZzZXQsIDAsIFNJWkVPRl9JTlQpO1xuICAgIHJldHVybiB0aGlzWyRmaWxlXS53cml0ZShvZnMsIGRhdGEpXG4gICAgICAudGhlbigoKSA9PiB7XG4gICAgICAgIGhlYWRlci53cml0ZUludDMyQkUobmV4dCwgT0ZTX0hFQUQsIFNJWkVPRl9JTlQpO1xuICAgICAgICByZXR1cm4gdGhpc1skZmlsZV0ud3JpdGUoT0ZTX0hFQUQsIGhlYWRlciwgT0ZTX0hFQUQsIFNJWkVPRl9JTlQpO1xuICAgICAgfSlcbiAgICAgIC50aGVuKCgpID0+IHtcbiAgICAgICAgcmV0dXJuIGN1cnJlbnQ7XG4gICAgICB9KTtcbiAgfVxuXG4gIG9wZW4oKSB7XG4gICAgYXNzZXJ0Lm9rKCF0aGlzWyRoZWFkZXJdLCAnaW5kZXggYWxyZWFkeSBvcGVuJyk7XG4gICAgbGV0IGZpbGUgPSB0aGlzWyRmaWxlXTtcbiAgICByZXR1cm4gZmlsZS5yZWFkKE9GU19NQVJLRVIsIEhMRU4pXG4gICAgICAudGhlbihkYXRhID0+IHtcbiAgICAgICAgbGV0IG1hcmtlciA9IGRhdGEudG9TdHJpbmcoJ2FzY2lpJywgT0ZTX01BUktFUiwgT0ZTX0JBU0VJTkRFWCk7XG4gICAgICAgIGlmIChtYXJrZXIgIT09IE1BUktFUikge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBpbmRleCBmaWxlOyBleHBlY3RlZCBmaWxlIG1hcmtlciB0byBiZSAke01BUktFUn0gYnV0IGZvdW5kICR7bWFya2VyfS5gKTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzWyRoZWFkZXJdID0gZGF0YTtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgICB9KTtcbiAgfVxuXG4gIGNsb3NlKCkge1xuICAgIHJldHVybiB0aGlzWyRmaWxlXS5jbG9zZSgpXG4gICAgICAudGhlbigoKSA9PiB7XG4gICAgICAgIHRoaXNbJGhlYWRlcl0gPSBudWxsO1xuICAgICAgfSk7XG4gIH1cblxufVxuXG5mdW5jdGlvbiBvcGVuKHBhdGgsIHdyaXRhYmxlKSB7XG4gIGFzc2VydC5zdHJpbmcocGF0aCwgJ3BhdGgnKTtcbiAgYXNzZXJ0Lm9wdGlvbmFsQm9vbCh3cml0YWJsZSwgJ3dyaXRhYmxlJyk7XG4gIHdyaXRhYmxlID0gd3JpdGFibGUgPT09IHRydWU7XG4gIHJldHVybiBSYW5kb21BY2Nlc3NGaWxlLm9wZW4ocGF0aCwgd3JpdGFibGUpXG4gICAgLnRoZW4oZmlsZSA9PiBuZXcgTG9nSW5kZXhGaWxlKGZpbGUpLm9wZW4oKSk7XG59XG5cbmZ1bmN0aW9uIGNyZWF0ZShwYXRoLCBiYXNlSW5kZXgsIGJ5dGVPZmZzZXQpIHtcbiAgYXNzZXJ0LnN0cmluZyhwYXRoLCAncGF0aCcpO1xuICBhc3NlcnQub3B0aW9uYWxOdW1iZXIoYmFzZUluZGV4LCAnYmFzZUluZGV4Jyk7XG4gIGFzc2VydC5vcHRpb25hbE51bWJlcihieXRlT2Zmc2V0LCAnYnl0ZU9mZnNldCcpO1xuICBpZiAoYmFzZUluZGV4ID09PSB1bmRlZmluZWQpIHtcbiAgICBiYXNlSW5kZXggPSAwO1xuICB9XG4gIGlmIChieXRlT2Zmc2V0ID09PSB1bmRlZmluZWQpIHtcbiAgICBieXRlT2Zmc2V0ID0gMDtcbiAgfVxuICBsZXQgZGF0YSA9IGFsbG9jRmlsbChITEVOICsgU0laRU9GX0lOVCk7XG4gIGRhdGEud3JpdGUoTUFSS0VSLCAnYXNjaWknKTtcbiAgZGF0YS53cml0ZUludDMyQkUoYmFzZUluZGV4LCBPRlNfQkFTRUlOREVYLCB0cnVlKTtcbiAgZGF0YS53cml0ZUludDMyQkUoYmFzZUluZGV4LCBPRlNfSEVBRCwgdHJ1ZSk7XG4gIGRhdGEud3JpdGVJbnQzMkJFKERFRl9DT01NSVQsIE9GU19DT01NSVQsIHRydWUpO1xuICBkYXRhLndyaXRlSW50MzJCRShieXRlT2Zmc2V0LCBITEVOLCB0cnVlKTtcbiAgcmV0dXJuIFJhbmRvbUFjY2Vzc0ZpbGUuY3JlYXRlKHBhdGgpXG4gICAgLnRoZW4oZmlsZSA9PiBmaWxlLndyaXRlKE9GU19NQVJLRVIsIGRhdGEpXG4gICAgICAudGhlbigoKSA9PiBuZXcgTG9nSW5kZXhGaWxlKGZpbGUpLm9wZW4oKSkpO1xufVxuXG5Mb2dJbmRleEZpbGUub3BlbiA9IG9wZW47XG5Mb2dJbmRleEZpbGUuY3JlYXRlID0gY3JlYXRlO1xuXG5leHBvcnQgZGVmYXVsdCBMb2dJbmRleEZpbGU7XG4iXX0=