wal
Version:
An implementation of write-ahead logging (WAL) for nodejs.
327 lines (291 loc) • 31.9 kB
JavaScript
'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=